Adds a trait for crypto, porting EC first (#606)

* Adds a trait for crypto, porting EC first

* Moves crypto implementation next to its trait

* Renames constants and types
This commit is contained in:
kaczmarczyck
2023-04-04 13:54:41 +02:00
committed by GitHub
parent 80b82ffd42
commit c168141b60
25 changed files with 880 additions and 219 deletions

View File

@@ -276,7 +276,6 @@ impl PubKey {
ExponentP256::modn(u.to_int()) == *sign.r.as_exponent()
}
#[cfg(feature = "std")]
pub fn verify_vartime<H>(&self, msg: &[u8], sign: &Signature) -> bool
where
H: Hash256,

View File

@@ -22,6 +22,8 @@ subtle = { version = "2.2", default-features = false, features = ["nightly"] }
arbitrary = { version = "0.4.7", features = ["derive"], optional = true }
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 }
[features]
debug_ctap = []
@@ -30,6 +32,7 @@ with_ctap1 = ["crypto/with_ctap1"]
vendor_hid = []
fuzz = ["arbitrary", "std"]
ed25519 = ["ed25519-compact"]
rust_crypto = ["p256", "rand_core"]
[dev-dependencies]
enum-iterator = "0.6.0"

View File

@@ -0,0 +1,53 @@
// 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::EC_FIELD_SIZE;
use rng256::Rng256;
/// Container for all ECDH cryptographic material.
pub trait Ecdh {
type SecretKey: SecretKey<PublicKey = Self::PublicKey, SharedSecret = Self::SharedSecret>;
type PublicKey: PublicKey;
type SharedSecret: SharedSecret;
}
/// ECDH ephemeral key.
pub trait SecretKey {
type PublicKey: PublicKey;
type SharedSecret: SharedSecret;
/// Generates a new random secret key.
fn random(rng: &mut impl Rng256) -> Self;
/// Computes the corresponding public key for this private key.
fn public_key(&self) -> Self::PublicKey;
/// Computes the shared secret when using Elliptic-curve DiffieHellman.
fn diffie_hellman(&self, public_key: &Self::PublicKey) -> Self::SharedSecret;
}
/// ECDH public key.
pub trait PublicKey: Sized {
/// Creates a public key from its coordinates.
fn from_coordinates(x: &[u8; EC_FIELD_SIZE], y: &[u8; EC_FIELD_SIZE]) -> Option<Self>;
/// Writes the public key coordinates into the passed in parameters.
fn to_coordinates(&self, x: &mut [u8; EC_FIELD_SIZE], y: &mut [u8; EC_FIELD_SIZE]);
}
/// ECDH shared secret.
pub trait SharedSecret {
/// Exports the x component of the point computed by DiffieHellman.
fn raw_secret_bytes(&self) -> [u8; EC_FIELD_SIZE];
}

View File

@@ -0,0 +1,72 @@
// 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::{EC_FIELD_SIZE, EC_SIGNATURE_SIZE};
use alloc::vec::Vec;
use rng256::Rng256;
/// Container for all ECDSA cryptographic material.
pub trait Ecdsa {
type SecretKey: SecretKey<PublicKey = Self::PublicKey, Signature = Self::Signature>;
type PublicKey: PublicKey<Signature = Self::Signature>;
type Signature: Signature;
}
/// ECDSA signing key.
pub trait SecretKey: Sized {
type PublicKey: PublicKey;
type Signature: Signature;
/// Generates a new random secret key.
fn random(rng: &mut impl Rng256) -> Self;
/// Creates a signing key from its representation in bytes.
fn from_slice(bytes: &[u8; EC_FIELD_SIZE]) -> Option<Self>;
/// Computes the corresponding public key for this private key.
fn public_key(&self) -> Self::PublicKey;
/// Signs the message.
///
/// For hashing, SHA256 is used implicitly.
fn sign(&self, message: &[u8]) -> Self::Signature;
/// Writes the signing key bytes into the passed in parameter.
fn to_slice(&self, bytes: &mut [u8; EC_FIELD_SIZE]);
}
/// ECDSA verifying key.
pub trait PublicKey: Sized {
type Signature: Signature;
/// Creates a public key from its coordinates.
fn from_coordinates(x: &[u8; EC_FIELD_SIZE], y: &[u8; EC_FIELD_SIZE]) -> Option<Self>;
/// Verifies if the signature matches the message.
///
/// For hashing, SHA256 is used implicitly.
fn verify(&self, message: &[u8], signature: &Self::Signature) -> bool;
/// Writes the public key coordinates into the passed in parameters.
fn to_coordinates(&self, x: &mut [u8; EC_FIELD_SIZE], y: &mut [u8; EC_FIELD_SIZE]);
}
/// ECDSA signature.
pub trait Signature: Sized {
/// Creates a signature from its affine coordinates, represented as concatenated bytes.
fn from_slice(bytes: &[u8; EC_SIGNATURE_SIZE]) -> Option<Self>;
/// Encodes the signatures as ASN1 DER.
fn to_der(&self) -> Vec<u8>;
}

View 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.
pub mod ecdh;
pub mod ecdsa;
#[cfg(feature = "rust_crypto")]
pub mod rust_crypto;
#[cfg(not(feature = "rust_crypto"))]
pub mod software_crypto;
#[cfg(feature = "rust_crypto")]
pub use rust_crypto as software_crypto;
use self::ecdh::Ecdh;
use self::ecdsa::Ecdsa;
/// The size of field elements in the elliptic curve P256.
pub const EC_FIELD_SIZE: usize = 32;
/// The size of a serialized ECDSA signature.
pub const EC_SIGNATURE_SIZE: usize = 2 * EC_FIELD_SIZE;
/// Necessary cryptographic primitives for CTAP.
pub trait Crypto {
type Ecdh: Ecdh;
type Ecdsa: Ecdsa;
}

View File

