Skip to main content

crypto/sha3/
shake128.rs

1use super::keccak::KeccakSponge;
2use crate::{Bytes, Hash, Hasher, Xof};
3
4pub(crate) const SHAKE128_RATE: usize = 168;
5const SHAKE128_DOMAIN_SEPARATOR: u8 = 0x1f;
6
7/// SHAKE128 extensible-output function (XOF) standardized in FIPS 202.
8///
9/// Implements both the [`Xof`] and [`Hasher`] traits.
10///
11/// # One-shot API
12///
13/// ```ignore
14/// use crypto::sha3::Shake128;
15///
16/// let mut output = [0u8; 32];
17/// Shake128::hash(b"hello world", &mut output);
18/// ```
19///
20/// # Incremental API
21///
22/// ```ignore
23/// use crypto::{sha3::Shake128, Xof};
24///
25/// let mut shake = Shake128::new();
26/// shake.absorb(b"hello ");
27/// shake.absorb(b"world");
28/// let mut out = [0u8; 32];
29/// shake.squeeze(&mut out);
30/// ```
31#[derive(Clone)]
32#[cfg_attr(feature = "zeroize", derive(zeroize::Zeroize, zeroize::ZeroizeOnDrop))]
33pub struct Shake128 {
34    keccak: KeccakSponge<24>,
35}
36
37impl Shake128 {
38    #[inline]
39    pub fn new() -> Self {
40        return Shake128 {
41            keccak: KeccakSponge::new(SHAKE128_RATE, SHAKE128_DOMAIN_SEPARATOR),
42        };
43    }
44
45    #[inline]
46    pub fn hash(data: &[u8], output: &mut [u8]) {
47        let mut hasher = Shake128::new();
48        hasher.absorb(data);
49        hasher.squeeze(output);
50    }
51}
52
53impl Hasher for Shake128 {
54    const BLOCK_SIZE: usize = SHAKE128_RATE;
55    const OUTPUT_SIZE: usize = 32;
56
57    #[inline]
58    fn new() -> Self {
59        return Shake128::new();
60    }
61
62    #[inline]
63    fn update(&mut self, data: &[u8]) {
64        self.absorb(data);
65    }
66
67    #[inline]
68    fn sum(mut self) -> Hash {
69        let mut hash = Bytes::<64>::with_length(Self::OUTPUT_SIZE);
70        self.squeeze(hash.as_mut());
71        return Hash(hash);
72    }
73}
74
75impl Xof for Shake128 {
76    #[inline]
77    fn absorb(&mut self, data: &[u8]) {
78        self.keccak.absorb(data);
79    }
80
81    #[inline]
82    fn squeeze(&mut self, out: &mut [u8]) {
83        self.keccak.squeeze(out);
84    }
85}
86
87#[cfg(test)]
88mod tests {
89    use super::Shake128;
90    use crate::Xof;
91
92    // NIST SHAKE128 test vectors
93    fn vectors_shake128() -> Vec<(Vec<u8>, usize, &'static str)> {
94        vec![
95            (
96                b"".to_vec(),
97                32,
98                "7f9c2ba4e88f827d616045507605853ed73b8093f6efbc88eb1a6eacfa66ef26",
99            ),
100            (
101                b"".to_vec(),
102                64,
103                "7f9c2ba4e88f827d616045507605853ed73b8093f6efbc88eb1a6eacfa66ef263cb1eea988004b93103cfb0aeefd2a686e01fa4a58e8a3639ca8a1e3f9ae57e2",
104            ),
105            (
106                b"abc".to_vec(),
107                32,
108                "5881092dd818bf5cf8a3ddb793fbcba74097d5c526a6d35f97b83351940f2cc8",
109            ),
110            (
111                b"The quick brown fox jumps over the lazy dog".to_vec(),
112                32,
113                "f4202e3c5852f9182a0430fd8144f0a74b95e7417ecae17db0f8cfeed0e3e66e",
114            ),
115        ]
116    }
117
118    #[test]
119    fn known_vectors() {
120        for (input, output_len, expected) in vectors_shake128() {
121            let mut output = vec![0u8; output_len];
122            Shake128::hash(&input, &mut output);
123            assert_eq!(hex::encode(&output), expected);
124        }
125    }
126
127    #[test]
128    fn incremental_and_streaming_read() {
129        let mut one_shot = vec![0u8; 64];
130        Shake128::hash(b"", &mut one_shot);
131
132        let mut shake = Shake128::new();
133        shake.absorb(b"");
134        let mut first = [0u8; 32];
135        let mut second = [0u8; 32];
136        shake.squeeze(&mut first);
137        shake.squeeze(&mut second);
138
139        let mut combined = vec![0u8; 64];
140        combined[..32].copy_from_slice(&first);
141        combined[32..].copy_from_slice(&second);
142
143        assert_eq!(combined, one_shot);
144    }
145}