Skip to content

Commit

Permalink
nist vectors
Browse files Browse the repository at this point in the history
  • Loading branch information
eschorn1 committed Sep 22, 2024
1 parent 070920e commit 00e7745
Show file tree
Hide file tree
Showing 7 changed files with 136 additions and 60 deletions.
26 changes: 19 additions & 7 deletions benches/benchmark.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,13 +51,25 @@ pub fn criterion_benchmark(c: &mut Criterion) {
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, &[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, &[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 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, &[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, &[0])));
c.bench_function("ml_dsa_65 pk verify", |b| b.iter(|| pk65.verify(&msg, &sig65, &[0])));
Expand Down
1 change: 0 additions & 1 deletion src/conversion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,6 @@ pub(crate) fn hint_bit_unpack<const K: usize>(

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

// 8: if Index > First then
if index > first {
//
Expand Down
44 changes: 21 additions & 23 deletions src/hashing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ pub(crate) fn sample_in_ball<const CTEST: bool>(tau: i32, rho: &[u8]) -> R {

// 2: ctx ← H.Init()
// 3: ctx ← H.Absorb(ctx, 𝜌)
let mut h_ctx = h_xof(&[rho]); // init and absorb
let mut h_ctx = h_xof(&[rho]); // init and absorb

// 4: (ctx, 𝑠) ← H.Squeeze(ctx, 8)
// 5: ℎ ← BytesToBits(𝑠)
Expand All @@ -65,14 +65,12 @@ 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);

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

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

Expand All @@ -92,29 +90,29 @@ pub(crate) fn sample_in_ball<const CTEST: bool>(tau: i32, rho: &[u8]) -> R {
}


// 2: k ← 8; k implicitly advances with each sample
// 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)
//
// 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!(
Expand Down
79 changes: 71 additions & 8 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,7 @@ macro_rules! functionality {
message,
ctx,
&[],
&[],
&[], false
)?;
Ok(sig)
}
Expand Down Expand Up @@ -286,7 +286,7 @@ macro_rules! functionality {
}
};
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, &oid, &phm,
rng, BETA, GAMMA1, GAMMA2, OMEGA, TAU, &esk, message, ctx, &oid, &phm, false
)?;
Ok(sig)
}
Expand All @@ -311,7 +311,7 @@ macro_rules! functionality {
message,
ctx,
&[],
&[],
&[], false
)?;
Ok(sig)
}
Expand All @@ -331,7 +331,7 @@ macro_rules! functionality {
message,
ctx,
&[],
&[],
&[], false
)?;
Ok(sig)
}
Expand All @@ -355,7 +355,7 @@ macro_rules! functionality {
TAU,
&epk.unwrap(),
&message,
&sig, ctx, &[], &[]
&sig, ctx, &[], &[], false
);
if res.is_err() {
return false;
Expand Down Expand Up @@ -399,7 +399,7 @@ macro_rules! functionality {
TAU,
&epk.unwrap(),
&message,
&sig, ctx, &oid, &phm
&sig, ctx, &oid, &phm, false
);
if res.is_err() {
return false;
Expand All @@ -414,7 +414,7 @@ macro_rules! functionality {

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, &[], &[], &[]
BETA, GAMMA1, GAMMA2, OMEGA, TAU, &self, &message, &sig, &[], &[], &[], false
);
if res.is_err() {
return false;
Expand Down Expand Up @@ -469,10 +469,73 @@ 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, &[], &[], &[]
rng, BETA, GAMMA1, GAMMA2, OMEGA, TAU, &esk, message, &[], &[], &[], false
)?;
Ok(sig)
}

#[deprecated="Temporary function to allow application of internal nist vectors; will be removed"]
/// As of Sep 22 2024, the NIST test vectors are applied to the **internal** functions rather than
/// the external API. T
///
/// he primary difference pertains to the prepending of domain, context, OID and
/// hash information to the message in the `sign_finish()` and `verify_finish()` functions (follow
/// the last `nist=true` function argument). This is expected to change such that the full API can
/// be robustly tested - when this happens, this function will no longer be needed.
/// # Errors
/// Propagate errors from the `sign_finish()` function (for failing RNG).
pub fn _internal_sign(
sk: &PrivateKey, rng: &mut impl CryptoRngCore, message: &[u8], ctx: &[u8],
) -> Result<[u8; SIG_LEN], &'static str> {
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
)?;
Ok(sig)
}

#[deprecated="Temporary function to allow application of internal nist vectors; will be removed"]
#[must_use]
/// As of Sep 22 2024, the NIST test vectors are applied to the **internal** functions rather than
/// the external API.
///
/// The primary difference pertains to the prepending of domain, context, OID and
/// hash information to the message in the `sign_finish()` and `verify_finish()` functions (follow
/// the last `nist=true` function argument). This is expected to change such that the full API can
/// be robustly tested - when this happens, this function will no longer be needed.
pub fn _internal_verify(pk: &PublicKey, message: &[u8], sig: &[u8; SIG_LEN], ctx: &[u8]) -> bool {
if ctx.len() > 255 { return false };
let epk = ml_dsa::verify_start(&pk.0);
if epk.is_err() {
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() {
return false;
};
res.unwrap()
}

};
}

