Skip to content

Commit

Permalink
dudect update
Browse files Browse the repository at this point in the history
  • Loading branch information
eschorn1 committed Sep 29, 2024
1 parent e21ef54 commit 39803c9
Show file tree
Hide file tree
Showing 9 changed files with 58 additions and 134 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## 0.4.0 (2024-09-32)

- Now aligned with **released** FIPS 204 including hash sig/verif and keygen with seed.


## 0.2.2 (2024-08-02)

- Bug fix to debug_assert in `power2round` and t_not_reduced in `keygen`; thank you @skilo-sh !!
Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ workspace = { exclude = ["ct_cm4", "dudect", "fuzz", "wasm"] }

[package]
name = "fips204"
version = "0.2.2"
version = "0.4.0"
authors = ["Eric Schorn <[email protected]>"]
description = "FIPS 204 (draft): Module-Lattice-Based Digital Signature"
categories = ["cryptography", "no-std"]
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ The Rust [Documentation][docs-link] lives under each **Module** corresponding to
## Notes

* This crate is fully functional and corresponds to FIPS 204 (August 13, 2024).
* **BEWARE:** As of September 27, 2024 NIST has not release external/hash test vectors!
* **BEWARE:** As of September 27, 2024 NIST has not released external/hash test vectors!
* Constant-time assurances target the source-code level only on MSRV, with confirmation via
manual review/inspection, the embedded target, and the `dudect` dynamic tests.
* Note that FIPS 204 places specific requirements on randomness per section 3.5.1, hence the exposed `RNG`.
Expand Down
2 changes: 1 addition & 1 deletion dudect/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "fips204-dudect"
version = "0.2.2"
version = "0.4.0"
authors = ["Eric Schorn <[email protected]>"]
description = "Dudect testbench for FIPS 204 (draft) ML-DSA"
edition = "2021"
Expand Down
27 changes: 12 additions & 15 deletions dudect/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,20 @@ See <https://docs.rs/dudect-bencher/latest/dudect_bencher/>
> t-values greater than 5 are generally considered a good indication that the function is not constant time. t-values less than 5 does not necessarily imply that the function is constant-time, since there may be other input distributions under which the function behaves significantly differently.
~~~
May 30, 2024
Intel® Core™ i7-7700K CPU @ 4.20GHz × 8 Circa 2017 Rust 1.70
September 29, 2024
Intel® Core™ i7-7700K CPU @ 4.20GHz × 8 Circa 2017 Rust 1.81
$ cd dudect # this directory
$ RUSTFLAGS="-C target-cpu=native" cargo run --release -- --continuous keygen_and_sign
$ RUSTFLAGS="-C target-cpu=native" cargo run --release
bench keygen_and_sign seeded with 0x487b8e4779e6365c
bench keygen_and_sign ... : n == +0.013M, max t = -1.88608, max tau = -0.01630, (5/tau)^2 = 94059
bench keygen_and_sign ... : n == +0.226M, max t = -1.97079, max tau = -0.00414, (5/tau)^2 = 1456142
bench keygen_and_sign ... : n == +0.332M, max t = -2.46353, max tau = -0.00428, (5/tau)^2 = 1367236
bench keygen_and_sign ... : n == +0.437M, max t = -3.09049, max tau = -0.00467, (5/tau)^2 = 1144967
bench keygen_and_sign ... : n == +0.546M, max t = -3.36390, max tau = -0.00455, (5/tau)^2 = 1205380
bench keygen_and_sign ... : n == +0.653M, max t = -3.20033, max tau = -0.00396, (5/tau)^2 = 1594027
bench keygen_and_sign ... : n == +0.763M, max t = -2.93715, max tau = -0.00336, (5/tau)^2 = 2212470
bench keygen_and_sign ... : n == +1.535M, max t = -2.52898, max tau = -0.00204, (5/tau)^2 = 6001424
bench keygen_and_sign ... : n == +1.728M, max t = -3.02826, max tau = -0.00230, (5/tau)^2 = 4710906
bench keygen_and_sign ... : n == +1.921M, max t = -2.54927, max tau = -0.00184, (5/tau)^2 = 7389844
bench keygen_and_sign ... : n == +2.113M, max t = -2.69149, max tau = -0.00185, (5/tau)^2 = 7293214
...
Compiling fips204-dudect v0.4.0 (/home/eric/work/fips204/dudect)
Finished `release` profile [optimized + debuginfo] target(s) in 19.97s
Running `target/release/fips204-dudect`
running 1 bench
bench keygen_and_sign seeded with 0x5a426c75ebe1613a
bench keygen_and_sign ... : n == +1.188M, max t = +3.14225, max tau = +0.00288, (5/tau)^2 = 3007343
dudect benches complete
~~~
30 changes: 17 additions & 13 deletions dudect/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ use dudect_bencher::{ctbench_main, BenchRng, Class, CtRunner};
use fips204::ml_dsa_44; // Could also be ml_dsa_65 or ml_dsa_87.
use rand_core::{CryptoRng, RngCore};

