Skip to content
Merged
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
2 changes: 2 additions & 0 deletions alpha_0.1.2_release_notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@
preventing exposure of stale data in oversized output buffers or on early error returns.
* Reworked the way KeyMaterial hazardous operations work; instead of a stateful .allow_hazardous_operations() /
.drop_hazardous_operations(), it now uses a closure-based do_hazardous_operations(). Github issue #39.
* Renamed KeyMaterial::KeyType's and deleted KeyMaterial::concatenate in order to give a better intuition and
FIPS-alignment.
* Github issues resolved:
* #6: https://github.com/bcgit/bc-rust/issues/6, thanks to Q. T. Felix (github: @Quant-TheodoreFelix)
* #10: https://github.com/bcgit/bc-rust/issues/10, thanks to Nicola Tuveri (github: @romen)
40 changes: 20 additions & 20 deletions crypto/core-test-framework/src/kdf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,31 +59,31 @@ impl TestFrameworkKDF {
assert_eq!(zeroized_key.key_type(), KeyType::Zeroized);
let out_key = H::default().derive_key(&zeroized_key, &[0u8; 10]).unwrap();
// since we've done some computation, the result will not actually be zeroized, even if all input key material was zeroized.
assert_eq!(out_key.key_type(), KeyType::BytesLowEntropy);
assert_eq!(out_key.key_type(), KeyType::Unknown);
assert_eq!(out_key.security_strength(), SecurityStrength::None);

// BytesLowEntropy -> BytesLowEntropy
let low_entropy_key =
KeyMaterial256::from_bytes_as_type(&[1u8; 16], KeyType::BytesLowEntropy).unwrap();
assert_eq!(low_entropy_key.key_type(), KeyType::BytesLowEntropy);
KeyMaterial256::from_bytes_as_type(&[1u8; 16], KeyType::Unknown).unwrap();
assert_eq!(low_entropy_key.key_type(), KeyType::Unknown);
let out_key = H::default().derive_key(&low_entropy_key, &[0u8; 10]).unwrap();
assert_eq!(out_key.key_type(), KeyType::BytesLowEntropy);
assert_eq!(out_key.key_type(), KeyType::Unknown);
assert_eq!(out_key.security_strength(), SecurityStrength::None);

// BytesFullEntropy -> BytesLowEntropy if not enough to fill the hash block
let low_entropy_key =
KeyMaterial256::from_bytes_as_type(&[1u8; 6], KeyType::BytesFullEntropy).unwrap();
assert_eq!(low_entropy_key.key_type(), KeyType::BytesFullEntropy);
KeyMaterial256::from_bytes_as_type(&[1u8; 6], KeyType::CryptographicRandom).unwrap();
assert_eq!(low_entropy_key.key_type(), KeyType::CryptographicRandom);
let out_key = H::default().derive_key(&low_entropy_key, &[0u8; 10]).unwrap();
assert_eq!(out_key.key_type(), KeyType::BytesLowEntropy);
assert_eq!(out_key.key_type(), KeyType::Unknown);
assert_eq!(out_key.security_strength(), SecurityStrength::None);

// BytesFullEntropy -> BytesFullEntropy
let full_entropy_key =
KeyMaterial512::from_bytes_as_type(&[1u8; 64], KeyType::BytesFullEntropy).unwrap();
assert_eq!(full_entropy_key.key_type(), KeyType::BytesFullEntropy);
KeyMaterial512::from_bytes_as_type(&[1u8; 64], KeyType::CryptographicRandom).unwrap();
assert_eq!(full_entropy_key.key_type(), KeyType::CryptographicRandom);
let out_key = H::default().derive_key(&full_entropy_key, &[0u8; 10]).unwrap();
assert_eq!(out_key.key_type(), KeyType::BytesFullEntropy);
assert_eq!(out_key.key_type(), KeyType::CryptographicRandom);
assert!(out_key.security_strength() > SecurityStrength::None);
}

