From 3cf3bcbf5952a8a27fd9d8518eb733130158621c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D1=80=D1=82=D1=91=D0=BC=20=D0=9F=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=BE=D0=B2=20=5BArtyom=20Pavlov=5D?= Date: Sat, 2 May 2026 20:10:26 +0300 Subject: [PATCH 1/8] Use `sponge-cursor` --- Cargo.lock | 10 +++ cshake/Cargo.toml | 5 +- cshake/src/lib.rs | 162 ++++++++++++++++------------------------------ 3 files changed, 67 insertions(+), 110 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 022295ef..eecbd623 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -123,6 +123,7 @@ dependencies = [ "digest", "hex-literal", "keccak", + "sponge-cursor", ] [[package]] @@ -355,6 +356,15 @@ dependencies = [ "hex-literal", ] +[[package]] +name = "sponge-cursor" +version = "0.1.0" +source = "git+https://github.com/RustCrypto/utils?branch=add-sponge-cursor#8ff587ce452a552ce7730338972b78a098079ed9" +dependencies = [ + "hybrid-array", + "zeroize", +] + [[package]] name = "streebog" version = "0.11.0" diff --git a/cshake/Cargo.toml b/cshake/Cargo.toml index 2dedf5fe..c74098f0 100644 --- a/cshake/Cargo.toml +++ b/cshake/Cargo.toml @@ -13,8 +13,9 @@ categories = ["cryptography", "no-std"] description = "Implementation of the cSHAKE family of extendable-output functions (XOFs)" [dependencies] -digest = "0.11" +digest = { version = "0.11", default-features = false } keccak = "0.2" +sponge-cursor = { git = "https://github.com/RustCrypto/utils", branch = "add-sponge-cursor" } [dev-dependencies] digest = { version = "0.11", features = ["dev"] } @@ -23,7 +24,7 @@ hex-literal = "1" [features] default = ["alloc"] alloc = ["digest/alloc"] -zeroize = ["digest/zeroize"] +zeroize = ["digest/zeroize", "sponge-cursor/zeroize"] [package.metadata.docs.rs] all-features = true diff --git a/cshake/src/lib.rs b/cshake/src/lib.rs index bc02e67a..becaf645 100644 --- a/cshake/src/lib.rs +++ b/cshake/src/lib.rs @@ -11,15 +11,15 @@ pub use digest; -use core::fmt; +use core::{fmt, marker::PhantomData}; use digest::{ CollisionResistance, CustomizedInit, ExtendableOutput, HashMarker, Update, XofReader, - array::Array, + array::ArraySize, block_api::{AlgorithmName, BlockSizeUser}, - block_buffer::{BlockSizes, EagerBuffer, LazyBuffer, ReadBuffer}, consts::{U16, U32, U136, U168}, }; use keccak::{Keccak, State1600}; +use sponge_cursor::SpongeCursor; const SHAKE_PAD: u8 = 0x1F; const CSHAKE_PAD: u8 = 0x04; @@ -33,21 +33,22 @@ pub type CShake256 = CShake; /// /// Rate MUST be either [`U168`] or [`U136`] for cSHAKE128 and cSHAKE256 respectively. #[derive(Clone)] -pub struct CShake { +pub struct CShake { state: State1600, - buffer: EagerBuffer, + cursor: SpongeCursor, pad: u8, keccak: Keccak, + _pd: PhantomData, } -impl Default for CShake { +impl Default for CShake { #[inline] fn default() -> Self { Self::new_with_function_name(b"", b"") } } -impl CShake { +impl CShake { /// Creates a new cSHAKE instance with the given function name and customization. /// /// Note that the function name is intended for use by NIST and should only be set to @@ -57,16 +58,16 @@ impl CShake { assert!(Rate::USIZE == 168 || Rate::USIZE == 136, "unsupported rate"); } - let buffer = Default::default(); let keccak = Keccak::new(); let mut state = State1600::default(); if function_name.is_empty() && customization.is_empty() { return Self { state, - buffer, - keccak, + cursor: Default::default(), pad: SHAKE_PAD, + keccak, + _pd: PhantomData, }; } @@ -79,102 +80,83 @@ impl CShake { } keccak.with_f1600(|f1600| { - let mut buffer: LazyBuffer = Default::default(); + let mut cursor: SpongeCursor = Default::default(); let state = &mut state; let mut b = [0u8; 9]; - buffer.digest_blocks(left_encode(Rate::U64, &mut b), |blocks| { - update_blocks(f1600, state, blocks) - }); + cursor.absorb_u64_le(state, f1600, left_encode(Rate::U64, &mut b)); let mut encode_str = |str: &[u8]| { let str_bits_len = 8 * u64::try_from(str.len()) .expect("in practice strings can not be longer than u64::MAX"); let encoded_len = left_encode(str_bits_len, &mut b); - buffer.digest_blocks(encoded_len, |blocks| update_blocks(f1600, state, blocks)); - buffer.digest_blocks(str, |blocks| update_blocks(f1600, state, blocks)); + cursor.absorb_u64_le(state, f1600, encoded_len); + cursor.absorb_u64_le(state, f1600, str); }; encode_str(function_name); encode_str(customization); - update_blocks(f1600, state, &[buffer.pad_with_zeros()]) + if cursor.pos() != 0 { + f1600(state); + } }); Self { state, - buffer, - keccak, + cursor: Default::default(), pad: CSHAKE_PAD, + keccak, + _pd: PhantomData, } } } -impl CustomizedInit for CShake { +impl CustomizedInit for CShake { #[inline] fn new_customized(customization: &[u8]) -> Self { Self::new_with_function_name(&[], customization) } } -impl HashMarker for CShake {} +impl HashMarker for CShake {} -impl BlockSizeUser for CShake { +impl BlockSizeUser for CShake { type BlockSize = Rate; } -impl Update for CShake { +impl Update for CShake { fn update(&mut self, data: &[u8]) { - let Self { - state, - buffer, - keccak, - .. - } = self; - - keccak.with_f1600(|f1600| { - buffer.digest_blocks(data, |blocks| update_blocks(f1600, state, blocks)); + self.keccak.with_f1600(|f1600| { + self.cursor.absorb_u64_le(&mut self.state, f1600, data); }); } } -impl CShake { - fn finalize_dirty(&mut self) { - let Self { - state, - buffer, - pad, - keccak, - } = self; - - let pos = buffer.get_pos(); - let mut block = buffer.pad_with_zeros(); - block[pos] = *pad; - let n = block.len(); - block[n - 1] |= 0x80; - - keccak.with_f1600(|f1600| { - xor_block(state, &block); - f1600(state); - }); - } -} - -impl ExtendableOutput for CShake { +impl ExtendableOutput for CShake { type Reader = CShakeReader; #[inline] fn finalize_xof(mut self) -> Self::Reader { - self.finalize_dirty(); + let pos = self.cursor.pos(); + let word_offset = pos / 8; + let byte_offset = pos % 8; + + let pad = u64::from(self.pad) << (8 * byte_offset); + self.state[word_offset] ^= pad; + self.state[Rate::USIZE / 8 - 1] ^= 1 << 63; + + // Note that `CShakeReader` applies the permutation to the state before reading from it + Self::Reader { state: self.state, + cursor: Default::default(), keccak: self.keccak, - buffer: Default::default(), } } } -impl AlgorithmName for CShake { +impl AlgorithmName for CShake { fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result { let alg_name = match Rate::USIZE { 168 => "cSHAKE128", @@ -185,7 +167,7 @@ impl AlgorithmName for CShake { } } -impl fmt::Debug for CShake { +impl fmt::Debug for CShake { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let debug_str = match Rate::USIZE { 168 => "CShake128 { ... }", @@ -196,53 +178,39 @@ impl fmt::Debug for CShake { } } -impl Drop for CShake { +impl Drop for CShake { fn drop(&mut self) { #[cfg(feature = "zeroize")] { use digest::zeroize::Zeroize; self.state.zeroize(); + self.cursor.zeroize(); self.pad.zeroize(); - // self.buffer is zeroized by its `Drop` } } } #[cfg(feature = "zeroize")] -impl digest::zeroize::ZeroizeOnDrop for CShake {} +impl digest::zeroize::ZeroizeOnDrop for CShake {} /// Generic cSHAKE XOF reader #[derive(Clone)] -pub struct CShakeReader { +pub struct CShakeReader { state: State1600, + cursor: SpongeCursor, keccak: Keccak, - buffer: ReadBuffer, } -impl XofReader for CShakeReader { +impl XofReader for CShakeReader { #[inline] fn read(&mut self, buf: &mut [u8]) { - let Self { - state, - keccak, - buffer, - } = self; - - buffer.read(buf, |block| { - let mut chunks = block.chunks_exact_mut(8); - for (src, dst) in state.iter().zip(&mut chunks) { - dst.copy_from_slice(&src.to_le_bytes()); - } - assert!( - chunks.into_remainder().is_empty(), - "rate is either 136 or 168", - ); - keccak.with_f1600(|f1600| f1600(state)); + self.keccak.with_f1600(|f1600| { + self.cursor.squeeze_u64_le(&mut self.state, f1600, buf); }); } } -impl fmt::Debug for CShakeReader { +impl fmt::Debug for CShakeReader { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let debug_str = match Rate::USIZE { 168 => "TurboShakeReader128 { ... }", @@ -253,44 +221,22 @@ impl fmt::Debug for CShakeReader { } } -impl Drop for CShakeReader { +impl Drop for CShakeReader { fn drop(&mut self) { #[cfg(feature = "zeroize")] { use digest::zeroize::Zeroize; self.state.zeroize(); - // self.buffer is zeroized by its `Drop` + self.cursor.zeroize(); } } } -// https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-185.pdf#[{"num":68,"gen":0},{"name":"XYZ"},108,440,null] +// See Section 8.3 of NIST SP 800-185: +// https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-185.pdf impl CollisionResistance for CShake128 { type CollisionResistance = U16; } impl CollisionResistance for CShake256 { type CollisionResistance = U32; } - -fn xor_block(state: &mut State1600, block: &[u8]) { - assert!(size_of_val(block) < size_of_val(state)); - - let mut chunks = block.chunks_exact(8); - for (s, chunk) in state.iter_mut().zip(&mut chunks) { - *s ^= u64::from_le_bytes(chunk.try_into().unwrap()); - } - - let rem = chunks.remainder(); - assert!(rem.is_empty(), "block size is equal to 136 or 168"); -} - -fn update_blocks( - f1600: keccak::Fn1600, - state: &mut State1600, - blocks: &[Array], -) { - for block in blocks { - xor_block(state, block); - f1600(state); - } -} From 5f4fc77f9a9b24a63c73e62b404457ca84528432 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D1=80=D1=82=D1=91=D0=BC=20=D0=9F=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=BE=D0=B2=20=5BArtyom=20Pavlov=5D?= Date: Sat, 2 May 2026 20:22:57 +0300 Subject: [PATCH 2/8] fix imports --- cshake/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cshake/src/lib.rs b/cshake/src/lib.rs index becaf645..042b6d54 100644 --- a/cshake/src/lib.rs +++ b/cshake/src/lib.rs @@ -15,7 +15,7 @@ use core::{fmt, marker::PhantomData}; use digest::{ CollisionResistance, CustomizedInit, ExtendableOutput, HashMarker, Update, XofReader, array::ArraySize, - block_api::{AlgorithmName, BlockSizeUser}, + common::{AlgorithmName, BlockSizeUser}, consts::{U16, U32, U136, U168}, }; use keccak::{Keccak, State1600}; From bcd6ad024e72ed26c8fb471994eb5f100a9a02f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D1=80=D1=82=D1=91=D0=BC=20=D0=9F=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=BE=D0=B2=20=5BArtyom=20Pavlov=5D?= Date: Sun, 3 May 2026 18:39:51 +0300 Subject: [PATCH 3/8] update turboshake --- Cargo.lock | 1 + Cargo.toml | 1 + cshake/Cargo.toml | 4 +- turboshake/Cargo.toml | 7 ++- turboshake/src/lib.rs | 130 ++++++++++++++---------------------------- 5 files changed, 51 insertions(+), 92 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index eecbd623..9cf58077 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -408,6 +408,7 @@ dependencies = [ "digest", "hex-literal", "keccak", + "sponge-cursor", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 9675dbbd..89605ea7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,3 +36,4 @@ opt-level = 2 [patch.crates-io] sha1 = { path = "sha1" } whirlpool = { path = "whirlpool" } +sponge-cursor = { git = "https://github.com/RustCrypto/utils", branch = "add-sponge-cursor" } diff --git a/cshake/Cargo.toml b/cshake/Cargo.toml index c74098f0..c2893a74 100644 --- a/cshake/Cargo.toml +++ b/cshake/Cargo.toml @@ -15,10 +15,10 @@ description = "Implementation of the cSHAKE family of extendable-output function [dependencies] digest = { version = "0.11", default-features = false } keccak = "0.2" -sponge-cursor = { git = "https://github.com/RustCrypto/utils", branch = "add-sponge-cursor" } +sponge-cursor = "0.1" [dev-dependencies] -digest = { version = "0.11", features = ["dev"] } +digest = { version = "0.11", default-features = false, features = ["dev"] } hex-literal = "1" [features] diff --git a/turboshake/Cargo.toml b/turboshake/Cargo.toml index 33c0a41d..7d914745 100644 --- a/turboshake/Cargo.toml +++ b/turboshake/Cargo.toml @@ -13,17 +13,18 @@ categories = ["cryptography", "no-std"] description = "Implementation of the TurboSHAKE family of extendable-output functions (XOFs)" [dependencies] -digest = "0.11" +digest = { version = "0.11", default-features = false } keccak = "0.2" +sponge-cursor = "0.1" [dev-dependencies] -digest = { version = "0.11", features = ["dev"] } +digest = { version = "0.11", default-features = false, features = ["dev"] } hex-literal = "1" [features] default = ["alloc"] alloc = ["digest/alloc"] -zeroize = ["digest/zeroize"] +zeroize = ["digest/zeroize", "sponge-cursor/zeroize"] [package.metadata.docs.rs] all-features = true diff --git a/turboshake/src/lib.rs b/turboshake/src/lib.rs index c5b93f19..593ef363 100644 --- a/turboshake/src/lib.rs +++ b/turboshake/src/lib.rs @@ -11,12 +11,14 @@ pub use digest; use keccak::{Keccak, State1600}; +use sponge_cursor::SpongeCursor; use core::fmt; use digest::{ - CollisionResistance, ExtendableOutput, ExtendableOutputReset, HashMarker, Update, XofReader, - block_api::{AlgorithmName, BlockSizeUser, Reset}, - block_buffer::{BlockSizes, EagerBuffer, ReadBuffer}, + CollisionResistance, ExtendableOutput, ExtendableOutputReset, HashMarker, Reset, Update, + XofReader, + array::ArraySize, + common::{AlgorithmName, BlockSizeUser}, consts::{U16, U32, U136, U168}, }; @@ -33,13 +35,13 @@ pub const DEFAULT_DS: u8 = 0x1F; /// /// Rate MUST be either [`U168`] or [`U136`] for TurboSHAKE128 and TurboSHAKE256 respectively. #[derive(Clone)] -pub struct TurboShake { +pub struct TurboShake { state: State1600, + cursor: SpongeCursor, keccak: Keccak, - buffer: EagerBuffer, } -impl Default for TurboShake { +impl Default for TurboShake { #[inline] fn default() -> Self { const { @@ -48,96 +50,76 @@ impl Default for TurboShake { } Self { state: Default::default(), + cursor: Default::default(), keccak: Keccak::new(), - buffer: Default::default(), } } } -impl HashMarker for TurboShake {} +impl HashMarker for TurboShake {} -impl BlockSizeUser for TurboShake { +impl BlockSizeUser for TurboShake { type BlockSize = Rate; } -impl Update for TurboShake { +impl Update for TurboShake { #[inline] fn update(&mut self, data: &[u8]) { - let Self { - state, - keccak, - buffer, - } = self; - - keccak.with_p1600::(|p1600| { - buffer.digest_blocks(data, |blocks| { - for block in blocks { - xor_block(state, block); - p1600(state); - } - }); + self.keccak.with_p1600::(|p1600| { + self.cursor.absorb_u64_le(&mut self.state, p1600, data); }); } } -impl TurboShake { - fn finalize_dirty(&mut self) { - let Self { - state, - keccak, - buffer, - } = self; - - let pos = buffer.get_pos(); - let mut block = buffer.pad_with_zeros(); - block[pos] = DS; - let n = block.len(); - block[n - 1] |= 0x80; +impl TurboShake { + fn pad(&mut self) { + let pos = self.cursor.pos(); + let word_offset = pos / 8; + let byte_offset = pos % 8; - keccak.with_p1600::(|p1600| { - xor_block(state, &block); - p1600(state); - }); + let pad = u64::from(DS) << (8 * byte_offset); + self.state[word_offset] ^= pad; + self.state[Rate::USIZE / 8 - 1] ^= 1 << 63; } } -impl ExtendableOutput for TurboShake { +impl ExtendableOutput for TurboShake { type Reader = TurboShakeReader; #[inline] fn finalize_xof(mut self) -> Self::Reader { - self.finalize_dirty(); + self.pad(); Self::Reader { state: self.state, + cursor: Default::default(), keccak: self.keccak, - buffer: Default::default(), } } } -impl ExtendableOutputReset for TurboShake { +impl ExtendableOutputReset for TurboShake { #[inline] fn finalize_xof_reset(&mut self) -> Self::Reader { - self.finalize_dirty(); + self.pad(); let reader = Self::Reader { state: self.state, + cursor: Default::default(), keccak: self.keccak, - buffer: Default::default(), }; self.reset(); reader } } -impl Reset for TurboShake { +impl Reset for TurboShake { #[inline] fn reset(&mut self) { self.state = Default::default(); - self.buffer.reset(); + self.cursor = Default::default(); } } -impl AlgorithmName for TurboShake { +impl AlgorithmName for TurboShake { fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result { let alg_name = match Rate::USIZE { 168 => "TurboSHAKE128", @@ -148,7 +130,7 @@ impl AlgorithmName for TurboShake { } } -impl fmt::Debug for TurboShake { +impl fmt::Debug for TurboShake { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let debug_str = match Rate::USIZE { 168 => "TurboShake128 { ... }", @@ -159,7 +141,7 @@ impl fmt::Debug for TurboShake { } } -impl Drop for TurboShake { +impl Drop for TurboShake { fn drop(&mut self) { #[cfg(feature = "zeroize")] { @@ -171,40 +153,26 @@ impl Drop for TurboShake { } #[cfg(feature = "zeroize")] -impl digest::zeroize::ZeroizeOnDrop for TurboShake {} +impl digest::zeroize::ZeroizeOnDrop for TurboShake {} /// Generic TurboSHAKE XOF reader #[derive(Clone)] -pub struct TurboShakeReader { +pub struct TurboShakeReader { state: State1600, + cursor: SpongeCursor, keccak: Keccak, - buffer: ReadBuffer, } -impl XofReader for TurboShakeReader { +impl XofReader for TurboShakeReader { #[inline] fn read(&mut self, buf: &mut [u8]) { - let Self { - state, - keccak, - buffer, - } = self; - - buffer.read(buf, |block| { - let mut chunks = block.chunks_exact_mut(8); - for (src, dst) in state.iter().zip(&mut chunks) { - dst.copy_from_slice(&src.to_le_bytes()); - } - assert!( - chunks.into_remainder().is_empty(), - "rate is either 136 or 168", - ); - keccak.with_p1600::(|p1600| p1600(state)); + self.keccak.with_p1600::(|p1600| { + self.cursor.squeeze_u64_le(&mut self.state, p1600, buf); }); } } -impl fmt::Debug for TurboShakeReader { +impl fmt::Debug for TurboShakeReader { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let debug_str = match Rate::USIZE { 168 => "TurboShakeReader128 { ... }", @@ -215,19 +183,19 @@ impl fmt::Debug for TurboShakeReader { } } -impl Drop for TurboShakeReader { +impl Drop for TurboShakeReader { fn drop(&mut self) { #[cfg(feature = "zeroize")] { use digest::zeroize::Zeroize; self.state.zeroize(); - // self.buffer is zeroized by its `Drop` + self.cursor.zeroize(); } } } #[cfg(feature = "zeroize")] -impl digest::zeroize::ZeroizeOnDrop for TurboShakeReader {} +impl digest::zeroize::ZeroizeOnDrop for TurboShakeReader {} /// TurboSHAKE128 hasher with domain separator. pub type TurboShake128 = TurboShake; @@ -248,15 +216,3 @@ impl CollisionResistance for TurboShake256 { // https://www.ietf.org/archive/id/draft-irtf-cfrg-kangarootwelve-17.html#section-7-8 type CollisionResistance = U32; } - -fn xor_block(state: &mut State1600, block: &[u8]) { - assert!(size_of_val(block) < size_of_val(state)); - - let mut chunks = block.chunks_exact(8); - for (s, chunk) in state.iter_mut().zip(&mut chunks) { - *s ^= u64::from_le_bytes(chunk.try_into().unwrap()); - } - - let rem = chunks.remainder(); - assert!(rem.is_empty(), "block size is equal to 136 or 168"); -} From aa2f94ee1de1f9da86660f5b0656bab0164fe549 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D1=80=D1=82=D1=91=D0=BC=20=D0=9F=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=BE=D0=B2=20=5BArtyom=20Pavlov=5D?= Date: Sun, 3 May 2026 19:10:35 +0300 Subject: [PATCH 4/8] migrate ascon-xof128 --- Cargo.lock | 3 +- ascon-xof128/Cargo.toml | 7 +- ascon-xof128/src/cxof.rs | 54 +++++++--------- ascon-xof128/src/reader.rs | 29 +++++++-- ascon-xof128/src/xof.rs | 61 ++++++++---------- .../data/ascon_cxof128_serialization.bin | Bin 48 -> 41 bytes .../tests/data/ascon_xof128_serialization.bin | Bin 48 -> 41 bytes ascon-xof128/tests/mod.rs | 12 ++-- 8 files changed, 83 insertions(+), 83 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9cf58077..77ace3d8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -24,6 +24,7 @@ dependencies = [ "ascon", "digest", "hex-literal", + "sponge-cursor", ] [[package]] @@ -359,7 +360,7 @@ dependencies = [ [[package]] name = "sponge-cursor" version = "0.1.0" -source = "git+https://github.com/RustCrypto/utils?branch=add-sponge-cursor#8ff587ce452a552ce7730338972b78a098079ed9" +source = "git+https://github.com/RustCrypto/utils?branch=add-sponge-cursor#d5900bf4ff1953757ad2a0a63d427c3e28ef6b26" dependencies = [ "hybrid-array", "zeroize", diff --git a/ascon-xof128/Cargo.toml b/ascon-xof128/Cargo.toml index 6d47a473..7a2cffe1 100644 --- a/ascon-xof128/Cargo.toml +++ b/ascon-xof128/Cargo.toml @@ -13,17 +13,18 @@ categories = ["cryptography", "no-std"] description = "Implementation of Ascon-XOF128 and Ascon-СXOF128" [dependencies] -digest = "0.11" +digest = { version = "0.11", default-features = false } ascon = "0.5" +sponge-cursor = "0.1" [dev-dependencies] -digest = { version = "0.11", features = ["dev"] } +digest = { version = "0.11", default-features = false, features = ["dev"] } hex-literal = "1" [features] default = ["alloc"] alloc = ["digest/alloc"] -zeroize = ["digest/zeroize"] +zeroize = ["digest/zeroize", "sponge-cursor/zeroize"] [package.metadata.docs.rs] all-features = true diff --git a/ascon-xof128/src/cxof.rs b/ascon-xof128/src/cxof.rs index 289c07c9..390202bd 100644 --- a/ascon-xof128/src/cxof.rs +++ b/ascon-xof128/src/cxof.rs @@ -1,11 +1,11 @@ use ascon::State; use digest::{ CollisionResistance, CustomizedInit, ExtendableOutput, HashMarker, OutputSizeUser, Update, - block_api::AlgorithmName, - block_buffer::EagerBuffer, + common::AlgorithmName, common::hazmat::{DeserializeStateError, SerializableState, SerializedState}, - consts::{U8, U16, U32, U48}, + consts::{U8, U16, U32, U41}, }; +use sponge_cursor::SpongeCursor; use crate::{AsconXof128Reader, consts::CXOF_INIT_STATE}; @@ -20,7 +20,7 @@ use crate::{AsconXof128Reader, consts::CXOF_INIT_STATE}; #[derive(Clone, Debug)] pub struct AsconCxof128 { state: State, - buffer: EagerBuffer, + cursor: SpongeCursor, } impl CustomizedInit for AsconCxof128 { @@ -52,8 +52,8 @@ impl CustomizedInit for AsconCxof128 { ascon::permute12(&mut state); - let buffer = Default::default(); - Self { state, buffer } + let cursor = Default::default(); + Self { state, cursor } } } @@ -71,12 +71,8 @@ impl CollisionResistance for AsconCxof128 { impl Update for AsconCxof128 { #[inline] fn update(&mut self, data: &[u8]) { - self.buffer.digest_blocks(data, |blocks| { - for block in blocks { - self.state[0] ^= u64::from_le_bytes(block.0); - ascon::permute12(&mut self.state); - } - }); + self.cursor + .absorb_u64_le(&mut self.state, ascon::permute12, data); } } @@ -84,13 +80,9 @@ impl ExtendableOutput for AsconCxof128 { type Reader = AsconXof128Reader; fn finalize_xof(mut self) -> Self::Reader { - let Self { state, buffer } = &mut self; - let len = buffer.get_pos(); - let last_block = buffer.pad_with_zeros(); - let pad = 1u64 << (8 * len); - state[0] ^= u64::from_le_bytes(last_block.0) ^ pad; - - AsconXof128Reader::new(state) + let pos = self.cursor.pos(); + self.state[0] ^= 1u64 << (8 * pos); + AsconXof128Reader::new(&self.state) } } @@ -102,18 +94,19 @@ impl AlgorithmName for AsconCxof128 { } impl SerializableState for AsconCxof128 { - type SerializedStateSize = U48; + type SerializedStateSize = U41; #[inline] fn serialize(&self) -> SerializedState { let mut res = SerializedState::::default(); - let (state_dst, buffer_dst) = res.split_at_mut(size_of::()); + let (state_dst, cursor_dst) = res.split_at_mut(size_of::()); let mut chunks = state_dst.chunks_exact_mut(size_of::()); for (src, dst) in self.state.iter().zip(&mut chunks) { dst.copy_from_slice(&src.to_le_bytes()); } assert!(chunks.into_remainder().is_empty()); - buffer_dst.copy_from_slice(&self.buffer.serialize()); + assert_eq!(cursor_dst.len(), 1); + cursor_dst[0] = self.cursor.raw_pos(); res } @@ -121,18 +114,16 @@ impl SerializableState for AsconCxof128 { fn deserialize( serialized_state: &SerializedState, ) -> Result { - let (state_src, buffer_src) = serialized_state.split_at(size_of::()); + let (state_src, cursor_src) = serialized_state.split_at(size_of::()); let state = core::array::from_fn(|i| { let n = size_of::(); let chunk = &state_src[n * i..][..n]; u64::from_le_bytes(chunk.try_into().expect("chunk has correct length")) }); - let buffer_src = buffer_src - .try_into() - .expect("buffer_src has correct length"); - EagerBuffer::deserialize(buffer_src) - .map_err(|_| DeserializeStateError) - .map(|buffer| Self { state, buffer }) + assert_eq!(cursor_src.len(), 1); + SpongeCursor::new(cursor_src[0]) + .ok_or(DeserializeStateError) + .map(|cursor| Self { state, cursor }) } } @@ -141,10 +132,9 @@ impl Drop for AsconCxof128 { fn drop(&mut self) { #[cfg(feature = "zeroize")] { - use digest::zeroize::{Zeroize, ZeroizeOnDrop}; - fn assert_zeroize_on_drop(_: &mut T) {} + use digest::zeroize::Zeroize; self.state.zeroize(); - assert_zeroize_on_drop(&mut self.buffer); + self.cursor.zeroize(); } } } diff --git a/ascon-xof128/src/reader.rs b/ascon-xof128/src/reader.rs index a5c76473..fef39221 100644 --- a/ascon-xof128/src/reader.rs +++ b/ascon-xof128/src/reader.rs @@ -1,27 +1,42 @@ use ascon::State; -use digest::{XofReader, block_buffer::ReadBuffer, consts::U8}; +use digest::{XofReader, consts::U8}; +use sponge_cursor::SpongeCursor; /// XOF reader used by Ascon-XOF128 and Ascon-CXOF128 #[derive(Clone, Debug)] pub struct AsconXof128Reader { state: State, - buffer: ReadBuffer, + cursor: SpongeCursor, } impl AsconXof128Reader { pub(super) fn new(state: &State) -> Self { Self { state: *state, - buffer: Default::default(), + cursor: Default::default(), } } } impl XofReader for AsconXof128Reader { + #[inline] fn read(&mut self, buf: &mut [u8]) { - self.buffer.read(buf, |dst| { - ascon::permute12(&mut self.state); - *dst = self.state[0].to_le_bytes().into(); - }); + self.cursor + .squeeze_u64_le(&mut self.state, ascon::permute12, buf); } } + +impl Drop for AsconXof128Reader { + #[inline] + fn drop(&mut self) { + #[cfg(feature = "zeroize")] + { + use digest::zeroize::Zeroize; + self.state.zeroize(); + self.cursor.zeroize(); + } + } +} + +#[cfg(feature = "zeroize")] +impl digest::zeroize::ZeroizeOnDrop for AsconXof128Reader {} diff --git a/ascon-xof128/src/xof.rs b/ascon-xof128/src/xof.rs index e25cfc10..e0e5bb2b 100644 --- a/ascon-xof128/src/xof.rs +++ b/ascon-xof128/src/xof.rs @@ -3,11 +3,11 @@ use core::fmt; use digest::{ CollisionResistance, ExtendableOutput, ExtendableOutputReset, HashMarker, OutputSizeUser, Reset, Update, - block_api::AlgorithmName, - block_buffer::EagerBuffer, + common::AlgorithmName, common::hazmat::{DeserializeStateError, SerializableState, SerializedState}, - consts::{U8, U16, U32, U48}, + consts::{U8, U16, U32, U41}, }; +use sponge_cursor::SpongeCursor; use crate::{AsconXof128Reader, consts::XOF_INIT_STATE}; @@ -15,7 +15,7 @@ use crate::{AsconXof128Reader, consts::XOF_INIT_STATE}; #[derive(Clone)] pub struct AsconXof128 { state: State, - buffer: EagerBuffer, + cursor: SpongeCursor, } impl Default for AsconXof128 { @@ -23,7 +23,7 @@ impl Default for AsconXof128 { fn default() -> Self { Self { state: XOF_INIT_STATE, - buffer: Default::default(), + cursor: Default::default(), } } } @@ -42,24 +42,15 @@ impl CollisionResistance for AsconXof128 { impl Update for AsconXof128 { #[inline] fn update(&mut self, data: &[u8]) { - self.buffer.digest_blocks(data, |blocks| { - for block in blocks { - self.state[0] ^= u64::from_le_bytes(block.0); - ascon::permute12(&mut self.state); - } - }); + self.cursor + .absorb_u64_le(&mut self.state, ascon::permute12, data); } } impl AsconXof128 { - fn finalize_xof_dirty(&mut self) -> AsconXof128Reader { - let Self { state, buffer } = self; - let len = buffer.get_pos(); - let last_block = buffer.pad_with_zeros(); - let pad = 1u64 << (8 * len); - state[0] ^= u64::from_le_bytes(last_block.0) ^ pad; - - AsconXof128Reader::new(state) + fn pad(&mut self) { + let pos = self.cursor.pos(); + self.state[0] ^= 1u64 << (8 * pos); } } @@ -67,13 +58,15 @@ impl ExtendableOutput for AsconXof128 { type Reader = AsconXof128Reader; fn finalize_xof(mut self) -> Self::Reader { - self.finalize_xof_dirty() + self.pad(); + AsconXof128Reader::new(&self.state) } } impl ExtendableOutputReset for AsconXof128 { fn finalize_xof_reset(&mut self) -> Self::Reader { - let res = self.finalize_xof_dirty(); + self.pad(); + let res = AsconXof128Reader::new(&self.state); self.reset(); res } @@ -83,7 +76,7 @@ impl Reset for AsconXof128 { #[inline] fn reset(&mut self) { self.state = XOF_INIT_STATE; - self.buffer.reset(); + self.cursor = Default::default(); } } @@ -102,18 +95,19 @@ impl fmt::Debug for AsconXof128 { } impl SerializableState for AsconXof128 { - type SerializedStateSize = U48; + type SerializedStateSize = U41; #[inline] fn serialize(&self) -> SerializedState { let mut res = SerializedState::::default(); - let (state_dst, buffer_dst) = res.split_at_mut(size_of::()); + let (state_dst, cursor_dst) = res.split_at_mut(size_of::()); let mut chunks = state_dst.chunks_exact_mut(size_of::()); for (src, dst) in self.state.iter().zip(&mut chunks) { dst.copy_from_slice(&src.to_le_bytes()); } assert!(chunks.into_remainder().is_empty()); - buffer_dst.copy_from_slice(&self.buffer.serialize()); + assert_eq!(cursor_dst.len(), 1); + cursor_dst[0] = self.cursor.raw_pos(); res } @@ -121,18 +115,16 @@ impl SerializableState for AsconXof128 { fn deserialize( serialized_state: &SerializedState, ) -> Result { - let (state_src, buffer_src) = serialized_state.split_at(size_of::()); + let (state_src, cursor_src) = serialized_state.split_at(size_of::()); let state = core::array::from_fn(|i| { let n = size_of::(); let chunk = &state_src[n * i..][..n]; u64::from_le_bytes(chunk.try_into().expect("chunk has correct length")) }); - let buffer_src = buffer_src - .try_into() - .expect("buffer_src has correct length"); - EagerBuffer::deserialize(buffer_src) - .map_err(|_| DeserializeStateError) - .map(|buffer| Self { state, buffer }) + assert_eq!(cursor_src.len(), 1); + SpongeCursor::new(cursor_src[0]) + .ok_or(DeserializeStateError) + .map(|cursor| Self { state, cursor }) } } @@ -141,10 +133,9 @@ impl Drop for AsconXof128 { fn drop(&mut self) { #[cfg(feature = "zeroize")] { - use digest::zeroize::{Zeroize, ZeroizeOnDrop}; - fn assert_zeroize_on_drop(_: &mut T) {} + use digest::zeroize::Zeroize; self.state.zeroize(); - assert_zeroize_on_drop(&mut self.buffer); + self.cursor.zeroize(); } } } diff --git a/ascon-xof128/tests/data/ascon_cxof128_serialization.bin b/ascon-xof128/tests/data/ascon_cxof128_serialization.bin index 166cb41fc58df34cfb3ed35f8117830a54c4e1be..20a0b53c95d3a307648660b8506a51451e3fed20 100644 GIT binary patch literal 41 xcmd<6G>dqv?!);@sruvpT7kS}%x+s8Me8CSDZDiK%|36VUz)Ig{%iy2NJQ&&>|2^K9%`kIgG-EwjHRFz3SMy1Z6JRsev>5<&m~ literal 48 zcmdlqv+P688s&@$diPJ()Spf*kzQ|=pW)`1($aKyyNaZpghX@6=ai}U7@z Date: Sun, 3 May 2026 21:08:40 +0300 Subject: [PATCH 5/8] migrate k12 --- Cargo.lock | 1 + k12/Cargo.toml | 5 ++-- k12/src/custom/borrow.rs | 28 +++++++++--------- k12/src/custom/owned.rs | 28 +++++++++--------- k12/src/lib.rs | 59 +++++++++++++++++-------------------- k12/src/node_turbo_shake.rs | 17 +++++------ k12/src/reader.rs | 50 ++++++++++++++----------------- k12/src/turbo_shake.rs | 53 +++++++++++++++------------------ k12/src/update.rs | 8 ++--- 9 files changed, 115 insertions(+), 134 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 77ace3d8..4bb575a8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -206,6 +206,7 @@ dependencies = [ "digest", "hex-literal", "keccak", + "sponge-cursor", ] [[package]] diff --git a/k12/Cargo.toml b/k12/Cargo.toml index a2b113f6..20278cb5 100644 --- a/k12/Cargo.toml +++ b/k12/Cargo.toml @@ -13,11 +13,12 @@ categories = ["cryptography", "no-std"] description = "Implementation of the KangarooTwelve family of extendable-output functions" [dependencies] -digest = "0.11" +digest = { version = "0.11", default-features = false } keccak = { version = "0.2", features = ["parallel"] } +sponge-cursor = "0.1" [dev-dependencies] -digest = { version = "0.11", features = ["alloc", "dev"] } +digest = { version = "0.11", default-features = false, features = ["alloc", "dev"] } hex-literal = "1" [features] diff --git a/k12/src/custom/borrow.rs b/k12/src/custom/borrow.rs index 6efa1d98..063e0233 100644 --- a/k12/src/custom/borrow.rs +++ b/k12/src/custom/borrow.rs @@ -1,7 +1,7 @@ use core::fmt; use digest::{ CollisionResistance, ExtendableOutput, ExtendableOutputReset, HashMarker, Reset, Update, - block_buffer::BlockSizes, + array::ArraySize, common::{AlgorithmName, BlockSizeUser}, consts::{U16, U32, U136, U168}, }; @@ -10,12 +10,12 @@ use crate::{Kt, KtReader, utils::length_encode}; /// Customized KangarooTwelve hasher generic over rate with borrrowed customization string. #[derive(Clone)] -pub struct CustomRefKt<'a, Rate: BlockSizes> { +pub struct CustomRefKt<'a, Rate: ArraySize> { customization: &'a [u8], inner: Kt, } -impl<'a, Rate: BlockSizes> CustomRefKt<'a, Rate> { +impl<'a, Rate: ArraySize> CustomRefKt<'a, Rate> { /// Create new customized KangarooTwelve hasher with borrrowed customization string. /// /// Note that this is an inherent method and `CustomRefKt` does not implement @@ -29,48 +29,48 @@ impl<'a, Rate: BlockSizes> CustomRefKt<'a, Rate> { } } -impl Default for CustomRefKt<'static, Rate> { +impl Default for CustomRefKt<'static, Rate> { #[inline] fn default() -> Self { Self::new_customized(&[]) } } -impl fmt::Debug for CustomRefKt<'_, Rate> { +impl fmt::Debug for CustomRefKt<'_, Rate> { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { write!(f, "CustomKt{} {{ ... }}", 4 * (200 - Rate::USIZE)) } } -impl AlgorithmName for CustomRefKt<'_, Rate> { +impl AlgorithmName for CustomRefKt<'_, Rate> { #[inline] fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result { Kt::::write_alg_name(f) } } -impl HashMarker for CustomRefKt<'_, Rate> {} +impl HashMarker for CustomRefKt<'_, Rate> {} -impl BlockSizeUser for CustomRefKt<'_, Rate> { +impl BlockSizeUser for CustomRefKt<'_, Rate> { type BlockSize = Rate; } -impl Update for CustomRefKt<'_, Rate> { +impl Update for CustomRefKt<'_, Rate> { #[inline] fn update(&mut self, data: &[u8]) { self.inner.update(data); } } -impl Reset for CustomRefKt<'_, Rate> { +impl Reset for CustomRefKt<'_, Rate> { #[inline] fn reset(&mut self) { self.inner.reset(); } } -impl CustomRefKt<'_, Rate> { +impl CustomRefKt<'_, Rate> { fn absorb_customization(&mut self) { self.inner.update(self.customization); let len = u64::try_from(self.customization.len()).expect("length always fits into `u64`"); @@ -78,7 +78,7 @@ impl CustomRefKt<'_, Rate> { } } -impl ExtendableOutput for CustomRefKt<'_, Rate> { +impl ExtendableOutput for CustomRefKt<'_, Rate> { type Reader = KtReader; #[inline] @@ -88,7 +88,7 @@ impl ExtendableOutput for CustomRefKt<'_, Rate> { } } -impl ExtendableOutputReset for CustomRefKt<'_, Rate> { +impl ExtendableOutputReset for CustomRefKt<'_, Rate> { #[inline] fn finalize_xof_reset(&mut self) -> Self::Reader { self.absorb_customization(); @@ -100,7 +100,7 @@ impl ExtendableOutputReset for CustomRefKt<'_, Rate> { // `inner` is zeroized by `Drop` and `customization` can not be zeroized #[cfg(feature = "zeroize")] -impl digest::zeroize::ZeroizeOnDrop for CustomRefKt<'_, Rate> {} +impl digest::zeroize::ZeroizeOnDrop for CustomRefKt<'_, Rate> {} /// Customized KT128 hasher with borrowed customization string. pub type CustomRefKt128<'a> = CustomRefKt<'a, U168>; diff --git a/k12/src/custom/owned.rs b/k12/src/custom/owned.rs index 1c109ae5..71ba00bb 100644 --- a/k12/src/custom/owned.rs +++ b/k12/src/custom/owned.rs @@ -5,7 +5,7 @@ use core::fmt; use digest::{ CollisionResistance, CustomizedInit, ExtendableOutput, ExtendableOutputReset, HashMarker, Reset, Update, - block_buffer::BlockSizes, + array::ArraySize, common::{AlgorithmName, BlockSizeUser}, consts::{U16, U32, U136, U168}, }; @@ -14,12 +14,12 @@ use crate::{Kt, KtReader, utils::length_encode}; /// Customized KangarooTwelve hasher generic over rate with owned customization string. #[derive(Clone)] -pub struct CustomKt { +pub struct CustomKt { customization: Vec, inner: Kt, } -impl CustomizedInit for CustomKt { +impl CustomizedInit for CustomKt { #[inline] fn new_customized(customization: &[u8]) -> Self { let len = u64::try_from(customization.len()).expect("length should always fit into `u64`"); @@ -37,48 +37,48 @@ impl CustomizedInit for CustomKt { } } -impl Default for CustomKt { +impl Default for CustomKt { #[inline] fn default() -> Self { Self::new_customized(&[]) } } -impl fmt::Debug for CustomKt { +impl fmt::Debug for CustomKt { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { write!(f, "CustomKt{} {{ ... }}", 4 * (200 - Rate::USIZE)) } } -impl AlgorithmName for CustomKt { +impl AlgorithmName for CustomKt { #[inline] fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result { Kt::::write_alg_name(f) } } -impl HashMarker for CustomKt {} +impl HashMarker for CustomKt {} -impl BlockSizeUser for CustomKt { +impl BlockSizeUser for CustomKt { type BlockSize = Rate; } -impl Update for CustomKt { +impl Update for CustomKt { #[inline] fn update(&mut self, data: &[u8]) { self.inner.update(data); } } -impl Reset for CustomKt { +impl Reset for CustomKt { #[inline] fn reset(&mut self) { self.inner.reset(); } } -impl ExtendableOutput for CustomKt { +impl ExtendableOutput for CustomKt { type Reader = KtReader; #[inline] @@ -88,7 +88,7 @@ impl ExtendableOutput for CustomKt { } } -impl ExtendableOutputReset for CustomKt { +impl ExtendableOutputReset for CustomKt { #[inline] fn finalize_xof_reset(&mut self) -> Self::Reader { self.inner.update(&self.customization); @@ -98,7 +98,7 @@ impl ExtendableOutputReset for CustomKt { } } -impl Drop for CustomKt { +impl Drop for CustomKt { #[inline] fn drop(&mut self) { #[cfg(feature = "zeroize")] @@ -111,7 +111,7 @@ impl Drop for CustomKt { } #[cfg(feature = "zeroize")] -impl digest::zeroize::ZeroizeOnDrop for CustomKt {} +impl digest::zeroize::ZeroizeOnDrop for CustomKt {} /// Customized KT128 hasher with owned customization string. pub type CustomKt128 = CustomKt; diff --git a/k12/src/lib.rs b/k12/src/lib.rs index 8c86b31a..7b444f78 100644 --- a/k12/src/lib.rs +++ b/k12/src/lib.rs @@ -13,8 +13,8 @@ pub use digest; use core::fmt; use digest::{ CollisionResistance, ExtendableOutput, ExtendableOutputReset, HashMarker, Reset, Update, - block_api::{AlgorithmName, BlockSizeUser}, - block_buffer::BlockSizes, + array::ArraySize, + common::{AlgorithmName, BlockSizeUser}, consts::{U16, U32, U136, U168}, }; @@ -35,23 +35,23 @@ mod utils; pub use custom::*; pub use reader::KtReader; -use consts::{CHUNK_SIZE_U64, FINAL_NODE_DS, INTERMEDIATE_NODE_DS, ROUNDS, SINGLE_NODE_DS}; +use consts::{CHUNK_SIZE_U64, FINAL_NODE_DS, ROUNDS, SINGLE_NODE_DS}; use turbo_shake::TurboShake; -use utils::{copy_cv, length_encode}; +use utils::length_encode; /// KangarooTwelve hasher generic over rate. /// /// Only `U136` and `U168` rates are supported which correspond to KT256 and KT128 respectively. /// Using other rates will result in a compilation error. #[derive(Clone)] -pub struct Kt { +pub struct Kt { accum_tshk: TurboShake, node_tshk: TurboShake, consumed_len: u64, keccak: keccak::Keccak, } -impl Default for Kt { +impl Default for Kt { #[inline] fn default() -> Self { const { assert!(matches!(Rate::USIZE, 136 | 168)) } @@ -64,27 +64,27 @@ impl Default for Kt { } } -impl fmt::Debug for Kt { +impl fmt::Debug for Kt { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { write!(f, "Kt{} {{ ... }}", 4 * (200 - Rate::USIZE)) } } -impl AlgorithmName for Kt { +impl AlgorithmName for Kt { #[inline] fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "KT{}", 4 * (200 - Rate::USIZE)) } } -impl HashMarker for Kt {} +impl HashMarker for Kt {} -impl BlockSizeUser for Kt { +impl BlockSizeUser for Kt { type BlockSize = Rate; } -impl Update for Kt { +impl Update for Kt { #[inline] fn update(&mut self, data: &[u8]) { let keccak = self.keccak; @@ -93,7 +93,7 @@ impl Update for Kt { } } -impl Reset for Kt { +impl Reset for Kt { #[inline] fn reset(&mut self) { self.accum_tshk.reset(); @@ -102,41 +102,36 @@ impl Reset for Kt { } } -impl Kt { +impl Kt { #[inline] fn raw_finalize(&mut self) -> KtReader { let keccak = self.keccak; - keccak.with_p1600::(|p1600| { - if self.consumed_len <= CHUNK_SIZE_U64 { - self.accum_tshk.finalize::(p1600); - } else { + if self.consumed_len <= CHUNK_SIZE_U64 { + self.accum_tshk.pad::(); + } else { + keccak.with_p1600::(|p1600| { let nodes_len = (self.consumed_len - 1) / CHUNK_SIZE_U64; let partial_node_len = self.consumed_len % CHUNK_SIZE_U64; if partial_node_len != 0 { - self.node_tshk.finalize::(p1600); // TODO: this should be [0u8; {200 - Rate}] let cv_dst = &mut [0u8; 200][..200 - Rate::USIZE]; - copy_cv(self.node_tshk.state(), cv_dst); + self.node_tshk.finalize_node(p1600, cv_dst); self.accum_tshk.absorb(p1600, cv_dst); } length_encode(nodes_len, |enc_len| self.accum_tshk.absorb(p1600, enc_len)); self.accum_tshk.absorb(p1600, b"\xFF\xFF"); - self.accum_tshk.finalize::(p1600); - }; - }); - - KtReader { - state: *self.accum_tshk.state(), - buffer: Default::default(), - keccak, - } + self.accum_tshk.pad::(); + }); + }; + + KtReader::new(self.accum_tshk.state(), keccak) } } -impl ExtendableOutput for Kt { +impl ExtendableOutput for Kt { type Reader = KtReader; #[inline] @@ -146,7 +141,7 @@ impl ExtendableOutput for Kt { } } -impl ExtendableOutputReset for Kt { +impl ExtendableOutputReset for Kt { #[inline] fn finalize_xof_reset(&mut self) -> Self::Reader { self.update(&[0x00]); @@ -156,7 +151,7 @@ impl ExtendableOutputReset for Kt { } } -impl Drop for Kt { +impl Drop for Kt { fn drop(&mut self) { #[cfg(feature = "zeroize")] { @@ -168,7 +163,7 @@ impl Drop for Kt { } #[cfg(feature = "zeroize")] -impl digest::zeroize::ZeroizeOnDrop for Kt {} +impl digest::zeroize::ZeroizeOnDrop for Kt {} /// KT128 hasher. pub type Kt128 = Kt; diff --git a/k12/src/node_turbo_shake.rs b/k12/src/node_turbo_shake.rs index 4b6bfba1..3603fa66 100644 --- a/k12/src/node_turbo_shake.rs +++ b/k12/src/node_turbo_shake.rs @@ -2,10 +2,7 @@ use crate::{ consts::{CHUNK_SIZE, INTERMEDIATE_NODE_DS, PAD}, utils::{copy_cv, xor_block}, }; -use digest::{ - array::{Array, ArraySize}, - block_buffer::BlockSizes, -}; +use digest::array::{Array, ArraySize}; use keccak::{Fn1600, State1600}; /// Parallel version of TurboSHAKE specialized for computation of chaining values. @@ -15,13 +12,13 @@ use keccak::{Fn1600, State1600}; /// fn par_turbo_shake( /// p1600: ParFn1600, /// data: &[u8; CHUNK_SIZE * B::PAR_SIZE_1600], -/// ) -> [[u8; {200 - RATE}]; B::PAR_SIZE_1600] { ... } +/// ) -> [[u64; {200 - RATE} / 8]; B::PAR_SIZE_1600] { ... } /// ``` /// But it requires advanced const generics or to deal with annoying `typenum`-based trait bounds, /// so instead we use "runtime" asserts which should be optimized out by the compiler, see: /// https://rust.godbolt.org/z/4Y7ervTd7 // TODO(MSRV-1.88): use `as_chunks::()` -pub(crate) fn parallel( +pub(crate) fn parallel( par_p1600: fn(&mut Array), data: &[u8], par_cv_dst: &mut [u8], @@ -75,9 +72,9 @@ pub(crate) fn parallel( /// fn turbo_shake-cv( /// p1600: Fn1600, /// data: &[u8; CHUNK_SIZE], -/// ) -> [u8; {200 - RATE}] { ... } +/// ) -> [u64; {200 - RATE} / 8] { ... } /// ``` -pub(crate) fn scalar(p1600: Fn1600, data: &[u8], cv_dst: &mut [u8]) { +pub(crate) fn scalar(p1600: Fn1600, data: &[u8], cv_dst: &mut [u8]) { assert_eq!(data.len(), CHUNK_SIZE); let cv_size = 200 - Rate::USIZE; assert_eq!(cv_dst.len(), cv_size); @@ -98,7 +95,7 @@ pub(crate) fn scalar(p1600: Fn1600, data: &[u8], cv_dst: &mut finalize::(p1600, &mut state, tail_data, cv_dst); } -pub(crate) fn finalize( +fn finalize( p1600: Fn1600, state: &mut State1600, tail_data: &[u8], @@ -109,7 +106,7 @@ pub(crate) fn finalize( copy_cv(state, cv_dst); } -fn process_tail_data(state: &mut State1600, tail_data: &[u8]) { +fn process_tail_data(state: &mut State1600, tail_data: &[u8]) { let block_size = Rate::USIZE; debug_assert_eq!( diff --git a/k12/src/reader.rs b/k12/src/reader.rs index 229b2e44..bed3b41a 100644 --- a/k12/src/reader.rs +++ b/k12/src/reader.rs @@ -1,43 +1,37 @@ use crate::consts::ROUNDS; use core::fmt; -use digest::{ - XofReader, - block_buffer::{BlockSizes, ReadBuffer}, -}; +use digest::{XofReader, array::ArraySize}; use keccak::{Keccak, State1600}; +use sponge_cursor::SpongeCursor; /// KangarooTwelve XOF reader generic over rate. #[derive(Clone)] -pub struct KtReader { - pub(crate) state: State1600, - pub(crate) buffer: ReadBuffer, - pub(crate) keccak: Keccak, +pub struct KtReader { + state: State1600, + cursor: SpongeCursor, + keccak: Keccak, } -impl XofReader for KtReader { - #[inline] - fn read(&mut self, buf: &mut [u8]) { - let Self { - state, - buffer, +impl KtReader { + pub(crate) fn new(state: &State1600, keccak: Keccak) -> Self { + Self { + state: *state, + cursor: Default::default(), keccak, - } = self; + } + } +} - buffer.read(buf, |block| { - let mut chunks = block.chunks_exact_mut(8); - for (src, dst) in state.iter().zip(&mut chunks) { - dst.copy_from_slice(&src.to_le_bytes()); - } - assert!( - chunks.into_remainder().is_empty(), - "rate is either 136 or 168", - ); - keccak.with_p1600::(|p1600| p1600(state)); +impl XofReader for KtReader { + #[inline] + fn read(&mut self, buf: &mut [u8]) { + self.keccak.with_p1600::(|p1600| { + self.cursor.squeeze_u64_le(&mut self.state, p1600, buf); }); } } -impl fmt::Debug for KtReader { +impl fmt::Debug for KtReader { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let debug_str = match Rate::USIZE { 168 => "Kt128Reader { ... }", @@ -48,7 +42,7 @@ impl fmt::Debug for KtReader { } } -impl Drop for KtReader { +impl Drop for KtReader { fn drop(&mut self) { #[cfg(feature = "zeroize")] { @@ -60,4 +54,4 @@ impl Drop for KtReader { } #[cfg(feature = "zeroize")] -impl digest::zeroize::ZeroizeOnDrop for KtReader {} +impl digest::zeroize::ZeroizeOnDrop for KtReader {} diff --git a/k12/src/turbo_shake.rs b/k12/src/turbo_shake.rs index c7314253..905b0601 100644 --- a/k12/src/turbo_shake.rs +++ b/k12/src/turbo_shake.rs @@ -1,42 +1,35 @@ -use digest::{ - block_api::Eager, - block_buffer::{BlockBuffer, BlockSizes}, -}; +use crate::consts::INTERMEDIATE_NODE_DS; +use digest::array::ArraySize; use keccak::{Fn1600, State1600}; +use sponge_cursor::SpongeCursor; -use crate::{consts::PAD, utils::xor_block}; +use crate::utils::copy_cv; #[derive(Default, Clone)] -pub(crate) struct TurboShake { +pub(crate) struct TurboShake { state: State1600, - buffer: BlockBuffer, + cursor: SpongeCursor, } -impl TurboShake { +impl TurboShake { pub(crate) fn absorb(&mut self, p1600: Fn1600, data: &[u8]) { - let Self { state, buffer } = self; - buffer.digest_blocks(data, |blocks| { - for block in blocks { - xor_block(state, block); - p1600(state) - } - }) + self.cursor.absorb_u64_le(&mut self.state, p1600, data); } - pub(crate) fn finalize(&mut self, p1600: Fn1600) { - let Self { state, buffer } = self; - let pos = buffer.get_pos(); - let mut block = buffer.pad_with_zeros(); - block[pos] = DS; - let n = block.len(); - block[n - 1] |= PAD; - xor_block(state, &block); - p1600(state); + pub(crate) fn pad(&mut self) { + let pos = self.cursor.pos(); + let word_offset = pos / 8; + let byte_offset = pos % 8; + + let pad = u64::from(DS) << (8 * byte_offset); + self.state[word_offset] ^= pad; + self.state[Rate::USIZE / 8 - 1] ^= 1 << 63; } - pub(crate) fn full_node_finalize(&mut self, p1600: Fn1600, cv_dst: &mut [u8]) { - let tail_data = self.buffer.get_data(); - crate::node_turbo_shake::finalize::(p1600, &mut self.state, tail_data, cv_dst); + pub(crate) fn finalize_node(&mut self, p1600: Fn1600, cv_dst: &mut [u8]) { + self.pad::(); + p1600(&mut self.state); + copy_cv(self.state(), cv_dst); } pub(crate) fn state(&self) -> &State1600 { @@ -45,17 +38,17 @@ impl TurboShake { pub(crate) fn reset(&mut self) { self.state = Default::default(); - self.buffer.reset(); + self.cursor = Default::default(); } } -impl Drop for TurboShake { +impl Drop for TurboShake { fn drop(&mut self) { #[cfg(feature = "zeroize")] { use digest::zeroize::Zeroize; self.state.zeroize(); - // `buffer` is zeroized by `Drop` + self.cursor.zeroize(); } } } diff --git a/k12/src/update.rs b/k12/src/update.rs index 9031c9ee..1fb40815 100644 --- a/k12/src/update.rs +++ b/k12/src/update.rs @@ -3,7 +3,7 @@ use crate::{ consts::{CHUNK_SIZE, CHUNK_SIZE_U64, ROUNDS, S0_DELIM}, node_turbo_shake, }; -use digest::{block_buffer::BlockSizes, typenum::Unsigned}; +use digest::{array::ArraySize, typenum::Unsigned}; use keccak::{Backend, BackendClosure}; /// Buffer size used by the update closure. @@ -11,12 +11,12 @@ use keccak::{Backend, BackendClosure}; /// 512 byte buffer is sufficient for 16x and 8x parallel KT128 and KT256 respectively. const BUFFER_LEN: usize = 512; -pub(crate) struct Closure<'a, Rate: BlockSizes> { +pub(crate) struct Closure<'a, Rate: ArraySize> { pub(crate) data: &'a [u8], pub(crate) kt: &'a mut Kt, } -impl BackendClosure for Closure<'_, Rate> { +impl BackendClosure for Closure<'_, Rate> { #[inline(always)] fn call_once(self) { let Kt { @@ -69,7 +69,7 @@ impl BackendClosure for Closure<'_, Rate> { node_tshk.absorb(p1600, part_data); let cv_dst = &mut cv_buf[..cv_len]; - node_tshk.full_node_finalize(p1600, cv_dst); + node_tshk.finalize_node(p1600, cv_dst); accum_tshk.absorb(p1600, cv_dst); *node_tshk = Default::default(); From a0629c000367de57eeee6a32c6fe901d4f47ebf1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D1=80=D1=82=D1=91=D0=BC=20=D0=9F=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=BE=D0=B2=20=5BArtyom=20Pavlov=5D?= Date: Sun, 3 May 2026 21:25:02 +0300 Subject: [PATCH 6/8] tweak k12 --- k12/Cargo.toml | 2 +- k12/src/consts.rs | 4 +--- k12/src/lib.rs | 8 +++++--- k12/src/node_turbo_shake.rs | 27 ++++++--------------------- k12/src/turbo_shake.rs | 6 +++--- k12/src/update.rs | 7 +++---- 6 files changed, 19 insertions(+), 35 deletions(-) diff --git a/k12/Cargo.toml b/k12/Cargo.toml index 20278cb5..6bf79acd 100644 --- a/k12/Cargo.toml +++ b/k12/Cargo.toml @@ -24,7 +24,7 @@ hex-literal = "1" [features] default = ["alloc"] alloc = ["digest/alloc"] -zeroize = ["digest/zeroize"] +zeroize = ["digest/zeroize", "sponge-cursor/zeroize"] [package.metadata.docs.rs] all-features = true diff --git a/k12/src/consts.rs b/k12/src/consts.rs index 21be28da..74462704 100644 --- a/k12/src/consts.rs +++ b/k12/src/consts.rs @@ -9,6 +9,4 @@ pub(crate) const INTERMEDIATE_NODE_DS: u8 = 0x0B; pub(crate) const FINAL_NODE_DS: u8 = 0x06; pub(crate) const S0_DELIM: u64 = 0x03; - -/// Padding byte -pub(crate) const PAD: u8 = 0x80; +pub(crate) const PAD: u64 = 1 << 63; diff --git a/k12/src/lib.rs b/k12/src/lib.rs index 7b444f78..0677ca88 100644 --- a/k12/src/lib.rs +++ b/k12/src/lib.rs @@ -107,6 +107,8 @@ impl Kt { fn raw_finalize(&mut self) -> KtReader { let keccak = self.keccak; + // Note that the reader applies permutation before reading from the state, + // so we only need to absorb the remaining data and pad the state if self.consumed_len <= CHUNK_SIZE_U64 { self.accum_tshk.pad::(); } else { @@ -117,7 +119,7 @@ impl Kt { if partial_node_len != 0 { // TODO: this should be [0u8; {200 - Rate}] let cv_dst = &mut [0u8; 200][..200 - Rate::USIZE]; - self.node_tshk.finalize_node(p1600, cv_dst); + self.node_tshk.finalize_intermediate_node(p1600, cv_dst); self.accum_tshk.absorb(p1600, cv_dst); } @@ -175,12 +177,12 @@ pub type Kt128Reader = KtReader; /// KT256 XOF reader. pub type Kt256Reader = KtReader; +// https://www.rfc-editor.org/rfc/rfc9861.html#section-7-7 impl CollisionResistance for Kt128 { - // https://www.rfc-editor.org/rfc/rfc9861.html#section-7-7 type CollisionResistance = U16; } +// https://www.rfc-editor.org/rfc/rfc9861.html#section-7-8 impl CollisionResistance for Kt256 { - // https://www.rfc-editor.org/rfc/rfc9861.html#section-7-8 type CollisionResistance = U32; } diff --git a/k12/src/node_turbo_shake.rs b/k12/src/node_turbo_shake.rs index 3603fa66..5c07c8e5 100644 --- a/k12/src/node_turbo_shake.rs +++ b/k12/src/node_turbo_shake.rs @@ -92,28 +92,13 @@ pub(crate) fn scalar(p1600: Fn1600, data: &[u8], cv_dst: &mut [ // Process the incomplete tail block let tail_data = blocks.remainder(); - finalize::(p1600, &mut state, tail_data, cv_dst); -} - -fn finalize( - p1600: Fn1600, - state: &mut State1600, - tail_data: &[u8], - cv_dst: &mut [u8], -) { - process_tail_data::(state, tail_data); - p1600(state); - copy_cv(state, cv_dst); + process_tail_data::(&mut state, tail_data); + p1600(&mut state); + copy_cv(&state, cv_dst); } fn process_tail_data(state: &mut State1600, tail_data: &[u8]) { - let block_size = Rate::USIZE; - - debug_assert_eq!( - tail_data.len(), - CHUNK_SIZE % block_size, - "tail_data has unexpected length", - ); + debug_assert_eq!(tail_data.len(), CHUNK_SIZE % Rate::USIZE); debug_assert_eq!(tail_data.len() % size_of::(), 0); xor_block(state, tail_data); @@ -121,9 +106,9 @@ fn process_tail_data(state: &mut State1600, tail_data: &[u8]) { // Apply padding by XORing the state. // Note that we use little endian byte order. let pos = tail_data.len() / size_of::(); - let pad_pos = block_size / size_of::() - 1; + let pad_pos = Rate::USIZE / size_of::() - 1; state[pos] ^= u64::from(INTERMEDIATE_NODE_DS); - state[pad_pos] ^= u64::from(PAD) << 56; + state[pad_pos] ^= PAD; } /// Tests vectors are generated by the `turbo-shake` crate diff --git a/k12/src/turbo_shake.rs b/k12/src/turbo_shake.rs index 905b0601..c5b57f7b 100644 --- a/k12/src/turbo_shake.rs +++ b/k12/src/turbo_shake.rs @@ -1,4 +1,4 @@ -use crate::consts::INTERMEDIATE_NODE_DS; +use crate::consts::{INTERMEDIATE_NODE_DS, PAD}; use digest::array::ArraySize; use keccak::{Fn1600, State1600}; use sponge_cursor::SpongeCursor; @@ -23,10 +23,10 @@ impl TurboShake { let pad = u64::from(DS) << (8 * byte_offset); self.state[word_offset] ^= pad; - self.state[Rate::USIZE / 8 - 1] ^= 1 << 63; + self.state[Rate::USIZE / 8 - 1] ^= PAD; } - pub(crate) fn finalize_node(&mut self, p1600: Fn1600, cv_dst: &mut [u8]) { + pub(crate) fn finalize_intermediate_node(&mut self, p1600: Fn1600, cv_dst: &mut [u8]) { self.pad::(); p1600(&mut self.state); copy_cv(self.state(), cv_dst); diff --git a/k12/src/update.rs b/k12/src/update.rs index 1fb40815..56bf27ad 100644 --- a/k12/src/update.rs +++ b/k12/src/update.rs @@ -59,9 +59,8 @@ impl BackendClosure for Closure<'_, Rate> { // Handle partially absorbed chunk if partial_chunk_len != 0 { let rem_len = CHUNK_SIZE - partial_chunk_len; - let split = data.split_at_checked(rem_len); - let Some((part_data, rem_data)) = split else { + let Some((part_data, rem_data)) = data.split_at_checked(rem_len) else { node_tshk.absorb(p1600, data); return; }; @@ -69,10 +68,10 @@ impl BackendClosure for Closure<'_, Rate> { node_tshk.absorb(p1600, part_data); let cv_dst = &mut cv_buf[..cv_len]; - node_tshk.finalize_node(p1600, cv_dst); + node_tshk.finalize_intermediate_node(p1600, cv_dst); accum_tshk.absorb(p1600, cv_dst); - *node_tshk = Default::default(); + node_tshk.reset(); data = rem_data; } From 3b592539fcc61a98269da01543d21b60aad20411 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D1=80=D1=82=D1=91=D0=BC=20=D0=9F=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=BE=D0=B2=20=5BArtyom=20Pavlov=5D?= Date: Mon, 4 May 2026 17:13:07 +0300 Subject: [PATCH 7/8] migrate rate to const generics --- Cargo.lock | 3 +- ascon-xof128/src/cxof.rs | 4 +- ascon-xof128/src/reader.rs | 6 +-- ascon-xof128/src/xof.rs | 4 +- cshake/src/lib.rs | 81 +++++++++++++++++++------------------ k12/src/custom/borrow.rs | 49 +++++++++++----------- k12/src/custom/owned.rs | 49 +++++++++++----------- k12/src/lib.rs | 65 +++++++++++++++-------------- k12/src/node_turbo_shake.rs | 36 ++++++++--------- k12/src/reader.rs | 20 ++++----- k12/src/turbo_shake.rs | 11 +++-- k12/src/update.rs | 14 +++---- turboshake/src/lib.rs | 75 +++++++++++++++++----------------- 13 files changed, 214 insertions(+), 203 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4bb575a8..72675b37 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -361,9 +361,8 @@ dependencies = [ [[package]] name = "sponge-cursor" version = "0.1.0" -source = "git+https://github.com/RustCrypto/utils?branch=add-sponge-cursor#d5900bf4ff1953757ad2a0a63d427c3e28ef6b26" +source = "git+https://github.com/RustCrypto/utils?branch=add-sponge-cursor#9b92270c2948906bc2ce687e4a080f59210c2808" dependencies = [ - "hybrid-array", "zeroize", ] diff --git a/ascon-xof128/src/cxof.rs b/ascon-xof128/src/cxof.rs index 390202bd..13539627 100644 --- a/ascon-xof128/src/cxof.rs +++ b/ascon-xof128/src/cxof.rs @@ -3,7 +3,7 @@ use digest::{ CollisionResistance, CustomizedInit, ExtendableOutput, HashMarker, OutputSizeUser, Update, common::AlgorithmName, common::hazmat::{DeserializeStateError, SerializableState, SerializedState}, - consts::{U8, U16, U32, U41}, + consts::{U16, U32, U41}, }; use sponge_cursor::SpongeCursor; @@ -20,7 +20,7 @@ use crate::{AsconXof128Reader, consts::CXOF_INIT_STATE}; #[derive(Clone, Debug)] pub struct AsconCxof128 { state: State, - cursor: SpongeCursor, + cursor: SpongeCursor<8>, } impl CustomizedInit for AsconCxof128 { diff --git a/ascon-xof128/src/reader.rs b/ascon-xof128/src/reader.rs index fef39221..a39f8c18 100644 --- a/ascon-xof128/src/reader.rs +++ b/ascon-xof128/src/reader.rs @@ -1,12 +1,12 @@ use ascon::State; -use digest::{XofReader, consts::U8}; +use digest::XofReader; use sponge_cursor::SpongeCursor; /// XOF reader used by Ascon-XOF128 and Ascon-CXOF128 #[derive(Clone, Debug)] pub struct AsconXof128Reader { state: State, - cursor: SpongeCursor, + cursor: SpongeCursor<8>, } impl AsconXof128Reader { @@ -22,7 +22,7 @@ impl XofReader for AsconXof128Reader { #[inline] fn read(&mut self, buf: &mut [u8]) { self.cursor - .squeeze_u64_le(&mut self.state, ascon::permute12, buf); + .squeeze_read_u64_le(&mut self.state, ascon::permute12, buf); } } diff --git a/ascon-xof128/src/xof.rs b/ascon-xof128/src/xof.rs index e0e5bb2b..d84680f3 100644 --- a/ascon-xof128/src/xof.rs +++ b/ascon-xof128/src/xof.rs @@ -5,7 +5,7 @@ use digest::{ Reset, Update, common::AlgorithmName, common::hazmat::{DeserializeStateError, SerializableState, SerializedState}, - consts::{U8, U16, U32, U41}, + consts::{U16, U32, U41}, }; use sponge_cursor::SpongeCursor; @@ -15,7 +15,7 @@ use crate::{AsconXof128Reader, consts::XOF_INIT_STATE}; #[derive(Clone)] pub struct AsconXof128 { state: State, - cursor: SpongeCursor, + cursor: SpongeCursor<8>, } impl Default for AsconXof128 { diff --git a/cshake/src/lib.rs b/cshake/src/lib.rs index 042b6d54..625861d7 100644 --- a/cshake/src/lib.rs +++ b/cshake/src/lib.rs @@ -11,10 +11,9 @@ pub use digest; -use core::{fmt, marker::PhantomData}; +use core::fmt; use digest::{ CollisionResistance, CustomizedInit, ExtendableOutput, HashMarker, Update, XofReader, - array::ArraySize, common::{AlgorithmName, BlockSizeUser}, consts::{U16, U32, U136, U168}, }; @@ -25,37 +24,36 @@ const SHAKE_PAD: u8 = 0x1F; const CSHAKE_PAD: u8 = 0x04; /// cSHAKE128 hasher. -pub type CShake128 = CShake; +pub type CShake128 = CShake<168>; /// cSHAKE256 hasher. -pub type CShake256 = CShake; +pub type CShake256 = CShake<136>; /// cSHAKE hasher generic over rate. /// -/// Rate MUST be either [`U168`] or [`U136`] for cSHAKE128 and cSHAKE256 respectively. +/// Rate MUST be either 168 or 136 for cSHAKE128 and cSHAKE256 respectively. #[derive(Clone)] -pub struct CShake { +pub struct CShake { state: State1600, - cursor: SpongeCursor, + cursor: SpongeCursor, pad: u8, keccak: Keccak, - _pd: PhantomData, } -impl Default for CShake { +impl Default for CShake { #[inline] fn default() -> Self { Self::new_with_function_name(b"", b"") } } -impl CShake { +impl CShake { /// Creates a new cSHAKE instance with the given function name and customization. /// /// Note that the function name is intended for use by NIST and should only be set to /// values defined by NIST. You probably don't need to use this function. pub fn new_with_function_name(function_name: &[u8], customization: &[u8]) -> Self { const { - assert!(Rate::USIZE == 168 || Rate::USIZE == 136, "unsupported rate"); + assert!(RATE == 168 || RATE == 136, "unsupported rate"); } let keccak = Keccak::new(); @@ -67,7 +65,6 @@ impl CShake { cursor: Default::default(), pad: SHAKE_PAD, keccak, - _pd: PhantomData, }; } @@ -80,11 +77,12 @@ impl CShake { } keccak.with_f1600(|f1600| { - let mut cursor: SpongeCursor = Default::default(); + let mut cursor: SpongeCursor = Default::default(); let state = &mut state; let mut b = [0u8; 9]; - cursor.absorb_u64_le(state, f1600, left_encode(Rate::U64, &mut b)); + let rate_u64 = u64::try_from(RATE).expect("RATE is smaller than 200"); + cursor.absorb_u64_le(state, f1600, left_encode(rate_u64, &mut b)); let mut encode_str = |str: &[u8]| { let str_bits_len = 8 * u64::try_from(str.len()) @@ -107,25 +105,20 @@ impl CShake { cursor: Default::default(), pad: CSHAKE_PAD, keccak, - _pd: PhantomData, } } } -impl CustomizedInit for CShake { +impl CustomizedInit for CShake { #[inline] fn new_customized(customization: &[u8]) -> Self { Self::new_with_function_name(&[], customization) } } -impl HashMarker for CShake {} +impl HashMarker for CShake {} -impl BlockSizeUser for CShake { - type BlockSize = Rate; -} - -impl Update for CShake { +impl Update for CShake { fn update(&mut self, data: &[u8]) { self.keccak.with_f1600(|f1600| { self.cursor.absorb_u64_le(&mut self.state, f1600, data); @@ -133,8 +126,8 @@ impl Update for CShake { } } -impl ExtendableOutput for CShake { - type Reader = CShakeReader; +impl ExtendableOutput for CShake { + type Reader = CShakeReader; #[inline] fn finalize_xof(mut self) -> Self::Reader { @@ -144,7 +137,7 @@ impl ExtendableOutput for CShake { let pad = u64::from(self.pad) << (8 * byte_offset); self.state[word_offset] ^= pad; - self.state[Rate::USIZE / 8 - 1] ^= 1 << 63; + self.state[RATE / 8 - 1] ^= 1 << 63; // Note that `CShakeReader` applies the permutation to the state before reading from it @@ -156,9 +149,9 @@ impl ExtendableOutput for CShake { } } -impl AlgorithmName for CShake { +impl AlgorithmName for CShake { fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result { - let alg_name = match Rate::USIZE { + let alg_name = match RATE { 168 => "cSHAKE128", 136 => "cSHAKE256", _ => unreachable!(), @@ -167,9 +160,9 @@ impl AlgorithmName for CShake { } } -impl fmt::Debug for CShake { +impl fmt::Debug for CShake { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let debug_str = match Rate::USIZE { + let debug_str = match RATE { 168 => "CShake128 { ... }", 136 => "CShake256 { ... }", _ => unreachable!(), @@ -178,7 +171,7 @@ impl fmt::Debug for CShake { } } -impl Drop for CShake { +impl Drop for CShake { fn drop(&mut self) { #[cfg(feature = "zeroize")] { @@ -191,37 +184,37 @@ impl Drop for CShake { } #[cfg(feature = "zeroize")] -impl digest::zeroize::ZeroizeOnDrop for CShake {} +impl digest::zeroize::ZeroizeOnDrop for CShake {} /// Generic cSHAKE XOF reader #[derive(Clone)] -pub struct CShakeReader { +pub struct CShakeReader { state: State1600, - cursor: SpongeCursor, + cursor: SpongeCursor, keccak: Keccak, } -impl XofReader for CShakeReader { +impl XofReader for CShakeReader { #[inline] fn read(&mut self, buf: &mut [u8]) { self.keccak.with_f1600(|f1600| { - self.cursor.squeeze_u64_le(&mut self.state, f1600, buf); + self.cursor.squeeze_read_u64_le(&mut self.state, f1600, buf); }); } } -impl fmt::Debug for CShakeReader { +impl fmt::Debug for CShakeReader { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let debug_str = match Rate::USIZE { - 168 => "TurboShakeReader128 { ... }", - 136 => "TurboShakeReader256 { ... }", + let debug_str = match RATE { + 168 => "CShakeReader128 { ... }", + 136 => "CShakeReader256 { ... }", _ => unreachable!(), }; f.write_str(debug_str) } } -impl Drop for CShakeReader { +impl Drop for CShakeReader { fn drop(&mut self) { #[cfg(feature = "zeroize")] { @@ -240,3 +233,11 @@ impl CollisionResistance for CShake128 { impl CollisionResistance for CShake256 { type CollisionResistance = U32; } + +impl BlockSizeUser for CShake128 { + type BlockSize = U168; +} + +impl BlockSizeUser for CShake256 { + type BlockSize = U136; +} diff --git a/k12/src/custom/borrow.rs b/k12/src/custom/borrow.rs index 063e0233..d8a86982 100644 --- a/k12/src/custom/borrow.rs +++ b/k12/src/custom/borrow.rs @@ -1,7 +1,6 @@ use core::fmt; use digest::{ CollisionResistance, ExtendableOutput, ExtendableOutputReset, HashMarker, Reset, Update, - array::ArraySize, common::{AlgorithmName, BlockSizeUser}, consts::{U16, U32, U136, U168}, }; @@ -10,12 +9,12 @@ use crate::{Kt, KtReader, utils::length_encode}; /// Customized KangarooTwelve hasher generic over rate with borrrowed customization string. #[derive(Clone)] -pub struct CustomRefKt<'a, Rate: ArraySize> { +pub struct CustomRefKt<'a, const RATE: usize> { customization: &'a [u8], - inner: Kt, + inner: Kt, } -impl<'a, Rate: ArraySize> CustomRefKt<'a, Rate> { +impl<'a, const RATE: usize> CustomRefKt<'a, RATE> { /// Create new customized KangarooTwelve hasher with borrrowed customization string. /// /// Note that this is an inherent method and `CustomRefKt` does not implement @@ -29,48 +28,44 @@ impl<'a, Rate: ArraySize> CustomRefKt<'a, Rate> { } } -impl Default for CustomRefKt<'static, Rate> { +impl Default for CustomRefKt<'static, RATE> { #[inline] fn default() -> Self { Self::new_customized(&[]) } } -impl fmt::Debug for CustomRefKt<'_, Rate> { +impl fmt::Debug for CustomRefKt<'_, RATE> { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - write!(f, "CustomKt{} {{ ... }}", 4 * (200 - Rate::USIZE)) + write!(f, "CustomKt{} {{ ... }}", 4 * (200 - RATE)) } } -impl AlgorithmName for CustomRefKt<'_, Rate> { +impl AlgorithmName for CustomRefKt<'_, RATE> { #[inline] fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result { - Kt::::write_alg_name(f) + Kt::::write_alg_name(f) } } -impl HashMarker for CustomRefKt<'_, Rate> {} +impl HashMarker for CustomRefKt<'_, RATE> {} -impl BlockSizeUser for CustomRefKt<'_, Rate> { - type BlockSize = Rate; -} - -impl Update for CustomRefKt<'_, Rate> { +impl Update for CustomRefKt<'_, RATE> { #[inline] fn update(&mut self, data: &[u8]) { self.inner.update(data); } } -impl Reset for CustomRefKt<'_, Rate> { +impl Reset for CustomRefKt<'_, RATE> { #[inline] fn reset(&mut self) { self.inner.reset(); } } -impl CustomRefKt<'_, Rate> { +impl CustomRefKt<'_, RATE> { fn absorb_customization(&mut self) { self.inner.update(self.customization); let len = u64::try_from(self.customization.len()).expect("length always fits into `u64`"); @@ -78,8 +73,8 @@ impl CustomRefKt<'_, Rate> { } } -impl ExtendableOutput for CustomRefKt<'_, Rate> { - type Reader = KtReader; +impl ExtendableOutput for CustomRefKt<'_, RATE> { + type Reader = KtReader; #[inline] fn finalize_xof(mut self) -> Self::Reader { @@ -88,7 +83,7 @@ impl ExtendableOutput for CustomRefKt<'_, Rate> { } } -impl ExtendableOutputReset for CustomRefKt<'_, Rate> { +impl ExtendableOutputReset for CustomRefKt<'_, RATE> { #[inline] fn finalize_xof_reset(&mut self) -> Self::Reader { self.absorb_customization(); @@ -100,12 +95,12 @@ impl ExtendableOutputReset for CustomRefKt<'_, Rate> { // `inner` is zeroized by `Drop` and `customization` can not be zeroized #[cfg(feature = "zeroize")] -impl digest::zeroize::ZeroizeOnDrop for CustomRefKt<'_, Rate> {} +impl digest::zeroize::ZeroizeOnDrop for CustomRefKt<'_, RATE> {} /// Customized KT128 hasher with borrowed customization string. -pub type CustomRefKt128<'a> = CustomRefKt<'a, U168>; +pub type CustomRefKt128<'a> = CustomRefKt<'a, 168>; /// Customized KT256 hasher with borrowed customization string. -pub type CustomRefKt256<'a> = CustomRefKt<'a, U136>; +pub type CustomRefKt256<'a> = CustomRefKt<'a, 136>; impl CollisionResistance for CustomRefKt128<'_> { type CollisionResistance = U16; @@ -114,3 +109,11 @@ impl CollisionResistance for CustomRefKt128<'_> { impl CollisionResistance for CustomRefKt256<'_> { type CollisionResistance = U32; } + +impl BlockSizeUser for CustomRefKt128<'_> { + type BlockSize = U168; +} + +impl BlockSizeUser for CustomRefKt256<'_> { + type BlockSize = U136; +} diff --git a/k12/src/custom/owned.rs b/k12/src/custom/owned.rs index 71ba00bb..44e37be2 100644 --- a/k12/src/custom/owned.rs +++ b/k12/src/custom/owned.rs @@ -5,7 +5,6 @@ use core::fmt; use digest::{ CollisionResistance, CustomizedInit, ExtendableOutput, ExtendableOutputReset, HashMarker, Reset, Update, - array::ArraySize, common::{AlgorithmName, BlockSizeUser}, consts::{U16, U32, U136, U168}, }; @@ -14,12 +13,12 @@ use crate::{Kt, KtReader, utils::length_encode}; /// Customized KangarooTwelve hasher generic over rate with owned customization string. #[derive(Clone)] -pub struct CustomKt { +pub struct CustomKt { customization: Vec, - inner: Kt, + inner: Kt, } -impl CustomizedInit for CustomKt { +impl CustomizedInit for CustomKt { #[inline] fn new_customized(customization: &[u8]) -> Self { let len = u64::try_from(customization.len()).expect("length should always fit into `u64`"); @@ -37,49 +36,45 @@ impl CustomizedInit for CustomKt { } } -impl Default for CustomKt { +impl Default for CustomKt { #[inline] fn default() -> Self { Self::new_customized(&[]) } } -impl fmt::Debug for CustomKt { +impl fmt::Debug for CustomKt { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - write!(f, "CustomKt{} {{ ... }}", 4 * (200 - Rate::USIZE)) + write!(f, "CustomKt{} {{ ... }}", 4 * (200 - RATE)) } } -impl AlgorithmName for CustomKt { +impl AlgorithmName for CustomKt { #[inline] fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result { - Kt::::write_alg_name(f) + Kt::::write_alg_name(f) } } -impl HashMarker for CustomKt {} +impl HashMarker for CustomKt {} -impl BlockSizeUser for CustomKt { - type BlockSize = Rate; -} - -impl Update for CustomKt { +impl Update for CustomKt { #[inline] fn update(&mut self, data: &[u8]) { self.inner.update(data); } } -impl Reset for CustomKt { +impl Reset for CustomKt { #[inline] fn reset(&mut self) { self.inner.reset(); } } -impl ExtendableOutput for CustomKt { - type Reader = KtReader; +impl ExtendableOutput for CustomKt { + type Reader = KtReader; #[inline] fn finalize_xof(mut self) -> Self::Reader { @@ -88,7 +83,7 @@ impl ExtendableOutput for CustomKt { } } -impl ExtendableOutputReset for CustomKt { +impl ExtendableOutputReset for CustomKt { #[inline] fn finalize_xof_reset(&mut self) -> Self::Reader { self.inner.update(&self.customization); @@ -98,7 +93,7 @@ impl ExtendableOutputReset for CustomKt { } } -impl Drop for CustomKt { +impl Drop for CustomKt { #[inline] fn drop(&mut self) { #[cfg(feature = "zeroize")] @@ -111,12 +106,12 @@ impl Drop for CustomKt { } #[cfg(feature = "zeroize")] -impl digest::zeroize::ZeroizeOnDrop for CustomKt {} +impl digest::zeroize::ZeroizeOnDrop for CustomKt {} /// Customized KT128 hasher with owned customization string. -pub type CustomKt128 = CustomKt; +pub type CustomKt128 = CustomKt<168>; /// Customized KT256 hasher with owned customization string. -pub type CustomKt256 = CustomKt; +pub type CustomKt256 = CustomKt<136>; impl CollisionResistance for CustomKt128 { // https://www.rfc-editor.org/rfc/rfc9861.html#section-7-7 @@ -127,3 +122,11 @@ impl CollisionResistance for CustomKt256 { // https://www.rfc-editor.org/rfc/rfc9861.html#section-7-8 type CollisionResistance = U32; } + +impl BlockSizeUser for CustomKt128 { + type BlockSize = U168; +} + +impl BlockSizeUser for CustomKt256 { + type BlockSize = U136; +} diff --git a/k12/src/lib.rs b/k12/src/lib.rs index 0677ca88..d21a6090 100644 --- a/k12/src/lib.rs +++ b/k12/src/lib.rs @@ -13,7 +13,6 @@ pub use digest; use core::fmt; use digest::{ CollisionResistance, ExtendableOutput, ExtendableOutputReset, HashMarker, Reset, Update, - array::ArraySize, common::{AlgorithmName, BlockSizeUser}, consts::{U16, U32, U136, U168}, }; @@ -44,17 +43,17 @@ use utils::length_encode; /// Only `U136` and `U168` rates are supported which correspond to KT256 and KT128 respectively. /// Using other rates will result in a compilation error. #[derive(Clone)] -pub struct Kt { - accum_tshk: TurboShake, - node_tshk: TurboShake, +pub struct Kt { + accum_tshk: TurboShake, + node_tshk: TurboShake, consumed_len: u64, keccak: keccak::Keccak, } -impl Default for Kt { +impl Default for Kt { #[inline] fn default() -> Self { - const { assert!(matches!(Rate::USIZE, 136 | 168)) } + const { assert!(matches!(RATE, 136 | 168)) } Self { accum_tshk: Default::default(), node_tshk: Default::default(), @@ -64,36 +63,32 @@ impl Default for Kt { } } -impl fmt::Debug for Kt { +impl fmt::Debug for Kt { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - write!(f, "Kt{} {{ ... }}", 4 * (200 - Rate::USIZE)) + write!(f, "Kt{} {{ ... }}", 4 * (200 - RATE)) } } -impl AlgorithmName for Kt { +impl AlgorithmName for Kt { #[inline] fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "KT{}", 4 * (200 - Rate::USIZE)) + write!(f, "KT{}", 4 * (200 - RATE)) } } -impl HashMarker for Kt {} +impl HashMarker for Kt {} -impl BlockSizeUser for Kt { - type BlockSize = Rate; -} - -impl Update for Kt { +impl Update for Kt { #[inline] fn update(&mut self, data: &[u8]) { let keccak = self.keccak; - let closure = update::Closure::<'_, Rate> { data, kt: self }; + let closure = update::Closure::<'_, RATE> { data, kt: self }; keccak.with_backend(closure); } } -impl Reset for Kt { +impl Reset for Kt { #[inline] fn reset(&mut self) { self.accum_tshk.reset(); @@ -102,9 +97,9 @@ impl Reset for Kt { } } -impl Kt { +impl Kt { #[inline] - fn raw_finalize(&mut self) -> KtReader { + fn raw_finalize(&mut self) -> KtReader { let keccak = self.keccak; // Note that the reader applies permutation before reading from the state, @@ -117,8 +112,8 @@ impl Kt { let partial_node_len = self.consumed_len % CHUNK_SIZE_U64; if partial_node_len != 0 { - // TODO: this should be [0u8; {200 - Rate}] - let cv_dst = &mut [0u8; 200][..200 - Rate::USIZE]; + // TODO: this should be [0u8; {200 - RATE}] + let cv_dst = &mut [0u8; 200][..200 - RATE]; self.node_tshk.finalize_intermediate_node(p1600, cv_dst); self.accum_tshk.absorb(p1600, cv_dst); } @@ -133,8 +128,8 @@ impl Kt { } } -impl ExtendableOutput for Kt { - type Reader = KtReader; +impl ExtendableOutput for Kt { + type Reader = KtReader; #[inline] fn finalize_xof(mut self) -> Self::Reader { @@ -143,7 +138,7 @@ impl ExtendableOutput for Kt { } } -impl ExtendableOutputReset for Kt { +impl ExtendableOutputReset for Kt { #[inline] fn finalize_xof_reset(&mut self) -> Self::Reader { self.update(&[0x00]); @@ -153,7 +148,7 @@ impl ExtendableOutputReset for Kt { } } -impl Drop for Kt { +impl Drop for Kt { fn drop(&mut self) { #[cfg(feature = "zeroize")] { @@ -165,17 +160,17 @@ impl Drop for Kt { } #[cfg(feature = "zeroize")] -impl digest::zeroize::ZeroizeOnDrop for Kt {} +impl digest::zeroize::ZeroizeOnDrop for Kt {} /// KT128 hasher. -pub type Kt128 = Kt; +pub type Kt128 = Kt<168>; /// KT256 hasher. -pub type Kt256 = Kt; +pub type Kt256 = Kt<136>; /// KT128 XOF reader. -pub type Kt128Reader = KtReader; +pub type Kt128Reader = KtReader<168>; /// KT256 XOF reader. -pub type Kt256Reader = KtReader; +pub type Kt256Reader = KtReader<136>; // https://www.rfc-editor.org/rfc/rfc9861.html#section-7-7 impl CollisionResistance for Kt128 { @@ -186,3 +181,11 @@ impl CollisionResistance for Kt128 { impl CollisionResistance for Kt256 { type CollisionResistance = U32; } + +impl BlockSizeUser for Kt128 { + type BlockSize = U168; +} + +impl BlockSizeUser for Kt256 { + type BlockSize = U136; +} diff --git a/k12/src/node_turbo_shake.rs b/k12/src/node_turbo_shake.rs index 5c07c8e5..93653aab 100644 --- a/k12/src/node_turbo_shake.rs +++ b/k12/src/node_turbo_shake.rs @@ -18,19 +18,19 @@ use keccak::{Fn1600, State1600}; /// so instead we use "runtime" asserts which should be optimized out by the compiler, see: /// https://rust.godbolt.org/z/4Y7ervTd7 // TODO(MSRV-1.88): use `as_chunks::()` -pub(crate) fn parallel( +pub(crate) fn parallel( par_p1600: fn(&mut Array), data: &[u8], par_cv_dst: &mut [u8], ) { let par_size = ParSize::USIZE; assert_eq!(data.len(), CHUNK_SIZE * par_size); - let cv_size = 200 - Rate::USIZE; + let cv_size = 200 - RATE; assert_eq!(par_cv_dst.len(), cv_size * par_size); let mut par_state: Array = Default::default(); - let block_size = Rate::USIZE; + let block_size = RATE; let full_blocks = CHUNK_SIZE / block_size; // Process full blocks @@ -53,7 +53,7 @@ pub(crate) fn parallel( let block_offset = chunk_offset + full_blocks * block_size; let tail_data = &data[block_offset..][..tail_block_size]; - process_tail_data::(state, tail_data); + process_tail_data::(state, tail_data); } par_p1600(&mut par_state); @@ -74,14 +74,14 @@ pub(crate) fn parallel( /// data: &[u8; CHUNK_SIZE], /// ) -> [u64; {200 - RATE} / 8] { ... } /// ``` -pub(crate) fn scalar(p1600: Fn1600, data: &[u8], cv_dst: &mut [u8]) { +pub(crate) fn scalar(p1600: Fn1600, data: &[u8], cv_dst: &mut [u8]) { assert_eq!(data.len(), CHUNK_SIZE); - let cv_size = 200 - Rate::USIZE; + let cv_size = 200 - RATE; assert_eq!(cv_dst.len(), cv_size); let mut state = State1600::default(); - let block_size = Rate::USIZE; + let block_size = RATE; let mut blocks = data.chunks_exact(block_size); // Process full blocks @@ -92,13 +92,13 @@ pub(crate) fn scalar(p1600: Fn1600, data: &[u8], cv_dst: &mut [ // Process the incomplete tail block let tail_data = blocks.remainder(); - process_tail_data::(&mut state, tail_data); + process_tail_data::(&mut state, tail_data); p1600(&mut state); copy_cv(&state, cv_dst); } -fn process_tail_data(state: &mut State1600, tail_data: &[u8]) { - debug_assert_eq!(tail_data.len(), CHUNK_SIZE % Rate::USIZE); +fn process_tail_data(state: &mut State1600, tail_data: &[u8]) { + debug_assert_eq!(tail_data.len(), CHUNK_SIZE % RATE); debug_assert_eq!(tail_data.len() % size_of::(), 0); xor_block(state, tail_data); @@ -106,7 +106,7 @@ fn process_tail_data(state: &mut State1600, tail_data: &[u8]) { // Apply padding by XORing the state. // Note that we use little endian byte order. let pos = tail_data.len() / size_of::(); - let pad_pos = Rate::USIZE / size_of::() - 1; + let pad_pos = RATE / size_of::() - 1; state[pos] ^= u64::from(INTERMEDIATE_NODE_DS); state[pad_pos] ^= PAD; } @@ -116,7 +116,7 @@ fn process_tail_data(state: &mut State1600, tail_data: &[u8]) { mod tests { use super::{parallel, scalar}; use crate::consts::{CHUNK_SIZE, ROUNDS}; - use digest::array::typenum::{U136, U168, Unsigned}; + use digest::array::typenum::Unsigned; use keccak::{Backend, BackendClosure}; const CHUNKS: usize = 32; @@ -150,7 +150,7 @@ mod tests { let mut cvs_chunks = cvs.chunks_exact_mut(KT128_CV_LEN); for (data_chunk, par_cv) in (&mut data_chunks).zip(&mut cvs_chunks) { - scalar::(p1600, data_chunk, par_cv); + scalar::<168>(p1600, data_chunk, par_cv); } assert!(data_chunks.remainder().is_empty()); @@ -167,7 +167,7 @@ mod tests { let mut cvs_chunks = cvs.chunks_exact_mut(KT256_CV_LEN); for (data_chunk, par_cv) in (&mut data_chunks).zip(&mut cvs_chunks) { - scalar::(p1600, data_chunk, par_cv); + scalar::<136>(p1600, data_chunk, par_cv); } assert!(data_chunks.remainder().is_empty()); @@ -194,14 +194,14 @@ mod tests { let mut par_cvs = cvs.chunks_exact_mut(par_cv_size); for (data_chunk, par_cv) in (&mut data_chunks).zip(&mut par_cvs) { - parallel::<_, U168>(par_p1600, data_chunk, par_cv); + parallel::<_, 168>(par_p1600, data_chunk, par_cv); } let mut data_chunks = data_chunks.remainder().chunks_exact(CHUNK_SIZE); let mut cvs_chunks = par_cvs.into_remainder().chunks_exact_mut(KT128_CV_LEN); for (data_chunk, par_cv) in (&mut data_chunks).zip(&mut cvs_chunks) { - scalar::(p1600, data_chunk, par_cv); + scalar::<168>(p1600, data_chunk, par_cv); } assert!(data_chunks.remainder().is_empty()); @@ -231,14 +231,14 @@ mod tests { let mut par_cvs = cvs.chunks_exact_mut(par_cv_size); for (data_chunk, par_cv) in (&mut data_chunks).zip(&mut par_cvs) { - parallel::<_, U136>(par_p1600, data_chunk, par_cv); + parallel::<_, 136>(par_p1600, data_chunk, par_cv); } let mut data_chunks = data_chunks.remainder().chunks_exact(CHUNK_SIZE); let mut cvs_chunks = par_cvs.into_remainder().chunks_exact_mut(KT256_CV_LEN); for (data_chunk, par_cv) in (&mut data_chunks).zip(&mut cvs_chunks) { - scalar::(p1600, data_chunk, par_cv); + scalar::<136>(p1600, data_chunk, par_cv); } assert!(data_chunks.remainder().is_empty()); diff --git a/k12/src/reader.rs b/k12/src/reader.rs index bed3b41a..7220e465 100644 --- a/k12/src/reader.rs +++ b/k12/src/reader.rs @@ -1,18 +1,18 @@ use crate::consts::ROUNDS; use core::fmt; -use digest::{XofReader, array::ArraySize}; +use digest::XofReader; use keccak::{Keccak, State1600}; use sponge_cursor::SpongeCursor; /// KangarooTwelve XOF reader generic over rate. #[derive(Clone)] -pub struct KtReader { +pub struct KtReader { state: State1600, - cursor: SpongeCursor, + cursor: SpongeCursor, keccak: Keccak, } -impl KtReader { +impl KtReader { pub(crate) fn new(state: &State1600, keccak: Keccak) -> Self { Self { state: *state, @@ -22,18 +22,18 @@ impl KtReader { } } -impl XofReader for KtReader { +impl XofReader for KtReader { #[inline] fn read(&mut self, buf: &mut [u8]) { self.keccak.with_p1600::(|p1600| { - self.cursor.squeeze_u64_le(&mut self.state, p1600, buf); + self.cursor.squeeze_read_u64_le(&mut self.state, p1600, buf); }); } } -impl fmt::Debug for KtReader { +impl fmt::Debug for KtReader { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let debug_str = match Rate::USIZE { + let debug_str = match RATE { 168 => "Kt128Reader { ... }", 136 => "Kt256Reader { ... }", _ => unreachable!(), @@ -42,7 +42,7 @@ impl fmt::Debug for KtReader { } } -impl Drop for KtReader { +impl Drop for KtReader { fn drop(&mut self) { #[cfg(feature = "zeroize")] { @@ -54,4 +54,4 @@ impl Drop for KtReader { } #[cfg(feature = "zeroize")] -impl digest::zeroize::ZeroizeOnDrop for KtReader {} +impl digest::zeroize::ZeroizeOnDrop for KtReader {} diff --git a/k12/src/turbo_shake.rs b/k12/src/turbo_shake.rs index c5b57f7b..14687385 100644 --- a/k12/src/turbo_shake.rs +++ b/k12/src/turbo_shake.rs @@ -1,17 +1,16 @@ use crate::consts::{INTERMEDIATE_NODE_DS, PAD}; -use digest::array::ArraySize; use keccak::{Fn1600, State1600}; use sponge_cursor::SpongeCursor; use crate::utils::copy_cv; #[derive(Default, Clone)] -pub(crate) struct TurboShake { +pub(crate) struct TurboShake { state: State1600, - cursor: SpongeCursor, + cursor: SpongeCursor, } -impl TurboShake { +impl TurboShake { pub(crate) fn absorb(&mut self, p1600: Fn1600, data: &[u8]) { self.cursor.absorb_u64_le(&mut self.state, p1600, data); } @@ -23,7 +22,7 @@ impl TurboShake { let pad = u64::from(DS) << (8 * byte_offset); self.state[word_offset] ^= pad; - self.state[Rate::USIZE / 8 - 1] ^= PAD; + self.state[RATE / 8 - 1] ^= PAD; } pub(crate) fn finalize_intermediate_node(&mut self, p1600: Fn1600, cv_dst: &mut [u8]) { @@ -42,7 +41,7 @@ impl TurboShake { } } -impl Drop for TurboShake { +impl Drop for TurboShake { fn drop(&mut self) { #[cfg(feature = "zeroize")] { diff --git a/k12/src/update.rs b/k12/src/update.rs index 56bf27ad..afbfa691 100644 --- a/k12/src/update.rs +++ b/k12/src/update.rs @@ -3,7 +3,7 @@ use crate::{ consts::{CHUNK_SIZE, CHUNK_SIZE_U64, ROUNDS, S0_DELIM}, node_turbo_shake, }; -use digest::{array::ArraySize, typenum::Unsigned}; +use digest::typenum::Unsigned; use keccak::{Backend, BackendClosure}; /// Buffer size used by the update closure. @@ -11,12 +11,12 @@ use keccak::{Backend, BackendClosure}; /// 512 byte buffer is sufficient for 16x and 8x parallel KT128 and KT256 respectively. const BUFFER_LEN: usize = 512; -pub(crate) struct Closure<'a, Rate: ArraySize> { +pub(crate) struct Closure<'a, const RATE: usize> { pub(crate) data: &'a [u8], - pub(crate) kt: &'a mut Kt, + pub(crate) kt: &'a mut Kt, } -impl BackendClosure for Closure<'_, Rate> { +impl BackendClosure for Closure<'_, RATE> { #[inline(always)] fn call_once(self) { let Kt { @@ -30,7 +30,7 @@ impl BackendClosure for Closure<'_, Rate> { let par_p1600 = B::get_par_p1600::(); let p1600 = B::get_p1600::(); let par_size = B::ParSize1600::USIZE; - let cv_len = 200 - Rate::USIZE; + let cv_len = 200 - RATE; // TODO: this should be [0u8; par_size * cv_len]` let mut cv_buf = [0u8; BUFFER_LEN]; @@ -85,7 +85,7 @@ impl BackendClosure for Closure<'_, Rate> { let mut par_data_chunks = data.chunks_exact(par_size * CHUNK_SIZE); for par_data_chunk in &mut par_data_chunks { - node_turbo_shake::parallel::<_, Rate>(par_p1600, par_data_chunk, cvs_dst); + node_turbo_shake::parallel::<_, RATE>(par_p1600, par_data_chunk, cvs_dst); accum_tshk.absorb(p1600, cvs_dst); } data = par_data_chunks.remainder(); @@ -95,7 +95,7 @@ impl BackendClosure for Closure<'_, Rate> { let cv_dst = &mut cv_buf[..cv_len]; let mut data_chunks = data.chunks_exact(CHUNK_SIZE); for data_chunk in &mut data_chunks { - node_turbo_shake::scalar::(p1600, data_chunk, cv_dst); + node_turbo_shake::scalar::(p1600, data_chunk, cv_dst); accum_tshk.absorb(p1600, cv_dst); } data = data_chunks.remainder(); diff --git a/turboshake/src/lib.rs b/turboshake/src/lib.rs index 593ef363..ce552f7a 100644 --- a/turboshake/src/lib.rs +++ b/turboshake/src/lib.rs @@ -17,7 +17,6 @@ use core::fmt; use digest::{ CollisionResistance, ExtendableOutput, ExtendableOutputReset, HashMarker, Reset, Update, XofReader, - array::ArraySize, common::{AlgorithmName, BlockSizeUser}, consts::{U16, U32, U136, U168}, }; @@ -33,20 +32,20 @@ pub const DEFAULT_DS: u8 = 0x1F; /// Domain separator `DS` MUST be in the range `0x01..=0x7f`. /// Use [`DEFAULT_DS`] if you want the default value. /// -/// Rate MUST be either [`U168`] or [`U136`] for TurboSHAKE128 and TurboSHAKE256 respectively. +/// Rate MUST be either 168 or 136 for TurboSHAKE128 and TurboSHAKE256 respectively. #[derive(Clone)] -pub struct TurboShake { +pub struct TurboShake { state: State1600, - cursor: SpongeCursor, + cursor: SpongeCursor, keccak: Keccak, } -impl Default for TurboShake { +impl Default for TurboShake { #[inline] fn default() -> Self { const { assert!(DS >= 0x01 && DS <= 0x7F, "invalid domain separator"); - assert!(Rate::USIZE == 168 || Rate::USIZE == 136, "unsupported rate"); + assert!(RATE == 168 || RATE == 136, "unsupported rate"); } Self { state: Default::default(), @@ -56,13 +55,9 @@ impl Default for TurboShake { } } -impl HashMarker for TurboShake {} +impl HashMarker for TurboShake {} -impl BlockSizeUser for TurboShake { - type BlockSize = Rate; -} - -impl Update for TurboShake { +impl Update for TurboShake { #[inline] fn update(&mut self, data: &[u8]) { self.keccak.with_p1600::(|p1600| { @@ -71,7 +66,7 @@ impl Update for TurboShake { } } -impl TurboShake { +impl TurboShake { fn pad(&mut self) { let pos = self.cursor.pos(); let word_offset = pos / 8; @@ -79,12 +74,12 @@ impl TurboShake { let pad = u64::from(DS) << (8 * byte_offset); self.state[word_offset] ^= pad; - self.state[Rate::USIZE / 8 - 1] ^= 1 << 63; + self.state[RATE / 8 - 1] ^= 1 << 63; } } -impl ExtendableOutput for TurboShake { - type Reader = TurboShakeReader; +impl ExtendableOutput for TurboShake { + type Reader = TurboShakeReader; #[inline] fn finalize_xof(mut self) -> Self::Reader { @@ -97,7 +92,7 @@ impl ExtendableOutput for TurboShake { } } -impl ExtendableOutputReset for TurboShake { +impl ExtendableOutputReset for TurboShake { #[inline] fn finalize_xof_reset(&mut self) -> Self::Reader { self.pad(); @@ -111,7 +106,7 @@ impl ExtendableOutputReset for TurboShake Reset for TurboShake { +impl Reset for TurboShake { #[inline] fn reset(&mut self) { self.state = Default::default(); @@ -119,9 +114,9 @@ impl Reset for TurboShake { } } -impl AlgorithmName for TurboShake { +impl AlgorithmName for TurboShake { fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result { - let alg_name = match Rate::USIZE { + let alg_name = match RATE { 168 => "TurboSHAKE128", 136 => "TurboSHAKE256", _ => unreachable!(), @@ -130,9 +125,9 @@ impl AlgorithmName for TurboShake { } } -impl fmt::Debug for TurboShake { +impl fmt::Debug for TurboShake { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let debug_str = match Rate::USIZE { + let debug_str = match RATE { 168 => "TurboShake128 { ... }", 136 => "TurboShake256 { ... }", _ => unreachable!(), @@ -141,7 +136,7 @@ impl fmt::Debug for TurboShake { } } -impl Drop for TurboShake { +impl Drop for TurboShake { fn drop(&mut self) { #[cfg(feature = "zeroize")] { @@ -153,28 +148,28 @@ impl Drop for TurboShake { } #[cfg(feature = "zeroize")] -impl digest::zeroize::ZeroizeOnDrop for TurboShake {} +impl digest::zeroize::ZeroizeOnDrop for TurboShake {} /// Generic TurboSHAKE XOF reader #[derive(Clone)] -pub struct TurboShakeReader { +pub struct TurboShakeReader { state: State1600, - cursor: SpongeCursor, + cursor: SpongeCursor, keccak: Keccak, } -impl XofReader for TurboShakeReader { +impl XofReader for TurboShakeReader { #[inline] fn read(&mut self, buf: &mut [u8]) { self.keccak.with_p1600::(|p1600| { - self.cursor.squeeze_u64_le(&mut self.state, p1600, buf); + self.cursor.squeeze_read_u64_le(&mut self.state, p1600, buf); }); } } -impl fmt::Debug for TurboShakeReader { +impl fmt::Debug for TurboShakeReader { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let debug_str = match Rate::USIZE { + let debug_str = match RATE { 168 => "TurboShakeReader128 { ... }", 136 => "TurboShakeReader256 { ... }", _ => unreachable!(), @@ -183,7 +178,7 @@ impl fmt::Debug for TurboShakeReader { } } -impl Drop for TurboShakeReader { +impl Drop for TurboShakeReader { fn drop(&mut self) { #[cfg(feature = "zeroize")] { @@ -195,17 +190,17 @@ impl Drop for TurboShakeReader { } #[cfg(feature = "zeroize")] -impl digest::zeroize::ZeroizeOnDrop for TurboShakeReader {} +impl digest::zeroize::ZeroizeOnDrop for TurboShakeReader {} /// TurboSHAKE128 hasher with domain separator. -pub type TurboShake128 = TurboShake; +pub type TurboShake128 = TurboShake<168, DS>; /// TurboSHAKE256 hasher with domain separator. -pub type TurboShake256 = TurboShake; +pub type TurboShake256 = TurboShake<136, DS>; /// TurboSHAKE128 XOF reader. -pub type TurboShake128Reader = TurboShakeReader; +pub type TurboShake128Reader = TurboShakeReader<168>; /// TurboSHAKE256 XOF reader. -pub type TurboShake256Reader = TurboShakeReader; +pub type TurboShake256Reader = TurboShakeReader<136>; impl CollisionResistance for TurboShake128 { // https://www.ietf.org/archive/id/draft-irtf-cfrg-kangarootwelve-17.html#section-7-7 @@ -216,3 +211,11 @@ impl CollisionResistance for TurboShake256 { // https://www.ietf.org/archive/id/draft-irtf-cfrg-kangarootwelve-17.html#section-7-8 type CollisionResistance = U32; } + +impl BlockSizeUser for TurboShake128 { + type BlockSize = U168; +} + +impl BlockSizeUser for TurboShake256 { + type BlockSize = U136; +} From 310f2f6549aeebfe74c56070f125b721d594b6ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D1=80=D1=82=D1=91=D0=BC=20=D0=9F=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=BE=D0=B2=20=5BArtyom=20Pavlov=5D?= Date: Mon, 4 May 2026 18:23:13 +0300 Subject: [PATCH 8/8] update sponge-cursor --- Cargo.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 72675b37..2693c03e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -361,7 +361,7 @@ dependencies = [ [[package]] name = "sponge-cursor" version = "0.1.0" -source = "git+https://github.com/RustCrypto/utils?branch=add-sponge-cursor#9b92270c2948906bc2ce687e4a080f59210c2808" +source = "git+https://github.com/RustCrypto/utils?branch=add-sponge-cursor#075f14c368b39ecd3207c0b93c853993751bebd5" dependencies = [ "zeroize", ]