Hashing: SHA-256 & HMAC

akurai-hash gives every AkurAI application FIPS 180-4 SHA-256 and RFC 2104 HMAC-SHA256 without linking sha2 or hmac. Pure std, #![forbid(unsafe_code)], and proven by known-answer vectors (NIST empty/abc/two-block/one-million-a, RFC 4231 cases 1, 2, 6, 7) in crates/hash/tests/vectors.rs.

One-shot

use akurai_hash::{hex_lower, sha256};

let digest: [u8; 32] = sha256(b"abc");
assert_eq!(
    hex_lower(&digest),
    "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"
);

Streaming

Hash data that does not fit in memory, or arrives in chunks:

use akurai_hash::Sha256;

let mut h = Sha256::new();
for chunk in reader_chunks() {
    h.update(&chunk);
}
let digest = h.finalize();

Chunk boundaries never affect the result — feeding one byte at a time yields the same digest as one call.

HMAC

use akurai_hash::{hex_lower, hmac_sha256};

let mac = hmac_sha256(b"Jefe", b"what do ya want for nothing?");
assert_eq!(
    hex_lower(&mac),
    "5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843"
);

Keys longer than the 64-byte block are hashed first, per RFC 2104. This is the building block for AWS SigV4 request signing and any keyed integrity check.

Content-addressed blobs

The blob store keeps its fast FNV-1a ids by default. When ids must resist an adversary crafting collisions, opt in per call:

use akurai_blobs::{content_id_sha256, BlobStore};

let mut store = BlobStore::open("data/blobs.db")?;
let id = store.put_sha256(bytes)?;          // 64 lowercase hex chars
assert_eq!(id, content_id_sha256(bytes));

FNV ids are 32 chars and SHA-256 ids are 64, so the two schemes never collide inside one store.

Performance

A straightforward scalar implementation: tens of MB/s, adequate for signing, ETags, and upload integrity. It is not SIMD-accelerated; hashing multi-gigabyte streams on the request path should be avoided regardless of implementation.