⚠️ VeridianOS Kernel Documentation - This is low-level kernel code. All functions are unsafe unless explicitly marked otherwise. no_std

veridian_kernel/crypto/
hash.rs

1//! Cryptographic Hash Functions
2//!
3//! Implements SHA-256, SHA-512, and BLAKE3 hash algorithms.
4//! Full implementations following FIPS 180-4 and BLAKE3 specification.
5
6#![allow(dead_code, clippy::wrong_self_convention)]
7
8use alloc::vec::Vec;
9
10use super::CryptoResult;
11
12/// Hash algorithm types
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
14pub(crate) enum HashAlgorithm {
15    Sha256,
16    Sha512,
17    Blake3,
18}
19
20/// 256-bit hash output
21#[derive(Debug, Clone, Copy, PartialEq, Eq)]
22pub(crate) struct Hash256(pub [u8; 32]);
23
24/// 512-bit hash output
25#[derive(Debug, Clone, PartialEq, Eq)]
26pub(crate) struct Hash512(pub [u8; 64]);
27
28impl Hash256 {
29    /// Create hash from bytes
30    pub(crate) fn from_bytes(bytes: &[u8; 32]) -> Self {
31        Self(*bytes)
32    }
33
34    /// Get hash as bytes
35    pub(crate) fn as_bytes(&self) -> &[u8; 32] {
36        &self.0
37    }
38
39    /// Convert to hex string
40    pub(crate) fn to_hex(&self) -> alloc::string::String {
41        use alloc::format;
42        let mut s = alloc::string::String::with_capacity(64);
43        for byte in self.0 {
44            s.push_str(&format!("{:02x}", byte));
45        }
46        s
47    }
48}
49
50impl Hash512 {
51    /// Create hash from bytes
52    pub(crate) fn from_bytes(bytes: &[u8; 64]) -> Self {
53        Self(*bytes)
54    }
55
56    /// Get hash as bytes
57    pub(crate) fn as_bytes(&self) -> &[u8; 64] {
58        &self.0
59    }
60
61    /// Convert to hex string
62    pub(crate) fn to_hex(&self) -> alloc::string::String {
63        use alloc::format;
64        let mut s = alloc::string::String::with_capacity(128);
65        for byte in self.0 {
66            s.push_str(&format!("{:02x}", byte));
67        }
68        s
69    }
70}
71
72/// Hash data with specified algorithm
73pub(crate) fn hash(algorithm: HashAlgorithm, data: &[u8]) -> CryptoResult<Vec<u8>> {
74    match algorithm {
75        HashAlgorithm::Sha256 => {
76            let hash = sha256(data);
77            Ok(hash.0.to_vec())
78        }
79        HashAlgorithm::Sha512 => {
80            let hash = sha512(data);
81            Ok(hash.0.to_vec())
82        }
83        HashAlgorithm::Blake3 => {
84            let hash = blake3(data);
85            Ok(hash.0.to_vec())
86        }
87    }
88}
89
90// SHA-256 Constants (first 32 bits of the fractional parts of the cube roots of
91// the first 64 primes)
92const SHA256_K: [u32; 64] = [
93    0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
94    0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
95    0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
96    0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
97    0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
98    0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
99    0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
100    0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
101];
102
103// SHA-256 Initial hash values (first 32 bits of the fractional parts of the
104// square roots of the first 8 primes)
105const SHA256_H0: [u32; 8] = [
106    0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19,
107];
108
109/// Process a single SHA-256 block (64 bytes)
110fn sha256_process_block(h: &mut [u32; 8], block: &[u8]) {
111    let mut w = [0u32; 64];
112
113    // Copy block into first 16 words of message schedule
114    for (i, word_bytes) in block.chunks(4).enumerate().take(16) {
115        w[i] = u32::from_be_bytes([word_bytes[0], word_bytes[1], word_bytes[2], word_bytes[3]]);
116    }
117
118    // Extend the first 16 words into the remaining 48 words
119    for i in 16..64 {
120        let s0 = w[i - 15].rotate_right(7) ^ w[i - 15].rotate_right(18) ^ (w[i - 15] >> 3);
121        let s1 = w[i - 2].rotate_right(17) ^ w[i - 2].rotate_right(19) ^ (w[i - 2] >> 10);
122        w[i] = w[i - 16]
123            .wrapping_add(s0)
124            .wrapping_add(w[i - 7])
125            .wrapping_add(s1);
126    }
127
128    // Initialize working variables
129    let (mut a, mut b, mut c, mut d, mut e, mut f, mut g, mut hh) =
130        (h[0], h[1], h[2], h[3], h[4], h[5], h[6], h[7]);
131
132    // Compression function main loop
133    for i in 0..64 {
134        let s1 = e.rotate_right(6) ^ e.rotate_right(11) ^ e.rotate_right(25);
135        let ch = (e & f) ^ ((!e) & g);
136        let temp1 = hh
137            .wrapping_add(s1)
138            .wrapping_add(ch)
139            .wrapping_add(SHA256_K[i])
140            .wrapping_add(w[i]);
141        let s0 = a.rotate_right(2) ^ a.rotate_right(13) ^ a.rotate_right(22);
142        let maj = (a & b) ^ (a & c) ^ (b & c);
143        let temp2 = s0.wrapping_add(maj);
144
145        hh = g;
146        g = f;
147        f = e;
148        e = d.wrapping_add(temp1);
149        d = c;
150        c = b;
151        b = a;
152        a = temp1.wrapping_add(temp2);
153    }
154
155    // Add compressed chunk to current hash value
156    h[0] = h[0].wrapping_add(a);
157    h[1] = h[1].wrapping_add(b);
158    h[2] = h[2].wrapping_add(c);
159    h[3] = h[3].wrapping_add(d);
160    h[4] = h[4].wrapping_add(e);
161    h[5] = h[5].wrapping_add(f);
162    h[6] = h[6].wrapping_add(g);
163    h[7] = h[7].wrapping_add(hh);
164}
165
166/// SHA-256 hash - Zero-allocation streaming implementation (FIPS 180-4)
167pub(crate) fn sha256(data: &[u8]) -> Hash256 {
168    let mut h = SHA256_H0;
169    let original_len_bits = (data.len() as u64) * 8;
170
171    // Process all complete 64-byte blocks directly from input (no copy)
172    let full_blocks = data.len() / 64;
173    for i in 0..full_blocks {
174        sha256_process_block(&mut h, &data[i * 64..(i + 1) * 64]);
175    }
176
177    // Handle the final partial block + padding on the stack (max 128 bytes)
178    let remainder = data.len() % 64;
179    let mut final_buf = [0u8; 128]; // At most 2 blocks needed for padding
180    final_buf[..remainder].copy_from_slice(&data[full_blocks * 64..]);
181
182    // Append bit '1' (0x80)
183    final_buf[remainder] = 0x80;
184
185    let padded_len = remainder + 1;
186
187    if padded_len <= 56 {
188        // Padding + length fit in one block
189        final_buf[56..64].copy_from_slice(&original_len_bits.to_be_bytes());
190        sha256_process_block(&mut h, &final_buf[..64]);
191    } else {
192        // Need two blocks: first block with padding, second with length
193        sha256_process_block(&mut h, &final_buf[..64]);
194        // Second block: zeros + length
195        final_buf[64..120].fill(0);
196        final_buf[120..128].copy_from_slice(&original_len_bits.to_be_bytes());
197        sha256_process_block(&mut h, &final_buf[64..128]);
198    }
199
200    // Produce the final hash value (big-endian)
201    let mut result = [0u8; 32];
202    for (i, &val) in h.iter().enumerate() {
203        result[i * 4..(i + 1) * 4].copy_from_slice(&val.to_be_bytes());
204    }
205
206    Hash256(result)
207}
208
209// SHA-512 Constants (first 64 bits of the fractional parts of the cube roots of
210// the first 80 primes)
211const SHA512_K: [u64; 80] = [
212    0x428a2f98d728ae22,
213    0x7137449123ef65cd,
214    0xb5c0fbcfec4d3b2f,
215    0xe9b5dba58189dbbc,
216    0x3956c25bf348b538,
217    0x59f111f1b605d019,
218    0x923f82a4af194f9b,
219    0xab1c5ed5da6d8118,
220    0xd807aa98a3030242,
221    0x12835b0145706fbe,
222    0x243185be4ee4b28c,
223    0x550c7dc3d5ffb4e2,
224    0x72be5d74f27b896f,
225    0x80deb1fe3b1696b1,
226    0x9bdc06a725c71235,
227    0xc19bf174cf692694,
228    0xe49b69c19ef14ad2,
229    0xefbe4786384f25e3,
230    0x0fc19dc68b8cd5b5,
231    0x240ca1cc77ac9c65,
232    0x2de92c6f592b0275,
233    0x4a7484aa6ea6e483,
234    0x5cb0a9dcbd41fbd4,
235    0x76f988da831153b5,
236    0x983e5152ee66dfab,
237    0xa831c66d2db43210,
238    0xb00327c898fb213f,
239    0xbf597fc7beef0ee4,
240    0xc6e00bf33da88fc2,
241    0xd5a79147930aa725,
242    0x06ca6351e003826f,
243    0x142929670a0e6e70,
244    0x27b70a8546d22ffc,
245    0x2e1b21385c26c926,
246    0x4d2c6dfc5ac42aed,
247    0x53380d139d95b3df,
248    0x650a73548baf63de,
249    0x766a0abb3c77b2a8,
250    0x81c2c92e47edaee6,
251    0x92722c851482353b,
252    0xa2bfe8a14cf10364,
253    0xa81a664bbc423001,
254    0xc24b8b70d0f89791,
255    0xc76c51a30654be30,
256    0xd192e819d6ef5218,
257    0xd69906245565a910,
258    0xf40e35855771202a,
259    0x106aa07032bbd1b8,
260    0x19a4c116b8d2d0c8,
261    0x1e376c085141ab53,
262    0x2748774cdf8eeb99,
263    0x34b0bcb5e19b48a8,
264    0x391c0cb3c5c95a63,
265    0x4ed8aa4ae3418acb,
266    0x5b9cca4f7763e373,
267    0x682e6ff3d6b2b8a3,
268    0x748f82ee5defb2fc,
269    0x78a5636f43172f60,
270    0x84c87814a1f0ab72,
271    0x8cc702081a6439ec,
272    0x90befffa23631e28,
273    0xa4506cebde82bde9,
274    0xbef9a3f7b2c67915,
275    0xc67178f2e372532b,
276    0xca273eceea26619c,
277    0xd186b8c721c0c207,
278    0xeada7dd6cde0eb1e,
279    0xf57d4f7fee6ed178,
280    0x06f067aa72176fba,
281    0x0a637dc5a2c898a6,
282    0x113f9804bef90dae,
283    0x1b710b35131c471b,
284    0x28db77f523047d84,
285    0x32caab7b40c72493,
286    0x3c9ebe0a15c9bebc,
287    0x431d67c49c100d4c,
288    0x4cc5d4becb3e42b6,
289    0x597f299cfc657e2a,
290    0x5fcb6fab3ad6faec,
291    0x6c44198c4a475817,
292];
293
294// SHA-512 Initial hash values
295const SHA512_H0: [u64; 8] = [
296    0x6a09e667f3bcc908,
297    0xbb67ae8584caa73b,
298    0x3c6ef372fe94f82b,
299    0xa54ff53a5f1d36f1,
300    0x510e527fade682d1,
301    0x9b05688c2b3e6c1f,
302    0x1f83d9abfb41bd6b,
303    0x5be0cd19137e2179,
304];
305
306/// Process a single SHA-512 block (128 bytes)
307fn sha512_process_block(h: &mut [u64; 8], block: &[u8]) {
308    let mut w = [0u64; 80];
309
310    // Copy block into first 16 words of message schedule
311    for (i, word_bytes) in block.chunks(8).enumerate().take(16) {
312        w[i] = u64::from_be_bytes([
313            word_bytes[0],
314            word_bytes[1],
315            word_bytes[2],
316            word_bytes[3],
317            word_bytes[4],
318            word_bytes[5],
319            word_bytes[6],
320            word_bytes[7],
321        ]);
322    }
323
324    // Extend the first 16 words into the remaining 64 words
325    for i in 16..80 {
326        let s0 = w[i - 15].rotate_right(1) ^ w[i - 15].rotate_right(8) ^ (w[i - 15] >> 7);
327        let s1 = w[i - 2].rotate_right(19) ^ w[i - 2].rotate_right(61) ^ (w[i - 2] >> 6);
328        w[i] = w[i - 16]
329            .wrapping_add(s0)
330            .wrapping_add(w[i - 7])
331            .wrapping_add(s1);
332    }
333
334    // Initialize working variables
335    let (mut a, mut b, mut c, mut d, mut e, mut f, mut g, mut hh) =
336        (h[0], h[1], h[2], h[3], h[4], h[5], h[6], h[7]);
337
338    // Compression function main loop
339    for i in 0..80 {
340        let s1 = e.rotate_right(14) ^ e.rotate_right(18) ^ e.rotate_right(41);
341        let ch = (e & f) ^ ((!e) & g);
342        let temp1 = hh
343            .wrapping_add(s1)
344            .wrapping_add(ch)
345            .wrapping_add(SHA512_K[i])
346            .wrapping_add(w[i]);
347        let s0 = a.rotate_right(28) ^ a.rotate_right(34) ^ a.rotate_right(39);
348        let maj = (a & b) ^ (a & c) ^ (b & c);
349        let temp2 = s0.wrapping_add(maj);
350
351        hh = g;
352        g = f;
353        f = e;
354        e = d.wrapping_add(temp1);
355        d = c;
356        c = b;
357        b = a;
358        a = temp1.wrapping_add(temp2);
359    }
360
361    // Add compressed chunk to current hash value
362    h[0] = h[0].wrapping_add(a);
363    h[1] = h[1].wrapping_add(b);
364    h[2] = h[2].wrapping_add(c);
365    h[3] = h[3].wrapping_add(d);
366    h[4] = h[4].wrapping_add(e);
367    h[5] = h[5].wrapping_add(f);
368    h[6] = h[6].wrapping_add(g);
369    h[7] = h[7].wrapping_add(hh);
370}
371
372/// SHA-512 hash - Zero-allocation streaming implementation (FIPS 180-4)
373pub(crate) fn sha512(data: &[u8]) -> Hash512 {
374    let mut h = SHA512_H0;
375    let original_len_bits = (data.len() as u128) * 8;
376
377    // Process all complete 128-byte blocks directly from input (no copy)
378    let full_blocks = data.len() / 128;
379    for i in 0..full_blocks {
380        sha512_process_block(&mut h, &data[i * 128..(i + 1) * 128]);
381    }
382
383    // Handle the final partial block + padding on the stack (max 256 bytes)
384    let remainder = data.len() % 128;
385    let mut final_buf = [0u8; 256]; // At most 2 blocks needed for padding
386    final_buf[..remainder].copy_from_slice(&data[full_blocks * 128..]);
387
388    // Append bit '1' (0x80)
389    final_buf[remainder] = 0x80;
390
391    let padded_len = remainder + 1;
392
393    if padded_len <= 112 {
394        // Padding + length fit in one block
395        final_buf[112..128].copy_from_slice(&original_len_bits.to_be_bytes());
396        sha512_process_block(&mut h, &final_buf[..128]);
397    } else {
398        // Need two blocks
399        sha512_process_block(&mut h, &final_buf[..128]);
400        final_buf[128..240].fill(0);
401        final_buf[240..256].copy_from_slice(&original_len_bits.to_be_bytes());
402        sha512_process_block(&mut h, &final_buf[128..256]);
403    }
404
405    // Produce the final hash value (big-endian)
406    let mut result = [0u8; 64];
407    for (i, &val) in h.iter().enumerate() {
408        result[i * 8..(i + 1) * 8].copy_from_slice(&val.to_be_bytes());
409    }
410
411    Hash512(result)
412}
413
414// BLAKE3 constants
415const BLAKE3_IV: [u32; 8] = [
416    0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A, 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19,
417];
418
419const BLAKE3_MSG_PERMUTATION: [usize; 16] = [2, 6, 3, 10, 7, 0, 4, 13, 1, 11, 12, 5, 9, 14, 15, 8];
420
421// BLAKE3 flags
422const BLAKE3_CHUNK_START: u32 = 1;
423const BLAKE3_CHUNK_END: u32 = 2;
424const BLAKE3_ROOT: u32 = 8;
425
426/// BLAKE3 quarter round function
427#[inline]
428fn blake3_g(state: &mut [u32; 16], a: usize, b: usize, c: usize, d: usize, mx: u32, my: u32) {
429    state[a] = state[a].wrapping_add(state[b]).wrapping_add(mx);
430    state[d] = (state[d] ^ state[a]).rotate_right(16);
431    state[c] = state[c].wrapping_add(state[d]);
432    state[b] = (state[b] ^ state[c]).rotate_right(12);
433    state[a] = state[a].wrapping_add(state[b]).wrapping_add(my);
434    state[d] = (state[d] ^ state[a]).rotate_right(8);
435    state[c] = state[c].wrapping_add(state[d]);
436    state[b] = (state[b] ^ state[c]).rotate_right(7);
437}
438
439/// BLAKE3 round function
440fn blake3_round(state: &mut [u32; 16], m: &[u32; 16]) {
441    // Column step
442    blake3_g(state, 0, 4, 8, 12, m[0], m[1]);
443    blake3_g(state, 1, 5, 9, 13, m[2], m[3]);
444    blake3_g(state, 2, 6, 10, 14, m[4], m[5]);
445    blake3_g(state, 3, 7, 11, 15, m[6], m[7]);
446    // Diagonal step
447    blake3_g(state, 0, 5, 10, 15, m[8], m[9]);
448    blake3_g(state, 1, 6, 11, 12, m[10], m[11]);
449    blake3_g(state, 2, 7, 8, 13, m[12], m[13]);
450    blake3_g(state, 3, 4, 9, 14, m[14], m[15]);
451}
452
453/// BLAKE3 permute message words
454fn blake3_permute(m: &mut [u32; 16]) {
455    let original = *m;
456    for i in 0..16 {
457        m[i] = original[BLAKE3_MSG_PERMUTATION[i]];
458    }
459}
460
461/// BLAKE3 compress function
462fn blake3_compress(
463    chaining_value: &[u32; 8],
464    block_words: &[u32; 16],
465    counter: u64,
466    block_len: u32,
467    flags: u32,
468) -> [u32; 16] {
469    let mut state = [
470        chaining_value[0],
471        chaining_value[1],
472        chaining_value[2],
473        chaining_value[3],
474        chaining_value[4],
475        chaining_value[5],
476        chaining_value[6],
477        chaining_value[7],
478        BLAKE3_IV[0],
479        BLAKE3_IV[1],
480        BLAKE3_IV[2],
481        BLAKE3_IV[3],
482        counter as u32,
483        (counter >> 32) as u32,
484        block_len,
485        flags,
486    ];
487
488    let mut m = *block_words;
489
490    // 7 rounds
491    for _ in 0..7 {
492        blake3_round(&mut state, &m);
493        blake3_permute(&mut m);
494    }
495
496    // XOR the two halves
497    for i in 0..8 {
498        state[i] ^= state[i + 8];
499        state[i + 8] ^= chaining_value[i];
500    }
501
502    state
503}
504
505/// BLAKE3 hash - Full implementation following BLAKE3 specification
506pub(crate) fn blake3(data: &[u8]) -> Hash256 {
507    // For simplicity, this implementation handles data up to 64 bytes (single
508    // chunk) For longer data, a full tree structure would be needed
509
510    let mut block_words = [0u32; 16];
511    let block_len = core::cmp::min(data.len(), 64) as u32;
512
513    // Convert input to little-endian words
514    for (i, chunk) in data.chunks(4).take(16).enumerate() {
515        let mut word_bytes = [0u8; 4];
516        let copy_len = core::cmp::min(chunk.len(), 4);
517        word_bytes[..copy_len].copy_from_slice(&chunk[..copy_len]);
518        block_words[i] = u32::from_le_bytes(word_bytes);
519    }
520
521    let flags = BLAKE3_CHUNK_START | BLAKE3_CHUNK_END | BLAKE3_ROOT;
522    let output = blake3_compress(&BLAKE3_IV, &block_words, 0, block_len, flags);
523
524    // Convert first 8 words to output hash
525    let mut result = [0u8; 32];
526    for i in 0..8 {
527        result[i * 4..(i + 1) * 4].copy_from_slice(&output[i].to_le_bytes());
528    }
529
530    Hash256(result)
531}
532
533// ============================================================================
534// BLAKE2s (RFC 7693)
535// ============================================================================
536
537/// BLAKE2s initialization vector (same as SHA-256 fractional parts of
538/// sqrt(primes))
539const BLAKE2S_IV: [u32; 8] = [
540    0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A, 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19,
541];
542
543/// BLAKE2s sigma permutation schedule (10 rounds)
544const BLAKE2S_SIGMA: [[usize; 16]; 10] = [
545    [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
546    [14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3],
547    [11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4],
548    [7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8],
549    [9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13],
550    [2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9],
551    [12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11],
552    [13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10],
553    [6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5],
554    [10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0],
555];
556
557/// BLAKE2s hash state (RFC 7693)
558#[derive(Clone)]
559pub(crate) struct Blake2s {
560    h: [u32; 8],
561    t: [u32; 2],
562    buf: [u8; 64],
563    buf_len: usize,
564    #[allow(dead_code)]
565    outlen: usize,
566}
567
568impl Blake2s {
569    /// Create new BLAKE2s hasher with specified output length (1-32 bytes)
570    pub(crate) fn new(outlen: usize) -> Self {
571        assert!((1..=32).contains(&outlen));
572        let mut h = BLAKE2S_IV;
573        // Parameter block: fan-out=1, depth=1, digest_length=outlen
574        h[0] ^= 0x01010000 ^ (outlen as u32);
575        Self {
576            h,
577            t: [0, 0],
578            buf: [0u8; 64],
579            buf_len: 0,
580            outlen,
581        }
582    }
583
584    /// Create new keyed BLAKE2s hasher
585    pub(crate) fn new_keyed(key: &[u8], outlen: usize) -> Self {
586        assert!(!key.is_empty() && key.len() <= 32);
587        assert!((1..=32).contains(&outlen));
588        let mut h = BLAKE2S_IV;
589        // Parameter block: fan-out=1, depth=1, digest_length=outlen,
590        // key_length=key.len()
591        h[0] ^= 0x01010000 ^ ((key.len() as u32) << 8) ^ (outlen as u32);
592        let mut state = Self {
593            h,
594            t: [0, 0],
595            buf: [0u8; 64],
596            buf_len: 0,
597            outlen,
598        };
599        // Pad key to 64 bytes and process as first block
600        let mut padded_key = [0u8; 64];
601        padded_key[..key.len()].copy_from_slice(key);
602        state.update(&padded_key);
603        state
604    }
605
606    /// Update state with input data
607    pub(crate) fn update(&mut self, data: &[u8]) {
608        let mut offset = 0;
609        let len = data.len();
610
611        // Fill buffer
612        if self.buf_len > 0 && self.buf_len + len > 64 {
613            let fill = 64 - self.buf_len;
614            self.buf[self.buf_len..64].copy_from_slice(&data[..fill]);
615            self.increment_counter(64);
616            self.compress(false);
617            self.buf_len = 0;
618            offset = fill;
619        }
620
621        // Process full blocks (keeping at least 1 byte for finalization)
622        while offset + 64 < len {
623            self.buf.copy_from_slice(&data[offset..offset + 64]);
624            self.increment_counter(64);
625            self.compress(false);
626            offset += 64;
627        }
628
629        // Buffer remaining
630        let remaining = len - offset;
631        if remaining > 0 {
632            self.buf[self.buf_len..self.buf_len + remaining].copy_from_slice(&data[offset..]);
633            self.buf_len += remaining;
634        }
635    }
636
637    /// Finalize and return the hash digest
638    pub(crate) fn finalize(mut self) -> [u8; 32] {
639        self.increment_counter(self.buf_len as u32);
640        // Zero-pad remaining buffer
641        let buf_len = self.buf_len;
642        for byte in &mut self.buf[buf_len..] {
643            *byte = 0;
644        }
645        self.compress(true);
646
647        let mut out = [0u8; 32];
648        for i in 0..8 {
649            let bytes = self.h[i].to_le_bytes();
650            out[i * 4..i * 4 + 4].copy_from_slice(&bytes);
651        }
652        out
653    }
654
655    /// BLAKE2s compression function: 10 rounds of G mixing
656    fn compress(&mut self, last: bool) {
657        let mut v = [0u32; 16];
658        v[..8].copy_from_slice(&self.h);
659        v[8..16].copy_from_slice(&BLAKE2S_IV);
660
661        v[12] ^= self.t[0];
662        v[13] ^= self.t[1];
663        if last {
664            v[14] = !v[14];
665        }
666
667        // Decode message block into 16 words
668        let mut m = [0u32; 16];
669        for (i, word) in m.iter_mut().enumerate() {
670            *word = u32::from_le_bytes([
671                self.buf[i * 4],
672                self.buf[i * 4 + 1],
673                self.buf[i * 4 + 2],
674                self.buf[i * 4 + 3],
675            ]);
676        }
677
678        // 10 rounds of G mixing
679        for s in &BLAKE2S_SIGMA[..10] {
680            // Column step
681            Self::g(&mut v, 0, 4, 8, 12, m[s[0]], m[s[1]]);
682            Self::g(&mut v, 1, 5, 9, 13, m[s[2]], m[s[3]]);
683            Self::g(&mut v, 2, 6, 10, 14, m[s[4]], m[s[5]]);
684            Self::g(&mut v, 3, 7, 11, 15, m[s[6]], m[s[7]]);
685            // Diagonal step
686            Self::g(&mut v, 0, 5, 10, 15, m[s[8]], m[s[9]]);
687            Self::g(&mut v, 1, 6, 11, 12, m[s[10]], m[s[11]]);
688            Self::g(&mut v, 2, 7, 8, 13, m[s[12]], m[s[13]]);
689            Self::g(&mut v, 3, 4, 9, 14, m[s[14]], m[s[15]]);
690        }
691
692        // Finalize
693        for i in 0..8 {
694            self.h[i] ^= v[i] ^ v[i + 8];
695        }
696    }
697
698    /// BLAKE2s G mixing function
699    #[inline]
700    fn g(v: &mut [u32; 16], a: usize, b: usize, c: usize, d: usize, x: u32, y: u32) {
701        v[a] = v[a].wrapping_add(v[b]).wrapping_add(x);
702        v[d] = (v[d] ^ v[a]).rotate_right(16);
703        v[c] = v[c].wrapping_add(v[d]);
704        v[b] = (v[b] ^ v[c]).rotate_right(12);
705        v[a] = v[a].wrapping_add(v[b]).wrapping_add(y);
706        v[d] = (v[d] ^ v[a]).rotate_right(8);
707        v[c] = v[c].wrapping_add(v[d]);
708        v[b] = (v[b] ^ v[c]).rotate_right(7);
709    }
710
711    fn increment_counter(&mut self, inc: u32) {
712        self.t[0] = self.t[0].wrapping_add(inc);
713        if self.t[0] < inc {
714            self.t[1] = self.t[1].wrapping_add(1);
715        }
716    }
717}
718
719/// Compute BLAKE2s hash of data with given output length
720pub(crate) fn blake2s_hash(data: &[u8], outlen: usize) -> [u8; 32] {
721    let mut hasher = Blake2s::new(outlen);
722    hasher.update(data);
723    hasher.finalize()
724}
725
726/// Compute keyed BLAKE2s hash
727pub(crate) fn blake2s_keyed_hash(key: &[u8], data: &[u8], outlen: usize) -> [u8; 32] {
728    let mut hasher = Blake2s::new_keyed(key, outlen);
729    hasher.update(data);
730    hasher.finalize()
731}
732
733/// Verify hash matches data
734pub(crate) fn verify_hash(
735    algorithm: HashAlgorithm,
736    data: &[u8],
737    expected: &[u8],
738) -> CryptoResult<bool> {
739    let computed = hash(algorithm, data)?;
740    Ok(computed == expected)
741}
742
743#[cfg(test)]
744mod tests {
745    use super::*;
746
747    #[test]
748    fn test_sha256() {
749        let data = b"Hello, VeridianOS!";
750        let hash = sha256(data);
751        assert_eq!(hash.as_bytes().len(), 32);
752    }
753
754    #[test]
755    fn test_hash_hex() {
756        let mut bytes = [0u8; 32];
757        bytes[0] = 0x12;
758        bytes[1] = 0x34;
759        bytes[2] = 0x56;
760        bytes[3] = 0x78;
761        let hash = Hash256(bytes);
762        let hex = hash.to_hex();
763        assert!(hex.starts_with("12345678"));
764    }
765}