Skip to content
Open
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 CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added

- (libcrux-hmac-drbg) [#1523](https://github.com/celabshq/libcrux/issues/1523): Allow configuring a custom reseed interval
- [#1474](https://github.com/celabshq/libcrux/pull/1474): Add support for AES-CCM from `libcrux-aes`, rename feature `aes_gcm` to `aes_aead`
- [#1382](https://github.com/celabshq/libcrux/pull/1382): Add support for HMAC-DRBG
- (libcrux-secrets) [#1446](https://github.com/celabshq/libcrux/pull/1446): Integrate valgrind requests when cfg `valgrind_ct_test` is set
Expand All @@ -37,6 +38,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- (libcrux-sha3) [#1292](https://github.com/celabshq/libcrux/pull/1292): Add support for incremental CShake (https://github.com/kraemv)
- (libcrux-hmac) [#1382](https://github.com/celabshq/libcrux/pull/1382): Add an incremental API


## [0.0.4] (2026-05-13)

### Fixed
Expand Down
19 changes: 15 additions & 4 deletions crates/algorithms/hmac-drbg/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ pub struct HmacDrbg<const OUTLEN: usize, Alg: HmacAlgorithm<OUTLEN>> {
key: [u8; OUTLEN],
v: [u8; OUTLEN],
reseed_counter: u64,
reseed_interval: u64,

/// All continuous health-test state, present only with feature `health-tests`.
#[cfg(feature = "health-tests")]
Expand Down Expand Up @@ -212,6 +213,7 @@ impl<const OUTLEN: usize, Alg: HmacAlgorithm<OUTLEN>> HmacDrbg<OUTLEN, Alg> {
key: [0x00u8; OUTLEN],
v: [0x01u8; OUTLEN],
reseed_counter: 1,
reseed_interval: RESEED_INTERVAL,
#[cfg(feature = "health-tests")]
health: HealthState::new(),
};
Expand Down Expand Up @@ -287,7 +289,7 @@ impl<const OUTLEN: usize, Alg: HmacAlgorithm<OUTLEN>> HmacDrbg<OUTLEN, Alg> {
// #hax: requires output.len() > 0
// #hax: requires output.len() <= MAX_GENERATE_BYTES
// #hax: requires true // additional_input has no length constraint
// #hax: requires self.reseed_counter <= RESEED_INTERVAL
// #hax: requires self.reseed_counter <= self.reseed_interval
// #hax: ensures result.is_ok() ==> self.reseed_counter == old(self.reseed_counter) + 1
pub fn generate(
&mut self,
Expand All @@ -299,7 +301,7 @@ impl<const OUTLEN: usize, Alg: HmacAlgorithm<OUTLEN>> HmacDrbg<OUTLEN, Alg> {
return Err(GenerateError::HealthCheckFailed);
}

if self.reseed_counter > RESEED_INTERVAL {
if self.reseed_counter > self.reseed_interval {
return Err(GenerateError::ReseedRequired);
}
if output.is_empty() {
Expand Down Expand Up @@ -350,9 +352,18 @@ impl<const OUTLEN: usize, Alg: HmacAlgorithm<OUTLEN>> HmacDrbg<OUTLEN, Alg> {
/// Callers can use this to proactively schedule a reseed before the counter
/// is exhausted rather than discovering it on the next `generate` call.
//
// #hax: ensures result == (self.reseed_counter > RESEED_INTERVAL)
// #hax: ensures result == (self.reseed_counter > self.reseed_interval)
pub fn needs_reseed(&self) -> bool {
self.reseed_counter > RESEED_INTERVAL
self.reseed_counter > self.reseed_interval
}

/// Set the reseed interval for this DRBG instance.
///
/// The interval is clamped to a maximum of [`RESEED_INTERVAL`] to ensure
/// compliance with SP 800-90A.
pub fn set_reseed_interval(&mut self, interval: u64) {
let max = RESEED_INTERVAL;
self.reseed_interval = if interval > max { max } else { interval };
}

/// Returns the current reseed counter.
Expand Down
10 changes: 9 additions & 1 deletion crates/algorithms/hmac-drbg/src/rand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ impl<const OUTLEN: usize, Alg: HmacAlgorithm<OUTLEN>> rand::TryRng for HmacDrbg<
/// Requests larger than [`MAX_GENERATE_BYTES`] are split into chunks so
/// that arbitrarily-sized buffers can be filled without error.
//
// #hax: requires self.reseed_counter + (dst.len() / MAX_GENERATE_BYTES) as u64 + 1 <= RESEED_INTERVAL
// #hax: requires self.reseed_counter + (dst.len() / MAX_GENERATE_BYTES) as u64 + 1 <= self.reseed_interval
// #hax: ensures result.is_ok() ==> dst is fully written
fn try_fill_bytes(&mut self, dst: &mut [u8]) -> Result<(), Self::Error> {
let mut written = 0;
Expand Down Expand Up @@ -121,6 +121,7 @@ impl<const OUTLEN: usize, Alg: HmacAlgorithm<OUTLEN>> SeedableRng for HmacDrbg<O
key: [0x00u8; OUTLEN],
v: [0x01u8; OUTLEN],
reseed_counter: 1,
reseed_interval: crate::RESEED_INTERVAL,
#[cfg(feature = "health-tests")]
health: HealthState::new(),
};
Expand Down Expand Up @@ -321,6 +322,13 @@ impl<const OUTLEN: usize, Hmac: HmacAlgorithm<OUTLEN>, ReseedRng: CryptoRng>
Self { drbg, rng }
}

/// Set the reseed interval for the underlying DRBG.
///
/// The interval is clamped to a maximum of [`crate::RESEED_INTERVAL`].
pub fn set_reseed_interval(&mut self, interval: u64) {
self.drbg.set_reseed_interval(interval);
}

/// Somewhat safely generates a bit of randomness:
/// - ensures we don't pass in too much additional_input (none, in fact)
/// - reseeds if needed
Expand Down
21 changes: 21 additions & 0 deletions crates/algorithms/hmac-drbg/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -254,5 +254,26 @@ fn reseed_required_error() {
fn needs_reseed_true_after_exhaustion() {
let mut drbg = HmacDrbgSha256::new(&E32, &[0u8; 16], &[]).unwrap();
drbg.set_reseed_counter(RESEED_INTERVAL + 1);
}

#[test]
fn custom_reseed_interval() {
let mut drbg = HmacDrbgSha256::new(&E32, &[0u8; 16], &[]).unwrap();
drbg.set_reseed_interval(1024);
drbg.set_reseed_counter(1025);
let mut out = [0u8; 32];
let err = drbg.generate(&mut out, &[]).unwrap_err();
assert_eq!(err, GenerateError::ReseedRequired);
assert!(drbg.needs_reseed());
}

#[test]
fn custom_reseed_interval_clamping() {
let mut drbg = HmacDrbgSha256::new(&E32, &[0u8; 16], &[]).unwrap();
drbg.set_reseed_interval(RESEED_INTERVAL + 100);
// It should be clamped to RESEED_INTERVAL
drbg.set_reseed_counter(RESEED_INTERVAL);
assert!(!drbg.needs_reseed());
drbg.set_reseed_counter(RESEED_INTERVAL + 1);
assert!(drbg.needs_reseed());
}
Loading