Skip to content

Commit

Permalink
2cc
Browse files Browse the repository at this point in the history
  • Loading branch information
eschorn1 committed Sep 15, 2024
1 parent a9d5aff commit d8d6fb2
Show file tree
Hide file tree
Showing 5 changed files with 153 additions and 74 deletions.
30 changes: 15 additions & 15 deletions benches/benchmark.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,37 +35,37 @@ pub fn criterion_benchmark(c: &mut Criterion) {
let (pk44, sk44) = ml_dsa_44::try_keygen_with_rng(&mut rng).unwrap();
let esk44 = ml_dsa_44::KG::gen_expanded_private(&sk44).unwrap();
let epk44 = ml_dsa_44::KG::gen_expanded_public(&pk44).unwrap();
let sig44 = sk44.try_sign(&msg).unwrap();
let sig44 = sk44.try_sign(&msg, &[0]).unwrap();

let (pk65, sk65) = ml_dsa_65::try_keygen_with_rng(&mut rng).unwrap();
let esk65 = ml_dsa_65::KG::gen_expanded_private(&sk65).unwrap();
let epk65 = ml_dsa_65::KG::gen_expanded_public(&pk65).unwrap();
let sig65 = sk65.try_sign(&msg).unwrap();
let sig65 = sk65.try_sign(&msg, &[0]).unwrap();

let (pk87, sk87) = ml_dsa_87::try_keygen_with_rng(&mut rng).unwrap();
let esk87 = ml_dsa_87::KG::gen_expanded_private(&sk87).unwrap();
let epk87 = ml_dsa_87::KG::gen_expanded_public(&pk87).unwrap();
let sig87 = sk87.try_sign(&msg).unwrap();
let sig87 = sk87.try_sign(&msg, &[0]).unwrap();

c.bench_function("ml_dsa_44 keygen", |b| b.iter(|| ml_dsa_44::try_keygen_with_rng(&mut rng)));
c.bench_function("ml_dsa_65 keygen", |b| b.iter(|| ml_dsa_65::try_keygen_with_rng(&mut rng)));
c.bench_function("ml_dsa_87 keygen", |b| b.iter(|| ml_dsa_87::try_keygen_with_rng(&mut rng)));

c.bench_function("ml_dsa_44 sk sign", |b| b.iter(|| sk44.try_sign_with_rng(&mut rng, &msg)));
c.bench_function("ml_dsa_65 sk sign", |b| b.iter(|| sk65.try_sign_with_rng(&mut rng, &msg)));
c.bench_function("ml_dsa_87 sk sign", |b| b.iter(|| sk87.try_sign_with_rng(&mut rng, &msg)));
c.bench_function("ml_dsa_44 sk sign", |b| b.iter(|| sk44.try_sign_with_rng(&mut rng, &msg, &[0])));
c.bench_function("ml_dsa_65 sk sign", |b| b.iter(|| sk65.try_sign_with_rng(&mut rng, &msg, &[0])));
c.bench_function("ml_dsa_87 sk sign", |b| b.iter(|| sk87.try_sign_with_rng(&mut rng, &msg, &[0])));

c.bench_function("ml_dsa_44 esk sign", |b| b.iter(|| esk44.try_sign_with_rng(&mut rng, &msg)));
c.bench_function("ml_dsa_65 esk sign", |b| b.iter(|| esk65.try_sign_with_rng(&mut rng, &msg)));
c.bench_function("ml_dsa_87 esk sign", |b| b.iter(|| esk87.try_sign_with_rng(&mut rng, &msg)));
c.bench_function("ml_dsa_44 esk sign", |b| b.iter(|| esk44.try_sign_with_rng(&mut rng, &msg, &[0])));
c.bench_function("ml_dsa_65 esk sign", |b| b.iter(|| esk65.try_sign_with_rng(&mut rng, &msg, &[0])));
c.bench_function("ml_dsa_87 esk sign", |b| b.iter(|| esk87.try_sign_with_rng(&mut rng, &msg, &[0])));

c.bench_function("ml_dsa_44 pk verify", |b| b.iter(|| pk44.verify(&msg, &sig44)));
c.bench_function("ml_dsa_65 pk verify", |b| b.iter(|| pk65.verify(&msg, &sig65)));
c.bench_function("ml_dsa_87 pk verify", |b| b.iter(|| pk87.verify(&msg, &sig87)));
c.bench_function("ml_dsa_44 pk verify", |b| b.iter(|| pk44.verify(&msg, &sig44, &[0])));
c.bench_function("ml_dsa_65 pk verify", |b| b.iter(|| pk65.verify(&msg, &sig65, &[0])));
c.bench_function("ml_dsa_87 pk verify", |b| b.iter(|| pk87.verify(&msg, &sig87, &[0])));

c.bench_function("ml_dsa_44 epk verify", |b| b.iter(|| epk44.verify(&msg, &sig44)));
c.bench_function("ml_dsa_65 epk verify", |b| b.iter(|| epk65.verify(&msg, &sig65)));
c.bench_function("ml_dsa_87 epk verify", |b| b.iter(|| epk87.verify(&msg, &sig87)));
c.bench_function("ml_dsa_44 epk verify", |b| b.iter(|| epk44.verify(&msg, &sig44, &[0])));
c.bench_function("ml_dsa_65 epk verify", |b| b.iter(|| epk65.verify(&msg, &sig65, &[0])));
c.bench_function("ml_dsa_87 epk verify", |b| b.iter(|| epk87.verify(&msg, &sig87, &[0])));
}

