Skip to main content

oxihuman_core/
hashing_blake3.rs

1// Copyright (C) 2026 COOLJAPAN OU (Team KitaSan)
2// SPDX-License-Identifier: Apache-2.0
3#![allow(dead_code)]
4
5//! BLAKE3 hash stub.
6
7/// 32-byte BLAKE3 digest.
8#[derive(Debug, Clone, PartialEq, Eq)]
9pub struct Blake3Digest(pub [u8; 32]);
10
11impl Blake3Digest {
12    /// Return the digest as a lowercase hex string.
13    pub fn to_hex(&self) -> String {
14        self.0.iter().map(|b| format!("{:02x}", b)).collect()
15    }
16
17    pub fn as_bytes(&self) -> &[u8; 32] {
18        &self.0
19    }
20}
21
22/// BLAKE3 hasher stub.
23#[derive(Debug, Clone, Default)]
24pub struct Blake3Hasher {
25    buf: Vec<u8>,
26    key: Option<[u8; 32]>,
27}
28
29impl Blake3Hasher {
30    pub fn new() -> Self {
31        Self::default()
32    }
33
34    pub fn new_keyed(key: [u8; 32]) -> Self {
35        Self {
36            buf: Vec::new(),
37            key: Some(key),
38        }
39    }
40
41    pub fn update(&mut self, data: &[u8]) {
42        self.buf.extend_from_slice(data);
43    }
44
45    pub fn finalize(&self) -> Blake3Digest {
46        if let Some(k) = &self.key {
47            blake3_keyed_hash(k, &self.buf)
48        } else {
49            blake3_hash(&self.buf)
50        }
51    }
52
53    pub fn reset(&mut self) {
54        self.buf.clear();
55    }
56}
57
58/// Compute a BLAKE3 stub digest.
59pub fn blake3_hash(data: &[u8]) -> Blake3Digest {
60    /* stub: mixing-based digest — not cryptographic */
61    let mut state = [
62        0x6b08e647u32,
63        0xbb67ae05,
64        0x3c6ef3a2,
65        0xa5cf053a,
66        0x514e527f,
67        0x9b0568fc,
68        0x1f09d9ab,
69        0x5be0cd19,
70    ];
71    for (i, &b) in data.iter().enumerate() {
72        let idx = i % 8;
73        state[idx] = state[idx]
74            .wrapping_mul(0x27220a95u32)
75            .wrapping_add(b as u32)
76            .rotate_left(5);
77    }
78    let mut digest = [0u8; 32];
79    for (i, &s) in state.iter().enumerate() {
80        digest[i * 4..(i + 1) * 4].copy_from_slice(&s.to_le_bytes());
81    }
82    Blake3Digest(digest)
83}
84
85/// Compute a keyed BLAKE3 stub digest.
86pub fn blake3_keyed_hash(key: &[u8; 32], data: &[u8]) -> Blake3Digest {
87    /* stub: mix key into initial state */
88    let mut combined = Vec::with_capacity(32 + data.len());
89    combined.extend_from_slice(key);
90    combined.extend_from_slice(data);
91    blake3_hash(&combined)
92}
93
94/// Return output length of BLAKE3 in bytes (always 32 for default output).
95pub fn blake3_output_len() -> usize {
96    32
97}
98
99/// Verify round-trip: hash twice and check equality.
100pub fn blake3_stable(data: &[u8]) -> bool {
101    blake3_hash(data) == blake3_hash(data)
102}
103
104#[cfg(test)]
105mod tests {
106    use super::*;
107
108    #[test]
109    fn test_hash_len() {
110        /* digest is 32 bytes */
111        let d = blake3_hash(b"test");
112        assert_eq!(d.0.len(), 32);
113    }
114
115    #[test]
116    fn test_hash_empty() {
117        /* empty input returns 32-byte digest */
118        let d = blake3_hash(&[]);
119        assert_eq!(d.0.len(), 32);
120    }
121
122    #[test]
123    fn test_hash_deterministic() {
124        /* same input gives same digest */
125        assert_eq!(blake3_hash(b"abc"), blake3_hash(b"abc"));
126    }
127
128    #[test]
129    fn test_hash_distinct_inputs() {
130        /* different input gives different digest */
131        assert_ne!(blake3_hash(b"x"), blake3_hash(b"y"));
132    }
133
134    #[test]
135    fn test_hex_length() {
136        /* hex string is 64 chars */
137        assert_eq!(blake3_hash(b"hi").to_hex().len(), 64);
138    }
139
140    #[test]
141    fn test_keyed_differs_from_plain() {
142        /* keyed hash differs from plain hash */
143        let key = [0xFFu8; 32];
144        assert_ne!(blake3_hash(b"msg"), blake3_keyed_hash(&key, b"msg"));
145    }
146
147    #[test]
148    fn test_hasher_incremental() {
149        /* incremental hasher matches direct hash */
150        let mut h = Blake3Hasher::new();
151        h.update(b"hello");
152        assert_eq!(h.finalize(), blake3_hash(b"hello"));
153    }
154
155    #[test]
156    fn test_hasher_reset() {
157        /* reset clears state */
158        let mut h = Blake3Hasher::new();
159        h.update(b"something");
160        h.reset();
161        assert_eq!(h.finalize(), blake3_hash(&[]));
162    }
163
164    #[test]
165    fn test_output_len() {
166        /* output len constant */
167        assert_eq!(blake3_output_len(), 32);
168    }
169
170    #[test]
171    fn test_stable() {
172        /* hash is deterministic */
173        assert!(blake3_stable(b"stability check"));
174    }
175}