From e77284a10d12b5fac4f59e789adf5da4763c2226 Mon Sep 17 00:00:00 2001 From: eschorn1 Date: Fri, 4 Oct 2024 11:02:10 -0500 Subject: [PATCH] v0.4.0 RC1 --- dudect/Cargo.toml | 4 ++-- dudect/README.md | 15 ++++++++------- dudect/src/main.rs | 4 ++-- src/hypertree.rs | 2 +- src/lib.rs | 12 +++++++++--- src/slh.rs | 9 ++++----- src/traits.rs | 28 +++++++++++++++++++++++++++- 7 files changed, 53 insertions(+), 21 deletions(-) diff --git a/dudect/Cargo.toml b/dudect/Cargo.toml index 186fdec..fa54f80 100644 --- a/dudect/Cargo.toml +++ b/dudect/Cargo.toml @@ -12,8 +12,8 @@ dudect-bencher = "0.6" rand_core = { version = "0.6.4", default-features = false } -[profile.bench] -debug = true +[profile.release] +debug = false debug-assertions = false incremental = false lto = true diff --git a/dudect/README.md b/dudect/README.md index dafabbf..af44bf1 100644 --- a/dudect/README.md +++ b/dudect/README.md @@ -11,21 +11,22 @@ Intel® Core™ i7-7700K CPU @ 4.20GHz × 8 Circa 2017 Rust 1.81 $ cd dudect # this directory $ cargo clean -$ time RUSTFLAGS="-C target-cpu=native" cargo run --release +$ time RUSTFLAGS="-C target-cpu=native" cargo run --release # A ~11 hour run ... + Compiling fips205 v0.4.0 (/home/eric/work/fips205) Compiling fips205-dudect v0.4.0 (/home/eric/work/fips205/dudect) - Finished `release` profile [optimized] target(s) in 7.36s + Finished `release` profile [optimized] target(s) in 12.04s Running `target/release/fips205-dudect` running 1 bench -bench keygen_and_sign seeded with 0x89b5d1d7e0207f97 -bench keygen_and_sign ... : n == +0.001M, max t = -1.03786, max tau = -0.03383, (5/tau)^2 = 21840 +bench keygen_and_sign seeded with 0x54522172d8fff6c0 +bench keygen_and_sign ... : n == +0.007M, max t = +3.04638, max tau = +0.03693, (5/tau)^2 = 18331 dudect benches complete -real 63m43.206s -user 64m12.095s -sys 0m2.511s +real 660m35.132s +user 660m47.614s +sys 0m2.662s ~~~ diff --git a/dudect/src/main.rs b/dudect/src/main.rs index c4ad62d..83c173f 100644 --- a/dudect/src/main.rs +++ b/dudect/src/main.rs @@ -30,8 +30,8 @@ pub struct AlignedBytes(pub(crate) [u8; BYTE_LEN]); fn keygen_and_sign(runner: &mut CtRunner, mut _rng: &mut BenchRng) { - const ITERATIONS_OUTER: usize = 1_000; - const ITERATIONS_INNER: usize = 4; + const ITERATIONS_OUTER: usize = 20_000; + const ITERATIONS_INNER: usize = 2; let message = AlignedBytes::<8>([0u8, 1, 2, 3, 4, 5, 6, 7]); let z_left = AlignedBytes::<16>([0xAAu8; 16]); diff --git a/src/hypertree.rs b/src/hypertree.rs index 3922fbb..2b6367c 100644 --- a/src/hypertree.rs +++ b/src/hypertree.rs @@ -157,5 +157,5 @@ pub(crate) fn ht_verify< // 15: else // 16: return false // 17: end if - node == *pk_root // TODO: CT equal (double-check: is this in signing path??) + node == *pk_root // Public data, thus no CT eq required } diff --git a/src/lib.rs b/src/lib.rs index e34790a..6e83827 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -48,7 +48,7 @@ // TODO: Roadmap // 1. Additional (external) top-level test vectors, particularly for hash variants (!!) -// 2. Implement fuzz harness, embedded target, code provenance functionality +// 2. Implement fuzz harness, embedded target, revise WASM test // 3. Experiment with struct alignment for performance uplift? (and fixed size 'slices') @@ -206,6 +206,7 @@ macro_rules! functionality { impl Signer for PrivateKey { type Signature = [u8; SIG_LEN]; + type PublicKey = PublicKey; // Documented in traits.rs fn try_sign_with_rng( @@ -244,6 +245,11 @@ macro_rules! functionality { sig.map(|s| s.serialize()) } + // Documented in traits.rs + fn get_public_key(&self) -> Self::PublicKey { + PublicKey(SlhPublicKey{pk_seed: self.0.pk_seed, pk_root: self.0.pk_root}) + } + // Documented in traits.rs fn _test_only_raw_sign( &self, rng: &mut impl CryptoRngCore, m: &[u8], hedged: bool, @@ -338,8 +344,6 @@ macro_rules! functionality { // Documented in traits.rs fn try_from_bytes(bytes: &Self::ByteArray) -> Result { - // Result: opportunity for validation - //let mut pk = SlhPublicKey::default(); let mut pk = SlhPublicKey { pk_seed: [0u8; N], pk_root: [0u8; N] }; pk.pk_seed.copy_from_slice(&bytes[..(PK_LEN / 2)]); pk.pk_root.copy_from_slice(&bytes[(PK_LEN / 2)..]); @@ -373,6 +377,8 @@ macro_rules! functionality { sk.sk_prf.copy_from_slice(&bytes[(SK_LEN / 4)..(SK_LEN / 2)]); sk.pk_seed.copy_from_slice(&bytes[(SK_LEN / 2)..(3 * SK_LEN / 4)]); sk.pk_root.copy_from_slice(&bytes[(3 * SK_LEN / 4)..]); + let (sk_test, _) = crate::slh::slh_keygen_internal::(&HASHERS, sk.sk_seed, sk.sk_prf, sk.pk_seed); + if sk_test.pk_root != sk.pk_root { return Err("Corrupted key")} Ok(PrivateKey(sk)) } } diff --git a/src/slh.rs b/src/slh.rs index 1711f55..ffb5557 100644 --- a/src/slh.rs +++ b/src/slh.rs @@ -237,7 +237,6 @@ pub(crate) fn slh_sign_internal< /// /// Input: Message `M`, signature `SIG`, context string `ctx`, public key `PK = (PK.seed, PK.root)`.
/// Output: Boolean. -#[allow(clippy::cast_possible_truncation)] // TODO: temporary #[allow(clippy::similar_names)] pub(crate) fn slh_verify< const A: usize, @@ -273,7 +272,6 @@ pub(crate) fn slh_verify< /// /// Input: Message `M`, signature `SIG`, public key `PK = (PK.seed, PK.root)`.
/// Output: Boolean. -#[allow(clippy::cast_possible_truncation)] // TODO: temporary #[allow(clippy::similar_names)] pub(crate) fn slh_verify_internal< const A: usize, @@ -293,7 +291,7 @@ pub(crate) fn slh_verify_internal< // 1: if |SIG| != (1 + k(1 + a) + h + d · len) · n then // 2: return false // 3: end if - // The above size is performed in the wrapper/adapter deserialize function + // The above size check is performed in the wrapper/adapter deserialize function // 4: ADRS ← toByte(0, 32) let mut adrs = Adrs::default(); @@ -337,7 +335,8 @@ pub(crate) fn slh_verify_internal< adrs.set_type_and_clear(FORS_TREE); // 16: ADRS.setKeyPairAddress(idx_leaf) - adrs.set_key_pair_address(idx_leaf as u32); + let Ok(idx_leaf_u32) = u32::try_from(idx_leaf) else { return false }; // should never fail + adrs.set_key_pair_address(idx_leaf_u32); // 17: PK_FORS ← fors_pkFromSig(SIG_FORS, md, PK.seed, ADRS) let pk_fors = @@ -350,7 +349,7 @@ pub(crate) fn slh_verify_internal< sig_ht, &pk.pk_seed, idx_tree, - idx_leaf as u32, + idx_leaf_u32, &pk.pk_root, ) } diff --git a/src/traits.rs b/src/traits.rs index 14f0fa3..c21c9f5 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -106,7 +106,8 @@ pub trait KeyGen { pub trait Signer { /// The signature is specific to the chosen security parameter set, e.g., `slh_dsa_shake_128s`, `slh_dsa_sha2_128s` etc type Signature; - + /// The public key that corresponds to the private/secret key + type PublicKey; /// Attempt to sign the given message, returning a digital signature on success, or an error if /// something went wrong. This function utilizes the **OS default** random number generator. @@ -296,6 +297,31 @@ pub trait Signer { ) -> Result; + /// Retrieves the public key associated with this private/secret key + /// # Examples + /// ```rust + /// use fips205::slh_dsa_shake_128s; // Could use any of the twelve security parameter sets. + /// use fips205::traits::{SerDes, Signer, Verifier}; + /// # use std::error::Error; + /// # use rand_core::OsRng; + /// # use fips205::Ph; + /// # fn main() -> Result<(), Box> { + /// + /// let msg_bytes = [0u8, 1, 2, 3, 4, 5, 6, 7]; + /// let mut rng = OsRng; + /// + /// + /// // Generate both public and secret keys, but only hang onto the secret key. + /// let (_, sk) = slh_dsa_shake_128s::try_keygen()?; + /// + /// // The public key can be derived from the secret key + /// let pk = sk.get_public_key(); + /// # Ok(()) + /// # } + /// ``` + fn get_public_key(&self) -> Self::PublicKey; + + /// As of October 4 2024, the available NIST test vectors are applied to the **internal** functions /// rather than the external API. This function should not be used outside of this scenario. /// # Errors