authenticator_ctap2_2021/ctap2/
attestation.rs

1use super::utils::from_slice_stream;
2use crate::crypto::COSEAlgorithm;
3use crate::ctap2::commands::CommandError;
4use crate::ctap2::server::RpIdHash;
5use crate::{crypto::COSEKey, errors::AuthenticatorError};
6use nom::{
7    bytes::complete::take,
8    combinator::{cond, map},
9    error::Error as NomError,
10    number::complete::{be_u16, be_u32, be_u8},
11    Err as NomErr, IResult,
12};
13use serde::ser::{Error as SerError, SerializeMap, Serializer};
14use serde::{
15    de::{Error as SerdeError, MapAccess, Visitor},
16    Deserialize, Deserializer, Serialize,
17};
18use serde_bytes::ByteBuf;
19use serde_cbor;
20use std::fmt;
21
22#[derive(Debug, PartialEq, Eq)]
23pub enum HmacSecretResponse {
24    /// This is returned by MakeCredential calls to display if CredRandom was
25    /// successfully generated
26    Confirmed(bool),
27    /// This is returned by GetAssertion:
28    /// AES256-CBC(shared_secret, HMAC-SHA265(CredRandom, salt1) || HMAC-SHA265(CredRandom, salt2))
29    Secret(Vec<u8>),
30}
31
32impl Serialize for HmacSecretResponse {
33    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
34    where
35        S: Serializer,
36    {
37        match self {
38            HmacSecretResponse::Confirmed(x) => serializer.serialize_bool(*x),
39            HmacSecretResponse::Secret(x) => serializer.serialize_bytes(&x),
40        }
41    }
42}
43impl<'de> Deserialize<'de> for HmacSecretResponse {
44    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
45    where
46        D: Deserializer<'de>,
47    {
48        struct HmacSecretResponseVisitor;
49
50        impl<'de> Visitor<'de> for HmacSecretResponseVisitor {
51            type Value = HmacSecretResponse;
52
53            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
54                formatter.write_str("a byte array or a boolean")
55            }
56
57            fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
58            where
59                E: SerdeError,
60            {
61                Ok(HmacSecretResponse::Secret(v.to_vec()))
62            }
63
64            fn visit_bool<E>(self, v: bool) -> Result<Self::Value, E>
65            where
66                E: SerdeError,
67            {
68                Ok(HmacSecretResponse::Confirmed(v))
69            }
70        }
71        deserializer.deserialize_any(HmacSecretResponseVisitor)
72    }
73}
74
75#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Default)]
76pub struct Extension {
77    #[serde(rename = "pinMinLength", skip_serializing_if = "Option::is_none")]
78    pub pin_min_length: Option<u64>,
79    #[serde(rename = "hmac-secret", skip_serializing_if = "Option::is_none")]
80    pub hmac_secret: Option<HmacSecretResponse>,
81}
82
83fn parse_extensions<'a>(input: &'a [u8]) -> IResult<&'a [u8], Extension, NomError<&'a [u8]>> {
84    serde_to_nom(input)
85}
86
87#[derive(Serialize, PartialEq, Default, Eq, Clone)]
88pub struct AAGuid(pub [u8; 16]);
89
90impl AAGuid {
91    pub fn from(src: &[u8]) -> Result<AAGuid, ()> {
92        let mut payload = [0u8; 16];
93        if src.len() != payload.len() {
94            Err(())
95        } else {
96            payload.copy_from_slice(src);
97            Ok(AAGuid(payload))
98        }
99    }
100}
101
102impl fmt::Debug for AAGuid {
103    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
104        write!(
105            f,
106            "AAGuid({:02x}{:02x}{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}{:02x}{:02x}{:02x}{:02x})",
107            self.0[0],
108            self.0[1],
109            self.0[2],
110            self.0[3],
111            self.0[4],
112            self.0[5],
113            self.0[6],
114            self.0[7],
115            self.0[8],
116            self.0[9],
117            self.0[10],
118            self.0[11],
119            self.0[12],
120            self.0[13],
121            self.0[14],
122            self.0[15]
123        )
124    }
125}
126
127impl fmt::Display for AAGuid {
128    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
129        write!(
130            f,
131            "{:x}{:x}{:x}{:x}-{:x}{:x}-{:x}{:x}-{:x}{:x}-{:x}{:x}{:x}{:x}{:x}{:x}",
132            self.0[0],
133            self.0[1],
134            self.0[2],
135            self.0[3],
136            self.0[4],
137            self.0[5],
138            self.0[6],
139            self.0[7],
140            self.0[8],
141            self.0[9],
142            self.0[10],
143            self.0[11],
144            self.0[12],
145            self.0[13],
146            self.0[14],
147            self.0[15]
148        )
149    }
150}
151
152impl<'de> Deserialize<'de> for AAGuid {
153    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
154    where
155        D: Deserializer<'de>,
156    {
157        struct AAGuidVisitor;
158
159        impl<'de> Visitor<'de> for AAGuidVisitor {
160            type Value = AAGuid;
161
162            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
163                formatter.write_str("a byte array")
164            }
165
166            fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
167            where
168                E: SerdeError,
169            {
170                let mut buf = [0u8; 16];
171                if v.len() != buf.len() {
172                    return Err(E::invalid_length(v.len(), &"16"));
173                }
174
175                buf.copy_from_slice(v);
176
177                Ok(AAGuid(buf))
178            }
179        }
180
181        deserializer.deserialize_bytes(AAGuidVisitor)
182    }
183}
184
185#[derive(Debug, PartialEq, Eq)]
186pub struct AttestedCredentialData {
187    pub aaguid: AAGuid,
188    pub credential_id: Vec<u8>,
189    pub credential_public_key: COSEKey,
190}
191
192fn serde_to_nom<'a, Output>(input: &'a [u8]) -> IResult<&'a [u8], Output>
193where
194    Output: Deserialize<'a>,
195{
196    from_slice_stream(input)
197        .map_err(|_e| nom::Err::Error(nom::error::make_error(input, nom::error::ErrorKind::NoneOf)))
198    // can't use custom errorkind because of error type mismatch in parse_attested_cred_data
199    //.map_err(|e| NomErr::Error(Context::Code(input, ErrorKind::Custom(e))))
200    //         .map_err(|_| NomErr::Error(Context::Code(input, ErrorKind::Custom(42))))
201}
202
203fn parse_attested_cred_data<'a>(
204    input: &'a [u8],
205) -> IResult<&'a [u8], AttestedCredentialData, NomError<&'a [u8]>> {
206    let (rest, aaguid_res) = map(take(16u8), AAGuid::from)(input)?;
207    // // We can unwrap here, since we _know_ the input will be 16 bytes error out before calling from()
208    let aaguid = aaguid_res.unwrap();
209    let (rest, cred_len) = be_u16(rest)?;
210    let (rest, credential_id) = map(take(cred_len), Vec::from)(rest)?;
211    let (rest, credential_public_key) = serde_to_nom(rest)?;
212    Ok((
213        rest,
214        (AttestedCredentialData {
215            aaguid,
216            credential_id,
217            credential_public_key: credential_public_key,
218        }),
219    ))
220}
221
222bitflags! {
223    pub struct AuthenticatorDataFlags: u8 {
224        const USER_PRESENT = 0x01;
225        const USER_VERIFIED = 0x04;
226        const ATTESTED = 0x40;
227        const EXTENSION_DATA = 0x80;
228    }
229}
230
231#[derive(Debug, PartialEq, Eq)]
232pub struct AuthenticatorData {
233    pub rp_id_hash: RpIdHash,
234    pub flags: AuthenticatorDataFlags,
235    pub counter: u32,
236    pub credential_data: Option<AttestedCredentialData>,
237    pub extensions: Extension,
238}
239
240fn parse_ad<'a>(input: &'a [u8]) -> IResult<&'a [u8], AuthenticatorData, NomError<&'a [u8]>> {
241    let (rest, rp_id_hash_res) = map(take(32u8), RpIdHash::from)(input)?;
242    // We can unwrap here, since we _know_ the input to from() will be 32 bytes or error out before calling from()
243    let rp_id_hash = rp_id_hash_res.unwrap();
244    // be conservative, there is a couple of reserved values in
245    // AuthenticatorDataFlags, just truncate the one we don't know
246    let (rest, flags) = map(be_u8, AuthenticatorDataFlags::from_bits_truncate)(rest)?;
247    let (rest, counter) = be_u32(rest)?;
248    let (rest, credential_data) = cond(
249        flags.contains(AuthenticatorDataFlags::ATTESTED),
250        parse_attested_cred_data,
251    )(rest)?;
252    let (rest, extensions) = cond(
253        flags.contains(AuthenticatorDataFlags::EXTENSION_DATA),
254        parse_extensions,
255    )(rest)?;
256    // TODO(baloo): we should check for end of buffer and raise a parse
257    //              parse error if data is still in the buffer
258    //eof!() >>
259    Ok((
260        rest,
261        AuthenticatorData {
262            rp_id_hash,
263            flags,
264            counter,
265            credential_data,
266            extensions: extensions.unwrap_or_default(),
267        },
268    ))
269}
270
271impl<'de> Deserialize<'de> for AuthenticatorData {
272    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
273    where
274        D: Deserializer<'de>,
275    {
276        struct AuthenticatorDataVisitor;
277
278        impl<'de> Visitor<'de> for AuthenticatorDataVisitor {
279            type Value = AuthenticatorData;
280
281            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
282                formatter.write_str("a byte array")
283            }
284
285            fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
286            where
287                E: SerdeError,
288            {
289                parse_ad(v)
290                    .map(|(_input, value)| value)
291                    .map_err(|e| match e {
292                        NomErr::Incomplete(nom::Needed::Size(len)) => {
293                            E::invalid_length(v.len(), &format!("{}", v.len() + len.get()).as_ref())
294                        }
295                        NomErr::Incomplete(nom::Needed::Unknown) => {
296                            E::invalid_length(v.len(), &"unknown") // We don't know the expected value
297                        }
298                        // TODO(baloo): is that enough? should we be more
299                        //              specific on the error type?
300                        e => E::custom(e.to_string()),
301                    })
302            }
303        }
304
305        deserializer.deserialize_bytes(AuthenticatorDataVisitor)
306    }
307}
308
309impl AuthenticatorData {
310    pub fn to_vec(&self) -> Result<Vec<u8>, AuthenticatorError> {
311        let mut data = Vec::new();
312        data.extend(&self.rp_id_hash.0);
313        data.extend(&[self.flags.bits()]);
314        data.extend(&self.counter.to_be_bytes());
315
316        // TODO(baloo): need to yield credential_data and extensions, but that dependends on flags,
317        //              should we consider another type system?
318        if let Some(cred) = &self.credential_data {
319            data.extend(&cred.aaguid.0);
320            data.extend(&(cred.credential_id.len() as u16).to_be_bytes());
321            data.extend(&cred.credential_id);
322            data.extend(
323                &serde_cbor::to_vec(&cred.credential_public_key)
324                    .map_err(CommandError::Serializing)?,
325            );
326        }
327        Ok(data)
328    }
329}
330
331#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
332/// x509 encoded attestation certificate
333pub struct AttestationCertificate(#[serde(with = "serde_bytes")] pub(crate) Vec<u8>);
334
335impl AsRef<[u8]> for AttestationCertificate {
336    fn as_ref(&self) -> &[u8] {
337        self.0.as_ref()
338    }
339}
340
341#[derive(Serialize, Deserialize, PartialEq, Eq)]
342pub struct Signature(#[serde(with = "serde_bytes")] pub(crate) ByteBuf);
343
344impl fmt::Debug for Signature {
345    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
346        let value = base64::encode_config(&self.0, base64::URL_SAFE_NO_PAD);
347        write!(f, "Signature({})", value)
348    }
349}
350
351impl AsRef<[u8]> for Signature {
352    fn as_ref(&self) -> &[u8] {
353        self.0.as_ref()
354    }
355}
356
357#[derive(Debug, PartialEq, Eq)]
358pub enum AttestationStatement {
359    None,
360    Packed(AttestationStatementPacked),
361    // TODO(baloo): there is a couple other options than None and Packed:
362    //              https://w3c.github.io/webauthn/#generating-an-attestation-object
363    //              https://w3c.github.io/webauthn/#defined-attestation-formats
364    //TPM,
365    //AndroidKey,
366    //AndroidSafetyNet,
367    FidoU2F(AttestationStatementFidoU2F),
368    // TODO(baloo): should we do an Unknow version? most likely
369    Unparsed(Vec<u8>),
370}
371
372// Not all crypto-backends currently provide "crypto::verify()", so we do not implement it yet.
373// Also not sure, if we really need it. Would be a sanity-check only, to verify the signature is valid,
374// before sendig it out.
375// impl AttestationStatement {
376//     pub fn verify(&self, data: &[u8]) -> Result<bool, AuthenticatorError> {
377//         match self {
378//             AttestationStatement::None => Ok(true),
379//             AttestationStatement::Unparsed(_) => Err(AuthenticatorError::Custom(
380//                 "Unparsed attestation object can't be used to verify signature.".to_string(),
381//             )),
382//             AttestationStatement::FidoU2F(att) => {
383//                 let res = crypto::verify(
384//                     crypto::SignatureAlgorithm::ES256,
385//                     &att.attestation_cert[0].as_ref(),
386//                     att.sig.as_ref(),
387//                     data,
388//                 )?;
389//                 Ok(res)
390//             }
391//             AttestationStatement::Packed(att) => {
392//                 if att.alg != Alg::ES256 {
393//                     return Err(AuthenticatorError::Custom(
394//                         "Verification only supported for ES256".to_string(),
395//                     ));
396//                 }
397//                 let res = crypto::verify(
398//                     crypto::SignatureAlgorithm::ES256,
399//                     att.attestation_cert[0].as_ref(),
400//                     att.sig.as_ref(),
401//                     data,
402//                 )?;
403//                 Ok(res)
404//             }
405//         }
406//     }
407// }
408
409#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
410// See https://www.w3.org/TR/webauthn/#fido-u2f-attestation
411pub struct AttestationStatementFidoU2F {
412    pub sig: Signature,
413
414    #[serde(rename = "x5c")]
415    /// Certificate chain in x509 format
416    pub attestation_cert: Vec<AttestationCertificate>,
417}
418
419#[allow(dead_code)] // TODO(MS): Remove me, once we can parse AttestationStatements and use this function
420impl AttestationStatementFidoU2F {
421    pub fn new(cert: &[u8], signature: &[u8]) -> Self {
422        AttestationStatementFidoU2F {
423            sig: Signature(ByteBuf::from(signature)),
424            attestation_cert: vec![AttestationCertificate(Vec::from(cert))],
425        }
426    }
427}
428
429#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
430// TODO(baloo): there is a couple other options than x5c:
431//              https://www.w3.org/TR/webauthn/#packed-attestation
432pub struct AttestationStatementPacked {
433    pub alg: COSEAlgorithm,
434    pub sig: Signature,
435
436    #[serde(rename = "x5c")]
437    /// Certificate chain in x509 format
438    pub attestation_cert: Vec<AttestationCertificate>,
439}
440
441#[derive(Debug, Deserialize, PartialEq, Eq)]
442#[serde(rename_all = "lowercase")]
443enum AttestationFormat {
444    #[serde(rename = "fido-u2f")]
445    FidoU2F,
446    Packed,
447    None,
448    // TOOD(baloo): only packed is implemented for now, see spec:
449    //              https://www.w3.org/TR/webauthn/#defined-attestation-formats
450    //TPM,
451    //AndroidKey,
452    //AndroidSafetyNet,
453}
454
455#[derive(Debug, PartialEq, Eq)]
456pub struct AttestationObject {
457    pub auth_data: AuthenticatorData,
458    pub att_statement: AttestationStatement,
459}
460
461impl<'de> Deserialize<'de> for AttestationObject {
462    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
463    where
464        D: Deserializer<'de>,
465    {
466        struct AttestationObjectVisitor;
467
468        impl<'de> Visitor<'de> for AttestationObjectVisitor {
469            type Value = AttestationObject;
470
471            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
472                formatter.write_str("a cbor map")
473            }
474
475            fn visit_map<M>(self, mut map: M) -> Result<Self::Value, M::Error>
476            where
477                M: MapAccess<'de>,
478            {
479                let mut format: Option<AttestationFormat> = None;
480                let mut auth_data = None;
481                let mut att_statement = None;
482
483                while let Some(key) = map.next_key()? {
484                    match key {
485                        // Spec for CTAP 2.0 is wrong and fmt should be numbered 1, and auth_data 2:
486                        // https://fidoalliance.org/specs/fido-v2.0-ps-20190130/fido-client-to-authenticator-protocol-v2.0-ps-20190130.html#authenticatorMakeCredential
487                        // Corrected in CTAP 2.1 and Webauthn spec
488                        1 => {
489                            if format.is_some() {
490                                return Err(SerdeError::duplicate_field("fmt"));
491                            }
492                            format = Some(map.next_value()?);
493                        }
494                        2 => {
495                            if auth_data.is_some() {
496                                return Err(SerdeError::duplicate_field("auth_data"));
497                            }
498                            auth_data = Some(map.next_value()?);
499                        }
500                        3 => {
501                            let format = format
502                                .as_ref()
503                                .ok_or_else(|| SerdeError::missing_field("fmt"))?;
504                            if att_statement.is_some() {
505                                return Err(SerdeError::duplicate_field("att_statement"));
506                            }
507                            match format {
508                                // This should not actually happen, but ...
509                                AttestationFormat::None => {
510                                    att_statement = Some(AttestationStatement::None);
511                                }
512                                AttestationFormat::Packed => {
513                                    att_statement =
514                                        Some(AttestationStatement::Packed(map.next_value()?));
515                                }
516                                AttestationFormat::FidoU2F => {
517                                    att_statement =
518                                        Some(AttestationStatement::FidoU2F(map.next_value()?));
519                                }
520                            }
521                        }
522                        k => return Err(M::Error::custom(format!("unexpected key: {:?}", k))),
523                    }
524                }
525
526                let auth_data =
527                    auth_data.ok_or_else(|| M::Error::custom("found no auth_data".to_string()))?;
528                let att_statement = att_statement.unwrap_or(AttestationStatement::None);
529
530                Ok(AttestationObject {
531                    auth_data,
532                    att_statement,
533                })
534            }
535        }
536
537        deserializer.deserialize_bytes(AttestationObjectVisitor)
538    }
539}
540
541impl Serialize for AttestationObject {
542    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
543    where
544        S: Serializer,
545    {
546        // serializer.serialize_bytes(&v)
547        let map_len = 3;
548        let mut map = serializer.serialize_map(Some(map_len))?;
549
550        let auth_data =
551            self
552                .auth_data
553                .to_vec()
554                .map(|v| serde_cbor::Value::Bytes(v))
555                .map_err(|_| SerError::custom("Failed to serialize auth_data"))?;
556
557        map.serialize_entry(
558            &"authData",
559            &auth_data
560        )?;
561        match self.att_statement {
562            AttestationStatement::None => {
563                // Even with Att None, an empty map is returned in the cbor!
564                map.serialize_entry(&"fmt", &"none")?;
565                let v = serde_cbor::Value::Map(std::collections::BTreeMap::new());
566                map.serialize_entry(&"attStmt", &v)?;
567            }
568            AttestationStatement::Packed(ref v) => {
569                map.serialize_entry(&"fmt", &"packed")?;
570                map.serialize_entry(&"attStmt", v)?;
571            }
572            AttestationStatement::FidoU2F(ref v) => {
573                map.serialize_entry(&"fmt", &"fido-u2f")?;
574                map.serialize_entry(&"attStmt", v)?;
575            }
576            AttestationStatement::Unparsed(ref v) => {
577                // Unparsed is currently only used in the fido1.0/u2f-case
578                map.serialize_entry(&"fmt", &"fido-u2f")?;
579                map.serialize_entry(&"attStmt", v)?;
580            }
581        }
582
583        map.end()
584    }
585}
586
587#[cfg(test)]
588mod test {
589    use super::super::utils::from_slice_stream;
590    use super::*;
591    use serde_cbor::from_slice;
592
593    const SAMPLE_ATTESTATION: [u8; 1006] = [
594        0xa3, 0x1, 0x66, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x64, 0x2, 0x58, 0xc4, 0x49, 0x96, 0xd,
595        0xe5, 0x88, 0xe, 0x8c, 0x68, 0x74, 0x34, 0x17, 0xf, 0x64, 0x76, 0x60, 0x5b, 0x8f, 0xe4,
596        0xae, 0xb9, 0xa2, 0x86, 0x32, 0xc7, 0x99, 0x5c, 0xf3, 0xba, 0x83, 0x1d, 0x97, 0x63, 0x41,
597        0x0, 0x0, 0x0, 0x7, 0xcb, 0x69, 0x48, 0x1e, 0x8f, 0xf7, 0x40, 0x39, 0x93, 0xec, 0xa, 0x27,
598        0x29, 0xa1, 0x54, 0xa8, 0x0, 0x40, 0xc3, 0xcf, 0x1, 0x3b, 0xc6, 0x26, 0x93, 0x28, 0xfb,
599        0x7f, 0xa9, 0x76, 0xef, 0xa8, 0x4b, 0x66, 0x71, 0xad, 0xa9, 0x64, 0xea, 0xcb, 0x58, 0x76,
600        0x54, 0x51, 0xa, 0xc8, 0x86, 0x4f, 0xbb, 0x53, 0x2d, 0xfb, 0x2, 0xfc, 0xdc, 0xa9, 0x84,
601        0xc2, 0x5c, 0x67, 0x8a, 0x3a, 0xab, 0x57, 0xf3, 0x71, 0x77, 0xd3, 0xd4, 0x41, 0x64, 0x1,
602        0x50, 0xca, 0x6c, 0x42, 0x73, 0x1c, 0x42, 0xcb, 0x81, 0xba, 0xa5, 0x1, 0x2, 0x3, 0x26,
603        0x20, 0x1, 0x21, 0x58, 0x20, 0x9, 0x2e, 0x34, 0xfe, 0xa7, 0xd7, 0x32, 0xc8, 0xae, 0x4c,
604        0xf6, 0x96, 0xbe, 0x7a, 0x12, 0xdc, 0x29, 0xd5, 0xf1, 0xd3, 0xf1, 0x55, 0x4d, 0xdc, 0x87,
605        0xc4, 0xc, 0x9b, 0xd0, 0x17, 0xba, 0xf, 0x22, 0x58, 0x20, 0xc9, 0xf0, 0x97, 0x33, 0x55,
606        0x36, 0x58, 0xd9, 0xdb, 0x76, 0xf5, 0xef, 0x95, 0xcf, 0x8a, 0xc7, 0xfc, 0xc1, 0xb6, 0x81,
607        0x25, 0x5f, 0x94, 0x6b, 0x62, 0x13, 0x7d, 0xd0, 0xc4, 0x86, 0x53, 0xdb, 0x3, 0xa3, 0x63,
608        0x61, 0x6c, 0x67, 0x26, 0x63, 0x73, 0x69, 0x67, 0x58, 0x48, 0x30, 0x46, 0x2, 0x21, 0x0,
609        0xac, 0x2a, 0x78, 0xa8, 0xaf, 0x18, 0x80, 0x39, 0x73, 0x8d, 0x3, 0x5e, 0x4, 0x4d, 0x94,
610        0x4f, 0x3f, 0x57, 0xce, 0x88, 0x41, 0xfa, 0x81, 0x50, 0x40, 0xb6, 0xd1, 0x95, 0xb5, 0xeb,
611        0xe4, 0x6f, 0x2, 0x21, 0x0, 0x8f, 0xf4, 0x15, 0xc9, 0xb3, 0x6d, 0x1c, 0xd, 0x4c, 0xa3,
612        0xcf, 0x99, 0x8a, 0x46, 0xd4, 0x4c, 0x8b, 0x5c, 0x26, 0x3f, 0xdf, 0x22, 0x6c, 0x9b, 0x23,
613        0x83, 0x8b, 0x69, 0x47, 0x67, 0x48, 0x45, 0x63, 0x78, 0x35, 0x63, 0x81, 0x59, 0x2, 0xc1,
614        0x30, 0x82, 0x2, 0xbd, 0x30, 0x82, 0x1, 0xa5, 0xa0, 0x3, 0x2, 0x1, 0x2, 0x2, 0x4, 0x18,
615        0xac, 0x46, 0xc0, 0x30, 0xd, 0x6, 0x9, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0xd, 0x1, 0x1, 0xb,
616        0x5, 0x0, 0x30, 0x2e, 0x31, 0x2c, 0x30, 0x2a, 0x6, 0x3, 0x55, 0x4, 0x3, 0x13, 0x23, 0x59,
617        0x75, 0x62, 0x69, 0x63, 0x6f, 0x20, 0x55, 0x32, 0x46, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20,
618        0x43, 0x41, 0x20, 0x53, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x20, 0x34, 0x35, 0x37, 0x32, 0x30,
619        0x30, 0x36, 0x33, 0x31, 0x30, 0x20, 0x17, 0xd, 0x31, 0x34, 0x30, 0x38, 0x30, 0x31, 0x30,
620        0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x18, 0xf, 0x32, 0x30, 0x35, 0x30, 0x30, 0x39, 0x30,
621        0x34, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x30, 0x6e, 0x31, 0xb, 0x30, 0x9, 0x6, 0x3,
622        0x55, 0x4, 0x6, 0x13, 0x2, 0x53, 0x45, 0x31, 0x12, 0x30, 0x10, 0x6, 0x3, 0x55, 0x4, 0xa,
623        0xc, 0x9, 0x59, 0x75, 0x62, 0x69, 0x63, 0x6f, 0x20, 0x41, 0x42, 0x31, 0x22, 0x30, 0x20,
624        0x6, 0x3, 0x55, 0x4, 0xb, 0xc, 0x19, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63,
625        0x61, 0x74, 0x6f, 0x72, 0x20, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f,
626        0x6e, 0x31, 0x27, 0x30, 0x25, 0x6, 0x3, 0x55, 0x4, 0x3, 0xc, 0x1e, 0x59, 0x75, 0x62, 0x69,
627        0x63, 0x6f, 0x20, 0x55, 0x32, 0x46, 0x20, 0x45, 0x45, 0x20, 0x53, 0x65, 0x72, 0x69, 0x61,
628        0x6c, 0x20, 0x34, 0x31, 0x33, 0x39, 0x34, 0x33, 0x34, 0x38, 0x38, 0x30, 0x59, 0x30, 0x13,
629        0x6, 0x7, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x2, 0x1, 0x6, 0x8, 0x2a, 0x86, 0x48, 0xce, 0x3d,
630        0x3, 0x1, 0x7, 0x3, 0x42, 0x0, 0x4, 0x79, 0xea, 0x3b, 0x2c, 0x7c, 0x49, 0x70, 0x10, 0x62,
631        0x23, 0xc, 0xd2, 0x3f, 0xeb, 0x60, 0xe5, 0x29, 0x31, 0x71, 0xd4, 0x83, 0xf1, 0x0, 0xbe,
632        0x85, 0x9d, 0x6b, 0xf, 0x83, 0x97, 0x3, 0x1, 0xb5, 0x46, 0xcd, 0xd4, 0x6e, 0xcf, 0xca,
633        0xe3, 0xe3, 0xf3, 0xf, 0x81, 0xe9, 0xed, 0x62, 0xbd, 0x26, 0x8d, 0x4c, 0x1e, 0xbd, 0x37,
634        0xb3, 0xbc, 0xbe, 0x92, 0xa8, 0xc2, 0xae, 0xeb, 0x4e, 0x3a, 0xa3, 0x6c, 0x30, 0x6a, 0x30,
635        0x22, 0x6, 0x9, 0x2b, 0x6, 0x1, 0x4, 0x1, 0x82, 0xc4, 0xa, 0x2, 0x4, 0x15, 0x31, 0x2e,
636        0x33, 0x2e, 0x36, 0x2e, 0x31, 0x2e, 0x34, 0x2e, 0x31, 0x2e, 0x34, 0x31, 0x34, 0x38, 0x32,
637        0x2e, 0x31, 0x2e, 0x37, 0x30, 0x13, 0x6, 0xb, 0x2b, 0x6, 0x1, 0x4, 0x1, 0x82, 0xe5, 0x1c,
638        0x2, 0x1, 0x1, 0x4, 0x4, 0x3, 0x2, 0x5, 0x20, 0x30, 0x21, 0x6, 0xb, 0x2b, 0x6, 0x1, 0x4,
639        0x1, 0x82, 0xe5, 0x1c, 0x1, 0x1, 0x4, 0x4, 0x12, 0x4, 0x10, 0xcb, 0x69, 0x48, 0x1e, 0x8f,
640        0xf7, 0x40, 0x39, 0x93, 0xec, 0xa, 0x27, 0x29, 0xa1, 0x54, 0xa8, 0x30, 0xc, 0x6, 0x3, 0x55,
641        0x1d, 0x13, 0x1, 0x1, 0xff, 0x4, 0x2, 0x30, 0x0, 0x30, 0xd, 0x6, 0x9, 0x2a, 0x86, 0x48,
642        0x86, 0xf7, 0xd, 0x1, 0x1, 0xb, 0x5, 0x0, 0x3, 0x82, 0x1, 0x1, 0x0, 0x97, 0x9d, 0x3, 0x97,
643        0xd8, 0x60, 0xf8, 0x2e, 0xe1, 0x5d, 0x31, 0x1c, 0x79, 0x6e, 0xba, 0xfb, 0x22, 0xfa, 0xa7,
644        0xe0, 0x84, 0xd9, 0xba, 0xb4, 0xc6, 0x1b, 0xbb, 0x57, 0xf3, 0xe6, 0xb4, 0xc1, 0x8a, 0x48,
645        0x37, 0xb8, 0x5c, 0x3c, 0x4e, 0xdb, 0xe4, 0x83, 0x43, 0xf4, 0xd6, 0xa5, 0xd9, 0xb1, 0xce,
646        0xda, 0x8a, 0xe1, 0xfe, 0xd4, 0x91, 0x29, 0x21, 0x73, 0x5, 0x8e, 0x5e, 0xe1, 0xcb, 0xdd,
647        0x6b, 0xda, 0xc0, 0x75, 0x57, 0xc6, 0xa0, 0xe8, 0xd3, 0x68, 0x25, 0xba, 0x15, 0x9e, 0x7f,
648        0xb5, 0xad, 0x8c, 0xda, 0xf8, 0x4, 0x86, 0x8c, 0xf9, 0xe, 0x8f, 0x1f, 0x8a, 0xea, 0x17,
649        0xc0, 0x16, 0xb5, 0x5c, 0x2a, 0x7a, 0xd4, 0x97, 0xc8, 0x94, 0xfb, 0x71, 0xd7, 0x53, 0xd7,
650        0x9b, 0x9a, 0x48, 0x4b, 0x6c, 0x37, 0x6d, 0x72, 0x3b, 0x99, 0x8d, 0x2e, 0x1d, 0x43, 0x6,
651        0xbf, 0x10, 0x33, 0xb5, 0xae, 0xf8, 0xcc, 0xa5, 0xcb, 0xb2, 0x56, 0x8b, 0x69, 0x24, 0x22,
652        0x6d, 0x22, 0xa3, 0x58, 0xab, 0x7d, 0x87, 0xe4, 0xac, 0x5f, 0x2e, 0x9, 0x1a, 0xa7, 0x15,
653        0x79, 0xf3, 0xa5, 0x69, 0x9, 0x49, 0x7d, 0x72, 0xf5, 0x4e, 0x6, 0xba, 0xc1, 0xc3, 0xb4,
654        0x41, 0x3b, 0xba, 0x5e, 0xaf, 0x94, 0xc3, 0xb6, 0x4f, 0x34, 0xf9, 0xeb, 0xa4, 0x1a, 0xcb,
655        0x6a, 0xe2, 0x83, 0x77, 0x6d, 0x36, 0x46, 0x53, 0x78, 0x48, 0xfe, 0xe8, 0x84, 0xbd, 0xdd,
656        0xf5, 0xb1, 0xba, 0x57, 0x98, 0x54, 0xcf, 0xfd, 0xce, 0xba, 0xc3, 0x44, 0x5, 0x95, 0x27,
657        0xe5, 0x6d, 0xd5, 0x98, 0xf8, 0xf5, 0x66, 0x71, 0x5a, 0xbe, 0x43, 0x1, 0xdd, 0x19, 0x11,
658        0x30, 0xe6, 0xb9, 0xf0, 0xc6, 0x40, 0x39, 0x12, 0x53, 0xe2, 0x29, 0x80, 0x3f, 0x3a, 0xef,
659        0x27, 0x4b, 0xed, 0xbf, 0xde, 0x3f, 0xcb, 0xbd, 0x42, 0xea, 0xd6, 0x79,
660    ];
661
662    const SAMPLE_CERT_CHAIN: [u8; 709] = [
663        0x81, 0x59, 0x2, 0xc1, 0x30, 0x82, 0x2, 0xbd, 0x30, 0x82, 0x1, 0xa5, 0xa0, 0x3, 0x2, 0x1,
664        0x2, 0x2, 0x4, 0x18, 0xac, 0x46, 0xc0, 0x30, 0xd, 0x6, 0x9, 0x2a, 0x86, 0x48, 0x86, 0xf7,
665        0xd, 0x1, 0x1, 0xb, 0x5, 0x0, 0x30, 0x2e, 0x31, 0x2c, 0x30, 0x2a, 0x6, 0x3, 0x55, 0x4, 0x3,
666        0x13, 0x23, 0x59, 0x75, 0x62, 0x69, 0x63, 0x6f, 0x20, 0x55, 0x32, 0x46, 0x20, 0x52, 0x6f,
667        0x6f, 0x74, 0x20, 0x43, 0x41, 0x20, 0x53, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x20, 0x34, 0x35,
668        0x37, 0x32, 0x30, 0x30, 0x36, 0x33, 0x31, 0x30, 0x20, 0x17, 0xd, 0x31, 0x34, 0x30, 0x38,
669        0x30, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x18, 0xf, 0x32, 0x30, 0x35, 0x30,
670        0x30, 0x39, 0x30, 0x34, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x30, 0x6e, 0x31, 0xb,
671        0x30, 0x9, 0x6, 0x3, 0x55, 0x4, 0x6, 0x13, 0x2, 0x53, 0x45, 0x31, 0x12, 0x30, 0x10, 0x6,
672        0x3, 0x55, 0x4, 0xa, 0xc, 0x9, 0x59, 0x75, 0x62, 0x69, 0x63, 0x6f, 0x20, 0x41, 0x42, 0x31,
673        0x22, 0x30, 0x20, 0x6, 0x3, 0x55, 0x4, 0xb, 0xc, 0x19, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e,
674        0x74, 0x69, 0x63, 0x61, 0x74, 0x6f, 0x72, 0x20, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61,
675        0x74, 0x69, 0x6f, 0x6e, 0x31, 0x27, 0x30, 0x25, 0x6, 0x3, 0x55, 0x4, 0x3, 0xc, 0x1e, 0x59,
676        0x75, 0x62, 0x69, 0x63, 0x6f, 0x20, 0x55, 0x32, 0x46, 0x20, 0x45, 0x45, 0x20, 0x53, 0x65,
677        0x72, 0x69, 0x61, 0x6c, 0x20, 0x34, 0x31, 0x33, 0x39, 0x34, 0x33, 0x34, 0x38, 0x38, 0x30,
678        0x59, 0x30, 0x13, 0x6, 0x7, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x2, 0x1, 0x6, 0x8, 0x2a, 0x86,
679        0x48, 0xce, 0x3d, 0x3, 0x1, 0x7, 0x3, 0x42, 0x0, 0x4, 0x79, 0xea, 0x3b, 0x2c, 0x7c, 0x49,
680        0x70, 0x10, 0x62, 0x23, 0xc, 0xd2, 0x3f, 0xeb, 0x60, 0xe5, 0x29, 0x31, 0x71, 0xd4, 0x83,
681        0xf1, 0x0, 0xbe, 0x85, 0x9d, 0x6b, 0xf, 0x83, 0x97, 0x3, 0x1, 0xb5, 0x46, 0xcd, 0xd4, 0x6e,
682        0xcf, 0xca, 0xe3, 0xe3, 0xf3, 0xf, 0x81, 0xe9, 0xed, 0x62, 0xbd, 0x26, 0x8d, 0x4c, 0x1e,
683        0xbd, 0x37, 0xb3, 0xbc, 0xbe, 0x92, 0xa8, 0xc2, 0xae, 0xeb, 0x4e, 0x3a, 0xa3, 0x6c, 0x30,
684        0x6a, 0x30, 0x22, 0x6, 0x9, 0x2b, 0x6, 0x1, 0x4, 0x1, 0x82, 0xc4, 0xa, 0x2, 0x4, 0x15,
685        0x31, 0x2e, 0x33, 0x2e, 0x36, 0x2e, 0x31, 0x2e, 0x34, 0x2e, 0x31, 0x2e, 0x34, 0x31, 0x34,
686        0x38, 0x32, 0x2e, 0x31, 0x2e, 0x37, 0x30, 0x13, 0x6, 0xb, 0x2b, 0x6, 0x1, 0x4, 0x1, 0x82,
687        0xe5, 0x1c, 0x2, 0x1, 0x1, 0x4, 0x4, 0x3, 0x2, 0x5, 0x20, 0x30, 0x21, 0x6, 0xb, 0x2b, 0x6,
688        0x1, 0x4, 0x1, 0x82, 0xe5, 0x1c, 0x1, 0x1, 0x4, 0x4, 0x12, 0x4, 0x10, 0xcb, 0x69, 0x48,
689        0x1e, 0x8f, 0xf7, 0x40, 0x39, 0x93, 0xec, 0xa, 0x27, 0x29, 0xa1, 0x54, 0xa8, 0x30, 0xc,
690        0x6, 0x3, 0x55, 0x1d, 0x13, 0x1, 0x1, 0xff, 0x4, 0x2, 0x30, 0x0, 0x30, 0xd, 0x6, 0x9, 0x2a,
691        0x86, 0x48, 0x86, 0xf7, 0xd, 0x1, 0x1, 0xb, 0x5, 0x0, 0x3, 0x82, 0x1, 0x1, 0x0, 0x97, 0x9d,
692        0x3, 0x97, 0xd8, 0x60, 0xf8, 0x2e, 0xe1, 0x5d, 0x31, 0x1c, 0x79, 0x6e, 0xba, 0xfb, 0x22,
693        0xfa, 0xa7, 0xe0, 0x84, 0xd9, 0xba, 0xb4, 0xc6, 0x1b, 0xbb, 0x57, 0xf3, 0xe6, 0xb4, 0xc1,
694        0x8a, 0x48, 0x37, 0xb8, 0x5c, 0x3c, 0x4e, 0xdb, 0xe4, 0x83, 0x43, 0xf4, 0xd6, 0xa5, 0xd9,
695        0xb1, 0xce, 0xda, 0x8a, 0xe1, 0xfe, 0xd4, 0x91, 0x29, 0x21, 0x73, 0x5, 0x8e, 0x5e, 0xe1,
696        0xcb, 0xdd, 0x6b, 0xda, 0xc0, 0x75, 0x57, 0xc6, 0xa0, 0xe8, 0xd3, 0x68, 0x25, 0xba, 0x15,
697        0x9e, 0x7f, 0xb5, 0xad, 0x8c, 0xda, 0xf8, 0x4, 0x86, 0x8c, 0xf9, 0xe, 0x8f, 0x1f, 0x8a,
698        0xea, 0x17, 0xc0, 0x16, 0xb5, 0x5c, 0x2a, 0x7a, 0xd4, 0x97, 0xc8, 0x94, 0xfb, 0x71, 0xd7,
699        0x53, 0xd7, 0x9b, 0x9a, 0x48, 0x4b, 0x6c, 0x37, 0x6d, 0x72, 0x3b, 0x99, 0x8d, 0x2e, 0x1d,
700        0x43, 0x6, 0xbf, 0x10, 0x33, 0xb5, 0xae, 0xf8, 0xcc, 0xa5, 0xcb, 0xb2, 0x56, 0x8b, 0x69,
701        0x24, 0x22, 0x6d, 0x22, 0xa3, 0x58, 0xab, 0x7d, 0x87, 0xe4, 0xac, 0x5f, 0x2e, 0x9, 0x1a,
702        0xa7, 0x15, 0x79, 0xf3, 0xa5, 0x69, 0x9, 0x49, 0x7d, 0x72, 0xf5, 0x4e, 0x6, 0xba, 0xc1,
703        0xc3, 0xb4, 0x41, 0x3b, 0xba, 0x5e, 0xaf, 0x94, 0xc3, 0xb6, 0x4f, 0x34, 0xf9, 0xeb, 0xa4,
704        0x1a, 0xcb, 0x6a, 0xe2, 0x83, 0x77, 0x6d, 0x36, 0x46, 0x53, 0x78, 0x48, 0xfe, 0xe8, 0x84,
705        0xbd, 0xdd, 0xf5, 0xb1, 0xba, 0x57, 0x98, 0x54, 0xcf, 0xfd, 0xce, 0xba, 0xc3, 0x44, 0x5,
706        0x95, 0x27, 0xe5, 0x6d, 0xd5, 0x98, 0xf8, 0xf5, 0x66, 0x71, 0x5a, 0xbe, 0x43, 0x1, 0xdd,
707        0x19, 0x11, 0x30, 0xe6, 0xb9, 0xf0, 0xc6, 0x40, 0x39, 0x12, 0x53, 0xe2, 0x29, 0x80, 0x3f,
708        0x3a, 0xef, 0x27, 0x4b, 0xed, 0xbf, 0xde, 0x3f, 0xcb, 0xbd, 0x42, 0xea, 0xd6, 0x79,
709    ];
710
711    const SAMPLE_AUTH_DATA_MAKE_CREDENTIAL: [u8; 164] = [
712        0x58, 0xA2, // bytes(162)
713        // authData
714        0xc2, 0x89, 0xc5, 0xca, 0x9b, 0x04, 0x60, 0xf9, 0x34, 0x6a, 0xb4, 0xe4, 0x2d, 0x84,
715        0x27, // rp_id_hash
716        0x43, 0x40, 0x4d, 0x31, 0xf4, 0x84, 0x68, 0x25, 0xa6, 0xd0, 0x65, 0xbe, 0x59, 0x7a,
717        0x87, // rp_id_hash
718        0x05, 0x1d, // rp_id_hash
719        0xC1, // authData Flags
720        0x00, 0x00, 0x00, 0x0b, // authData counter
721        0xf8, 0xa0, 0x11, 0xf3, 0x8c, 0x0a, 0x4d, 0x15, 0x80, 0x06, 0x17, 0x11, 0x1f, 0x9e, 0xdc,
722        0x7d, // AAGUID
723        0x00, 0x10, // credential id length
724        0x89, 0x59, 0xce, 0xad, 0x5b, 0x5c, 0x48, 0x16, 0x4e, 0x8a, 0xbc, 0xd6, 0xd9, 0x43, 0x5c,
725        0x6f, // credential id
726        // credential public key
727        0xa5, 0x01, 0x02, 0x03, 0x26, 0x20, 0x01, 0x21, 0x58, 0x20, 0xa5, 0xfd, 0x5c, 0xe1, 0xb1,
728        0xc4, 0x58, 0xc5, 0x30, 0xa5, 0x4f, 0xa6, 0x1b, 0x31, 0xbf, 0x6b, 0x04, 0xbe, 0x8b, 0x97,
729        0xaf, 0xde, 0x54, 0xdd, 0x8c, 0xbb, 0x69, 0x27, 0x5a, 0x8a, 0x1b, 0xe1, 0x22, 0x58, 0x20,
730        0xfa, 0x3a, 0x32, 0x31, 0xdd, 0x9d, 0xee, 0xd9, 0xd1, 0x89, 0x7b, 0xe5, 0xa6, 0x22, 0x8c,
731        0x59, 0x50, 0x1e, 0x4b, 0xcd, 0x12, 0x97, 0x5d, 0x3d, 0xff, 0x73, 0x0f, 0x01, 0x27, 0x8e,
732        0xa6, 0x1c, // pub key end
733        // Extensions
734        0xA1, // map(1)
735        0x6B, // text(11)
736        0x68, 0x6D, 0x61, 0x63, 0x2D, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, // "hmac-secret"
737        0xF5, // true
738    ];
739
740    const SAMPLE_AUTH_DATA_GET_ASSERTION: [u8; 229] = [
741        0x58, 0xE3, // bytes(227)
742        // authData
743        0xc2, 0x89, 0xc5, 0xca, 0x9b, 0x04, 0x60, 0xf9, 0x34, 0x6a, 0xb4, 0xe4, 0x2d, 0x84,
744        0x27, // rp_id_hash
745        0x43, 0x40, 0x4d, 0x31, 0xf4, 0x84, 0x68, 0x25, 0xa6, 0xd0, 0x65, 0xbe, 0x59, 0x7a,
746        0x87, // rp_id_hash
747        0x05, 0x1d, // rp_id_hash
748        0xC1, // authData Flags
749        0x00, 0x00, 0x00, 0x0b, // authData counter
750        0xf8, 0xa0, 0x11, 0xf3, 0x8c, 0x0a, 0x4d, 0x15, 0x80, 0x06, 0x17, 0x11, 0x1f, 0x9e, 0xdc,
751        0x7d, // AAGUID
752        0x00, 0x10, // credential id length
753        0x89, 0x59, 0xce, 0xad, 0x5b, 0x5c, 0x48, 0x16, 0x4e, 0x8a, 0xbc, 0xd6, 0xd9, 0x43, 0x5c,
754        0x6f, // credential id
755        // credential public key
756        0xa5, 0x01, 0x02, 0x03, 0x26, 0x20, 0x01, 0x21, 0x58, 0x20, 0xa5, 0xfd, 0x5c, 0xe1, 0xb1,
757        0xc4, 0x58, 0xc5, 0x30, 0xa5, 0x4f, 0xa6, 0x1b, 0x31, 0xbf, 0x6b, 0x04, 0xbe, 0x8b, 0x97,
758        0xaf, 0xde, 0x54, 0xdd, 0x8c, 0xbb, 0x69, 0x27, 0x5a, 0x8a, 0x1b, 0xe1, 0x22, 0x58, 0x20,
759        0xfa, 0x3a, 0x32, 0x31, 0xdd, 0x9d, 0xee, 0xd9, 0xd1, 0x89, 0x7b, 0xe5, 0xa6, 0x22, 0x8c,
760        0x59, 0x50, 0x1e, 0x4b, 0xcd, 0x12, 0x97, 0x5d, 0x3d, 0xff, 0x73, 0x0f, 0x01, 0x27, 0x8e,
761        0xa6, 0x1c, // pub key end
762        // Extensions
763        0xA1, // map(1)
764        0x6B, // text(11)
765        0x68, 0x6D, 0x61, 0x63, 0x2D, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, // "hmac-secret"
766        0x58, 0x40, // bytes(64)
767        0x1F, 0x91, 0x52, 0x6C, 0xAE, 0x45, 0x6E, 0x4C, 0xBB, 0x71, 0xC4, 0xDD, 0xE7, 0xBB, 0x87,
768        0x71, 0x57, 0xE6, 0xE5, 0x4D, 0xFE, 0xD3, 0x01, 0x5D, 0x7D, 0x4D, 0xBB, 0x22, 0x69, 0xAF,
769        0xCD, 0xE6, 0xA9, 0x1B, 0x8D, 0x26, 0x7E, 0xBB, 0xF8, 0x48, 0xEB, 0x95, 0xA6, 0x8E, 0x79,
770        0xC7, 0xAC, 0x70, 0x5E, 0x35, 0x1D, 0x54, 0x3D, 0xB0, 0x16, 0x58, 0x87, 0xD6, 0x29, 0x0F,
771        0xD4, 0x7A, 0x40, 0xC4,
772    ];
773
774    #[test]
775    fn parse_cert_chain() {
776        let cert: AttestationCertificate = from_slice(&SAMPLE_CERT_CHAIN[1..]).unwrap();
777        assert_eq!(&cert.0, &SAMPLE_CERT_CHAIN[4..]);
778
779        let _cert: Vec<AttestationCertificate> = from_slice(&SAMPLE_CERT_CHAIN).unwrap();
780    }
781
782    #[test]
783    fn parse_attestation_object() {
784        let value: AttestationObject = from_slice(&SAMPLE_ATTESTATION).unwrap();
785        println!("{:?}", value);
786
787        //assert_eq!(true, false);
788    }
789
790    #[test]
791    fn parse_reader() {
792        let v: Vec<u8> = vec![
793            0x66, 0x66, 0x6f, 0x6f, 0x62, 0x61, 0x72, 0x66, 0x66, 0x6f, 0x6f, 0x62, 0x61, 0x72,
794        ];
795        let (rest, value): (&[u8], String) = from_slice_stream(&v).unwrap();
796        assert_eq!(value, "foobar");
797        assert_eq!(rest, &[0x66, 0x66, 0x6f, 0x6f, 0x62, 0x61, 0x72]);
798        let (rest, value): (&[u8], String) = from_slice_stream(rest).unwrap();
799        assert_eq!(value, "foobar");
800        assert!(rest.is_empty());
801    }
802
803    #[test]
804    fn parse_extensions() {
805        let auth_make: AuthenticatorData = from_slice(&SAMPLE_AUTH_DATA_MAKE_CREDENTIAL).unwrap();
806        assert_eq!(
807            auth_make.extensions.hmac_secret,
808            Some(HmacSecretResponse::Confirmed(true))
809        );
810        let auth_get: AuthenticatorData = from_slice(&SAMPLE_AUTH_DATA_GET_ASSERTION).unwrap();
811        assert_eq!(
812            auth_get.extensions.hmac_secret,
813            Some(HmacSecretResponse::Secret(vec![
814                0x1F, 0x91, 0x52, 0x6C, 0xAE, 0x45, 0x6E, 0x4C, 0xBB, 0x71, 0xC4, 0xDD, 0xE7, 0xBB,
815                0x87, 0x71, 0x57, 0xE6, 0xE5, 0x4D, 0xFE, 0xD3, 0x01, 0x5D, 0x7D, 0x4D, 0xBB, 0x22,
816                0x69, 0xAF, 0xCD, 0xE6, 0xA9, 0x1B, 0x8D, 0x26, 0x7E, 0xBB, 0xF8, 0x48, 0xEB, 0x95,
817                0xA6, 0x8E, 0x79, 0xC7, 0xAC, 0x70, 0x5E, 0x35, 0x1D, 0x54, 0x3D, 0xB0, 0x16, 0x58,
818                0x87, 0xD6, 0x29, 0x0F, 0xD4, 0x7A, 0x40, 0xC4,
819            ]))
820        );
821    }
822
823    /// See: https://github.com/mozilla/authenticator-rs/issues/187
824    #[test]
825    fn test_aaguid_output() {
826        let input = [
827            0xcb, 0x69, 0x48, 0x1e, 0x8f, 0xf0, 0x00, 0x39, 0x93, 0xec, 0x0a, 0x27, 0x29, 0xa1,
828            0x54, 0xa8,
829        ];
830        let expected = "AAGuid(cb69481e-8ff0-0039-93ec-0a2729a154a8)";
831        let result = AAGuid::from(&input).expect("Failed to parse AAGuid");
832        let res_str = format!("{:?}", result);
833        assert_eq!(expected, &res_str);
834    }
835}