criterion_group!(benches, criterion_benchmark);
Expand Down
51 changes: 28 additions & 23 deletions src/conversion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -319,48 +319,53 @@ pub(crate) fn hint_bit_unpack<const K: usize>(
//
// 4: if y[ω + i] < Index or y[ω + i] > ω then return ⊥
if (y_bytes[omega_u + i] < index) || (y_bytes[omega_u + i] > omega.to_le_bytes()[0]) {
return Err("Alg 15a: returns ⊥");
return Err("Alg 15a: returns ⊥ (4)");

// 5: end if
}

// Note that there is a bug in the FIPS 204 draft specification that allows forgeability.
// Discussion/thread here: https://groups.google.com/a/list.nist.gov/g/pqc-forum/c/TQo-qFbBO1A/m/YcYKjMblAAAJ
// The missed portion of reference code: https://github.com/pq-crystals/dilithium/blob/master/ref/packing.c#L223
// The code currently implemented here intentionally matches the flawed FIPS 204 draft spec.
// The `bad_sig()` test implemented in `integration.rs` demonstrates the flaw, and the adjacent `forever()`
// test is able to uncover additional instances.
// This code will implement the fix forthcoming in FIPS 204 as soon as it is available.
// 6: First ← Index
let first = index;

// 6: while Index < y[ω + i] do
// 7: while Index < y[ω + i] do
while index < y_bytes[omega_u + i] {

// 8: if Index > First then
if index > first {
//
// 9: if 𝑦[Index − 1] ≥ 𝑦[Index] then return ⊥ ▷ malformed input
if y_bytes[usize::from(index) - 1] >= y_bytes[usize::from(index)] {
return Err("Alg 15a: returns ⊥ (9)");

// 10: end if
}

// 11: end if
}
//
// 7: h[i]_{y[Index]} ← 1
// 12: h[i]_{y[Index]} ← 1
h[i].0[y_bytes[index as usize] as usize] = 1;

// 8: Index ← Index + 1
// 13: Index ← Index + 1
index += 1;

// 9: end while
// 14: end while
}

// 10: end for
// 15: end for
}

