Skip to content

Commit 39803c9

Browse files
committed
dudect update
1 parent e21ef54 commit 39803c9

File tree

9 files changed

+58
-134
lines changed

9 files changed

+58
-134
lines changed

CHANGELOG.md

+5
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## 0.4.0 (2024-09-32)
9+
10+
- Now aligned with **released** FIPS 204 including hash sig/verif and keygen with seed.
11+
12+
813
## 0.2.2 (2024-08-02)
914

1015
- Bug fix to debug_assert in `power2round` and t_not_reduced in `keygen`; thank you @skilo-sh !!

Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ workspace = { exclude = ["ct_cm4", "dudect", "fuzz", "wasm"] }
22

33
[package]
44
name = "fips204"
5-
version = "0.2.2"
5+
version = "0.4.0"
66
authors = ["Eric Schorn <[email protected]>"]
77
description = "FIPS 204 (draft): Module-Lattice-Based Digital Signature"
88
categories = ["cryptography", "no-std"]

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ The Rust [Documentation][docs-link] lives under each **Module** corresponding to
5555
## Notes
5656

5757
* This crate is fully functional and corresponds to FIPS 204 (August 13, 2024).
58-
* **BEWARE:** As of September 27, 2024 NIST has not release external/hash test vectors!
58+
* **BEWARE:** As of September 27, 2024 NIST has not released external/hash test vectors!
5959
* Constant-time assurances target the source-code level only on MSRV, with confirmation via
6060
manual review/inspection, the embedded target, and the `dudect` dynamic tests.
6161
* Note that FIPS 204 places specific requirements on randomness per section 3.5.1, hence the exposed `RNG`.

dudect/Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "fips204-dudect"
3-
version = "0.2.2"
3+
version = "0.4.0"
44
authors = ["Eric Schorn <[email protected]>"]
55
description = "Dudect testbench for FIPS 204 (draft) ML-DSA"
66
edition = "2021"

dudect/README.md

+12-15
Original file line numberDiff line numberDiff line change
@@ -6,23 +6,20 @@ See <https://docs.rs/dudect-bencher/latest/dudect_bencher/>
66
> 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.
77
88
~~~
9-
May 30, 2024
10-
Intel® Core™ i7-7700K CPU @ 4.20GHz × 8 Circa 2017 Rust 1.70
9+
September 29, 2024
10+
Intel® Core™ i7-7700K CPU @ 4.20GHz × 8 Circa 2017 Rust 1.81
1111
1212
$ cd dudect # this directory
13-
$ RUSTFLAGS="-C target-cpu=native" cargo run --release -- --continuous keygen_and_sign
13+
$ RUSTFLAGS="-C target-cpu=native" cargo run --release
1414
15-
bench keygen_and_sign seeded with 0x487b8e4779e6365c
16-
bench keygen_and_sign ... : n == +0.013M, max t = -1.88608, max tau = -0.01630, (5/tau)^2 = 94059
17-
bench keygen_and_sign ... : n == +0.226M, max t = -1.97079, max tau = -0.00414, (5/tau)^2 = 1456142
18-
bench keygen_and_sign ... : n == +0.332M, max t = -2.46353, max tau = -0.00428, (5/tau)^2 = 1367236
19-
bench keygen_and_sign ... : n == +0.437M, max t = -3.09049, max tau = -0.00467, (5/tau)^2 = 1144967
20-
bench keygen_and_sign ... : n == +0.546M, max t = -3.36390, max tau = -0.00455, (5/tau)^2 = 1205380
21-
bench keygen_and_sign ... : n == +0.653M, max t = -3.20033, max tau = -0.00396, (5/tau)^2 = 1594027
22-
bench keygen_and_sign ... : n == +0.763M, max t = -2.93715, max tau = -0.00336, (5/tau)^2 = 2212470
23-
bench keygen_and_sign ... : n == +1.535M, max t = -2.52898, max tau = -0.00204, (5/tau)^2 = 6001424
24-
bench keygen_and_sign ... : n == +1.728M, max t = -3.02826, max tau = -0.00230, (5/tau)^2 = 4710906
25-
bench keygen_and_sign ... : n == +1.921M, max t = -2.54927, max tau = -0.00184, (5/tau)^2 = 7389844
26-
bench keygen_and_sign ... : n == +2.113M, max t = -2.69149, max tau = -0.00185, (5/tau)^2 = 7293214
2715
...
16+
Compiling fips204-dudect v0.4.0 (/home/eric/work/fips204/dudect)
17+
Finished `release` profile [optimized + debuginfo] target(s) in 19.97s
18+
Running `target/release/fips204-dudect`
19+
20+
running 1 bench
21+
bench keygen_and_sign seeded with 0x5a426c75ebe1613a
22+
bench keygen_and_sign ... : n == +1.188M, max t = +3.14225, max tau = +0.00288, (5/tau)^2 = 3007343
23+
24+
dudect benches complete
2825
~~~

