Skip to content

Commit d4b86b6

Browse files
authored
cshake: remove implementation of Reset traits (#825)
The reset ability is probably needed extremely rarely in practice and its support balloons the `CShake` state size. Additionally, comparing `initial_state` against zeros to determine whether the function was customized or not could be potentially problematic, since it may be possible to craft a customization string which results in a zeroed state after permutation is applied. (Considering the used encoding, I don't think it's possible, but it has to be proved separately) In future we could add separate resettable types if there is demand for them.
1 parent 61080e4 commit d4b86b6

2 files changed

Lines changed: 36 additions & 74 deletions

File tree

cshake/src/lib.rs

Lines changed: 32 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,17 @@ pub use digest;
1313

1414
use core::fmt;
1515
use digest::{
16-
CollisionResistance, CustomizedInit, ExtendableOutput, ExtendableOutputReset, HashMarker,
17-
Reset, Update, XofReader,
16+
CollisionResistance, CustomizedInit, ExtendableOutput, HashMarker, Update, XofReader,
1817
array::Array,
1918
block_api::{AlgorithmName, BlockSizeUser},
2019
block_buffer::{BlockSizes, EagerBuffer, ReadBuffer},
2120
consts::{U16, U32, U136, U168},
2221
};
2322
use keccak::{Keccak, State1600};
2423

24+
const SHAKE_PAD: u8 = 0x1F;
25+
const CSHAKE_PAD: u8 = 0x04;
26+
2527
/// cSHAKE128 hasher.
2628
pub type CShake128 = CShake<U168>;
2729
/// cSHAKE256 hasher.
@@ -33,23 +35,15 @@ pub type CShake256 = CShake<U136>;
3335
#[derive(Clone)]
3436
pub struct CShake<Rate: BlockSizes> {
3537
state: State1600,
36-
initial_state: State1600,
37-
keccak: Keccak,
3838
buffer: EagerBuffer<Rate>,
39+
pad: u8,
40+
keccak: Keccak,
3941
}
4042

4143
impl<Rate: BlockSizes> Default for CShake<Rate> {
4244
#[inline]
4345
fn default() -> Self {
44-
const {
45-
assert!(Rate::USIZE == 168 || Rate::USIZE == 136, "unsupported rate");
46-
}
47-
Self {
48-
state: Default::default(),
49-
initial_state: Default::default(),
50-
keccak: Keccak::new(),
51-
buffer: Default::default(),
52-
}
46+
Self::new_with_function_name(b"", b"")
5347
}
5448
}
5549

@@ -59,10 +53,21 @@ impl<Rate: BlockSizes> CShake<Rate> {
5953
/// Note that the function name is intended for use by NIST and should only be set to
6054
/// values defined by NIST. You probably don't need to use this function.
6155
pub fn new_with_function_name(function_name: &[u8], customization: &[u8]) -> Self {
62-
let mut state = Self::default();
56+
const {
57+
assert!(Rate::USIZE == 168 || Rate::USIZE == 136, "unsupported rate");
58+
}
59+
60+
let buffer = Default::default();
61+
let keccak = Keccak::new();
62+
let mut state = State1600::default();
6363

6464
if function_name.is_empty() && customization.is_empty() {
65-
return state;
65+
return Self {
66+
state,
67+
buffer,
68+
keccak,
69+
pad: SHAKE_PAD,
70+
};
6671
}
6772

6873
#[inline(always)]
@@ -73,9 +78,9 @@ impl<Rate: BlockSizes> CShake<Rate> {
7378
&b[i..]
7479
}
7580

76-
state.keccak.with_f1600(|f1600| {
81+
keccak.with_f1600(|f1600| {
7782
let mut buffer: EagerBuffer<Rate> = Default::default();
78-
let state = &mut state.state;
83+
let state = &mut state;
7984
let mut b = [0u8; 9];
8085

8186
buffer.digest_blocks(left_encode(Rate::U64, &mut b), |blocks| {
@@ -96,8 +101,12 @@ impl<Rate: BlockSizes> CShake<Rate> {
96101
update_blocks(f1600, state, &[buffer.pad_with_zeros()])
97102
});
98103

99-
state.initial_state = state.state;
100-
state
104+
Self {
105+
state,
106+
buffer,
107+
keccak,
108+
pad: CSHAKE_PAD,
109+
}
101110
}
102111
}
103112

@@ -129,34 +138,18 @@ impl<Rate: BlockSizes> Update for CShake<Rate> {
129138
}
130139
}
131140

132-
impl<Rate: BlockSizes> Reset for CShake<Rate> {
133-
#[inline]
134-
fn reset(&mut self) {
135-
self.state = self.initial_state;
136-
self.buffer.reset();
137-
}
138-
}
139-
140141
impl<Rate: BlockSizes> CShake<Rate> {
141142
fn finalize_dirty(&mut self) {
142143
let Self {
143144
state,
144-
keccak,
145145
buffer,
146-
initial_state,
146+
pad,
147+
keccak,
147148
} = self;
148149

149-
const SHAKE_PAD: u8 = 0x1f;
150-
const CSHAKE_PAD: u8 = 0x04;
151-
152150
let pos = buffer.get_pos();
153151
let mut block = buffer.pad_with_zeros();
154-
let pad = if initial_state.iter().all(|&b| b == 0) {
155-
SHAKE_PAD
156-
} else {
157-
CSHAKE_PAD
158-
};
159-
block[pos] = pad;
152+
block[pos] = *pad;
160153
let n = block.len();
161154
block[n - 1] |= 0x80;
162155

@@ -181,20 +174,6 @@ impl<Rate: BlockSizes> ExtendableOutput for CShake<Rate> {
181174
}
182175
}
183176

184-
impl<Rate: BlockSizes> ExtendableOutputReset for CShake<Rate> {
185-
#[inline]
186-
fn finalize_xof_reset(&mut self) -> Self::Reader {
187-
self.finalize_dirty();
188-
let reader = Self::Reader {
189-
state: self.state,
190-
keccak: self.keccak,
191-
buffer: Default::default(),
192-
};
193-
self.reset();
194-
reader
195-
}
196-
}
197-
198177
impl<Rate: BlockSizes> AlgorithmName for CShake<Rate> {
199178
fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result {
200179
let alg_name = match Rate::USIZE {
@@ -223,7 +202,7 @@ impl<Rate: BlockSizes> Drop for CShake<Rate> {
223202
{
224203
use digest::zeroize::Zeroize;
225204
self.state.zeroize();
226-
self.initial_state.zeroize();
205+
self.pad.zeroize();
227206
// self.buffer is zeroized by its `Drop`
228207
}
229208
}

cshake/tests/cshake.rs

Lines changed: 4 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use digest::{CustomizedInit, ExtendableOutputReset};
1+
use digest::{CustomizedInit, ExtendableOutput};
22

33
#[derive(Debug, Clone, Copy)]
44
pub struct TestVector {
@@ -7,15 +7,15 @@ pub struct TestVector {
77
pub output: &'static [u8],
88
}
99

10-
pub(crate) fn cshake_reset_test<D>(
10+
pub(crate) fn cshake_test<D>(
1111
&TestVector {
1212
customization,
1313
input,
1414
output,
1515
}: &TestVector,
1616
) -> Result<(), &'static str>
1717
where
18-
D: CustomizedInit + ExtendableOutputReset + Clone,
18+
D: CustomizedInit + ExtendableOutput + Clone,
1919
{
2020
let mut hasher = D::new_customized(customization);
2121
let mut buf = [0u8; 1024];
@@ -29,15 +29,6 @@ where
2929
}
3030
buf.iter_mut().for_each(|b| *b = 0);
3131

32-
// Test if reset works correctly
33-
hasher2.reset();
34-
hasher2.update(input);
35-
hasher2.finalize_xof_reset_into(buf);
36-
if buf != output {
37-
return Err("whole message after reset");
38-
}
39-
buf.iter_mut().for_each(|b| *b = 0);
40-
4132
// Test that it works when accepting the message in chunks
4233
for n in 1..core::cmp::min(17, input.len()) {
4334
let mut hasher = D::new_customized(customization);
@@ -50,12 +41,6 @@ where
5041
return Err("message in chunks");
5142
}
5243
buf.iter_mut().for_each(|b| *b = 0);
53-
54-
hasher2.finalize_xof_reset_into(buf);
55-
if buf != output {
56-
return Err("message in chunks");
57-
}
58-
buf.iter_mut().for_each(|b| *b = 0);
5944
}
6045

6146
Ok(())
@@ -71,9 +56,7 @@ macro_rules! new_cshake_test {
7156
);
7257

7358
for (i, tv) in TEST_VECTORS.iter().enumerate() {
74-
if let Err(reason) =
75-
cshake_reset_test::<$hasher>(tv)
76-
{
59+
if let Err(reason) = cshake_test::<$hasher>(tv) {
7760
panic!(
7861
"\n\
7962
Failed test #{i}\n\

0 commit comments

Comments
 (0)