Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  mod.rs   Sprache: unbekannt

 
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use crate::ctap2::commands::client_pin::PinUvAuthTokenPermission;
use crate::ctap2::commands::get_info::AuthenticatorInfo;
use crate::errors::AuthenticatorError;
use crate::{ctap2::commands::CommandError, transport::errors::HIDError};
use serde::{
    de::{Error as SerdeError, MapAccess, Unexpected, Visitor},
    Deserialize, Deserializer, Serialize, Serializer,
};
use serde_bytes::ByteBuf;
use std::convert::TryFrom;
use std::fmt;

#[cfg(feature = "crypto_nss")]
mod nss;
#[cfg(feature = "crypto_nss")]
use nss as backend;

#[cfg(feature = "crypto_openssl")]
mod openssl;
#[cfg(feature = "crypto_openssl")]
use self::openssl as backend;

#[cfg(feature = "crypto_dummy")]
mod dummy;
#[cfg(feature = "crypto_dummy")]
use dummy as backend;

use backend::{
    decrypt_aes_256_cbc_no_pad, ecdhe_p256_raw, encrypt_aes_256_cbc_no_pad, gen_p256, hmac_sha256,
    random_bytes, sha256,
};

mod der;

pub use backend::ecdsa_p256_sha256_sign_raw;

pub struct PinUvAuthProtocol(Box<dyn PinProtocolImpl + Send + Sync>);
impl PinUvAuthProtocol {
    pub fn id(&self) -> u64 {
        self.0.protocol_id()
    }
    pub fn encapsulate(&self, peer_cose_key: &COSEKey) -> Result<SharedSecret, CryptoError> {
        self.0.encapsulate(peer_cose_key)
    }
}

/// The output of `PinUvAuthProtocol::encapsulate` is supposed to be used with the same
/// PinProtocolImpl. So we stash a copy of the calling PinUvAuthProtocol in the output SharedSecret.
/// We need a trick here to tell the compiler that every PinProtocolImpl we define will implement
/// Clone.
trait ClonablePinProtocolImpl {
    fn clone_box(&self) -> Box<dyn PinProtocolImpl + Send + Sync>;
}

impl<T> ClonablePinProtocolImpl for T
where
    T: 'static + PinProtocolImpl + Clone + Send + Sync,
{
    fn clone_box(&self) -> Box<dyn PinProtocolImpl + Send + Sync> {
        Box::new(self.clone())
    }
}

impl Clone for PinUvAuthProtocol {
    fn clone(&self) -> Self {
        PinUvAuthProtocol(self.0.as_ref().clone_box())
    }
}

/// CTAP 2.1, Section 6.5.4. PIN/UV Auth Protocol Abstract Definition
trait PinProtocolImpl: ClonablePinProtocolImpl {
    fn protocol_id(&self) -> u64;
    fn initialize(&self);
    fn encrypt(&self, key: &[u8], plaintext: &[u8]) -> Result<Vec<u8>, CryptoError>;
    fn decrypt(&self, key: &[u8], ciphertext: &[u8]) -> Result<Vec<u8>, CryptoError>;
    fn authenticate(&self, key: &[u8], message: &[u8]) -> Result<Vec<u8>, CryptoError>;
    fn kdf(&self, z: &[u8]) -> Result<Vec<u8>, CryptoError>;
    fn encapsulate(&self, peer_cose_key: &COSEKey) -> Result<SharedSecret, CryptoError> {
        // [CTAP 2.1]
        // encapsulate(peerCoseKey) → (coseKey, sharedSecret) | error
        //      1) Let sharedSecret be the result of calling ecdh(peerCoseKey). Return any
        //         resulting error.
        //      2) Return (getPublicKey(), sharedSecret)
        //
        // ecdh(peerCoseKey) → sharedSecret | error
        //         Parse peerCoseKey as specified for getPublicKey, below, and produce a P-256
        //         point, Y. If unsuccessful, or if the resulting point is not on the curve, return
        //         error.  Calculate xY, the shared point. (I.e. the scalar-multiplication of the
        //         peer's point, Y, with the local private key agreement key.) Let Z be the
        //         32-byte, big-endian encoding of the x-coordinate of the shared point.  Return
        //         kdf(Z).

        match peer_cose_key.alg {
            // There is no COSEAlgorithm for ECDHE with the KDF used here. Section 6.5.6. of CTAP
            // 2.1 says to use value -25 (= ECDH_ES_HKDF256) even though "this is not the algorithm
            // actually used".
            COSEAlgorithm::ECDH_ES_HKDF256 => (),
            other => return Err(CryptoError::UnsupportedAlgorithm(other)),
        }

        let peer_cose_ec2_key = match peer_cose_key.key {
            COSEKeyType::EC2(ref key) => key,
            _ => return Err(CryptoError::UnsupportedKeyType),
        };

        let peer_spki = peer_cose_ec2_key.der_spki()?;

        let (shared_point, client_public_sec1) = ecdhe_p256_raw(&peer_spki)?;

        let client_cose_ec2_key =
            COSEEC2Key::from_sec1_uncompressed(Curve::SECP256R1, &client_public_sec1)?;

        let client_cose_key = COSEKey {
            alg: COSEAlgorithm::ECDH_ES_HKDF256,
            key: COSEKeyType::EC2(client_cose_ec2_key),
        };

        let shared_secret = SharedSecret {
            pin_protocol: PinUvAuthProtocol(self.clone_box()),
            key: self.kdf(&shared_point)?,
            inputs: PublicInputs {
                peer: peer_cose_key.clone(),
                client: client_cose_key,
            },
        };

        Ok(shared_secret)
    }
}

impl TryFrom<&AuthenticatorInfo> for PinUvAuthProtocol {
    type Error = CommandError;

    fn try_from(info: &AuthenticatorInfo) -> Result<Self, Self::Error> {
        // CTAP 2.1, Section 6.5.5.4
        // "If there are multiple mutually supported protocols, and the platform
        // has no preference, it SHOULD select the one listed first in
        // pinUvAuthProtocols."
        if let Some(pin_protocols) = &info.pin_protocols {
            for proto_id in pin_protocols.iter() {
                match proto_id {
                    1 => return Ok(PinUvAuthProtocol(Box::new(PinUvAuth1 {}))),
                    2 => return Ok(PinUvAuthProtocol(Box::new(PinUvAuth2 {}))),
                    _ => continue,
                }
            }
        } else {
            match info.max_supported_version() {
                crate::ctap2::commands::get_info::AuthenticatorVersion::U2F_V2 => {
                    return Err(CommandError::UnsupportedPinProtocol)
                }
                crate::ctap2::commands::get_info::AuthenticatorVersion::FIDO_2_0 => {
                    return Ok(PinUvAuthProtocol(Box::new(PinUvAuth1 {})))
                }
                crate::ctap2::commands::get_info::AuthenticatorVersion::FIDO_2_1_PRE
                | crate::ctap2::commands::get_info::AuthenticatorVersion::FIDO_2_1 => {
                    return Ok(PinUvAuthProtocol(Box::new(PinUvAuth2 {})))
                }
            }
        }
        Err(CommandError::UnsupportedPinProtocol)
    }
}

impl fmt::Debug for PinUvAuthProtocol {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("PinUvAuthProtocol")
            .field("id", &self.id())
            .finish()
    }
}

/// CTAP 2.1, Section 6.5.6.
#[derive(Copy, Clone)]
pub struct PinUvAuth1;

impl PinProtocolImpl for PinUvAuth1 {
    fn protocol_id(&self) -> u64 {
        1
    }

    fn initialize(&self) {}

    fn encrypt(&self, key: &[u8], plaintext: &[u8]) -> Result<Vec<u8>, CryptoError> {
        // [CTAP 2.1]
        // encrypt(key, demPlaintext) → ciphertext
        //      Return the AES-256-CBC encryption of plaintext using an all-zero IV. (No padding is
        //      performed as the size of plaintext is required to be a multiple of the AES block
        //      length.)
        encrypt_aes_256_cbc_no_pad(key, None, plaintext)
    }

    fn decrypt(&self, key: &[u8], ciphertext: &[u8]) -> Result<Vec<u8>, CryptoError> {
        // [CTAP 2.1]
        // decrypt(key, demCiphertext) → plaintext | error
        //      If the size of ciphertext is not a multiple of the AES block length, return error.
        //      Otherwise return the AES-256-CBC decryption of ciphertext using an all-zero IV.
        decrypt_aes_256_cbc_no_pad(key, None, ciphertext)
    }

    fn authenticate(&self, key: &[u8], message: &[u8]) -> Result<Vec<u8>, CryptoError> {
        // [CTAP 2.1]
        // authenticate(key, message) → signature
        //      Return the first 16 bytes of the result of computing HMAC-SHA-256 with the given
        //      key and message.
        let mut hmac = hmac_sha256(key, message)?;
        hmac.truncate(16);
        Ok(hmac)
    }