Expand Down Expand Up @@ -141,35 +141,35 @@ impl TestFrameworkKDF {
assert_eq!(zeroized_key.security_strength(), SecurityStrength::None);
let keys = [&zeroized_key, &zeroized_key];
let out_key = H::default().derive_key_from_multiple(&keys, &[0u8; 10]).unwrap();
assert_eq!(out_key.key_type(), KeyType::BytesLowEntropy);
assert_eq!(out_key.key_type(), KeyType::Unknown);
assert_eq!(out_key.security_strength(), SecurityStrength::None);

// BytesLowEntropy -> BytesLowEntropy
let low_entropy_key =
KeyMaterial256::from_bytes_as_type(&[1u8; 16], KeyType::BytesLowEntropy).unwrap();
assert_eq!(low_entropy_key.key_type(), KeyType::BytesLowEntropy);
KeyMaterial256::from_bytes_as_type(&[1u8; 16], KeyType::Unknown).unwrap();
assert_eq!(low_entropy_key.key_type(), KeyType::Unknown);
let keys = [&zeroized_key, &low_entropy_key];
let out_key = H::default().derive_key_from_multiple(&keys, &[0u8; 10]).unwrap();
assert_eq!(out_key.key_type(), KeyType::BytesLowEntropy);
assert_eq!(out_key.key_type(), KeyType::Unknown);
assert_eq!(out_key.security_strength(), SecurityStrength::None);

// BytesFullEntropy -> BytesLowEntropy if not enough to fill the hash block
let low_entropy_key =
KeyMaterial256::from_bytes_as_type(&[1u8; 6], KeyType::BytesFullEntropy).unwrap();
assert_eq!(low_entropy_key.key_type(), KeyType::BytesFullEntropy);
KeyMaterial256::from_bytes_as_type(&[1u8; 6], KeyType::CryptographicRandom).unwrap();
assert_eq!(low_entropy_key.key_type(), KeyType::CryptographicRandom);
let keys = [&zeroized_key, &low_entropy_key];
let out_key = H::default().derive_key_from_multiple(&keys, &[0u8; 10]).unwrap();
assert_eq!(out_key.key_type(), KeyType::BytesLowEntropy);
assert_eq!(out_key.key_type(), KeyType::Unknown);
assert_eq!(out_key.security_strength(), SecurityStrength::None);

// BytesFullEntropy -> BytesFullEntropy
let zeroized64_key = KeyMaterial512::new();
let full_entropy_key =
KeyMaterial512::from_bytes_as_type(&[1u8; 64], KeyType::BytesFullEntropy).unwrap();
assert_eq!(full_entropy_key.key_type(), KeyType::BytesFullEntropy);
KeyMaterial512::from_bytes_as_type(&[1u8; 64], KeyType::CryptographicRandom).unwrap();
assert_eq!(full_entropy_key.key_type(), KeyType::CryptographicRandom);
let keys = [&zeroized64_key, &full_entropy_key];
let out_key = H::default().derive_key_from_multiple(&keys, &[0u8; 10]).unwrap();
assert_eq!(out_key.key_type(), KeyType::BytesFullEntropy);
assert_eq!(out_key.key_type(), KeyType::CryptographicRandom);
assert!(out_key.security_strength() > SecurityStrength::None);
}
}
85 changes: 34 additions & 51 deletions crypto/core/src/key_material.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,13 +85,13 @@ pub trait KeyMaterialTrait: KeyMaterialInternalTrait {
///
/// let key_bytes = [0u8; 16];
/// let mut key = KeyMaterial256::new();
/// let res = key.set_bytes_as_type(&key_bytes, KeyType::BytesLowEntropy);
/// let res = key.set_bytes_as_type(&key_bytes, KeyType::Unknown);
/// match res {
/// Err(KeyMaterialError::ActingOnZeroizedKey) => {
/// // Either figure out why your passed an all-zero key,
/// // or set the key type manually, if that's what you intended.
/// do_hazardous_operations(&mut key, |key| {
/// key.set_key_type(KeyType::BytesLowEntropy)
/// key.set_key_type(KeyType::Unknown)
/// }).unwrap(); // probably you should do something more elegant than .unwrap in your code ;)
/// },
/// Err(_) => { /* figure out what else went wrong */ },
Expand All @@ -103,7 +103,7 @@ pub trait KeyMaterialTrait: KeyMaterialInternalTrait {
/// Since this zeroizes and resets the key material, this is considered a dangerous conversion.
///
/// Will set the [SecurityStrength] automatically according to the following rules:
/// * If [KeyType] is [KeyType::Zeroized] or [KeyType::BytesLowEntropy] then it will be [SecurityStrength::None].
/// * If [KeyType] is [KeyType::Zeroized] or [KeyType::Unknown] then it will be [SecurityStrength::None].
/// * Otherwise it will set it based on the length of the provided source bytes.
fn set_bytes_as_type(
&mut self,
Expand Down Expand Up @@ -160,7 +160,7 @@ pub trait KeyMaterialTrait: KeyMaterialInternalTrait {
///
/// # 🚨 Hazardous Operation🚨
/// Inside a [do_hazardous_operations] closure this will set the key to any [KeyType].
/// Outside such a closure, only "safe" conversions are permitted: a [KeyType::BytesFullEntropy]
/// Outside such a closure, only "safe" conversions are permitted: a [KeyType::CryptographicRandom]
/// key may be converted to any type, and any type may be converted to itself (a no-op).
/// A hazardous conversion attempted outside a [do_hazardous_operations] closure returns
/// [KeyMaterialError::HazardousOperationNotPermitted], and converting a [KeyType::Zeroized] key
Expand Down Expand Up @@ -202,18 +202,6 @@ pub trait KeyMaterialTrait: KeyMaterialInternalTrait {
/// hold a different key, potentially of a different length.
fn zeroize(&mut self);

/// Adds the other KeyMaterial into this one, assuming there is space.
///
/// Throws [KeyMaterialError::InvalidLength] if this object does not have enough space to add the other one.
///
/// The resulting [KeyType] and security strength will be the lesser of the two keys.
/// In other words, concatenating two 128-bit full entropy keys generated at a 128-bit DRBG security level
/// will result in a 256-bit full entropy key still at the 128-bit DRBG security level.
/// Concatenating a full entropy key with a low entropy key will result in a low entropy key.
///
/// Returns the new key_len.
fn concatenate(&mut self, other: &dyn KeyMaterialTrait) -> Result<usize, KeyMaterialError>;

/// Perform a constant-time comparison between the two key material buffers,
/// ignoring differences in capacity, [KeyType], [SecurityStrength], etc.
fn equals(&self, other: &dyn KeyMaterialTrait) -> bool;
Expand All @@ -238,11 +226,18 @@ pub enum KeyType {
/// The KeyMaterial is zeroized and MUST NOT be used for any cryptographic operation in this state.
Zeroized,

/// The KeyMaterial contains data of low or unknown entropy.
BytesLowEntropy,
/// The KeyMaterial contains non-zero data of unknown key type.
/// A KeyMaterial of key type Unknown will always have a [SecurityStrength] of [SecurityStrength::None].
///
/// This is the default KeyType for data loaded via [KeyMaterial::from_bytes].
/// Promotion from Unknown to any other key type is considered to be a hazardous operation
/// and must be done within a [do_hazardous_operations] closure.
/// If you want to import key material directly into a known key type, use [KeyMaterial::from_bytes_as_type],
/// which does not require a hazardous operations closure.
Unknown,

/// The KeyMaterial contains data of full entropy and can be safely converted to any other full-entropy key type.
BytesFullEntropy,
/// The KeyMaterial contains data of full entropy and can be safely converted to any other key type.
CryptographicRandom,

/// A seed for asymmetric private keys, RNGs, and other seed-based cryptographic objects.
Seed,
Expand Down Expand Up @@ -283,16 +278,16 @@ impl<const KEY_LEN: usize> KeyMaterial<KEY_LEN> {
})?;

key.key_len = KEY_LEN;
key.key_type = KeyType::BytesFullEntropy;
key.key_type = KeyType::CryptographicRandom;
key.security_strength = rng.security_strength();
Ok(key)
}

/// Constructor.
/// Loads the provided data into a new KeyMaterial of type [KeyType::BytesLowEntropy].
/// Loads the provided data into a new KeyMaterial of type [KeyType::Unknown].
/// It will detect if you give it all-zero source data and set the key type to [KeyType::Zeroized] instead.
pub fn from_bytes(source: &[u8]) -> Result<Self, KeyMaterialError> {
Self::from_bytes_as_type(source, KeyType::BytesLowEntropy)
Self::from_bytes_as_type(source, KeyType::Unknown)
}

/// Constructor.
Expand All @@ -302,7 +297,7 @@ impl<const KEY_LEN: usize> KeyMaterial<KEY_LEN> {
/// It will detect if you give it all-zero source data and set the key type to [KeyType::Zeroized] instead.
///
/// Will set the [SecurityStrength] automatically according to the following rules:
/// * If [KeyType] is [KeyType::Zeroized] or [KeyType::BytesLowEntropy] then it will be [SecurityStrength::None].
/// * If [KeyType] is [KeyType::Zeroized] or [KeyType::Unknown] then it will be [SecurityStrength::None].
/// * Otherwise it will set it based on the length of the provided source bytes.
pub fn from_bytes_as_type(source: &[u8], key_type: KeyType) -> Result<Self, KeyMaterialError> {
let mut key_material = Self::default();
Expand Down Expand Up @@ -359,7 +354,7 @@ impl<const KEY_LEN: usize> KeyMaterialTrait for KeyMaterial<KEY_LEN> {
self.key_type = new_key_type;

do_hazardous_operations(self, |s| {
if new_key_type <= KeyType::BytesLowEntropy {
if new_key_type <= KeyType::Unknown {
s.set_security_strength(SecurityStrength::None)?;
} else {
s.set_security_strength(SecurityStrength::from_bits(source.len() * 8))?;
Expand Down Expand Up @@ -435,12 +430,12 @@ impl<const KEY_LEN: usize> KeyMaterialTrait for KeyMaterial<KEY_LEN> {
KeyType::Zeroized => {
return Err(KeyMaterialError::ActingOnZeroizedKey);
}
KeyType::BytesFullEntropy => {
KeyType::CryptographicRandom => {
// raw full entropy can be safely converted to anything.
self.key_type = key_type;
}
KeyType::BytesLowEntropy => match key_type {
KeyType::BytesLowEntropy => { /* No change */ }
KeyType::Unknown => match key_type {
KeyType::Unknown => { /* No change */ }
_ => {
return Err(KeyMaterialError::HazardousOperationNotPermitted);
}
Expand Down Expand Up @@ -482,7 +477,7 @@ impl<const KEY_LEN: usize> KeyMaterialTrait for KeyMaterial<KEY_LEN> {
return Err(KeyMaterialError::HazardousOperationNotPermitted);
};

if self.key_type <= KeyType::BytesLowEntropy && strength > SecurityStrength::None {
if self.key_type <= KeyType::Unknown && strength > SecurityStrength::None {
return Err(KeyMaterialError::SecurityStrength(
"BytesLowEntropy keys cannot have a security strength other than None.",
));
Expand Down Expand Up @@ -525,11 +520,11 @@ impl<const KEY_LEN: usize> KeyMaterialTrait for KeyMaterial<KEY_LEN> {
}
fn is_full_entropy(&self) -> bool {
match self.key_type {
KeyType::BytesFullEntropy
KeyType::CryptographicRandom
| KeyType::Seed
| KeyType::MACKey
| KeyType::SymmetricCipherKey => true,
KeyType::Zeroized | KeyType::BytesLowEntropy => false,
KeyType::Zeroized | KeyType::Unknown => false,
}
}

Expand All @@ -539,18 +534,6 @@ impl<const KEY_LEN: usize> KeyMaterialTrait for KeyMaterial<KEY_LEN> {
self.key_type = KeyType::Zeroized;
}

fn concatenate(&mut self, other: &dyn KeyMaterialTrait) -> Result<usize, KeyMaterialError> {
let new_key_len = self.key_len() + other.key_len();
if self.key_len() + other.key_len() > KEY_LEN {
return Err(KeyMaterialError::InputDataLongerThanKeyCapacity);
}
self.buf[self.key_len..new_key_len].copy_from_slice(other.ref_to_bytes());
self.key_len += other.key_len();
self.key_type = min(&self.key_type, &other.key_type()).clone();
self.security_strength = min(&self.security_strength, &other.security_strength()).clone();
Ok(self.key_len())
}

fn equals(&self, other: &dyn KeyMaterialTrait) -> bool {
if self.key_len() != other.key_len() {
return false;
Expand All @@ -561,7 +544,7 @@ impl<const KEY_LEN: usize> KeyMaterialTrait for KeyMaterial<KEY_LEN> {

/// Checks for equality of the key data (using a constant-time comparison), but does not check that
/// the two keys have the same type.
/// Therefore, for example, two keys loaded from the same bytes, one with type [KeyType::BytesLowEntropy] and
/// Therefore, for example, two keys loaded from the same bytes, one with type [KeyType::Unknown] and
/// the other with [KeyType::MACKey] will be considered equal.
impl<const KEY_LEN: usize> PartialEq for KeyMaterial<KEY_LEN> {
fn eq(&self, other: &Self) -> bool {
Expand All @@ -582,18 +565,18 @@ impl PartialOrd for KeyType {
KeyType::Zeroized => Some(Ordering::Equal),
_ => Some(Ordering::Less),
},
KeyType::BytesLowEntropy => match other {
KeyType::Unknown => match other {
KeyType::Zeroized => Some(Ordering::Greater),
KeyType::BytesLowEntropy => Some(Ordering::Equal),
KeyType::Unknown => Some(Ordering::Equal),
_ => Some(Ordering::Less),
},
KeyType::BytesFullEntropy => match other {
KeyType::Zeroized | KeyType::BytesLowEntropy => Some(Ordering::Greater),
KeyType::BytesFullEntropy => Some(Ordering::Equal),
KeyType::CryptographicRandom => match other {
KeyType::Zeroized | KeyType::Unknown => Some(Ordering::Greater),
KeyType::CryptographicRandom => Some(Ordering::Equal),
_ => Some(Ordering::Less),
},
KeyType::Seed | KeyType::MACKey | KeyType::SymmetricCipherKey => match other {
KeyType::Zeroized | KeyType::BytesLowEntropy | KeyType::BytesFullEntropy => {
KeyType::Zeroized | KeyType::Unknown | KeyType::CryptographicRandom => {
Some(Ordering::Greater)
}
KeyType::Seed | KeyType::MACKey | KeyType::SymmetricCipherKey => {
Expand Down Expand Up @@ -736,7 +719,7 @@ impl<const KEY_LEN: usize> KeyMaterialInternalTrait for KeyMaterial<KEY_LEN> {
/// // In this example, we initialize a KeyMateriol512 (64 bytes) with only 32 bytes of input.
/// let mut key = KeyMaterial512::from_bytes_as_type(
/// &[1u8; 32],
/// KeyType::BytesFullEntropy
/// KeyType::CryptographicRandom
/// ).unwrap();
/// assert_eq!(key.key_len(), 32);
///
Expand Down
14 changes: 7 additions & 7 deletions crypto/core/src/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,14 +98,14 @@ pub trait KDF: Default {
///
/// ex.:
///
/// * [KeyType::BytesLowEntropy] -> [KeyType::BytesLowEntropy])
/// * [KeyType::BytesFullEntropy] -> [KeyType::BytesFullEntropy])
/// * [KeyType::Unknown] -> [KeyType::Unknown])
/// * [KeyType::CryptographicRandom] -> [KeyType::CryptographicRandom])
/// * [KeyType::SymmetricCipherKey] -> [KeyType::SymmetricCipherKey])
///
/// If provided with an input key, even if it is [KeyType::BytesFullEntropy], but that
/// If provided with an input key, even if it is [KeyType::CryptographicRandom], but that
/// contains less key material than the internal block size of the KDF, then the KDF
/// will not be considered properly seeded, and the output [KeyMaterial] will be set to
/// [KeyType::BytesLowEntropy] -- for example, seeding SHA3-256 with a [KeyMaterial] containing
/// [KeyType::Unknown] -- 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
Expand Down Expand Up @@ -152,9 +152,9 @@ pub trait KDF: Default {
///
/// Implementations can, and in most cases SHOULD, return a [KeyMaterial] of the same type as the
/// strongest key, and SHOULD throw a [HashError] if all input keys are zeroized.
/// For example output a [KeyType::BytesFullEntropy] key whenever any one of
/// the input keys is a [KeyType::BytesFullEntropy] key.
/// As another example, combining a [KeyType::BytesLowEntropy] key with a [KeyType::MACKey] key
/// For example output a [KeyType::CryptographicRandom] key whenever any one of
/// the input keys is a [KeyType::CryptographicRandom] key.
/// As another example, combining a [KeyType::Unknown] key with a [KeyType::MACKey] key
/// should return a [KeyType::MACKey].
///
/// Output length: this function will create a KeyMaterial populated with the default output length
Expand Down
Loading
Loading