Security

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

#checksums#files#cache#integrity

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 error

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.

hash_file(algorithm: str, path: str) → stror an error

The 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) → map

Every 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 error

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.

sri_text(text: str, algorithm: str) → stror an error

The 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.qwe

Or with curl:

$ curl -O https://quillolang.org/plugins/files/hashes.qwer$ curl -O https://quillolang.org/plugins/files/hashes.qwe

Then, in your program, all of it or only what you call:

main.qwe
use hashes.qweruse hash_text, hash_file from hashes      // Quillo 0.6: only these

It uses these crates, which cargo fetches the first time the plugin is built:

[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"

Example

hashes.qwe
// 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.qwer
//! 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())))}
Thread
76Useful? Like it, or add to its thread below.

Thread 0 posts

On Hashes: questions, tips, corrections, and code that uses it.

No posts yet. The first one opens the thread.