    fn kdf(&self, z: &[u8]) -> Result<Vec<u8>, CryptoError> {
        // kdf(Z) → sharedSecret
        //         Return SHA-256(Z)
        sha256(z)
    }
}

/// CTAP 2.1, Section 6.5.7.
#[derive(Copy, Clone)]
pub struct PinUvAuth2;

impl PinProtocolImpl for PinUvAuth2 {
    fn protocol_id(&self) -> u64 {
        2
    }

    fn initialize(&self) {}

    fn encrypt(&self, key: &[u8], plaintext: &[u8]) -> Result<Vec<u8>, CryptoError> {
        // [CTAP 2.1]
        // encrypt(key, demPlaintext) → ciphertext
        //      1. Discard the first 32 bytes of key. (This selects the AES-key portion of the
        //         shared secret.)
        //      2. Let iv be a 16-byte, random bytestring.
        //      3. Let ct be the AES-256-CBC encryption of demPlaintext using key and iv. (No
        //         padding is performed as the size of demPlaintext is required to be a multiple of
        //         the AES block length.)
        //      4. Return iv || ct.
        if key.len() != 64 {
            return Err(CryptoError::LibraryFailure);
        }
        let key = &key[32..64];

        let iv = random_bytes(16)?;
        let mut ct = encrypt_aes_256_cbc_no_pad(key, Some(&iv), plaintext)?;

        let mut out = iv;
        out.append(&mut ct);
        Ok(out)
    }

    fn decrypt(&self, key: &[u8], ciphertext: &[u8]) -> Result<Vec<u8>, CryptoError> {
        // decrypt(key, demCiphertext) → plaintext | error
        //      1. Discard the first 32 bytes of key. (This selects the AES-key portion of the
        //         shared secret.)
        //      2. If demCiphertext is less than 16 bytes in length, return an error
        //      3. Split demCiphertext after the 16th byte to produce two subspans, iv and ct.
        //      4. Return the AES-256-CBC decryption of ct using key and iv.
        if key.len() < 64 || ciphertext.len() < 16 {
            return Err(CryptoError::LibraryFailure);
        }
        let key = &key[32..64];
        let (iv, ct) = ciphertext.split_at(16);
        decrypt_aes_256_cbc_no_pad(key, Some(iv), ct)
    }

    fn authenticate(&self, key: &[u8], message: &[u8]) -> Result<Vec<u8>, CryptoError> {
        // authenticate(key, message) → signature
        //      1. If key is longer than 32 bytes, discard the excess. (This selects the HMAC-key
        //         portion of the shared secret. When key is the pinUvAuthToken, it is exactly 32
        //         bytes long and thus this step has no effect.)
        //      2. Return the result of computing HMAC-SHA-256 on key and message.
        if key.len() < 32 {
            return Err(CryptoError::LibraryFailure);
        }
        let key = &key[0..32];
        hmac_sha256(key, message)
    }

    fn kdf(&self, z: &[u8]) -> Result<Vec<u8>, CryptoError> {
        // kdf(Z) → sharedSecret
        //      return HKDF-SHA-256(salt, Z, L = 32, info = "CTAP2 HMAC key") ||
        //             HKDF-SHA-256(salt, Z, L = 32, info = "CTAP2 AES key")
        // where salt = [0u8; 32].
        //
        // From Section 2 of RFC 5869, we have
        //   HKDF(salt, Z, 32, info) =
        //      HKDF-Expand(HKDF-Extract(salt, Z), info || 0x01)
        //
        // And for HKDF-SHA256 both Extract and Expand are instantiated with HMAC-SHA256.

        let prk = hmac_sha256(&[0u8; 32], z)?;
        let mut shared_secret = hmac_sha256(&prk, "CTAP2 HMAC key\x01".as_bytes())?;
        shared_secret.append(&mut hmac_sha256(&prk, "CTAP2 AES key\x01".as_bytes())?);
        Ok(shared_secret)
    }
}

#[derive(Clone, Debug)]
struct PublicInputs {
    client: COSEKey,
    peer: COSEKey,
}

#[derive(Clone, Debug)]
pub struct SharedSecret {
    pub pin_protocol: PinUvAuthProtocol,
    key: Vec<u8>,
    inputs: PublicInputs,
}

impl SharedSecret {
    pub fn encrypt(&self, plaintext: &[u8]) -> Result<Vec<u8>, CryptoError> {
        self.pin_protocol.0.encrypt(&self.key, plaintext)
    }
    pub fn decrypt(&self, ciphertext: &[u8]) -> Result<Vec<u8>, CryptoError> {
        self.pin_protocol.0.decrypt(&self.key, ciphertext)
    }
    pub fn decrypt_pin_token(
        &self,
        permissions: PinUvAuthTokenPermission,
        encrypted_pin_token: &[u8],
    ) -> Result<PinUvAuthToken, CryptoError> {
        let pin_token = self.decrypt(encrypted_pin_token)?;
        Ok(PinUvAuthToken {
            pin_protocol: self.pin_protocol.clone(),
            pin_token,
            permissions,
        })
    }
    pub fn authenticate(&self, message: &[u8]) -> Result<Vec<u8>, CryptoError> {
        self.pin_protocol.0.authenticate(&self.key, message)
    }
    pub fn client_input(&self) -> &COSEKey {
        &self.inputs.client
    }
    pub fn peer_input(&self) -> &COSEKey {
        &self.inputs.peer
    }

    #[cfg(test)]
    pub fn new_test(
        pin_protocol: PinUvAuthProtocol,
        key: Vec<u8>,
        client_input: COSEKey,
        peer_input: COSEKey,
    ) -> Self {
        Self {
            pin_protocol,
            key,
            inputs: PublicInputs {
                client: client_input,
                peer: peer_input,
            },
        }
    }
}

#[derive(Clone, Debug)]
pub struct PinUvAuthToken {
    pub pin_protocol: PinUvAuthProtocol,
    pin_token: Vec<u8>,
    pub permissions: PinUvAuthTokenPermission,
}

impl PinUvAuthToken {
    pub fn derive(self, message: &[u8]) -> Result<PinUvAuthParam, CryptoError> {
        let pin_auth = self.pin_protocol.0.authenticate(&self.pin_token, message)?;
        Ok(PinUvAuthParam {
            pin_auth,
            pin_protocol: self.pin_protocol,
            permissions: self.permissions,
        })
    }
}

#[derive(Clone, Debug)]
pub struct PinUvAuthParam {
    pin_auth: Vec<u8>,
    pub pin_protocol: PinUvAuthProtocol,
    #[allow(dead_code)] // Not yet used
    permissions: PinUvAuthTokenPermission,
}

impl PinUvAuthParam {
    pub(crate) fn create_empty() -> Self {
        let pin_protocol = PinUvAuthProtocol(Box::new(PinUvAuth1 {}));
        Self {
            pin_auth: vec![],
            pin_protocol,
            permissions: PinUvAuthTokenPermission::empty(),
        }
    }

    #[cfg(test)]
    pub(crate) fn create_test(
        pin_protocol: u64,
        pin_auth: Vec<u8>,
        permissions: PinUvAuthTokenPermission,
    ) -> Self {
        let pin_protocol = PinUvAuthProtocol::try_from(&AuthenticatorInfo {
            pin_protocols: Some(vec![pin_protocol]),
            ..Default::default()
        })
        .expect("Failed to create PIN protocol");
        Self {
            pin_auth,
            pin_protocol,
            permissions,
        }
    }
}

impl Serialize for PinUvAuthParam {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        serde_bytes::serialize(&self.pin_auth[..], serializer)
    }
}

/// A Curve identifier. You probably will never need to alter
/// or use this value, as it is set inside the Credential for you.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Curve {
    // +---------+-------+----------+------------------------------------+
    // | Name    | Value | Key Type | Description                        |
    // +---------+-------+----------+------------------------------------+
    // | P-256   | 1     | EC2      | NIST P-256 also known as secp256r1 |
    // | P-384   | 2     | EC2      | NIST P-384 also known as secp384r1 |
    // | P-521   | 3     | EC2      | NIST P-521 also known as secp521r1 |
    // | X25519  | 4     | OKP      | X25519 for use w/ ECDH only        |
    // | X448    | 5     | OKP      | X448 for use w/ ECDH only          |
    // | Ed25519 | 6     | OKP      | Ed25519 for use w/ EdDSA only      |
    // | Ed448   | 7     | OKP      | Ed448 for use w/ EdDSA only        |
    // +---------+-------+----------+------------------------------------+
    /// Identifies this curve as SECP256R1 (X9_62_PRIME256V1 in OpenSSL)
    SECP256R1 = 1,
    /// Identifies this curve as SECP384R1
    SECP384R1 = 2,
    /// Identifies this curve as SECP521R1
    SECP521R1 = 3,
    /// Identifieds this as OKP X25519 for use w/ ECDH only
    X25519 = 4,
    /// Identifieds this as OKP X448 for use w/ ECDH only
    X448 = 5,
    /// Identifieds this as OKP Ed25519 for use w/ EdDSA only
    Ed25519 = 6,
    /// Identifieds this as OKP Ed448 for use w/ EdDSA only
    Ed448 = 7,
}

