1use super::{
2 Command, CommandError, PinAuthCommand, Request, RequestCtap1, RequestCtap2, Retryable,
3 StatusCode,
4};
5use crate::consts::{PARAMETER_SIZE, U2F_REGISTER, U2F_REQUEST_USER_PRESENCE};
6use crate::crypto::{serialize_key, COSEAlgorithm, COSEEC2Key, COSEKey, COSEKeyType, ECDSACurve};
7use crate::ctap2::attestation::{
8 AAGuid, AttestationObject, AttestationStatement, AttestedCredentialData, AuthenticatorData,
9 AuthenticatorDataFlags,
10};
11use crate::ctap2::client_data::CollectedClientData;
12use crate::ctap2::commands::client_pin::{Pin, PinAuth};
13use crate::ctap2::server::{
14 PublicKeyCredentialDescriptor, PublicKeyCredentialParameters, RelyingPartyWrapper, User,
15};
16use crate::transport::errors::{ApduErrorStatus, HIDError};
17use crate::u2ftypes::{U2FAPDUHeader, U2FDevice};
18use nom::{
19 bytes::complete::{tag, take},
20 error::VerboseError,
21 number::complete::be_u8,
22};
23#[cfg(test)]
24use serde::Deserialize;
25use serde::{
26 de::Error as DesError,
27 ser::{Error as SerError, SerializeMap},
28 Serialize, Serializer,
29};
30use serde_cbor::{self, de::from_slice, ser, Value};
31use std::fmt;
32use std::io;
33
34#[derive(Debug)]
35pub enum MakeCredentialsResult {
36 CTAP1(Vec<u8>),
37 CTAP2(AttestationObject, CollectedClientData),
38}
39
40#[derive(Copy, Clone, Debug, Serialize)]
41#[cfg_attr(test, derive(Deserialize))]
42pub struct MakeCredentialsOptions {
43 #[serde(rename = "rk", skip_serializing_if = "Option::is_none")]
44 pub resident_key: Option<bool>,
45 #[serde(rename = "uv", skip_serializing_if = "Option::is_none")]
46 pub user_verification: Option<bool>,
47 }
50
51impl Default for MakeCredentialsOptions {
52 fn default() -> Self {
53 Self {
54 resident_key: None,
55 user_verification: None,
56 }
57 }
58}
59
60impl MakeCredentialsOptions {
61 pub(crate) fn has_some(&self) -> bool {
62 self.resident_key.is_some() || self.user_verification.is_some()
63 }
64}
65
66pub(crate) trait UserVerification {
67 fn ask_user_verification(&self) -> bool;
68}
69
70impl UserVerification for MakeCredentialsOptions {
71 fn ask_user_verification(&self) -> bool {
72 if let Some(e) = self.user_verification {
73 e
74 } else {
75 false
76 }
77 }
78}
79
80#[derive(Debug, Clone, Serialize, Default)]
81pub struct MakeCredentialsExtensions {
82 #[serde(rename = "pinMinLength", skip_serializing_if = "Option::is_none")]
83 pub pin_min_length: Option<bool>,
84 #[serde(rename = "hmac-secret", skip_serializing_if = "Option::is_none")]
85 pub hmac_secret: Option<bool>,
86}
87
88impl MakeCredentialsExtensions {
89 fn has_extensions(&self) -> bool {
90 self.pin_min_length.or(self.hmac_secret).is_some()
91 }
92}
93
94#[derive(Debug, Clone)]
95pub struct MakeCredentials {
96 pub(crate) client_data: CollectedClientData,
97 pub(crate) rp: RelyingPartyWrapper,
98 pub(crate) user: Option<User>,
100 pub(crate) pub_cred_params: Vec<PublicKeyCredentialParameters>,
101 pub(crate) exclude_list: Vec<PublicKeyCredentialDescriptor>,
102
103 pub(crate) extensions: MakeCredentialsExtensions,
110 pub(crate) options: MakeCredentialsOptions,
111 pub(crate) pin: Option<Pin>,
112 pub(crate) pin_auth: Option<PinAuth>,
113 }
115
116impl MakeCredentials {
117 pub fn new(
118 client_data: CollectedClientData,
119 rp: RelyingPartyWrapper,
120 user: Option<User>,
121 pub_cred_params: Vec<PublicKeyCredentialParameters>,
122 exclude_list: Vec<PublicKeyCredentialDescriptor>,
123 options: MakeCredentialsOptions,
124 extensions: MakeCredentialsExtensions,
125 pin: Option<Pin>,
126 ) -> Self {
127 Self {
128 client_data,
129 rp,
130 user,
131 pub_cred_params,
132 exclude_list,
133 extensions,
134 options,
135 pin,
136 pin_auth: None,
137 }
138 }
139}
140
141impl PinAuthCommand for MakeCredentials {
142 fn pin(&self) -> &Option<Pin> {
143 &self.pin
144 }
145
146 fn set_pin(&mut self, pin: Option<Pin>) {
147 self.pin = pin;
148 }
149
150 fn pin_auth(&self) -> &Option<PinAuth> {
151 &self.pin_auth
152 }
153
154 fn set_pin_auth(&mut self, pin_auth: Option<PinAuth>) {
155 self.pin_auth = pin_auth;
156 }
157
158 fn client_data(&self) -> &CollectedClientData {
159 &self.client_data
160 }
161
162 fn unset_uv_option(&mut self) {
163 self.options.user_verification = None;
164 }
165}
166
167impl Serialize for MakeCredentials {
168 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
169 where
170 S: Serializer,
171 {
172 debug!("Serialize MakeCredentials");
173 let mut map_len = 4;
176 if !self.exclude_list.is_empty() {
177 map_len += 1;
178 }
179 if self.extensions.has_extensions() {
180 map_len += 1;
181 }
182 if self.options.has_some() {
183 map_len += 1;
184 }
185 if self.pin_auth.is_some() {
186 map_len += 2;
187 }
188
189 let mut map = serializer.serialize_map(Some(map_len))?;
190 let client_data_hash = self
191 .client_data
192 .hash()
193 .map_err(|e| S::Error::custom(format!("error while hashing client data: {}", e)))?;
194 map.serialize_entry(&1, &client_data_hash)?;
195 match self.rp {
196 RelyingPartyWrapper::Data(ref d) => {
197 map.serialize_entry(&2, &d)?;
198 }
199 _ => {
200 return Err(S::Error::custom(
201 "Can't serialize a RelyingParty::Hash for CTAP2",
202 ));
203 }
204 }
205 map.serialize_entry(&3, &self.user)?;
206 map.serialize_entry(&4, &self.pub_cred_params)?;
207 if !self.exclude_list.is_empty() {
208 map.serialize_entry(&5, &self.exclude_list)?;
209 }
210 if self.extensions.has_extensions() {
211 map.serialize_entry(&6, &self.extensions)?;
212 }
213 if self.options.has_some() {
214 map.serialize_entry(&7, &self.options)?;
215 }
216 if let Some(pin_auth) = &self.pin_auth {
217 map.serialize_entry(&8, &pin_auth)?;
218 map.serialize_entry(&9, &1)?; }
220 map.end()
221 }
222}
223
224impl Request<MakeCredentialsResult> for MakeCredentials {
225 fn is_ctap2_request(&self) -> bool {
226 self.user.is_some()
227 }
228}
229
230impl RequestCtap1 for MakeCredentials {
231 type Output = MakeCredentialsResult;
232
233 fn apdu_format<Dev>(&self, _dev: &mut Dev) -> Result<Vec<u8>, HIDError>
234 where
235 Dev: U2FDevice,
236 {
237 let flags = if self.options.ask_user_verification() {
245 U2F_REQUEST_USER_PRESENCE
246 } else {
247 0
248 };
249
250 let mut register_data = Vec::with_capacity(2 * PARAMETER_SIZE);
251 if self.is_ctap2_request() {
252 register_data.extend_from_slice(
253 self.client_data
254 .hash()
255 .map_err(|e| HIDError::Command(CommandError::Json(e)))?
256 .as_ref(),
257 );
258 } else {
259 let decoded =
260 base64::decode_config(&self.client_data.challenge.0, base64::URL_SAFE_NO_PAD)
261 .map_err(|_| HIDError::DeviceError)?; register_data.extend_from_slice(&decoded);
263 }
264 register_data.extend_from_slice(self.rp.hash().as_ref());
265 let cmd = U2F_REGISTER;
266 let apdu = U2FAPDUHeader::serialize(cmd, flags, ®ister_data)?;
267
268 Ok(apdu)
269 }
270
271 fn handle_response_ctap1(
272 &self,
273 status: Result<(), ApduErrorStatus>,
274 input: &[u8],
275 ) -> Result<Self::Output, Retryable<HIDError>> {
276 if Err(ApduErrorStatus::ConditionsNotSatisfied) == status {
277 return Err(Retryable::Retry);
278 }
279 if let Err(err) = status {
280 return Err(Retryable::Error(HIDError::ApduStatus(err)));
281 }
282
283 if self.is_ctap2_request() {
284 let parse_register = |input| {
285 let (rest, _) = tag(&[0x05])(input)?;
286 let (rest, public_key) = take(65u8)(rest)?;
287 let (rest, key_handle_len) = be_u8(rest)?;
288 let (rest, key_handle) = take(key_handle_len)(rest)?;
289 Ok((rest, public_key, key_handle))
290 };
291 let (rest, public_key, key_handle) = parse_register(input)
292 .map_err(|e: nom::Err<VerboseError<_>>| {
293 error!("error while parsing registration: {:?}", e);
294 CommandError::Deserializing(DesError::custom("unable to parse registration"))
295 })
296 .map_err(HIDError::Command)
297 .map_err(Retryable::Error)?;
298 let (x, y) = serialize_key(ECDSACurve::SECP256R1, public_key)
312 .map_err(|e| HIDError::Command(CommandError::Crypto(e.into())))
313 .map_err(Retryable::Error)?;
314 let credential_public_key = COSEKey {
315 alg: COSEAlgorithm::ES256,
316 key: COSEKeyType::EC2(COSEEC2Key {
317 curve: ECDSACurve::SECP256R1,
318 x: x.to_vec(),
319 y: y.to_vec(),
320 }),
321 };
322 let auth_data = AuthenticatorData {
323 rp_id_hash: self.rp.hash(),
324 flags: AuthenticatorDataFlags::USER_PRESENT | AuthenticatorDataFlags::ATTESTED,
328 counter: 0,
329 credential_data: Some(AttestedCredentialData {
330 aaguid: AAGuid::default(),
331 credential_id: Vec::from(key_handle),
332 credential_public_key,
333 }),
334 extensions: Default::default(),
335 };
336
337 let att_statement = AttestationStatement::Unparsed(rest.to_vec());
340 let attestation_object = AttestationObject {
341 auth_data,
342 att_statement,
343 };
344 let client_data = self.client_data.clone();
345
346 Ok(MakeCredentialsResult::CTAP2(
347 attestation_object,
348 client_data,
349 ))
350 } else {
351 Ok(MakeCredentialsResult::CTAP1(input.to_vec()))
352 }
353 }
354}
355
356impl RequestCtap2 for MakeCredentials {
357 type Output = MakeCredentialsResult;
358
359 fn command() -> Command {
360 Command::MakeCredentials
361 }
362
363 fn wire_format<Dev>(&self, _dev: &mut Dev) -> Result<Vec<u8>, HIDError>
364 where
365 Dev: U2FDevice + io::Read + io::Write + fmt::Debug,
366 {
367 Ok(ser::to_vec(&self).map_err(CommandError::Serializing)?)
368 }
369
370 fn handle_response_ctap2<Dev>(
371 &self,
372 _dev: &mut Dev,
373 input: &[u8],
374 ) -> Result<Self::Output, HIDError>
375 where
376 Dev: U2FDevice + io::Read + io::Write + fmt::Debug,
377 {
378 if input.is_empty() {
379 return Err(HIDError::Command(CommandError::InputTooSmall));
380 }
381
382 let status: StatusCode = input[0].into();
383 debug!("response status code: {:?}", status);
384 if input.len() > 1 {
385 if status.is_ok() {
386 let attestation = from_slice(&input[1..]).map_err(CommandError::Deserializing)?;
387 let client_data = self.client_data.clone();
388 Ok(MakeCredentialsResult::CTAP2(attestation, client_data))
389 } else {
390 let data: Value = from_slice(&input[1..]).map_err(CommandError::Deserializing)?;
391 Err(HIDError::Command(CommandError::StatusCode(
392 status,
393 Some(data),
394 )))
395 }
396 } else if status.is_ok() {
397 Err(HIDError::Command(CommandError::InputTooSmall))
398 } else {
399 Err(HIDError::Command(CommandError::StatusCode(status, None)))
400 }
401 }
402}
403
404#[cfg(test)]
405pub mod test {
406 use super::{MakeCredentials, MakeCredentialsOptions, MakeCredentialsResult};
407 use crate::crypto::{COSEAlgorithm, COSEEC2Key, COSEKey, COSEKeyType, ECDSACurve};
408 use crate::ctap2::attestation::{
409 AAGuid, AttestationCertificate, AttestationObject, AttestationStatement,
410 AttestationStatementPacked, AttestedCredentialData, AuthenticatorData,
411 AuthenticatorDataFlags, Signature,
412 };
413 use crate::ctap2::client_data::{Challenge, CollectedClientData, TokenBinding, WebauthnType};
414 use crate::ctap2::commands::{RequestCtap1, RequestCtap2};
415 use crate::ctap2::server::RpIdHash;
416 use crate::ctap2::server::{
417 PublicKeyCredentialParameters, RelyingParty, RelyingPartyWrapper, User,
418 };
419 use crate::transport::device_selector::Device;
420 use crate::transport::hid::HIDDevice;
421 use serde_bytes::ByteBuf;
422
423 #[test]
424 fn test_make_credentials_ctap2() {
425 let req = MakeCredentials::new(
426 CollectedClientData {
427 webauthn_type: WebauthnType::Create,
428 challenge: Challenge::from(vec![0x00, 0x01, 0x02, 0x03]),
429 origin: String::from("example.com"),
430 cross_origin: false,
431 token_binding: Some(TokenBinding::Present(String::from("AAECAw"))),
432 },
433 RelyingPartyWrapper::Data(RelyingParty {
434 id: String::from("example.com"),
435 name: Some(String::from("Acme")),
436 icon: None,
437 }),
438 Some(User {
439 id: base64::decode_config(
440 "MIIBkzCCATigAwIBAjCCAZMwggE4oAMCAQIwggGTMII=",
441 base64::URL_SAFE_NO_PAD,
442 )
443 .unwrap(),
444 icon: Some("https://pics.example.com/00/p/aBjjjpqPb.png".to_string()),
445 name: Some(String::from("johnpsmith@example.com")),
446 display_name: Some(String::from("John P. Smith")),
447 }),
448 vec![
449 PublicKeyCredentialParameters {
450 alg: COSEAlgorithm::ES256,
451 },
452 PublicKeyCredentialParameters {
453 alg: COSEAlgorithm::RS256,
454 },
455 ],
456 Vec::new(),
457 MakeCredentialsOptions {
458 resident_key: Some(true),
459 user_verification: None,
460 },
461 Default::default(),
462 None,
463 );
464
465 let mut device = Device::new("commands/make_credentials").unwrap(); let req_serialized = req
467 .wire_format(&mut device)
468 .expect("Failed to serialize MakeCredentials request");
469 assert_eq!(req_serialized, MAKE_CREDENTIALS_SAMPLE_REQUEST_CTAP2);
470 let (attestation_object, _collected_client_data) = match req
471 .handle_response_ctap2(&mut device, &MAKE_CREDENTIALS_SAMPLE_RESPONSE_CTAP2)
472 .expect("Failed to handle CTAP2 response")
473 {
474 MakeCredentialsResult::CTAP2(attestation_object, _collected_client_data) => {
475 (attestation_object, _collected_client_data)
476 }
477 _ => panic!("Got CTAP1 Result, but CTAP2 expected"),
478 };
479
480 let expected = AttestationObject {
481 auth_data: AuthenticatorData {
482 rp_id_hash: RpIdHash::from(&[
483 0xc2, 0x89, 0xc5, 0xca, 0x9b, 0x04, 0x60, 0xf9, 0x34, 0x6a, 0xb4, 0xe4, 0x2d,
484 0x84, 0x27, 0x43, 0x40, 0x4d, 0x31, 0xf4, 0x84, 0x68, 0x25, 0xa6, 0xd0, 0x65,
485 0xbe, 0x59, 0x7a, 0x87, 0x5, 0x1d,
486 ])
487 .unwrap(),
488 flags: AuthenticatorDataFlags::USER_PRESENT | AuthenticatorDataFlags::ATTESTED,
489 counter: 11,
490 credential_data: Some(AttestedCredentialData {
491 aaguid: AAGuid::from(&[
492 0xf8, 0xa0, 0x11, 0xf3, 0x8c, 0x0a, 0x4d, 0x15, 0x80, 0x06, 0x17, 0x11,
493 0x1f, 0x9e, 0xdc, 0x7d,
494 ])
495 .unwrap(),
496 credential_id: vec![
497 0x89, 0x59, 0xce, 0xad, 0x5b, 0x5c, 0x48, 0x16, 0x4e, 0x8a, 0xbc, 0xd6,
498 0xd9, 0x43, 0x5c, 0x6f,
499 ],
500 credential_public_key: COSEKey {
501 alg: COSEAlgorithm::ES256,
502 key: COSEKeyType::EC2(COSEEC2Key {
503 curve: ECDSACurve::SECP256R1,
504 x: vec![
505 0xA5, 0xFD, 0x5C, 0xE1, 0xB1, 0xC4, 0x58, 0xC5, 0x30, 0xA5, 0x4F,
506 0xA6, 0x1B, 0x31, 0xBF, 0x6B, 0x04, 0xBE, 0x8B, 0x97, 0xAF, 0xDE,
507 0x54, 0xDD, 0x8C, 0xBB, 0x69, 0x27, 0x5A, 0x8A, 0x1B, 0xE1,
508 ],
509 y: vec![
510 0xFA, 0x3A, 0x32, 0x31, 0xDD, 0x9D, 0xEE, 0xD9, 0xD1, 0x89, 0x7B,
511 0xE5, 0xA6, 0x22, 0x8C, 0x59, 0x50, 0x1E, 0x4B, 0xCD, 0x12, 0x97,
512 0x5D, 0x3D, 0xFF, 0x73, 0x0F, 0x01, 0x27, 0x8E, 0xA6, 0x1C,
513 ],
514 }),
515 },
516 }),
517 extensions: Default::default(),
518 },
519 att_statement: AttestationStatement::Packed(AttestationStatementPacked {
520 alg: COSEAlgorithm::ES256,
521 sig: Signature(ByteBuf::from([
522 0x30, 0x45, 0x02, 0x20, 0x13, 0xf7, 0x3c, 0x5d, 0x9d, 0x53, 0x0e, 0x8c, 0xc1,
523 0x5c, 0xc9, 0xbd, 0x96, 0xad, 0x58, 0x6d, 0x39, 0x36, 0x64, 0xe4, 0x62, 0xd5,
524 0xf0, 0x56, 0x12, 0x35, 0xe6, 0x35, 0x0f, 0x2b, 0x72, 0x89, 0x02, 0x21, 0x00,
525 0x90, 0x35, 0x7f, 0xf9, 0x10, 0xcc, 0xb5, 0x6a, 0xc5, 0xb5, 0x96, 0x51, 0x19,
526 0x48, 0x58, 0x1c, 0x8f, 0xdd, 0xb4, 0xa2, 0xb7, 0x99, 0x59, 0x94, 0x80, 0x78,
527 0xb0, 0x9f, 0x4b, 0xdc, 0x62, 0x29,
528 ])),
529 attestation_cert: vec![AttestationCertificate(vec![
530 0x30, 0x82, 0x01, 0x93, 0x30, 0x82, 0x01, 0x38, 0xa0, 0x03, 0x02, 0x01, 0x02,
531 0x02, 0x09, 0x00, 0x85, 0x9b, 0x72, 0x6c, 0xb2, 0x4b, 0x4c, 0x29, 0x30, 0x0a,
532 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, 0x30, 0x47, 0x31,
533 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31,
534 0x14, 0x30, 0x12, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x0b, 0x59, 0x75, 0x62,
535 0x69, 0x63, 0x6f, 0x20, 0x54, 0x65, 0x73, 0x74, 0x31, 0x22, 0x30, 0x20, 0x06,
536 0x03, 0x55, 0x04, 0x0b, 0x0c, 0x19, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74,
537 0x69, 0x63, 0x61, 0x74, 0x6f, 0x72, 0x20, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74,
538 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x36, 0x31, 0x32,
539 0x30, 0x34, 0x31, 0x31, 0x35, 0x35, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x36,
540 0x31, 0x32, 0x30, 0x32, 0x31, 0x31, 0x35, 0x35, 0x30, 0x30, 0x5a, 0x30, 0x47,
541 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53,
542 0x31, 0x14, 0x30, 0x12, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x0b, 0x59, 0x75,
543 0x62, 0x69, 0x63, 0x6f, 0x20, 0x54, 0x65, 0x73, 0x74, 0x31, 0x22, 0x30, 0x20,
544 0x06, 0x03, 0x55, 0x04, 0x0b, 0x0c, 0x19, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e,
545 0x74, 0x69, 0x63, 0x61, 0x74, 0x6f, 0x72, 0x20, 0x41, 0x74, 0x74, 0x65, 0x73,
546 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a,
547 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d,
548 0x03, 0x01, 0x07, 0x03, 0x42, 0x00, 0x04, 0xad, 0x11, 0xeb, 0x0e, 0x88, 0x52,
549 0xe5, 0x3a, 0xd5, 0xdf, 0xed, 0x86, 0xb4, 0x1e, 0x61, 0x34, 0xa1, 0x8e, 0xc4,
550 0xe1, 0xaf, 0x8f, 0x22, 0x1a, 0x3c, 0x7d, 0x6e, 0x63, 0x6c, 0x80, 0xea, 0x13,
551 0xc3, 0xd5, 0x04, 0xff, 0x2e, 0x76, 0x21, 0x1b, 0xb4, 0x45, 0x25, 0xb1, 0x96,
552 0xc4, 0x4c, 0xb4, 0x84, 0x99, 0x79, 0xcf, 0x6f, 0x89, 0x6e, 0xcd, 0x2b, 0xb8,
553 0x60, 0xde, 0x1b, 0xf4, 0x37, 0x6b, 0xa3, 0x0d, 0x30, 0x0b, 0x30, 0x09, 0x06,
554 0x03, 0x55, 0x1d, 0x13, 0x04, 0x02, 0x30, 0x00, 0x30, 0x0a, 0x06, 0x08, 0x2a,
555 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, 0x03, 0x49, 0x00, 0x30, 0x46, 0x02,
556 0x21, 0x00, 0xe9, 0xa3, 0x9f, 0x1b, 0x03, 0x19, 0x75, 0x25, 0xf7, 0x37, 0x3e,
557 0x10, 0xce, 0x77, 0xe7, 0x80, 0x21, 0x73, 0x1b, 0x94, 0xd0, 0xc0, 0x3f, 0x3f,
558 0xda, 0x1f, 0xd2, 0x2d, 0xb3, 0xd0, 0x30, 0xe7, 0x02, 0x21, 0x00, 0xc4, 0xfa,
559 0xec, 0x34, 0x45, 0xa8, 0x20, 0xcf, 0x43, 0x12, 0x9c, 0xdb, 0x00, 0xaa, 0xbe,
560 0xfd, 0x9a, 0xe2, 0xd8, 0x74, 0xf9, 0xc5, 0xd3, 0x43, 0xcb, 0x2f, 0x11, 0x3d,
561 0xa2, 0x37, 0x23, 0xf3,
562 ])],
563 }),
564 };
565
566 assert_eq!(attestation_object, expected);
567 }
568
569 #[test]
570 fn test_make_credentials_ctap1() {
571 let req = MakeCredentials::new(
572 CollectedClientData {
573 webauthn_type: WebauthnType::Create,
574 challenge: Challenge::new(vec![0x00, 0x01, 0x02, 0x03]),
575 origin: String::from("example.com"),
576 cross_origin: false,
577 token_binding: Some(TokenBinding::Present(String::from("AAECAw"))),
578 },
579 RelyingPartyWrapper::Data(RelyingParty {
580 id: String::from("example.com"),
581 name: Some(String::from("Acme")),
582 icon: None,
583 }),
584 Some(User {
585 id: base64::decode_config(
586 "MIIBkzCCATigAwIBAjCCAZMwggE4oAMCAQIwggGTMII=",
587 base64::URL_SAFE_NO_PAD,
588 )
589 .unwrap(),
590 icon: Some("https://pics.example.com/00/p/aBjjjpqPb.png".to_string()),
591 name: Some(String::from("johnpsmith@example.com")),
592 display_name: Some(String::from("John P. Smith")),
593 }),
594 vec![
595 PublicKeyCredentialParameters {
596 alg: COSEAlgorithm::ES256,
597 },
598 PublicKeyCredentialParameters {
599 alg: COSEAlgorithm::RS256,
600 },
601 ],
602 Vec::new(),
603 MakeCredentialsOptions {
604 resident_key: Some(true),
605 user_verification: None,
606 },
607 Default::default(),
608 None,
609 );
610
611 let mut device = Device::new("commands/make_credentials").unwrap(); let req_serialized = req
613 .apdu_format(&mut device)
614 .expect("Failed to serialize MakeCredentials request");
615 assert_eq!(
616 req_serialized, MAKE_CREDENTIALS_SAMPLE_REQUEST_CTAP1,
617 "\nGot: {:X?}\nExpected: {:X?}",
618 req_serialized, MAKE_CREDENTIALS_SAMPLE_REQUEST_CTAP1
619 );
620 let (attestation_object, _collected_client_data) = match req
621 .handle_response_ctap1(Ok(()), &MAKE_CREDENTIALS_SAMPLE_RESPONSE_CTAP1)
622 .expect("Failed to handle CTAP1 response")
623 {
624 MakeCredentialsResult::CTAP2(attestation_object, _collected_client_data) => {
625 (attestation_object, _collected_client_data)
626 }
627 _ => panic!("Got CTAP1 Result, but CTAP2 expected"),
628 };
629
630 let expected = AttestationObject {
631 auth_data: AuthenticatorData {
632 rp_id_hash: RpIdHash::from(&[
633 0xA3, 0x79, 0xA6, 0xF6, 0xEE, 0xAF, 0xB9, 0xA5, 0x5E, 0x37, 0x8C, 0x11, 0x80,
634 0x34, 0xE2, 0x75, 0x1E, 0x68, 0x2F, 0xAB, 0x9F, 0x2D, 0x30, 0xAB, 0x13, 0xD2,
635 0x12, 0x55, 0x86, 0xCE, 0x19, 0x47,
636 ])
637 .unwrap(),
638 flags: AuthenticatorDataFlags::USER_PRESENT | AuthenticatorDataFlags::ATTESTED,
639 counter: 0,
640 credential_data: Some(AttestedCredentialData {
641 aaguid: AAGuid::default(),
642 credential_id: vec![
643 0x3E, 0xBD, 0x89, 0xBF, 0x77, 0xEC, 0x50, 0x97, 0x55, 0xEE, 0x9C, 0x26,
644 0x35, 0xEF, 0xAA, 0xAC, 0x7B, 0x2B, 0x9C, 0x5C, 0xEF, 0x17, 0x36, 0xC3,
645 0x71, 0x7D, 0xA4, 0x85, 0x34, 0xC8, 0xC6, 0xB6, 0x54, 0xD7, 0xFF, 0x94,
646 0x5F, 0x50, 0xB5, 0xCC, 0x4E, 0x78, 0x05, 0x5B, 0xDD, 0x39, 0x6B, 0x64,
647 0xF7, 0x8D, 0xA2, 0xC5, 0xF9, 0x62, 0x00, 0xCC, 0xD4, 0x15, 0xCD, 0x08,
648 0xFE, 0x42, 0x00, 0x38,
649 ],
650 credential_public_key: COSEKey {
651 alg: COSEAlgorithm::ES256,
652 key: COSEKeyType::EC2(COSEEC2Key {
653 curve: ECDSACurve::SECP256R1,
654 x: vec![
655 0xE8, 0x76, 0x25, 0x89, 0x6E, 0xE4, 0xE4, 0x6D, 0xC0, 0x32, 0x76,
656 0x6E, 0x80, 0x87, 0x96, 0x2F, 0x36, 0xDF, 0x9D, 0xFE, 0x8B, 0x56,
657 0x7F, 0x37, 0x63, 0x01, 0x5B, 0x19, 0x90, 0xA6, 0x0E, 0x14,
658 ],
659 y: vec![
660 0x27, 0xDE, 0x61, 0x2D, 0x66, 0x41, 0x8B, 0xDA, 0x19, 0x50, 0x58,
661 0x1E, 0xBC, 0x5C, 0x8C, 0x1D, 0xAD, 0x71, 0x0C, 0xB1, 0x4C, 0x22,
662 0xF8, 0xC9, 0x70, 0x45, 0xF4, 0x61, 0x2F, 0xB2, 0x0C, 0x91,
663 ],
664 }),
665 },
666 }),
667 extensions: Default::default(),
668 },
669 att_statement: AttestationStatement::Unparsed(vec![
670 0x30, 0x82, 0x02, 0x4A, 0x30, 0x82, 0x01, 0x32, 0xA0, 0x03, 0x02, 0x01, 0x02, 0x02,
671 0x04, 0x04, 0x6C, 0x88, 0x22, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7,
672 0x0D, 0x01, 0x01, 0x0B, 0x05, 0x00, 0x30, 0x2E, 0x31, 0x2C, 0x30, 0x2A, 0x06, 0x03,
673 0x55, 0x04, 0x03, 0x13, 0x23, 0x59, 0x75, 0x62, 0x69, 0x63, 0x6F, 0x20, 0x55, 0x32,
674 0x46, 0x20, 0x52, 0x6F, 0x6F, 0x74, 0x20, 0x43, 0x41, 0x20, 0x53, 0x65, 0x72, 0x69,
675 0x61, 0x6C, 0x20, 0x34, 0x35, 0x37, 0x32, 0x30, 0x30, 0x36, 0x33, 0x31, 0x30, 0x20,
676 0x17, 0x0D, 0x31, 0x34, 0x30, 0x38, 0x30, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
677 0x5A, 0x18, 0x0F, 0x32, 0x30, 0x35, 0x30, 0x30, 0x39, 0x30, 0x34, 0x30, 0x30, 0x30,
678 0x30, 0x30, 0x30, 0x5A, 0x30, 0x2C, 0x31, 0x2A, 0x30, 0x28, 0x06, 0x03, 0x55, 0x04,
679 0x03, 0x0C, 0x21, 0x59, 0x75, 0x62, 0x69, 0x63, 0x6F, 0x20, 0x55, 0x32, 0x46, 0x20,
680 0x45, 0x45, 0x20, 0x53, 0x65, 0x72, 0x69, 0x61, 0x6C, 0x20, 0x32, 0x34, 0x39, 0x31,
681 0x38, 0x32, 0x33, 0x32, 0x34, 0x37, 0x37, 0x30, 0x30, 0x59, 0x30, 0x13, 0x06, 0x07,
682 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02, 0x01, 0x06, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D,
683 0x03, 0x01, 0x07, 0x03, 0x42, 0x00, 0x04, 0x3C, 0xCA, 0xB9, 0x2C, 0xCB, 0x97, 0x28,
684 0x7E, 0xE8, 0xE6, 0x39, 0x43, 0x7E, 0x21, 0xFC, 0xD6, 0xB6, 0xF1, 0x65, 0xB2, 0xD5,
685 0xA3, 0xF3, 0xDB, 0x13, 0x1D, 0x31, 0xC1, 0x6B, 0x74, 0x2B, 0xB4, 0x76, 0xD8, 0xD1,
686 0xE9, 0x90, 0x80, 0xEB, 0x54, 0x6C, 0x9B, 0xBD, 0xF5, 0x56, 0xE6, 0x21, 0x0F, 0xD4,
687 0x27, 0x85, 0x89, 0x9E, 0x78, 0xCC, 0x58, 0x9E, 0xBE, 0x31, 0x0F, 0x6C, 0xDB, 0x9F,
688 0xF4, 0xA3, 0x3B, 0x30, 0x39, 0x30, 0x22, 0x06, 0x09, 0x2B, 0x06, 0x01, 0x04, 0x01,
689 0x82, 0xC4, 0x0A, 0x02, 0x04, 0x15, 0x31, 0x2E, 0x33, 0x2E, 0x36, 0x2E, 0x31, 0x2E,
690 0x34, 0x2E, 0x31, 0x2E, 0x34, 0x31, 0x34, 0x38, 0x32, 0x2E, 0x31, 0x2E, 0x32, 0x30,
691 0x13, 0x06, 0x0B, 0x2B, 0x06, 0x01, 0x04, 0x01, 0x82, 0xE5, 0x1C, 0x02, 0x01, 0x01,
692 0x04, 0x04, 0x03, 0x02, 0x04, 0x30, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86,
693 0xF7, 0x0D, 0x01, 0x01, 0x0B, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x9F, 0x9B,
694 0x05, 0x22, 0x48, 0xBC, 0x4C, 0xF4, 0x2C, 0xC5, 0x99, 0x1F, 0xCA, 0xAB, 0xAC, 0x9B,
695 0x65, 0x1B, 0xBE, 0x5B, 0xDC, 0xDC, 0x8E, 0xF0, 0xAD, 0x2C, 0x1C, 0x1F, 0xFB, 0x36,
696 0xD1, 0x87, 0x15, 0xD4, 0x2E, 0x78, 0xB2, 0x49, 0x22, 0x4F, 0x92, 0xC7, 0xE6, 0xE7,
697 0xA0, 0x5C, 0x49, 0xF0, 0xE7, 0xE4, 0xC8, 0x81, 0xBF, 0x2E, 0x94, 0xF4, 0x5E, 0x4A,
698 0x21, 0x83, 0x3D, 0x74, 0x56, 0x85, 0x1D, 0x0F, 0x6C, 0x14, 0x5A, 0x29, 0x54, 0x0C,
699 0x87, 0x4F, 0x30, 0x92, 0xC9, 0x34, 0xB4, 0x3D, 0x22, 0x2B, 0x89, 0x62, 0xC0, 0xF4,
700 0x10, 0xCE, 0xF1, 0xDB, 0x75, 0x89, 0x2A, 0xF1, 0x16, 0xB4, 0x4A, 0x96, 0xF5, 0xD3,
701 0x5A, 0xDE, 0xA3, 0x82, 0x2F, 0xC7, 0x14, 0x6F, 0x60, 0x04, 0x38, 0x5B, 0xCB, 0x69,
702 0xB6, 0x5C, 0x99, 0xE7, 0xEB, 0x69, 0x19, 0x78, 0x67, 0x03, 0xC0, 0xD8, 0xCD, 0x41,
703 0xE8, 0xF7, 0x5C, 0xCA, 0x44, 0xAA, 0x8A, 0xB7, 0x25, 0xAD, 0x8E, 0x79, 0x9F, 0xF3,
704 0xA8, 0x69, 0x6A, 0x6F, 0x1B, 0x26, 0x56, 0xE6, 0x31, 0xB1, 0xE4, 0x01, 0x83, 0xC0,
705 0x8F, 0xDA, 0x53, 0xFA, 0x4A, 0x8F, 0x85, 0xA0, 0x56, 0x93, 0x94, 0x4A, 0xE1, 0x79,
706 0xA1, 0x33, 0x9D, 0x00, 0x2D, 0x15, 0xCA, 0xBD, 0x81, 0x00, 0x90, 0xEC, 0x72, 0x2E,
707 0xF5, 0xDE, 0xF9, 0x96, 0x5A, 0x37, 0x1D, 0x41, 0x5D, 0x62, 0x4B, 0x68, 0xA2, 0x70,
708 0x7C, 0xAD, 0x97, 0xBC, 0xDD, 0x17, 0x85, 0xAF, 0x97, 0xE2, 0x58, 0xF3, 0x3D, 0xF5,
709 0x6A, 0x03, 0x1A, 0xA0, 0x35, 0x6D, 0x8E, 0x8D, 0x5E, 0xBC, 0xAD, 0xC7, 0x4E, 0x07,
710 0x16, 0x36, 0xC6, 0xB1, 0x10, 0xAC, 0xE5, 0xCC, 0x9B, 0x90, 0xDF, 0xEA, 0xCA, 0xE6,
711 0x40, 0xFF, 0x1B, 0xB0, 0xF1, 0xFE, 0x5D, 0xB4, 0xEF, 0xF7, 0xA9, 0x5F, 0x06, 0x07,
712 0x33, 0xF5, 0x30, 0x45, 0x02, 0x20, 0x32, 0x47, 0x79, 0xC6, 0x8F, 0x33, 0x80, 0x28, 0x8A, 0x11,
714 0x97, 0xB6, 0x09, 0x5F, 0x7A, 0x6E, 0xB9, 0xB1, 0xB1, 0xC1, 0x27, 0xF6, 0x6A, 0xE1,
715 0x2A, 0x99, 0xFE, 0x85, 0x32, 0xEC, 0x23, 0xB9, 0x02, 0x21, 0x00, 0xE3, 0x95, 0x16,
716 0xAC, 0x4D, 0x61, 0xEE, 0x64, 0x04, 0x4D, 0x50, 0xB4, 0x15, 0xA6, 0xA4, 0xD4, 0xD8,
717 0x4B, 0xA6, 0xD8, 0x95, 0xCB, 0x5A, 0xB7, 0xA1, 0xAA, 0x7D, 0x08, 0x1D, 0xE3, 0x41,
718 0xFA, ]),
761 };
763
764 assert_eq!(attestation_object, expected);
765 }
766
767 #[rustfmt::skip]
768 pub const MAKE_CREDENTIALS_SAMPLE_RESPONSE_CTAP2: [u8; 660] = [
769 0x00, 0xa3, 0x01, 0x66, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x64, 0x02, 0x58, 0x94, 0xc2, 0x89, 0xc5, 0xca, 0x9b, 0x04, 0x60, 0xf9, 0x34, 0x6a, 0xb4, 0xe4, 0x2d, 0x84, 0x27, 0x43, 0x40, 0x4d, 0x31, 0xf4, 0x84, 0x68, 0x25, 0xa6, 0xd0, 0x65, 0xbe, 0x59, 0x7a, 0x87, 0x05, 0x1d, 0x41, 0x00, 0x00, 0x00, 0x0b, 0xf8, 0xa0, 0x11, 0xf3, 0x8c, 0x0a, 0x4d, 0x15, 0x80, 0x06, 0x17, 0x11, 0x1f, 0x9e, 0xdc, 0x7d, 0x00, 0x10, 0x89, 0x59, 0xce, 0xad, 0x5b, 0x5c, 0x48, 0x16, 0x4e, 0x8a, 0xbc, 0xd6, 0xd9, 0x43, 0x5c, 0x6f, 0xa5, 0x01, 0x02, 0x03, 0x26, 0x20, 0x01, 0x21, 0x58, 0x20, 0xa5, 0xfd, 0x5c, 0xe1, 0xb1, 0xc4,
787 0x58, 0xc5, 0x30, 0xa5, 0x4f, 0xa6, 0x1b, 0x31, 0xbf, 0x6b, 0x04, 0xbe, 0x8b, 0x97, 0xaf, 0xde,
788 0x54, 0xdd, 0x8c, 0xbb, 0x69, 0x27, 0x5a, 0x8a, 0x1b, 0xe1, 0x22, 0x58, 0x20, 0xfa, 0x3a, 0x32,
789 0x31, 0xdd, 0x9d, 0xee, 0xd9, 0xd1, 0x89, 0x7b, 0xe5, 0xa6, 0x22, 0x8c, 0x59, 0x50, 0x1e, 0x4b,
790 0xcd, 0x12, 0x97, 0x5d, 0x3d, 0xff, 0x73, 0x0f, 0x01, 0x27, 0x8e, 0xa6, 0x1c,
791 0x03, 0xa3, 0x63, 0x61, 0x6c, 0x67, 0x26, 0x63, 0x73, 0x69, 0x67, 0x58, 0x47, 0x30, 0x45, 0x02, 0x20, 0x13, 0xf7, 0x3c, 0x5d, 0x9d, 0x53, 0x0e, 0x8c, 0xc1, 0x5c, 0xc9, 0xbd, 0x96, 0xad, 0x58, 0x6d, 0x39, 0x36, 0x64, 0xe4, 0x62, 0xd5, 0xf0, 0x56, 0x12, 0x35, 0xe6, 0x35, 0x0f, 0x2b, 0x72, 0x89, 0x02, 0x21, 0x00, 0x90, 0x35, 0x7f, 0xf9, 0x10, 0xcc, 0xb5, 0x6a, 0xc5, 0xb5, 0x96, 0x51, 0x19, 0x48, 0x58, 0x1c, 0x8f, 0xdd, 0xb4, 0xa2, 0xb7, 0x99, 0x59, 0x94, 0x80, 0x78, 0xb0, 0x9f, 0x4b, 0xdc, 0x62, 0x29, 0x63, 0x78, 0x35, 0x63, 0x81, 0x59, 0x01, 0x97, 0x30, 0x82, 0x01, 0x93, 0x30, 0x82, 0x01, 0x38, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x09, 0x00, 0x85, 0x9b, 0x72, 0x6c, 0xb2, 0x4b,
810 0x4c, 0x29, 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, 0x30,
811 0x47, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31,
812 0x14, 0x30, 0x12, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x0b, 0x59, 0x75, 0x62, 0x69, 0x63,
813 0x6f, 0x20, 0x54, 0x65, 0x73, 0x74, 0x31, 0x22, 0x30, 0x20, 0x06, 0x03, 0x55, 0x04, 0x0b,
814 0x0c, 0x19, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x6f, 0x72,
815 0x20, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x30, 0x1e, 0x17,
816 0x0d, 0x31, 0x36, 0x31, 0x32, 0x30, 0x34, 0x31, 0x31, 0x35, 0x35, 0x30, 0x30, 0x5a, 0x17,
817 0x0d, 0x32, 0x36, 0x31, 0x32, 0x30, 0x32, 0x31, 0x31, 0x35, 0x35, 0x30, 0x30, 0x5a, 0x30,
818 0x47, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31,
819 0x14, 0x30, 0x12, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x0b, 0x59, 0x75, 0x62, 0x69, 0x63,
820 0x6f, 0x20, 0x54, 0x65, 0x73, 0x74, 0x31, 0x22, 0x30, 0x20, 0x06, 0x03, 0x55, 0x04, 0x0b,
821 0x0c, 0x19, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x6f, 0x72,
822 0x20, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x30, 0x59, 0x30,
823 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48,
824 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, 0x42, 0x00, 0x04, 0xad, 0x11, 0xeb, 0x0e, 0x88, 0x52,
825 0xe5, 0x3a, 0xd5, 0xdf, 0xed, 0x86, 0xb4, 0x1e, 0x61, 0x34, 0xa1, 0x8e, 0xc4, 0xe1, 0xaf,
826 0x8f, 0x22, 0x1a, 0x3c, 0x7d, 0x6e, 0x63, 0x6c, 0x80, 0xea, 0x13, 0xc3, 0xd5, 0x04, 0xff,
827 0x2e, 0x76, 0x21, 0x1b, 0xb4, 0x45, 0x25, 0xb1, 0x96, 0xc4, 0x4c, 0xb4, 0x84, 0x99, 0x79,
828 0xcf, 0x6f, 0x89, 0x6e, 0xcd, 0x2b, 0xb8, 0x60, 0xde, 0x1b, 0xf4, 0x37, 0x6b, 0xa3, 0x0d,
829 0x30, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x04, 0x02, 0x30, 0x00, 0x30, 0x0a,
830 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, 0x03, 0x49, 0x00, 0x30, 0x46,
831 0x02, 0x21, 0x00, 0xe9, 0xa3, 0x9f, 0x1b, 0x03, 0x19, 0x75, 0x25, 0xf7, 0x37, 0x3e, 0x10,
832 0xce, 0x77, 0xe7, 0x80, 0x21, 0x73, 0x1b, 0x94, 0xd0, 0xc0, 0x3f, 0x3f, 0xda, 0x1f, 0xd2,
833 0x2d, 0xb3, 0xd0, 0x30, 0xe7, 0x02, 0x21, 0x00, 0xc4, 0xfa, 0xec, 0x34, 0x45, 0xa8, 0x20,
834 0xcf, 0x43, 0x12, 0x9c, 0xdb, 0x00, 0xaa, 0xbe, 0xfd, 0x9a, 0xe2, 0xd8, 0x74, 0xf9, 0xc5,
835 0xd3, 0x43, 0xcb, 0x2f, 0x11, 0x3d, 0xa2, 0x37, 0x23, 0xf3,
836 ];
837
838 #[rustfmt::skip]
839 pub const MAKE_CREDENTIALS_SAMPLE_REQUEST_CTAP2: [u8; 260] = [
840 0xa5, 0x01, 0x58, 0x20, 0x75, 0x35, 0x35, 0x7d, 0x49, 0x6e, 0x33, 0xc8, 0x18, 0x7f, 0xea, 0x8d, 0x11, 0x32, 0x64, 0xaa, 0xa4, 0x52, 0x3e, 0x13, 0x40, 0x14, 0x9f, 0xbe, 0x00, 0x3f, 0x10, 0x87, 0x54, 0xc3, 0x2d, 0x80, 0x02, 0xa2, 0x62, 0x69, 0x64, 0x6b, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x64, 0x6e, 0x61, 0x6d, 0x65, 0x64, 0x41, 0x63, 0x6d, 0x65, 0x03, 0xa4, 0x62, 0x69, 0x64, 0x58, 0x20, 0x30, 0x82, 0x01, 0x93, 0x30, 0x82, 0x01, 0x38, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x30, 0x82, 0x01, 0x93, 0x30, 0x82, 0x01, 0x38, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x30, 0x82, 0x01, 0x93, 0x30, 0x82, 0x64, 0x69, 0x63, 0x6f, 0x6e, 0x78, 0x2b, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x70, 0x69, 0x63, 0x73, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x30, 0x30, 0x2f, 0x70, 0x2f, 0x61, 0x42, 0x6a, 0x6a, 0x6a, 0x70, 0x71, 0x50, 0x62, 0x2e, 0x70, 0x6e, 0x67, 0x64, 0x6e, 0x61, 0x6d, 0x65, 0x76, 0x6a, 0x6f, 0x68, 0x6e, 0x70, 0x73, 0x6d, 0x69, 0x74, 0x68, 0x40, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x6b, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x6d, 0x4a, 0x6f, 0x68, 0x6e, 0x20, 0x50, 0x2e, 0x20, 0x53, 0x6d, 0x69, 0x74, 0x68, 0x04, 0x82, 0xa2, 0x63, 0x61, 0x6c, 0x67, 0x26, 0x64, 0x74, 0x79, 0x70, 0x65, 0x6a, 0x70, 0x75, 0x62, 0x6C, 0x69, 0x63, 0x2D, 0x6B, 0x65, 0x79, 0xa2, 0x63, 0x61, 0x6c, 0x67, 0x39, 0x01, 0x00, 0x64, 0x74, 0x79, 0x70, 0x65, 0x6a, 0x70, 0x75, 0x62, 0x6C, 0x69, 0x63, 0x2D, 0x6B, 0x65, 0x79, 0x07, 0xa1, 0x62, 0x72, 0x6b, 0xf5, ];
908
909 pub const MAKE_CREDENTIALS_SAMPLE_REQUEST_CTAP1: [u8; 73] = [
910 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x40, 0x75, 0x35, 0x35, 0x7d, 0x49, 0x6e, 0x33, 0xc8, 0x18, 0x7f, 0xea, 0x8d, 0x11, 0x32, 0x64, 0xaa, 0xa4, 0x52, 0x3e, 0x13, 0x40, 0x14, 0x9f, 0xbe, 0x00, 0x3f, 0x10, 0x87, 0x54, 0xc3, 0x2d, 0x80, 0xA3, 0x79, 0xA6, 0xF6, 0xEE, 0xAF, 0xB9, 0xA5, 0x5E, 0x37, 0x8C, 0x11, 0x80, 0x34, 0xE2,
925 0x75, 0x1E, 0x68, 0x2F, 0xAB, 0x9F, 0x2D, 0x30, 0xAB, 0x13, 0xD2, 0x12, 0x55, 0x86, 0xCE,
926 0x19, 0x47, 0x0, 0x0, ];
928
929 pub const MAKE_CREDENTIALS_SAMPLE_RESPONSE_CTAP1: [u8; 792] = [
930 0x05, 0x04, 0xE8, 0x76, 0x25, 0x89, 0x6E, 0xE4, 0xE4, 0x6D, 0xC0, 0x32, 0x76, 0x6E, 0x80, 0x87,
933 0x96, 0x2F, 0x36, 0xDF, 0x9D, 0xFE, 0x8B, 0x56, 0x7F, 0x37, 0x63, 0x01, 0x5B, 0x19, 0x90,
934 0xA6, 0x0E, 0x14, 0x27, 0xDE, 0x61, 0x2D, 0x66, 0x41, 0x8B, 0xDA, 0x19, 0x50, 0x58, 0x1E,
935 0xBC, 0x5C, 0x8C, 0x1D, 0xAD, 0x71, 0x0C, 0xB1, 0x4C, 0x22, 0xF8, 0xC9, 0x70, 0x45, 0xF4,
936 0x61, 0x2F, 0xB2, 0x0C, 0x91, 0x40, 0x3E, 0xBD, 0x89, 0xBF, 0x77, 0xEC, 0x50, 0x97, 0x55, 0xEE, 0x9C, 0x26, 0x35, 0xEF, 0xAA,
940 0xAC, 0x7B, 0x2B, 0x9C, 0x5C, 0xEF, 0x17, 0x36, 0xC3, 0x71, 0x7D, 0xA4, 0x85, 0x34, 0xC8,
941 0xC6, 0xB6, 0x54, 0xD7, 0xFF, 0x94, 0x5F, 0x50, 0xB5, 0xCC, 0x4E, 0x78, 0x05, 0x5B, 0xDD,
942 0x39, 0x6B, 0x64, 0xF7, 0x8D, 0xA2, 0xC5, 0xF9, 0x62, 0x00, 0xCC, 0xD4, 0x15, 0xCD, 0x08,
943 0xFE, 0x42, 0x00, 0x38, 0x30, 0x82, 0x02, 0x4A, 0x30, 0x82, 0x01, 0x32, 0xA0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x04,
946 0x04, 0x6C, 0x88, 0x22, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01,
947 0x01, 0x0B, 0x05, 0x00, 0x30, 0x2E, 0x31, 0x2C, 0x30, 0x2A, 0x06, 0x03, 0x55, 0x04, 0x03,
948 0x13, 0x23, 0x59, 0x75, 0x62, 0x69, 0x63, 0x6F, 0x20, 0x55, 0x32, 0x46, 0x20, 0x52, 0x6F,
949 0x6F, 0x74, 0x20, 0x43, 0x41, 0x20, 0x53, 0x65, 0x72, 0x69, 0x61, 0x6C, 0x20, 0x34, 0x35,
950 0x37, 0x32, 0x30, 0x30, 0x36, 0x33, 0x31, 0x30, 0x20, 0x17, 0x0D, 0x31, 0x34, 0x30, 0x38,
951 0x30, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5A, 0x18, 0x0F, 0x32, 0x30, 0x35, 0x30,
952 0x30, 0x39, 0x30, 0x34, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5A, 0x30, 0x2C, 0x31, 0x2A,
953 0x30, 0x28, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0C, 0x21, 0x59, 0x75, 0x62, 0x69, 0x63, 0x6F,
954 0x20, 0x55, 0x32, 0x46, 0x20, 0x45, 0x45, 0x20, 0x53, 0x65, 0x72, 0x69, 0x61, 0x6C, 0x20,
955 0x32, 0x34, 0x39, 0x31, 0x38, 0x32, 0x33, 0x32, 0x34, 0x37, 0x37, 0x30, 0x30, 0x59, 0x30,
956 0x13, 0x06, 0x07, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02, 0x01, 0x06, 0x08, 0x2A, 0x86, 0x48,
957 0xCE, 0x3D, 0x03, 0x01, 0x07, 0x03, 0x42, 0x00, 0x04, 0x3C, 0xCA, 0xB9, 0x2C, 0xCB, 0x97,
958 0x28, 0x7E, 0xE8, 0xE6, 0x39, 0x43, 0x7E, 0x21, 0xFC, 0xD6, 0xB6, 0xF1, 0x65, 0xB2, 0xD5,
959 0xA3, 0xF3, 0xDB, 0x13, 0x1D, 0x31, 0xC1, 0x6B, 0x74, 0x2B, 0xB4, 0x76, 0xD8, 0xD1, 0xE9,
960 0x90, 0x80, 0xEB, 0x54, 0x6C, 0x9B, 0xBD, 0xF5, 0x56, 0xE6, 0x21, 0x0F, 0xD4, 0x27, 0x85,
961 0x89, 0x9E, 0x78, 0xCC, 0x58, 0x9E, 0xBE, 0x31, 0x0F, 0x6C, 0xDB, 0x9F, 0xF4, 0xA3, 0x3B,
962 0x30, 0x39, 0x30, 0x22, 0x06, 0x09, 0x2B, 0x06, 0x01, 0x04, 0x01, 0x82, 0xC4, 0x0A, 0x02,
963 0x04, 0x15, 0x31, 0x2E, 0x33, 0x2E, 0x36, 0x2E, 0x31, 0x2E, 0x34, 0x2E, 0x31, 0x2E, 0x34,
964 0x31, 0x34, 0x38, 0x32, 0x2E, 0x31, 0x2E, 0x32, 0x30, 0x13, 0x06, 0x0B, 0x2B, 0x06, 0x01,
965 0x04, 0x01, 0x82, 0xE5, 0x1C, 0x02, 0x01, 0x01, 0x04, 0x04, 0x03, 0x02, 0x04, 0x30, 0x30,
966 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0B, 0x05, 0x00, 0x03,
967 0x82, 0x01, 0x01, 0x00, 0x9F, 0x9B, 0x05, 0x22, 0x48, 0xBC, 0x4C, 0xF4, 0x2C, 0xC5, 0x99,
968 0x1F, 0xCA, 0xAB, 0xAC, 0x9B, 0x65, 0x1B, 0xBE, 0x5B, 0xDC, 0xDC, 0x8E, 0xF0, 0xAD, 0x2C,
969 0x1C, 0x1F, 0xFB, 0x36, 0xD1, 0x87, 0x15, 0xD4, 0x2E, 0x78, 0xB2, 0x49, 0x22, 0x4F, 0x92,
970 0xC7, 0xE6, 0xE7, 0xA0, 0x5C, 0x49, 0xF0, 0xE7, 0xE4, 0xC8, 0x81, 0xBF, 0x2E, 0x94, 0xF4,
971 0x5E, 0x4A, 0x21, 0x83, 0x3D, 0x74, 0x56, 0x85, 0x1D, 0x0F, 0x6C, 0x14, 0x5A, 0x29, 0x54,
972 0x0C, 0x87, 0x4F, 0x30, 0x92, 0xC9, 0x34, 0xB4, 0x3D, 0x22, 0x2B, 0x89, 0x62, 0xC0, 0xF4,
973 0x10, 0xCE, 0xF1, 0xDB, 0x75, 0x89, 0x2A, 0xF1, 0x16, 0xB4, 0x4A, 0x96, 0xF5, 0xD3, 0x5A,
974 0xDE, 0xA3, 0x82, 0x2F, 0xC7, 0x14, 0x6F, 0x60, 0x04, 0x38, 0x5B, 0xCB, 0x69, 0xB6, 0x5C,
975 0x99, 0xE7, 0xEB, 0x69, 0x19, 0x78, 0x67, 0x03, 0xC0, 0xD8, 0xCD, 0x41, 0xE8, 0xF7, 0x5C,
976 0xCA, 0x44, 0xAA, 0x8A, 0xB7, 0x25, 0xAD, 0x8E, 0x79, 0x9F, 0xF3, 0xA8, 0x69, 0x6A, 0x6F,
977 0x1B, 0x26, 0x56, 0xE6, 0x31, 0xB1, 0xE4, 0x01, 0x83, 0xC0, 0x8F, 0xDA, 0x53, 0xFA, 0x4A,
978 0x8F, 0x85, 0xA0, 0x56, 0x93, 0x94, 0x4A, 0xE1, 0x79, 0xA1, 0x33, 0x9D, 0x00, 0x2D, 0x15,
979 0xCA, 0xBD, 0x81, 0x00, 0x90, 0xEC, 0x72, 0x2E, 0xF5, 0xDE, 0xF9, 0x96, 0x5A, 0x37, 0x1D,
980 0x41, 0x5D, 0x62, 0x4B, 0x68, 0xA2, 0x70, 0x7C, 0xAD, 0x97, 0xBC, 0xDD, 0x17, 0x85, 0xAF,
981 0x97, 0xE2, 0x58, 0xF3, 0x3D, 0xF5, 0x6A, 0x03, 0x1A, 0xA0, 0x35, 0x6D, 0x8E, 0x8D, 0x5E,
982 0xBC, 0xAD, 0xC7, 0x4E, 0x07, 0x16, 0x36, 0xC6, 0xB1, 0x10, 0xAC, 0xE5, 0xCC, 0x9B, 0x90,
983 0xDF, 0xEA, 0xCA, 0xE6, 0x40, 0xFF, 0x1B, 0xB0, 0xF1, 0xFE, 0x5D, 0xB4, 0xEF, 0xF7, 0xA9,
984 0x5F, 0x06, 0x07, 0x33, 0xF5, 0x30, 0x45, 0x02, 0x20, 0x32, 0x47, 0x79, 0xC6, 0x8F, 0x33, 0x80, 0x28, 0x8A, 0x11, 0x97,
987 0xB6, 0x09, 0x5F, 0x7A, 0x6E, 0xB9, 0xB1, 0xB1, 0xC1, 0x27, 0xF6, 0x6A, 0xE1, 0x2A, 0x99,
988 0xFE, 0x85, 0x32, 0xEC, 0x23, 0xB9, 0x02, 0x21, 0x00, 0xE3, 0x95, 0x16, 0xAC, 0x4D, 0x61,
989 0xEE, 0x64, 0x04, 0x4D, 0x50, 0xB4, 0x15, 0xA6, 0xA4, 0xD4, 0xD8, 0x4B, 0xA6, 0xD8, 0x95,
990 0xCB, 0x5A, 0xB7, 0xA1, 0xAA, 0x7D, 0x08, 0x1D, 0xE3, 0x41, 0xFA, ];
992}