Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions ascon-xof128/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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])
Expand Down
4 changes: 2 additions & 2 deletions ascon-xof128/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
8 changes: 4 additions & 4 deletions ascon-xof128/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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];
Expand All @@ -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.

Expand Down
44 changes: 34 additions & 10 deletions ascon-xof128/src/cxof.rs
Original file line number Diff line number Diff line change
@@ -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<Self, InvalidCustomizationError> {
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);

Expand All @@ -53,7 +62,7 @@ impl CustomizedInit for AsconCxof128 {
ascon::permute12(&mut state);

let cursor = Default::default();
Self { state, cursor }
Ok(Self { state, cursor })
}
}

Expand Down Expand Up @@ -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 {}
2 changes: 1 addition & 1 deletion ascon-xof128/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
14 changes: 9 additions & 5 deletions ascon-xof128/tests/mod.rs
Original file line number Diff line number Diff line change
@@ -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},
};
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -90,14 +90,16 @@ struct CxofTestVector {
}

/// Customized XOF test.
fn cxof_reset_test<D: ExtendableOutput + CustomizedInit + Debug + Clone>(
fn cxof_reset_test<D: ExtendableOutput + TryCustomizedInit + Debug + Clone>(
&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
Expand All @@ -110,7 +112,9 @@ fn cxof_reset_test<D: ExtendableOutput + CustomizedInit + Debug + Clone>(

// 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);
}
Expand Down