impl Serialize for Curve {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        serializer.serialize_i64(*self as i64)
    }
}

impl TryFrom<i64> for Curve {
    type Error = CryptoError;
    fn try_from(i: i64) -> Result<Self, Self::Error> {
        match i {
            i if i == Curve::SECP256R1 as i64 => Ok(Curve::SECP256R1),
            i if i == Curve::SECP384R1 as i64 => Ok(Curve::SECP384R1),
            i if i == Curve::SECP521R1 as i64 => Ok(Curve::SECP521R1),
            i if i == Curve::X25519 as i64 => Ok(Curve::X25519),
            i if i == Curve::X448 as i64 => Ok(Curve::X448),
            i if i == Curve::Ed25519 as i64 => Ok(Curve::Ed25519),
            i if i == Curve::Ed448 as i64 => Ok(Curve::Ed448),
            _ => Err(CryptoError::UnknownKeyType),
        }
    }
}
/// A COSE signature algorithm, indicating the type of key and hash type
/// that should be used.
/// see: https://www.iana.org/assignments/cose/cose.xhtml#table-algorithms
#[rustfmt::skip]
#[allow(non_camel_case_types)]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum COSEAlgorithm {
    // /// Identifies this key as ECDSA (recommended SECP256R1) with SHA256 hashing
    // //#[serde(alias = "ECDSA_SHA256")]
    // ES256 = -7, // recommends curve SECP256R1
    // /// Identifies this key as ECDSA (recommended SECP384R1) with SHA384 hashing
    // //#[serde(alias = "ECDSA_SHA384")]
    // ES384 = -35, // recommends curve SECP384R1
    // /// Identifies this key as ECDSA (recommended SECP521R1) with SHA512 hashing
    // //#[serde(alias = "ECDSA_SHA512")]
    // ES512 = -36, // recommends curve SECP521R1
    // /// Identifies this key as RS256 aka RSASSA-PKCS1-v1_5 w/ SHA-256
    // RS256 = -257,
    // /// Identifies this key as RS384 aka RSASSA-PKCS1-v1_5 w/ SHA-384
    // RS384 = -258,
    // /// Identifies this key as RS512 aka RSASSA-PKCS1-v1_5 w/ SHA-512
    // RS512 = -259,
    // /// Identifies this key as PS256 aka RSASSA-PSS w/ SHA-256
    // PS256 = -37,
    // /// Identifies this key as PS384 aka RSASSA-PSS w/ SHA-384
    // PS384 = -38,
    // /// Identifies this key as PS512 aka RSASSA-PSS w/ SHA-512
    // PS512 = -39,
    // /// Identifies this key as EdDSA (likely curve ed25519)
    // EDDSA = -8,
    // /// Identifies this as an INSECURE RS1 aka RSASSA-PKCS1-v1_5 using SHA-1. This is not
    // /// used by validators, but can exist in some windows hello tpm's
    // INSECURE_RS1 = -65535,
    INSECURE_RS1 = -65535,             //  RSASSA-PKCS1-v1_5 using SHA-1
    RS512 = -259,                      //    RSASSA-PKCS1-v1_5 using SHA-512
    RS384 = -258,                      //    RSASSA-PKCS1-v1_5 using SHA-384
    RS256 = -257,                      //    RSASSA-PKCS1-v1_5 using SHA-256
    ES256K = -47,                      //     ECDSA using secp256k1 curve and SHA-256
    HSS_LMS = -46,                     //     HSS/LMS hash-based digital signature
    SHAKE256 = -45,                    //     SHAKE-256 512-bit Hash Value
    SHA512 = -44,                      //     SHA-2 512-bit Hash
    SHA384 = -43,                      //     SHA-2 384-bit Hash
    RSAES_OAEP_SHA_512 = -42,          //     RSAES-OAEP w/ SHA-512
    RSAES_OAEP_SHA_256 = -41,          //     RSAES-OAEP w/ SHA-256
    RSAES_OAEP_RFC_8017_default = -40, //     RSAES-OAEP w/ SHA-1
    PS512 = -39,                       //     RSASSA-PSS w/ SHA-512
    PS384 = -38,                       //     RSASSA-PSS w/ SHA-384
    PS256 = -37,                       //     RSASSA-PSS w/ SHA-256
    ES512 = -36,                       //     ECDSA w/ SHA-512
    ES384 = -35,                       //     ECDSA w/ SHA-384
    ECDH_SS_A256KW = -34,              //     ECDH SS w/ Concat KDF and AES Key Wrap w/ 256-bit key
    ECDH_SS_A192KW = -33,              //     ECDH SS w/ Concat KDF and AES Key Wrap w/ 192-bit key
    ECDH_SS_A128KW = -32,              //     ECDH SS w/ Concat KDF and AES Key Wrap w/ 128-bit key
    ECDH_ES_A256KW = -31,              //     ECDH ES w/ Concat KDF and AES Key Wrap w/ 256-bit key
    ECDH_ES_A192KW = -30,              //     ECDH ES w/ Concat KDF and AES Key Wrap w/ 192-bit key
    ECDH_ES_A128KW = -29,              //     ECDH ES w/ Concat KDF and AES Key Wrap w/ 128-bit key
    ECDH_SS_HKDF512 = -28,             //     ECDH SS w/ HKDF - generate key directly
    ECDH_SS_HKDF256 = -27,             //     ECDH SS w/ HKDF - generate key directly
    ECDH_ES_HKDF512 = -26,             //     ECDH ES w/ HKDF - generate key directly
    ECDH_ES_HKDF256 = -25,             //     ECDH ES w/ HKDF - generate key directly
    SHAKE128 = -18,                    //     SHAKE-128 256-bit Hash Value
    SHA512_256 = -17,                  //     SHA-2 512-bit Hash truncated to 256-bits
    SHA256 = -16,                      //     SHA-2 256-bit Hash
    SHA256_64 = -15,                   //     SHA-2 256-bit Hash truncated to 64-bits
    SHA1 = -14,                        //     SHA-1 Hash
    Direct_HKDF_AES256 = -13,          //     Shared secret w/ AES-MAC 256-bit key
    Direct_HKDF_AES128 = -12,          //     Shared secret w/ AES-MAC 128-bit key
    Direct_HKDF_SHA512 = -11,          //     Shared secret w/ HKDF and SHA-512
    Direct_HKDF_SHA256 = -10,          //     Shared secret w/ HKDF and SHA-256
    EDDSA = -8,                        //  EdDSA
    ES256 = -7,                        //  ECDSA w/ SHA-256
    Direct = -6,                       //  Direct use of CEK
    A256KW = -5,                       //  AES Key Wrap w/ 256-bit key
    A192KW = -4,                       //  AES Key Wrap w/ 192-bit key
    A128KW = -3,                       //  AES Key Wrap w/ 128-bit key
    A128GCM = 1,                       //   AES-GCM mode w/ 128-bit key, 128-bit tag
    A192GCM = 2,                       //   AES-GCM mode w/ 192-bit key, 128-bit tag
    A256GCM = 3,                       //   AES-GCM mode w/ 256-bit key, 128-bit tag
    HMAC256_64 = 4,                    //   HMAC w/ SHA-256 truncated to 64 bits
    HMAC256_256 = 5,                   //   HMAC w/ SHA-256
    HMAC384_384 = 6,                   //   HMAC w/ SHA-384
    HMAC512_512 = 7,                   //   HMAC w/ SHA-512
    AES_CCM_16_64_128 = 10,            //  AES-CCM mode 128-bit key, 64-bit tag, 13-byte nonce
    AES_CCM_16_64_256 = 11,            //  AES-CCM mode 256-bit key, 64-bit tag, 13-byte nonce
    AES_CCM_64_64_128 = 12,            //  AES-CCM mode 128-bit key, 64-bit tag, 7-byte nonce
    AES_CCM_64_64_256 = 13,            //  AES-CCM mode 256-bit key, 64-bit tag, 7-byte nonce
    AES_MAC_128_64 = 14,               //  AES-MAC 128-bit key, 64-bit tag
    AES_MAC_256_64 = 15,               //  AES-MAC 256-bit key, 64-bit tag
    ChaCha20_Poly1305 = 24,            //  ChaCha20/Poly1305 w/ 256-bit key, 128-bit tag
    AES_MAC_128_128 = 25,              //  AES-MAC 128-bit key, 128-bit tag
    AES_MAC_256_128 = 26,              //  AES-MAC 256-bit key, 128-bit tag
    AES_CCM_16_128_128 = 30,           //  AES-CCM mode 128-bit key, 128-bit tag, 13-byte nonce
    AES_CCM_16_128_256 = 31,           //  AES-CCM mode 256-bit key, 128-bit tag, 13-byte nonce
    AES_CCM_64_128_128 = 32,           //  AES-CCM mode 128-bit key, 128-bit tag, 7-byte nonce
    AES_CCM_64_128_256 = 33,           //  AES-CCM mode 256-bit key, 128-bit tag, 7-byte nonce
    IV_GENERATION = 34,                //  For doing IV generation for symmetric algorithms.
}

