From 472a345dbc0aeb44a96a387517271e6218823fe7 Mon Sep 17 00:00:00 2001 From: eschorn1 Date: Mon, 17 Jun 2024 14:17:08 -0500 Subject: [PATCH] feedback from review 2 --- .../{build-attest.yaml => provenance.yaml} | 9 ++-- CHANGELOG.md | 5 ++ Cargo.toml | 2 +- README.md | 2 +- benches/benchmark.rs | 12 ++--- ct_cm4/Cargo.toml | 2 +- ct_cm4/src/main.rs | 2 +- dudect/Cargo.toml | 2 +- fuzz/Cargo.toml | 2 +- fuzz/README.md | 16 +++--- fuzz/fuzz_targets/fuzz_all.rs | 22 +++------ src/conversion.rs | 34 +++++++------ src/hashing.rs | 25 +++++++--- src/helpers.rs | 33 ++++++++----- src/high_low.rs | 27 +++++++--- src/lib.rs | 49 ++++++++++--------- src/ml_dsa.rs | 31 ++++++------ src/ntt.rs | 4 +- src/traits.rs | 22 ++++----- src/types.rs | 2 +- tests/integration.rs | 34 ++++++------- tests/messages.rs | 2 +- tests/test_vectors.rs | 12 ++--- wasm/Cargo.toml | 2 +- wasm/src/lib.rs | 2 +- 25 files changed, 196 insertions(+), 159 deletions(-) rename .github/workflows/{build-attest.yaml => provenance.yaml} (94%) diff --git a/.github/workflows/build-attest.yaml b/.github/workflows/provenance.yaml similarity index 94% rename from .github/workflows/build-attest.yaml rename to .github/workflows/provenance.yaml index 1b4b48e..634a9bb 100644 --- a/.github/workflows/build-attest.yaml +++ b/.github/workflows/provenance.yaml @@ -1,6 +1,9 @@ name: build-attest -on: [ push ] +on: + push: + tags: + - '*' jobs: build: @@ -26,13 +29,13 @@ jobs: - name: Attest uses: actions/attest-build-provenance@v1 with: - subject-path: '${{ github.workspace }}/target/*/release/libfips*' + subject-path: '${{ github.workspace }}/target/*/release/libfips204.rlib' - name: 'Upload Artifact' uses: actions/upload-artifact@v4 with: name: ${{ matrix.target }}--libfips204.rlib path: '${{ github.workspace }}/target/*/release/libfips204.rlib' - retention-days: 25 + retention-days: 60 - name: Checkout actions-oidc-debugger uses: actions/checkout@v4 with: diff --git a/CHANGELOG.md b/CHANGELOG.md index 0eba5dc..bfc7dc9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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.2.1 (2024-06-18) + +- Internal rework based on review 2 feedback +- API: try_verify() -> verify() change to prevent usage mistakes + ## 0.2.0 (2024-05-25) diff --git a/Cargo.toml b/Cargo.toml index 79cbc5e..ad2cfaf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,7 @@ workspace = { exclude = ["ct_cm4", "dudect", "fuzz", "wasm"] } [package] name = "fips204" -version = "0.2.0" +version = "0.2.1" edition = "2021" license = "MIT OR Apache-2.0" description = "FIPS 204 (draft): Module-Lattice-Based Digital Signature" diff --git a/README.md b/README.md index e6acffa..cfffdbc 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,7 @@ let (pk_recv, msg_recv, sig_recv) = (pk_send, msg_send, sig_send); // Deserialize the public key and signature, then verify the message let pk2 = ml_dsa_44::PublicKey::try_from_bytes(pk_recv)?; -let v = pk2.try_verify(&msg_recv, &sig_recv)?; // Use the public to verify message signature +let v = pk2.verify(&msg_recv, &sig_recv); // Use the public to verify message signature assert!(v); # } # Ok(()) diff --git a/benches/benchmark.rs b/benches/benchmark.rs index 5a95ba5..609e50a 100644 --- a/benches/benchmark.rs +++ b/benches/benchmark.rs @@ -59,13 +59,13 @@ pub fn criterion_benchmark(c: &mut Criterion) { 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 pk verify", |b| b.iter(|| pk44.try_verify(&msg, &sig44))); - c.bench_function("ml_dsa_65 pk verify", |b| b.iter(|| pk65.try_verify(&msg, &sig65))); - c.bench_function("ml_dsa_87 pk verify", |b| b.iter(|| pk87.try_verify(&msg, &sig87))); + 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 epk verify", |b| b.iter(|| epk44.try_verify(&msg, &sig44))); - c.bench_function("ml_dsa_65 epk verify", |b| b.iter(|| epk65.try_verify(&msg, &sig65))); - c.bench_function("ml_dsa_87 epk verify", |b| b.iter(|| epk87.try_verify(&msg, &sig87))); + 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))); } criterion_group!(benches, criterion_benchmark); diff --git a/ct_cm4/Cargo.toml b/ct_cm4/Cargo.toml index 483df22..e18ff42 100644 --- a/ct_cm4/Cargo.toml +++ b/ct_cm4/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "fips204-ct_cm4" -version = "0.2.0" +version = "0.2.1" license = "MIT OR Apache-2.0" description = "Cortex-M4 testbench for FIPS 204 (draft) ML-DSA" authors = ["Eric Schorn "] diff --git a/ct_cm4/src/main.rs b/ct_cm4/src/main.rs index d3724f8..d9732b5 100644 --- a/ct_cm4/src/main.rs +++ b/ct_cm4/src/main.rs @@ -37,7 +37,7 @@ fn main() -> ! { let start = DWT::cycle_count(); asm::isb(); - assert!(pk.try_verify(&MESSAGE, &SIGNATURE).unwrap()); + assert!(pk.verify(&MESSAGE, &SIGNATURE)); asm::isb(); let finish = DWT::cycle_count(); diff --git a/dudect/Cargo.toml b/dudect/Cargo.toml index 88cb8e4..5f74ef0 100644 --- a/dudect/Cargo.toml +++ b/dudect/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "fips204-dudect" -version = "0.2.0" +version = "0.2.1" authors = ["Eric Schorn "] description = "Dudect testbench for FIPS 204 (draft) ML-DSA" publish = false diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml index 6fa445f..1469dc1 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "fips204-fuzz" -version = "0.2.0" +version = "0.2.1" publish = false edition = "2021" license = "MIT OR Apache-2.0" diff --git a/fuzz/README.md b/fuzz/README.md index 60acc4a..d1daaad 100644 --- a/fuzz/README.md +++ b/fuzz/README.md @@ -12,14 +12,16 @@ $ mkdir -p corpus/fuzz_all $ dd if=/dev/zero bs=1 count=6292 > corpus/fuzz_all/seed0 $ for i in $(seq 1 2); do head -c 6292 corpus/fuzz_all/seed$i; done $ dd if=/dev/zero bs=1 count=6292 | tr '\0x00' '\377' > corpus/fuzz_all/seed3 -$ cargo fuzz run fuzz_all -j 4 -- -max_total_time=3600 # run twice +$ cargo fuzz run fuzz_all -j 4 -- -max_total_time=1000 # run twice -#452: cov: 13276 ft: 9644 corp: 84 exec/s 0 oom/timeout/crash: 0/0/0 time: 509s job: 37 dft_time: 0 -#483: cov: 13276 ft: 9651 corp: 86 exec/s 0 oom/timeout/crash: 0/0/0 time: 521s job: 38 dft_time: 0 -#500: cov: 13276 ft: 9652 corp: 87 exec/s 0 oom/timeout/crash: 0/0/0 time: 527s job: 39 dft_time: 0 -#518: cov: 13276 ft: 9652 corp: 87 exec/s 0 oom/timeout/crash: 0/0/0 time: 536s job: 40 dft_time: 0 -#534: cov: 13276 ft: 9681 corp: 89 exec/s 0 oom/timeout/crash: 0/0/0 time: 557s job: 41 dft_time: 0 -#553: cov: 13276 ft: 10255 corp: 92 exec/s 0 oom/timeout/crash: 0/0/0 time: 575s job: 42 dft_time: 0 +#1054: cov: 13559 ft: 10028 corp: 102 exec/s 0 oom/timeout/crash: 0/0/0 time: 899s job: 60 dft_time: 0 +#1084: cov: 13559 ft: 10028 corp: 102 exec/s 0 oom/timeout/crash: 0/0/0 time: 913s job: 61 dft_time: 0 +#1124: cov: 13559 ft: 10028 corp: 102 exec/s 0 oom/timeout/crash: 0/0/0 time: 936s job: 62 dft_time: 0 +#1170: cov: 13559 ft: 10134 corp: 104 exec/s 0 oom/timeout/crash: 0/0/0 time: 954s job: 63 dft_time: 0 +#1207: cov: 13559 ft: 10135 corp: 105 exec/s 0 oom/timeout/crash: 0/0/0 time: 970s job: 64 dft_time: 0 +#1240: cov: 13559 ft: 10135 corp: 105 exec/s 0 oom/timeout/crash: 0/0/0 time: 985s job: 65 dft_time: 0 +INFO: fuzzed for 1001 seconds, wrapping up soon +INFO: exiting: 0 time: 1004s ~~~ Coverage status of ml_dsa_44 is robust, see: diff --git a/fuzz/fuzz_targets/fuzz_all.rs b/fuzz/fuzz_targets/fuzz_all.rs index dda153b..a63a396 100644 --- a/fuzz/fuzz_targets/fuzz_all.rs +++ b/fuzz/fuzz_targets/fuzz_all.rs @@ -24,31 +24,25 @@ fuzz_target!(|data: [u8; 2560+2420+1312]| { // sk_len + sig_len + pk_len = 6292 // Try to use 'fuzzy' sk if let Ok(ref sk) = sk_fuzz { let sig1 = sk.try_sign(&[0u8, 1, 2, 3]).unwrap(); - let sk2 = KG::gen_expanded_private(&sk).unwrap(); + let sk2 = KG::gen_expanded_private(sk).unwrap(); let sig2 = sk2.try_sign(&[4u8, 5, 6, 7]).unwrap(); // ...with good pk - let res = pk_good.try_verify(&[0u8, 1, 2, 3], &sig1); - assert!(res.is_err() || !res.unwrap(), "err 1"); - let res = pk_good.try_verify(&[0u8, 1, 2, 3], &sig2); - assert!(res.is_err() || !res.unwrap(), "err 2"); + let _res = pk_good.verify(&[0u8, 1, 2, 3], &sig1); + let _res = pk_good.verify(&[0u8, 1, 2, 3], &sig2); } // Try to use 'fuzzy' pk if let Ok(ref pk) = pk_fuzz { - let res = pk.try_verify(&[0u8, 1, 2, 3], &sig_fuzz); - assert!(res.is_err() || !res.unwrap(), "err 3"); - let pk2 = KG::gen_expanded_public(&pk).unwrap(); - let res = pk2.try_verify(&[0u8, 1, 2, 3], &sig_fuzz); - assert!(res.is_err() || !res.unwrap(), "err 4"); + let _res = pk.verify(&[0u8, 1, 2, 3], &sig_fuzz); + let pk2 = KG::gen_expanded_public(pk).unwrap(); + let _res = pk2.verify(&[0u8, 1, 2, 3], &sig_fuzz); // .. with good sig - let res = pk2.try_verify(&[0u8, 1, 2, 3], &sig_good); - assert!(res.is_err() || !res.unwrap(), "err 5"); + let _res = pk2.verify(&[0u8, 1, 2, 3], &sig_good); } // Try to use 'fuzzy' sk and 'fuzzy' pk if let (Ok(sk), Ok(pk)) = (sk_fuzz, pk_fuzz) { let _sig = sk.try_sign(&[0u8, 1, 2, 3]).unwrap(); - let res = pk.try_verify(&[0u8, 1, 2, 3], &sig_fuzz); - assert!(res.is_err() || !res.unwrap(), "err 6"); // hmm, odds of getting good sig on fuzzy signature + let _res = pk.verify(&[0u8, 1, 2, 3], &sig_fuzz); } }); diff --git a/src/conversion.rs b/src/conversion.rs index 5d019a9..7e072ec 100644 --- a/src/conversion.rs +++ b/src/conversion.rs @@ -18,7 +18,7 @@ use crate::Q; // algorithms have been reimplemented at a higher level. -/// # Algorithm 8: `CoefFromThreeBytes(b0,b1,b2)` on page 21. +/// # Algorithm 8: `CoeffFromThreeBytes(b0,b1,b2)` on page 21. /// Generates an element of `{0, 1, 2, ... , q − 1} ∪ {⊥}` used in rejection sampling. /// This function is used during keygen and signing, but only operates on the non-secret /// `rho` value stored in the public key, so need not be constant-time in normal @@ -33,7 +33,7 @@ use crate::Q; /// Returns an error `⊥` on input 3 bytes forming values between `Q=0x7F_E0_01`--`0x7F_FF_FF`, /// and between `0xFF_E0_01`--`0xFF_FF_FF` (latter range due to masking of bit 7 of byte 2) /// per spec; for rejection sampling. -pub(crate) fn coef_from_three_bytes(bbb: [u8; 3]) -> Result { +pub(crate) fn coeff_from_three_bytes(bbb: [u8; 3]) -> Result { // 1: if b2 > 127 then // 2: b2 ← b2 − 128 ▷ Set the top bit of b2 to zero // 3: end if @@ -56,7 +56,7 @@ pub(crate) fn coef_from_three_bytes(bbb: [u8; 3]) -> Result(bbb: [u8; 3]) -> Result 8 for rejection sampling. (panics on b > 15) #[allow(clippy::cast_possible_truncation)] // rem as u8 -pub(crate) fn coef_from_half_byte(eta: i32, b: u8) -> Result { +pub(crate) fn coeff_from_half_byte( + eta: i32, b: u8, +) -> Result { const M5: u32 = ((1u32 << 24) / 5) + 1; debug_assert!((eta == 2) || (eta == 4), "Alg 9: incorrect eta"); debug_assert!(b < 16, "Alg 9: b out of range"); // Note other cases involving b/eta will fall through to Err() @@ -269,9 +271,9 @@ pub(crate) fn hint_bit_pack( if CTEST || (h[i].0[j] != 0) { // // 6: y[Index] ← j ▷ Store the locations of the nonzero coefficients in h[i] - y_bytes[index] = j.to_le_bytes()[0]; - // - // 7: Index ← Index + 1 + y_bytes[index] = j.to_le_bytes()[0]; // ...bytes()[0] for clippy benefit ; ) + // + // 7: Index ← Index + 1 index += 1; // 8: end if @@ -378,21 +380,21 @@ mod tests { #[test] fn test_coef_from_three_bytes1() { let bytes = [0x12u8, 0x34, 0x56]; - let res = coef_from_three_bytes::(bytes).unwrap(); + let res = coeff_from_three_bytes::(bytes).unwrap(); assert_eq!(res, 0x0056_3412); } #[test] fn test_coef_from_three_bytes2() { let bytes = [0x12u8, 0x34, 0x80]; - let res = coef_from_three_bytes::(bytes).unwrap(); + let res = coeff_from_three_bytes::(bytes).unwrap(); assert_eq!(res, 0x0000_3412); } #[test] fn test_coef_from_three_bytes3() { let bytes = [0x01u8, 0xe0, 0x80]; - let res = coef_from_three_bytes::(bytes).unwrap(); + let res = coeff_from_three_bytes::(bytes).unwrap(); assert_eq!(res, 0x0000_e001); } @@ -400,21 +402,21 @@ mod tests { #[should_panic(expected = "panic: out of range")] fn test_coef_from_three_bytes4() { let bytes = [0x01u8, 0xe0, 0x7f]; - let res = coef_from_three_bytes::(bytes).expect("panic: out of range"); + let res = coeff_from_three_bytes::(bytes).expect("panic: out of range"); assert_eq!(res, 0x0056_3412); } #[test] fn test_coef_from_half_byte1() { let inp = 3; - let res = coef_from_half_byte::(2, inp).unwrap(); + let res = coeff_from_half_byte::(2, inp).unwrap(); assert_eq!(-1, res); } #[test] fn test_coef_from_half_byte2() { let inp = 8; - let res = coef_from_half_byte::(4, inp).unwrap(); + let res = coeff_from_half_byte::(4, inp).unwrap(); assert_eq!(-4, res); } @@ -423,7 +425,7 @@ mod tests { #[test] fn test_coef_from_half_byte_validation1() { let inp = 22; - let res = coef_from_half_byte::(2, inp); + let res = coeff_from_half_byte::(2, inp); assert!(res.is_err()); } @@ -432,14 +434,14 @@ mod tests { #[test] fn test_coef_from_half_byte_validation2() { let inp = 5; - let res = coef_from_half_byte::(1, inp); + let res = coeff_from_half_byte::(1, inp); assert!(res.is_err()); } #[test] fn test_coef_from_half_byte_validation3() { let inp = 10; - let res = coef_from_half_byte::(4, inp); + let res = coeff_from_half_byte::(4, inp); assert!(res.is_err()); } diff --git a/src/hashing.rs b/src/hashing.rs index 4aa1b77..77614b3 100644 --- a/src/hashing.rs +++ b/src/hashing.rs @@ -1,6 +1,6 @@ // This file implements functionality from FIPS 204 section 8.3 Hashing and Pseudorandom Sampling -use crate::conversion::{bit_unpack, coef_from_half_byte, coef_from_three_bytes}; +use crate::conversion::{bit_unpack, coeff_from_half_byte, coeff_from_three_bytes}; use crate::helpers::{bit_length, is_in_range}; use crate::types::{R, R0, T, T0}; use sha3::digest::{ExtendableOutput, Update, XofReader}; @@ -84,8 +84,17 @@ pub(crate) fn sample_in_ball(tau: i32, rho: &[u8; 32]) -> R { // 11: end for } + // slightly redundant... + debug_assert!( + c.0.iter().map(|&e| usize::from(e != 0)).sum::() == tau, + "Alg 23: bad hamming weight (a)" + ); + debug_assert!( + c.0.iter().map(|&e| e & 1).sum::() == tau.try_into().expect("cannot fail"), + "Alg 23: bad hamming weight (b)" + ); + // 12: return c - debug_assert!(c.0.iter().filter(|e| e.abs() != 0).count() == tau, "Alg 23: bad hamming weight"); c } @@ -112,7 +121,7 @@ pub(crate) fn rej_ntt_poly(rhos: &[&[u8]]) -> T { // 4: a_hat[j] ← CoefFromThreeBytes(H128(ρ)[[c]], H128(ρ)[[c + 1]], H128(ρ)[[c + 2]]) let mut h128pc = [0u8; 3]; xof.read(&mut h128pc); // implicit c += 3 - let a_hat_j = coef_from_three_bytes::(h128pc); // gets a result + let a_hat_j = coeff_from_three_bytes::(h128pc); // gets a result // 5: c ← c + 3 (implicit) @@ -162,10 +171,10 @@ pub(crate) fn rej_bounded_poly(eta: i32, rhos: &[&[u8]]) -> R xof.read(&mut z); // 5: z0 ← CoefFromHalfByte(z mod 16, η) - let z0 = coef_from_half_byte::(eta, z[0] & 0x0f); + let z0 = coeff_from_half_byte::(eta, z[0] & 0x0f); // 6: z1 ← CoefFromHalfByte(⌊z/16⌋, η) - let z1 = coef_from_half_byte::(eta, z[0] >> 4); + let z1 = coeff_from_half_byte::(eta, z[0] >> 4); // 7: if z0 != ⊥ then if let Ok(z0) = z0 { @@ -280,8 +289,8 @@ pub(crate) fn expand_mask(gamma1: i32, rho: &[u8; 64], mu: u16) for r in 0..u16::try_from(L).expect("cannot fail") { // // 3: n ← IntegerToBits(µ + r, 16) - debug_assert!((mu + r < 1024), "Alg 28: mu + r out of range"); // TODO consider revising - let n = mu + r; + // debug_assert!((mu + r < 1024), "Alg 28: mu + r out of range"); // arbitrary limit not needed + let n = mu + r; // This will perform overflow check in debug, which removes need for above assert // 4: v ← (H(ρ || n)[[32rc]], H(ρ || n)[[32rc+1]], ..., H(ρ || n)[[32rc+32c − 1]]) let mut xof = h_xof(&[rho, &n.to_le_bytes()]); @@ -290,7 +299,7 @@ pub(crate) fn expand_mask(gamma1: i32, rho: &[u8; 64], mu: u16) // 5: s[r] ← BitUnpack(v, γ_1 − 1, γ_1) s[r as usize] = bit_unpack(&v[0..32 * c], gamma1 - 1, gamma1).expect("cannot fail"); debug_assert!( - s.iter().all(|r| is_in_range(r, gamma1, gamma1)), + s.iter().all(|r| is_in_range(r, gamma1 - 1, gamma1)), "Alg 28: s coeff out of range" ); diff --git a/src/helpers.rs b/src/helpers.rs index ef64704..12404e6 100644 --- a/src/helpers.rs +++ b/src/helpers.rs @@ -29,14 +29,14 @@ pub(crate) fn is_in_range(w: &R, lo: i32, hi: i32) -> bool { #[allow(clippy::cast_possible_truncation)] pub(crate) const fn partial_reduce64(a: i64) -> i32 { const M: i64 = (1 << 48) / (Q as i64); - debug_assert!(a < (i64::MAX / 64), "partial_reduce64 input"); // bit of headroom + debug_assert!(a.abs() < (67_058_539 << 32), "partial_reduce64 input"); let x = a >> 23; let a = a - x * (Q as i64); let x = a >> 23; let a = a - x * (Q as i64); let q = (a * M) >> 48; let res = a - q * (Q as i64); - debug_assert!(res < 2 * Q as i64, "partial_reduce64 output"); + debug_assert!(res.abs() < 2 * Q as i64, "partial_reduce64 output"); res as i32 } @@ -46,10 +46,10 @@ pub(crate) const fn partial_reduce64(a: i64) -> i32 { #[allow(clippy::cast_possible_truncation)] pub(crate) const fn partial_reduce64b(a: i64) -> i32 { const MM: i64 = ((1 << 64) / (Q as i128)) as i64; - debug_assert!(a < (i64::MAX / 64), "partial_reduce64b input"); // bit of headroom + debug_assert!(a < (i64::MAX / 64), "partial_reduce64b input"); // actually, works for all 64b inputs!! let q = (a as i128 * MM as i128) >> 64; // only top half is relevant let res = a - (q as i64 * Q as i64); - debug_assert!(res < 2 * Q as i64, "partial_reduce64b output"); + debug_assert!(res.abs() < 2 * Q as i64, "partial_reduce64b output"); res as i32 } @@ -60,6 +60,7 @@ pub(crate) const fn partial_reduce64b(a: i64) -> i32 { // error). So, increment these bits and then subtract off the corresponding // number of Q. The result is within (better than) -Q < res < Q. pub(crate) const fn partial_reduce32(a: i32) -> i32 { + debug_assert!(a.abs() < 2_143_289_344, "partial_reduce32 input"); let x = (a + (1 << 22)) >> 23; let res = a - x * Q; debug_assert!(res.abs() < Q, "partial_reduce32 output"); @@ -68,6 +69,7 @@ pub(crate) const fn partial_reduce32(a: i32) -> i32 { pub(crate) const fn full_reduce32(a: i32) -> i32 { + debug_assert!(a.abs() < 2_143_289_344, "full_reduce32 input"); let x = partial_reduce32(a); // puts us within better than -Q to +Q let res = x + ((x >> 31) & Q); // add Q if negative debug_assert!(res < Q, "full_reduce32 output"); @@ -85,7 +87,8 @@ pub(crate) const fn bit_length(a: i32) -> usize { a.ilog2() as usize + 1 } /// element m′ ∈ Z in the range −α/2 < m′ ≤ α/2 such that m and m′ are congruent /// modulo α. 'ready to optimize' pub(crate) fn center_mod(m: i32) -> i32 { - let t = partial_reduce32(m); + debug_assert!(m.abs() < 2_143_289_344, "center_mod input"); // for clarity; caught in full_reduce32 + let t = full_reduce32(m); let over2 = (Q / 2) - t; // check if t is larger than Q/2 let res = t - ((over2 >> 31) & Q); // sub Q if over2 is negative debug_assert_eq!(m.rem_euclid(Q), res.rem_euclid(Q), "center_mod output"); @@ -93,7 +96,7 @@ pub(crate) fn center_mod(m: i32) -> i32 { } -/// Matrix by vector multiplication; See top of page 10, first row: `w_hat` = `A_hat` mul `u_hat` +/// Matrix by vector multiplication; e.g., fips 203 top of page 10, first row: `w_hat` = `A_hat` mul `u_hat` #[must_use] pub(crate) fn mat_vec_mul( a_hat: &[[T; L]; K], u_hat: &[T; L], @@ -112,7 +115,7 @@ pub(crate) fn mat_vec_mul( } -/// Vector addition; See bottom of page 9, second row: `z_hat` = `u_hat` + `v_hat` +/// Vector addition; e.g., fips 203 bottom of page 9, second row: `z_hat` = `u_hat` + `v_hat` #[must_use] pub(crate) fn vec_add(vec_a: &[R; K], vec_b: &[R; K]) -> [R; K] { core::array::from_fn(|k| R(core::array::from_fn(|n| vec_a[k].0[n] + vec_b[k].0[n]))) @@ -131,15 +134,20 @@ pub(crate) fn infinity_norm(w: &[R; ROW]) -> i32 { w.iter() .flat_map(|row| row.0) .map(|element| center_mod(element).abs()) + // .max() might be non-CT on some targets. infinity_norm() is used in signature generation and + // verification; the values are ultimately revealed in the signature, so worst case is leaking + // which vector element failed. Not a problem since the whole thing is permutation-agnostic .max() .expect("infinity norm fails") } -#[allow(clippy::cast_possible_truncation)] // res as i32 +#[allow(clippy::cast_possible_truncation)] // a as i32, res as i32 pub(crate) const fn mont_reduce(a: i64) -> i32 { - const QINV: i64 = 58_728_449; // (Q * QINV) % 2**32 = 1 - let t = a.wrapping_mul(QINV) as i32; + const QINV: i32 = 58_728_449; // (Q * QINV) % 2**32 = 1 + debug_assert!(a >= -17_996_808_479_301_632, "mont_reduce input (a)"); + debug_assert!(a <= 17_996_808_470_921_215, "mont_reduce input (b)"); + let t = (a as i32).wrapping_mul(QINV); let res = (a - (t as i64).wrapping_mul(Q as i64)) >> 32; debug_assert!(res < (Q as i64), "mont_reduce output 1"); debug_assert!(-(Q as i64) < res, "mont_reduce output 2"); @@ -155,9 +163,8 @@ const fn gen_zeta_table_mont() -> [i32; 256] { let mut x = 1i64; let mut i = 0u32; while i < 256 { - result[(i as u8).reverse_bits() as usize] = - x.wrapping_mul(1 << 32).rem_euclid(Q as i64) as i32; - x = (x * ZETA as i64).rem_euclid(Q as i64); + result[(i as u8).reverse_bits() as usize] = ((x << 32) % (Q as i64)) as i32; + x = (x * ZETA as i64) % (Q as i64); i += 1; } result diff --git a/src/high_low.rs b/src/high_low.rs index ea8b94b..60944e7 100644 --- a/src/high_low.rs +++ b/src/high_low.rs @@ -7,7 +7,7 @@ use crate::{D, Q}; // Some arith routines leverage dilithium https://github.com/PQClean/PQClean/tree/master/crypto_sign -/// # Algorithm 29: `Power2Round(r)` on page 34. +/// # Algorithm 29: `Power2Round(r)` on page 33/34 applied over all coefficients of K polys. /// Decomposes `r` into `(r1, r0)` such that `r ≡ r1·2^d + r0 mod q`. /// /// **Input**: `r ∈ Zq`.
@@ -16,6 +16,10 @@ pub(crate) fn power2round(r: &[R; K]) -> ([R; K], [R; K]) { // 1: r+ ← r mod q // 2: r0 ← r+ mod±2^d // 3: return ((r+ − r0)/2^d, r0) + debug_assert!( + r.iter().flat_map(|row| row.0).all(|element| element < (i32::MAX - (1 << D))), + "power2round input" + ); let r_1: [R; K] = core::array::from_fn(|k| { R(core::array::from_fn(|n| (r[k].0[n] + (1 << (D - 1)) - 1) >> D)) }); @@ -40,8 +44,16 @@ pub(crate) fn power2round(r: &[R; K]) -> ([R; K], [R; K]) { /// # Algorithm 30: `Decompose(r)` on page 34. -/// Decomposes `r` into `(r1, r0)` such that `r ≡ r1·(2·γ_2) + r0 mod q`. -/// (this description is not sufficient; r0 is 'centered' wrt `2·γ_2`) +/// Decomposes `r` into `(r1, r0)` such that `r ≡ r1·(2·γ_2) + r0 mod q`. If using the +/// 'centered' `r0` yields `r1 = (q - 1)/(2*gamma2)` then the result should be adjusted +/// by setting `r1` to `0`, and subtracting `1` from `r0`. In practice, this happens +/// only with one input value of `rp` for each possible `gamma2`:
+/// ` gamma2 rp r0' r1' r0 r1`
+/// ` 95232 8285185 -95231 44 -95232 0`
+/// ` 261888 8118529 -261887 16 -261888 0`
+/// For each `gamma2` and the specific `r mod q` value, `r0'` is `rp mod± 2*gamma2`, and +/// `r1'` is `rp - 2*gamma2*r0`. Values `(r0,r1)` are the value that just be returned in +/// that case. /// /// **Input**: `r ∈ Zq`
/// **Output**: Integers `(r1, r0)`. @@ -127,9 +139,10 @@ pub(crate) fn make_hint(gamma2: i32, z: Zq, r: Zq) -> bool { /// # Algorithm 34: `UseHint(h,r)` on page 35. /// Returns the high bits of `r` adjusted according to hint `h`. -/// This function uses public data from the signature; thus does not need to be constant time +/// This non-CT function uses public data from the signature; thus +/// does not need to be constant time /// -/// **Input**: boolean `h`, `r` ∈ `Zq`
+/// **Input**: Boolean `h` (cast in Zq), `r` ∈ `Zq`
/// **Output**: `r1 ∈ Z` with `0 ≤ r1 ≤ (q − 1)/(2·γ_2)` pub(crate) fn use_hint(gamma2: i32, h: Zq, r: Zq) -> Zq { // @@ -146,13 +159,13 @@ pub(crate) fn use_hint(gamma2: i32, h: Zq, r: Zq) -> Zq { // 3: if h = 1 and r0 > 0 return (r1 + 1) mod m // 4: if h = 1 and r0 ≤ 0 return (r1 − 1) mod m if gamma2 & (1 << 17) == 0 { - // ml-dsa-44; explicit r1 + 1 mod m(43) + // ml-dsa-44; explicit r1 + 1 mod m(44) if r0 > 0 { if r1 == 43 { return 0; } return r1 + 1; - } // explicit r1 - 1 mod m(43) + } // explicit r1 - 1 mod m(44) if r1 == 0 { return 43; } diff --git a/src/lib.rs b/src/lib.rs index 69a7be7..6920f7d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -103,7 +103,7 @@ macro_rules! functionality { use zeroize::{Zeroize, ZeroizeOnDrop}; const LAMBDA_DIV4: usize = LAMBDA / 4; - const CTILDE_LEN: usize = 32 * K * bit_length((Q - 1) / (2 * GAMMA2) - 1); + const W1_LEN: usize = 32 * K * bit_length((Q - 1) / (2 * GAMMA2) - 1); const CTEST: bool = false; // when true, the logid goes into CT test mode // ----- 'EXTERNAL' DATA TYPES ----- @@ -228,10 +228,9 @@ macro_rules! functionality { &self, rng: &mut impl CryptoRngCore, message: &[u8], ) -> Result { let esk = ml_dsa::sign_start::(ETA, &self.0)?; - let sig = - ml_dsa::sign_finish::( - rng, BETA, GAMMA1, GAMMA2, OMEGA, TAU, &esk, message, - )?; + let sig = ml_dsa::sign_finish::( + rng, BETA, GAMMA1, GAMMA2, OMEGA, TAU, &esk, message, + )?; Ok(sig) } } @@ -243,10 +242,9 @@ macro_rules! functionality { fn try_sign_with_rng( &self, rng: &mut impl CryptoRngCore, message: &[u8], ) -> Result { - let sig = - ml_dsa::sign_finish::( - rng, BETA, GAMMA1, GAMMA2, OMEGA, TAU, &self, message, - )?; + let sig = ml_dsa::sign_finish::( + rng, BETA, GAMMA1, GAMMA2, OMEGA, TAU, &self, message, + )?; Ok(sig) } } @@ -255,13 +253,16 @@ macro_rules! functionality { impl Verifier for PublicKey { type Signature = [u8; SIG_LEN]; - fn try_verify( + fn verify( &self, message: &[u8], sig: &Self::Signature, - ) -> Result { - let epk = ml_dsa::verify_start(&self.0)?; - ml_dsa::verify_finish::( - BETA, GAMMA1, GAMMA2, OMEGA, TAU, &epk, &message, &sig, - ) + ) -> bool { + let epk = ml_dsa::verify_start(&self.0); + if epk.is_err() { return false }; + let res = ml_dsa::verify_finish::( + BETA, GAMMA1, GAMMA2, OMEGA, TAU, &epk.unwrap(), &message, &sig, + ); + if res.is_err() { return false }; + res.unwrap() } } @@ -269,12 +270,14 @@ macro_rules! functionality { impl Verifier for ExpandedPublicKey { type Signature = [u8; SIG_LEN]; - fn try_verify( + fn verify( &self, message: &[u8], sig: &Self::Signature, - ) -> Result { - ml_dsa::verify_finish::( + ) -> bool { + let res = ml_dsa::verify_finish::( BETA, GAMMA1, GAMMA2, OMEGA, TAU, &self, &message, &sig, - ) + ); + if res.is_err() { return false } + res.unwrap() } } @@ -318,7 +321,7 @@ macro_rules! functionality { ) -> Result<[u8; SIG_LEN], &'static str> { let (_pk, sk) = ml_dsa::key_gen::(rng, ETA)?; let esk = ml_dsa::sign_start::(ETA, &sk)?; - let sig = ml_dsa::sign_finish::( + let sig = ml_dsa::sign_finish::( rng, BETA, GAMMA1, GAMMA2, OMEGA, TAU, &esk, message, )?; Ok(sig) @@ -344,7 +347,7 @@ macro_rules! functionality { /// the remote party utilizes the [`traits::SerDes::try_from_bytes()`] functions to deserialize the /// byte-arrays into structs. /// -/// **3)** Finally, the remote party uses the [`traits::Verifier::try_verify()`] function implemented on the +/// **3)** Finally, the remote party uses the [`traits::Verifier::verify()`] function implemented on the /// [`ml_dsa_44::PublicKey`] struct to verify the message with the `Signature` byte array. /// /// See the top-level [crate] documentation for example code that implements the above flow. @@ -386,7 +389,7 @@ pub mod ml_dsa_44 { /// the remote party utilizes the [`traits::SerDes::try_from_bytes()`] functions to deserialize the /// byte-arrays into structs. /// -/// **3)** Finally, the remote party uses the [`traits::Verifier::try_verify()`] function implemented on the +/// **3)** Finally, the remote party uses the [`traits::Verifier::verify()`] function implemented on the /// [`ml_dsa_44::PublicKey`] struct to verify the message with the `Signature` byte array. /// /// See the top-level [crate] documentation for example code that implements the above flow. @@ -428,7 +431,7 @@ pub mod ml_dsa_65 { /// the remote party utilizes the [`traits::SerDes::try_from_bytes()`] functions to deserialize the /// byte-arrays into structs. /// -/// **3)** Finally, the remote party uses the [`traits::Verifier::try_verify()`] function implemented on the +/// **3)** Finally, the remote party uses the [`traits::Verifier::verify()`] function implemented on the /// [`ml_dsa_44::PublicKey`] struct to verify the message with the `Signature` byte array. /// /// See the top-level [crate] documentation for example code that implements the above flow. diff --git a/src/ml_dsa.rs b/src/ml_dsa.rs index e7c0ea9..e319ad7 100644 --- a/src/ml_dsa.rs +++ b/src/ml_dsa.rs @@ -120,12 +120,12 @@ pub(crate) fn sign_start( rand_gen: &mut impl CryptoRngCore, beta: i32, gamma1: i32, gamma2: i32, omega: i32, tau: i32, esk: &ExpandedPrivateKey, message: &[u8], @@ -171,18 +171,18 @@ pub(crate) fn sign_finish< h8.read(&mut rho_prime); // 9: κ ← 0 ▷ Initialize counter κ - let mut k_ctr = 0u16; + let mut kappa_ctr = 0u16; // 10: (z, h) ← ⊥ ▷ we will handle ⊥ inline with 'continue' let mut z: [R; L]; let mut h: [R; K]; - let mut c_tilde = [0u8; LAMBDA_DIV4]; + let mut c_tilde = [0u8; LAMBDA_DIV4]; // size could be fixed at 32; but spec will fix flaw // 11: while (z, h) = ⊥ do ▷ Rejection sampling loop (with continue for ⊥) loop { // // 12: y ← ExpandMask(ρ′, κ) - let y: [R; L] = expand_mask(gamma1, &rho_prime, k_ctr); + let y: [R; L] = expand_mask(gamma1, &rho_prime, kappa_ctr); // 13: w ← NTT−1(cap_a_hat ◦ NTT(y)) let w: [R; K] = { @@ -196,7 +196,7 @@ pub(crate) fn sign_finish< core::array::from_fn(|k| R(core::array::from_fn(|n| high_bits(gamma2, w[k].0[n])))); // 15: c_tilde ∈ {0,1}^{2·Lambda} ← H(µ || w1Encode(w_1), 2·Lambda) ▷ Commitment hash - let mut w1_tilde = [0u8; CTILDE_LEN]; + let mut w1_tilde = [0u8; W1_LEN]; w1_encode::(gamma2, &w_1, &mut w1_tilde); let mut h15 = h_xof(&[&mu, &w1_tilde]); h15.read(&mut c_tilde); @@ -248,7 +248,7 @@ pub(crate) fn sign_finish< let r0_norm = infinity_norm(&r0); // CTEST is used only for constant-time measurements via `dudect` if !CTEST && ((z_norm >= (gamma1 - beta)) || (r0_norm >= (gamma2 - beta))) { - k_ctr += u16::try_from(L).expect("cannot fail"); + kappa_ctr += u16::try_from(L).expect("cannot fail"); continue; // 24: else ... not needed with 'continue' } @@ -276,10 +276,11 @@ pub(crate) fn sign_finish< // 27: if ||⟨⟨c_t_0⟩⟩||∞ ≥ Gamma2 or the number of 1’s in h is greater than ω, then (z, h) ← ⊥ // CTEST is used only for constant-time measurements via `dudect` - if !CTEST && (infinity_norm(&c_t_0) >= gamma2) - || (h.iter().map(|h_i| h_i.0.iter().sum::()).sum::() > omega) + if !CTEST + && ((infinity_norm(&c_t_0) >= gamma2) + || (h.iter().map(|h_i| h_i.0.iter().sum::()).sum::() > omega)) { - k_ctr += u16::try_from(L).expect("cannot fail"); + kappa_ctr += u16::try_from(L).expect("cannot fail"); continue; // 28: end if } @@ -340,15 +341,15 @@ pub(crate) fn verify_start( } -/// Continuation of `verify_start()`. +/// Continuation of `verify_start()`. The `lib.rs` wrapper around this will convert `Error()` to false. #[allow(clippy::too_many_arguments, clippy::similar_names)] pub(crate) fn verify_finish< - const CTILDE_LEN: usize, const K: usize, const L: usize, const LAMBDA_DIV4: usize, const PK_LEN: usize, const SIG_LEN: usize, + const W1_LEN: usize, >( beta: i32, gamma1: i32, gamma2: i32, omega: i32, tau: i32, epk: &ExpandedPublicKey, m: &[u8], sig: &[u8; SIG_LEN], @@ -385,7 +386,7 @@ pub(crate) fn verify_finish< // 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"); - // c_tilde_2 is just discarded... + // 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::(tau, c_tilde_1); // false, as this instance isn't pertinent to CT @@ -410,15 +411,15 @@ pub(crate) fn verify_finish< }); // 12: c_tilde_′ ← H(µ || w1Encode(w′_1), 2λ) ▷ Hash it; this should match c_tilde - let mut tmp = [0u8; CTILDE_LEN]; + let mut tmp = [0u8; W1_LEN]; w1_encode::(gamma2, &wp_1, &mut tmp); let mut h12 = h_xof(&[&mu, &tmp]); - let mut c_tilde_p = [0u8; 64]; + let mut c_tilde_p = [0u8; LAMBDA_DIV4]; h12.read(&mut c_tilde_p); // leftover to be ignored // 13: return [[ ||z||∞ < γ1 −β]] and [[c_tilde = c_tilde_′]] and [[number of 1’s in h is ≤ ω]] let left = infinity_norm(&z) < (gamma1 - beta); - let center = c_tilde[0..LAMBDA_DIV4] == c_tilde_p[0..LAMBDA_DIV4]; // verify not CT + let center = c_tilde == c_tilde_p; // verify not CT let right = h.iter().all(|r| r.0.iter().filter(|&&e| e == 1).sum::() <= omega); Ok(left && center && right) } diff --git a/src/ntt.rs b/src/ntt.rs index ab3bfba..7b94b24 100644 --- a/src/ntt.rs +++ b/src/ntt.rs @@ -7,7 +7,7 @@ use crate::Q; /// # Algorithm 35 NTT(w) on page 36. /// Computes the Number-Theoretic Transform. An inner loop over `w/w_hat` has -/// been refactored into this function. +/// been refactored into this function, so it processes an array of elements. /// /// **Input**: polynomial `w(X) = ∑_{j=0}^{255} w_j X^j ∈ Rq`
/// **Output**: `w_hat = (w_hat[0], ... , w_hat[255]) ∈ Tq` @@ -78,7 +78,7 @@ pub(crate) fn ntt(w: &[R; KL]) -> [T; KL] { /// # Algorithm 36 NTT−1 (`w_hat`) on page 37. /// Computes the inverse of the Number-Theoretic Transform. An inner loop over `w/w_hat` has -/// been refactored into this function. +/// been refactored into this function, so it processes an array of elements. /// /// **Input**: `w_hat` = `(w_hat[0], . . . , w_hat[255]) ∈ Tq`
/// **Output**: polynomial `w(X) = ∑_{j=0}^{255} w_j X^j ∈ Rq` diff --git a/src/traits.rs b/src/traits.rs index ef13c9a..448a594 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -125,7 +125,7 @@ pub trait Signer { /// // Generate key pair and signature /// let (pk, sk) = ml_dsa_65::KG::try_keygen()?; // Generate both public and secret keys /// let sig = sk.try_sign(&message)?; // Use the secret key to generate a message signature - /// let v = pk.try_verify(&message, &sig)?; // Use the public to verify message signature + /// let v = pk.verify(&message, &sig); // Use the public to verify message signature /// # } /// # Ok(())} /// ``` @@ -158,7 +158,7 @@ pub trait Signer { /// // Generate key pair and signature /// let (pk, sk) = ml_dsa_65::KG::try_keygen_with_rng(&mut rng)?; // Generate both public and secret keys /// let sig = sk.try_sign_with_rng(&mut rng, &message)?; // Use the secret key to generate a message signature - /// let v = pk.try_verify(&message, &sig)?; // Use the public to verify message signature + /// let v = pk.verify(&message, &sig); // Use the public to verify message signature /// # } /// # Ok(())} /// ``` @@ -175,9 +175,7 @@ pub trait Verifier { type Signature; /// Verifies a digital signature with respect to a `PublicKey`. As this function operates on - /// purely public data, it need not proved constant-time assurances. - /// # Errors - /// Returns an error on a malformed signature; propagates internal errors. + /// purely public data, it need/does not provide constant-time assurances. /// # Examples /// ```rust /// # use std::error::Error; @@ -191,21 +189,21 @@ pub trait Verifier { /// // Generate key pair and signature /// let (pk, sk) = ml_dsa_65::KG::try_keygen()?; // Generate both public and secret keys /// let sig = sk.try_sign(&message)?; // Use the secret key to generate a message signature - /// let v = pk.try_verify(&message, &sig)?; // Use the public to verify message signature + /// let v = pk.verify(&message, &sig); // Use the public to verify message signature /// # } /// # Ok(())} /// ``` - fn try_verify(&self, message: &[u8], signature: &Self::Signature) - -> Result; + fn verify(&self, message: &[u8], signature: &Self::Signature) + -> bool; } /// The `SerDes` trait provides for validated serialization and deserialization of fixed- and correctly-size elements. /// Note that FIPS 204 currently states that outside of exact length checks "ML-DSA is not designed to require any -/// additional public-key validity checks". Nonetheless, a `Result()` is returned during all deserialization operations -/// to preserve the ability to add future checks (and for symmetry across structures). Note that for the current -/// implementation, both of the private and public key deserialization routines invoke an internal decode that catches -/// over-sized coefficients (for early detection). +/// additional public-key validity checks" (perhaps "...designed not to require..." would be better). Nonetheless, a +/// `Result()` is returned during all deserialization operations to preserve the ability to add future checks (and for +/// symmetry across structures). Note that for the current implementation, both of the private and public key +/// deserialization routines invoke an internal decode that catches over-sized coefficients (for early detection). pub trait SerDes { /// The fixed-size byte array to be serialized or deserialized type ByteArray; diff --git a/src/types.rs b/src/types.rs index 7ee7643..ec669ff 100644 --- a/src/types.rs +++ b/src/types.rs @@ -49,7 +49,7 @@ pub(crate) struct R(pub(crate) [i32; 256]); pub(crate) const R0: R = R([0i32; 256]); -/// Polynomial coefficients inT, with default T0 +/// Polynomial coefficients in T, with default T0 #[derive(Clone, Zeroize, ZeroizeOnDrop)] #[repr(align(8))] pub(crate) struct T(pub(crate) [i32; 256]); diff --git a/tests/integration.rs b/tests/integration.rs index 5651845..2ac5660 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -32,22 +32,22 @@ fn forever() { rng.fill_bytes(&mut msg); let (pk, sk) = ml_dsa_44::KG::try_keygen_with_rng(&mut rng).unwrap(); let sig = sk.try_sign(&msg).unwrap(); - let ver = pk.try_verify(&msg, &sig); - assert!(ver.unwrap()); + let ver = pk.verify(&msg, &sig); + assert!(ver); // now, confirm that we do not accept an invalid signature rng.fill_bytes(&mut flip); let index = u32::from_le_bytes(flip[0..4].try_into().unwrap()); // index of byte to flip let mut sig2 = core::array::from_fn(|i| sig[i]); sig2[index as usize % (sig2.len() - 2)] ^= if flip[4] != 0 { flip[4] } else { 0x55 }; // investigate sig[last] - let ver = pk.try_verify(&msg, &sig2); - if ver.is_ok() && ver.unwrap() { + let ver = pk.verify(&msg, &sig2); + if ver { eprintln!("Msg is {}\n", hex::encode(msg)); eprintln!("Index is {}\nflip[4] is{}\n", index, flip[4]); eprintln!("sk is {}\n", hex::encode(sk.into_bytes())); eprintln!("pk is {}\n", hex::encode(pk.into_bytes())); eprintln!("good sig is {}\n", hex::encode(sig)); eprintln!("bad sig is {}\n", hex::encode(sig2)); - assert!(!ver.unwrap()); // fail and stop if 'verified' + assert!(!ver); // fail and stop if 'verified' } if i % 10000 == 0 { println!("So far i: {}", i) @@ -71,8 +71,8 @@ fn bad_sig() { let pk = ml_dsa_44::PublicKey::try_from_bytes(<[u8; 1312]>::try_from(hex::decode("6ef73b20d211607012d7697a8277141e905239801f7bd539cbb8ea8e3d9146a5035af8ef22935f07f254a03558b8aa3b7baff5d1d0c63890beebfa28b13c67954696229413467a676b4611a7d9f0edefe78b7d70db79dbfb31df0dda9d81906af3295a285ed27288e00badf22724be26a3a6e3f00bb1f8671777b5eeca11fb6432969d38a8e8022548ee39b26bafc2071f26a1f0f602c8451b37e1c170494fba123b6f5a789e71f005fc0b1c4209b99cc9780b6b4ce4cfcf26ab3c8cb3991b0e516ae6ece41c1ad81432cb50112e59bc66e67d3f6e88dd80f91a319a5237064ec463ea5023780a480760a1c110816b1348e15ac2e8bc10ce0a2d8b47da53d8b3ab8ffa4fde0fb861d05a582492998f0d60d45a95885d1d4e5b3bfca1c7d9fba9b11f99d7f3087ae8891c6a1f5cfd3a02d4fe8e1c510ef023574c0dfab9f86a7e8491b405c659ba84eaf6b2b5d1eb4db575d0d25bd6e92f43a80dad5725b8847dfea0ee18e2222000d738210accfc75d3718f78eaeb9f2964688276c8269fd8803c5854eda05efed36cf4e57dbf4b54bd18db022e4494812644353818eb2f5509e14f8a3c682dea883deec5bf0ac75b7c893ab14c01c3336987b718b92dc6ec34a8278dc5a0981e8162fb822cfb86ce283cf22145f701623d1aaa36c653463b373a07fcecadfdf24287bc2213b43108550c505160b14f68ec36bd94bfcc372e82c3bb5044126b76541e0776e874de03c4eade5cc42bed862f7b30ced6da5cdb09d761a848bce989cd7219929fac382266d49268a542a6484b8d29b5423ff28d18d5d052a751cef7d4f550b68b01bdb510aea11cfd314489f522d8f6273f3612e9f49b459819c1ff19c9f38f17cc0a8934612a52df3ce09d807caa2414c449efe8c9a90697fcc48607d344bc1f4140c4a159a8e1ecb13b9396e74f379f7f4da69a2d20cde7c3f360f4c2afe140a4ffed050dd0ca6851b4c6e8c959e8ace271afd3ee880367a1c862a47f5cfc95f07e970766c00cc45b7d6060b48110fe8dec28793d1a3cb784d16ea82ab39ea01a7cc86028c98589586a11d3b0ab468cb160ddc7a70cd9b8214b9fe54c6d7d69dbc8e1efeae24a8985fae533737d2156aa5c3d78f50209d2039566d98aa1ee442a738a53fd004f4e464734793d4a24d6bb3a76f0dac5cff75417c5f89d9abaad89994e420add0d6c0f7ec7910b46bf758e71e1fbe598a1dfd746ba15f6ef8e5135b6f3cf03f9c36981ed6b6cbba00f881dc1f929101ca774af1c89d2fd96e713609740b74592758e70b23c8f7ee774564d9654974c783fadb0589858f1f70a9294e84fd19db1cd43d7034831c6eee7f6fdc2a0d4ff97ae2c6e97c44906f9ad18a393ecf346fdeb0c4aeb1e705d3b699bedb5ae435f28ae4ef1e35220bf90c58afeb1aa26fa215b35ad0ee32ff8422f8943feb2887a9f094acf13df4bb2666e7f5463f9ed7129279e31d73ae000208aded4fd233eb837ee0b70dae358965651df6ddb9181b1b820eb86e78cfca93d682d47e78eeb09782dae582b359a78e7746c3f81a15ab8a325b848c64336790baafe0fb6045312e1168b62b58dcc89f54972426d3ef542040de0dcfd67534356cef46ab464e580d89c73ceebf3bd79229d7a669eb96dc76808924693a948d60dc6c377aaaa7f5769aa051777082cd0821e95c5647e53eb889830bb6c15dc7374da7726e14d14e82985122295c6dfc1ba7266b0d108561f58eb183ef8f7c7c247c11a229ac8f7ed989c78855b8bd949de7f94762e1f692f373880115b8faa9558dca7abbd36ff1a64f35853a79f447d9381a7b93b4c43fb8b4152d884a7d319508bccc3a21b2d").unwrap()).unwrap()).unwrap(); let good_sig =<[u8; 2420]>::try_from(hex::decode("bcb502b1c16c3bf5c40450fd32ec0d15f5d31e454716984b76cef27ff5bd3e4d11f80cf857d677a984bcb70f29915840ecce898914a65d33a425417308afc2fb247428a5ed843d0603469973172102ecf997fdfeaeab39b41ee3be5afd4157e1dc34ce9b782aef95edacdfcecab116315a925bdb0c17fec880052804efcad89a0cea15e76e9ad8de73002bb6e6d6bb182dbb16c6b4714e07f5c130656cd253d5711c446871bf02a3a28f90a3b1f26e8d6116afce733986563fc6c0c1f4ff4c8a86f5f49d7a949b38ba2351fd38ac6a33996c6ea818ba0830dd004f90ceeeb1f4bcadd3a28d9baa8c3ea7c3b12c478553ff9d323bc2e480528941714ca59f08da5e870ea30bda4ffa9ac194bdba21cb9e08502d400b810d12534b8a4476ae3a48a0e8cde465f6bd0bbc25b9668e6753464aef17d5cc84f9107856f379ec42cf762224090d91f2d0b26721b56182a2150d24b766f5735d37cc594965c9a02462231891ea1f37849313dfc3af65d2b24b510caa689e912be1bc49011aea6276459d52a4b2a7ad00cf27f73e830ae02c051ab868533fb0f08b189123b60acd5bfd4719dc15a4d07a118b25f53cc20b10d26af2ade3e4593e47ce4729f20a1762f5baeebe8de435f237c248bd9530ade80627c6414bcd80edb35bb23efafd82c10ddc4733e76a1e5a5173ef4ee1988060994c50b3aceff0740a319d7b9e1c9241e6a49cf25109015de1cdaa7a5f89dd7be7174a153e906648ec3e567d104d352d34cebed293cef06ef551b46a1f1516ffa54827829ecfeba79853ea850ba67c74e694f1d2f7195a7568b61f576997cf3e409f51a67020f446981dc8bace88424739938a40c21165e0a2b94f51ffb39bb82bfb62533f62d0ceec50aef1ad079478c3c45ed6ee7d608e1d0f29b605da0c55cfea0ba40d4e8499872fa445da3cee7ae8b8ec62ec20ff158b70243a350d59de0c2e69254fe7a1ca825686c0c81a9c3ea1129d9f97395c9fd91ba5c996a510bf3a87dd2ed57cbc717aeb3e218dc1f28558709d9a88d4f6dea89ab69a61eb84515a9ef1577c2c57673bdd462d26ab30b306ac4101e3e05af0915b0cbbf4743ba0107149fc8576738e42005eb7978f2d753e2adb4839a8335fb48a8bdeef60bbb5cb44f54b8624d2365d5b92d159c7227116a87100bb1a2e3394124149a32c859c0a9f30371f80d048c02f154d14bf1b37b1db32c701c51397faa880483c90b01aa61d50b5f148c3326d569e2fcde732f8d6bd1f439add3fff31ab158aad4bd13bcecce77ff7bd46c277766a8404582b55cf9e67088ba7040e459844e821e60bc9db11c65083baf60d7bcea45e9e121b52f4f72ec142ec691ba61ade30255d1379c61182da55416e2d8e60f5cb1e9801bf7b1805a6542213da0311fa7aa078d94c86909f5358209ecb9d07129cedad0031012df076fcb03120a6276563711fea05964ddbbf09a1badb0a93b1ec60be62159b669c0623d9242c3d03bf29047de26e92d3ba300ca2fab5859e59e37542454141589ebf8e823fd8a08470951bc2db82de8b72fa839a483b7f7326cdea03246588ee10993a6962f8e07f4765b063d89d48d49ad5de307df0de6b5b8574d40a190adf709776aac04dd3c6f531f96d07c2186eb9d0e7418f6777a42323e144202392a664f938601bfa0aa028d7f5835e6811d7efea967bbcd9db824365eda0988ea1002d28939d0e3f66ebb41daa20ab33b728771ab422a8beb8571828947601fe833feea78c393b36c75880b1f0d4f9efeb268ed03cdb0e81e8139f578495e6ea6e55dbe0b8c661f1c013b14540bff775f6b095c03dffacb8534161aa8e10362d6eb3f5622ab5ef1203f03e9a141f5f54fe8e8dbdb403fcf399558baf8154a9d74e2f2033eed030b38980066b45f71854565f239df1bfbc773e991d033862b59f2ac5ccb932f0178a08669f56c07593bd11c4612a6ef4aeaa4f7532cbdb6604c659764d5a574602ba5c344c470eb29303b89955df5bcce7f7ab7b7ba8da332f30d5444111731e988d597bfb1928f4eda9ad1ca3c9c56d70bbaa4e47be0a386b4de060ba4538ec7db8a617a35252c6b121cf9e1910ea6385943263b1b18224746e421687648fb598f9a9e9aba59bea2a208d6b98c33d8268c5d2370d2eb354a66ba7ff6c35633fcd5614f71c68b56859e37983316fd99794684365d0ce167f891c1393c3f016c3293f16879d00c65ed86ad7684c825d87a853767149c8c7aead44f63e5a425134f6f6823a1f7825143d51b89bc8e077b174e5d1ae41815ad3f80c0dd27d4c6358ed4441eb89b8027b0b2cd3f700c1934b4410ef714ee8c54aaae4562d14f4bbe9f3b60bf07d547e25a8145b302e3e32731a68a106d4289cb1298fae0e6709ffb855d9673bd41e3459bc5c2df3b218d44cce81759a5a5cee8ba755a1dcdfd3e264c38bdf475ef6221b6a684987aace346e3b4d70ba4c5591682032fe0b20df05f54ea3c83289860cc73be8c8296fc1addd0195b9be04f3f531f035429eb38b8f58d9ad1f715a78702224e711d3b36d863721e5799d79e56785d0220e77cb3fa21062b68eeccd6abb9cea936fc517b153789d7a1fecbaaaf7ed116f032558aa813716574f550edfe3687424f67596a04916a591bfbb05b24d778de7d880ad711ca3162128f65d71ba8609bb931c19dfd38ea341fd25aca1f0169816258361e8b49b053fa203b8fb5c86c1d5bb0190d59040cf145b0ddcd27c5361a28850bd2c8181b4e72063832a38e200f98fdd54bb77ad685daffc31d27d8fc2fc374d437510f077cc278e7305b10f076e8a01dca35d3d1d54966a74c49a4c6ea168d6ebb8f07c3b8b6122edc4a1b642e6fb7b6e4b529ec743d63da129a889347e7c4b1109c4419bf13fb1cab50b6229015466175cb57b577b399bca25a0b784f99f90317082949b01e518a6d4348dc00ed76d57253ee6959b01c6fd89d1f006d9cb1e08b70fcb5ddc4b5b531989e3a2d1544b6041eaba1375fbcf8b756020b10ebeeb24e349f75ccfdd2c2eec5183e12a9a4daa677ca7b138185e3fa5a54f30df049ca4065ca928d07bc337f6383852291e12273800a3e9e492b5219cc25e021d91b5be483c8c1066c254ca3a28aef1de15dac4adcf425bebae41ea47788e8aa9d461b35a5d157073b68a155a609077ed9cabf1a1683a26f789afa7c104f321676225c438901aa0dd7719d43d89faf4b92785d0d63ebb4a1f91c66868dc5ccea9c9fffa80c5c111d77000f33865b17f12ce08c64f35708756f28b1025ca0f0ea2b29228393f4144546264717e8589c2c3d7ddedf4040b0d6a829ea5d2ddfd16376f70a0b0b1b3b8bccccdcee1fb0031438ca3a9cb000000000000000000000000000000000000000000000000000000000000121c2b32").unwrap()).unwrap(); let bad_sig = <[u8; 2420]>::try_from(hex::decode("bcb502b1c16c3bf5c40450fd32ec0d15f5d31e454716984b76cef27ff5bd3e4d11f80cf857d677a984bcb70f29915840ecce898914a65d33a425417308afc2fb247428a5ed843d0603469973172102ecf997fdfeaeab39b41ee3be5afd4157e1dc34ce9b782aef95edacdfcecab116315a925bdb0c17fec880052804efcad89a0cea15e76e9ad8de73002bb6e6d6bb182dbb16c6b4714e07f5c130656cd253d5711c446871bf02a3a28f90a3b1f26e8d6116afce733986563fc6c0c1f4ff4c8a86f5f49d7a949b38ba2351fd38ac6a33996c6ea818ba0830dd004f90ceeeb1f4bcadd3a28d9baa8c3ea7c3b12c478553ff9d323bc2e480528941714ca59f08da5e870ea30bda4ffa9ac194bdba21cb9e08502d400b810d12534b8a4476ae3a48a0e8cde465f6bd0bbc25b9668e6753464aef17d5cc84f9107856f379ec42cf762224090d91f2d0b26721b56182a2150d24b766f5735d37cc594965c9a02462231891ea1f37849313dfc3af65d2b24b510caa689e912be1bc49011aea6276459d52a4b2a7ad00cf27f73e830ae02c051ab868533fb0f08b189123b60acd5bfd4719dc15a4d07a118b25f53cc20b10d26af2ade3e4593e47ce4729f20a1762f5baeebe8de435f237c248bd9530ade80627c6414bcd80edb35bb23efafd82c10ddc4733e76a1e5a5173ef4ee1988060994c50b3aceff0740a319d7b9e1c9241e6a49cf25109015de1cdaa7a5f89dd7be7174a153e906648ec3e567d104d352d34cebed293cef06ef551b46a1f1516ffa54827829ecfeba79853ea850ba67c74e694f1d2f7195a7568b61f576997cf3e409f51a67020f446981dc8bace88424739938a40c21165e0a2b94f51ffb39bb82bfb62533f62d0ceec50aef1ad079478c3c45ed6ee7d608e1d0f29b605da0c55cfea0ba40d4e8499872fa445da3cee7ae8b8ec62ec20ff158b70243a350d59de0c2e69254fe7a1ca825686c0c81a9c3ea1129d9f97395c9fd91ba5c996a510bf3a87dd2ed57cbc717aeb3e218dc1f28558709d9a88d4f6dea89ab69a61eb84515a9ef1577c2c57673bdd462d26ab30b306ac4101e3e05af0915b0cbbf4743ba0107149fc8576738e42005eb7978f2d753e2adb4839a8335fb48a8bdeef60bbb5cb44f54b8624d2365d5b92d159c7227116a87100bb1a2e3394124149a32c859c0a9f30371f80d048c02f154d14bf1b37b1db32c701c51397faa880483c90b01aa61d50b5f148c3326d569e2fcde732f8d6bd1f439add3fff31ab158aad4bd13bcecce77ff7bd46c277766a8404582b55cf9e67088ba7040e459844e821e60bc9db11c65083baf60d7bcea45e9e121b52f4f72ec142ec691ba61ade30255d1379c61182da55416e2d8e60f5cb1e9801bf7b1805a6542213da0311fa7aa078d94c86909f5358209ecb9d07129cedad0031012df076fcb03120a6276563711fea05964ddbbf09a1badb0a93b1ec60be62159b669c0623d9242c3d03bf29047de26e92d3ba300ca2fab5859e59e37542454141589ebf8e823fd8a08470951bc2db82de8b72fa839a483b7f7326cdea03246588ee10993a6962f8e07f4765b063d89d48d49ad5de307df0de6b5b8574d40a190adf709776aac04dd3c6f531f96d07c2186eb9d0e7418f6777a42323e144202392a664f938601bfa0aa028d7f5835e6811d7efea967bbcd9db824365eda0988ea1002d28939d0e3f66ebb41daa20ab33b728771ab422a8beb8571828947601fe833feea78c393b36c75880b1f0d4f9efeb268ed03cdb0e81e8139f578495e6ea6e55dbe0b8c661f1c013b14540bff775f6b095c03dffacb8534161aa8e10362d6eb3f5622ab5ef1203f03e9a141f5f54fe8e8dbdb403fcf399558baf8154a9d74e2f2033eed030b38980066b45f71854565f239df1bfbc773e991d033862b59f2ac5ccb932f0178a08669f56c07593bd11c4612a6ef4aeaa4f7532cbdb6604c659764d5a574602ba5c344c470eb29303b89955df5bcce7f7ab7b7ba8da332f30d5444111731e988d597bfb1928f4eda9ad1ca3c9c56d70bbaa4e47be0a386b4de060ba4538ec7db8a617a35252c6b121cf9e1910ea6385943263b1b18224746e421687648fb598f9a9e9aba59bea2a208d6b98c33d8268c5d2370d2eb354a66ba7ff6c35633fcd5614f71c68b56859e37983316fd99794684365d0ce167f891c1393c3f016c3293f16879d00c65ed86ad7684c825d87a853767149c8c7aead44f63e5a425134f6f6823a1f7825143d51b89bc8e077b174e5d1ae41815ad3f80c0dd27d4c6358ed4441eb89b8027b0b2cd3f700c1934b4410ef714ee8c54aaae4562d14f4bbe9f3b60bf07d547e25a8145b302e3e32731a68a106d4289cb1298fae0e6709ffb855d9673bd41e3459bc5c2df3b218d44cce81759a5a5cee8ba755a1dcdfd3e264c38bdf475ef6221b6a684987aace346e3b4d70ba4c5591682032fe0b20df05f54ea3c83289860cc73be8c8296fc1addd0195b9be04f3f531f035429eb38b8f58d9ad1f715a78702224e711d3b36d863721e5799d79e56785d0220e77cb3fa21062b68eeccd6abb9cea936fc517b153789d7a1fecbaaaf7ed116f032558aa813716574f550edfe3687424f67596a04916a591bfbb05b24d778de7d880ad711ca3162128f65d71ba8609bb931c19dfd38ea341fd25aca1f0169816258361e8b49b053fa203b8fb5c86c1d5bb0190d59040cf145b0ddcd27c5361a28850bd2c8181b4e72063832a38e200f98fdd54bb77ad685daffc31d27d8fc2fc374d437510f077cc278e7305b10f076e8a01dca35d3d1d54966a74c49a4c6ea168d6ebb8f07c3b8b6122edc4a1b642e6fb7b6e4b529ec743d63da129a889347e7c4b1109c4419bf13fb1cab50b6229015466175cb57b577b399bca25a0b784f99f90317082949b01e518a6d4348dc00ed76d57253ee6959b01c6fd89d1f006d9cb1e08b70fcb5ddc4b5b531989e3a2d1544b6041eaba1375fbcf8b756020b10ebeeb24e349f75ccfdd2c2eec5183e12a9a4daa677ca7b138185e3fa5a54f30df049ca4065ca928d07bc337f6383852291e12273800a3e9e492b5219cc25e021d91b5be483c8c1066c254ca3a28aef1de15dac4adcf425bebae41ea47788e8aa9d461b35a5d157073b68a155a609077ed9cabf1a1683a26f789afa7c104f321676225c438901aa0dd7719d43d89faf4b92785d0d63ebb4a1f91c66868dc5ccea9c9fffa80c5c111d77000f33865b17f12ce08c64f35708756f28b1025ca0f0ea2b29228393f4144546264717e8589c2c3d7ddedf4040b0d6a829ea5d2ddfd16376f70a0b0b1b3b8bccccdcee1fb0031438ca3a9cb000000000000000000000000000000000000000000000000000000000000121c2b48").unwrap()).unwrap(); - assert!(pk.try_verify(&msg, &good_sig).unwrap()); - assert!(pk.try_verify(&msg, &bad_sig).unwrap()); + assert!(pk.verify(&msg, &good_sig)); + assert!(pk.verify(&msg, &bad_sig)); } #[cfg(all(feature = "ml-dsa-44", feature = "default-rng"))] @@ -84,8 +84,8 @@ fn test_44_rounds() { msg[0] = i as u8; let (pk, sk) = ml_dsa_44::KG::try_keygen_with_rng(&mut rng).unwrap(); let sig = sk.try_sign(&msg).unwrap(); - let ver = pk.try_verify(&msg, &sig); - assert!(ver.unwrap()) + let ver = pk.verify(&msg, &sig); + assert!(ver) } } @@ -98,8 +98,8 @@ fn test_65_rounds() { msg[0] = i as u8; let (pk, sk) = ml_dsa_65::KG::try_keygen_with_rng(&mut rng).unwrap(); let sig = sk.try_sign_with_rng(&mut rng, &msg).unwrap(); - let ver = pk.try_verify(&msg, &sig); - assert!(ver.unwrap()) + let ver = pk.verify(&msg, &sig); + assert!(ver) } } @@ -112,8 +112,8 @@ fn test_87_rounds() { msg[0] = i as u8; let (pk, sk) = ml_dsa_87::KG::try_keygen_with_rng(&mut rng).unwrap(); let sig = sk.try_sign_with_rng(&mut rng, &msg).unwrap(); - let ver = pk.try_verify(&msg, &sig); - assert!(ver.unwrap()) + let ver = pk.verify(&msg, &sig); + assert!(ver) } } @@ -129,7 +129,7 @@ fn test_44_no_verif() { for i in 0..8 { let mut msg_bad = msg; msg_bad[i] ^= 0x08; - let ver = pk.try_verify(&msg_bad, &sig).unwrap(); + let ver = pk.verify(&msg_bad, &sig); assert!(!ver) } @@ -139,7 +139,7 @@ fn test_44_no_verif() { sk_bad[70 + i * 10] ^= 0x08; let sk_bad = ml_dsa_44::PrivateKey::try_from_bytes(sk_bad).unwrap(); let sig = sk_bad.try_sign_with_rng(&mut rng, &msg).unwrap(); - let ver = pk.try_verify(&msg, &sig).unwrap(); + let ver = pk.verify(&msg, &sig); assert!(!ver) } @@ -148,7 +148,7 @@ fn test_44_no_verif() { let mut pk_bad = pk.clone().into_bytes(); pk_bad[i * 10] ^= 0x08; let pk_bad = ml_dsa_44::PublicKey::try_from_bytes(pk_bad).unwrap(); - let ver = pk_bad.try_verify(&msg, &sig).unwrap(); + let ver = pk_bad.verify(&msg, &sig); assert!(!ver) } @@ -156,7 +156,7 @@ fn test_44_no_verif() { for i in 0..8 { let mut sig_bad = sig; sig_bad[i * 10] ^= 0x08; - let ver = pk.try_verify(&msg, &sig_bad).unwrap(); + let ver = pk.verify(&msg, &sig_bad); assert!(!ver) } } diff --git a/tests/messages.rs b/tests/messages.rs index 8c69460..fbb9273 100644 --- a/tests/messages.rs +++ b/tests/messages.rs @@ -12,7 +12,7 @@ fn test_browser_message() { let mut rng = rand_chacha::ChaCha8Rng::seed_from_u64(123); let (pk, sk) = ml_dsa_44::KG::try_keygen_with_rng(&mut rng).unwrap(); let sig = sk.try_sign_with_rng(&mut rng, msg).unwrap(); - assert!(pk.try_verify(msg, &sig).unwrap()); + assert!(pk.verify(msg, &sig)); assert_eq!(sk.into_bytes(), *hex::decode("755193ec3402eb0e17947e6ed5605118967eb1200a28f9d8173097059141c572f89a1e160ddeea3733d4269eef95714edf361f4ea94a6a6a15988abaf3a0b96d866a1b34339105fa5eaebcd8443a8235c7dcf137148e31245644dff462d2c5f971ad7807207bed65b31ef7c4e8b89813360eeb5775a02c8bb34fb7599677b314cb388c61a02d11430cd0128910186411250621a97009446a0c8980008109c88609a44641d2420d14442c52288981088499088198106419b34824b4086300308ab40d02052922c62149c22c414262104160a2c42d80b08448122001230052086a9b9600c4b065e2a4298b02910239504c8680a1280913874d19b571dc0269808069a38265d34400dc02241c2621d1302a1a41880329121b454d20918c59b00501a181e4262544382811288a824641532264e24642c880854b2888a4364d94b6041a346c192706a008280b492dc314200a212cc32246a1c489e08865cbc0518442222280904a146a229245118049da388462826464348141c02420a1305294041a96815832600a8891012081db861190b2001027321c11440c2861a1440614a09101828d22a32850102212c1042103241940290a9430da964442463193a640439461e2164013a40c04862900c48140c6814ac42c102226d9c809e40261c2940091825093444548a62910a12904106908226e0436101c22925a18264224040298019230615908310c042018442218c78093a491923625ca0000d4246208b60c4b8820e14632643446504009e034811b498cd1228a02044ec93645da9241814286a44045448625c00209cab82c14b66c1ba3405cc0045b129050c08d63362d521820c34661193672d2204953882889b644c8008a8b886cd3162a59020819992411a14ccb4881a0146da13651d90652831608243766db38650bc20d24340c582841da32299b1285d242068c1265839481000909a2186990b48c600030a13289db866463c62c592425d2c020e4284e14b905483444142541da000103163191220542a26400a64dc3242d084869222380622245a12264d9002d1c183102040022326444446c804628842249c318280496298946498b96640c01845ca6909306308bc27063368e440801424690a2480ae4304a9c14290c040893466a14a82d18422c03a6011498200a202292a40940b88464c624080429a218000a884c92c80da320901b832d62280698168d42c669d2288522132481026e080822049891a2a26588a884d429c236d53e588ce26befb446068b8d645b6340a67629df72eb1d57749921ae68121b27d1be0b621700a6d74905bd4fea70abe4d8feb328604cedc398a05c092691fb21a660419076e4a7af47c12d5fa2c22de7bb639d838520360c2e498570b176c5701c0f83128b07c55ff86c870e12e59799d79f5c840bf76a289b20e23b55009b6f8b305c6308215651ed34d6ba2a3e68ea22b15bfbf7e2601ebbd67e62be92a68bd58f5d6073990c56f329334e7c2cd05f7351ba00f5dec74a860affabc2d659e66f690e301fe31bfedd80c9f3d28b80de370fb31fd083fe9672b6abd7d3c380617af9a03dfed16bec77306b3f70dd23852cc53c33745c0f721b0378651a721a0ee420498da53d07f24587547d2ef2fe6d27b73544d1b34c1065dc1ca93fad3b0fd5bd3f46fd04a292a04bf2a96b8310e72f6df8020daad70ddd7dbcdb3d97c3b29a90b9e2d4f57f9498da26a59b12311da8c3479da84de1608a98f663b82d19347fef84262b6ee2092a8d2671486b044ef53ba95b487ec708399c3bf6947abbf0631b056bef4d8eb4f6caac11c435eba6e3fd09088e59cd4bf03bc04dcd8db06568b81e5da620e20f10d502aaf3a53d23911abd8f92ed2a0d08415047bb07c0133963cc130e13e59704f82dbe75517bcbdbb8493352935473616a86aadcd47cc897af3fc62dccc135fd97bb1d8073fe2a7014eb13bd7a1a8e88de401b3ca33d20991ee6c0ab67b11204f763733dec1ac61ae888b73cdb820e0b4c8023bace285ea217a317d006ab0166e53a1d3006dc358f91d2e33b327f039b60292a5cb9062a0ef5b58096d11cdef6acd2daa9348fa72c92e79929acd39eda35b5c061854d6c4de697b6712aa08ef40576e2e6c211e0bf5bed8e38595c39925f5b3a63586b52976b3c9019c0c172bcc52739ad8c174220ef2000d4b67bfc84e5d7674bd10f8562e62bba0ed3087a44f91148e9a05542cc9eaea64d4c435a1213c2179138943f6aa7c946948f91d8a8d8b6110184da06608a3b97146af85be96976dd9896bbeaa3b6d6a597397839ca882e27039a2d38109fe3a212540f4763e7dd3a5f49c08ec7eff9d96f4b238718f2826c690a0094da2bf6b0a728843c40b72204d03e7885d1c0818521087ad2dad0fd681fe00bc59984f8ca2016556067fb5adb64369d371a4cfc59ae2c3d63973b3cb105f03574016c2ab0be88b474f5d84bfc9579e9c33aa1d5c423a15b3e20170441752a5a7c8426868fb71439763da42746d20c1f42fd94bc9f475200fab8844f8cd080728c0935b0b3eeceb2fbf33127de2d17ac01c9d73ce8b9b7a81bed673cdb9665812e1c3bfbd5d8b6d16b6ab41004e507b1d85680dd92bc2dcba32e2e1101e94f7b73a877618bf90796320ab1f56ff01c0abfa6b50718e6f1c2714969c440ba932a43a39215836afb9940034e8efed8b1e4a7eb852dd5afe147e4e31ade7731a36c82ce583560102b0255394f4bc34cb3b34f32094138989ff992ee2c933077cfa226d96710954348415935af6eee21947e962ed4fe68dc555ab02e9e60a623589ca3502261f5ea54e89ebee32697cc48edd1869a40f6b7cb532483a53e82ad4f6511306ef0067e8084e6f0accd4528e8927a3f1c6ef3623f7e4fe3d90f3a3e1e6b770255975ebf7b3f7a78708190b87eec9e447df0c48b694fca20905005072277610888c7a7e907be47b788d9aba9f8a3a73261da4a14ce3dd11ae8109347927ac74a2fc775e8587bc7223a473ef4d732b690080f0903462f016df4c801a60246920201e12e7bd80864b88b126f2ad98b5b42c39bb549394132ac096a74eb39deaecf922ccb44d21213f7c81b71494740475535abc0c401e36038863724bd0a5e4cb227ff831c3c3c85f3dd6787e899873964a4ac583e903f32e79093552b5871f7b4a806151c603c8a3d1f63eac47f08eee013078af7733692a1688137cb4a047eb3d8657469194f8dd7c57dd75a1263931fd420fddafa31ed4ae51afb507cc9fe7f8a0bf0e7ebecfc14c2e9537a73a8ae7956fb5f6cb8fccb8e94c9d60c9e7129cecf94b0bd47b0794e3069d7dc59958179bdcef2168639f4f4867a674c00bdb5379f2c3e3d9ba115bce0fece5e51d3f9142254a6cf8de6323b4fe22b779ae7aac6dc6ac259bee4f8d0c708131c22a6f1530708bcbaa0305639425d92965ceb45d911ad13b98ea4c20868004aaebe3ef1d2dd737d114d075990704ebb7c480cc59eec5b08eb9d6b7a8b850355e2650fe5fcb40a6e8145c9470ffcbdfe26d2ceccad99fcc06cbd5be7c06d4e90992aa1bb2829701272defd878f3e59a0377de7a021e73320e5ba46f426857888").unwrap(), "sk not correct"); assert_eq!(sig, *hex::decode("204e71dc783f063158152fdce71919c5d4e6c832f174bb4185a179dd228987f7a8cc5234eb918ae5cc6667b080bad306fa60de7729e002e2d84596456985a6fd2e2251b7b4d1ce3f5d52e319312c1bb1433f4d1009a15724a75f8a3aaeea98c7c5a64c017b931dd6927d682f0608ab72866a6f55dba1a2a44ba3cf76893cab7a989325f1717577cc2afe4becc5d5489b5cc83132956ee5f44069068fb27ee0eb404ab2fd97d87c2ea4133a8cb7b51d4870ad71f36c2086cbf9dae9943f674b02269b6542a91b455977d687c932473676dfebff831a3cffa9bea3742d0237ab25f14917541d136183717f2999860f48bf135f5fd0a0e3096e9a4e7fdf041673f476c4a97746a79aea11182033da6d46583446a56eec999dd894939380c164f64e9d2642a21a93c61b96e2e1a64a98f24fb4c1adf3a228e8c1cc8890fe62ce9bf2b9d9eb0c0a8b9257504626b0218e67af1902e25564617f936b9351a0de225964103b15b4acb52351ceed45f74520f2d9b4cd76fecfac78a0dcc1dfb4cada17e257a2559ccf4f68ff9ae15acc5033209a179e01d94fc3b0a4d52e5f3fac6ae487f10cc0ad07ecec8def6c7f356ea4ef08f479ed83db9b5c346af252a2624c6e51170620180f1296881a9ce5970acb4ce9dd8279f17d5dc5b65f367601ff941c0307bbf75761af428f2af32159e5516b397cfef7ae924faa07a63461a60f32c9027217f06e5bba3bf8f82621b5ee5393a405609dcfc3580602391eee890bb27112da6981acb1a7bfdba5b1a46cdb315b5d7f6f3bf20346d3016e9d964ac81e3481f5f7848e2df803075f9bca43cfa61758480a45f90a9dd281c9ec3d707762d83da6dcb321d57cbbdde3e1bdde8834ce3613ad999d3e007b7358e45611ff3995edf5e90125521763f9ec10930cd7c11a0154c8d58a68fb891f210e0aac24f9e72061a29ed74c5f70e71ac6f9427f68959f062bb90af17d6ce1521d52674d2762fff239365094f84f95929ca690c7f4d8f6bf1d76ba7372ee9f98315fc26593ea4367434440004c117c85ac833d9be51d5410f1c3a644adb104268b94e2418735fb645d73351ef5f480dd1f4ff1623f479d6fae6cdacf5075f971e81bc218850da40695f47d3c392fd23657f70a5cc9fa394ab7b5c2622ea4949210435ecc29878ca4a582af0a977a5947003f03bcbdcebe25ed395468480da9c155b02e4a42094fbda5068ad21890c03bac7a1e55e67a9c79a0f5458075f6b602b51b1ab223a5206193770d0170a3d39870abdb5f5fc948071ecc81b6201eb76e1b68f0f2838b0e2a9413273a19ba44f097c13a452b6b5dcbae603cdec3e4a8657924d813e190b0c7f18e77f2ff1ff51bc263bea49dc190a667d581264614594a57910376215f9bee23261e08b82242d20a087c1cc6d11ad27d4d613bc5f61177924946a9b7d45020e643f5a66d032b42e7fff9a462a778b447993158c9561c830db561a4e92453523e605ca229fc4458356effa93dc64e8f4d659392bc9b3e291342eba8cff9b97344e11f6ad14f69335fc48eda49f1ebcb4fbe2c40657eb8628a1ed21924e676f2019e868bdcbc24b318e74aa4533ab28b5c56386e905fd92e4fc80d10e98b586cce1c4c9c6bf8fcc02c4b99e9e43153a2652cbaae0f6e53c15eff93b0099548a2c3a8a9e1e4186874a083ec981f94ace4da1ea3ad0500173de9d9e15fed94975152cf5fd07e46c41e7207799e13b7b1a01e7895dfde5ba81e45b5f0529154b152d2a98c149f2ada55cd6f05823ae0ecac0e174dab9ba7979e793e65a024994007a50a3739ad4a9511f5af1d46fe3701014d46e7e9da21c27aabcbc13438bbf5fbc2f60073bbf71bb7ccefed210eb621557345cc53c1a631117da74b3df288d30004ba10c1d0bf7ce30acd706c6acc65b5bb6e7a550f93c3529453fa4811d18a4117700267c501c202f3de0e55a72afb5f592efe4cbffb0e3c686086ca253fe38a4349b39b94f40b60b64c7b34b1657550f2ccecad18d0ade2212b46edeff7c63dc30bd3d6d248e083f10f3e156e7bdf4b6ccee2a577ed98317c34f8b987b6d1f8458496fea46c7df8cf29bc32bbf76294698b115c999fe9f50d18c2ca451fc356392cb6553af5f78d4b816aeabdc392b33638fc2f12ada1bdd403978a65c370bd98225af3febd978393d92c73a419ee78542adfb6a452e3eea0d0d5a5ad332a14c7c98219c5b8566e0ce1a7bb473a105e7dba3d2829f8bd6e2eda5a98f1b609698ea1dd0f2eed00cdc5cbc2912fe7b98f7fde7f9bf795b0c2a5e49cee77c9d2caa4e408ee964137b0ec192dfbf04c32fa7ac0b9ad682dcdda249a17c2c2b1f664cf3d877aa9619a44b6dfbd5a75738fbb40ecd0d753f8d4cd753cf2f6bb2cee52c6875bef04a887ddf8dab3173c208f1f7a370cf2514fd807e316c86f4f54e98b42313e5e8a508f1957d5c6a54c4e4f7e53da8911163c87c2d3fdf66b2642428db4cc13de10aedbd431e9306f12fdf95e88d163fdd93f30330611e3fa277a606013c3c9cb16cedddb3183757f450d530f263d22a765c3afb027664353d5879605cfc5e673a728b975161835bbc85e274edcf6b071bd5fde7d6fa80ffea0bf124bf72ae642adab1b0ec6c6247f462d32622ea63df8322fe5e966e7701c6783df9642d98ca1026852f91fb04fea69ae521bfde282cf39ae3c02e368f5e3aab180ce736fb22330e26fd91c767e0789ba9b356d59bb55112f9c438dc049adf69278edfa504da7cb5c291efc76f1d22c81f50a1edc1fa9e841b55599e0ea09dd0b63da47073e7fa7afa0e3a5f5f4ce778aab7cbd0ce54b5c105ffc405ca3482274fd64a14caf1db94bce36d95714eb416f6ce8a2a02067bb0ebe925587592c1ead49c22a16cef7386f1a94335d7c5d9383293b7d47fc9108d971856c35d834f369bedc555ffb5573f286ca30325e319c9a6b59207b51f1ea2f57197a01b9d1f7e9b8f70690eaeb07b104f94dd9870e493bd6909e5efd0894788e8b9f5c6d615b96f46a06457cb88a4befffd15c882953a6b265618443a8fec0fe2977cc871dc400a2d352afb7d82f2fd5d87bb19dbea8eae0a137885ad98d10e0412e698524e646061706b697b464977fb016331429e8e788e59c63ffb985d87cce0cbe9db668bb866e729f7f85de173d3c34d1eed790ad8ea0f54a86a222ed012a36320fa3a79c4f4c9c996996f28e422f4ff5e2fd6d1c5c94933f1965a4a71dda50dc75f1b5343d1ba5d7b0d66fc31d0fc050f65689636b335634315ea2bf23d870b1b386aa57c80c0d3f60657bccd1d621283f5a6590aab7c9d8ecf72a31414b5764798698b7c1d1dce3060b1130343e5376788ca1d8f6fb0000000000000000000000000000000000000000000000000000000000000009152331").unwrap(), "sig not correct"); diff --git a/tests/test_vectors.rs b/tests/test_vectors.rs index 7cafb28..1491bc2 100644 --- a/tests/test_vectors.rs +++ b/tests/test_vectors.rs @@ -172,8 +172,8 @@ fn test_verify() { let (pk, msg, sig) = get_verify_vec("./tests/test_vectors/Signature Verification -- ML-DSA-44.txt"); let pk = ml_dsa_44::PublicKey::try_from_bytes(pk.try_into().unwrap()).unwrap(); - let pass = pk.try_verify(&msg, &sig.try_into().unwrap()); - assert!(pass.unwrap()); + let pass = pk.verify(&msg, &sig.try_into().unwrap()); + assert!(pass); } #[cfg(feature = "ml-dsa-65")] @@ -181,8 +181,8 @@ fn test_verify() { let (pk, message, sig) = get_verify_vec("./tests/test_vectors/Signature Verification -- ML-DSA-65.txt"); let pk = ml_dsa_65::PublicKey::try_from_bytes(pk.try_into().unwrap()).unwrap(); - let pass = pk.try_verify(&message, &sig.try_into().unwrap()); - assert!(pass.unwrap()); + let pass = pk.verify(&message, &sig.try_into().unwrap()); + assert!(pass); } #[cfg(feature = "ml-dsa-87")] @@ -190,7 +190,7 @@ fn test_verify() { let (pk, message, sig) = get_verify_vec("./tests/test_vectors/Signature Verification -- ML-DSA-87.txt"); let pk = ml_dsa_87::PublicKey::try_from_bytes(pk.try_into().unwrap()).unwrap(); - let pass = pk.try_verify(&message, &sig.try_into().unwrap()); - assert!(pass.unwrap()); + let pass = pk.verify(&message, &sig.try_into().unwrap()); + assert!(pass); } } diff --git a/wasm/Cargo.toml b/wasm/Cargo.toml index 78fd0ef..f20fc1a 100644 --- a/wasm/Cargo.toml +++ b/wasm/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "fips204-wasm" -version = "0.1.2" +version = "0.2.1" authors = ["Eric Schorn "] description = "Sample web page utilizing FIPS 204 code" repository = "" diff --git a/wasm/src/lib.rs b/wasm/src/lib.rs index 9c4ce0c..d03f560 100644 --- a/wasm/src/lib.rs +++ b/wasm/src/lib.rs @@ -10,7 +10,7 @@ pub fn sign(message: &str) -> String { let mut rng = rand_chacha::ChaCha8Rng::seed_from_u64(seed); let (pk, sk) = ml_dsa_44::try_keygen_with_rng(&mut rng).expect("keygen failed"); let sig = sk.try_sign_with_rng(&mut rng, message.as_ref()).expect("sign failed"); - assert!(pk.try_verify(message.as_ref(), &sig).expect("verify error"), "verify failed"); + assert!(pk.verify(message.as_ref(), &sig), "verify failed"); let sk_hex = hex::encode(&sk.into_bytes()); let sig_hex = hex::encode(&sig);