Skip to content
Draft
6 changes: 3 additions & 3 deletions cli/src/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ pub(crate) fn read_from_file(filename: &str) -> Vec<u8> {
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
}
}
Expand Down Expand Up @@ -47,7 +47,7 @@ pub(crate) fn read_from_file_or_stdin(filename: &Option<String>) -> Vec<u8> {
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
}
}
Expand Down Expand Up @@ -98,7 +98,7 @@ pub(crate) fn parse_seed<const SEED_LEN: usize>(bytes: &[u8]) -> Result<KeyMater
}
};

// I think I've checked for all the error conditions, so this shouldn't fail.
// TODO: Verify that all error conditions have been checked
let mut seed = KeyMaterial::<SEED_LEN>::from_bytes_as_type(&seed_bytes, KeyType::Seed).unwrap();

if seed.key_type() == KeyType::Zeroized || seed.security_strength() < SecurityStrength::_256bit
Expand Down
4 changes: 2 additions & 2 deletions cli/src/mldsa_cmd.rs
Original file line number Diff line number Diff line change
@@ -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};
Expand Down
39 changes: 20 additions & 19 deletions crypto/base64/src/lib.rs
Original file line number Diff line number Diff line change
@@ -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<u8>`.
//!
//!```
Expand Down Expand Up @@ -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.
//!
//! ```
Expand All @@ -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:
Expand Down Expand Up @@ -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),
Expand Down Expand Up @@ -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);
Expand All @@ -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<T: AsRef<[u8]>>(mut self, input: T) -> Result<Vec<u8>, 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);
Expand Down
9 changes: 8 additions & 1 deletion crypto/core/src/key_material.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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>;
Expand Down Expand Up @@ -389,16 +389,19 @@ impl<const KEY_LEN: usize> KeyMaterialTrait for KeyMaterial<KEY_LEN> {
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);
}
self.key_type = key_type.clone();
Ok(())
}

fn security_strength(&self) -> SecurityStrength {
self.security_strength.clone()
}
Expand Down Expand Up @@ -453,6 +456,7 @@ impl<const KEY_LEN: usize> KeyMaterialTrait for KeyMaterial<KEY_LEN> {
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.
///
Expand All @@ -463,10 +467,12 @@ impl<const KEY_LEN: usize> KeyMaterialTrait for KeyMaterial<KEY_LEN> {
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
Expand Down Expand Up @@ -529,6 +535,7 @@ impl<const KEY_LEN: usize> KeyMaterialTrait for KeyMaterial<KEY_LEN> {
self.drop_hazardous_operations();
Ok(())
}

fn is_full_entropy(&self) -> bool {
match self.key_type {
KeyType::BytesFullEntropy
Expand Down
4 changes: 2 additions & 2 deletions crypto/core/src/traits.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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
Expand Down
30 changes: 16 additions & 14 deletions crypto/core/tests/key_material_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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"),
Expand All @@ -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();
Expand All @@ -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.
}

Expand All @@ -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]);
Expand Down Expand Up @@ -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"),
Expand Down Expand Up @@ -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);

Expand All @@ -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();
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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());
Expand Down Expand Up @@ -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();
}
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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",
)
Expand Down
2 changes: 1 addition & 1 deletion crypto/factory/src/hash_factory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
//! let h = bouncycastle_factory::hash_factory::HashFactory::new(sha3::SHA3_256_NAME).unwrap();
//! let output: Vec<u8> = 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;
Expand Down
10 changes: 5 additions & 5 deletions crypto/factory/src/kdf_factory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,37 +9,37 @@
//! 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";
//!
//! let mut h = bouncycastle_factory::kdf_factory::KDFFactory::new(bouncycastle_hkdf::HKDF_SHA256_NAME).unwrap();
//! 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";
//!
//! let h = bouncycastle_factory::kdf_factory::KDFFactory::new("HKDF-SHA256").unwrap();
//! 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";
//!
Expand Down
Loading