dudect/src/main.rs

+17-13
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@ use dudect_bencher::{ctbench_main, BenchRng, Class, CtRunner};
22
use fips204::ml_dsa_44; // Could also be ml_dsa_65 or ml_dsa_87.
33
use rand_core::{CryptoRng, RngCore};
44

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

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

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

2523
impl CryptoRng for TestRng {}
2624

2725

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

2930
fn keygen_and_sign(runner: &mut CtRunner, mut _rng: &mut BenchRng) {
3031
const ITERATIONS_INNER: usize = 5;
31-
const ITERATIONS_OUTER: usize = 200_000;
32+
const ITERATIONS_OUTER: usize = 2_000_000;
33+
34+
let message = AlignedBytes::<8>([0u8, 1, 2, 3, 4, 5, 6, 7]);
35+
let z_left = AlignedBytes::<32>([0xAAu8; 32]);
36+
let z_right = AlignedBytes::<32>([0x55u8; 32]);
3237

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

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

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

44-
for (class, rng) in classes.into_iter().zip(rngs.into_iter()) {
48+
for (class, z) in classes.into_iter().zip(z_refs.into_iter()) {
4549
runner.run_one(class, || {
46-
let mut rng = rng.clone();
50+
let mut rng = TestRng(*z); // regurgitates z as rng
4751
for _ in 0..ITERATIONS_INNER {
48-
let _ = ml_dsa_44::dudect_keygen_sign_with_rng(&mut rng, &message).unwrap();
52+
let _ = ml_dsa_44::dudect_keygen_sign_with_rng(&mut rng, &message.0);
4953
}
5054
})
5155
}

src/hashing.rs

+2-27
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,8 @@ pub(crate) fn sample_in_ball<const CTEST: bool>(tau: i32, rho: &[u8]) -> R {
5858
// 6: for 𝑖 from 256 − 𝜏 to 255 do
5959
for i in (256 - tau)..=255 {
6060
// 7: (ctx, 𝑗) ← H.Squeeze(ctx, 1)
61-
let mut j = [0u8];
62-
h_ctx.read(&mut j);
61+
let mut j = [i.to_le_bytes()[0]]; // remove timing variability
62+
if !CTEST { h_ctx.read(&mut j) }; // ..
6363

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

84-
85-
// 2: k ← 8; k implicitly advances with each sample
86-
//let mut hpk = [0u8];
87-
88-
// 3: for i from 256 − τ to 255 do
89-
//
90-
// 4: while H(ρ)[[k]] > i do
91-
// 5: k ← k + 1
92-
// 6: end while
93-
// The above/below loop reads xof bytes until less than or equal to i
94-
// loop {
95-
// xof.read(&mut hpk); // Every 'read' effectively contains k = k + 1
96-
// if CTEST {
97-
// hpk[0] = i.to_le_bytes()[0];
98-
// }
99-
// if hpk[0] <= i.to_le_bytes()[0] {
100-
// break;
101-
// }
102-
// }
103-
104-
// 7: j ← H(ρ)[[k]] ▷ j is a pseudorandom byte that is ≤ i
105-
//let j = hpk[0];
106-
107-
// 10: k ← k + 1 (implicit)
108-
10984
// slightly redundant...
11085
debug_assert!(
11186
c.0.iter().map(|&e| usize::from(e != 0)).sum::<usize>() == tau,

src/helpers.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,9 @@ pub(crate) const fn full_reduce32(a: i32) -> i32 {
7979

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

8486

8587
/// Mod +/- see definition on page 6.

src/lib.rs

+16-75
Original file line numberDiff line numberDiff line change
@@ -355,24 +355,12 @@ macro_rules! functionality {
355355
// start+finish enables the ability of verifing with a pre-computeed expanded public
356356
// key for performance.
357357
fn verify(&self, message: &[u8], sig: &Self::Signature, _ctx: &[u8]) -> bool {
358-
let res = ml_dsa::verify_finish::<K, L, LAMBDA_DIV4, PK_LEN, SIG_LEN, W1_LEN>(
359-
BETA,
360-
GAMMA1,
361-
GAMMA2,
362-
OMEGA,
363-
TAU,
364-
&self,
365-
&message,
366-
&sig,
367-
&[],
368-
&[],
369-
&[],
370-
false,
371-
);
372-
if res.is_err() {
358+
let Ok(res) = ml_dsa::verify_finish::<K, L, LAMBDA_DIV4, PK_LEN, SIG_LEN, W1_LEN>(
359+
BETA, GAMMA1, GAMMA2, OMEGA, TAU, &self, &message, &sig, &[], &[], &[], false
360+
) else {
373361
return false;
374-
}
375-
res.unwrap()
362+
};
363+
res
376364
}
377365

378366
// Algorithm 5 in Verifier trait. Rather than an external+internal split, this split of
@@ -384,24 +372,12 @@ macro_rules! functionality {
384372
};
385373
let mut phm = [0u8; 64]; // hashers don't all play well with each other
386374
let (oid, phm_len) = hash_message(message, ph, &mut phm);
387-
let res = ml_dsa::verify_finish::<K, L, LAMBDA_DIV4, PK_LEN, SIG_LEN, W1_LEN>(
388-
BETA,
389-
GAMMA1,
390-
GAMMA2,
391-
OMEGA,
392-
TAU,
393-
&self,
394-
&message,
395-
&sig,
396-
ctx,
397-
&oid,
398-
&phm[0..phm_len],
399-
false,
400-
);
401-
if res.is_err() {
375+
let Ok(res) = ml_dsa::verify_finish::<K, L, LAMBDA_DIV4, PK_LEN, SIG_LEN, W1_LEN>(
376+
BETA, GAMMA1, GAMMA2, OMEGA, TAU, &self, &message, &sig, ctx, &oid, &phm[0..phm_len], false
377+
) else {
402378
return false;
403379
};
404-
res.unwrap()
380+
res
405381
}
406382
}
407383

@@ -470,18 +446,7 @@ macro_rules! functionality {
470446
let (_pk, sk) = ml_dsa::key_gen::<true, K, L, PK_LEN, SK_LEN>(rng, ETA)?;
471447
let esk = ml_dsa::sign_start::<true, K, L, SK_LEN>(ETA, &sk)?;
472448
let sig = ml_dsa::sign_finish::<true, K, L, LAMBDA_DIV4, SIG_LEN, SK_LEN, W1_LEN>(
473-
rng,
474-
BETA,
475-
GAMMA1,
476-
GAMMA2,
477-
OMEGA,
478-
TAU,
479-
&esk,
480-
message,
481-
&[],
482-
&[],
483-
&[],
484-
false,
449+
rng, BETA, GAMMA1, GAMMA2, OMEGA, TAU, &esk, message, &[1], &[2], &[3], false
485450
)?;
486451
Ok(sig)
487452
}
@@ -502,18 +467,7 @@ macro_rules! functionality {
502467
ensure!(ctx.len() < 256, "ML-DSA.Sign: ctx too long");
503468
let esk = ml_dsa::sign_start::<CTEST, K, L, SK_LEN>(ETA, &sk.0)?;
504469
let sig = ml_dsa::sign_finish::<CTEST, K, L, LAMBDA_DIV4, SIG_LEN, SK_LEN, W1_LEN>(
505-
rng,
506-
BETA,
507-
GAMMA1,
508-
GAMMA2,
509-
OMEGA,
510-
TAU,
511-
&esk,
512-
message,
513-
ctx,
514-
&[],
515-
&[],
516-
true,
470+
rng, BETA, GAMMA1, GAMMA2, OMEGA, TAU, &esk, message, ctx, &[], &[], true
517471
)?;
518472
Ok(sig)
519473
}
@@ -531,28 +485,15 @@ macro_rules! functionality {
531485
if ctx.len() > 255 {
532486
return false;
533487
};
534-
let epk = ml_dsa::verify_start(&pk.0);
535-
if epk.is_err() {
488+
let Ok(epk) = ml_dsa::verify_start(&pk.0) else {
536489
return false;
537490
};
538-
let res = ml_dsa::verify_finish::<K, L, LAMBDA_DIV4, PK_LEN, SIG_LEN, W1_LEN>(
539-
BETA,
540-
GAMMA1,
541-
GAMMA2,
542-
OMEGA,
543-
TAU,
544-
&epk.unwrap(),
545-
&message,
546-
&sig,
547-
ctx,
548-
&[],
549-
&[],
550-
true,
551-
);
552-
if res.is_err() {
491+
let Ok(res) = ml_dsa::verify_finish::<K, L, LAMBDA_DIV4, PK_LEN, SIG_LEN, W1_LEN>(
492+
BETA, GAMMA1, GAMMA2, OMEGA, TAU, &epk, &message, &sig, ctx, &[], &[], true
493+
) else {
553494
return false;
554495
};
555-
res.unwrap()
496+
res
556497
}
557498
};
558499
}

0 commit comments

Comments
 (0)