Hashes
MD5, SHA-1, SHA-2, SHA-3, BLAKE2, xxHash and CRC-32 of text and files, and Subresource Integrity values for script tags.
Version 1.0.0 · 5 functions · 8 crates
Functions
What use hashes.qwer gives a program, with the types Quillo sees. To take only some, or under other names: use hash_text, hash_file from hashes (Quillo 0.6).
hash_text(algorithm: str, text: str) → stror an errorThe hash of text (its UTF-8 bytes) in lower-case hex. algorithm: md5, sha1, sha224, sha256, sha384, sha512, sha3-256, sha3-512, blake2b, blake2s, xxh3, xxh64, xxh32 or crc32. MD5, SHA-1, xxHash and CRC-32 are for checksums and cache keys, never for passwords or signatures.
hash_file(algorithm: str, path: str) → stror an errorThe hash of the file at path, read in pieces (any size), in hex: to compare with a checksum a vendor publishes.
hash_all(text: str) → mapEvery algorithm's hash of text at once, as a map from its name to hex: to find which one an old system used.
sri_file(path: str, algorithm: str) → stror an errorThe Subresource Integrity value of a file (sha384-... with algorithm "" or "sha384"; also sha256 and sha512), for <script src integrity> and <link integrity>, so browsers refuse a copy that was changed on a CDN.
sri_text(text: str, algorithm: str) → stror an errorThe Subresource Integrity value of text, as sri_file gives it for a file.
Get it
$ quillo install hashes # hashes.qwer into this folder$ quillo install hashes --example # and its example, hashes.qweOr with curl:
$ curl -O https://quillolang.org/plugins/files/hashes.qwer$ curl -O https://quillolang.org/plugins/files/hashes.qweThen, in your program, all of it or only what you call:
use hashes.qweruse hash_text, hash_file from hashes // Quillo 0.6: only theseIt uses these crates, which cargo fetches the first time the plugin is built:
md-5 = "0.10"sha1 = "0.10"sha2 = "0.10"sha3 = "0.10"blake2 = "0.10"xxhash-rust = { version = "0.8", features = ["xxh3", "xxh64", "xxh32"] }crc32fast = "1"base64 = "0.22"Example
// Integrity values for the site's scripts, a checksum of a download, and cache keys.//// quillo run hashes.qwe then open http://localhost:8080/pageuse "hashes.qwer"route GET /page { write_file("data/app.js", "console.log('hello from Quillo')\n") let integrity = sri_file("data/app.js", "sha384")? respond(200, `<script src="/app.js" integrity="${integrity}" crossorigin="anonymous"></script>`, { "content-type": "text/html" })}route GET /checksums = { md5: hash_text("md5", "hello")?, sha1: hash_text("sha1", "hello")?, sha3_256: hash_text("sha3-256", "hello")?, blake2b: hash_text("blake2b", "hello")?, xxh3: hash_text("xxh3", "hello")?, crc32: hash_text("crc32", "hello")?, same_as_builtin: hash_text("sha256", "hello")? == crypto.sha256("hello"),}route GET /download { write_file("data/release.txt", repeat("Quillo 1.0 release notes. ", 100)) { sha256: hash_file("sha256", "data/release.txt")?, crc32: hash_file("crc32", "data/release.txt")? }}// Which algorithm an old system used, from a password reset token it stored.route GET /which ? { text: str = "hello", hash: str = "aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d" } { let all = hash_all(text) { used: keys(all) | filter((name) => all[name] == hash) }}$ quillo run hashes.qwe$ curl 'localhost:8080/checksums'$ curl 'localhost:8080/page'$ curl 'localhost:8080/which'Source
//! Hashes and checksums of text and files: MD5, SHA-1, SHA-2, SHA-3, BLAKE2, xxHash and CRC-32, and Subresource Integrity values for `<script integrity>`.//! version: 1.0.0//!//! [dependencies]//! md-5 = "0.10"//! sha1 = "0.10"//! sha2 = "0.10"//! sha3 = "0.10"//! blake2 = "0.10"//! xxhash-rust = { version = "0.8", features = ["xxh3", "xxh64", "xxh32"] }//! crc32fast = "1"//! base64 = "0.22"use base64::engine::general_purpose::STANDARD;use base64::Engine;use sha2::digest::DynDigest;use std::collections::HashMap;use std::io::Read;const NAMES: &str = "md5, sha1, sha224, sha256, sha384, sha512, sha3-256, sha3-512, blake2b, blake2s, xxh3, xxh64, xxh32, crc32";enum Hasher { Digest(Box<dyn DynDigest>), Xxh3(xxhash_rust::xxh3::Xxh3), Xxh64(xxhash_rust::xxh64::Xxh64), Xxh32(xxhash_rust::xxh32::Xxh32), Crc32(crc32fast::Hasher),}fn hasher(algorithm: &str) -> Result<Hasher, String> { use Hasher::*; Ok(match algorithm.to_lowercase().replace('_', "-").as_str() { "md5" => Digest(Box::new(md5::Md5::default())), "sha1" | "sha-1" => Digest(Box::new(sha1::Sha1::default())), "sha224" | "sha-224" => Digest(Box::new(sha2::Sha224::default())), "sha256" | "sha-256" => Digest(Box::new(sha2::Sha256::default())), "sha384" | "sha-384" => Digest(Box::new(sha2::Sha384::default())), "sha512" | "sha-512" => Digest(Box::new(sha2::Sha512::default())), "sha3-256" => Digest(Box::new(sha3::Sha3_256::default())), "sha3-512" => Digest(Box::new(sha3::Sha3_512::default())), "blake2b" | "blake2b-512" => Digest(Box::new(blake2::Blake2b512::default())), "blake2s" | "blake2s-256" => Digest(Box::new(blake2::Blake2s256::default())), "xxh3" | "xxh3-64" => Xxh3(xxhash_rust::xxh3::Xxh3::new()), "xxh64" => Xxh64(xxhash_rust::xxh64::Xxh64::new(0)), "xxh32" => Xxh32(xxhash_rust::xxh32::Xxh32::new(0)), "crc32" => Crc32(crc32fast::Hasher::new()), other => return Err(format!("unknown algorithm `{}`: {}", other, NAMES)), })}impl Hasher { fn update(&mut self, b: &[u8]) { match self { Hasher::Digest(d) => d.update(b), Hasher::Xxh3(h) => h.update(b), Hasher::Xxh64(h) => h.update(b), Hasher::Xxh32(h) => h.update(b), Hasher::Crc32(h) => h.update(b), } } fn finish(self) -> Vec<u8> { match self { Hasher::Digest(d) => d.finalize().to_vec(), Hasher::Xxh3(h) => h.digest().to_be_bytes().to_vec(), Hasher::Xxh64(h) => h.digest().to_be_bytes().to_vec(), Hasher::Xxh32(h) => h.digest().to_be_bytes().to_vec(), Hasher::Crc32(h) => h.finalize().to_be_bytes().to_vec(), } }}fn hex(b: &[u8]) -> String { b.iter().map(|x| format!("{:02x}", x)).collect()}fn of_file(algorithm: &str, path: &str) -> Result<Vec<u8>, String> { let mut h = hasher(algorithm)?; let mut f = std::fs::File::open(path).map_err(|e| format!("{}: {}", path, e))?; let mut buf = vec![0u8; 64 * 1024]; loop { let n = f.read(&mut buf).map_err(|e| format!("{}: {}", path, e))?; if n == 0 { break; } h.update(&buf[..n]); } Ok(h.finish())}fn sri_alg(algorithm: &str) -> Result<&'static str, String> { match algorithm.to_lowercase().as_str() { "" | "sha384" => Ok("sha384"), "sha256" => Ok("sha256"), "sha512" => Ok("sha512"), other => Err(format!("SRI takes sha256, sha384 or sha512, not `{}`", other)), }}/// The hash of `text` (its UTF-8 bytes) in lower-case hex. `algorithm`: md5, sha1, sha224, sha256, sha384, sha512, sha3-256,/// sha3-512, blake2b, blake2s, xxh3, xxh64, xxh32 or crc32. MD5, SHA-1, xxHash and CRC-32 are for checksums and cache keys,/// never for passwords or signatures.pub fn hash_text(algorithm: &str, text: &str) -> Result<String, String> { let mut h = hasher(algorithm)?; h.update(text.as_bytes()); Ok(hex(&h.finish()))}/// The hash of the file at `path`, read in pieces (any size), in hex: to compare with a checksum a vendor publishes.pub fn hash_file(algorithm: &str, path: &str) -> Result<String, String> { Ok(hex(&of_file(algorithm, path)?))}/// Every algorithm's hash of `text` at once, as a map from its name to hex: to find which one an old system used.pub fn hash_all(text: &str) -> HashMap<String, String> { NAMES.split(", ").filter_map(|n| hash_text(n, text).ok().map(|h| (n.to_string(), h))).collect()}/// The Subresource Integrity value of a file (`sha384-...` with `algorithm` "" or "sha384"; also sha256 and sha512), for/// `<script src integrity>` and `<link integrity>`, so browsers refuse a copy that was changed on a CDN.pub fn sri_file(path: &str, algorithm: &str) -> Result<String, String> { let a = sri_alg(algorithm)?; Ok(format!("{}-{}", a, STANDARD.encode(of_file(a, path)?)))}/// The Subresource Integrity value of `text`, as sri_file gives it for a file.pub fn sri_text(text: &str, algorithm: &str) -> Result<String, String> { let a = sri_alg(algorithm)?; let mut h = hasher(a)?; h.update(text.as_bytes()); Ok(format!("{}-{}", a, STANDARD.encode(h.finish())))}Related
file_hash.qwer · 1.0.0SHA-256, SHA-512 and BLAKE3 of files read as bytes: checksums for downloads, dedupe and cache keys.7 views · 6 downloadsBencode and torrents bencode.qwer · 1.0.0Bencode, BitTorrent's format, encoded and decoded; .torrent files read (files, sizes, trackers, info hash, magnet link) and made from a file.7 views · 10 downloadsLZ4 lz4.qwer · 1.0.0LZ4 compression of files and text in the standard .lz4 frame format: the fastest to pack and unpack, for caches, logs and data passed between services.6 views · 7 downloadsage age.qwer · 1.0.0age encryption: text and files encrypted to people's public keys and opened only with their identity, compatible with the age command.7 views · 8 downloadsEncrypt encrypt.qwer · 1.0.0Encrypt and decrypt text and files with a secret key (AES-256-GCM): API tokens, notes and uploads kept unreadable at rest.7 views · 7 downloadsHash chain hash_chain.qwer · 1.0.0A tamper-evident audit log: each entry's SHA-256 covers the one before, so a changed, removed or reordered entry is found.7 views · 11 downloadsThread 0 posts
On Hashes: questions, tips, corrections, and code that uses it.
No posts yet. The first one opens the thread.