impl Serialize for COSEAlgorithm {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        serializer.serialize_i64(*self as i64)
    }
}

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 {
            i if i == COSEAlgorithm::RS512 as i64 => Ok(COSEAlgorithm::RS512),
            i if i == COSEAlgorithm::RS384 as i64 => Ok(COSEAlgorithm::RS384),
            i if i == COSEAlgorithm::RS256 as i64 => Ok(COSEAlgorithm::RS256),
            i if i == COSEAlgorithm::ES256K as i64 => Ok(COSEAlgorithm::ES256K),
            i if i == COSEAlgorithm::HSS_LMS as i64 => Ok(COSEAlgorithm::HSS_LMS),
            i if i == COSEAlgorithm::SHAKE256 as i64 => Ok(COSEAlgorithm::SHAKE256),
            i if i == COSEAlgorithm::SHA512 as i64 => Ok(COSEAlgorithm::SHA512),
            i if i == COSEAlgorithm::SHA384 as i64 => Ok(COSEAlgorithm::SHA384),
            i if i == COSEAlgorithm::RSAES_OAEP_SHA_512 as i64 => {
                Ok(COSEAlgorithm::RSAES_OAEP_SHA_512)
            }
            i if i == COSEAlgorithm::RSAES_OAEP_SHA_256 as i64 => {
                Ok(COSEAlgorithm::RSAES_OAEP_SHA_256)
            }
            i if i == COSEAlgorithm::RSAES_OAEP_RFC_8017_default as i64 => {
                Ok(COSEAlgorithm::RSAES_OAEP_RFC_8017_default)
            }
            i if i == COSEAlgorithm::PS512 as i64 => Ok(COSEAlgorithm::PS512),
            i if i == COSEAlgorithm::PS384 as i64 => Ok(COSEAlgorithm::PS384),
            i if i == COSEAlgorithm::PS256 as i64 => Ok(COSEAlgorithm::PS256),
            i if i == COSEAlgorithm::ES512 as i64 => Ok(COSEAlgorithm::ES512),
            i if i == COSEAlgorithm::ES384 as i64 => Ok(COSEAlgorithm::ES384),
            i if i == COSEAlgorithm::ECDH_SS_A256KW as i64 => Ok(COSEAlgorithm::ECDH_SS_A256KW),
            i if i == COSEAlgorithm::ECDH_SS_A192KW as i64 => Ok(COSEAlgorithm::ECDH_SS_A192KW),
            i if i == COSEAlgorithm::ECDH_SS_A128KW as i64 => Ok(COSEAlgorithm::ECDH_SS_A128KW),
            i if i == COSEAlgorithm::ECDH_ES_A256KW as i64 => Ok(COSEAlgorithm::ECDH_ES_A256KW),
            i if i == COSEAlgorithm::ECDH_ES_A192KW as i64 => Ok(COSEAlgorithm::ECDH_ES_A192KW),
            i if i == COSEAlgorithm::ECDH_ES_A128KW as i64 => Ok(COSEAlgorithm::ECDH_ES_A128KW),
            i if i == COSEAlgorithm::ECDH_SS_HKDF512 as i64 => Ok(COSEAlgorithm::ECDH_SS_HKDF512),
            i if i == COSEAlgorithm::ECDH_SS_HKDF256 as i64 => Ok(COSEAlgorithm::ECDH_SS_HKDF256),
            i if i == COSEAlgorithm::ECDH_ES_HKDF512 as i64 => Ok(COSEAlgorithm::ECDH_ES_HKDF512),
            i if i == COSEAlgorithm::ECDH_ES_HKDF256 as i64 => Ok(COSEAlgorithm::ECDH_ES_HKDF256),
            i if i == COSEAlgorithm::SHAKE128 as i64 => Ok(COSEAlgorithm::SHAKE128),
            i if i == COSEAlgorithm::SHA512_256 as i64 => Ok(COSEAlgorithm::SHA512_256),
            i if i == COSEAlgorithm::SHA256 as i64 => Ok(COSEAlgorithm::SHA256),
            i if i == COSEAlgorithm::SHA256_64 as i64 => Ok(COSEAlgorithm::SHA256_64),
            i if i == COSEAlgorithm::SHA1 as i64 => Ok(COSEAlgorithm::SHA1),
            i if i == COSEAlgorithm::Direct_HKDF_AES256 as i64 => {
                Ok(COSEAlgorithm::Direct_HKDF_AES256)
            }
            i if i == COSEAlgorithm::Direct_HKDF_AES128 as i64 => {
                Ok(COSEAlgorithm::Direct_HKDF_AES128)
            }
            i if i == COSEAlgorithm::Direct_HKDF_SHA512 as i64 => {
                Ok(COSEAlgorithm::Direct_HKDF_SHA512)
            }
            i if i == COSEAlgorithm::Direct_HKDF_SHA256 as i64 => {
                Ok(COSEAlgorithm::Direct_HKDF_SHA256)
            }
            i if i == COSEAlgorithm::EDDSA as i64 => Ok(COSEAlgorithm::EDDSA),
            i if i == COSEAlgorithm::ES256 as i64 => Ok(COSEAlgorithm::ES256),
            i if i == COSEAlgorithm::Direct as i64 => Ok(COSEAlgorithm::Direct),
            i if i == COSEAlgorithm::A256KW as i64 => Ok(COSEAlgorithm::A256KW),
            i if i == COSEAlgorithm::A192KW as i64 => Ok(COSEAlgorithm::A192KW),
            i if i == COSEAlgorithm::A128KW as i64 => Ok(COSEAlgorithm::A128KW),
            i if i == COSEAlgorithm::A128GCM as i64 => Ok(COSEAlgorithm::A128GCM),
            i if i == COSEAlgorithm::A192GCM as i64 => Ok(COSEAlgorithm::A192GCM),
            i if i == COSEAlgorithm::A256GCM as i64 => Ok(COSEAlgorithm::A256GCM),
            i if i == COSEAlgorithm::HMAC256_64 as i64 => Ok(COSEAlgorithm::HMAC256_64),
            i if i == COSEAlgorithm::HMAC256_256 as i64 => Ok(COSEAlgorithm::HMAC256_256),
            i if i == COSEAlgorithm::HMAC384_384 as i64 => Ok(COSEAlgorithm::HMAC384_384),
            i if i == COSEAlgorithm::HMAC512_512 as i64 => Ok(COSEAlgorithm::HMAC512_512),
            i if i == COSEAlgorithm::AES_CCM_16_64_128 as i64 => {
                Ok(COSEAlgorithm::AES_CCM_16_64_128)
            }
            i if i == COSEAlgorithm::AES_CCM_16_64_256 as i64 => {
                Ok(COSEAlgorithm::AES_CCM_16_64_256)
            }
            i if i == COSEAlgorithm::AES_CCM_64_64_128 as i64 => {
                Ok(COSEAlgorithm::AES_CCM_64_64_128)
            }
            i if i == COSEAlgorithm::AES_CCM_64_64_256 as i64 => {
                Ok(COSEAlgorithm::AES_CCM_64_64_256)
            }
            i if i == COSEAlgorithm::AES_MAC_128_64 as i64 => Ok(COSEAlgorithm::AES_MAC_128_64),
            i if i == COSEAlgorithm::AES_MAC_256_64 as i64 => Ok(COSEAlgorithm::AES_MAC_256_64),
            i if i == COSEAlgorithm::ChaCha20_Poly1305 as i64 => {
                Ok(COSEAlgorithm::ChaCha20_Poly1305)
            }
            i if i == COSEAlgorithm::AES_MAC_128_128 as i64 => Ok(COSEAlgorithm::AES_MAC_128_128),
            i if i == COSEAlgorithm::AES_MAC_256_128 as i64 => Ok(COSEAlgorithm::AES_MAC_256_128),
            i if i == COSEAlgorithm::AES_CCM_16_128_128 as i64 => {
                Ok(COSEAlgorithm::AES_CCM_16_128_128)
            }
            i if i == COSEAlgorithm::AES_CCM_16_128_256 as i64 => {
                Ok(COSEAlgorithm::AES_CCM_16_128_256)
            }
            i if i == COSEAlgorithm::AES_CCM_64_128_128 as i64 => {
                Ok(COSEAlgorithm::AES_CCM_64_128_128)
            }
            i if i == COSEAlgorithm::AES_CCM_64_128_256 as i64 => {
                Ok(COSEAlgorithm::AES_CCM_64_128_256)
            }
            i if i == COSEAlgorithm::IV_GENERATION as i64 => Ok(COSEAlgorithm::IV_GENERATION),
            i if i == COSEAlgorithm::INSECURE_RS1 as i64 => Ok(COSEAlgorithm::INSECURE_RS1),
            _ => Err(CryptoError::UnknownAlgorithm),
        }
    }
}

