Makes our CredRandom derivation FIPS compliant (#613)
* Makes our CredRandom derivation FIPS compliant This change breaks existing usage of CredRandom. * fixes rust_crypto and HKDF test style
This commit is contained in:
@@ -17,6 +17,27 @@ use super::Hash256;
|
|||||||
|
|
||||||
const HASH_SIZE: usize = 32;
|
const HASH_SIZE: usize = 32;
|
||||||
|
|
||||||
|
/// Computes the HKDF with the given salt and 256 bit (one block) output.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `ikm` - Input keying material
|
||||||
|
/// * `salt` - Byte string that acts as a key
|
||||||
|
/// * `info` - Optional context and application specific information
|
||||||
|
///
|
||||||
|
/// This implementation is equivalent to a standard HKD, with `salt` fixed at a length of
|
||||||
|
/// 32 byte and the output length l as 32.
|
||||||
|
pub fn hkdf_256<H>(ikm: &[u8], salt: &[u8; HASH_SIZE], info: &[u8]) -> [u8; HASH_SIZE]
|
||||||
|
where
|
||||||
|
H: Hash256,
|
||||||
|
{
|
||||||
|
let prk = hmac_256::<H>(salt, ikm);
|
||||||
|
// l is implicitly the block size, so we iterate exactly once.
|
||||||
|
let mut t = info.to_vec();
|
||||||
|
t.push(1);
|
||||||
|
hmac_256::<H>(&prk, t.as_slice())
|
||||||
|
}
|
||||||
|
|
||||||
/// Computes the HKDF with empty salt and 256 bit (one block) output.
|
/// Computes the HKDF with empty salt and 256 bit (one block) output.
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
@@ -31,11 +52,7 @@ where
|
|||||||
H: Hash256,
|
H: Hash256,
|
||||||
{
|
{
|
||||||
// Salt is a zero block here.
|
// Salt is a zero block here.
|
||||||
let prk = hmac_256::<H>(&[0; HASH_SIZE], ikm);
|
hkdf_256::<H>(ikm, &[0; HASH_SIZE], info)
|
||||||
// l is implicitly the block size, so we iterate exactly once.
|
|
||||||
let mut t = info.to_vec();
|
|
||||||
t.push(1);
|
|
||||||
hmac_256::<H>(&prk, t.as_slice())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@@ -49,32 +66,53 @@ mod test {
|
|||||||
// Test vectors generated by pycryptodome using:
|
// Test vectors generated by pycryptodome using:
|
||||||
// HKDF(b'0', 32, b'', SHA256, context=b'\x00').hex()
|
// HKDF(b'0', 32, b'', SHA256, context=b'\x00').hex()
|
||||||
let test_okms = [
|
let test_okms = [
|
||||||
hex::decode("f9be72116cb97f41828210289caafeabde1f3dfb9723bf43538ab18f3666783a")
|
"f9be72116cb97f41828210289caafeabde1f3dfb9723bf43538ab18f3666783a",
|
||||||
.unwrap(),
|
"f50f964f5b94d62fd1da9356ab8662b0a0f5b8e36e277178b69b6ffecf50cf44",
|
||||||
hex::decode("f50f964f5b94d62fd1da9356ab8662b0a0f5b8e36e277178b69b6ffecf50cf44")
|
"fc8772ceb5592d67442dcb4353cdd28519e82d6e55b4cf664b5685252c2d2998",
|
||||||
.unwrap(),
|
"62831b924839a180f53be5461eeea1b89dc21779f50142b5a54df0f0cc86d61a",
|
||||||
hex::decode("fc8772ceb5592d67442dcb4353cdd28519e82d6e55b4cf664b5685252c2d2998")
|
"6991f00a12946a4e3b8315cdcf0132c2ca508fd17b769f08d1454d92d33733e0",
|
||||||
.unwrap(),
|
"0f9bb7dddd1ec61f91d8c4f5369b5870f9d44c4ceabccca1b83f06fec115e4e3",
|
||||||
hex::decode("62831b924839a180f53be5461eeea1b89dc21779f50142b5a54df0f0cc86d61a")
|
"235367e2ab6cca2aba1a666825458dba6b272a215a2537c05feebe4b80dab709",
|
||||||
.unwrap(),
|
"96e8edad661da48d1a133b38c255d33e05555bc9aa442579dea1cd8d8b8d2aef",
|
||||||
hex::decode("6991f00a12946a4e3b8315cdcf0132c2ca508fd17b769f08d1454d92d33733e0")
|
|
||||||
.unwrap(),
|
|
||||||
hex::decode("0f9bb7dddd1ec61f91d8c4f5369b5870f9d44c4ceabccca1b83f06fec115e4e3")
|
|
||||||
.unwrap(),
|
|
||||||
hex::decode("235367e2ab6cca2aba1a666825458dba6b272a215a2537c05feebe4b80dab709")
|
|
||||||
.unwrap(),
|
|
||||||
hex::decode("96e8edad661da48d1a133b38c255d33e05555bc9aa442579dea1cd8d8b8d2aef")
|
|
||||||
.unwrap(),
|
|
||||||
];
|
];
|
||||||
for (i, okm) in test_okms.iter().enumerate() {
|
for (i, okm) in test_okms.iter().enumerate() {
|
||||||
// String of number i.
|
// String of number i.
|
||||||
let ikm = i.to_string();
|
let ikm = i.to_string();
|
||||||
// Byte i.
|
// Byte i.
|
||||||
let info = [i as u8];
|
let info = [i as u8];
|
||||||
|
let okm = hex::decode(okm).unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
&hkdf_empty_salt_256::<Sha256>(&ikm.as_bytes(), &info[..]),
|
&hkdf_empty_salt_256::<Sha256>(&ikm.as_bytes(), &info[..]),
|
||||||
array_ref!(okm, 0, 32)
|
array_ref!(okm, 0, 32)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_hkdf_256_sha256_vectors() {
|
||||||
|
// Test vectors generated as above, but with salt:
|
||||||
|
let test_okms = [
|
||||||
|
"f9be72116cb97f41828210289caafeabde1f3dfb9723bf43538ab18f3666783a",
|
||||||
|
"a2480a09c7349d76e459f98a8259da40544bfbd2930d357a0f3250ade0acf941",
|
||||||
|
"3904f7bf3615df9512fc6b1af651ed69b43f7fad424f9c718aaab63f377a36b9",
|
||||||
|
"a0027dcffb27d356317199c6e65f153a9286ba114aee2d3cf45bdba83cb7c065",
|
||||||
|
"786d1f89f54668bac443cc6a8887c95d6fbde07702cb4c16d76c452e87c50f79",
|
||||||
|
"8e9a5bdf362c5aec2c31a742dfebd0b7b56e16ab8408d9d0609a4fad06446875",
|
||||||
|
"4a35d3d7c80ff4fab65f7e30d6b305fc7bb39ffe905aabedd6593354f86177b6",
|
||||||
|
"b1121deabd8b4308f3805cda8af991ee976bd8e413bcb6a8dd3fc26ebe2312d2",
|
||||||
|
];
|
||||||
|
for (i, okm) in test_okms.iter().enumerate() {
|
||||||
|
// String of number i.
|
||||||
|
let ikm = i.to_string();
|
||||||
|
// Bytestring of byte i.
|
||||||
|
let salt = [i as u8; 32];
|
||||||
|
// Byte i.
|
||||||
|
let info = [i as u8];
|
||||||
|
let okm = hex::decode(okm).unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
&hkdf_256::<Sha256>(&ikm.as_bytes(), &salt, &info[..]),
|
||||||
|
array_ref!(okm, 0, 32)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,11 +16,28 @@ use super::HASH_SIZE;
|
|||||||
|
|
||||||
/// HKDF using SHA256.
|
/// HKDF using SHA256.
|
||||||
pub trait Hkdf256 {
|
pub trait Hkdf256 {
|
||||||
|
/// Computes the HKDF with 256 bit (one block) output.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `ikm` - Input keying material
|
||||||
|
/// * `salt` - Byte string that acts as a key
|
||||||
|
/// * `info` - Optional context and application specific information
|
||||||
|
///
|
||||||
|
/// This implementation is equivalent to a standard HKD, with `salt` fixed at a length of
|
||||||
|
/// 32 byte and the output length l as 32.
|
||||||
|
fn hkdf_256(ikm: &[u8], salt: &[u8; HASH_SIZE], info: &[u8]) -> [u8; HASH_SIZE];
|
||||||
|
|
||||||
/// Computes the HKDF with empty salt and 256 bit (one block) output.
|
/// Computes the HKDF with empty salt and 256 bit (one block) output.
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
///
|
///
|
||||||
/// * `ikm` - Input keying material
|
/// * `ikm` - Input keying material
|
||||||
/// * `info` - Optional context and application specific information
|
/// * `info` - Optional context and application specific information
|
||||||
fn hkdf_empty_salt_256(ikm: &[u8], info: &[u8]) -> [u8; HASH_SIZE];
|
///
|
||||||
|
/// This implementation is equivalent to a standard HKDF, with `salt` set to the
|
||||||
|
/// default block of zeros and the output length l as 32.
|
||||||
|
fn hkdf_empty_salt_256(ikm: &[u8], info: &[u8]) -> [u8; HASH_SIZE] {
|
||||||
|
Self::hkdf_256(ikm, &[0; HASH_SIZE], info)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -158,6 +158,16 @@ mod test {
|
|||||||
assert_eq!(&SoftwareHkdf256::hkdf_empty_salt_256(b"0", &[0]), &okm);
|
assert_eq!(&SoftwareHkdf256::hkdf_empty_salt_256(b"0", &[0]), &okm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_hkdf_256_matches() {
|
||||||
|
let ikm = [0x11; 16];
|
||||||
|
let salt = [0; 32];
|
||||||
|
let info = [0x22; 16];
|
||||||
|
let empty_salt_output = SoftwareHkdf256::hkdf_empty_salt_256(&ikm, &info);
|
||||||
|
let explicit_output = SoftwareHkdf256::hkdf_256(&ikm, &salt, &info);
|
||||||
|
assert_eq!(empty_salt_output, explicit_output);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_aes_encrypt_decrypt_block() {
|
fn test_aes_encrypt_decrypt_block() {
|
||||||
let mut block = [0x55; AES_BLOCK_SIZE];
|
let mut block = [0x55; AES_BLOCK_SIZE];
|
||||||
|
|||||||
@@ -257,8 +257,8 @@ impl Hmac256 for SoftwareHmac256 {
|
|||||||
pub struct SoftwareHkdf256;
|
pub struct SoftwareHkdf256;
|
||||||
|
|
||||||
impl Hkdf256 for SoftwareHkdf256 {
|
impl Hkdf256 for SoftwareHkdf256 {
|
||||||
fn hkdf_empty_salt_256(ikm: &[u8], info: &[u8]) -> [u8; HASH_SIZE] {
|
fn hkdf_256(ikm: &[u8], salt: &[u8; HASH_SIZE], info: &[u8]) -> [u8; HASH_SIZE] {
|
||||||
let hk = hkdf::Hkdf::<sha2::Sha256>::new(Some(&[0; HASH_SIZE]), ikm);
|
let hk = hkdf::Hkdf::<sha2::Sha256>::new(Some(salt), ikm);
|
||||||
let mut okm = [0u8; HASH_SIZE];
|
let mut okm = [0u8; HASH_SIZE];
|
||||||
hk.expand(info, &mut okm).unwrap();
|
hk.expand(info, &mut okm).unwrap();
|
||||||
okm
|
okm
|
||||||
|
|||||||
@@ -208,8 +208,8 @@ impl Hmac256 for SoftwareHmac256 {
|
|||||||
pub struct SoftwareHkdf256;
|
pub struct SoftwareHkdf256;
|
||||||
|
|
||||||
impl Hkdf256 for SoftwareHkdf256 {
|
impl Hkdf256 for SoftwareHkdf256 {
|
||||||
fn hkdf_empty_salt_256(ikm: &[u8], info: &[u8]) -> [u8; HASH_SIZE] {
|
fn hkdf_256(ikm: &[u8], salt: &[u8; HASH_SIZE], info: &[u8]) -> [u8; HASH_SIZE] {
|
||||||
crypto::hkdf::hkdf_empty_salt_256::<crypto::sha256::Sha256>(ikm, info)
|
crypto::hkdf::hkdf_256::<crypto::sha256::Sha256>(ikm, salt, info)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -66,14 +66,14 @@ use crate::api::attestation_store::{self, Attestation, AttestationStore};
|
|||||||
use crate::api::clock::Clock;
|
use crate::api::clock::Clock;
|
||||||
use crate::api::connection::{HidConnection, SendOrRecvStatus, UsbEndpoint};
|
use crate::api::connection::{HidConnection, SendOrRecvStatus, UsbEndpoint};
|
||||||
use crate::api::crypto::ecdsa::{SecretKey as _, Signature};
|
use crate::api::crypto::ecdsa::{SecretKey as _, Signature};
|
||||||
use crate::api::crypto::hmac256::Hmac256;
|
use crate::api::crypto::hkdf256::Hkdf256;
|
||||||
use crate::api::crypto::sha256::Sha256;
|
use crate::api::crypto::sha256::Sha256;
|
||||||
use crate::api::customization::Customization;
|
use crate::api::customization::Customization;
|
||||||
use crate::api::firmware_protection::FirmwareProtection;
|
use crate::api::firmware_protection::FirmwareProtection;
|
||||||
use crate::api::rng::Rng;
|
use crate::api::rng::Rng;
|
||||||
use crate::api::upgrade_storage::UpgradeStorage;
|
use crate::api::upgrade_storage::UpgradeStorage;
|
||||||
use crate::api::user_presence::{UserPresence, UserPresenceError};
|
use crate::api::user_presence::{UserPresence, UserPresenceError};
|
||||||
use crate::env::{EcdsaSk, Env, Hmac, Sha};
|
use crate::env::{EcdsaSk, Env, Hkdf, Sha};
|
||||||
use alloc::boxed::Box;
|
use alloc::boxed::Box;
|
||||||
use alloc::string::{String, ToString};
|
use alloc::string::{String, ToString};
|
||||||
use alloc::vec;
|
use alloc::vec;
|
||||||
@@ -956,9 +956,10 @@ impl<E: Env> CtapState<E> {
|
|||||||
private_key: &PrivateKey,
|
private_key: &PrivateKey,
|
||||||
has_uv: bool,
|
has_uv: bool,
|
||||||
) -> Result<[u8; 32], Ctap2StatusCode> {
|
) -> Result<[u8; 32], Ctap2StatusCode> {
|
||||||
let entropy = private_key.to_bytes();
|
let private_key_bytes = private_key.to_bytes();
|
||||||
|
let salt = array_ref!(private_key_bytes, 0, 32);
|
||||||
let key = storage::cred_random_secret(env, has_uv)?;
|
let key = storage::cred_random_secret(env, has_uv)?;
|
||||||
Ok(Hmac::<E>::mac(&key, &entropy))
|
Ok(Hkdf::<E>::hkdf_256(&key, salt, b"credRandom"))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Processes the input of a get_assertion operation for a given credential
|
// Processes the input of a get_assertion operation for a given credential
|
||||||
|
|||||||
Reference in New Issue
Block a user