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:
@@ -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,
|
||||
|
||||
@@ -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"
|
||||
|
||||
53
libraries/opensk/src/api/crypto/ecdh.rs
Normal file
53
libraries/opensk/src/api/crypto/ecdh.rs
Normal 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 Diffie–Hellman.
|
||||
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 Diffie–Hellman.
|
||||
fn raw_secret_bytes(&self) -> [u8; EC_FIELD_SIZE];
|
||||
}
|
||||
72
libraries/opensk/src/api/crypto/ecdsa.rs
Normal file
72
libraries/opensk/src/api/crypto/ecdsa.rs
Normal 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>;
|
||||
}
|
||||
37
libraries/opensk/src/api/crypto/mod.rs
Normal file
37
libraries/opensk/src/api/crypto/mod.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.
|
||||
|
||||
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;
|
||||
}
|
||||
249
libraries/opensk/src/api/crypto/rust_crypto.rs
Normal file
249
libraries/opensk/src/api/crypto/rust_crypto.rs
Normal 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);
|
||||
}
|
||||
}
|
||||
213
libraries/opensk/src/api/crypto/software_crypto.rs
Normal file
213
libraries/opensk/src/api/crypto/software_crypto.rs
Normal 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);
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)?;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 authenticator’s 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)
|
||||
|
||||
@@ -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),
|
||||
|
||||
9
libraries/opensk/src/env/mod.rs
vendored
9
libraries/opensk/src/env/mod.rs
vendored
@@ -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;
|
||||
|
||||
2
libraries/opensk/src/env/test/mod.rs
vendored
2
libraries/opensk/src/env/test/mod.rs
vendored
@@ -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
|
||||
|
||||
@@ -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
2
src/env/tock/mod.rs
vendored
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user