use super ::utils::{from_slice_stream, read_be_u16, read_be_u32, read_byte};
use crate ::crypto::{COSEAlgorithm, CryptoError, SharedSecret};
use crate ::ctap2::server::{CredentialProtectionPolicy, HMACGetSecretOutput, RpIdHas
h};
use crate ::ctap2::utils::serde_parse_err;
use crate ::{crypto::COSEKey, errors::AuthenticatorError};
use base64::Engine;
use serde::ser::{Error as SerError, SerializeMap, Serializer};
use serde::{
de::{Error as SerdeError, Unexpected, Visitor},
Deserialize, Deserializer, Serialize,
};
use serde_cbor;
use std::convert::TryInto;
use std::fmt;
use std::io::{Cursor, Read};
#[ derive(Debug, PartialEq, Eq)]
pub enum HmacSecretResponse {
/// This is returned by MakeCredential calls to display if CredRandom was
/// successfully generated
Confirmed(bool),
/// This is returned by GetAssertion:
/// AES256-CBC(shared_secret, HMAC-SHA265(CredRandom, salt1) || HMAC-SHA265(CredRandom, salt2))
Secret(Vec<u8>),
}
impl HmacSecretResponse {
/// Return the decrypted HMAC outputs, if this is an instance of [HmacSecretResponse::Secret].
pub fn decrypt_secrets(
&self ,
shared_secret: &SharedSecret,
) -> Option<Result<HMACGetSecretOutput, CryptoError>> {
if let HmacSecretResponse::Secret(hmac_outputs) = self {
Some(Self ::decrypt_secrets_internal(shared_secret, hmac_outputs))
} else {
None
}
}
fn decrypt_secrets_internal(
shared_secret: &SharedSecret,
hmac_outputs: &[u8],
) -> Result<HMACGetSecretOutput, CryptoError> {
let output_secrets = shared_secret.decrypt(hmac_outputs)?;
match if output_secrets.len() < 32 {
Err(CryptoError::WrongSaltLength)
} else {
let (output1, output2) = output_secrets.split_at(32 );
Ok(HMACGetSecretOutput {
output1: output1
.try_into()
.map_err(|_| CryptoError::WrongSaltLength)?,
output2: (!output2.is_empty())
.then(|| output2.try_into().map_err(|_| CryptoError::WrongSaltLength))
.transpose()?,
})
} {
err @ Err(CryptoError::WrongSaltLength) => {
// TODO: Use Result::inspect_err when stable
debug!(
"Bad hmac-secret output length: {} bytes (expected exactly 32 or 64)" ,
output_secrets.len()
);
err
}
other => other,
}
}
}
impl Serialize for HmacSecretResponse {
fn serialize<S>(&self , serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match self {
HmacSecretResponse::Confirmed(x) => serializer.serialize_bool(*x),
HmacSecretResponse::Secret(x) => serializer.serialize_bytes(x),
}
}
}
impl <'de> Deserialize<' de> for HmacSecretResponse {
fn deserialize<D>(deserializer: D) -> Result<Self , D::Error>
where
D: Deserializer<'de>,
{
struct HmacSecretResponseVisitor;
impl <'de> Visitor<' de> for HmacSecretResponseVisitor {
type Value = HmacSecretResponse;
fn expecting(&self , formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("a byte array or a boolean" )
}
fn visit_bytes<E>(self , v: &[u8]) -> Result<Self ::Value, E>
where
E: SerdeError,
{
Ok(HmacSecretResponse::Secret(v.to_vec()))
}
fn visit_bool<E>(self , v: bool) -> Result<Self ::Value, E>
where
E: SerdeError,
{
Ok(HmacSecretResponse::Confirmed(v))
}
}
deserializer.deserialize_any(HmacSecretResponseVisitor)
}
}
#[ derive(Debug, Serialize, Deserialize, PartialEq, Eq, Default)]
pub struct Extension {
#[ serde(rename = "credProtect" , skip_serializing_if = "Option::is_none" )]
pub cred_protect: Option<CredentialProtectionPolicy>,
#[ serde(rename = "hmac-secret" , skip_serializing_if = "Option::is_none" )]
pub hmac_secret: Option<HmacSecretResponse>,
#[ serde(rename = "minPinLength" , skip_serializing_if = "Option::is_none" )]
pub min_pin_length: Option<u64>,
}
impl Extension {
pub fn has_some(&self ) -> bool {
self .min_pin_length.is_some() || self .hmac_secret.is_some() || self .cred_protect.is_some()
}
}
#[ derive(Serialize, PartialEq, Default, Eq, Clone)]
pub struct AAGuid(pub [u8; 16 ]);
impl AAGuid {
pub fn from(src: &[u8]) -> Result<AAGuid, AuthenticatorError> {
let mut payload = [0 u8; 16 ];
if src.len() != payload.len() {
Err(AuthenticatorError::InternalError(String::from(
"Failed to parse AAGuid" ,
)))
} else {
payload.copy_from_slice(src);
Ok(AAGuid(payload))
}
}
}
impl fmt::Debug for AAGuid {
fn fmt(&self , f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"AAGuid({:02x}{:02x}{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}{:02x}{:02x}{:02x}{:02x})" ,
self .0 [0 ],
self .0 [1 ],
self .0 [2 ],
self .0 [3 ],
self .0 [4 ],
self .0 [5 ],
self .0 [6 ],
self .0 [7 ],
self .0 [8 ],
self .0 [9 ],
self .0 [10 ],
self .0 [11 ],
self .0 [12 ],
self .0 [13 ],
self .0 [14 ],
self .0 [15 ]
)
}
}
impl <'de> Deserialize<' de> for AAGuid {
fn deserialize<D>(deserializer: D) -> Result<Self , D::Error>
where
D: Deserializer<'de>,
{
struct AAGuidVisitor;
impl <'de> Visitor<' de> for AAGuidVisitor {
type Value = AAGuid;
fn expecting(&self , formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("a byte array" )
}
fn visit_bytes<E>(self , v: &[u8]) -> Result<Self ::Value, E>
where
E: SerdeError,
{
let mut buf = [0 u8; 16 ];
if v.len() != buf.len() {
return Err(E::invalid_length(v.len(), &"16" ));
}
buf.copy_from_slice(v);
Ok(AAGuid(buf))
}
}
deserializer.deserialize_bytes(AAGuidVisitor)
}
}
#[ derive(Debug, PartialEq, Eq)]
pub struct AttestedCredentialData {
pub aaguid: AAGuid,
pub credential_id: Vec<u8>,
pub credential_public_key: COSEKey,
}
fn parse_attested_cred_data<R: Read, E: SerdeError>(
data: &mut R,
) -> Result<AttestedCredentialData, E> {
let mut aaguid_raw = [0 u8; 16 ];
data.read_exact(&mut aaguid_raw)
.map_err(|_| serde_parse_err("AAGuid" ))?;
let aaguid = AAGuid(aaguid_raw);
let cred_len = read_be_u16(data)?;
let mut credential_id = vec![0 u8; cred_len as usize];
data.read_exact(&mut credential_id)
.map_err(|_| serde_parse_err("CredentialId" ))?;
let credential_public_key = from_slice_stream(data)?;
Ok(AttestedCredentialData {
aaguid,
credential_id,
credential_public_key,
})
}
bitflags! {
// Defining an exhaustive list of flags here ensures that `from_bits_truncate` is lossless and
// that `from_bits` never returns None.
pub struct AuthenticatorDataFlags: u8 {
const USER_PRESENT = 0 x01;
const RESERVED_1 = 0 x02;
const USER_VERIFIED = 0 x04;
const RESERVED_3 = 0 x08;
const RESERVED_4 = 0 x10;
const RESERVED_5 = 0 x20;
const ATTESTED = 0 x40;
const EXTENSION_DATA = 0 x80;
}
}
#[ derive(Debug, PartialEq, Eq)]
pub struct AuthenticatorData {
pub rp_id_hash: RpIdHash,
pub flags: AuthenticatorDataFlags,
pub counter: u32,
pub credential_data: Option<AttestedCredentialData>,
pub extensions: Extension,
}
impl AuthenticatorData {
pub fn to_vec(&self ) -> Vec<u8> {
match serde_cbor::value::to_value(self ) {
Ok(serde_cbor::value::Value::Bytes(out)) => out,
_ => unreachable!(), // Serialize is guaranteed to produce bytes
}
}
}
impl <'de> Deserialize<' de> for AuthenticatorData {
fn deserialize<D>(deserializer: D) -> Result<Self , D::Error>
where
D: Deserializer<'de>,
{
struct AuthenticatorDataVisitor;
impl <'de> Visitor<' de> for AuthenticatorDataVisitor {
type Value = AuthenticatorData;
fn expecting(&self , formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("a byte array" )
}
fn visit_bytes<E>(self , input: &[u8]) -> Result<Self ::Value, E>
where
E: SerdeError,
{
let mut cursor = Cursor::new(input);
let mut rp_id_hash_raw = [0 u8; 32 ];
cursor
.read_exact(&mut rp_id_hash_raw)
.map_err(|_| serde_parse_err("32 bytes" ))?;
let rp_id_hash = RpIdHash(rp_id_hash_raw);
// preserve the flags, even if some reserved values are set.
let flags = AuthenticatorDataFlags::from_bits_truncate(read_byte(&mut cursor)?);
let counter = read_be_u32(&mut cursor)?;
let mut credential_data = None;
if flags.contains(AuthenticatorDataFlags::ATTESTED) {
credential_data = Some(parse_attested_cred_data(&mut cursor)?);
}
let extensions = if flags.contains(AuthenticatorDataFlags::EXTENSION_DATA) {
from_slice_stream(&mut cursor)?
} else {
Default::default()
};
// TODO(baloo): we should check for end of buffer and raise a parse
// parse error if data is still in the buffer
Ok(AuthenticatorData {
rp_id_hash,
flags,
counter,
credential_data,
extensions,
})
}
}
deserializer.deserialize_bytes(AuthenticatorDataVisitor)
}
}
impl Serialize for AuthenticatorData {
// see https://www.w3.org/TR/webauthn-2/#sctn-authenticator-data
// Authenticator Data
// Name Length (in bytes)
// rpIdHash 32
// flags 1
// signCount 4
// attestedCredentialData variable (if present)
// extensions variable (if present)
fn serialize<S>(&self , serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut data = Vec::new();
data.extend(self .rp_id_hash.0 ); // (1) "rpIDHash", len=32
data.extend([self .flags.bits()]); // (2) "flags", len=1 (u8)
data.extend(self .counter.to_be_bytes()); // (3) "signCount", len=4, 32-bit unsigned big-endian integer.
if let Some(cred) = &self .credential_data {
// see https://www.w3.org/TR/webauthn-2/#sctn-attested-credential-data
// Attested Credential Data
// Name Length (in bytes)
// aaguid 16
// credentialIdLength 2
// credentialId L
// credentialPublicKey variable
data.extend(cred.aaguid.0 ); // (1) "aaguid", len=16
data.extend((cred.credential_id.len() as u16).to_be_bytes()); // (2) "credentialIdLength", len=2, 16-bit unsigned big-endian integer
data.extend(&cred.credential_id); // (3) "credentialId", len= see (2)
data.extend(
// (4) "credentialPublicKey", len=variable
&serde_cbor::to_vec(&cred.credential_public_key)
.map_err(|_| SerError::custom("Failed to serialize auth_data" ))?,
);
}
// If we have parsed extension data, then we should serialize it even if the authenticator
// failed to set the extension data flag.
// If we don't have parsed extension data, then what we output depends on the flag.
// If the flag is set, we output the empty CBOR map. If it is not set, we output nothing.
if self .extensions.has_some() || self .flags.contains(AuthenticatorDataFlags::EXTENSION_DATA)
{
data.extend(
// (5) "extensions", len=variable
&serde_cbor::to_vec(&self .extensions)
.map_err(|_| SerError::custom("Failed to serialize auth_data" ))?,
);
}
serializer.serialize_bytes(&data)
}
}
#[ derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
/// x509 encoded attestation certificate
pub struct AttestationCertificate(#[ serde(with = "serde_bytes" )] pub Vec<u8>);
impl AsRef<[u8]> for AttestationCertificate {
fn as_ref(&self ) -> &[u8] {
self .0 .as_ref()
}
}
#[ derive(Serialize, Deserialize, PartialEq, Eq)]
pub struct Signature(#[ serde(with = "serde_bytes" )] pub Vec<u8>);
impl fmt::Debug for Signature {
fn fmt(&self , f: &mut fmt::Formatter) -> fmt::Result {
let value = base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(&self .0 );
write!(f, "Signature({value})" )
}
}
impl AsRef<[u8]> for Signature {
fn as_ref(&self ) -> &[u8] {
self .0 .as_ref()
}
}
impl From<&[u8]> for Signature {
fn from(sig: &[u8]) -> Signature {
Signature(sig.to_vec())
}
}
#[ derive(Debug, PartialEq, Eq, Deserialize)]
// The tag and content attributes here are really for AttestationObject, which contains an
// "internally tagged" AttestationStatement.
#[ serde(tag = "fmt" , content = "attStmt" , rename_all = "lowercase" )]
pub enum AttestationStatement {
#[ serde(deserialize_with = "deserialize_none_att_stmt" )]
None,
Packed(AttestationStatementPacked),
#[ serde(rename = "fido-u2f" )]
FidoU2F(AttestationStatementFidoU2F),
// The remaining attestation statement formats are deserialized as serde_cbor::Values---we do
// not perform any validation of their contents. These are expected to be used primarily when
// anonymizing attestation objects that contain attestation statements in these formats.
#[ serde(rename = "android-key" )]
AndroidKey(serde_cbor::Value),
#[ serde(rename = "android-safetynet" )]
AndroidSafetyNet(serde_cbor::Value),
Apple(serde_cbor::Value),
Tpm(serde_cbor::Value),
}
impl AttestationStatement {
/// The [attestation statement format identifier][att-fmt-id].
///
/// [att-fmt-id]: https://w3c.github.io/webauthn/#attestation-statement-format-identifier
pub fn id(&self ) -> &str {
match self {
Self ::None => "none" ,
Self ::Packed(..) => "packed" ,
Self ::FidoU2F(..) => "fido-u2f" ,
Self ::AndroidKey(..) => "android-key" ,
Self ::AndroidSafetyNet(..) => "android-safetynet" ,
Self ::Apple(..) => "apple" ,
Self ::Tpm(..) => "tpm" ,
}
}
}
impl Serialize for AttestationStatement {
fn serialize<S>(&self , serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match self {
Self ::None => serializer.serialize_map(Some(0 ))?.end(),
Self ::Packed(ref v) => serializer.serialize_some(v),
Self ::FidoU2F(ref v) => serializer.serialize_some(v),
Self ::AndroidKey(ref v) => serializer.serialize_some(v),
Self ::AndroidSafetyNet(ref v) => serializer.serialize_some(v),
Self ::Apple(ref v) => serializer.serialize_some(v),
Self ::Tpm(ref v) => serializer.serialize_some(v),
}
}
}
// AttestationStatement::None is serialized as the empty map. We need to enforce
// the emptyness condition manually while deserializing.
fn deserialize_none_att_stmt<'de, D>(deserializer: D) -> Result<(), D::Error>
where
D: Deserializer<'de>,
{
let map = <std::collections::BTreeMap<(), ()>>::deserialize(deserializer)?;
if !map.is_empty() {
return Err(D::Error::invalid_value(Unexpected::Map, &"the empty map" ));
}
Ok(())
}
// Not all crypto-backends currently provide "crypto::verify()", so we do not implement it yet.
// Also not sure, if we really need it. Would be a sanity-check only, to verify the signature is valid,
// before sendig it out.
// impl AttestationStatement {
// pub fn verify(&self, data: &[u8]) -> Result<bool, AuthenticatorError> {
// match self {
// AttestationStatement::None => Ok(true),
// AttestationStatement::Unparsed(_) => Err(AuthenticatorError::Custom(
// "Unparsed attestation object can't be used to verify signature.".to_string(),
// )),
// AttestationStatement::FidoU2F(att) => {
// let res = crypto::verify(
// crypto::SignatureAlgorithm::ES256,
// &att.attestation_cert[0].as_ref(),
// att.sig.as_ref(),
// data,
// )?;
// Ok(res)
// }
// AttestationStatement::Packed(att) => {
// if att.alg != Alg::ES256 {
// return Err(AuthenticatorError::Custom(
// "Verification only supported for ES256".to_string(),
// ));
// }
// let res = crypto::verify(
// crypto::SignatureAlgorithm::ES256,
// att.attestation_cert[0].as_ref(),
// att.sig.as_ref(),
// data,
// )?;
// Ok(res)
// }
// }
// }
// }
#[ derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
// See https://www.w3.org/TR/webauthn-2/#sctn-fido-u2f-attestation
// u2fStmtFormat = {
// x5c: [ attestnCert: bytes ],
// sig: bytes
// }
pub struct AttestationStatementFidoU2F {
pub sig: Signature, // (1) "sig"
/// Certificate chain in x509 format
#[ serde(rename = "x5c" )]
pub attestation_cert: Vec<AttestationCertificate>, // (2) "x5c"
}
impl AttestationStatementFidoU2F {
pub fn new(cert: &[u8], signature: &[u8]) -> Self {
AttestationStatementFidoU2F {
attestation_cert: vec![AttestationCertificate(Vec::from(cert))],
sig: Signature::from(signature),
}
}
}
#[ derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
// https://www.w3.org/TR/webauthn-2/#sctn-packed-attestation
// packedStmtFormat = {
// alg: COSEAlgorithmIdentifier,
// sig: bytes,
// x5c: [ attestnCert: bytes, * (caCert: bytes) ]
// } //
// {
// alg: COSEAlgorithmIdentifier
// sig: bytes,
// }
pub struct AttestationStatementPacked {
pub alg: COSEAlgorithm, // (1) "alg"
pub sig: Signature, // (2) "sig"
/// Certificate chain in x509 format
#[ serde(rename = "x5c" , skip_serializing_if = "Vec::is_empty" , default)]
pub attestation_cert: Vec<AttestationCertificate>, // (3) "x5c"
}
// A WebAuthn attestation object is a CBOR map with keys "fmt", "attStmt", and "authData". The
// "fmt" field determines the type of "attStmt". The flatten attribute here turns the tag and
// content attributes on AttestationStatement (defined above) into expected keys for
// AttestationObject, which allows us to derive Deserialize. Like many of our other structs, the
// derived Deserialize implementation is permissive: it does not enforce CTAP2 canonical CBOR
// encoding and it allows repeated keys (the last one wins).
#[ derive(Debug, PartialEq, Eq, Deserialize)]
#[ serde(rename_all = "camelCase" )]
pub struct AttestationObject {
pub auth_data: AuthenticatorData,
#[ serde(flatten)]
pub att_stmt: AttestationStatement,
}
impl AttestationObject {
pub fn anonymize(&mut self ) {
// Remove the attestation statement and the AAGUID from the authenticator data.
self .att_stmt = AttestationStatement::None;
if let Some(credential_data) = self .auth_data.credential_data.as_mut() {
credential_data.aaguid = AAGuid::default();
}
}
}
impl Serialize for AttestationObject {
fn serialize<S>(&self , serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serialize_map!(
serializer,
// CTAP2 canonical CBOR order for these entries is ("fmt", "attStmt", "authData")
// as strings are sorted by length and then lexically.
// see https://www.w3.org/TR/webauthn-2/#attestation-object
&"fmt" => self .att_stmt.id(),
&"attStmt" => &self .att_stmt,
&"authData" => &self .auth_data,
)
}
}
#[ cfg(test)]
pub mod test {
use super ::super ::utils::from_slice_stream;
use super ::*;
use crate ::crypto::{COSEAlgorithm, COSEEC2Key, COSEKey, COSEKeyType, Curve};
use serde_cbor::{from_slice, to_vec};
const SAMPLE_ATTESTATION_STMT_NONE: [u8; 19 ] = [
0 xa2, // map(2)
0 x63, // text(3)
0 x66, 0 x6d, 0 x74, // "fmt"
0 x64, // text(4)
0 x6e, 0 x6f, 0 x6e, 0 x65, // "none"
0 x67, // text(7)
0 x61, 0 x74, 0 x74, 0 x53, 0 x74, 0 x6d, 0 x74, // "attStmt"
0 xa0, // map(0)
];
const SAMPLE_ATTESTATION_STMT_FIDO_U2F: [u8; 840 ] = [
0 xa2, // map(2)
0 x63, // text(3)
0 x66, 0 x6d, 0 x74, // "fmt"
0 x68, // text(8)
0 x66, 0 x69, 0 x64, 0 x6f, 0 x2d, 0 x75, 0 x32, 0 x66, // "fido-u2f"
0 x67, // text(7)
0 x61, 0 x74, 0 x74, 0 x53, 0 x74, 0 x6d, 0 x74, // "attStmt"
0 xa2, // map(2)
0 x63, // text(3)
0 x78, 0 x35, 0 x63, // "x5c"
0 x81, // array(1)
0 x59, 0 x02, 0 xdd, // bytes(733)
0 x30, 0 x82, 0 x02, 0 xd9, 0 x30, 0 x82, 0 x01, 0 xc1, 0 xa0, 0 x03, 0 x02, 0 x01, 0 x02, 0 x02, 0 x09,
0 x00, 0 xdf, 0 x92, 0 xd9, 0 xc4, 0 xe2, 0 xed, 0 x66, 0 x0a, 0 x30, 0 x0d, 0 x06, 0 x09, 0 x2a, 0 x86,
0 x48, 0 x86, 0 xf7, 0 x0d, 0 x01, 0 x01, 0 x0b, 0 x05, 0 x00, 0 x30, 0 x2e, 0 x31, 0 x2c, 0 x30, 0 x2a,
0 x06, 0 x03, 0 x55, 0 x04, 0 x03, 0 x13, 0 x23, 0 x59, 0 x75, 0 x62, 0 x69, 0 x63, 0 x6f, 0 x20, 0 x55,
0 x32, 0 x46, 0 x20, 0 x52, 0 x6f, 0 x6f, 0 x74, 0 x20, 0 x43, 0 x41, 0 x20, 0 x53, 0 x65, 0 x72, 0 x69,
0 x61, 0 x6c, 0 x20, 0 x34, 0 x35, 0 x37, 0 x32, 0 x30, 0 x30, 0 x36, 0 x33, 0 x31, 0 x30, 0 x20, 0 x17,
0 x0d, 0 x31, 0 x34, 0 x30, 0 x38, 0 x30, 0 x31, 0 x30, 0 x30, 0 x30, 0 x30, 0 x30, 0 x30, 0 x5a, 0 x18,
0 x0f, 0 x32, 0 x30, 0 x35, 0 x30, 0 x30, 0 x39, 0 x30, 0 x34, 0 x30, 0 x30, 0 x30, 0 x30, 0 x30, 0 x30,
0 x5a, 0 x30, 0 x6f, 0 x31, 0 x0b, 0 x30, 0 x09, 0 x06, 0 x03, 0 x55, 0 x04, 0 x06, 0 x13, 0 x02, 0 x53,
0 x45, 0 x31, 0 x12, 0 x30, 0 x10, 0 x06, 0 x03, 0 x55, 0 x04, 0 x0a, 0 x0c, 0 x09, 0 x59, 0 x75, 0 x62,
0 x69, 0 x63, 0 x6f, 0 x20, 0 x41, 0 x42, 0 x31, 0 x22, 0 x30, 0 x20, 0 x06, 0 x03, 0 x55, 0 x04, 0 x0b,
0 x0c, 0 x19, 0 x41, 0 x75, 0 x74, 0 x68, 0 x65, 0 x6e, 0 x74, 0 x69, 0 x63, 0 x61, 0 x74, 0 x6f, 0 x72,
0 x20, 0 x41, 0 x74, 0 x74, 0 x65, 0 x73, 0 x74, 0 x61, 0 x74, 0 x69, 0 x6f, 0 x6e, 0 x31, 0 x28, 0 x30,
0 x26, 0 x06, 0 x03, 0 x55, 0 x04, 0 x03, 0 x0c, 0 x1f, 0 x59, 0 x75, 0 x62, 0 x69, 0 x63, 0 x6f, 0 x20,
0 x55, 0 x32, 0 x46, 0 x20, 0 x45, 0 x45, 0 x20, 0 x53, 0 x65, 0 x72, 0 x69, 0 x61, 0 x6c, 0 x20, 0 x31,
0 x31, 0 x35, 0 x35, 0 x31, 0 x30, 0 x39, 0 x35, 0 x39, 0 x39, 0 x30, 0 x59, 0 x30, 0 x13, 0 x06, 0 x07,
0 x2a, 0 x86, 0 x48, 0 xce, 0 x3d, 0 x02, 0 x01, 0 x06, 0 x08, 0 x2a, 0 x86, 0 x48, 0 xce, 0 x3d, 0 x03,
0 x01, 0 x07, 0 x03, 0 x42, 0 x00, 0 x04, 0 x0a, 0 x18, 0 x6c, 0 x6e, 0 x4d, 0 x0a, 0 x6a, 0 x52, 0 x8a,
0 x44, 0 x90, 0 x9a, 0 x7a, 0 x24, 0 x23, 0 x68, 0 x70, 0 x28, 0 xd4, 0 xc5, 0 x7e, 0 xcc, 0 xb7, 0 x17,
0 xba, 0 x12, 0 x80, 0 xb8, 0 x5c, 0 x2f, 0 xc1, 0 xe4, 0 xe0, 0 x61, 0 x66, 0 x8c, 0 x3c, 0 x20, 0 xae,
0 xf3, 0 x33, 0 x50, 0 xd1, 0 x96, 0 x45, 0 x23, 0 x8a, 0 x2c, 0 x39, 0 x0b, 0 xf5, 0 xdf, 0 xfa, 0 x34,
0 xff, 0 x25, 0 x50, 0 x2f, 0 x47, 0 x0f, 0 x3d, 0 x40, 0 xb8, 0 x88, 0 xa3, 0 x81, 0 x81, 0 x30, 0 x7f,
0 x30, 0 x13, 0 x06, 0 x0a, 0 x2b, 0 x06, 0 x01, 0 x04, 0 x01, 0 x82, 0 xc4, 0 x0a, 0 x0d, 0 x01, 0 x04,
0 x05, 0 x04, 0 x03, 0 x05, 0 x04, 0 x03, 0 x30, 0 x22, 0 x06, 0 x09, 0 x2b, 0 x06, 0 x01, 0 x04, 0 x01,
0 x82, 0 xc4, 0 x0a, 0 x02, 0 x04, 0 x15, 0 x31, 0 x2e, 0 x33, 0 x2e, 0 x36, 0 x2e, 0 x31, 0 x2e, 0 x34,
0 x2e, 0 x31, 0 x2e, 0 x34, 0 x31, 0 x34, 0 x38, 0 x32, 0 x2e, 0 x31, 0 x2e, 0 x37, 0 x30, 0 x13, 0 x06,
0 x0b, 0 x2b, 0 x06, 0 x01, 0 x04, 0 x01, 0 x82, 0 xe5, 0 x1c, 0 x02, 0 x01, 0 x01, 0 x04, 0 x04, 0 x03,
0 x02, 0 x04, 0 x30, 0 x30, 0 x21, 0 x06, 0 x0b, 0 x2b, 0 x06, 0 x01, 0 x04, 0 x01, 0 x82, 0 xe5, 0 x1c,
0 x01, 0 x01, 0 x04, 0 x04, 0 x12, 0 x04, 0 x10, 0 x2f, 0 xc0, 0 x57, 0 x9f, 0 x81, 0 x13, 0 x47, 0 xea,
0 xb1, 0 x16, 0 xbb, 0 x5a, 0 x8d, 0 xb9, 0 x20, 0 x2a, 0 x30, 0 x0c, 0 x06, 0 x03, 0 x55, 0 x1d, 0 x13,
0 x01, 0 x01, 0 xff, 0 x04, 0 x02, 0 x30, 0 x00, 0 x30, 0 x0d, 0 x06, 0 x09, 0 x2a, 0 x86, 0 x48, 0 x86,
0 xf7, 0 x0d, 0 x01, 0 x01, 0 x0b, 0 x05, 0 x00, 0 x03, 0 x82, 0 x01, 0 x01, 0 x00, 0 x82, 0 xac, 0 xaf,
0 x11, 0 x30, 0 xa9, 0 x9b, 0 xd1, 0 x43, 0 x27, 0 xd2, 0 xf8, 0 xf9, 0 xb0, 0 x41, 0 xa2, 0 xa0, 0 x4a,
0 x66, 0 x85, 0 x27, 0 x24, 0 x22, 0 xe5, 0 x7b, 0 x14, 0 xb0, 0 xb8, 0 xf8, 0 x3b, 0 x6f, 0 x15, 0 x45,
0 x66, 0 x4b, 0 xbf, 0 x55, 0 x68, 0 x1e, 0 xaf, 0 x01, 0 x58, 0 x72, 0 x2a, 0 xbf, 0 xce, 0 xd2, 0 xe4,
0 xac, 0 x63, 0 x3c, 0 xec, 0 x09, 0 x59, 0 x56, 0 x45, 0 x24, 0 xb0, 0 xf2, 0 xe5, 0 x17, 0 xdd, 0 x97,
0 x10, 0 x98, 0 xb9, 0 x89, 0 x15, 0 x17, 0 xec, 0 xd0, 0 xc5, 0 x53, 0 xa2, 0 xe4, 0 x73, 0 x9f, 0 x9d,
0 xe1, 0 x3d, 0 xaf, 0 xd0, 0 xd5, 0 xd7, 0 xb8, 0 xac, 0 x4a, 0 x37, 0 xf4, 0 xf2, 0 xcc, 0 x30, 0 xef,
0 x25, 0 xcb, 0 x00, 0 x65, 0 x2d, 0 x19, 0 xdb, 0 x69, 0 xd7, 0 xda, 0 x57, 0 xbd, 0 x1a, 0 x9c, 0 x1d,
0 x8e, 0 xd8, 0 x7d, 0 x46, 0 xd8, 0 x0d, 0 x2b, 0 x3b, 0 xdf, 0 xd1, 0 xd9, 0 xef, 0 x9d, 0 x2b, 0 x68,
0 x32, 0 xd4, 0 xad, 0 x5b, 0 xcd, 0 x74, 0 x21, 0 x4c, 0 xe6, 0 xa6, 0 x14, 0 x1d, 0 x16, 0 xb2, 0 xe9,
0 x3a, 0 xcb, 0 x2c, 0 x88, 0 xf6, 0 x0a, 0 x3e, 0 xb6, 0 xd5, 0 xf6, 0 x14, 0 x71, 0 x97, 0 x59, 0 x09,
0 x37, 0 x3b, 0 xc6, 0 x77, 0 x90, 0 x23, 0 x24, 0 x57, 0 x1a, 0 x57, 0 x3f, 0 x60, 0 xf0, 0 x7b, 0 xbe,
0 xd1, 0 x7b, 0 x92, 0 xc8, 0 xb5, 0 x9f, 0 xa2, 0 x82, 0 x10, 0 xbf, 0 xa8, 0 xc6, 0 x01, 0 x22, 0 x93,
0 x00, 0 x1b, 0 x39, 0 xef, 0 xe5, 0 x7b, 0 xf9, 0 xcb, 0 x1e, 0 x3a, 0 xca, 0 x8a, 0 x41, 0 x30, 0 xf8,
0 x3a, 0 xf8, 0 x66, 0 x8f, 0 x73, 0 xde, 0 xf2, 0 x71, 0 x1b, 0 x20, 0 xdc, 0 x99, 0 xe8, 0 xa8, 0 x04,
0 xee, 0 xa3, 0 xf7, 0 x42, 0 x71, 0 x97, 0 xb6, 0 xb4, 0 x51, 0 xb3, 0 x73, 0 x5c, 0 x23, 0 xbc, 0 x9b,
0 x1b, 0 xe2, 0 x74, 0 xc2, 0 x6d, 0 x3b, 0 xf9, 0 x19, 0 x6f, 0 x8c, 0 x4a, 0 x4b, 0 x71, 0 x5f, 0 x4b,
0 x95, 0 xc4, 0 xdb, 0 x7b, 0 x97, 0 xe7, 0 x59, 0 x4e, 0 xb4, 0 x65, 0 x64, 0 x8c, 0 x1c, 0 x63, 0 x73,
0 x69, 0 x67, // "sig"
0 x58, 0 x46, // bytes(70)
0 x30, 0 x44, 0 x02, 0 x20, 0 x48, 0 x5a, 0 x72, 0 x40, 0 xdf, 0 x2c, 0 x1e, 0 x31, 0 xa5, 0 xb3, 0 x0b,
0 x3b, 0 x2c, 0 xd1, 0 xad, 0 xd0, 0 x8d, 0 xae, 0 x8d, 0 x7a, 0 x25, 0 x3e, 0 xf5, 0 xa6, 0 x25, 0 xdb,
0 x2e, 0 x22, 0 x1b, 0 x71, 0 xe5, 0 x78, 0 x02, 0 x20, 0 x45, 0 xbd, 0 xdc, 0 x30, 0 xde, 0 xf4, 0 x05,
0 x97, 0 x5c, 0 xac, 0 x72, 0 x58, 0 x96, 0 xa6, 0 x00, 0 x94, 0 x57, 0 x3a, 0 xa5, 0 xe8, 0 x1e, 0 xf4,
0 xfd, 0 x30, 0 xd3, 0 x88, 0 x11, 0 x8b, 0 x49, 0 x97, 0 xdf, 0 x34,
];
const SAMPLE_ATTESTATION_OBJ_PACKED: [u8; 677 ] = [
0 xa3, // map(3)
0 x63, // text(3)
0 x66, 0 x6D, 0 x74, // "fmt"
0 x66, // text(6)
0 x70, 0 x61, 0 x63, 0 x6b, 0 x65, 0 x64, // "packed"
0 x67, // text(7)
0 x61, 0 x74, 0 x74, 0 x53, 0 x74, 0 x6D, 0 x74, // "attStmt"
0 xa3, // map(3)
0 x63, // text(3)
0 x61, 0 x6c, 0 x67, // "alg"
0 x26, // -7 (ES256)
0 x63, // text(3)
0 x73, 0 x69, 0 x67, // "sig"
0 x58, 0 x47, // bytes(71)
0 x30, 0 x45, 0 x02, 0 x20, 0 x13, 0 xf7, 0 x3c, 0 x5d, 0 x9d, 0 x53, 0 x0e, 0 x8c, 0 xc1, 0 x5c,
0 xc9, // signature
0 xbd, 0 x96, 0 xad, 0 x58, 0 x6d, 0 x39, 0 x36, 0 x64, 0 xe4, 0 x62, 0 xd5, 0 xf0, 0 x56, 0 x12,
0 x35, // ..
0 xe6, 0 x35, 0 x0f, 0 x2b, 0 x72, 0 x89, 0 x02, 0 x21, 0 x00, 0 x90, 0 x35, 0 x7f, 0 xf9, 0 x10,
0 xcc, // ..
0 xb5, 0 x6a, 0 xc5, 0 xb5, 0 x96, 0 x51, 0 x19, 0 x48, 0 x58, 0 x1c, 0 x8f, 0 xdd, 0 xb4, 0 xa2,
0 xb7, // ..
0 x99, 0 x59, 0 x94, 0 x80, 0 x78, 0 xb0, 0 x9f, 0 x4b, 0 xdc, 0 x62, 0 x29, // ..
0 x63, // text(3)
0 x78, 0 x35, 0 x63, // "x5c"
0 x81, // array(1)
0 x59, 0 x01, 0 x97, // bytes(407)
0 x30, 0 x82, 0 x01, 0 x93, 0 x30, 0 x82, 0 x01, //certificate...
0 x38, 0 xa0, 0 x03, 0 x02, 0 x01, 0 x02, 0 x02, 0 x09, 0 x00, 0 x85, 0 x9b, 0 x72, 0 x6c, 0 xb2, 0 x4b,
0 x4c, 0 x29, 0 x30, 0 x0a, 0 x06, 0 x08, 0 x2a, 0 x86, 0 x48, 0 xce, 0 x3d, 0 x04, 0 x03, 0 x02, 0 x30,
0 x47, 0 x31, 0 x0b, 0 x30, 0 x09, 0 x06, 0 x03, 0 x55, 0 x04, 0 x06, 0 x13, 0 x02, 0 x55, 0 x53, 0 x31,
0 x14, 0 x30, 0 x12, 0 x06, 0 x03, 0 x55, 0 x04, 0 x0a, 0 x0c, 0 x0b, 0 x59, 0 x75, 0 x62, 0 x69, 0 x63,
0 x6f, 0 x20, 0 x54, 0 x65, 0 x73, 0 x74, 0 x31, 0 x22, 0 x30, 0 x20, 0 x06, 0 x03, 0 x55, 0 x04, 0 x0b,
0 x0c, 0 x19, 0 x41, 0 x75, 0 x74, 0 x68, 0 x65, 0 x6e, 0 x74, 0 x69, 0 x63, 0 x61, 0 x74, 0 x6f, 0 x72,
0 x20, 0 x41, 0 x74, 0 x74, 0 x65, 0 x73, 0 x74, 0 x61, 0 x74, 0 x69, 0 x6f, 0 x6e, 0 x30, 0 x1e, 0 x17,
0 x0d, 0 x31, 0 x36, 0 x31, 0 x32, 0 x30, 0 x34, 0 x31, 0 x31, 0 x35, 0 x35, 0 x30, 0 x30, 0 x5a, 0 x17,
0 x0d, 0 x32, 0 x36, 0 x31, 0 x32, 0 x30, 0 x32, 0 x31, 0 x31, 0 x35, 0 x35, 0 x30, 0 x30, 0 x5a, 0 x30,
0 x47, 0 x31, 0 x0b, 0 x30, 0 x09, 0 x06, 0 x03, 0 x55, 0 x04, 0 x06, 0 x13, 0 x02, 0 x55, 0 x53, 0 x31,
0 x14, 0 x30, 0 x12, 0 x06, 0 x03, 0 x55, 0 x04, 0 x0a, 0 x0c, 0 x0b, 0 x59, 0 x75, 0 x62, 0 x69, 0 x63,
0 x6f, 0 x20, 0 x54, 0 x65, 0 x73, 0 x74, 0 x31, 0 x22, 0 x30, 0 x20, 0 x06, 0 x03, 0 x55, 0 x04, 0 x0b,
0 x0c, 0 x19, 0 x41, 0 x75, 0 x74, 0 x68, 0 x65, 0 x6e, 0 x74, 0 x69, 0 x63, 0 x61, 0 x74, 0 x6f, 0 x72,
0 x20, 0 x41, 0 x74, 0 x74, 0 x65, 0 x73, 0 x74, 0 x61, 0 x74, 0 x69, 0 x6f, 0 x6e, 0 x30, 0 x59, 0 x30,
0 x13, 0 x06, 0 x07, 0 x2a, 0 x86, 0 x48, 0 xce, 0 x3d, 0 x02, 0 x01, 0 x06, 0 x08, 0 x2a, 0 x86, 0 x48,
0 xce, 0 x3d, 0 x03, 0 x01, 0 x07, 0 x03, 0 x42, 0 x00, 0 x04, 0 xad, 0 x11, 0 xeb, 0 x0e, 0 x88, 0 x52,
0 xe5, 0 x3a, 0 xd5, 0 xdf, 0 xed, 0 x86, 0 xb4, 0 x1e, 0 x61, 0 x34, 0 xa1, 0 x8e, 0 xc4, 0 xe1, 0 xaf,
0 x8f, 0 x22, 0 x1a, 0 x3c, 0 x7d, 0 x6e, 0 x63, 0 x6c, 0 x80, 0 xea, 0 x13, 0 xc3, 0 xd5, 0 x04, 0 xff,
0 x2e, 0 x76, 0 x21, 0 x1b, 0 xb4, 0 x45, 0 x25, 0 xb1, 0 x96, 0 xc4, 0 x4c, 0 xb4, 0 x84, 0 x99, 0 x79,
0 xcf, 0 x6f, 0 x89, 0 x6e, 0 xcd, 0 x2b, 0 xb8, 0 x60, 0 xde, 0 x1b, 0 xf4, 0 x37, 0 x6b, 0 xa3, 0 x0d,
0 x30, 0 x0b, 0 x30, 0 x09, 0 x06, 0 x03, 0 x55, 0 x1d, 0 x13, 0 x04, 0 x02, 0 x30, 0 x00, 0 x30, 0 x0a,
0 x06, 0 x08, 0 x2a, 0 x86, 0 x48, 0 xce, 0 x3d, 0 x04, 0 x03, 0 x02, 0 x03, 0 x49, 0 x00, 0 x30, 0 x46,
0 x02, 0 x21, 0 x00, 0 xe9, 0 xa3, 0 x9f, 0 x1b, 0 x03, 0 x19, 0 x75, 0 x25, 0 xf7, 0 x37, 0 x3e, 0 x10,
0 xce, 0 x77, 0 xe7, 0 x80, 0 x21, 0 x73, 0 x1b, 0 x94, 0 xd0, 0 xc0, 0 x3f, 0 x3f, 0 xda, 0 x1f, 0 xd2,
0 x2d, 0 xb3, 0 xd0, 0 x30, 0 xe7, 0 x02, 0 x21, 0 x00, 0 xc4, 0 xfa, 0 xec, 0 x34, 0 x45, 0 xa8, 0 x20,
0 xcf, 0 x43, 0 x12, 0 x9c, 0 xdb, 0 x00, 0 xaa, 0 xbe, 0 xfd, 0 x9a, 0 xe2, 0 xd8, 0 x74, 0 xf9, 0 xc5,
0 xd3, 0 x43, 0 xcb, 0 x2f, 0 x11, 0 x3d, 0 xa2, 0 x37, 0 x23, 0 xf3, 0 x68, // text(8)
0 x61, 0 x75, 0 x74, 0 x68, 0 x44, 0 x61, 0 x74, 0 x61, // "authData"
0 x58, 0 x94, // bytes(148)
// authData
0 xc2, 0 x89, 0 xc5, 0 xca, 0 x9b, 0 x04, 0 x60, 0 xf9, 0 x34, 0 x6a, 0 xb4, 0 xe4, 0 x2d, 0 x84,
0 x27, // rp_id_hash
0 x43, 0 x40, 0 x4d, 0 x31, 0 xf4, 0 x84, 0 x68, 0 x25, 0 xa6, 0 xd0, 0 x65, 0 xbe, 0 x59, 0 x7a,
0 x87, // rp_id_hash
0 x05, 0 x1d, // rp_id_hash
0 x41, // authData Flags
0 x00, 0 x00, 0 x00, 0 x0b, // authData counter
0 xf8, 0 xa0, 0 x11, 0 xf3, 0 x8c, 0 x0a, 0 x4d, 0 x15, 0 x80, 0 x06, 0 x17, 0 x11, 0 x1f, 0 x9e, 0 xdc,
0 x7d, // AAGUID
0 x00, 0 x10, // credential id length
0 x89, 0 x59, 0 xce, 0 xad, 0 x5b, 0 x5c, 0 x48, 0 x16, 0 x4e, 0 x8a, 0 xbc, 0 xd6, 0 xd9, 0 x43, 0 x5c,
0 x6f, // credential id
// credential public key
0 xa5, 0 x01, 0 x02, 0 x03, 0 x26, 0 x20, 0 x01, 0 x21, 0 x58, 0 x20, 0 xa5, 0 xfd, 0 x5c, 0 xe1, 0 xb1,
0 xc4, 0 x58, 0 xc5, 0 x30, 0 xa5, 0 x4f, 0 xa6, 0 x1b, 0 x31, 0 xbf, 0 x6b, 0 x04, 0 xbe, 0 x8b, 0 x97,
0 xaf, 0 xde, 0 x54, 0 xdd, 0 x8c, 0 xbb, 0 x69, 0 x27, 0 x5a, 0 x8a, 0 x1b, 0 xe1, 0 x22, 0 x58, 0 x20,
0 xfa, 0 x3a, 0 x32, 0 x31, 0 xdd, 0 x9d, 0 xee, 0 xd9, 0 xd1, 0 x89, 0 x7b, 0 xe5, 0 xa6, 0 x22, 0 x8c,
0 x59, 0 x50, 0 x1e, 0 x4b, 0 xcd, 0 x12, 0 x97, 0 x5d, 0 x3d, 0 xff, 0 x73, 0 x0f, 0 x01, 0 x27, 0 x8e,
0 xa6, 0 x1c,
];
const SAMPLE_CERT_CHAIN: [u8; 709 ] = [
0 x81, 0 x59, 0 x2, 0 xc1, 0 x30, 0 x82, 0 x2, 0 xbd, 0 x30, 0 x82, 0 x1, 0 xa5, 0 xa0, 0 x3, 0 x2, 0 x1,
0 x2, 0 x2, 0 x4, 0 x18, 0 xac, 0 x46, 0 xc0, 0 x30, 0 xd, 0 x6, 0 x9, 0 x2a, 0 x86, 0 x48, 0 x86, 0 xf7,
0 xd, 0 x1, 0 x1, 0 xb, 0 x5, 0 x0, 0 x30, 0 x2e, 0 x31, 0 x2c, 0 x30, 0 x2a, 0 x6, 0 x3, 0 x55, 0 x4, 0 x3,
0 x13, 0 x23, 0 x59, 0 x75, 0 x62, 0 x69, 0 x63, 0 x6f, 0 x20, 0 x55, 0 x32, 0 x46, 0 x20, 0 x52, 0 x6f,
0 x6f, 0 x74, 0 x20, 0 x43, 0 x41, 0 x20, 0 x53, 0 x65, 0 x72, 0 x69, 0 x61, 0 x6c, 0 x20, 0 x34, 0 x35,
0 x37, 0 x32, 0 x30, 0 x30, 0 x36, 0 x33, 0 x31, 0 x30, 0 x20, 0 x17, 0 xd, 0 x31, 0 x34, 0 x30, 0 x38,
0 x30, 0 x31, 0 x30, 0 x30, 0 x30, 0 x30, 0 x30, 0 x30, 0 x5a, 0 x18, 0 xf, 0 x32, 0 x30, 0 x35, 0 x30,
0 x30, 0 x39, 0 x30, 0 x34, 0 x30, 0 x30, 0 x30, 0 x30, 0 x30, 0 x30, 0 x5a, 0 x30, 0 x6e, 0 x31, 0 xb,
0 x30, 0 x9, 0 x6, 0 x3, 0 x55, 0 x4, 0 x6, 0 x13, 0 x2, 0 x53, 0 x45, 0 x31, 0 x12, 0 x30, 0 x10, 0 x6,
0 x3, 0 x55, 0 x4, 0 xa, 0 xc, 0 x9, 0 x59, 0 x75, 0 x62, 0 x69, 0 x63, 0 x6f, 0 x20, 0 x41, 0 x42, 0 x31,
0 x22, 0 x30, 0 x20, 0 x6, 0 x3, 0 x55, 0 x4, 0 xb, 0 xc, 0 x19, 0 x41, 0 x75, 0 x74, 0 x68, 0 x65, 0 x6e,
0 x74, 0 x69, 0 x63, 0 x61, 0 x74, 0 x6f, 0 x72, 0 x20, 0 x41, 0 x74, 0 x74, 0 x65, 0 x73, 0 x74, 0 x61,
0 x74, 0 x69, 0 x6f, 0 x6e, 0 x31, 0 x27, 0 x30, 0 x25, 0 x6, 0 x3, 0 x55, 0 x4, 0 x3, 0 xc, 0 x1e, 0 x59,
0 x75, 0 x62, 0 x69, 0 x63, 0 x6f, 0 x20, 0 x55, 0 x32, 0 x46, 0 x20, 0 x45, 0 x45, 0 x20, 0 x53, 0 x65,
0 x72, 0 x69, 0 x61, 0 x6c, 0 x20, 0 x34, 0 x31, 0 x33, 0 x39, 0 x34, 0 x33, 0 x34, 0 x38, 0 x38, 0 x30,
0 x59, 0 x30, 0 x13, 0 x6, 0 x7, 0 x2a, 0 x86, 0 x48, 0 xce, 0 x3d, 0 x2, 0 x1, 0 x6, 0 x8, 0 x2a, 0 x86,
0 x48, 0 xce, 0 x3d, 0 x3, 0 x1, 0 x7, 0 x3, 0 x42, 0 x0, 0 x4, 0 x79, 0 xea, 0 x3b, 0 x2c, 0 x7c, 0 x49,
0 x70, 0 x10, 0 x62, 0 x23, 0 xc, 0 xd2, 0 x3f, 0 xeb, 0 x60, 0 xe5, 0 x29, 0 x31, 0 x71, 0 xd4, 0 x83,
0 xf1, 0 x0, 0 xbe, 0 x85, 0 x9d, 0 x6b, 0 xf, 0 x83, 0 x97, 0 x3, 0 x1, 0 xb5, 0 x46, 0 xcd, 0 xd4, 0 x6e,
0 xcf, 0 xca, 0 xe3, 0 xe3, 0 xf3, 0 xf, 0 x81, 0 xe9, 0 xed, 0 x62, 0 xbd, 0 x26, 0 x8d, 0 x4c, 0 x1e,
0 xbd, 0 x37, 0 xb3, 0 xbc, 0 xbe, 0 x92, 0 xa8, 0 xc2, 0 xae, 0 xeb, 0 x4e, 0 x3a, 0 xa3, 0 x6c, 0 x30,
0 x6a, 0 x30, 0 x22, 0 x6, 0 x9, 0 x2b, 0 x6, 0 x1, 0 x4, 0 x1, 0 x82, 0 xc4, 0 xa, 0 x2, 0 x4, 0 x15,
0 x31, 0 x2e, 0 x33, 0 x2e, 0 x36, 0 x2e, 0 x31, 0 x2e, 0 x34, 0 x2e, 0 x31, 0 x2e, 0 x34, 0 x31, 0 x34,
0 x38, 0 x32, 0 x2e, 0 x31, 0 x2e, 0 x37, 0 x30, 0 x13, 0 x6, 0 xb, 0 x2b, 0 x6, 0 x1, 0 x4, 0 x1, 0 x82,
0 xe5, 0 x1c, 0 x2, 0 x1, 0 x1, 0 x4, 0 x4, 0 x3, 0 x2, 0 x5, 0 x20, 0 x30, 0 x21, 0 x6, 0 xb, 0 x2b, 0 x6,
0 x1, 0 x4, 0 x1, 0 x82, 0 xe5, 0 x1c, 0 x1, 0 x1, 0 x4, 0 x4, 0 x12, 0 x4, 0 x10, 0 xcb, 0 x69, 0 x48,
0 x1e, 0 x8f, 0 xf7, 0 x40, 0 x39, 0 x93, 0 xec, 0 xa, 0 x27, 0 x29, 0 xa1, 0 x54, 0 xa8, 0 x30, 0 xc,
0 x6, 0 x3, 0 x55, 0 x1d, 0 x13, 0 x1, 0 x1, 0 xff, 0 x4, 0 x2, 0 x30, 0 x0, 0 x30, 0 xd, 0 x6, 0 x9, 0 x2a,
0 x86, 0 x48, 0 x86, 0 xf7, 0 xd, 0 x1, 0 x1, 0 xb, 0 x5, 0 x0, 0 x3, 0 x82, 0 x1, 0 x1, 0 x0, 0 x97, 0 x9d,
0 x3, 0 x97, 0 xd8, 0 x60, 0 xf8, 0 x2e, 0 xe1, 0 x5d, 0 x31, 0 x1c, 0 x79, 0 x6e, 0 xba, 0 xfb, 0 x22,
0 xfa, 0 xa7, 0 xe0, 0 x84, 0 xd9, 0 xba, 0 xb4, 0 xc6, 0 x1b, 0 xbb, 0 x57, 0 xf3, 0 xe6, 0 xb4, 0 xc1,
0 x8a, 0 x48, 0 x37, 0 xb8, 0 x5c, 0 x3c, 0 x4e, 0 xdb, 0 xe4, 0 x83, 0 x43, 0 xf4, 0 xd6, 0 xa5, 0 xd9,
0 xb1, 0 xce, 0 xda, 0 x8a, 0 xe1, 0 xfe, 0 xd4, 0 x91, 0 x29, 0 x21, 0 x73, 0 x5, 0 x8e, 0 x5e, 0 xe1,
0 xcb, 0 xdd, 0 x6b, 0 xda, 0 xc0, 0 x75, 0 x57, 0 xc6, 0 xa0, 0 xe8, 0 xd3, 0 x68, 0 x25, 0 xba, 0 x15,
0 x9e, 0 x7f, 0 xb5, 0 xad, 0 x8c, 0 xda, 0 xf8, 0 x4, 0 x86, 0 x8c, 0 xf9, 0 xe, 0 x8f, 0 x1f, 0 x8a,
0 xea, 0 x17, 0 xc0, 0 x16, 0 xb5, 0 x5c, 0 x2a, 0 x7a, 0 xd4, 0 x97, 0 xc8, 0 x94, 0 xfb, 0 x71, 0 xd7,
0 x53, 0 xd7, 0 x9b, 0 x9a, 0 x48, 0 x4b, 0 x6c, 0 x37, 0 x6d, 0 x72, 0 x3b, 0 x99, 0 x8d, 0 x2e, 0 x1d,
0 x43, 0 x6, 0 xbf, 0 x10, 0 x33, 0 xb5, 0 xae, 0 xf8, 0 xcc, 0 xa5, 0 xcb, 0 xb2, 0 x56, 0 x8b, 0 x69,
0 x24, 0 x22, 0 x6d, 0 x22, 0 xa3, 0 x58, 0 xab, 0 x7d, 0 x87, 0 xe4, 0 xac, 0 x5f, 0 x2e, 0 x9, 0 x1a,
0 xa7, 0 x15, 0 x79, 0 xf3, 0 xa5, 0 x69, 0 x9, 0 x49, 0 x7d, 0 x72, 0 xf5, 0 x4e, 0 x6, 0 xba, 0 xc1,
0 xc3, 0 xb4, 0 x41, 0 x3b, 0 xba, 0 x5e, 0 xaf, 0 x94, 0 xc3, 0 xb6, 0 x4f, 0 x34, 0 xf9, 0 xeb, 0 xa4,
0 x1a, 0 xcb, 0 x6a, 0 xe2, 0 x83, 0 x77, 0 x6d, 0 x36, 0 x46, 0 x53, 0 x78, 0 x48, 0 xfe, 0 xe8, 0 x84,
0 xbd, 0 xdd, 0 xf5, 0 xb1, 0 xba, 0 x57, 0 x98, 0 x54, 0 xcf, 0 xfd, 0 xce, 0 xba, 0 xc3, 0 x44, 0 x5,
0 x95, 0 x27, 0 xe5, 0 x6d, 0 xd5, 0 x98, 0 xf8, 0 xf5, 0 x66, 0 x71, 0 x5a, 0 xbe, 0 x43, 0 x1, 0 xdd,
0 x19, 0 x11, 0 x30, 0 xe6, 0 xb9, 0 xf0, 0 xc6, 0 x40, 0 x39, 0 x12, 0 x53, 0 xe2, 0 x29, 0 x80, 0 x3f,
0 x3a, 0 xef, 0 x27, 0 x4b, 0 xed, 0 xbf, 0 xde, 0 x3f, 0 xcb, 0 xbd, 0 x42, 0 xea, 0 xd6, 0 x79,
];
const SAMPLE_AUTH_DATA_MAKE_CREDENTIAL: [u8; 164 ] = [
0 x58, 0 xA2, // bytes(162)
// authData
0 xc2, 0 x89, 0 xc5, 0 xca, 0 x9b, 0 x04, 0 x60, 0 xf9, 0 x34, 0 x6a, 0 xb4, 0 xe4, 0 x2d, 0 x84,
0 x27, // rp_id_hash
0 x43, 0 x40, 0 x4d, 0 x31, 0 xf4, 0 x84, 0 x68, 0 x25, 0 xa6, 0 xd0, 0 x65, 0 xbe, 0 x59, 0 x7a,
0 x87, // rp_id_hash
0 x05, 0 x1d, // rp_id_hash
0 xC1, // authData Flags
0 x00, 0 x00, 0 x00, 0 x0b, // authData counter
0 xf8, 0 xa0, 0 x11, 0 xf3, 0 x8c, 0 x0a, 0 x4d, 0 x15, 0 x80, 0 x06, 0 x17, 0 x11, 0 x1f, 0 x9e, 0 xdc,
0 x7d, // AAGUID
0 x00, 0 x10, // credential id length
0 x89, 0 x59, 0 xce, 0 xad, 0 x5b, 0 x5c, 0 x48, 0 x16, 0 x4e, 0 x8a, 0 xbc, 0 xd6, 0 xd9, 0 x43, 0 x5c,
0 x6f, // credential id
// credential public key
0 xa5, 0 x01, 0 x02, 0 x03, 0 x26, 0 x20, 0 x01, 0 x21, 0 x58, 0 x20, 0 xa5, 0 xfd, 0 x5c, 0 xe1, 0 xb1,
0 xc4, 0 x58, 0 xc5, 0 x30, 0 xa5, 0 x4f, 0 xa6, 0 x1b, 0 x31, 0 xbf, 0 x6b, 0 x04, 0 xbe, 0 x8b, 0 x97,
0 xaf, 0 xde, 0 x54, 0 xdd, 0 x8c, 0 xbb, 0 x69, 0 x27, 0 x5a, 0 x8a, 0 x1b, 0 xe1, 0 x22, 0 x58, 0 x20,
0 xfa, 0 x3a, 0 x32, 0 x31, 0 xdd, 0 x9d, 0 xee, 0 xd9, 0 xd1, 0 x89, 0 x7b, 0 xe5, 0 xa6, 0 x22, 0 x8c,
0 x59, 0 x50, 0 x1e, 0 x4b, 0 xcd, 0 x12, 0 x97, 0 x5d, 0 x3d, 0 xff, 0 x73, 0 x0f, 0 x01, 0 x27, 0 x8e,
0 xa6, 0 x1c, // pub key end
// Extensions
0 xA1, // map(1)
0 x6B, // text(11)
0 x68, 0 x6D, 0 x61, 0 x63, 0 x2D, 0 x73, 0 x65, 0 x63, 0 x72, 0 x65, 0 x74, // "hmac-secret"
0 xF5, // true
];
const SAMPLE_AUTH_DATA_GET_ASSERTION: [u8; 229 ] = [
0 x58, 0 xE3, // bytes(227)
// authData
0 xc2, 0 x89, 0 xc5, 0 xca, 0 x9b, 0 x04, 0 x60, 0 xf9, 0 x34, 0 x6a, 0 xb4, 0 xe4, 0 x2d, 0 x84,
0 x27, // rp_id_hash
0 x43, 0 x40, 0 x4d, 0 x31, 0 xf4, 0 x84, 0 x68, 0 x25, 0 xa6, 0 xd0, 0 x65, 0 xbe, 0 x59, 0 x7a,
0 x87, // rp_id_hash
0 x05, 0 x1d, // rp_id_hash
0 xC1, // authData Flags
0 x00, 0 x00, 0 x00, 0 x0b, // authData counter
0 xf8, 0 xa0, 0 x11, 0 xf3, 0 x8c, 0 x0a, 0 x4d, 0 x15, 0 x80, 0 x06, 0 x17, 0 x11, 0 x1f, 0 x9e, 0 xdc,
0 x7d, // AAGUID
0 x00, 0 x10, // credential id length
0 x89, 0 x59, 0 xce, 0 xad, 0 x5b, 0 x5c, 0 x48, 0 x16, 0 x4e, 0 x8a, 0 xbc, 0 xd6, 0 xd9, 0 x43, 0 x5c,
0 x6f, // credential id
// credential public key
0 xa5, 0 x01, 0 x02, 0 x03, 0 x26, 0 x20, 0 x01, 0 x21, 0 x58, 0 x20, 0 xa5, 0 xfd, 0 x5c, 0 xe1, 0 xb1,
0 xc4, 0 x58, 0 xc5, 0 x30, 0 xa5, 0 x4f, 0 xa6, 0 x1b, 0 x31, 0 xbf, 0 x6b, 0 x04, 0 xbe, 0 x8b, 0 x97,
0 xaf, 0 xde, 0 x54, 0 xdd, 0 x8c, 0 xbb, 0 x69, 0 x27, 0 x5a, 0 x8a, 0 x1b, 0 xe1, 0 x22, 0 x58, 0 x20,
0 xfa, 0 x3a, 0 x32, 0 x31, 0 xdd, 0 x9d, 0 xee, 0 xd9, 0 xd1, 0 x89, 0 x7b, 0 xe5, 0 xa6, 0 x22, 0 x8c,
0 x59, 0 x50, 0 x1e, 0 x4b, 0 xcd, 0 x12, 0 x97, 0 x5d, 0 x3d, 0 xff, 0 x73, 0 x0f, 0 x01, 0 x27, 0 x8e,
0 xa6, 0 x1c, // pub key end
// Extensions
0 xA1, // map(1)
0 x6B, // text(11)
0 x68, 0 x6D, 0 x61, 0 x63, 0 x2D, 0 x73, 0 x65, 0 x63, 0 x72, 0 x65, 0 x74, // "hmac-secret"
0 x58, 0 x40, // bytes(64)
0 x1F, 0 x91, 0 x52, 0 x6C, 0 xAE, 0 x45, 0 x6E, 0 x4C, 0 xBB, 0 x71, 0 xC4, 0 xDD, 0 xE7, 0 xBB, 0 x87,
0 x71, 0 x57, 0 xE6, 0 xE5, 0 x4D, 0 xFE, 0 xD3, 0 x01, 0 x5D, 0 x7D, 0 x4D, 0 xBB, 0 x22, 0 x69, 0 xAF,
0 xCD, 0 xE6, 0 xA9, 0 x1B, 0 x8D, 0 x26, 0 x7E, 0 xBB, 0 xF8, 0 x48, 0 xEB, 0 x95, 0 xA6, 0 x8E, 0 x79,
0 xC7, 0 xAC, 0 x70, 0 x5E, 0 x35, 0 x1D, 0 x54, 0 x3D, 0 xB0, 0 x16, 0 x58, 0 x87, 0 xD6, 0 x29, 0 x0F,
0 xD4, 0 x7A, 0 x40, 0 xC4,
];
pub fn create_attestation_obj() -> AttestationObject {
AttestationObject {
auth_data: AuthenticatorData {
rp_id_hash: RpIdHash::from(&[
0 xc2, 0 x89, 0 xc5, 0 xca, 0 x9b, 0 x04, 0 x60, 0 xf9, 0 x34, 0 x6a, 0 xb4, 0 xe4, 0 x2d,
0 x84, 0 x27, 0 x43, 0 x40, 0 x4d, 0 x31, 0 xf4, 0 x84, 0 x68, 0 x25, 0 xa6, 0 xd0, 0 x65,
0 xbe, 0 x59, 0 x7a, 0 x87, 0 x5, 0 x1d,
])
.unwrap(),
flags: AuthenticatorDataFlags::USER_PRESENT | AuthenticatorDataFlags::ATTESTED,
counter: 11 ,
credential_data: Some(AttestedCredentialData {
aaguid: AAGuid::from(&[
0 xf8, 0 xa0, 0 x11, 0 xf3, 0 x8c, 0 x0a, 0 x4d, 0 x15, 0 x80, 0 x06, 0 x17, 0 x11,
0 x1f, 0 x9e, 0 xdc, 0 x7d,
])
.unwrap(),
credential_id: vec![
0 x89, 0 x59, 0 xce, 0 xad, 0 x5b, 0 x5c, 0 x48, 0 x16, 0 x4e, 0 x8a, 0 xbc, 0 xd6,
0 xd9, 0 x43, 0 x5c, 0 x6f,
],
credential_public_key: COSEKey {
alg: COSEAlgorithm::ES256,
key: COSEKeyType::EC2(COSEEC2Key {
curve: Curve::SECP256R1,
x: vec![
0 xA5, 0 xFD, 0 x5C, 0 xE1, 0 xB1, 0 xC4, 0 x58, 0 xC5, 0 x30, 0 xA5, 0 x4F,
0 xA6, 0 x1B, 0 x31, 0 xBF, 0 x6B, 0 x04, 0 xBE, 0 x8B, 0 x97, 0 xAF, 0 xDE,
0 x54, 0 xDD, 0 x8C, 0 xBB, 0 x69, 0 x27, 0 x5A, 0 x8A, 0 x1B, 0 xE1,
],
y: vec![
0 xFA, 0 x3A, 0 x32, 0 x31, 0 xDD, 0 x9D, 0 xEE, 0 xD9, 0 xD1, 0 x89, 0 x7B,
0 xE5, 0 xA6, 0 x22, 0 x8C, 0 x59, 0 x50, 0 x1E, 0 x4B, 0 xCD, 0 x12, 0 x97,
0 x5D, 0 x3D, 0 xFF, 0 x73, 0 x0F, 0 x01, 0 x27, 0 x8E, 0 xA6, 0 x1C,
],
}),
},
}),
extensions: Default::default(),
},
att_stmt: AttestationStatement::Packed(AttestationStatementPacked {
alg: COSEAlgorithm::ES256,
sig: Signature(vec![
0 x30, 0 x45, 0 x02, 0 x20, 0 x13, 0 xf7, 0 x3c, 0 x5d, 0 x9d, 0 x53, 0 x0e, 0 x8c, 0 xc1,
0 x5c, 0 xc9, 0 xbd, 0 x96, 0 xad, 0 x58, 0 x6d, 0 x39, 0 x36, 0 x64, 0 xe4, 0 x62, 0 xd5,
0 xf0, 0 x56, 0 x12, 0 x35, 0 xe6, 0 x35, 0 x0f, 0 x2b, 0 x72, 0 x89, 0 x02, 0 x21, 0 x00,
0 x90, 0 x35, 0 x7f, 0 xf9, 0 x10, 0 xcc, 0 xb5, 0 x6a, 0 xc5, 0 xb5, 0 x96, 0 x51, 0 x19,
0 x48, 0 x58, 0 x1c, 0 x8f, 0 xdd, 0 xb4, 0 xa2, 0 xb7, 0 x99, 0 x59, 0 x94, 0 x80, 0 x78,
0 xb0, 0 x9f, 0 x4b, 0 xdc, 0 x62, 0 x29,
]),
attestation_cert: vec![AttestationCertificate(vec![
0 x30, 0 x82, 0 x01, 0 x93, 0 x30, 0 x82, 0 x01, 0 x38, 0 xa0, 0 x03, 0 x02, 0 x01, 0 x02,
0 x02, 0 x09, 0 x00, 0 x85, 0 x9b, 0 x72, 0 x6c, 0 xb2, 0 x4b, 0 x4c, 0 x29, 0 x30, 0 x0a,
0 x06, 0 x08, 0 x2a, 0 x86, 0 x48, 0 xce, 0 x3d, 0 x04, 0 x03, 0 x02, 0 x30, 0 x47, 0 x31,
0 x0b, 0 x30, 0 x09, 0 x06, 0 x03, 0 x55, 0 x04, 0 x06, 0 x13, 0 x02, 0 x55, 0 x53, 0 x31,
0 x14, 0 x30, 0 x12, 0 x06, 0 x03, 0 x55, 0 x04, 0 x0a, 0 x0c, 0 x0b, 0 x59, 0 x75, 0 x62,
0 x69, 0 x63, 0 x6f, 0 x20, 0 x54, 0 x65, 0 x73, 0 x74, 0 x31, 0 x22, 0 x30, 0 x20, 0 x06,
0 x03, 0 x55, 0 x04, 0 x0b, 0 x0c, 0 x19, 0 x41, 0 x75, 0 x74, 0 x68, 0 x65, 0 x6e, 0 x74,
0 x69, 0 x63, 0 x61, 0 x74, 0 x6f, 0 x72, 0 x20, 0 x41, 0 x74, 0 x74, 0 x65, 0 x73, 0 x74,
0 x61, 0 x74, 0 x69, 0 x6f, 0 x6e, 0 x30, 0 x1e, 0 x17, 0 x0d, 0 x31, 0 x36, 0 x31, 0 x32,
0 x30, 0 x34, 0 x31, 0 x31, 0 x35, 0 x35, 0 x30, 0 x30, 0 x5a, 0 x17, 0 x0d, 0 x32, 0 x36,
0 x31, 0 x32, 0 x30, 0 x32, 0 x31, 0 x31, 0 x35, 0 x35, 0 x30, 0 x30, 0 x5a, 0 x30, 0 x47,
0 x31, 0 x0b, 0 x30, 0 x09, 0 x06, 0 x03, 0 x55, 0 x04, 0 x06, 0 x13, 0 x02, 0 x55, 0 x53,
0 x31, 0 x14, 0 x30, 0 x12, 0 x06, 0 x03, 0 x55, 0 x04, 0 x0a, 0 x0c, 0 x0b, 0 x59, 0 x75,
0 x62, 0 x69, 0 x63, 0 x6f, 0 x20, 0 x54, 0 x65, 0 x73, 0 x74, 0 x31, 0 x22, 0 x30, 0 x20,
0 x06, 0 x03, 0 x55, 0 x04, 0 x0b, 0 x0c, 0 x19, 0 x41, 0 x75, 0 x74, 0 x68, 0 x65, 0 x6e,
0 x74, 0 x69, 0 x63, 0 x61, 0 x74, 0 x6f, 0 x72, 0 x20, 0 x41, 0 x74, 0 x74, 0 x65, 0 x73,
0 x74, 0 x61, 0 x74, 0 x69, 0 x6f, 0 x6e, 0 x30, 0 x59, 0 x30, 0 x13, 0 x06, 0 x07, 0 x2a,
0 x86, 0 x48, 0 xce, 0 x3d, 0 x02, 0 x01, 0 x06, 0 x08, 0 x2a, 0 x86, 0 x48, 0 xce, 0 x3d,
0 x03, 0 x01, 0 x07, 0 x03, 0 x42, 0 x00, 0 x04, 0 xad, 0 x11, 0 xeb, 0 x0e, 0 x88, 0 x52,
0 xe5, 0 x3a, 0 xd5, 0 xdf, 0 xed, 0 x86, 0 xb4, 0 x1e, 0 x61, 0 x34, 0 xa1, 0 x8e, 0 xc4,
0 xe1, 0 xaf, 0 x8f, 0 x22, 0 x1a, 0 x3c, 0 x7d, 0 x6e, 0 x63, 0 x6c, 0 x80, 0 xea, 0 x13,
0 xc3, 0 xd5, 0 x04, 0 xff, 0 x2e, 0 x76, 0 x21, 0 x1b, 0 xb4, 0 x45, 0 x25, 0 xb1, 0 x96,
0 xc4, 0 x4c, 0 xb4, 0 x84, 0 x99, 0 x79, 0 xcf, 0 x6f, 0 x89, 0 x6e, 0 xcd, 0 x2b, 0 xb8,
0 x60, 0 xde, 0 x1b, 0 xf4, 0 x37, 0 x6b, 0 xa3, 0 x0d, 0 x30, 0 x0b, 0 x30, 0 x09, 0 x06,
0 x03, 0 x55, 0 x1d, 0 x13, 0 x04, 0 x02, 0 x30, 0 x00, 0 x30, 0 x0a, 0 x06, 0 x08, 0 x2a,
0 x86, 0 x48, 0 xce, 0 x3d, 0 x04, 0 x03, 0 x02, 0 x03, 0 x49, 0 x00, 0 x30, 0 x46, 0 x02,
0 x21, 0 x00, 0 xe9, 0 xa3, 0 x9f, 0 x1b, 0 x03, 0 x19, 0 x75, 0 x25, 0 xf7, 0 x37, 0 x3e,
0 x10, 0 xce, 0 x77, 0 xe7, 0 x80, 0 x21, 0 x73, 0 x1b, 0 x94, 0 xd0, 0 xc0, 0 x3f, 0 x3f,
0 xda, 0 x1f, 0 xd2, 0 x2d, 0 xb3, 0 xd0, 0 x30, 0 xe7, 0 x02, 0 x21, 0 x00, 0 xc4, 0 xfa,
0 xec, 0 x34, 0 x45, 0 xa8, 0 x20, 0 xcf, 0 x43, 0 x12, 0 x9c, 0 xdb, 0 x00, 0 xaa, 0 xbe,
0 xfd, 0 x9a, 0 xe2, 0 xd8, 0 x74, 0 xf9, 0 xc5, 0 xd3, 0 x43, 0 xcb, 0 x2f, 0 x11, 0 x3d,
0 xa2, 0 x37, 0 x23, 0 xf3,
])],
}),
}
}
#[ test]
fn parse_cert_chain() {
let cert: AttestationCertificate = from_slice(&SAMPLE_CERT_CHAIN[1 ..]).unwrap();
assert_eq!(&cert.0 , &SAMPLE_CERT_CHAIN[4 ..]);
let _cert: Vec<AttestationCertificate> = from_slice(&SAMPLE_CERT_CHAIN).unwrap();
}
#[ test]
fn parse_attestation_statement() {
let actual: AttestationStatement = from_slice(&SAMPLE_ATTESTATION_STMT_NONE).unwrap();
let expected = AttestationStatement::None;
assert_eq!(expected, actual);
let actual: AttestationStatement = from_slice(&SAMPLE_ATTESTATION_STMT_FIDO_U2F).unwrap();
let expected = AttestationStatement::FidoU2F(AttestationStatementFidoU2F {
attestation_cert: vec![AttestationCertificate(vec![
0 x30, 0 x82, 0 x02, 0 xd9, 0 x30, 0 x82, 0 x01, 0 xc1, 0 xa0, 0 x03, 0 x02, 0 x01, 0 x02, 0 x02,
0 x09, 0 x00, 0 xdf, 0 x92, 0 xd9, 0 xc4, 0 xe2, 0 xed, 0 x66, 0 x0a, 0 x30, 0 x0d, 0 x06, 0 x09,
0 x2a, 0 x86, 0 x48, 0 x86, 0 xf7, 0 x0d, 0 x01, 0 x01, 0 x0b, 0 x05, 0 x00, 0 x30, 0 x2e, 0 x31,
0 x2c, 0 x30, 0 x2a, 0 x06, 0 x03, 0 x55, 0 x04, 0 x03, 0 x13, 0 x23, 0 x59, 0 x75, 0 x62, 0 x69,
0 x63, 0 x6f, 0 x20, 0 x55, 0 x32, 0 x46, 0 x20, 0 x52, 0 x6f, 0 x6f, 0 x74, 0 x20, 0 x43, 0 x41,
0 x20, 0 x53, 0 x65, 0 x72, 0 x69, 0 x61, 0 x6c, 0 x20, 0 x34, 0 x35, 0 x37, 0 x32, 0 x30, 0 x30,
0 x36, 0 x33, 0 x31, 0 x30, 0 x20, 0 x17, 0 x0d, 0 x31, 0 x34, 0 x30, 0 x38, 0 x30, 0 x31, 0 x30,
0 x30, 0 x30, 0 x30, 0 x30, 0 x30, 0 x5a, 0 x18, 0 x0f, 0 x32, 0 x30, 0 x35, 0 x30, 0 x30, 0 x39,
0 x30, 0 x34, 0 x30, 0 x30, 0 x30, 0 x30, 0 x30, 0 x30, 0 x5a, 0 x30, 0 x6f, 0 x31, 0 x0b, 0 x30,
0 x09, 0 x06, 0 x03, 0 x55, 0 x04, 0 x06, 0 x13, 0 x02, 0 x53, 0 x45, 0 x31, 0 x12, 0 x30, 0 x10,
0 x06, 0 x03, 0 x55, 0 x04, 0 x0a, 0 x0c, 0 x09, 0 x59, 0 x75, 0 x62, 0 x69, 0 x63, 0 x6f, 0 x20,
0 x41, 0 x42, 0 x31, 0 x22, 0 x30, 0 x20, 0 x06, 0 x03, 0 x55, 0 x04, 0 x0b, 0 x0c, 0 x19, 0 x41,
0 x75, 0 x74, 0 x68, 0 x65, 0 x6e, 0 x74, 0 x69, 0 x63, 0 x61, 0 x74, 0 x6f, 0 x72, 0 x20, 0 x41,
0 x74, 0 x74, 0 x65, 0 x73, 0 x74, 0 x61, 0 x74, 0 x69, 0 x6f, 0 x6e, 0 x31, 0 x28, 0 x30, 0 x26,
0 x06, 0 x03, 0 x55, 0 x04, 0 x03, 0 x0c, 0 x1f, 0 x59, 0 x75, 0 x62, 0 x69, 0 x63, 0 x6f, 0 x20,
0 x55, 0 x32, 0 x46, 0 x20, 0 x45, 0 x45, 0 x20, 0 x53, 0 x65, 0 x72, 0 x69, 0 x61, 0 x6c, 0 x20,
0 x31, 0 x31, 0 x35, 0 x35, 0 x31, 0 x30, 0 x39, 0 x35, 0 x39, 0 x39, 0 x30, 0 x59, 0 x30, 0 x13,
0 x06, 0 x07, 0 x2a, 0 x86, 0 x48, 0 xce, 0 x3d, 0 x02, 0 x01, 0 x06, 0 x08, 0 x2a, 0 x86, 0 x48,
0 xce, 0 x3d, 0 x03, 0 x01, 0 x07, 0 x03, 0 x42, 0 x00, 0 x04, 0 x0a, 0 x18, 0 x6c, 0 x6e, 0 x4d,
0 x0a, 0 x6a, 0 x52, 0 x8a, 0 x44, 0 x90, 0 x9a, 0 x7a, 0 x24, 0 x23, 0 x68, 0 x70, 0 x28, 0 xd4,
0 xc5, 0 x7e, 0 xcc, 0 xb7, 0 x17, 0 xba, 0 x12, 0 x80, 0 xb8, 0 x5c, 0 x2f, 0 xc1, 0 xe4, 0 xe0,
0 x61, 0 x66, 0 x8c, 0 x3c, 0 x20, 0 xae, 0 xf3, 0 x33, 0 x50, 0 xd1, 0 x96, 0 x45, 0 x23, 0 x8a,
0 x2c, 0 x39, 0 x0b, 0 xf5, 0 xdf, 0 xfa, 0 x34, 0 xff, 0 x25, 0 x50, 0 x2f, 0 x47, 0 x0f, 0 x3d,
0 x40, 0 xb8, 0 x88, 0 xa3, 0 x81, 0 x81, 0 x30, 0 x7f, 0 x30, 0 x13, 0 x06, 0 x0a, 0 x2b, 0 x06,
0 x01, 0 x04, 0 x01, 0 x82, 0 xc4, 0 x0a, 0 x0d, 0 x01, 0 x04, 0 x05, 0 x04, 0 x03, 0 x05, 0 x04,
0 x03, 0 x30, 0 x22, 0 x06, 0 x09, 0 x2b, 0 x06, 0 x01, 0 x04, 0 x01, 0 x82, 0 xc4, 0 x0a, 0 x02,
0 x04, 0 x15, 0 x31, 0 x2e, 0 x33, 0 x2e, 0 x36, 0 x2e, 0 x31, 0 x2e, 0 x34, 0 x2e, 0 x31, 0 x2e,
0 x34, 0 x31, 0 x34, 0 x38, 0 x32, 0 x2e, 0 x31, 0 x2e, 0 x37, 0 x30, 0 x13, 0 x06, 0 x0b, 0 x2b,
0 x06, 0 x01, 0 x04, 0 x01, 0 x82, 0 xe5, 0 x1c, 0 x02, 0 x01, 0 x01, 0 x04, 0 x04, 0 x03, 0 x02,
0 x04, 0 x30, 0 x30, 0 x21, 0 x06, 0 x0b, 0 x2b, 0 x06, 0 x01, 0 x04, 0 x01, 0 x82, 0 xe5, 0 x1c,
0 x01, 0 x01, 0 x04, 0 x04, 0 x12, 0 x04, 0 x10, 0 x2f, 0 xc0, 0 x57, 0 x9f, 0 x81, 0 x13, 0 x47,
0 xea, 0 xb1, 0 x16, 0 xbb, 0 x5a, 0 x8d, 0 xb9, 0 x20, 0 x2a, 0 x30, 0 x0c, 0 x06, 0 x03, 0 x55,
0 x1d, 0 x13, 0 x01, 0 x01, 0 xff, 0 x04, 0 x02, 0 x30, 0 x00, 0 x30, 0 x0d, 0 x06, 0 x09, 0 x2a,
0 x86, 0 x48, 0 x86, 0 xf7, 0 x0d, 0 x01, 0 x01, 0 x0b, 0 x05, 0 x00, 0 x03, 0 x82, 0 x01, 0 x01,
0 x00, 0 x82, 0 xac, 0 xaf, 0 x11, 0 x30, 0 xa9, 0 x9b, 0 xd1, 0 x43, 0 x27, 0 xd2, 0 xf8, 0 xf9,
0 xb0, 0 x41, 0 xa2, 0 xa0, 0 x4a, 0 x66, 0 x85, 0 x27, 0 x24, 0 x22, 0 xe5, 0 x7b, 0 x14, 0 xb0,
0 xb8, 0 xf8, 0 x3b, 0 x6f, 0 x15, 0 x45, 0 x66, 0 x4b, 0 xbf, 0 x55, 0 x68, 0 x1e, 0 xaf, 0 x01,
0 x58, 0 x72, 0 x2a, 0 xbf, 0 xce, 0 xd2, 0 xe4, 0 xac, 0 x63, 0 x3c, 0 xec, 0 x09, 0 x59, 0 x56,
0 x45, 0 x24, 0 xb0, 0 xf2, 0 xe5, 0 x17, 0 xdd, 0 x97, 0 x10, 0 x98, 0 xb9, 0 x89, 0 x15, 0 x17,
0 xec, 0 xd0, 0 xc5, 0 x53, 0 xa2, 0 xe4, 0 x73, 0 x9f, 0 x9d, 0 xe1, 0 x3d, 0 xaf, 0 xd0, 0 xd5,
0 xd7, 0 xb8, 0 xac, 0 x4a, 0 x37, 0 xf4, 0 xf2, 0 xcc, 0 x30, 0 xef, 0 x25, 0 xcb, 0 x00, 0 x65,
0 x2d, 0 x19, 0 xdb, 0 x69, 0 xd7, 0 xda, 0 x57, 0 xbd, 0 x1a, 0 x9c, 0 x1d, 0 x8e, 0 xd8, 0 x7d,
0 x46, 0 xd8, 0 x0d, 0 x2b, 0 x3b, 0 xdf, 0 xd1, 0 xd9, 0 xef, 0 x9d, 0 x2b, 0 x68, 0 x32, 0 xd4,
0 xad, 0 x5b, 0 xcd, 0 x74, 0 x21, 0 x4c, 0 xe6, 0 xa6, 0 x14, 0 x1d, 0 x16, 0 xb2, 0 xe9, 0 x3a,
0 xcb, 0 x2c, 0 x88, 0 xf6, 0 x0a, 0 x3e, 0 xb6, 0 xd5, 0 xf6, 0 x14, 0 x71, 0 x97, 0 x59, 0 x09,
0 x37, 0 x3b, 0 xc6, 0 x77, 0 x90, 0 x23, 0 x24, 0 x57, 0 x1a, 0 x57, 0 x3f, 0 x60, 0 xf0, 0 x7b,
0 xbe, 0 xd1, 0 x7b, 0 x92, 0 xc8, 0 xb5, 0 x9f, 0 xa2, 0 x82, 0 x10, 0 xbf, 0 xa8, 0 xc6, 0 x01,
0 x22, 0 x93, 0 x00, 0 x1b, 0 x39, 0 xef, 0 xe5, 0 x7b, 0 xf9, 0 xcb, 0 x1e, 0 x3a, 0 xca, 0 x8a,
0 x41, 0 x30, 0 xf8, 0 x3a, 0 xf8, 0 x66, 0 x8f, 0 x73, 0 xde, 0 xf2, 0 x71, 0 x1b, 0 x20, 0 xdc,
0 x99, 0 xe8, 0 xa8, 0 x04, 0 xee, 0 xa3, 0 xf7, 0 x42, 0 x71, 0 x97, 0 xb6, 0 xb4, 0 x51, 0 xb3,
0 x73, 0 x5c, 0 x23, 0 xbc, 0 x9b, 0 x1b, 0 xe2, 0 x74, 0 xc2, 0 x6d, 0 x3b, 0 xf9, 0 x19, 0 x6f,
0 x8c, 0 x4a, 0 x4b, 0 x71, 0 x5f, 0 x4b, 0 x95, 0 xc4, 0 xdb, 0 x7b, 0 x97, 0 xe7, 0 x59, 0 x4e,
0 xb4, 0 x65, 0 x64, 0 x8c, 0 x1c,
])],
sig: Signature(vec![
0 x30, 0 x44, 0 x02, 0 x20, 0 x48, 0 x5a, 0 x72, 0 x40, 0 xdf, 0 x2c, 0 x1e, 0 x31, 0 xa5, 0 xb3,
0 x0b, 0 x3b, 0 x2c, 0 xd1, 0 xad, 0 xd0, 0 x8d, 0 xae, 0 x8d, 0 x7a, 0 x25, 0 x3e, 0 xf5, 0 xa6,
0 x25, 0 xdb, 0 x2e, 0 x22, 0 x1b, 0 x71, 0 xe5, 0 x78, 0 x02, 0 x20, 0 x45, 0 xbd, 0 xdc, 0 x30,
0 xde, 0 xf4, 0 x05, 0 x97, 0 x5c, 0 xac, 0 x72, 0 x58, 0 x96, 0 xa6, 0 x00, 0 x94, 0 x57, 0 x3a,
0 xa5, 0 xe8, 0 x1e, 0 xf4, 0 xfd, 0 x30, 0 xd3, 0 x88, 0 x11, 0 x8b, 0 x49, 0 x97, 0 xdf, 0 x34,
]),
});
assert_eq!(expected, actual);
}
#[ test]
fn parse_attestation_object() {
let actual: AttestationObject = from_slice(&SAMPLE_ATTESTATION_OBJ_PACKED).unwrap();
let expected = create_attestation_obj();
assert_eq!(expected, actual);
}
#[ test]
fn test_anonymize_att_obj() {
// Anonymize should prevent identifying data in the attestation statement from being
// serialized.
let mut att_obj = create_attestation_obj();
// This test assumes that the sample attestation object contains identifying information
assert_ne!(att_obj.att_stmt, AttestationStatement::None);
assert_ne!(
att_obj
.auth_data
.credential_data
.as_ref()
.expect("credential_data should be Some" )
.aaguid,
AAGuid::default()
);
att_obj.anonymize();
// Write the attestation object out to bytes and read it back. The result should not
// have an attestation statement, and it should have the default AAGUID.
let encoded_att_obj = to_vec(&att_obj).expect("could not serialize anonymized att_obj" );
let att_obj: AttestationObject =
from_slice(&encoded_att_obj).expect("could not deserialize anonymized att_obj" );
assert_eq!(att_obj.att_stmt, AttestationStatement::None);
assert_eq!(
att_obj
.auth_data
.credential_data
.as_ref()
.expect("credential_data should be Some" )
.aaguid,
AAGuid::default()
);
}
#[ test]
fn parse_reader() {
let v: Vec<u8> = vec![
0 x66, 0 x66, 0 x6f, 0 x6f, 0 x62, 0 x61, 0 x72, 0 x66, 0 x66, 0 x6f, 0 x6f, 0 x62, 0 x61, 0 x72,
];
let mut data = Cursor::new(v);
let value: String = from_slice_stream::<_, _, serde_cbor::Error>(&mut data).unwrap();
assert_eq!(value, "foobar" );
let mut remaining = Vec::new();
data.read_to_end(&mut remaining).unwrap();
assert_eq!(
remaining.as_slice(),
&[0 x66, 0 x66, 0 x6f, 0 x6f, 0 x62, 0 x61, 0 x72]
);
let mut data = Cursor::new(remaining);
let value: String = from_slice_stream::<_, _, serde_cbor::Error>(&mut data).unwrap();
assert_eq!(value, "foobar" );
let mut remaining = Vec::new();
data.read_to_end(&mut remaining).unwrap();
assert!(remaining.is_empty());
}
#[ test]
fn parse_extensions() {
let auth_make: AuthenticatorData = from_slice(&SAMPLE_AUTH_DATA_MAKE_CREDENTIAL).unwrap();
assert_eq!(
auth_make.extensions.hmac_secret,
Some(HmacSecretResponse::Confirmed(true ))
);
let auth_get: AuthenticatorData = from_slice(&SAMPLE_AUTH_DATA_GET_ASSERTION).unwrap();
assert_eq!(
auth_get.extensions.hmac_secret,
Some(HmacSecretResponse::Secret(vec![
0 x1F, 0 x91, 0 x52, 0 x6C, 0 xAE, 0 x45, 0 x6E, 0 x4C, 0 xBB, 0 x71, 0 xC4, 0 xDD, 0 xE7, 0 xBB,
0 x87, 0 x71, 0 x57, 0 xE6, 0 xE5, 0 x4D, 0 xFE, 0 xD3, 0 x01, 0 x5D, 0 x7D, 0 x4D, 0 xBB, 0 x22,
0 x69, 0 xAF, 0 xCD, 0 xE6, 0 xA9, 0 x1B, 0 x8D, 0 x26, 0 x7E, 0 xBB, 0 xF8, 0 x48, 0 xEB, 0 x95,
0 xA6, 0 x8E, 0 x79, 0 xC7, 0 xAC, 0 x70, 0 x5E, 0 x35, 0 x1D, 0 x54, 0 x3D, 0 xB0, 0 x16, 0 x58,
0 x87, 0 xD6, 0 x29, 0 x0F, 0 xD4, 0 x7A, 0 x40, 0 xC4,
]))
);
}
#[ test]
fn test_empty_extension_data() {
let mut parsed_auth_data: AuthenticatorData =
from_slice(&SAMPLE_AUTH_DATA_MAKE_CREDENTIAL).unwrap();
assert!(parsed_auth_data
.flags
.contains(AuthenticatorDataFlags::EXTENSION_DATA));
// Remove the extension data but keep the extension data flag set.
parsed_auth_data.extensions = Default::default();
let with_flag = to_vec(&parsed_auth_data).expect("could not serialize auth data" );
// The serialized auth data should end with an empty map (CBOR 0xA0).
assert_eq!(with_flag[with_flag.len() - 1 ], 0 xA0);
// Remove the extension data flag.
parsed_auth_data
.flags
.remove(AuthenticatorDataFlags::EXTENSION_DATA);
let without_flag = to_vec(&parsed_auth_data).expect("could not serialize auth data" );
// The serialized auth data should be one byte shorter.
assert!(with_flag.len() == without_flag.len() + 1 );
}
/// See: https://github.com/mozilla/authenticator-rs/issues/187
#[ test]
fn test_aaguid_output() {
let input = [
0 xcb, 0 x69, 0 x48, 0 x1e, 0 x8f, 0 xf0, 0 x00, 0 x39, 0 x93, 0 xec, 0 x0a, 0 x27, 0 x29, 0 xa1,
0 x54, 0 xa8,
];
let expected = "AAGuid(cb69481e-8ff0-0039-93ec-0a2729a154a8)" ;
let result = AAGuid::from(&input).expect("Failed to parse AAGuid" );
let res_str = format!("{result:?}" );
assert_eq!(expected, &res_str);
}
#[ test]
fn test_ad_flags_from_bits() {
// Check that AuthenticatorDataFlags is defined on the entire u8 range and that
// `from_bits_truncate` is lossless
for x in 0 ..=u8::MAX {
assert_eq!(
AuthenticatorDataFlags::from_bits(x),
Some(AuthenticatorDataFlags::from_bits_truncate(x))
);
}
}
#[ test]
fn serialize_att_obj_none() -> Result<(), serde_cbor::Error> {
let att_stmt = AttestationStatement::None;
// Python: from fido2.cbor import encode; print(f"[{", ".join(f'0x{b:02x}' for b in encode({}))}]")
assert_eq!(serde_cbor::ser::to_vec(&att_stmt)?, [0 xa0]);
let att_obj = AttestationObject {
auth_data: serde_cbor::de::from_slice(&SAMPLE_AUTH_DATA_MAKE_CREDENTIAL)?,
att_stmt,
};
assert_eq!(
serde_cbor::ser::to_vec(&att_obj)?,
// Python: from fido2.cbor import encode; print(f"[{", ".join(f'0x{b:02x}' for b in encode({"fmt": "none", "attStmt": {}, "authData": SAMPLE_AUTH_DATA_MAKE_CREDENTIAL[2:] }))}]")
[
0 xa3, 0 x63, 0 x66, 0 x6d, 0 x74, 0 x64, 0 x6e, 0 x6f, 0 x6e, 0 x65, 0 x67, 0 x61, 0 x74, 0 x74,
0 x53, 0 x74, 0 x6d, 0 x74, 0 xa0, 0 x68, 0 x61, 0 x75, 0 x74, 0 x68, 0 x44, 0 x61, 0 x74, 0 x61,
0 x58, 0 xa2, 0 xc2, 0 x89, 0 xc5, 0 xca, 0 x9b, 0 x04, 0 x60, 0 xf9, 0 x34, 0 x6a, 0 xb4, 0 xe4,
0 x2d, 0 x84, 0 x27, 0 x43, 0 x40, 0 x4d, 0 x31, 0 xf4, 0 x84, 0 x68, 0 x25, 0 xa6, 0 xd0, 0 x65,
0 xbe, 0 x59, 0 x7a, 0 x87, 0 x05, 0 x1d, 0 xc1, 0 x00, 0 x00, 0 x00, 0 x0b, 0 xf8, 0 xa0, 0 x11,
0 xf3, 0 x8c, 0 x0a, 0 x4d, 0 x15, 0 x80, 0 x06, 0 x17, 0 x11, 0 x1f, 0 x9e, 0 xdc, 0 x7d, 0 x00,
0 x10, 0 x89, 0 x59, 0 xce, 0 xad, 0 x5b, 0 x5c, 0 x48, 0 x16, 0 x4e, 0 x8a, 0 xbc, 0 xd6, 0 xd9,
0 x43, 0 x5c, 0 x6f, 0 xa5, 0 x01, 0 x02, 0 x03, 0 x26, 0 x20, 0 x01, 0 x21, 0 x58, 0 x20, 0 xa5,
0 xfd, 0 x5c, 0 xe1, 0 xb1, 0 xc4, 0 x58, 0 xc5, 0 x30, 0 xa5, 0 x4f, 0 xa6, 0 x1b, 0 x31, 0 xbf,
0 x6b, 0 x04, 0 xbe, 0 x8b, 0 x97, 0 xaf, 0 xde, 0 x54, 0 xdd, 0 x8c, 0 xbb, 0 x69, 0 x27, 0 x5a,
0 x8a, 0 x1b, 0 xe1, 0 x22, 0 x58, 0 x20, 0 xfa, 0 x3a, 0 x32, 0 x31, 0 xdd, 0 x9d, 0 xee, 0 xd9,
0 xd1, 0 x89, 0 x7b, 0 xe5, 0 xa6, 0 x22, 0 x8c, 0 x59, 0 x50, 0 x1e, 0 x4b, 0 xcd, 0 x12, 0 x97,
0 x5d, 0 x3d, 0 xff, 0 x73, 0 x0f, 0 x01, 0 x27, 0 x8e, 0 xa6, 0 x1c, 0 xa1, 0 x6b, 0 x68, 0 x6d,
0 x61, 0 x63, 0 x2d, 0 x73, 0 x65, 0 x63, 0 x72, 0 x65, 0 x74, 0 xf5
]
);
Ok(())
}
#[ test]
fn serialize_att_obj_packed() -> Result<(), serde_cbor::Error> {
let att_stmt = AttestationStatement::Packed(AttestationStatementPacked {
alg: COSEAlgorithm::ES256,
sig: Signature(vec![1 , 2 , 3 , 4 ]),
attestation_cert: vec![
AttestationCertificate(vec![5 , 6 , 7 , 8 ]),
AttestationCertificate(vec![9 , 10 , 11 , 12 ]),
],
});
assert_eq!(
serde_cbor::ser::to_vec(&att_stmt)?,
// Python: from fido2.cbor import encode; print(f"[{", ".join(f'0x{b:02x}' for b in encode({"alg":-7, "sig":bytes([1, 2, 3, 4]), "x5c": [bytes([5, 6, 7, 8]), bytes([9, 10, 11, 12])] }))}]")
[
0 xa3, 0 x63, 0 x61, 0 x6c, 0 x67, 0 x26, 0 x63, 0 x73, 0 x69, 0 x67, 0 x44, 0 x01, 0 x02, 0 x03,
0 x04, 0 x63, 0 x78, 0 x35, 0 x63, 0 x82, 0 x44, 0 x05, 0 x06, 0 x07, 0 x08, 0 x44, 0 x09, 0 x0a,
0 x0b, 0 x0c
]
);
let att_obj = AttestationObject {
auth_data: serde_cbor::de::from_slice(&SAMPLE_AUTH_DATA_MAKE_CREDENTIAL)?,
att_stmt,
};
assert_eq!(
serde_cbor::ser::to_vec(&att_obj)?,
// Python: from fido2.cbor import encode; print(f"[{", ".join(f'0x{b:02x}' for b in encode({"fmt": "packed", "attStmt": {"alg":-7, "sig":bytes([1, 2, 3, 4]), "x5c": [bytes([5, 6, 7, 8]), bytes([9, 10, 11, 12])] }, "authData": SAMPLE_AUTH_DATA_MAKE_CREDENTIAL[2:] }))}]")
[
0 xa3, 0 x63, 0 x66, 0 x6d, 0 x74, 0 x66, 0 x70, 0 x61, 0 x63, 0 x6b, 0 x65, 0 x64, 0 x67, 0 x61,
0 x74, 0 x74, 0 x53, 0 x74, 0 x6d, 0 x74, 0 xa3, 0 x63, 0 x61, 0 x6c, 0 x67, 0 x26, 0 x63, 0 x73,
0 x69, 0 x67, 0 x44, 0 x01, 0 x02, 0 x03, 0 x04, 0 x63, 0 x78, 0 x35, 0 x63, 0 x82, 0 x44, 0 x05,
0 x06, 0 x07, 0 x08, 0 x44, 0 x09, 0 x0a, 0 x0b, 0 x0c, 0 x68, 0 x61, 0 x75, 0 x74, 0 x68, 0 x44,
0 x61, 0 x74, 0 x61, 0 x58, 0 xa2, 0 xc2, 0 x89, 0 xc5, 0 xca, 0 x9b, 0 x04, 0 x60, 0 xf9, 0 x34,
0 x6a, 0 xb4, 0 xe4, 0 x2d, 0 x84, 0 x27, 0 x43, 0 x40, 0 x4d, 0 x31, 0 xf4, 0 x84, 0 x68, 0 x25,
0 xa6, 0 xd0, 0 x65, 0 xbe, 0 x59, 0 x7a, 0 x87, 0 x05, 0 x1d, 0 xc1, 0 x00, 0 x00, 0 x00, 0 x0b,
0 xf8, 0 xa0, 0 x11, 0 xf3, 0 x8c, 0 x0a, 0 x4d, 0 x15, 0 x80, 0 x06, 0 x17, 0 x11, 0 x1f, 0 x9e,
0 xdc, 0 x7d, 0 x00, 0 x10, 0 x89, 0 x59, 0 xce, 0 xad, 0 x5b, 0 x5c, 0 x48, 0 x16, 0 x4e, 0 x8a,
0 xbc, 0 xd6, 0 xd9, 0 x43, 0 x5c, 0 x6f, 0 xa5, 0 x01, 0 x02, 0 x03, 0 x26, 0 x20, 0 x01, 0 x21,
0 x58, 0 x20, 0 xa5, 0 xfd, 0 x5c, 0 xe1, 0 xb1, 0 xc4, 0 x58, 0 xc5, 0 x30, 0 xa5, 0 x4f, 0 xa6,
0 x1b, 0 x31, 0 xbf, 0 x6b, 0 x04, 0 xbe, 0 x8b, 0 x97, 0 xaf, 0 xde, 0 x54, 0 xdd, 0 x8c, 0 xbb,
0 x69, 0 x27, 0 x5a, 0 x8a, 0 x1b, 0 xe1, 0 x22, 0 x58, 0 x20, 0 xfa, 0 x3a, 0 x32, 0 x31, 0 xdd,
0 x9d, 0 xee, 0 xd9, 0 xd1, 0 x89, 0 x7b, 0 xe5, 0 xa6, 0 x22, 0 x8c, 0 x59, 0 x50, 0 x1e, 0 x4b,
0 xcd, 0 x12, 0 x97, 0 x5d, 0 x3d, 0 xff, 0 x73, 0 x0f, 0 x01, 0 x27, 0 x8e, 0 xa6, 0 x1c, 0 xa1,
0 x6b, 0 x68, 0 x6d, 0 x61, 0 x63, 0 x2d, 0 x73, 0 x65, 0 x63, 0 x72, 0 x65, 0 x74, 0 xf5
]
);
Ok(())
}
#[ test]
fn serialize_att_obj_fido_u2f() -> Result<(), serde_cbor::Error> {
let att_stmt = AttestationStatement::FidoU2F(AttestationStatementFidoU2F {
sig: Signature(vec![1 , 2 , 3 , 4 ]),
attestation_cert: vec![
AttestationCertificate(vec![5 , 6 , 7 , 8 ]),
AttestationCertificate(vec![9 , 10 , 11 , 12 ]),
],
});
assert_eq!(
serde_cbor::ser::to_vec(&att_stmt)?,
// Python: from fido2.cbor import encode; print(f"[{", ".join(f'0x{b:02x}' for b in encode({"x5c": [bytes([5, 6, 7, 8]), bytes([9, 10, 11, 12])], "sig": bytes([1, 2, 3, 4]) }))}]")
[
0 xa2, 0 x63, 0 x73, 0 x69, 0 x67, 0 x44, 0 x01, 0 x02, 0 x03, 0 x04, 0 x63, 0 x78, 0 x35, 0 x63,
0 x82, 0 x44, 0 x05, 0 x06, 0 x07, 0 x08, 0 x44, 0 x09, 0 x0a, 0 x0b, 0 x0c
]
);
let att_obj = AttestationObject {
auth_data: serde_cbor::de::from_slice(&SAMPLE_AUTH_DATA_MAKE_CREDENTIAL)?,
att_stmt,
};
assert_eq!(
serde_cbor::ser::to_vec(&att_obj)?,
// Python: from fido2.cbor import encode; print(f"[{", ".join(f'0x{b:02x}' for b in encode({"fmt": "fido-u2f", "attStmt": {"x5c": [bytes([5, 6, 7, 8]), bytes([9, 10, 11, 12])], "sig": bytes([1, 2, 3, 4]) }, "authData": SAMPLE_AUTH_DATA_MAKE_CREDENTIAL[2:] }))}]")
[
0 xa3, 0 x63, 0 x66, 0 x6d, 0 x74, 0 x68, 0 x66, 0 x69, 0 x64, 0 x6f, 0 x2d, 0 x75, 0 x32, 0 x66,
0 x67, 0 x61, 0 x74, 0 x74, 0 x53, 0 x74, 0 x6d, 0 x74, 0 xa2, 0 x63, 0 x73, 0 x69, 0 x67, 0 x44,
0 x01, 0 x02, 0 x03, 0 x04, 0 x63, 0 x78, 0 x35, 0 x63, 0 x82, 0 x44, 0 x05, 0 x06, 0 x07, 0 x08,
0 x44, 0 x09, 0 x0a, 0 x0b, 0 x0c, 0 x68, 0 x61, 0 x75, 0 x74, 0 x68, 0 x44, 0 x61, 0 x74, 0 x61,
0 x58, 0 xa2, 0 xc2, 0 x89, 0 xc5, 0 xca, 0 x9b, 0 x04, 0 x60, 0 xf9, 0 x34, 0 x6a, 0 xb4, 0 xe4,
0 x2d, 0 x84, 0 x27, 0 x43, 0 x40, 0 x4d, 0 x31, 0 xf4, 0 x84, 0 x68, 0 x25, 0 xa6, 0 xd0, 0 x65,
0 xbe, 0 x59, 0 x7a, 0 x87, 0 x05, 0 x1d, 0 xc1, 0 x00, 0 x00, 0 x00, 0 x0b, 0 xf8, 0 xa0, 0 x11,
0 xf3, 0 x8c, 0 x0a, 0 x4d, 0 x15, 0 x80, 0 x06, 0 x17, 0 x11, 0 x1f, 0 x9e, 0 xdc, 0 x7d, 0 x00,
0 x10, 0 x89, 0 x59, 0 xce, 0 xad, 0 x5b, 0 x5c, 0 x48, 0 x16, 0 x4e, 0 x8a, 0 xbc, 0 xd6, 0 xd9,
0 x43, 0 x5c, 0 x6f, 0 xa5, 0 x01, 0 x02, 0 x03, 0 x26, 0 x20, 0 x01, 0 x21, 0 x58, 0 x20, 0 xa5,
0 xfd, 0 x5c, 0 xe1, 0 xb1, 0 xc4, 0 x58, 0 xc5, 0 x30, 0 xa5, 0 x4f, 0 xa6, 0 x1b, 0 x31, 0 xbf,
0 x6b, 0 x04, 0 xbe, 0 x8b, 0 x97, 0 xaf, 0 xde, 0 x54, 0 xdd, 0 x8c, 0 xbb, 0 x69, 0 x27, 0 x5a,
0 x8a, 0 x1b, 0 xe1, 0 x22, 0 x58, 0 x20, 0 xfa, 0 x3a, 0 x32, 0 x31, 0 xdd, 0 x9d, 0 xee, 0 xd9,
0 xd1, 0 x89, 0 x7b, 0 xe5, 0 xa6, 0 x22, 0 x8c, 0 x59, 0 x50, 0 x1e, 0 x4b, 0 xcd, 0 x12, 0 x97,
0 x5d, 0 x3d, 0 xff, 0 x73, 0 x0f, 0 x01, 0 x27, 0 x8e, 0 xa6, 0 x1c, 0 xa1, 0 x6b, 0 x68, 0 x6d,
0 x61, 0 x63, 0 x2d, 0 x73, 0 x65, 0 x63, 0 x72, 0 x65, 0 x74, 0 xf5
]
);
Ok(())
}
#[ test]
fn serialize_att_obj_android_key() -> Result<(), serde_cbor::Error> {
let att_stmt = AttestationStatement::AndroidKey(serde_cbor::Value::Map(
vec![
("alg" .to_string().into(), serde_cbor::Value::Integer(-7 )),
(
"sig" .to_string().into(),
serde_cbor::Value::Bytes(vec![1 , 2 , 3 , 4 ]),
),
(
"x5c" .to_string().into(),
serde_cbor::Value::Array(vec![
serde_cbor::Value::Bytes(vec![5 , 6 , 7 , 8 ]),
serde_cbor::Value::Bytes(vec![9 , 10 , 11 , 12 ]),
]),
),
]
.into_iter()
.collect(),
));
assert_eq!(
serde_cbor::ser::to_vec(&att_stmt)?,
// Python: from fido2.cbor import encode; print(f"[{", ".join(f'0x{b:02x}' for b in encode({"alg": -7, "sig": bytes([1, 2, 3, 4]), "x5c": [bytes([5, 6, 7, 8]), bytes([9, 10, 11, 12])] }))}]")
[
0 xa3, 0 x63, 0 x61, 0 x6c, 0 x67, 0 x26, 0 x63, 0 x73, 0 x69, 0 x67, 0 x44, 0 x01, 0 x02, 0 x03,
0 x04, 0 x63, 0 x78, 0 x35, 0 x63, 0 x82, 0 x44, 0 x05, 0 x06, 0 x07, 0 x08, 0 x44, 0 x09, 0 x0a,
0 x0b, 0 x0c
]
);
let att_obj = AttestationObject {
auth_data: serde_cbor::de::from_slice(&SAMPLE_AUTH_DATA_MAKE_CREDENTIAL)?,
att_stmt,
};
assert_eq!(
serde_cbor::ser::to_vec(&att_obj)?,
// Python: fido2.cbor import encode; print(f"[{", ".join(f'0x{b:02x}' for b in encode({ "fmt": "android-key", "attStmt": {"alg": -7, "sig": bytes([1, 2, 3, 4]), "x5c": [bytes([5, 6, 7, 8]), bytes([9, 10, 11, 12])] }, "authData": SAMPLE_AUTH_DATA_MAKE_CREDENTIAL[2:] }))}]")
[
0 xa3, 0 x63, 0 x66, 0 x6d, 0 x74, 0 x6b, 0 x61, 0 x6e, 0 x64, 0 x72, 0 x6f, 0 x69, 0 x64, 0 x2d,
0 x6b, 0 x65, 0 x79, 0 x67, 0 x61, 0 x74, 0 x74, 0 x53, 0 x74, 0 x6d, 0 x74, 0 xa3, 0 x63, 0 x61,
0 x6c, 0 x67, 0 x26, 0 x63, 0 x73, 0 x69, 0 x67, 0 x44, 0 x01, 0 x02, 0 x03, 0 x04, 0 x63, 0 x78,
0 x35, 0 x63, 0 x82, 0 x44, 0 x05, 0 x06, 0 x07, 0 x08, 0 x44, 0 x09, 0 x0a, 0 x0b, 0 x0c, 0 x68,
0 x61, 0 x75, 0 x74, 0 x68, 0 x44, 0 x61, 0 x74, 0 x61, 0 x58, 0 xa2, 0 xc2, 0 x89, 0 xc5, 0 xca,
0 x9b, 0 x04, 0 x60, 0 xf9, 0 x34, 0 x6a, 0 xb4, 0 xe4, 0 x2d, 0 x84, 0 x27, 0 x43, 0 x40, 0 x4d,
0 x31, 0 xf4, 0 x84, 0 x68, 0 x25, 0 xa6, 0 xd0, 0 x65, 0 xbe, 0 x59, 0 x7a, 0 x87, 0 x05, 0 x1d,
0 xc1, 0 x00, 0 x00, 0 x00, 0 x0b, 0 xf8, 0 xa0, 0 x11, 0 xf3, 0 x8c, 0 x0a, 0 x4d, 0 x15, 0 x80,
0 x06, 0 x17, 0 x11, 0 x1f, 0 x9e, 0 xdc, 0 x7d, 0 x00, 0 x10, 0 x89, 0 x59, 0 xce, 0 xad, 0 x5b,
0 x5c, 0 x48, 0 x16, 0 x4e, 0 x8a, 0 xbc, 0 xd6, 0 xd9, 0 x43, 0 x5c, 0 x6f, 0 xa5, 0 x01, 0 x02,
0 x03, 0 x26, 0 x20, 0 x01, 0 x21, 0 x58, 0 x20, 0 xa5, 0 xfd, 0 x5c, 0 xe1, 0 xb1, 0 xc4, 0 x58,
0 xc5, 0 x30, 0 xa5, 0 x4f, 0 xa6, 0 x1b, 0 x31, 0 xbf, 0 x6b, 0 x04, 0 xbe, 0 x8b, 0 x97, 0 xaf,
0 xde, 0 x54, 0 xdd, 0 x8c, 0 xbb, 0 x69, 0 x27, 0 x5a, 0 x8a, 0 x1b, 0 xe1, 0 x22, 0 x58, 0 x20,
0 xfa, 0 x3a, 0 x32, 0 x31, 0 xdd, 0 x9d, 0 xee, 0 xd9, 0 xd1, 0 x89, 0 x7b, 0 xe5, 0 xa6, 0 x22,
0 x8c, 0 x59, 0 x50, 0 x1e, 0 x4b, 0 xcd, 0 x12, 0 x97, 0 x5d, 0 x3d, 0 xff, 0 x73, 0 x0f, 0 x01,
0 x27, 0 x8e, 0 xa6, 0 x1c, 0 xa1, 0 x6b, 0 x68, 0 x6d, 0 x61, 0 x63, 0 x2d, 0 x73, 0 x65, 0 x63,
0 x72, 0 x65, 0 x74, 0 xf5
]
);
Ok(())
}
#[ test]
fn serialize_att_obj_android_safetynet() -> Result<(), serde_cbor::Error> {
let att_stmt = AttestationStatement::AndroidSafetyNet(serde_cbor::Value::Map(
vec![
(
"ver" .to_string().into(),
"Kom ihåg att du aldrig får snyta dig i mattan!"
.to_string()
.into(),
),
(
"response" .to_string().into(),
serde_cbor::Value::Bytes(vec![1 , 2 , 3 , 4 ]),
),
]
.into_iter()
.collect(),
));
assert_eq!(
serde_cbor::ser::to_vec(&att_stmt)?,
// Python: from fido2.cbor import encode; print(f"[{", ".join(f'0x{b:02x}' for b in encode({"ver": "Kom ihåg att du aldrig får snyta dig i mattan!", "response": bytes([1, 2, 3, 4]) }))}]")
[
0 xa2, 0 x63, 0 x76, 0 x65, 0 x72, 0 x78, 0 x30, 0 x4b, 0 x6f, 0 x6d, 0 x20, 0 x69, 0 x68, 0 xc3,
0 xa5, 0 x67, 0 x20, 0 x61, 0 x74, 0 x74, 0 x20, 0 x64, 0 x75, 0 x20, 0 x61, 0 x6c, 0 x64, 0 x72,
0 x69, 0 x67, 0 x20, 0 x66, 0 xc3, 0 xa5, 0 x72, 0 x20, 0 x73, 0 x6e, 0 x79, 0 x74, 0 x61, 0 x20,
0 x64, 0 x69, 0 x67, 0 x20, 0 x69, 0 x20, 0 x6d, 0 x61, 0 x74, 0 x74, 0 x61, 0 x6e, 0 x21, 0 x68,
0 x72, 0 x65, 0 x73, 0 x70, 0 x6f, 0 x6e, 0 x73, 0 x65, 0 x44, 0 x01, 0 x02, 0 x03, 0 x04
]
);
let att_obj = AttestationObject {
auth_data: serde_cbor::de::from_slice(&SAMPLE_AUTH_DATA_MAKE_CREDENTIAL)?,
att_stmt,
};
assert_eq!(
serde_cbor::ser::to_vec(&att_obj)?,
// Python: from fido2.cbor import encode; print(f"[{", ".join(f'0x{b:02x}' for b in encode({ "fmt": "android-safetynet", "attStmt": {"ver": "Kom ihåg att du aldrig får snyta dig i mattan!", "response": bytes([1, 2, 3, 4]) }, "authData": SAMPLE_AUTH_DATA_MAKE_CREDENTIAL[2:] }))}]")
[
0 xa3, 0 x63, 0 x66, 0 x6d, 0 x74, 0 x71, 0 x61, 0 x6e, 0 x64, 0 x72, 0 x6f, 0 x69, 0 x64, 0 x2d,
0 x73, 0 x61, 0 x66, 0 x65, 0 x74, 0 x79, 0 x6e, 0 x65, 0 x74, 0 x67, 0 x61, 0 x74, 0 x74, 0 x53,
0 x74, 0 x6d, 0 x74, 0 xa2, 0 x63, 0 x76, 0 x65, 0 x72, 0 x78, 0 x30, 0 x4b, 0 x6f, 0 x6d, 0 x20,
0 x69, 0 x68, 0 xc3, 0 xa5, 0 x67, 0 x20, 0 x61, 0 x74, 0 x74, 0 x20, 0 x64, 0 x75, 0 x20, 0 x61,
0 x6c, 0 x64, 0 x72, 0 x69, 0 x67, 0 x20, 0 x66, 0 xc3, 0 xa5, 0 x72, 0 x20, 0 x73, 0 x6e, 0 x79,
0 x74, 0 x61, 0 x20, 0 x64, 0 x69, 0 x67, 0 x20, 0 x69, 0 x20, 0 x6d, 0 x61, 0 x74, 0 x74, 0 x61,
0 x6e, 0 x21, 0 x68, 0 x72, 0 x65, 0 x73, 0 x70, 0 x6f, 0 x6e, 0 x73, 0 x65, 0 x44, 0 x01, 0 x02,
0 x03, 0 x04, 0 x68, 0 x61, 0 x75, 0 x74, 0 x68, 0 x44, 0 x61, 0 x74, 0 x61, 0 x58, 0 xa2, 0 xc2,
0 x89, 0 xc5, 0 xca, 0 x9b, 0 x04, 0 x60, 0 xf9, 0 x34, 0 x6a, 0 xb4, 0 xe4, 0 x2d, 0 x84, 0 x27,
0 x43, 0 x40, 0 x4d, 0 x31, 0 xf4, 0 x84, 0 x68, 0 x25, 0 xa6, 0 xd0, 0 x65, 0 xbe, 0 x59, 0 x7a,
0 x87, 0 x05, 0 x1d, 0 xc1, 0 x00, 0 x00, 0 x00, 0 x0b, 0 xf8, 0 xa0, 0 x11, 0 xf3, 0 x8c, 0 x0a,
0 x4d, 0 x15, 0 x80, 0 x06, 0 x17, 0 x11, 0 x1f, 0 x9e, 0 xdc, 0 x7d, 0 x00, 0 x10, 0 x89, 0 x59,
0 xce, 0 xad, 0 x5b, 0 x5c, 0 x48, 0 x16, 0 x4e, 0 x8a, 0 xbc, 0 xd6, 0 xd9, 0 x43, 0 x5c, 0 x6f,
0 xa5, 0 x01, 0 x02, 0 x03, 0 x26, 0 x20, 0 x01, 0 x21, 0 x58, 0 x20, 0 xa5, 0 xfd, 0 x5c, 0 xe1,
0 xb1, 0 xc4, 0 x58, 0 xc5, 0 x30, 0 xa5, 0 x4f, 0 xa6, 0 x1b, 0 x31, 0 xbf, 0 x6b, 0 x04, 0 xbe,
0 x8b, 0 x97, 0 xaf, 0 xde, 0 x54, 0 xdd, 0 x8c, 0 xbb, 0 x69, 0 x27, 0 x5a, 0 x8a, 0 x1b, 0 xe1,
0 x22, 0 x58, 0 x20, 0 xfa, 0 x3a, 0 x32, 0 x31, 0 xdd, 0 x9d, 0 xee, 0 xd9, 0 xd1, 0 x89, 0 x7b,
0 xe5, 0 xa6, 0 x22, 0 x8c, 0 x59, 0 x50, 0 x1e, 0 x4b, 0 xcd, 0 x12, 0 x97, 0 x5d, 0 x3d, 0 xff,
0 x73, 0 x0f, 0 x01, 0 x27, 0 x8e, 0 xa6, 0 x1c, 0 xa1, 0 x6b, 0 x68, 0 x6d, 0 x61, 0 x63, 0 x2d,
0 x73, 0 x65, 0 x63, 0 x72, 0 x65, 0 x74, 0 xf5
]
);
Ok(())
}
#[ test]
fn serialize_att_obj_apple() -> Result<(), serde_cbor::Error> {
let att_stmt = AttestationStatement::Apple(serde_cbor::Value::Map(
vec![(
"x5c" .to_string().into(),
serde_cbor::Value::Array(vec![
serde_cbor::Value::Bytes(vec![1 , 2 , 3 , 4 ]),
serde_cbor::Value::Bytes(vec![5 , 6 , 7 , 8 ]),
]),
)]
.into_iter()
.collect(),
));
assert_eq!(
serde_cbor::ser::to_vec(&att_stmt)?,
// Python: from fido2.cbor import encode; print(f"[{", ".join(f'0x{b:02x}' for b in encode({"x5c": [bytes([1, 2, 3, 4]), bytes([5, 6, 7, 8])] }))}]")
[
0 xa1, 0 x63, 0 x78, 0 x35, 0 x63, 0 x82, 0 x44, 0 x01, 0 x02, 0 x03, 0 x04, 0 x44, 0 x05, 0 x06,
0 x07, 0 x08
]
);
let att_obj = AttestationObject {
auth_data: serde_cbor::de::from_slice(&SAMPLE_AUTH_DATA_MAKE_CREDENTIAL)?,
att_stmt,
};
assert_eq!(
serde_cbor::ser::to_vec(&att_obj)?,
// Python: from fido2.cbor import encode; print(f"[{", ".join(f'0x{b:02x}' for b in encode({"fmt": "apple", "attStmt": {"x5c": [bytes([1, 2, 3, 4]), bytes([5, 6, 7, 8])] }, "authData": SAMPLE_AUTH_DATA_MAKE_CREDENTIAL[2:] }))}]")
[
0 xa3, 0 x63, 0 x66, 0 x6d, 0 x74, 0 x65, 0 x61, 0 x70, 0 x70, 0 x6c, 0 x65, 0 x67, 0 x61, 0 x74,
0 x74, 0 x53, 0 x74, 0 x6d, 0 x74, 0 xa1, 0 x63, 0 x78, 0 x35, 0 x63, 0 x82, 0 x44, 0 x01, 0 x02,
0 x03, 0 x04, 0 x44, 0 x05, 0 x06, 0 x07, 0 x08, 0 x68, 0 x61, 0 x75, 0 x74, 0 x68, 0 x44, 0 x61,
0 x74, 0 x61, 0 x58, 0 xa2, 0 xc2, 0 x89, 0 xc5, 0 xca, 0 x9b, 0 x04, 0 x60, 0 xf9, 0 x34, 0 x6a,
0 xb4, 0 xe4, 0 x2d, 0 x84, 0 x27, 0 x43, 0 x40, 0 x4d, 0 x31, 0 xf4, 0 x84, 0 x68, 0 x25, 0 xa6,
0 xd0, 0 x65, 0 xbe, 0 x59, 0 x7a, 0 x87, 0 x05, 0 x1d, 0 xc1, 0 x00, 0 x00, 0 x00, 0 x0b, 0 xf8,
0 xa0, 0 x11, 0 xf3, 0 x8c, 0 x0a, 0 x4d, 0 x15, 0 x80, 0 x06, 0 x17, 0 x11, 0 x1f, 0 x9e, 0 xdc,
0 x7d, 0 x00, 0 x10, 0 x89, 0 x59, 0 xce, 0 xad, 0 x5b, 0 x5c, 0 x48, 0 x16, 0 x4e, 0 x8a, 0 xbc,
0 xd6, 0 xd9, 0 x43, 0 x5c, 0 x6f, 0 xa5, 0 x01, 0 x02, 0 x03, 0 x26, 0 x20, 0 x01, 0 x21, 0 x58,
0 x20, 0 xa5, 0 xfd, 0 x5c, 0 xe1, 0 xb1, 0 xc4, 0 x58, 0 xc5, 0 x30, 0 xa5, 0 x4f, 0 xa6, 0 x1b,
0 x31, 0 xbf, 0 x6b, 0 x04, 0 xbe, 0 x8b, 0 x97, 0 xaf, 0 xde, 0 x54, 0 xdd, 0 x8c, 0 xbb, 0 x69,
0 x27, 0 x5a, 0 x8a, 0 x1b, 0 xe1, 0 x22, 0 x58, 0 x20, 0 xfa, 0 x3a, 0 x32, 0 x31, 0 xdd, 0 x9d,
0 xee, 0 xd9, 0 xd1, 0 x89, 0 x7b, 0 xe5, 0 xa6, 0 x22, 0 x8c, 0 x59, 0 x50, 0 x1e, 0 x4b, 0 xcd,
0 x12, 0 x97, 0 x5d, 0 x3d, 0 xff, 0 x73, 0 x0f, 0 x01, 0 x27, 0 x8e, 0 xa6, 0 x1c, 0 xa1, 0 x6b,
0 x68, 0 x6d, 0 x61, 0 x63, 0 x2d, 0 x73, 0 x65, 0 x63, 0 x72, 0 x65, 0 x74, 0 xf5
]
);
Ok(())
}
#[ test]
fn serialize_att_obj_tpm() -> Result<(), serde_cbor::Error> {
let att_stmt = AttestationStatement::Tpm(serde_cbor::Value::Map(
vec![
("ver" .to_string().into(), "2.0" .to_string().into()),
("alg" .to_string().into(), (-7 ).into()),
(
"x5c" .to_string().into(),
serde_cbor::Value::Array(vec![
serde_cbor::Value::Bytes(vec![1 , 2 , 3 , 4 ]),
serde_cbor::Value::Bytes(vec![5 , 6 , 7 , 8 ]),
]),
),
(
"sig" .to_string().into(),
serde_cbor::Value::Bytes(vec![9 , 10 , 11 , 12 ]),
),
(
"certInfo" .to_string().into(),
serde_cbor::Value::Bytes(vec![13 , 14 , 15 , 16 ]),
),
(
"pubArea" .to_string().into(),
serde_cbor::Value::Bytes(vec![17 , 18 , 19 , 20 ]),
),
]
.into_iter()
.collect(),
));
assert_eq!(
serde_cbor::ser::to_vec(&att_stmt)?,
// Python: from fido2.cbor import encode; print(f"[{", ".join(f'0x{b:02x}' for b in encode({"ver": "2.0", "alg": -7, "x5c": [bytes([1, 2, 3, 4]), bytes([5, 6, 7, 8])], "sig": bytes([9, 10, 11, 12]), "certInfo": bytes([13, 14, 15, 16]), "pubArea": bytes([17, 18, 19, 20]) }))}]")
[
0 xa6, 0 x63, 0 x61, 0 x6c, 0 x67, 0 x26, 0 x63, 0 x73, 0 x69, 0 x67, 0 x44, 0 x09, 0 x0a, 0 x0b,
0 x0c, 0 x63, 0 x76, 0 x65, 0 x72, 0 x63, 0 x32, 0 x2e, 0 x30, 0 x63, 0 x78, 0 x35, 0 x63, 0 x82,
0 x44, 0 x01, 0 x02, 0 x03, 0 x04, 0 x44, 0 x05, 0 x06, 0 x07, 0 x08, 0 x67, 0 x70, 0 x75, 0 x62,
0 x41, 0 x72, 0 x65, 0 x61, 0 x44, 0 x11, 0 x12, 0 x13, 0 x14, 0 x68, 0 x63, 0 x65, 0 x72, 0 x74,
0 x49, 0 x6e, 0 x66, 0 x6f, 0 x44, 0 x0d, 0 x0e, 0 x0f, 0 x10
]
);
let att_obj = AttestationObject {
auth_data: serde_cbor::de::from_slice(&SAMPLE_AUTH_DATA_MAKE_CREDENTIAL)?,
att_stmt,
};
assert_eq!(
serde_cbor::ser::to_vec(&att_obj)?,
// Python: from fido2.cbor import encode; print(f"[{", ".join(f'0x{b:02x}' for b in encode({"fmt": "tpm", "attStmt": {"ver": "2.0", "alg": -7, "x5c": [bytes([1, 2, 3, 4]), bytes([5, 6, 7, 8])], "sig": bytes([9, 10, 11, 12]), "certInfo": bytes([13, 14, 15, 16]), "pubArea": bytes([17, 18, 19, 20]) }, "authData": SAMPLE_AUTH_DATA_MAKE_CREDENTIAL[2:] }))}]")
[
0 xa3, 0 x63, 0 x66, 0 x6d, 0 x74, 0 x63, 0 x74, 0 x70, 0 x6d, 0 x67, 0 x61, 0 x74, 0 x74, 0 x53,
0 x74, 0 x6d, 0 x74, 0 xa6, 0 x63, 0 x61, 0 x6c, 0 x67, 0 x26, 0 x63, 0 x73, 0 x69, 0 x67, 0 x44,
0 x09, 0 x0a, 0 x0b, 0 x0c, 0 x63, 0 x76, 0 x65, 0 x72, 0 x63, 0 x32, 0 x2e, 0 x30, 0 x63, 0 x78,
0 x35, 0 x63, 0 x82, 0 x44, 0 x01, 0 x02, 0 x03, 0 x04, 0 x44, 0 x05, 0 x06, 0 x07, 0 x08, 0 x67,
0 x70, 0 x75, 0 x62, 0 x41, 0 x72, 0 x65, 0 x61, 0 x44, 0 x11, 0 x12, 0 x13, 0 x14, 0 x68, 0 x63,
0 x65, 0 x72, 0 x74, 0 x49, 0 x6e, 0 x66, 0 x6f, 0 x44, 0 x0d, 0 x0e, 0 x0f, 0 x10, 0 x68, 0 x61,
0 x75, 0 x74, 0 x68, 0 x44, 0 x61, 0 x74, 0 x61, 0 x58, 0 xa2, 0 xc2, 0 x89, 0 xc5, 0 xca, 0 x9b,
0 x04, 0 x60, 0 xf9, 0 x34, 0 x6a, 0 xb4, 0 xe4, 0 x2d, 0 x84, 0 x27, 0 x43, 0 x40, 0 x4d, 0 x31,
0 xf4, 0 x84, 0 x68, 0 x25, 0 xa6, 0 xd0, 0 x65, 0 xbe, 0 x59, 0 x7a, 0 x87, 0 x05, 0 x1d, 0 xc1,
0 x00, 0 x00, 0 x00, 0 x0b, 0 xf8, 0 xa0, 0 x11, 0 xf3, 0 x8c, 0 x0a, 0 x4d, 0 x15, 0 x80, 0 x06,
0 x17, 0 x11, 0 x1f, 0 x9e, 0 xdc, 0 x7d, 0 x00, 0 x10, 0 x89, 0 x59, 0 xce, 0 xad, 0 x5b, 0 x5c,
0 x48, 0 x16, 0 x4e, 0 x8a, 0 xbc, 0 xd6, 0 xd9, 0 x43, 0 x5c, 0 x6f, 0 xa5, 0 x01, 0 x02, 0 x03,
0 x26, 0 x20, 0 x01, 0 x21, 0 x58, 0 x20, 0 xa5, 0 xfd, 0 x5c, 0 xe1, 0 xb1, 0 xc4, 0 x58, 0 xc5,
0 x30, 0 xa5, 0 x4f, 0 xa6, 0 x1b, 0 x31, 0 xbf, 0 x6b, 0 x04, 0 xbe, 0 x8b, 0 x97, 0 xaf, 0 xde,
0 x54, 0 xdd, 0 x8c, 0 xbb, 0 x69, 0 x27, 0 x5a, 0 x8a, 0 x1b, 0 xe1, 0 x22, 0 x58, 0 x20, 0 xfa,
0 x3a, 0 x32, 0 x31, 0 xdd, 0 x9d, 0 xee, 0 xd9, 0 xd1, 0 x89, 0 x7b, 0 xe5, 0 xa6, 0 x22, 0 x8c,
0 x59, 0 x50, 0 x1e, 0 x4b, 0 xcd, 0 x12, 0 x97, 0 x5d, 0 x3d, 0 xff, 0 x73, 0 x0f, 0 x01, 0 x27,
0 x8e, 0 xa6, 0 x1c, 0 xa1, 0 x6b, 0 x68, 0 x6d, 0 x61, 0 x63, 0 x2d, 0 x73, 0 x65, 0 x63, 0 x72,
0 x65, 0 x74, 0 xf5
]
);
Ok(())
}
mod hmac_secret {
use std::convert::TryFrom;
use crate ::{
crypto::{
COSEAlgorithm, COSEEC2Key, COSEKey, COSEKeyType, Curve, PinUvAuthProtocol,
SharedSecret,
},
ctap2::{attestation::HmacSecretResponse, commands::CommandError},
AuthenticatorInfo,
};
fn make_test_secret(pin_protocol: u64) -> Result<SharedSecret, CommandError> {
let fake_unused_key = COSEKey {
alg: COSEAlgorithm::ECDH_ES_HKDF256,
key: COSEKeyType::EC2(COSEEC2Key {
curve: Curve::SECP256R1,
x: vec![],
y: vec![],
}),
};
let pin_protocol = PinUvAuthProtocol::try_from(&AuthenticatorInfo {
pin_protocols: Some(vec![pin_protocol]),
..Default::default()
})?;
let key = {
let aes_key = 0 ..32 ;
let hmac_key = 32 ..64 ;
match pin_protocol.id() {
1 => aes_key.collect(),
2 => hmac_key.chain(aes_key).collect(),
_ => unimplemented!(),
}
};
Ok(SharedSecret::new_test(
pin_protocol,
key,
fake_unused_key.clone(),
fake_unused_key,
))
}
#[ test]
fn decrypt_confirmed_returns_none() -> Result<(), CommandError> {
let shared_secret = make_test_secret(2 )?;
for flag in [true , false ] {
let resp = HmacSecretResponse::Confirmed(flag);
let hmac_output = resp.decrypt_secrets(&shared_secret);
assert_eq!(hmac_output, None, "Failed for confirmed flag: {:?}" , flag);
}
Ok(())
}
#[ cfg(not(feature = "crypto_dummy" ))]
mod requires_crypto {
use super ::*;
use crate ::{
crypto::CryptoError,
ctap2::{attestation::HmacSecretResponse, commands::CommandError},
};
const PIN_PROTOCOL_2_IV: [u8; 16 ] = [0 ; 16 ]; // PIN protocol 1 uses a hard-coded all-zero IV
/// Generated using AES key 0..32 and ciphertext 0..64:
/// ```
/// #!/usr/bin/env python3
/// from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
///
/// key = bytes(range(32))
/// iv = bytes([0] * 16)
/// ciphertext = bytes(range(64))
///
/// cipher = Cipher(algorithms.AES256(key), modes.CBC(iv))
/// decryptor = cipher.decryptor()
/// outputs = list(decryptor.update(ciphertext) + decryptor.finalize())
/// EXPECTED_OUTPUT1 = outputs[0:32]
/// EXPECTED_OUTPUT2 = outputs[32:64]
/// print(EXPECTED_OUTPUT1)
/// print(EXPECTED_OUTPUT2)
/// ```
/// Note: Using WebCrypto to generate these is impractical since they MUST NOT be padded, but WebCrypto inserts PKCS#7 padding.
const EXPECTED_OUTPUT1: [u8; 32 ] = [
145 , 61 , 188 , 229 , 73 , 58 , 253 , 192 , 87 , 114 , 133 , 138 , 173 , 74 , 68 , 50 , 105 , 3 ,
44 , 7 , 205 , 92 , 54 , 139 , 137 , 207 , 7 , 105 , 89 , 85 , 211 , 130 ,
];
/// See [EXPECTED_OUTPUT1] for generation instructions
const EXPECTED_OUTPUT2: Option<[u8; 32 ]> = Some([
155 , 19 , 88 , 255 , 192 , 226 , 50 , 42 , 243 , 22 , 42 , 12 , 146 , 77 , 108 , 29 , 71 , 72 , 149 ,
153 , 183 , 65 , 182 , 149 , 71 , 202 , 57 , 123 , 239 , 79 , 94 , 230 ,
]);
#[ test]
fn decrypt_one_secret_pin_protocol_1() -> Result<(), CommandError> {
const CT_LEN: u8 = 32 ;
let shared_secret = make_test_secret(1 )?;
let resp = HmacSecretResponse::Secret((0 ..CT_LEN).collect());
let hmac_output = resp.decrypt_secrets(&shared_secret).unwrap()?;
assert_eq!(hmac_output.output1, EXPECTED_OUTPUT1, "Incorrect output1" );
assert_eq!(hmac_output.output2, None, "Incorrect output2" );
Ok(())
}
#[ test]
fn decrypt_two_secrets_pin_protocol_1() -> Result<(), CommandError> {
const CT_LEN: u8 = 32 * 2 ;
let shared_secret = make_test_secret(1 )?;
let resp = HmacSecretResponse::Secret((0 ..CT_LEN).collect());
let hmac_output = resp.decrypt_secrets(&shared_secret).unwrap()?;
assert_eq!(hmac_output.output1, EXPECTED_OUTPUT1, "Incorrect output1" );
assert_eq!(hmac_output.output2, EXPECTED_OUTPUT2, "Incorrect output2" );
Ok(())
}
#[ test]
fn decrypt_one_secret_pin_protocol_2() -> Result<(), CommandError> {
const CT_LEN: u8 = 32 ;
let shared_secret = make_test_secret(2 )?;
let resp = HmacSecretResponse::Secret(
PIN_PROTOCOL_2_IV.iter().copied().chain(0 ..CT_LEN).collect(),
);
let hmac_output = resp.decrypt_secrets(&shared_secret).unwrap()?;
assert_eq!(hmac_output.output1, EXPECTED_OUTPUT1, "Incorrect output1" );
assert_eq!(hmac_output.output2, None, "Incorrect output2" );
Ok(())
}
#[ test]
fn decrypt_two_secrets_pin_protocol_2() -> Result<(), CommandError> {
const CT_LEN: u8 = 32 * 2 ;
let shared_secret = make_test_secret(2 )?;
let resp = HmacSecretResponse::Secret(
PIN_PROTOCOL_2_IV.iter().copied().chain(0 ..CT_LEN).collect(),
);
let hmac_output = resp.decrypt_secrets(&shared_secret).unwrap()?;
assert_eq!(hmac_output.output1, EXPECTED_OUTPUT1, "Incorrect output1" );
assert_eq!(hmac_output.output2, EXPECTED_OUTPUT2, "Incorrect output2" );
Ok(())
}
#[ test]
fn decrypt_wrong_length_pin_protocol_2() -> Result<(), CommandError> {
// hmac-secret output can only be multiples of 32 bytes since it operates on whole AES cipher blocks
let shared_secret = make_test_secret(2 )?;
{
// Empty cleartext
let resp = HmacSecretResponse::Secret(PIN_PROTOCOL_2_IV.to_vec());
let hmac_output = resp.decrypt_secrets(&shared_secret).unwrap();
assert_eq!(hmac_output, Err(CryptoError::WrongSaltLength));
}
{
// Too long cleartext
let resp = HmacSecretResponse::Secret(
PIN_PROTOCOL_2_IV.iter().copied().chain(0 ..96 ).collect(),
);
let hmac_output = resp.decrypt_secrets(&shared_secret).unwrap();
assert_eq!(hmac_output, Err(CryptoError::WrongSaltLength));
}
Ok(())
}
}
}
}
Messung V0.5 in Prozent C=86 H=100 G=93
¤ Dauer der Verarbeitung: 0.83 Sekunden
(vorverarbeitet am 2026-06-18)
¤
*© Formatika GbR, Deutschland