Skip to main content

arcana/hash/
sha3.rs

1//! SHA-3 and SHAKE implementations (FIPS 202).
2//!
3//! Wraps the Keccak-f\[1600\] sponge construction to provide:
4//! - SHA3-256, SHA3-384, SHA3-512 (fixed-output, implementing `Hasher`)
5//! - SHAKE128, SHAKE256 (extendable-output, implementing `Xof`)
6
7use crate::Hasher;
8use crate::Xof;
9
10// ============================================================
11// Keccak permutation (copied from ml_kem/src/sha3.rs)
12// ============================================================
13
14const KECCAK_ROUNDS: usize = 24;
15
16const RC: [u64; 24] = [
17    0x0000000000000001,
18    0x0000000000008082,
19    0x800000000000808A,
20    0x8000000080008000,
21    0x000000000000808B,
22    0x0000000080000001,
23    0x8000000080008081,
24    0x8000000000008009,
25    0x000000000000008A,
26    0x0000000000000088,
27    0x0000000080008009,
28    0x000000008000000A,
29    0x000000008000808B,
30    0x800000000000008B,
31    0x8000000000008089,
32    0x8000000000008003,
33    0x8000000000008002,
34    0x8000000000000080,
35    0x000000000000800A,
36    0x800000008000000A,
37    0x8000000080008081,
38    0x8000000000008080,
39    0x0000000080000001,
40    0x8000000080008008,
41];
42
43const ROTC: [u32; 24] = [
44    1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 2, 14, 27, 41, 56, 8, 25, 43, 62, 18, 39, 61, 20, 44,
45];
46
47const PI: [usize; 24] = [
48    10, 7, 11, 17, 18, 3, 5, 16, 8, 21, 24, 4, 15, 23, 19, 13, 12, 2, 20, 14, 22, 9, 6, 1,
49];
50
51#[inline(always)]
52fn keccak_f(state: &mut [u64; 25]) {
53    for round in 0..KECCAK_ROUNDS {
54        // theta
55        let mut c = [0u64; 5];
56        for x in 0..5 {
57            c[x] = state[x] ^ state[x + 5] ^ state[x + 10] ^ state[x + 15] ^ state[x + 20];
58        }
59        let mut d = [0u64; 5];
60        for x in 0..5 {
61            d[x] = c[(x + 4) % 5] ^ c[(x + 1) % 5].rotate_left(1);
62        }
63        for i in 0..25 {
64            state[i] ^= d[i % 5];
65        }
66
67        // rho and pi
68        let mut last = state[1];
69        for i in 0..24 {
70            let j = PI[i];
71            let temp = state[j];
72            state[j] = last.rotate_left(ROTC[i]);
73            last = temp;
74        }
75
76        // chi
77        for y in (0..25).step_by(5) {
78            let t0 = state[y];
79            let t1 = state[y + 1];
80            let t2 = state[y + 2];
81            let t3 = state[y + 3];
82            let t4 = state[y + 4];
83            state[y] = t0 ^ (!t1 & t2);
84            state[y + 1] = t1 ^ (!t2 & t3);
85            state[y + 2] = t2 ^ (!t3 & t4);
86            state[y + 3] = t3 ^ (!t4 & t0);
87            state[y + 4] = t4 ^ (!t0 & t1);
88        }
89
90        // iota
91        state[0] ^= RC[round];
92    }
93}
94
95#[inline]
96fn state_as_bytes(state: &[u64; 25]) -> &[u8; 200] {
97    unsafe { &*(state.as_ptr() as *const [u8; 200]) }
98}
99
100#[inline]
101fn state_as_bytes_mut(state: &mut [u64; 25]) -> &mut [u8; 200] {
102    unsafe { &mut *(state.as_mut_ptr() as *mut [u8; 200]) }
103}
104
105// ============================================================
106// Keccak sponge state
107// ============================================================
108
109#[derive(Clone)]
110pub(crate) struct KeccakState {
111    state: [u64; 25],
112    offset: usize,
113    rate: usize,
114    suffix: u8,
115    squeezing: bool,
116}
117
118impl KeccakState {
119    pub(crate) fn new(rate: usize, suffix: u8) -> Self {
120        Self {
121            state: [0u64; 25],
122            offset: 0,
123            rate,
124            suffix,
125            squeezing: false,
126        }
127    }
128
129    pub(crate) fn absorb(&mut self, data: &[u8]) {
130        debug_assert!(!self.squeezing);
131        let mut pos = 0;
132        while pos < data.len() {
133            let block_remaining = self.rate - self.offset;
134            let to_copy = block_remaining.min(data.len() - pos);
135            let state_bytes = state_as_bytes_mut(&mut self.state);
136            for i in 0..to_copy {
137                state_bytes[self.offset + i] ^= data[pos + i];
138            }
139            self.offset += to_copy;
140            pos += to_copy;
141            if self.offset == self.rate {
142                keccak_f(&mut self.state);
143                self.offset = 0;
144            }
145        }
146    }
147
148    fn pad_and_squeeze(&mut self) {
149        if !self.squeezing {
150            let state_bytes = state_as_bytes_mut(&mut self.state);
151            state_bytes[self.offset] ^= self.suffix;
152            state_bytes[self.rate - 1] ^= 0x80;
153            keccak_f(&mut self.state);
154            self.offset = 0;
155            self.squeezing = true;
156        }
157    }
158
159    pub(crate) fn squeeze(&mut self, out: &mut [u8]) {
160        self.pad_and_squeeze();
161        let mut pos = 0;
162        while pos < out.len() {
163            if self.offset == self.rate {
164                keccak_f(&mut self.state);
165                self.offset = 0;
166            }
167            let available = self.rate - self.offset;
168            let to_copy = available.min(out.len() - pos);
169            let state_bytes = state_as_bytes(&self.state);
170            out[pos..pos + to_copy].copy_from_slice(&state_bytes[self.offset..self.offset + to_copy]);
171            self.offset += to_copy;
172            pos += to_copy;
173        }
174    }
175}
176
177// ============================================================
178// SHA3-224
179// ============================================================
180
181/// SHA3-224: rate = 1152 bits (144 bytes), capacity = 448 bits, output = 28 bytes.
182#[derive(Clone)]
183pub struct Sha3_224 {
184    state: KeccakState,
185}
186
187impl Hasher for Sha3_224 {
188    const OUTPUT_LEN: usize = 28;
189    const BLOCK_LEN: usize = 144;
190
191    fn new() -> Self {
192        Self {
193            state: KeccakState::new(144, 0x06),
194        }
195    }
196
197    fn update(&mut self, data: &[u8]) {
198        self.state.absorb(data);
199    }
200
201    fn finalize(self) -> Vec<u8> {
202        let mut out = vec![0u8; 28];
203        self.finalize_into(&mut out);
204        out
205    }
206
207    fn finalize_into(mut self, out: &mut [u8]) {
208        let len = out.len().min(28);
209        let mut buf = [0u8; 28];
210        self.state.squeeze(&mut buf);
211        out[..len].copy_from_slice(&buf[..len]);
212    }
213}
214
215// ============================================================
216// SHA3-256
217// ============================================================
218
219/// SHA3-256: rate = 1088 bits (136 bytes), capacity = 512 bits, output = 32 bytes.
220#[derive(Clone)]
221pub struct Sha3_256 {
222    state: KeccakState,
223}
224
225impl Hasher for Sha3_256 {
226    const OUTPUT_LEN: usize = 32;
227    const BLOCK_LEN: usize = 136;
228
229    fn new() -> Self {
230        Self {
231            state: KeccakState::new(136, 0x06),
232        }
233    }
234
235    fn update(&mut self, data: &[u8]) {
236        self.state.absorb(data);
237    }
238
239    fn finalize(self) -> Vec<u8> {
240        let mut out = vec![0u8; 32];
241        self.finalize_into(&mut out);
242        out
243    }
244
245    fn finalize_into(mut self, out: &mut [u8]) {
246        let len = out.len().min(32);
247        let mut buf = [0u8; 32];
248        self.state.squeeze(&mut buf);
249        out[..len].copy_from_slice(&buf[..len]);
250    }
251}
252
253// ============================================================
254// SHA3-384
255// ============================================================
256
257/// SHA3-384: rate = 832 bits (104 bytes), capacity = 768 bits, output = 48 bytes.
258#[derive(Clone)]
259pub struct Sha3_384 {
260    state: KeccakState,
261}
262
263impl Hasher for Sha3_384 {
264    const OUTPUT_LEN: usize = 48;
265    const BLOCK_LEN: usize = 104;
266
267    fn new() -> Self {
268        Self {
269            state: KeccakState::new(104, 0x06),
270        }
271    }
272
273    fn update(&mut self, data: &[u8]) {
274        self.state.absorb(data);
275    }
276
277    fn finalize(self) -> Vec<u8> {
278        let mut out = vec![0u8; 48];
279        self.finalize_into(&mut out);
280        out
281    }
282
283    fn finalize_into(mut self, out: &mut [u8]) {
284        let len = out.len().min(48);
285        let mut buf = [0u8; 48];
286        self.state.squeeze(&mut buf);
287        out[..len].copy_from_slice(&buf[..len]);
288    }
289}
290
291// ============================================================
292// SHA3-512
293// ============================================================
294
295/// SHA3-512: rate = 576 bits (72 bytes), capacity = 1024 bits, output = 64 bytes.
296#[derive(Clone)]
297pub struct Sha3_512 {
298    state: KeccakState,
299}
300
301impl Hasher for Sha3_512 {
302    const OUTPUT_LEN: usize = 64;
303    const BLOCK_LEN: usize = 72;
304
305    fn new() -> Self {
306        Self {
307            state: KeccakState::new(72, 0x06),
308        }
309    }
310
311    fn update(&mut self, data: &[u8]) {
312        self.state.absorb(data);
313    }
314
315    fn finalize(self) -> Vec<u8> {
316        let mut out = vec![0u8; 64];
317        self.finalize_into(&mut out);
318        out
319    }
320
321    fn finalize_into(mut self, out: &mut [u8]) {
322        let len = out.len().min(64);
323        let mut buf = [0u8; 64];
324        self.state.squeeze(&mut buf);
325        out[..len].copy_from_slice(&buf[..len]);
326    }
327}
328
329// ============================================================
330// SHAKE128
331// ============================================================
332
333/// SHAKE128: rate = 1344 bits (168 bytes), capacity = 256 bits, extendable output.
334#[derive(Clone)]
335pub struct Shake128 {
336    state: KeccakState,
337}
338
339impl Xof for Shake128 {
340    const BLOCK_LEN: usize = 168;
341
342    fn new() -> Self {
343        Self {
344            state: KeccakState::new(168, 0x1f),
345        }
346    }
347
348    fn update(&mut self, data: &[u8]) {
349        self.state.absorb(data);
350    }
351
352    fn squeeze(&mut self, out: &mut [u8]) {
353        self.state.squeeze(out);
354    }
355}
356
357// ============================================================
358// SHAKE256
359// ============================================================
360
361/// SHAKE256: rate = 1088 bits (136 bytes), capacity = 512 bits, extendable output.
362#[derive(Clone)]
363pub struct Shake256 {
364    state: KeccakState,
365}
366
367impl Xof for Shake256 {
368    const BLOCK_LEN: usize = 136;
369
370    fn new() -> Self {
371        Self {
372            state: KeccakState::new(136, 0x1f),
373        }
374    }
375
376    fn update(&mut self, data: &[u8]) {
377        self.state.absorb(data);
378    }
379
380    fn squeeze(&mut self, out: &mut [u8]) {
381        self.state.squeeze(out);
382    }
383}
384
385// ============================================================
386// cSHAKE128, cSHAKE256 (NIST SP 800-185)
387// ============================================================
388
389/// cSHAKE128: customizable SHAKE with 128-bit security (NIST SP 800-185).
390///
391/// If both `function_name` and `customization` are empty, cSHAKE128
392/// reduces to SHAKE128 (the domain-separation prefix is omitted).
393#[derive(Clone)]
394pub struct CShake128 {
395    state: KeccakState,
396}
397
398impl CShake128 {
399    /// Rate in bytes (same as SHAKE128).
400    pub const RATE: usize = 168;
401
402    /// Create a new cSHAKE128 instance with a function name and
403    /// customization string.
404    pub fn new(function_name: &[u8], customization: &[u8]) -> Self {
405        let mut state = KeccakState::new(
406            168,
407            if function_name.is_empty() && customization.is_empty() {
408                0x1F
409            } else {
410                0x04
411            },
412        );
413        if !function_name.is_empty() || !customization.is_empty() {
414            let prefix = cshake_prefix(168, function_name, customization);
415            state.absorb(&prefix);
416        }
417        Self { state }
418    }
419
420    /// Absorb input data.
421    pub fn update(&mut self, data: &[u8]) {
422        self.state.absorb(data);
423    }
424
425    /// Squeeze output bytes. Can be called multiple times.
426    pub fn squeeze(&mut self, out: &mut [u8]) {
427        self.state.squeeze(out);
428    }
429}
430
431/// cSHAKE256: customizable SHAKE with 256-bit security (NIST SP 800-185).
432///
433/// If both `function_name` and `customization` are empty, cSHAKE256
434/// reduces to SHAKE256 (the domain-separation prefix is omitted).
435#[derive(Clone)]
436pub struct CShake256 {
437    state: KeccakState,
438}
439
440impl CShake256 {
441    /// Rate in bytes (same as SHAKE256).
442    pub const RATE: usize = 136;
443
444    /// Create a new cSHAKE256 instance with a function name and
445    /// customization string.
446    pub fn new(function_name: &[u8], customization: &[u8]) -> Self {
447        let mut state = KeccakState::new(
448            136,
449            if function_name.is_empty() && customization.is_empty() {
450                0x1F
451            } else {
452                0x04
453            },
454        );
455        if !function_name.is_empty() || !customization.is_empty() {
456            let prefix = cshake_prefix(136, function_name, customization);
457            state.absorb(&prefix);
458        }
459        Self { state }
460    }
461
462    /// Absorb input data.
463    pub fn update(&mut self, data: &[u8]) {
464        self.state.absorb(data);
465    }
466
467    /// Squeeze output bytes. Can be called multiple times.
468    pub fn squeeze(&mut self, out: &mut [u8]) {
469        self.state.squeeze(out);
470    }
471}
472
473/// Build the cSHAKE domain-separation prefix:
474/// `bytepad(encode_string(N) || encode_string(S), rate)`.
475fn cshake_prefix(rate: usize, function_name: &[u8], customization: &[u8]) -> Vec<u8> {
476    let mut buf = Vec::new();
477    // left_encode(rate)
478    let le = left_encode(rate as u64);
479    buf.extend_from_slice(&le);
480    // encode_string(N) = left_encode(len_bits(N)) || N
481    append_encode_string(&mut buf, function_name);
482    // encode_string(S) = left_encode(len_bits(S)) || S
483    append_encode_string(&mut buf, customization);
484    // Pad to a multiple of rate.
485    let pad = (rate - (buf.len() % rate)) % rate;
486    buf.extend(core::iter::repeat(0u8).take(pad));
487    buf
488}
489
490fn left_encode(x: u64) -> Vec<u8> {
491    let mut bytes = Vec::new();
492    let be = x.to_be_bytes();
493    let first = be.iter().position(|&b| b != 0).unwrap_or(7);
494    let n = (8 - first) as u8;
495    bytes.push(n);
496    bytes.extend_from_slice(&be[first..]);
497    bytes
498}
499
500fn append_encode_string(out: &mut Vec<u8>, s: &[u8]) {
501    out.extend_from_slice(&left_encode((s.len() as u64) * 8));
502    out.extend_from_slice(s);
503}
504
505#[cfg(test)]
506mod tests {
507    use super::*;
508    use crate::Hasher;
509
510    #[test]
511    fn test_sha3_224_empty() {
512        let digest = Sha3_224::hash(b"");
513        let expected: [u8; 28] = [
514            0x6b, 0x4e, 0x03, 0x42, 0x36, 0x67, 0xdb, 0xb7, 0x3b, 0x6e, 0x15, 0x45, 0x4f, 0x0e, 0xb1, 0xab, 0xd4, 0x59,
515            0x7f, 0x9a, 0x1b, 0x07, 0x8e, 0x3f, 0x5b, 0x5a, 0x6b, 0xc7,
516        ];
517        assert_eq!(&digest[..], &expected[..]);
518    }
519
520    #[test]
521    fn test_sha3_224_abc() {
522        let digest = Sha3_224::hash(b"abc");
523        let expected: [u8; 28] = [
524            0xe6, 0x42, 0x82, 0x4c, 0x3f, 0x8c, 0xf2, 0x4a, 0xd0, 0x92, 0x34, 0xee, 0x7d, 0x3c, 0x76, 0x6f, 0xc9, 0xa3,
525            0xa5, 0x16, 0x8d, 0x0c, 0x94, 0xad, 0x73, 0xb4, 0x6f, 0xdf,
526        ];
527        assert_eq!(&digest[..], &expected[..]);
528    }
529
530    #[test]
531    fn test_sha3_256_empty() {
532        let digest = Sha3_256::hash(b"");
533        let expected: [u8; 32] = [
534            0xa7, 0xff, 0xc6, 0xf8, 0xbf, 0x1e, 0xd7, 0x66, 0x51, 0xc1, 0x47, 0x56, 0xa0, 0x61, 0xd6, 0x62, 0xf5, 0x80,
535            0xff, 0x4d, 0xe4, 0x3b, 0x49, 0xfa, 0x82, 0xd8, 0x0a, 0x4b, 0x80, 0xf8, 0x43, 0x4a,
536        ];
537        assert_eq!(&digest[..], &expected[..]);
538    }
539
540    #[test]
541    fn test_sha3_256_abc() {
542        let digest = Sha3_256::hash(b"abc");
543        let expected: [u8; 32] = [
544            0x3a, 0x98, 0x5d, 0xa7, 0x4f, 0xe2, 0x25, 0xb2, 0x04, 0x5c, 0x17, 0x2d, 0x6b, 0xd3, 0x90, 0xbd, 0x85, 0x5f,
545            0x08, 0x6e, 0x3e, 0x9d, 0x52, 0x5b, 0x46, 0xbf, 0xe2, 0x45, 0x11, 0x43, 0x15, 0x32,
546        ];
547        assert_eq!(&digest[..], &expected[..]);
548    }
549
550    #[test]
551    fn test_sha3_512_empty() {
552        let digest = Sha3_512::hash(b"");
553        let expected_first_8: [u8; 8] = [0xa6, 0x9f, 0x73, 0xcc, 0xa2, 0x3a, 0x9a, 0xc5];
554        assert_eq!(&digest[..8], &expected_first_8[..]);
555    }
556
557    #[test]
558    fn test_sha3_384_abc() {
559        let digest = Sha3_384::hash(b"abc");
560        let expected: [u8; 48] = [
561            0xec, 0x01, 0x49, 0x82, 0x88, 0x51, 0x6f, 0xc9, 0x26, 0x45, 0x9f, 0x58, 0xe2, 0xc6, 0xad, 0x8d, 0xf9, 0xb4,
562            0x73, 0xcb, 0x0f, 0xc0, 0x8c, 0x25, 0x96, 0xda, 0x7c, 0xf0, 0xe4, 0x9b, 0xe4, 0xb2, 0x98, 0xd8, 0x8c, 0xea,
563            0x92, 0x7a, 0xc7, 0xf5, 0x39, 0xf1, 0xed, 0xf2, 0x28, 0x37, 0x6d, 0x25,
564        ];
565        assert_eq!(&digest[..], &expected[..]);
566    }
567
568    // cSHAKE test vectors from NIST SP 800-185 §4.
569
570    #[test]
571    fn test_cshake128_empty_custom_is_shake128() {
572        // With empty N and S, cSHAKE128 == SHAKE128.
573        let data = [0x00u8; 4];
574        let mut cshake = CShake128::new(b"", b"");
575        cshake.update(&data);
576        let mut out1 = [0u8; 32];
577        cshake.squeeze(&mut out1);
578
579        let mut shake = Shake128::new();
580        shake.update(&data);
581        let mut out2 = [0u8; 32];
582        shake.squeeze(&mut out2);
583
584        assert_eq!(out1, out2);
585    }
586
587    #[test]
588    fn test_cshake128_sample1() {
589        // NIST SP 800-185 §4, Sample #3: N="", S="Email Signature",
590        // X=0x00010203, output 256 bits.
591        let data = [0x00, 0x01, 0x02, 0x03];
592        let mut c = CShake128::new(b"", b"Email Signature");
593        c.update(&data);
594        let mut out = [0u8; 32];
595        c.squeeze(&mut out);
596        let expected: [u8; 32] = [
597            0xC1, 0xC3, 0x69, 0x25, 0xB6, 0x40, 0x9A, 0x04, 0xF1, 0xB5, 0x04, 0xFC, 0xBC, 0xA9, 0xD8, 0x2B, 0x40, 0x17,
598            0x27, 0x7C, 0xB5, 0xED, 0x2B, 0x20, 0x65, 0xFC, 0x1D, 0x38, 0x14, 0xD5, 0xAA, 0xF5,
599        ];
600        assert_eq!(out, expected);
601    }
602
603    #[test]
604    fn test_cshake256_sample1() {
605        // NIST SP 800-185 §4, Sample #5: N="", S="Email Signature",
606        // X=0x00010203, output 256 bits.
607        let data = [0x00, 0x01, 0x02, 0x03];
608        let mut c = CShake256::new(b"", b"Email Signature");
609        c.update(&data);
610        let mut out = [0u8; 32];
611        c.squeeze(&mut out);
612        let expected: [u8; 32] = [
613            0xD0, 0x08, 0x82, 0x8E, 0x2B, 0x80, 0xAC, 0x9D, 0x22, 0x18, 0xFF, 0xEE, 0x1D, 0x07, 0x0C, 0x48, 0xB8, 0xE4,
614            0xC8, 0x7B, 0xFF, 0x32, 0xC9, 0x69, 0x9D, 0x5B, 0x68, 0x96, 0xEE, 0xE0, 0xED, 0xD1,
615        ];
616        assert_eq!(out, expected);
617    }
618}