Skip to main content

bouncycastle_sha3/
keccak.rs

1use bouncycastle_core::errors::{HashError, SuspendableError};
2use bouncycastle_core::key_material::KeyType;
3use bouncycastle_core::traits::SecurityStrength;
4use bouncycastle_utils::secret::Secret;
5
6const KECCAK_ROUND_CONSTANTS: [u64; 24] = [
7    0x0000000000000001, 0x0000000000008082, 0x800000000000808A, 0x8000000080008000,
8    0x000000000000808B, 0x0000000080000001, 0x8000000080008081, 0x8000000000008009,
9    0x000000000000008A, 0x0000000000000088, 0x0000000080008009, 0x000000008000000A,
10    0x000000008000808B, 0x800000000000008B, 0x8000000000008089, 0x8000000000008003,
11    0x8000000000008002, 0x8000000000000080, 0x000000000000800A, 0x800000008000000A,
12    0x8000000080008081, 0x8000000000008080, 0x0000000080000001, 0x8000000080008008,
13];
14
15#[derive(Clone)]
16pub(crate) struct KeccakState {
17    buf: Secret<[u64; 25]>,
18    rate: usize,
19}
20
21impl KeccakState {
22    fn new(rate: usize) -> Self {
23        Self { buf: Secret::new(), rate }
24    }
25
26    fn absorb(&mut self, data: &[u8]) {
27        let count = self.rate >> 6;
28        for i in 0..count {
29            let mut tmp = [0u8; 8];
30            tmp.copy_from_slice(&data[i * 8..(i + 1) * 8]);
31            self.buf[i] ^= u64::from_le_bytes(tmp);
32        }
33
34        Self::permute(self);
35    }
36
37    pub fn permute(a: &mut Self) {
38        let [
39            mut a00,
40            mut a01,
41            mut a02,
42            mut a03,
43            mut a04,
44            mut a05,
45            mut a06,
46            mut a07,
47            mut a08,
48            mut a09,
49            mut a10,
50            mut a11,
51            mut a12,
52            mut a13,
53            mut a14,
54            mut a15,
55            mut a16,
56            mut a17,
57            mut a18,
58            mut a19,
59            mut a20,
60            mut a21,
61            mut a22,
62            mut a23,
63            mut a24,
64        ] = *a.buf;
65
66        for round_constant in KECCAK_ROUND_CONSTANTS {
67            // theta
68            let mut c0 = a00 ^ a05 ^ a10 ^ a15 ^ a20;
69            let mut c1 = a01 ^ a06 ^ a11 ^ a16 ^ a21;
70            let c2 = a02 ^ a07 ^ a12 ^ a17 ^ a22;
71            let c3 = a03 ^ a08 ^ a13 ^ a18 ^ a23;
72            let c4 = a04 ^ a09 ^ a14 ^ a19 ^ a24;
73
74            let d0 = c0.rotate_left(1) ^ c3;
75            let d1 = c1.rotate_left(1) ^ c4;
76            let d2 = c2.rotate_left(1) ^ c0;
77            let d3 = c3.rotate_left(1) ^ c1;
78            let d4 = c4.rotate_left(1) ^ c2;
79
80            a00 ^= d1;
81            a05 ^= d1;
82            a10 ^= d1;
83            a15 ^= d1;
84            a20 ^= d1;
85            a01 ^= d2;
86            a06 ^= d2;
87            a11 ^= d2;
88            a16 ^= d2;
89            a21 ^= d2;
90            a02 ^= d3;
91            a07 ^= d3;
92            a12 ^= d3;
93            a17 ^= d3;
94            a22 ^= d3;
95            a03 ^= d4;
96            a08 ^= d4;
97            a13 ^= d4;
98            a18 ^= d4;
99            a23 ^= d4;
100            a04 ^= d0;
101            a09 ^= d0;
102            a14 ^= d0;
103            a19 ^= d0;
104            a24 ^= d0;
105
106            // rho/pi
107            c1 = a01.rotate_left(1);
108            a01 = a06.rotate_left(44);
109            a06 = a09.rotate_left(20);
110            a09 = a22.rotate_left(61);
111            a22 = a14.rotate_left(39);
112            a14 = a20.rotate_left(18);
113            a20 = a02.rotate_left(62);
114            a02 = a12.rotate_left(43);
115            a12 = a13.rotate_left(25);
116            a13 = a19.rotate_left(8);
117            a19 = a23.rotate_left(56);
118            a23 = a15.rotate_left(41);
119            a15 = a04.rotate_left(27);
120            a04 = a24.rotate_left(14);
121            a24 = a21.rotate_left(2);
122            a21 = a08.rotate_left(55);
123            a08 = a16.rotate_left(45);
124            a16 = a05.rotate_left(36);
125            a05 = a03.rotate_left(28);
126            a03 = a18.rotate_left(21);
127            a18 = a17.rotate_left(15);
128            a17 = a11.rotate_left(10);
129            a11 = a07.rotate_left(6);
130            a07 = a10.rotate_left(3);
131            a10 = c1;
132
133            // chi
134            c0 = a00 ^ (!a01 & a02);
135            c1 = a01 ^ (!a02 & a03);
136            a02 ^= !a03 & a04;
137            a03 ^= !a04 & a00;
138            a04 ^= !a00 & a01;
139            a00 = c0;
140            a01 = c1;
141
142            c0 = a05 ^ (!a06 & a07);
143            c1 = a06 ^ (!a07 & a08);
144            a07 ^= !a08 & a09;
145            a08 ^= !a09 & a05;
146            a09 ^= !a05 & a06;
147            a05 = c0;
148            a06 = c1;
149
150            c0 = a10 ^ (!a11 & a12);
151            c1 = a11 ^ (!a12 & a13);
152            a12 ^= !a13 & a14;
153            a13 ^= !a14 & a10;
154            a14 ^= !a10 & a11;
155            a10 = c0;
156            a11 = c1;
157
158            c0 = a15 ^ (!a16 & a17);
159            c1 = a16 ^ (!a17 & a18);
160            a17 ^= !a18 & a19;
161            a18 ^= !a19 & a15;
162            a19 ^= !a15 & a16;
163            a15 = c0;
164            a16 = c1;
165
166            c0 = a20 ^ (!a21 & a22);
167            c1 = a21 ^ (!a22 & a23);
168            a22 ^= !a23 & a24;
169            a23 ^= !a24 & a20;
170            a24 ^= !a20 & a21;
171            a20 = c0;
172            a21 = c1;
173
174            // iota
175            a00 ^= round_constant;
176        }
177
178        *a.buf = [
179            a00, a01, a02, a03, a04, a05, a06, a07, a08, a09, a10, a11, a12, a13, a14, a15, a16,
180            a17, a18, a19, a20, a21, a22, a23, a24,
181        ];
182    }
183}
184
185// This is pub(crate) so that the SerializableState handlers can unpack it.
186#[derive(Clone)]
187pub(crate) struct KeccakInternal {
188    state: KeccakState,
189    pub data_queue: Secret<[u8; 192]>,
190    rate: usize,
191    pub bits_in_queue: usize,
192    pub squeezing: bool,
193}
194
195#[derive(Clone)]
196pub(crate) enum KeccakSize {
197    _128 = 128,
198    _224 = 224,
199    _256 = 256,
200    _288 = 288,
201    _384 = 384,
202    _512 = 512,
203}
204
205impl KeccakInternal {
206    pub(super) fn new(size: KeccakSize) -> Self {
207        let rate = 1600 - ((size as usize) << 1);
208
209        Self {
210            state: KeccakState::new(rate),
211            data_queue: Secret::new(),
212            rate,
213            bits_in_queue: 0,
214            squeezing: false,
215        }
216    }
217
218    /// Absorbs `data` (whole bytes only) into the sponge.
219    ///
220    /// # Contract
221    /// This private function has preconditions to entry that the caller must uphold:
222    /// ## `bits_in_queue` is byte-aligned (a multiple of 8) on entry.
223    /// currently all call sites within the crate respect this, and there are unit tests to trigger
224    /// the embedded debug_assert for existing call sites, but any new call sites to this MUST
225    /// respect this precondition. If we ever open up the [`KeccakInternal`] object to be called publicly,
226    /// then we'll have to add proper error-handling here.
227    ///
228    /// ## No absorbing after squeezing
229    /// The prohibition on absorbing more input after squeezing is only technically enforced
230    /// for SHAKE and not for KECCAK (and only because FIPS 202 Section 6.2 places a four-bit suffix
231    /// "1111" after the last byte of input). There is a debug_assert to catch this, but we could
232    /// in theory expose the Keccak primitive externally in the future and relax this restriction,
233    /// but some more careful reading of FIPS 202 and some thinking about error and security handling
234    /// would need to be done.
235    pub(super) fn absorb(&mut self, data: &[u8]) {
236        // Debug-only backstops for the two contract preconditions; see the doc comment above. Both are
237        // enforced for real by the SHA3 / SHAKE callers within these crates, so these asserts
238        // _should_ be unreachable.
239        debug_assert!(self.bits_in_queue & 7 == 0, "attempt to absorb with odd length queue");
240        debug_assert!(!self.squeezing, "attempt to absorb while squeezing");
241
242        for byte in data {
243            self.data_queue[self.bits_in_queue >> 3] = *byte;
244            self.bits_in_queue += 8;
245
246            if self.bits_in_queue == self.rate {
247                self.state.absorb(&*self.data_queue);
248                self.bits_in_queue = 0;
249            }
250        }
251    }
252
253    pub(super) fn absorb_bits(&mut self, data: u8, bits: usize) -> Result<(), HashError> {
254        if bits == 0 {
255            return Ok(());
256        }
257        if !(1..=7).contains(&bits) {
258            return Err(HashError::InvalidLength("bits must be in the range 1 to 7"));
259        }
260        if (self.bits_in_queue & 7) != 0 {
261            return Err(HashError::InvalidState("attempt to absorb with odd length queue"));
262        }
263        if self.squeezing {
264            return Err(HashError::InvalidState("attempt to absorb while squeezing"));
265        }
266
267        let mask = (1 << bits) - 1;
268        self.data_queue[self.bits_in_queue >> 3] = data & mask;
269
270        // NOTE: After this, bits_in_queue is no longer a multiple of 8, so no more absorbs will work
271        self.bits_in_queue += bits;
272        self.pad_and_switch_to_squeezing_phase();
273        Ok(())
274    }
275
276    /// Panics if the output buffer is too small.
277    /// Returns the number of bytes written.
278    pub(super) fn squeeze(&mut self, out: &mut [u8]) -> usize {
279        out.fill(0);
280
281        if !self.squeezing {
282            self.pad_and_switch_to_squeezing_phase();
283        }
284        let output_length = out.len() << 3;
285
286        let mut i = 0;
287        while i < output_length {
288            if self.bits_in_queue == 0 {
289                self.keccak_extract();
290            }
291            let partial_block = self.bits_in_queue.min(output_length - i);
292
293            let length = partial_block >> 3;
294            let start_data_queue = (self.rate - self.bits_in_queue) >> 3;
295            let start_output = i >> 3;
296            out[start_output..(start_output + length)]
297                .copy_from_slice(&self.data_queue[start_data_queue..(start_data_queue + length)]);
298
299            self.bits_in_queue -= partial_block;
300            i += partial_block;
301        }
302        output_length >> 3
303    }
304
305    #[inline(always)]
306    fn keccak_extract(&mut self) {
307        KeccakState::permute(&mut self.state);
308
309        let (chunks, _) = self.data_queue.as_chunks_mut::<8>();
310
311        for (i, chunk) in chunks.iter_mut().enumerate() {
312            *chunk = self.state.buf[i].to_le_bytes();
313        }
314
315        self.bits_in_queue = self.rate;
316    }
317
318    pub(super) fn pad_and_switch_to_squeezing_phase(&mut self) {
319        debug_assert!(self.bits_in_queue < self.rate);
320        self.data_queue[self.bits_in_queue >> 3] |= (1 << (self.bits_in_queue & 7)) as u8;
321
322        self.bits_in_queue += 1;
323        if self.bits_in_queue == self.rate {
324            self.state.absorb(&*self.data_queue);
325        } else {
326            let full = self.bits_in_queue >> 6;
327            let partial = self.bits_in_queue & 63;
328            let mut off = 0;
329
330            for i in 0..full {
331                let mut tmp = [0u8; 8];
332                tmp.copy_from_slice(&self.data_queue[off..off + 8]);
333                self.state.buf[i] ^= u64::from_le_bytes(tmp);
334                off += 8;
335            }
336
337            let mask = (1 << partial) - 1;
338
339            let mut tmp = [0u8; 8];
340            tmp.copy_from_slice(&self.data_queue[off..off + 8]);
341            self.state.buf[full] ^= u64::from_le_bytes(tmp) & mask;
342        }
343
344        self.state.buf[(self.rate - 1) >> 6] ^= 1 << 63;
345
346        self.bits_in_queue = 0;
347        self.squeezing = true;
348    }
349}
350
351/*** State serialization ***/
352//
353// The SHA3 and SHAKE public objects have identical state: a [KeccakDigest] plus three pieces of
354// KDF metadata. The helpers below serialize that shared state so the `SerializableState` impls in
355// `sha3.rs` and `shake.rs` are just thin wrappers that add/check the library version header.
356
357/// Number of bytes needed to serialize a [`KeccakInternal`]'s mutable state.
358///
359/// The `rate` is intentionally NOT serialized: it is fully determined by the SHA3/SHAKE variant and
360/// is re-supplied at deserialization time (see [`KeccakInternal::from_serialized_state`]).
361///
362/// Layout (all integers little-endian):
363///   [0   .. 200)  state.buf     [u64; 25]
364///   [200 .. 392)  data_queue    [u8; 192]
365///   [392 .. 400)  bits_in_queue usize serialized as u64
366///   [400 .. 401)  squeezing     bool  (0 or 1)
367const KECCAK_SERIALIZED_LEN: usize = 200 + 192 + 8 + 1;
368
369/// Number of bytes needed to serialize the shared SHA3-family state (a variant tag, a [`KeccakInternal`],
370/// plus the three KDF metadata fields), excluding the library version header.
371///
372/// The leading variant tag distinguishes every SHA3/SHAKE variant — crucially including same-rate
373/// pairs such as SHA3-256 and SHAKE256 — so a serialized state can never be deserialized into a
374/// different algorithm (which would silently apply the wrong domain separation).
375///
376/// Layout (all integers little-endian):
377///   [0 .. 1)                              variant tag           (see `STATE_TAG` on the param traits)
378///   [1 .. 1 + KECCAK_SERIALIZED_LEN)      keccak digest state
379///   [.. + 1)                              kdf_key_type          (1 byte enum tag)
380///   [.. + 1)                              kdf_security_strength (1 byte enum tag)
381///   [.. + 8)                              kdf_entropy           usize serialized as u64
382pub(crate) const SHA3_FAMILY_STATE_LEN: usize = 1 + KECCAK_SERIALIZED_LEN + 10;
383
384/// Length in bytes of the serialized state of a SHA3 or SHAKE instance.
385pub const SUSPENDED_SHA3_STATE_LEN: usize = 3 + SHA3_FAMILY_STATE_LEN;
386
387impl KeccakInternal {
388    /// Serializes this digest's mutable state into `out`. The `rate` is deliberately omitted; see
389    /// [`KECCAK_SERIALIZED_LEN`].
390    fn serialize_state(&self, out: &mut [u8; KECCAK_SERIALIZED_LEN]) {
391        // state.buf: [u64; 25]
392        for i in 0..25 {
393            out[i * 8..(i * 8) + 8].copy_from_slice(&self.state.buf[i].to_le_bytes());
394        }
395
396        // data_queue: [u8; 192]
397        out[200..392].copy_from_slice(&*self.data_queue);
398
399        // bits_in_queue: usize
400        out[392..400].copy_from_slice(&(self.bits_in_queue as u64).to_le_bytes());
401
402        // squeezing: bool
403        out[400] = self.squeezing as u8;
404    }
405
406    /// Reconstructs a [`KeccakInternal`] from a state produced by [`KeccakInternal::serialize_state`].
407    ///
408    /// `rate` is supplied by the caller (derived from its algorithm parameters) rather than read
409    /// from the serialized bytes, since the rate is fully determined by the SHA3/SHAKE variant. The
410    /// caller is responsible for having already verified the variant tag so that this `rate` is the
411    /// correct one for the serialized state.
412    fn from_serialized_state(
413        input: &[u8; KECCAK_SERIALIZED_LEN],
414        rate: usize,
415    ) -> Result<Self, SuspendableError> {
416        // state.buf: [u64; 25]
417        let mut buf = Secret::<[u64; 25]>::new();
418        for i in 0..25 {
419            buf[i] = u64::from_le_bytes(input[i * 8..(i * 8) + 8].try_into().unwrap());
420        }
421
422        // data_queue: [u8; 192]
423        let mut data_queue = Secret::<[u8; 192]>::new();
424        data_queue.copy_from_slice(&input[200..392]);
425
426        // bits_in_queue: usize.
427        // In a legitimate state it is always a multiple of 8 AND strictly less
428        // than the rate: absorb() only enqueues whole bytes, and a sub-byte queue exists only
429        // transiently inside absorb_bits(), which pads and switches to squeezing before returning.
430        // So here we reject unaligned values (ie where bits_in_queue is not a multiple of 8)
431        let bits_in_queue = u64::from_le_bytes(input[392..400].try_into().unwrap()) as usize;
432        if bits_in_queue >= rate || (bits_in_queue & 7) != 0 {
433            return Err(SuspendableError::InvalidData);
434        }
435
436        // squeezing: bool
437        let squeezing = match input[400] {
438            0 => false,
439            1 => true,
440            _ => return Err(SuspendableError::InvalidData),
441        };
442
443        Ok(Self { state: KeccakState { buf, rate }, data_queue, rate, bits_in_queue, squeezing })
444    }
445}
446
447/// Serializes the state shared by all SHA3-family objects (the `variant_tag`, a [`KeccakInternal`], plus
448/// the three KDF metadata fields) into `out`. See [`SHA3_FAMILY_STATE_LEN`] for the layout.
449pub(crate) fn serialize_sha3_family_state(
450    out: &mut [u8; SHA3_FAMILY_STATE_LEN],
451    variant_tag: u8,
452    keccak: &KeccakInternal,
453    kdf_key_type: KeyType,
454    kdf_security_strength: SecurityStrength,
455    kdf_entropy: usize,
456) {
457    out[0] = variant_tag;
458
459    let keccak_out: &mut [u8; KECCAK_SERIALIZED_LEN] =
460        (&mut out[1..1 + KECCAK_SERIALIZED_LEN]).try_into().unwrap();
461    keccak.serialize_state(keccak_out);
462
463    out[1 + KECCAK_SERIALIZED_LEN] = kdf_key_type as u8;
464    out[1 + KECCAK_SERIALIZED_LEN + 1] = kdf_security_strength as u8;
465    out[1 + KECCAK_SERIALIZED_LEN + 2..1 + KECCAK_SERIALIZED_LEN + 10]
466        .copy_from_slice(&(kdf_entropy as u64).to_le_bytes());
467}
468
469/// Reconstructs the shared SHA3-family state from a buffer produced by [`serialize_sha3_family_state`].
470///
471/// `expected_variant_tag` and `rate` are both derived from the caller's algorithm parameters. The
472/// tag is checked against the serialized one first: this is what prevents a state from one variant
473/// being loaded into another (e.g. SHA3-256 vs SHAKE256, which share a rate but differ in domain
474/// separation). Only once the tag matches is `rate` guaranteed to be the correct one to rebuild with.
475pub(crate) fn deserialize_sha3_family_state(
476    input: &[u8; SHA3_FAMILY_STATE_LEN],
477    expected_variant_tag: u8,
478    rate: usize,
479) -> Result<(KeccakInternal, KeyType, SecurityStrength, usize), SuspendableError> {
480    if input[0] != expected_variant_tag {
481        return Err(SuspendableError::InvalidData);
482    }
483
484    let keccak_in: &[u8; KECCAK_SERIALIZED_LEN] =
485        input[1..1 + KECCAK_SERIALIZED_LEN].try_into().unwrap();
486    let keccak = KeccakInternal::from_serialized_state(keccak_in, rate)?;
487
488    // KeyType and SecurityStrength each own their canonical 1-byte encoding (`as u8` / `TryFrom<u8>`).
489    let kdf_key_type = KeyType::try_from(input[1 + KECCAK_SERIALIZED_LEN])?;
490    let kdf_security_strength = SecurityStrength::try_from(input[1 + KECCAK_SERIALIZED_LEN + 1])?;
491    let kdf_entropy = u64::from_le_bytes(
492        input[1 + KECCAK_SERIALIZED_LEN + 2..1 + KECCAK_SERIALIZED_LEN + 10].try_into().unwrap(),
493    ) as usize;
494
495    Ok((keccak, kdf_key_type, kdf_security_strength, kdf_entropy))
496}
497
498#[cfg(test)]
499mod keccak_tests {
500    use super::*;
501    use bouncycastle_hex as hex;
502
503    #[test]
504    fn test_keccak() {
505        let mut d = KeccakInternal::new(KeccakSize::_256);
506        let m_vec = hex::decode("6d657373616765").unwrap();
507        d.absorb(&m_vec);
508
509        let mut out = [0u8; 32];
510        d.squeeze(&mut out);
511        println!("n1: {:x?}", &out);
512
513        d.squeeze(&mut out);
514        println!("n2: {:x?}", &out);
515    }
516
517    /// Regression test for from_serialized_state's validation of a not-yet-squeezing queue: a corrupt
518    /// state whose bits_in_queue is not byte-aligned, or equals/exceeds the rate, must be rejected as
519    /// InvalidData rather than deserialized into a value that later trips the debug_assert in absorb()
520    /// or the pad_and_switch_to_squeezing_phase() invariant.
521    #[test]
522    fn from_serialized_state_rejects_corrupt_bits_in_queue() {
523        let rate = 1600 - ((KeccakSize::_256 as usize) << 1);
524
525        // A valid, mid-absorb (not squeezing) serialized state.
526        let mut d = KeccakInternal::new(KeccakSize::_256);
527        d.absorb(b"message");
528        assert!(!d.squeezing);
529        let mut good = [0u8; KECCAK_SERIALIZED_LEN];
530        d.serialize_state(&mut good);
531        assert!(KeccakInternal::from_serialized_state(&good, rate).is_ok());
532
533        // bits_in_queue lives at [392..400) as a little-endian u64 with squeezing at [400].
534        assert_eq!(good[400], 0, "test setup expects a non-squeezing state");
535        let set_biq = |state: &mut [u8; KECCAK_SERIALIZED_LEN], v: u64| {
536            state[392..400].copy_from_slice(&v.to_le_bytes());
537        };
538
539        // > rate -> rejected.
540        let mut corrupt = good;
541        set_biq(&mut corrupt, rate as u64 + 8);
542        assert!(matches!(
543            KeccakInternal::from_serialized_state(&corrupt, rate),
544            Err(SuspendableError::InvalidData)
545        ));
546
547        // == rate while not squeezing -> rejected (would trip the pad_and_switch debug_assert).
548        let mut corrupt = good;
549        set_biq(&mut corrupt, rate as u64);
550        assert!(matches!(
551            KeccakInternal::from_serialized_state(&corrupt, rate),
552            Err(SuspendableError::InvalidData)
553        ));
554
555        // Non byte-aligned number of bits in queue -> rejected
556        // (meaning a bits_in_queue which is not a multiple of 8, and only allowed during the absorb_bits,
557        // which may not be called outside of absorb_last_partial_byte(), so is not a valid suspendable state)
558        let mut corrupt = good;
559        set_biq(&mut corrupt, 9);
560        assert!(matches!(
561            KeccakInternal::from_serialized_state(&corrupt, rate),
562            Err(SuspendableError::InvalidData)
563        ));
564    }
565}