Skip to main content

ic_hash/
sp800_185.rs

1//! SP 800-185: the string encodings, and cSHAKE.
2//!
3//! Everything in SP 800-185 — cSHAKE, KMAC, TupleHash, ParallelHash — is built
4//! out of three encoding functions and a padding rule. They are unglamorous and
5//! easy to get subtly wrong, and a mistake in them does not look like a
6//! mistake: the output is still a well-distributed hash, just not the one every
7//! other implementation computes.
8//!
9//! ```text
10//! left_encode(x)    = n || x as n big-endian bytes      -- length first
11//! right_encode(x)   = x as n big-endian bytes || n      -- length last
12//! encode_string(S)  = left_encode(len(S) in bits) || S
13//! bytepad(X, w)     = left_encode(w) || X || zeros, to a multiple of w
14//! ```
15//!
16//! Note that `encode_string` counts **bits**, while `bytepad` pads to a
17//! multiple of **bytes**. Mixing those up is the classic error here, and it
18//! produces output that is wrong by a factor of eight in one field.
19//!
20//! # Why cSHAKE lives here and not beside SHAKE
21//!
22//! cSHAKE is SHAKE with a different domain separator and a prefix. SP 800-185
23//! section 3.3 defines it so that with no customization at all it is *exactly*
24//! SHAKE:
25//!
26//! ```text
27//! cSHAKE128(X, L, "", "") == SHAKE128(X, L)
28//! ```
29//!
30//! That identity is a free oracle against an already-validated implementation,
31//! and `empty_customization_is_plain_shake` checks it. It does not
32//! cover the customized path, which uses domain separator `0x04` where SHAKE
33//! uses `0x1f`; that path is checked against an independent Keccak written from
34//! FIPS 202 in the tests below.
35
36use crate::sha3::Sponge;
37use ic_core::traits::Algorithm;
38
39/// The largest `left_encode`/`right_encode` output: eight value bytes plus the
40/// length byte.
41pub const MAX_ENCODE: usize = 9;
42
43/// `left_encode(x)` from SP 800-185 section 2.3.1, written into `buf`.
44///
45/// Returns the number of bytes used.
46pub fn left_encode(x: u64, buf: &mut [u8; MAX_ENCODE]) -> usize {
47    let n = value_bytes(x);
48    buf[0] = n as u8;
49    for i in 0..n {
50        buf[1 + i] = (x >> (8 * (n - 1 - i))) as u8;
51    }
52    n + 1
53}
54
55/// `right_encode(x)` from SP 800-185 section 2.3.1, written into `buf`.
56pub fn right_encode(x: u64, buf: &mut [u8; MAX_ENCODE]) -> usize {
57    let n = value_bytes(x);
58    for (i, slot) in buf.iter_mut().take(n).enumerate() {
59        *slot = (x >> (8 * (n - 1 - i))) as u8;
60    }
61    buf[n] = n as u8;
62    n + 1
63}
64
65/// How many bytes the value needs. Zero takes one, not zero: the encodings have
66/// no empty case.
67fn value_bytes(x: u64) -> usize {
68    if x == 0 {
69        1
70    } else {
71        8 - (x.leading_zeros() / 8) as usize
72    }
73}
74
75/// Absorbs a byte string into a sponge while counting what it fed in, so that
76/// `bytepad` knows how much padding to add without a scratch buffer.
77///
78/// SP 800-185's `bytepad` is defined on a fully assembled string. Assembling it
79/// would mean allocating, or a buffer large enough for the longest key anyone
80/// might use. Streaming it and tracking the length gives the same bytes.
81pub(crate) struct CountingAbsorb<'a> {
82    sponge: &'a mut Sponge,
83    written: usize,
84}
85
86impl<'a> CountingAbsorb<'a> {
87    pub(crate) fn new(sponge: &'a mut Sponge) -> Self {
88        Self { sponge, written: 0 }
89    }
90
91    pub(crate) fn feed(&mut self, data: &[u8]) {
92        self.sponge.absorb(data);
93        self.written += data.len();
94    }
95
96    /// `encode_string(S)`: the bit length, then the bytes.
97    pub(crate) fn feed_encoded_string(&mut self, s: &[u8]) {
98        let mut buf = [0u8; MAX_ENCODE];
99        // Bits, not bytes. The multiplication cannot overflow for any string
100        // that fits in memory on a 64-bit target, and on a 32-bit one the
101        // length is far smaller still.
102        let n = left_encode((s.len() as u64) * 8, &mut buf);
103        self.feed(&buf[..n]);
104        self.feed(s);
105    }
106
107    /// Pad with zeros up to a multiple of `w` bytes, completing `bytepad`.
108    pub(crate) fn finish_bytepad(self, w: usize) {
109        let remainder = self.written % w;
110        if remainder != 0 {
111            let zeros = [0u8; 168];
112            let mut left = w - remainder;
113            while left > 0 {
114                let chunk = core::cmp::min(left, zeros.len());
115                self.sponge.absorb(&zeros[..chunk]);
116                left -= chunk;
117            }
118        }
119    }
120}
121
122/// Build a cSHAKE type over one SHAKE parameter set.
123macro_rules! cshake {
124    ($name:ident, $id:literal, $disp:literal, $rate:literal, $doc:literal) => {
125        #[doc = $doc]
126        #[derive(Clone)]
127        pub struct $name {
128            sponge: Sponge,
129        }
130
131        impl Algorithm for $name {
132            const ID: &'static str = $id;
133            const NAME: &'static str = $disp;
134        }
135
136        impl $name {
137            /// The sponge rate in bytes, which is also `bytepad`'s width here.
138            pub const RATE: usize = $rate;
139
140            /// Start a cSHAKE with a function name `n` and customization `s`.
141            ///
142            /// `n` is reserved for NIST-defined functions — KMAC passes
143            /// `"KMAC"`. Application customization belongs in `s`.
144            ///
145            /// With both empty this is plain SHAKE, per SP 800-185 section 3.3,
146            /// including the domain separator.
147            pub fn new(n: &[u8], s: &[u8]) -> Self {
148                if n.is_empty() && s.is_empty() {
149                    return Self {
150                        sponge: Sponge::new($rate, 0x1f),
151                    };
152                }
153                // The customized form uses the `00` domain bits, which become
154                // 0x04 once the pad10*1 rule adds its leading one.
155                let mut sponge = Sponge::new($rate, 0x04);
156                let mut prefix = CountingAbsorb::new(&mut sponge);
157                let mut buf = [0u8; MAX_ENCODE];
158                let used = left_encode($rate as u64, &mut buf);
159                prefix.feed(&buf[..used]);
160                prefix.feed_encoded_string(n);
161                prefix.feed_encoded_string(s);
162                prefix.finish_bytepad($rate);
163                Self { sponge }
164            }
165
166            /// Absorb more input.
167            pub fn update(&mut self, data: &[u8]) {
168                self.sponge.absorb(data);
169            }
170
171            /// Squeeze `out.len()` bytes.
172            pub fn finalize_xof(mut self, out: &mut [u8]) {
173                self.sponge.finish();
174                self.sponge.squeeze(out);
175            }
176
177            /// One-shot.
178            pub fn xof(n: &[u8], s: &[u8], data: &[u8], out: &mut [u8]) {
179                let mut x = Self::new(n, s);
180                x.update(data);
181                x.finalize_xof(out);
182            }
183
184            /// Absorb `bytepad(encode_string(s), RATE)`.
185            ///
186            /// KMAC prefixes its message with the key encoded exactly this way.
187            /// It is exposed here rather than rebuilt in `ic-mac` because the
188            /// padding width is the sponge rate, which is cSHAKE's property and
189            /// not the caller's to know.
190            pub fn absorb_bytepadded_string(&mut self, s: &[u8]) {
191                let mut pad = CountingAbsorb::new(&mut self.sponge);
192                let mut buf = [0u8; MAX_ENCODE];
193                let used = left_encode($rate as u64, &mut buf);
194                pad.feed(&buf[..used]);
195                pad.feed_encoded_string(s);
196                pad.finish_bytepad($rate);
197            }
198        }
199    };
200}
201
202/// Known-answer test for a cSHAKE, built on the SP 800-185 section 3.3
203/// identity rather than on a pinned value.
204macro_rules! cshake_self_test {
205    ($name:ident, $shake:ty, $id:literal) => {
206        impl ic_core::traits::SelfTest for $name {
207            /// Two checks, neither of which needs a vector this code produced.
208            ///
209            /// With no customization cSHAKE must equal SHAKE exactly, and SHAKE
210            /// has its own CAST against a published FIPS 202 answer — so this
211            /// inherits that evidence. Then customization must change the
212            /// output, which catches a build where the prefix was silently
213            /// dropped and every customized call collapsed onto plain SHAKE.
214            fn self_test() -> ic_core::Result<()> {
215                let mut plain = [0u8; 32];
216                let mut shake = [0u8; 32];
217                Self::xof(b"", b"", b"abc", &mut plain);
218                <$shake>::xof(b"abc", &mut shake);
219                ic_core::ensure!(ic_core::ct::verify(&plain, &shake), SelfTestFailed, $id);
220
221                let mut customized = [0u8; 32];
222                Self::xof(b"", b"self-test", b"abc", &mut customized);
223                ic_core::ensure!(
224                    !ic_core::ct::verify(&plain, &customized),
225                    SelfTestFailed,
226                    $id
227                );
228                Ok(())
229            }
230        }
231    };
232}
233
234cshake!(
235    CShake128,
236    "cshake128",
237    "cSHAKE128",
238    168,
239    "SP 800-185 cSHAKE128: SHAKE128 with a customization string."
240);
241cshake!(
242    CShake256,
243    "cshake256",
244    "cSHAKE256",
245    136,
246    "SP 800-185 cSHAKE256: SHAKE256 with a customization string."
247);
248
249cshake_self_test!(CShake128, crate::Shake128, "cshake128");
250cshake_self_test!(CShake256, crate::Shake256, "cshake256");
251
252// ---------------------------------------------------------------------------
253// TupleHash
254// ---------------------------------------------------------------------------
255
256/// Declare a TupleHash over one cSHAKE parameter set.
257macro_rules! tuple_hash {
258    ($name:ident, $cshake:ty, $id:literal, $disp:literal, $doc:literal) => {
259        #[doc = $doc]
260        #[derive(Clone)]
261        pub struct $name {
262            inner: $cshake,
263        }
264
265        impl Algorithm for $name {
266            const ID: &'static str = $id;
267            const NAME: &'static str = $disp;
268        }
269
270        impl $name {
271            /// Start a TupleHash with a customization string.
272            pub fn new(custom: &[u8]) -> Self {
273                Self {
274                    inner: <$cshake>::new(b"TupleHash", custom),
275                }
276            }
277
278            /// Add one element of the tuple.
279            ///
280            /// Each element is length-prefixed, which is the entire point: see
281            /// the type documentation.
282            pub fn update(&mut self, element: &[u8]) {
283                let mut buf = [0u8; MAX_ENCODE];
284                let used = left_encode((element.len() as u64) * 8, &mut buf);
285                self.inner.update(&buf[..used]);
286                self.inner.update(element);
287            }
288
289            /// Finish with a fixed-length output, binding the length in.
290            pub fn finalize(mut self, out: &mut [u8]) {
291                let mut buf = [0u8; MAX_ENCODE];
292                let used = right_encode((out.len() as u64) * 8, &mut buf);
293                self.inner.update(&buf[..used]);
294                self.inner.finalize_xof(out);
295            }
296
297            /// Finish in XOF mode, where the output is a stream.
298            pub fn finalize_xof(mut self, out: &mut [u8]) {
299                let mut buf = [0u8; MAX_ENCODE];
300                let used = right_encode(0, &mut buf);
301                self.inner.update(&buf[..used]);
302                self.inner.finalize_xof(out);
303            }
304
305            /// One-shot over a slice of elements.
306            pub fn hash(custom: &[u8], elements: &[&[u8]], out: &mut [u8]) {
307                let mut t = Self::new(custom);
308                for element in elements {
309                    t.update(element);
310                }
311                t.finalize(out);
312            }
313
314            /// One-shot in XOF mode.
315            pub fn hash_xof(custom: &[u8], elements: &[&[u8]], out: &mut [u8]) {
316                let mut t = Self::new(custom);
317                for element in elements {
318                    t.update(element);
319                }
320                t.finalize_xof(out);
321            }
322        }
323
324        impl ic_core::traits::SelfTest for $name {
325            /// The property TupleHash exists for: two different tuples that
326            /// concatenate to the same bytes must hash differently.
327            fn self_test() -> ic_core::Result<()> {
328                let mut a = [0u8; 32];
329                let mut b = [0u8; 32];
330                Self::hash(b"self-test", &[b"abc", b"d"], &mut a);
331                Self::hash(b"self-test", &[b"ab", b"cd"], &mut b);
332                ic_core::ensure!(!ic_core::ct::verify(&a, &b), SelfTestFailed, $id);
333
334                // And it is deterministic.
335                let mut again = [0u8; 32];
336                Self::hash(b"self-test", &[b"abc", b"d"], &mut again);
337                ic_core::ensure!(ic_core::ct::verify(&a, &again), SelfTestFailed, $id);
338                Ok(())
339            }
340        }
341    };
342}
343
344tuple_hash!(
345    TupleHash128,
346    CShake128,
347    "tuplehash128",
348    "TupleHash128",
349    "SP 800-185 TupleHash128: hashes a *sequence* of strings unambiguously.\n\
350     \n\
351     Hashing `a || b` cannot distinguish `(\"abc\", \"d\")` from `(\"ab\", \"cd\")`,\n\
352     and a protocol that concatenates fields before hashing them has a\n\
353     forgery waiting in it. TupleHash length-prefixes every element, so\n\
354     distinct tuples always hash distinctly."
355);
356tuple_hash!(
357    TupleHash256,
358    CShake256,
359    "tuplehash256",
360    "TupleHash256",
361    "SP 800-185 TupleHash256, at the 256-bit security level."
362);
363
364// ---------------------------------------------------------------------------
365// ParallelHash
366// ---------------------------------------------------------------------------
367
368/// Declare a ParallelHash over one cSHAKE parameter set.
369///
370/// `$chain` is the inner digest width in bytes: 32 for the 128-bit parameter
371/// set, 64 for the 256-bit one, per SP 800-185 section 6.2.
372macro_rules! parallel_hash {
373    ($name:ident, $cshake:ty, $chain:literal, $id:literal, $disp:literal, $doc:literal) => {
374        #[doc = $doc]
375        pub struct $name;
376
377        impl Algorithm for $name {
378            const ID: &'static str = $id;
379            const NAME: &'static str = $disp;
380        }
381
382        impl $name {
383            /// Width of each block's inner digest, in bytes.
384            pub const CHAINING_LEN: usize = $chain;
385
386            /// Hash `data` in blocks of `block_size` bytes.
387            ///
388            /// `block_size` must be at least one. SP 800-185 places no upper
389            /// bound on it, and neither does this.
390            pub fn hash(custom: &[u8], block_size: usize, data: &[u8], out: &mut [u8]) {
391                Self::run(custom, block_size, data, out, false)
392            }
393
394            /// As [`Self::hash`], in XOF mode.
395            pub fn hash_xof(custom: &[u8], block_size: usize, data: &[u8], out: &mut [u8]) {
396                Self::run(custom, block_size, data, out, true)
397            }
398
399            fn run(custom: &[u8], block_size: usize, data: &[u8], out: &mut [u8], xof: bool) {
400                assert!(block_size > 0, "parallelhash block size must be positive");
401                let mut outer = <$cshake>::new(b"ParallelHash", custom);
402                let mut buf = [0u8; MAX_ENCODE];
403
404                let used = left_encode(block_size as u64, &mut buf);
405                outer.update(&buf[..used]);
406
407                let mut blocks = 0u64;
408                for block in data.chunks(block_size) {
409                    // Each block's digest is plain SHAKE at the chaining width,
410                    // which is what cSHAKE with no customization gives.
411                    let mut chain = [0u8; $chain];
412                    <$cshake>::xof(b"", b"", block, &mut chain);
413                    outer.update(&chain);
414                    blocks += 1;
415                }
416
417                let used = right_encode(blocks, &mut buf);
418                outer.update(&buf[..used]);
419                let used = right_encode(if xof { 0 } else { (out.len() as u64) * 8 }, &mut buf);
420                outer.update(&buf[..used]);
421                outer.finalize_xof(out);
422            }
423        }
424
425        impl ic_core::traits::SelfTest for $name {
426            /// The block size is bound into the result, and the whole thing is
427            /// deterministic. A build that dropped `left_encode(B)` would give
428            /// the same answer for every block size, which is what this catches.
429            fn self_test() -> ic_core::Result<()> {
430                let data = [0x5au8; 200];
431                let mut a = [0u8; 32];
432                let mut b = [0u8; 32];
433                Self::hash(b"self-test", 16, &data, &mut a);
434                Self::hash(b"self-test", 32, &data, &mut b);
435                ic_core::ensure!(!ic_core::ct::verify(&a, &b), SelfTestFailed, $id);
436
437                let mut again = [0u8; 32];
438                Self::hash(b"self-test", 16, &data, &mut again);
439                ic_core::ensure!(ic_core::ct::verify(&a, &again), SelfTestFailed, $id);
440                Ok(())
441            }
442        }
443    };
444}
445
446parallel_hash!(
447    ParallelHash128,
448    CShake128,
449    32,
450    "parallelhash128",
451    "ParallelHash128",
452    "SP 800-185 ParallelHash128: hashes fixed-size blocks independently, then\n\
453     hashes their digests.\n\
454     \n\
455     The structure is designed so the per-block work can be spread across\n\
456     cores. This implementation does them in order — the workspace has no\n\
457     threading and `no_std` targets have no threads to spread onto — so what\n\
458     it buys here is interoperability with implementations that do, not\n\
459     speed. The output is identical either way."
460);
461parallel_hash!(
462    ParallelHash256,
463    CShake256,
464    64,
465    "parallelhash256",
466    "ParallelHash256",
467    "SP 800-185 ParallelHash256, at the 256-bit security level."
468);
469
470#[cfg(test)]
471mod tests {
472    use super::*;
473    use crate::{Sha3_256, Shake128, Shake256};
474    use ic_core::codec::hex;
475    use ic_core::traits::Digest;
476
477    // -- the encodings ----------------------------------------------------
478
479    /// Straight from SP 800-185 section 2.3.1, independent of the
480    /// implementation above.
481    fn reference_left_encode(x: u64) -> Vec<u8> {
482        let mut bytes = x.to_be_bytes().to_vec();
483        while bytes.len() > 1 && bytes[0] == 0 {
484            bytes.remove(0);
485        }
486        let mut out = vec![bytes.len() as u8];
487        out.extend_from_slice(&bytes);
488        out
489    }
490
491    fn reference_right_encode(x: u64) -> Vec<u8> {
492        let mut bytes = x.to_be_bytes().to_vec();
493        while bytes.len() > 1 && bytes[0] == 0 {
494            bytes.remove(0);
495        }
496        let n = bytes.len() as u8;
497        bytes.push(n);
498        bytes
499    }
500
501    #[test]
502    fn the_encodings_match_an_independent_construction() {
503        for x in [
504            0u64,
505            1,
506            2,
507            127,
508            128,
509            255,
510            256,
511            65535,
512            65536,
513            1 << 24,
514            u32::MAX as u64,
515            u64::MAX,
516        ] {
517            let mut buf = [0u8; MAX_ENCODE];
518            let n = left_encode(x, &mut buf);
519            assert_eq!(&buf[..n], &reference_left_encode(x)[..], "left_encode({x})");
520
521            let n = right_encode(x, &mut buf);
522            assert_eq!(
523                &buf[..n],
524                &reference_right_encode(x)[..],
525                "right_encode({x})"
526            );
527        }
528    }
529
530    /// The published examples in SP 800-185 section 2.3.1.
531    #[test]
532    fn the_encodings_match_the_published_examples() {
533        let mut buf = [0u8; MAX_ENCODE];
534        let n = left_encode(0, &mut buf);
535        assert_eq!(&buf[..n], &[0x01, 0x00]);
536        let n = right_encode(0, &mut buf);
537        assert_eq!(&buf[..n], &[0x00, 0x01]);
538        // 1 encodes as one value byte either way, with the count on the other
539        // end; this is the pair that makes the difference between the two
540        // functions visible.
541        let n = left_encode(1, &mut buf);
542        assert_eq!(&buf[..n], &[0x01, 0x01]);
543        let n = right_encode(1, &mut buf);
544        assert_eq!(&buf[..n], &[0x01, 0x01]);
545        let n = left_encode(256, &mut buf);
546        assert_eq!(&buf[..n], &[0x02, 0x01, 0x00]);
547        let n = right_encode(256, &mut buf);
548        assert_eq!(&buf[..n], &[0x01, 0x00, 0x02]);
549    }
550
551    // -- an independent Keccak -------------------------------------------
552
553    /// Keccak-f[1600] written the slow, obvious way from FIPS 202 section 3.2:
554    /// a 5x5 lane array, each step separate, no flattening and no precomputed
555    /// permutation tables.
556    ///
557    /// This exists to check cSHAKE's customized path, which uses a domain
558    /// separator the SHAKE vectors never exercise. Its own correctness is
559    /// established by [`the_reference_keccak_reproduces_sha3`], which runs it
560    /// against the published FIPS 202 SHA3-256 answer — so the chain is
561    /// published vector -> this reference -> cSHAKE.
562    ///
563    /// The index-based loops are the point: FIPS 202 is written in terms of
564    /// `A[x, y]`, and matching that notation is what makes this checkable
565    /// against the document. Iterator form would be tidier and less useful.
566    #[allow(clippy::needless_range_loop)]
567    fn reference_keccak(lanes: &mut [[u64; 5]; 5]) {
568        const RC: [u64; 24] = [
569            0x0000000000000001,
570            0x0000000000008082,
571            0x800000000000808a,
572            0x8000000080008000,
573            0x000000000000808b,
574            0x0000000080000001,
575            0x8000000080008081,
576            0x8000000000008009,
577            0x000000000000008a,
578            0x0000000000000088,
579            0x0000000080008009,
580            0x000000008000000a,
581            0x000000008000808b,
582            0x800000000000008b,
583            0x8000000000008089,
584            0x8000000000008003,
585            0x8000000000008002,
586            0x8000000000000080,
587            0x000000000000800a,
588            0x800000008000000a,
589            0x8000000080008081,
590            0x8000000000008080,
591            0x0000000080000001,
592            0x8000000080008008,
593        ];
594        // Rotation offsets, indexed [x][y], from FIPS 202 Table 2.
595        const R: [[u32; 5]; 5] = [
596            [0, 36, 3, 41, 18],
597            [1, 44, 10, 45, 2],
598            [62, 6, 43, 15, 61],
599            [28, 55, 25, 21, 56],
600            [27, 20, 39, 8, 14],
601        ];
602
603        for rc in RC {
604            // theta
605            let mut c = [0u64; 5];
606            for (x, cx) in c.iter_mut().enumerate() {
607                *cx = lanes[x][0] ^ lanes[x][1] ^ lanes[x][2] ^ lanes[x][3] ^ lanes[x][4];
608            }
609            let mut d = [0u64; 5];
610            for x in 0..5 {
611                d[x] = c[(x + 4) % 5] ^ c[(x + 1) % 5].rotate_left(1);
612            }
613            for x in 0..5 {
614                for y in 0..5 {
615                    lanes[x][y] ^= d[x];
616                }
617            }
618
619            // rho and pi
620            let mut b = [[0u64; 5]; 5];
621            for x in 0..5 {
622                for y in 0..5 {
623                    b[y][(2 * x + 3 * y) % 5] = lanes[x][y].rotate_left(R[x][y]);
624                }
625            }
626
627            // chi
628            for x in 0..5 {
629                for y in 0..5 {
630                    lanes[x][y] = b[x][y] ^ ((!b[(x + 1) % 5][y]) & b[(x + 2) % 5][y]);
631                }
632            }
633
634            // iota
635            lanes[0][0] ^= rc;
636        }
637    }
638
639    /// A sponge over the reference permutation, again written plainly.
640    fn reference_sponge(rate: usize, pad: u8, input: &[u8], out: &mut [u8]) {
641        let mut lanes = [[0u64; 5]; 5];
642        let put = |lanes: &mut [[u64; 5]; 5], i: usize, byte: u8| {
643            let lane = i / 8;
644            lanes[lane % 5][lane / 5] ^= (byte as u64) << (8 * (i % 8));
645        };
646        let get = |lanes: &[[u64; 5]; 5], i: usize| -> u8 {
647            let lane = i / 8;
648            (lanes[lane % 5][lane / 5] >> (8 * (i % 8))) as u8
649        };
650
651        // Absorb, padding the final block with pad10*1.
652        let mut padded = input.to_vec();
653        padded.push(pad);
654        while padded.len() % rate != 0 {
655            padded.push(0);
656        }
657        let last = padded.len() - 1;
658        padded[last] |= 0x80;
659
660        for block in padded.chunks(rate) {
661            for (i, byte) in block.iter().enumerate() {
662                put(&mut lanes, i, *byte);
663            }
664            reference_keccak(&mut lanes);
665        }
666
667        // Squeeze.
668        let mut produced = 0;
669        while produced < out.len() {
670            let take = core::cmp::min(rate, out.len() - produced);
671            for i in 0..take {
672                out[produced + i] = get(&lanes, i);
673            }
674            produced += take;
675            if produced < out.len() {
676                reference_keccak(&mut lanes);
677            }
678        }
679    }
680
681    /// Anchors the reference implementation to a published value before
682    /// anything is checked against it. FIPS 202's SHA3-256("abc").
683    #[test]
684    fn the_reference_keccak_reproduces_sha3() {
685        let mut got = [0u8; 32];
686        // SHA-3 uses rate 136 for 256-bit output, and domain bits 01.
687        reference_sponge(136, 0x06, b"abc", &mut got);
688        assert_eq!(
689            hex(&got),
690            "3a985da74fe225b2045c172d6bd390bd855f086e3e9d525b46bfe24511431532"
691        );
692        // And it agrees with the shipped implementation on the same input.
693        assert_eq!(hex(Sha3_256::digest(b"abc").as_ref()), hex(&got));
694    }
695
696    // -- cSHAKE ------------------------------------------------------------
697
698    /// SP 800-185 section 3.3: with no customization, cSHAKE *is* SHAKE.
699    #[test]
700    fn empty_customization_is_plain_shake() {
701        for len in [1usize, 16, 32, 168, 169, 512] {
702            let mut a = vec![0u8; len];
703            let mut b = vec![0u8; len];
704            CShake128::xof(b"", b"", b"the quick brown fox", &mut a);
705            Shake128::xof(b"the quick brown fox", &mut b);
706            assert_eq!(a, b, "cSHAKE128 with no customization, {len} bytes");
707
708            CShake256::xof(b"", b"", b"the quick brown fox", &mut a);
709            Shake256::xof(b"the quick brown fox", &mut b);
710            assert_eq!(a, b, "cSHAKE256 with no customization, {len} bytes");
711        }
712    }
713
714    /// The customized path, against the independent Keccak. This is the check
715    /// the SHAKE identity above cannot make, because customization switches the
716    /// domain separator from 0x1f to 0x04.
717    #[test]
718    fn the_customized_path_matches_the_reference_keccak() {
719        let cases: &[(&[u8], &[u8], &[u8])] = &[
720            (b"", b"Email Signature", b"\x00\x01\x02\x03"),
721            (b"KMAC", b"", b"hello"),
722            (b"KMAC", b"My Tagged Application", b""),
723            (b"N", b"S", &[0x5a; 200]),
724        ];
725
726        for (n, s, data) in cases {
727            for (rate, is_128) in [(168usize, true), (136, false)] {
728                // Assemble bytepad(encode_string(N) || encode_string(S), rate)
729                // by hand, then the message, then run the reference sponge.
730                let mut prefix = reference_left_encode(rate as u64);
731                prefix.extend_from_slice(&reference_left_encode((n.len() as u64) * 8));
732                prefix.extend_from_slice(n);
733                prefix.extend_from_slice(&reference_left_encode((s.len() as u64) * 8));
734                prefix.extend_from_slice(s);
735                while prefix.len() % rate != 0 {
736                    prefix.push(0);
737                }
738                prefix.extend_from_slice(data);
739
740                let mut want = [0u8; 64];
741                reference_sponge(rate, 0x04, &prefix, &mut want);
742
743                let mut got = [0u8; 64];
744                if is_128 {
745                    CShake128::xof(n, s, data, &mut got);
746                } else {
747                    CShake256::xof(n, s, data, &mut got);
748                }
749                assert_eq!(
750                    hex(&got),
751                    hex(&want),
752                    "cSHAKE rate {rate}, N={n:?}, S={s:?}"
753                );
754            }
755        }
756    }
757
758    /// Different customization must give different output, or the parameter is
759    /// not doing anything.
760    #[test]
761    fn customization_changes_the_output() {
762        let mut a = [0u8; 32];
763        let mut b = [0u8; 32];
764        let mut c = [0u8; 32];
765        CShake128::xof(b"", b"one", b"message", &mut a);
766        CShake128::xof(b"", b"two", b"message", &mut b);
767        CShake128::xof(b"", b"", b"message", &mut c);
768        assert_ne!(a, b, "S is bound into the output");
769        assert_ne!(a, c, "and distinguishes customized from plain");
770    }
771
772    #[test]
773    fn streaming_matches_the_one_shot() {
774        let data = [0x37u8; 500];
775        let mut one = [0u8; 64];
776        CShake128::xof(b"KMAC", b"S", &data, &mut one);
777
778        let mut x = CShake128::new(b"KMAC", b"S");
779        for chunk in data.chunks(7) {
780            x.update(chunk);
781        }
782        let mut streamed = [0u8; 64];
783        x.finalize_xof(&mut streamed);
784        assert_eq!(one, streamed);
785    }
786}
787
788#[cfg(test)]
789mod tuple_parallel_tests {
790    use super::*;
791    use ic_core::traits::SelfTest;
792
793    fn enc(x: u64) -> Vec<u8> {
794        let mut bytes = x.to_be_bytes().to_vec();
795        while bytes.len() > 1 && bytes[0] == 0 {
796            bytes.remove(0);
797        }
798        let mut v = vec![bytes.len() as u8];
799        v.extend_from_slice(&bytes);
800        v
801    }
802
803    fn renc(x: u64) -> Vec<u8> {
804        let mut bytes = x.to_be_bytes().to_vec();
805        while bytes.len() > 1 && bytes[0] == 0 {
806            bytes.remove(0);
807        }
808        let n = bytes.len() as u8;
809        bytes.push(n);
810        bytes
811    }
812
813    /// SP 800-185 section 5.1, assembled literally over cSHAKE.
814    ///
815    /// cSHAKE is trusted because its own tests check it against a Keccak
816    /// written from FIPS 202 and anchored to a published SHA-3 vector, so this
817    /// checks the layer TupleHash adds: the per-element length prefixes and the
818    /// trailing output length.
819    fn reference_tuple_hash(
820        wide: bool,
821        custom: &[u8],
822        elements: &[&[u8]],
823        out: &mut [u8],
824        xof: bool,
825    ) {
826        let mut z = Vec::new();
827        for e in elements {
828            z.extend_from_slice(&enc((e.len() as u64) * 8));
829            z.extend_from_slice(e);
830        }
831        z.extend_from_slice(&renc(if xof { 0 } else { (out.len() as u64) * 8 }));
832        if wide {
833            CShake256::xof(b"TupleHash", custom, &z, out);
834        } else {
835            CShake128::xof(b"TupleHash", custom, &z, out);
836        }
837    }
838
839    /// SP 800-185 section 6.2, likewise.
840    fn reference_parallel_hash(
841        wide: bool,
842        custom: &[u8],
843        block_size: usize,
844        data: &[u8],
845        out: &mut [u8],
846        xof: bool,
847    ) {
848        let chain_len = if wide { 64 } else { 32 };
849        let mut z = enc(block_size as u64);
850        let mut n = 0u64;
851        for block in data.chunks(block_size) {
852            let mut chain = vec![0u8; chain_len];
853            if wide {
854                CShake256::xof(b"", b"", block, &mut chain);
855            } else {
856                CShake128::xof(b"", b"", block, &mut chain);
857            }
858            z.extend_from_slice(&chain);
859            n += 1;
860        }
861        z.extend_from_slice(&renc(n));
862        z.extend_from_slice(&renc(if xof { 0 } else { (out.len() as u64) * 8 }));
863        if wide {
864            CShake256::xof(b"ParallelHash", custom, &z, out);
865        } else {
866            CShake128::xof(b"ParallelHash", custom, &z, out);
867        }
868    }
869
870    #[test]
871    fn tuple_hash_matches_an_independent_construction() {
872        let cases: &[(&[u8], &[&[u8]])] = &[
873            (b"", &[]),
874            (b"", &[b"abc"]),
875            (b"My Tupled App", &[b"abc", b"d"]),
876            (b"", &[b"", b"", b""]),
877            (b"S", &[&[0x5au8; 300][..], b"x", &[0u8; 168][..]]),
878        ];
879
880        for (custom, elements) in cases {
881            for len in [16usize, 32, 64] {
882                let mut want = vec![0u8; len];
883                let mut got = vec![0u8; len];
884
885                reference_tuple_hash(false, custom, elements, &mut want, false);
886                TupleHash128::hash(custom, elements, &mut got);
887                assert_eq!(got, want, "TupleHash128 fixed, {len} bytes");
888
889                reference_tuple_hash(true, custom, elements, &mut want, false);
890                TupleHash256::hash(custom, elements, &mut got);
891                assert_eq!(got, want, "TupleHash256 fixed, {len} bytes");
892
893                reference_tuple_hash(false, custom, elements, &mut want, true);
894                TupleHash128::hash_xof(custom, elements, &mut got);
895                assert_eq!(got, want, "TupleHash128 xof, {len} bytes");
896            }
897        }
898    }
899
900    /// The reason TupleHash exists. Concatenation cannot tell these apart;
901    /// TupleHash must.
902    #[test]
903    fn tuples_that_concatenate_alike_hash_differently() {
904        let splits: &[&[&[u8]]] = &[
905            &[b"abc", b"d"],
906            &[b"ab", b"cd"],
907            &[b"a", b"bcd"],
908            &[b"abcd"],
909            &[b"abcd", b""],
910            &[b"", b"abcd"],
911        ];
912
913        let mut seen: Vec<[u8; 32]> = Vec::new();
914        for elements in splits {
915            let mut out = [0u8; 32];
916            TupleHash128::hash(b"", elements, &mut out);
917            assert!(
918                !seen.contains(&out),
919                "two different tuples collided: {elements:?}"
920            );
921            seen.push(out);
922        }
923    }
924
925    #[test]
926    fn tuple_hash_streams() {
927        let mut one = [0u8; 32];
928        TupleHash128::hash(b"S", &[b"alpha", b"beta", b"gamma"], &mut one);
929
930        let mut t = TupleHash128::new(b"S");
931        t.update(b"alpha");
932        t.update(b"beta");
933        t.update(b"gamma");
934        let mut streamed = [0u8; 32];
935        t.finalize(&mut streamed);
936        assert_eq!(one, streamed);
937    }
938
939    #[test]
940    fn parallel_hash_matches_an_independent_construction() {
941        let data = [0x37u8; 500];
942        for block_size in [1usize, 8, 32, 137, 500, 1024] {
943            for len in [16usize, 32, 64] {
944                let mut want = vec![0u8; len];
945                let mut got = vec![0u8; len];
946
947                reference_parallel_hash(false, b"S", block_size, &data, &mut want, false);
948                ParallelHash128::hash(b"S", block_size, &data, &mut got);
949                assert_eq!(got, want, "ParallelHash128 B={block_size}, {len} bytes");
950
951                reference_parallel_hash(true, b"", block_size, &data, &mut want, false);
952                ParallelHash256::hash(b"", block_size, &data, &mut got);
953                assert_eq!(got, want, "ParallelHash256 B={block_size}, {len} bytes");
954
955                reference_parallel_hash(false, b"S", block_size, &data, &mut want, true);
956                ParallelHash128::hash_xof(b"S", block_size, &data, &mut got);
957                assert_eq!(got, want, "ParallelHash128 xof B={block_size}");
958            }
959        }
960    }
961
962    /// An empty input still has a well-defined answer: zero blocks.
963    #[test]
964    fn parallel_hash_handles_an_empty_input() {
965        let mut want = [0u8; 32];
966        let mut got = [0u8; 32];
967        reference_parallel_hash(false, b"", 64, b"", &mut want, false);
968        ParallelHash128::hash(b"", 64, b"", &mut got);
969        assert_eq!(got, want);
970    }
971
972    /// The block size is part of the computation, not a performance knob.
973    #[test]
974    fn the_block_size_changes_the_result() {
975        let data = [0xa1u8; 256];
976        let mut a = [0u8; 32];
977        let mut b = [0u8; 32];
978        ParallelHash128::hash(b"", 32, &data, &mut a);
979        ParallelHash128::hash(b"", 64, &data, &mut b);
980        assert_ne!(a, b, "B is bound into the output");
981    }
982
983    #[test]
984    fn output_length_is_bound_in_for_both() {
985        let mut short = [0u8; 32];
986        let mut long = [0u8; 64];
987        TupleHash128::hash(b"", &[b"x"], &mut short);
988        TupleHash128::hash(b"", &[b"x"], &mut long);
989        assert_ne!(short[..], long[..32], "TupleHash binds L");
990
991        ParallelHash128::hash(b"", 32, b"x", &mut short);
992        ParallelHash128::hash(b"", 32, b"x", &mut long);
993        assert_ne!(short[..], long[..32], "ParallelHash binds L");
994
995        // The XOF forms, by contrast, extend.
996        TupleHash128::hash_xof(b"", &[b"x"], &mut short);
997        TupleHash128::hash_xof(b"", &[b"x"], &mut long);
998        assert_eq!(short[..], long[..32], "the xof form is a stream");
999    }
1000
1001    #[test]
1002    fn every_self_test_passes() {
1003        TupleHash128::self_test().unwrap();
1004        TupleHash256::self_test().unwrap();
1005        ParallelHash128::self_test().unwrap();
1006        ParallelHash256::self_test().unwrap();
1007    }
1008}