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(); 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 pub(crate) extensions: GetAssertionExtensions,
192 pub(crate) options: GetAssertionOptions,
193 pub(crate) pin: Option<Pin>,
194 pub(crate) pin_auth: Option<PinAuth>,
195 }
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 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 #[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 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());
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)?; 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 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 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>, 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#[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 } 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]); msg.extend(vec![
721 0xa4, 0x1, 0x6b, 101, 120, 97, 109, 112, 108, 101, 46, 99, 111, 109, 0x2, 0x58, 0x20, 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, 0x3, 0x81, 0xa2, 0x64, 0x74, 0x79, 0x70, ]);
736 device.add_write(&msg, 0);
737
738 msg = cid.to_vec();
739 msg.extend(&[0x0]); msg.extend(vec![
741 0x65, 0x6a, 0x70, 0x75, 0x62, 0x6C, 0x69, 0x63, 0x2D, 0x6B, 0x65, 0x79, 0x62, 0x69, 0x64, 0x58, 0x40, ]);
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]); msg.extend(&assertion.allow_list[0].id[42..]);
754 msg.extend(vec![
755 0x5, 0xa1, 0x62, 0x75, 0x70, 0xf5, ]);
761 device.add_write(&msg, 0);
762
763 let mut msg = cid.to_vec();
765 msg.extend(&[HIDCmd::Cbor.into(), 0x1, 0x5c]); 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]); 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]); 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]); 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]); 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]); msg.extend(&GET_ASSERTION_SAMPLE_RESPONSE_CTAP2[293..]);
791 device.add_read(&msg, 0);
792
793 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 let mut msg = cid.to_vec();
846 msg.extend(&[HIDCmd::Msg.into(), 0x00, 0x8A]); msg.extend(&[0x00, 0x2]); msg.extend(&[flags]);
849 msg.extend(&[0x00, 0x00, 0x00]);
850 msg.extend(&[0x81]); msg.extend(&CLIENT_DATA_HASH);
852 msg.extend(&RELYING_PARTY_HASH[..18]);
853 device.add_write(&msg, 0);
854
855 let mut msg = cid.to_vec();
857 msg.extend(vec![0x00]); 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]); msg.extend(&KEY_HANDLE[44..]);
866 device.add_write(&msg, 0);
867
868 let mut msg = cid.to_vec();
870 msg.extend(&[HIDCmd::Msg.into(), 0x0, 0x4D]); 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]); 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(); let mut cid = [0u8; 4];
917 thread_rng().fill_bytes(&mut cid);
918
919 device.set_cid(cid);
920
921 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 assert_eq!(ctap1_request, GET_ASSERTION_SAMPLE_REQUEST_CTAP1);
931
932 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 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, 0x32, 0x64, 0xaa, 0xa4, 0x52, 0x3e, 0x13, 0x40, 0x14, 0x9f, 0xbe, 0x00, 0x3f, 0x10, 0x87, 0x54, 0xc3, 0x2d, 0x80, ];
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 0x0, 0x2, 0x3, 0x0, 0x0, 0x0, 0x81, 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,
1006 0x75, 0x1E, 0x68, 0x2F, 0xAB, 0x9F, 0x2D, 0x30, 0xAB, 0x13, 0xD2, 0x12, 0x55, 0x86, 0xCE,
1007 0x19, 0x47, 0x40, 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, ];
1017
1018 const GET_ASSERTION_SAMPLE_REQUEST_CTAP2: [u8; 138] = [
1019 0x0, 0x2, 0x3, 0x0, 0x0, 0x0, 0x81, 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, 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, 0x40, 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, ];
1045
1046 const GET_ASSERTION_SAMPLE_RESPONSE_CTAP1: [u8; 75] = [
1047 0x01, 0x00, 0x00, 0x00, 0x3B, 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, 0xA5, 0x01, 0xA2, 0x62, 0x69, 0x64, 0x58, 0x40, 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, 0x64, 0x74, 0x79, 0x70, 0x65, 0x6A, 0x70, 0x75, 0x62, 0x6C, 0x69, 0x63, 0x2D, 0x6B, 0x65, 0x79, 0x02, 0x58, 0x25, 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, 0x03, 0x58, 0x47, 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, 0x04, 0xA4, 0x62, 0x69, 0x64, 0x58, 0x20, 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, 0x64, 0x69, 0x63, 0x6F, 0x6E, 0x78, 0x2B, 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, 0x64, 0x6E, 0x61, 0x6D, 0x65, 0x76, 0x6A, 0x6F, 0x68, 0x6E, 0x70, 0x73, 0x6D, 0x69, 0x74, 0x68, 0x40, 0x65, 0x78, 0x61, 0x6D,
1109 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,
1114 0x68, 0x05, 0x01, ];
1118}