Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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