use crate::errors::AuthenticatorError;
use crate::{ctap2::commands::CommandError, transport::errors::HIDError};
use serde::{
de::{Error as SerdeError, MapAccess, Unexpected, Visitor},
ser::SerializeMap,
Deserialize, Deserializer, Serialize, Serializer,
};
use serde_bytes::ByteBuf;
use serde_cbor::Value;
use std::convert::TryFrom;
use std::fmt;
cfg_if::cfg_if! {
if #[cfg(feature = "crypto_ring")] {
#[path = "ring.rs"]
pub mod imp;
} else if #[cfg(feature = "crypto_openssl")] {
#[path = "openssl.rs"]
pub mod imp;
} else if #[cfg(feature = "crypto_dummy")] {
#[path = "dummy.rs"]
pub mod imp;
} else {
#[path = "nss.rs"]
pub mod imp;
}
}
pub(crate) use imp::{authenticate, decrypt, encapsulate, encrypt, serialize_key, BackendError};
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum ECDSACurve {
SECP256R1 = 1,
SECP384R1 = 2,
SECP521R1 = 3,
X25519 = 4,
X448 = 5,
Ed25519 = 6,
Ed448 = 7,
}
impl TryFrom<u64> for ECDSACurve {
type Error = CryptoError;
fn try_from(i: u64) -> Result<Self, Self::Error> {
match i {
1 => Ok(ECDSACurve::SECP256R1),
2 => Ok(ECDSACurve::SECP384R1),
3 => Ok(ECDSACurve::SECP521R1),
4 => Ok(ECDSACurve::X25519),
5 => Ok(ECDSACurve::X448),
6 => Ok(ECDSACurve::Ed25519),
7 => Ok(ECDSACurve::Ed448),
_ => Err(CryptoError::UnknownKeyType),
}
}
}
#[allow(non_camel_case_types)]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum COSEAlgorithm {
INSECURE_RS1 = -65535, RS512 = -259, RS384 = -258, RS256 = -257, ES256K = -47, HSS_LMS = -46, SHAKE256 = -45, SHA512 = -44, SHA384 = -43, RSAES_OAEP_SHA_512 = -42, RSAES_OAEP_SHA_256 = -41, RSAES_OAEP_RFC_8017_default = -40, PS512 = -39, PS384 = -38, PS256 = -37, ES512 = -36, ES384 = -35, ECDH_SS_A256KW = -34, ECDH_SS_A192KW = -33, ECDH_SS_A128KW = -32, ECDH_ES_A256KW = -31, ECDH_ES_A192KW = -30, ECDH_ES_A128KW = -29, ECDH_SS_HKDF512 = -28, ECDH_SS_HKDF256 = -27, ECDH_ES_HKDF512 = -26, ECDH_ES_HKDF256 = -25, SHAKE128 = -18, SHA512_256 = -17, SHA256 = -16, SHA256_64 = -15, SHA1 = -14, Direct_HKDF_AES256 = -13, Direct_HKDF_AES128 = -12, Direct_HKDF_SHA512 = -11, Direct_HKDF_SHA256 = -10, EDDSA = -8, ES256 = -7, Direct = -6, A256KW = -5, A192KW = -4, A128KW = -3, A128GCM = 1, A192GCM = 2, A256GCM = 3, HMAC256_64 = 4, HMAC256_256 = 5, HMAC384_384 = 6, HMAC512_512 = 7, AES_CCM_16_64_128 = 10, AES_CCM_16_64_256 = 11, AES_CCM_64_64_128 = 12, AES_CCM_64_64_256 = 13, AES_MAC_128_64 = 14, AES_MAC_256_64 = 15, ChaCha20_Poly1305 = 24, AES_MAC_128_128 = 25, AES_MAC_256_128 = 26, AES_CCM_16_128_128 = 30, AES_CCM_16_128_256 = 31, AES_CCM_64_128_128 = 32, AES_CCM_64_128_256 = 33, IV_GENERATION = 34, }
impl Serialize for COSEAlgorithm {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match *self {
COSEAlgorithm::RS512 => serializer.serialize_i16(-259),
COSEAlgorithm::RS384 => serializer.serialize_i16(-258),
COSEAlgorithm::RS256 => serializer.serialize_i16(-257),
COSEAlgorithm::ES256K => serializer.serialize_i8(-47),
COSEAlgorithm::HSS_LMS => serializer.serialize_i8(-46),
COSEAlgorithm::SHAKE256 => serializer.serialize_i8(-45),
COSEAlgorithm::SHA512 => serializer.serialize_i8(-44),
COSEAlgorithm::SHA384 => serializer.serialize_i8(-43),
COSEAlgorithm::RSAES_OAEP_SHA_512 => serializer.serialize_i8(-42),
COSEAlgorithm::RSAES_OAEP_SHA_256 => serializer.serialize_i8(-41),
COSEAlgorithm::RSAES_OAEP_RFC_8017_default => serializer.serialize_i8(-40),
COSEAlgorithm::PS512 => serializer.serialize_i8(-39),
COSEAlgorithm::PS384 => serializer.serialize_i8(-38),
COSEAlgorithm::PS256 => serializer.serialize_i8(-37),
COSEAlgorithm::ES512 => serializer.serialize_i8(-36),
COSEAlgorithm::ES384 => serializer.serialize_i8(-35),
COSEAlgorithm::ECDH_SS_A256KW => serializer.serialize_i8(-34),
COSEAlgorithm::ECDH_SS_A192KW => serializer.serialize_i8(-33),
COSEAlgorithm::ECDH_SS_A128KW => serializer.serialize_i8(-32),
COSEAlgorithm::ECDH_ES_A256KW => serializer.serialize_i8(-31),
COSEAlgorithm::ECDH_ES_A192KW => serializer.serialize_i8(-30),
COSEAlgorithm::ECDH_ES_A128KW => serializer.serialize_i8(-29),
COSEAlgorithm::ECDH_SS_HKDF512 => serializer.serialize_i8(-28),
COSEAlgorithm::ECDH_SS_HKDF256 => serializer.serialize_i8(-27),
COSEAlgorithm::ECDH_ES_HKDF512 => serializer.serialize_i8(-26),
COSEAlgorithm::ECDH_ES_HKDF256 => serializer.serialize_i8(-25),
COSEAlgorithm::SHAKE128 => serializer.serialize_i8(-18),
COSEAlgorithm::SHA512_256 => serializer.serialize_i8(-17),
COSEAlgorithm::SHA256 => serializer.serialize_i8(-16),
COSEAlgorithm::SHA256_64 => serializer.serialize_i8(-15),
COSEAlgorithm::SHA1 => serializer.serialize_i8(-14),
COSEAlgorithm::Direct_HKDF_AES256 => serializer.serialize_i8(-13),
COSEAlgorithm::Direct_HKDF_AES128 => serializer.serialize_i8(-12),
COSEAlgorithm::Direct_HKDF_SHA512 => serializer.serialize_i8(-11),
COSEAlgorithm::Direct_HKDF_SHA256 => serializer.serialize_i8(-10),
COSEAlgorithm::EDDSA => serializer.serialize_i8(-8),
COSEAlgorithm::ES256 => serializer.serialize_i8(-7),
COSEAlgorithm::Direct => serializer.serialize_i8(-6),
COSEAlgorithm::A256KW => serializer.serialize_i8(-5),
COSEAlgorithm::A192KW => serializer.serialize_i8(-4),
COSEAlgorithm::A128KW => serializer.serialize_i8(-3),
COSEAlgorithm::A128GCM => serializer.serialize_i8(1),
COSEAlgorithm::A192GCM => serializer.serialize_i8(2),
COSEAlgorithm::A256GCM => serializer.serialize_i8(3),
COSEAlgorithm::HMAC256_64 => serializer.serialize_i8(4),
COSEAlgorithm::HMAC256_256 => serializer.serialize_i8(5),
COSEAlgorithm::HMAC384_384 => serializer.serialize_i8(6),
COSEAlgorithm::HMAC512_512 => serializer.serialize_i8(7),
COSEAlgorithm::AES_CCM_16_64_128 => serializer.serialize_i8(10),
COSEAlgorithm::AES_CCM_16_64_256 => serializer.serialize_i8(11),
COSEAlgorithm::AES_CCM_64_64_128 => serializer.serialize_i8(12),
COSEAlgorithm::AES_CCM_64_64_256 => serializer.serialize_i8(13),
COSEAlgorithm::AES_MAC_128_64 => serializer.serialize_i8(14),
COSEAlgorithm::AES_MAC_256_64 => serializer.serialize_i8(15),
COSEAlgorithm::ChaCha20_Poly1305 => serializer.serialize_i8(24),
COSEAlgorithm::AES_MAC_128_128 => serializer.serialize_i8(25),
COSEAlgorithm::AES_MAC_256_128 => serializer.serialize_i8(26),
COSEAlgorithm::AES_CCM_16_128_128 => serializer.serialize_i8(30),
COSEAlgorithm::AES_CCM_16_128_256 => serializer.serialize_i8(31),
COSEAlgorithm::AES_CCM_64_128_128 => serializer.serialize_i8(32),
COSEAlgorithm::AES_CCM_64_128_256 => serializer.serialize_i8(33),
COSEAlgorithm::IV_GENERATION => serializer.serialize_i8(34),
COSEAlgorithm::INSECURE_RS1 => serializer.serialize_i32(-65535),
}
}
}
impl<'de> Deserialize<'de> for COSEAlgorithm {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
struct COSEAlgorithmVisitor;
impl<'de> Visitor<'de> for COSEAlgorithmVisitor {
type Value = COSEAlgorithm;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("a signed integer")
}
fn visit_i64<E>(self, v: i64) -> Result<Self::Value, E>
where
E: SerdeError,
{
COSEAlgorithm::try_from(v).map_err(|_| {
SerdeError::invalid_value(Unexpected::Signed(v), &"valid COSEAlgorithm")
})
}
}
deserializer.deserialize_any(COSEAlgorithmVisitor)
}
}
impl TryFrom<i64> for COSEAlgorithm {
type Error = CryptoError;
fn try_from(i: i64) -> Result<Self, Self::Error> {
match i {
-259 => Ok(COSEAlgorithm::RS512),
-258 => Ok(COSEAlgorithm::RS384),
-257 => Ok(COSEAlgorithm::RS256),
-47 => Ok(COSEAlgorithm::ES256K),
-46 => Ok(COSEAlgorithm::HSS_LMS),
-45 => Ok(COSEAlgorithm::SHAKE256),
-44 => Ok(COSEAlgorithm::SHA512),
-43 => Ok(COSEAlgorithm::SHA384),
-42 => Ok(COSEAlgorithm::RSAES_OAEP_SHA_512),
-41 => Ok(COSEAlgorithm::RSAES_OAEP_SHA_256),
-40 => Ok(COSEAlgorithm::RSAES_OAEP_RFC_8017_default),
-39 => Ok(COSEAlgorithm::PS512),
-38 => Ok(COSEAlgorithm::PS384),
-37 => Ok(COSEAlgorithm::PS256),
-36 => Ok(COSEAlgorithm::ES512),
-35 => Ok(COSEAlgorithm::ES384),
-34 => Ok(COSEAlgorithm::ECDH_SS_A256KW),
-33 => Ok(COSEAlgorithm::ECDH_SS_A192KW),
-32 => Ok(COSEAlgorithm::ECDH_SS_A128KW),
-31 => Ok(COSEAlgorithm::ECDH_ES_A256KW),
-30 => Ok(COSEAlgorithm::ECDH_ES_A192KW),
-29 => Ok(COSEAlgorithm::ECDH_ES_A128KW),
-28 => Ok(COSEAlgorithm::ECDH_SS_HKDF512),
-27 => Ok(COSEAlgorithm::ECDH_SS_HKDF256),
-26 => Ok(COSEAlgorithm::ECDH_ES_HKDF512),
-25 => Ok(COSEAlgorithm::ECDH_ES_HKDF256),
-18 => Ok(COSEAlgorithm::SHAKE128),
-17 => Ok(COSEAlgorithm::SHA512_256),
-16 => Ok(COSEAlgorithm::SHA256),
-15 => Ok(COSEAlgorithm::SHA256_64),
-14 => Ok(COSEAlgorithm::SHA1),
-13 => Ok(COSEAlgorithm::Direct_HKDF_AES256),
-12 => Ok(COSEAlgorithm::Direct_HKDF_AES128),
-11 => Ok(COSEAlgorithm::Direct_HKDF_SHA512),
-10 => Ok(COSEAlgorithm::Direct_HKDF_SHA256),
-8 => Ok(COSEAlgorithm::EDDSA),
-7 => Ok(COSEAlgorithm::ES256),
-6 => Ok(COSEAlgorithm::Direct),
-5 => Ok(COSEAlgorithm::A256KW),
-4 => Ok(COSEAlgorithm::A192KW),
-3 => Ok(COSEAlgorithm::A128KW),
1 => Ok(COSEAlgorithm::A128GCM),
2 => Ok(COSEAlgorithm::A192GCM),
3 => Ok(COSEAlgorithm::A256GCM),
4 => Ok(COSEAlgorithm::HMAC256_64),
5 => Ok(COSEAlgorithm::HMAC256_256),
6 => Ok(COSEAlgorithm::HMAC384_384),
7 => Ok(COSEAlgorithm::HMAC512_512),
10 => Ok(COSEAlgorithm::AES_CCM_16_64_128),
11 => Ok(COSEAlgorithm::AES_CCM_16_64_256),
12 => Ok(COSEAlgorithm::AES_CCM_64_64_128),
13 => Ok(COSEAlgorithm::AES_CCM_64_64_256),
14 => Ok(COSEAlgorithm::AES_MAC_128_64),
15 => Ok(COSEAlgorithm::AES_MAC_256_64),
24 => Ok(COSEAlgorithm::ChaCha20_Poly1305),
25 => Ok(COSEAlgorithm::AES_MAC_128_128),
26 => Ok(COSEAlgorithm::AES_MAC_256_128),
30 => Ok(COSEAlgorithm::AES_CCM_16_128_128),
31 => Ok(COSEAlgorithm::AES_CCM_16_128_256),
32 => Ok(COSEAlgorithm::AES_CCM_64_128_128),
33 => Ok(COSEAlgorithm::AES_CCM_64_128_256),
34 => Ok(COSEAlgorithm::IV_GENERATION),
-65535 => Ok(COSEAlgorithm::INSECURE_RS1),
_ => Err(CryptoError::UnknownAlgorithm),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct COSEEC2Key {
pub curve: ECDSACurve,
pub x: Vec<u8>,
pub y: Vec<u8>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct COSEOKPKey {
pub curve: ECDSACurve,
pub x: Vec<u8>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct COSERSAKey {
pub n: Vec<u8>,
pub e: Vec<u8>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct COSESymmetricKey {
pub key: Vec<u8>,
}
#[allow(non_camel_case_types)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[repr(i64)]
pub enum COSEKeyTypeId {
OKP = 1,
EC2 = 2,
RSA = 3,
Symmetric = 4,
}
impl TryFrom<u64> for COSEKeyTypeId {
type Error = CryptoError;
fn try_from(i: u64) -> Result<Self, Self::Error> {
match i {
1 => Ok(COSEKeyTypeId::OKP),
2 => Ok(COSEKeyTypeId::EC2),
3 => Ok(COSEKeyTypeId::RSA),
4 => Ok(COSEKeyTypeId::Symmetric),
_ => Err(CryptoError::UnknownKeyType),
}
}
}
#[allow(non_camel_case_types)]
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum COSEKeyType {
OKP(COSEOKPKey), EC2(COSEEC2Key),
RSA(COSERSAKey), Symmetric(COSESymmetricKey), }
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct COSEKey {
pub alg: COSEAlgorithm,
pub key: COSEKeyType,
}
impl<'de> Deserialize<'de> for COSEKey {
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
where
D: Deserializer<'de>,
{
struct COSEKeyVisitor;
impl<'de> Visitor<'de> for COSEKeyVisitor {
type Value = COSEKey;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("a map")
}
fn visit_map<M>(self, mut map: M) -> std::result::Result<Self::Value, M::Error>
where
M: MapAccess<'de>,
{
let mut curve: Option<ECDSACurve> = None;
let mut key_type: Option<COSEKeyTypeId> = None;
let mut alg: Option<COSEAlgorithm> = None;
let mut x: Option<Vec<u8>> = None;
let mut y: Option<Vec<u8>> = None;
while let Some(key) = map.next_key()? {
trace!("cose key {:?}", key);
match key {
1 => {
if key_type.is_some() {
return Err(SerdeError::duplicate_field("key_type"));
}
let value: u64 = map.next_value()?;
let val = COSEKeyTypeId::try_from(value).map_err(|_| {
SerdeError::custom(format!("unsupported key_type {}", value))
})?;
key_type = Some(val);
}
-1 => {
let key_type = key_type.ok_or(SerdeError::missing_field("key_type"))?;
if key_type == COSEKeyTypeId::RSA {
if y.is_some() {
return Err(SerdeError::duplicate_field("y"));
}
let value: ByteBuf = map.next_value()?;
y = Some(value.to_vec());
} else {
if curve.is_some() {
return Err(SerdeError::duplicate_field("curve"));
}
let value: u64 = map.next_value()?;
let val = ECDSACurve::try_from(value).map_err(|_| {
SerdeError::custom(format!("unsupported curve {}", value))
})?;
curve = Some(val);
}
}
-2 => {
if x.is_some() {
return Err(SerdeError::duplicate_field("x"));
}
let value: ByteBuf = map.next_value()?;
x = Some(value.to_vec());
}
-3 => {
if y.is_some() {
return Err(SerdeError::duplicate_field("y"));
}
let value: ByteBuf = map.next_value()?;
y = Some(value.to_vec());
}
3 => {
if alg.is_some() {
return Err(SerdeError::duplicate_field("alg"));
}
let value: i64 = map.next_value()?;
let val = COSEAlgorithm::try_from(value).map_err(|_| {
SerdeError::custom(format!("unsupported algorithm {}", value))
})?;
alg = Some(val);
}
_ => {
let value: Value = map.next_value()?;
trace!("cose unknown value {:?}:{:?}", key, value);
}
};
}
let key_type = key_type.ok_or(SerdeError::missing_field("key_type"))?;
let x = x.ok_or(SerdeError::missing_field("x"))?;
let alg = alg.ok_or(SerdeError::missing_field("alg"))?;
let res = match key_type {
COSEKeyTypeId::OKP => {
let curve = curve.ok_or(SerdeError::missing_field("curve"))?;
COSEKeyType::OKP(COSEOKPKey { curve, x })
}
COSEKeyTypeId::EC2 => {
let curve = curve.ok_or(SerdeError::missing_field("curve"))?;
let y = y.ok_or(SerdeError::missing_field("y"))?;
COSEKeyType::EC2(COSEEC2Key { curve, x, y })
}
COSEKeyTypeId::RSA => {
let e = y.ok_or(SerdeError::missing_field("y"))?;
COSEKeyType::RSA(COSERSAKey { e, n: x })
}
COSEKeyTypeId::Symmetric => COSEKeyType::Symmetric(COSESymmetricKey { key: x }),
};
Ok(COSEKey { alg, key: res })
}
}
deserializer.deserialize_bytes(COSEKeyVisitor)
}
}
impl Serialize for COSEKey {
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: Serializer,
{
let map_len = match &self.key {
COSEKeyType::OKP(_) => 3,
COSEKeyType::EC2(_) => 5,
COSEKeyType::RSA(_) => 4,
COSEKeyType::Symmetric(_) => 3,
};
let mut map = serializer.serialize_map(Some(map_len))?;
match &self.key {
COSEKeyType::OKP(key) => {
map.serialize_entry(&1, &COSEKeyTypeId::OKP)?;
map.serialize_entry(&3, &self.alg)?;
map.serialize_entry(&-1, &key.curve)?;
map.serialize_entry(&-2, &key.x)?;
}
COSEKeyType::EC2(key) => {
map.serialize_entry(&1, &(COSEKeyTypeId::EC2 as u8))?;
map.serialize_entry(&3, &self.alg)?;
map.serialize_entry(&-1, &(key.curve as u8))?;
map.serialize_entry(&-2, &serde_bytes::Bytes::new(&key.x))?;
map.serialize_entry(&-3, &serde_bytes::Bytes::new(&key.y))?;
}
COSEKeyType::RSA(key) => {
map.serialize_entry(&1, &COSEKeyTypeId::RSA)?;
map.serialize_entry(&3, &self.alg)?;
map.serialize_entry(&-1, &key.n)?;
map.serialize_entry(&-2, &key.e)?;
}
COSEKeyType::Symmetric(key) => {
map.serialize_entry(&1, &COSEKeyTypeId::Symmetric)?;
map.serialize_entry(&3, &self.alg)?;
map.serialize_entry(&-1, &key.key)?;
}
}
map.end()
}
}
#[derive(Debug)]
pub enum CryptoError {
UnknownKeyType,
UnknownSignatureScheme,
UnknownAlgorithm,
WrongSaltLength,
Backend(BackendError),
}
impl From<BackendError> for CryptoError {
fn from(e: BackendError) -> Self {
CryptoError::Backend(e)
}
}
impl From<CryptoError> for CommandError {
fn from(e: CryptoError) -> Self {
CommandError::Crypto(e)
}
}
impl From<CryptoError> for AuthenticatorError {
fn from(e: CryptoError) -> Self {
AuthenticatorError::HIDError(HIDError::Command(CommandError::Crypto(e)))
}
}
#[derive(Clone)]
pub struct ECDHSecret {
remote: COSEKey,
my: COSEKey,
shared_secret: Vec<u8>,
}
impl ECDHSecret {
pub fn my_public_key(&self) -> &COSEKey {
&self.my
}
pub fn shared_secret(&self) -> &[u8] {
&self.shared_secret
}
}
impl fmt::Debug for ECDHSecret {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"ECDHSecret(remote: {:?}, my: {:?})",
self.remote,
self.my_public_key()
)
}
}
#[cfg(all(test, not(feature = "crypto_dummy")))]
mod test {
use super::{
authenticate, decrypt, encrypt, imp::parse_key, imp::test_encapsulate, serialize_key,
COSEAlgorithm, COSEKey, ECDSACurve,
};
use crate::crypto::{COSEEC2Key, COSEKeyType};
use crate::ctap2::commands::client_pin::Pin;
use crate::util::decode_hex;
use serde_cbor::de::from_slice;
#[test]
fn test_serialize_key() {
let x = [
0xfc, 0x9e, 0xd3, 0x6f, 0x7c, 0x1a, 0xa9, 0x15, 0xce, 0x3e, 0xa1, 0x77, 0xf0, 0x75,
0x67, 0xf0, 0x7f, 0x16, 0xf9, 0x47, 0x9d, 0x95, 0xad, 0x8e, 0xd4, 0x97, 0x1d, 0x33,
0x05, 0xe3, 0x1a, 0x80,
];
let y = [
0x50, 0xb7, 0x33, 0xaf, 0x8c, 0x0b, 0x0e, 0xe1, 0xda, 0x8d, 0xe0, 0xac, 0xf9, 0xd8,
0xe1, 0x32, 0x82, 0xf0, 0x63, 0xb7, 0xb3, 0x0d, 0x73, 0xd4, 0xd3, 0x2c, 0x9a, 0xad,
0x6d, 0xfa, 0x8b, 0x27,
];
let serialized_key = [
0x04, 0xfc, 0x9e, 0xd3, 0x6f, 0x7c, 0x1a, 0xa9, 0x15, 0xce, 0x3e, 0xa1, 0x77, 0xf0,
0x75, 0x67, 0xf0, 0x7f, 0x16, 0xf9, 0x47, 0x9d, 0x95, 0xad, 0x8e, 0xd4, 0x97, 0x1d,
0x33, 0x05, 0xe3, 0x1a, 0x80, 0x50, 0xb7, 0x33, 0xaf, 0x8c, 0x0b, 0x0e, 0xe1, 0xda,
0x8d, 0xe0, 0xac, 0xf9, 0xd8, 0xe1, 0x32, 0x82, 0xf0, 0x63, 0xb7, 0xb3, 0x0d, 0x73,
0xd4, 0xd3, 0x2c, 0x9a, 0xad, 0x6d, 0xfa, 0x8b, 0x27,
];
let (res_x, res_y) =
serialize_key(ECDSACurve::SECP256R1, &serialized_key).expect("Failed to serialize key");
assert_eq!(res_x, x);
assert_eq!(res_y, y);
let res_key = parse_key(ECDSACurve::SECP256R1, &x, &y).expect("Failed to parse key");
assert_eq!(res_key, serialized_key)
}
#[test]
fn test_parse_es256_serialize_key() {
let key_data = decode_hex("A5010203262001215820A5FD5CE1B1C458C530A54FA61B31BF6B04BE8B97AFDE54DD8CBB69275A8A1BE1225820FA3A3231DD9DEED9D1897BE5A6228C59501E4BCD12975D3DFF730F01278EA61C");
let key: COSEKey = from_slice(&key_data).unwrap();
assert_eq!(key.alg, COSEAlgorithm::ES256);
if let COSEKeyType::EC2(ec2key) = &key.key {
assert_eq!(ec2key.curve, ECDSACurve::SECP256R1);
assert_eq!(
ec2key.x,
decode_hex("A5FD5CE1B1C458C530A54FA61B31BF6B04BE8B97AFDE54DD8CBB69275A8A1BE1")
);
assert_eq!(
ec2key.y,
decode_hex("FA3A3231DD9DEED9D1897BE5A6228C59501E4BCD12975D3DFF730F01278EA61C")
);
} else {
panic!("Wrong key type!");
}
let serialized = serde_cbor::to_vec(&key).expect("Failed to serialize key");
assert_eq!(key_data, serialized);
}
#[test]
#[allow(non_snake_case)]
fn test_shared_secret() {
let EC_PRIV =
decode_hex("7452E599FEE739D8A653F6A507343D12D382249108A651402520B72F24FE7684");
let EC_PUB_X =
decode_hex("44D78D7989B97E62EA993496C9EF6E8FD58B8B00715F9A89153DDD9C4657E47F");
let EC_PUB_Y =
decode_hex("EC802EE7D22BD4E100F12E48537EB4E7E96ED3A47A0A3BD5F5EEAB65001664F9");
let DEV_PUB_X =
decode_hex("0501D5BC78DA9252560A26CB08FCC60CBE0B6D3B8E1D1FCEE514FAC0AF675168");
let DEV_PUB_Y =
decode_hex("D551B3ED46F665731F95B4532939C25D91DB7EB844BD96D4ABD4083785F8DF47");
let SHARED = decode_hex("c42a039d548100dfba521e487debcbbb8b66bb7496f8b1862a7a395ed83e1a1c");
let TOKEN_ENC = decode_hex("7A9F98E31B77BE90F9C64D12E9635040");
let TOKEN = decode_hex("aff12c6dcfbf9df52f7a09211e8865cd");
let PIN_HASH_ENC = decode_hex("afe8327ce416da8ee3d057589c2ce1a9");
let my_pub_key_data = parse_key(ECDSACurve::SECP256R1, &EC_PUB_X, &EC_PUB_Y).unwrap();
let peer_key = COSEEC2Key {
curve: ECDSACurve::SECP256R1,
x: DEV_PUB_X,
y: DEV_PUB_Y,
};
let shared_secret =
test_encapsulate(&peer_key, COSEAlgorithm::ES256, &my_pub_key_data, &EC_PRIV).unwrap();
assert_eq!(shared_secret.shared_secret, SHARED);
let token_enc = encrypt(&shared_secret.shared_secret(), &TOKEN).unwrap();
assert_eq!(token_enc, TOKEN_ENC);
let token = decrypt(&shared_secret.shared_secret(), &TOKEN_ENC).unwrap();
assert_eq!(token, TOKEN);
let pin = Pin::new("1234");
let pin_hash_enc =
encrypt(&shared_secret.shared_secret(), pin.for_pin_token().as_ref()).unwrap();
assert_eq!(pin_hash_enc, PIN_HASH_ENC);
}
#[test]
fn test_authenticate() {
let key = "key";
let message = "The quick brown fox jumps over the lazy dog";
let expected =
decode_hex("f7bc83f430538424b13298e6aa6fb143ef4d59a14946175997479dbc2d1a3cd8");
let result =
authenticate(key.as_bytes(), message.as_bytes()).expect("Failed to authenticate");
assert_eq!(result, expected);
let key = "The quick brown fox jumps over the lazy dogThe quick brown fox jumps over the lazy dog";
let message = "message";
let expected =
decode_hex("5597b93a2843078cbb0c920ae41dfe20f1685e10c67e423c11ab91adfc319d12");
let result =
authenticate(key.as_bytes(), message.as_bytes()).expect("Failed to authenticate");
assert_eq!(result, expected);
}
#[test]
fn test_pin_encryption_and_hashing() {
let pin = "1234";
let shared_secret = vec![
0x82, 0xE3, 0xD8, 0x41, 0xE2, 0x5C, 0x5C, 0x13, 0x46, 0x2C, 0x12, 0x3C, 0xC3, 0xD3,
0x98, 0x78, 0x65, 0xBA, 0x3D, 0x20, 0x46, 0x74, 0xFB, 0xED, 0xD4, 0x7E, 0xF5, 0xAB,
0xAB, 0x8D, 0x13, 0x72,
];
let expected_new_pin_enc = vec![
0x70, 0x66, 0x4B, 0xB5, 0x81, 0xE2, 0x57, 0x45, 0x1A, 0x3A, 0xB9, 0x1B, 0xF1, 0xAA,
0xD8, 0xE4, 0x5F, 0x6C, 0xE9, 0xB5, 0xC3, 0xB0, 0xF3, 0x2B, 0x5E, 0xCD, 0x62, 0xD0,
0xBA, 0x3B, 0x60, 0x5F, 0xD9, 0x18, 0x31, 0x66, 0xF6, 0xC5, 0xFA, 0xF3, 0xE4, 0xDA,
0x24, 0x81, 0x50, 0x2C, 0xD0, 0xCE, 0xE0, 0x15, 0x8B, 0x35, 0x1F, 0xC3, 0x92, 0x08,
0xA7, 0x7C, 0xB2, 0x74, 0x4B, 0xD4, 0x3C, 0xF9,
];
let expected_pin_auth = vec![
0x8E, 0x7F, 0x01, 0x69, 0x97, 0xF3, 0xB0, 0xA2, 0x7B, 0xA4, 0x34, 0x7A, 0x0E, 0x49,
0xFD, 0xF5,
];
let input: Vec<u8> = pin
.as_bytes()
.iter()
.chain(std::iter::repeat(&0x00))
.take(64)
.cloned()
.collect();
let new_pin_enc = encrypt(&shared_secret, &input).expect("Failed to encrypt pin");
assert_eq!(new_pin_enc, expected_new_pin_enc);
let pin_auth = authenticate(&shared_secret, &new_pin_enc).expect("Failed to authenticate");
assert_eq!(pin_auth[0..16], expected_pin_auth);
}
}