Oregami
Repositories/oxedyne/fe2o3

oxedyne/fe2o3/fe2o3_data/src/iblt/hash.rs

2.0 KiB, 1 run

created by r1870400018:11212, which is this file's identity for as long as the history lasts, whatever it is later renamed to

download · who wrote it · its history

1//! Internal hash primitive used by the IBLT.
2//!
3//! Deterministic, seeded, and portable: a splitmix64 finaliser composed over
4//! 8-byte chunks of the key. Not cryptographically secure -- adversarial
5//! callers should pre-hash their keys with a keyed cryptographic hash (SipHash,
6//! BLAKE3) before feeding them to the IBLT. The interior hash here only has to
7//! be pseudo-random enough that the `k` cell selections are independent and
8//! uniformly distributed over `num_cells`, which the splitmix64 mixer provides
9//! comfortably for non-adversarial inputs.
10
11/// A splitmix64 avalanche step. Deterministic, parameter-free, reversible.
12pub(crate) fn mix64(mut x: u64) -> u64 {
13 x ^= x >> 30;
14 x = x.wrapping_mul(0xbf58_476d_1ce4_e5b9);
15 x ^= x >> 27;
16 x = x.wrapping_mul(0x94d0_49bb_1331_11eb);
17 x ^= x >> 31;
18 x
19}
20
21/// Hashes a byte slice under a seed, producing a 64-bit value.
22///
23/// Processes the key in 8-byte chunks, XOR-accumulating each chunk into the
24/// running state and mixing between chunks. The final state is XORed with the
25/// length to distinguish inputs that differ only in trailing zero bytes, then
26/// mixed once more.
27pub(crate) fn hash_bytes(bytes: &[u8], seed: u64) -> u64 {
28 let mut h = seed.wrapping_add(0x9e37_79b9_7f4a_7c15);
29 h = mix64(h);
30 for chunk in bytes.chunks(8) {
31 let mut buf = [0u8; 8];
32 buf[..chunk.len()].copy_from_slice(chunk);
33 h ^= u64::from_le_bytes(buf);
34 h = mix64(h);
35 }
36 h ^= bytes.len() as u64;
37 mix64(h)
38}
39
40/// Two-output hash used for double-hashing. Returns `(h1, h2)` where `h1` is
41/// the primary hash used for the purity-check fingerprint and the first cell
42/// index, and `h2` is the step used for subsequent cell indices.
43pub(crate) fn hash_pair(bytes: &[u8], seed: u64) -> (u64, u64) {
44 let h1 = hash_bytes(bytes, seed);
45 // Second seed differs deterministically from the first. Mixing the seed
46 // through a fixed constant keeps the two outputs independent without
47 // exposing a second seed parameter.
48 let h2 = hash_bytes(bytes, seed ^ 0x5851_f42d_4c95_7f2d);
49 (h1, h2)
50}