1use crate::error::WebauthnError;
5use crate::proto::*;
6use serde::Deserialize;
7
8use base64urlsafedata::{Base64UrlSafeData, HumanBinaryData};
9
10use std::borrow::Borrow;
11use std::ops::Deref;
12
13use nom::bytes::complete::{tag, take};
14use nom::combinator::cond;
15use nom::combinator::{map_opt, verify};
16use nom::error::ParseError;
17use nom::number::complete::{be_u16, be_u32, be_u64};
18
19pub type UserId = Vec<u8>;
21
22#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
24pub struct Challenge(Vec<u8>);
25
26impl Challenge {
27 pub(crate) fn new(challenge: Vec<u8>) -> Self {
29 Challenge(challenge)
30 }
31}
32
33impl From<Challenge> for HumanBinaryData {
34 fn from(chal: Challenge) -> Self {
35 HumanBinaryData::from(chal.0)
36 }
37}
38
39impl From<Challenge> for Base64UrlSafeData {
40 fn from(chal: Challenge) -> Self {
41 Base64UrlSafeData::from(chal.0)
42 }
43}
44
45impl From<HumanBinaryData> for Challenge {
46 fn from(d: HumanBinaryData) -> Self {
47 Challenge(d.into())
48 }
49}
50
51impl<'a> From<&'a HumanBinaryData> for &'a ChallengeRef {
52 fn from(d: &'a HumanBinaryData) -> Self {
53 ChallengeRef::new(d.as_slice())
54 }
55}
56
57impl ToOwned for ChallengeRef {
58 type Owned = Challenge;
59
60 fn to_owned(&self) -> Self::Owned {
61 Challenge(self.0.to_vec())
62 }
63}
64
65impl AsRef<[u8]> for ChallengeRef {
66 fn as_ref(&self) -> &[u8] {
67 &self.0
68 }
69}
70
71impl Deref for ChallengeRef {
72 type Target = [u8];
73
74 fn deref(&self) -> &Self::Target {
75 self.as_ref()
76 }
77}
78
79impl Borrow<ChallengeRef> for Challenge {
80 fn borrow(&self) -> &ChallengeRef {
81 ChallengeRef::new(&self.0)
82 }
83}
84
85impl AsRef<ChallengeRef> for Challenge {
86 fn as_ref(&self) -> &ChallengeRef {
87 ChallengeRef::new(&self.0)
88 }
89}
90
91impl Deref for Challenge {
92 type Target = ChallengeRef;
93
94 fn deref(&self) -> &Self::Target {
95 self.as_ref()
96 }
97}
98
99#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
105#[repr(transparent)]
106pub struct ChallengeRef([u8]);
107
108impl ChallengeRef {
109 pub fn new(challenge: &[u8]) -> &ChallengeRef {
111 unsafe { &*(challenge as *const [u8] as *const ChallengeRef) }
115 }
116}
117
118impl PartialEq<Credential> for Credential {
119 fn eq(&self, c: &Credential) -> bool {
120 self.cred_id == c.cred_id
121 }
122}
123#[allow(clippy::too_many_arguments)]
124impl Credential {
125 pub(crate) fn new(
126 acd: &AttestedCredentialData,
127 auth_data: &AuthenticatorData<Registration>,
128 ck: COSEKey,
129 registration_policy: UserVerificationPolicy,
130 attestation: ParsedAttestation,
131 req_extn: &RequestRegistrationExtensions,
132 client_extn: &RegistrationExtensionsClientOutputs,
133 attestation_format: AttestationFormat,
134 transports: &Option<Vec<AuthenticatorTransport>>,
135 ) -> Self {
136 let cred_protect = match (
137 auth_data.extensions.cred_protect.as_ref(),
138 req_extn.cred_protect.is_some(),
139 ) {
140 (Some(credprotect), false) => ExtnState::Unsolicited(credprotect.0),
141 (Some(credprotect), true) => ExtnState::Set(credprotect.0),
142 (None, true) => ExtnState::Ignored,
143 (None, false) => ExtnState::NotRequested,
144 };
145
146 let hmac_create_secret = match (
147 auth_data.extensions.hmac_secret.as_ref(),
148 req_extn.hmac_create_secret.is_some(),
149 ) {
150 (Some(hmac_create_secret), false) => ExtnState::Unsolicited(*hmac_create_secret),
151 (Some(hmac_create_secret), true) => ExtnState::Set(*hmac_create_secret),
152 (None, true) => ExtnState::Ignored,
153 (None, false) => ExtnState::NotRequested,
154 };
155
156 let appid = ExtnState::NotRequested;
157 let cred_props = match (
169 client_extn.cred_props.as_ref(),
170 req_extn.cred_props.is_some(),
171 ) {
172 (Some(b), _) => ExtnState::Unsigned(b.clone()),
173 (None, true) => ExtnState::Ignored,
174 (None, false) => ExtnState::NotRequested,
175 };
176
177 let extensions = RegisteredExtensions {
178 cred_protect,
179 hmac_create_secret,
180 appid,
181 cred_props,
182 };
183
184 trace!(?extensions);
185 let counter = auth_data.counter;
186 let user_verified = auth_data.user_verified;
187 let backup_eligible = auth_data.backup_eligible;
188 let backup_state = auth_data.backup_state;
189
190 let transports = if attestation_format == AttestationFormat::Packed
191 || attestation_format == AttestationFormat::Tpm
192 {
193 transports.clone()
194 } else {
195 None
196 };
197
198 Credential {
199 cred_id: acd.credential_id.clone(),
200 cred: ck,
201 counter,
202 transports,
203 user_verified,
204 backup_eligible,
205 backup_state,
206 registration_policy,
207 extensions,
208 attestation,
209 attestation_format,
210 }
211 }
212}
213
214pub(crate) fn process_authentication_extensions(
215 auth_extn: &AuthenticationSignedExtensions,
216) -> AuthenticationExtensions {
217 trace!(?auth_extn);
218 AuthenticationExtensions {}
219}
220
221impl TryFrom<i128> for ECDSACurve {
244 type Error = WebauthnError;
245 fn try_from(u: i128) -> Result<Self, Self::Error> {
246 match u {
247 1 => Ok(ECDSACurve::SECP256R1),
248 2 => Ok(ECDSACurve::SECP384R1),
249 3 => Ok(ECDSACurve::SECP521R1),
250 _ => Err(WebauthnError::COSEKeyECDSAInvalidCurve),
251 }
252 }
253}
254
255impl TryFrom<i128> for EDDSACurve {
256 type Error = WebauthnError;
257 fn try_from(u: i128) -> Result<Self, Self::Error> {
258 match u {
259 6 => Ok(EDDSACurve::ED25519),
260 7 => Ok(EDDSACurve::ED448),
261 _ => Err(WebauthnError::COSEKeyEDDSAInvalidCurve),
262 }
263 }
264}
265
266fn cbor_parser(i: &[u8]) -> nom::IResult<&[u8], serde_cbor_2::Value> {
267 let mut deserializer = serde_cbor_2::Deserializer::from_slice(i);
268 let v = serde::de::Deserialize::deserialize(&mut deserializer).map_err(|e| {
269 error!(?e, "cbor_parser");
270 nom::Err::Failure(nom::error::Error::from_error_kind(
271 i,
272 nom::error::ErrorKind::Fail,
273 ))
274 })?;
275
276 let len = deserializer.byte_offset();
277
278 Ok((&i[len..], v))
279}
280
281fn extensions_parser<T: Ceremony>(i: &[u8]) -> nom::IResult<&[u8], T::SignedExtensions> {
282 let (i, v) = cbor_parser(i)?;
283 trace!(?v, "OK!");
284
285 let v: T::SignedExtensions = serde_cbor_2::value::from_value(v).map_err(|e| {
286 error!(?e, "extensions_parser");
287 nom::Err::Failure(nom::error::Error::from_error_kind(
288 i,
289 nom::error::ErrorKind::Fail,
290 ))
291 })?;
292 Ok((i, v))
293}
294
295fn aaguid_parser(i: &[u8]) -> nom::IResult<&[u8], Aaguid> {
296 let (i, aaguid) = take(16usize)(i)?;
297 Ok((i, aaguid.try_into().expect("took 16 bytes exactly")))
298}
299
300fn acd_parser(i: &[u8]) -> nom::IResult<&[u8], AttestedCredentialData> {
301 let (i, aaguid) = aaguid_parser(i)?;
302 let (i, cred_id_len) = be_u16(i)?;
303
304 if usize::from(cred_id_len) > i.len() {
305 warn!(
306 "cred_id_len ({:?}) is larger than remaining number of bytes to parse ({:?}).",
307 cred_id_len,
308 i.len()
309 );
310 }
311
312 let (i, cred_id) = take(cred_id_len as usize)(i)?;
313 let (i, cred_pk) = cbor_parser(i)?;
314
315 Ok((
316 i,
317 AttestedCredentialData {
318 aaguid,
319 credential_id: HumanBinaryData::from(cred_id.to_vec()),
320 credential_pk: cred_pk,
321 },
322 ))
323}
324#[allow(clippy::type_complexity)]
325fn authenticator_data_flags(i: &[u8]) -> nom::IResult<&[u8], (bool, bool, bool, bool, bool, bool)> {
326 let (i, ctrl) = nom::number::complete::u8(i)?;
328 let exten_pres = (ctrl & 0b1000_0000) != 0;
329 let acd_pres = (ctrl & 0b0100_0000) != 0;
330 let res_1 = (ctrl & 0b0010_0000) != 0;
331 let bak_st = (ctrl & 0b0001_0000) != 0;
332 let bak_el = (ctrl & 0b0000_1000) != 0;
333 let u_ver = (ctrl & 0b0000_0100) != 0;
334 let res_4 = (ctrl & 0b0000_0010) != 0;
335 let u_pres = (ctrl & 0b0000_0001) != 0;
336
337 if res_1 || res_4 {
338 warn!(
339 "Usage of unknown authenticator data flags detected! {:b}",
340 ctrl
341 );
342 }
343
344 Ok((i, (exten_pres, acd_pres, u_ver, u_pres, bak_el, bak_st)))
345}
346
347fn authenticator_data_parser<T: Ceremony>(i: &[u8]) -> nom::IResult<&[u8], AuthenticatorData<T>> {
348 let (i, rp_id_hash) = take(32usize)(i)?;
349 let (i, data_flags) = authenticator_data_flags(i)?;
350 let (i, counter) = be_u32(i)?;
351 let (i, acd) = cond(data_flags.1, acd_parser)(i)?;
352 let (i, extensions) = cond(data_flags.0, extensions_parser::<T>)(i)?;
353 trace!(?extensions);
354 let extensions = extensions.unwrap_or_default();
355
356 Ok((
357 i,
358 AuthenticatorData {
359 rp_id_hash: rp_id_hash.try_into().expect("took 32 bytes from input"),
360 counter,
361 user_verified: data_flags.2,
362 user_present: data_flags.3,
363 backup_eligible: data_flags.4,
364 backup_state: data_flags.5,
365 acd,
366 extensions,
367 },
368 ))
369}
370
371#[derive(Debug, Clone)]
373pub struct AuthenticatorData<T: Ceremony> {
374 pub(crate) rp_id_hash: [u8; 32],
376 pub counter: u32,
378 pub user_present: bool,
380 pub user_verified: bool,
382 pub backup_eligible: bool,
385 pub backup_state: bool,
388 pub acd: Option<AttestedCredentialData>,
390 pub extensions: T::SignedExtensions,
392}
393
394impl<T: Ceremony> TryFrom<&[u8]> for AuthenticatorData<T> {
395 type Error = WebauthnError;
396 fn try_from(auth_data_bytes: &[u8]) -> Result<Self, Self::Error> {
397 authenticator_data_parser(auth_data_bytes)
398 .map_err(|e| {
399 error!(?e, "try_from authenticator_data_parser");
400 WebauthnError::ParseNOMFailure
401 })
402 .map(|(_, ad)| ad)
403 }
404}
405
406#[derive(Debug, Deserialize)]
407#[serde(rename_all = "camelCase")]
408pub(crate) struct AttestationObjectInner<'a> {
409 pub(crate) fmt: &'a str,
410 pub(crate) att_stmt: serde_cbor_2::Value,
411 pub(crate) auth_data: &'a [u8],
412 pub(crate) ep_att: Option<bool>,
413 pub(crate) large_blob_key: Option<&'a [u8]>,
414}
415
416#[derive(Debug)]
418pub struct AttestationObject<T: Ceremony> {
419 pub(crate) fmt: String,
421 pub(crate) att_stmt: serde_cbor_2::Value,
423 pub(crate) auth_data: AuthenticatorData<T>,
425 pub(crate) auth_data_bytes: Vec<u8>,
427 pub(crate) _ep_att: Option<bool>,
429 pub(crate) _large_blob_key: Option<Vec<u8>>,
431}
432
433impl<T: Ceremony> TryFrom<&[u8]> for AttestationObject<T> {
434 type Error = WebauthnError;
435
436 fn try_from(data: &[u8]) -> Result<AttestationObject<T>, WebauthnError> {
437 let v: serde_cbor_2::Value =
438 serde_cbor_2::from_slice(data).map_err(WebauthnError::ParseCBORFailure)?;
439 trace!(?v, "AttestationObjectInner");
440
441 let aoi: AttestationObjectInner =
442 serde_cbor_2::from_slice(data).map_err(WebauthnError::ParseCBORFailure)?;
443 let auth_data_bytes: &[u8] = aoi.auth_data;
444 let auth_data = AuthenticatorData::try_from(auth_data_bytes)?;
445
446 Ok(AttestationObject {
448 fmt: aoi.fmt.to_owned(),
449 att_stmt: aoi.att_stmt,
450 auth_data,
451 auth_data_bytes: auth_data_bytes.to_owned(),
452 _ep_att: aoi.ep_att,
453 _large_blob_key: aoi.large_blob_key.map(|v| v.to_owned()),
454 })
455 }
456}
457
458impl TryFrom<u8> for CredProtectResponse {
459 type Error = <CredentialProtectionPolicy as TryFrom<u8>>::Error;
460
461 fn try_from(v: u8) -> Result<Self, Self::Error> {
462 CredentialProtectionPolicy::try_from(v).map(CredProtectResponse)
463 }
464}
465
466impl From<CredProtectResponse> for u8 {
467 fn from(policy: CredProtectResponse) -> Self {
468 policy.0 as u8
469 }
470}
471
472pub(crate) struct AuthenticatorAttestationResponse<T: Ceremony> {
473 pub(crate) attestation_object: AttestationObject<T>,
474 pub(crate) client_data_json: CollectedClientData,
475 pub(crate) client_data_json_bytes: Vec<u8>,
476 pub(crate) transports: Option<Vec<AuthenticatorTransport>>,
477}
478
479impl<T: Ceremony> TryFrom<&AuthenticatorAttestationResponseRaw>
480 for AuthenticatorAttestationResponse<T>
481{
482 type Error = WebauthnError;
483 fn try_from(aarr: &AuthenticatorAttestationResponseRaw) -> Result<Self, Self::Error> {
484 let ccdj = serde_json::from_slice(aarr.client_data_json.as_ref())
485 .map_err(WebauthnError::ParseJSONFailure)?;
486
487 let ao = AttestationObject::try_from(aarr.attestation_object.as_ref())?;
488
489 Ok(AuthenticatorAttestationResponse {
490 attestation_object: ao,
491 client_data_json: ccdj,
492 client_data_json_bytes: aarr.client_data_json.clone().into(),
493 transports: aarr.transports.clone(),
494 })
495 }
496}
497
498#[derive(Debug)]
499pub(crate) struct AuthenticatorAssertionResponse<T: Ceremony> {
500 pub(crate) authenticator_data: AuthenticatorData<T>,
501 pub(crate) authenticator_data_bytes: Vec<u8>,
502 pub(crate) client_data: CollectedClientData,
503 pub(crate) client_data_bytes: Vec<u8>,
504 pub(crate) signature: Vec<u8>,
505 pub(crate) _user_handle: Option<Vec<u8>>,
506}
507
508impl<T: Ceremony> TryFrom<&AuthenticatorAssertionResponseRaw>
509 for AuthenticatorAssertionResponse<T>
510{
511 type Error = WebauthnError;
512 fn try_from(aarr: &AuthenticatorAssertionResponseRaw) -> Result<Self, Self::Error> {
513 Ok(AuthenticatorAssertionResponse {
514 authenticator_data: AuthenticatorData::try_from(aarr.authenticator_data.as_ref())?,
515 authenticator_data_bytes: aarr.authenticator_data.clone().into(),
516 client_data: serde_json::from_slice(aarr.client_data_json.as_ref())
517 .map_err(WebauthnError::ParseJSONFailure)?,
518 client_data_bytes: aarr.client_data_json.clone().into(),
519 signature: aarr.signature.clone().into(),
520 _user_handle: aarr.user_handle.clone().map(|uh| uh.into()),
521 })
522 }
523}
524
525pub const TPM_GENERATED_VALUE: u32 = 0xff544347;
529
530#[derive(Debug, PartialEq, Eq)]
531#[repr(u16)]
532pub enum TpmSt {
534 RspCommand = 0x00c4,
536 Null = 0x8000,
538 NoSessions = 0x8001,
540 Sessions = 0x8002,
542 ReservedA = 0x8003,
544 ReservedB = 0x8004,
546 AttestNV = 0x8014,
548 AttestCommandAudit = 0x8015,
550 AttestSessionAudit = 0x8016,
552 AttestCertify = 0x8017,
554 AttestQuote = 0x8018,
556 AttestTime = 0x8019,
558 AttestCreation = 0x801a,
560 ReservedC = 0x801b,
562 Creation = 0x8021,
564 Verified = 0x8022,
566 AuthSecret = 0x8023,
568 Hashcheck = 0x8024,
570 AuthSigned = 0x8025,
572 FUManifest = 0x8029,
574}
575
576impl TpmSt {
577 fn new(v: u16) -> Option<Self> {
578 match v {
579 0x00c4 => Some(TpmSt::RspCommand),
580 0x8000 => Some(TpmSt::Null),
581 0x8001 => Some(TpmSt::NoSessions),
582 0x8002 => Some(TpmSt::Sessions),
583 0x8003 => Some(TpmSt::ReservedA),
584 0x8004 => Some(TpmSt::ReservedB),
585 0x8014 => Some(TpmSt::AttestNV),
586 0x8015 => Some(TpmSt::AttestCommandAudit),
587 0x8016 => Some(TpmSt::AttestSessionAudit),
588 0x8017 => Some(TpmSt::AttestCertify),
589 0x8018 => Some(TpmSt::AttestQuote),
590 0x8019 => Some(TpmSt::AttestTime),
591 0x801a => Some(TpmSt::AttestCreation),
592 0x801b => Some(TpmSt::ReservedC),
593 0x8021 => Some(TpmSt::Creation),
594 0x8022 => Some(TpmSt::Verified),
595 0x8023 => Some(TpmSt::AuthSecret),
596 0x8024 => Some(TpmSt::Hashcheck),
597 0x8025 => Some(TpmSt::AuthSigned),
598 0x8029 => Some(TpmSt::FUManifest),
599 _ => None,
600 }
601 }
602}
603
604#[derive(Debug)]
605pub struct TpmsClockInfo {
607 _clock: u64,
608 _reset_count: u32,
609 _restart_count: u32,
610 _safe: bool, }
612
613fn tpmsclockinfo_parser(i: &[u8]) -> nom::IResult<&[u8], TpmsClockInfo> {
614 let (i, clock) = be_u64(i)?;
615 let (i, reset_count) = be_u32(i)?;
616 let (i, restart_count) = be_u32(i)?;
617 let (i, safe) = nom::combinator::map(nom::number::complete::u8, |v| v != 0)(i)?;
618
619 Ok((
620 i,
621 TpmsClockInfo {
622 _clock: clock,
623 _reset_count: reset_count,
624 _restart_count: restart_count,
625 _safe: safe,
626 },
627 ))
628}
629
630#[derive(Debug)]
631pub enum Tpm2bName {
633 None,
635 Handle(u32),
637 Digest(Vec<u8>),
639}
640
641#[derive(Debug)]
642pub enum TpmuAttest {
644 AttestCertify(Tpm2bName, Tpm2bName),
646 #[allow(dead_code)]
653 Invalid,
655}
656
657#[derive(Debug)]
658pub struct TpmsAttest {
660 pub magic: u32,
662 pub type_: TpmSt,
664 pub qualified_signer: Tpm2bName,
666 pub extra_data: Option<Vec<u8>>,
668 pub clock_info: TpmsClockInfo,
670 pub firmware_version: u64,
672 pub typeattested: TpmuAttest,
674}
675
676fn tpm2b_name(i: &[u8]) -> nom::IResult<&[u8], Tpm2bName> {
677 let (i, case) = be_u16(i)?;
678 let (i, r) = match case {
679 0 => (i, Tpm2bName::None),
680 4 => {
681 let (i, h) = be_u32(i)?;
682 (i, Tpm2bName::Handle(h))
683 }
684 size => {
685 let (i, d) = take(size as usize)(i)?;
686 (i, Tpm2bName::Digest(d.to_vec()))
687 }
688 };
689
690 Ok((i, r))
691}
692
693fn tpm2b_data(i: &[u8]) -> nom::IResult<&[u8], Option<Vec<u8>>> {
694 let (i, size) = be_u16(i)?;
695 match size {
696 0 => Ok((i, None)),
697 size => {
698 let (i, d) = take(size as usize)(i)?;
699 Ok((i, Some(d.to_vec())))
700 }
701 }
702}
703
704fn tpmuattestcertify(i: &[u8]) -> nom::IResult<&[u8], TpmuAttest> {
705 let (i, name) = tpm2b_name(i)?;
706 let (i, qualified_name) = tpm2b_name(i)?;
707
708 Ok((i, TpmuAttest::AttestCertify(name, qualified_name)))
709}
710
711fn tpmsattest_parser(i: &[u8]) -> nom::IResult<&[u8], TpmsAttest> {
712 let (i, magic) = verify(be_u32, |x| *x == TPM_GENERATED_VALUE)(i)?;
713 let (i, type_) = map_opt(be_u16, TpmSt::new)(i)?;
714 let (i, qualified_signer) = tpm2b_name(i)?;
715 let (i, extra_data) = tpm2b_data(i)?;
716 let (i, clock_info) = tpmsclockinfo_parser(i)?;
717 let (i, firmware_version) = be_u64(i)?;
718 let (i, typeattested) = tpmuattestcertify(i)?;
719
720 Ok((
721 i,
722 TpmsAttest {
723 magic,
724 type_,
725 qualified_signer,
726 extra_data,
727 clock_info,
728 firmware_version,
729 typeattested,
730 },
731 ))
732}
733
734impl TryFrom<&[u8]> for TpmsAttest {
735 type Error = WebauthnError;
736
737 fn try_from(data: &[u8]) -> Result<TpmsAttest, WebauthnError> {
738 tpmsattest_parser(data)
739 .map_err(|e| {
740 error!(?e, "try_from tpmsattest_parser");
741 WebauthnError::ParseNOMFailure
742 })
743 .map(|(_, v)| v)
744 }
745}
746
747#[derive(Debug, Clone, Copy)]
748#[repr(u16)]
749pub enum TpmAlgId {
751 Error = 0x0000,
753 Rsa = 0x0001,
755 Sha1 = 0x0004,
757 Hmac = 0x0005,
759 Aes = 0x0006,
761 Sha256 = 0x000B,
766 Sha384 = 0x000C,
768 Sha512 = 0x000D,
770 Null = 0x0010,
772 RsaSSA = 0x0014,
776 RsaPSS = 0x0016,
779 Ecdsa = 0x0018,
782 Ecdaa = 0x001A,
785 Ecc = 0x0023,
793 }
801
802impl TpmAlgId {
803 fn new(v: u16) -> Option<Self> {
804 trace!("TpmAlgId::new ( {:x?} )", v);
805 match v {
806 0x0000 => Some(TpmAlgId::Error),
807 0x0001 => Some(TpmAlgId::Rsa),
808 0x0004 => Some(TpmAlgId::Sha1),
809 0x0005 => Some(TpmAlgId::Hmac),
810 0x0006 => Some(TpmAlgId::Aes),
811 0x000B => Some(TpmAlgId::Sha256),
812 0x000C => Some(TpmAlgId::Sha384),
813 0x000D => Some(TpmAlgId::Sha512),
814 0x0010 => Some(TpmAlgId::Null),
815 0x0014 => Some(TpmAlgId::RsaSSA),
816 0x0016 => Some(TpmAlgId::RsaPSS),
817 0x0018 => Some(TpmAlgId::Ecdsa),
818 0x001A => Some(TpmAlgId::Ecdaa),
819 0x0023 => Some(TpmAlgId::Ecc),
820 _ => None,
821 }
822 }
823}
824
825#[derive(Debug)]
828pub struct TpmtSymDefObject {
830 _algorithm: TpmAlgId,
831 }
835
836fn parse_tpmtsymdefobject(input: &[u8]) -> nom::IResult<&[u8], Option<TpmtSymDefObject>> {
837 let (data, algorithm) = map_opt(be_u16, TpmAlgId::new)(input)?;
838 match algorithm {
839 TpmAlgId::Null => Ok((data, None)),
840 _ => Err(nom::Err::Failure(nom::error::Error::from_error_kind(
841 input,
842 nom::error::ErrorKind::Fail,
843 ))),
844 }
845}
846
847#[derive(Debug)]
848pub struct TpmtEccScheme {
850 _algorithm: TpmAlgId,
851}
852
853fn parse_tpmteccscheme(input: &[u8]) -> nom::IResult<&[u8], Option<TpmtEccScheme>> {
854 let (data, algorithm) = map_opt(be_u16, TpmAlgId::new)(input)?;
855 match algorithm {
856 TpmAlgId::Null => Ok((data, None)),
857 _ => Err(nom::Err::Failure(nom::error::Error::from_error_kind(
858 input,
859 nom::error::ErrorKind::Fail,
860 ))),
861 }
862}
863
864#[derive(Debug, Clone, Copy)]
867#[repr(u16)]
868pub(crate) enum TpmiEccCurve {
869 None = 0x0000,
870 NistP192 = 0x0001,
871 NistP224 = 0x0002,
872 NistP256 = 0x0003,
873 NistP384 = 0x0004,
874 NistP521 = 0x0005,
875 BnP256 = 0x0010,
876 BnP638 = 0x0011,
877 Sm2P256 = 0x0020,
878}
879
880impl TpmiEccCurve {
881 fn new(v: u16) -> Option<Self> {
882 trace!("TpmiEccCurve::new ( {:x?} )", v);
883 match v {
884 0x0000 => Some(TpmiEccCurve::None),
885 0x0001 => Some(TpmiEccCurve::NistP192),
886 0x0002 => Some(TpmiEccCurve::NistP224),
887 0x0003 => Some(TpmiEccCurve::NistP256),
888 0x0004 => Some(TpmiEccCurve::NistP384),
889 0x0005 => Some(TpmiEccCurve::NistP521),
890 0x0010 => Some(TpmiEccCurve::BnP256),
891 0x0011 => Some(TpmiEccCurve::BnP638),
892 0x0020 => Some(TpmiEccCurve::Sm2P256),
893 _ => None,
894 }
895 }
896}
897
898#[derive(Debug)]
899pub struct TpmtKdfScheme {
901 _algorithm: TpmAlgId,
902}
903
904fn parse_tpmtkdfscheme(input: &[u8]) -> nom::IResult<&[u8], Option<TpmtKdfScheme>> {
905 let (data, algorithm) = map_opt(be_u16, TpmAlgId::new)(input)?;
906 match algorithm {
907 TpmAlgId::Null => Ok((data, None)),
908 _ => Err(nom::Err::Failure(nom::error::Error::from_error_kind(
909 input,
910 nom::error::ErrorKind::Fail,
911 ))),
912 }
913}
914
915#[derive(Debug)]
916pub struct TpmtRsaScheme {
918 _algorithm: TpmAlgId,
919 }
921
922fn parse_tpmtrsascheme(input: &[u8]) -> nom::IResult<&[u8], Option<TpmtRsaScheme>> {
923 let (data, algorithm) = map_opt(be_u16, TpmAlgId::new)(input)?;
924 match algorithm {
925 TpmAlgId::Null => Ok((data, None)),
926 _ => Err(nom::Err::Failure(nom::error::Error::from_error_kind(
927 input,
928 nom::error::ErrorKind::Fail,
929 ))),
930 }
931}
932
933#[derive(Debug)]
934pub(crate) struct TpmsRsaParms {
936 _symmetric: Option<TpmtSymDefObject>,
938 _scheme: Option<TpmtRsaScheme>,
940 _keybits: u16,
942 _exponent: u32,
945}
946
947fn tpmsrsaparms_parser(i: &[u8]) -> nom::IResult<&[u8], TpmsRsaParms> {
948 let (i, symmetric) = parse_tpmtsymdefobject(i)?;
949 let (i, scheme) = parse_tpmtrsascheme(i)?;
950 let (i, keybits) = be_u16(i)?;
951 let (i, exponent) = be_u32(i)?;
952 Ok((
953 i,
954 TpmsRsaParms {
955 _symmetric: symmetric,
956 _scheme: scheme,
957 _keybits: keybits,
958 _exponent: exponent,
959 },
960 ))
961 }
978
979fn tpmseccparms_parser(i: &[u8]) -> nom::IResult<&[u8], TpmsEccParms> {
981 trace!(?i);
982 let (i, symmetric) = parse_tpmtsymdefobject(i)?;
983 let (i, scheme) = parse_tpmteccscheme(i)?;
984 let (i, curve_id) = map_opt(be_u16, TpmiEccCurve::new)(i)?;
985 let (i, kdf) = parse_tpmtkdfscheme(i)?;
986
987 Ok((
988 i,
989 TpmsEccParms {
990 _symmetric: symmetric,
991 _scheme: scheme,
992 curve_id,
993 _kdf: kdf,
994 },
995 ))
996}
997
998#[derive(Debug)]
999pub(crate) struct TpmsEccParms {
1000 _symmetric: Option<TpmtSymDefObject>,
1001 _scheme: Option<TpmtEccScheme>,
1002 pub curve_id: TpmiEccCurve,
1004 _kdf: Option<TpmtKdfScheme>,
1005}
1006
1007#[derive(Debug)]
1008pub(crate) enum TpmuPublicParms {
1010 Rsa(TpmsRsaParms),
1014 Ecc(TpmsEccParms),
1015 }
1017
1018fn parse_tpmupublicparms(input: &[u8], alg: TpmAlgId) -> nom::IResult<&[u8], TpmuPublicParms> {
1019 trace!(?input, ?alg, "tpmupublicparms input");
1020 match alg {
1021 TpmAlgId::Rsa => {
1022 tpmsrsaparms_parser(input).map(|(data, inner)| (data, TpmuPublicParms::Rsa(inner)))
1023 }
1024 TpmAlgId::Ecc => {
1025 tpmseccparms_parser(input).map(|(data, inner)| (data, TpmuPublicParms::Ecc(inner)))
1026 }
1027 a => {
1028 debug!(?a, "unsuported alg in parse_tpmupublicparms");
1029 Err(nom::Err::Failure(nom::error::Error::from_error_kind(
1030 input,
1031 nom::error::ErrorKind::Fail,
1032 )))
1033 }
1034 }
1035}
1036
1037#[derive(Debug)]
1039pub(crate) struct TpmsEccPoint {
1040 pub x: Vec<u8>,
1041 pub y: Vec<u8>,
1042}
1043
1044fn tpmseccpoint_parser(i: &[u8]) -> nom::IResult<&[u8], TpmsEccPoint> {
1045 let (i, size) = be_u16(i)?;
1046 let (i, x) = match size {
1047 0 => (i, Vec::new()),
1048 size => {
1049 let (i, d) = take(size as usize)(i)?;
1050 (i, d.to_vec())
1051 }
1052 };
1053 let (i, size) = be_u16(i)?;
1054 let (i, y) = match size {
1055 0 => (i, Vec::new()),
1056 size => {
1057 let (i, d) = take(size as usize)(i)?;
1058 (i, d.to_vec())
1059 }
1060 };
1061 Ok((i, TpmsEccPoint { x, y }))
1062}
1063
1064#[derive(Debug)]
1065pub(crate) enum TpmuPublicId {
1067 Rsa(Vec<u8>),
1071 Ecc(TpmsEccPoint),
1073 }
1075
1076fn tpmsrsapublickey_parser(i: &[u8]) -> nom::IResult<&[u8], Vec<u8>> {
1077 let (i, size) = be_u16(i)?;
1078 match size {
1079 0 => Ok((i, Vec::new())),
1080 size => {
1081 let (i, d) = take(size as usize)(i)?;
1082 Ok((i, d.to_vec()))
1083 }
1084 }
1085}
1086
1087fn parse_tpmupublicid(input: &[u8], alg: TpmAlgId) -> nom::IResult<&[u8], TpmuPublicId> {
1088 match alg {
1090 TpmAlgId::Rsa => {
1091 tpmsrsapublickey_parser(input).map(|(data, inner)| (data, TpmuPublicId::Rsa(inner)))
1092 }
1093 TpmAlgId::Ecc => {
1094 tpmseccpoint_parser(input).map(|(data, inner)| (data, TpmuPublicId::Ecc(inner)))
1095 }
1096 a => {
1097 debug!(?a, "unsuported alg in parse_tpmupublicid");
1098 Err(nom::Err::Failure(nom::error::Error::from_error_kind(
1099 input,
1100 nom::error::ErrorKind::Fail,
1101 )))
1102 }
1103 }
1104}
1105
1106#[derive(Debug)]
1107pub(crate) struct TpmtPublic {
1109 pub _type: TpmAlgId,
1111 pub name_alg: TpmAlgId,
1113 pub _object_attributes: u32,
1116 pub _auth_policy: Option<Vec<u8>>,
1118 pub parameters: TpmuPublicParms,
1122 pub unique: TpmuPublicId,
1125}
1126
1127impl TryFrom<&[u8]> for TpmtPublic {
1128 type Error = WebauthnError;
1129
1130 fn try_from(data: &[u8]) -> Result<TpmtPublic, WebauthnError> {
1131 trace!(?data);
1132 tpmtpublic_parser(data)
1133 .map_err(|e| {
1134 error!(?e, "try_from tpmtpublic_parser");
1135 WebauthnError::ParseNOMFailure
1136 })
1137 .map(|(_, v)| v)
1138 }
1139}
1140
1141fn tpm2b_digest(i: &[u8]) -> nom::IResult<&[u8], Option<Vec<u8>>> {
1142 let (i, size) = be_u16(i)?;
1143 match size {
1144 0 => Ok((i, None)),
1145 size => {
1146 let (i, d) = take(size as usize)(i)?;
1147 Ok((i, Some(d.to_vec())))
1148 }
1149 }
1150}
1151
1152fn tpmtpublic_parser(i: &[u8]) -> nom::IResult<&[u8], TpmtPublic> {
1153 let (i, type_) = map_opt(be_u16, TpmAlgId::new)(i)?;
1154 let (i, name_alg) = map_opt(be_u16, TpmAlgId::new)(i)?;
1155 let (i, _object_attributes) = be_u32(i)?;
1156 let (i, _auth_policy) = tpm2b_digest(i)?;
1157 let (i, parameters) = parse_tpmupublicparms(i, type_)?;
1158 let (i, unique) = parse_tpmupublicid(i, type_)?;
1159
1160 Ok((
1161 i,
1162 TpmtPublic {
1163 _type: type_,
1164 name_alg,
1165 _object_attributes,
1166 _auth_policy,
1167 parameters,
1168 unique,
1169 },
1170 ))
1171}
1172
1173#[derive(Debug)]
1174pub enum TpmtSignature {
1176 RawSignature(Vec<u8>),
1182}
1183
1184impl TryFrom<&[u8]> for TpmtSignature {
1185 type Error = WebauthnError;
1186
1187 fn try_from(data: &[u8]) -> Result<TpmtSignature, WebauthnError> {
1188 tpmtsignature_parser(data)
1189 .map_err(|e| {
1190 error!(?e, "try_from tpmtsignature_parser");
1191 WebauthnError::ParseNOMFailure
1192 })
1193 .map(|(_, v)| v)
1194 }
1195}
1196
1197fn tpmtsignature_parser(input: &[u8]) -> nom::IResult<&[u8], TpmtSignature> {
1198 let (_data, algorithm) = nom::combinator::map(be_u16, TpmAlgId::new)(input)?;
1199 match algorithm {
1200 None => Ok((&[], TpmtSignature::RawSignature(Vec::from(input)))),
1201 _ => Err(nom::Err::Failure(nom::error::Error::from_error_kind(
1202 input,
1203 nom::error::ErrorKind::Fail,
1204 ))),
1205 }
1206}
1207
1208#[derive(Debug)]
1212#[allow(clippy::upper_case_acronyms)]
1213pub(crate) enum TpmVendor {
1214 AMD,
1215 Atmel,
1216 Broadcom,
1217 Cisco,
1218 FlySliceTechnologies,
1219 FuzhouRockchip,
1220 Google,
1221 HPE,
1222 Huawei,
1223 IBM,
1224 Infineon,
1225 Intel,
1226 Lenovo,
1227 Microsoft,
1228 NationalSemi,
1229 Nationz,
1230 NuvotonTechnology,
1231 Qualcomm,
1232 Samsung,
1233 Sinosun,
1234 SMSC,
1235 StMicroelectronics,
1236 TexasInstruments,
1237 Winbond,
1238}
1239
1240impl TryFrom<&[u8; 8]> for TpmVendor {
1245 type Error = WebauthnError;
1246
1247 fn try_from(v: &[u8; 8]) -> Result<Self, Self::Error> {
1248 match v {
1249 b"414d4400" => Ok(Self::AMD),
1250 b"41544D4C" => Ok(Self::Atmel),
1251 b"4252434D" => Ok(Self::Broadcom),
1252 b"4353434F" => Ok(Self::Cisco),
1253 b"464C5953" => Ok(Self::FlySliceTechnologies),
1254 b"524F4343" => Ok(Self::FuzhouRockchip),
1255 b"474F4F47" => Ok(Self::Google),
1256 b"48504500" => Ok(Self::HPE),
1257 b"48495349" => Ok(Self::Huawei),
1258 b"49424D00" => Ok(Self::IBM),
1259 b"49465800" => Ok(Self::Infineon),
1260 b"494E5443" => Ok(Self::Intel),
1261 b"4C454E00" => Ok(Self::Lenovo),
1262 b"4D534654" => Ok(Self::Microsoft),
1263 b"4E534D20" => Ok(Self::NationalSemi),
1264 b"4E545A00" => Ok(Self::Nationz),
1265 b"4E544300" => Ok(Self::NuvotonTechnology),
1266 b"51434F4D" => Ok(Self::Qualcomm),
1267 b"534D534E" => Ok(Self::Samsung),
1268 b"534E5300" => Ok(Self::Sinosun),
1269 b"534D5343" => Ok(Self::SMSC),
1270 b"53544D20" => Ok(Self::StMicroelectronics),
1271 b"54584E00" => Ok(Self::TexasInstruments),
1272 b"57454300" => Ok(Self::Winbond),
1273 _ => Err(WebauthnError::ParseNOMFailure),
1274 }
1275 }
1276}
1277
1278pub(crate) fn tpm_device_attribute_parser(i: &[u8]) -> nom::IResult<&[u8], &[u8; 8]> {
1281 let (i, _) = tag("id:")(i)?;
1282 let (i, vendor_code) = take(8usize)(i)?;
1283
1284 Ok((i, vendor_code.try_into().expect("took exactly 8 bytes")))
1285}
1286
1287impl TryFrom<&[u8]> for TpmVendor {
1288 type Error = WebauthnError;
1289 fn try_from(data: &[u8]) -> Result<Self, Self::Error> {
1290 tpm_device_attribute_parser(data)
1291 .map_err(|e| {
1292 error!(?e, "try_from tpm_device_attribute_parser");
1293 WebauthnError::ParseNOMFailure
1294 })
1295 .and_then(|(_, v)| TpmVendor::try_from(v))
1296 }
1297}
1298
1299#[cfg(test)]
1300mod tests {
1301 use super::{
1302 AttestationObject, Authentication, AuthenticatorData, CredentialProtectionPolicy,
1303 RegisterPublicKeyCredential, Registration, RegistrationSignedExtensions, TpmsAttest,
1304 TpmtPublic, TpmtSignature, TPM_GENERATED_VALUE,
1305 };
1306 use crate::interface::*;
1307 use base64::{engine::general_purpose::STANDARD, Engine};
1308
1309 #[test]
1310 fn deserialise_register_response() {
1311 let x = r#"
1312 { "id":"4oiUggKcrpRIlB-cFzFbfkx_BNeM7UAnz3wO7ZpT4I2GL_n-g8TICyJTHg11l0wyc-VkQUVnJ0yM08-1D5oXnw",
1313 "rawId":"4oiUggKcrpRIlB-cFzFbfkx_BNeM7UAnz3wO7ZpT4I2GL_n-g8TICyJTHg11l0wyc-VkQUVnJ0yM08-1D5oXnw",
1314 "response":{
1315 "attestationObject":"o2NmbXRkbm9uZWdhdHRTdG10oGhhdXRoRGF0YVjEEsoXtJryKJQ28wPgFmAwoh5SXSZuIJJnQzgBqP1AcaBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAQOKIlIICnK6USJQfnBcxW35MfwTXjO1AJ898Du2aU-CNhi_5_oPEyAsiUx4NdZdMMnPlZEFFZydMjNPPtQ-aF5-lAQIDJiABIVggFo08FM4Je1yfCSuPsxP6h0zvlJSjfocUk75EvXw2oSMiWCArRwLD8doar0bACWS1PgVJKzp_wStyvOkTd4NlWHW8rQ",
1316 "clientDataJSON":"eyJjaGFsbGVuZ2UiOiJwZENXRDJWamRMSVkzN2VSYTVfazdhS3BqdkF2VmNOY04ycVozMjk0blpVIiwiY2xpZW50RXh0ZW5zaW9ucyI6e30sImhhc2hBbGdvcml0aG0iOiJTSEEtMjU2Iiwib3JpZ2luIjoiaHR0cDovLzEyNy4wLjAuMTo4MDgwIiwidHlwZSI6IndlYmF1dGhuLmNyZWF0ZSJ9"
1317 },
1318 "type":"public-key"
1319 }
1320 "#;
1321 let _y: RegisterPublicKeyCredential = serde_json::from_str(x).unwrap();
1322 }
1323
1324 #[test]
1325 fn deserialise_attestation_object() {
1326 let raw_ao = STANDARD.decode(
1327 "o2NmbXRkbm9uZWdhdHRTdG10oGhhdXRoRGF0YVjEEsoXtJryKJQ28wPgFmAwoh5SXSZuIJJnQzgBqP1AcaBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAQCgxaVISCxE+DrcxP5/+aPM88CTI+04J+o61SK6mnepjGZYv062AbtydzWmbAxF00VSAyp0ImP94uoy+0y7w9yilAQIDJiABIVggGT9woA+UoX+jBxuiHQpdkm0kCVh75WTj3TXl4zLJuzoiWCBKiCneKgWJgWiwrZedNwl06GTaXyaGrYS4bPbBraInyg=="
1328 ).unwrap();
1329
1330 let _ao = AttestationObject::<Registration>::try_from(raw_ao.as_slice()).unwrap();
1331 }
1332
1333 #[test]
1336 fn deserialise_tpms_attest() {
1337 let data: Vec<u8> = vec![
1338 255, 84, 67, 71, 128, 23, 0, 34, 0, 11, 174, 74, 152, 70, 1, 87, 191, 156, 96, 74, 177, 221, 37, 132, 6, 8, 101, 35,
1342 124, 216, 85, 173, 85, 195, 115, 137, 194, 247, 145, 61, 82, 40, 0, 20, 234, 98, 144, 49, 146, 39, 99, 47, 44, 82, 115, 48, 64, 40, 152, 224, 227, 42, 63,
1345 133, 0, 0, 0, 2, 219, 215, 137, 38, 187, 106, 183, 8, 100, 145, 106, 200, 1, 86, 5, 220, 81, 118, 234, 131, 141, 0, 34, 0, 11, 239, 53, 112, 255, 253, 12, 189, 168, 16, 253, 10, 149, 108, 7, 31, 212, 143,
1353 21, 153, 7, 7, 153, 99, 73, 205, 97, 90, 110, 182, 120, 4, 250, 0, 34, 0, 11, 249, 72,
1354 224, 84, 16, 96, 147, 197, 167, 195, 110, 181, 77, 207, 147, 16, 34, 64, 139, 185, 120,
1355 190, 196, 209, 213, 29, 1, 136, 76, 235, 223, 247,
1356 ];
1357
1358 let tpms_attest = TpmsAttest::try_from(data.as_slice()).unwrap();
1359 println!("{tpms_attest:?}");
1360 assert!(tpms_attest.magic == TPM_GENERATED_VALUE);
1361 }
1362
1363 #[test]
1364 fn deserialise_tpmt_public() {
1365 let data: Vec<u8> = vec![
1367 0, 1, 0, 11, 0, 6, 4, 114, 0, 32, 157, 255, 203, 243, 108, 56, 58, 230, 153, 251, 152,
1368 104, 220, 109, 203, 137, 215, 21, 56, 132, 190, 40, 3, 146, 44, 18, 65, 88, 191, 173,
1369 34, 174, 0, 16, 0, 16, 8, 0, 0, 0, 0, 0, 1, 0, 220, 20, 243, 114, 251, 142, 90, 236,
1370 17, 204, 181, 223, 8, 72, 230, 209, 122, 44, 90, 55, 96, 134, 69, 16, 125, 139, 112,
1371 81, 154, 230, 133, 211, 129, 37, 75, 208, 222, 70, 210, 239, 209, 188, 152, 93, 222,
1372 222, 154, 169, 217, 160, 90, 243, 135, 151, 25, 87, 240, 178, 106, 119, 150, 89, 23,
1373 223, 158, 88, 107, 72, 101, 61, 184, 132, 19, 110, 144, 107, 22, 178, 252, 206, 50,
1374 207, 11, 177, 137, 35, 139, 68, 212, 148, 121, 249, 50, 35, 89, 52, 47, 26, 23, 6, 15,
1375 115, 155, 127, 59, 168, 208, 196, 78, 125, 205, 0, 98, 43, 223, 233, 65, 137, 103, 2,
1376 227, 35, 81, 107, 247, 230, 186, 111, 27, 4, 57, 42, 220, 32, 29, 181, 159, 6, 176,
1377 182, 94, 191, 222, 212, 235, 60, 101, 83, 86, 217, 203, 151, 251, 254, 219, 204, 195,
1378 10, 74, 147, 5, 27, 167, 127, 117, 149, 245, 157, 92, 124, 2, 196, 214, 107, 246, 228,
1379 171, 229, 100, 212, 67, 88, 215, 75, 33, 183, 199, 51, 171, 210, 213, 65, 45, 96, 96,
1380 226, 29, 130, 254, 58, 92, 252, 133, 207, 105, 63, 156, 208, 149, 142, 9, 83, 1, 193,
1381 217, 244, 35, 137, 43, 138, 137, 140, 82, 231, 195, 145, 213, 230, 185, 245, 104, 105,
1382 62, 142, 124, 34, 9, 157, 167, 188, 243, 112, 104, 248, 63, 50, 19, 53, 173, 69, 12,
1383 39, 252, 9, 69, 223,
1384 ];
1385 let tpmt_public = TpmtPublic::try_from(data.as_slice()).unwrap();
1386 println!("{tpmt_public:?}");
1387 }
1388
1389 #[test]
1390 fn deserialise_tpmt_signature() {
1391 let data: Vec<u8> = vec![
1393 5, 3, 162, 216, 151, 57, 210, 103, 145, 121, 161, 186, 63, 232, 221, 255, 89, 37, 17,
1394 59, 155, 241, 77, 30, 35, 201, 30, 140, 84, 214, 250, 185, 47, 248, 58, 89, 177, 187,
1395 231, 202, 220, 45, 167, 126, 243, 194, 94, 33, 39, 205, 163, 51, 40, 171, 35, 118, 196,
1396 244, 247, 143, 166, 193, 223, 94, 244, 157, 121, 220, 22, 94, 163, 15, 151, 223, 214,
1397 131, 105, 202, 40, 16, 176, 11, 154, 102, 100, 212, 174, 103, 166, 92, 90, 154, 224,
1398 20, 165, 106, 127, 53, 91, 230, 217, 199, 172, 195, 203, 242, 41, 158, 64, 252, 65, 9,
1399 155, 160, 63, 40, 94, 94, 64, 145, 173, 71, 85, 173, 2, 199, 18, 148, 88, 223, 93, 154,
1400 203, 197, 170, 142, 35, 249, 146, 107, 146, 2, 14, 54, 39, 151, 181, 10, 176, 216, 117,
1401 25, 196, 2, 205, 159, 140, 155, 56, 89, 87, 31, 135, 93, 97, 78, 95, 176, 228, 72, 237,
1402 130, 171, 23, 66, 232, 35, 115, 218, 105, 168, 6, 253, 121, 161, 129, 44, 78, 252, 44,
1403 11, 23, 172, 66, 37, 214, 113, 128, 28, 33, 209, 66, 34, 32, 196, 153, 80, 87, 243,
1404 162, 7, 25, 62, 252, 243, 174, 31, 168, 98, 123, 100, 2, 143, 134, 36, 154, 236, 18,
1405 128, 175, 185, 189, 177, 51, 53, 216, 190, 43, 63, 35, 84, 14, 64, 249, 23, 9, 125,
1406 147, 160, 176, 137, 30, 174, 245, 148, 189,
1407 ];
1408 let tpmt_sig = TpmtSignature::try_from(data.as_slice()).unwrap();
1409 println!("{tpmt_sig:?}");
1410 }
1411
1412 #[test]
1413 fn deserialize_extensions() {
1414 let data: Vec<u8> = vec![
1415 161, 107, 99, 114, 101, 100, 80, 114, 111, 116, 101, 99, 116, 3,
1416 ];
1417
1418 let extensions: RegistrationSignedExtensions = serde_cbor_2::from_slice(&data).unwrap();
1419
1420 let cred_protect = extensions
1421 .cred_protect
1422 .expect("should have cred protect extension");
1423 println!("{cred_protect:?}");
1424 assert_eq!(
1425 cred_protect.0,
1426 CredentialProtectionPolicy::UserVerificationRequired
1427 );
1428 }
1429
1430 #[test]
1431 fn credential_protection_policy_conversions() {
1432 use CredentialProtectionPolicy::*;
1433 assert_eq!(
1434 UserVerificationOptional,
1435 CredentialProtectionPolicy::try_from(UserVerificationOptional as u8).unwrap()
1436 );
1437 assert_eq!(
1438 UserVerificationOptionalWithCredentialIDList,
1439 CredentialProtectionPolicy::try_from(
1440 UserVerificationOptionalWithCredentialIDList as u8
1441 )
1442 .unwrap()
1443 );
1444 assert_eq!(
1445 UserVerificationRequired,
1446 CredentialProtectionPolicy::try_from(UserVerificationRequired as u8).unwrap()
1447 );
1448 }
1449
1450 #[test]
1451 fn authenticator_data_parser_extensions() {
1452 let _ = tracing_subscriber::fmt::try_init();
1453 let raw = [
1454 73, 150, 13, 229, 136, 14, 140, 104, 116, 52, 23, 15, 100, 118, 96, 91, 143, 228, 174,
1455 185, 162, 134, 50, 199, 153, 92, 243, 186, 131, 29, 151, 99, 133, 0, 0, 0, 6, 161, 104,
1456 99, 114, 101, 100, 66, 108, 111, 98, 64,
1457 ];
1458
1459 assert!(AuthenticatorData::<Authentication>::try_from(raw.as_slice()).is_ok());
1460 }
1461
1462 #[test]
1463 fn migrate_credentialv3() {
1464 let legacy_cred = r#"{"cred_id":[185,151,21,12,21,82,235,193,63,50,208,32,121,10,68,148,156,101,116,95,250,113,143,108,74,246,214,171,31,234,70,31,48,138,238,54,151,36,65,70,104,121,200,87,131,254,191,100,215,125,29,49,177,71,4,114,61,69,49,96,116,148,8,205],"cred":{"type_":"ES256","key":{"EC_EC2":{"curve":"SECP256R1","x":[194,126,127,109,252,23,131,21,252,6,223,99,44,254,140,27,230,17,94,5,133,28,104,41,144,69,171,149,161,26,200,243],"y":[143,123,183,156,24,178,21,248,117,159,162,69,171,52,188,252,26,59,6,47,103,92,19,58,117,103,249,0,219,8,95,196]}}},"counter":2,"verified":false,"registration_policy":"preferred"}"#;
1465
1466 let cred: CredentialV3 = serde_json::from_str(legacy_cred).unwrap();
1467
1468 println!("{cred:?}");
1469
1470 let cred_migrated: Credential = cred.into();
1471
1472 println!("{cred_migrated:?}");
1473 }
1474}