/// A COSE Elliptic Curve Public Key. This is generally the provided credential
/// that an authenticator registers, and is used to authenticate the user.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct COSEEC2Key {
    /// The curve that this key references.
    pub curve: Curve,
    /// The key's public X coordinate.
    pub x: Vec<u8>,
    /// The key's public Y coordinate.
    pub y: Vec<u8>,
}

impl COSEEC2Key {
    // The SEC 1 uncompressed point format is "0x04 || x coordinate || y coordinate".
    // See Section 2.3.3 of "SEC 1: Elliptic Curve Cryptography" https://www.secg.org/sec1-v2.pdf.
    pub fn from_sec1_uncompressed(curve: Curve, key: &[u8]) -> Result<Self, CryptoError> {
        if !(curve == Curve::SECP256R1 && key.len() == 65) {
            return Err(CryptoError::UnsupportedCurve(curve));
        }
        if key[0] != 0x04 {
            return Err(CryptoError::MalformedInput);
        }
        let key = &key[1..];
        let (x, y) = key.split_at(key.len() / 2);
        Ok(COSEEC2Key {
            curve,
            x: x.to_vec(),
            y: y.to_vec(),
        })
    }

    pub fn der_spki(&self) -> Result<Vec<u8>, CryptoError> {
        if self.curve != Curve::SECP256R1 {
            return Err(CryptoError::UnsupportedCurve(self.curve));
        }

        // SubjectPublicKeyInfo
        der::sequence(&[
            // algorithm: AlgorithmIdentifier
            &der::sequence(&[
                // algorithm
                &der::object_id(der::OID_EC_PUBLIC_KEY_BYTES)?,
                // parameters
                &der::object_id(der::OID_SECP256R1_BYTES)?,
            ])?,
            // subjectPublicKey
            &der::bit_string(
                // SEC 1 uncompressed format
                &[&[0x04], self.x.as_slice(), self.y.as_slice()].concat(),
            )?,
        ])
    }
}

/// A Octet Key Pair (OKP).
/// The other version uses only the x-coordinate as the y-coordinate is
/// either to be recomputed or not needed for the key agreement operation ('OKP').
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct COSEOKPKey {
    /// The curve that this key references.
    pub curve: Curve,
    /// The key's public X coordinate.
    pub x: Vec<u8>,
}

impl COSEOKPKey {
    pub fn der_spki(&self) -> Result<Vec<u8>, CryptoError> {
        if self.curve != Curve::Ed25519 {
            return Err(CryptoError::UnsupportedCurve(self.curve));
        }

        // SubjectPublicKeyInfo
        der::sequence(&[
            // algorithm: AlgorithmIdentifier
            &der::sequence(&[
                // algorithm
                &der::object_id(der::OID_ED25519_BYTES)?,
                // parameters
                // (absent as per RFC 8410)
            ])?,
            // subjectPublicKey
            &der::bit_string(
                // RFC 8410
                self.x.as_slice(),
            )?,
        ])
    }
}

/// A COSE RSA PublicKey. This is a provided credential from a registered authenticator.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct COSERSAKey {
    /// An RSA modulus
    pub n: Vec<u8>,
    /// An RSA exponent
    pub e: Vec<u8>,
}

impl COSERSAKey {
    pub fn der_spki(&self) -> Result<Vec<u8>, CryptoError> {
        // SubjectPublicKeyInfo
        der::sequence(&[
            // algorithm: AlgorithmIdentifier
            &der::sequence(&[
                // algorithm
                &der::object_id(der::OID_RSA_ENCRYPTION_BYTES)?,
                // parameters
                &der::null()?,
            ])?,
            // subjectPublicKey
            &der::bit_string(
                // RFC 4055 RSAPublicKey
                &der::sequence(&[&der::integer(&self.n)?, &der::integer(&self.e)?])?,
            )?,
        ])
    }
}

// https://tools.ietf.org/html/rfc8152#section-13
#[allow(non_camel_case_types)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum COSEKeyTypeId {
    // Reserved is invalid
    // Reserved = 0,
    /// Octet Key Pair
    OKP = 1,
    /// Elliptic Curve Keys w/ x- and y-coordinate
    EC2 = 2,
    /// RSA
    RSA = 3,
}

impl Serialize for COSEKeyTypeId {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        serializer.serialize_i64(*self as i64)
    }
}

impl TryFrom<i64> for COSEKeyTypeId {
    type Error = CryptoError;
    fn try_from(i: i64) -> Result<Self, Self::Error> {
        match i {
            i if i == COSEKeyTypeId::OKP as i64 => Ok(COSEKeyTypeId::OKP),
            i if i == COSEKeyTypeId::EC2 as i64 => Ok(COSEKeyTypeId::EC2),
            i if i == COSEKeyTypeId::RSA as i64 => Ok(COSEKeyTypeId::RSA),
            _ => Err(CryptoError::UnknownKeyType),
        }
    }
}

/// The type of Key contained within a COSE value. You should never need
/// to alter or change this type.
#[allow(non_camel_case_types)]
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum COSEKeyType {
    /// Identifies this as an Elliptic Curve EC2 key
    EC2(COSEEC2Key),
    /// Identifies this as an Elliptic Curve octet key pair
    OKP(COSEOKPKey),
    /// Identifies this as an RSA key
    RSA(COSERSAKey),
}

/// A COSE Key as provided by the Authenticator. You should never need
/// to alter or change these values.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct COSEKey {
    /// COSE signature algorithm, indicating the type of key and hash type
    /// that should be used.
    pub alg: COSEAlgorithm,
    /// The public key
    pub key: COSEKeyType,
}

impl COSEKey {
    /// Generates a new key pair for the specified algorithm.
    /// Returns an PKCS#8 encoding of the private key, and the public key as a COSEKey.
    pub fn generate(alg: COSEAlgorithm) -> Result<(Vec<u8>, Self), CryptoError> {
        if alg != COSEAlgorithm::ES256 && alg != COSEAlgorithm::ECDH_ES_HKDF256 {
            return Err(CryptoError::UnsupportedAlgorithm(alg));
        }
        let (private, public) = gen_p256()?;
        let cose_ec2_key = COSEEC2Key::from_sec1_uncompressed(Curve::SECP256R1, &public)?;
        let public = COSEKey {
            alg,
            key: COSEKeyType::EC2(cose_ec2_key),
        };
        Ok((private, public))
    }

