diff --git a/ascon-xof128/CHANGELOG.md b/ascon-xof128/CHANGELOG.md index d9088c2ec..2cc20d8c8 100644 --- a/ascon-xof128/CHANGELOG.md +++ b/ascon-xof128/CHANGELOG.md @@ -8,8 +8,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - Internal implementation by removing unnecessary buffering ([#849]) - Serialization format used by `SerializableState` implementations ([#849]) +- Replaced implementation of `CustomizedInit` with `TryCustomizedInit` ([#857]) [#849]: https://github.com/RustCrypto/hashes/pull/849 +[#857]: https://github.com/RustCrypto/hashes/pull/857 ## 0.1.0 (2026-04-24) - Initial release ([#841]) diff --git a/ascon-xof128/Cargo.toml b/ascon-xof128/Cargo.toml index 7a2cffe19..91849f959 100644 --- a/ascon-xof128/Cargo.toml +++ b/ascon-xof128/Cargo.toml @@ -13,12 +13,12 @@ categories = ["cryptography", "no-std"] description = "Implementation of Ascon-XOF128 and Ascon-СXOF128" [dependencies] -digest = { version = "0.11", default-features = false } +digest = { version = "0.11.3", default-features = false } ascon = "0.5" sponge-cursor = "0.1" [dev-dependencies] -digest = { version = "0.11", default-features = false, features = ["dev"] } +digest = { version = "0.11.3", default-features = false, features = ["dev"] } hex-literal = "1" [features] diff --git a/ascon-xof128/README.md b/ascon-xof128/README.md index 45f439340..96c158554 100644 --- a/ascon-xof128/README.md +++ b/ascon-xof128/README.md @@ -30,10 +30,10 @@ assert_eq!(dst, hex!("8C7DD114A0")); Ascon-CXOF128 works similarly, but you must specify a customization string to initialize it: ```rust -use ascon_xof128::{AsconCxof128, CustomizedInit, ExtendableOutput, Update, XofReader}; +use ascon_xof128::{AsconCxof128, ExtendableOutput, TryCustomizedInit, Update, XofReader}; use hex_literal::hex; -let mut xof = AsconCxof128::new_customized(b"some customization string"); +let mut xof = AsconCxof128::try_new_customized(b"some customization string").unwrap(); xof.update(b"some bytes"); let mut reader = xof.finalize_xof(); let mut dst = [0u8; 5]; @@ -45,8 +45,8 @@ Note that the NIST specifies that: >The length of the customization string **shall** be at most 2048 bits (i.e., 256 bytes). -This limit is not enforced by the `new_customized` method; users must ensure their customization -strings meet the size requirement if they intend to claim conformance to the standard. +Implementation of the `TryCustomizedInit` trait for this type returns `InvalidCustomizationError` +for longer customization strings. See the [`digest`] crate docs for additional examples. diff --git a/ascon-xof128/src/cxof.rs b/ascon-xof128/src/cxof.rs index 135396275..03384af70 100644 --- a/ascon-xof128/src/cxof.rs +++ b/ascon-xof128/src/cxof.rs @@ -1,36 +1,45 @@ use ascon::State; +use core::fmt; use digest::{ - CollisionResistance, CustomizedInit, ExtendableOutput, HashMarker, OutputSizeUser, Update, - common::AlgorithmName, - common::hazmat::{DeserializeStateError, SerializableState, SerializedState}, + CollisionResistance, ExtendableOutput, HashMarker, OutputSizeUser, TryCustomizedInit, Update, + common::{ + AlgorithmName, + hazmat::{DeserializeStateError, SerializableState, SerializedState}, + }, consts::{U16, U32, U41}, }; use sponge_cursor::SpongeCursor; use crate::{AsconXof128Reader, consts::CXOF_INIT_STATE}; +const MAX_CUSTOMIZATION_LEN: usize = 256; + /// Ascon-CXOF128 hasher. /// /// Note that NIST SP 800-232 specifies the following: /// /// >The length of the customization string **shall** be at most 2048 bits (i.e., 256 bytes). /// -/// We do not check this condition as part of [`CustomizedInit::new_customized`] and -/// consider it the user's responsibility to use appropriately sized customization strings. +/// Implementation of the [`TryCustomizedInit`] trait for this type returns +/// [`InvalidCustomizationError`] for longer customization strings. #[derive(Clone, Debug)] pub struct AsconCxof128 { state: State, cursor: SpongeCursor<8>, } -impl CustomizedInit for AsconCxof128 { +impl TryCustomizedInit for AsconCxof128 { + type Error = InvalidCustomizationError; #[inline] - fn new_customized(customization: &[u8]) -> Self { - // We assume that in practice customization strings are always smaller than 2^61 bytes. + fn try_new_customized(customization: &[u8]) -> Result { + if customization.len() > MAX_CUSTOMIZATION_LEN { + return Err(InvalidCustomizationError); + } + let bit_len = 8 * customization.len(); let mut state = CXOF_INIT_STATE; - state[0] ^= u64::try_from(bit_len).expect("`usize` always fits into `u64` in practice"); + state[0] ^= u64::try_from(bit_len).expect("`bit_len` can not be greater than 2048"); ascon::permute12(&mut state); @@ -53,7 +62,7 @@ impl CustomizedInit for AsconCxof128 { ascon::permute12(&mut state); let cursor = Default::default(); - Self { state, cursor } + Ok(Self { state, cursor }) } } @@ -141,3 +150,18 @@ impl Drop for AsconCxof128 { #[cfg(feature = "zeroize")] impl digest::zeroize::ZeroizeOnDrop for AsconCxof128 {} + +/// Invalid Ascon-CXOF128 customization string error. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct InvalidCustomizationError; + +impl fmt::Display for InvalidCustomizationError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str( + "Invalid Ascon-CXOF128 customization string. \ + The length of the customization string shall be at most 256 bytes.", + ) + } +} + +impl core::error::Error for InvalidCustomizationError {} diff --git a/ascon-xof128/src/lib.rs b/ascon-xof128/src/lib.rs index 2af8296f5..05124e936 100644 --- a/ascon-xof128/src/lib.rs +++ b/ascon-xof128/src/lib.rs @@ -8,7 +8,7 @@ #![warn(missing_docs, unreachable_pub)] #![forbid(unsafe_code)] -pub use digest::{self, CustomizedInit, ExtendableOutput, Update, XofReader}; +pub use digest::{self, ExtendableOutput, TryCustomizedInit, Update, XofReader}; mod consts; mod cxof; diff --git a/ascon-xof128/tests/mod.rs b/ascon-xof128/tests/mod.rs index 1db703d27..6bd494a34 100644 --- a/ascon-xof128/tests/mod.rs +++ b/ascon-xof128/tests/mod.rs @@ -1,7 +1,7 @@ use ascon_xof128::{AsconCxof128, AsconXof128}; use core::fmt::Debug; use digest::{ - CustomizedInit, ExtendableOutput, Update, + ExtendableOutput, TryCustomizedInit, Update, common::hazmat::SerializableState, dev::{feed_rand_16mib, xof_reset_test}, }; @@ -59,7 +59,7 @@ fn ascon_xof128_rand() { #[test] fn ascon_cxof128_rand() { - let mut h = AsconCxof128::new_customized(b"randomized cxof test"); + let mut h = AsconCxof128::try_new_customized(b"randomized cxof test").unwrap(); h.update(b"hello"); feed_rand_16mib(&mut h); @@ -90,14 +90,16 @@ struct CxofTestVector { } /// Customized XOF test. -fn cxof_reset_test( +fn cxof_reset_test( &CxofTestVector { customization, input, output, }: &CxofTestVector, ) -> Result<(), &'static str> { - let mut hasher = D::new_customized(customization); + let Ok(mut hasher) = D::try_new_customized(customization) else { + return Err("initialization error"); + }; let mut buf = [0u8; 1024]; let buf = &mut buf[..output.len()]; // Test that it works when accepting the message all at once @@ -110,7 +112,9 @@ fn cxof_reset_test( // Test that it works when accepting the message in chunks for n in 1..core::cmp::min(17, input.len()) { - let mut hasher = D::new_customized(customization); + let Ok(mut hasher) = D::try_new_customized(customization) else { + return Err("initialization error"); + }; for chunk in input.chunks(n) { hasher.update(chunk); }