// Test RNG to regurgitate incremented values when 'asked'
// Simplistic RNG to regurgitate set value
#[derive(Clone)]
#[repr(align(8))]
struct TestRng { value: u32 }
struct TestRng([u8; 32]);

impl RngCore for TestRng {
fn next_u32(&mut self) -> u32 { unimplemented!() }
Expand All @@ -15,37 +15,41 @@ impl RngCore for TestRng {
fn fill_bytes(&mut self, _out: &mut [u8]) { unimplemented!() }

fn try_fill_bytes(&mut self, out: &mut [u8]) -> Result<(), rand_core::Error> {
out.iter_mut().for_each(|b| *b = self.value.to_le_bytes()[0]);
out[0..4].copy_from_slice(&self.value.to_be_bytes());
self.value = self.value.wrapping_add(1);
out.copy_from_slice(&self.0);
Ok(())
}
}

impl CryptoRng for TestRng {}


#[repr(align(8))]
pub struct AlignedBytes<const BYTE_LEN: usize>(pub(crate) [u8; BYTE_LEN]);


fn keygen_and_sign(runner: &mut CtRunner, mut _rng: &mut BenchRng) {
const ITERATIONS_INNER: usize = 5;
const ITERATIONS_OUTER: usize = 200_000;
const ITERATIONS_OUTER: usize = 2_000_000;

let message = AlignedBytes::<8>([0u8, 1, 2, 3, 4, 5, 6, 7]);
let z_left = AlignedBytes::<32>([0xAAu8; 32]);
let z_right = AlignedBytes::<32>([0x55u8; 32]);

let message = [0u8, 1, 2, 3, 4, 5, 6, 7];

let mut classes = [Class::Right; ITERATIONS_OUTER];
let mut rngs: [TestRng; ITERATIONS_OUTER] = core::array::from_fn(|_| TestRng {value: 12});
let mut classes = vec![Class::Right; ITERATIONS_OUTER];
let mut z_refs = vec![&z_right.0; ITERATIONS_OUTER];

// Interleave left and right
for i in (0..ITERATIONS_OUTER).step_by(2) {
classes[i] = Class::Left;
rngs[i] = TestRng {value: 56}; // <--- different seed value
z_refs[i] = &z_left.0;
}

for (class, rng) in classes.into_iter().zip(rngs.into_iter()) {
for (class, z) in classes.into_iter().zip(z_refs.into_iter()) {
runner.run_one(class, || {
let mut rng = rng.clone();
let mut rng = TestRng(*z); // regurgitates z as rng
for _ in 0..ITERATIONS_INNER {
let _ = ml_dsa_44::dudect_keygen_sign_with_rng(&mut rng, &message).unwrap();
let _ = ml_dsa_44::dudect_keygen_sign_with_rng(&mut rng, &message.0);
}
})
}
Expand Down
29 changes: 2 additions & 27 deletions src/hashing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,8 @@ pub(crate) fn sample_in_ball<const CTEST: bool>(tau: i32, rho: &[u8]) -> R {
// 6: for 𝑖 from 256 − 𝜏 to 255 do
for i in (256 - tau)..=255 {
// 7: (ctx, 𝑗) ← H.Squeeze(ctx, 1)
let mut j = [0u8];
h_ctx.read(&mut j);
let mut j = [i.to_le_bytes()[0]]; // remove timing variability
if !CTEST { h_ctx.read(&mut j) }; // ..

// 8: while 𝑗 > 𝑖 do
while usize::from(j[0]) > i {
Expand All @@ -81,31 +81,6 @@ pub(crate) fn sample_in_ball<const CTEST: bool>(tau: i32, rho: &[u8]) -> R {
// 13: end for
}


// 2: k ← 8; k implicitly advances with each sample
//let mut hpk = [0u8];

// 3: for i from 256 − τ to 255 do
//
// 4: while H(ρ)[[k]] > i do
// 5: k ← k + 1
// 6: end while
// The above/below loop reads xof bytes until less than or equal to i
// loop {
// xof.read(&mut hpk); // Every 'read' effectively contains k = k + 1
// if CTEST {
// hpk[0] = i.to_le_bytes()[0];
// }
// if hpk[0] <= i.to_le_bytes()[0] {
// break;
// }
// }

// 7: j ← H(ρ)[[k]] ▷ j is a pseudorandom byte that is ≤ i
//let j = hpk[0];

// 10: k ← k + 1 (implicit)

// slightly redundant...
debug_assert!(
c.0.iter().map(|&e| usize::from(e != 0)).sum::<usize>() == tau,
Expand Down
4 changes: 3 additions & 1 deletion src/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,9 @@ pub(crate) const fn full_reduce32(a: i32) -> i32 {

// Note: this is only used on 'fixed' security parameters (not secret values), so as not to impact CT
/// Bit length required to express `a` in bits
pub(crate) const fn bit_length(a: i32) -> usize { a.ilog2() as usize + 1 }
pub(crate) const fn bit_length(x: i32) -> usize {
x.ilog2() as usize + 1
}


/// Mod +/- see definition on page 6.
Expand Down
91 changes: 16 additions & 75 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -355,24 +355,12 @@ macro_rules! functionality {
// start+finish enables the ability of verifing with a pre-computeed expanded public
// key for performance.
fn verify(&self, message: &[u8], sig: &Self::Signature, _ctx: &[u8]) -> bool {
let res = ml_dsa::verify_finish::<K, L, LAMBDA_DIV4, PK_LEN, SIG_LEN, W1_LEN>(
BETA,
GAMMA1,
GAMMA2,
OMEGA,
TAU,
&self,
&message,
&sig,
&[],
&[],
&[],
false,
);
if res.is_err() {
let Ok(res) = ml_dsa::verify_finish::<K, L, LAMBDA_DIV4, PK_LEN, SIG_LEN, W1_LEN>(
BETA, GAMMA1, GAMMA2, OMEGA, TAU, &self, &message, &sig, &[], &[], &[], false
) else {
return false;
}
res.unwrap()
};
res
}

// Algorithm 5 in Verifier trait. Rather than an external+internal split, this split of
Expand All @@ -384,24 +372,12 @@ macro_rules! functionality {
};
let mut phm = [0u8; 64]; // hashers don't all play well with each other
let (oid, phm_len) = hash_message(message, ph, &mut phm);
let res = ml_dsa::verify_finish::<K, L, LAMBDA_DIV4, PK_LEN, SIG_LEN, W1_LEN>(
BETA,
GAMMA1,
GAMMA2,
OMEGA,
TAU,
&self,
&message,
&sig,
ctx,
&oid,
&phm[0..phm_len],
false,
);
if res.is_err() {
let Ok(res) = ml_dsa::verify_finish::<K, L, LAMBDA_DIV4, PK_LEN, SIG_LEN, W1_LEN>(
BETA, GAMMA1, GAMMA2, OMEGA, TAU, &self, &message, &sig, ctx, &oid, &phm[0..phm_len], false
) else {
return false;
};
res.unwrap()
res
}
}

Expand Down Expand Up @@ -470,18 +446,7 @@ macro_rules! functionality {
let (_pk, sk) = ml_dsa::key_gen::<true, K, L, PK_LEN, SK_LEN>(rng, ETA)?;
let esk = ml_dsa::sign_start::<true, K, L, SK_LEN>(ETA, &sk)?;
let sig = ml_dsa::sign_finish::<true, K, L, LAMBDA_DIV4, SIG_LEN, SK_LEN, W1_LEN>(
rng,
BETA,
GAMMA1,
GAMMA2,
OMEGA,
TAU,
&esk,
message,
&[],
&[],
&[],
false,
rng, BETA, GAMMA1, GAMMA2, OMEGA, TAU, &esk, message, &[1], &[2], &[3], false
)?;
Ok(sig)
}
Expand All @@ -502,18 +467,7 @@ macro_rules! functionality {
ensure!(ctx.len() < 256, "ML-DSA.Sign: ctx too long");
let esk = ml_dsa::sign_start::<CTEST, K, L, SK_LEN>(ETA, &sk.0)?;
let sig = ml_dsa::sign_finish::<CTEST, K, L, LAMBDA_DIV4, SIG_LEN, SK_LEN, W1_LEN>(
rng,
BETA,
GAMMA1,
GAMMA2,
OMEGA,
TAU,
&esk,
message,
ctx,
&[],
&[],
true,
rng, BETA, GAMMA1, GAMMA2, OMEGA, TAU, &esk, message, ctx, &[], &[], true
)?;
Ok(sig)
}
Expand All @@ -531,28 +485,15 @@ macro_rules! functionality {
if ctx.len() > 255 {
return false;
};
let epk = ml_dsa::verify_start(&pk.0);
if epk.is_err() {
let Ok(epk) = ml_dsa::verify_start(&pk.0) else {
return false;
};
let res = ml_dsa::verify_finish::<K, L, LAMBDA_DIV4, PK_LEN, SIG_LEN, W1_LEN>(
BETA,
GAMMA1,
GAMMA2,
OMEGA,
TAU,
&epk.unwrap(),
&message,
&sig,
ctx,
&[],
&[],
true,
);
if res.is_err() {
let Ok(res) = ml_dsa::verify_finish::<K, L, LAMBDA_DIV4, PK_LEN, SIG_LEN, W1_LEN>(
BETA, GAMMA1, GAMMA2, OMEGA, TAU, &epk, &message, &sig, ctx, &[], &[], true
) else {
return false;
};
res.unwrap()
res
}
};
}
Expand Down

0 comments on commit 39803c9

Please sign in to comment.