    pub fn der_spki(&self) -> Result<Vec<u8>, CryptoError> {
        match &self.key {
            COSEKeyType::EC2(ec2_key) => ec2_key.der_spki(),
            COSEKeyType::OKP(okp_key) => okp_key.der_spki(),
            COSEKeyType::RSA(rsa_key) => rsa_key.der_spki(),
        }
    }
}

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 key_type: Option<COSEKeyTypeId> = None;
                let mut alg: Option<COSEAlgorithm> = None;
                // OKP / EC2
                let mut curve: Option<Curve> = None;
                let mut x: Option<Vec<u8>> = None;
                let mut y: Option<Vec<u8>> = None;

                // RSA specific
                let mut n: Option<Vec<u8>> = None;
                let mut e: Option<Vec<u8>> = None;

                while let Some(key) = map.next_key()? {
                    // See https://www.iana.org/assignments/cose/cose.xhtml#key-type-parameters
                    match key {
                        1 => {
                            if key_type.is_some() {
                                return Err(SerdeError::duplicate_field("key_type"));
                            }
                            let value: i64 = map.next_value()?;
                            let val = COSEKeyTypeId::try_from(value).map_err(|_| {
                                SerdeError::custom(format!("unsupported key_type {value}"))
                            })?;
                            key_type = Some(val);
                        }
                        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);
                        }
                        -1 => match key_type {
                            None => return Err(SerdeError::missing_field("key_type")),
                            Some(COSEKeyTypeId::OKP) | Some(COSEKeyTypeId::EC2) => {
                                if curve.is_some() {
                                    return Err(SerdeError::duplicate_field("curve"));
                                }
                                let value: i64 = map.next_value()?;
                                let val = Curve::try_from(value).map_err(|_| {
                                    SerdeError::custom(format!("unsupported curve {value}"))
                                })?;
                                curve = Some(val);
                            }
                            Some(COSEKeyTypeId::RSA) => {
                                if n.is_some() {
                                    return Err(SerdeError::duplicate_field("n"));
                                }
                                let value: ByteBuf = map.next_value()?;
                                n = Some(value.to_vec());
                            }
                        },
                        -2 => match key_type {
                            None => return Err(SerdeError::missing_field("key_type")),
                            Some(COSEKeyTypeId::OKP) | Some(COSEKeyTypeId::EC2) => {
                                if x.is_some() {
                                    return Err(SerdeError::duplicate_field("x"));
                                }
                                let value: ByteBuf = map.next_value()?;
                                x = Some(value.to_vec());
                            }
                            Some(COSEKeyTypeId::RSA) => {
                                if e.is_some() {
                                    return Err(SerdeError::duplicate_field("e"));
                                }
                                let value: ByteBuf = map.next_value()?;
                                e = Some(value.to_vec());
                            }
                        },
                        -3 if key_type == Some(COSEKeyTypeId::EC2) => {
                            if y.is_some() {
                                return Err(SerdeError::duplicate_field("y"));
                            }
                            let value: ByteBuf = map.next_value()?;
                            y = Some(value.to_vec());
                        }
                        other => {
                            return Err(SerdeError::custom(format!("unexpected field: {other}")));
                        }
                    };
                }

                let key_type = key_type.ok_or_else(|| SerdeError::missing_field("key_type (1)"))?;
                let alg = alg.ok_or_else(|| SerdeError::missing_field("alg (3)"))?;

                let res = match key_type {
                    COSEKeyTypeId::OKP => {
                        let curve = curve.ok_or_else(|| SerdeError::missing_field("curve (-1)"))?;
                        let x = x.ok_or_else(|| SerdeError::missing_field("x (-2)"))?;
                        COSEKeyType::OKP(COSEOKPKey { curve, x })
                    }
                    COSEKeyTypeId::EC2 => {
                        let curve = curve.ok_or_else(|| SerdeError::missing_field("curve (-1)"))?;
                        let x = x.ok_or_else(|| SerdeError::missing_field("x (-2)"))?;
                        let y = y.ok_or_else(|| SerdeError::missing_field("y (-3)"))?;
                        COSEKeyType::EC2(COSEEC2Key { curve, x, y })
                    }
                    COSEKeyTypeId::RSA => {
                        let n = n.ok_or_else(|| SerdeError::missing_field("n (-1)"))?;
                        let e = e.ok_or_else(|| SerdeError::missing_field("e (-2)"))?;
                        COSEKeyType::RSA(COSERSAKey { e, n })
                    }
                };
                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,
    {
        match &self.key {
            COSEKeyType::OKP(key) => {
                serialize_map!(
                    serializer,
                    &1 => &COSEKeyTypeId::OKP,
                    &3 => &self.alg,
                    &-1 => &key.curve,
                    &-2 => &serde_bytes::Bytes::new(&key.x),
                )
            }
            COSEKeyType::EC2(key) => {
                serialize_map!(
                    serializer,
                    &1 => &COSEKeyTypeId::EC2,
                    &3 => &self.alg,
                    &-1 => &key.curve,
                    &-2 => &serde_bytes::Bytes::new(&key.x),
                    &-3 => &serde_bytes::Bytes::new(&key.y),
                )
            }
            COSEKeyType::RSA(key) => {
                serialize_map!(
                    serializer,
                    &1 => &COSEKeyTypeId::RSA,
                    &3 => &self.alg,
                    &-1 => &serde_bytes::Bytes::new(&key.n),
                    &-2 => &serde_bytes::Bytes::new(&key.e),
                )
            }
        }
    }
}

/// Errors that can be returned from COSE functions.
#[derive(Debug, Clone, PartialEq, Serialize)]
pub enum CryptoError {
    // DecodingFailure,
    LibraryFailure,
    MalformedInput,
    // MissingHeader,
    // UnexpectedHeaderValue,
    // UnexpectedTag,
    // UnexpectedType,
    // Unimplemented,
    // VerificationFailed,
    // SigningFailed,
    // InvalidArgument,
    UnknownKeyType,
    UnknownSignatureScheme,
    UnknownAlgorithm,
    WrongSaltLength,
    UnsupportedAlgorithm(COSEAlgorithm),
    UnsupportedCurve(Curve),
    UnsupportedKeyType,
    Backend(String),
}

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)))
    }
}

pub struct U2FRegisterAnswer<'a> {
    pub certificate: &'a [u8],
    pub signature: &'a [u8],
}

// We will only return MalformedInput here
pub fn parse_u2f_der_certificate(data: &[u8]) -> Result<U2FRegisterAnswer, CryptoError> {
    // So we don't panic below, when accessing individual bytes
    if data.len() < 4 {
        return Err(CryptoError::MalformedInput);
    }
    // Check if it is a SEQUENCE
    if data[0] != 0x30 {
        return Err(CryptoError::MalformedInput);
    }

    // This algorithm is taken from mozilla-central/security/nss/lib/mozpkix/lib/pkixder.cpp
    // The short form of length is a single byte with the high order bit set
    // to zero. The long form of length is one byte with the high order bit
    // set, followed by N bytes, where N is encoded in the lowest 7 bits of
    // the first byte.
    let end = if (data[1] & 0x80) == 0 {
        2 + data[1] as usize
    } else if data[1] == 0x81 {
        // The next byte specifies the length

        if data[2] < 128 {
            // Not shortest possible encoding
            // Forbidden by DER-format
            return Err(CryptoError::MalformedInput);
        }
        3 + data[2] as usize
    } else if data[1] == 0x82 {
        // The next 2 bytes specify the length
        let l = u16::from_be_bytes([data[2], data[3]]);
        if l < 256 {
            // Not shortest possible encoding
            // Forbidden by DER-format
            return Err(CryptoError::MalformedInput);
        }
        4 + l as usize
    } else {
        // We don't support lengths larger than 2^16 - 1.
        return Err(CryptoError::MalformedInput);
    };

    if data.len() < end {
        return Err(CryptoError::MalformedInput);
    }

    Ok(U2FRegisterAnswer {
        certificate: &data[0..end],
        signature: &data[end..],
    })
}

#[cfg(all(test, not(feature = "crypto_dummy")))]
mod test {
    use std::convert::TryFrom;

    #[cfg(feature = "crypto_nss")]
    use super::backend::{ecdsa_p256_sha256_sign_raw, test_ecdsa_p256_sha256_verify_raw};
    use super::{
        backend::hmac_sha256, backend::sha256, backend::test_ecdh_p256_raw, COSEAlgorithm, COSEKey,
        Curve, PinProtocolImpl, PinUvAuth1, PinUvAuth2, PinUvAuthProtocol, PublicInputs,
        SharedSecret,
    };
    use crate::crypto::{COSEEC2Key, COSEKeyType, COSEOKPKey, COSERSAKey};
    use crate::ctap2::attestation::AAGuid;
    use crate::ctap2::commands::client_pin::Pin;
    use crate::ctap2::commands::get_info::{
        tests::AAGUID_RAW, AuthenticatorOptions, AuthenticatorVersion,
    };
    use crate::util::decode_hex;
    use crate::AuthenticatorInfo;
    use serde_cbor::de::from_slice;

    // Extracted from RFC 8410 Example 10.1
    const SAMPLE_ED25519_KEY: &[u8] = &[
        0x19, 0xbf, 0x44, 0x09, 0x69, 0x84, 0xcd, 0xfe, 0x85, 0x41, 0xba, 0xc1, 0x67, 0xdc, 0x3b,
        0x96, 0xc8, 0x50, 0x86, 0xaa, 0x30, 0xb6, 0xb6, 0xcb, 0x0c, 0x5c, 0x38, 0xad, 0x70, 0x31,
        0x66, 0xe1,
    ];

    const SAMPLE_P256_X: &[u8] = &[
        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,
    ];
    const SAMPLE_P256_Y: &[u8] = &[
        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,
    ];

