authenticator_ctap2_2021/ctap2/commands/
get_assertion.rs

1use super::{
2    Command, CommandError, PinAuthCommand, Request, RequestCtap1, RequestCtap2, Retryable,
3    StatusCode,
4};
5use crate::consts::{
6    PARAMETER_SIZE, U2F_AUTHENTICATE, U2F_CHECK_IS_REGISTERED, U2F_REQUEST_USER_PRESENCE,
7};
8use crate::crypto::{authenticate, encrypt, COSEKey, CryptoError, ECDHSecret};
9use crate::ctap2::attestation::{AuthenticatorData, AuthenticatorDataFlags};
10use crate::ctap2::client_data::CollectedClientData;
11use crate::ctap2::commands::client_pin::{Pin, PinAuth};
12use crate::ctap2::commands::get_next_assertion::GetNextAssertion;
13use crate::ctap2::commands::make_credentials::UserVerification;
14use crate::ctap2::server::{PublicKeyCredentialDescriptor, RelyingPartyWrapper, User};
15use crate::errors::AuthenticatorError;
16use crate::transport::errors::{ApduErrorStatus, HIDError};
17use crate::transport::FidoDevice;
18use crate::u2ftypes::{U2FAPDUHeader, U2FDevice};
19use nom::{
20    error::VerboseError,
21    number::complete::{be_u32, be_u8},
22    sequence::tuple,
23};
24use serde::{
25    de::{Error as DesError, MapAccess, Visitor},
26    ser::{Error as SerError, SerializeMap},
27    Deserialize, Deserializer, Serialize, Serializer,
28};
29use serde_bytes::ByteBuf;
30use serde_cbor::{de::from_slice, ser, Value};
31use std::convert::TryInto;
32use std::fmt;
33use std::io;
34
35#[derive(Debug, PartialEq)]
36pub enum GetAssertionResult {
37    CTAP1(Vec<u8>),
38    CTAP2(AssertionObject, CollectedClientData),
39}
40
41#[derive(Clone, Copy, Debug, Serialize)]
42#[cfg_attr(test, derive(Deserialize))]
43pub struct GetAssertionOptions {
44    #[serde(rename = "uv", skip_serializing_if = "Option::is_none")]
45    pub user_verification: Option<bool>,
46    #[serde(rename = "up", skip_serializing_if = "Option::is_none")]
47    pub user_presence: Option<bool>,
48}
49
50impl Default for GetAssertionOptions {
51    fn default() -> Self {
52        Self {
53            user_presence: Some(true),
54            user_verification: None,
55        }
56    }
57}
58
59impl GetAssertionOptions {
60    pub(crate) fn has_some(&self) -> bool {
61        self.user_presence.is_some() || self.user_verification.is_some()
62    }
63}
64
65impl UserVerification for GetAssertionOptions {
66    fn ask_user_verification(&self) -> bool {
67        if let Some(e) = self.user_verification {
68            e
69        } else {
70            false
71        }
72    }
73}
74
75#[derive(Debug, Clone)]
76pub struct CalculatedHmacSecretExtension {
77    pub public_key: COSEKey,
78    pub salt_enc: Vec<u8>,
79    pub salt_auth: [u8; 16],
80}
81
82#[derive(Debug, Clone, Default)]
83pub struct HmacSecretExtension {
84    pub salt1: Vec<u8>,
85    pub salt2: Option<Vec<u8>>,
86    calculated_hmac: Option<CalculatedHmacSecretExtension>,
87}
88
89impl HmacSecretExtension {
90    pub fn new(salt1: Vec<u8>, salt2: Option<Vec<u8>>) -> Self {
91        HmacSecretExtension {
92            salt1,
93            salt2,
94            calculated_hmac: None,
95        }
96    }
97
98    pub fn calculate(&mut self, secret: &ECDHSecret) -> Result<(), AuthenticatorError> {
99        if self.salt1.len() < 32 {
100            return Err(CryptoError::WrongSaltLength.into());
101        }
102        let salt_enc = match &self.salt2 {
103            Some(salt2) => {
104                if salt2.len() < 32 {
105                    return Err(CryptoError::WrongSaltLength.into());
106                }
107                let salts = [&self.salt1[..32], &salt2[..32]].concat(); // salt1 || salt2
108                encrypt(secret.shared_secret(), &salts)
109            }
110            None => encrypt(secret.shared_secret(), &self.salt1[..32]),
111        }
112        .map_err(|e| CryptoError::Backend(e))?;
113        let salt_auth_full =
114            authenticate(secret.shared_secret(), &salt_enc).map_err(|e| CryptoError::Backend(e))?;
115        let salt_auth = salt_auth_full
116            .windows(16)
117            .next()
118            .ok_or(AuthenticatorError::InternalError(String::from(
119                "salt_auth too short",
120            )))?
121            .try_into()
122            .map_err(|_| {
123                AuthenticatorError::InternalError(String::from(
124                    "salt_auth conversion failed. Should never happen.",
125                ))
126            })?;
127        let public_key = secret.my_public_key().clone();
128        self.calculated_hmac = Some(CalculatedHmacSecretExtension {
129            public_key,
130            salt_enc,
131            salt_auth,
132        });
133
134        Ok(())
135    }
136}
137
138impl Serialize for HmacSecretExtension {
139    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
140    where
141        S: Serializer,
142    {
143        if let Some(calc) = &self.calculated_hmac {
144            let mut map = serializer.serialize_map(Some(3))?;
145            map.serialize_entry(&1, &calc.public_key)?;
146            map.serialize_entry(&2, serde_bytes::Bytes::new(&calc.salt_enc))?;
147            map.serialize_entry(&3, serde_bytes::Bytes::new(&calc.salt_auth))?;
148            map.end()
149        } else {
150            Err(SerError::custom(
151                "hmac secret has not been calculated before being serialized",
152            ))
153        }
154    }
155}
156
157#[derive(Debug, Clone, Default)]
158pub struct GetAssertionExtensions {
159    pub hmac_secret: Option<HmacSecretExtension>,
160}
161
162impl Serialize for GetAssertionExtensions {
163    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
164    where
165        S: Serializer,
166    {
167        let mut map = serializer.serialize_map(Some(1))?;
168        map.serialize_entry(&"hmac-secret", &self.hmac_secret)?;
169        map.end()
170    }
171}
172
173impl GetAssertionExtensions {
174    fn has_extensions(&self) -> bool {
175        self.hmac_secret.is_some()
176    }
177}
178
179#[derive(Debug, Clone)]
180pub struct GetAssertion {
181    pub(crate) client_data: CollectedClientData,
182    pub(crate) rp: RelyingPartyWrapper,
183    pub(crate) allow_list: Vec<PublicKeyCredentialDescriptor>,
184
185    // https://www.w3.org/TR/webauthn/#client-extension-input
186    // The client extension input, which is a value that can be encoded in JSON,
187    // is passed from the WebAuthn Relying Party to the client in the get() or
188    // create() call, while the CBOR authenticator extension input is passed
189    // from the client to the authenticator for authenticator extensions during
190    // the processing of these calls.
191    pub(crate) extensions: GetAssertionExtensions,
192    pub(crate) options: GetAssertionOptions,
193    pub(crate) pin: Option<Pin>,
194    pub(crate) pin_auth: Option<PinAuth>,
195    //TODO(MS): pinProtocol
196}
197
198impl GetAssertion {
199    pub fn new(
200        client_data: CollectedClientData,
201        rp: RelyingPartyWrapper,
202        allow_list: Vec<PublicKeyCredentialDescriptor>,
203        options: GetAssertionOptions,
204        extensions: GetAssertionExtensions,
205        pin: Option<Pin>,
206    ) -> Self {
207        Self {
208            client_data,
209            rp,
210            allow_list,
211            extensions,
212            options,
213            pin,
214            pin_auth: None,
215        }
216    }
217}
218
219impl PinAuthCommand for GetAssertion {
220    fn pin(&self) -> &Option<Pin> {
221        &self.pin
222    }
223
224    fn set_pin(&mut self, pin: Option<Pin>) {
225        self.pin = pin;
226    }
227
228    fn pin_auth(&self) -> &Option<PinAuth> {
229        &self.pin_auth
230    }
231
232    fn set_pin_auth(&mut self, pin_auth: Option<PinAuth>) {
233        self.pin_auth = pin_auth;
234    }
235
236    fn client_data(&self) -> &CollectedClientData {
237        &self.client_data
238    }
239
240    fn unset_uv_option(&mut self) {
241        self.options.user_verification = None;
242    }
243}
244
245impl Serialize for GetAssertion {
246    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
247    where
248        S: Serializer,
249    {
250        // Need to define how many elements are going to be in the map
251        // beforehand
252        let mut map_len = 2;
253        if !self.allow_list.is_empty() {
254            map_len += 1;
255        }
256        if self.extensions.has_extensions() {
257            map_len += 1;
258        }
259        if self.options.has_some() {
260            map_len += 1;
261        }
262        if self.pin_auth.is_some() {
263            map_len += 2;
264        }
265
266        let mut map = serializer.serialize_map(Some(map_len))?;
267        match self.rp {
268            RelyingPartyWrapper::Data(ref d) => {
269                map.serialize_entry(&1, &d.id)?;
270            }
271            _ => {
272                return Err(S::Error::custom(
273                    "Can't serialize a RelyingParty::Hash for CTAP2",
274                ));
275            }
276        }
277
278        let client_data_hash = self
279            .client_data
280            .hash()
281            .map_err(|e| S::Error::custom(format!("error while hashing client data: {}", e)))?;
282        map.serialize_entry(&2, &client_data_hash)?;
283        if !self.allow_list.is_empty() {
284            map.serialize_entry(&3, &self.allow_list)?;
285        }
286        if self.extensions.has_extensions() {
287            map.serialize_entry(&4, &self.extensions)?;
288        }
289        if self.options.has_some() {
290            map.serialize_entry(&5, &self.options)?;
291        }
292        if let Some(pin_auth) = &self.pin_auth {
293            map.serialize_entry(&6, &pin_auth)?;
294            map.serialize_entry(&7, &1)?;
295        }
296        map.end()
297    }
298}
299
300impl Request<GetAssertionResult> for GetAssertion {
301    fn is_ctap2_request(&self) -> bool {
302        match self.rp {
303            RelyingPartyWrapper::Data(_) => true,
304            RelyingPartyWrapper::Hash(_) => false,
305        }
306    }
307}
308
309impl RequestCtap1 for GetAssertion {
310    type Output = GetAssertionResult;
311
312    fn apdu_format<Dev>(&self, dev: &mut Dev) -> Result<Vec<u8>, HIDError>
313    where
314        Dev: io::Read + io::Write + fmt::Debug + FidoDevice,
315    {
316        /// This command is used to check which key_handle is valid for this
317        /// token this is sent before a GetAssertion command, to determine which
318        /// is valid for a specific token and which key_handle GetAssertion
319        /// should send to the token.
320        #[derive(Debug)]
321        struct GetAssertionCheck<'assertion> {
322            key_handle: &'assertion [u8],
323            client_data: &'assertion CollectedClientData,
324            rp: &'assertion RelyingPartyWrapper,
325        }
326
327        impl<'assertion> RequestCtap1 for GetAssertionCheck<'assertion> {
328            type Output = ();
329
330            fn apdu_format<Dev>(&self, _dev: &mut Dev) -> Result<Vec<u8>, HIDError>
331            where
332                Dev: U2FDevice + io::Read + io::Write + fmt::Debug,
333            {
334                let flags = U2F_CHECK_IS_REGISTERED;
335                // TODO(MS): Need to check "up" here. If up==false, set to 0x08? Or not? Spec is
336                // ambiguous
337                let mut auth_data =
338                    Vec::with_capacity(2 * PARAMETER_SIZE + 1 + self.key_handle.len());
339
340                auth_data.extend_from_slice(
341                    self.client_data
342                        .hash()
343                        .map_err(|e| HIDError::Command(CommandError::Json(e)))?
344                        .as_ref(),
345                );
346                auth_data.extend_from_slice(self.rp.hash().as_ref());
347                auth_data.extend_from_slice(&[self.key_handle.len() as u8]);
348                auth_data.extend_from_slice(self.key_handle);
349                let cmd = U2F_AUTHENTICATE;
350                let apdu = U2FAPDUHeader::serialize(cmd, flags, &auth_data)?;
351                Ok(apdu)
352            }
353
354            fn handle_response_ctap1(
355                &self,
356                status: Result<(), ApduErrorStatus>,
357                _input: &[u8],
358            ) -> Result<Self::Output, Retryable<HIDError>> {
359                match status {
360                    Ok(_) | Err(ApduErrorStatus::ConditionsNotSatisfied) => Ok(()),
361                    Err(e) => Err(Retryable::Error(HIDError::ApduStatus(e))),
362                }
363            }
364        }
365
366        let key_handle = self
367            .allow_list
368            .iter()
369            .find_map(|allowed_handle| {
370                let check_command = GetAssertionCheck {
371                    key_handle: allowed_handle.id.as_ref(),
372                    client_data: &self.client_data,
373                    rp: &self.rp,
374                };
375                let res = dev.send_apdu(&check_command);
376                match res {
377                    Ok(_) => Some(allowed_handle.id.clone()),
378                    _ => None,
379                }
380            })
381            .ok_or(HIDError::DeviceNotSupported)?;
382
383        debug!("sending key_handle = {:?}", key_handle);
384
385        let flags = if self.options.user_presence.unwrap_or(false) {
386            U2F_REQUEST_USER_PRESENCE
387        } else {
388            0
389        };
390        let mut auth_data =
391            Vec::with_capacity(2 * PARAMETER_SIZE + 1 /* key_handle_len */ + key_handle.len());
392
393        if self.is_ctap2_request() {
394            auth_data.extend_from_slice(
395                self.client_data
396                    .hash()
397                    .map_err(|e| HIDError::Command(CommandError::Json(e)))?
398                    .as_ref(),
399            );
400        } else {
401            let decoded =
402                base64::decode_config(&self.client_data.challenge.0, base64::URL_SAFE_NO_PAD)
403                    .map_err(|_| HIDError::DeviceError)?; // We encoded it, so this should never fail
404            auth_data.extend_from_slice(&decoded);
405        }
406        auth_data.extend_from_slice(self.rp.hash().as_ref());
407        auth_data.extend_from_slice(&[key_handle.len() as u8]);
408        auth_data.extend_from_slice(key_handle.as_ref());
409
410        let cmd = U2F_AUTHENTICATE;
411        let apdu = U2FAPDUHeader::serialize(cmd, flags, &auth_data)?;
412        Ok(apdu)
413    }
414
415    fn handle_response_ctap1(
416        &self,
417        status: Result<(), ApduErrorStatus>,
418        input: &[u8],
419    ) -> Result<Self::Output, Retryable<HIDError>> {
420        if Err(ApduErrorStatus::ConditionsNotSatisfied) == status {
421            return Err(Retryable::Retry);
422        }
423        if let Err(err) = status {
424            return Err(Retryable::Error(HIDError::ApduStatus(err)));
425        }
426
427        if self.is_ctap2_request() {
428            let parse_authentication = |input| {
429                // Parsing an u8, then a u32, and the rest is the signature
430                let (rest, (user_presence, counter)) = tuple((be_u8, be_u32))(input)?;
431                let signature = Vec::from(rest);
432                Ok((user_presence, counter, signature))
433            };
434            let (user_presence, counter, signature) = parse_authentication(input)
435                .map_err(|e: nom::Err<VerboseError<_>>| {
436                    error!("error while parsing authentication: {:?}", e);
437                    CommandError::Deserializing(DesError::custom("unable to parse authentication"))
438                })
439                .map_err(HIDError::Command)
440                .map_err(Retryable::Error)?;
441
442            let mut flags = AuthenticatorDataFlags::empty();
443            if user_presence == 1 {
444                flags |= AuthenticatorDataFlags::USER_PRESENT;
445            }
446            let auth_data = AuthenticatorData {
447                rp_id_hash: self.rp.hash(),
448                flags,
449                counter,
450                credential_data: None,
451                extensions: Default::default(),
452            };
453            let assertion = Assertion {
454                credentials: None,
455                signature,
456                user: None,
457                auth_data,
458            };
459
460            Ok(GetAssertionResult::CTAP2(
461                AssertionObject(vec![assertion]),
462                self.client_data.clone(),
463            ))
464        } else {
465            Ok(GetAssertionResult::CTAP1(input.to_vec()))
466        }
467    }
468}
469
470impl RequestCtap2 for GetAssertion {
471    type Output = GetAssertionResult;
472
473    fn command() -> Command {
474        Command::GetAssertion
475    }
476
477    fn wire_format<Dev>(&self, _dev: &mut Dev) -> Result<Vec<u8>, HIDError>
478    where
479        Dev: FidoDevice + io::Read + io::Write + fmt::Debug,
480    {
481        Ok(ser::to_vec(&self).map_err(CommandError::Serializing)?)
482    }
483
484    fn handle_response_ctap2<Dev>(
485        &self,
486        dev: &mut Dev,
487        input: &[u8],
488    ) -> Result<Self::Output, HIDError>
489    where
490        Dev: FidoDevice + io::Read + io::Write + fmt::Debug,
491    {
492        if input.is_empty() {
493            return Err(CommandError::InputTooSmall.into());
494        }
495
496        let status: StatusCode = input[0].into();
497        debug!(
498            "response status code: {:?}, rest: {:?}",
499            status,
500            &input[1..]
501        );
502        if input.len() > 1 {
503            if status.is_ok() {
504                let assertion: GetAssertionResponse =
505                    from_slice(&input[1..]).map_err(CommandError::Deserializing)?;
506                let number_of_credentials = assertion.number_of_credentials.unwrap_or(1);
507                let mut assertions = Vec::with_capacity(number_of_credentials);
508                assertions.push(assertion.into());
509
510                let msg = GetNextAssertion;
511                // We already have one, so skipping 0
512                for _ in 1..number_of_credentials {
513                    let new_cred = dev.send_cbor(&msg)?;
514                    assertions.push(new_cred.into());
515                }
516
517                Ok(GetAssertionResult::CTAP2(
518                    AssertionObject(assertions),
519                    self.client_data.clone(),
520                ))
521            } else {
522                let data: Value = from_slice(&input[1..]).map_err(CommandError::Deserializing)?;
523                Err(CommandError::StatusCode(status, Some(data)).into())
524            }
525        } else if status.is_ok() {
526            Err(CommandError::InputTooSmall.into())
527        } else {
528            Err(CommandError::StatusCode(status, None).into())
529        }
530    }
531}
532
533#[derive(Debug, PartialEq)]
534pub struct Assertion {
535    pub credentials: Option<PublicKeyCredentialDescriptor>, /* Was optional in CTAP2.0, is
536                                                             * mandatory in CTAP2.1 */
537    pub auth_data: AuthenticatorData,
538    pub signature: Vec<u8>,
539    pub user: Option<User>,
540}
541
542impl From<GetAssertionResponse> for Assertion {
543    fn from(r: GetAssertionResponse) -> Self {
544        Assertion {
545            credentials: r.credentials,
546            auth_data: r.auth_data,
547            signature: r.signature,
548            user: r.user,
549        }
550    }
551}
552
553// TODO(baloo): Move this to src/ctap2/mod.rs?
554#[derive(Debug, PartialEq)]
555pub struct AssertionObject(pub Vec<Assertion>);
556
557impl AssertionObject {
558    pub fn u2f_sign_data(&self) -> Vec<u8> {
559        if let Some(first) = self.0.first() {
560            let mut res = Vec::new();
561            res.push(first.auth_data.flags.bits());
562            res.extend(&first.auth_data.counter.to_be_bytes());
563            res.extend(&first.signature);
564            res
565            // first.signature.clone()
566        } else {
567            Vec::new()
568        }
569    }
570}
571
572pub(crate) struct GetAssertionResponse {
573    credentials: Option<PublicKeyCredentialDescriptor>,
574    auth_data: AuthenticatorData,
575    signature: Vec<u8>,
576    user: Option<User>,
577    number_of_credentials: Option<usize>,
578}
579
580impl<'de> Deserialize<'de> for GetAssertionResponse {
581    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
582    where
583        D: Deserializer<'de>,
584    {
585        struct GetAssertionResponseVisitor;
586
587        impl<'de> Visitor<'de> for GetAssertionResponseVisitor {
588            type Value = GetAssertionResponse;
589
590            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
591                formatter.write_str("a byte array")
592            }
593
594            fn visit_map<M>(self, mut map: M) -> Result<Self::Value, M::Error>
595            where
596                M: MapAccess<'de>,
597            {
598                let mut credentials = None;
599                let mut auth_data = None;
600                let mut signature = None;
601                let mut user = None;
602                let mut number_of_credentials = None;
603
604                while let Some(key) = map.next_key()? {
605                    match key {
606                        1 => {
607                            if credentials.is_some() {
608                                return Err(M::Error::duplicate_field("credentials"));
609                            }
610                            credentials = Some(map.next_value()?);
611                        }
612                        2 => {
613                            if auth_data.is_some() {
614                                return Err(M::Error::duplicate_field("auth_data"));
615                            }
616                            auth_data = Some(map.next_value()?);
617                        }
618                        3 => {
619                            if signature.is_some() {
620                                return Err(M::Error::duplicate_field("signature"));
621                            }
622                            let signature_bytes: ByteBuf = map.next_value()?;
623                            let signature_bytes: Vec<u8> = signature_bytes.into_vec();
624                            signature = Some(signature_bytes);
625                        }
626                        4 => {
627                            if user.is_some() {
628                                return Err(M::Error::duplicate_field("user"));
629                            }
630                            user = map.next_value()?;
631                        }
632                        5 => {
633                            if number_of_credentials.is_some() {
634                                return Err(M::Error::duplicate_field("number_of_credentials"));
635                            }
636                            number_of_credentials = Some(map.next_value()?);
637                        }
638                        k => return Err(M::Error::custom(format!("unexpected key: {:?}", k))),
639                    }
640                }
641
642                let auth_data = auth_data.ok_or_else(|| M::Error::missing_field("auth_data"))?;
643                let signature = signature.ok_or_else(|| M::Error::missing_field("signature"))?;
644
645                Ok(GetAssertionResponse {
646                    credentials,
647                    auth_data,
648                    signature,
649                    user,
650                    number_of_credentials,
651                })
652            }
653        }
654
655        deserializer.deserialize_bytes(GetAssertionResponseVisitor)
656    }
657}
658
659#[cfg(test)]
660pub mod test {
661    use super::{Assertion, GetAssertion, GetAssertionOptions, GetAssertionResult};
662    use crate::consts::{
663        HIDCmd, SW_CONDITIONS_NOT_SATISFIED, SW_NO_ERROR, U2F_CHECK_IS_REGISTERED,
664        U2F_REQUEST_USER_PRESENCE,
665    };
666    use crate::ctap2::attestation::{AuthenticatorData, AuthenticatorDataFlags};
667    use crate::ctap2::client_data::{Challenge, CollectedClientData, TokenBinding, WebauthnType};
668    use crate::ctap2::commands::get_assertion::AssertionObject;
669    use crate::ctap2::commands::RequestCtap1;
670    use crate::ctap2::server::{
671        PublicKeyCredentialDescriptor, RelyingParty, RelyingPartyWrapper, RpIdHash, Transport, User,
672    };
673    use crate::transport::device_selector::Device;
674    use crate::transport::hid::HIDDevice;
675    use crate::transport::FidoDevice;
676    use crate::u2ftypes::U2FDevice;
677    use rand::{thread_rng, RngCore};
678
679    #[test]
680    fn test_get_assertion_ctap2() {
681        let client_data = CollectedClientData {
682            webauthn_type: WebauthnType::Create,
683            challenge: Challenge::from(vec![0x00, 0x01, 0x02, 0x03]),
684            origin: String::from("example.com"),
685            cross_origin: false,
686            token_binding: Some(TokenBinding::Present(String::from("AAECAw"))),
687        };
688        let assertion = GetAssertion::new(
689            client_data.clone(),
690            RelyingPartyWrapper::Data(RelyingParty {
691                id: String::from("example.com"),
692                name: Some(String::from("Acme")),
693                icon: None,
694            }),
695            vec![PublicKeyCredentialDescriptor {
696                id: vec![
697                    0x3E, 0xBD, 0x89, 0xBF, 0x77, 0xEC, 0x50, 0x97, 0x55, 0xEE, 0x9C, 0x26, 0x35,
698                    0xEF, 0xAA, 0xAC, 0x7B, 0x2B, 0x9C, 0x5C, 0xEF, 0x17, 0x36, 0xC3, 0x71, 0x7D,
699                    0xA4, 0x85, 0x34, 0xC8, 0xC6, 0xB6, 0x54, 0xD7, 0xFF, 0x94, 0x5F, 0x50, 0xB5,
700                    0xCC, 0x4E, 0x78, 0x05, 0x5B, 0xDD, 0x39, 0x6B, 0x64, 0xF7, 0x8D, 0xA2, 0xC5,
701                    0xF9, 0x62, 0x00, 0xCC, 0xD4, 0x15, 0xCD, 0x08, 0xFE, 0x42, 0x00, 0x38,
702                ],
703                transports: vec![Transport::USB],
704            }],
705            GetAssertionOptions {
706                user_presence: Some(true),
707                user_verification: None,
708            },
709            Default::default(),
710            None,
711        );
712        let mut device = Device::new("commands/get_assertion").unwrap();
713        let mut cid = [0u8; 4];
714        thread_rng().fill_bytes(&mut cid);
715        device.set_cid(cid);
716
717        let mut msg = cid.to_vec();
718        msg.extend(vec![HIDCmd::Cbor.into(), 0x00, 0x90]);
719        msg.extend(vec![0x2]); // u2f command
720        msg.extend(vec![
721            0xa4, // map(4)
722            0x1,  // rpid
723            0x6b, // text(11)
724            101, 120, 97, 109, 112, 108, 101, 46, 99, 111, 109, // example.com
725            0x2, // clientDataHash
726            0x58, 0x20, //bytes(32)
727            0x75, 0x35, 0x35, 0x7d, 0x49, 0x6e, 0x33, 0xc8, 0x18, 0x7f, 0xea, 0x8d, 0x11, 0x32,
728            0x64, 0xaa, 0xa4, 0x52, 0x3e, 0x13, 0x40, 0x14, 0x9f, 0xbe, 0x00, 0x3f, 0x10, 0x87,
729            0x54, 0xc3, 0x2d, 0x80, // hash
730            0x3,  //allowList
731            0x81, // array(1)
732            0xa2, // map(2)
733            0x64, // text(4),
734            0x74, 0x79, 0x70, // typ
735        ]);
736        device.add_write(&msg, 0);
737
738        msg = cid.to_vec();
739        msg.extend(&[0x0]); //SEQ
740        msg.extend(vec![
741            0x65, // e (continuation of type)
742            0x6a, // text(10)
743            0x70, 0x75, 0x62, 0x6C, 0x69, 0x63, 0x2D, 0x6B, 0x65, 0x79, // public-key
744            0x62, // text(2)
745            0x69, 0x64, // id
746            0x58, 0x40, // bytes(64)
747        ]);
748        msg.extend(&assertion.allow_list[0].id[..42]);
749        device.add_write(&msg, 0);
750
751        msg = cid.to_vec();
752        msg.extend(&[0x1]); //SEQ
753        msg.extend(&assertion.allow_list[0].id[42..]);
754        msg.extend(vec![
755            0x5,  // options
756            0xa1, // map(1)
757            0x62, // text(2)
758            0x75, 0x70, // up
759            0xf5, // true
760        ]);
761        device.add_write(&msg, 0);
762
763        // fido response
764        let mut msg = cid.to_vec();
765        msg.extend(&[HIDCmd::Cbor.into(), 0x1, 0x5c]); // cmd + bcnt
766        msg.extend(&GET_ASSERTION_SAMPLE_RESPONSE_CTAP2[..57]);
767        device.add_read(&msg, 0);
768
769        let mut msg = cid.to_vec();
770        msg.extend(&[0x0]); // SEQ
771        msg.extend(&GET_ASSERTION_SAMPLE_RESPONSE_CTAP2[57..116]);
772        device.add_read(&msg, 0);
773
774        let mut msg = cid.to_vec();
775        msg.extend(&[0x1]); // SEQ
776        msg.extend(&GET_ASSERTION_SAMPLE_RESPONSE_CTAP2[116..175]);
777        device.add_read(&msg, 0);
778
779        let mut msg = cid.to_vec();
780        msg.extend(&[0x2]); // SEQ
781        msg.extend(&GET_ASSERTION_SAMPLE_RESPONSE_CTAP2[175..234]);
782        device.add_read(&msg, 0);
783
784        let mut msg = cid.to_vec();
785        msg.extend(&[0x3]); // SEQ
786        msg.extend(&GET_ASSERTION_SAMPLE_RESPONSE_CTAP2[234..293]);
787        device.add_read(&msg, 0);
788        let mut msg = cid.to_vec();
789        msg.extend(&[0x4]); // SEQ
790        msg.extend(&GET_ASSERTION_SAMPLE_RESPONSE_CTAP2[293..]);
791        device.add_read(&msg, 0);
792
793        // Check if response is correct
794        let expected_auth_data = AuthenticatorData {
795            rp_id_hash: RpIdHash([
796                0x62, 0x5d, 0xda, 0xdf, 0x74, 0x3f, 0x57, 0x27, 0xe6, 0x6b, 0xba, 0x8c, 0x2e, 0x38,
797                0x79, 0x22, 0xd1, 0xaf, 0x43, 0xc5, 0x03, 0xd9, 0x11, 0x4a, 0x8f, 0xba, 0x10, 0x4d,
798                0x84, 0xd0, 0x2b, 0xfa,
799            ]),
800            flags: AuthenticatorDataFlags::USER_PRESENT,
801            counter: 0x11,
802            credential_data: None,
803            extensions: Default::default(),
804        };
805
806        let expected_assertion = Assertion {
807            credentials: Some(PublicKeyCredentialDescriptor {
808                id: vec![
809                    242, 32, 6, 222, 79, 144, 90, 246, 138, 67, 148, 47, 2, 79, 42, 94, 206, 96,
810                    61, 156, 109, 75, 61, 248, 190, 8, 237, 1, 252, 68, 38, 70, 208, 52, 133, 138,
811                    199, 91, 237, 63, 213, 128, 191, 152, 8, 217, 79, 203, 238, 130, 185, 178, 239,
812                    102, 119, 175, 10, 220, 195, 88, 82, 234, 107, 158,
813                ],
814                transports: vec![],
815            }),
816            signature: vec![
817                0x30, 0x45, 0x02, 0x20, 0x4a, 0x5a, 0x9d, 0xd3, 0x92, 0x98, 0x14, 0x9d, 0x90, 0x47,
818                0x69, 0xb5, 0x1a, 0x45, 0x14, 0x33, 0x00, 0x6f, 0x18, 0x2a, 0x34, 0xfb, 0xdf, 0x66,
819                0xde, 0x5f, 0xc7, 0x17, 0xd7, 0x5f, 0xb3, 0x50, 0x02, 0x21, 0x00, 0xa4, 0x6b, 0x8e,
820                0xa3, 0xc3, 0xb9, 0x33, 0x82, 0x1c, 0x6e, 0x7f, 0x5e, 0xf9, 0xda, 0xae, 0x94, 0xab,
821                0x47, 0xf1, 0x8d, 0xb4, 0x74, 0xc7, 0x47, 0x90, 0xea, 0xab, 0xb1, 0x44, 0x11, 0xe7,
822                0xa0,
823            ],
824            user: Some(User {
825                id: vec![
826                    0x30, 0x82, 0x01, 0x93, 0x30, 0x82, 0x01, 0x38, 0xa0, 0x03, 0x02, 0x01, 0x02,
827                    0x30, 0x82, 0x01, 0x93, 0x30, 0x82, 0x01, 0x38, 0xa0, 0x03, 0x02, 0x01, 0x02,
828                    0x30, 0x82, 0x01, 0x93, 0x30, 0x82,
829                ],
830                icon: Some("https://pics.example.com/00/p/aBjjjpqPb.png".to_string()),
831                name: Some("johnpsmith@example.com".to_string()),
832                display_name: Some("John P. Smith".to_string()),
833            }),
834            auth_data: expected_auth_data,
835        };
836
837        let expected =
838            GetAssertionResult::CTAP2(AssertionObject(vec![expected_assertion]), client_data);
839        let response = device.send_cbor(&assertion).unwrap();
840        assert_eq!(response, expected);
841    }
842
843    fn fill_device_ctap1(device: &mut Device, cid: [u8; 4], flags: u8, answer_status: [u8; 2]) {
844        // ctap2 request
845        let mut msg = cid.to_vec();
846        msg.extend(&[HIDCmd::Msg.into(), 0x00, 0x8A]); // cmd + bcnt
847        msg.extend(&[0x00, 0x2]); // U2F_AUTHENTICATE
848        msg.extend(&[flags]);
849        msg.extend(&[0x00, 0x00, 0x00]);
850        msg.extend(&[0x81]); // Data len - 7
851        msg.extend(&CLIENT_DATA_HASH);
852        msg.extend(&RELYING_PARTY_HASH[..18]);
853        device.add_write(&msg, 0);
854
855        // Continuation package
856        let mut msg = cid.to_vec();
857        msg.extend(vec![0x00]); // SEQ
858        msg.extend(&RELYING_PARTY_HASH[18..]);
859        msg.extend(&[KEY_HANDLE.len() as u8]);
860        msg.extend(&KEY_HANDLE[..44]);
861        device.add_write(&msg, 0);
862
863        let mut msg = cid.to_vec();
864        msg.extend(vec![0x01]); // SEQ
865        msg.extend(&KEY_HANDLE[44..]);
866        device.add_write(&msg, 0);
867
868        // fido response
869        let mut msg = cid.to_vec();
870        msg.extend(&[HIDCmd::Msg.into(), 0x0, 0x4D]); // cmd + bcnt
871        msg.extend(&GET_ASSERTION_SAMPLE_RESPONSE_CTAP1[0..57]);
872        device.add_read(&msg, 0);
873
874        let mut msg = cid.to_vec();
875        msg.extend(&[0x0]); // SEQ
876        msg.extend(&GET_ASSERTION_SAMPLE_RESPONSE_CTAP1[57..]);
877        msg.extend(&answer_status);
878        device.add_read(&msg, 0);
879    }
880
881    #[test]
882    fn test_get_assertion_ctap1() {
883        let client_data = CollectedClientData {
884            webauthn_type: WebauthnType::Create,
885            challenge: Challenge::from(vec![0x00, 0x01, 0x02, 0x03]),
886            origin: String::from("example.com"),
887            cross_origin: false,
888            token_binding: Some(TokenBinding::Present(String::from("AAECAw"))),
889        };
890        let assertion = GetAssertion::new(
891            client_data.clone(),
892            RelyingPartyWrapper::Data(RelyingParty {
893                id: String::from("example.com"),
894                name: Some(String::from("Acme")),
895                icon: None,
896            }),
897            vec![PublicKeyCredentialDescriptor {
898                id: vec![
899                    0x3E, 0xBD, 0x89, 0xBF, 0x77, 0xEC, 0x50, 0x97, 0x55, 0xEE, 0x9C, 0x26, 0x35,
900                    0xEF, 0xAA, 0xAC, 0x7B, 0x2B, 0x9C, 0x5C, 0xEF, 0x17, 0x36, 0xC3, 0x71, 0x7D,
901                    0xA4, 0x85, 0x34, 0xC8, 0xC6, 0xB6, 0x54, 0xD7, 0xFF, 0x94, 0x5F, 0x50, 0xB5,
902                    0xCC, 0x4E, 0x78, 0x05, 0x5B, 0xDD, 0x39, 0x6B, 0x64, 0xF7, 0x8D, 0xA2, 0xC5,
903                    0xF9, 0x62, 0x00, 0xCC, 0xD4, 0x15, 0xCD, 0x08, 0xFE, 0x42, 0x00, 0x38,
904                ],
905                transports: vec![Transport::USB],
906            }],
907            GetAssertionOptions {
908                user_presence: Some(true),
909                user_verification: None,
910            },
911            Default::default(),
912            None,
913        );
914        let mut device = Device::new("commands/get_assertion").unwrap(); // not really used (all functions ignore it)
915                                                                         // channel id
916        let mut cid = [0u8; 4];
917        thread_rng().fill_bytes(&mut cid);
918
919        device.set_cid(cid);
920
921        // ctap2 request
922        fill_device_ctap1(
923            &mut device,
924            cid,
925            U2F_CHECK_IS_REGISTERED,
926            SW_CONDITIONS_NOT_SATISFIED,
927        );
928        let ctap1_request = assertion.apdu_format(&mut device).unwrap();
929        // Check if the request is going to be correct
930        assert_eq!(ctap1_request, GET_ASSERTION_SAMPLE_REQUEST_CTAP1);
931
932        // Now do it again, but parse the actual response
933        fill_device_ctap1(
934            &mut device,
935            cid,
936            U2F_CHECK_IS_REGISTERED,
937            SW_CONDITIONS_NOT_SATISFIED,
938        );
939        fill_device_ctap1(&mut device, cid, U2F_REQUEST_USER_PRESENCE, SW_NO_ERROR);
940
941        let response = device.send_apdu(&assertion).unwrap();
942
943        // Check if response is correct
944        let expected_auth_data = AuthenticatorData {
945            rp_id_hash: RpIdHash(RELYING_PARTY_HASH),
946            flags: AuthenticatorDataFlags::USER_PRESENT,
947            counter: 0x3B,
948            credential_data: None,
949            extensions: Default::default(),
950        };
951
952        let expected_assertion = Assertion {
953            credentials: None,
954            signature: vec![
955                0x30, 0x44, 0x02, 0x20, 0x7B, 0xDE, 0x0A, 0x52, 0xAC, 0x1F, 0x4C, 0x8B, 0x27, 0xE0,
956                0x03, 0xA3, 0x70, 0xCD, 0x66, 0xA4, 0xC7, 0x11, 0x8D, 0xD2, 0x2D, 0x54, 0x47, 0x83,
957                0x5F, 0x45, 0xB9, 0x9C, 0x68, 0x42, 0x3F, 0xF7, 0x02, 0x20, 0x3C, 0x51, 0x7B, 0x47,
958                0x87, 0x7F, 0x85, 0x78, 0x2D, 0xE1, 0x00, 0x86, 0xA7, 0x83, 0xD1, 0xE7, 0xDF, 0x4E,
959                0x36, 0x39, 0xE7, 0x71, 0xF5, 0xF6, 0xAF, 0xA3, 0x5A, 0xAD, 0x53, 0x73, 0x85, 0x8E,
960            ],
961            user: None,
962            auth_data: expected_auth_data,
963        };
964
965        let expected =
966            GetAssertionResult::CTAP2(AssertionObject(vec![expected_assertion]), client_data);
967
968        assert_eq!(response, expected);
969    }
970
971    const CLIENT_DATA_HASH: [u8; 32] = [
972        0x75, 0x35, 0x35, 0x7d, 0x49, 0x6e, 0x33, 0xc8, 0x18, 0x7f, 0xea, 0x8d, 0x11, // hash
973        0x32, 0x64, 0xaa, 0xa4, 0x52, 0x3e, 0x13, 0x40, 0x14, 0x9f, 0xbe, 0x00, 0x3f, // hash
974        0x10, 0x87, 0x54, 0xc3, 0x2d, 0x80, // hash
975    ];
976
977    const RELYING_PARTY_HASH: [u8; 32] = [
978        0xA3, 0x79, 0xA6, 0xF6, 0xEE, 0xAF, 0xB9, 0xA5, 0x5E, 0x37, 0x8C, 0x11, 0x80, 0x34, 0xE2,
979        0x75, 0x1E, 0x68, 0x2F, 0xAB, 0x9F, 0x2D, 0x30, 0xAB, 0x13, 0xD2, 0x12, 0x55, 0x86, 0xCE,
980        0x19, 0x47,
981    ];
982    const KEY_HANDLE: [u8; 64] = [
983        0x3E, 0xBD, 0x89, 0xBF, 0x77, 0xEC, 0x50, 0x97, 0x55, 0xEE, 0x9C, 0x26, 0x35, 0xEF, 0xAA,
984        0xAC, 0x7B, 0x2B, 0x9C, 0x5C, 0xEF, 0x17, 0x36, 0xC3, 0x71, 0x7D, 0xA4, 0x85, 0x34, 0xC8,
985        0xC6, 0xB6, 0x54, 0xD7, 0xFF, 0x94, 0x5F, 0x50, 0xB5, 0xCC, 0x4E, 0x78, 0x05, 0x5B, 0xDD,
986        0x39, 0x6B, 0x64, 0xF7, 0x8D, 0xA2, 0xC5, 0xF9, 0x62, 0x00, 0xCC, 0xD4, 0x15, 0xCD, 0x08,
987        0xFE, 0x42, 0x00, 0x38,
988    ];
989
990    const GET_ASSERTION_SAMPLE_REQUEST_CTAP1: [u8; 138] = [
991        // CBOR Header
992        0x0, // leading zero
993        0x2, // CMD U2F_Authenticate
994        0x3, // Flags (user presence)
995        0x0, 0x0, // zero bits
996        0x0, 0x81, // size
997        // NOTE: This has been taken from CTAP2.0 spec, but the clientDataHash has been replaced
998        //       to be able to operate with known values for CollectedClientData (spec doesn't say
999        //       what values led to the provided example hash)
1000        // clientDataHash:
1001        0x75, 0x35, 0x35, 0x7d, 0x49, 0x6e, 0x33, 0xc8, 0x18, 0x7f, 0xea, 0x8d, 0x11, // hash
1002        0x32, 0x64, 0xaa, 0xa4, 0x52, 0x3e, 0x13, 0x40, 0x14, 0x9f, 0xbe, 0x00, 0x3f, // hash
1003        0x10, 0x87, 0x54, 0xc3, 0x2d, 0x80, // hash
1004        // rpIdHash:
1005        0xA3, 0x79, 0xA6, 0xF6, 0xEE, 0xAF, 0xB9, 0xA5, 0x5E, 0x37, 0x8C, 0x11, 0x80, 0x34, 0xE2,
1006        0x75, 0x1E, 0x68, 0x2F, 0xAB, 0x9F, 0x2D, 0x30, 0xAB, 0x13, 0xD2, 0x12, 0x55, 0x86, 0xCE,
1007        0x19, 0x47, // ..
1008        // Key Handle Length (1 Byte):
1009        0x40, // ..
1010        // Key Handle (Key Handle Length Bytes):
1011        0x3E, 0xBD, 0x89, 0xBF, 0x77, 0xEC, 0x50, 0x97, 0x55, 0xEE, 0x9C, 0x26, 0x35, 0xEF, 0xAA,
1012        0xAC, 0x7B, 0x2B, 0x9C, 0x5C, 0xEF, 0x17, 0x36, 0xC3, 0x71, 0x7D, 0xA4, 0x85, 0x34, 0xC8,
1013        0xC6, 0xB6, 0x54, 0xD7, 0xFF, 0x94, 0x5F, 0x50, 0xB5, 0xCC, 0x4E, 0x78, 0x05, 0x5B, 0xDD,
1014        0x39, 0x6B, 0x64, 0xF7, 0x8D, 0xA2, 0xC5, 0xF9, 0x62, 0x00, 0xCC, 0xD4, 0x15, 0xCD, 0x08,
1015        0xFE, 0x42, 0x00, 0x38, 0x0, 0x0, // 2 trailing zeros from protocol
1016    ];
1017
1018    const GET_ASSERTION_SAMPLE_REQUEST_CTAP2: [u8; 138] = [
1019        // CBOR Header
1020        0x0, // leading zero
1021        0x2, // CMD U2F_Authenticate
1022        0x3, // Flags (user presence)
1023        0x0, 0x0, // zero bits
1024        0x0, 0x81, // size
1025        // NOTE: This has been taken from CTAP2.0 spec, but the clientDataHash has been replaced
1026        //       to be able to operate with known values for CollectedClientData (spec doesn't say
1027        //       what values led to the provided example hash)
1028        // clientDataHash:
1029        0x75, 0x35, 0x35, 0x7d, 0x49, 0x6e, 0x33, 0xc8, 0x18, 0x7f, 0xea, 0x8d, 0x11, 0x32, 0x64,
1030        0xaa, 0xa4, 0x52, 0x3e, 0x13, 0x40, 0x14, 0x9f, 0xbe, 0x00, 0x3f, 0x10, 0x87, 0x54, 0xc3,
1031        0x2d, 0x80, // hash
1032        // rpIdHash:
1033        0xA3, 0x79, 0xA6, 0xF6, 0xEE, 0xAF, 0xB9, 0xA5, 0x5E, 0x37, 0x8C, 0x11, 0x80, 0x34, 0xE2,
1034        0x75, 0x1E, 0x68, 0x2F, 0xAB, 0x9F, 0x2D, 0x30, 0xAB, 0x13, 0xD2, 0x12, 0x55, 0x86, 0xCE,
1035        0x19, 0x47, // ..
1036        // Key Handle Length (1 Byte):
1037        0x40, // ..
1038        // Key Handle (Key Handle Length Bytes):
1039        0x3E, 0xBD, 0x89, 0xBF, 0x77, 0xEC, 0x50, 0x97, 0x55, 0xEE, 0x9C, 0x26, 0x35, 0xEF, 0xAA,
1040        0xAC, 0x7B, 0x2B, 0x9C, 0x5C, 0xEF, 0x17, 0x36, 0xC3, 0x71, 0x7D, 0xA4, 0x85, 0x34, 0xC8,
1041        0xC6, 0xB6, 0x54, 0xD7, 0xFF, 0x94, 0x5F, 0x50, 0xB5, 0xCC, 0x4E, 0x78, 0x05, 0x5B, 0xDD,
1042        0x39, 0x6B, 0x64, 0xF7, 0x8D, 0xA2, 0xC5, 0xF9, 0x62, 0x00, 0xCC, 0xD4, 0x15, 0xCD, 0x08,
1043        0xFE, 0x42, 0x00, 0x38, 0x0, 0x0, // 2 trailing zeros from protocol
1044    ];
1045
1046    const GET_ASSERTION_SAMPLE_RESPONSE_CTAP1: [u8; 75] = [
1047        0x01, // User Presence (1 Byte)
1048        0x00, 0x00, 0x00, 0x3B, // Sign Count (4 Bytes)
1049        // Signature (variable Length)
1050        0x30, 0x44, 0x02, 0x20, 0x7B, 0xDE, 0x0A, 0x52, 0xAC, 0x1F, 0x4C, 0x8B, 0x27, 0xE0, 0x03,
1051        0xA3, 0x70, 0xCD, 0x66, 0xA4, 0xC7, 0x11, 0x8D, 0xD2, 0x2D, 0x54, 0x47, 0x83, 0x5F, 0x45,
1052        0xB9, 0x9C, 0x68, 0x42, 0x3F, 0xF7, 0x02, 0x20, 0x3C, 0x51, 0x7B, 0x47, 0x87, 0x7F, 0x85,
1053        0x78, 0x2D, 0xE1, 0x00, 0x86, 0xA7, 0x83, 0xD1, 0xE7, 0xDF, 0x4E, 0x36, 0x39, 0xE7, 0x71,
1054        0xF5, 0xF6, 0xAF, 0xA3, 0x5A, 0xAD, 0x53, 0x73, 0x85, 0x8E,
1055    ];
1056
1057    const GET_ASSERTION_SAMPLE_RESPONSE_CTAP2: [u8; 348] = [
1058        0x00, // status == success
1059        0xA5, // map(5)
1060        0x01, // unsigned(1)
1061        0xA2, // map(2)
1062        0x62, // text(2)
1063        0x69, 0x64, // "id"
1064        0x58, 0x40, // bytes(0x64, )
1065        0xF2, 0x20, 0x06, 0xDE, 0x4F, 0x90, 0x5A, 0xF6, 0x8A, 0x43, 0x94, 0x2F, 0x02, 0x4F, 0x2A,
1066        0x5E, 0xCE, 0x60, 0x3D, 0x9C, 0x6D, 0x4B, 0x3D, 0xF8, 0xBE, 0x08, 0xED, 0x01, 0xFC, 0x44,
1067        0x26, 0x46, 0xD0, 0x34, 0x85, 0x8A, 0xC7, 0x5B, 0xED, 0x3F, 0xD5, 0x80, 0xBF, 0x98, 0x08,
1068        0xD9, 0x4F, 0xCB, 0xEE, 0x82, 0xB9, 0xB2, 0xEF, 0x66, 0x77, 0xAF, 0x0A, 0xDC, 0xC3, 0x58,
1069        0x52, 0xEA, 0x6B,
1070        0x9E, // "\x0xF2,  \x0x06, \x0xDE, O\x0x90, Z\x0xF6, \x0x8A, C\x0x94, /\x0x02, O*^\x0xCE, `=\x0x9C, mK=\x0xF8, \x0xBE, \b\x0xED, \x0x01, \x0xFC, D&F\x0xD0, 4\x0x85, \x0x8A, \x0xC7, [\x0xED, ?\x0xD5, \x0x80, \x0xBF, \x0x98, \b\x0xD9, O\x0xCB, \x0xEE, \x0x82, \x0xB9, \x0xB2, \x0xEF, fw\x0xAF, \n\x0xDC, \x0xC3, 0xXR, \x0xEA, k\x0x9E, "
1071        0x64, // text(4)
1072        0x74, 0x79, 0x70, 0x65, // "type"
1073        0x6A, // text(0x10, )
1074        0x70, 0x75, 0x62, 0x6C, 0x69, 0x63, 0x2D, 0x6B, 0x65, 0x79, // "public-key"
1075        0x02, // unsigned(2)
1076        0x58, 0x25, // bytes(0x37, )
1077        0x62, 0x5D, 0xDA, 0xDF, 0x74, 0x3F, 0x57, 0x27, 0xE6, 0x6B, 0xBA, 0x8C, 0x2E, 0x38, 0x79,
1078        0x22, 0xD1, 0xAF, 0x43, 0xC5, 0x03, 0xD9, 0x11, 0x4A, 0x8F, 0xBA, 0x10, 0x4D, 0x84, 0xD0,
1079        0x2B, 0xFA, 0x01, 0x00, 0x00, 0x00,
1080        0x11, // "b]\x0xDA, \x0xDF, t?W'\x0xE6, k\x0xBA, \x0x8C, .8y\"\x0xD1, \x0xAF, C\x0xC5, \x0x03, \x0xD9, \x0x11, J\x0x8F, \x0xBA, \x0x10, M\x0x84, \x0xD0, +\x0xFA, \x0x01, \x0x00, \x0x00, \x0x00, \x0x11, "
1081        0x03, // unsigned(3)
1082        0x58, 0x47, // bytes(0x71, )
1083        0x30, 0x45, 0x02, 0x20, 0x4A, 0x5A, 0x9D, 0xD3, 0x92, 0x98, 0x14, 0x9D, 0x90, 0x47, 0x69,
1084        0xB5, 0x1A, 0x45, 0x14, 0x33, 0x00, 0x6F, 0x18, 0x2A, 0x34, 0xFB, 0xDF, 0x66, 0xDE, 0x5F,
1085        0xC7, 0x17, 0xD7, 0x5F, 0xB3, 0x50, 0x02, 0x21, 0x00, 0xA4, 0x6B, 0x8E, 0xA3, 0xC3, 0xB9,
1086        0x33, 0x82, 0x1C, 0x6E, 0x7F, 0x5E, 0xF9, 0xDA, 0xAE, 0x94, 0xAB, 0x47, 0xF1, 0x8D, 0xB4,
1087        0x74, 0xC7, 0x47, 0x90, 0xEA, 0xAB, 0xB1, 0x44, 0x11, 0xE7,
1088        0xA0, // "0x0E, \x0x02,  0xJZ, \x0x9D, \x0xD3, \x0x92, \x0x98, \x0x14, \x0x9D, \x0x90, Gi\x0xB5, \x0x1A, E\x0x14, 3\x0x00, o\x0x18, *4\x0xFB, \x0xDF, f\x0xDE, _\x0xC7, \x0x17, \x0xD7, _\x0xB3, P\x0x02, !\x0x00, \x0xA4, k\x0x8E, \x0xA3, \x0xC3, \x0xB9, 3\x0x82, \x0x1C, n\x0x7F, ^\x0xF9, \x0xDA, \x0xAE, \x0x94, \x0xAB, G\x0xF1, \x0x8D, \x0xB4, t\x0xC7, G\x0x90, \x0xEA, \x0xAB, \x0xB1, D\x0x11, \x0xE7, \x0xA0, "
1089        0x04, // unsigned(4)
1090        0xA4, // map(4)
1091        0x62, // text(2)
1092        0x69, 0x64, // "id"
1093        0x58, 0x20, // bytes(0x32, )
1094        0x30, 0x82, 0x01, 0x93, 0x30, 0x82, 0x01, 0x38, 0xA0, 0x03, 0x02, 0x01, 0x02, 0x30, 0x82,
1095        0x01, 0x93, 0x30, 0x82, 0x01, 0x38, 0xA0, 0x03, 0x02, 0x01, 0x02, 0x30, 0x82, 0x01, 0x93,
1096        0x30,
1097        0x82, // "0\x0x82, \x0x01, \x0x93, 0\x0x82, \x0x01, 8\x0xA0, \x0x03, \x0x02, \x0x01, \x0x02, 0\x0x82, \x0x01, \x0x93, 0\x0x82, \x0x01, 8\x0xA0, \x0x03, \x0x02, \x0x01, \x0x02, 0\x0x82, \x0x01, \x0x93, 0\x0x82, "
1098        0x64, // text(4)
1099        0x69, 0x63, 0x6F, 0x6E, // "icon"
1100        0x78, 0x2B, // text(0x43, )
1101        0x68, 0x74, 0x74, 0x70, 0x73, 0x3A, 0x2F, 0x2F, 0x70, 0x69, 0x63, 0x73, 0x2E, 0x65, 0x78,
1102        0x61, 0x6D, 0x70, 0x6C, 0x65, 0x2E, 0x63, 0x6F, 0x6D, 0x2F, 0x30, 0x30, 0x2F, 0x70, 0x2F,
1103        0x61, 0x42, 0x6A, 0x6A, 0x6A, 0x70, 0x71, 0x50, 0x62, 0x2E, 0x70, 0x6E,
1104        0x67, // "https://pics.example.com/0x00, /p/aBjjjpqPb.png"
1105        0x64, // text(4)
1106        0x6E, 0x61, 0x6D, 0x65, // "name"
1107        0x76, // text(0x22, )
1108        0x6A, 0x6F, 0x68, 0x6E, 0x70, 0x73, 0x6D, 0x69, 0x74, 0x68, 0x40, 0x65, 0x78, 0x61, 0x6D,
1109        0x70, 0x6C, 0x65, 0x2E, 0x63, 0x6F, 0x6D, // "johnpsmith@example.com"
1110        0x6B, // text(0x11, )
1111        0x64, 0x69, 0x73, 0x70, 0x6C, 0x61, 0x79, 0x4E, 0x61, 0x6D, 0x65, // "displayName"
1112        0x6D, // text(0x13, )
1113        0x4A, 0x6F, 0x68, 0x6E, 0x20, 0x50, 0x2E, 0x20, 0x53, 0x6D, 0x69, 0x74,
1114        0x68, // "John P. Smith"
1115        0x05, // unsigned(5)
1116        0x01, // unsigned(1)
1117    ];
1118}