From 37969beeb8431557220bd5842c4a89b2c17b4d7d Mon Sep 17 00:00:00 2001 From: integritychain Date: Tue, 20 Feb 2024 17:46:27 -0600 Subject: [PATCH] rustfmt --- Cargo.toml | 12 ++++++------ README.md | 3 ++- benches/benchmark.rs | 4 ++-- src/byte_fns.rs | 11 +++++------ src/helpers.rs | 13 ++++++------- src/k_pke.rs | 4 +--- src/lib.rs | 35 +++++++++++++++++++++-------------- src/ml_kem.rs | 6 ++---- src/ntt.rs | 10 +++++----- src/sampling.rs | 3 ++- src/traits.rs | 8 +++++++- src/types.rs | 7 +++---- tests/cctv_vectors/mod.rs | 2 +- tests/integration.rs | 4 +--- tests/nist_vectors/mod.rs | 8 ++------ 15 files changed, 66 insertions(+), 64 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 3493900..dcae55a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,12 +14,6 @@ keywords = ["FIPS", "FIPS203", "lattice", "key", "encapsulation"] rust-version = "1.72" -[dependencies] -zeroize = { version = "1.6.0", default-features = false, features = ["zeroize_derive"] } -rand_core = { version = "0.6.4", default-features = false } -sha3 = { version = "0.10.2", default-features = false } - - [features] default = ["default-rng", "ml-kem-512", "ml-kem-768", "ml-kem-1024"] default-rng = ["rand_core/getrandom"] @@ -28,6 +22,12 @@ ml-kem-768 = [] ml-kem-1024 = [] +[dependencies] +zeroize = { version = "1.6.0", default-features = false, features = ["zeroize_derive"] } +rand_core = { version = "0.6.4", default-features = false } +sha3 = { version = "0.10.2", default-features = false } + + [dev-dependencies] rand = "0.8.5" regex = "1.10.2" diff --git a/README.md b/README.md index 016b141..3ad8392 100644 --- a/README.md +++ b/README.md @@ -55,7 +55,8 @@ The Rust [Documentation][docs-link] lives under each **Module** corresponding to ## Notes * This crate is fully functional and corresponds to the first initial public draft of FIPS 203. -* Constant-time assurances target the source-code level only, and are a work in progress. +* Constant-time ambitions target the source-code level only, and are a work in progress (thus the API + suffix of `_vt`). * Note that FIPS 203 places specific requirements on randomness per section 3.3, hence the exposed `RNG`. * Requires Rust **1.72** or higher. The minimum supported Rust version may be changed in the future, but it will be done with a minor version bump. diff --git a/benches/benchmark.rs b/benches/benchmark.rs index bafb2c6..a1437a9 100644 --- a/benches/benchmark.rs +++ b/benches/benchmark.rs @@ -1,7 +1,7 @@ -use criterion::{Criterion, criterion_group, criterion_main}; +use criterion::{criterion_group, criterion_main, Criterion}; -use fips203::{ml_kem_1024, ml_kem_512, ml_kem_768}; use fips203::traits::{Decaps, Encaps, KeyGen}; +use fips203::{ml_kem_1024, ml_kem_512, ml_kem_768}; #[allow(clippy::redundant_closure)] pub fn criterion_benchmark(c: &mut Criterion) { diff --git a/src/byte_fns.rs b/src/byte_fns.rs index 026b046..6820910 100644 --- a/src/byte_fns.rs +++ b/src/byte_fns.rs @@ -1,10 +1,10 @@ use crate::helpers::ensure; -use crate::Q; use crate::types::Z; +use crate::Q; -// Note: Algorithm 2 and 3 have been "optimized away" as they had a lot of overhead -// and made memory management tricky. The headers are left here for reference. +// Note: Algorithm 2 and 3 have been "optimized away" as they had a lot of overhead +// and made memory allocations tricky. The definitions are left here for reference. // /// Algorithm 2 `BitsToBytes(b)` on page 17. // /// Converts a bit string (of length a multiple of eight) into an array of bytes. @@ -12,7 +12,6 @@ use crate::types::Z; // /// Input: bit array b ∈ {0,1}^{8·ℓ}
// /// Output: byte array B ∈ B^ℓ - // /// Algorithm 3 `BytesToBits(B)` on page 18. // /// Performs the inverse of `BitsToBytes`, converting a byte array into a bit array. // /// @@ -51,7 +50,7 @@ pub(crate) fn byte_encode( bit_index += d as usize; // While we have enough bits to drop a byte, do so - #[allow(clippy::cast_possible_truncation)] + #[allow(clippy::cast_possible_truncation)] // Intentional truncation while bit_index > 7 { // // Drop the byte @@ -91,7 +90,7 @@ pub(crate) fn byte_decode( bit_index += 8; // If we have enough bits to drop an int, do so - #[allow(clippy::cast_possible_truncation)] + #[allow(clippy::cast_possible_truncation)] // Intentional truncation while bit_index >= d { // // Mask off the upport portion and drop it in diff --git a/src/helpers.rs b/src/helpers.rs index 32e7e04..4989c27 100644 --- a/src/helpers.rs +++ b/src/helpers.rs @@ -1,10 +1,11 @@ -use sha3::{Digest, Sha3_256, Sha3_512, Shake128, Shake256}; -use sha3::digest::{ExtendableOutput, XofReader}; use sha3::digest::Update; +use sha3::digest::{ExtendableOutput, XofReader}; +use sha3::{Digest, Sha3_256, Sha3_512, Shake128, Shake256}; use crate::ntt::multiply_ntts; -use crate::Q; use crate::types::Z; +use crate::Q; + /// If the condition is not met, return an error message. Borrowed from the `anyhow` crate. macro_rules! ensure { @@ -115,7 +116,6 @@ pub(crate) fn xof(rho: &[u8; 32], i: u8, j: u8) -> impl XofReader { pub(crate) fn g(bytes: &[&[u8]]) -> ([u8; 32], [u8; 32]) { let mut hasher = Sha3_512::new(); bytes.iter().for_each(|b| Digest::update(&mut hasher, b)); - //Digest::update(&mut hasher, bytes); let digest = hasher.finalize(); let mut a = [0u8; 32]; let mut b = [0u8; 32]; @@ -160,11 +160,10 @@ pub(crate) fn compress(d: u32, inout: &mut [Z]) { // Barrett division, quotient could be too small by one let top = u64::from(x_ref.get_u32()) << d; let quot = (top * m) >> k; - let bump = (top - quot * q64) > u64::from(Q); + let (_diff, bump) = u64::from(Q).overflowing_sub(top - quot * q64); let quot = quot + (u64::from(bump).wrapping_neg() & 1); // Round to nearest - let rem = top - quot * q64; - let bump = rem > (q64 >> 1); + let (_diff, bump) = u64::from(Q >> 1).overflowing_sub(top - quot * q64); let result = quot + (u64::from(bump).wrapping_neg() & 1); x_ref.set_u16(result as u16); } diff --git a/src/k_pke.rs b/src/k_pke.rs index 4f8a51d..071108b 100644 --- a/src/k_pke.rs +++ b/src/k_pke.rs @@ -8,6 +8,7 @@ use crate::ntt::{ntt, ntt_inv}; use crate::sampling::{sample_ntt, sample_poly_cbd}; use crate::types::Z; + /// Algorithm 12 `K-PKE.KeyGen()` on page 26. /// Generates an encryption key and a corresponding decryption key. /// @@ -309,7 +310,6 @@ mod tests { let m = [0u8; 32]; let r = [0u8; 32]; - let pkg = k_pke_key_gen::; let res = pkg(&mut rng, ETA1, &mut ek, &mut dk[0..384 * K]); assert!(res.is_ok()); @@ -322,7 +322,6 @@ mod tests { let res = pkg(&mut rng, ETA1, &mut ek, &mut bad_dk); assert!(res.is_err()); - let pke = k_pke_encrypt::; let res = pke(DU, DV, ETA1, ETA2, &ek, &m, &r, &mut ct); assert!(res.is_ok()); @@ -348,7 +347,6 @@ mod tests { let res = pke(DU, DV, ETA1, ETA2 + 2, &ek, &m, &r, &mut ct); assert!(res.is_err()); - let pkd = k_pke_decrypt::; let res = pkd(DU, DV, &dk[0..384 * K], &ct); assert!(res.is_ok()); diff --git a/src/lib.rs b/src/lib.rs index 5af84bb..067c0b4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,11 +4,9 @@ #![deny(missing_docs)] #![doc = include_str!("../README.md")] +// Implements FIPS 203 draft Module-Lattice-based Key-Encapsulation Mechanism Standard. +// See -/// -/// Implements FIPS 203 draft Module-Lattice-based Key-Encapsulation Mechanism Standard. -/// See -// // Supports automatically clearing sensitive data on drop use zeroize::{Zeroize, ZeroizeOnDrop}; @@ -38,7 +36,8 @@ use crate::traits::SerDes; // // The three parameter sets are modules in this file with injected macro code // that connects them into the functionality in ml_kem.rs. Some of the 'obtuse' -// coding style is driven by clippy pedantic. +// coding style is driven by `clippy pedantic`. + mod byte_fns; mod helpers; @@ -51,32 +50,35 @@ mod types; /// All functionality is covered by traits, such that consumers can utilize trait objects as desired. pub mod traits; + // Relevant to all parameter sets const _N: u32 = 256; const Q: u32 = 3329; const ZETA: u32 = 17; -/// Shared Secret Key Length for all ML-KEM variants (in bytes) + +/// Shared Secret Key length for all ML-KEM variants (in bytes) pub const SSK_LEN: usize = 32; /// The (opaque) secret key that can be de/serialized by each party. #[derive(Clone, Debug, Zeroize, ZeroizeOnDrop)] pub struct SharedSecretKey([u8; SSK_LEN]); + impl SerDes for SharedSecretKey { type ByteArray = [u8; SSK_LEN]; fn into_bytes(self) -> Self::ByteArray { self.0 } fn try_from_bytes(ssk: Self::ByteArray) -> Result { - // Not really needed but provided for symmetry. - // No opportunity for validation, but using a Result for a future possibility + // The `try_from` is not really needed but provided for symmetry, e.g., there + // is no opportunity for validation, but using a Result for the future possibility Ok(SharedSecretKey(ssk)) } } -// Conservative (constant-time) paranoia... +// Conservative (constant-time) ambitions... impl PartialEq for SharedSecretKey { fn eq(&self, other: &Self) -> bool { let mut result = true; @@ -103,6 +105,7 @@ macro_rules! functionality { use crate::SharedSecretKey; use rand_core::CryptoRngCore; + /// Correctly sized encapsulation key specific to the target security parameter set. pub type EncapsKey = crate::types::EncapsKey; @@ -115,6 +118,7 @@ macro_rules! functionality { /// Supports the `KeyGen` trait, allowing for keypair generation pub struct KG(); + impl KeyGen for KG { type DecapsByteArray = [u8; DK_LEN]; type DecapsKey = DecapsKey; @@ -126,7 +130,7 @@ macro_rules! functionality { ) -> Result<(EncapsKey, DecapsKey), &'static str> { let (mut ek, mut dk) = ([0u8; EK_LEN], [0u8; DK_LEN]); ml_kem_key_gen::(rng, ETA1, &mut ek, &mut dk)?; - Ok((EncapsKey{0: ek}, DecapsKey{0: dk})) + Ok((EncapsKey { 0: ek }, DecapsKey { 0: dk })) } fn validate_keypair_vt(ek: &Self::EncapsByteArray, dk: &Self::DecapsByteArray) -> bool { @@ -139,6 +143,7 @@ macro_rules! functionality { } } + impl Encaps for EncapsKey { type CipherText = CipherText; type SharedSecretKey = SharedSecretKey; @@ -150,10 +155,11 @@ macro_rules! functionality { let ssk = ml_kem_encaps::( rng, DU, DV, ETA1, ETA2, &self.0, &mut ct, )?; - Ok((ssk, CipherText{0: ct})) + Ok((ssk, CipherText { 0: ct })) } } + impl Decaps for DecapsKey { type CipherText = CipherText; type SharedSecretKey = SharedSecretKey; @@ -181,7 +187,7 @@ macro_rules! functionality { for i in 0..K { byte_decode(12, &ek[384 * i..384 * (i + 1)], &mut ek_hat)?; } - Ok(EncapsKey{0: ek}) + Ok(EncapsKey { 0: ek }) } } @@ -195,10 +201,11 @@ macro_rules! functionality { // Validation per pg 31. Note that the two checks specify fixed sizes, and these // functions take only byte arrays of correct size. Nonetheless, we use a Result // here in case future opportunities for validation arise. - Ok(DecapsKey{0: dk}) + Ok(DecapsKey { 0: dk }) } } + impl SerDes for CipherText { type ByteArray = [u8; CT_LEN]; @@ -208,7 +215,7 @@ macro_rules! functionality { // Validation per pg 31. Note that the two checks specify fixed sizes, and these // functions take only byte arrays of correct size. Nonetheless, we use a Result // here in case future opportunities for validation arise. - Ok(CipherText{0: ct}) + Ok(CipherText { 0: ct }) } } }; diff --git a/src/ml_kem.rs b/src/ml_kem.rs index e29b333..f337a24 100644 --- a/src/ml_kem.rs +++ b/src/ml_kem.rs @@ -3,8 +3,9 @@ use rand_core::CryptoRngCore; use crate::byte_fns::{byte_decode, byte_encode}; use crate::helpers::{ensure, g, h, j}; use crate::k_pke::{k_pke_decrypt, k_pke_encrypt, k_pke_key_gen}; -use crate::SharedSecretKey; use crate::types::Z; +use crate::SharedSecretKey; + /// Algorithm 15 `ML-KEM.KeyGen()` on page 29. /// Generates an encapsulation key and a corresponding decapsulation key. @@ -175,7 +176,6 @@ mod tests { let mut dk = [0u8; DK_LEN]; let mut ct = [0u8; CT_LEN]; - let mkg = ml_kem_key_gen::; let res = mkg(&mut rng, ETA1, &mut ek, &mut dk); assert!(res.is_ok()); @@ -188,7 +188,6 @@ mod tests { let res = mkg(&mut rng, ETA1, &mut ek, &mut bad_dk); assert!(res.is_err()); - let mke = ml_kem_encaps::; let res = mke(&mut rng, DU, DV, ETA1, ETA2, &ek, &mut ct); assert!(res.is_ok()); @@ -200,7 +199,6 @@ mod tests { let res = mke(&mut rng, DU, DV, ETA1, ETA2, &ff_ek, &mut ct); assert!(res.is_err()); - let mkd = ml_kem_decaps::; let res = mkd(DU, DV, ETA1, ETA2, &dk, &ct); assert!(res.is_ok()); diff --git a/src/ntt.rs b/src/ntt.rs index 3b62d88..4cc92c5 100644 --- a/src/ntt.rs +++ b/src/ntt.rs @@ -1,5 +1,6 @@ -use crate::{Q, ZETA}; use crate::types::Z; +use crate::{Q, ZETA}; + /// Algorithm 8 `NTT(f)` on page 22. /// Computes the NTT representation `f_hat` of the given polynomial f ∈ `R_q`. @@ -165,10 +166,9 @@ pub fn base_case_multiply(a0: Z, a1: Z, b0: Z, b1: Z, gamma: Z) -> (Z, Z) { // ---------- // The functionality below calculates the Zeta array at compile-time. Thus, not particularly optimal or CT. - /// HAC Algorithm 14.76 Right-to-left binary exponentiation mod Q. #[must_use] -#[allow(clippy::cast_possible_truncation)] +#[allow(clippy::cast_possible_truncation)] // on result const fn pow_mod_q(g: u32, e: u8) -> u16 { let g = g as u64; let mut result = 1; @@ -187,7 +187,7 @@ const fn pow_mod_q(g: u32, e: u8) -> u16 { } -#[allow(clippy::cast_possible_truncation)] +#[allow(clippy::cast_possible_truncation)] // i as u8 const fn gen_zeta_table() -> [u16; 256] { let mut result = [0u16; 256]; let mut i = 0; @@ -205,8 +205,8 @@ pub(crate) static ZETA_TABLE: [u16; 256] = gen_zeta_table(); #[cfg(test)] mod tests { use crate::ntt::{gen_zeta_table, pow_mod_q}; - use crate::SharedSecretKey; use crate::traits::SerDes; + use crate::SharedSecretKey; #[test] fn test_zeta_misc() { diff --git a/src/sampling.rs b/src/sampling.rs index b4c4001..8ebddbf 100644 --- a/src/sampling.rs +++ b/src/sampling.rs @@ -1,7 +1,8 @@ use sha3::digest::XofReader; -use crate::Q; use crate::types::Z; +use crate::Q; + /// Algorithm 6 `SampleNTT(B)` on page 20. /// If the input is a stream of uniformly random bytes, the output is a uniformly random element of `T_q`. diff --git a/src/traits.rs b/src/traits.rs index 0cacef5..68c8246 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -2,6 +2,7 @@ use rand_core::CryptoRngCore; #[cfg(feature = "default-rng")] use rand_core::OsRng; + /// The `KeyGen` trait is defined to allow trait objects. pub trait KeyGen { /// The (public) encapsulation key sent from the originator to the remote party. @@ -48,6 +49,7 @@ pub trait KeyGen { Self::try_keygen_with_rng_vt(&mut OsRng) } + /// Generates an encapsulation and decapsulation key key pair specific to this security parameter set.
/// This function utilizes a supplied random number generator, and makes no (constant) /// timing assurances. @@ -99,7 +101,6 @@ pub trait KeyGen { /// /// # Ok(())} /// ``` - fn validate_keypair_vt(ek: &Self::EncapsByteArray, dk: &Self::DecapsByteArray) -> bool; } @@ -111,6 +112,7 @@ pub trait Encaps { /// The ciphertext transmitted from the remote party to the originator. type CipherText; + /// Generates a shared secret and ciphertext from an encapsulation key specific to this security parameter set.
/// This function utilizes the OS default random number generator, and makes no (constant) /// timing assurances. @@ -146,6 +148,7 @@ pub trait Encaps { self.try_encaps_with_rng_vt(&mut OsRng) } + /// Generates a shared secret and ciphertext from an encapsulation key specific to this security parameter set.
/// This function utilizes a supplied random number generator, and makes no (constant) /// timing assurances. @@ -189,6 +192,7 @@ pub trait Decaps { /// Shared secret struct type SharedSecretKey; + /// Generates a shared secret from a decapsulation key and ciphertext specific to this security parameter set.
/// This function makes no (constant) timing assurances. /// # Errors @@ -227,6 +231,7 @@ pub trait SerDes { /// Correctly sized byte array for struct type ByteArray; + /// Produces a byte array of fixed-size specific to the struct being serialized. /// # Examples /// ```rust @@ -255,6 +260,7 @@ pub trait SerDes { /// ``` fn into_bytes(self) -> Self::ByteArray; + /// Consumes a byte array of fixed-size specific to the struct being deserialized; performs validation /// # Errors /// Returns an error on malformed input. diff --git a/src/types.rs b/src/types.rs index d4f3d3c..00aa310 100644 --- a/src/types.rs +++ b/src/types.rs @@ -2,14 +2,17 @@ use zeroize::{Zeroize, ZeroizeOnDrop}; use crate::Q; + /// Correctly sized encapsulation key specific to the target security parameter set. #[derive(Clone, Zeroize, ZeroizeOnDrop)] pub struct EncapsKey(pub(crate) [u8; EK_LEN]); + /// Correctly sized decapsulation key specific to the target security parameter set. #[derive(Clone, Zeroize, ZeroizeOnDrop)] pub struct DecapsKey(pub(crate) [u8; DK_LEN]); + /// Correctly sized ciphertext specific to the target security parameter set. #[derive(Clone, Zeroize, ZeroizeOnDrop)] pub struct CipherText(pub(crate) [u8; CT_LEN]); @@ -23,7 +26,6 @@ pub struct CipherText(pub(crate) [u8; CT_LEN]); #[derive(Clone, Copy, Default)] pub struct Z(u16); - #[allow(clippy::inline_always)] impl Z { const M: u64 = 2u64.pow(32) / (Self::Q64); @@ -43,7 +45,6 @@ impl Z { let (trial, borrow) = sum.overflowing_sub(Self::Q16); let select_sum = u16::from(borrow).wrapping_neg(); let result = (!select_sum & trial) | (select_sum & sum); - // let result = if borrow { sum } else { trial }; // Not quite CT Self(result) } @@ -52,7 +53,6 @@ impl Z { let (diff, borrow) = self.0.overflowing_sub(other.0); let mask = u16::from(borrow).wrapping_neg(); let result = diff.wrapping_add(Self::Q16 & mask); - // let result = if borrow { trial } else { diff }; // Not quite CT Self(result) } @@ -66,7 +66,6 @@ impl Z { let (diff, borrow) = (rem as u16).overflowing_sub(Self::Q16); let mask = u16::from(borrow).wrapping_neg(); let result = diff.wrapping_add(Self::Q16 & mask); - // let result = if borrow { rem } else { diff }; Not quite CT Self(result) } } diff --git a/tests/cctv_vectors/mod.rs b/tests/cctv_vectors/mod.rs index 7d882de..57207c4 100644 --- a/tests/cctv_vectors/mod.rs +++ b/tests/cctv_vectors/mod.rs @@ -5,8 +5,8 @@ use flate2::read::GzDecoder; use hex::decode; use regex::Regex; -use fips203::{ml_kem_1024, ml_kem_512, ml_kem_768}; use fips203::traits::{Decaps, Encaps, KeyGen, SerDes}; +use fips203::{ml_kem_1024, ml_kem_512, ml_kem_768}; use super::TestRng; diff --git a/tests/integration.rs b/tests/integration.rs index 439c4c9..3c95fc9 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -1,7 +1,7 @@ use rand_chacha::rand_core::SeedableRng; -use fips203::{ml_kem_1024, ml_kem_512, ml_kem_768}; use fips203::traits::{Decaps, Encaps, KeyGen, SerDes}; +use fips203::{ml_kem_1024, ml_kem_512, ml_kem_768}; #[test] fn test_expected_flow_512() { @@ -35,7 +35,6 @@ fn test_expected_flow_512() { } } - #[test] fn test_expected_flow_768() { let mut rng = rand_chacha::ChaCha8Rng::seed_from_u64(456); @@ -68,7 +67,6 @@ fn test_expected_flow_768() { } } - #[test] fn test_expected_flow_1024() { let mut rng = rand_chacha::ChaCha8Rng::seed_from_u64(789); diff --git a/tests/nist_vectors/mod.rs b/tests/nist_vectors/mod.rs index 55dedd4..c3b5aa7 100644 --- a/tests/nist_vectors/mod.rs +++ b/tests/nist_vectors/mod.rs @@ -7,8 +7,8 @@ use hex::decode; use rand_core::{CryptoRng, RngCore}; use regex::Regex; -use fips203::{ml_kem_1024, ml_kem_512, ml_kem_768}; use fips203::traits::{Decaps, Encaps, KeyGen, SerDes}; +use fips203::{ml_kem_1024, ml_kem_512, ml_kem_768}; // ----- CUSTOM RNG TO REPLAY VALUES ----- @@ -43,7 +43,6 @@ impl MyRng { } } - // ----- EXTRACT I/O VALUES FROM OFFICIAL VECTORS ----- fn get_keygen_vec(filename: &str) -> (Vec, Vec, Vec, Vec) { @@ -87,11 +86,10 @@ fn get_decaps_vec(filename: &str) -> (Vec, Vec, Vec) { .unwrap() .as_str(), ) - .unwrap(); + .unwrap(); (dk, c, kprime) } - // ----- TEST KEYGEN, SIGN AND VERIFY #[test] @@ -124,7 +122,6 @@ fn test_keygen() { assert_eq!(dk_exp, dk_act.into_bytes()); } - #[test] fn test_encaps() { let (ek, m, ssk_exp, ct_exp) = @@ -155,7 +152,6 @@ fn test_encaps() { assert_eq!(ct_exp, ct_act.into_bytes()); } - #[test] fn test_decaps() { let (dk, c, kprime_exp) =