    const SAMPLE_RSA_MODULUS: &[u8] = &[
        0xd4, 0xd2, 0x53, 0xed, 0x7a, 0x69, 0xb1, 0x84, 0xc9, 0xfb, 0x70, 0x30, 0x0c, 0x51, 0xb1,
        0x8f, 0x89, 0x6c, 0xb1, 0x31, 0x6d, 0x87, 0xbe, 0xe1, 0xc7, 0xf7, 0xb0, 0x4f, 0xe7, 0x27,
        0xa7, 0xb7, 0x7c, 0x55, 0x20, 0x37, 0xa8, 0xac, 0x40, 0xf4, 0xbc, 0x59, 0xc4, 0x92, 0x8f,
        0x13, 0x5b, 0x5e, 0xa7, 0x18, 0x05, 0xcc, 0xd7, 0x9c, 0xfb, 0x88, 0x6c, 0xf1, 0xbc, 0x6b,
        0x1b, 0x8d, 0xb7, 0x8d, 0x2d, 0xaa, 0xcb, 0xee, 0xdb, 0xab, 0x49, 0x36, 0x77, 0xe5, 0xd1,
        0x84, 0xa1, 0x40, 0x3f, 0xf6, 0xf7, 0x98, 0x6c, 0xaa, 0x24, 0x48, 0x30, 0x44, 0xdc, 0x68,
        0xbd, 0x9e, 0x74, 0x37, 0xaf, 0x27, 0x12, 0x90, 0x74, 0x0d, 0x9e, 0x3c, 0xa5, 0x3a, 0x1d,
        0xb8, 0x54, 0x92, 0xd4, 0x6d, 0x1f, 0xf9, 0x39, 0xb8, 0x1d, 0x8a, 0x5e, 0xbe, 0x12, 0xbd,
        0xe2, 0x9c, 0xf2, 0x5a, 0x48, 0x5d, 0x71, 0x2c, 0x71, 0x72, 0x6d, 0xd2, 0xcb, 0x37, 0xb1,
        0xe6, 0x2f, 0x76, 0x43, 0xda, 0xca, 0x44, 0x30, 0x7b, 0x28, 0xe7, 0xe4, 0xec, 0xa9, 0xc9,
        0x1a, 0x5f, 0xe5, 0x51, 0x03, 0x25, 0x60, 0x7c, 0x5a, 0x69, 0x12, 0x4d, 0x50, 0xfd, 0xb2,
        0xb8, 0x6e, 0x13, 0xb2, 0x92, 0xda, 0x0e, 0x31, 0xc9, 0xf1, 0x9c, 0xde, 0x17, 0x63, 0xe4,
        0xcb, 0xac, 0xd5, 0xee, 0x84, 0x06, 0xde, 0x67, 0x2d, 0xb8, 0xd2, 0xe1, 0x4b, 0xbb, 0x49,
        0xea, 0x45, 0xd4, 0xa1, 0x7f, 0x46, 0xf2, 0xd6, 0x0c, 0x05, 0x9d, 0x1d, 0x1a, 0x99, 0x41,
        0x20, 0x5e, 0x1a, 0xa4, 0xcc, 0x21, 0x44, 0x58, 0x8b, 0xcd, 0x98, 0xe4, 0x3d, 0x53, 0x20,
        0xfc, 0xfc, 0x7b, 0x9f, 0x43, 0x35, 0xfb, 0x38, 0x37, 0x23, 0xd0, 0x76, 0xe3, 0x3d, 0x4f,
        0x89, 0x9b, 0x89, 0x32, 0x81, 0x89, 0xed, 0x58, 0xc0, 0x80, 0x18, 0x83, 0x5b, 0xaf, 0x5a,
        0xa5,
    ];

    #[test]
    fn test_rsa_key_to_der_spki() {
        // $ ascii2der | xxd -i
        // SEQUENCE {
        //   SEQUENCE {
        //     # rsaEncryption
        //     OBJECT_IDENTIFIER { 1.2.840.113549.1.1.1 }
        //     NULL {}
        //   }
        //   BIT_STRING {
        //     `00`
        //     SEQUENCE {
        //       INTEGER { `00d4d253ed7a69b184c9fb70300c51b18f896cb1316d87bee1c7f7b04fe727a7b77c552037a8ac40f4bc59c4928f135b5ea71805ccd79cfb886cf1bc6b1b8db78d2daacbeedbab493677e5d184a1403ff6f7986caa24483044dc68bd9e7437af271290740d9e3ca53a1db85492d46d1ff939b81d8a5ebe12bde29cf25a485d712c71726dd2cb37b1e62f7643daca44307b28e7e4eca9c91a5fe5510325607c5a69124d50fdb2b86e13b292da0e31c9f19cde1763e4cbacd5ee8406de672db8d2e14bbb49ea45d4a17f46f2d60c059d1d1a9941205e1aa4cc2144588bcd98e43d5320fcfc7b9f4335fb383723d076e33d4f899b89328189ed58c08018835baf5aa5` }
        //       INTEGER { 65537 }
        //     }
        //   }
        // }
        let expected: &[u8] = &[
            0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
            0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a,
            0x02, 0x82, 0x01, 0x01, 0x00, 0xd4, 0xd2, 0x53, 0xed, 0x7a, 0x69, 0xb1, 0x84, 0xc9,
            0xfb, 0x70, 0x30, 0x0c, 0x51, 0xb1, 0x8f, 0x89, 0x6c, 0xb1, 0x31, 0x6d, 0x87, 0xbe,
            0xe1, 0xc7, 0xf7, 0xb0, 0x4f, 0xe7, 0x27, 0xa7, 0xb7, 0x7c, 0x55, 0x20, 0x37, 0xa8,
            0xac, 0x40, 0xf4, 0xbc, 0x59, 0xc4, 0x92, 0x8f, 0x13, 0x5b, 0x5e, 0xa7, 0x18, 0x05,
            0xcc, 0xd7, 0x9c, 0xfb, 0x88, 0x6c, 0xf1, 0xbc, 0x6b, 0x1b, 0x8d, 0xb7, 0x8d, 0x2d,
            0xaa, 0xcb, 0xee, 0xdb, 0xab, 0x49, 0x36, 0x77, 0xe5, 0xd1, 0x84, 0xa1, 0x40, 0x3f,
            0xf6, 0xf7, 0x98, 0x6c, 0xaa, 0x24, 0x48, 0x30, 0x44, 0xdc, 0x68, 0xbd, 0x9e, 0x74,
            0x37, 0xaf, 0x27, 0x12, 0x90, 0x74, 0x0d, 0x9e, 0x3c, 0xa5, 0x3a, 0x1d, 0xb8, 0x54,
            0x92, 0xd4, 0x6d, 0x1f, 0xf9, 0x39, 0xb8, 0x1d, 0x8a, 0x5e, 0xbe, 0x12, 0xbd, 0xe2,
            0x9c, 0xf2, 0x5a, 0x48, 0x5d, 0x71, 0x2c, 0x71, 0x72, 0x6d, 0xd2, 0xcb, 0x37, 0xb1,
            0xe6, 0x2f, 0x76, 0x43, 0xda, 0xca, 0x44, 0x30, 0x7b, 0x28, 0xe7, 0xe4, 0xec, 0xa9,
            0xc9, 0x1a, 0x5f, 0xe5, 0x51, 0x03, 0x25, 0x60, 0x7c, 0x5a, 0x69, 0x12, 0x4d, 0x50,
            0xfd, 0xb2, 0xb8, 0x6e, 0x13, 0xb2, 0x92, 0xda, 0x0e, 0x31, 0xc9, 0xf1, 0x9c, 0xde,
            0x17, 0x63, 0xe4, 0xcb, 0xac, 0xd5, 0xee, 0x84, 0x06, 0xde, 0x67, 0x2d, 0xb8, 0xd2,
            0xe1, 0x4b, 0xbb, 0x49, 0xea, 0x45, 0xd4, 0xa1, 0x7f, 0x46, 0xf2, 0xd6, 0x0c, 0x05,
            0x9d, 0x1d, 0x1a, 0x99, 0x41, 0x20, 0x5e, 0x1a, 0xa4, 0xcc, 0x21, 0x44, 0x58, 0x8b,
            0xcd, 0x98, 0xe4, 0x3d, 0x53, 0x20, 0xfc, 0xfc, 0x7b, 0x9f, 0x43, 0x35, 0xfb, 0x38,
            0x37, 0x23, 0xd0, 0x76, 0xe3, 0x3d, 0x4f, 0x89, 0x9b, 0x89, 0x32, 0x81, 0x89, 0xed,
            0x58, 0xc0, 0x80, 0x18, 0x83, 0x5b, 0xaf, 0x5a, 0xa5, 0x02, 0x03, 0x01, 0x00, 0x01,
        ];
        let rsa_key = COSERSAKey {
            e: vec![1, 0, 1],
            n: SAMPLE_RSA_MODULUS.to_vec(),
        };
        let cose_key: COSEKey = COSEKey {
            alg: COSEAlgorithm::RS256,
            key: COSEKeyType::RSA(rsa_key),
        };
        let actual = cose_key.der_spki().expect("Failed to serialize to SPKI");
        assert_eq!(expected, &actual);
    }

