Skip to main content

dryoc/
sha3.rs

1//! # SHA-3 hash algorithms
2//!
3//! Provides implementations of the SHA3-256 and SHA3-512 hash algorithms.
4//!
5//! SHA-3 hashes are unkeyed cryptographic hash functions. They turn arbitrary
6//! input bytes into fixed-size digests. Hashes are useful for fingerprints and
7//! compatibility with protocols that require SHA-3, but they do not
8//! authenticate messages by themselves. Use [`crate::auth`] or [`crate::hmac`]
9//! when a secret key must be involved.
10//!
11//! ## Example
12//!
13//! ```
14//! # #[cfg(feature = "alloc")]
15//! # {
16//! use dryoc::sha3::Sha3256;
17//!
18//! let mut state = Sha3256::new();
19//! state.update(b"The web of our life is of a mingled yarn.");
20//! let hash = state.finalize_to_vec();
21//! assert_eq!(hash.len(), 32);
22//! # }
23//! ```
24#[cfg(feature = "alloc")]
25use alloc::vec::Vec;
26
27use crate::constants::{CRYPTO_HASH_SHA3256_BYTES, CRYPTO_HASH_SHA3512_BYTES};
28use crate::keccak::{DOMAIN_SHA3, RATE_256, RATE_512, ROUNDS_FULL, Sponge};
29use crate::types::*;
30
31/// Type alias for a SHA3-256 digest.
32pub type Digest256 = StackByteArray<CRYPTO_HASH_SHA3256_BYTES>;
33/// Type alias for a SHA3-512 digest.
34pub type Digest512 = StackByteArray<CRYPTO_HASH_SHA3512_BYTES>;
35
36/// Defines a SHA-3 hasher over the shared Keccak [`Sponge`].
37///
38/// - `$name`: the hasher type; leading attributes (docs) are applied to it.
39/// - `$algo`: the algorithm name for the generated method docs.
40/// - `$rate`: the sponge rate in bytes.
41/// - `$digest_bytes`: the digest size constant.
42macro_rules! sha3_hasher {
43    (
44        $(#[$meta:meta])*
45        $name:ident,
46        $algo:literal,
47        $rate:expr,
48        $digest_bytes:expr,
49    ) => {
50        $(#[$meta])*
51        #[derive(Clone)]
52        pub struct $name {
53            sponge: Sponge<$rate, ROUNDS_FULL>,
54        }
55
56        impl $name {
57            #[doc = concat!("Returns a new ", $algo, " hasher instance.")]
58            #[must_use]
59            pub fn new() -> Self {
60                Self {
61                    sponge: Sponge::new(),
62                }
63            }
64
65            #[doc = concat!(
66                "One-time interface to compute ",
67                $algo,
68                " digest for `input`, copying\nresult into `output`."
69            )]
70            pub fn compute_into_bytes<Output: MutByteArray<$digest_bytes>, Input: Bytes + ?Sized>(
71                output: &mut Output,
72                input: &Input,
73            ) {
74                let mut hasher = Self::new();
75                hasher.update(input);
76                hasher.finalize_in_place(output.as_mut_array());
77            }
78
79            #[doc = concat!(
80                "One-time interface to compute ",
81                $algo,
82                " digest for `input`."
83            )]
84            #[must_use]
85            pub fn compute<Output: NewByteArray<$digest_bytes>, Input: Bytes + ?Sized>(
86                input: &Input,
87            ) -> Output {
88                let mut hasher = Self::new();
89                hasher.update(input);
90                let mut hash = Output::new_byte_array();
91                hasher.finalize_in_place(hash.as_mut_array());
92                hash
93            }
94
95            #[doc = concat!(
96                "Wrapper around [`",
97                stringify!($name),
98                "::compute`], returning a [`Vec`]. Provided for\nconvenience."
99            )]
100            #[cfg(feature = "alloc")]
101            #[must_use]
102            pub fn compute_to_vec<Input: Bytes + ?Sized>(input: &Input) -> Vec<u8> {
103                Self::compute::<StackByteArray<$digest_bytes>, _>(input).to_vec()
104            }
105
106            #[doc = concat!("Updates ", $algo, " hash state with `input`.")]
107            pub fn update<Input: Bytes + ?Sized>(&mut self, input: &Input) {
108                self.sponge.absorb(input.as_slice())
109            }
110
111            /// Consumes hasher and return final computed hash.
112            #[must_use]
113            pub fn finalize<Output: NewByteArray<$digest_bytes>>(mut self) -> Output {
114                let mut hash = Output::new_byte_array();
115                self.finalize_in_place(hash.as_mut_array());
116                hash
117            }
118
119            /// Consumes hasher and writes final computed hash into `output`.
120            pub fn finalize_into_bytes<Output: MutByteArray<$digest_bytes>>(
121                mut self,
122                output: &mut Output,
123            ) {
124                self.finalize_in_place(output.as_mut_array());
125            }
126
127            /// Pads and squeezes the digest without moving the hasher: every
128            /// consuming method finishes here, on the sponge where it lies,
129            /// which then drops (wipes). Passing the hasher on by value would
130            /// copy the secret-absorbing sponge and leave the moved-from copy
131            /// unwiped.
132            #[inline]
133            fn finalize_in_place(&mut self, output: &mut [u8; $digest_bytes]) {
134                self.sponge.pad(DOMAIN_SHA3);
135                self.sponge.squeeze(output);
136            }
137
138            /// Consumes hasher and returns final computed hash as a [`Vec`].
139            #[cfg(feature = "alloc")]
140            #[must_use]
141            pub fn finalize_to_vec(mut self) -> Vec<u8> {
142                let mut hash = StackByteArray::<$digest_bytes>::default();
143                self.finalize_in_place(hash.as_mut_array());
144                hash.to_vec()
145            }
146        }
147
148        impl Default for $name {
149            fn default() -> Self {
150                Self::new()
151            }
152        }
153    };
154}
155
156sha3_hasher! {
157    /// SHA3-256 wrapper, provided for convenience.
158    Sha3256,
159    "SHA3-256",
160    RATE_256,
161    CRYPTO_HASH_SHA3256_BYTES,
162}
163
164sha3_hasher! {
165    /// SHA3-512 wrapper, provided for convenience.
166    Sha3512,
167    "SHA3-512",
168    RATE_512,
169    CRYPTO_HASH_SHA3512_BYTES,
170}
171
172/// FIPS 202 known answers shared with the classic `crypto_hash_sha3*` tests.
173///
174/// The rate-boundary messages (rate-1, rate, rate+1 and, for SHA3-512, twice
175/// the rate) are the Keccak team's `ShortMsgKAT` entries at those byte
176/// lengths, as vendored by the RustCrypto `sha3` crate; every digest was
177/// cross-checked against Python's `hashlib` (OpenSSL) and a from-scratch
178/// Keccak-f[1600]. The 272-byte SHA3-256 message (twice its rate) is the
179/// `(i * 31 % 251)` pattern used by the SHA-2 tests, checked the same way.
180/// The million-`a` digests are the NIST SHA-3 example values.
181#[cfg(test)]
182pub(crate) mod test_vectors {
183    pub(crate) use crate::keccak::{RATE_256 as SHA3_256_RATE, RATE_512 as SHA3_512_RATE};
184    use crate::test_prelude::*;
185    use crate::utils::test_util::hex;
186
187    fn pattern(len: usize) -> Vec<u8> {
188        (0..len as u32).map(|i| (i * 31 % 251) as u8).collect()
189    }
190
191    /// `(message, digest)` pairs for SHA3-256 at lengths 0, 3, 135, 136,
192    /// 137, 272 and 1,000,000.
193    pub(crate) fn sha3_256() -> Vec<(Vec<u8>, Vec<u8>)> {
194        vec![
195            (
196                vec![],
197                hex("a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a"),
198            ),
199            (
200                b"abc".to_vec(),
201                hex("3a985da74fe225b2045c172d6bd390bd855f086e3e9d525b46bfe24511431532"),
202            ),
203            (
204                hex(concat!(
205                    "b771d5cef5d1a41a93d15643d7181d2a2ef0a8e84d91812f20ed21f147bef732",
206                    "bf3a60ef4067c3734b85bc8cd471780f10dc9e8291b58339a677b960218f71e7",
207                    "93f2797aea349406512829065d37bb55ea796fa4f56fd8896b49b2cd19b43215",
208                    "ad967c712b24e5032d065232e02c127409d2ed4146b9d75d763d52db98d949d3",
209                    "b0fed6a8052fbb",
210                )),
211                hex("a19eee92bb2097b64e823d597798aa18be9b7c736b8059abfd6779ac35ac81b5"),
212            ),
213            (
214                hex(concat!(
215                    "b32d95b0b9aad2a8816de6d06d1f86008505bd8c14124f6e9a163b5a2ade55f8",
216                    "35d0ec3880ef50700d3b25e42cc0af050ccd1be5e555b23087e04d7bf9813622",
217                    "780c7313a1954f8740b6ee2d3f71f768dd417f520482bd3a08d4f222b4ee9dbd",
218                    "015447b33507dd50f3ab4247c5de9a8abd62a8decea01e3b87c8b927f5b08beb",
219                    "37674c6f8e380c04",
220                )),
221                hex("df673f4105379ff6b755eeab20ceb0dc77b5286364fe16c59cc8a907aff07732"),
222            ),
223            (
224                hex(concat!(
225                    "04410e31082a47584b406f051398a6abe74e4da59bb6f85e6b49e8a1f7f2ca00",
226                    "dfba5462c2cd2bfde8b64fb21d70c083f11318b56a52d03b81cac5eec29eb31b",
227                    "d0078b6156786da3d6d8c33098c5c47bb67ac64db14165af65b44544d806dde5",
228                    "f487d5373c7f9792c299e9686b7e5821e7c8e2458315b996b5677d926dac57b3",
229                    "f22da873c601016a0d",
230                )),
231                hex("d52432cf3b6b4b949aa848e058dcd62d735e0177279222e7ac0af8504762faa0"),
232            ),
233            (
234                pattern(2 * SHA3_256_RATE),
235                hex("eff96935ef1690d1f7140a486ef18e2d193baa080205e2a69f3b4a184ca03b7f"),
236            ),
237            // Rate boundaries above cover buffering under Miri; keep the
238            // million-byte stress vector in the native suite.
239            #[cfg(not(miri))]
240            (
241                vec![b'a'; 1_000_000],
242                hex("5c8875ae474a3634ba4fd55ec85bffd661f32aca75c6d699d0cdcb6c115891c1"),
243            ),
244        ]
245    }
246
247    /// `(message, digest)` pairs for SHA3-512 at lengths 0, 3, 71, 72, 73,
248    /// 144 and 1,000,000.
249    pub(crate) fn sha3_512() -> Vec<(Vec<u8>, Vec<u8>)> {
250        vec![
251            (
252                vec![],
253                hex(concat!(
254                    "a69f73cca23a9ac5c8b567dc185a756e97c982164fe25859e0d1dcc1475c80a6",
255                    "15b2123af1f5f94c11e3e9402c3ac558f500199d95b6d3e301758586281dcd26",
256                )),
257            ),
258            (
259                b"abc".to_vec(),
260                hex(concat!(
261                    "b751850b1a57168a5693cd924b6b096e08f621827444f70d884f5d0240d2712e",
262                    "10e116e9192af3c91a7ec57647e3934057340b4cf408d5a56592f8274eec53f0",
263                )),
264            ),
265            (
266                hex(concat!(
267                    "13bd2811f6ed2b6f04ff3895aceed7bef8dcd45eb121791bc194a0f806206bff",
268                    "c3b9281c2b308b1a729ce008119dd3066e9378acdcc50a98a82e20738800b6cd",
269                    "dbe5fe9694ad6d",
270                )),
271                hex(concat!(
272                    "def4ab6cda8839729a03e000846604b17f03c5d5d7ec23c483670a13e11573c1",
273                    "e9347a63ec69a5abb21305f9382ecdaaabc6850f92840e86f88f4dabfcd93cc0",
274                )),
275            ),
276            (
277                hex(concat!(
278                    "1eed9cba179a009ec2ec5508773dd305477ca117e6d569e66b5f64c6bc64801c",
279                    "e25a8424ce4a26d575b8a6fb10ead3fd1992edddeec2ebe7150dc98f63adc323",
280                    "7ef57b91397aa8a7",
281                )),
282                hex(concat!(
283                    "a3e168b0d6c143ee9e17eae92930b97e6600356b73aebb5d68005dd1d0749445",
284                    "1a37052f7b39ff030c1ae1d7efc4e0c3667eb7a76c627ec14354c4f6a796e2c6",
285                )),
286            ),
287            (
288                hex(concat!(
289                    "ba5b67b5ec3a3ffae2c19dd8176a2ef75c0cd903725d45c9cb7009a900c0b0ca",
290                    "7a2967a95ae68269a6dbf8466c7b6844a1d608ac661f7eff00538e323db5f2c6",
291                    "44b78b2d48de1a08aa",
292                )),
293                hex(concat!(
294                    "635741b37f66cd5ce4dbd1f78accd907f96146e770b239046afb9181910b612d",
295                    "0e65841ff866806eed83c3ae7012fc55e42c3ffc9c6e3d03ce2870442f293ab4",
296                )),
297            ),
298            (
299                hex(concat!(
300                    "157d5b7e4507f66d9a267476d33831e7bb768d4d04cc3438da12f9010263ea5f",
301                    "cafbde2579db2f6b58f911d593d5f79fb05fe3596e3fa80ff2f761d1b0e57080",
302                    "055c118c53e53cdb63055261d7c9b2b39bd90acc32520cbbdbda2c4fd8856dbc",
303                    "ee173132a2679198daf83007a9b5c51511ae49766c792a29520388444ebefe28",
304                    "256fb33d4260439cba73a9479ee00c63",
305                )),
306                hex(concat!(
307                    "fe45289874879720ce2a844ae34bb73522775dcb6019dcd22b8885994672a088",
308                    "9c69e8115c641dc8b83e39f7311815a164dc46e0ba2fca344d86d4bc2ef2532c",
309                )),
310            ),
311            #[cfg(not(miri))]
312            (
313                vec![b'a'; 1_000_000],
314                hex(concat!(
315                    "3c3a876da14034ab60627c077bb98f7e120a2a5370212dffb3385a18d4f38859",
316                    "ed311d0a9d5141ce9cc5c66ee689b266a8aa18ace8282a0e0db596c90b0a7b87",
317                )),
318            ),
319        ]
320    }
321}
322
323#[cfg(all(test, feature = "alloc"))]
324mod tests {
325    use super::test_vectors::*;
326    use super::*;
327
328    /// Every vector: one-shot, and streamed in rate-sized pieces so each
329    /// update ends exactly on a permutation, with the last (partial or empty)
330    /// piece left for `finalize`.
331    #[test]
332    fn test_sha3256_known_answers() {
333        for (message, expected) in sha3_256() {
334            let len = message.len();
335            assert_eq!(Sha3256::compute_to_vec(&message), expected, "len {len}");
336            let mut digest = Digest256::default();
337            Sha3256::compute_into_bytes(&mut digest, &message);
338            assert_eq!(digest.as_slice(), expected, "len {len}");
339
340            let mut state = Sha3256::new();
341            for chunk in message.chunks(SHA3_256_RATE) {
342                state.update(chunk);
343            }
344            assert_eq!(state.finalize_to_vec(), expected, "rate chunks, len {len}");
345        }
346    }
347
348    #[test]
349    fn test_sha3512_known_answers() {
350        for (message, expected) in sha3_512() {
351            let len = message.len();
352            assert_eq!(Sha3512::compute_to_vec(&message), expected, "len {len}");
353            let mut digest = Digest512::default();
354            Sha3512::compute_into_bytes(&mut digest, &message);
355            assert_eq!(digest.as_slice(), expected, "len {len}");
356
357            let mut state = Sha3512::new();
358            for chunk in message.chunks(SHA3_512_RATE) {
359                state.update(chunk);
360            }
361            assert_eq!(state.finalize_to_vec(), expected, "rate chunks, len {len}");
362        }
363    }
364
365    /// One byte at a time, with an empty update before and after each byte,
366    /// so the absorb buffer crosses the rate boundary from every fill level.
367    #[test]
368    fn test_byte_and_empty_updates_match_known_answers() {
369        for (message, expected) in sha3_256()
370            .into_iter()
371            .filter(|(m, _)| m.len() <= 2 * SHA3_256_RATE)
372        {
373            let mut state = Sha3256::new();
374            state.update(b"");
375            for byte in &message {
376                state.update(core::slice::from_ref(byte));
377                state.update(b"");
378            }
379            assert_eq!(state.finalize_to_vec(), expected, "len {}", message.len());
380        }
381        for (message, expected) in sha3_512()
382            .into_iter()
383            .filter(|(m, _)| m.len() <= 2 * SHA3_512_RATE)
384        {
385            let mut state = Sha3512::new();
386            state.update(b"");
387            for byte in &message {
388                state.update(core::slice::from_ref(byte));
389                state.update(b"");
390            }
391            assert_eq!(state.finalize_to_vec(), expected, "len {}", message.len());
392        }
393    }
394}