// 11: while Index < ω do
while index < omega.to_le_bytes()[0] {
// 16: for 𝑖 from Index to 𝜔 − 1 do ▷ read any leftover bytes in the first 𝜔 bytes of 𝑦
for i in index..omega.to_le_bytes()[0] {
//
// 12: if y[Index] != 0 then return ⊥
if y_bytes[index as usize] != 0 {
return Err("Alg 15b: returns ⊥");
// 17: if y[i] != 0 then return ⊥
if y_bytes[i as usize] != 0 {
return Err("Alg 15b: returns ⊥ (17");

// 13: end if
// 18: end if
}

// 14: Index ← Index + 1
index += 1;

// 15: end while
// 19: end for
}

// 16: return h
Expand Down
92 changes: 62 additions & 30 deletions src/hashing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,15 @@ use crate::types::{R, R0, T, T0};
use sha3::digest::{ExtendableOutput, Update, XofReader};
use sha3::{Shake128, Shake256};

// use sha3::Shake256Core;
// use sha3::digest::core_api::CoreWrapper;
//
// pub(crate) fn hhh_xof(v: &[&[u8]]) -> CoreWrapper<Shake256Core> {
// let mut hasher = Shake256::default();
// v.iter().for_each(|b| hasher.update(b));
// hasher
// }


/// # Function H(v,d) of (8.1) on page 29.
/// Takes a reference to a list of byte-slice references and runs them through Shake256.
Expand Down Expand Up @@ -38,52 +47,75 @@ pub(crate) fn h128_xof(v: &[&[u8]]) -> impl XofReader {
///
/// **Input**: A seed `ρ ∈{0,1}^256` <br>
/// **Output**: A polynomial `c` in `Rq`.
pub(crate) fn sample_in_ball<const CTEST: bool>(tau: i32, rho: &[u8; 32]) -> R {
pub(crate) fn sample_in_ball<const CTEST: bool>(tau: i32, rho: &[u8]) -> R {
let tau = usize::try_from(tau).expect("cannot fail");
let mut xof = h_xof(&[rho]);
let mut hpk8 = [0u8; 8];
xof.read(&mut hpk8); // Save the first 8 bytes for step 9
//let mut xof = hhh_xof(&[rho]).finalize_xof();

// 1: c ← 0
let mut c = R0;

// 2: k ← 8; k implicitly advances with each sample
let mut hpk = [0u8];
// 2: ctx ← H.Init()
// 3: ctx ← H.Absorb(ctx, 𝜌)
let mut h_ctx = h_xof(&[rho]); // init and absorb

// 3: for i from 256 − τ to 255 do
// 4: (ctx, 𝑠) ← H.Squeeze(ctx, 8)
// 5: ℎ ← BytesToBits(𝑠)
let mut h = [0u8; 8];
h_ctx.read(&mut h); // Save the first 8 bytes for step 9

// 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);

// 8: while 𝑗 > 𝑖 do
while usize::from(j[0]) > i {

// 9: (ctx, 𝑗) ← H.Squeeze(ctx, 1)
h_ctx.read(&mut j);

// 10: end while
}

// 11: ci ← cj
c.0[i] = c.0[usize::from(j[0])];

// 12: c_j ← (−1)^{H(ρ)[i+τ−256]
let index = i + tau - 256;
let bite = h[index / 8];
let shifted = bite >> (index & 0x07);
c.0[usize::from(j[0])] = 1 - 2 * i32::from(shifted & 0x01);

// 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;
}
}
// 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];

// 8: ci ← cj
c.0[i] = c.0[usize::from(j)];

// 9: c_j ← (−1)^{H(ρ)[i+τ−256]
let index = i + tau - 256;
let bite = hpk8[index / 8];
let shifted = bite >> (index & 0x07);
c.0[usize::from(j)] = 1 - 2 * i32::from(shifted & 0x01);
//let j = hpk[0];

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

// 11: end for
}

// slightly redundant...
debug_assert!(
c.0.iter().map(|&e| usize::from(e != 0)).sum::<usize>() == tau,
Expand All @@ -94,7 +126,7 @@ pub(crate) fn sample_in_ball<const CTEST: bool>(tau: i32, rho: &[u8; 32]) -> R {
"Alg 23: bad hamming weight (b)"
);

// 12: return c
// 14: return c
c
}

Expand Down
10 changes: 5 additions & 5 deletions src/ml_dsa.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ pub(crate) fn sign_finish<
// We may have arrived via `HashML-DSA.Sign()`
let mut h6 = if oid.is_empty() {
// From ML-DSA.Sing(): 𝑀′ ← BytesToBits(IntegerToBytes(0,1) ∥ IntegerToBytes(|𝑐𝑡𝑥|,1) ∥ 𝑐𝑡𝑥) ∥ 𝑀
h_xof(&[tr, &[0x00u8], &[ctx.len().to_le_bytes()[0]], ctx, message])
h_xof(&[tr, &[0u8], &[ctx.len().to_le_bytes()[0]], ctx, message])
} else {
// From HashML-DSA.Sign(): 𝑀′ ← BytesToBits(IntegerToBytes(1,1) ∥ IntegerToBytes(|𝑐𝑡𝑥|,1) ∥ 𝑐𝑡𝑥 ∥ OID ∥ PH𝑀 )
h_xof(&[tr, &[0x01u8], &[oid.len().to_le_bytes()[0]], ctx, oid, phm])
Expand Down Expand Up @@ -177,11 +177,11 @@ pub(crate) fn sign_finish<
h15.read(&mut c_tilde);

// 16: (c_tilde_1, c_tilde_2) ∈ {0,1}^256 × {0,1}^{2·Lambda-256} ← c_tilde ▷ First 256 bits of commitment hash
let c_tilde_1: [u8; 32] = core::array::from_fn(|i| c_tilde[i]);
//let c_tilde_1: [u8; 32] = core::array::from_fn(|i| c_tilde[i]);
// c_tilde_2 is never used!

// 17: c ← SampleInBall(c_tilde_1) ▷ Verifier’s challenge
let c: R = sample_in_ball::<CTEST>(tau, &c_tilde_1);
let c: R = sample_in_ball::<CTEST>(tau, &c_tilde);

// 18: c_hat ← NTT(c)
let c_hat: &T = &ntt(&[c])[0];
Expand Down Expand Up @@ -379,11 +379,11 @@ pub(crate) fn verify_finish<
h7.read(&mut mu);

// 8: (c_tilde_1, c_tilde_2) ∈ {0,1}^256 × {0,1}^{2λ-256} ← c_tilde
let c_tilde_1 = <&[u8; 32]>::try_from(&c_tilde[0..32]).expect("cannot fail");
//let c_tilde_1 = <&[u8; 32]>::try_from(&c_tilde[0..32]).expect("cannot fail");
// c_tilde_2 identifier is unused...

// 9: c ← SampleInBall(c_tilde_1) ▷ Compute verifier’s challenge from c_tilde
let c: R = sample_in_ball::<false>(tau, c_tilde_1); // false, as this instance isn't pertinent to CT
let c: R = sample_in_ball::<false>(tau, &c_tilde); // false, as this instance isn't pertinent to CT

// 10: w′_Approx ← invNTT(cap_A_hat ◦ NTT(z) - NTT(c) ◦ NTT(t_1 · 2^d) ▷ w′_Approx = Az − ct1·2^d
let wp_approx: [R; K] = {
Expand Down
44 changes: 43 additions & 1 deletion tests/nist_vectors/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use fips204::ml_dsa_65;
#[cfg(feature = "ml-dsa-87")]
use fips204::ml_dsa_87;

use fips204::traits::SerDes; //{KeyGen, SerDes, Signer, Verifier};
use fips204::traits::{SerDes, Signer}; //{KeyGen, SerDes, Signer, Verifier};


// ----- CUSTOM RNG TO REPLAY VALUES -----
Expand Down Expand Up @@ -90,3 +90,45 @@ fn test_keygen() {
}
}
}

#[ignore]
#[test]
fn test_siggen() {
let vectors =
fs::read_to_string("./tests/nist_vectors/ML-DSA-sigGen-FIPS204/internalProjection.json")
.expect("Unable to read file");
let v: Value = serde_json::from_str(&vectors).unwrap();

for test_group in v["testGroups"].as_array().unwrap().iter() {
for test in test_group["tests"].as_array().unwrap().iter() {
let sk_bytes = decode(test["sk"].as_str().unwrap()).unwrap();
let message = decode(test["message"].as_str().unwrap()).unwrap();
let sig_exp = decode(test["signature"].as_str().unwrap()).unwrap();
let seed = test["rnd"].as_str();
if seed.is_none() {continue}; // TODO: no seed means 00000...00?
let mut rnd = TestRng::new();
rnd.push(&decode(seed.unwrap()).unwrap());

#[cfg(feature = "ml-dsa-44")]
if test_group["parameterSet"] == "ML-DSA-44" {
let sk = ml_dsa_44::PrivateKey::try_from_bytes(sk_bytes.try_into().unwrap()).unwrap();
let sig_act = sk.try_sign_with_rng(&mut rnd, &message, &[]).unwrap();
assert_eq!(sig_exp, sig_act);
}

// #[cfg(feature = "ml-dsa-65")]
// if test_group["parameterSet"] == "ML-DSA-65" {
// let (pk_act, sk_act) = ml_dsa_65::try_keygen_with_rng(&mut rnd).unwrap();
// assert_eq!(pk_exp, pk_act.into_bytes());
// assert_eq!(sk_exp, sk_act.into_bytes());
// }
//
// #[cfg(feature = "ml-dsa-87")]
// if test_group["parameterSet"] == "ML-DSA-87" {
// let (pk_act, sk_act) = ml_dsa_87::try_keygen_with_rng(&mut rnd).unwrap();
// assert_eq!(pk_exp, pk_act.into_bytes());
// assert_eq!(sk_exp, sk_act.into_bytes());
// }
}
}
}

0 comments on commit d8d6fb2

Please sign in to comment.