SHA and HMAC for the Crypto trait (#609)
* Implements SHA256 into the Crypto trait * Fixes documentation * Descriptive documentation
This commit is contained in:
@@ -24,6 +24,8 @@ rand = { version = "0.8.4", optional = true }
|
||||
ed25519-compact = { version = "1", default-features = false, optional = true }
|
||||
p256 = { version = "0.13.0", features = ["ecdh"], optional = true }
|
||||
rand_core = { version = "0.6.4", optional = true }
|
||||
sha2 = { version = "0.10.6", optional = true }
|
||||
hmac = { version = "0.12.1", optional = true }
|
||||
|
||||
[features]
|
||||
debug_ctap = []
|
||||
@@ -32,7 +34,7 @@ with_ctap1 = ["crypto/with_ctap1"]
|
||||
vendor_hid = []
|
||||
fuzz = ["arbitrary", "std"]
|
||||
ed25519 = ["ed25519-compact"]
|
||||
rust_crypto = ["p256", "rand_core"]
|
||||
rust_crypto = ["p256", "rand_core", "sha2", "hmac"]
|
||||
|
||||
[dev-dependencies]
|
||||
enum-iterator = "0.6.0"
|
||||
|
||||
37
libraries/opensk/src/api/crypto/hmac256.rs
Normal file
37
libraries/opensk/src/api/crypto/hmac256.rs
Normal file
@@ -0,0 +1,37 @@
|
||||
// Copyright 2023 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use super::{HASH_SIZE, HMAC_KEY_SIZE, TRUNCATED_HMAC_SIZE};
|
||||
|
||||
/// For a given hash function, computes and verifies the HMAC.
|
||||
pub trait Hmac256 {
|
||||
/// Computes the HMAC.
|
||||
fn mac(key: &[u8; HMAC_KEY_SIZE], data: &[u8]) -> [u8; HASH_SIZE];
|
||||
|
||||
/// Verifies the HMAC.
|
||||
///
|
||||
/// This function does best effort to not leak information about the key through side-channels
|
||||
/// (e.g. usage of constant time comparison).
|
||||
fn verify(key: &[u8; HMAC_KEY_SIZE], data: &[u8], mac: &[u8; HASH_SIZE]) -> bool;
|
||||
|
||||
/// Verifies the first bytes of an HMAC.
|
||||
///
|
||||
/// This function does best effort to not leak information about the key through side-channels
|
||||
/// (e.g. usage of constant time comparison).
|
||||
fn verify_truncated_left(
|
||||
key: &[u8; HMAC_KEY_SIZE],
|
||||
data: &[u8],
|
||||
mac: &[u8; TRUNCATED_HMAC_SIZE],
|
||||
) -> bool;
|
||||
}
|
||||
@@ -20,9 +20,13 @@ pub mod rust_crypto;
|
||||
pub mod software_crypto;
|
||||
#[cfg(feature = "rust_crypto")]
|
||||
pub use rust_crypto as software_crypto;
|
||||
pub mod hmac256;
|
||||
pub mod sha256;
|
||||
|
||||
use self::ecdh::Ecdh;
|
||||
use self::ecdsa::Ecdsa;
|
||||
use self::hmac256::Hmac256;
|
||||
use self::sha256::Sha256;
|
||||
|
||||
/// The size of field elements in the elliptic curve P256.
|
||||
pub const EC_FIELD_SIZE: usize = 32;
|
||||
@@ -30,8 +34,21 @@ pub const EC_FIELD_SIZE: usize = 32;
|
||||
/// The size of a serialized ECDSA signature.
|
||||
pub const EC_SIGNATURE_SIZE: usize = 2 * EC_FIELD_SIZE;
|
||||
|
||||
/// The size in bytes of a SHA256.
|
||||
pub const HASH_SIZE: usize = 32;
|
||||
|
||||
/// The size in bytes of an HMAC.
|
||||
pub const HMAC_KEY_SIZE: usize = 32;
|
||||
|
||||
/// The size in bytes of a truncated HMAC.
|
||||
///
|
||||
/// Truncated HMACs are used in PIN protocol V1 in CTAP2.
|
||||
pub const TRUNCATED_HMAC_SIZE: usize = 16;
|
||||
|
||||
/// Necessary cryptographic primitives for CTAP.
|
||||
pub trait Crypto {
|
||||
type Ecdh: Ecdh;
|
||||
type Ecdsa: Ecdsa;
|
||||
type Sha256: Sha256;
|
||||
type Hmac256: Hmac256;
|
||||
}
|
||||
|
||||
@@ -20,8 +20,14 @@
|
||||
//!
|
||||
//! If you want to use OpenSK outside of Tock v1, maybe this is useful for you though!
|
||||
|
||||
use crate::api::crypto::{ecdh, ecdsa, Crypto, EC_FIELD_SIZE, EC_SIGNATURE_SIZE};
|
||||
use crate::api::crypto::hmac256::Hmac256;
|
||||
use crate::api::crypto::sha256::Sha256;
|
||||
use crate::api::crypto::{
|
||||
ecdh, ecdsa, Crypto, EC_FIELD_SIZE, EC_SIGNATURE_SIZE, HASH_SIZE, HMAC_KEY_SIZE,
|
||||
TRUNCATED_HMAC_SIZE,
|
||||
};
|
||||
use core::convert::TryFrom;
|
||||
use hmac::Mac;
|
||||
use p256::ecdh::EphemeralSecret;
|
||||
use p256::ecdsa::signature::{SignatureEncoding, Signer, Verifier};
|
||||
use p256::ecdsa::{SigningKey, VerifyingKey};
|
||||
@@ -29,6 +35,7 @@ use p256::elliptic_curve::sec1::ToEncodedPoint;
|
||||
// TODO: implement CryptoRngCore for our Rng instead
|
||||
use rand_core::OsRng;
|
||||
use rng256::Rng256;
|
||||
use sha2::Digest;
|
||||
|
||||
pub struct SoftwareCrypto;
|
||||
pub struct SoftwareEcdh;
|
||||
@@ -37,6 +44,8 @@ pub struct SoftwareEcdsa;
|
||||
impl Crypto for SoftwareCrypto {
|
||||
type Ecdh = SoftwareEcdh;
|
||||
type Ecdsa = SoftwareEcdsa;
|
||||
type Sha256 = SoftwareSha256;
|
||||
type Hmac256 = SoftwareHmac256;
|
||||
}
|
||||
|
||||
impl ecdh::Ecdh for SoftwareEcdh {
|
||||
@@ -188,6 +197,57 @@ impl ecdsa::Signature for SoftwareEcdsaSignature {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SoftwareSha256 {
|
||||
hasher: sha2::Sha256,
|
||||
}
|
||||
|
||||
impl Sha256 for SoftwareSha256 {
|
||||
fn digest(data: &[u8]) -> [u8; HASH_SIZE] {
|
||||
sha2::Sha256::digest(data).into()
|
||||
}
|
||||
|
||||
fn new() -> Self {
|
||||
let hasher = sha2::Sha256::new();
|
||||
Self { hasher }
|
||||
}
|
||||
|
||||
/// Digest the next part of the message to hash.
|
||||
fn update(&mut self, data: &[u8]) {
|
||||
self.hasher.update(data);
|
||||
}
|
||||
|
||||
/// Finalizes the hashing process, returns the hash value.
|
||||
fn finalize(self) -> [u8; HASH_SIZE] {
|
||||
self.hasher.finalize().into()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SoftwareHmac256;
|
||||
|
||||
impl Hmac256 for SoftwareHmac256 {
|
||||
fn mac(key: &[u8; HMAC_KEY_SIZE], data: &[u8]) -> [u8; HASH_SIZE] {
|
||||
let mut hmac = hmac::Hmac::<sha2::Sha256>::new_from_slice(key).unwrap();
|
||||
hmac.update(data);
|
||||
hmac.finalize().into_bytes().into()
|
||||
}
|
||||
|
||||
fn verify(key: &[u8; HMAC_KEY_SIZE], data: &[u8], mac: &[u8; HASH_SIZE]) -> bool {
|
||||
let mut hmac = hmac::Hmac::<sha2::Sha256>::new_from_slice(key).unwrap();
|
||||
hmac.update(data);
|
||||
hmac.verify_slice(mac).is_ok()
|
||||
}
|
||||
|
||||
fn verify_truncated_left(
|
||||
key: &[u8; HMAC_KEY_SIZE],
|
||||
data: &[u8],
|
||||
mac: &[u8; TRUNCATED_HMAC_SIZE],
|
||||
) -> bool {
|
||||
let mut hmac = hmac::Hmac::<sha2::Sha256>::new_from_slice(key).unwrap();
|
||||
hmac.update(data);
|
||||
hmac.verify_truncated_left(mac).is_ok()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
@@ -246,4 +306,27 @@ mod test {
|
||||
second_key.to_slice(&mut new_bytes);
|
||||
assert_eq!(key_bytes, new_bytes);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sha256_hash_matches() {
|
||||
let data = [0x55; 16];
|
||||
let mut hasher = SoftwareSha256::new();
|
||||
hasher.update(&data);
|
||||
assert_eq!(SoftwareSha256::digest(&data), hasher.finalize());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_hmac256_verifies() {
|
||||
let key = [0xAA; HMAC_KEY_SIZE];
|
||||
let data = [0x55; 16];
|
||||
let mac = SoftwareHmac256::mac(&key, &data);
|
||||
assert!(SoftwareHmac256::verify(&key, &data, &mac));
|
||||
let truncated_mac =
|
||||
<&[u8; TRUNCATED_HMAC_SIZE]>::try_from(&mac[..TRUNCATED_HMAC_SIZE]).unwrap();
|
||||
assert!(SoftwareHmac256::verify_truncated_left(
|
||||
&key,
|
||||
&data,
|
||||
&truncated_mac
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
34
libraries/opensk/src/api/crypto/sha256.rs
Normal file
34
libraries/opensk/src/api/crypto/sha256.rs
Normal file
@@ -0,0 +1,34 @@
|
||||
// Copyright 2023 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use super::HASH_SIZE;
|
||||
|
||||
/// Hashes data using SHA256.
|
||||
pub trait Sha256: Sized {
|
||||
/// Computes the hash of a given message directly.
|
||||
fn digest(data: &[u8]) -> [u8; HASH_SIZE] {
|
||||
let mut hasher = Self::new();
|
||||
hasher.update(data);
|
||||
hasher.finalize()
|
||||
}
|
||||
|
||||
/// Create a new object that can be incrementally updated for digesting.
|
||||
fn new() -> Self;
|
||||
|
||||
/// Digest the next part of the message to hash.
|
||||
fn update(&mut self, data: &[u8]);
|
||||
|
||||
/// Finalizes the hashing process, returns the hash value.
|
||||
fn finalize(self) -> [u8; HASH_SIZE];
|
||||
}
|
||||
@@ -12,8 +12,14 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::api::crypto::{ecdh, ecdsa, Crypto, EC_FIELD_SIZE, EC_SIGNATURE_SIZE};
|
||||
use crate::api::crypto::hmac256::Hmac256;
|
||||
use crate::api::crypto::sha256::Sha256;
|
||||
use crate::api::crypto::{
|
||||
ecdh, ecdsa, Crypto, EC_FIELD_SIZE, EC_SIGNATURE_SIZE, HASH_SIZE, HMAC_KEY_SIZE,
|
||||
TRUNCATED_HMAC_SIZE,
|
||||
};
|
||||
use alloc::vec::Vec;
|
||||
use crypto::Hash256;
|
||||
use rng256::Rng256;
|
||||
|
||||
pub struct SoftwareCrypto;
|
||||
@@ -23,6 +29,8 @@ pub struct SoftwareEcdsa;
|
||||
impl Crypto for SoftwareCrypto {
|
||||
type Ecdh = SoftwareEcdh;
|
||||
type Ecdsa = SoftwareEcdsa;
|
||||
type Sha256 = SoftwareSha256;
|
||||
type Hmac256 = SoftwareHmac256;
|
||||
}
|
||||
|
||||
impl ecdh::Ecdh for SoftwareEcdh {
|
||||
@@ -152,6 +160,47 @@ impl ecdsa::Signature for SoftwareEcdsaSignature {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SoftwareSha256 {
|
||||
hasher: crypto::sha256::Sha256,
|
||||
}
|
||||
|
||||
impl Sha256 for SoftwareSha256 {
|
||||
fn new() -> Self {
|
||||
let hasher = crypto::sha256::Sha256::new();
|
||||
Self { hasher }
|
||||
}
|
||||
|
||||
/// Digest the next part of the message to hash.
|
||||
fn update(&mut self, data: &[u8]) {
|
||||
self.hasher.update(data);
|
||||
}
|
||||
|
||||
/// Finalizes the hashing process, returns the hash value.
|
||||
fn finalize(self) -> [u8; HASH_SIZE] {
|
||||
self.hasher.finalize()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SoftwareHmac256;
|
||||
|
||||
impl Hmac256 for SoftwareHmac256 {
|
||||
fn mac(key: &[u8; HMAC_KEY_SIZE], data: &[u8]) -> [u8; HASH_SIZE] {
|
||||
crypto::hmac::hmac_256::<crypto::sha256::Sha256>(key, data)
|
||||
}
|
||||
|
||||
fn verify(key: &[u8; HMAC_KEY_SIZE], data: &[u8], mac: &[u8; HASH_SIZE]) -> bool {
|
||||
crypto::hmac::verify_hmac_256::<crypto::sha256::Sha256>(key, data, mac)
|
||||
}
|
||||
|
||||
fn verify_truncated_left(
|
||||
key: &[u8; HMAC_KEY_SIZE],
|
||||
data: &[u8],
|
||||
mac: &[u8; TRUNCATED_HMAC_SIZE],
|
||||
) -> bool {
|
||||
crypto::hmac::verify_hmac_256_first_128bits::<crypto::sha256::Sha256>(key, data, mac)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
@@ -160,6 +209,7 @@ mod test {
|
||||
};
|
||||
use crate::api::crypto::ecdsa::{PublicKey as EcdsaPublicKey, SecretKey as EcdsaSecretKey};
|
||||
use crate::env::test::TestEnv;
|
||||
use core::convert::TryFrom;
|
||||
|
||||
#[test]
|
||||
fn test_shared_secret_symmetric() {
|
||||
@@ -210,4 +260,27 @@ mod test {
|
||||
second_key.to_slice(&mut new_bytes);
|
||||
assert_eq!(key_bytes, new_bytes);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sha256_hash_matches() {
|
||||
let data = [0x55; 16];
|
||||
let mut hasher = SoftwareSha256::new();
|
||||
hasher.update(&data);
|
||||
assert_eq!(SoftwareSha256::digest(&data), hasher.finalize());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_hmac256_verifies() {
|
||||
let key = [0xAA; HMAC_KEY_SIZE];
|
||||
let data = [0x55; 16];
|
||||
let mac = SoftwareHmac256::mac(&key, &data);
|
||||
assert!(SoftwareHmac256::verify(&key, &data, &mac));
|
||||
let truncated_mac =
|
||||
<&[u8; TRUNCATED_HMAC_SIZE]>::try_from(&mac[..TRUNCATED_HMAC_SIZE]).unwrap();
|
||||
assert!(SoftwareHmac256::verify_truncated_left(
|
||||
&key,
|
||||
&data,
|
||||
truncated_mac
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,18 +22,16 @@ use super::status_code::Ctap2StatusCode;
|
||||
use super::token_state::PinUvAuthTokenState;
|
||||
#[cfg(test)]
|
||||
use crate::api::crypto::ecdh::SecretKey as _;
|
||||
use crate::api::crypto::hmac256::Hmac256;
|
||||
use crate::api::crypto::sha256::Sha256;
|
||||
use crate::api::customization::Customization;
|
||||
use crate::ctap::storage;
|
||||
#[cfg(test)]
|
||||
use crate::env::EcdhSk;
|
||||
use crate::env::Env;
|
||||
use alloc::boxed::Box;
|
||||
use crate::env::{Env, Hmac, Sha};
|
||||
use alloc::str;
|
||||
use alloc::string::String;
|
||||
use alloc::vec::Vec;
|
||||
use crypto::hmac::hmac_256;
|
||||
use crypto::sha256::Sha256;
|
||||
use crypto::Hash256;
|
||||
#[cfg(test)]
|
||||
use enum_iterator::IntoEnumIterator;
|
||||
use subtle::ConstantTimeEq;
|
||||
@@ -60,8 +58,8 @@ pub const PIN_TOKEN_LENGTH: usize = 32;
|
||||
const PIN_PADDED_LENGTH: usize = 64;
|
||||
|
||||
/// Decrypts the new_pin_enc and outputs the found PIN.
|
||||
fn decrypt_pin(
|
||||
shared_secret: &dyn SharedSecret,
|
||||
fn decrypt_pin<E: Env>(
|
||||
shared_secret: &SharedSecret<E>,
|
||||
new_pin_enc: Vec<u8>,
|
||||
) -> Result<Vec<u8>, Ctap2StatusCode> {
|
||||
let decrypted_pin = shared_secret.decrypt(&new_pin_enc)?;
|
||||
@@ -79,9 +77,9 @@ fn decrypt_pin(
|
||||
/// The new PIN is passed encrypted, so it is first decrypted and stripped from
|
||||
/// padding. Next, it is checked against the PIN policy. Last, it is hashed and
|
||||
/// truncated for persistent storage.
|
||||
fn check_and_store_new_pin(
|
||||
env: &mut impl Env,
|
||||
shared_secret: &dyn SharedSecret,
|
||||
fn check_and_store_new_pin<E: Env>(
|
||||
env: &mut E,
|
||||
shared_secret: &SharedSecret<E>,
|
||||
new_pin_enc: Vec<u8>,
|
||||
) -> Result<(), Ctap2StatusCode> {
|
||||
let pin = decrypt_pin(shared_secret, new_pin_enc)?;
|
||||
@@ -91,7 +89,7 @@ fn check_and_store_new_pin(
|
||||
return Err(Ctap2StatusCode::CTAP2_ERR_PIN_POLICY_VIOLATION);
|
||||
}
|
||||
let mut pin_hash = [0u8; PIN_AUTH_LENGTH];
|
||||
pin_hash.copy_from_slice(&Sha256::hash(&pin[..])[..PIN_AUTH_LENGTH]);
|
||||
pin_hash.copy_from_slice(&Sha::<E>::digest(&pin[..])[..PIN_AUTH_LENGTH]);
|
||||
// The PIN length is always < PIN_PADDED_LENGTH < 256.
|
||||
storage::set_pin(env, &pin_hash, pin_length as u8)?;
|
||||
Ok(())
|
||||
@@ -149,7 +147,7 @@ impl<E: Env> ClientPin<E> {
|
||||
&self,
|
||||
pin_uv_auth_protocol: PinUvAuthProtocol,
|
||||
key_agreement: CoseKey,
|
||||
) -> Result<Box<dyn SharedSecret>, Ctap2StatusCode> {
|
||||
) -> Result<SharedSecret<E>, Ctap2StatusCode> {
|
||||
self.get_pin_protocol(pin_uv_auth_protocol)
|
||||
.decapsulate(key_agreement, pin_uv_auth_protocol)
|
||||
}
|
||||
@@ -163,7 +161,7 @@ impl<E: Env> ClientPin<E> {
|
||||
&mut self,
|
||||
env: &mut E,
|
||||
pin_uv_auth_protocol: PinUvAuthProtocol,
|
||||
shared_secret: &dyn SharedSecret,
|
||||
shared_secret: &SharedSecret<E>,
|
||||
pin_hash_enc: Vec<u8>,
|
||||
) -> Result<(), Ctap2StatusCode> {
|
||||
match storage::pin_hash(env)? {
|
||||
@@ -247,7 +245,7 @@ impl<E: Env> ClientPin<E> {
|
||||
let shared_secret = self.get_shared_secret(pin_uv_auth_protocol, key_agreement)?;
|
||||
shared_secret.verify(&new_pin_enc, &pin_uv_auth_param)?;
|
||||
|
||||
check_and_store_new_pin(env, shared_secret.as_ref(), new_pin_enc)?;
|
||||
check_and_store_new_pin(env, &shared_secret, new_pin_enc)?;
|
||||
storage::reset_pin_retries(env)?;
|
||||
Ok(())
|
||||
}
|
||||
@@ -277,14 +275,9 @@ impl<E: Env> ClientPin<E> {
|
||||
let mut auth_param_data = new_pin_enc.clone();
|
||||
auth_param_data.extend(&pin_hash_enc);
|
||||
shared_secret.verify(&auth_param_data, &pin_uv_auth_param)?;
|
||||
self.verify_pin_hash_enc(
|
||||
env,
|
||||
pin_uv_auth_protocol,
|
||||
shared_secret.as_ref(),
|
||||
pin_hash_enc,
|
||||
)?;
|
||||
self.verify_pin_hash_enc(env, pin_uv_auth_protocol, &shared_secret, pin_hash_enc)?;
|
||||
|
||||
check_and_store_new_pin(env, shared_secret.as_ref(), new_pin_enc)?;
|
||||
check_and_store_new_pin(env, &shared_secret, new_pin_enc)?;
|
||||
self.pin_protocol_v1.reset_pin_uv_auth_token(env.rng());
|
||||
self.pin_protocol_v2.reset_pin_uv_auth_token(env.rng());
|
||||
Ok(())
|
||||
@@ -313,12 +306,7 @@ impl<E: Env> ClientPin<E> {
|
||||
return Err(Ctap2StatusCode::CTAP2_ERR_PIN_BLOCKED);
|
||||
}
|
||||
let shared_secret = self.get_shared_secret(pin_uv_auth_protocol, key_agreement)?;
|
||||
self.verify_pin_hash_enc(
|
||||
env,
|
||||
pin_uv_auth_protocol,
|
||||
shared_secret.as_ref(),
|
||||
pin_hash_enc,
|
||||
)?;
|
||||
self.verify_pin_hash_enc(env, pin_uv_auth_protocol, &shared_secret, pin_hash_enc)?;
|
||||
if storage::has_force_pin_change(env)? {
|
||||
return Err(Ctap2StatusCode::CTAP2_ERR_PIN_INVALID);
|
||||
}
|
||||
@@ -435,7 +423,7 @@ impl<E: Env> ClientPin<E> {
|
||||
if !self.pin_uv_auth_token_state.is_in_use() {
|
||||
return Err(Ctap2StatusCode::CTAP2_ERR_PIN_AUTH_INVALID);
|
||||
}
|
||||
verify_pin_uv_auth_token(
|
||||
verify_pin_uv_auth_token::<E>(
|
||||
self.get_pin_protocol(pin_uv_auth_protocol)
|
||||
.get_pin_uv_auth_token(),
|
||||
hmac_contents,
|
||||
@@ -483,9 +471,9 @@ impl<E: Env> ClientPin<E> {
|
||||
if decrypted_salts.len() != 32 && decrypted_salts.len() != 64 {
|
||||
return Err(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER);
|
||||
}
|
||||
let mut output = hmac_256::<Sha256>(cred_random, &decrypted_salts[..32]).to_vec();
|
||||
let mut output = Hmac::<E>::mac(cred_random, &decrypted_salts[..32]).to_vec();
|
||||
if decrypted_salts.len() == 64 {
|
||||
let mut output2 = hmac_256::<Sha256>(cred_random, &decrypted_salts[32..]).to_vec();
|
||||
let mut output2 = Hmac::<E>::mac(cred_random, &decrypted_salts[32..]).to_vec();
|
||||
output.append(&mut output2);
|
||||
}
|
||||
shared_secret.encrypt(env.rng(), &output)
|
||||
@@ -596,12 +584,12 @@ mod test {
|
||||
let mut pin = [0u8; 64];
|
||||
pin[..4].copy_from_slice(b"1234");
|
||||
let mut pin_hash = [0u8; 16];
|
||||
pin_hash.copy_from_slice(&Sha256::hash(&pin[..])[..16]);
|
||||
pin_hash.copy_from_slice(&Sha::<TestEnv>::digest(&pin[..])[..16]);
|
||||
storage::set_pin(env, &pin_hash, 4).unwrap();
|
||||
}
|
||||
|
||||
/// Fails on PINs bigger than 64 bytes.
|
||||
fn encrypt_pin(shared_secret: &dyn SharedSecret, pin: Vec<u8>) -> Vec<u8> {
|
||||
fn encrypt_pin(shared_secret: &SharedSecret<TestEnv>, pin: Vec<u8>) -> Vec<u8> {
|
||||
assert!(pin.len() <= 64);
|
||||
let mut env = TestEnv::default();
|
||||
let mut padded_pin = [0u8; 64];
|
||||
@@ -617,7 +605,7 @@ mod test {
|
||||
/// should fail.
|
||||
fn create_client_pin_and_shared_secret(
|
||||
pin_uv_auth_protocol: PinUvAuthProtocol,
|
||||
) -> (ClientPin<TestEnv>, Box<dyn SharedSecret>) {
|
||||
) -> (ClientPin<TestEnv>, SharedSecret<TestEnv>) {
|
||||
let mut env = TestEnv::default();
|
||||
let key_agreement_key = EcdhSk::<TestEnv>::random(env.rng());
|
||||
let pk = key_agreement_key.public_key();
|
||||
@@ -649,16 +637,10 @@ mod test {
|
||||
let pin = b"1234";
|
||||
let mut padded_pin = [0u8; 64];
|
||||
padded_pin[..pin.len()].copy_from_slice(&pin[..]);
|
||||
let pin_hash = Sha256::hash(&padded_pin);
|
||||
let new_pin_enc = shared_secret
|
||||
.as_ref()
|
||||
.encrypt(env.rng(), &padded_pin)
|
||||
.unwrap();
|
||||
let pin_uv_auth_param = shared_secret.as_ref().authenticate(&new_pin_enc);
|
||||
let pin_hash_enc = shared_secret
|
||||
.as_ref()
|
||||
.encrypt(env.rng(), &pin_hash[..16])
|
||||
.unwrap();
|
||||
let pin_hash = Sha::<TestEnv>::digest(&padded_pin);
|
||||
let new_pin_enc = shared_secret.encrypt(env.rng(), &padded_pin).unwrap();
|
||||
let pin_uv_auth_param = shared_secret.authenticate(&new_pin_enc);
|
||||
let pin_hash_enc = shared_secret.encrypt(env.rng(), &pin_hash[..16]).unwrap();
|
||||
let (permissions, permissions_rp_id) = match sub_command {
|
||||
ClientPinSubCommand::GetPinUvAuthTokenUsingUvWithPermissions
|
||||
| ClientPinSubCommand::GetPinUvAuthTokenUsingPinWithPermissions => {
|
||||
@@ -739,15 +721,12 @@ mod test {
|
||||
];
|
||||
storage::set_pin(&mut env, &pin_hash, 4).unwrap();
|
||||
|
||||
let pin_hash_enc = shared_secret
|
||||
.as_ref()
|
||||
.encrypt(env.rng(), &pin_hash)
|
||||
.unwrap();
|
||||
let pin_hash_enc = shared_secret.encrypt(env.rng(), &pin_hash).unwrap();
|
||||
assert_eq!(
|
||||
client_pin.verify_pin_hash_enc(
|
||||
&mut env,
|
||||
pin_uv_auth_protocol,
|
||||
shared_secret.as_ref(),
|
||||
&shared_secret,
|
||||
pin_hash_enc
|
||||
),
|
||||
Ok(())
|
||||
@@ -758,22 +737,19 @@ mod test {
|
||||
client_pin.verify_pin_hash_enc(
|
||||
&mut env,
|
||||
pin_uv_auth_protocol,
|
||||
shared_secret.as_ref(),
|
||||
&shared_secret,
|
||||
pin_hash_enc
|
||||
),
|
||||
Err(Ctap2StatusCode::CTAP2_ERR_PIN_INVALID)
|
||||
);
|
||||
|
||||
let pin_hash_enc = shared_secret
|
||||
.as_ref()
|
||||
.encrypt(env.rng(), &pin_hash)
|
||||
.unwrap();
|
||||
let pin_hash_enc = shared_secret.encrypt(env.rng(), &pin_hash).unwrap();
|
||||
client_pin.consecutive_pin_mismatches = 3;
|
||||
assert_eq!(
|
||||
client_pin.verify_pin_hash_enc(
|
||||
&mut env,
|
||||
pin_uv_auth_protocol,
|
||||
shared_secret.as_ref(),
|
||||
&shared_secret,
|
||||
pin_hash_enc
|
||||
),
|
||||
Err(Ctap2StatusCode::CTAP2_ERR_PIN_AUTH_BLOCKED)
|
||||
@@ -785,7 +761,7 @@ mod test {
|
||||
client_pin.verify_pin_hash_enc(
|
||||
&mut env,
|
||||
pin_uv_auth_protocol,
|
||||
shared_secret.as_ref(),
|
||||
&shared_secret,
|
||||
pin_hash_enc
|
||||
),
|
||||
Err(Ctap2StatusCode::CTAP2_ERR_PIN_INVALID)
|
||||
@@ -796,7 +772,7 @@ mod test {
|
||||
client_pin.verify_pin_hash_enc(
|
||||
&mut env,
|
||||
pin_uv_auth_protocol,
|
||||
shared_secret.as_ref(),
|
||||
&shared_secret,
|
||||
pin_hash_enc
|
||||
),
|
||||
Err(Ctap2StatusCode::CTAP2_ERR_PIN_INVALID)
|
||||
@@ -1176,29 +1152,29 @@ mod test {
|
||||
.decapsulate(pin_protocol.get_public_key(), pin_uv_auth_protocol)
|
||||
.unwrap();
|
||||
|
||||
let new_pin_enc = encrypt_pin(shared_secret.as_ref(), b"1234".to_vec());
|
||||
let new_pin_enc = encrypt_pin(&shared_secret, b"1234".to_vec());
|
||||
assert_eq!(
|
||||
decrypt_pin(shared_secret.as_ref(), new_pin_enc),
|
||||
decrypt_pin::<TestEnv>(&shared_secret, new_pin_enc),
|
||||
Ok(b"1234".to_vec()),
|
||||
);
|
||||
|
||||
let new_pin_enc = encrypt_pin(shared_secret.as_ref(), b"123".to_vec());
|
||||
let new_pin_enc = encrypt_pin(&shared_secret, b"123".to_vec());
|
||||
assert_eq!(
|
||||
decrypt_pin(shared_secret.as_ref(), new_pin_enc),
|
||||
decrypt_pin::<TestEnv>(&shared_secret, new_pin_enc),
|
||||
Ok(b"123".to_vec()),
|
||||
);
|
||||
|
||||
// Encrypted PIN is too short.
|
||||
let new_pin_enc = vec![0x44; 63];
|
||||
assert_eq!(
|
||||
decrypt_pin(shared_secret.as_ref(), new_pin_enc),
|
||||
decrypt_pin::<TestEnv>(&shared_secret, new_pin_enc),
|
||||
Err(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER)
|
||||
);
|
||||
|
||||
// Encrypted PIN is too long.
|
||||
let new_pin_enc = vec![0x44; 65];
|
||||
assert_eq!(
|
||||
decrypt_pin(shared_secret.as_ref(), new_pin_enc),
|
||||
decrypt_pin::<TestEnv>(&shared_secret, new_pin_enc),
|
||||
Err(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER)
|
||||
);
|
||||
}
|
||||
@@ -1241,10 +1217,10 @@ mod test {
|
||||
];
|
||||
for (pin, result) in test_cases {
|
||||
let old_pin_hash = storage::pin_hash(&mut env).unwrap();
|
||||
let new_pin_enc = encrypt_pin(shared_secret.as_ref(), pin);
|
||||
let new_pin_enc = encrypt_pin(&shared_secret, pin);
|
||||
|
||||
assert_eq!(
|
||||
check_and_store_new_pin(&mut env, shared_secret.as_ref(), new_pin_enc),
|
||||
check_and_store_new_pin(&mut env, &shared_secret, new_pin_enc),
|
||||
result
|
||||
);
|
||||
if result.is_ok() {
|
||||
@@ -1274,7 +1250,7 @@ mod test {
|
||||
let mut env = TestEnv::default();
|
||||
let (client_pin, shared_secret) = create_client_pin_and_shared_secret(pin_uv_auth_protocol);
|
||||
|
||||
let salt_enc = shared_secret.as_ref().encrypt(env.rng(), &salt).unwrap();
|
||||
let salt_enc = shared_secret.encrypt(env.rng(), &salt).unwrap();
|
||||
let salt_auth = shared_secret.authenticate(&salt_enc);
|
||||
let hmac_secret_input = GetAssertionHmacSecretInput {
|
||||
key_agreement: client_pin
|
||||
@@ -1285,7 +1261,7 @@ mod test {
|
||||
pin_uv_auth_protocol,
|
||||
};
|
||||
let output = client_pin.process_hmac_secret(&mut env, hmac_secret_input, cred_random);
|
||||
output.map(|v| shared_secret.as_ref().decrypt(&v).unwrap())
|
||||
output.map(|v| shared_secret.decrypt(&v).unwrap())
|
||||
}
|
||||
|
||||
fn test_helper_process_hmac_secret_bad_salt_auth(pin_uv_auth_protocol: PinUvAuthProtocol) {
|
||||
@@ -1322,7 +1298,7 @@ mod test {
|
||||
let cred_random = [0xC9; 32];
|
||||
|
||||
let salt = vec![0x01; 32];
|
||||
let expected_output = hmac_256::<Sha256>(&cred_random, &salt);
|
||||
let expected_output = Hmac::<TestEnv>::mac(&cred_random, &salt);
|
||||
|
||||
let output =
|
||||
get_process_hmac_secret_decrypted_output(pin_uv_auth_protocol, &cred_random, salt)
|
||||
@@ -1345,8 +1321,8 @@ mod test {
|
||||
|
||||
let salt1 = [0x01; 32];
|
||||
let salt2 = [0x02; 32];
|
||||
let expected_output1 = hmac_256::<Sha256>(&cred_random, &salt1);
|
||||
let expected_output2 = hmac_256::<Sha256>(&cred_random, &salt2);
|
||||
let expected_output1 = Hmac::<TestEnv>::mac(&cred_random, &salt1);
|
||||
let expected_output2 = Hmac::<TestEnv>::mac(&cred_random, &salt2);
|
||||
|
||||
let mut salt12 = vec![0x00; 64];
|
||||
salt12[..32].copy_from_slice(&salt1);
|
||||
@@ -1460,7 +1436,7 @@ mod test {
|
||||
fn test_has_no_or_rp_id_hash_permission() {
|
||||
let mut env = TestEnv::default();
|
||||
let mut client_pin = ClientPin::<TestEnv>::new(&mut env);
|
||||
let rp_id_hash = Sha256::hash(b"example.com");
|
||||
let rp_id_hash = Sha::<TestEnv>::digest(b"example.com");
|
||||
assert_eq!(
|
||||
client_pin.has_no_or_rp_id_hash_permission(&rp_id_hash),
|
||||
Ok(())
|
||||
|
||||
@@ -18,14 +18,13 @@ use super::data_formats::{
|
||||
};
|
||||
use super::status_code::Ctap2StatusCode;
|
||||
use super::{cbor_read, cbor_write};
|
||||
use crate::api::crypto::hmac256::Hmac256;
|
||||
use crate::api::key_store::KeyStore;
|
||||
use crate::ctap::data_formats::{extract_byte_string, extract_map};
|
||||
use crate::env::Env;
|
||||
use crate::env::{Env, Hmac};
|
||||
use alloc::string::String;
|
||||
use alloc::vec::Vec;
|
||||
use core::convert::{TryFrom, TryInto};
|
||||
use crypto::hmac::{hmac_256, verify_hmac_256};
|
||||
use crypto::sha256::Sha256;
|
||||
use sk_cbor::{cbor_map_options, destructure_cbor_map};
|
||||
|
||||
pub const LEGACY_CREDENTIAL_ID_SIZE: usize = 112;
|
||||
@@ -161,8 +160,8 @@ fn remove_padding(data: &mut Vec<u8>) -> Result<(), Ctap2StatusCode> {
|
||||
///
|
||||
/// Other information, such as a user name, are not stored. Since encrypted credential IDs are
|
||||
/// stored server-side, this information is already available (unencrypted).
|
||||
pub fn encrypt_to_credential_id(
|
||||
env: &mut impl Env,
|
||||
pub fn encrypt_to_credential_id<E: Env>(
|
||||
env: &mut E,
|
||||
private_key: &PrivateKey,
|
||||
rp_id_hash: &[u8; 32],
|
||||
cred_protect_policy: Option<CredentialProtectionPolicy>,
|
||||
@@ -183,7 +182,7 @@ pub fn encrypt_to_credential_id(
|
||||
let mut credential_id = encrypted_payload;
|
||||
credential_id.insert(0, CBOR_CREDENTIAL_ID_VERSION);
|
||||
|
||||
let id_hmac = hmac_256::<Sha256>(
|
||||
let id_hmac = Hmac::<E>::mac(
|
||||
&env.key_store().key_handle_authentication()?,
|
||||
&credential_id[..],
|
||||
);
|
||||
@@ -208,8 +207,8 @@ pub fn encrypt_to_credential_id(
|
||||
/// - 16 bytes: initialization vector for AES-256,
|
||||
/// - 192 bytes: encrypted CBOR-encoded credential source fields,
|
||||
/// - 32 bytes: HMAC-SHA256 over everything else.
|
||||
pub fn decrypt_credential_id(
|
||||
env: &mut impl Env,
|
||||
pub fn decrypt_credential_id<E: Env>(
|
||||
env: &mut E,
|
||||
credential_id: Vec<u8>,
|
||||
rp_id_hash: &[u8],
|
||||
) -> Result<Option<PublicKeyCredentialSource>, Ctap2StatusCode> {
|
||||
@@ -217,7 +216,7 @@ pub fn decrypt_credential_id(
|
||||
return Ok(None);
|
||||
}
|
||||
let hmac_message_size = credential_id.len() - 32;
|
||||
if !verify_hmac_256::<Sha256>(
|
||||
if !Hmac::<E>::verify(
|
||||
&env.key_store().key_handle_authentication()?,
|
||||
&credential_id[..hmac_message_size],
|
||||
array_ref![credential_id, hmac_message_size, 32],
|
||||
@@ -274,7 +273,6 @@ mod test {
|
||||
use crate::ctap::SignatureAlgorithm;
|
||||
use crate::env::test::TestEnv;
|
||||
use crate::env::EcdsaSk;
|
||||
use crypto::hmac::hmac_256;
|
||||
|
||||
const UNSUPPORTED_CREDENTIAL_ID_VERSION: u8 = 0x80;
|
||||
|
||||
@@ -315,7 +313,7 @@ mod test {
|
||||
// Override the HMAC to pass the check.
|
||||
encrypted_id.truncate(&encrypted_id.len() - 32);
|
||||
let hmac_key = env.key_store().key_handle_authentication().unwrap();
|
||||
let id_hmac = hmac_256::<Sha256>(&hmac_key, &encrypted_id[..]);
|
||||
let id_hmac = Hmac::<TestEnv>::mac(&hmac_key, &encrypted_id[..]);
|
||||
encrypted_id.extend(&id_hmac);
|
||||
|
||||
assert_eq!(
|
||||
@@ -392,7 +390,7 @@ mod test {
|
||||
plaintext[32..64].copy_from_slice(application);
|
||||
|
||||
let mut encrypted_id = aes256_cbc_encrypt(env.rng(), &aes_enc_key, &plaintext, true)?;
|
||||
let id_hmac = hmac_256::<Sha256>(
|
||||
let id_hmac = Hmac::<TestEnv>::mac(
|
||||
&env.key_store().key_handle_authentication()?,
|
||||
&encrypted_id[..],
|
||||
);
|
||||
|
||||
@@ -22,14 +22,13 @@ use super::data_formats::{
|
||||
use super::response::{AuthenticatorCredentialManagementResponse, ResponseData};
|
||||
use super::status_code::Ctap2StatusCode;
|
||||
use super::{Channel, StatefulCommand, StatefulPermission};
|
||||
use crate::api::crypto::sha256::Sha256;
|
||||
use crate::ctap::storage;
|
||||
use crate::env::Env;
|
||||
use crate::env::{Env, Sha};
|
||||
use alloc::collections::BTreeSet;
|
||||
use alloc::string::String;
|
||||
use alloc::vec;
|
||||
use alloc::vec::Vec;
|
||||
use crypto::sha256::Sha256;
|
||||
use crypto::Hash256;
|
||||
|
||||
/// Generates a set with all existing RP IDs.
|
||||
fn get_stored_rp_ids(env: &mut impl Env) -> Result<BTreeSet<String>, Ctap2StatusCode> {
|
||||
@@ -43,11 +42,11 @@ fn get_stored_rp_ids(env: &mut impl Env) -> Result<BTreeSet<String>, Ctap2Status
|
||||
}
|
||||
|
||||
/// Generates the response for subcommands enumerating RPs.
|
||||
fn enumerate_rps_response(
|
||||
fn enumerate_rps_response<E: Env>(
|
||||
rp_id: String,
|
||||
total_rps: Option<u64>,
|
||||
) -> Result<AuthenticatorCredentialManagementResponse, Ctap2StatusCode> {
|
||||
let rp_id_hash = Some(Sha256::hash(rp_id.as_bytes()).to_vec());
|
||||
let rp_id_hash = Some(Sha::<E>::digest(rp_id.as_bytes()).to_vec());
|
||||
let rp = Some(PublicKeyCredentialRpEntity {
|
||||
rp_id,
|
||||
rp_name: None,
|
||||
@@ -150,7 +149,7 @@ fn process_enumerate_rps_begin<E: Env>(
|
||||
.into_iter()
|
||||
.next()
|
||||
.ok_or(Ctap2StatusCode::CTAP2_ERR_NO_CREDENTIALS)?;
|
||||
enumerate_rps_response(rp_id, Some(total_rps as u64))
|
||||
enumerate_rps_response::<E>(rp_id, Some(total_rps as u64))
|
||||
}
|
||||
|
||||
/// Processes the subcommand enumerateRPsGetNextRP for CredentialManagement.
|
||||
@@ -165,7 +164,7 @@ fn process_enumerate_rps_get_next_rp<E: Env>(
|
||||
.into_iter()
|
||||
.nth(rp_id_index)
|
||||
.ok_or(Ctap2StatusCode::CTAP2_ERR_NOT_ALLOWED)?;
|
||||
enumerate_rps_response(rp_id, None)
|
||||
enumerate_rps_response::<E>(rp_id, None)
|
||||
}
|
||||
|
||||
/// Processes the subcommand enumerateCredentialsBegin for CredentialManagement.
|
||||
@@ -184,7 +183,7 @@ fn process_enumerate_credentials_begin<E: Env>(
|
||||
let iter = storage::iter_credentials(env, &mut iter_result)?;
|
||||
let mut rp_credentials: Vec<usize> = iter
|
||||
.filter_map(|(key, credential)| {
|
||||
let cred_rp_id_hash = Sha256::hash(credential.rp_id.as_bytes());
|
||||
let cred_rp_id_hash = Sha::<E>::digest(credential.rp_id.as_bytes());
|
||||
if cred_rp_id_hash == rp_id_hash.as_slice() {
|
||||
Some(key)
|
||||
} else {
|
||||
@@ -511,7 +510,7 @@ mod test {
|
||||
ResponseData::AuthenticatorCredentialManagement(Some(response)) => {
|
||||
assert_eq!(response.total_rps, Some(2));
|
||||
let rp_id = response.rp.unwrap().rp_id;
|
||||
let rp_id_hash = Sha256::hash(rp_id.as_bytes());
|
||||
let rp_id_hash = Sha::<TestEnv>::digest(rp_id.as_bytes());
|
||||
assert_eq!(rp_id_hash, response.rp_id_hash.unwrap().as_slice());
|
||||
rp_id
|
||||
}
|
||||
@@ -535,7 +534,7 @@ mod test {
|
||||
ResponseData::AuthenticatorCredentialManagement(Some(response)) => {
|
||||
assert_eq!(response.total_rps, None);
|
||||
let rp_id = response.rp.unwrap().rp_id;
|
||||
let rp_id_hash = Sha256::hash(rp_id.as_bytes());
|
||||
let rp_id_hash = Sha::<TestEnv>::digest(rp_id.as_bytes());
|
||||
assert_eq!(rp_id_hash, response.rp_id_hash.unwrap().as_slice());
|
||||
rp_id
|
||||
}
|
||||
@@ -617,7 +616,7 @@ mod test {
|
||||
assert_eq!(response.total_rps, None);
|
||||
}
|
||||
let rp_id = response.rp.unwrap().rp_id;
|
||||
let rp_id_hash = Sha256::hash(rp_id.as_bytes());
|
||||
let rp_id_hash = Sha::<TestEnv>::digest(rp_id.as_bytes());
|
||||
assert_eq!(rp_id_hash, response.rp_id_hash.unwrap().as_slice());
|
||||
assert!(!rp_set.contains(&rp_id));
|
||||
rp_set.insert(rp_id);
|
||||
@@ -676,7 +675,7 @@ mod test {
|
||||
]);
|
||||
|
||||
let sub_command_params = CredentialManagementSubCommandParameters {
|
||||
rp_id_hash: Some(Sha256::hash(b"example.com").to_vec()),
|
||||
rp_id_hash: Some(Sha::<TestEnv>::digest(b"example.com").to_vec()),
|
||||
credential_id: None,
|
||||
user: None,
|
||||
};
|
||||
|
||||
@@ -19,7 +19,7 @@ use super::CtapState;
|
||||
use crate::api::attestation_store::{self, Attestation, AttestationStore};
|
||||
use crate::api::crypto::ecdsa::{self, SecretKey as _, Signature};
|
||||
use crate::api::crypto::EC_FIELD_SIZE;
|
||||
use crate::env::Env;
|
||||
use crate::env::{EcdsaSk, Env};
|
||||
use alloc::vec::Vec;
|
||||
use arrayref::{array_ref, mut_array_refs};
|
||||
use core::convert::TryFrom;
|
||||
@@ -249,8 +249,8 @@ impl Ctap1Command {
|
||||
// +------+-------------------+-----------------+------------+--------------------+
|
||||
// + 0x00 | application (32B) | challenge (32B) | key handle | User pub key (65B) |
|
||||
// +------+-------------------+-----------------+------------+--------------------+
|
||||
fn process_register(
|
||||
env: &mut impl Env,
|
||||
fn process_register<E: Env>(
|
||||
env: &mut E,
|
||||
challenge: [u8; 32],
|
||||
application: [u8; 32],
|
||||
) -> Result<Vec<u8>, Ctap1StatusCode> {
|
||||
@@ -290,10 +290,10 @@ impl Ctap1Command {
|
||||
signature_data.extend(key_handle);
|
||||
signature_data.extend_from_slice(&user_pk);
|
||||
|
||||
let attestation_key = crypto::ecdsa::SecKey::from_bytes(&private_key).unwrap();
|
||||
let signature = attestation_key.sign_rfc6979::<crypto::sha256::Sha256>(&signature_data);
|
||||
let attestation_key = EcdsaSk::<E>::from_slice(&private_key).unwrap();
|
||||
let signature = attestation_key.sign(&signature_data);
|
||||
|
||||
response.extend(signature.to_asn1_der());
|
||||
response.extend(signature.to_der());
|
||||
Ok(response)
|
||||
}
|
||||
|
||||
@@ -357,10 +357,11 @@ mod test {
|
||||
use super::super::data_formats::SignatureAlgorithm;
|
||||
use super::super::TOUCH_TIMEOUT_MS;
|
||||
use super::*;
|
||||
use crate::api::crypto::sha256::Sha256;
|
||||
use crate::api::customization::Customization;
|
||||
use crate::ctap::storage;
|
||||
use crate::env::test::TestEnv;
|
||||
use crypto::Hash256;
|
||||
use crate::env::Sha;
|
||||
|
||||
fn create_register_message(application: &[u8; 32]) -> Vec<u8> {
|
||||
let mut message = vec![
|
||||
@@ -497,7 +498,7 @@ mod test {
|
||||
let mut ctap_state = CtapState::new(&mut env);
|
||||
|
||||
let rp_id = "example.com";
|
||||
let application = crypto::sha256::Sha256::hash(rp_id.as_bytes());
|
||||
let application = Sha::<TestEnv>::digest(rp_id.as_bytes());
|
||||
let key_handle = encrypt_to_credential_id(&mut env, &sk, &application, None, None).unwrap();
|
||||
let message = create_authenticate_message(&application, Ctap1Flags::CheckOnly, &key_handle);
|
||||
|
||||
@@ -514,7 +515,7 @@ mod test {
|
||||
let mut ctap_state = CtapState::new(&mut env);
|
||||
|
||||
let rp_id = "example.com";
|
||||
let application = crypto::sha256::Sha256::hash(rp_id.as_bytes());
|
||||
let application = Sha::<TestEnv>::digest(rp_id.as_bytes());
|
||||
let key_handle = encrypt_to_credential_id(&mut env, &sk, &application, None, None).unwrap();
|
||||
let application = [0x55; 32];
|
||||
let message = create_authenticate_message(&application, Ctap1Flags::CheckOnly, &key_handle);
|
||||
@@ -532,7 +533,7 @@ mod test {
|
||||
let mut ctap_state = CtapState::new(&mut env);
|
||||
|
||||
let rp_id = "example.com";
|
||||
let application = crypto::sha256::Sha256::hash(rp_id.as_bytes());
|
||||
let application = Sha::<TestEnv>::digest(rp_id.as_bytes());
|
||||
let key_handle = encrypt_to_credential_id(&mut env, &sk, &application, None, None).unwrap();
|
||||
let mut message = create_authenticate_message(
|
||||
&application,
|
||||
@@ -566,7 +567,7 @@ mod test {
|
||||
let mut ctap_state = CtapState::new(&mut env);
|
||||
|
||||
let rp_id = "example.com";
|
||||
let application = crypto::sha256::Sha256::hash(rp_id.as_bytes());
|
||||
let application = Sha::<TestEnv>::digest(rp_id.as_bytes());
|
||||
let key_handle = encrypt_to_credential_id(&mut env, &sk, &application, None, None).unwrap();
|
||||
let mut message =
|
||||
create_authenticate_message(&application, Ctap1Flags::CheckOnly, &key_handle);
|
||||
@@ -585,7 +586,7 @@ mod test {
|
||||
let mut ctap_state = CtapState::new(&mut env);
|
||||
|
||||
let rp_id = "example.com";
|
||||
let application = crypto::sha256::Sha256::hash(rp_id.as_bytes());
|
||||
let application = Sha::<TestEnv>::digest(rp_id.as_bytes());
|
||||
let key_handle = encrypt_to_credential_id(&mut env, &sk, &application, None, None).unwrap();
|
||||
let mut message =
|
||||
create_authenticate_message(&application, Ctap1Flags::CheckOnly, &key_handle);
|
||||
@@ -604,7 +605,7 @@ mod test {
|
||||
let mut ctap_state = CtapState::new(&mut env);
|
||||
|
||||
let rp_id = "example.com";
|
||||
let application = crypto::sha256::Sha256::hash(rp_id.as_bytes());
|
||||
let application = Sha::<TestEnv>::digest(rp_id.as_bytes());
|
||||
let key_handle = encrypt_to_credential_id(&mut env, &sk, &application, None, None).unwrap();
|
||||
let mut message =
|
||||
create_authenticate_message(&application, Ctap1Flags::CheckOnly, &key_handle);
|
||||
@@ -631,7 +632,7 @@ mod test {
|
||||
let mut ctap_state = CtapState::new(&mut env);
|
||||
|
||||
let rp_id = "example.com";
|
||||
let application = crypto::sha256::Sha256::hash(rp_id.as_bytes());
|
||||
let application = Sha::<TestEnv>::digest(rp_id.as_bytes());
|
||||
let key_handle = encrypt_to_credential_id(&mut env, &sk, &application, None, None).unwrap();
|
||||
let message =
|
||||
create_authenticate_message(&application, Ctap1Flags::EnforceUpAndSign, &key_handle);
|
||||
@@ -657,7 +658,7 @@ mod test {
|
||||
let mut ctap_state = CtapState::new(&mut env);
|
||||
|
||||
let rp_id = "example.com";
|
||||
let application = crypto::sha256::Sha256::hash(rp_id.as_bytes());
|
||||
let application = Sha::<TestEnv>::digest(rp_id.as_bytes());
|
||||
let key_handle = encrypt_to_credential_id(&mut env, &sk, &application, None, None).unwrap();
|
||||
let message = create_authenticate_message(
|
||||
&application,
|
||||
|
||||
@@ -16,14 +16,13 @@ use super::client_pin::{ClientPin, PinPermission};
|
||||
use super::command::AuthenticatorLargeBlobsParameters;
|
||||
use super::response::{AuthenticatorLargeBlobsResponse, ResponseData};
|
||||
use super::status_code::Ctap2StatusCode;
|
||||
use crate::api::crypto::sha256::Sha256;
|
||||
use crate::api::customization::Customization;
|
||||
use crate::ctap::storage;
|
||||
use crate::env::Env;
|
||||
use crate::env::{Env, Sha};
|
||||
use alloc::vec;
|
||||
use alloc::vec::Vec;
|
||||
use byteorder::{ByteOrder, LittleEndian};
|
||||
use crypto::sha256::Sha256;
|
||||
use crypto::Hash256;
|
||||
|
||||
/// The length of the truncated hash that is appended to the large blob data.
|
||||
const TRUNCATED_HASH_LEN: usize = 16;
|
||||
@@ -97,7 +96,7 @@ impl LargeBlobs {
|
||||
let mut offset_bytes = [0u8; 4];
|
||||
LittleEndian::write_u32(&mut offset_bytes, offset as u32);
|
||||
large_blob_data.extend(&offset_bytes);
|
||||
large_blob_data.extend(&Sha256::hash(set.as_slice()));
|
||||
large_blob_data.extend(&Sha::<E>::digest(set.as_slice()));
|
||||
client_pin.verify_pin_uv_auth_token(
|
||||
&large_blob_data,
|
||||
&pin_uv_auth_param,
|
||||
@@ -118,7 +117,7 @@ impl LargeBlobs {
|
||||
self.expected_next_offset = 0;
|
||||
// Must be a positive number.
|
||||
let buffer_hash_index = self.buffer.len() - TRUNCATED_HASH_LEN;
|
||||
if Sha256::hash(&self.buffer[..buffer_hash_index])[..TRUNCATED_HASH_LEN]
|
||||
if Sha::<E>::digest(&self.buffer[..buffer_hash_index])[..TRUNCATED_HASH_LEN]
|
||||
!= self.buffer[buffer_hash_index..]
|
||||
{
|
||||
self.buffer = Vec::new();
|
||||
@@ -195,7 +194,8 @@ mod test {
|
||||
const BLOB_LEN: usize = 200;
|
||||
const DATA_LEN: usize = BLOB_LEN - TRUNCATED_HASH_LEN;
|
||||
let mut large_blob = vec![0x1B; DATA_LEN];
|
||||
large_blob.extend_from_slice(&Sha256::hash(&large_blob[..])[..TRUNCATED_HASH_LEN]);
|
||||
large_blob
|
||||
.extend_from_slice(&Sha::<TestEnv>::digest(&large_blob[..])[..TRUNCATED_HASH_LEN]);
|
||||
|
||||
let large_blobs_params = AuthenticatorLargeBlobsParameters {
|
||||
get: None,
|
||||
@@ -261,7 +261,8 @@ mod test {
|
||||
const BLOB_LEN: usize = 200;
|
||||
const DATA_LEN: usize = BLOB_LEN - TRUNCATED_HASH_LEN;
|
||||
let mut large_blob = vec![0x1B; DATA_LEN];
|
||||
large_blob.extend_from_slice(&Sha256::hash(&large_blob[..])[..TRUNCATED_HASH_LEN]);
|
||||
large_blob
|
||||
.extend_from_slice(&Sha::<TestEnv>::digest(&large_blob[..])[..TRUNCATED_HASH_LEN]);
|
||||
|
||||
let large_blobs_params = AuthenticatorLargeBlobsParameters {
|
||||
get: None,
|
||||
@@ -311,7 +312,8 @@ mod test {
|
||||
const BLOB_LEN: usize = 200;
|
||||
const DATA_LEN: usize = BLOB_LEN - TRUNCATED_HASH_LEN;
|
||||
let mut large_blob = vec![0x1B; DATA_LEN];
|
||||
large_blob.extend_from_slice(&Sha256::hash(&large_blob[..])[..TRUNCATED_HASH_LEN]);
|
||||
large_blob
|
||||
.extend_from_slice(&Sha::<TestEnv>::digest(&large_blob[..])[..TRUNCATED_HASH_LEN]);
|
||||
|
||||
let large_blobs_params = AuthenticatorLargeBlobsParameters {
|
||||
get: None,
|
||||
@@ -420,13 +422,14 @@ mod test {
|
||||
const BLOB_LEN: usize = 20;
|
||||
const DATA_LEN: usize = BLOB_LEN - TRUNCATED_HASH_LEN;
|
||||
let mut large_blob = vec![0x1B; DATA_LEN];
|
||||
large_blob.extend_from_slice(&Sha256::hash(&large_blob[..])[..TRUNCATED_HASH_LEN]);
|
||||
large_blob
|
||||
.extend_from_slice(&Sha::<TestEnv>::digest(&large_blob[..])[..TRUNCATED_HASH_LEN]);
|
||||
|
||||
storage::set_pin(&mut env, &[0u8; 16], 4).unwrap();
|
||||
let mut large_blob_data = vec![0xFF; 32];
|
||||
// Command constant and offset bytes.
|
||||
large_blob_data.extend(&[0x0C, 0x00, 0x00, 0x00, 0x00, 0x00]);
|
||||
large_blob_data.extend(&Sha256::hash(&large_blob));
|
||||
large_blob_data.extend(&Sha::<TestEnv>::digest(&large_blob));
|
||||
let pin_uv_auth_param = authenticate_pin_uv_auth_token(
|
||||
&pin_uv_auth_token,
|
||||
&large_blob_data,
|
||||
|
||||
@@ -66,20 +66,19 @@ use crate::api::attestation_store::{self, Attestation, AttestationStore};
|
||||
use crate::api::clock::Clock;
|
||||
use crate::api::connection::{HidConnection, SendOrRecvStatus, UsbEndpoint};
|
||||
use crate::api::crypto::ecdsa::{SecretKey as _, Signature};
|
||||
use crate::api::crypto::hmac256::Hmac256;
|
||||
use crate::api::crypto::sha256::Sha256;
|
||||
use crate::api::customization::Customization;
|
||||
use crate::api::firmware_protection::FirmwareProtection;
|
||||
use crate::api::upgrade_storage::UpgradeStorage;
|
||||
use crate::api::user_presence::{UserPresence, UserPresenceError};
|
||||
use crate::env::{EcdsaSk, Env};
|
||||
use crate::env::{EcdsaSk, Env, Hmac, Sha};
|
||||
use alloc::boxed::Box;
|
||||
use alloc::string::{String, ToString};
|
||||
use alloc::vec;
|
||||
use alloc::vec::Vec;
|
||||
use byteorder::{BigEndian, ByteOrder};
|
||||
use core::convert::TryFrom;
|
||||
use crypto::hmac::hmac_256;
|
||||
use crypto::sha256::Sha256;
|
||||
use crypto::Hash256;
|
||||
use rng256::Rng256;
|
||||
use sk_cbor as cbor;
|
||||
use sk_cbor::cbor_map_options;
|
||||
@@ -781,7 +780,7 @@ impl<E: Env> CtapState<E> {
|
||||
};
|
||||
flags |= UP_FLAG | AT_FLAG;
|
||||
|
||||
let rp_id_hash = Sha256::hash(rp_id.as_bytes());
|
||||
let rp_id_hash = Sha::<E>::digest(rp_id.as_bytes());
|
||||
if let Some(exclude_list) = exclude_list {
|
||||
for cred_desc in exclude_list {
|
||||
if self.check_cred_protect_for_listed_credential(
|
||||
@@ -958,7 +957,7 @@ impl<E: Env> CtapState<E> {
|
||||
) -> Result<[u8; 32], Ctap2StatusCode> {
|
||||
let entropy = private_key.to_bytes();
|
||||
let key = storage::cred_random_secret(env, has_uv)?;
|
||||
Ok(hmac_256::<Sha256>(&key, &entropy))
|
||||
Ok(Hmac::<E>::mac(&key, &entropy))
|
||||
}
|
||||
|
||||
// Processes the input of a get_assertion operation for a given credential
|
||||
@@ -1135,7 +1134,7 @@ impl<E: Env> CtapState<E> {
|
||||
flags |= ED_FLAG;
|
||||
}
|
||||
|
||||
let rp_id_hash = Sha256::hash(rp_id.as_bytes());
|
||||
let rp_id_hash = Sha::<E>::digest(rp_id.as_bytes());
|
||||
let (credential, next_credential_keys) = if let Some(allow_list) = allow_list {
|
||||
(
|
||||
self.get_any_credential_from_allow_list(
|
||||
@@ -1382,7 +1381,7 @@ impl<E: Env> CtapState<E> {
|
||||
params: AuthenticatorVendorUpgradeParameters,
|
||||
) -> Result<ResponseData, Ctap2StatusCode> {
|
||||
let AuthenticatorVendorUpgradeParameters { offset, data, hash } = params;
|
||||
let calculated_hash = Sha256::hash(&data);
|
||||
let calculated_hash = Sha::<E>::digest(&data);
|
||||
if hash != calculated_hash {
|
||||
return Err(Ctap2StatusCode::CTAP2_ERR_INTEGRITY_FAILURE);
|
||||
}
|
||||
@@ -2415,7 +2414,7 @@ mod test {
|
||||
.unwrap();
|
||||
|
||||
let salt = vec![0x01; 32];
|
||||
let salt_enc = shared_secret.as_ref().encrypt(env.rng(), &salt).unwrap();
|
||||
let salt_enc = shared_secret.encrypt(env.rng(), &salt).unwrap();
|
||||
let salt_auth = shared_secret.authenticate(&salt_enc);
|
||||
let hmac_secret_input = GetAssertionHmacSecretInput {
|
||||
key_agreement: CoseKey::from_ecdh_public_key(platform_public_key),
|
||||
@@ -3332,9 +3331,9 @@ mod test {
|
||||
|
||||
const METADATA_LEN: usize = 0x1000;
|
||||
let metadata = vec![0xFF; METADATA_LEN];
|
||||
let metadata_hash = Sha256::hash(&metadata);
|
||||
let metadata_hash = Sha::<TestEnv>::digest(&metadata);
|
||||
let data = vec![0xFF; 0x1000];
|
||||
let hash = Sha256::hash(&data);
|
||||
let hash = Sha::<TestEnv>::digest(&data);
|
||||
|
||||
// Write to partition.
|
||||
let response = ctap_state.process_vendor_upgrade(
|
||||
@@ -3410,7 +3409,7 @@ mod test {
|
||||
let mut ctap_state = CtapState::<TestEnv>::new(&mut env);
|
||||
|
||||
let data = vec![0xFF; 0x1000];
|
||||
let hash = Sha256::hash(&data);
|
||||
let hash = Sha::<TestEnv>::digest(&data);
|
||||
let response = ctap_state.process_vendor_upgrade(
|
||||
&mut env,
|
||||
AuthenticatorVendorUpgradeParameters {
|
||||
|
||||
@@ -12,22 +12,19 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::api::crypto::ecdh::{
|
||||
PublicKey as EcdhPublicKey, SecretKey as EcdhSecretKey, SharedSecret as EcdhSharedSecret,
|
||||
};
|
||||
use crate::api::crypto::ecdh::{PublicKey as _, SecretKey as _, SharedSecret as _};
|
||||
use crate::api::crypto::hmac256::Hmac256;
|
||||
use crate::api::crypto::sha256::Sha256;
|
||||
use crate::ctap::client_pin::PIN_TOKEN_LENGTH;
|
||||
use crate::ctap::crypto_wrapper::{aes256_cbc_decrypt, aes256_cbc_encrypt};
|
||||
use crate::ctap::data_formats::{CoseKey, PinUvAuthProtocol};
|
||||
use crate::ctap::status_code::Ctap2StatusCode;
|
||||
use crate::env::{EcdhPk, EcdhSk, Env};
|
||||
use alloc::boxed::Box;
|
||||
use alloc::vec::Vec;
|
||||
use crypto::hkdf::hkdf_empty_salt_256;
|
||||
#[cfg(test)]
|
||||
use crypto::hmac::hmac_256;
|
||||
use crypto::hmac::{verify_hmac_256, verify_hmac_256_first_128bits};
|
||||
use crypto::sha256::Sha256;
|
||||
use crypto::Hash256;
|
||||
use crate::env::test::TestEnv;
|
||||
use crate::env::{EcdhPk, EcdhSk, Env, Hmac, Sha};
|
||||
use alloc::vec::Vec;
|
||||
use core::marker::PhantomData;
|
||||
use crypto::hkdf::hkdf_empty_salt_256;
|
||||
use rng256::Rng256;
|
||||
|
||||
/// Implements common functions between existing PIN protocols for handshakes.
|
||||
@@ -69,7 +66,7 @@ impl<E: Env> PinProtocol<E> {
|
||||
&self,
|
||||
peer_cose_key: CoseKey,
|
||||
pin_uv_auth_protocol: PinUvAuthProtocol,
|
||||
) -> Result<Box<dyn SharedSecret>, Ctap2StatusCode> {
|
||||
) -> Result<SharedSecret<E>, Ctap2StatusCode> {
|
||||
let (x_bytes, y_bytes) = peer_cose_key.try_into_ecdh_coordinates()?;
|
||||
let pk = EcdhPk::<E>::from_coordinates(&x_bytes, &y_bytes)
|
||||
.ok_or(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER)?;
|
||||
@@ -77,10 +74,7 @@ impl<E: Env> PinProtocol<E> {
|
||||
.key_agreement_key
|
||||
.diffie_hellman(&pk)
|
||||
.raw_secret_bytes();
|
||||
match pin_uv_auth_protocol {
|
||||
PinUvAuthProtocol::V1 => Ok(Box::new(SharedSecretV1::new(handshake))),
|
||||
PinUvAuthProtocol::V2 => Ok(Box::new(SharedSecretV2::new(handshake))),
|
||||
}
|
||||
Ok(SharedSecret::new(pin_uv_auth_protocol, handshake))
|
||||
}
|
||||
|
||||
/// Getter for pinUvAuthToken.
|
||||
@@ -109,79 +103,127 @@ pub fn authenticate_pin_uv_auth_token(
|
||||
pin_uv_auth_protocol: PinUvAuthProtocol,
|
||||
) -> Vec<u8> {
|
||||
match pin_uv_auth_protocol {
|
||||
PinUvAuthProtocol::V1 => hmac_256::<Sha256>(token, message)[..16].to_vec(),
|
||||
PinUvAuthProtocol::V2 => hmac_256::<Sha256>(token, message).to_vec(),
|
||||
PinUvAuthProtocol::V1 => Hmac::<TestEnv>::mac(token, message)[..16].to_vec(),
|
||||
PinUvAuthProtocol::V2 => Hmac::<TestEnv>::mac(token, message).to_vec(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Verifies the pinUvAuthToken for the given PIN protocol.
|
||||
pub fn verify_pin_uv_auth_token(
|
||||
pub fn verify_pin_uv_auth_token<E: Env>(
|
||||
token: &[u8; PIN_TOKEN_LENGTH],
|
||||
message: &[u8],
|
||||
signature: &[u8],
|
||||
pin_uv_auth_protocol: PinUvAuthProtocol,
|
||||
) -> Result<(), Ctap2StatusCode> {
|
||||
match pin_uv_auth_protocol {
|
||||
PinUvAuthProtocol::V1 => verify_v1(token, message, signature),
|
||||
PinUvAuthProtocol::V2 => verify_v2(token, message, signature),
|
||||
PinUvAuthProtocol::V1 => verify_v1::<E>(token, message, signature),
|
||||
PinUvAuthProtocol::V2 => verify_v2::<E>(token, message, signature),
|
||||
}
|
||||
}
|
||||
|
||||
pub trait SharedSecret {
|
||||
pub enum SharedSecret<E: Env> {
|
||||
V1(SharedSecretV1<E>),
|
||||
V2(SharedSecretV2<E>),
|
||||
}
|
||||
|
||||
impl<E: Env> SharedSecret<E> {
|
||||
/// Creates a new shared secret for the respective PIN protocol.
|
||||
///
|
||||
/// This enum wraps all types of shared secrets.
|
||||
fn new(pin_uv_auth_protocol: PinUvAuthProtocol, handshake: [u8; 32]) -> Self {
|
||||
match pin_uv_auth_protocol {
|
||||
PinUvAuthProtocol::V1 => SharedSecret::V1(SharedSecretV1::new(handshake)),
|
||||
PinUvAuthProtocol::V2 => SharedSecret::V2(SharedSecretV2::new(handshake)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the encrypted plaintext.
|
||||
fn encrypt(&self, rng: &mut dyn Rng256, plaintext: &[u8]) -> Result<Vec<u8>, Ctap2StatusCode>;
|
||||
pub fn encrypt(
|
||||
&self,
|
||||
rng: &mut dyn Rng256,
|
||||
plaintext: &[u8],
|
||||
) -> Result<Vec<u8>, Ctap2StatusCode> {
|
||||
match self {
|
||||
SharedSecret::V1(s) => s.encrypt(rng, plaintext),
|
||||
SharedSecret::V2(s) => s.encrypt(rng, plaintext),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the decrypted ciphertext.
|
||||
fn decrypt(&self, ciphertext: &[u8]) -> Result<Vec<u8>, Ctap2StatusCode>;
|
||||
pub fn decrypt(&self, ciphertext: &[u8]) -> Result<Vec<u8>, Ctap2StatusCode> {
|
||||
match self {
|
||||
SharedSecret::V1(s) => s.decrypt(ciphertext),
|
||||
SharedSecret::V2(s) => s.decrypt(ciphertext),
|
||||
}
|
||||
}
|
||||
|
||||
/// Verifies that the signature is a valid MAC for the given message.
|
||||
fn verify(&self, message: &[u8], signature: &[u8]) -> Result<(), Ctap2StatusCode>;
|
||||
pub fn verify(&self, message: &[u8], signature: &[u8]) -> Result<(), Ctap2StatusCode> {
|
||||
match self {
|
||||
SharedSecret::V1(s) => s.verify(message, signature),
|
||||
SharedSecret::V2(s) => s.verify(message, signature),
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a signature that matches verify.
|
||||
#[cfg(test)]
|
||||
fn authenticate(&self, message: &[u8]) -> Vec<u8>;
|
||||
}
|
||||
|
||||
fn verify_v1(key: &[u8; 32], message: &[u8], signature: &[u8]) -> Result<(), Ctap2StatusCode> {
|
||||
if signature.len() != 16 {
|
||||
return Err(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER);
|
||||
}
|
||||
if verify_hmac_256_first_128bits::<Sha256>(key, message, array_ref![signature, 0, 16]) {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Ctap2StatusCode::CTAP2_ERR_PIN_AUTH_INVALID)
|
||||
}
|
||||
}
|
||||
|
||||
fn verify_v2(key: &[u8; 32], message: &[u8], signature: &[u8]) -> Result<(), Ctap2StatusCode> {
|
||||
if signature.len() != 32 {
|
||||
return Err(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER);
|
||||
}
|
||||
if verify_hmac_256::<Sha256>(key, message, array_ref![signature, 0, 32]) {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Ctap2StatusCode::CTAP2_ERR_PIN_AUTH_INVALID)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SharedSecretV1 {
|
||||
common_secret: [u8; 32],
|
||||
aes_enc_key: crypto::aes256::EncryptionKey,
|
||||
}
|
||||
|
||||
impl SharedSecretV1 {
|
||||
/// Creates a new shared secret from the handshake result.
|
||||
fn new(handshake: [u8; 32]) -> SharedSecretV1 {
|
||||
let common_secret = Sha256::hash(&handshake);
|
||||
let aes_enc_key = crypto::aes256::EncryptionKey::new(&common_secret);
|
||||
SharedSecretV1 {
|
||||
common_secret,
|
||||
aes_enc_key,
|
||||
pub fn authenticate(&self, message: &[u8]) -> Vec<u8> {
|
||||
match self {
|
||||
SharedSecret::V1(s) => s.authenticate(message),
|
||||
SharedSecret::V2(s) => s.authenticate(message),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SharedSecret for SharedSecretV1 {
|
||||
fn verify_v1<E: Env>(
|
||||
key: &[u8; 32],
|
||||
message: &[u8],
|
||||
signature: &[u8],
|
||||
) -> Result<(), Ctap2StatusCode> {
|
||||
if signature.len() != 16 {
|
||||
return Err(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER);
|
||||
}
|
||||
if Hmac::<E>::verify_truncated_left(key, message, array_ref![signature, 0, 16]) {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Ctap2StatusCode::CTAP2_ERR_PIN_AUTH_INVALID)
|
||||
}
|
||||
}
|
||||
|
||||
fn verify_v2<E: Env>(
|
||||
key: &[u8; 32],
|
||||
message: &[u8],
|
||||
signature: &[u8],
|
||||
) -> Result<(), Ctap2StatusCode> {
|
||||
if signature.len() != 32 {
|
||||
return Err(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER);
|
||||
}
|
||||
if Hmac::<E>::verify(key, message, array_ref![signature, 0, 32]) {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Ctap2StatusCode::CTAP2_ERR_PIN_AUTH_INVALID)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SharedSecretV1<E: Env> {
|
||||
common_secret: [u8; 32],
|
||||
aes_enc_key: crypto::aes256::EncryptionKey,
|
||||
// TODO: remove after porting AES to env crypto
|
||||
phantom: PhantomData<E>,
|
||||
}
|
||||
|
||||
impl<E: Env> SharedSecretV1<E> {
|
||||
/// Creates a new shared secret from the handshake result.
|
||||
fn new(handshake: [u8; 32]) -> Self {
|
||||
let common_secret = Sha::<E>::digest(&handshake);
|
||||
let aes_enc_key = crypto::aes256::EncryptionKey::new(&common_secret);
|
||||
SharedSecretV1 {
|
||||
common_secret,
|
||||
aes_enc_key,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
fn encrypt(&self, rng: &mut dyn Rng256, plaintext: &[u8]) -> Result<Vec<u8>, Ctap2StatusCode> {
|
||||
aes256_cbc_encrypt(rng, &self.aes_enc_key, plaintext, false)
|
||||
}
|
||||
@@ -191,32 +233,33 @@ impl SharedSecret for SharedSecretV1 {
|
||||
}
|
||||
|
||||
fn verify(&self, message: &[u8], signature: &[u8]) -> Result<(), Ctap2StatusCode> {
|
||||
verify_v1(&self.common_secret, message, signature)
|
||||
verify_v1::<E>(&self.common_secret, message, signature)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn authenticate(&self, message: &[u8]) -> Vec<u8> {
|
||||
hmac_256::<Sha256>(&self.common_secret, message)[..16].to_vec()
|
||||
Hmac::<E>::mac(&self.common_secret, message)[..16].to_vec()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SharedSecretV2 {
|
||||
pub struct SharedSecretV2<E: Env> {
|
||||
aes_enc_key: crypto::aes256::EncryptionKey,
|
||||
hmac_key: [u8; 32],
|
||||
// TODO: remove after porting AES to env crypto
|
||||
phantom: PhantomData<E>,
|
||||
}
|
||||
|
||||
impl SharedSecretV2 {
|
||||
impl<E: Env> SharedSecretV2<E> {
|
||||
/// Creates a new shared secret from the handshake result.
|
||||
fn new(handshake: [u8; 32]) -> SharedSecretV2 {
|
||||
let aes_key = hkdf_empty_salt_256::<Sha256>(&handshake, b"CTAP2 AES key");
|
||||
fn new(handshake: [u8; 32]) -> Self {
|
||||
let aes_key = hkdf_empty_salt_256::<crypto::sha256::Sha256>(&handshake, b"CTAP2 AES key");
|
||||
SharedSecretV2 {
|
||||
aes_enc_key: crypto::aes256::EncryptionKey::new(&aes_key),
|
||||
hmac_key: hkdf_empty_salt_256::<Sha256>(&handshake, b"CTAP2 HMAC key"),
|
||||
hmac_key: hkdf_empty_salt_256::<crypto::sha256::Sha256>(&handshake, b"CTAP2 HMAC key"),
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SharedSecret for SharedSecretV2 {
|
||||
fn encrypt(&self, rng: &mut dyn Rng256, plaintext: &[u8]) -> Result<Vec<u8>, Ctap2StatusCode> {
|
||||
aes256_cbc_encrypt(rng, &self.aes_enc_key, plaintext, true)
|
||||
}
|
||||
@@ -226,12 +269,12 @@ impl SharedSecret for SharedSecretV2 {
|
||||
}
|
||||
|
||||
fn verify(&self, message: &[u8], signature: &[u8]) -> Result<(), Ctap2StatusCode> {
|
||||
verify_v2(&self.hmac_key, message, signature)
|
||||
verify_v2::<E>(&self.hmac_key, message, signature)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn authenticate(&self, message: &[u8]) -> Vec<u8> {
|
||||
hmac_256::<Sha256>(&self.hmac_key, message).to_vec()
|
||||
Hmac::<E>::mac(&self.hmac_key, message).to_vec()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -263,7 +306,7 @@ mod test {
|
||||
#[test]
|
||||
fn test_shared_secret_v1_encrypt_decrypt() {
|
||||
let mut env = TestEnv::default();
|
||||
let shared_secret = SharedSecretV1::new([0x55; 32]);
|
||||
let shared_secret = SharedSecretV1::<TestEnv>::new([0x55; 32]);
|
||||
let plaintext = vec![0xAA; 64];
|
||||
let ciphertext = shared_secret.encrypt(env.rng(), &plaintext).unwrap();
|
||||
assert_eq!(shared_secret.decrypt(&ciphertext), Ok(plaintext));
|
||||
@@ -271,7 +314,7 @@ mod test {
|
||||
|
||||
#[test]
|
||||
fn test_shared_secret_v1_authenticate_verify() {
|
||||
let shared_secret = SharedSecretV1::new([0x55; 32]);
|
||||
let shared_secret = SharedSecretV1::<TestEnv>::new([0x55; 32]);
|
||||
let message = [0xAA; 32];
|
||||
let signature = shared_secret.authenticate(&message);
|
||||
assert_eq!(shared_secret.verify(&message, &signature), Ok(()));
|
||||
@@ -279,7 +322,7 @@ mod test {
|
||||
|
||||
#[test]
|
||||
fn test_shared_secret_v1_verify() {
|
||||
let shared_secret = SharedSecretV1::new([0x55; 32]);
|
||||
let shared_secret = SharedSecretV1::<TestEnv>::new([0x55; 32]);
|
||||
let message = [0xAA];
|
||||
let signature = [
|
||||
0x8B, 0x60, 0x15, 0x7D, 0xF3, 0x44, 0x82, 0x2E, 0x54, 0x34, 0x7A, 0x01, 0xFB, 0x02,
|
||||
@@ -299,7 +342,7 @@ mod test {
|
||||
#[test]
|
||||
fn test_shared_secret_v2_encrypt_decrypt() {
|
||||
let mut env = TestEnv::default();
|
||||
let shared_secret = SharedSecretV2::new([0x55; 32]);
|
||||
let shared_secret = SharedSecretV2::<TestEnv>::new([0x55; 32]);
|
||||
let plaintext = vec![0xAA; 64];
|
||||
let ciphertext = shared_secret.encrypt(env.rng(), &plaintext).unwrap();
|
||||
assert_eq!(shared_secret.decrypt(&ciphertext), Ok(plaintext));
|
||||
@@ -307,7 +350,7 @@ mod test {
|
||||
|
||||
#[test]
|
||||
fn test_shared_secret_v2_authenticate_verify() {
|
||||
let shared_secret = SharedSecretV2::new([0x55; 32]);
|
||||
let shared_secret = SharedSecretV2::<TestEnv>::new([0x55; 32]);
|
||||
let message = [0xAA; 32];
|
||||
let signature = shared_secret.authenticate(&message);
|
||||
assert_eq!(shared_secret.verify(&message, &signature), Ok(()));
|
||||
@@ -315,7 +358,7 @@ mod test {
|
||||
|
||||
#[test]
|
||||
fn test_shared_secret_v2_verify() {
|
||||
let shared_secret = SharedSecretV2::new([0x55; 32]);
|
||||
let shared_secret = SharedSecretV2::<TestEnv>::new([0x55; 32]);
|
||||
let message = [0xAA];
|
||||
let signature = [
|
||||
0xC0, 0x3F, 0x2A, 0x22, 0x5C, 0xC3, 0x4E, 0x05, 0xC1, 0x0E, 0x72, 0x9C, 0x8D, 0xD5,
|
||||
@@ -360,11 +403,16 @@ mod test {
|
||||
0x49, 0x68,
|
||||
];
|
||||
assert_eq!(
|
||||
verify_pin_uv_auth_token(&token, &message, &signature, PinUvAuthProtocol::V1),
|
||||
verify_pin_uv_auth_token::<TestEnv>(
|
||||
&token,
|
||||
&message,
|
||||
&signature,
|
||||
PinUvAuthProtocol::V1
|
||||
),
|
||||
Ok(())
|
||||
);
|
||||
assert_eq!(
|
||||
verify_pin_uv_auth_token(
|
||||
verify_pin_uv_auth_token::<TestEnv>(
|
||||
&[0x12; PIN_TOKEN_LENGTH],
|
||||
&message,
|
||||
&signature,
|
||||
@@ -373,11 +421,16 @@ mod test {
|
||||
Err(Ctap2StatusCode::CTAP2_ERR_PIN_AUTH_INVALID)
|
||||
);
|
||||
assert_eq!(
|
||||
verify_pin_uv_auth_token(&token, &[0xBB], &signature, PinUvAuthProtocol::V1),
|
||||
verify_pin_uv_auth_token::<TestEnv>(&token, &[0xBB], &signature, PinUvAuthProtocol::V1),
|
||||
Err(Ctap2StatusCode::CTAP2_ERR_PIN_AUTH_INVALID)
|
||||
);
|
||||
assert_eq!(
|
||||
verify_pin_uv_auth_token(&token, &message, &[0x12; 16], PinUvAuthProtocol::V1),
|
||||
verify_pin_uv_auth_token::<TestEnv>(
|
||||
&token,
|
||||
&message,
|
||||
&[0x12; 16],
|
||||
PinUvAuthProtocol::V1
|
||||
),
|
||||
Err(Ctap2StatusCode::CTAP2_ERR_PIN_AUTH_INVALID)
|
||||
);
|
||||
}
|
||||
@@ -392,11 +445,16 @@ mod test {
|
||||
0x36, 0x93, 0xF7, 0x84,
|
||||
];
|
||||
assert_eq!(
|
||||
verify_pin_uv_auth_token(&token, &message, &signature, PinUvAuthProtocol::V2),
|
||||
verify_pin_uv_auth_token::<TestEnv>(
|
||||
&token,
|
||||
&message,
|
||||
&signature,
|
||||
PinUvAuthProtocol::V2
|
||||
),
|
||||
Ok(())
|
||||
);
|
||||
assert_eq!(
|
||||
verify_pin_uv_auth_token(
|
||||
verify_pin_uv_auth_token::<TestEnv>(
|
||||
&[0x12; PIN_TOKEN_LENGTH],
|
||||
&message,
|
||||
&signature,
|
||||
@@ -405,11 +463,16 @@ mod test {
|
||||
Err(Ctap2StatusCode::CTAP2_ERR_PIN_AUTH_INVALID)
|
||||
);
|
||||
assert_eq!(
|
||||
verify_pin_uv_auth_token(&token, &[0xBB], &signature, PinUvAuthProtocol::V2),
|
||||
verify_pin_uv_auth_token::<TestEnv>(&token, &[0xBB], &signature, PinUvAuthProtocol::V2),
|
||||
Err(Ctap2StatusCode::CTAP2_ERR_PIN_AUTH_INVALID)
|
||||
);
|
||||
assert_eq!(
|
||||
verify_pin_uv_auth_token(&token, &message, &[0x12; 32], PinUvAuthProtocol::V2),
|
||||
verify_pin_uv_auth_token::<TestEnv>(
|
||||
&token,
|
||||
&message,
|
||||
&[0x12; 32],
|
||||
PinUvAuthProtocol::V2
|
||||
),
|
||||
Err(Ctap2StatusCode::CTAP2_ERR_PIN_AUTH_INVALID)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -13,12 +13,11 @@
|
||||
// limitations under the License.
|
||||
|
||||
use crate::api::clock::Clock;
|
||||
use crate::api::crypto::sha256::Sha256;
|
||||
use crate::ctap::client_pin::PinPermission;
|
||||
use crate::ctap::status_code::Ctap2StatusCode;
|
||||
use crate::env::Env;
|
||||
use crate::env::{Env, Sha};
|
||||
use alloc::string::String;
|
||||
use crypto::sha256::Sha256;
|
||||
use crypto::Hash256;
|
||||
|
||||
/// Timeout for auth tokens.
|
||||
///
|
||||
@@ -87,7 +86,7 @@ impl<E: Env> PinUvAuthTokenState<E> {
|
||||
/// Checks if the permissions RPID's association matches the hash.
|
||||
pub fn has_permissions_rp_id_hash(&self, rp_id_hash: &[u8]) -> Result<(), Ctap2StatusCode> {
|
||||
match &self.permissions_rp_id {
|
||||
Some(p) if rp_id_hash == Sha256::hash(p.as_bytes()) => Ok(()),
|
||||
Some(p) if rp_id_hash == Sha::<E>::digest(p.as_bytes()) => Ok(()),
|
||||
_ => Err(Ctap2StatusCode::CTAP2_ERR_PIN_AUTH_INVALID),
|
||||
}
|
||||
}
|
||||
@@ -210,7 +209,7 @@ mod test {
|
||||
#[test]
|
||||
fn test_permissions_rp_id_none() {
|
||||
let mut token_state = PinUvAuthTokenState::<TestEnv>::new();
|
||||
let example_hash = Sha256::hash(b"example.com");
|
||||
let example_hash = Sha::<TestEnv>::digest(b"example.com");
|
||||
token_state.set_permissions_rp_id(None);
|
||||
assert_eq!(token_state.has_no_permissions_rp_id(), Ok(()));
|
||||
assert_eq!(
|
||||
@@ -226,7 +225,7 @@ mod test {
|
||||
#[test]
|
||||
fn test_permissions_rp_id_some() {
|
||||
let mut token_state = PinUvAuthTokenState::<TestEnv>::new();
|
||||
let example_hash = Sha256::hash(b"example.com");
|
||||
let example_hash = Sha::<TestEnv>::digest(b"example.com");
|
||||
token_state.set_permissions_rp_id(Some(String::from("example.com")));
|
||||
|
||||
assert_eq!(
|
||||
|
||||
2
libraries/opensk/src/env/mod.rs
vendored
2
libraries/opensk/src/env/mod.rs
vendored
@@ -33,6 +33,8 @@ pub type EcdhSk<E> = <<<E as Env>::Crypto as Crypto>::Ecdh as Ecdh>::SecretKey;
|
||||
pub type EcdhPk<E> = <<<E as Env>::Crypto as Crypto>::Ecdh as Ecdh>::PublicKey;
|
||||
pub type EcdsaSk<E> = <<<E as Env>::Crypto as Crypto>::Ecdsa as Ecdsa>::SecretKey;
|
||||
pub type EcdsaPk<E> = <<<E as Env>::Crypto as Crypto>::Ecdsa as Ecdsa>::PublicKey;
|
||||
pub type Sha<E> = <<E as Env>::Crypto as Crypto>::Sha256;
|
||||
pub type Hmac<E> = <<E as Env>::Crypto as Crypto>::Hmac256;
|
||||
|
||||
/// Describes what CTAP needs to function.
|
||||
pub trait Env {
|
||||
|
||||
Reference in New Issue
Block a user