diff --git a/cli/src/helpers.rs b/cli/src/helpers.rs index 62a7741..36fd1fa 100644 --- a/cli/src/helpers.rs +++ b/cli/src/helpers.rs @@ -17,7 +17,7 @@ pub(crate) fn read_from_file(filename: &str) -> Vec { match hex::decode(&buf) { Ok(decoded) => decoded, Err(_) => { - // well, it's not hex, so return it raw + // it's not hex, so return it raw buf } } @@ -47,7 +47,7 @@ pub(crate) fn read_from_file_or_stdin(filename: &Option) -> Vec { match hex::decode(&buf) { Ok(decoded) => decoded, Err(_) => { - // well, it's not hex, so return it raw + // it's not hex, so return it raw buf } } @@ -98,7 +98,7 @@ pub(crate) fn parse_seed(bytes: &[u8]) -> Result::from_bytes_as_type(&seed_bytes, KeyType::Seed).unwrap(); if seed.key_type() == KeyType::Zeroized || seed.security_strength() < SecurityStrength::_256bit diff --git a/cli/src/mldsa_cmd.rs b/cli/src/mldsa_cmd.rs index 8a1a4e3..82c7e8c 100644 --- a/cli/src/mldsa_cmd.rs +++ b/cli/src/mldsa_cmd.rs @@ -1,5 +1,5 @@ -//! Yup, this file is as absolutely atrocious mess of duplicate code that could be much improved -//! by using generics or macros. I just, haven't ... yet. +//! Work in progress. +//! TODO: Use generic macros to eliminate duplicated code. use crate::helpers::{parse_seed, read_from_file, read_from_file_or_stdin, write_bytes_or_hex}; use bouncycastle::core::traits::{Signature, SignaturePrivateKey, SignaturePublicKey}; diff --git a/crypto/base64/src/lib.rs b/crypto/base64/src/lib.rs index b3e57d8..bf47f03 100644 --- a/crypto/base64/src/lib.rs +++ b/crypto/base64/src/lib.rs @@ -1,7 +1,8 @@ //! Good old fashioned base64 encoder and decoder. //! -//! It should just work the way you expect: [encode] takes any bytes-like rust type -//! and returns a String, while [decode] takes a String (which can be in any bytes-like container) +//! It should just work the way it is normally expected: +//! [encode] takes any bytes-like rust type and returns a String, +//! while [decode] takes a String (which can be in any bytes-like container) //! and returns a `Vec`. //! //!``` @@ -31,10 +32,10 @@ //! Unlike Hex, Base64 does not align cleanly to byte boundaries. //! That means that the above one-shot APIs should only be used if you have the entire content to //! process at the same time. -//! In other words, if you arbitrarily break your data into chunks and hand it to the one-shot [encode] and [decode] APIs, -//! you will get incorrect results. -//! If you need to process your data in chunks, you need to use the streaming API that allows -//! repeated calls to `do_update`, producing output as it goes, and correctly holds on to the unprocessed +//! In other words, if data is arbitrarily broken into chunks and handed to the one-shot [encode] and [decode] APIs, +//! the results obtained will be incorrect. +//! Whenever it is necessary to process data in chunks, the streaming API that allows repeated calls to `do_update` +//! must be used. This produces output as it goes, and correctly holds on to the unprocessed //! partial block until either `do_update` or `do_final` is called. //! //! ``` @@ -60,15 +61,15 @@ //! //! > [Util::Lookup: Exploiting key decoding in cryptographic libraries (Sieck, 2021)](https://arxiv.org/pdf/2108.04600.pdf), //! -//! As this is a cryptography library, we are assuming that this base64 implementation will be used to encode +//! As this is a cryptography library, it can be assumed that this base64 implementation will be used to encode //! and decode private keys in PEM and JWK formats and so we are only providing a constant-time implementation //! in order to remove the temptation to shoot yourself in the foot in the name of a small performance gain. //! -//! In our testing, a naΓ―ve lookup table-based implementation of base64::decode was 1.7x faster than -//! our constant-time implementation, and we are quite sure that optimized base64 implementations exist that -//! provide still better performance. -//! So if you find yourself in a position of needing to base64 encode gigabytes of non-sensitive data, then -//! we recommend you use one of the good, fast, but non-constant-time base64 implementations available from other projects. +//! During testing, a naΓ―ve lookup table-based implementation of base64::decode was 1.7x faster than +//! a constant-time implementation. +//! We are quite sure that optimized base64 implementations exist that provide still better performance. +//! It is necessary to encode gigabytes of non-sensitive data on base64, it is advised to use +//! one of the good, fast, but non-constant-time base64 implementations available from other projects. //! //! //! # Alphabets: @@ -100,7 +101,7 @@ pub enum Base64Error { /// pass the same input to do_final(). Note that do_final() is tolerant of incomplete padding blocks, /// so even if an additional padding character is contained in the next chunk of input, do_final() will still produce /// the correct output -- ie any additional chunks held by the caller can be discarded. - PaddingEnconteredDuringDoUpdate, + PaddingEncounteredDuringDoUpdate, /// Input contained a character that was not in the base64 alphabet. The index of the illegal character is included in the output. InvalidB64Character(usize), @@ -288,7 +289,7 @@ impl Base64Decoder { i += 1; self.vals_in_buf += 1; - // here we get to assume that the buffer contains no padding. + // here, it can be assumed that the buffer contains no padding. if self.vals_in_buf == 4 { // decode block out.push(self.buf[0] << 2 | self.buf[1] >> 4); @@ -302,23 +303,23 @@ impl Base64Decoder { Ok(out) } - /// As you would expect, do_final() consumes the object. + /// As can be expected, do_final() consumes the object. pub fn do_final>(mut self, input: T) -> Result, Base64Error> { // process as much as we can the usual way. let mut out = match self.decode_internal(input, false) { Ok(out) => out, - Err(Base64Error::PaddingEnconteredDuringDoUpdate) => { + Err(Base64Error::PaddingEncounteredDuringDoUpdate) => { panic!( - "rollback_if_padding = false should not produce a Base64Error::PaddingEnconteredDuringDoUpdate" + "rollback_if_padding = false should not produce a Base64Error::PaddingEncounteredDuringDoUpdate" ); } Err(e) => return Err(e), }; - // now we only, maybe, have a single block containing padding to deal with. + // now, a single block containing padding remains to be dealt with. if self.vals_in_buf != 0 { // be tolerant of missing padding - // if we're at the end and it's not a complete block, then imagine the missing padding. + // if it is not a complete block at the end, the infer the byte count from the number of leftover symbols let pad_count: u8 = 3 - (self.vals_in_buf as u8 - 1); out.push(self.buf[0] << 2 | self.buf[1] >> 4); diff --git a/crypto/core/src/key_material.rs b/crypto/core/src/key_material.rs index 48f903e..ebb000f 100644 --- a/crypto/core/src/key_material.rs +++ b/crypto/core/src/key_material.rs @@ -44,7 +44,7 @@ use bouncycastle_utils::{ct, min}; use core::cmp::{Ordering, PartialOrd}; use core::fmt; -/// Sometimes you just need a zero-length dummy key. +/// When it is necessary to get a zero-length dummy key. pub type KeyMaterial0 = KeyMaterial<0>; pub type KeyMaterial128 = KeyMaterial<16>; @@ -389,9 +389,11 @@ impl KeyMaterialTrait for KeyMaterial { self.key_len = key_len; Ok(()) } + fn key_type(&self) -> KeyType { self.key_type.clone() } + fn set_key_type(&mut self, key_type: KeyType) -> Result<(), KeyMaterialError> { if !self.allow_hazardous_operations { return Err(KeyMaterialError::HazardousOperationNotPermitted); @@ -399,6 +401,7 @@ impl KeyMaterialTrait for KeyMaterial { self.key_type = key_type.clone(); Ok(()) } + fn security_strength(&self) -> SecurityStrength { self.security_strength.clone() } @@ -453,6 +456,7 @@ impl KeyMaterialTrait for KeyMaterial { self.drop_hazardous_operations(); Ok(()) } + /// Sets this instance to be able to perform potentially hazardous operations such as /// casting a KeyMaterial of type RawUnknownEntropy or RawLowEntropy into RawFullEntropy or SymmetricCipherKey. /// @@ -463,10 +467,12 @@ impl KeyMaterialTrait for KeyMaterial { fn allow_hazardous_operations(&mut self) { self.allow_hazardous_operations = true; } + /// Resets this instance to not be able to perform potentially hazardous operations. fn drop_hazardous_operations(&mut self) { self.allow_hazardous_operations = false; } + /// Sets the key_type of this KeyMaterial object. /// Does not perform any operations on the actual key material, other than changing the key_type field. /// If allow_hazardous_operations is true, this method will allow conversion to any KeyType, otherwise @@ -529,6 +535,7 @@ impl KeyMaterialTrait for KeyMaterial { self.drop_hazardous_operations(); Ok(()) } + fn is_full_entropy(&self) -> bool { match self.key_type { KeyType::BytesFullEntropy diff --git a/crypto/core/src/traits.rs b/crypto/core/src/traits.rs index 089e282..72f8149 100644 --- a/crypto/core/src/traits.rs +++ b/crypto/core/src/traits.rs @@ -1,4 +1,4 @@ -//! Provides simplified abstracted APIs over classes of cryptigraphic primitives, such as Hash, KDF, etc. +//! Provides simplified abstracted APIs over classes of cryptographic primitives, such as Hash, KDF, etc. use crate::errors::{HashError, KDFError, KEMError, MACError, RNGError, SignatureError}; use crate::key_material::KeyMaterialTrait; @@ -108,7 +108,7 @@ pub trait KDF: Default { /// [KeyType::BytesLowEntropy] -- for example, seeding SHA3-256 with a [KeyMaterial] containing /// only 128 bits of key material. /// - /// An implement can, and in most cases SHOULD, return a [HashError] if provided + /// An implementation can, and in most cases SHOULD, return a [HashError] if provided /// with a [KeyMaterial] of type [KeyType::Zeroized]. /// /// # Additional Input diff --git a/crypto/core/tests/key_material_tests.rs b/crypto/core/tests/key_material_tests.rs index 5e773fd..d022480 100644 --- a/crypto/core/tests/key_material_tests.rs +++ b/crypto/core/tests/key_material_tests.rs @@ -26,7 +26,7 @@ mod test_key_material { _ => panic!("Expected InvalidLength"), } - // But you can slice it down. + // This can be sliced down. match KeyMaterial512::from_bytes(&DUMMY_KEY_TOO_LONG[..64]) { Ok(key) => assert_eq!(key.key_len(), 64), _ => panic!("Expected InvalidLength"), @@ -47,7 +47,7 @@ mod test_key_material { assert_eq!(key.key_type(), KeyType::Zeroized); assert_eq!(key.key_len(), 16); - // ... but we can force it. + // however, it can be forced by allowing hazardous operations. key.allow_hazardous_operations(); key.set_key_type(KeyType::BytesLowEntropy).unwrap(); key.drop_hazardous_operations(); @@ -59,13 +59,12 @@ mod test_key_material { assert_eq!(key.key_type(), KeyType::BytesLowEntropy); assert_eq!(key.security_strength(), SecurityStrength::None); - // but it'll allow it if you allow hazardous operations first. + // it can be enabled by allowing hazardous operations first. let key_bytes = [0u8; 16]; let mut key = KeyMaterial256::new(); key.allow_hazardous_operations(); key.set_bytes_as_type(&key_bytes, KeyType::BytesLowEntropy).unwrap(); assert_eq!(key.key_type(), KeyType::BytesLowEntropy); - // nothing else requires setting hazardous operations. } @@ -89,7 +88,7 @@ mod test_key_material { assert_eq!(key.mut_ref_to_bytes().unwrap()[..16], [1u8; 16]); assert_eq!(key.mut_ref_to_bytes().unwrap()[16..], [0u8; 16]); - // and I can set them + // Then they can be set key.mut_ref_to_bytes().unwrap().copy_from_slice(&[2u8; 32]); key.set_key_len(32).unwrap(); assert_eq!(key.ref_to_bytes(), &[2u8; 32]); @@ -247,7 +246,7 @@ mod test_key_material { assert_eq!(key.key_type(), KeyType::BytesLowEntropy); assert!(!key.is_full_entropy()); - // Note: can't use the usual assert_eq!() here because that requires PartialEq, but we're in a no_std context here. + // Note: the usual assert_eq!() can't be used here because that requires PartialEq, but the current context is no_std. match key.key_type() { KeyType::BytesLowEntropy => { /* good */ } _ => panic!("Expected BytesLowEntropy"), @@ -343,12 +342,13 @@ mod test_key_material { } let zero_key = KeyMaterial256::from_bytes(&[0u8; 19]).unwrap(); - // it should have set the key bytes and length, but also set the key type to Zeroized. + // key bytes and length should be set accordingly, + // as well as the key type should be set to Zeroized. assert_eq!(zero_key.key_type(), KeyType::Zeroized); assert_eq!(zero_key.key_len(), 19); assert_eq!(zero_key.ref_to_bytes(), &[0u8; 19]); - // But it's totally fine if you give it non-zero input data. + // It also admits non-zero input data. let not_zero_key = KeyMaterial256::from_bytes(&[1u8; 19]).unwrap(); assert_eq!(not_zero_key.key_type(), KeyType::BytesLowEntropy); @@ -364,10 +364,10 @@ mod test_key_material { panic!("should have thrown a KeyMaterialError::ActingOnZeroizedKey error.") } } - // but it should still have set the key bytes; it's just giving you a friendly warning + // This should still set the key bytes; only giving a friendly warning that the key is zeroized assert_eq!(zero_key.key_type(), KeyType::Zeroized); - // ... but will allow it if you set .allow_hazardous_operations() first. + // The operation is allowed by setting .allow_hazardous_operations() first. let mut zero_key = KeyMaterial256::new(); zero_key.allow_hazardous_operations(); zero_key.set_bytes_as_type(&[0u8; 19], KeyType::MACKey).unwrap(); @@ -402,7 +402,7 @@ mod test_key_material { _ => panic!("Expected HazardousConversion"), } - /* Should work if you allow hazardous conversions. */ + /* Should work when hazardous conversions are allowed. */ key = KeyMaterial256::from_bytes(&DUMMY_KEY[..32]).unwrap(); key.allow_hazardous_operations(); key.convert_key_type(KeyType::BytesFullEntropy).unwrap(); @@ -445,7 +445,8 @@ mod test_key_material { assert_eq!(key1.key_type(), KeyType::MACKey); assert_eq!(key1.security_strength(), SecurityStrength::_256bit); - // success case: same size using default From impl; only works if the sizes are the same (ie the compiler knows that they are the same type. + // success case: same size using default From impl; + // only works if the sizes are the same (i.e. the compiler knows that they are the same type). let key2 = KeyMaterial256::from(key1.clone()); assert_eq!(key1.key_len(), key2.key_len()); assert_eq!(key1.key_type(), key2.key_type()); @@ -490,7 +491,7 @@ mod test_key_material { _ => panic!("Expected HazardousConversion"), } - // should work if you allow hazardous conversions. + // should work when hazardous conversions are allowed. key.allow_hazardous_operations(); key.convert_key_type(KeyType::SymmetricCipherKey).unwrap(); } @@ -540,7 +541,7 @@ mod test_key_material { assert_eq!(key.key_type(), KeyType::BytesFullEntropy); assert_eq!(key.security_strength(), SecurityStrength::None); - // even if it's long enough, BytesLowEntropy or Zeroized always get ::None + // even if it's long enough, BytesLowEntropy or Zeroized always get ::None as security strength let key = KeyMaterial512::from_bytes_as_type(DUMMY_KEY, KeyType::BytesLowEntropy).unwrap(); assert_eq!(key.key_type(), KeyType::BytesLowEntropy); assert_eq!(key.key_len(), 64); @@ -683,6 +684,7 @@ mod test_key_material { b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F", ) .unwrap(); + let key2 = KeyMaterial256::from_bytes( b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F", ) diff --git a/crypto/factory/src/hash_factory.rs b/crypto/factory/src/hash_factory.rs index 9d12117..05edc13 100644 --- a/crypto/factory/src/hash_factory.rs +++ b/crypto/factory/src/hash_factory.rs @@ -14,7 +14,7 @@ //! let h = bouncycastle_factory::hash_factory::HashFactory::new(sha3::SHA3_256_NAME).unwrap(); //! let output: Vec = h.hash(data); //! ``` -//! You can equivalently invoke this by string instead of using the constant: +//! Equivalently, it may be invoked by passing a string instead of using the constant: //! //! ``` //! use bouncycastle_factory::AlgorithmFactory; diff --git a/crypto/factory/src/kdf_factory.rs b/crypto/factory/src/kdf_factory.rs index f3deffb..85e5b3e 100644 --- a/crypto/factory/src/kdf_factory.rs +++ b/crypto/factory/src/kdf_factory.rs @@ -9,7 +9,7 @@ //! use bouncycastle_core::traits::KDF; //! use bouncycastle_factory::AlgorithmFactory; //! -//! // get your key material from a secure place; here we'll use the default RNG, seeded from the OS +//! // Obtain key material from a secure place; here the default RNG is used, seeded from the OS is used //! let seed_key = KeyMaterial256::from_rng(&mut bouncycastle_rng::DefaultRNG::default()).unwrap(); //! let additional_input: &[u8] = b"some additional input"; //! @@ -17,14 +17,14 @@ //! let new_key = h.derive_key(&seed_key, additional_input).unwrap(); //! ``` //! -//! You can equivalently invoke this by string instead of using the constant: +//! Equivalently, it may be invoked by passing a string instead of using the constant: //! //! ``` //! use bouncycastle_core::key_material::{KeyMaterial256, KeyType}; //! use bouncycastle_core::traits::KDF; //! use bouncycastle_factory::AlgorithmFactory; //! -//! // get your key material from a secure place; here we'll use the default RNG, seeded from the OS +//! // Obtain key material from a secure place; here the default RNG is used, seeded from the OS is used //! let seed_key = KeyMaterial256::from_rng(&mut bouncycastle_rng::DefaultRNG::default()).unwrap(); //! let additional_input: &[u8] = b"some additional input"; //! @@ -32,14 +32,14 @@ //! let new_key = h.derive_key(&seed_key, additional_input).unwrap(); //! ``` //! -//! Or if you don't particularly care which algorithm is used, you can use the built-in default: +//! If the algorithm used is not particularly important, the built-in default may be used: //! //! ``` //! use bouncycastle_core::key_material::{KeyMaterial256, KeyType}; //! use bouncycastle_core::traits::KDF; //! use bouncycastle_factory::AlgorithmFactory; //! -//! // get your key material from a secure place; here we'll use the default RNG, seeded from the OS +//! // Obtain key material from a secure place; here the default RNG is used, seeded from the OS is used //! let seed_key = KeyMaterial256::from_rng(&mut bouncycastle_rng::DefaultRNG::default()).unwrap(); //! let additional_input: &[u8] = b"some additional input"; //! diff --git a/crypto/factory/src/mac_factory.rs b/crypto/factory/src/mac_factory.rs index 141c59f..f5efc79 100644 --- a/crypto/factory/src/mac_factory.rs +++ b/crypto/factory/src/mac_factory.rs @@ -34,7 +34,7 @@ //! } //! ``` //! -//! You can equivalently construct an instance of [MACFactory] by string instead of using the constant: +//! Equivalently, an instance of [MACFactory] may be constructed by string instead of using the constant: //! //! ``` //! use bouncycastle_core::key_material::{KeyMaterial256, KeyType}; @@ -52,7 +52,7 @@ //! let hmac = MACFactory::new("HMAC-SHA256", &key).unwrap(); //! ``` //! -//! Or if you don't particularly care which algorithm is used, you can use the built-in default: +//! If the algorithm used is not particularly important, the built-in default may be used: //! //! ``` //! use bouncycastle_core::key_material::{KeyMaterial256, KeyType}; @@ -138,12 +138,12 @@ impl MACFactory { } impl MAC for MACFactory { - /// This is a dummy function, required by the [MAC] trait. Don't call it, it doesn't do anything. + /// This is a dummy function, required by the [MAC] trait. DO NOT call it, it does not do anything. fn new(_key: &impl KeyMaterialTrait) -> Result { unimplemented!() } - /// This is a dummy function, required by the [MAC] trait. Don't call it, it doesn't do anything. + /// This is a dummy function, required by the [MAC] trait. DO NOT call it, it does not do anything. fn new_allow_weak_key(_key: &impl KeyMaterialTrait) -> Result { unimplemented!() } diff --git a/crypto/factory/src/rng_factory.rs b/crypto/factory/src/rng_factory.rs index 9f1b8e0..4925f25 100644 --- a/crypto/factory/src/rng_factory.rs +++ b/crypto/factory/src/rng_factory.rs @@ -29,8 +29,8 @@ //! let h = bouncycastle_factory::hash_factory::HashFactory::new(sha3::SHA3_256_NAME).unwrap(); //! let output: Vec = h.hash(data); //! ``` -//! You can equivalently invoke this by string instead of using the constant: -//! +//! Equivalently, it may be invoked by passing a string instead of using the constant: +//! //! ``` //! use bouncycastle_factory::AlgorithmFactory; //! use bouncycastle_core::traits::Hash; diff --git a/crypto/factory/src/xof_factory.rs b/crypto/factory/src/xof_factory.rs index e35e86e..24d5127 100644 --- a/crypto/factory/src/xof_factory.rs +++ b/crypto/factory/src/xof_factory.rs @@ -16,7 +16,7 @@ //! h.absorb(data); //! let output: Vec = h.squeeze(16); //! ``` -//! You can equivalently invoke this by string instead of using the constant: +//! Equivalently, it may be invoked by passing a string instead of using the constant: //! //! ``` //! use bouncycastle_factory::AlgorithmFactory; @@ -24,8 +24,7 @@ //! //! let mut h = XOFFactory::new("SHAKE128"); //! ``` -//! -//! Or, if you don't particularly care which algorithm you get, you can use the configured default: +//! If the algorithm used is not particularly important, the configured default may be used: //! //! ``` //! use bouncycastle_factory::AlgorithmFactory; diff --git a/crypto/factory/tests/kdf_factory_tests.rs b/crypto/factory/tests/kdf_factory_tests.rs index 16e6949..d34af5f 100644 --- a/crypto/factory/tests/kdf_factory_tests.rs +++ b/crypto/factory/tests/kdf_factory_tests.rs @@ -63,7 +63,7 @@ mod kdf_factory_tests { fn hkdf_tests() { /* HKDF-SHA256 */ // Note: this value is not checked against any external reference implementation, - // I just hard-coded the value to make sure it stays consistent. + // The value is hard-coded to ensure consistency. let key_material = KeyMaterial256::from_bytes_as_type(&DUMMY_SEED_512[..32], KeyType::MACKey).unwrap(); let derived_key = @@ -73,7 +73,7 @@ mod kdf_factory_tests { /* HKDF-SHA512 */ // Note: this value is not checked against any external reference implementation, - // I just hard-coded the value to make sure it stays consistent. + // The value is hard-coded to ensure consistency. let key_material = KeyMaterial512::from_bytes(&DUMMY_SEED_512[..64]).unwrap(); let derived_key = KDFFactory::new("HKDF-SHA512").unwrap().derive_key(&key_material, &[0u8; 0]).unwrap(); diff --git a/crypto/hex/src/lib.rs b/crypto/hex/src/lib.rs index 6fcf787..865c50f 100644 --- a/crypto/hex/src/lib.rs +++ b/crypto/hex/src/lib.rs @@ -3,9 +3,9 @@ //! This one is implemented using constant-time operations in the conversions //! from Strings to byte values, so it is safe to use on cryptographic secret values. //! -//! It should just work the way you expect: encode takes any bytes-like rust type -//! and returns a String, decode takes a String (which can be in any bytes-like container) -//! and returns a `Vec`. +//! It should just work as expected: +//! encode takes any bytes-like rust type and returns a String, +//! decode takes a String (which can be in any bytes-like container) and returns a `Vec`. //! //! ``` //! use bouncycastle_hex as hex; @@ -63,7 +63,8 @@ pub fn encode_out>(input: T, out: &mut [u8]) -> Result::is_within_range(c as i64, 0, 9); let in_af = Condition::::is_within_range(c as i64, 10, 15); - // TODO: redo this once we have ct::u8 implemented ... the i64 is wasteful + // TODO: redo this once we have ct::u8 implemented + // The i64 is wasteful let c_09: i64 = '0' as i64 + (c as i64); let c_az: i64 = 'a' as i64 + (c as i64 - 10); @@ -101,7 +102,8 @@ pub fn decode_out>(input: T, out: &mut [u8]) -> Result { i += 1; @@ -146,7 +148,8 @@ pub fn decode_out>(input: T, out: &mut [u8]) -> Result::is_within_range(b as i64, 65, 70); - // TODO: redo this once we have ct::u8 implemented ... the i64 is wasteful + // TODO: redo this once we have ct::u8 implemented + // The i64 is wasteful let c_09: i64 = b as i64 - ('0' as i64); #[allow(non_snake_case)] diff --git a/crypto/hkdf/src/lib.rs b/crypto/hkdf/src/lib.rs index 76b47f0..2754adc 100644 --- a/crypto/hkdf/src/lib.rs +++ b/crypto/hkdf/src/lib.rs @@ -163,8 +163,8 @@ use bouncycastle_core::traits::XOF; // end doc-only imports /*** Constants ***/ -// Slightly hacky, but set this to accomodate the underlying hash primitive with the largest output size. -// Would be better to somehow pull that at compile time from H, but I'm not sure how to do that. +// Set this to accomodate the underlying hash primitive with the largest output size. +// TODO: pull this value at compile time from H const HMAC_BLOCK_LEN: usize = 64; /*** String constants ***/ @@ -179,7 +179,7 @@ pub type HKDF_SHA256 = HKDF; pub type HKDF_SHA512 = HKDF; pub struct HKDF { - hmac: Option>, // Optional because we can't construct an HMAC until they give us a key + hmac: Option>, // Optional because an HMAC cannot be constructed until a key is provided // to initialize it with. // None should correspond to a state of Uninitialized. entropy: HkdfEntropyTracker, @@ -239,7 +239,7 @@ impl HkdfEntropyTracker { } } -// Since I don't want this struct to be public, the tests have to go here. +// Because this struct is not public, the tests have to go here. #[test] fn test_entropy_tracker() { let mut entropy = HkdfEntropyTracker::::new(); @@ -279,7 +279,7 @@ impl HKDF { self.entropy.get_entropy() } - /// Has the entropy input so far met the threshold for this object to be considered fully seeded? + /// Check whether the entropy input so far met the threshold for this object to be considered fully seeded pub fn is_fully_seeded(&self) -> bool { self.entropy.is_fully_seeded() } @@ -400,7 +400,7 @@ impl HKDF { let out: &mut [u8] = okm.mut_ref_to_bytes()?; // Could potentially speed this up by unrolling T(0) and T(1) - // We're gonna have to kludge the prk key type to MACKey to make HMAC happy, but we'll set it back to the original value afterwards. + // The prk key type must be temporarily changed to MACKey to satisfy HMAC, then restored afterwards. let prk_as_mac_key = KeyMaterial::::from_bytes_as_type(prk.ref_to_bytes(), KeyType::MACKey)?; @@ -409,7 +409,7 @@ impl HKDF { let mut t_len: usize = 0; let mut i = 1u8; while i < N { - // todo: might need this to be new_allow_weak_key() + // TODO: might need this to be new_allow_weak_key() let mut hmac = HMAC::::new(&prk_as_mac_key)?; hmac.do_update(&T[..t_len]); hmac.do_update(info); @@ -422,9 +422,9 @@ impl HKDF { i += 1; } - // On the last iteration, we don't take all of the output. + // Part of the output is not taken on the last iteration let remaining = L - bytes_written; - // todo: might need this to be new_allow_weak_key() + // TODO: might need this to be new_allow_weak_key() let mut hmac = HMAC::::new(&prk_as_mac_key)?; hmac.do_update(&T[..t_len]); hmac.do_update(info); @@ -435,8 +435,8 @@ impl HKDF { out[bytes_written..bytes_written + t_len].copy_from_slice(&T[..t_len]); bytes_written += t_len; - // set the KeyType of the output - // since we've done some computation, the result will not actually be zeroized, even if all input key material was zeroized. + // Set the KeyType of the output + // Since some computation has been performed, the result will not actually be zeroized, even if all input key material was zeroized. if prk.key_type() == KeyType::Zeroized { okm.set_key_type(KeyType::BytesLowEntropy)?; } else { @@ -487,7 +487,7 @@ impl HKDF { }; // Often HMAC is initialized with a zero salt, - // So we're gonna ignore key strength errors here + // Key strength errors are ignored here. // This will all be tabulated correctly via entropy.credit_entropy() self.hmac = Some(HMAC::::new_allow_weak_key(salt)?); diff --git a/crypto/hkdf/tests/hkdf_tests.rs b/crypto/hkdf/tests/hkdf_tests.rs index 1e13d37..e25624e 100644 --- a/crypto/hkdf/tests/hkdf_tests.rs +++ b/crypto/hkdf/tests/hkdf_tests.rs @@ -371,7 +371,7 @@ mod hkdf_tests { assert_eq!(okm.key_type(), KeyType::BytesFullEntropy); // success case -- insufficient entropy due to key types -- KeyType::BytesLowEntropy - // Note: will still return MACKey because that one was first in the inputs. + // Note: this will still return MACKey because that one was first in the inputs. let salt = KeyMaterial128::from_bytes_as_type(&DUMMY_SEED_512[..16], KeyType::MACKey).unwrap(); let ikm = @@ -381,7 +381,7 @@ mod hkdf_tests { _ = HKDF_SHA256::extract_and_expand_out(&salt, &ikm, &[], 32, &mut okm); assert_eq!(okm.key_type(), KeyType::BytesLowEntropy); - // no way to test this on derive_out + // derive_key_out can't reproduce the two-input salt+ikm arrangement let keys = [&salt, &ikm]; _ = HKDF_SHA256::new().derive_key_from_multiple_out(&keys, &[], &mut okm); diff --git a/crypto/hmac/tests/hmac_tests.rs b/crypto/hmac/tests/hmac_tests.rs index a3d8e8c..62de534 100644 --- a/crypto/hmac/tests/hmac_tests.rs +++ b/crypto/hmac/tests/hmac_tests.rs @@ -92,7 +92,7 @@ mod hmac_tests { ) .unwrap(); assert_eq!(short_key.security_strength(), SecurityStrength::_112bit); - // key is too short, so we expect it to fail + // key is too short, so it is expected to fail match HMAC::::new(&short_key) { Err(MACError::KeyMaterialError(KeyMaterialError::SecurityStrength(_))) => { /* good */ } _ => panic!( @@ -100,10 +100,10 @@ mod hmac_tests { ), } - // but this'll work fine + // It works after allowing weak keys HMAC::::new_allow_weak_key(&short_key).unwrap(); - // as will a long enough key + // It works with a long enough key let key = KeyMaterial256::from_bytes_as_type( &hex::decode("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b").unwrap(), KeyType::MACKey, @@ -143,11 +143,11 @@ mod hmac_tests { #[test] fn security_strength_tests() { - // test: provided key has the correct length, but insufficient tagged security strength + // Test: provided key has the correct length, but insufficient tagged security strength // HMAC should still work, but should return an error // it works with a zero key (as new_allow_weak_key) - // zero-len ey + // zero-len key let mut zero_key = KeyMaterial256::default(); HMAC_SHA256::new_allow_weak_key(&zero_key).unwrap(); @@ -156,14 +156,14 @@ mod hmac_tests { zero_key.set_key_len(32).unwrap(); HMAC_SHA256::new_allow_weak_key(&zero_key).unwrap(); - // but we don't allow zero-len keys that are not Zeroized or MACKey + // Note: zero-len keys that are not Zeroized or MACKey are not allowed // init let mut key = KeyMaterial512::from_bytes_as_type(&DUMMY_SEED_512[..64], KeyType::MACKey).unwrap(); assert_eq!(key.security_strength(), SecurityStrength::_256bit); key.set_security_strength(SecurityStrength::_128bit).unwrap(); - // complains at first + // The call should fail, as the key's security strength is set below the required threshold match HMAC::::new(&key) { Err(MACError::KeyMaterialError(KeyMaterialError::SecurityStrength(_))) => { /* fine */ } _ => { @@ -172,7 +172,7 @@ mod hmac_tests { ) } } - // but fine if you set .allow_weak_keys() + // It passes after setting .allow_weak_keys() let mut hmac = HMAC::::new_allow_weak_key(&key).unwrap(); hmac.do_update(b"Hi There"); hmac.do_final(); @@ -180,7 +180,7 @@ mod hmac_tests { // one-shot APIs still work with a weak key let out = HMAC::::new_allow_weak_key(&key).unwrap().mac(b"Hi There"); assert!(HMAC::::new_allow_weak_key(&key).unwrap().verify(b"Hi There", &out)); - // but fine if you set .allow_weak_keys() + // likewise with pre-allocated buffers let mut out = [0u8; 64]; HMAC::::new_allow_weak_key(&key).unwrap().mac_out(b"Hi There", &mut out).unwrap(); assert!(HMAC::::new_allow_weak_key(&key).unwrap().verify(b"Hi There", &out)); @@ -473,7 +473,7 @@ mod hmac_tests { ) .unwrap(); let mut out = [0u8; 128 / 8]; - // Key is shorter than HMAC security strength, so need to use new_allow_weak_keys() + // Key is shorter than HMAC security strength, so it needs to use new_allow_weak_keys() let hmac = HMAC::::new_allow_weak_key(&key).unwrap(); hmac.mac_out(b"Test With Truncation", &mut out).unwrap(); assert_eq!(&Vec::from(out), &hex::decode("3abf34c3503b2a23a46efc619baef897").unwrap()); diff --git a/crypto/mlkem/benches/mlkem_benches.rs b/crypto/mlkem/benches/mlkem_benches.rs index 5330f73..7d0e3dd 100644 --- a/crypto/mlkem/benches/mlkem_benches.rs +++ b/crypto/mlkem/benches/mlkem_benches.rs @@ -12,8 +12,8 @@ use std::hint::black_box; fn bench_mlkem_keygen(c: &mut Criterion) { let mut group = c.benchmark_group("KeyGen"); - // set up the seeds outside of the timing loop - // Doing different seeds so that the CPU doesn't cache them or do too much branch prediction + // Set up the seeds outside of the timing loop + // Doing different seeds so that the CPU does not cache them or do too much branch prediction let mut seeds = Vec::::new(); for dummy_seed in DUMMY_SEED_1024.chunks(64) { seeds.extend(KeyMaterial512::from_bytes_as_type(dummy_seed, KeyType::Seed)); @@ -51,8 +51,8 @@ fn bench_mlkem_keygen(c: &mut Criterion) { fn bench_mlkem_keygen_and_expand(c: &mut Criterion) { let mut group = c.benchmark_group("KeyGen_and_expand"); - // set up the seeds outside of the timing loop - // Doing different seeds so that the CPU doesn't cache them or do too much branch prediction + // Set up the seeds outside of the timing loop + // Doing different seeds so that the CPU does not cache them or do too much branch prediction let mut seeds = Vec::::new(); for dummy_seed in DUMMY_SEED_1024.chunks(64) { seeds.extend(KeyMaterial512::from_bytes_as_type(dummy_seed, KeyType::Seed)); @@ -93,8 +93,8 @@ fn bench_mlkem_keygen_and_expand(c: &mut Criterion) { fn bench_mlkem_encaps(c: &mut Criterion) { let mut group = c.benchmark_group("Encaps"); - // set up the seeds outside of the timing loop - // Doing different seeds so that the CPU doesn't cache them or do too much branch prediction + // Set up the seeds outside of the timing loop + // Doing different seeds so that the CPU does not cache them or do too much branch prediction let seed = KeyMaterial512::from_bytes_as_type( &hex::decode( "000102030405060708090a0b0c0d0e0f @@ -107,7 +107,7 @@ fn bench_mlkem_encaps(c: &mut Criterion) { ) .unwrap(); - // create a vector of signing nonces so that we're not measuring the time of the RNG + // Create a vector of signing nonces so that we're not measuring the time of the RNG const NUM_ELEMS: usize = 256; let mut nonces = [[0u8; 32]; NUM_ELEMS]; for i in 0..256 { @@ -159,8 +159,8 @@ fn bench_mlkem_encaps(c: &mut Criterion) { fn bench_mlkem_encaps_for_expanded(c: &mut Criterion) { let mut group = c.benchmark_group("Encaps_for_expanded_key"); - // set up the seeds outside of the timing loop - // Doing different seeds so that the CPU doesn't cache them or do too much branch prediction + // Set up the seeds outside of the timing loop + // Doing different seeds so that the CPU does not cache them or do too much branch prediction let seed = KeyMaterial512::from_bytes_as_type( &hex::decode( "000102030405060708090a0b0c0d0e0f @@ -173,7 +173,7 @@ fn bench_mlkem_encaps_for_expanded(c: &mut Criterion) { ) .unwrap(); - // create a vector of signing nonces so that we're not measuring the time of the RNG + // Create a vector of signing nonces so that we're not measuring the time of the RNG const NUM_ELEMS: usize = 256; let mut nonces = [[0u8; 32]; NUM_ELEMS]; for i in 0..256 { @@ -228,8 +228,8 @@ fn bench_mlkem_encaps_for_expanded(c: &mut Criterion) { fn bench_mlkem_decaps(c: &mut Criterion) { let mut group = c.benchmark_group("Decaps"); - // set up the seeds outside of the timing loop - // Doing different seeds so that the CPU doesn't cache them or do too much branch prediction + // Set up the seeds outside of the timing loop + // Doing different seeds so that the CPU does not cache them or do too much branch prediction let seed = KeyMaterial512::from_bytes_as_type( &hex::decode( "000102030405060708090a0b0c0d0e0f @@ -247,10 +247,10 @@ fn bench_mlkem_decaps(c: &mut Criterion) { /*** ML-KEM-512 ***/ let (pk, sk) = MLKEM512::keygen_from_seed(&seed).unwrap(); - // create a bunch of ciphertexts to decaps + // Create a bunch of ciphertexts to decaps let mut cts = [[0u8; MLKEM512_CT_LEN]; NUM_ELEMS]; for i in 0..NUM_ELEMS { - // create each ct with a unique nonce + // Create each ct with a unique nonce // encaps_internal() returns (ss, ct) ... we only want ct, hence the ".1" cts[i].copy_from_slice(&MLKEM512::encaps_internal(&pk, None, [i as u8; MLKEM_RND_LEN]).1); } @@ -268,10 +268,10 @@ fn bench_mlkem_decaps(c: &mut Criterion) { /*** ML-KEM-768 ***/ let (pk, sk) = MLKEM768::keygen_from_seed(&seed).unwrap(); - // create a bunch of ciphertexts to decaps + // Create a bunch of ciphertexts to decaps let mut cts = [[0u8; MLKEM768_CT_LEN]; NUM_ELEMS]; for i in 0..NUM_ELEMS { - // create each ct with a unique nonce + // Create each ct with a unique nonce // encaps_internal() returns (ss, ct) ... we only want ct, hence the ".1" cts[i].copy_from_slice(&MLKEM768::encaps_internal(&pk, None, [i as u8; MLKEM_RND_LEN]).1); } @@ -289,10 +289,10 @@ fn bench_mlkem_decaps(c: &mut Criterion) { /*** ML-KEM-1024 ***/ let (pk, sk) = MLKEM1024::keygen_from_seed(&seed).unwrap(); - // create a bunch of ciphertexts to decaps + // Create a bunch of ciphertexts to decaps let mut cts = [[0u8; MLKEM1024_CT_LEN]; NUM_ELEMS]; for i in 0..NUM_ELEMS { - // create each ct with a unique nonce + // Create each ct with a unique nonce // encaps_internal() returns (ss, ct) ... we only want ct, hence the ".1" cts[i].copy_from_slice(&MLKEM1024::encaps_internal(&pk, None, [i as u8; MLKEM_RND_LEN]).1); } @@ -313,8 +313,8 @@ fn bench_mlkem_decaps(c: &mut Criterion) { fn bench_mlkem_decaps_with_expanded_key(c: &mut Criterion) { let mut group = c.benchmark_group("Decaps_with_expanded_key"); - // set up the seeds outside of the timing loop - // Doing different seeds so that the CPU doesn't cache them or do too much branch prediction + // Set up the seeds outside of the timing loop + // Doing different seeds so that the CPU does not cache them or do too much branch prediction let seed = KeyMaterial512::from_bytes_as_type( &hex::decode( "000102030405060708090a0b0c0d0e0f @@ -333,10 +333,10 @@ fn bench_mlkem_decaps_with_expanded_key(c: &mut Criterion) { let (pk, sk) = MLKEM512::keygen_from_seed(&seed).unwrap(); let sk_expanded = MLKEM512PrivateKeyExpanded::from(&sk); - // create a bunch of ciphertexts to decaps + // Create a bunch of ciphertexts to decaps let mut cts = [[0u8; MLKEM512_CT_LEN]; NUM_ELEMS]; for i in 0..NUM_ELEMS { - // create each ct with a unique nonce + // Create each ct with a unique nonce // encaps_internal() returns (ss, ct) ... we only want ct, hence the ".1" cts[i].copy_from_slice(&MLKEM512::encaps_internal(&pk, None, [i as u8; MLKEM_RND_LEN]).1); } @@ -355,10 +355,10 @@ fn bench_mlkem_decaps_with_expanded_key(c: &mut Criterion) { let (pk, sk) = MLKEM768::keygen_from_seed(&seed).unwrap(); let sk_expanded = MLKEM768PrivateKeyExpanded::from(&sk); - // create a bunch of ciphertexts to decaps + // Create a bunch of ciphertexts to decaps let mut cts = [[0u8; MLKEM768_CT_LEN]; NUM_ELEMS]; for i in 0..NUM_ELEMS { - // create each ct with a unique nonce + // Create each ct with a unique nonce // encaps_internal() returns (ss, ct) ... we only want ct, hence the ".1" cts[i].copy_from_slice(&MLKEM768::encaps_internal(&pk, None, [i as u8; MLKEM_RND_LEN]).1); } @@ -377,10 +377,10 @@ fn bench_mlkem_decaps_with_expanded_key(c: &mut Criterion) { let (pk, sk) = MLKEM1024::keygen_from_seed(&seed).unwrap(); let sk_expanded = MLKEM1024PrivateKeyExpanded::from(&sk); - // create a bunch of ciphertexts to decaps + // Create a bunch of ciphertexts to decaps let mut cts = [[0u8; MLKEM1024_CT_LEN]; NUM_ELEMS]; for i in 0..NUM_ELEMS { - // create each ct with a unique nonce + // Create each ct with a unique nonce // encaps_internal() returns (ss, ct) ... we only want ct, hence the ".1" cts[i].copy_from_slice(&MLKEM1024::encaps_internal(&pk, None, [i as u8; MLKEM_RND_LEN]).1); } @@ -401,8 +401,8 @@ fn bench_mlkem_decaps_with_expanded_key(c: &mut Criterion) { fn bench_mlkem_decaps_from_seed(c: &mut Criterion) { let mut group = c.benchmark_group("decaps_from_seed"); - // set up the seeds outside of the timing loop - // Doing different seeds so that the CPU doesn't cache them or do too much branch prediction + // Set up the seeds outside of the timing loop + // Doing different seeds so that the CPU does not cache them or do too much branch prediction let seed = KeyMaterial512::from_bytes_as_type( &hex::decode( "000102030405060708090a0b0c0d0e0f @@ -420,10 +420,10 @@ fn bench_mlkem_decaps_from_seed(c: &mut Criterion) { /*** ML-KEM-512 ***/ let (pk, _sk) = MLKEM512::keygen_from_seed(&seed).unwrap(); - // create a bunch of ciphertexts to decaps + // Create a bunch of ciphertexts to decaps let mut cts = [[0u8; MLKEM512_CT_LEN]; NUM_ELEMS]; for i in 0..NUM_ELEMS { - // create each ct with a unique nonce + // Create each ct with a unique nonce // encaps_internal() returns (ss, ct) ... we only want ct, hence the ".1" cts[i].copy_from_slice(&MLKEM512::encaps_internal(&pk, None, [i as u8; MLKEM_RND_LEN]).1); } @@ -441,10 +441,10 @@ fn bench_mlkem_decaps_from_seed(c: &mut Criterion) { /*** ML-KEM-768 ***/ let (pk, _sk) = MLKEM768::keygen_from_seed(&seed).unwrap(); - // create a bunch of ciphertexts to decaps + // Create a bunch of ciphertexts to decaps let mut cts = [[0u8; MLKEM768_CT_LEN]; NUM_ELEMS]; for i in 0..NUM_ELEMS { - // create each ct with a unique nonce + // Create each ct with a unique nonce // encaps_internal() returns (ss, ct) ... we only want ct, hence the ".1" cts[i].copy_from_slice(&MLKEM768::encaps_internal(&pk, None, [i as u8; MLKEM_RND_LEN]).1); } @@ -462,10 +462,10 @@ fn bench_mlkem_decaps_from_seed(c: &mut Criterion) { /*** ML-KEM-1024 ***/ let (pk, _sk) = MLKEM1024::keygen_from_seed(&seed).unwrap(); - // create a bunch of ciphertexts to decaps + // Create a bunch of ciphertexts to decaps let mut cts = [[0u8; MLKEM1024_CT_LEN]; NUM_ELEMS]; for i in 0..NUM_ELEMS { - // create each ct with a unique nonce + // Create each ct with a unique nonce // encaps_internal() returns (ss, ct) ... we only want ct, hence the ".1" cts[i].copy_from_slice(&MLKEM1024::encaps_internal(&pk, None, [i as u8; MLKEM_RND_LEN]).1); } diff --git a/crypto/mlkem/src/aux_functions.rs b/crypto/mlkem/src/aux_functions.rs index 0d331ef..afd7044 100644 --- a/crypto/mlkem/src/aux_functions.rs +++ b/crypto/mlkem/src/aux_functions.rs @@ -97,9 +97,9 @@ pub(crate) fn sample_ntt(rho: &[u8; 32], nonce: &[u8; 2]) -> Polynomial { // 3: 𝑗 ← 0 let mut j = 0usize; - // SHAKE is fairly inefficient if you just squeeze 3 bytes at a time, so we'll do a block. - // size doesn't really matter, so long as it's a multiple of 3. - // 288 seemed to be the sweet spot from playing with benchmarks + // SHAKE is fairly inefficient if you just squeeze 3 bytes at a time, therefore a block is squeezed here. + // Size is not an important factor, so long as it's a multiple of 3. + // 288 seemed to be the sweet spot according to the benchmarks // It's probably around the average rejection rate, and 216 is a multiple of both 3 (required for this alg) // and 8 (efficient for SHAKE). let mut C = [0u8; 216]; @@ -297,8 +297,10 @@ pub(crate) fn barrett_reduce(a: i16) -> i16 { a - (((t as i32) * q as i32) as i16) } -// not currently used, but I'll leave it here because it's useful for debugging if you want to output values -// that are normalized to [0,q] to compare against intermediate results from other libraries. + +// Not currently used. It is left here as a reference since it's useful for debugging if it's +// necessary to output values that are normalized to [0,q] to compare against intermediate results +// from other libraries. // pub(super) fn cond_sub_q(a: i16) -> i16 { // let tmp = a - q; // tmp + ((tmp >> 15) & q) diff --git a/crypto/mlkem/src/lib.rs b/crypto/mlkem/src/lib.rs index 4de7d42..aa84425 100644 --- a/crypto/mlkem/src/lib.rs +++ b/crypto/mlkem/src/lib.rs @@ -115,13 +115,13 @@ //! | ML-KEM-1024_expanded | 1568 | 10272 | 3168 | 12418 | //! //! All values are in bytes. The "in memory" sizes are measured by rust's `std::mem::size_of`. -//! Values in parentheses are the usual sizes in our un-optimized implementation in the \[bouncycastle_mldsa] crate. +//! Values in parentheses are the usual sizes in the un-optimized implementation in the \[bouncycastle_mldsa] crate. //! //! # Security //! All functionality exposed by this crate is considered secure to use. //! In other words, this crate does not contain any "hazmat" except for the obvious points about //! handling your private keys properly: if you post your private key to github, or you generate -//! production keys from a weak seed, I can't help you, that's on you. +//! production keys from a weak seed, that use is unsupported //! It is worth mentioning, however, that if using a [MLKEM::keygen_from_seed], then it is your //! responsibility to ensure that the seed is cryptographically random and unpredictable. //! And also that [MLKEM::encaps_internal] requires you to provide the randomness, so the ciphertext @@ -133,8 +133,8 @@ //! constructions. That should give this implementation reasonably good resistance to timing and //! power analysis key extraction attacks, however: A) this is a "best-effort" and not formally verified, //! and B) the Rust compiler does not guarantee constant-time behaviour no matter how clever your code, -//! so like all Safe Rust code (ie Rust code that does not include inline assembly), we are at the mercy -//! of the Rust compiler's optimizer for whether our bitshift-and-xor code actually remains +//! so like all Safe Rust code (ie Rust code that does not include inline assembly), the Rust compiler's optimizer +//! determines whether the bitshift-and-xor code actually remains //! constant-time after compilation. #![no_std] @@ -143,13 +143,13 @@ #![allow(incomplete_features)] // needed because currently generic_const_exprs is experimental #![feature(generic_const_exprs)] #![feature(adt_const_params)] -// These are because I'm matching variable names exactly against FIPS 204, for example both 'K' and 'k', +// These are because variable names are matched exactly against FIPS 204, for example both 'K' and 'k', // or 'A' and 'a' are used and have specific meanings. // But need to tell the rust linter to not care. #![allow(non_snake_case)] #![allow(non_upper_case_globals)] -// so I can use private traits to hide internal stuff that needs to be generic within the -// MLKEM implementation, but I don't want accessed from outside, such as FIPS-internal functions. +// so private traits can hide internal items that need to be generic within the +// MLKEM implementation, but should not be accessed from outside, such as FIPS-internal functions. #![allow(private_bounds)] // imports needed just for docs diff --git a/crypto/mlkem/src/matrix.rs b/crypto/mlkem/src/matrix.rs index 100d6e0..06ffa0a 100644 --- a/crypto/mlkem/src/matrix.rs +++ b/crypto/mlkem/src/matrix.rs @@ -1,4 +1,4 @@ -//! These are somewhat unnecessary wrappers around simple arrays, but they are helpful to me in clearly +//! These are somewhat unnecessary wrappers around simple arrays, but they are helpful for clearly //! keeping the types and sizes obvious. use core::ops::{Index, IndexMut}; @@ -79,7 +79,8 @@ impl Matrix { // Matrix and Vector do not need to impl Secret because the actual data is in the polynomials, which have their own zeroizing drop. // Technically all matrices and some vectors are only part of the public key and might not need to be zeroized, -// but I'll leave it zeroizing for now and leave this as a potential future optimization. +// but this is left zeroizing for now. +// TODO: Investigate potential optimization related to the above. #[derive(Clone)] pub(crate) struct Vector { @@ -113,7 +114,7 @@ impl Vector { /// Add another vector to this vector pub(crate) fn add_vector_ntt(&mut self, s: &Self) { for i in 0..k { - // perform montgomery addition of each polynomial in the vector + // perform Montgomery addition of each polynomial in the vector self[i].add(&s[i]); } } @@ -126,9 +127,8 @@ impl Vector { let w1 = polynomial::base_mult_montgomery(&self[i], &v[i]); w.add(&w1); } - // in theory, we need this here, but all unit tests pass without it since - // it actually doesn't matter if you go outside the [0, q] range as long as you - // reduce down before encoding out. + // Note: This function DOES NOT perform modular reduction, as the current + // construction of ML-KEM only reduces modulo q when it's necessary. // w.poly_reduce(); w @@ -169,7 +169,12 @@ impl Vector { // each of the N i16's will take dv bits debug_assert_eq!(out.len(), k * (N * (du as usize) / 8)); - // bc-java has a conditional_sub_q() here, but I pass all unit tests without it, so I'm taking it out for performance. + // No conditional_sub_q needed (as done in bc-java): callers must reduce() first, + // so coefficients are in [0, q) (barrett_reduce, floor variant). The Compress mask `& (2^du - 1)` folds + // mod q, so values in [q, 2q) would also be correct. WARNING: the `as u32` cast + // below REQUIRES non-negative coefficients. That is to say DO NOT switch barrett_reduce to a + // signed/centered variant (e.g. pq-crystals' rounded form) without restoring a + // reduction here, or this will silently produce garbage. // let mut s = self.clone(); // s.conditional_sub_q(); diff --git a/crypto/mlkem/src/mlkem.rs b/crypto/mlkem/src/mlkem.rs index bd31e43..5f480ab 100644 --- a/crypto/mlkem/src/mlkem.rs +++ b/crypto/mlkem/src/mlkem.rs @@ -71,7 +71,7 @@ //! KeyType::Seed, //! ).unwrap(); //! -//! // for this demo, we do need to run keygen only to get the public key +//! // for this demo, it is necessary to run keygen only to get the public key //! let (pk, _sk) = MLKEM768::keygen_from_seed(&seed).unwrap(); //! //! // Create the shared secret and ciphertext using the public key @@ -388,7 +388,8 @@ impl< // 2: 𝑁 ← 0 // Note: in the definition of PRF_eta on page 18, it's said to be one byte. - // since the number of loops here is static; we can hard-code the N values rather than using a counter + // since the number of loops here is static, it is possible to hard-code the N values + // rather than using a counter // 8: for (𝑖 ← 0; 𝑖 < π‘˜; 𝑖++) // β–· generate 𝐬 ∈ (β„€256)^k @@ -436,7 +437,7 @@ impl< // 19: ekPKE ← ByteEncode12(𝐭)β€–πœŒ β–· run ByteEncode12 π‘˜ times, then append 𝐀-seed // 20: dkPKE ← ByteEncode12(𝐬)Μ‚ β–· run ByteEncode12 π‘˜ times - // Note: I'm skipping the encoding at this stage and leaving it expanded for future efficiency when it's used. + // Note: The encoding is skipped at this stage and left expanded for future efficiency when it's used. // 21: return (ekPKE, dkPKE) (PK::new(t_hat, rho), s_hat) } @@ -449,7 +450,8 @@ impl< /// Output: ciphertext 𝑐 ∈ 𝔹32(π‘‘π‘’π‘˜+𝑑𝑣). fn pke_encrypt(ek: &PK, A_hat: &Matrix, m: [u8; 32], r: &[u8; 32]) -> [u8; CT_LEN] { // 1: 𝑁 ← 0 - // since the number of loops here is static; we can hard-code the N values rather than using a counter + // since the number of loops here is static, it is possible to hard-code the N values + // rather than using a counter // 2: 𝐭 ← ByteDecode12(ekPKE[0 ∢ 384π‘˜]) // 3: 𝜌 ← ekPKE[384π‘˜ ∢ 384π‘˜ + 32] @@ -457,7 +459,7 @@ impl< // 4: for (𝑖 ← 0; 𝑖 < π‘˜; 𝑖++) // β–· re-generate matrix 𝐀 ∈ (β„€256_π‘ž )π‘˜Γ—π‘˜ sampled in Alg. 13 - // We're doing an optimization where the user can pre-expand A_hat within the + // An optimization is done where the user can pre-expand A_hat within the // public key object for faster repeated encapsulations against this public key. // 9: for (𝑖 ← 0; 𝑖 < π‘˜; 𝑖++) @@ -518,8 +520,8 @@ impl< /// Output: shared secret key 𝐾 ∈ 𝔹32 . /// Output: ciphertext 𝑐 ∈ 𝔹32(π‘‘π‘’π‘˜+𝑑𝑣). /// - /// This function also takes an Option for the public matrix A. - /// If you don't know what it is, just provide None. + /// This function also takes an Option for the public matrix `A`. + /// If `A` is not known, `None` may be provided. /// This is to enable performance /// optimizations when the same public key is used for multiple encapsulations and the intermediate /// value called the public matrix A_hat can be re-used for multiple encapsulations. @@ -534,11 +536,14 @@ impl< /// randomness (which is the message `m` to be encrypted by the underlying PKE scheme). /// This function should not be used directly unless you really have a /// good reason. [KEM::encaps] should be used in 99.9% of cases. - /// The reason this is exposed publicly is: A) for unit testing that requires access - /// to the deterministically reproducible function, and B) for operational environments - /// that wish to provide randomness from their own source instead of the built-in RNG in bc-rust. - /// If you think you will be clever and invent some scheme that uses a deterministic KEM, - /// then you will almost certainly end up with security problems. Please don't do this. + /// The reason this is exposed publicly is: + /// A) for unit testing that requires access to the deterministically reproducible function, and + /// B) for operational environments that wish to provide randomness from their own source instead + /// of the built-in RNG in bc-rust. + /// As a reminder, any deterministic KEM (or any encryption mechanism) fails to satisfy any security + /// notion involving indistinguishability (e.g. IND-CPA, IND-CCA2, etc.). + /// Failing to use this properly will result in catastrophic vulnerabilities. + /// Please don't do it. pub fn encaps_internal( ek: &PK, A_hat: Option<&Matrix>, @@ -564,8 +569,8 @@ impl< // 2: 𝑐 ← K-PKE.Encrypt(ek, π‘š, π‘Ÿ) // β–· encrypt π‘š using K-PKE with randomness π‘Ÿ // deviation from FIPS: - // To allow for pre-computing A_hat for multiple encapsulations, we will either take - // A_hat passed in, or compute it fresh. + // To allow for pre-computing A_hat for multiple encapsulations, the code either takes + // A_hat passed in, or computes it fresh. let ct = match A_hat { Some(A_hat) => Self::pke_encrypt(ek, A_hat, m, &r), None => Self::pke_encrypt(ek, &ek.A_hat(), m, &r), @@ -621,9 +626,9 @@ impl< A_hat: Option<&Matrix>, c: [u8; CT_LEN], ) -> [u8; MLKEM_SS_LEN] { - // I have tried to keep this as clean as possible for correspondence with the FIPS, - // but I have moved things around so that I can use unnamed scopes to limit how many - // stack variables are alive at the same time. + + // Structured to mirror the FIPS as closely as possible, with unnamed scopes + // used to limit the number of live stack variables at any given time. // 1: dkPKE ← dk[0 ∢ 384π‘˜] β–· extract (from KEM decaps key) the PKE decryption key // 2: ekPKE ← dk[384π‘˜ ∢ 768π‘˜ + 32] β–· extract PKE encryption key @@ -651,9 +656,9 @@ impl< // 7: 𝐾_bar ← J(𝑧‖𝑐) // Compute the rejection sampling key. - // Note to future optimizers: this needs to be computed outside of the if at line 9 below - // because if its computation is conditional on the Fujisaki-Okamoto check failing, then - // you'll have a timing difference between success and failure. + // Note to future optimizers: this needs to be computed outside of the conditional at line 9 below. + // This is because if the computation is conditional on the Fujisaki-Okamoto check failing, then + // it will result in a timing difference between success and failure. let K_bar: [u8; MLKEM_SS_LEN]; K_bar = { @@ -670,7 +675,7 @@ impl< // 8: 𝑐′ ← K-PKE.Encrypt(ekPKE, π‘šβ€², π‘Ÿβ€²) // β–· re-encrypt using the derived randomness π‘Ÿβ€² // deviation from FIPS: - // To allow for pre-computing A_hat for multiple encapsulations, we will either take + // To allow for pre-computing A_hat for multiple encapsulations, we will either take // A_hat passed in, or compute it fresh. let c_prime = match A_hat { Some(A_hat) => Self::pke_encrypt(dk.pk(), A_hat, m_prime, &r_prime), @@ -688,7 +693,7 @@ impl< /// Alternative initialization of the streaming signer where you have your private key /// as a seed and you want to delay its expansion as late as possible for memory-usage reasons. - // todo -- should we build a fully-stitched-together decaps-from-seed ... or not? + // TODO: Check whether a fully-stitched-together decaps-from-seed implementation is beneficial pub fn decaps_from_seed( seed: &KeyMaterial<64>, ct: &[u8], diff --git a/crypto/mlkem/src/mlkem_keys.rs b/crypto/mlkem/src/mlkem_keys.rs index 327878c..2acca0b 100644 --- a/crypto/mlkem/src/mlkem_keys.rs +++ b/crypto/mlkem/src/mlkem_keys.rs @@ -119,10 +119,10 @@ impl MLKEMPublicKeyTrait // FIPS 203 says: // "Specifically, ByteDecode12 converts each 12-bit - // segment of its input into an integer modulo 212 = 4096 and then reduces the result + // segment of its input into an integer modulo 2^{12} = 4096 and then reduces the result // modulo π‘ž. This is no longer a one-to-one operation. Indeed, some 12-bit segments could // correspond to an integer greater than π‘ž βˆ’ 1 = 3328 but less than 4096." - // Since we are here in the d=12 case, we can and should check that all coeffs are less than q-1 + // Since this concerns to the case d=12, it should be checked that all coeffs are less than q-1 for coeff in t_i.coeffs.iter() { if *coeff < 0 || *coeff >= q { return Err(KEMError::DecodingError("Invalid or corrupted key")); @@ -505,10 +505,10 @@ impl< // FIPS 203 says: // "Specifically, ByteDecode12 converts each 12-bit - // segment of its input into an integer modulo 212 = 4096 and then reduces the result + // segment of its input into an integer modulo 2^{12} = 4096 and then reduces the result // modulo π‘ž. This is no longer a one-to-one operation. Indeed, some 12-bit segments could // correspond to an integer greater than π‘ž βˆ’ 1 = 3328 but less than 4096." - // Since we are here in the d=12 case, we can and should check that all coeffs are less than q + // Since this concerns to the case d=12, it should be checked that all coeffs are less than q-1 for coeff in s_hat[i].coeffs.iter() { if *coeff < 0 || *coeff >= q { return Err(KEMError::DecodingError("Invalid or corrupted key")); @@ -526,8 +526,8 @@ impl< pos += 32; // This satisfies the "Decapsulation input check #3) in FIPS 203 section 7.3. - // We're doing it here on key load rather than as part of the decapsulation for performance - // because if you're doing multiple decapsulations, you only need to perform this check once. + // It is done here on key load rather than as part of the decapsulation for performance + // because if multiple decapsulations are being performed, this check needs to be done only once. if h_pk != ek.compute_hash() { return Err(KEMError::ConsistencyCheckFailed( "Corrupted private key: computed hash of ek != h_ek stored in private key", diff --git a/crypto/mlkem/src/polynomial.rs b/crypto/mlkem/src/polynomial.rs index 951cc13..274b7dd 100644 --- a/crypto/mlkem/src/polynomial.rs +++ b/crypto/mlkem/src/polynomial.rs @@ -1,4 +1,4 @@ -//! Represents a polynomial over the ML-DSA ring. +//! Represents a polynomial over the ML-KEM ring. use core::fmt; use core::fmt::{Debug, Display, Formatter}; @@ -11,9 +11,10 @@ use crate::mlkem::{N, q}; use bouncycastle_core::traits::Secret; /// A polynomial over the ML-KEM ring. -/// Dev note: this doesn't strictly need to be pub ... ie there's no good reason for a caller to use this class directly, -/// but in order to test the Debug and Display traits, you need STD, so those can't be tested from inline tests in this file -/// and the real unit tests are in a different crate, so here we are. +/// Dev note: The following structure does not necessarily need to be declared as public. +/// There is no real scenario where this function is called directly. However, in order to test the Debug and Display traits, +/// it is necessary to use STD, so those can't be tested from inline tests in this file and the real unit tests are in a different crate. +/// That's the reason why pub is used. #[derive(Clone)] pub struct Polynomial { pub(crate) coeffs: [i16; N], @@ -40,7 +41,14 @@ impl Polynomial { Self { coeffs: [0i16; N] } } - /// Create a Polynomial from the message m + /// Encodes a 32-byte message `m` into a `Polynomial`, implementing the message + /// encoding step of K-PKE.Encrypt `Decompress_1(ByteDecode_1(m))`, + /// (FIPS 203, Alg. 14). Each message bit becomes one coefficient: `Decompress_1` + /// (Β§4.2.1) maps bit `1` to `⌈q/2βŒ‰ = (q + 1) / 2 = 1665` (for `q = 3329`) and bit + /// `0` to `0`, placing a set bit at the point farthest from `0` to maximize the + /// decryption noise margin. The mapping is computed branchlessly (constant-time) + /// via a bit-derived all-ones / all-zeros mask, and bits are read LSB-first. This + /// is the exact inverse of [`to_msg`]. pub(crate) fn from_msg(m: [u8; 32]) -> Self { let mut w = Polynomial::new(); @@ -54,16 +62,26 @@ impl Polynomial { w } - /// Convert a Polynomial back into a message m + /// Decodes a `Polynomial` into its 32-byte message `m`, implementing the message + /// recovery step of K-PKE.Decrypt `ByteEncode_1(Compress_1(self))`, + /// (FIPS 203, Alg. 15). Each coefficient yields one message bit: `Compress_1` + /// (Β§4.2.1) sets the bit when the coefficient lies nearer `q/2` than `0`, i.e. in + /// the central interval `[833, 2496]` for `q = 3329`. The decision is computed + /// branchlessly and the bits are packed LSB-first. + /// Coefficients are expected to already be canonical in `[0, q)`: the unsigned + /// interval test is not periodic mod `q`, so the caller reduces beforehand (`poly_reduce()` + /// in `pke_decrypt`) and no reduction is repeated here. pub(crate) fn to_msg(self) -> [u8; 32] { - const LOWER: i32 = q as i32 >> 2; // 832 - const UPPER: i32 = q as i32 - LOWER; // 2497 + const LOWER: i32 = q as i32 >> 2; // ⌊q/4βŒ‹ = 832 + const UPPER: i32 = q as i32 - LOWER; // q - ⌊q/2βŒ‹ = 2497 let mut msg = [0u8; 32]; - // you would expect to use a full reduce() here, but since this is data coming from - // out matrix math and not from an attacker, we can get away with the lighter cond_sub_q(). - // Actually; further testing against the bc-test-data set of KATs shows that everything passes even with nothing + // Using full reduce() might be expected here. + // However, this function is only called by pke_decrypt (see mlkem.rs), which performs a + // reduction on every coefficient of the polynomial immediately prior to the call. + // For completeness, testing against the bc-test-data set of KATs shows that everything passes + // without modular reduction. // self.cond_sub_q(); // for (i, item) in msg.iter_mut().enumerate().take(N/8) { @@ -78,8 +96,9 @@ impl Polynomial { msg } - // not currently used, but I'll leave it here because it's useful for debugging if you want to output values - // that are normalized to [0,q] to compare against intermediate results from other libraries. + // Not currently used. It is left here as a reference since it's useful for debugging if it's + // necessary to output values that are normalized to [0,q] to compare against intermediate results + // from other libraries. // pub(crate) fn conditional_add_q(&mut self) { // for x in self.0.iter_mut() { // *x = conditional_add_q(*x); @@ -123,14 +142,17 @@ impl Polynomial { // make sure we have received a dv debug_assert!(dv == 4 || dv == 5); - // make sure we were given the right size output buffer + // make sure the right size output buffer is given // each of the N i16's will take dv bits debug_assert_eq!(out.len(), N * (dv as usize) / 8); let mut t = [0u8; 8]; let mut idx = 0; - // bc-java has a cond_sub_q() here, but unit tests show that we don't need it. + // bc-java has a cond_sub_q() here, however, it is not needed + // The reason for this is because a modular reduction is performed immediately + // prior to calling pack_ciphertext in mlkem.rs + // This can be corroborated by running the corresponding unit tests // let mut s = self.clone(); // s.cond_sub_q(); @@ -173,7 +195,7 @@ impl Polynomial { } /// This is an optimized version of - /// Decompress_𝑑𝑣( ByteDecode_𝑑𝑣(𝑐2) ) + /// Decompress_𝑑𝑣( ByteDecode_𝑑𝑣(𝑐2) ) /// which unpacks a single polynomial according to the packing coefficient dv pub(crate) fn decompress_poly(compressed_v: &[u8]) -> Polynomial { // make sure we have received a dv @@ -223,8 +245,9 @@ impl Polynomial { v } - // not currently used, but I'll leave it here because it's useful for debugging if you want to output values - // that are normalized to [0,q] to compare against intermediate results from other libraries. + // Not currently used. It is left here as a reference since it's useful for debugging if it's + // necessary to output values that are normalized to [0,q] to compare against intermediate results + // from other libraries. // pub(crate) fn cond_sub_q(&mut self) { // for i in 0..N { // self[i] = cond_sub_q(self[i]); @@ -263,7 +286,7 @@ impl Polynomial { /// Output: array 𝑓 ∈ β„€256 β–· the coefficients of the inverse NTT of the input pub(crate) fn inv_ntt(&mut self) { // FIPS 203 ALg 10 wants you to copy f_hat into f, and then act of f - // but we're going to do this in-place for memory-saving reasons. + // but here it is performed in-place in order to optimize memory usage. let mut len = 2; let mut k = 0; @@ -346,8 +369,9 @@ impl Display for Polynomial { } } -// Not currently used, but I'll leave it here because it's useful for debugging if you want to output values -// that are normalized to [0,q] to compare against intermediate results from other libraries. +// Not currently used. It is left here as a reference since it's useful for debugging if it's +// necessary to output values that are normalized to [0,q] to compare against intermediate results +// from other libraries. // /// if a is in \[-q..0], then it shifts it up by q to be in \[0..q] // pub(crate) fn conditional_add_q(a: i16) -> i16 { // a + ((a >> 15) & q) diff --git a/crypto/mlkem/tests/mlkem_key_tests.rs b/crypto/mlkem/tests/mlkem_key_tests.rs index 7317d50..66b54a8 100644 --- a/crypto/mlkem/tests/mlkem_key_tests.rs +++ b/crypto/mlkem/tests/mlkem_key_tests.rs @@ -35,13 +35,13 @@ mod mlkem_key_tests { let expected_pk_bytes: [u8; MLKEM512_PK_LEN] = hex::decode("3995815e597d104355cf29aa5333c93251869d5bcdbe487124f602b8b6a66c16c4761648ad765cf5d8006b515e905a7f0ac076b0c62efa328153e7ca5701699f1305f1e6bc6f90b0e49b693512b6ce992a8b8016ddfc1a662c7e3f9619cbd869dd771af30896ccd5918ac6cb77466c5e779996d67ff9aabc97503f2c7b7e2d000d86450fb1807ca4cabda465825a31c789a1b7a491ab3872765d320d0b71920fa213c94093416b83b8124e69f65e62cb5000dcc37aa9a0fff73970c4772f357d24189ca6f5305568c0e2376a3762a68c605e563c5d209572e0fc7532ca294729535567b5fc413c5e8792d2464536cc808f98add74664f141566f9016a90a541829a98a0464ce41a8bb44c2d4fa3c2c209460728ef14a1a7c4c9b98d12203b4cc3529160a9ab2d7838f7ff6b53ae05aa31a7d646b7afa6c45932526a3c3755619be994c211c2a31c05b3447836cb2150be1829dae6b04c5535cff546e392ba797411720f924f490a5ac5495f21356d550b782a64c1688b6b655bcc7842197a434c2f6563b5b7f09a78bcc488232783561d16f4cbab6755400050781570c66604b817ad1252294736e8b01861a4b5a74519b8b6fe51489a5072392e587626c713776575d33806a1c8e2732af97c2680f51666331c4eb8bbc0431c4f96832daf1b3c45528fba153f6c78b1c198702947ccd337727a46fb53ba11de5cb4191346859516cb6ad72400f3cf209b236aef35a580ac87eb3e30fafd66973ca8a7dd2675af41f7a17b61433cd1af80f7708869f665488497980b1ac10a0cdcb636a00ed8681b35e429124ca80350725b85f83a5eac3a4a3cc1600903e65293560b9b336e5af0d529dac1a048119302cb7a9bcc110b94851bf02117f199dc485a852b7473f09b831a6831d5b54c0b790d225cf6bb92d9462a26cdb33dda5123c7aaf0e26a0b83655eea28bf3a8074725018fd6bae4b601cf61baab71a7a3d35197a343e74b4a272c125d540896426d85b7958d3b38a6ba987ec37225c7b44cdb12dde4539b4ab082363683f04bf7a09cc5c41dfe830a1b162e0b324334362f084a14467723344badd000f8d8c537c48f998f05307cebd1ede0b81c3bc59a065a1b6d63b26c").unwrap() .try_into().unwrap(); - // Decode and re-encode the sk, make sure you get the same thing + // Decode and re-encode the sk, ensure the output is the same let sk = MLKEM512PrivateKey::from_bytes(&expected_sk_bytes).unwrap(); let sk_bytes = sk.encode(); assert_eq!(sk_bytes.len(), expected_sk_bytes.len()); assert_eq!(sk_bytes, expected_sk_bytes.as_slice()); - // Decode and re-encode the pk, make sure you get the same thing + // Decode and re-encode the pk, ensure the output is the same let decoded_pk = MLKEM512PublicKey::from_bytes(&expected_pk_bytes).unwrap(); let pk_bytes = decoded_pk.encode(); assert_eq!(pk_bytes.len(), expected_pk_bytes.len()); @@ -54,9 +54,10 @@ mod mlkem_key_tests { #[test] fn test_ek_hash() { // three separate tests here: - // 1) does it calculate H(ek) properly from a public key? - // 2) does it calculate H(ek) properly from a private key? - // 3) does it reject a private key if the H(ek) is wrong? + // 1) whether it calculates H(ek) properly from a public key + // 2) whether it calculates H(ek) properly from a private key + // 3) whether it rejects a private key if the H(ek) is wrong + let seed = KeyMaterial512::from_bytes_as_type( &hex::decode( @@ -80,10 +81,12 @@ mod mlkem_key_tests { .try_into() .unwrap(); + // 1) test whether it calculates H(ek) properly from a public key assert_eq!(pk.compute_hash(), expected_h_ek); + // 2) test whether it calculates H(ek) properly from a private key assert_eq!(sk.pk_hash(), &expected_h_ek); - // 3) does it reject a private key if the H(ek) is wrong? + // 3) test whether it rejects a private key if the H(ek) is wrong let mut sk_bytes: [u8; MLKEM512_SK_LEN] = sk.encode(); // h is: // dk[768π‘˜ + 32 ∢ 768π‘˜ + 64] @@ -204,7 +207,7 @@ mod mlkem_key_tests { // FIPS 203 says: // " Indeed, some 12-bit segments could // correspond to an integer greater than π‘ž βˆ’ 1 = 3328 but less than 4096." - // so let's test these conditions in both private key s_hat and public key t_hat + // Test these conditions in both private key s_hat and public key t_hat match MLKEM512PrivateKey::from_bytes(&[255u8; MLKEM512_SK_LEN]) { Err(KEMError::DecodingError(_)) => { /* good */ } diff --git a/crypto/mlkem/tests/mlkem_tests.rs b/crypto/mlkem/tests/mlkem_tests.rs index c24abd2..8d20b67 100644 --- a/crypto/mlkem/tests/mlkem_tests.rs +++ b/crypto/mlkem/tests/mlkem_tests.rs @@ -47,7 +47,7 @@ mod mlkem_tests { } /// This runs the full bitflipping tests and takes about 30s.. - /// I'm leaving this commented out, but feel free to un-comment it and run it. + /// This is left out of the testing suite, but it can be included if so desired // #[test] // fn test_framework_kem_extensive() { // use bouncycastle_core_test_framework::kem::{TestFrameworkKEM}; @@ -262,7 +262,7 @@ mod mlkem_tests { let expected_pk_bytes: [u8; MLKEM1024_PK_LEN] = hex::decode("A04184D4BC7B532A0F70A54D7757CDE6175A6843B861CB2BC4830C0012554CFC5D2C8A2027AA3CD967130E9B96241B11C4320C7649CC23A71BAFE691AFC08E680BCEF42907000718E4EACE8DA28214197BE1C269DA9CB541E1A3CE97CFADF9C6058780FE6793DBFA8218A2760B802B8DA2AA271A38772523A76736A7A31B9D3037AD21CEBB11A472B8792EB17558B940E70883F264592C689B240BB43D5408BF446432F412F4B9A5F6865CC252A43CF40A320391555591D67561FDD05353AB6B019B3A08A73353D51B6113AB2FA51D975648EE254AF89A230504A236A4658257740BDCBBE1708AB022C3C588A410DB3B9C308A06275BDF5B4859D3A2617A295E1A22F90198BAD0166F4A943417C5B831736CB2C8580ABFDE5714B586ABEEC0A175A08BC710C7A2895DE93AC438061BF7765D0D21CD418167CAF89D1EFC3448BCBB96D69B3E010C82D15CAB6CACC6799D3639669A5B21A633C865F8593B5B7BC800262BB837A924A6C5440E4FC73B41B23092C3912F4C6BEBB4C7B4C62908B03775666C22220DF9C88823E344C7308332345C8B795D34E8C051F21F5A21C214B69841358709B1C305B32CC2C3806AE9CCD3819FFF4507FE520FBFC27199BC23BE6B9B2D2AC1717579AC769279E2A7AAC68A371A47BA3A7DBE016F14E1A727333663C4A5CD1A0F8836CF7B5C49AC51485CA60345C990E06888720003731322C5B8CD5E6907FDA1157F468FD3FC20FA8175EEC95C291A262BA8C5BE990872418930852339D88A19B37FEFA3CFE82175C224407CA414BAEB37923B4D2D83134AE154E490A9B45A0563B06C953C3301450A2176A07C614A74E3478E48509F9A60AE945A8EBC7815121D90A3B0E07091A096CF02C57B25BCA58126AD0C629CE166A7EDB4B33221A0D3F72B85D562EC698B7D0A913D73806F1C5C87B38EC003CB303A3DC51B4B35356A67826D6EDAA8FEB93B98493B2D1C11B676A6AD9506A1AAAE13A824C7C08D1C6C2C4DBA9642C76EA7F6C8264B64A23CCCA9A74635FCBF03E00F1B5722B214376790793B2C4F0A13B5C40760B4218E1D2594DCB30A70D9C1782A5DD30576FA4144BFC8416EDA8118FC6472F56A979586F33BB070FB0F1B0B10BC4897EBE01BCA3893D4E16ADB25093A7417D0708C83A26322E22E6330091E30152BF823597C04CCF4CFC7331578F43A2726CCB428289A90C863259DD180C5FF142BEF41C7717094BE07856DA2B140FA67710967356AA47DFBC8D255B4722AB86D439B7E0A6090251D2D4C1ED5F20BBE6807BF65A90B7CB2EC0102AF02809DC9AC7D0A3ABC69C18365BCFF59185F33996887746185906C0191AED4407E139446459BE29C6822717644353D24AB6339156A9C424909F0A9025BB74720779BE43F16D81C8CC666E99710D8C68BB5CC4E12F314E925A551F09CC59003A1F88103C254BB978D75F394D3540E31E771CDA36E39EC54A62B5832664D821A72F1E6AFBBA27F84295B2694C498498E812BC8E9378FE541CEC5891B25062901CB7212E3CDC46179EC5BCEC10BC0B9311DE05074290687FD6A5392671654284CD9C8CC3EBA80EB3B662EB53EB75116704A1FEB5C2D056338532868DDF24EB8992AB8565D9E490CADF14804360DAA90718EAB616BAB0765D33987B47EFB6599C5563235E61E4BE670E97955AB292D9732CB8930948AC82DF230AC72297A23679D6B94C17F1359483254FEDC2F05819F0D069A443B78E3FC6C3EF4714B05A3FCA81CBBA60242A7060CD885D8F39981BB18092B23DAA59FD9578388688A09BBA079BC809A54843A60385E2310BBCBCC0213CE3DFAAB33B47F9D6305BC95C6107813C585C4B657BF30542833B14949F573C0612AD524BAAE69590C1277B86C286571BF66B3CFF46A3858C09906A794DF4A06E9D4B0A2E43F10F72A6C6C47E5646E2C799B71C33ED2F01EEB45938EB7A4E2E2908C53558A540D350369FA189C616943F7981D7618CF02A5B0A2BCC422E857D1A47871253D08293C1C179BCDC0437069107418205FDB9856623B8CA6B694C96C084B17F13BB6DF12B2CFBBC2B0E0C34B00D0FCD0AECFB27924F6984E747BE2A09D83A8664590A8077331491A4F7D720843F23E652C6FA840308DB4020337AAD37967034A9FB523B67CA70330F02D9EA20C1E84CB8E5757C9E1896B60581441ED618AA5B26DA56C0A5A73C4DCFD755E610B4FC81FF84E21").unwrap() .try_into().unwrap(); - // we're just going to keep the pk + // pk is kept here let (pk, _sk) = MLKEM1024::keygen_from_seed(&seed).unwrap(); assert_eq!(pk.encode(), expected_pk_bytes.as_slice()); @@ -371,13 +371,14 @@ mod mlkem_tests { // 2. (Decapsulation key type check) If dk is not a byte array of length 768π‘˜ + 96 for the value of // π‘˜ specified by the relevant parameter set, then input checking has failed. - // This does not need to be tested because of the static-sizing of the SK array. + // This does not need to be tested because of the static-sizing of the dk array. // 3. (Hash check) Perform the computation // test ← H(dk[384π‘˜ ∢ 768π‘˜ + 32])) (7.2) // If test β‰  dk[768π‘˜ + 32 ∢ 768π‘˜ + 64], then input checking has failed. - // Sure, let's test a busted ek component of the sk - // This is actually caught on loading the sk, not on decaps, which is better for performance + + // Test the procedure with portions of dk corresponding to ek that have been corrupted + // This is actually caught on loading the dk, not on decaps, which is better for performance // since you might do many decaps's on one key, and that should be fine for FIPS? let (pk1, sk) = MLKEM512::keygen().unwrap(); diff --git a/crypto/rng/src/hash_drbg80090a.rs b/crypto/rng/src/hash_drbg80090a.rs index 431d036..278bd0e 100644 --- a/crypto/rng/src/hash_drbg80090a.rs +++ b/crypto/rng/src/hash_drbg80090a.rs @@ -61,13 +61,14 @@ impl HashDRBG80090AParams for HashDRBG80090AParams_SHA512 { const RESEED_INTERVAL: u64 = 1u64 << 48; // 2^48 requests } -// TODO: is there a rustacious way to extract this from HASH? +// TODO: is there a rust way to extract this from HASH? const LARGEST_HASHER_OUTPUT_LEN: usize = 64; #[allow(private_bounds)] /// Implementation of the Hash_DRBG algorithm as specified in NIST SP 800-90Ar1. pub struct HashDRBG80090A { - // Rust is stupid. What's the point of having a generic parameter if we can't use constants inside it? + // Ideally `state: WorkingState`, but stable Rust can't use an associated const + // of a generic type parameter as a const-generic argument // state: WorkingState, state: WorkingState, admin_info: AdministrativeInfo, @@ -194,7 +195,7 @@ impl Sp80090ADrbg for HashDRBG80090A { )); } - // todo: take this out once supported + // TODO: take this out once supported if prediction_resistance { todo!("Prediction resistance is not yet supported by Hash_DRBG80090A.") } @@ -219,7 +220,8 @@ impl Sp80090ADrbg for HashDRBG80090A { "Provided seed exceeds the maximum seed length.", ))?; } - // On purpose not checking the SecurityStrength field of the seed, because we assume it's pure entropy and hasn't been touched by any actual algoritms yet. + // On purpose not checking the SecurityStrength field of the seed, + // because we assume it's pure entropy and hasn't been touched by any actual algoritms yet. if security_strength > H::MAX_SECURITY_STRENGTH { return Err(KeyMaterialError::SecurityStrength( "Requested security strength exceeds the maximum strength that this DRBG instance can provide.", @@ -517,7 +519,8 @@ impl RNG for HashDRBG80090A { /// the hash_df function as defined in SP 800-90Ar1 section 10.3.1. /// no_of_bits_to_return is the length of the provided output buffer. -/// Because array concatenation is not available in a no_std / no_alloc build, this takes many input parameters. To leave a parameter unused, simply provide an empty array &[0u8;0] +/// Because array concatenation is not available in a no_std / no_alloc build, this takes many input parameters. +// To leave a parameter unused, simply provide an empty array &[0u8;0] fn hash_df( in1: &[u8], in2: &[u8], @@ -527,7 +530,7 @@ fn hash_df( ) { // Note: all lengths here are in bytes, whereas the spec uses bits. - // // I'm gonna panic! here because this is private and shouldn't get into weird inputs. + // The implementation panic! here because this is private and shouldn't get into weird inputs. if out.len() > 255 * H::OUTPUT_LEN { panic!("hash_df can't produce that much output!") } @@ -539,8 +542,8 @@ fn hash_df( let len = u32::div_ceil(out.len() as u32, H::OUTPUT_LEN as u32); let mut counter: u8 = 0x01; - // note: this could probably be performance optimized a tiny bit by pulling no_of_bits_to_return.to_le_bytes() out of the loop - // and by merging i and counter into the same variable. + // note: this could probably be performance optimized a tiny bit by pulling no_of_bits_to_return.to_le_bytes() + // out of the loop and by merging i and counter into the same variable. for i in 1..len { let mut h = H::default(); h.do_update(&counter.to_le_bytes()); @@ -555,7 +558,8 @@ fn hash_df( } // Handle the last block separately since not all of it will fit in the output buffer. - // First, do we even need to do a last block, or was the requested number of bits already a multiple of the output length? + // TODO: Check whether it is necessary to do a last block, + // or was the requested number of bits already a multiple of the output length let bytes_written = (len - 1) as usize * H::OUTPUT_LEN; let remainder = out.len() - bytes_written; if remainder != 0 { @@ -567,7 +571,6 @@ fn hash_df( h.do_update(in3); h.do_update(in4); - // I don't understand rust // let mut temp = [0u8; H::OUTPUT_LEN]; let mut temp = [0u8; 64]; h.do_final_out(&mut temp); @@ -662,11 +665,11 @@ fn hashgen(v: &[u8], out: &mut [u8]) { } // Handle the last block separately since not all of it will fit in the output buffer. - // First, do we even need to do a last block, or was the requested number of bits already a multiple of the output length? + // TODO: Check whether it is necessary to do a last block, + // or was the requested number of bits already a multiple of the output length let bytes_written = (m - 1) as usize * H::OUTPUT_LEN; let remainder = out.len() - bytes_written; if remainder != 0 { - // I don't understand rust // let mut temp = [0u8; H::OUTPUT_LEN]; let mut temp = [0u8; 64]; H::default().hash_out(&data, &mut temp); diff --git a/crypto/rng/src/lib.rs b/crypto/rng/src/lib.rs index dbe62fb..a4dccab 100644 --- a/crypto/rng/src/lib.rs +++ b/crypto/rng/src/lib.rs @@ -3,7 +3,7 @@ //! This crate provides the implementations of the deterministic random bit generator (DRBG) algorithms //! which, together with a strong entropy source, form the basis of cryptographic random number generation. //! -//! Here's the basic way to get some random bytes: +//! Here is the basic way to get some random bytes: //! //! ``` //! use bouncycastle_core::traits::RNG; @@ -13,7 +13,7 @@ //! ``` //! This is secure because `::default()` seeds the RNG from the OS, configured for general use. //! -//! **WARNING: most people should stop reading here and should not be mucking around with the internals of RNGs. +//! **WARNING: most people should stop reading here and should not attempt to modify the internals of RNGs. //! This crate contains dragons and other horrible things. πŸ‰πŸπŸœ** //! //! # 🚨🚨🚨Security Warning 🚨🚨🚨 @@ -60,8 +60,14 @@ pub type DefaultRNG = HashDRBG_SHA512; pub type Default128BitRNG = HashDRBG_SHA256; pub type Default256BitRNG = HashDRBG_SHA512; -/// Implements the five functions specified in SP 800-90A section 7.4 are instantate, generate, reseed, uninstantiate, and health_test. -/// Note: this function implements Rust's Drop on the sensitive working state in place of the explicit Uninstantiate function listed in SP 800-90Ar1. +/// Implements the five functions specified in SP 800-90A section 7.4 are +/// - instantate, +/// - generate, +/// - reseed, +/// - uninstantiate, and +/// - health_test. +/// Note: this function implements Rust's Drop on the sensitive working state in place of the explicit +/// Uninstantiate function listed in SP 800-90Ar1. pub trait Sp80090ADrbg { /// The input KeyMaterial must be of type [KeyType::Seed]. /// @@ -79,7 +85,8 @@ pub trait Sp80090ADrbg { /// required. /// """ /// - /// This function takes ownership of the seed KeyMaterial object, to reduce the likelihood of its reuse in a second function call. + /// This function takes ownership of the seed KeyMaterial object, + /// to reduce the likelihood of its reuse in a second function call. /// /// There is no entropy requirement on the nonce, but it is expected as a KeyMaterial so that it /// benefits from the secure erasure and logging protections in the KeyMaterial object. @@ -93,7 +100,8 @@ pub trait Sp80090ADrbg { ) -> Result<(), RNGError>; /// Reseeds the DRBG with the provided seed. - /// TODO: this needs to be thought out to take some sort of EntropySource object that'll work well with DRBGs that require frequent reseeding. + /// TODO: this needs to be redesigned to take some sort of EntropySource object that will work well + // with DRBGs that require frequent reseeding. fn reseed( &mut self, seed: &impl KeyMaterialTrait, diff --git a/crypto/rng/tests/hash_drbg80090a_tests.rs b/crypto/rng/tests/hash_drbg80090a_tests.rs index e4fd534..c08a81e 100644 --- a/crypto/rng/tests/hash_drbg80090a_tests.rs +++ b/crypto/rng/tests/hash_drbg80090a_tests.rs @@ -85,9 +85,12 @@ mod tests { _ => panic!("Expected KeyMaterialError error"), } - // Skipping tests for max lengths of seeds and personalization strings, because they're in the gigabyte range and that'll blow up the test suite. + // Skipping tests for max lengths of seeds and personalization strings + // because they are on the order of a gigabyte in size. + // Testing would blow up the test suite. - // Error case: security strength requested at init is higher than the underlying hash function's max security strength + // Error case: security strength requested at init is higher than the underlying + // hash function's max security strength let mut rng = HashDRBG_SHA256::new_unititialized(); let seed = KeyMaterial256::from_bytes_as_type(&DUMMY_SEED_512[..32], KeyType::Seed).unwrap(); @@ -96,7 +99,8 @@ mod tests { _ => panic!("Expected KeyMaterialError error"), } - // Success case: security strength requested at init is lower than the underlying hash function's max security strength + // Success case: security strength requested at init is lower than the underlying + // hash function's max security strength // ... 112 bit let mut rng = HashDRBG_SHA256::new_unititialized(); let seed = @@ -162,7 +166,10 @@ mod tests { _ => panic!("Expected KeyMaterialError error"), } - // Skipping tests for max lengths of seeds and personalization strings, because they're in the gigabyte range and that'll blow up the test suite. + // Skipping tests for max lengths of seeds and personalization strings + // because they are on the order of a gigabyte in size. + // Testing would blow up the test suite. + } #[test] @@ -197,9 +204,11 @@ mod tests { _ => panic!("Expected Uninitialized error"), } - // Skipping tests for max lengths of seeds and personalization strings, because they're in the gigabyte range and that'll blow up the test suite. + // Skipping tests for max lengths of seeds and personalization strings + // because they are on the order of a gigabyte in size. + // Testing would blow up the test suite. - // TODO: tests for ReseedRequired. How do I trigger this? The limits are in the exobyte range. + // TODO: Tests for ReseedRequired. Investigate how this gets triggered. The limits are in the exobyte range. } #[test] @@ -241,9 +250,11 @@ mod tests { _ => panic!("Expected Uninitialized error"), } - // Skipping tests for max lengths of seeds and personalization strings, because they're in the gigabyte range and that'll blow up the test suite. + // Skipping tests for max lengths of seeds and personalization strings + // because they are on the order of a gigabyte in size. + // Testing would blow up the test suite. - // TODO: tests for ReseedRequired. How do I trigger this? The limits are in the exobyte range. + // TODO: tests for ReseedRequired. Investigate how this gets triggered. The limits are in the exobyte range. } #[test] @@ -289,9 +300,11 @@ mod tests { _ => panic!("Expected Uninitialized error"), } - // Skipping tests for max lengths of seeds and personalization strings, because they're in the gigabyte range and that'll blow up the test suite. + // Skipping tests for max lengths of seeds and personalization strings + // because they are on the order of a gigabyte in size. + // Testing would blow up the test suite. - // TODO: tests for ReseedRequired. How do I trigger this? The limits are in the exobyte range. + // TODO: tests for ReseedRequired. Investigate how this gets triggered. The limits are in the exobyte range. } #[test] diff --git a/crypto/sha2/src/sha256.rs b/crypto/sha2/src/sha256.rs index 7be7f47..617cb54 100644 --- a/crypto/sha2/src/sha256.rs +++ b/crypto/sha2/src/sha256.rs @@ -154,7 +154,8 @@ pub struct SHA256Internal { byte_count: u64, x_buf: [u8; 64], x_buf_off: usize, - // TODO: should we add a maximum message size according to FIPS 180-4? (2^64 for SHA256 and 2^128 for SHA512) + // TODO: Investigate whether maximum message size (according to FIPS 180-4) should be added + // (2^64 for SHA256 and 2^128 for SHA512) } impl Drop for SHA256Internal { @@ -279,7 +280,7 @@ impl Hash for SHA256Internal { /// TODO: This is defined in FIPS 180-4 s. 5.1.2 /// TODO: - /// TODO: Could implement if there is demand. + /// TODO: It can be implemented if required #[allow(unused)] fn do_final_partial_bits( self, @@ -291,7 +292,7 @@ impl Hash for SHA256Internal { /// TODO: This is defined in FIPS 180-4 s. 5.1.2 /// TODO: - /// TODO: Could implement if there is demand. + /// TODO: It can be implemented if required #[allow(unused)] fn do_final_partial_bits_out( self, diff --git a/crypto/sha2/src/sha512.rs b/crypto/sha2/src/sha512.rs index b4ae990..541c1a9 100644 --- a/crypto/sha2/src/sha512.rs +++ b/crypto/sha2/src/sha512.rs @@ -163,7 +163,8 @@ impl Sha512State { pub struct Sha512Internal { _params: std::marker::PhantomData, state: Sha512State, - byte_count: u64, // NOTE We only support 2^67 bits, not the full 2^128 + // NOTE The code currently only supports 2^67 bits, not the full 2^128 + byte_count: u64, x_buf: [u8; 128], x_buf_off: usize, } @@ -290,7 +291,7 @@ impl Hash for Sha512Internal { /// TODO: This is defined in FIPS 180-4 s. 5.1.2 /// TODO: - /// TODO: Could implement if there is demand. + /// TODO: It can be implemented if required #[allow(unused)] fn do_final_partial_bits( self, @@ -302,7 +303,7 @@ impl Hash for Sha512Internal { /// TODO: This is defined in FIPS 180-4 s. 5.1.2 /// TODO: - /// TODO: Could implement if there is demand. + /// TODO: It can be implemented if required #[allow(unused)] fn do_final_partial_bits_out( self, diff --git a/crypto/sha3/src/keccak.rs b/crypto/sha3/src/keccak.rs index 5e6f6a8..c446b72 100644 --- a/crypto/sha3/src/keccak.rs +++ b/crypto/sha3/src/keccak.rs @@ -179,7 +179,7 @@ impl KeccakState { } } -// Mutants note: this fails because you can't write unit tests for drop() +// Mutants note: this fails because unit tests cannot be written for drop() impl Drop for KeccakState { fn drop(&mut self) { // Zeroize the contents before returning the memory to the OS. @@ -210,7 +210,9 @@ impl KeccakDigest { pub(super) fn new(size: KeccakSize) -> Self { let rate = 1600 - ((size as usize) << 1); - // todo I think this check is not needed since the fixed set of allowed sizes can't yield an invalid rate, but I'll leave this here for now. + // TODO: Verify whether this check is needed. + // The code currently skips the check since the fixed set of allowed sizes can't yield an + // invalid rate. It is, for now, left as a comment for reference. // if rate == 0 || rate >= 1600 || (rate & 63) != 0 { // return Err(HashError::InvalidLength("invalid rate value")); // } @@ -229,8 +231,8 @@ impl KeccakDigest { panic!("attempt to absorb with odd length queue"); } if self.squeezing { - // Maybe this should be an error rather than a panic? - // But, like, don't write your code to absorb after squeezing. + // TODO: check whether this should be an error rather than a panic + // It should warn the user to not absorb after squeezing. panic!("attempt to absorb while squeezing"); } diff --git a/crypto/sha3/src/lib.rs b/crypto/sha3/src/lib.rs index 42ecac4..8f944fc 100644 --- a/crypto/sha3/src/lib.rs +++ b/crypto/sha3/src/lib.rs @@ -146,7 +146,7 @@ trait SHA3Params: HashAlgParams { const SIZE: KeccakSize; } -// TODO: more elegant to macro this? +// TODO: Consider whether is better to macro this impl Algorithm for SHA3_224 { const ALG_NAME: &'static str = SHA3_224_NAME; const MAX_SECURITY_STRENGTH: SecurityStrength = SecurityStrength::_112bit; diff --git a/crypto/sha3/src/sha3.rs b/crypto/sha3/src/sha3.rs index cb22998..8ec39eb 100644 --- a/crypto/sha3/src/sha3.rs +++ b/crypto/sha3/src/sha3.rs @@ -14,7 +14,7 @@ pub struct SHA3 { kdf_entropy: usize, } -// Note: don't need a zeroizing Drop here because all the sensitive info is in KeccakDigest, which has one. +// Note: zeroizing Drop is not necessary here because all the sensitive info is in KeccakDigest, which has one. impl SHA3 { pub fn new() -> Self { @@ -71,7 +71,7 @@ impl SHA3 { ) -> Result { // For the KDF to be considered "fully-seeded" and be capable of outputting full-entropy KeyMaterials, // it requires full-entropy input that is at least block length. - // TODO: citation needed, which NIST spec did I get this from? + // TODO: citation needed (NIST) if self.kdf_entropy < PARAMS::OUTPUT_LEN { self.kdf_key_type = min(&self.kdf_key_type, &KeyType::BytesLowEntropy).clone(); self.kdf_security_strength = SecurityStrength::None; // BytesLowEntropy can't have a securtiy level. @@ -85,7 +85,7 @@ impl SHA3 { let bytes_written = self.do_final_out(output_key.mut_ref_to_bytes()?); output_key.set_key_len(bytes_written)?; - // since we've done some computation, the result will not actually be zeroized, even if all input key material was zeroized. + // since computation has been performed, the result will not actually be zeroized, even if all input key material was zeroized. if key_type == KeyType::Zeroized { key_type = KeyType::BytesLowEntropy; } @@ -141,12 +141,14 @@ impl Hash for SHA3 { output } - // todo -- why doesn't this take a &mut [u8; HASH_LEN] ? - // That's probably more user-friendly than this auto-truncating that I have here. + // TODO: investigate why this doesn't take a &mut [u8; HASH_LEN] + // Being able to do so would improve ergonomics fn do_final_out(mut self, output: &mut [u8]) -> usize { output.fill(0); - self.keccak.absorb_bits(0x02, 2).expect("do_final_out: keccak.absorb_bits failed."); // this shouldn't fail because by construction you can only enter this function once, and this is the only way to absorb partial bits. + // this shouldn't fail because, by construction, the function is only called once, + // and this is the only way to absorb partial bits. + self.keccak.absorb_bits(0x02, 2).expect("do_final_out: keccak.absorb_bits failed."); let bytes_written = if output.len() <= self.output_len() { self.keccak.squeeze(output) @@ -180,7 +182,8 @@ impl Hash for SHA3 { ) -> Result { output.fill(0); - // Mutants note: yep, this is just bit-setting into empty space, so it doesn't matter whether it's OR or XOR. + // Mutants note: This is just bit-setting into empty space. + // It works the same regardless of whether it's OR or XOR. let mut final_input: u16 = ((partial_byte as u16) & ((1 << num_partial_bits) - 1)) | (0x02 << num_partial_bits); let mut final_bits = num_partial_bits + 2; diff --git a/crypto/sha3/src/shake.rs b/crypto/sha3/src/shake.rs index 59f3bac..ae64a3a 100644 --- a/crypto/sha3/src/shake.rs +++ b/crypto/sha3/src/shake.rs @@ -28,7 +28,7 @@ pub struct SHAKE { kdf_entropy: usize, } -// Note: don't need a zeroizing Drop here because all the sensitive info is in KeccakDigest, which has one. +// Note: zeroizing Drop here is not needed because all the sensitive info is in KeccakDigest, which has one. impl Algorithm for SHAKE { const ALG_NAME: &'static str = PARAMS::ALG_NAME; @@ -82,8 +82,7 @@ impl SHAKE { mut self, additional_input: &[u8], ) -> Result, KDFError> { - // It's unfortunate to return an oversized KeyMaterial most of the time, but I've had enough - // of fighting with Rust traits for now ... + // At the moment, oversized KeyMaterial is returned for most cases. let mut output_key = KeyMaterial::<64>::new(); self.derive_key_out_final_internal(additional_input, &mut output_key)?; @@ -99,9 +98,9 @@ impl SHAKE { ) -> Result { // For the KDF to be considered "fully-seeded" and be capable of outputting full-entropy KeyMaterials, // it requires full-entropy input that is at least 2x the bit size (ie 256 bits for SHAKE128, and 512 bits for SHAKE256). - // TODO: citation needed, which NIST spec did I get this from? - // TODO: intuitivitely this makes sense since SHAKE256 and SHA3-256 are both KECCAK[512], and SHAKE128 is KECCAK[256], - // TODO: but I would rather find an actual reference for this "fully-seeded" threshold. + // TODO: citation needed (NIST) + // TODO: The intuition behind this is that SHAKE256 and SHA3-256 are both KECCAK[512], and SHAKE128 is KECCAK[256], + // TODO: However, it is necessary to find an actual reference for this "fully-seeded" threshold. if self.kdf_entropy < 2 * (PARAMS::SIZE as usize) / 8 { self.kdf_key_type = min(&self.kdf_key_type, &KeyType::BytesLowEntropy).clone(); self.kdf_security_strength = SecurityStrength::None; // BytesLowEntropy can't have a securtiy level. @@ -118,7 +117,7 @@ impl SHAKE { ); output_key.set_key_len(bytes_written)?; - // since we've done some computation, the result will not actually be zeroized, even if all input key material was zeroized. + // since computation has been performed, the result will not actually be zeroized, even if all input key material was zeroized. if self.kdf_key_type == KeyType::Zeroized { self.kdf_key_type = KeyType::BytesLowEntropy; } @@ -225,7 +224,8 @@ impl XOF for SHAKE { if !(1..=7).contains(&num_partial_bits) { return Err(HashError::InvalidLength("must be in the range [0,7]")); } - // Mutants note: yep, this is just bit-setting into empty space, so it doesn't matter whether it's OR or XOR. + // Mutants note: This is just bit-setting into empty space. + // It works the same regardless of whether it's OR or XOR. let mut final_input: u16 = ((partial_byte as u16) & ((1 << num_partial_bits) - 1)) | (0x0F << num_partial_bits); let mut final_bits = num_partial_bits + 4; diff --git a/crypto/sha3/tests/sha3_tests.rs b/crypto/sha3/tests/sha3_tests.rs index 5fb5cae..4b834e4 100644 --- a/crypto/sha3/tests/sha3_tests.rs +++ b/crypto/sha3/tests/sha3_tests.rs @@ -353,7 +353,7 @@ mod sha3_tests { Err(_) => { /* good */ } } - // This works because we allow hazardous conversions before doing the conversion. + // This works because hazardous conversions are allowed before doing the conversion. let input_seed = KeyMaterial256::from_bytes(&DUMMY_SEED_512[..32]).expect("Error happened"); let mut output_seed = SHA3_256::new() .derive_key(&input_seed, b"some addtional input to the KDF") @@ -364,7 +364,7 @@ mod sha3_tests { // will automatically drop allow_hazardous_conversions() after performing one. assert_eq!(output_seed.key_type(), KeyType::MACKey); - // This works because we explicitly tag the input data as BytesFullEntropy. + // This works because the input data is tagged explicitly as BytesFullEntropy. // This is the preferred and better way to do it. let input_seed = KeyMaterial256::from_bytes_as_type(&DUMMY_SEED_512[..32], KeyType::BytesFullEntropy) diff --git a/crypto/sha3/tests/shake_tests.rs b/crypto/sha3/tests/shake_tests.rs index e87ea0d..280d1e0 100644 --- a/crypto/sha3/tests/shake_tests.rs +++ b/crypto/sha3/tests/shake_tests.rs @@ -13,8 +13,8 @@ mod shake_tests { #[test] fn test_xof_partial_bit_output() { - // we know that the 4th ([3]) byte of the output of SHA128(\x00\x01\x02\x03\x04) is 0xFF - // So we'll play with that to test partial byte output. + // The 4th ([3]) byte of the output of SHA128(\x00\x01\x02\x03\x04) is known to be 0xFF + // That fact is used to test partial byte output. let output = SHAKE128::new().hash_xof(&[0u8, 1u8, 2u8, 3u8, 4u8], 4); assert_eq!(output[3], 0xFF); diff --git a/crypto/utils/src/ct.rs b/crypto/utils/src/ct.rs index 96b1950..a3f93e2 100644 --- a/crypto/utils/src/ct.rs +++ b/crypto/utils/src/ct.rs @@ -75,7 +75,7 @@ impl Condition { Self::is_negative(x - y) } - // Note: haven't found a clever way to make this const, since it either needs a (non-const) not (!) or a boolean OR is_zero. + // Note: this cannot currently be marked as const, since it either needs a (non-const) not (!) or a boolean OR is_zero. pub fn is_lte(x: i64, y: i64) -> Self { !Self::is_gt(x, y) } @@ -84,7 +84,7 @@ impl Condition { Self::is_lt(y, x) } - // Note: haven't found a clever way to make this const, since it either needs a (non-const) not (!) or a boolean OR is_zero. + // Note: this cannot currently be marked as const, since it either needs a (non-const) not (!) or a boolean OR is_zero. pub fn is_gte(x: i64, y: i64) -> Self { !Self::is_lt(x, y) } @@ -262,7 +262,7 @@ where } /// Rust doesn't guarantee that anything can truly be constant-time under all compilation targets -/// and optimization levels, but we'll try. +/// and optimization levels. The following presents the standard constant-time shape. pub fn ct_eq_bytes(a: &[u8], b: &[u8]) -> bool { if a.len() != b.len() { return false; @@ -275,7 +275,7 @@ pub fn ct_eq_bytes(a: &[u8], b: &[u8]) -> bool { } /// Rust doesn't guarantee that anything can truly be constant-time under all compilation targets -/// and optimization levels, but we'll try. +/// and optimization levels. The following presents the standard constant-time shape. pub fn ct_eq_zero_bytes(a: &[u8]) -> bool { let mut result = 0u8; for i in 0..a.len() { diff --git a/crypto/utils/src/lib.rs b/crypto/utils/src/lib.rs index 67644be..3f6a385 100644 --- a/crypto/utils/src/lib.rs +++ b/crypto/utils/src/lib.rs @@ -2,12 +2,12 @@ pub mod ct; -/// Basic max function. If they are equal, you get back the first one. +/// Basic max function. If they are equal, it returns the first one. pub fn max<'a, T: PartialOrd>(x: &'a T, y: &'a T) -> &'a T { if x >= y { x } else { y } } -/// Basic min function. If they are equal, you get back the first one. +/// Basic min function. If they are equal, it returns the first one. pub fn min<'a, T: PartialOrd>(x: &'a T, y: &'a T) -> &'a T { if x <= y { x } else { y } } diff --git a/src/bench_mldsa_mem_usage.rs b/src/bench_mldsa_mem_usage.rs index 55bdc24..2717da4 100644 --- a/src/bench_mldsa_mem_usage.rs +++ b/src/bench_mldsa_mem_usage.rs @@ -5,15 +5,16 @@ //! //! > ms_print massif.out.835000 //! -//! or, shoved all into one line: +//! alternatively, as a one line command: //! //! > clear; clear; valgrind --tool=massif --heap=no --stacks=yes -- target/release/bench_mldsa_mem_usage > /dev/null; ms_print massif.out.*; rm massif.out.* //! //! Make sure you build in release mode! //! -//! Note: I'm using print!() to force the compiler not to optimize away the actual code. -//! I'm printing the important stuff for benchmarking to stderr so that I can pipe the junk to /dev/null -//! (I'm not doing it the other way because /usr/bin/time prints its useful stuff to stderr as well) +//! Note: +//! The code is using print!() to force the compiler not to optimize away the actual code. +//! It is printing important outputs for benchmarking to stderr so that the rest can be mapped to /dev/null +//! (this is because /usr/bin/time prints useful outputs to stderr as well) //! //! Main is at the bottom, controls which this was actually run. @@ -25,7 +26,7 @@ use bouncycastle_core_interface::traits::{Signature, SignaturePublicKey}; use bouncycastle_hex as hex; use bouncycastle_mldsa::MLDSA44PublicKey; -/// This exists so I can use /usr/bin/time to measure the base memory footprint of the cargo bench harness +/// This exists so that /usr/bin/time can be used to measure the base memory footprint of the cargo bench harness fn bench_do_nothing() { eprintln!("DoNothing");