@@ -0,0 +1,249 @@
// 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.
//! This cryptography implementation is an alternative for our own library.
//!
//! You can use it with the `rust_crypto` feature. An example call to cargo test is in
//! `run_desktop_tests.sh`. It is currently impossible to use it with our version of TockOS due to
//! a compiler version imcompatibility.
//!
//! 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 core::convert::TryFrom;
use p256::ecdh::EphemeralSecret;
use p256::ecdsa::signature::{SignatureEncoding, Signer, Verifier};
use p256::ecdsa::{SigningKey, VerifyingKey};
use p256::elliptic_curve::sec1::ToEncodedPoint;
// TODO: implement CryptoRngCore for our Rng instead
use rand_core::OsRng;
use rng256::Rng256;
pub struct SoftwareCrypto;
pub struct SoftwareEcdh;
pub struct SoftwareEcdsa;
impl Crypto for SoftwareCrypto {
type Ecdh = SoftwareEcdh;
type Ecdsa = SoftwareEcdsa;
}
impl ecdh::Ecdh for SoftwareEcdh {
type SecretKey = SoftwareEcdhSecretKey;
type PublicKey = SoftwareEcdhPublicKey;
type SharedSecret = SoftwareEcdhSharedSecret;
}
pub struct SoftwareEcdhSecretKey {
ephemeral_secret: EphemeralSecret,
}
impl ecdh::SecretKey for SoftwareEcdhSecretKey {
type PublicKey = SoftwareEcdhPublicKey;
type SharedSecret = SoftwareEcdhSharedSecret;
fn random(_rng: &mut impl Rng256) -> Self {
let ephemeral_secret = EphemeralSecret::random(&mut OsRng);
Self { ephemeral_secret }
}
fn public_key(&self) -> Self::PublicKey {
let public_key = self.ephemeral_secret.public_key();
SoftwareEcdhPublicKey { public_key }
}
fn diffie_hellman(&self, public_key: &SoftwareEcdhPublicKey) -> Self::SharedSecret {
let shared_secret = self.ephemeral_secret.diffie_hellman(&public_key.public_key);
SoftwareEcdhSharedSecret { shared_secret }
}
}
pub struct SoftwareEcdhPublicKey {
public_key: p256::PublicKey,
}
impl ecdh::PublicKey for SoftwareEcdhPublicKey {
fn from_coordinates(x: &[u8; EC_FIELD_SIZE], y: &[u8; EC_FIELD_SIZE]) -> Option<Self> {
let encoded_point: p256::EncodedPoint =
p256::EncodedPoint::from_affine_coordinates(x.into(), y.into(), false);
let public_key = p256::PublicKey::from_sec1_bytes(encoded_point.as_bytes()).ok()?;
Some(Self { public_key })
}
fn to_coordinates(&self, x: &mut [u8; EC_FIELD_SIZE], y: &mut [u8; EC_FIELD_SIZE]) {
let point = self.public_key.to_encoded_point(false);
x.copy_from_slice(point.x().unwrap());
y.copy_from_slice(point.y().unwrap());
}
}
pub struct SoftwareEcdhSharedSecret {
shared_secret: p256::ecdh::SharedSecret,
}
impl ecdh::SharedSecret for SoftwareEcdhSharedSecret {
fn raw_secret_bytes(&self) -> [u8; EC_FIELD_SIZE] {
let mut bytes = [0; EC_FIELD_SIZE];
bytes.copy_from_slice(self.shared_secret.raw_secret_bytes().as_slice());
bytes
}
}
impl ecdsa::Ecdsa for SoftwareEcdsa {
type SecretKey = SoftwareEcdsaSecretKey;
type PublicKey = SoftwareEcdsaPublicKey;
type Signature = SoftwareEcdsaSignature;
}
pub struct SoftwareEcdsaSecretKey {
signing_key: SigningKey,
}
impl ecdsa::SecretKey for SoftwareEcdsaSecretKey {
type PublicKey = SoftwareEcdsaPublicKey;
type Signature = SoftwareEcdsaSignature;
fn random(_rng: &mut impl Rng256) -> Self {
let signing_key = SigningKey::random(&mut OsRng);
SoftwareEcdsaSecretKey { signing_key }
}
fn from_slice(bytes: &[u8; EC_FIELD_SIZE]) -> Option<Self> {
let signing_key = SigningKey::from_slice(bytes).ok()?;
Some(SoftwareEcdsaSecretKey { signing_key })
}
fn public_key(&self) -> Self::PublicKey {
let verifying_key = VerifyingKey::from(&self.signing_key);
SoftwareEcdsaPublicKey { verifying_key }
}
fn sign(&self, message: &[u8]) -> Self::Signature {
let signature = self.signing_key.sign(message);
SoftwareEcdsaSignature { signature }
}
fn to_slice(&self, bytes: &mut [u8; EC_FIELD_SIZE]) {
bytes.copy_from_slice(&self.signing_key.to_bytes());
}
}
pub struct SoftwareEcdsaPublicKey {
verifying_key: VerifyingKey,
}
impl ecdsa::PublicKey for SoftwareEcdsaPublicKey {
type Signature = SoftwareEcdsaSignature;
fn from_coordinates(x: &[u8; EC_FIELD_SIZE], y: &[u8; EC_FIELD_SIZE]) -> Option<Self> {
let encoded_point: p256::EncodedPoint =
p256::EncodedPoint::from_affine_coordinates(x.into(), y.into(), false);
let verifying_key = VerifyingKey::from_encoded_point(&encoded_point).ok()?;
Some(SoftwareEcdsaPublicKey { verifying_key })
}
fn verify(&self, message: &[u8], signature: &Self::Signature) -> bool {
self.verifying_key
.verify(message, &signature.signature)
.is_ok()
}
fn to_coordinates(&self, x: &mut [u8; EC_FIELD_SIZE], y: &mut [u8; EC_FIELD_SIZE]) {
let point = self.verifying_key.to_encoded_point(false);
x.copy_from_slice(point.x().unwrap());
y.copy_from_slice(point.y().unwrap());
}
}
pub struct SoftwareEcdsaSignature {
signature: p256::ecdsa::Signature,
}
impl ecdsa::Signature for SoftwareEcdsaSignature {
fn from_slice(bytes: &[u8; EC_SIGNATURE_SIZE]) -> Option<Self> {
// Assumes EC_SIGNATURE_SIZE == 2 * EC_FIELD_SIZE
let r = &bytes[..EC_FIELD_SIZE];
let s = &bytes[EC_FIELD_SIZE..];
let r = p256::NonZeroScalar::try_from(r).ok()?;
let s = p256::NonZeroScalar::try_from(s).ok()?;
let r = p256::FieldBytes::from(r);
let s = p256::FieldBytes::from(s);
let signature = p256::ecdsa::Signature::from_scalars(r, s).ok()?;
Some(SoftwareEcdsaSignature { signature })
}
fn to_der(&self) -> Vec<u8> {
self.signature.to_der().to_vec()
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::api::crypto::ecdh::{
PublicKey as EcdhPublicKey, SecretKey as EcdhSecretKey, SharedSecret,
};
use crate::api::crypto::ecdsa::{PublicKey as EcdsaPublicKey, SecretKey as EcdsaSecretKey};
use crate::env::test::TestEnv;
#[test]
fn test_shared_secret_symmetric() {
let mut env = TestEnv::default();
let private1 = SoftwareEcdhSecretKey::random(env.rng());
let private2 = SoftwareEcdhSecretKey::random(env.rng());
let pub1 = private1.public_key();
let pub2 = private2.public_key();
let shared1 = private1.diffie_hellman(&pub2);
let shared2 = private2.diffie_hellman(&pub1);
assert_eq!(shared1.raw_secret_bytes(), shared2.raw_secret_bytes());
}
#[test]
fn test_ecdh_public_key_from_to_bytes() {
let mut env = TestEnv::default();
let first_key = SoftwareEcdhSecretKey::random(env.rng());
let first_public = first_key.public_key();
let mut x = [0; EC_FIELD_SIZE];
let mut y = [0; EC_FIELD_SIZE];
first_public.to_coordinates(&mut x, &mut y);
let new_public = SoftwareEcdhPublicKey::from_coordinates(&x, &y).unwrap();
let mut new_x = [0; EC_FIELD_SIZE];
let mut new_y = [0; EC_FIELD_SIZE];
new_public.to_coordinates(&mut new_x, &mut new_y);
assert_eq!(x, new_x);
assert_eq!(y, new_y);
}
#[test]
fn test_sign_verify() {
let mut env = TestEnv::default();
let private_key = SoftwareEcdsaSecretKey::random(env.rng());
let public_key = private_key.public_key();
let message = [0x12, 0x34, 0x56, 0x78];
let signature = private_key.sign(&message);
assert!(public_key.verify(&message, &signature));
}
#[test]
fn test_ecdsa_secret_key_from_to_bytes() {
let mut env = TestEnv::default();
let first_key = SoftwareEcdsaSecretKey::random(env.rng());
let mut key_bytes = [0; EC_FIELD_SIZE];
first_key.to_slice(&mut key_bytes);
let second_key = SoftwareEcdsaSecretKey::from_slice(&key_bytes).unwrap();
let mut new_bytes = [0; EC_FIELD_SIZE];
second_key.to_slice(&mut new_bytes);
assert_eq!(key_bytes, new_bytes);
}
}

View File

@@ -0,0 +1,213 @@
// 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 crate::api::crypto::{ecdh, ecdsa, Crypto, EC_FIELD_SIZE, EC_SIGNATURE_SIZE};
use alloc::vec::Vec;
use rng256::Rng256;
pub struct SoftwareCrypto;
pub struct SoftwareEcdh;
pub struct SoftwareEcdsa;
impl Crypto for SoftwareCrypto {
type Ecdh = SoftwareEcdh;
type Ecdsa = SoftwareEcdsa;
}
impl ecdh::Ecdh for SoftwareEcdh {
type SecretKey = SoftwareEcdhSecretKey;
type PublicKey = SoftwareEcdhPublicKey;
type SharedSecret = SoftwareEcdhSharedSecret;
}
pub struct SoftwareEcdhSecretKey {
sec_key: crypto::ecdh::SecKey,
}
impl ecdh::SecretKey for SoftwareEcdhSecretKey {
type PublicKey = SoftwareEcdhPublicKey;
type SharedSecret = SoftwareEcdhSharedSecret;
fn random(rng: &mut impl Rng256) -> Self {
let sec_key = crypto::ecdh::SecKey::gensk(rng);
Self { sec_key }
}
fn public_key(&self) -> Self::PublicKey {
let pub_key = self.sec_key.genpk();
SoftwareEcdhPublicKey { pub_key }
}
fn diffie_hellman(&self, public_key: &SoftwareEcdhPublicKey) -> Self::SharedSecret {
let shared_secret = self.sec_key.exchange_x(&public_key.pub_key);
SoftwareEcdhSharedSecret { shared_secret }
}
}
pub struct SoftwareEcdhPublicKey {
pub_key: crypto::ecdh::PubKey,
}
impl ecdh::PublicKey for SoftwareEcdhPublicKey {
fn from_coordinates(x: &[u8; EC_FIELD_SIZE], y: &[u8; EC_FIELD_SIZE]) -> Option<Self> {
crypto::ecdh::PubKey::from_coordinates(x, y).map(|k| Self { pub_key: k })
}
fn to_coordinates(&self, x: &mut [u8; EC_FIELD_SIZE], y: &mut [u8; EC_FIELD_SIZE]) {
self.pub_key.to_coordinates(x, y);
}
}
pub struct SoftwareEcdhSharedSecret {
shared_secret: [u8; EC_FIELD_SIZE],
}
impl ecdh::SharedSecret for SoftwareEcdhSharedSecret {
fn raw_secret_bytes(&self) -> [u8; EC_FIELD_SIZE] {
self.shared_secret
}
}
impl ecdsa::Ecdsa for SoftwareEcdsa {
type SecretKey = SoftwareEcdsaSecretKey;
type PublicKey = SoftwareEcdsaPublicKey;
type Signature = SoftwareEcdsaSignature;
}
pub struct SoftwareEcdsaSecretKey {
sec_key: crypto::ecdsa::SecKey,
}
impl ecdsa::SecretKey for SoftwareEcdsaSecretKey {
type PublicKey = SoftwareEcdsaPublicKey;
type Signature = SoftwareEcdsaSignature;
fn random(rng: &mut impl Rng256) -> Self {
let sec_key = crypto::ecdsa::SecKey::gensk(rng);
Self { sec_key }
}
fn from_slice(bytes: &[u8; EC_FIELD_SIZE]) -> Option<Self> {
crypto::ecdsa::SecKey::from_bytes(bytes).map(|k| Self { sec_key: k })
}
fn public_key(&self) -> Self::PublicKey {
let pub_key = self.sec_key.genpk();
SoftwareEcdsaPublicKey { pub_key }
}
fn sign(&self, message: &[u8]) -> Self::Signature {
let signature = self.sec_key.sign_rfc6979::<crypto::sha256::Sha256>(message);
SoftwareEcdsaSignature { signature }
}
fn to_slice(&self, bytes: &mut [u8; EC_FIELD_SIZE]) {
self.sec_key.to_bytes(bytes);
}
}
pub struct SoftwareEcdsaPublicKey {
pub_key: crypto::ecdsa::PubKey,
}
impl ecdsa::PublicKey for SoftwareEcdsaPublicKey {
type Signature = SoftwareEcdsaSignature;
fn from_coordinates(x: &[u8; EC_FIELD_SIZE], y: &[u8; EC_FIELD_SIZE]) -> Option<Self> {
crypto::ecdsa::PubKey::from_coordinates(x, y).map(|k| Self { pub_key: k })
}
fn verify(&self, message: &[u8], signature: &Self::Signature) -> bool {
self.pub_key
.verify_vartime::<crypto::sha256::Sha256>(message, &signature.signature)
}
fn to_coordinates(&self, x: &mut [u8; EC_FIELD_SIZE], y: &mut [u8; EC_FIELD_SIZE]) {
self.pub_key.to_coordinates(x, y);
}
}
pub struct SoftwareEcdsaSignature {
signature: crypto::ecdsa::Signature,
}
impl ecdsa::Signature for SoftwareEcdsaSignature {
fn from_slice(bytes: &[u8; EC_SIGNATURE_SIZE]) -> Option<Self> {
crypto::ecdsa::Signature::from_bytes(bytes).map(|s| SoftwareEcdsaSignature { signature: s })
}
fn to_der(&self) -> Vec<u8> {
self.signature.to_asn1_der()
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::api::crypto::ecdh::{
PublicKey as EcdhPublicKey, SecretKey as EcdhSecretKey, SharedSecret,
};
use crate::api::crypto::ecdsa::{PublicKey as EcdsaPublicKey, SecretKey as EcdsaSecretKey};
use crate::env::test::TestEnv;
#[test]
fn test_shared_secret_symmetric() {
let mut env = TestEnv::default();
let private1 = SoftwareEcdhSecretKey::random(env.rng());
let private2 = SoftwareEcdhSecretKey::random(env.rng());
let pub1 = private1.public_key();
let pub2 = private2.public_key();
let shared1 = private1.diffie_hellman(&pub2);
let shared2 = private2.diffie_hellman(&pub1);
assert_eq!(shared1.raw_secret_bytes(), shared2.raw_secret_bytes());
}
#[test]
fn test_ecdh_public_key_from_to_bytes() {
let mut env = TestEnv::default();
let first_key = SoftwareEcdhSecretKey::random(env.rng());
let first_public = first_key.public_key();
let mut x = [0; EC_FIELD_SIZE];
let mut y = [0; EC_FIELD_SIZE];
first_public.to_coordinates(&mut x, &mut y);
let new_public = SoftwareEcdhPublicKey::from_coordinates(&x, &y).unwrap();
let mut new_x = [0; EC_FIELD_SIZE];
let mut new_y = [0; EC_FIELD_SIZE];
new_public.to_coordinates(&mut new_x, &mut new_y);
assert_eq!(x, new_x);
assert_eq!(y, new_y);
}
#[test]
fn test_sign_verify() {
let mut env = TestEnv::default();
let private_key = SoftwareEcdsaSecretKey::random(env.rng());
let public_key = private_key.public_key();
let message = [0x12, 0x34, 0x56, 0x78];
let signature = private_key.sign(&message);
assert!(public_key.verify(&message, &signature));
}
#[test]
fn test_ecdsa_secret_key_from_to_bytes() {
let mut env = TestEnv::default();
let first_key = SoftwareEcdsaSecretKey::random(env.rng());
let mut key_bytes = [0; EC_FIELD_SIZE];
first_key.to_slice(&mut key_bytes);
let second_key = SoftwareEcdsaSecretKey::from_slice(&key_bytes).unwrap();
let mut new_bytes = [0; EC_FIELD_SIZE];
second_key.to_slice(&mut new_bytes);
assert_eq!(key_bytes, new_bytes);
}
}

View File

@@ -12,13 +12,12 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::api::crypto::ecdsa::SecretKey as _;
use crate::env::{EcdsaSk, Env};
use alloc::vec::Vec;
use crypto::ecdsa::SecKey;
use persistent_store::StoreError;
use rng256::Rng256;
use crate::env::Env;
/// Provides storage for secret keys.
///
/// Implementations may use the environment store: [`STORAGE_KEY`] is reserved for this usage.
@@ -63,7 +62,7 @@ impl<T: Helper> KeyStore for T {
}
fn derive_ecdsa(&mut self, seed: &[u8; 32]) -> Result<[u8; 32], Error> {
match SecKey::from_bytes(seed) {
match EcdsaSk::<T>::from_slice(seed) {
None => Err(Error),
Some(_) => Ok(*seed),
}
@@ -71,7 +70,7 @@ impl<T: Helper> KeyStore for T {
fn generate_ecdsa_seed(&mut self) -> Result<[u8; 32], Error> {
let mut seed = [0; 32];
SecKey::gensk(self.rng()).to_bytes(&mut seed);
EcdsaSk::<T>::random(self.rng()).to_slice(&mut seed);
Ok(seed)
}

View File

@@ -20,6 +20,7 @@
pub mod attestation_store;
pub mod clock;
pub mod connection;
pub mod crypto;
pub mod customization;
pub mod firmware_protection;
pub mod key_store;

View File

@@ -20,8 +20,12 @@ use super::pin_protocol::{verify_pin_uv_auth_token, PinProtocol, SharedSecret};
use super::response::{AuthenticatorClientPinResponse, ResponseData};
use super::status_code::Ctap2StatusCode;
use super::token_state::PinUvAuthTokenState;
#[cfg(test)]
use crate::api::crypto::ecdh::SecretKey as _;
use crate::api::customization::Customization;
use crate::ctap::storage;
#[cfg(test)]
use crate::env::EcdhSk;
use crate::env::Env;
use alloc::boxed::Box;
use alloc::str;
@@ -105,8 +109,8 @@ pub enum PinPermission {
}
pub struct ClientPin<E: Env> {
pin_protocol_v1: PinProtocol,
pin_protocol_v2: PinProtocol,
pin_protocol_v1: PinProtocol<E>,
pin_protocol_v2: PinProtocol<E>,
consecutive_pin_mismatches: u8,
pin_uv_auth_token_state: PinUvAuthTokenState<E>,
}
@@ -122,7 +126,7 @@ impl<E: Env> ClientPin<E> {
}
/// Gets a reference to the PIN protocol of the given version.
fn get_pin_protocol(&self, pin_uv_auth_protocol: PinUvAuthProtocol) -> &PinProtocol {
fn get_pin_protocol(&self, pin_uv_auth_protocol: PinUvAuthProtocol) -> &PinProtocol<E> {
match pin_uv_auth_protocol {
PinUvAuthProtocol::V1 => &self.pin_protocol_v1,
PinUvAuthProtocol::V2 => &self.pin_protocol_v2,
@@ -133,7 +137,7 @@ impl<E: Env> ClientPin<E> {
fn get_mut_pin_protocol(
&mut self,
pin_uv_auth_protocol: PinUvAuthProtocol,
) -> &mut PinProtocol {
) -> &mut PinProtocol<E> {
match pin_uv_auth_protocol {
PinUvAuthProtocol::V1 => &mut self.pin_protocol_v1,
PinUvAuthProtocol::V2 => &mut self.pin_protocol_v2,
@@ -558,20 +562,21 @@ impl<E: Env> ClientPin<E> {
#[cfg(test)]
pub fn new_test(
env: &mut E,
key_agreement_key: crypto::ecdh::SecKey,
key_agreement_key: EcdhSk<E>,
pin_uv_auth_token: [u8; PIN_TOKEN_LENGTH],
pin_uv_auth_protocol: PinUvAuthProtocol,
) -> Self {
let random_key = EcdhSk::<E>::random(env.rng());
let (key_agreement_key_v1, key_agreement_key_v2) = match pin_uv_auth_protocol {
PinUvAuthProtocol::V1 => (key_agreement_key, crypto::ecdh::SecKey::gensk(env.rng())),
PinUvAuthProtocol::V2 => (crypto::ecdh::SecKey::gensk(env.rng()), key_agreement_key),
PinUvAuthProtocol::V1 => (key_agreement_key, random_key),
PinUvAuthProtocol::V2 => (random_key, key_agreement_key),
};
let mut pin_uv_auth_token_state = PinUvAuthTokenState::new();
pin_uv_auth_token_state.set_permissions(0xFF);
pin_uv_auth_token_state.begin_using_pin_uv_auth_token(env);
Self {
pin_protocol_v1: PinProtocol::new_test(key_agreement_key_v1, pin_uv_auth_token),
pin_protocol_v2: PinProtocol::new_test(key_agreement_key_v2, pin_uv_auth_token),
pin_protocol_v1: PinProtocol::<E>::new_test(key_agreement_key_v1, pin_uv_auth_token),
pin_protocol_v2: PinProtocol::<E>::new_test(key_agreement_key_v2, pin_uv_auth_token),
consecutive_pin_mismatches: 0,
pin_uv_auth_token_state,
}
@@ -583,6 +588,7 @@ mod test {
use super::super::pin_protocol::authenticate_pin_uv_auth_token;
use super::*;
use crate::env::test::TestEnv;
use crate::env::EcdhSk;
use alloc::vec;
/// Stores a PIN hash corresponding to the dummy PIN "1234".
@@ -613,9 +619,9 @@ mod test {
pin_uv_auth_protocol: PinUvAuthProtocol,
) -> (ClientPin<TestEnv>, Box<dyn SharedSecret>) {
let mut env = TestEnv::default();
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
let pk = key_agreement_key.genpk();
let key_agreement = CoseKey::from(pk);
let key_agreement_key = EcdhSk::<TestEnv>::random(env.rng());
let pk = key_agreement_key.public_key();
let key_agreement = CoseKey::from_ecdh_public_key(pk);
let pin_uv_auth_token = [0x91; PIN_TOKEN_LENGTH];
let client_pin = ClientPin::<TestEnv>::new_test(
&mut env,
@@ -1165,7 +1171,7 @@ mod test {
fn test_helper_decrypt_pin(pin_uv_auth_protocol: PinUvAuthProtocol) {
let mut env = TestEnv::default();
let pin_protocol = PinProtocol::new(env.rng());
let pin_protocol = PinProtocol::<TestEnv>::new(env.rng());
let shared_secret = pin_protocol
.decapsulate(pin_protocol.get_public_key(), pin_uv_auth_protocol)
.unwrap();
@@ -1209,7 +1215,7 @@ mod test {
fn test_helper_check_and_store_new_pin(pin_uv_auth_protocol: PinUvAuthProtocol) {
let mut env = TestEnv::default();
let pin_protocol = PinProtocol::new(env.rng());
let pin_protocol = PinProtocol::<TestEnv>::new(env.rng());
let shared_secret = pin_protocol
.decapsulate(pin_protocol.get_public_key(), pin_uv_auth_protocol)
.unwrap();

View File

@@ -627,7 +627,6 @@ mod test {
};
use super::super::ES256_CRED_PARAM;
use super::*;
use crate::env::test::TestEnv;
use cbor::{cbor_array, cbor_map};
#[test]
@@ -740,11 +739,7 @@ mod test {
#[test]
fn test_from_cbor_client_pin_parameters() {
let mut env = TestEnv::default();
let sk = crypto::ecdh::SecKey::gensk(env.rng());
let pk = sk.genpk();
let cose_key = CoseKey::from(pk);
let cose_key = CoseKey::example_ecdh_pubkey();
let cbor_value = cbor_map! {
0x01 => 1,
0x02 => ClientPinSubCommand::GetPinRetries,

View File

@@ -123,15 +123,17 @@ pub fn process_config<E: Env>(
#[cfg(test)]
mod test {
use super::*;
use crate::api::crypto::ecdh::SecretKey as _;
use crate::api::customization::Customization;
use crate::ctap::data_formats::PinUvAuthProtocol;
use crate::ctap::pin_protocol::authenticate_pin_uv_auth_token;
use crate::env::test::TestEnv;
use crate::env::EcdhSk;
#[test]
fn test_process_enable_enterprise_attestation() {
let mut env = TestEnv::default();
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
let key_agreement_key = EcdhSk::<TestEnv>::random(env.rng());
let pin_uv_auth_token = [0x55; 32];
let mut client_pin = ClientPin::<TestEnv>::new_test(
&mut env,
@@ -162,7 +164,7 @@ mod test {
#[test]
fn test_process_toggle_always_uv() {
let mut env = TestEnv::default();
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
let key_agreement_key = EcdhSk::<TestEnv>::random(env.rng());
let pin_uv_auth_token = [0x55; 32];
let mut client_pin = ClientPin::<TestEnv>::new_test(
&mut env,
@@ -201,7 +203,7 @@ mod test {
fn test_helper_process_toggle_always_uv_with_pin(pin_uv_auth_protocol: PinUvAuthProtocol) {
let mut env = TestEnv::default();
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
let key_agreement_key = EcdhSk::<TestEnv>::random(env.rng());
let pin_uv_auth_token = [0x55; 32];
let mut client_pin = ClientPin::<TestEnv>::new_test(
&mut env,
@@ -275,7 +277,7 @@ mod test {
#[test]
fn test_process_set_min_pin_length() {
let mut env = TestEnv::default();
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
let key_agreement_key = EcdhSk::<TestEnv>::random(env.rng());
let pin_uv_auth_token = [0x55; 32];
let mut client_pin = ClientPin::<TestEnv>::new_test(
&mut env,
@@ -323,7 +325,7 @@ mod test {
#[test]
fn test_process_set_min_pin_length_rp_ids() {
let mut env = TestEnv::default();
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
let key_agreement_key = EcdhSk::<TestEnv>::random(env.rng());
let pin_uv_auth_token = [0x55; 32];
let mut client_pin = ClientPin::<TestEnv>::new_test(
&mut env,
@@ -403,7 +405,7 @@ mod test {
#[test]
fn test_process_set_min_pin_length_force_pin_change_implicit() {
let mut env = TestEnv::default();
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
let key_agreement_key = EcdhSk::<TestEnv>::random(env.rng());
let pin_uv_auth_token = [0x55; 32];
let mut client_pin = ClientPin::<TestEnv>::new_test(
&mut env,
@@ -430,7 +432,7 @@ mod test {
#[test]
fn test_process_set_min_pin_length_force_pin_change_explicit() {
let mut env = TestEnv::default();
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
let key_agreement_key = EcdhSk::<TestEnv>::random(env.rng());
let pin_uv_auth_token = [0x55; 32];
let mut client_pin = ClientPin::<TestEnv>::new_test(
&mut env,
@@ -465,7 +467,7 @@ mod test {
#[test]
fn test_process_config_vendor_prototype() {
let mut env = TestEnv::default();
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
let key_agreement_key = EcdhSk::<TestEnv>::random(env.rng());
let pin_uv_auth_token = [0x55; 32];
let mut client_pin = ClientPin::<TestEnv>::new_test(
&mut env,

View File

@@ -268,10 +268,12 @@ pub fn decrypt_credential_id(
#[cfg(test)]
mod test {
use super::*;
use crate::api::crypto::ecdsa::SecretKey as _;
use crate::api::customization::Customization;
use crate::ctap::credential_id::CBOR_CREDENTIAL_ID_SIZE;
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;
@@ -380,13 +382,13 @@ mod test {
/// This is a copy of the function that genereated deprecated key handles.
fn legacy_encrypt_to_credential_id(
env: &mut impl Env,
private_key: crypto::ecdsa::SecKey,
private_key: EcdsaSk<TestEnv>,
application: &[u8; 32],
) -> Result<Vec<u8>, Ctap2StatusCode> {
let aes_enc_key =
crypto::aes256::EncryptionKey::new(&env.key_store().key_handle_encryption()?);
let mut plaintext = [0; 64];
private_key.to_bytes(array_mut_ref!(plaintext, 0, 32));
private_key.to_slice(array_mut_ref!(plaintext, 0, 32));
plaintext[32..64].copy_from_slice(application);
let mut encrypted_id = aes256_cbc_encrypt(env.rng(), &aes_enc_key, &plaintext, true)?;

View File

@@ -359,7 +359,9 @@ mod test {
use super::super::pin_protocol::authenticate_pin_uv_auth_token;
use super::super::CtapState;
use super::*;
use crate::api::crypto::ecdh::SecretKey as _;
use crate::env::test::TestEnv;
use crate::env::EcdhSk;
use rng256::Rng256;
const DUMMY_CHANNEL: Channel = Channel::MainHid([0x12, 0x34, 0x56, 0x78]);
@@ -384,7 +386,7 @@ mod test {
fn test_helper_process_get_creds_metadata(pin_uv_auth_protocol: PinUvAuthProtocol) {
let mut env = TestEnv::default();
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
let key_agreement_key = EcdhSk::<TestEnv>::random(env.rng());
let pin_uv_auth_token = [0x55; 32];
let client_pin = ClientPin::<TestEnv>::new_test(
&mut env,
@@ -468,7 +470,7 @@ mod test {
#[test]
fn test_process_enumerate_rps_with_uv() {
let mut env = TestEnv::default();
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
let key_agreement_key = EcdhSk::<TestEnv>::random(env.rng());
let pin_uv_auth_token = [0x55; 32];
let client_pin = ClientPin::<TestEnv>::new_test(
&mut env,
@@ -563,7 +565,7 @@ mod test {
#[test]
fn test_process_enumerate_rps_completeness() {
let mut env = TestEnv::default();
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
let key_agreement_key = EcdhSk::<TestEnv>::random(env.rng());
let pin_uv_auth_token = [0x55; 32];
let client_pin = ClientPin::<TestEnv>::new_test(
&mut env,
@@ -646,7 +648,7 @@ mod test {
#[test]
fn test_process_enumerate_credentials_with_uv() {
let mut env = TestEnv::default();
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
let key_agreement_key = EcdhSk::<TestEnv>::random(env.rng());
let pin_uv_auth_token = [0x55; 32];
let client_pin = ClientPin::<TestEnv>::new_test(
&mut env,
@@ -749,7 +751,7 @@ mod test {
#[test]
fn test_process_delete_credential() {
let mut env = TestEnv::default();
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
let key_agreement_key = EcdhSk::<TestEnv>::random(env.rng());
let pin_uv_auth_token = [0x55; 32];
let client_pin = ClientPin::<TestEnv>::new_test(
&mut env,
@@ -821,7 +823,7 @@ mod test {
#[test]
fn test_process_update_user_information() {
let mut env = TestEnv::default();
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
let key_agreement_key = EcdhSk::<TestEnv>::random(env.rng());
let pin_uv_auth_token = [0x55; 32];
let client_pin = ClientPin::<TestEnv>::new_test(
&mut env,

View File

@@ -12,16 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::api::crypto::ecdsa::{SecretKey as _, Signature};
use crate::api::key_store::KeyStore;
use crate::ctap::data_formats::{extract_array, extract_byte_string, CoseKey, SignatureAlgorithm};
use crate::ctap::status_code::Ctap2StatusCode;
use crate::env::Env;
use crate::env::{EcdsaSk, Env};
use alloc::vec;
use alloc::vec::Vec;
use core::convert::TryFrom;
use crypto::cbc::{cbc_decrypt, cbc_encrypt};
use crypto::ecdsa;
use crypto::sha256::Sha256;
use rng256::Rng256;
use sk_cbor as cbor;
use sk_cbor::{cbor_array, cbor_bytes, cbor_int};
@@ -129,7 +128,7 @@ impl PrivateKey {
}
/// Returns the ECDSA private key.
pub fn ecdsa_key(&self, env: &mut impl Env) -> Result<ecdsa::SecKey, Ctap2StatusCode> {
pub fn ecdsa_key<E: Env>(&self, env: &mut E) -> Result<EcdsaSk<E>, Ctap2StatusCode> {
match self {
PrivateKey::Ecdsa(seed) => ecdsa_key_from_seed(env, seed),
#[allow(unreachable_patterns)]
@@ -141,7 +140,7 @@ impl PrivateKey {
pub fn get_pub_key(&self, env: &mut impl Env) -> Result<CoseKey, Ctap2StatusCode> {
Ok(match self {
PrivateKey::Ecdsa(ecdsa_seed) => {
CoseKey::from(ecdsa_key_from_seed(env, ecdsa_seed)?.genpk())
CoseKey::from_ecdsa_public_key(ecdsa_key_from_seed(env, ecdsa_seed)?.public_key())
}
#[cfg(feature = "ed25519")]
PrivateKey::Ed25519(ed25519_key) => CoseKey::from(ed25519_key.public_key()),
@@ -155,9 +154,9 @@ impl PrivateKey {
message: &[u8],
) -> Result<Vec<u8>, Ctap2StatusCode> {
Ok(match self {
PrivateKey::Ecdsa(ecdsa_seed) => ecdsa_key_from_seed(env, ecdsa_seed)?
.sign_rfc6979::<Sha256>(message)
.to_asn1_der(),
PrivateKey::Ecdsa(ecdsa_seed) => {
ecdsa_key_from_seed(env, ecdsa_seed)?.sign(message).to_der()
}
#[cfg(feature = "ed25519")]
PrivateKey::Ed25519(ed25519_key) => ed25519_key.sign(message, None).to_vec(),
})
@@ -182,12 +181,12 @@ impl PrivateKey {
}
}
fn ecdsa_key_from_seed(
env: &mut impl Env,
fn ecdsa_key_from_seed<E: Env>(
env: &mut E,
seed: &[u8; 32],
) -> Result<ecdsa::SecKey, Ctap2StatusCode> {
) -> Result<EcdsaSk<E>, Ctap2StatusCode> {
let ecdsa_bytes = env.key_store().derive_ecdsa(seed)?;
Ok(ecdsa::SecKey::from_bytes(&ecdsa_bytes).unwrap())
Ok(EcdsaSk::<E>::from_slice(&ecdsa_bytes).unwrap())
}
impl From<&PrivateKey> for cbor::Value {
@@ -334,10 +333,10 @@ mod test {
let mut env = TestEnv::default();
let private_key = PrivateKey::new_ecdsa(&mut env);
let ecdsa_key = private_key.ecdsa_key(&mut env).unwrap();
let public_key = ecdsa_key.genpk();
let public_key = ecdsa_key.public_key();
assert_eq!(
private_key.get_pub_key(&mut env),
Ok(CoseKey::from(public_key))
Ok(CoseKey::from_ecdsa_public_key(public_key))
);
}
@@ -347,7 +346,7 @@ mod test {
let message = [0x5A; 32];
let private_key = PrivateKey::new_ecdsa(&mut env);
let ecdsa_key = private_key.ecdsa_key(&mut env).unwrap();
let signature = ecdsa_key.sign_rfc6979::<Sha256>(&message).to_asn1_der();
let signature = ecdsa_key.sign(&message).to_der();
assert_eq!(
private_key.sign_and_encode(&mut env, &message),
Ok(signature)

View File

@@ -17,9 +17,11 @@ use super::credential_id::{decrypt_credential_id, encrypt_to_credential_id};
use super::crypto_wrapper::PrivateKey;
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 alloc::vec::Vec;
use arrayref::array_ref;
use arrayref::{array_ref, mut_array_refs};
use core::convert::TryFrom;
// For now, they're the same thing with apdu.rs containing the authoritative definition
@@ -156,6 +158,17 @@ impl TryFrom<&[u8]> for U2fCommand {
}
}
fn to_uncompressed(public_key: &impl ecdsa::PublicKey) -> [u8; 1 + 2 * EC_FIELD_SIZE] {
// Formatting according to:
// https://tools.ietf.org/id/draft-jivsov-ecc-compact-05.html#overview
const B0_BYTE_MARKER: u8 = 0x04;
let mut representation = [0; 1 + 2 * EC_FIELD_SIZE];
let (marker, x, y) = mut_array_refs![&mut representation, 1, EC_FIELD_SIZE, EC_FIELD_SIZE];
marker[0] = B0_BYTE_MARKER;
public_key.to_coordinates(x, y);
representation
}
pub struct Ctap1Command {}
impl Ctap1Command {
@@ -245,7 +258,7 @@ impl Ctap1Command {
let sk = private_key
.ecdsa_key(env)
.map_err(|_| Ctap1StatusCode::SW_INTERNAL_EXCEPTION)?;
let pk = sk.genpk();
let pk = sk.public_key();
let key_handle = encrypt_to_credential_id(env, &private_key, &application, None, None)
.map_err(|_| Ctap1StatusCode::SW_INTERNAL_EXCEPTION)?;
if key_handle.len() > 0xFF {
@@ -263,7 +276,7 @@ impl Ctap1Command {
let mut response = Vec::with_capacity(105 + key_handle.len() + certificate.len());
response.push(Ctap1Command::LEGACY_BYTE);
let user_pk = pk.to_uncompressed();
let user_pk = to_uncompressed(&pk);
response.extend_from_slice(&user_pk);
response.push(key_handle.len() as u8);
response.extend(key_handle.clone());
@@ -327,10 +340,10 @@ impl Ctap1Command {
)
.map_err(|_| Ctap1StatusCode::SW_WRONG_DATA)?;
signature_data.extend(&challenge);
let signature = ecdsa_key.sign_rfc6979::<crypto::sha256::Sha256>(&signature_data);
let signature = ecdsa_key.sign(&signature_data);
let mut response = signature_data[application.len()..application.len() + 5].to_vec();
response.extend(signature.to_asn1_der());
response.extend(signature.to_der());
Ok(response)
} else {
Err(Ctap1StatusCode::SW_WRONG_DATA)

View File

@@ -14,13 +14,13 @@
use super::crypto_wrapper::PrivateKey;
use super::status_code::Ctap2StatusCode;
use crate::api::crypto::{ecdh, ecdsa, EC_FIELD_SIZE};
use alloc::string::String;
use alloc::vec::Vec;
#[cfg(feature = "fuzz")]
use arbitrary::Arbitrary;
use arrayref::array_ref;
use core::convert::TryFrom;
use crypto::{ecdh, ecdsa};
#[cfg(test)]
use enum_iterator::IntoEnumIterator;
use sk_cbor as cbor;
@@ -728,8 +728,8 @@ impl PublicKeyCredentialSource {
#[derive(Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "fuzz", derive(Arbitrary))]
pub struct CoseKey {
x_bytes: [u8; ecdh::NBYTES],
y_bytes: [u8; ecdh::NBYTES],
x_bytes: [u8; EC_FIELD_SIZE],
y_bytes: [u8; EC_FIELD_SIZE],
algorithm: i64,
key_type: i64,
curve: i64,
@@ -748,6 +748,78 @@ impl CoseKey {
const P_256_CURVE: i64 = 1;
#[cfg(feature = "ed25519")]
const ED25519_CURVE: i64 = 6;
pub fn from_ecdh_public_key(pk: impl ecdh::PublicKey) -> Self {
let mut x_bytes = [0; EC_FIELD_SIZE];
let mut y_bytes = [0; EC_FIELD_SIZE];
pk.to_coordinates(&mut x_bytes, &mut y_bytes);
CoseKey {
x_bytes,
y_bytes,
algorithm: CoseKey::ECDH_ALGORITHM,
key_type: CoseKey::EC2_KEY_TYPE,
curve: CoseKey::P_256_CURVE,
}
}
pub fn from_ecdsa_public_key(pk: impl ecdsa::PublicKey) -> Self {
let mut x_bytes = [0; EC_FIELD_SIZE];
let mut y_bytes = [0; EC_FIELD_SIZE];
pk.to_coordinates(&mut x_bytes, &mut y_bytes);
CoseKey {
x_bytes,
y_bytes,
algorithm: ES256_ALGORITHM,
key_type: CoseKey::EC2_KEY_TYPE,
curve: CoseKey::P_256_CURVE,
}
}
/// Returns the x and y coordinates, if the key is an ECDH public key.
pub fn try_into_ecdh_coordinates(
self,
) -> Result<([u8; EC_FIELD_SIZE], [u8; EC_FIELD_SIZE]), Ctap2StatusCode> {
let CoseKey {
x_bytes,
y_bytes,
algorithm,
key_type,
curve,
} = self;
// Since algorithm can be used for different COSE key types, we check
// whether the current type is correct for ECDH. For an OpenSSH bugfix,
// the algorithm ES256_ALGORITHM is allowed here too.
// https://github.com/google/OpenSK/issues/90
if algorithm != CoseKey::ECDH_ALGORITHM && algorithm != ES256_ALGORITHM {
return Err(Ctap2StatusCode::CTAP2_ERR_UNSUPPORTED_ALGORITHM);
}
if key_type != CoseKey::EC2_KEY_TYPE || curve != CoseKey::P_256_CURVE {
return Err(Ctap2StatusCode::CTAP2_ERR_UNSUPPORTED_ALGORITHM);
}
Ok((x_bytes, y_bytes))
}
#[cfg(test)]
pub fn example_ecdh_pubkey() -> Self {
let x_bytes = [
0x74, 0x4A, 0x48, 0xA0, 0xDC, 0x56, 0x9A, 0x42, 0x0B, 0x3F, 0x58, 0xBF, 0xD8, 0xD9,
0x62, 0xCF, 0x3A, 0xEA, 0xB1, 0x5A, 0x32, 0x03, 0xC1, 0xA4, 0x23, 0x8B, 0x57, 0x75,
0x74, 0xA4, 0x29, 0x50,
];
let y_bytes = [
0xCD, 0x93, 0x26, 0x4A, 0xAF, 0x2A, 0xBA, 0xD1, 0x09, 0x3D, 0x2E, 0xD6, 0x8C, 0xC0,
0x59, 0xB1, 0xD9, 0xAB, 0xD7, 0x81, 0x71, 0x60, 0x35, 0xFE, 0xFF, 0xE8, 0xE1, 0x94,
0x05, 0x60, 0xA0, 0xBC,
];
CoseKey {
x_bytes,
y_bytes,
algorithm: CoseKey::ECDH_ALGORITHM,
key_type: CoseKey::EC2_KEY_TYPE,
curve: CoseKey::P_256_CURVE,
}
}
}
// This conversion accepts both ECDH and ECDSA.
@@ -767,17 +839,15 @@ impl TryFrom<cbor::Value> for CoseKey {
}
let algorithm = extract_integer(ok_or_missing(algorithm)?)?;
let nbytes = match algorithm {
CoseKey::ECDH_ALGORITHM => ecdh::NBYTES,
ES256_ALGORITHM => ecdsa::NBYTES,
_ => return Err(Ctap2StatusCode::CTAP2_ERR_UNSUPPORTED_ALGORITHM),
if algorithm != CoseKey::ECDH_ALGORITHM && algorithm != ES256_ALGORITHM {
return Err(Ctap2StatusCode::CTAP2_ERR_UNSUPPORTED_ALGORITHM);
};
let x_bytes = extract_byte_string(ok_or_missing(x_bytes)?)?;
if x_bytes.len() != nbytes {
if x_bytes.len() != EC_FIELD_SIZE {
return Err(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER);
}
let y_bytes = extract_byte_string(ok_or_missing(y_bytes)?)?;
if y_bytes.len() != nbytes {
if y_bytes.len() != EC_FIELD_SIZE {
return Err(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER);
}
let curve = extract_integer(ok_or_missing(curve)?)?;
@@ -790,8 +860,8 @@ impl TryFrom<cbor::Value> for CoseKey {
}
Ok(CoseKey {
x_bytes: *array_ref![x_bytes.as_slice(), 0, ecdh::NBYTES],
y_bytes: *array_ref![y_bytes.as_slice(), 0, ecdh::NBYTES],
x_bytes: *array_ref![x_bytes.as_slice(), 0, EC_FIELD_SIZE],
y_bytes: *array_ref![y_bytes.as_slice(), 0, EC_FIELD_SIZE],
algorithm,
key_type,
curve,
@@ -819,36 +889,6 @@ impl From<CoseKey> for cbor::Value {
}
}
impl From<ecdh::PubKey> for CoseKey {
fn from(pk: ecdh::PubKey) -> Self {
let mut x_bytes = [0; ecdh::NBYTES];
let mut y_bytes = [0; ecdh::NBYTES];
pk.to_coordinates(&mut x_bytes, &mut y_bytes);
CoseKey {
x_bytes,
y_bytes,
algorithm: CoseKey::ECDH_ALGORITHM,
key_type: CoseKey::EC2_KEY_TYPE,
curve: CoseKey::P_256_CURVE,
}
}
}
impl From<ecdsa::PubKey> for CoseKey {
fn from(pk: ecdsa::PubKey) -> Self {
let mut x_bytes = [0; ecdsa::NBYTES];
let mut y_bytes = [0; ecdsa::NBYTES];
pk.to_coordinates(&mut x_bytes, &mut y_bytes);
CoseKey {
x_bytes,
y_bytes,
algorithm: ES256_ALGORITHM,
key_type: CoseKey::EC2_KEY_TYPE,
curve: CoseKey::P_256_CURVE,
}
}
}
#[cfg(feature = "ed25519")]
impl From<ed25519_compact::PublicKey> for CoseKey {
fn from(pk: ed25519_compact::PublicKey) -> Self {
@@ -862,56 +902,6 @@ impl From<ed25519_compact::PublicKey> for CoseKey {
}
}
impl TryFrom<CoseKey> for ecdh::PubKey {
type Error = Ctap2StatusCode;
fn try_from(cose_key: CoseKey) -> Result<Self, Ctap2StatusCode> {
let CoseKey {
x_bytes,
y_bytes,
algorithm,
key_type,
curve,
} = cose_key;
// Since algorithm can be used for different COSE key types, we check
// whether the current type is correct for ECDH. For an OpenSSH bugfix,
// the algorithm ES256_ALGORITHM is allowed here too.
// https://github.com/google/OpenSK/issues/90
if algorithm != CoseKey::ECDH_ALGORITHM && algorithm != ES256_ALGORITHM {
return Err(Ctap2StatusCode::CTAP2_ERR_UNSUPPORTED_ALGORITHM);
}
if key_type != CoseKey::EC2_KEY_TYPE || curve != CoseKey::P_256_CURVE {
return Err(Ctap2StatusCode::CTAP2_ERR_UNSUPPORTED_ALGORITHM);
}
ecdh::PubKey::from_coordinates(&x_bytes, &y_bytes)
.ok_or(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER)
}
}
impl TryFrom<CoseKey> for ecdsa::PubKey {
type Error = Ctap2StatusCode;
fn try_from(cose_key: CoseKey) -> Result<Self, Ctap2StatusCode> {
let CoseKey {
x_bytes,
y_bytes,
algorithm,
key_type,
curve,
} = cose_key;
if algorithm != ES256_ALGORITHM
|| key_type != CoseKey::EC2_KEY_TYPE
|| curve != CoseKey::P_256_CURVE
{
return Err(Ctap2StatusCode::CTAP2_ERR_UNSUPPORTED_ALGORITHM);
}
ecdsa::PubKey::from_coordinates(&x_bytes, &y_bytes)
.ok_or(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "fuzz", derive(Arbitrary))]
pub enum PinUvAuthProtocol {
@@ -1240,7 +1230,10 @@ pub(super) fn ok_or_missing<T>(value_option: Option<T>) -> Result<T, Ctap2Status
mod test {
use self::Ctap2StatusCode::CTAP2_ERR_CBOR_UNEXPECTED_TYPE;
use super::*;
use crate::api::crypto::ecdh::PublicKey as _;
use crate::api::crypto::ecdsa::PublicKey as _;
use crate::env::test::TestEnv;
use crate::env::{EcdhPk, EcdsaPk};
use cbor::{
cbor_array, cbor_bool, cbor_bytes, cbor_bytes_lit, cbor_false, cbor_int, cbor_null,
cbor_text, cbor_unsigned,
@@ -1733,10 +1726,7 @@ mod test {
#[test]
fn test_from_get_assertion_extensions_default_protocol() {
let mut env = TestEnv::default();
let sk = crypto::ecdh::SecKey::gensk(env.rng());
let pk = sk.genpk();
let cose_key = CoseKey::from(pk);
let cose_key = CoseKey::example_ecdh_pubkey();
let cbor_extensions = cbor_map! {
"credBlob" => true,
"hmac-secret" => cbor_map! {
@@ -1763,10 +1753,7 @@ mod test {
#[test]
fn test_from_get_assertion_extensions_with_protocol() {
let mut env = TestEnv::default();
let sk = crypto::ecdh::SecKey::gensk(env.rng());
let pk = sk.genpk();
let cose_key = CoseKey::from(pk);
let cose_key = CoseKey::example_ecdh_pubkey();
let cbor_extensions = cbor_map! {
"credBlob" => true,
"hmac-secret" => cbor_map! {
@@ -1938,20 +1925,29 @@ mod test {
#[test]
fn test_from_into_cose_key_ecdh() {
let mut env = TestEnv::default();
let sk = crypto::ecdh::SecKey::gensk(env.rng());
let pk = sk.genpk();
let cose_key = CoseKey::from(pk.clone());
let created_pk = ecdh::PubKey::try_from(cose_key);
assert_eq!(created_pk, Ok(pk));
let cose_key = CoseKey::example_ecdh_pubkey();
let (x_bytes, y_bytes) = cose_key.clone().try_into_ecdh_coordinates().unwrap();
let created_pk = EcdhPk::<TestEnv>::from_coordinates(&x_bytes, &y_bytes).unwrap();
let new_cose_key = CoseKey::from_ecdh_public_key(created_pk);
assert_eq!(cose_key, new_cose_key);
}
#[test]
fn test_into_cose_key_ecdsa() {
let mut env = TestEnv::default();
let sk = crypto::ecdsa::SecKey::gensk(env.rng());
let pk = sk.genpk();
let cose_key = CoseKey::from(pk);
fn test_from_cose_key_ecdsa() {
let x_bytes = [
0x74, 0x4A, 0x48, 0xA0, 0xDC, 0x56, 0x9A, 0x42, 0x0B, 0x3F, 0x58, 0xBF, 0xD8, 0xD9,
0x62, 0xCF, 0x3A, 0xEA, 0xB1, 0x5A, 0x32, 0x03, 0xC1, 0xA4, 0x23, 0x8B, 0x57, 0x75,
0x74, 0xA4, 0x29, 0x50,
];
let y_bytes = [
0xCD, 0x93, 0x26, 0x4A, 0xAF, 0x2A, 0xBA, 0xD1, 0x09, 0x3D, 0x2E, 0xD6, 0x8C, 0xC0,
0x59, 0xB1, 0xD9, 0xAB, 0xD7, 0x81, 0x71, 0x60, 0x35, 0xFE, 0xFF, 0xE8, 0xE1, 0x94,
0x05, 0x60, 0xA0, 0xBC,
];
let created_pk = EcdsaPk::<TestEnv>::from_coordinates(&x_bytes, &y_bytes).unwrap();
let cose_key = CoseKey::from_ecdsa_public_key(created_pk);
assert_eq!(cose_key.x_bytes, x_bytes);
assert_eq!(cose_key.y_bytes, y_bytes);
assert_eq!(cose_key.algorithm, ES256_ALGORITHM);
}

View File

@@ -140,12 +140,14 @@ mod test {
use super::super::data_formats::PinUvAuthProtocol;
use super::super::pin_protocol::authenticate_pin_uv_auth_token;
use super::*;
use crate::api::crypto::ecdh::SecretKey as EcdhSecretKey;
use crate::env::test::TestEnv;
use crate::env::EcdhSk;
#[test]
fn test_process_command_get_empty() {
let mut env = TestEnv::default();
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
let key_agreement_key = EcdhSk::<TestEnv>::random(env.rng());
let pin_uv_auth_token = [0x55; 32];
let mut client_pin = ClientPin::<TestEnv>::new_test(
&mut env,
@@ -180,7 +182,7 @@ mod test {
#[test]
fn test_process_command_commit_and_get() {
let mut env = TestEnv::default();
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
let key_agreement_key = EcdhSk::<TestEnv>::random(env.rng());
let pin_uv_auth_token = [0x55; 32];
let mut client_pin = ClientPin::<TestEnv>::new_test(
&mut env,
@@ -246,7 +248,7 @@ mod test {
#[test]
fn test_process_command_commit_unexpected_offset() {
let mut env = TestEnv::default();
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
let key_agreement_key = EcdhSk::<TestEnv>::random(env.rng());
let pin_uv_auth_token = [0x55; 32];
let mut client_pin = ClientPin::<TestEnv>::new_test(
&mut env,
@@ -296,7 +298,7 @@ mod test {
#[test]
fn test_process_command_commit_unexpected_length() {
let mut env = TestEnv::default();
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
let key_agreement_key = EcdhSk::<TestEnv>::random(env.rng());
let pin_uv_auth_token = [0x55; 32];
let mut client_pin = ClientPin::<TestEnv>::new_test(
&mut env,
@@ -346,7 +348,7 @@ mod test {
#[test]
fn test_process_command_commit_end_offset_overflow() {
let mut env = TestEnv::default();
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
let key_agreement_key = EcdhSk::<TestEnv>::random(env.rng());
let pin_uv_auth_token = [0x55; 32];
let mut client_pin = ClientPin::<TestEnv>::new_test(
&mut env,
@@ -373,7 +375,7 @@ mod test {
#[test]
fn test_process_command_commit_unexpected_hash() {
let mut env = TestEnv::default();
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
let key_agreement_key = EcdhSk::<TestEnv>::random(env.rng());
let pin_uv_auth_token = [0x55; 32];
let mut client_pin = ClientPin::<TestEnv>::new_test(
&mut env,
@@ -405,7 +407,7 @@ mod test {
fn test_helper_process_command_commit_with_pin(pin_uv_auth_protocol: PinUvAuthProtocol) {
let mut env = TestEnv::default();
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
let key_agreement_key = EcdhSk::<TestEnv>::random(env.rng());
let pin_uv_auth_token = [0x55; 32];
let mut client_pin = ClientPin::<TestEnv>::new_test(
&mut env,

View File

@@ -65,11 +65,12 @@ use self::u2f_up::U2fUserPresenceState;
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::customization::Customization;
use crate::api::firmware_protection::FirmwareProtection;
use crate::api::upgrade_storage::UpgradeStorage;
use crate::api::user_presence::{UserPresence, UserPresenceError};
use crate::env::Env;
use crate::env::{EcdsaSk, Env};
use alloc::boxed::Box;
use alloc::string::{String, ToString};
use alloc::vec;
@@ -78,7 +79,7 @@ use byteorder::{BigEndian, ByteOrder};
use core::convert::TryFrom;
use crypto::hmac::hmac_256;
use crypto::sha256::Sha256;
use crypto::{ecdsa, Hash256};
use crypto::Hash256;
use rng256::Rng256;
use sk_cbor as cbor;
use sk_cbor::cbor_map_options;
@@ -921,11 +922,9 @@ impl<E: Env> CtapState<E> {
.attestation_store()
.get(&id)?
.ok_or(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR)?;
let attestation_key = ecdsa::SecKey::from_bytes(&private_key).unwrap();
let attestation_key = EcdsaSk::<E>::from_slice(&private_key).unwrap();
(
attestation_key
.sign_rfc6979::<Sha256>(&signature_data)
.to_asn1_der(),
attestation_key.sign(&signature_data).to_der(),
Some(vec![certificate]),
)
}
@@ -1451,9 +1450,11 @@ mod test {
};
use super::pin_protocol::{authenticate_pin_uv_auth_token, PinProtocol};
use super::*;
use crate::api::crypto::ecdh::SecretKey as _;
use crate::api::customization;
use crate::api::user_presence::UserPresenceResult;
use crate::env::test::TestEnv;
use crate::env::EcdhSk;
use crate::test_helpers;
use cbor::{cbor_array, cbor_array_vec, cbor_map};
@@ -2033,7 +2034,7 @@ mod test {
pin_uv_auth_protocol: PinUvAuthProtocol,
) {
let mut env = TestEnv::default();
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
let key_agreement_key = EcdhSk::<TestEnv>::random(env.rng());
let pin_uv_auth_token = [0x91; PIN_TOKEN_LENGTH];
let client_pin = ClientPin::<TestEnv>::new_test(
&mut env,
@@ -2395,20 +2396,20 @@ mod test {
}
fn get_assertion_hmac_secret_params(
key_agreement_key: crypto::ecdh::SecKey,
key_agreement_key: EcdhSk<TestEnv>,
key_agreement_response: ResponseData,
credential_id: Option<Vec<u8>>,
pin_uv_auth_protocol: PinUvAuthProtocol,
) -> AuthenticatorGetAssertionParameters {
let mut env = TestEnv::default();
let platform_public_key = key_agreement_key.genpk();
let platform_public_key = key_agreement_key.public_key();
let public_key = match key_agreement_response {
ResponseData::AuthenticatorClientPin(Some(client_pin_response)) => {
client_pin_response.key_agreement.unwrap()
}
_ => panic!("Invalid response type"),
};
let pin_protocol = PinProtocol::new_test(key_agreement_key, [0x91; 32]);
let pin_protocol = PinProtocol::<TestEnv>::new_test(key_agreement_key, [0x91; 32]);
let shared_secret = pin_protocol
.decapsulate(public_key, pin_uv_auth_protocol)
.unwrap();
@@ -2417,7 +2418,7 @@ mod test {
let salt_enc = shared_secret.as_ref().encrypt(env.rng(), &salt).unwrap();
let salt_auth = shared_secret.authenticate(&salt_enc);
let hmac_secret_input = GetAssertionHmacSecretInput {
key_agreement: CoseKey::from(platform_public_key),
key_agreement: CoseKey::from_ecdh_public_key(platform_public_key),
salt_enc,
salt_auth,
pin_uv_auth_protocol,
@@ -2449,7 +2450,7 @@ mod test {
fn test_helper_process_get_assertion_hmac_secret(pin_uv_auth_protocol: PinUvAuthProtocol) {
let mut env = TestEnv::default();
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
let key_agreement_key = EcdhSk::<TestEnv>::random(env.rng());
let mut ctap_state = CtapState::<TestEnv>::new(&mut env);
let make_extensions = MakeCredentialExtensions {
@@ -2505,7 +2506,7 @@ mod test {
pin_uv_auth_protocol: PinUvAuthProtocol,
) {
let mut env = TestEnv::default();
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
let key_agreement_key = EcdhSk::<TestEnv>::random(env.rng());
let mut ctap_state = CtapState::<TestEnv>::new(&mut env);
let make_extensions = MakeCredentialExtensions {
@@ -2900,7 +2901,7 @@ mod test {
pin_uv_auth_protocol: PinUvAuthProtocol,
) {
let mut env = TestEnv::default();
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
let key_agreement_key = EcdhSk::<TestEnv>::random(env.rng());
let pin_uv_auth_token = [0x88; 32];
let client_pin = ClientPin::<TestEnv>::new_test(
&mut env,
@@ -3504,7 +3505,7 @@ mod test {
#[test]
fn test_credential_management_timeout() {
let mut env = TestEnv::default();
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
let key_agreement_key = EcdhSk::<TestEnv>::random(env.rng());
let pin_uv_auth_token = [0x55; 32];
let client_pin = ClientPin::<TestEnv>::new_test(
&mut env,

View File

@@ -12,13 +12,16 @@
// 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::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 core::convert::TryInto;
use crypto::hkdf::hkdf_empty_salt_256;
#[cfg(test)]
use crypto::hmac::hmac_256;
@@ -28,17 +31,17 @@ use crypto::Hash256;
use rng256::Rng256;
/// Implements common functions between existing PIN protocols for handshakes.
pub struct PinProtocol {
key_agreement_key: crypto::ecdh::SecKey,
pub struct PinProtocol<E: Env> {
key_agreement_key: EcdhSk<E>,
pin_uv_auth_token: [u8; PIN_TOKEN_LENGTH],
}
impl PinProtocol {
impl<E: Env> PinProtocol<E> {
/// This process is run by the authenticator at power-on.
///
/// This function implements "initialize" from the specification.
pub fn new(rng: &mut impl Rng256) -> PinProtocol {
let key_agreement_key = crypto::ecdh::SecKey::gensk(rng);
pub fn new(rng: &mut impl Rng256) -> Self {
let key_agreement_key = EcdhSk::<E>::random(rng);
let pin_uv_auth_token = rng.gen_uniform_u8x32();
PinProtocol {
key_agreement_key,
@@ -48,7 +51,7 @@ impl PinProtocol {
/// Generates a fresh public key.
pub fn regenerate(&mut self, rng: &mut impl Rng256) {
self.key_agreement_key = crypto::ecdh::SecKey::gensk(rng);
self.key_agreement_key = EcdhSk::<E>::random(rng);
}
/// Generates a fresh pinUvAuthToken.
@@ -58,7 +61,7 @@ impl PinProtocol {
/// Returns the authenticators public key as a CoseKey structure.
pub fn get_public_key(&self) -> CoseKey {
CoseKey::from(self.key_agreement_key.genpk())
CoseKey::from_ecdh_public_key(self.key_agreement_key.public_key())
}
/// Processes the peer's encapsulated CoseKey and returns the shared secret.
@@ -67,8 +70,13 @@ impl PinProtocol {
peer_cose_key: CoseKey,
pin_uv_auth_protocol: PinUvAuthProtocol,
) -> Result<Box<dyn SharedSecret>, Ctap2StatusCode> {
let pk: crypto::ecdh::PubKey = CoseKey::try_into(peer_cose_key)?;
let handshake = self.key_agreement_key.exchange_x(&pk);
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)?;
let handshake = self
.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))),
@@ -83,9 +91,9 @@ impl PinProtocol {
/// This is used for debugging to inject key material.
#[cfg(test)]
pub fn new_test(
key_agreement_key: crypto::ecdh::SecKey,
key_agreement_key: EcdhSk<E>,
pin_uv_auth_token: [u8; PIN_TOKEN_LENGTH],
) -> PinProtocol {
) -> Self {
PinProtocol {
key_agreement_key,
pin_uv_auth_token,
@@ -235,7 +243,7 @@ mod test {
#[test]
fn test_pin_protocol_public_key() {
let mut env = TestEnv::default();
let mut pin_protocol = PinProtocol::new(env.rng());
let mut pin_protocol = PinProtocol::<TestEnv>::new(env.rng());
let public_key = pin_protocol.get_public_key();
pin_protocol.regenerate(env.rng());
let new_public_key = pin_protocol.get_public_key();
@@ -245,7 +253,7 @@ mod test {
#[test]
fn test_pin_protocol_pin_uv_auth_token() {
let mut env = TestEnv::default();
let mut pin_protocol = PinProtocol::new(env.rng());
let mut pin_protocol = PinProtocol::<TestEnv>::new(env.rng());
let token = *pin_protocol.get_pin_uv_auth_token();
pin_protocol.reset_pin_uv_auth_token(env.rng());
let new_token = pin_protocol.get_pin_uv_auth_token();
@@ -328,8 +336,8 @@ mod test {
#[test]
fn test_decapsulate_symmetric() {
let mut env = TestEnv::default();
let pin_protocol1 = PinProtocol::new(env.rng());
let pin_protocol2 = PinProtocol::new(env.rng());
let pin_protocol1 = PinProtocol::<TestEnv>::new(env.rng());
let pin_protocol2 = PinProtocol::<TestEnv>::new(env.rng());
for &protocol in &[PinUvAuthProtocol::V1, PinUvAuthProtocol::V2] {
let shared_secret1 = pin_protocol1
.decapsulate(pin_protocol2.get_public_key(), protocol)

View File

@@ -343,7 +343,6 @@ mod test {
use super::super::data_formats::{PackedAttestationStatement, PublicKeyCredentialType};
use super::super::ES256_CRED_PARAM;
use super::*;
use crate::env::test::TestEnv;
use cbor::{cbor_array, cbor_bytes, cbor_map};
#[test]
@@ -506,10 +505,7 @@ mod test {
#[test]
fn test_used_client_pin_into_cbor() {
let mut env = TestEnv::default();
let sk = crypto::ecdh::SecKey::gensk(env.rng());
let pk = sk.genpk();
let cose_key = CoseKey::from(pk);
let cose_key = CoseKey::example_ecdh_pubkey();
let client_pin_response = AuthenticatorClientPinResponse {
key_agreement: Some(cose_key.clone()),
pin_uv_auth_token: Some(vec![70]),
@@ -550,8 +546,8 @@ mod test {
#[test]
fn test_used_credential_management_optionals_into_cbor() {
let mut env = TestEnv::default();
let sk = crypto::ecdh::SecKey::gensk(env.rng());
let cose_key = CoseKey::example_ecdh_pubkey();
let rp = PublicKeyCredentialRpEntity {
rp_id: String::from("example.com"),
rp_name: None,
@@ -568,8 +564,6 @@ mod test {
key_id: vec![0x1D; 32],
transports: None,
};
let pk = sk.genpk();
let cose_key = CoseKey::from(pk);
let cred_management_response = AuthenticatorCredentialManagementResponse {
existing_resident_credentials_count: Some(100),

View File

@@ -15,6 +15,9 @@
use crate::api::attestation_store::AttestationStore;
use crate::api::clock::Clock;
use crate::api::connection::HidConnection;
use crate::api::crypto::ecdh::Ecdh;
use crate::api::crypto::ecdsa::Ecdsa;
use crate::api::crypto::Crypto;
use crate::api::customization::Customization;
use crate::api::firmware_protection::FirmwareProtection;
use crate::api::key_store::KeyStore;
@@ -26,6 +29,11 @@ use rng256::Rng256;
#[cfg(feature = "std")]
pub mod test;
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;
/// Describes what CTAP needs to function.
pub trait Env {
type Rng: Rng256;
@@ -39,6 +47,7 @@ pub trait Env {
type HidConnection: HidConnection;
type AttestationStore: AttestationStore;
type Clock: Clock;
type Crypto: Crypto;
fn rng(&mut self) -> &mut Self::Rng;
fn user_presence(&mut self) -> &mut Self::UserPresence;

View File

@@ -16,6 +16,7 @@ use self::upgrade_storage::BufferUpgradeStorage;
use crate::api::attestation_store::AttestationStore;
use crate::api::clock::Clock;
use crate::api::connection::{HidConnection, SendOrRecvResult, SendOrRecvStatus};
use crate::api::crypto::software_crypto::SoftwareCrypto;
use crate::api::customization::DEFAULT_CUSTOMIZATION;
use crate::api::firmware_protection::FirmwareProtection;
use crate::api::user_presence::{UserPresence, UserPresenceResult};
@@ -217,6 +218,7 @@ impl Env for TestEnv {
type Write = TestWrite;
type Customization = TestCustomization;
type HidConnection = Self;
type Crypto = SoftwareCrypto;
fn rng(&mut self) -> &mut Self::Rng {
&mut self.rng

View File

@@ -137,6 +137,8 @@ then
cargo +nightly test --release --features std
echo "Running CTAP library unit tests (release mode + all features)..."
cargo +nightly test --release --features std,debug_ctap,with_ctap1,vendor_hid,ed25519
echo "Running CTAP library unit tests (release mode + experimental rust crypto)..."
cargo +nightly test --release --features std,debug_ctap,with_ctap1,vendor_hid,ed25519,rust_crypto
echo "Running CTAP library unit tests (debug mode)..."
cargo +nightly test --features std

2
src/env/tock/mod.rs vendored
View File

@@ -27,6 +27,7 @@ use opensk::api::attestation_store::AttestationStore;
use opensk::api::connection::{
HidConnection, SendOrRecvError, SendOrRecvResult, SendOrRecvStatus, UsbEndpoint,
};
use opensk::api::crypto::software_crypto::SoftwareCrypto;
use opensk::api::customization::{CustomizationImpl, AAGUID_LENGTH, DEFAULT_CUSTOMIZATION};
use opensk::api::firmware_protection::FirmwareProtection;
use opensk::api::user_presence::{UserPresence, UserPresenceError, UserPresenceResult};
@@ -249,6 +250,7 @@ impl Env for TockEnv {
type Write = Console;
type Customization = CustomizationImpl;
type HidConnection = TockHidConnection;
type Crypto = SoftwareCrypto;
fn rng(&mut self) -> &mut Self::Rng {
&mut self.rng