kenqtade's picture
Use ken-q org namespace for project Spaces
0865492
Raw
History Blame Contribute Delete
2.01 kB
import { Hasher } from "@huggingface/blake3-jit";
import type { Chunk } from "./xet-chunker.js";
import { xorbHash } from "./xorb-hash.js";
const ZERO_KEY = new Uint8Array(32);
const VERIFICATION_KEY = new Uint8Array([
127, 24, 87, 214, 206, 86, 237, 102, 18, 127, 249, 19, 231, 165, 195, 243, 164, 205, 38, 213, 181, 219, 73, 230,
65, 36, 152, 127, 40, 251, 148, 195,
]);
const fileHasher = Hasher.newKeyed(ZERO_KEY);
const verificationHasher = Hasher.newKeyed(VERIFICATION_KEY);
/**
* file_hash = hmac(xorb_hash(chunks), zero_key)
*
* Matches Rust's `merklehash::file_hash` which calls
* `file_hash_with_salt(chunks, &[0; 32])`.
*/
export function fileHash(chunks: Chunk[]): Uint8Array {
// Empty input short-circuits to the all-zero MerkleHash, matching Rust's
// `file_hash_with_salt` (`if chunks.is_empty() { return MerkleHash::default(); }`).
// Without this we'd return `hmac(0, zero_key)`, which the CAS shard validation rejects
// for empty files with "file reconstruction does not produce this hash".
if (chunks.length === 0) {
return new Uint8Array(32);
}
const xorb = xorbHash(chunks);
return fileHasher.reset().update(xorb).finalize(32);
}
/**
* HMAC: blake3_keyed_hash(key_bytes, hash_bytes)
*
* Both inputs are 32-byte Uint8Arrays.
* Matches Rust's `DataHash::hmac`.
*
* Uses a fresh hasher per call since the key varies.
*/
export function hmac(hash: Uint8Array, key: Uint8Array): Uint8Array {
return Hasher.newKeyed(key).update(hash).finalize(32);
}
/**
* Verification hash for a range of chunk hashes.
* Concatenates all 32-byte hashes and applies blake3_keyed_hash
* with VERIFICATION_KEY.
*
* Matches Rust's `chunk_verification::range_hash_from_chunks`.
*/
export function verificationHash(chunkHashes: Uint8Array[]): Uint8Array {
const combined = new Uint8Array(chunkHashes.length * 32);
for (let i = 0; i < chunkHashes.length; i++) {
combined.set(chunkHashes[i], i * 32);
}
return verificationHasher.reset().update(combined).finalize(32);
}