    #[test]
    fn test_rsa_key_to_cbor() {
        let key = COSERSAKey {
            e: vec![1, 0, 1],
            n: SAMPLE_RSA_MODULUS.to_vec(),
        };
        let cose_key: COSEKey = COSEKey {
            alg: COSEAlgorithm::RS256,
            key: COSEKeyType::RSA(key),
        };
        let cose_key_cbor = serde_cbor::to_vec(&cose_key).expect("Failed to serialize key");
        let actual = serde_cbor::from_slice(&cose_key_cbor).expect("Failed to deserialize key");
        assert_eq!(cose_key, actual);
    }

    #[test]
    fn test_ec2_key_to_der_spki() {
        // $ ascii2der | xxd -i
        // SEQUENCE {
        //   SEQUENCE {
        //     # ecPublicKey
        //     OBJECT_IDENTIFIER { 1.2.840.10045.2.1 }
        //     # secp256r1
        //     OBJECT_IDENTIFIER { 1.2.840.10045.3.1.7 }
        //   }
        //   BIT_STRING { `00` `04fc9ed36f7c1aa915ce3ea177f07567f07f16f9479d95ad8ed4971d3305e31a8050b733af8c0b0ee1da8de0acf9d8e13282f063b7b30d73d4d32c9aad6dfa8b27` }
        // }
        let expected = [
            0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06,
            0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, 0x42, 0x00, 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 ec2_key = COSEEC2Key {
            curve: Curve::SECP256R1,
            x: SAMPLE_P256_X.to_vec(),
            y: SAMPLE_P256_Y.to_vec(),
        };
        let cose_key = COSEKey {
            alg: COSEAlgorithm::EDDSA,
            key: COSEKeyType::EC2(ec2_key),
        };
        let actual = cose_key.der_spki().expect("Failed to serialize key");
        assert_eq!(actual, expected);
    }

    #[test]
    fn test_ec2_key_to_cbor() {
        let ec2_key = COSEEC2Key {
            curve: Curve::SECP256R1,
            x: SAMPLE_P256_X.to_vec(),
            y: SAMPLE_P256_Y.to_vec(),
        };
        let cose_key = COSEKey {
            alg: COSEAlgorithm::EDDSA,
            key: COSEKeyType::EC2(ec2_key),
        };
        let cose_key_cbor = serde_cbor::to_vec(&cose_key).expect("Failed to serialize key");
        let actual = serde_cbor::from_slice(&cose_key_cbor).expect("Failed to deserialize key");
        assert_eq!(cose_key, actual);
    }

    #[test]
    fn test_okp_key_to_der_spki() {
        // RFC 8410 Example 10.1
        let expected = [
            0x30, 0x2a, 0x30, 0x05, 0x06, 0x03, 0x2b, 0x65, 0x70, 0x03, 0x21, 0x00, 0x19, 0xbf,
            0x44, 0x09, 0x69, 0x84, 0xcd, 0xfe, 0x85, 0x41, 0xba, 0xc1, 0x67, 0xdc, 0x3b, 0x96,
            0xc8, 0x50, 0x86, 0xaa, 0x30, 0xb6, 0xb6, 0xcb, 0x0c, 0x5c, 0x38, 0xad, 0x70, 0x31,
            0x66, 0xe1,
        ];
        let okp_key = COSEOKPKey {
            curve: Curve::Ed25519,
            x: SAMPLE_ED25519_KEY.to_vec(),
        };
        let cose_key = COSEKey {
            alg: COSEAlgorithm::EDDSA,
            key: COSEKeyType::OKP(okp_key),
        };
        let actual = cose_key.der_spki().expect("Failed to serialize key");
        assert_eq!(actual, expected);
    }

    #[test]
    fn test_okp_key_to_cbor() {
        let okp_key = COSEOKPKey {
            curve: Curve::Ed25519,
            x: SAMPLE_ED25519_KEY.to_vec(),
        };
        let cose_key = COSEKey {
            alg: COSEAlgorithm::EDDSA,
            key: COSEKeyType::OKP(okp_key),
        };
        let cose_key_cbor = serde_cbor::to_vec(&cose_key).expect("Failed to serialize key");
        let actual = serde_cbor::from_slice(&cose_key_cbor).expect("Failed to deserialize key");
        assert_eq!(cose_key, actual);
    }

    #[test]
    fn test_parse_es256_serialize_key() {
        // Test values taken from https://github.com/Yubico/python-fido2/blob/master/test/test_cose.py
        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, Curve::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() {
        // Test values taken from https://github.com/Yubico/python-fido2/blob/main/tests/test_ctap2.py
        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 client_ec2_key = COSEEC2Key {
            curve: Curve::SECP256R1,
            x: EC_PUB_X.clone(),
            y: EC_PUB_Y.clone(),
        };

        let peer_ec2_key = COSEEC2Key {
            curve: Curve::SECP256R1,
            x: DEV_PUB_X,
            y: DEV_PUB_Y,
        };

        // We are using `test_cose_ec2_p256_ecdh_sha256()` here, because we need a way to hand in
        // the private key which would be generated on the fly otherwise (ephemeral keys),
        // to predict the outputs
        let peer_spki = peer_ec2_key.der_spki().unwrap();
        let shared_point = test_ecdh_p256_raw(&peer_spki, &EC_PUB_X, &EC_PUB_Y, &EC_PRIV).unwrap();
        let shared_secret = SharedSecret {
            pin_protocol: PinUvAuthProtocol(Box::new(PinUvAuth1 {})),
            key: sha256(&shared_point).unwrap(),
            inputs: PublicInputs {
                client: COSEKey {
                    alg: COSEAlgorithm::ES256,
                    key: COSEKeyType::EC2(client_ec2_key),
                },
                peer: COSEKey {
                    alg: COSEAlgorithm::ES256,
                    key: COSEKeyType::EC2(peer_ec2_key),
                },
            },
        };
        assert_eq!(shared_secret.key, SHARED);

        let token_enc = shared_secret.encrypt(&TOKEN).unwrap();
        assert_eq!(token_enc, TOKEN_ENC);

        let token = shared_secret.decrypt(&TOKEN_ENC).unwrap();
        assert_eq!(token, TOKEN);

        let pin = Pin::new("1234");
        let pin_hash_enc = shared_secret.encrypt(&pin.for_pin_token()).unwrap();
        assert_eq!(pin_hash_enc, PIN_HASH_ENC);
    }

    #[test]
    fn test_pin_uv_auth2_kdf() {
        // We don't pull a complete HKDF implementation from the crypto backend, so we need to
        // check that PinUvAuth2::kdf makes the right sequence of HMAC-SHA256 calls.
        //
        // ```python
        // from cryptography.hazmat.primitives.kdf.hkdf import HKDF
        // from cryptography.hazmat.primitives import hashes
        // from cryptography.hazmat.backends import default_backend
        //
        // Z = b"\xFF" * 32
        //
        // hmac_key = HKDF(
        //     algorithm=hashes.SHA256(),
        //     length=32,
        //     salt=b"\x00" * 32,
        //     info=b"CTAP2 HMAC key",
        // ).derive(Z)
        //
        // aes_key = HKDF(
        //     algorithm=hashes.SHA256(),
        //     length=32,
        //     salt=b"\x00" * 32,
        //     info=b"CTAP2 AES key",
        // ).derive(Z)
        //
        // print((hmac_key+aes_key).hex())
        // ```
        let input = decode_hex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF");
        let expected = decode_hex("570B4ED82AA5DFB49DB79DBEAF4B315D8ABB1A9867B245F3367026987C0D47A17D9A93C39BAEC741D141C6238D8E1846DE323D8EED022CB397D19A73B98945E2");
        let output = PinUvAuth2 {}.kdf(&input).unwrap();
        assert_eq!(&expected, &output);
    }

    #[test]
    fn test_hmac_sha256() {
        let key = "key";
        let message = "The quick brown fox jumps over the lazy dog";
        let expected =
            decode_hex("f7bc83f430538424b13298e6aa6fb143ef4d59a14946175997479dbc2d1a3cd8");

        let result = hmac_sha256(key.as_bytes(), message.as_bytes()).expect("HMAC-SHA256 failed");
        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 = hmac_sha256(key.as_bytes(), message.as_bytes()).expect("HMAC-SHA256 failed");
        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![
--> --------------------

--> maximum size reached

--> --------------------

[ zur Elbe Produktseite wechseln0.85Quellennavigators  Analyse erneut starten  ]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge