Skip to content

Commit 3cf3bcb

Browse files
committed
Use sponge-cursor
1 parent bb10c3a commit 3cf3bcb

3 files changed

Lines changed: 67 additions & 110 deletions

File tree

Cargo.lock

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cshake/Cargo.toml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,9 @@ categories = ["cryptography", "no-std"]
1313
description = "Implementation of the cSHAKE family of extendable-output functions (XOFs)"
1414

1515
[dependencies]
16-
digest = "0.11"
16+
digest = { version = "0.11", default-features = false }
1717
keccak = "0.2"
18+
sponge-cursor = { git = "https://github.com/RustCrypto/utils", branch = "add-sponge-cursor" }
1819

1920
[dev-dependencies]
2021
digest = { version = "0.11", features = ["dev"] }
@@ -23,7 +24,7 @@ hex-literal = "1"
2324
[features]
2425
default = ["alloc"]
2526
alloc = ["digest/alloc"]
26-
zeroize = ["digest/zeroize"]
27+
zeroize = ["digest/zeroize", "sponge-cursor/zeroize"]
2728

2829
[package.metadata.docs.rs]
2930
all-features = true

cshake/src/lib.rs

Lines changed: 54 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,15 @@
1111

1212
pub use digest;
1313

14-
use core::fmt;
14+
use core::{fmt, marker::PhantomData};
1515
use digest::{
1616
CollisionResistance, CustomizedInit, ExtendableOutput, HashMarker, Update, XofReader,
17-
array::Array,
17+
array::ArraySize,
1818
block_api::{AlgorithmName, BlockSizeUser},
19-
block_buffer::{BlockSizes, EagerBuffer, LazyBuffer, ReadBuffer},
2019
consts::{U16, U32, U136, U168},
2120
};
2221
use keccak::{Keccak, State1600};
22+
use sponge_cursor::SpongeCursor;
2323

2424
const SHAKE_PAD: u8 = 0x1F;
2525
const CSHAKE_PAD: u8 = 0x04;
@@ -33,21 +33,22 @@ pub type CShake256 = CShake<U136>;
3333
///
3434
/// Rate MUST be either [`U168`] or [`U136`] for cSHAKE128 and cSHAKE256 respectively.
3535
#[derive(Clone)]
36-
pub struct CShake<Rate: BlockSizes> {
36+
pub struct CShake<Rate: ArraySize> {
3737
state: State1600,
38-
buffer: EagerBuffer<Rate>,
38+
cursor: SpongeCursor<Rate>,
3939
pad: u8,
4040
keccak: Keccak,
41+
_pd: PhantomData<Rate>,
4142
}
4243

43-
impl<Rate: BlockSizes> Default for CShake<Rate> {
44+
impl<Rate: ArraySize> Default for CShake<Rate> {
4445
#[inline]
4546
fn default() -> Self {
4647
Self::new_with_function_name(b"", b"")
4748
}
4849
}
4950

50-
impl<Rate: BlockSizes> CShake<Rate> {
51+
impl<Rate: ArraySize> CShake<Rate> {
5152
/// Creates a new cSHAKE instance with the given function name and customization.
5253
///
5354
/// Note that the function name is intended for use by NIST and should only be set to
@@ -57,16 +58,16 @@ impl<Rate: BlockSizes> CShake<Rate> {
5758
assert!(Rate::USIZE == 168 || Rate::USIZE == 136, "unsupported rate");
5859
}
5960

60-
let buffer = Default::default();
6161
let keccak = Keccak::new();
6262
let mut state = State1600::default();
6363

6464
if function_name.is_empty() && customization.is_empty() {
6565
return Self {
6666
state,
67-
buffer,
68-
keccak,
67+
cursor: Default::default(),
6968
pad: SHAKE_PAD,
69+
keccak,
70+
_pd: PhantomData,
7071
};
7172
}
7273

@@ -79,102 +80,83 @@ impl<Rate: BlockSizes> CShake<Rate> {
7980
}
8081

8182
keccak.with_f1600(|f1600| {
82-
let mut buffer: LazyBuffer<Rate> = Default::default();
83+
let mut cursor: SpongeCursor<Rate> = Default::default();
8384
let state = &mut state;
8485
let mut b = [0u8; 9];
8586

86-
buffer.digest_blocks(left_encode(Rate::U64, &mut b), |blocks| {
87-
update_blocks(f1600, state, blocks)
88-
});
87+
cursor.absorb_u64_le(state, f1600, left_encode(Rate::U64, &mut b));
8988

9089
let mut encode_str = |str: &[u8]| {
9190
let str_bits_len = 8 * u64::try_from(str.len())
9291
.expect("in practice strings can not be longer than u64::MAX");
9392
let encoded_len = left_encode(str_bits_len, &mut b);
94-
buffer.digest_blocks(encoded_len, |blocks| update_blocks(f1600, state, blocks));
95-
buffer.digest_blocks(str, |blocks| update_blocks(f1600, state, blocks));
93+
cursor.absorb_u64_le(state, f1600, encoded_len);
94+
cursor.absorb_u64_le(state, f1600, str);
9695
};
9796

9897
encode_str(function_name);
9998
encode_str(customization);
10099

101-
update_blocks(f1600, state, &[buffer.pad_with_zeros()])
100+
if cursor.pos() != 0 {
101+
f1600(state);
102+
}
102103
});
103104

104105
Self {
105106
state,
106-
buffer,
107-
keccak,
107+
cursor: Default::default(),
108108
pad: CSHAKE_PAD,
109+
keccak,
110+
_pd: PhantomData,
109111
}
110112
}
111113
}
112114

113-
impl<Rate: BlockSizes> CustomizedInit for CShake<Rate> {
115+
impl<Rate: ArraySize> CustomizedInit for CShake<Rate> {
114116
#[inline]
115117
fn new_customized(customization: &[u8]) -> Self {
116118
Self::new_with_function_name(&[], customization)
117119
}
118120
}
119121

120-
impl<Rate: BlockSizes> HashMarker for CShake<Rate> {}
122+
impl<Rate: ArraySize> HashMarker for CShake<Rate> {}
121123

122-
impl<Rate: BlockSizes> BlockSizeUser for CShake<Rate> {
124+
impl<Rate: ArraySize> BlockSizeUser for CShake<Rate> {
123125
type BlockSize = Rate;
124126
}
125127

126-
impl<Rate: BlockSizes> Update for CShake<Rate> {
128+
impl<Rate: ArraySize> Update for CShake<Rate> {
127129
fn update(&mut self, data: &[u8]) {
128-
let Self {
129-
state,
130-
buffer,
131-
keccak,
132-
..
133-
} = self;
134-
135-
keccak.with_f1600(|f1600| {
136-
buffer.digest_blocks(data, |blocks| update_blocks(f1600, state, blocks));
130+
self.keccak.with_f1600(|f1600| {
131+
self.cursor.absorb_u64_le(&mut self.state, f1600, data);
137132
});
138133
}
139134
}
140135

141-
impl<Rate: BlockSizes> CShake<Rate> {
142-
fn finalize_dirty(&mut self) {
143-
let Self {
144-
state,
145-
buffer,
146-
pad,
147-
keccak,
148-
} = self;
149-
150-
let pos = buffer.get_pos();
151-
let mut block = buffer.pad_with_zeros();
152-
block[pos] = *pad;
153-
let n = block.len();
154-
block[n - 1] |= 0x80;
155-
156-
keccak.with_f1600(|f1600| {
157-
xor_block(state, &block);
158-
f1600(state);
159-
});
160-
}
161-
}
162-
163-
impl<Rate: BlockSizes> ExtendableOutput for CShake<Rate> {
136+
impl<Rate: ArraySize> ExtendableOutput for CShake<Rate> {
164137
type Reader = CShakeReader<Rate>;
165138

166139
#[inline]
167140
fn finalize_xof(mut self) -> Self::Reader {
168-
self.finalize_dirty();
141+
let pos = self.cursor.pos();
142+
let word_offset = pos / 8;
143+
let byte_offset = pos % 8;
144+
145+
let pad = u64::from(self.pad) << (8 * byte_offset);
146+
self.state[word_offset] ^= pad;
147+
self.state[Rate::USIZE / 8 - 1] ^= 1 << 63;
148+
149+
// Note that `CShakeReader` applies the permutation to the state before reading from it
150+
169151
Self::Reader {
170152
state: self.state,
153+
cursor: Default::default(),
171154
keccak: self.keccak,
172-
buffer: Default::default(),
173155
}
174156
}
175157
}
176158

177-
impl<Rate: BlockSizes> AlgorithmName for CShake<Rate> {
159+
impl<Rate: ArraySize> AlgorithmName for CShake<Rate> {
178160
fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result {
179161
let alg_name = match Rate::USIZE {
180162
168 => "cSHAKE128",
@@ -185,7 +167,7 @@ impl<Rate: BlockSizes> AlgorithmName for CShake<Rate> {
185167
}
186168
}
187169

188-
impl<Rate: BlockSizes> fmt::Debug for CShake<Rate> {
170+
impl<Rate: ArraySize> fmt::Debug for CShake<Rate> {
189171
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
190172
let debug_str = match Rate::USIZE {
191173
168 => "CShake128 { ... }",
@@ -196,53 +178,39 @@ impl<Rate: BlockSizes> fmt::Debug for CShake<Rate> {
196178
}
197179
}
198180

199-
impl<Rate: BlockSizes> Drop for CShake<Rate> {
181+
impl<Rate: ArraySize> Drop for CShake<Rate> {
200182
fn drop(&mut self) {
201183
#[cfg(feature = "zeroize")]
202184
{
203185
use digest::zeroize::Zeroize;
204186
self.state.zeroize();
187+
self.cursor.zeroize();
205188
self.pad.zeroize();
206-
// self.buffer is zeroized by its `Drop`
207189
}
208190
}
209191
}
210192

211193
#[cfg(feature = "zeroize")]
212-
impl<Rate: BlockSizes> digest::zeroize::ZeroizeOnDrop for CShake<Rate> {}
194+
impl<Rate: ArraySize> digest::zeroize::ZeroizeOnDrop for CShake<Rate> {}
213195

214196
/// Generic cSHAKE XOF reader
215197
#[derive(Clone)]
216-
pub struct CShakeReader<Rate: BlockSizes> {
198+
pub struct CShakeReader<Rate: ArraySize> {
217199
state: State1600,
200+
cursor: SpongeCursor<Rate>,
218201
keccak: Keccak,
219-
buffer: ReadBuffer<Rate>,
220202
}
221203

222-
impl<Rate: BlockSizes> XofReader for CShakeReader<Rate> {
204+
impl<Rate: ArraySize> XofReader for CShakeReader<Rate> {
223205
#[inline]
224206
fn read(&mut self, buf: &mut [u8]) {
225-
let Self {
226-
state,
227-
keccak,
228-
buffer,
229-
} = self;
230-
231-
buffer.read(buf, |block| {
232-
let mut chunks = block.chunks_exact_mut(8);
233-
for (src, dst) in state.iter().zip(&mut chunks) {
234-
dst.copy_from_slice(&src.to_le_bytes());
235-
}
236-
assert!(
237-
chunks.into_remainder().is_empty(),
238-
"rate is either 136 or 168",
239-
);
240-
keccak.with_f1600(|f1600| f1600(state));
207+
self.keccak.with_f1600(|f1600| {
208+
self.cursor.squeeze_u64_le(&mut self.state, f1600, buf);
241209
});
242210
}
243211
}
244212

245-
impl<Rate: BlockSizes> fmt::Debug for CShakeReader<Rate> {
213+
impl<Rate: ArraySize> fmt::Debug for CShakeReader<Rate> {
246214
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
247215
let debug_str = match Rate::USIZE {
248216
168 => "TurboShakeReader128 { ... }",
@@ -253,44 +221,22 @@ impl<Rate: BlockSizes> fmt::Debug for CShakeReader<Rate> {
253221
}
254222
}
255223

256-
impl<Rate: BlockSizes> Drop for CShakeReader<Rate> {
224+
impl<Rate: ArraySize> Drop for CShakeReader<Rate> {
257225
fn drop(&mut self) {
258226
#[cfg(feature = "zeroize")]
259227
{
260228
use digest::zeroize::Zeroize;
261229
self.state.zeroize();
262-
// self.buffer is zeroized by its `Drop`
230+
self.cursor.zeroize();
263231
}
264232
}
265233
}
266234

267-
// https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-185.pdf#[{"num":68,"gen":0},{"name":"XYZ"},108,440,null]
235+
// See Section 8.3 of NIST SP 800-185:
236+
// https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-185.pdf
268237
impl CollisionResistance for CShake128 {
269238
type CollisionResistance = U16;
270239
}
271240
impl CollisionResistance for CShake256 {
272241
type CollisionResistance = U32;
273242
}
274-
275-
fn xor_block(state: &mut State1600, block: &[u8]) {
276-
assert!(size_of_val(block) < size_of_val(state));
277-
278-
let mut chunks = block.chunks_exact(8);
279-
for (s, chunk) in state.iter_mut().zip(&mut chunks) {
280-
*s ^= u64::from_le_bytes(chunk.try_into().unwrap());
281-
}
282-
283-
let rem = chunks.remainder();
284-
assert!(rem.is_empty(), "block size is equal to 136 or 168");
285-
}
286-
287-
fn update_blocks<N: BlockSizes>(
288-
f1600: keccak::Fn1600,
289-
state: &mut State1600,
290-
blocks: &[Array<u8, N>],
291-
) {
292-
for block in blocks {
293-
xor_block(state, block);
294-
f1600(state);
295-
}
296-
}

0 commit comments

Comments
 (0)