1use dcrypt_internal::zeroing::{
11 boxed_bytes_zeroed, Zeroize, ZeroizeOnDrop, Zeroizing, ZeroizingBytes,
12};
13
14use super::ExtendableOutputFunction;
15use crate::error::{validate, Error, Result};
16
17use dcrypt_common::security::{barrier, EphemeralSecret, SecretBuffer, SecureZeroingType};
19
20const KECCAK_ROUNDS: usize = 24;
22const KECCAK_STATE_SIZE: usize = 25; const SHAKE128_RATE: usize = 168; const SHAKE256_RATE: usize = 136; const RC: [u64; KECCAK_ROUNDS] = [
30 0x0000000000000001,
31 0x0000000000008082,
32 0x800000000000808A,
33 0x8000000080008000,
34 0x000000000000808B,
35 0x0000000080000001,
36 0x8000000080008081,
37 0x8000000000008009,
38 0x000000000000008A,
39 0x0000000000000088,
40 0x0000000080008009,
41 0x000000008000000A,
42 0x000000008000808B,
43 0x800000000000008B,
44 0x8000000000008089,
45 0x8000000000008003,
46 0x8000000000008002,
47 0x8000000000000080,
48 0x000000000000800A,
49 0x800000008000000A,
50 0x8000000080008081,
51 0x8000000000008080,
52 0x0000000080000001,
53 0x8000000080008008,
54];
55
56const RHO: [u32; 24] = [
58 1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 2, 14, 27, 41, 56, 8, 25, 43, 62, 18, 39, 61, 20, 44,
59];
60
61const PI: [usize; 24] = [
63 10, 7, 11, 17, 18, 3, 5, 16, 8, 21, 24, 4, 15, 23, 19, 13, 12, 2, 20, 14, 22, 9, 6, 1,
64];
65
66#[derive(Clone)]
68struct SecureKeccakState {
69 state: SecretBuffer<200>, }
71
72impl Zeroize for SecureKeccakState {
73 fn zeroize(&mut self) {
74 self.state.zeroize();
75 }
76}
77
78impl Drop for SecureKeccakState {
79 fn drop(&mut self) {
80 self.zeroize();
81 }
82}
83
84impl ZeroizeOnDrop for SecureKeccakState {}
85
86impl SecureKeccakState {
87 fn new() -> Self {
88 Self {
89 state: SecretBuffer::zeroed(),
90 }
91 }
92
93 fn from_u64_array(array: &[u64; KECCAK_STATE_SIZE]) -> Self {
94 let mut state = SecretBuffer::<200>::zeroed();
95 for (i, word) in array.iter().enumerate() {
96 for byte in 0..8 {
97 state.as_mut()[i * 8 + byte] = (word >> (byte * 8)) as u8;
98 }
99 }
100 Self { state }
101 }
102
103 fn to_u64_array(&self) -> Zeroizing<[u64; KECCAK_STATE_SIZE]> {
104 let mut array = Zeroizing::new([0u64; KECCAK_STATE_SIZE]);
105 let bytes = self.state.as_ref();
106 for (i, word) in array.iter_mut().enumerate() {
107 let start = i * 8;
108 for byte in 0..8 {
109 *word |= (bytes[start + byte] as u64) << (byte * 8);
110 }
111 }
112 array
113 }
114
115 fn apply_permutation(&mut self) {
116 let mut state_array = self.to_u64_array();
117 keccak_f1600(&mut state_array);
118 *self = Self::from_u64_array(&state_array);
119 }
120}
121
122impl SecureZeroingType for SecureKeccakState {
123 fn zeroed() -> Self {
124 Self::new()
125 }
126
127 fn secure_clone(&self) -> Self {
128 Self {
129 state: self.state.secure_clone(),
130 }
131 }
132}
133
134#[derive(Clone)]
136pub struct ShakeXof128 {
137 state: SecureKeccakState,
138 buffer: SecretBuffer<SHAKE128_RATE>,
139 buffer_idx: usize,
140 is_finalized: bool,
141 squeezing: bool,
142}
143
144impl Zeroize for ShakeXof128 {
145 fn zeroize(&mut self) {
146 self.state.zeroize();
147 self.buffer.zeroize();
148 self.buffer_idx.zeroize();
149 self.is_finalized = false;
150 self.squeezing = false;
151 }
152}
153
154impl Drop for ShakeXof128 {
155 fn drop(&mut self) {
156 self.zeroize();
157 }
158}
159
160impl ZeroizeOnDrop for ShakeXof128 {}
161
162#[derive(Clone)]
164pub struct ShakeXof256 {
165 state: SecureKeccakState,
166 buffer: SecretBuffer<SHAKE256_RATE>,
167 buffer_idx: usize,
168 is_finalized: bool,
169 squeezing: bool,
170}
171
172impl Zeroize for ShakeXof256 {
173 fn zeroize(&mut self) {
174 self.state.zeroize();
175 self.buffer.zeroize();
176 self.buffer_idx.zeroize();
177 self.is_finalized = false;
178 self.squeezing = false;
179 }
180}
181
182impl Drop for ShakeXof256 {
183 fn drop(&mut self) {
184 self.zeroize();
185 }
186}
187
188impl ZeroizeOnDrop for ShakeXof256 {}
189
190fn keccak_f1600(state: &mut [u64; KECCAK_STATE_SIZE]) {
194 for (_round, &rc) in RC.iter().enumerate().take(KECCAK_ROUNDS) {
196 let mut c = EphemeralSecret::new([0u64; 5]);
198 for x in 0..5 {
199 c.as_mut()[x] = state[x] ^ state[x + 5] ^ state[x + 10] ^ state[x + 15] ^ state[x + 20];
200 }
201
202 let mut d = EphemeralSecret::new([0u64; 5]);
203 for x in 0..5 {
204 d.as_mut()[x] = c.as_ref()[(x + 4) % 5] ^ c.as_ref()[(x + 1) % 5].rotate_left(1);
205 }
206
207 for y in 0..5 {
208 for x in 0..5 {
209 state[x + 5 * y] ^= d.as_ref()[x];
210 }
211 }
212
213 let mut b = EphemeralSecret::new([0u64; KECCAK_STATE_SIZE]);
215 let mut x = 1;
216 let mut y = 0;
217 b.as_mut()[0] = state[0];
218
219 for i in 0..24 {
220 let idx = x + 5 * y;
221 b.as_mut()[PI[i]] = state[idx].rotate_left(RHO[i]);
222 let temp = y;
223 y = (2 * x + 3 * y) % 5;
224 x = temp;
225 }
226
227 for y in 0..5 {
229 for x in 0..5 {
230 let idx = x + 5 * y;
231 state[idx] = b.as_ref()[idx]
232 ^ ((!b.as_ref()[(x + 1) % 5 + 5 * y]) & b.as_ref()[(x + 2) % 5 + 5 * y]);
233 }
234 }
235
236 state[0] ^= rc;
238 }
239
240 barrier::compiler_fence_seq_cst();
242}
243
244fn keccak_absorb(state: &mut SecureKeccakState, data: &[u8], rate: usize) {
246 let mut state_array = state.to_u64_array();
248
249 for (i, &byte) in data.iter().enumerate() {
250 let pos = i % rate;
251 let byte_idx = pos % 8;
252 let word_idx = pos / 8;
253 state_array[word_idx] ^= (byte as u64) << (8 * byte_idx);
254 }
255
256 if !data.is_empty() && data.len() % rate == 0 {
257 keccak_f1600(&mut state_array);
258 }
259
260 *state = SecureKeccakState::from_u64_array(&state_array);
262}
263
264fn validate_bit_string(data: &[u8], bit_len: usize) -> Result<(usize, usize)> {
265 let rounded_len = bit_len
266 .checked_add(7)
267 .ok_or_else(|| Error::param("bit_length", "Bit length is too large"))?
268 / 8;
269 validate::length("bit-oriented SHAKE input", data.len(), rounded_len)?;
270
271 let partial_bits = bit_len % 8;
272 if partial_bits != 0 {
273 let unused_mask = (1u8 << (8 - partial_bits)) - 1;
274 validate::parameter(
275 data[rounded_len - 1] & unused_mask == 0,
276 "data",
277 "Unused low bits in the final byte must be zero",
278 )?;
279 }
280
281 Ok((bit_len / 8, partial_bits))
282}
283
284fn append_bit<const RATE: usize>(
285 state: &mut SecureKeccakState,
286 block: &mut SecretBuffer<RATE>,
287 bit_pos: &mut usize,
288 bit: u8,
289) {
290 block.as_mut()[*bit_pos / 8] ^= (bit & 1) << (*bit_pos % 8);
291 *bit_pos += 1;
292 if *bit_pos == RATE * 8 {
293 keccak_absorb(state, block.as_ref(), RATE);
294 block.zeroize();
295 *bit_pos = 0;
296 }
297}
298
299fn finalize_bit_string<const RATE: usize>(
300 state: &mut SecureKeccakState,
301 buffered: &SecretBuffer<RATE>,
302 buffer_idx: usize,
303 mut partial_byte: u8,
304 partial_bits: usize,
305) {
306 let mut block = SecretBuffer::<RATE>::zeroed();
307 block.as_mut()[..buffer_idx].copy_from_slice(&buffered.as_ref()[..buffer_idx]);
308 let mut bit_pos = buffer_idx * 8;
309
310 for i in 0..partial_bits {
311 append_bit(state, &mut block, &mut bit_pos, (partial_byte >> i) & 1);
312 }
313 partial_byte.zeroize();
314
315 for i in 0..5 {
318 append_bit(state, &mut block, &mut bit_pos, (0x1f >> i) & 1);
319 }
320
321 block.as_mut()[RATE - 1] ^= 0x80;
323 keccak_absorb(state, block.as_ref(), RATE);
324}
325
326impl ShakeXof128 {
327 fn init() -> Self {
328 ShakeXof128 {
329 state: SecureKeccakState::new(),
330 buffer: SecretBuffer::zeroed(),
331 buffer_idx: 0,
332 is_finalized: false,
333 squeezing: false,
334 }
335 }
336
337 fn finalize_bits_internal(&mut self, partial_byte: u8, partial_bits: usize) -> Result<()> {
338 validate::parameter(
339 !self.is_finalized,
340 "state",
341 "SHAKE input has already been finalized",
342 )?;
343 validate::parameter(
344 !self.squeezing,
345 "state",
346 "SHAKE output is already being squeezed",
347 )?;
348 finalize_bit_string(
349 &mut self.state,
350 &self.buffer,
351 self.buffer_idx,
352 partial_byte,
353 partial_bits,
354 );
355 self.buffer.zeroize();
356 self.buffer_idx = 0;
357 self.is_finalized = true;
358 Ok(())
359 }
360
361 #[doc(hidden)]
365 pub fn generate_bits(data: &[u8], bit_len: usize, output_len: usize) -> Result<ZeroizingBytes> {
366 validate::parameter(
367 output_len > 0,
368 "output_length",
369 "XOF output length must be greater than 0",
370 )?;
371 let (full_bytes, partial_bits) = validate_bit_string(data, bit_len)?;
372 let mut xof = Self::init();
373 xof.update(&data[..full_bytes])?;
374 let mut partial_byte = if partial_bits == 0 {
375 0
376 } else {
377 data[full_bytes] >> (8 - partial_bits)
378 };
379 xof.finalize_bits_internal(partial_byte, partial_bits)?;
380 partial_byte.zeroize();
381 xof.squeeze_into_vec(output_len)
382 }
383}
384
385impl ExtendableOutputFunction for ShakeXof128 {
386 fn new() -> Self {
387 Self::init()
388 }
389
390 fn update(&mut self, data: &[u8]) -> Result<()> {
391 if self.is_finalized {
392 return Err(Error::xof_finalized());
393 }
394 if self.squeezing {
395 return Err(Error::xof_squeezing());
396 }
397
398 let mut idx = 0;
399 if self.buffer_idx > 0 {
400 let to_copy = (SHAKE128_RATE - self.buffer_idx).min(data.len());
401 let buffer_slice =
402 &mut self.buffer.as_mut()[self.buffer_idx..self.buffer_idx + to_copy];
403 buffer_slice.copy_from_slice(&data[..to_copy]);
404 self.buffer_idx += to_copy;
405 idx = to_copy;
406
407 if self.buffer_idx == SHAKE128_RATE {
408 keccak_absorb(&mut self.state, self.buffer.as_ref(), SHAKE128_RATE);
409 self.buffer.zeroize();
410 self.buffer_idx = 0;
411 }
412 }
413
414 let remaining = data.len() - idx;
415 let full_blocks = remaining / SHAKE128_RATE;
416 for i in 0..full_blocks {
417 let start = idx + i * SHAKE128_RATE;
418 let block = &data[start..start + SHAKE128_RATE];
419
420 keccak_absorb(&mut self.state, block, SHAKE128_RATE);
422 }
423 idx += full_blocks * SHAKE128_RATE;
424
425 if idx < data.len() {
426 let rem = data.len() - idx;
427 self.buffer.as_mut()[..rem].copy_from_slice(&data[idx..]);
428 self.buffer_idx = rem;
429 }
430
431 Ok(())
432 }
433
434 fn finalize(&mut self) -> Result<()> {
435 if self.is_finalized {
436 return Ok(());
437 }
438
439 let mut pad_block = SecretBuffer::<SHAKE128_RATE>::zeroed();
441 pad_block.as_mut()[..self.buffer_idx]
442 .copy_from_slice(&self.buffer.as_ref()[..self.buffer_idx]);
443 pad_block.as_mut()[self.buffer_idx] ^= 0x1F;
444 pad_block.as_mut()[SHAKE128_RATE - 1] ^= 0x80;
445
446 self.buffer.zeroize();
447
448 keccak_absorb(&mut self.state, pad_block.as_ref(), SHAKE128_RATE);
449
450 self.is_finalized = true;
451 self.buffer_idx = 0;
452 Ok(())
453 }
454
455 fn squeeze(&mut self, output: &mut [u8]) -> Result<()> {
456 validate::parameter(
457 !output.is_empty(),
458 "output_length",
459 "Output buffer must not be empty",
460 )?;
461
462 if !self.is_finalized {
463 self.finalize()?;
464 }
465 self.squeezing = true;
466
467 let mut offset = 0;
468 let rate = SHAKE128_RATE;
469
470 while offset < output.len() {
471 if self.buffer_idx >= rate {
472 self.state.apply_permutation();
473 self.buffer_idx = 0;
474 }
475
476 if self.buffer_idx == 0 {
477 let state_array = self.state.to_u64_array();
479 let buffer_mut = self.buffer.as_mut();
480
481 for i in 0..(rate / 8) {
482 let mut lane = state_array[i];
483 for j in 0..8 {
484 if i * 8 + j < rate {
485 buffer_mut[i * 8 + j] = ((lane >> (8 * j)) & 0xFF) as u8;
486 }
487 }
488 lane.zeroize();
489 }
490 }
491
492 let available = rate - self.buffer_idx;
493 let needed = output.len() - offset;
494 let to_copy = available.min(needed);
495
496 output[offset..offset + to_copy]
497 .copy_from_slice(&self.buffer.as_ref()[self.buffer_idx..self.buffer_idx + to_copy]);
498
499 offset += to_copy;
500 self.buffer_idx += to_copy;
501 }
502
503 barrier::compiler_fence_seq_cst();
505 Ok(())
506 }
507
508 fn squeeze_into_vec(&mut self, len: usize) -> Result<ZeroizingBytes> {
509 validate::parameter(
510 len > 0,
511 "output_length",
512 "Output length must be greater than 0",
513 )?;
514
515 let mut v = Zeroizing::new(boxed_bytes_zeroed(len));
516 self.squeeze(&mut v)?;
517 Ok(v)
518 }
519
520 fn reset(&mut self) -> Result<()> {
521 *self = Self::new();
522 Ok(())
523 }
524
525 fn security_level() -> usize {
526 128
527 }
528}
529
530impl ShakeXof256 {
531 fn init() -> Self {
532 ShakeXof256 {
533 state: SecureKeccakState::new(),
534 buffer: SecretBuffer::zeroed(),
535 buffer_idx: 0,
536 is_finalized: false,
537 squeezing: false,
538 }
539 }
540
541 fn finalize_bits_internal(&mut self, partial_byte: u8, partial_bits: usize) -> Result<()> {
542 validate::parameter(
543 !self.is_finalized,
544 "state",
545 "SHAKE input has already been finalized",
546 )?;
547 validate::parameter(
548 !self.squeezing,
549 "state",
550 "SHAKE output is already being squeezed",
551 )?;
552 finalize_bit_string(
553 &mut self.state,
554 &self.buffer,
555 self.buffer_idx,
556 partial_byte,
557 partial_bits,
558 );
559 self.buffer.zeroize();
560 self.buffer_idx = 0;
561 self.is_finalized = true;
562 Ok(())
563 }
564
565 #[doc(hidden)]
569 pub fn generate_bits(data: &[u8], bit_len: usize, output_len: usize) -> Result<ZeroizingBytes> {
570 validate::parameter(
571 output_len > 0,
572 "output_length",
573 "XOF output length must be greater than 0",
574 )?;
575 let (full_bytes, partial_bits) = validate_bit_string(data, bit_len)?;
576 let mut xof = Self::init();
577 xof.update(&data[..full_bytes])?;
578 let mut partial_byte = if partial_bits == 0 {
579 0
580 } else {
581 data[full_bytes] >> (8 - partial_bits)
582 };
583 xof.finalize_bits_internal(partial_byte, partial_bits)?;
584 partial_byte.zeroize();
585 xof.squeeze_into_vec(output_len)
586 }
587}
588
589impl ExtendableOutputFunction for ShakeXof256 {
590 fn new() -> Self {
591 Self::init()
592 }
593
594 fn update(&mut self, data: &[u8]) -> Result<()> {
595 if self.is_finalized {
596 return Err(Error::xof_finalized());
597 }
598 if self.squeezing {
599 return Err(Error::xof_squeezing());
600 }
601
602 let mut idx = 0;
603 if self.buffer_idx > 0 {
604 let to_copy = (SHAKE256_RATE - self.buffer_idx).min(data.len());
605 let buffer_slice =
606 &mut self.buffer.as_mut()[self.buffer_idx..self.buffer_idx + to_copy];
607 buffer_slice.copy_from_slice(&data[..to_copy]);
608 self.buffer_idx += to_copy;
609 idx = to_copy;
610
611 if self.buffer_idx == SHAKE256_RATE {
612 keccak_absorb(&mut self.state, self.buffer.as_ref(), SHAKE256_RATE);
613 self.buffer.zeroize();
614 self.buffer_idx = 0;
615 }
616 }
617
618 let remaining = data.len() - idx;
619 let full_blocks = remaining / SHAKE256_RATE;
620 for i in 0..full_blocks {
621 let start = idx + i * SHAKE256_RATE;
622 let block = &data[start..start + SHAKE256_RATE];
623
624 keccak_absorb(&mut self.state, block, SHAKE256_RATE);
626 }
627 idx += full_blocks * SHAKE256_RATE;
628
629 if idx < data.len() {
630 let rem = data.len() - idx;
631 self.buffer.as_mut()[..rem].copy_from_slice(&data[idx..]);
632 self.buffer_idx = rem;
633 }
634
635 Ok(())
636 }
637
638 fn finalize(&mut self) -> Result<()> {
639 if self.is_finalized {
640 return Ok(());
641 }
642
643 let mut pad_block = SecretBuffer::<SHAKE256_RATE>::zeroed();
645 pad_block.as_mut()[..self.buffer_idx]
646 .copy_from_slice(&self.buffer.as_ref()[..self.buffer_idx]);
647 pad_block.as_mut()[self.buffer_idx] ^= 0x1F;
648 pad_block.as_mut()[SHAKE256_RATE - 1] ^= 0x80;
649
650 self.buffer.zeroize();
651
652 keccak_absorb(&mut self.state, pad_block.as_ref(), SHAKE256_RATE);
653
654 self.is_finalized = true;
655 self.buffer_idx = 0;
656 Ok(())
657 }
658
659 fn squeeze(&mut self, output: &mut [u8]) -> Result<()> {
660 validate::parameter(
661 !output.is_empty(),
662 "output_length",
663 "Output buffer must not be empty",
664 )?;
665
666 if !self.is_finalized {
667 self.finalize()?;
668 }
669 self.squeezing = true;
670
671 let mut offset = 0;
672 let rate = SHAKE256_RATE;
673
674 while offset < output.len() {
675 if self.buffer_idx >= rate {
676 self.state.apply_permutation();
677 self.buffer_idx = 0;
678 }
679
680 if self.buffer_idx == 0 {
681 let state_array = self.state.to_u64_array();
683 let buffer_mut = self.buffer.as_mut();
684
685 for i in 0..(rate / 8) {
686 let mut lane = state_array[i];
687 for j in 0..8 {
688 if i * 8 + j < rate {
689 buffer_mut[i * 8 + j] = ((lane >> (8 * j)) & 0xFF) as u8;
690 }
691 }
692 lane.zeroize();
693 }
694 }
695
696 let available = rate - self.buffer_idx;
697 let needed = output.len() - offset;
698 let to_copy = available.min(needed);
699
700 output[offset..offset + to_copy]
701 .copy_from_slice(&self.buffer.as_ref()[self.buffer_idx..self.buffer_idx + to_copy]);
702
703 offset += to_copy;
704 self.buffer_idx += to_copy;
705 }
706
707 barrier::compiler_fence_seq_cst();
709 Ok(())
710 }
711
712 fn squeeze_into_vec(&mut self, len: usize) -> Result<ZeroizingBytes> {
713 validate::parameter(
714 len > 0,
715 "output_length",
716 "Output length must be greater than 0",
717 )?;
718
719 let mut v = Zeroizing::new(boxed_bytes_zeroed(len));
720 self.squeeze(&mut v)?;
721 Ok(v)
722 }
723
724 fn reset(&mut self) -> Result<()> {
725 *self = Self::new();
726 Ok(())
727 }
728
729 fn security_level() -> usize {
730 256
731 }
732}
733
734#[cfg(test)]
735mod tests;