Expand Down
20 changes: 11 additions & 9 deletions src/ml_dsa.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ pub(crate) fn sign_start<const CTEST: bool, const K: usize, const L: usize, cons
}

/// Continuation of `sign_start()`
#[allow(clippy::similar_names, clippy::many_single_char_names, clippy::too_many_arguments)]
#[allow(clippy::similar_names, clippy::many_single_char_names, clippy::too_many_arguments, clippy::too_many_lines)]
pub(crate) fn sign_finish<
const CTEST: bool,
const K: usize,
Expand All @@ -96,7 +96,7 @@ pub(crate) fn sign_finish<
const W1_LEN: usize,
>(
rand_gen: &mut impl CryptoRngCore, beta: i32, gamma1: i32, gamma2: i32, omega: i32, tau: i32,
esk: &ExpandedPrivateKey<K, L>, message: &[u8], ctx: &[u8], oid: &[u8], phm: &[u8],
esk: &ExpandedPrivateKey<K, L>, message: &[u8], ctx: &[u8], oid: &[u8], phm: &[u8], nist: bool,
) -> Result<[u8; SIG_LEN], &'static str> {
//
// 1: (ρ, K, tr, s_1, s_2, t_0) ← skDecode(sk)
Expand Down Expand Up @@ -126,10 +126,11 @@ pub(crate) fn sign_finish<

// 6: µ ← H(tr || M', 512) ▷ Compute message representative µ
// 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, &[0u8], &[ctx.len().to_le_bytes()[0]], ctx, message]) // TODO: OMFG! <---- CAVP VECTORS WHA!!!
let mut h6 = if nist {
h_xof(&[tr, message])
} else if oid.is_empty() {
// From ML-DSA.Sing(): 𝑀′ ← BytesToBits(IntegerToBytes(0,1) ∥ IntegerToBytes(|𝑐𝑡𝑥|,1) ∥ 𝑐𝑡𝑥) ∥ 𝑀
h_xof(&[tr, &[0u8], &[ctx.len().to_le_bytes()[0]], ctx, message]) // TODO: OMFG! <---- CAVP VECTORS WHA!!!
} 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 @@ -328,7 +329,7 @@ pub(crate) fn verify_finish<
const W1_LEN: usize,
>(
beta: i32, gamma1: i32, gamma2: i32, omega: i32, tau: i32, epk: &ExpandedPublicKey<K, L>,
m: &[u8], sig: &[u8; SIG_LEN], ctx: &[u8], oid: &[u8], phm: &[u8]
m: &[u8], sig: &[u8; SIG_LEN], ctx: &[u8], oid: &[u8], phm: &[u8], nist: bool,
) -> Result<bool, &'static str> {
//
let ExpandedPublicKey { cap_a_hat, tr, t1_d2_hat_mont } = epk;
Expand Down Expand Up @@ -369,10 +370,11 @@ pub(crate) fn verify_finish<


// 7: µ ← H(tr || M, 512) ▷ Compute message representative µ
let mut h7 = if oid.is_empty() {
// From ML-DSA.Verify(): 5: 𝑀′ ← BytesToBits(IntegerToBytes(0,1) ∥ IntegerToBytes(|𝑐𝑡𝑥|,1) ∥ 𝑐𝑡𝑥) ∥ 𝑀
// h_xof(&[tr, &[0u8], &[ctx.len().to_le_bytes()[0]], ctx, m]) // TODO: OMFG! <---- CAVP VECTORS WHA!!!
let mut h7 = if nist {
h_xof(&[tr, m])
} else if oid.is_empty() {
// From ML-DSA.Verify(): 5: 𝑀′ ← BytesToBits(IntegerToBytes(0,1) ∥ IntegerToBytes(|𝑐𝑡𝑥|,1) ∥ 𝑐𝑡𝑥) ∥ 𝑀
h_xof(&[tr, &[0u8], &[ctx.len().to_le_bytes()[0]], ctx, m]) // TODO: OMFG! <---- CAVP VECTORS WHA!!!
} else {
// From HashML-DSA.Verify(): 18: 𝑀′ ← BytesToBits(IntegerToBytes(1,1) ∥ IntegerToBytes(|𝑐𝑡𝑥|,1) ∥ 𝑐𝑡𝑥 ∥ OID ∥ PH𝑀 )
h_xof(&[tr, &[0x01u8], &[oid.len().to_le_bytes()[0]], ctx, oid, phm])
Expand Down
2 changes: 1 addition & 1 deletion tests/messages.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use fips204::traits::{KeyGen, SerDes, Signer, Verifier};
#[cfg(feature = "ml-dsa-44")]
use rand_chacha::rand_core::SeedableRng;

#[ignore] // TODO: Fix in final update
#[ignore] // TODO: Fix in final update
#[cfg(feature = "ml-dsa-44")]
#[test]
fn test_browser_message() {
Expand Down
Loading

0 comments on commit 00e7745

Please sign in to comment.