ser.rs (223952B)
1 use super::{ 2 super::{ 3 super::request::register::CoseAlgorithmIdentifier, 4 ser::{ 5 AuthenticationExtensionsPrfOutputsHelper, AuthenticationExtensionsPrfValues, 6 Base64DecodedVal, ClientExtensions, PublicKeyCredential, 7 }, 8 }, 9 AttestationObject, AttestedCredentialData, AuthTransports, AuthenticationExtensionsPrfOutputs, 10 AuthenticatorAttestation, ClientExtensionsOutputs, CredentialPropertiesOutput, FromCbor as _, 11 Registration, UncompressedPubKey, 12 }; 13 #[cfg(doc)] 14 use super::{AuthenticatorAttachment, CredentialId}; 15 use core::{ 16 fmt::{self, Formatter}, 17 marker::PhantomData, 18 str, 19 }; 20 use rsa::sha2::{Sha256, digest::OutputSizeUser as _}; 21 use serde::de::{Deserialize, Deserializer, Error, IgnoredAny, MapAccess, Unexpected, Visitor}; 22 /// Functionality for deserializing DER-encoded `SubjectPublicKeyInfo` _without_ making copies of data or 23 /// verifying the key is valid. This exists purely to ensure that the public key we receive in JSON is the same as 24 /// the public key in the attestation object. 25 mod spki { 26 use super::super::{ 27 Ed25519PubKey, RsaPubKey, RsaPubKeyErr, UncompressedP256PubKey, UncompressedP384PubKey, 28 UncompressedPubKey, 29 }; 30 use core::fmt::{self, Display, Formatter}; 31 use p256::{ 32 NistP256, 33 elliptic_curve::{Curve, generic_array::typenum::type_operators::ToInt as _}, 34 }; 35 use p384::NistP384; 36 /// Value assigned to the integer type under the universal tag class per 37 /// [ITU-T X.680](https://www.itu.int/rec/T-REC-X.680-202102-I/en). 38 const INTEGER: u8 = 2; 39 /// Value assigned to the bitstring type under the universal tag class per 40 /// [ITU-T X.680](https://www.itu.int/rec/T-REC-X.680-202102-I/en). 41 const BITSTRING: u8 = 3; 42 /// Value assigned to the null type under the universal tag class per 43 /// [ITU-T X.680](https://www.itu.int/rec/T-REC-X.680-202102-I/en). 44 const NULL: u8 = 5; 45 /// Value assigned to the object identifier type under the universal tag class per 46 /// [ITU-T X.680](https://www.itu.int/rec/T-REC-X.680-202102-I/en). 47 const OID: u8 = 6; 48 /// Value assigned to the sequence type under the universal tag class per 49 /// [ITU-T X.680](https://www.itu.int/rec/T-REC-X.680-202102-I/en). 50 const SEQUENCE: u8 = 16; 51 /// Value assigned to a constructed [`SEQUENCE`] per 52 /// [ITU-T X.690](https://www.itu.int/rec/T-REC-X.690-202102-I/en). 53 /// 54 /// All sequences are constructed once encoded, so this will likely always be used instead of 55 /// `SEQUENCE`. 56 const CONSTRUCTED_SEQUENCE: u8 = SEQUENCE | 0b0010_0000; 57 /// Length of the header before the compressed y-coordinate in a DER-encoded ASN.1 `SubjectPublicKeyInfo` 58 /// for an Ed25519 public key. 59 const ED25519_HEADER_LEN: usize = 12; 60 /// Length of a DER-encoded ASN.1 `SubjectPublicKeyInfo` for Ed25519 public key. 61 const ED25519_LEN: usize = ED25519_HEADER_LEN + ed25519_dalek::PUBLIC_KEY_LENGTH; 62 /// `ED25519_LEN` as a `u8`. 63 // `44 as u8` is clearly OK. 64 #[expect( 65 clippy::as_conversions, 66 clippy::cast_possible_truncation, 67 reason = "comments above justify their correctness" 68 )] 69 const ED25519_LEN_U8: u8 = ED25519_LEN as u8; 70 /// Length of the header before the uncompressed SEC- 1 pubic key in a DER-encoded ASN.1 `SubjectPublicKeyInfo` 71 /// for an uncompressed ECDSA public key based on secp256r1/P-256. 72 const P256_HEADER_LEN: usize = 27; 73 /// Number of bytes the x-coordinate takes in an uncompressed P-256 public key. 74 const P256_X_LEN: usize = <NistP256 as Curve>::FieldBytesSize::INT; 75 /// Number of bytes the y-coordinate takes in an uncompressed P-256 public key. 76 const P256_Y_LEN: usize = <NistP256 as Curve>::FieldBytesSize::INT; 77 /// Number of bytes the x and y coordinates take in an uncompressed P-256 public key when concatenated together. 78 const P256_COORD_LEN: usize = P256_X_LEN + P256_Y_LEN; 79 /// Length of a DER-encoded ASN.1 `SubjectPublicKeyInfo` for an uncompressed SEC-1 ECDSA public key 80 /// based on secp256r1/P-256. 81 const P256_LEN: usize = P256_HEADER_LEN + P256_COORD_LEN; 82 /// `P256_LEN` as a `u8`. 83 // `91 as u8` is clearly OK. 84 #[expect( 85 clippy::as_conversions, 86 clippy::cast_possible_truncation, 87 reason = "comments above justify their correctness" 88 )] 89 const P256_LEN_U8: u8 = P256_LEN as u8; 90 /// Length of the header before the uncompressed SEC- 1 pubic key in a DER-encoded ASN.1 `SubjectPublicKeyInfo` 91 /// for an uncompressed ECDSA public key based on secp384r1/P-384. 92 const P384_HEADER_LEN: usize = 24; 93 /// Number of bytes the x-coordinate takes in an uncompressed P-384 public key. 94 const P384_X_LEN: usize = <NistP384 as Curve>::FieldBytesSize::INT; 95 /// Number of bytes the y-coordinate takes in an uncompressed P-384 public key. 96 const P384_Y_LEN: usize = <NistP384 as Curve>::FieldBytesSize::INT; 97 /// Number of bytes the x and y coordinates take in an uncompressed P-384 public key when concatenated together. 98 const P384_COORD_LEN: usize = P384_X_LEN + P384_Y_LEN; 99 /// Length of a DER-encoded ASN.1 `SubjectPublicKeyInfo` for an uncompressed SEC-1 ECDSA public key 100 /// based on secp384r1/P-384. 101 const P384_LEN: usize = P384_HEADER_LEN + P384_COORD_LEN; 102 /// `P384_LEN` as a `u8`. 103 // `120 as u8` is clearly OK. 104 #[expect( 105 clippy::as_conversions, 106 clippy::cast_possible_truncation, 107 reason = "comments above justify their correctness" 108 )] 109 const P384_LEN_U8: u8 = P384_LEN as u8; 110 /// Error returned from [`SubjectPublicKeyInfo::from_der`]. 111 pub(super) enum SubjectPublicKeyInfoErr { 112 /// The DER-encoded `SubjectPublicKeyInfo` had an invalid length. 113 Len, 114 /// The length of the DER-encoded Ed25519 key was invalid. 115 Ed25519Len, 116 /// The header of the DER-encoded Ed25519 key was invalid. 117 Ed25519Header, 118 /// The length of the DER-encoded P-256 key was invalid. 119 P256Len, 120 /// The header of the DER-encoded P-256 key was invalid. 121 P256Header, 122 /// The length of the DER-encoded P-384 key was invalid. 123 P384Len, 124 /// The header of the DER-encoded P-384 key was invalid. 125 P384Header, 126 /// The length of the DER-encoded RSA key was invalid. 127 RsaLen, 128 /// The DER-encoding of the RSA key was invalid. 129 RsaEncoding, 130 /// The exponent of the DER-encoded RSA key was too large. 131 RsaExponentTooLarge, 132 /// The DER-encoded RSA key was not a valid [`RsaPubKey`]. 133 RsaPubKey(RsaPubKeyErr), 134 } 135 impl Display for SubjectPublicKeyInfoErr { 136 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 137 match *self { 138 Self::Len => { 139 f.write_str("the DER-encoded SubjectPublicKeyInfo had an invalid length") 140 } 141 Self::Ed25519Len => { 142 f.write_str("length of the DER-encoded Ed25519 key was invalid") 143 } 144 Self::Ed25519Header => { 145 f.write_str("header of the DER-encoded Ed25519 key was invalid") 146 } 147 Self::P256Len => f.write_str("length of the DER-encoded P-256 key was invalid"), 148 Self::P256Header => f.write_str("header of the DER-encoded P-256 key was invalid"), 149 Self::P384Len => f.write_str("length of the DER-encoded P-384 key was invalid"), 150 Self::P384Header => f.write_str("header of the DER-encoded P-384 key was invalid"), 151 Self::RsaLen => f.write_str("length of the DER-encoded RSA key was invalid"), 152 Self::RsaEncoding => { 153 f.write_str("the DER-encoding of the RSA public key was invalid") 154 } 155 Self::RsaExponentTooLarge => { 156 f.write_str("the DER-encoded RSA public key had an exponent that was too large") 157 } 158 Self::RsaPubKey(err) => { 159 write!(f, "the DER-encoded RSA public was not valid: {err}") 160 } 161 } 162 } 163 } 164 /// Types that can be deserialized from the DER-encoded ASN.1 `SubjectPublicKeyInfo` as defined in 165 /// [RFC 5280](https://datatracker.ietf.org/doc/html/rfc5280#appendix-A.1) and other applicable RFCs 166 /// and documents (e.g., [ITU-T X.690](https://www.itu.int/rec/T-REC-X.690-202102-I/en)). 167 pub(super) trait SubjectPublicKeyInfo<'a>: Sized { 168 /// Transforms the DER-encoded ASN.1 `SubjectPublicKeyInfo` into `Self`. 169 /// 170 /// # Errors 171 /// 172 /// Errors iff `der` does not conform. 173 #[expect(single_use_lifetimes, reason = "false positive")] 174 fn from_der<'b: 'a>(der: &'b [u8]) -> Result<Self, SubjectPublicKeyInfoErr>; 175 } 176 impl<'a> SubjectPublicKeyInfo<'a> for Ed25519PubKey<&'a [u8]> { 177 #[expect(single_use_lifetimes, reason = "false positive")] 178 fn from_der<'b: 'a>(der: &'b [u8]) -> Result<Self, SubjectPublicKeyInfoErr> { 179 /// ```asn 180 /// SubjectPublicKeyInfo ::= SEQUENCE { 181 /// algorithm AlgorithmIdentifier, 182 /// subjectPublicKey BIT STRING 183 /// } 184 /// 185 /// AlgorithmIdentifier ::= SEQUENCE { 186 /// algorithm OBJECT IDENTIFIER, 187 /// parameters ANY DEFINED BY algorithm OPTIONAL 188 /// } 189 /// ``` 190 /// 191 /// [RFC 8410](https://www.rfc-editor.org/rfc/rfc8410#section-3) requires parameters to not exist 192 /// in `AlgorithmIdentifier`. 193 /// 194 /// RFC 8410 defines the OID as 1.3.101.112 which is encoded as 43.101.112 195 /// per [X.690](https://www.itu.int/rec/T-REC-X.690-202102-I/en). 196 /// 197 /// RFC 8410 defines the bitstring as a reinterpretation of the byte string. 198 const HEADER: [u8; ED25519_HEADER_LEN] = [ 199 CONSTRUCTED_SEQUENCE, 200 // `ED25519_LEN_U8` is the length of the entire payload; thus we subtract 201 // the "consumed" length. 202 ED25519_LEN_U8 - 2, 203 CONSTRUCTED_SEQUENCE, 204 // AlgorithmIdentifier only contains the OID; thus it's length is 5. 205 3 + 2, 206 OID, 207 3, 208 43, 209 101, 210 112, 211 BITSTRING, 212 // `ED25519_LEN_U8` is the length of the entire payload; thus we subtract 213 // the "consumed" length. 214 ED25519_LEN_U8 - 11, 215 // The number of unused bits. 216 0, 217 ]; 218 der.split_at_checked(HEADER.len()) 219 .ok_or(SubjectPublicKeyInfoErr::Ed25519Len) 220 .and_then(|(header, rem)| { 221 if header == HEADER { 222 if rem.len() == ed25519_dalek::PUBLIC_KEY_LENGTH { 223 Ok(Self(rem)) 224 } else { 225 Err(SubjectPublicKeyInfoErr::Ed25519Len) 226 } 227 } else { 228 Err(SubjectPublicKeyInfoErr::Ed25519Header) 229 } 230 }) 231 } 232 } 233 impl<'a> SubjectPublicKeyInfo<'a> for UncompressedP256PubKey<'a> { 234 #[expect(single_use_lifetimes, reason = "false positive")] 235 fn from_der<'b: 'a>(der: &'b [u8]) -> Result<Self, SubjectPublicKeyInfoErr> { 236 // ```asn 237 // SubjectPublicKeyInfo ::= SEQUENCE { 238 // algorithm AlgorithmIdentifier, 239 // subjectPublicKey BIT STRING 240 // } 241 // 242 // AlgorithmIdentifier ::= SEQUENCE { 243 // algorithm OBJECT IDENTIFIER, 244 // parameters ANY DEFINED BY algorithm OPTIONAL 245 // } 246 // 247 // ECParameters ::= CHOICE { 248 // namedCurve OBJECT IDENTIFIER 249 // -- implicitCurve NULL 250 // -- specifiedCurve SpecifiedECDomain 251 // } 252 // ``` 253 // [RFC 5480](https://www.rfc-editor.org/rfc/rfc5480#section-2.1.1) requires parameters to exist and 254 // be of the `ECParameters` form with the requirement that `namedCurve` is chosen. 255 // 256 // RFC 5480 defines the OID for id-ecPublicKey as 1.2.840.10045.2.1 and the OID for the namedCurve 257 // secp256r1 as 1.2.840.10045.3.1.7. The former OID is encoded as 42.134.72.206.61.2.1 and the latter 258 // is encoded as 42.134.72.206.61.3.1.7 per [X.690](https://www.itu.int/rec/T-REC-X.690-202102-I/en). 259 // 260 // [RFC 5480](https://www.rfc-editor.org/rfc/rfc5480#section-5) only requires support for the 261 // uncompressed form and only states that the compressed form MAY be supported. In practice this means 262 // DER-encoded payloads almost always are of the uncompressed form for compatibility reasons. This in 263 // conjunction with the fact the COSE key is required to be in the uncompressed form means we only support 264 // DER-encoded payloads containing uncompressed keys. 265 // 266 // [SEC 1](https://secg.org/sec1-v2.pdf) defines the point as an octet string, and the conversion 267 // to a bitstring simply requires reinterpreting the octet string as a bitstring. 268 269 /// Header of the DER-encoded payload before the public key. 270 const HEADER: [u8; P256_HEADER_LEN] = [ 271 CONSTRUCTED_SEQUENCE, 272 // `P256_LEN_U8` is the length of the entire payload; thus we subtract 273 // the "consumed" length. 274 P256_LEN_U8 - 2, 275 CONSTRUCTED_SEQUENCE, 276 7 + 2 + 8 + 2, 277 OID, 278 7, 279 42, 280 134, 281 72, 282 206, 283 61, 284 2, 285 1, 286 OID, 287 8, 288 42, 289 134, 290 72, 291 206, 292 61, 293 3, 294 1, 295 7, 296 BITSTRING, 297 // `P256_LEN_U8` is the length of the entire payload; thus we subtract 298 // the "consumed" length. 299 P256_LEN_U8 - 25, 300 // The number of unused bits. 301 0, 302 // SEC-1 tag for an uncompressed key. 303 4, 304 ]; 305 der.split_at_checked(HEADER.len()) 306 .ok_or(SubjectPublicKeyInfoErr::P256Len) 307 .and_then(|(header, header_rem)| { 308 if header == HEADER { 309 header_rem 310 .split_at_checked(P256_X_LEN) 311 .ok_or(SubjectPublicKeyInfoErr::P256Len) 312 .and_then(|(x, y)| { 313 if y.len() == P256_Y_LEN { 314 Ok(Self(x, y)) 315 } else { 316 Err(SubjectPublicKeyInfoErr::P256Len) 317 } 318 }) 319 } else { 320 Err(SubjectPublicKeyInfoErr::P256Header) 321 } 322 }) 323 } 324 } 325 impl<'a> SubjectPublicKeyInfo<'a> for UncompressedP384PubKey<'a> { 326 #[expect(single_use_lifetimes, reason = "false positive")] 327 fn from_der<'b: 'a>(der: &'b [u8]) -> Result<Self, SubjectPublicKeyInfoErr> { 328 // ```asn 329 // SubjectPublicKeyInfo ::= SEQUENCE { 330 // algorithm AlgorithmIdentifier, 331 // subjectPublicKey BIT STRING 332 // } 333 // 334 // AlgorithmIdentifier ::= SEQUENCE { 335 // algorithm OBJECT IDENTIFIER, 336 // parameters ANY DEFINED BY algorithm OPTIONAL 337 // } 338 // 339 // ECParameters ::= CHOICE { 340 // namedCurve OBJECT IDENTIFIER 341 // -- implicitCurve NULL 342 // -- specifiedCurve SpecifiedECDomain 343 // } 344 // ``` 345 // [RFC 5480](https://www.rfc-editor.org/rfc/rfc5480#section-2.1.1) requires parameters to exist and 346 // be of the `ECParameters` form with the requirement that `namedCurve` is chosen. 347 // 348 // RFC 5480 defines the OID for id-ecPublicKey as 1.2.840.10045.2.1 and the OID for the namedCurve 349 // secp384r1 as 1.3.132.0.34. The former OID is encoded as 42.134.72.206.61.2.1 and the latter 350 // is encoded as 43.129.4.0.34 per [X.690](https://www.itu.int/rec/T-REC-X.690-202102-I/en). 351 // 352 // [RFC 5480](https://www.rfc-editor.org/rfc/rfc5480#section-5) only requires support for the 353 // uncompressed form and only states that the compressed form MAY be supported. In practice this means 354 // DER-encoded payloads almost always are of the uncompressed form for compatibility reasons. This in 355 // conjunction with the fact the COSE key is required to be in the uncompressed form means we only support 356 // DER-encoded payloads containing uncompressed keys. 357 // 358 // [SEC 1](https://secg.org/sec1-v2.pdf) defines the point as an octet string, and the conversion 359 // to a bitstring simply requires reinterpreting the octet string as a bitstring. 360 361 /// Header of the DER-encoded payload before the public key. 362 const HEADER: [u8; P384_HEADER_LEN] = [ 363 CONSTRUCTED_SEQUENCE, 364 // `P384_LEN_U8` is the length of the entire payload; thus we subtract 365 // the "consumed" length. 366 P384_LEN_U8 - 2, 367 CONSTRUCTED_SEQUENCE, 368 7 + 2 + 5 + 2, 369 OID, 370 7, 371 42, 372 134, 373 72, 374 206, 375 61, 376 2, 377 1, 378 OID, 379 5, 380 43, 381 129, 382 4, 383 0, 384 34, 385 BITSTRING, 386 // `P384_LEN_U8` is the length of the entire payload; thus we subtract 387 // the "consumed" length. 388 P384_LEN_U8 - 22, 389 // The number of unused bits. 390 0, 391 // SEC-1 tag for an uncompressed key. 392 4, 393 ]; 394 der.split_at_checked(HEADER.len()) 395 .ok_or(SubjectPublicKeyInfoErr::P384Len) 396 .and_then(|(header, header_rem)| { 397 if header == HEADER { 398 header_rem 399 .split_at_checked(P384_X_LEN) 400 .ok_or(SubjectPublicKeyInfoErr::P384Len) 401 .and_then(|(x, y)| { 402 if y.len() == P384_Y_LEN { 403 Ok(Self(x, y)) 404 } else { 405 Err(SubjectPublicKeyInfoErr::P384Len) 406 } 407 }) 408 } else { 409 Err(SubjectPublicKeyInfoErr::P384Header) 410 } 411 }) 412 } 413 } 414 impl<'a> SubjectPublicKeyInfo<'a> for RsaPubKey<&'a [u8]> { 415 #[expect(single_use_lifetimes, reason = "false positive")] 416 #[expect( 417 clippy::arithmetic_side_effects, 418 clippy::big_endian_bytes, 419 clippy::indexing_slicing, 420 clippy::missing_asserts_for_indexing, 421 reason = "comments justify their correctness" 422 )] 423 #[expect( 424 clippy::too_many_lines, 425 reason = "rsa keys are the only type that would benefit from a modular SubjectPublicKeyInfo (similar to FromCbor). if more types benefit in the future, then this will be done." 426 )] 427 fn from_der<'b: 'a>(der: &'b [u8]) -> Result<Self, SubjectPublicKeyInfoErr> { 428 // ```asn 429 // SubjectPublicKeyInfo ::= SEQUENCE { 430 // algorithm AlgorithmIdentifier, 431 // subjectPublicKey BIT STRING 432 // } 433 // 434 // AlgorithmIdentifier ::= SEQUENCE { 435 // algorithm OBJECT IDENTIFIER, 436 // parameters ANY DEFINED BY algorithm OPTIONAL 437 // } 438 // 439 // RSAPublicKey ::= SEQUENCE { 440 // modulus INTEGER, -- n 441 // publicExponent INTEGER --e 442 // } 443 // 444 // pkcs-1 OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840) 445 // rsadsi(113549) pkcs(1) 1 446 // } 447 // 448 // rsaEncryption OBJECT IDENTIFIER ::= { pkcs-1 1} 449 // ``` 450 // [RFC 3279](https://www.rfc-editor.org/rfc/rfc3279#section-2.3.1) requires parameters to exist and 451 // be null. 452 // 453 // RFC 3279 defines the OID for rsaEncryption as 1.2.840.113549.1.1.1 which is encoded as 454 // 42.134.72.134.247.13.1.1.1 per [X.690](https://www.itu.int/rec/T-REC-X.690-202102-I/en). 455 // 456 // Note we only allow moduli that are 256 to 2048 bytes in length inclusively. Additionally 457 // we only allow `u32` exponents; consequently all lengths that include the modulus will always be 458 // encoded with two bytes. 459 460 /// `AlgorithmIdentifier` header including the `BITSTRING` and number of bytes the length 461 /// is encoded in of the `BITSTRING` type of `subjectPublicKey` 462 /// (130-128 = 2 bytes to encode the length). 463 const ALG_OID_HEADER: [u8; 17] = [ 464 CONSTRUCTED_SEQUENCE, 465 9 + 2 + 2, 466 OID, 467 9, 468 42, 469 134, 470 72, 471 134, 472 247, 473 13, 474 1, 475 1, 476 1, 477 NULL, 478 0, 479 BITSTRING, 480 130, 481 ]; 482 /// `CONSTRUCTED_SEQUENCE` whose length is encoded in two bytes. 483 const SEQ_LONG: [u8; 2] = [CONSTRUCTED_SEQUENCE, 130]; 484 /// `INTEGER` whose length is encoded in two bytes. 485 const INT_LONG: [u8; 2] = [INTEGER, 130]; 486 der.split_at_checked(SEQ_LONG.len()) 487 .ok_or(SubjectPublicKeyInfoErr::RsaLen) 488 .and_then(|(seq, seq_rem)| { 489 if seq == SEQ_LONG { 490 seq_rem 491 .split_at_checked(2) 492 .ok_or(SubjectPublicKeyInfoErr::RsaLen) 493 .and_then(|(seq_len, seq_len_rem)| { 494 let mut len = [0; 2]; 495 len.copy_from_slice(seq_len); 496 let rem_len = usize::from(u16::from_be_bytes(len)); 497 if rem_len == seq_len_rem.len() { 498 if rem_len > 255 { 499 // We can safely split here since we know `seq_len_rem` is at least 500 // 256 which is greater than `ALG_OID_HEADER.len()`. 501 let (a_oid, a_oid_rem) = seq_len_rem.split_at(ALG_OID_HEADER.len()); 502 if a_oid == ALG_OID_HEADER { 503 // `a_oid_rem.len()` is at least 239, so splitting is fine. 504 let (bit_str_len_enc, bit_str_val) = a_oid_rem.split_at(2); 505 let mut bit_string_len = [0; 2]; 506 bit_string_len.copy_from_slice(bit_str_len_enc); 507 let bit_str_val_len = usize::from(u16::from_be_bytes(bit_string_len)); 508 if bit_str_val_len == bit_str_val.len() { 509 if bit_str_val_len > 255 { 510 // `bit_str_val.len() > 255`, so splitting is fine. 511 let (unused_bits, bits_rem) = bit_str_val.split_at(1); 512 if unused_bits == [0] { 513 // We can safely split here since we know `bits_rem.len()` is at least 514 // 255. 515 let (rsa_seq, rsa_seq_rem) = bits_rem.split_at(SEQ_LONG.len()); 516 if rsa_seq == SEQ_LONG { 517 // `rsa_seq_rem.len()` is at least 253, so splitting is fine. 518 let (rsa_seq_len_enc, rsa_seq_len_enc_rem) = rsa_seq_rem.split_at(2); 519 let mut rsa_seq_len = [0; 2]; 520 rsa_seq_len.copy_from_slice(rsa_seq_len_enc); 521 let rsa_key_info_len = usize::from(u16::from_be_bytes(rsa_seq_len)); 522 if rsa_key_info_len == rsa_seq_len_enc_rem.len() { 523 if rsa_key_info_len > 255 { 524 // We can safely split here since we know `rsa_seq_len_enc_rem.len()` 525 // is at least 256. 526 let (n_meta, n_meta_rem) = rsa_seq_len_enc_rem.split_at(INT_LONG.len()); 527 if n_meta == INT_LONG { 528 // `n_meta_rem.len()` is at least 254, so splitting is fine. 529 let (n_len_enc, n_len_enc_rem) = n_meta_rem.split_at(2); 530 let mut n_len = [0; 2]; 531 n_len.copy_from_slice(n_len_enc); 532 let mod_len = usize::from(u16::from_be_bytes(n_len)); 533 if mod_len > 255 { 534 n_len_enc_rem.split_at_checked(mod_len).ok_or(SubjectPublicKeyInfoErr::RsaLen).and_then(|(mut n, n_rem)| { 535 // `n.len() > 255`, so indexing is fine. 536 let n_first = n[0]; 537 // DER integers are signed; thus the most significant bit must be 0. 538 // DER integers are minimally encoded; thus when a leading 0 exists, 539 // the second byte must be at least 128. 540 // `n.len() > 255`, so indexing is fine. 541 if n_first < 128 && (n_first != 0 || n[1] > 127) { 542 if n_first == 0 { 543 // `n.len() > 255`, so indexing is fine. 544 // We must remove the leading 0. 545 n = &n[1..]; 546 } 547 n_rem.split_first().ok_or(SubjectPublicKeyInfoErr::RsaLen).and_then(|(e_type, e_type_rem)| { 548 if *e_type == INTEGER { 549 e_type_rem.split_first().ok_or(SubjectPublicKeyInfoErr::RsaLen).and_then(|(e_len, e_len_rem)| { 550 let e_len_usize = usize::from(*e_len); 551 if e_len_usize == e_len_rem.len() { 552 e_len_rem.first().ok_or(SubjectPublicKeyInfoErr::RsaLen).and_then(|&e_first| { 553 // DER integers are signed; thus the most significant bit must be 0. 554 if e_first < 128 { 555 // `RsaPubKey` only allows `u32` exponents, which means we only care 556 // about lengths up to 5. 557 match e_len_usize { 558 1 => Ok(u32::from(e_first)), 559 2..=5 => if e_first == 0 { 560 // DER integers are minimally encoded; thus when a leading 561 // 0 exists, the second byte must be at least 128. 562 // We know the length is at least 2; thus this won't `panic`. 563 if e_len_rem[1] > 127 { 564 let mut e = [0; 4]; 565 if e_len_usize == 5 { 566 // We know the length is at least 2; thus this won't `panic`. 567 e.copy_from_slice(&e_len_rem[1..]); 568 } else { 569 // `e.len() == 4` and `e_len_usize` is at most 4; thus underflow 570 // won't occur nor will indexing `panic`. `e` is big-endian, 571 // so we start from the right. 572 e[4 - e_len_usize..].copy_from_slice(e_len_rem); 573 } 574 Ok(u32::from_be_bytes(e)) 575 } else { 576 Err(SubjectPublicKeyInfoErr::RsaEncoding) 577 } 578 } else if e_len_usize == 5 { 579 // 5 bytes are only possible for `INTEGER`s that 580 // are greater than `i32::MAX`, which will be encoded 581 // with a leading 0. 582 Err(SubjectPublicKeyInfoErr::RsaEncoding) 583 } else { 584 let mut e = [0; 4]; 585 // `e.len() == 4` and `e_len_usize` is at most 4; thus underflow 586 // won't occur nor will indexing `panic`. `e` is big-endian, 587 // so we start from the right. 588 e[4 - e_len_usize..].copy_from_slice(e_len_rem); 589 Ok(u32::from_be_bytes(e)) 590 }, 591 _ => Err(SubjectPublicKeyInfoErr::RsaExponentTooLarge), 592 }.and_then(|e| Self::try_from((n, e)).map_err(SubjectPublicKeyInfoErr::RsaPubKey)) 593 } else { 594 Err(SubjectPublicKeyInfoErr::RsaEncoding) 595 } 596 }) 597 } else { 598 Err(SubjectPublicKeyInfoErr::RsaLen) 599 } 600 }) 601 } else { 602 Err(SubjectPublicKeyInfoErr::RsaEncoding) 603 } 604 }) 605 } else { 606 Err(SubjectPublicKeyInfoErr::RsaEncoding) 607 } 608 }) 609 } else { 610 Err(SubjectPublicKeyInfoErr::RsaEncoding) 611 } 612 } else { 613 Err(SubjectPublicKeyInfoErr::RsaEncoding) 614 } 615 } else { 616 Err(SubjectPublicKeyInfoErr::RsaEncoding) 617 } 618 } else { 619 Err(SubjectPublicKeyInfoErr::RsaLen) 620 } 621 } else { 622 Err(SubjectPublicKeyInfoErr::RsaEncoding) 623 } 624 } else { 625 Err(SubjectPublicKeyInfoErr::RsaEncoding) 626 } 627 } else { 628 Err(SubjectPublicKeyInfoErr::RsaEncoding) 629 } 630 } else { 631 Err(SubjectPublicKeyInfoErr::RsaLen) 632 } 633 } else { 634 Err(SubjectPublicKeyInfoErr::RsaEncoding) 635 } 636 } else { 637 Err(SubjectPublicKeyInfoErr::RsaEncoding) 638 } 639 } else { 640 Err(SubjectPublicKeyInfoErr::RsaLen) 641 } 642 }) 643 } else { 644 Err(SubjectPublicKeyInfoErr::RsaEncoding) 645 } 646 }) 647 } 648 } 649 impl<'a> SubjectPublicKeyInfo<'a> for UncompressedPubKey<'a> { 650 #[expect(single_use_lifetimes, reason = "false positive")] 651 fn from_der<'b: 'a>(der: &'b [u8]) -> Result<Self, SubjectPublicKeyInfoErr> { 652 // The lengths of the three key types do not overlap. 653 match der.len() { 654 // The minimum modulus we support for RSA is 2048 bits which is 256 bytes; 655 // thus clearly its encoding will be at least 256 which is greater than 656 // all of the other values. 657 ED25519_LEN => Ed25519PubKey::from_der(der).map(Self::Ed25519), 658 P256_LEN => UncompressedP256PubKey::from_der(der).map(Self::P256), 659 P384_LEN => UncompressedP384PubKey::from_der(der).map(Self::P384), 660 256.. => RsaPubKey::from_der(der).map(Self::Rsa), 661 _ => Err(SubjectPublicKeyInfoErr::Len), 662 } 663 } 664 } 665 } 666 /// Helper type returned from [`AuthenticatorAttestationVisitor::visit_map`]. 667 /// 668 /// The purpose of this type is to hopefully avoid re-parsing the raw attestation object multiple times. In 669 /// particular [`Registration`] and [`super::ser_relaxed::RegistrationRelaxed`] will attempt to validate `id` is the 670 /// same as the [`CredentialId`] within the attestation object. 671 pub(super) struct AuthAttest { 672 /// The data we care about. 673 pub attest: AuthenticatorAttestation, 674 /// [`CredentialId`] information. This is `None` iff `authenticatorData`, `publicKey`, and 675 /// `publicKeyAlgorithm` do not exist and we are performing a `RELAXED` parsing. When `Some`, the first 676 /// `usize` is the starting index of `CredentialId` within the attestation object; and the second `usize` is 677 /// 1 past the last index of `CredentialId`. 678 pub cred_info: Option<(usize, usize)>, 679 } 680 /// Fields in `AuthenticatorAttestationResponseJSON`. 681 enum AttestField<const IGNORE_UNKNOWN: bool> { 682 /// `clientDataJSON`. 683 ClientDataJson, 684 /// `attestationObject`. 685 AttestationObject, 686 /// `authenticatorData`. 687 AuthenticatorData, 688 /// `transports`. 689 Transports, 690 /// `publicKey`. 691 PublicKey, 692 /// `publicKeyAlgorithm`. 693 PublicKeyAlgorithm, 694 /// Unknown fields. 695 Other, 696 } 697 impl<'e, const I: bool> Deserialize<'e> for AttestField<I> { 698 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 699 where 700 D: Deserializer<'e>, 701 { 702 /// `Visitor` for `AttestField`. 703 struct AttestFieldVisitor<const IGNORE_UNKNOWN: bool>; 704 impl<const IG: bool> Visitor<'_> for AttestFieldVisitor<IG> { 705 type Value = AttestField<IG>; 706 fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result { 707 write!( 708 formatter, 709 "'{CLIENT_DATA_JSON}', '{ATTESTATION_OBJECT}', '{AUTHENTICATOR_DATA}', '{TRANSPORTS}', '{PUBLIC_KEY}', or '{PUBLIC_KEY_ALGORITHM}'" 710 ) 711 } 712 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E> 713 where 714 E: Error, 715 { 716 match v { 717 CLIENT_DATA_JSON => Ok(AttestField::ClientDataJson), 718 ATTESTATION_OBJECT => Ok(AttestField::AttestationObject), 719 AUTHENTICATOR_DATA => Ok(AttestField::AuthenticatorData), 720 TRANSPORTS => Ok(AttestField::Transports), 721 PUBLIC_KEY => Ok(AttestField::PublicKey), 722 PUBLIC_KEY_ALGORITHM => Ok(AttestField::PublicKeyAlgorithm), 723 _ => { 724 if IG { 725 Ok(AttestField::Other) 726 } else { 727 Err(E::unknown_field(v, AUTH_ATTEST_FIELDS)) 728 } 729 } 730 } 731 } 732 } 733 deserializer.deserialize_identifier(AttestFieldVisitor::<I>) 734 } 735 } 736 /// Attestation object. We use this instead of `Base64DecodedVal` since we want to manually 737 /// allocate the `Vec` in order to avoid re-allocation. Internally `AuthenticatorAttestation::new` 738 /// appends the SHA-256 hash to the passed attestation object `Vec` to avoid temporarily allocating 739 /// a `Vec` that contains the attestation object and hash for signature verification. Calling code 740 /// can avoid any reallocation that would occur when the capacity is not large enough by ensuring the 741 /// passed `Vec` has at least 32 bytes of available capacity. 742 pub(super) struct AttObj(pub Vec<u8>); 743 impl<'e> Deserialize<'e> for AttObj { 744 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 745 where 746 D: Deserializer<'e>, 747 { 748 /// `Visitor` for `AttObj`. 749 struct AttObjVisitor; 750 impl Visitor<'_> for AttObjVisitor { 751 type Value = AttObj; 752 fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result { 753 formatter.write_str("base64url-encoded attestation object") 754 } 755 #[expect( 756 clippy::arithmetic_side_effects, 757 reason = "comment justifies their correctness" 758 )] 759 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E> 760 where 761 E: Error, 762 { 763 base64url_nopad::decode_len(v.len()) 764 .ok_or_else(|| E::invalid_value(Unexpected::Str(v), &"base64url-encoded value")) 765 .and_then(|len| { 766 // The decoded length is 3/4 of the encoded length, so overflow could only occur 767 // if usize::MAX / 4 < 32 => usize::MAX < 128 < u8::MAX; thus overflow is not 768 // possible. We add 32 since the SHA-256 hash of `clientDataJSON` will be added to 769 // the raw attestation object by `AuthenticatorAttestation::new`. 770 let mut att_obj = vec![0; len + Sha256::output_size()]; 771 att_obj.truncate(len); 772 base64url_nopad::decode_buffer_exact(v.as_bytes(), &mut att_obj) 773 .map_err(E::custom) 774 .map(|()| AttObj(att_obj)) 775 }) 776 } 777 } 778 deserializer.deserialize_str(AttObjVisitor) 779 } 780 } 781 /// `Visitor` for `AuthenticatorAttestation`. 782 /// 783 /// Unknown fields are ignored and only `clientDataJSON` and `attestationObject` are required iff `RELAXED`. 784 pub(super) struct AuthenticatorAttestationVisitor<const RELAXED: bool>; 785 impl<'d, const R: bool> Visitor<'d> for AuthenticatorAttestationVisitor<R> { 786 type Value = AuthAttest; 787 fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result { 788 formatter.write_str("AuthenticatorAttestation") 789 } 790 #[expect(clippy::too_many_lines, reason = "find it easier to reason about")] 791 #[expect( 792 clippy::arithmetic_side_effects, 793 clippy::indexing_slicing, 794 reason = "comments justify their correctness" 795 )] 796 fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error> 797 where 798 A: MapAccess<'d>, 799 { 800 use spki::SubjectPublicKeyInfo as _; 801 let mut client_data = None; 802 let mut attest = None; 803 let mut auth = None; 804 let mut pub_key = None; 805 let mut key_alg = None; 806 let mut trans = None; 807 while let Some(key) = map.next_key::<AttestField<R>>()? { 808 match key { 809 AttestField::ClientDataJson => { 810 if client_data.is_some() { 811 return Err(Error::duplicate_field(CLIENT_DATA_JSON)); 812 } 813 client_data = map 814 .next_value::<Base64DecodedVal>() 815 .map(|c_data| Some(c_data.0))?; 816 } 817 AttestField::AttestationObject => { 818 if attest.is_some() { 819 return Err(Error::duplicate_field(ATTESTATION_OBJECT)); 820 } 821 attest = map.next_value::<AttObj>().map(|att_obj| Some(att_obj.0))?; 822 } 823 AttestField::AuthenticatorData => { 824 if auth.is_some() { 825 return Err(Error::duplicate_field(AUTHENTICATOR_DATA)); 826 } 827 auth = map.next_value::<Option<Base64DecodedVal>>().map(Some)?; 828 } 829 AttestField::Transports => { 830 if trans.is_some() { 831 return Err(Error::duplicate_field(TRANSPORTS)); 832 } 833 trans = map.next_value::<Option<_>>().map(Some)?; 834 } 835 AttestField::PublicKey => { 836 if pub_key.is_some() { 837 return Err(Error::duplicate_field(PUBLIC_KEY)); 838 } 839 pub_key = map.next_value::<Option<Base64DecodedVal>>().map(Some)?; 840 } 841 AttestField::PublicKeyAlgorithm => { 842 if key_alg.is_some() { 843 return Err(Error::duplicate_field(PUBLIC_KEY_ALGORITHM)); 844 } 845 key_alg = map 846 .next_value::<Option<CoseAlgorithmIdentifier>>() 847 .map(Some)?; 848 } 849 AttestField::Other => map.next_value::<IgnoredAny>().map(|_| ())?, 850 } 851 } 852 // Note the order of this matters from a performance perspective. In particular `auth` must be evaluated 853 // before `pub_key` which must be evaluated before `key_alg` as this allows us to parse the attestation 854 // object at most once and allow us to prioritize parsing `authenticatorData` over the attestation object. 855 client_data.ok_or_else(|| Error::missing_field(CLIENT_DATA_JSON)).and_then(|client_data_json| attest.ok_or_else(|| Error::missing_field(ATTESTATION_OBJECT)).and_then(|attestation_object| { 856 trans.ok_or(false).and_then(|opt_trans| opt_trans.ok_or(true)).or_else( 857 |flag| { 858 if R { 859 Ok(AuthTransports::new()) 860 } else if flag { 861 Err(Error::invalid_type(Unexpected::Other("null"), &format!("{TRANSPORTS} to be a sequence of AuthenticatorTransports").as_str())) 862 } else { 863 Err(Error::missing_field(TRANSPORTS)) 864 } 865 }, 866 ).and_then(|transports| { 867 auth.ok_or(false).and_then(|opt_auth| opt_auth.ok_or(true)).as_ref().map_or_else( 868 |flag| { 869 if R { 870 Ok(None) 871 } else if *flag { 872 Err(Error::invalid_type(Unexpected::Other("null"), &format!("{AUTHENTICATOR_DATA} to be a base64url-encoded AuthenticatorData").as_str())) 873 } else { 874 Err(Error::missing_field(AUTHENTICATOR_DATA)) 875 } 876 }, 877 |a_data| { 878 if a_data.0.len() > 37 { 879 // The last portion of attestation object is always authenticator data. 880 attestation_object.len().checked_sub(a_data.0.len()).ok_or_else(|| Error::invalid_value(Unexpected::Bytes(a_data.0.as_slice()), &format!("authenticator data to match the authenticator data portion of attestation object: {attestation_object:?}").as_str())).and_then(|idx| { 881 // Indexing is fine; otherwise the above check would have returned `None`. 882 if *a_data.0 == attestation_object[idx..] { 883 // We know `a_data.len() > 37`; thus indexing is fine. 884 // We start at 37 since that is the beginning of `attestedCredentialData`. 885 // Recall the first 32 bytes are `rpIdHash`, then a 1 byte `flags`, then a 886 // 4-byte big-endian integer `signCount`. 887 // The starting index of `credentialId` is 18 within `attestedCredentialData`. 888 // Recall the first 16 bytes are `aaguid`, then a 2-byte big-endian integer 889 // `credentialIdLength`. Consequently the starting index within 890 // `attestation_object` is `idx + 37 + 18` = `idx + 55`. Overflow cannot occur 891 // since we successfully parsed `AttestedCredentialData`. 892 AttestedCredentialData::from_cbor(&a_data.0[37..]).map_err(Error::custom).map(|success| Some((success.value, idx + 55))) 893 } else { 894 Err(Error::invalid_value(Unexpected::Bytes(a_data.0.as_slice()), &format!("authenticator data to match the authenticator data portion of attestation object: {:?}", &attestation_object[idx..]).as_str())) 895 } 896 }) 897 } else { 898 Err(Error::invalid_value(Unexpected::Bytes(a_data.0.as_slice()), &"authenticator data to be long enough to contain attested credential data")) 899 } 900 } 901 ).and_then(|attested_info| { 902 pub_key.ok_or(false).and_then(|opt_key| opt_key.ok_or(true)).map_or_else( 903 |flag| { 904 if R { 905 attested_info.as_ref().map_or(Ok(None), |&(ref attested_data, cred_id_start)| Ok(Some((match attested_data.credential_public_key { 906 UncompressedPubKey::Ed25519(_) => CoseAlgorithmIdentifier::Eddsa, 907 UncompressedPubKey::P256(_) => CoseAlgorithmIdentifier::Es256, 908 UncompressedPubKey::P384(_) => CoseAlgorithmIdentifier::Es384, 909 UncompressedPubKey::Rsa(_) => CoseAlgorithmIdentifier::Rs256, 910 // Overflow won't occur since this is correct as 911 // `AttestedCredentialData::from_cbor` would have erred if not. 912 }, cred_id_start, cred_id_start + attested_data.credential_id.0.len())))) 913 } else { 914 // `publicKey` is only allowed to not exist when `CoseAlgorithmIdentifier::Eddsa`, 915 // `CoseAlgorithmIdentifier::Es256`, or `CoseAlgorithmIdentifier::Rs256` is not 916 // used. 917 attested_info.as_ref().map_or_else( 918 || AttestationObject::parse_data(attestation_object.as_slice()).map_err(Error::custom).and_then(|(att_obj, auth_idx)| { 919 match att_obj.auth_data.attested_credential_data.credential_public_key { 920 UncompressedPubKey::P384(_) => { 921 // This won't overflow since `AttestationObject::parse_data` succeeded and `auth_idx` 922 // is the start of the raw authenticator data which itself contains the raw Credential ID. 923 Ok(Some((CoseAlgorithmIdentifier::Es384, auth_idx, auth_idx + att_obj.auth_data.attested_credential_data.credential_id.0.len()))) 924 } 925 UncompressedPubKey::Ed25519(_) | UncompressedPubKey::P256(_) | UncompressedPubKey::Rsa(_) => Err(Error::missing_field(PUBLIC_KEY)), 926 } 927 }), 928 |&(ref attested_data, cred_id_start)| { 929 match attested_data.credential_public_key { 930 UncompressedPubKey::P384(_) => { 931 // Overflow won't occur since this is correct. This is correct since we successfully parsed 932 // `AttestedCredentialData` and calculated `cred_id_start` from it. 933 Ok(Some((CoseAlgorithmIdentifier::Es384, cred_id_start, cred_id_start + attested_data.credential_id.0.len()))) 934 } 935 UncompressedPubKey::Ed25519(_) | UncompressedPubKey::P256(_) | UncompressedPubKey::Rsa(_) => if flag { Err(Error::invalid_type(Unexpected::Other("null"), &format!("{PUBLIC_KEY} to be a base64url-encoded DER-encoded SubjectPublicKeyInfo").as_str())) } else { Err(Error::missing_field(PUBLIC_KEY)) }, 936 } 937 } 938 ) 939 } 940 }, 941 |der| { 942 UncompressedPubKey::from_der(der.0.as_slice()).map_err(Error::custom).and_then(|key| { 943 attested_info.as_ref().map_or_else( 944 || AttestationObject::parse_data(attestation_object.as_slice()).map_err(Error::custom).and_then(|(att_obj, auth_idx)| { 945 if key == att_obj.auth_data.attested_credential_data.credential_public_key { 946 let alg = match att_obj.auth_data.attested_credential_data.credential_public_key { 947 UncompressedPubKey::Ed25519(_) => CoseAlgorithmIdentifier::Eddsa, 948 UncompressedPubKey::P256(_) => CoseAlgorithmIdentifier::Es256, 949 UncompressedPubKey::P384(_) => CoseAlgorithmIdentifier::Es384, 950 UncompressedPubKey::Rsa(_) => CoseAlgorithmIdentifier::Rs256, 951 }; 952 // This won't overflow since `AttestationObject::parse_data` succeeded and `auth_idx` 953 // is the start of the raw authenticator data which itself contains the raw Credential ID. 954 Ok(Some((alg, auth_idx, auth_idx+ att_obj.auth_data.attested_credential_data.credential_id.0.len()))) 955 } else { 956 Err(Error::invalid_value(Unexpected::Bytes(der.0.as_slice()), &format!("DER-encoded public key to match the public key within the attestation object: {:?}", att_obj.auth_data.attested_credential_data.credential_public_key).as_str())) 957 } 958 }), 959 |&(ref attested_data, cred_id_start)| { 960 if key == attested_data.credential_public_key { 961 let alg = match attested_data.credential_public_key { 962 UncompressedPubKey::Ed25519(_) => CoseAlgorithmIdentifier::Eddsa, 963 UncompressedPubKey::P256(_) => CoseAlgorithmIdentifier::Es256, 964 UncompressedPubKey::P384(_) => CoseAlgorithmIdentifier::Es384, 965 UncompressedPubKey::Rsa(_) => CoseAlgorithmIdentifier::Rs256, 966 }; 967 // Overflow won't occur since this is correct. This is correct since we successfully parsed 968 // `AttestedCredentialData` and calculated `cred_id_start` from it. 969 Ok(Some((alg, cred_id_start, cred_id_start + attested_data.credential_id.0.len()))) 970 } else { 971 Err(Error::invalid_value(Unexpected::Bytes(der.0.as_slice()), &format!("DER-encoded public key to match the public key within the attestation object: {:?}", attested_data.credential_public_key).as_str())) 972 } 973 } 974 ) 975 }) 976 } 977 ).and_then(|cred_key_alg_cred_info| { 978 key_alg.ok_or(false).and_then(|opt_alg| opt_alg.ok_or(true)).map_or_else( 979 |flag| { 980 if R { 981 Ok(cred_key_alg_cred_info.map(|info| (info.1, info.2))) 982 } else if flag { 983 Err(Error::invalid_type(Unexpected::Other("null"), &format!("{PUBLIC_KEY_ALGORITHM} to be a base64url-encoded DER-encoded SubjectPublicKeyInfo").as_str())) 984 } else { 985 Err(Error::missing_field(PUBLIC_KEY_ALGORITHM)) 986 } 987 }, 988 |alg| { 989 cred_key_alg_cred_info.map_or_else( 990 || AttestationObject::parse_data(attestation_object.as_slice()).map_err(Error::custom).and_then(|(att_obj, auth_idx)| { 991 let att_obj_alg = match att_obj.auth_data.attested_credential_data.credential_public_key { 992 UncompressedPubKey::Ed25519(_) => CoseAlgorithmIdentifier::Eddsa, 993 UncompressedPubKey::P256(_) => CoseAlgorithmIdentifier::Es256, 994 UncompressedPubKey::P384(_) => CoseAlgorithmIdentifier::Es384, 995 UncompressedPubKey::Rsa(_) => CoseAlgorithmIdentifier::Rs256, 996 }; 997 if alg == att_obj_alg { 998 // This won't overflow since `AttestationObject::parse_data` succeeded and `auth_idx` 999 // is the start of the raw authenticator data which itself contains the raw Credential ID. 1000 Ok(Some((auth_idx, auth_idx + att_obj.auth_data.attested_credential_data.credential_id.0.len()))) 1001 } else { 1002 Err(Error::invalid_value(Unexpected::Other(format!("{alg:?}").as_str()), &format!("public key algorithm to match the algorithm associated with the public key within the attestation object: {att_obj_alg:?}").as_str())) 1003 } 1004 }), 1005 |(a, start, last)| if alg == a { 1006 Ok(Some((start, last))) 1007 } else { 1008 Err(Error::invalid_value(Unexpected::Other(format!("{alg:?}").as_str()), &format!("public key algorithm to match the algorithm associated with the public key within the attestation object: {a:?}").as_str())) 1009 }, 1010 ) 1011 } 1012 ).map(|cred_info| AuthAttest{ attest: AuthenticatorAttestation::new(client_data_json, attestation_object, transports), cred_info, }) 1013 }) 1014 }) 1015 }) 1016 })) 1017 } 1018 } 1019 /// `"clientDataJSON"` 1020 const CLIENT_DATA_JSON: &str = "clientDataJSON"; 1021 /// `"attestationObject"` 1022 const ATTESTATION_OBJECT: &str = "attestationObject"; 1023 /// `"authenticatorData"` 1024 const AUTHENTICATOR_DATA: &str = "authenticatorData"; 1025 /// `"transports"` 1026 const TRANSPORTS: &str = "transports"; 1027 /// `"publicKey"` 1028 const PUBLIC_KEY: &str = "publicKey"; 1029 /// `"publicKeyAlgorithm"` 1030 const PUBLIC_KEY_ALGORITHM: &str = "publicKeyAlgorithm"; 1031 /// Fields in `AuthenticatorAttestationResponseJSON`. 1032 pub(super) const AUTH_ATTEST_FIELDS: &[&str; 6] = &[ 1033 CLIENT_DATA_JSON, 1034 ATTESTATION_OBJECT, 1035 AUTHENTICATOR_DATA, 1036 TRANSPORTS, 1037 PUBLIC_KEY, 1038 PUBLIC_KEY_ALGORITHM, 1039 ]; 1040 impl<'de> Deserialize<'de> for AuthAttest { 1041 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 1042 where 1043 D: Deserializer<'de>, 1044 { 1045 deserializer.deserialize_struct( 1046 "AuthenticatorAttestation", 1047 AUTH_ATTEST_FIELDS, 1048 AuthenticatorAttestationVisitor::<false>, 1049 ) 1050 } 1051 } 1052 impl<'de> Deserialize<'de> for AuthenticatorAttestation { 1053 /// Deserializes a `struct` based on 1054 /// [`AuthenticatorAttestationResponseJSON`](https://www.w3.org/TR/webauthn-3/#dictdef-authenticatorattestationresponsejson). 1055 /// 1056 /// Note unknown keys and duplicate keys are forbidden; 1057 /// [`clientDataJSON`](https://www.w3.org/TR/webauthn-3/#dom-authenticatorattestationresponsejson-clientdatajson), 1058 /// [`authenticatorData`](https://www.w3.org/TR/webauthn-3/#dom-authenticatorattestationresponsejson-authenticatordata), 1059 /// [`publicKey`](https://www.w3.org/TR/webauthn-3/#dom-authenticatorattestationresponsejson-publickey) 1060 /// and 1061 /// [`attestationObject`](https://www.w3.org/TR/webauthn-3/#dom-authenticatorattestationresponsejson-attestationobject) 1062 /// are base64url-decoded; 1063 /// [`transports`](https://www.w3.org/TR/webauthn-3/#dom-authenticatorattestationresponsejson-transports) 1064 /// is deserialized via [`AuthTransports::deserialize`]; the decoded `publicKey` is parsed according to the 1065 /// applicable DER-encoded ASN.1 `SubjectPublicKeyInfo` schema; 1066 /// [`publicKeyAlgorithm`](https://www.w3.org/TR/webauthn-3/#dom-authenticatorattestationresponsejson-publickeyalgorithm) 1067 /// is deserialized according to 1068 /// [`CoseAlgorithmIdentifier`](https://www.w3.org/TR/webauthn-3/#typedefdef-cosealgorithmidentifier); all `required` 1069 /// fields in the `AuthenticatorAttestationResponseJSON` Web IDL `dictionary` exist (and must not be `null`); `publicKey` 1070 /// exists when Ed25519, P-256 with SHA-256, or RSASSA-PKCS1-v1_5 with SHA-256 is used (and must not be `null`) 1071 /// [per WebAuthn](https://www.w3.org/TR/webauthn-3/#sctn-public-key-easy); the `publicKeyAlgorithm` aligns 1072 /// with 1073 /// [`credentialPublicKey`](https://www.w3.org/TR/webauthn-3/#authdata-attestedcredentialdata-credentialpublickey) 1074 /// within 1075 /// [`attestedCredentialData`](https://www.w3.org/TR/webauthn-3/#authdata-attestedcredentialdata) within the 1076 /// decoded `authenticatorData`; the decoded `publicKey` is the same as `credentialPublicKey` within 1077 /// `attestedCredentialData` within the decoded `authenticatorData`; and the decoded `authenticatorData` is the 1078 /// same as [`authData`](https://www.w3.org/TR/webauthn-3/#attestation-object) within the decoded 1079 /// `attestationObject`. 1080 #[inline] 1081 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 1082 where 1083 D: Deserializer<'de>, 1084 { 1085 AuthAttest::deserialize(deserializer).map(|val| val.attest) 1086 } 1087 } 1088 /// `Visitor` for `CredentialPropertiesOutput`. 1089 /// 1090 /// Unknown fields are ignored iff `RELAXED`. 1091 pub(super) struct CredentialPropertiesOutputVisitor<const RELAXED: bool>; 1092 impl<'d, const R: bool> Visitor<'d> for CredentialPropertiesOutputVisitor<R> { 1093 type Value = CredentialPropertiesOutput; 1094 fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result { 1095 formatter.write_str("CredentialPropertiesOutput") 1096 } 1097 fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error> 1098 where 1099 A: MapAccess<'d>, 1100 { 1101 /// Allowed fields. 1102 enum Field<const IGNORE_UNKNOWN: bool> { 1103 /// `rk` field. 1104 Rk, 1105 /// Unknown field. 1106 Other, 1107 } 1108 impl<'e, const I: bool> Deserialize<'e> for Field<I> { 1109 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 1110 where 1111 D: Deserializer<'e>, 1112 { 1113 /// `Visitor` for `Field`. 1114 /// 1115 /// Unknown fields are ignored iff `IGNORE_UNKNOWN`. 1116 struct FieldVisitor<const IGNORE_UNKNOWN: bool>; 1117 impl<const IG: bool> Visitor<'_> for FieldVisitor<IG> { 1118 type Value = Field<IG>; 1119 fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result { 1120 write!(formatter, "'{RK}'") 1121 } 1122 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E> 1123 where 1124 E: Error, 1125 { 1126 match v { 1127 RK => Ok(Field::Rk), 1128 _ => { 1129 if IG { 1130 Ok(Field::Other) 1131 } else { 1132 Err(E::unknown_field(v, PROPS_FIELDS)) 1133 } 1134 } 1135 } 1136 } 1137 } 1138 deserializer.deserialize_identifier(FieldVisitor) 1139 } 1140 } 1141 let mut rk = None; 1142 while let Some(key) = map.next_key::<Field<R>>()? { 1143 match key { 1144 Field::Rk => { 1145 if rk.is_some() { 1146 return Err(Error::duplicate_field(RK)); 1147 } 1148 rk = map.next_value().map(Some)?; 1149 } 1150 Field::Other => map.next_value::<IgnoredAny>().map(|_| ())?, 1151 } 1152 } 1153 Ok(CredentialPropertiesOutput { rk: rk.flatten() }) 1154 } 1155 } 1156 /// `"rk"` 1157 const RK: &str = "rk"; 1158 /// `CredentialPropertiesOutput` fields. 1159 pub(super) const PROPS_FIELDS: &[&str; 1] = &[RK]; 1160 impl<'de> Deserialize<'de> for CredentialPropertiesOutput { 1161 /// Deserializes a `struct` based on 1162 /// [`CredentialPropertiesOutput`](https://www.w3.org/TR/webauthn-3/#dictdef-credentialpropertiesoutput). 1163 /// 1164 /// Note unknown and duplicate keys are forbidden. 1165 #[inline] 1166 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 1167 where 1168 D: Deserializer<'de>, 1169 { 1170 deserializer.deserialize_struct( 1171 "CredentialPropertiesOutput", 1172 PROPS_FIELDS, 1173 CredentialPropertiesOutputVisitor::<false>, 1174 ) 1175 } 1176 } 1177 impl<'de> Deserialize<'de> for AuthenticationExtensionsPrfOutputs { 1178 /// Deserializes a `struct` based on 1179 /// [`AuthenticationExtensionsPRFOutputsJSON`](https://www.w3.org/TR/webauthn-3/#dictdef-authenticationextensionsprfoutputsjson). 1180 /// 1181 /// Note unknown and duplicate keys are forbidden; 1182 /// [`enabled`](https://www.w3.org/TR/webauthn-3/#dom-authenticationextensionsprfoutputs-enabled) 1183 /// must exist (and not be `null`); and 1184 /// [`results`](https://www.w3.org/TR/webauthn-3/#dom-authenticationextensionsprfoutputs-results) must not exist, 1185 /// be `null`, or be an 1186 /// [`AuthenticationExtensionsPRFValues`](https://www.w3.org/TR/webauthn-3/#dictdef-authenticationextensionsprfvalues) 1187 /// with no unknown or duplicate keys, 1188 /// [`first`](https://www.w3.org/TR/webauthn-3/#dom-authenticationextensionsprfvalues-first) must exist but be 1189 /// `null`, and 1190 /// [`second`](https://www.w3.org/TR/webauthn-3/#dom-authenticationextensionsprfvalues-second) can exist but 1191 /// must be `null` if so. 1192 #[inline] 1193 #[expect(clippy::unreachable, reason = "we want to crash when there is a bug")] 1194 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 1195 where 1196 D: Deserializer<'de>, 1197 { 1198 AuthenticationExtensionsPrfOutputsHelper::<false, true, AuthenticationExtensionsPrfValues>::deserialize(deserializer).map(|val| Self { 1199 enabled: val.0.unwrap_or_else(|| { 1200 unreachable!( 1201 "there is a bug in AuthenticationExtensionsPrfOutputsHelper::deserialize" 1202 ) 1203 }), 1204 }) 1205 } 1206 } 1207 /// `Visitor` for `ClientExtensionsOutputs`. 1208 /// 1209 /// Unknown fields are ignored iff `RELAXED`. 1210 pub(super) struct ClientExtensionsOutputsVisitor<const RELAXED: bool, PROPS, PRF>( 1211 pub PhantomData<fn() -> (PROPS, PRF)>, 1212 ); 1213 impl<'d, const R: bool, C, P> Visitor<'d> for ClientExtensionsOutputsVisitor<R, C, P> 1214 where 1215 C: for<'a> Deserialize<'a> + Into<CredentialPropertiesOutput>, 1216 P: for<'a> Deserialize<'a> + Into<AuthenticationExtensionsPrfOutputs>, 1217 { 1218 type Value = ClientExtensionsOutputs; 1219 fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result { 1220 formatter.write_str("ClientExtensionsOutputs") 1221 } 1222 fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error> 1223 where 1224 A: MapAccess<'d>, 1225 { 1226 /// Allowed fields. 1227 enum Field<const IGNORE_UNKNOWN: bool> { 1228 /// `credProps` field. 1229 CredProps, 1230 /// `prf` field. 1231 Prf, 1232 /// Unknown field. 1233 Other, 1234 } 1235 impl<'e, const I: bool> Deserialize<'e> for Field<I> { 1236 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 1237 where 1238 D: Deserializer<'e>, 1239 { 1240 /// `Visitor` for `Field`. 1241 /// 1242 /// Unknown fields are ignored iff `IGNORE_UNKNOWN`. 1243 struct FieldVisitor<const IGNORE_UNKNOWN: bool>; 1244 impl<const IG: bool> Visitor<'_> for FieldVisitor<IG> { 1245 type Value = Field<IG>; 1246 fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result { 1247 write!(formatter, "'{CRED_PROPS}' or '{PRF}'") 1248 } 1249 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E> 1250 where 1251 E: Error, 1252 { 1253 match v { 1254 CRED_PROPS => Ok(Field::CredProps), 1255 PRF => Ok(Field::Prf), 1256 _ => { 1257 if IG { 1258 Ok(Field::Other) 1259 } else { 1260 Err(E::unknown_field(v, EXT_FIELDS)) 1261 } 1262 } 1263 } 1264 } 1265 } 1266 deserializer.deserialize_identifier(FieldVisitor) 1267 } 1268 } 1269 let mut cred_props = None; 1270 let mut prf = None; 1271 while let Some(key) = map.next_key::<Field<R>>()? { 1272 match key { 1273 Field::CredProps => { 1274 if cred_props.is_some() { 1275 return Err(Error::duplicate_field(CRED_PROPS)); 1276 } 1277 cred_props = map.next_value::<Option<C>>().map(Some)?; 1278 } 1279 Field::Prf => { 1280 if prf.is_some() { 1281 return Err(Error::duplicate_field(PRF)); 1282 } 1283 prf = map.next_value::<Option<P>>().map(Some)?; 1284 } 1285 Field::Other => map.next_value::<IgnoredAny>().map(|_| ())?, 1286 } 1287 } 1288 Ok(ClientExtensionsOutputs { 1289 cred_props: cred_props.flatten().map(Into::into), 1290 prf: prf.flatten().map(Into::into), 1291 }) 1292 } 1293 } 1294 impl ClientExtensions for ClientExtensionsOutputs { 1295 fn empty() -> Self { 1296 Self { 1297 prf: None, 1298 cred_props: None, 1299 } 1300 } 1301 } 1302 /// `"credProps"` 1303 const CRED_PROPS: &str = "credProps"; 1304 /// `"prf"` 1305 const PRF: &str = "prf"; 1306 /// `AuthenticationExtensionsClientOutputsJSON` fields. 1307 pub(super) const EXT_FIELDS: &[&str; 2] = &[CRED_PROPS, PRF]; 1308 impl<'de> Deserialize<'de> for ClientExtensionsOutputs { 1309 /// Deserializes a `struct` based on 1310 /// [`AuthenticationExtensionsClientOutputsJSON`](https://www.w3.org/TR/webauthn-3/#dictdef-authenticationextensionsclientoutputsjson). 1311 /// 1312 /// Note that unknown and duplicate keys are forbidden; 1313 /// [`credProps`](https://www.w3.org/TR/webauthn-3/#dom-authenticationextensionsclientoutputs-credprops) is 1314 /// `null` or deserialized via [`CredentialPropertiesOutput::deserialize`]; and 1315 /// [`prf`](https://www.w3.org/TR/webauthn-3/#dom-authenticationextensionsclientoutputs-prf) is `null` 1316 /// or deserialized via [`AuthenticationExtensionsPrfOutputs::deserialize`]. 1317 #[inline] 1318 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 1319 where 1320 D: Deserializer<'de>, 1321 { 1322 deserializer.deserialize_struct( 1323 "ClientExtensionsOutputs", 1324 EXT_FIELDS, 1325 ClientExtensionsOutputsVisitor::< 1326 false, 1327 CredentialPropertiesOutput, 1328 AuthenticationExtensionsPrfOutputs, 1329 >(PhantomData), 1330 ) 1331 } 1332 } 1333 impl<'de> Deserialize<'de> for Registration { 1334 /// Deserializes a `struct` based on 1335 /// [`RegistrationResponseJSON`](https://www.w3.org/TR/webauthn-3/#dictdef-registrationresponsejson). 1336 /// 1337 /// Note that unknown and duplicate keys are forbidden; 1338 /// [`id`](https://www.w3.org/TR/webauthn-3/#dom-registrationresponsejson-id) and 1339 /// [`rawId`](https://www.w3.org/TR/webauthn-3/#dom-registrationresponsejson-rawid) are deserialized 1340 /// via [`CredentialId::deserialize`]; 1341 /// [`response`](https://www.w3.org/TR/webauthn-3/#dom-registrationresponsejson-response) is deserialized 1342 /// via [`AuthenticatorAttestation::deserialize`]; 1343 /// [`authenticatorAttachment`](https://www.w3.org/TR/webauthn-3/#dom-registrationresponsejson-authenticatorattachment) 1344 /// is `null` or deserialized via [`AuthenticatorAttachment::deserialize`]; 1345 /// [`clientExtensionResults`](https://www.w3.org/TR/webauthn-3/#dom-registrationresponsejson-clientextensionresults) 1346 /// is deserialized via [`ClientExtensionsOutputs::deserialize`]; all `required` fields in the 1347 /// `RegistrationResponseJSON` Web IDL `dictionary` exist (and are not `null`); 1348 /// [`type`](https://www.w3.org/TR/webauthn-3/#dom-registrationresponsejson-type) is `"public-key"`; 1349 /// and the decoded `id`, decoded `rawId`, and 1350 /// [`credentialId`](https://www.w3.org/TR/webauthn-3/#authdata-attestedcredentialdata-credentialid) within 1351 /// [`attestedCredentialData`](https://www.w3.org/TR/webauthn-3/#authdata-attestedcredentialdata) within 1352 /// [`authData`](https://www.w3.org/TR/webauthn-3/#attestation-object) within the decoded 1353 /// [`attestationObject`](https://www.w3.org/TR/webauthn-3/#dom-authenticatorattestationresponsejson-attestationobject) 1354 /// are all the same. 1355 #[expect(clippy::unreachable, reason = "when there is a bug, we want to crash")] 1356 #[expect(clippy::indexing_slicing, reason = "comment justifies its correctness")] 1357 #[inline] 1358 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 1359 where 1360 D: Deserializer<'de>, 1361 { 1362 PublicKeyCredential::<false, true, AuthAttest, ClientExtensionsOutputs>::deserialize(deserializer).and_then(|cred| { 1363 let id = cred.id.unwrap_or_else(|| unreachable!("there is a bug in PublicKeyCredential::deserialize")); 1364 cred.response.cred_info.map_or_else( 1365 || AttestationObject::try_from(cred.response.attest.attestation_object()).map_err(Error::custom).and_then(|att_obj| { 1366 if id == att_obj.auth_data.attested_credential_data.credential_id { 1367 Ok(()) 1368 } else { 1369 Err(Error::invalid_value(Unexpected::Bytes(id.as_ref()), &format!("id, rawId, and the credential id in the attested credential data to all match: {:?}", att_obj.auth_data.attested_credential_data.credential_id.0).as_str())) 1370 } 1371 }), 1372 // `start` and `last` were calculated based on `cred.response.attest.attestation_object()` 1373 // and represent the starting and ending index of the `CredentialId`; therefore this is correct 1374 // let alone won't `panic`. 1375 |(start, last)| if id.0 == cred.response.attest.attestation_object()[start..last] { 1376 Ok(()) 1377 } else { 1378 Err(Error::invalid_value(Unexpected::Bytes(id.as_ref()), &format!("id, rawId, and the credential id in the attested credential data to all match: {:?}", &cred.response.attest.attestation_object()[start..last]).as_str())) 1379 }, 1380 ).map(|()| Self { response: cred.response.attest, authenticator_attachment: cred.authenticator_attachment, client_extension_results: cred.client_extension_results, }) 1381 }) 1382 } 1383 } 1384 #[cfg(test)] 1385 mod tests { 1386 use super::{ 1387 super::{ 1388 ALG, AuthenticatorAttachment, EC2, EDDSA, ES256, ES384, Ed25519PubKey, KTY, OKP, RSA, 1389 Registration, RsaPubKey, UncompressedP256PubKey, UncompressedP384PubKey, cbor, 1390 }, 1391 CoseAlgorithmIdentifier, 1392 spki::SubjectPublicKeyInfo, 1393 }; 1394 use ed25519_dalek::{VerifyingKey, pkcs8::EncodePublicKey}; 1395 use p256::{ 1396 EncodedPoint as P256Pt, PublicKey as P256PubKey, SecretKey as P256Key, 1397 elliptic_curve::sec1::{FromEncodedPoint as _, ToEncodedPoint as _}, 1398 }; 1399 use p384::{EncodedPoint as P384Pt, PublicKey as P384PubKey, SecretKey as P384Key}; 1400 use rsa::{ 1401 BigUint, RsaPrivateKey, 1402 sha2::{Digest as _, Sha256}, 1403 traits::PublicKeyParts, 1404 }; 1405 use serde::de::{Error as _, Unexpected}; 1406 use serde_json::Error; 1407 #[test] 1408 fn ed25519_spki() { 1409 assert!( 1410 Ed25519PubKey::from_der( 1411 VerifyingKey::from_bytes(&[1; 32]) 1412 .unwrap() 1413 .to_public_key_der() 1414 .unwrap() 1415 .as_bytes() 1416 ) 1417 .map_or(false, |k| k.0 == [1; 32]) 1418 ); 1419 } 1420 #[test] 1421 fn p256_spki() { 1422 let key = P256Key::from_bytes( 1423 &[ 1424 137, 133, 36, 206, 163, 47, 255, 5, 76, 144, 163, 141, 40, 109, 108, 240, 246, 115, 1425 178, 237, 169, 68, 6, 129, 92, 21, 238, 127, 55, 158, 207, 95, 1426 ] 1427 .into(), 1428 ) 1429 .unwrap() 1430 .public_key(); 1431 let enc_key = key.to_encoded_point(false); 1432 assert!( 1433 UncompressedP256PubKey::from_der(key.to_public_key_der().unwrap().as_bytes()).map_or( 1434 false, 1435 |k| k.0 == enc_key.x().unwrap().as_slice() 1436 && k.1 == enc_key.y().unwrap().as_slice() 1437 ) 1438 ); 1439 } 1440 #[test] 1441 fn p384_spki() { 1442 let key = P384Key::from_bytes( 1443 &[ 1444 158, 99, 156, 49, 190, 211, 85, 167, 28, 2, 80, 57, 31, 22, 17, 38, 85, 78, 232, 1445 42, 45, 199, 154, 243, 136, 251, 84, 34, 5, 120, 208, 91, 61, 248, 64, 144, 87, 1, 1446 32, 86, 220, 68, 182, 11, 105, 223, 75, 70, 1447 ] 1448 .into(), 1449 ) 1450 .unwrap() 1451 .public_key(); 1452 let enc_key = key.to_encoded_point(false); 1453 assert!( 1454 UncompressedP384PubKey::from_der(key.to_public_key_der().unwrap().as_bytes()).map_or( 1455 false, 1456 |k| k.0 == enc_key.x().unwrap().as_slice() 1457 && k.1 == enc_key.y().unwrap().as_slice() 1458 ) 1459 ); 1460 } 1461 #[test] 1462 fn rsa_spki() { 1463 let n = [ 1464 111, 183, 124, 133, 38, 167, 70, 148, 44, 50, 30, 60, 121, 14, 38, 37, 96, 114, 107, 1465 195, 248, 64, 79, 36, 237, 140, 43, 27, 94, 74, 102, 152, 135, 102, 184, 150, 186, 206, 1466 185, 19, 165, 209, 48, 98, 98, 9, 3, 205, 208, 82, 250, 105, 132, 201, 73, 62, 60, 165, 1467 100, 128, 153, 9, 41, 118, 66, 95, 236, 214, 73, 135, 197, 68, 184, 10, 27, 116, 204, 1468 145, 50, 174, 58, 42, 183, 181, 119, 232, 126, 252, 217, 96, 162, 190, 103, 122, 64, 1469 87, 145, 45, 32, 207, 17, 239, 223, 3, 35, 14, 112, 119, 124, 141, 123, 208, 239, 105, 1470 81, 217, 151, 162, 190, 17, 88, 182, 176, 158, 81, 200, 42, 166, 133, 48, 23, 236, 55, 1471 117, 248, 233, 151, 203, 122, 155, 231, 46, 177, 20, 20, 151, 64, 222, 239, 226, 7, 21, 1472 254, 81, 202, 64, 232, 161, 235, 22, 51, 246, 207, 213, 0, 229, 138, 46, 222, 205, 157, 1473 108, 139, 253, 230, 80, 50, 2, 122, 212, 163, 100, 180, 114, 12, 113, 52, 56, 99, 188, 1474 42, 198, 212, 23, 182, 222, 56, 221, 200, 79, 96, 239, 221, 135, 10, 17, 106, 183, 56, 1475 104, 68, 94, 198, 196, 35, 200, 83, 204, 26, 185, 204, 212, 31, 183, 19, 111, 233, 13, 1476 72, 93, 53, 65, 111, 59, 242, 122, 160, 244, 162, 126, 38, 235, 156, 47, 88, 39, 132, 1477 153, 79, 0, 133, 78, 7, 218, 165, 241, 1478 ]; 1479 let e = 65537u32; 1480 let d = [ 1481 145, 79, 21, 97, 233, 3, 192, 194, 177, 68, 181, 80, 120, 197, 23, 44, 185, 74, 144, 0, 1482 132, 149, 139, 11, 16, 224, 4, 112, 236, 94, 238, 97, 121, 124, 213, 145, 24, 253, 168, 1483 35, 190, 205, 132, 115, 33, 201, 38, 253, 246, 180, 66, 155, 165, 46, 3, 254, 68, 108, 1484 154, 247, 246, 45, 187, 0, 204, 96, 185, 157, 249, 174, 158, 38, 62, 244, 183, 76, 102, 1485 6, 219, 92, 212, 138, 59, 147, 163, 219, 111, 39, 105, 21, 236, 196, 38, 255, 114, 247, 1486 82, 104, 113, 204, 29, 152, 209, 219, 48, 239, 74, 129, 19, 247, 33, 239, 119, 166, 1487 216, 152, 94, 138, 238, 164, 242, 129, 50, 150, 57, 20, 53, 224, 56, 241, 138, 97, 111, 1488 215, 107, 212, 195, 146, 108, 143, 0, 229, 181, 171, 73, 152, 105, 146, 25, 243, 242, 1489 140, 252, 248, 162, 247, 63, 168, 180, 20, 153, 120, 10, 248, 211, 1, 71, 127, 212, 1490 249, 237, 203, 202, 48, 26, 216, 226, 228, 186, 13, 204, 70, 255, 240, 89, 255, 59, 83, 1491 31, 253, 55, 43, 158, 90, 248, 83, 32, 159, 105, 57, 134, 34, 96, 18, 255, 245, 153, 1492 162, 60, 91, 99, 220, 51, 44, 85, 114, 67, 125, 202, 65, 217, 245, 40, 8, 81, 165, 142, 1493 24, 245, 127, 122, 247, 152, 212, 75, 45, 59, 90, 184, 234, 31, 147, 36, 8, 212, 45, 1494 50, 23, 3, 25, 253, 87, 227, 79, 119, 161, 1495 ]; 1496 let p = BigUint::from_bytes_le( 1497 [ 1498 215, 166, 5, 21, 11, 179, 41, 77, 198, 92, 165, 48, 77, 162, 42, 41, 206, 141, 60, 1499 69, 47, 164, 19, 92, 46, 72, 100, 238, 100, 53, 214, 197, 163, 185, 6, 140, 229, 1500 250, 195, 77, 8, 12, 5, 236, 178, 173, 86, 201, 43, 213, 165, 51, 108, 101, 161, 1501 99, 76, 240, 14, 234, 76, 197, 137, 53, 198, 168, 135, 205, 212, 198, 120, 29, 16, 1502 82, 98, 233, 236, 177, 12, 171, 141, 100, 107, 146, 33, 176, 125, 202, 172, 79, 1503 147, 179, 30, 62, 247, 206, 169, 19, 168, 114, 26, 73, 108, 178, 105, 84, 89, 191, 1504 168, 253, 228, 214, 54, 16, 212, 199, 111, 72, 3, 41, 247, 227, 165, 244, 32, 188, 1505 24, 247, 1506 ] 1507 .as_slice(), 1508 ); 1509 let p_2 = BigUint::from_bytes_le( 1510 [ 1511 41, 25, 198, 240, 134, 206, 121, 57, 11, 5, 134, 192, 212, 77, 229, 197, 14, 78, 1512 85, 212, 190, 114, 179, 188, 21, 171, 174, 12, 104, 74, 15, 164, 136, 173, 62, 177, 1513 141, 213, 93, 102, 147, 83, 59, 124, 146, 59, 175, 213, 55, 27, 25, 248, 154, 29, 1514 39, 85, 50, 235, 134, 60, 203, 106, 186, 195, 190, 185, 71, 169, 142, 236, 92, 11, 1515 250, 187, 198, 8, 201, 184, 120, 178, 227, 87, 63, 243, 89, 227, 234, 184, 28, 252, 1516 112, 211, 193, 69, 23, 92, 5, 72, 93, 53, 69, 159, 73, 160, 105, 244, 249, 94, 214, 1517 173, 9, 236, 4, 255, 129, 11, 224, 140, 252, 168, 57, 143, 176, 241, 60, 219, 90, 1518 250, 1519 ] 1520 .as_slice(), 1521 ); 1522 let key = RsaPrivateKey::from_components( 1523 BigUint::from_bytes_le(n.as_slice()), 1524 e.into(), 1525 BigUint::from_bytes_le(d.as_slice()), 1526 vec![p, p_2], 1527 ) 1528 .unwrap() 1529 .to_public_key(); 1530 assert!( 1531 RsaPubKey::from_der(key.to_public_key_der().unwrap().as_bytes()) 1532 .map_or(false, |k| k.0 == key.n().to_bytes_be() 1533 && BigUint::from(k.1) == *key.e()) 1534 ); 1535 } 1536 #[test] 1537 fn eddsa_registration_deserialize_data_mismatch() { 1538 let c_data_json = serde_json::json!({}).to_string(); 1539 let att_obj = [ 1540 cbor::MAP_3, 1541 cbor::TEXT_3, 1542 b'f', 1543 b'm', 1544 b't', 1545 cbor::TEXT_4, 1546 b'n', 1547 b'o', 1548 b'n', 1549 b'e', 1550 cbor::TEXT_7, 1551 b'a', 1552 b't', 1553 b't', 1554 b'S', 1555 b't', 1556 b'm', 1557 b't', 1558 cbor::MAP_0, 1559 cbor::TEXT_8, 1560 b'a', 1561 b'u', 1562 b't', 1563 b'h', 1564 b'D', 1565 b'a', 1566 b't', 1567 b'a', 1568 cbor::BYTES_INFO_24, 1569 115, 1570 // `rpIdHash`. 1571 0, 1572 0, 1573 0, 1574 0, 1575 0, 1576 0, 1577 0, 1578 0, 1579 0, 1580 0, 1581 0, 1582 0, 1583 0, 1584 0, 1585 0, 1586 0, 1587 0, 1588 0, 1589 0, 1590 0, 1591 0, 1592 0, 1593 0, 1594 0, 1595 0, 1596 0, 1597 0, 1598 0, 1599 0, 1600 0, 1601 0, 1602 0, 1603 // `flags`. 1604 0b0100_0101, 1605 // `signCount`. 1606 0, 1607 0, 1608 0, 1609 0, 1610 // `aaguid`. 1611 0, 1612 0, 1613 0, 1614 0, 1615 0, 1616 0, 1617 0, 1618 0, 1619 0, 1620 0, 1621 0, 1622 0, 1623 0, 1624 0, 1625 0, 1626 0, 1627 // `credentialIdLength`. 1628 0, 1629 16, 1630 // `credentialId`. 1631 0, 1632 0, 1633 0, 1634 0, 1635 0, 1636 0, 1637 0, 1638 0, 1639 0, 1640 0, 1641 0, 1642 0, 1643 0, 1644 0, 1645 0, 1646 0, 1647 // Ed25519 COSE key. 1648 cbor::MAP_4, 1649 KTY, 1650 OKP, 1651 ALG, 1652 EDDSA, 1653 // `crv`. 1654 cbor::NEG_ONE, 1655 // `Ed25519`. 1656 cbor::SIX, 1657 // `x`. 1658 cbor::NEG_TWO, 1659 cbor::BYTES_INFO_24, 1660 32, 1661 // Compressed y-coordinate. 1662 1, 1663 1, 1664 1, 1665 1, 1666 1, 1667 1, 1668 1, 1669 1, 1670 1, 1671 1, 1672 1, 1673 1, 1674 1, 1675 1, 1676 1, 1677 1, 1678 1, 1679 1, 1680 1, 1681 1, 1682 1, 1683 1, 1684 1, 1685 1, 1686 1, 1687 1, 1688 1, 1689 1, 1690 1, 1691 1, 1692 1, 1693 1, 1694 ]; 1695 let pub_key = VerifyingKey::from_bytes(&[1; 32]) 1696 .unwrap() 1697 .to_public_key_der() 1698 .unwrap(); 1699 let b64_cdata = base64url_nopad::encode(c_data_json.as_bytes()); 1700 let b64_adata = base64url_nopad::encode(&att_obj[att_obj.len() - 113..]); 1701 let b64_key = base64url_nopad::encode(pub_key.as_bytes()); 1702 let b64_aobj = base64url_nopad::encode(att_obj.as_slice()); 1703 // Base case is valid. 1704 assert!( 1705 serde_json::from_str::<Registration>( 1706 serde_json::json!({ 1707 "id": "AAAAAAAAAAAAAAAAAAAAAA", 1708 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 1709 "response": { 1710 "clientDataJSON": b64_cdata, 1711 "authenticatorData": b64_adata, 1712 "transports": ["ble", "usb", "hybrid", "internal", "nfc", "smart-card"], 1713 "publicKey": b64_key, 1714 "publicKeyAlgorithm": -8, 1715 "attestationObject": b64_aobj, 1716 }, 1717 "authenticatorAttachment": "cross-platform", 1718 "clientExtensionResults": {}, 1719 "type": "public-key" 1720 }) 1721 .to_string() 1722 .as_str() 1723 ) 1724 .map_or(false, |reg| reg.response.client_data_json 1725 == c_data_json.as_bytes() 1726 && reg.response.attestation_object_and_c_data_hash[..att_obj.len()] 1727 == att_obj 1728 && reg.response.attestation_object_and_c_data_hash[att_obj.len()..] 1729 == *Sha256::digest(c_data_json.as_bytes()).as_slice() 1730 && reg.response.transports.count() == 6 1731 && matches!( 1732 reg.authenticator_attachment, 1733 AuthenticatorAttachment::CrossPlatform 1734 ) 1735 && reg.client_extension_results.cred_props.is_none() 1736 && reg.client_extension_results.prf.is_none()) 1737 ); 1738 // `id` and `rawId` mismatch. 1739 let mut err = Error::invalid_value( 1740 Unexpected::Bytes( 1741 base64url_nopad::decode("ABABABABABABABABABABAA".as_bytes()) 1742 .unwrap() 1743 .as_slice(), 1744 ), 1745 &format!("id and rawId to match: CredentialId({:?})", [0; 16]).as_str(), 1746 ) 1747 .to_string() 1748 .into_bytes(); 1749 assert_eq!( 1750 serde_json::from_str::<Registration>( 1751 serde_json::json!({ 1752 "id": "AAAAAAAAAAAAAAAAAAAAAA", 1753 "rawId": "ABABABABABABABABABABAA", 1754 "response": { 1755 "clientDataJSON": b64_cdata, 1756 "authenticatorData": b64_adata, 1757 "transports": [], 1758 "publicKey": b64_key, 1759 "publicKeyAlgorithm": -8, 1760 "attestationObject": b64_aobj, 1761 }, 1762 "clientExtensionResults": {}, 1763 "type": "public-key" 1764 }) 1765 .to_string() 1766 .as_str() 1767 ) 1768 .unwrap_err() 1769 .to_string() 1770 .into_bytes()[..err.len()], 1771 err 1772 ); 1773 // missing `id`. 1774 err = Error::missing_field("id").to_string().into_bytes(); 1775 assert_eq!( 1776 serde_json::from_str::<Registration>( 1777 serde_json::json!({ 1778 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 1779 "response": { 1780 "clientDataJSON": b64_cdata, 1781 "authenticatorData": b64_adata, 1782 "transports": [], 1783 "publicKey": b64_key, 1784 "publicKeyAlgorithm": -8, 1785 "attestationObject": b64_aobj, 1786 }, 1787 "clientExtensionResults": {}, 1788 "type": "public-key" 1789 }) 1790 .to_string() 1791 .as_str() 1792 ) 1793 .unwrap_err() 1794 .to_string() 1795 .into_bytes()[..err.len()], 1796 err 1797 ); 1798 // `null` `id`. 1799 err = Error::invalid_type(Unexpected::Other("null"), &"CredentialId") 1800 .to_string() 1801 .into_bytes(); 1802 assert_eq!( 1803 serde_json::from_str::<Registration>( 1804 serde_json::json!({ 1805 "id": null, 1806 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 1807 "response": { 1808 "clientDataJSON": b64_cdata, 1809 "authenticatorData": b64_adata, 1810 "transports": [], 1811 "publicKey": b64_key, 1812 "publicKeyAlgorithm": -8, 1813 "attestationObject": b64_aobj, 1814 }, 1815 "clientExtensionResults": {}, 1816 "type": "public-key" 1817 }) 1818 .to_string() 1819 .as_str() 1820 ) 1821 .unwrap_err() 1822 .to_string() 1823 .into_bytes()[..err.len()], 1824 err 1825 ); 1826 // missing `rawId`. 1827 err = Error::missing_field("rawId").to_string().into_bytes(); 1828 assert_eq!( 1829 serde_json::from_str::<Registration>( 1830 serde_json::json!({ 1831 "id": "AAAAAAAAAAAAAAAAAAAAAA", 1832 "response": { 1833 "clientDataJSON": b64_cdata, 1834 "authenticatorData": b64_adata, 1835 "transports": [], 1836 "publicKey": b64_key, 1837 "publicKeyAlgorithm": -8, 1838 "attestationObject": b64_aobj, 1839 }, 1840 "clientExtensionResults": {}, 1841 "type": "public-key" 1842 }) 1843 .to_string() 1844 .as_str() 1845 ) 1846 .unwrap_err() 1847 .to_string() 1848 .into_bytes()[..err.len()], 1849 err 1850 ); 1851 // `null` `rawId`. 1852 err = Error::invalid_type(Unexpected::Other("null"), &"CredentialId") 1853 .to_string() 1854 .into_bytes(); 1855 assert_eq!( 1856 serde_json::from_str::<Registration>( 1857 serde_json::json!({ 1858 "id": "AAAAAAAAAAAAAAAAAAAAAA", 1859 "rawId": null, 1860 "response": { 1861 "clientDataJSON": b64_cdata, 1862 "authenticatorData": b64_adata, 1863 "transports": [], 1864 "publicKey": b64_key, 1865 "publicKeyAlgorithm": -8, 1866 "attestationObject": b64_aobj, 1867 }, 1868 "clientExtensionResults": {}, 1869 "type": "public-key" 1870 }) 1871 .to_string() 1872 .as_str() 1873 ) 1874 .unwrap_err() 1875 .to_string() 1876 .into_bytes()[..err.len()], 1877 err 1878 ); 1879 // `id` and the credential id in authenticator data mismatch. 1880 err = Error::invalid_value( 1881 Unexpected::Bytes( 1882 base64url_nopad 1883 ::decode("ABABABABABABABABABABAA".as_bytes()) 1884 .unwrap() 1885 .as_slice(), 1886 ), 1887 &format!("id, rawId, and the credential id in the attested credential data to all match: {:?}", [0; 16]).as_str(), 1888 ) 1889 .to_string().into_bytes(); 1890 assert_eq!( 1891 serde_json::from_str::<Registration>( 1892 serde_json::json!({ 1893 "id": "ABABABABABABABABABABAA", 1894 "rawId": "ABABABABABABABABABABAA", 1895 "response": { 1896 "clientDataJSON": b64_cdata, 1897 "authenticatorData": b64_adata, 1898 "transports": [], 1899 "publicKey": b64_key, 1900 "publicKeyAlgorithm": -8, 1901 "attestationObject": b64_aobj, 1902 }, 1903 "clientExtensionResults": {}, 1904 "type": "public-key" 1905 }) 1906 .to_string() 1907 .as_str() 1908 ) 1909 .unwrap_err() 1910 .to_string() 1911 .into_bytes()[..err.len()], 1912 err 1913 ); 1914 // `authenticatorData` mismatches `authData` in attestation object. 1915 let mut bad_auth = [0; 113]; 1916 bad_auth.copy_from_slice(&att_obj[att_obj.len() - 113..]); 1917 bad_auth[113 - 32..].copy_from_slice([0; 32].as_slice()); 1918 err = Error::invalid_value( 1919 Unexpected::Bytes(bad_auth.as_slice()), 1920 &format!("authenticator data to match the authenticator data portion of attestation object: {:?}", &att_obj[att_obj.len() - bad_auth.len()..]).as_str(), 1921 ) 1922 .to_string().into_bytes(); 1923 assert_eq!( 1924 serde_json::from_str::<Registration>( 1925 serde_json::json!({ 1926 "id": "AAAAAAAAAAAAAAAAAAAAAA", 1927 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 1928 "response": { 1929 "clientDataJSON": b64_cdata, 1930 "authenticatorData": base64url_nopad::encode(bad_auth.as_slice()), 1931 "transports": [], 1932 "publicKey": b64_key, 1933 "publicKeyAlgorithm": -8, 1934 "attestationObject": b64_aobj, 1935 }, 1936 "clientExtensionResults": {}, 1937 "type": "public-key" 1938 }) 1939 .to_string() 1940 .as_str() 1941 ) 1942 .unwrap_err() 1943 .to_string() 1944 .into_bytes()[..err.len()], 1945 err 1946 ); 1947 // Missing `authenticatorData`. 1948 err = Error::missing_field("authenticatorData") 1949 .to_string() 1950 .into_bytes(); 1951 assert_eq!( 1952 serde_json::from_str::<Registration>( 1953 serde_json::json!({ 1954 "id": "AAAAAAAAAAAAAAAAAAAAAA", 1955 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 1956 "response": { 1957 "clientDataJSON": b64_cdata, 1958 "transports": [], 1959 "publicKey": b64_key, 1960 "publicKeyAlgorithm": -8, 1961 "attestationObject": b64_aobj, 1962 }, 1963 "clientExtensionResults": {}, 1964 "type": "public-key" 1965 }) 1966 .to_string() 1967 .as_str() 1968 ) 1969 .unwrap_err() 1970 .to_string() 1971 .into_bytes()[..err.len()], 1972 err 1973 ); 1974 // `null` `authenticatorData`. 1975 err = Error::invalid_type(Unexpected::Other("null"), &"authenticatorData") 1976 .to_string() 1977 .into_bytes(); 1978 assert_eq!( 1979 serde_json::from_str::<Registration>( 1980 serde_json::json!({ 1981 "id": "AAAAAAAAAAAAAAAAAAAAAA", 1982 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 1983 "response": { 1984 "clientDataJSON": b64_cdata, 1985 "transports": [], 1986 "authenticatorData": null, 1987 "publicKey": b64_key, 1988 "publicKeyAlgorithm": -8, 1989 "attestationObject": b64_aobj, 1990 }, 1991 "clientExtensionResults": {}, 1992 "type": "public-key" 1993 }) 1994 .to_string() 1995 .as_str() 1996 ) 1997 .unwrap_err() 1998 .to_string() 1999 .into_bytes()[..err.len()], 2000 err 2001 ); 2002 // `publicKeyAlgorithm` mismatch. 2003 err = Error::invalid_value( 2004 Unexpected::Other(&format!("{:?}", CoseAlgorithmIdentifier::Es256).as_str()), 2005 &format!("public key algorithm to match the algorithm associated with the public key within the attestation object: {:?}", CoseAlgorithmIdentifier::Eddsa).as_str() 2006 ) 2007 .to_string().into_bytes(); 2008 assert_eq!( 2009 serde_json::from_str::<Registration>( 2010 serde_json::json!({ 2011 "id": "AAAAAAAAAAAAAAAAAAAAAA", 2012 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 2013 "response": { 2014 "clientDataJSON": b64_cdata, 2015 "authenticatorData": b64_adata, 2016 "transports": [], 2017 "publicKey": b64_key, 2018 "publicKeyAlgorithm": -7, 2019 "attestationObject": b64_aobj, 2020 }, 2021 "clientExtensionResults": {}, 2022 "type": "public-key" 2023 }) 2024 .to_string() 2025 .as_str() 2026 ) 2027 .unwrap_err() 2028 .to_string() 2029 .into_bytes()[..err.len()], 2030 err 2031 ); 2032 // Missing `publicKeyAlgorithm`. 2033 err = Error::missing_field("publicKeyAlgorithm") 2034 .to_string() 2035 .into_bytes(); 2036 assert_eq!( 2037 serde_json::from_str::<Registration>( 2038 serde_json::json!({ 2039 "id": "AAAAAAAAAAAAAAAAAAAAAA", 2040 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 2041 "response": { 2042 "clientDataJSON": b64_cdata, 2043 "authenticatorData": b64_adata, 2044 "transports": [], 2045 "publicKey": b64_key, 2046 "attestationObject": b64_aobj, 2047 }, 2048 "clientExtensionResults": {}, 2049 "type": "public-key" 2050 }) 2051 .to_string() 2052 .as_str() 2053 ) 2054 .unwrap_err() 2055 .to_string() 2056 .into_bytes()[..err.len()], 2057 err 2058 ); 2059 // `null` `publicKeyAlgorithm`. 2060 err = Error::invalid_type(Unexpected::Other("null"), &"publicKeyAlgorithm") 2061 .to_string() 2062 .into_bytes(); 2063 assert_eq!( 2064 serde_json::from_str::<Registration>( 2065 serde_json::json!({ 2066 "id": "AAAAAAAAAAAAAAAAAAAAAA", 2067 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 2068 "response": { 2069 "clientDataJSON": b64_cdata, 2070 "authenticatorData": b64_adata, 2071 "transports": [], 2072 "publicKey": b64_key, 2073 "publicKeyAlgorithm": null, 2074 "attestationObject": b64_aobj, 2075 }, 2076 "clientExtensionResults": {}, 2077 "type": "public-key" 2078 }) 2079 .to_string() 2080 .as_str() 2081 ) 2082 .unwrap_err() 2083 .to_string() 2084 .into_bytes()[..err.len()], 2085 err 2086 ); 2087 // `publicKey` mismatch. 2088 err = Error::invalid_value( 2089 Unexpected::Bytes([0; 32].as_slice()), 2090 &format!( 2091 "DER-encoded public key to match the public key within the attestation object: Ed25519(Ed25519PubKey({:?}))", 2092 &att_obj[att_obj.len() - 32..], 2093 ) 2094 .as_str(), 2095 ) 2096 .to_string().into_bytes(); 2097 assert_eq!(serde_json::from_str::<Registration>( 2098 serde_json::json!({ 2099 "id": "AAAAAAAAAAAAAAAAAAAAAA", 2100 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 2101 "response": { 2102 "clientDataJSON": b64_cdata, 2103 "authenticatorData": b64_adata, 2104 "transports": [], 2105 "publicKey": base64url_nopad::encode(VerifyingKey::from_bytes(&[0; 32]).unwrap().to_public_key_der().unwrap().as_bytes()), 2106 "publicKeyAlgorithm": -8, 2107 "attestationObject": b64_aobj, 2108 }, 2109 "clientExtensionResults": {}, 2110 "type": "public-key" 2111 }) 2112 .to_string() 2113 .as_str() 2114 ) 2115 .unwrap_err().to_string().into_bytes()[..err.len()], 2116 err 2117 ); 2118 // Missing `publicKey` when using EdDSA, ES256, or RS256. 2119 err = Error::missing_field("publicKey").to_string().into_bytes(); 2120 assert_eq!( 2121 serde_json::from_str::<Registration>( 2122 serde_json::json!({ 2123 "id": "AAAAAAAAAAAAAAAAAAAAAA", 2124 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 2125 "response": { 2126 "clientDataJSON": b64_cdata, 2127 "authenticatorData": b64_adata, 2128 "transports": [], 2129 "publicKeyAlgorithm": -8, 2130 "attestationObject": b64_aobj, 2131 }, 2132 "clientExtensionResults": {}, 2133 "type": "public-key" 2134 }) 2135 .to_string() 2136 .as_str() 2137 ) 2138 .unwrap_err() 2139 .to_string() 2140 .into_bytes()[..err.len()], 2141 err 2142 ); 2143 // `null` `publicKey` when using EdDSA, ES256, or RS256. 2144 err = Error::invalid_type(Unexpected::Other("null"), &"publicKey") 2145 .to_string() 2146 .into_bytes(); 2147 assert_eq!( 2148 serde_json::from_str::<Registration>( 2149 serde_json::json!({ 2150 "id": "AAAAAAAAAAAAAAAAAAAAAA", 2151 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 2152 "response": { 2153 "clientDataJSON": b64_cdata, 2154 "authenticatorData": b64_adata, 2155 "transports": [], 2156 "publicKey": null, 2157 "publicKeyAlgorithm": -8, 2158 "attestationObject": b64_aobj, 2159 }, 2160 "clientExtensionResults": {}, 2161 "type": "public-key" 2162 }) 2163 .to_string() 2164 .as_str() 2165 ) 2166 .unwrap_err() 2167 .to_string() 2168 .into_bytes()[..err.len()], 2169 err 2170 ); 2171 // Missing `transports`. 2172 err = Error::missing_field("transports").to_string().into_bytes(); 2173 assert_eq!( 2174 serde_json::from_str::<Registration>( 2175 serde_json::json!({ 2176 "id": "AAAAAAAAAAAAAAAAAAAAAA", 2177 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 2178 "response": { 2179 "clientDataJSON": b64_cdata, 2180 "authenticatorData": b64_adata, 2181 "publicKey": b64_key, 2182 "publicKeyAlgorithm": -8, 2183 "attestationObject": b64_aobj, 2184 }, 2185 "clientExtensionResults": {}, 2186 "type": "public-key" 2187 }) 2188 .to_string() 2189 .as_str() 2190 ) 2191 .unwrap_err() 2192 .to_string() 2193 .into_bytes()[..err.len()], 2194 err 2195 ); 2196 // Duplicate `transports` are allowed. 2197 assert!( 2198 serde_json::from_str::<Registration>( 2199 serde_json::json!({ 2200 "id": "AAAAAAAAAAAAAAAAAAAAAA", 2201 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 2202 "response": { 2203 "clientDataJSON": b64_cdata, 2204 "authenticatorData": b64_adata, 2205 "transports": ["usb", "usb"], 2206 "publicKey": b64_key, 2207 "publicKeyAlgorithm": -8, 2208 "attestationObject": b64_aobj, 2209 }, 2210 "clientExtensionResults": {}, 2211 "type": "public-key" 2212 }) 2213 .to_string() 2214 .as_str() 2215 ) 2216 .map_or(false, |reg| reg.response.transports.count() == 1) 2217 ); 2218 // `null` `transports`. 2219 err = Error::invalid_type(Unexpected::Other("null"), &"transports") 2220 .to_string() 2221 .into_bytes(); 2222 assert_eq!( 2223 serde_json::from_str::<Registration>( 2224 serde_json::json!({ 2225 "id": "AAAAAAAAAAAAAAAAAAAAAA", 2226 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 2227 "response": { 2228 "clientDataJSON": b64_cdata, 2229 "authenticatorData": b64_adata, 2230 "transports": null, 2231 "publicKey": b64_key, 2232 "publicKeyAlgorithm": -8, 2233 "attestationObject": b64_aobj, 2234 }, 2235 "clientExtensionResults": {}, 2236 "type": "public-key" 2237 }) 2238 .to_string() 2239 .as_str() 2240 ) 2241 .unwrap_err() 2242 .to_string() 2243 .into_bytes()[..err.len()], 2244 err 2245 ); 2246 // Unknown `transports`. 2247 err = Error::invalid_value( 2248 Unexpected::Str("Usb"), 2249 &"'ble', 'cable', 'hybrid', 'internal', 'nfc', 'smart-card', or 'usb'", 2250 ) 2251 .to_string() 2252 .into_bytes(); 2253 assert_eq!( 2254 serde_json::from_str::<Registration>( 2255 serde_json::json!({ 2256 "id": "AAAAAAAAAAAAAAAAAAAAAA", 2257 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 2258 "response": { 2259 "clientDataJSON": b64_cdata, 2260 "authenticatorData": b64_adata, 2261 "transports": ["Usb"], 2262 "publicKey": b64_key, 2263 "publicKeyAlgorithm": -8, 2264 "attestationObject": b64_aobj, 2265 }, 2266 "clientExtensionResults": {}, 2267 "type": "public-key" 2268 }) 2269 .to_string() 2270 .as_str() 2271 ) 2272 .unwrap_err() 2273 .to_string() 2274 .into_bytes()[..err.len()], 2275 err 2276 ); 2277 // `null` `authenticatorAttachment`. 2278 assert!( 2279 serde_json::from_str::<Registration>( 2280 serde_json::json!({ 2281 "id": "AAAAAAAAAAAAAAAAAAAAAA", 2282 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 2283 "response": { 2284 "clientDataJSON": b64_cdata, 2285 "authenticatorData": b64_adata, 2286 "transports": [], 2287 "publicKey": b64_key, 2288 "publicKeyAlgorithm": -8, 2289 "attestationObject": b64_aobj, 2290 }, 2291 "authenticatorAttachment": null, 2292 "clientExtensionResults": {}, 2293 "type": "public-key" 2294 }) 2295 .to_string() 2296 .as_str() 2297 ) 2298 .map_or(false, |reg| matches!( 2299 reg.authenticator_attachment, 2300 AuthenticatorAttachment::None 2301 )) 2302 ); 2303 // Unknown `authenticatorAttachment`. 2304 err = Error::invalid_value( 2305 Unexpected::Str("Platform"), 2306 &"'platform' or 'cross-platform'", 2307 ) 2308 .to_string() 2309 .into_bytes(); 2310 assert_eq!( 2311 serde_json::from_str::<Registration>( 2312 serde_json::json!({ 2313 "id": "AAAAAAAAAAAAAAAAAAAAAA", 2314 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 2315 "response": { 2316 "clientDataJSON": b64_cdata, 2317 "authenticatorData": b64_adata, 2318 "transports": [], 2319 "publicKey": b64_key, 2320 "publicKeyAlgorithm": -8, 2321 "attestationObject": b64_aobj, 2322 }, 2323 "authenticatorAttachment": "Platform", 2324 "clientExtensionResults": {}, 2325 "type": "public-key" 2326 }) 2327 .to_string() 2328 .as_str() 2329 ) 2330 .unwrap_err() 2331 .to_string() 2332 .into_bytes()[..err.len()], 2333 err 2334 ); 2335 // Missing `clientDataJSON`. 2336 err = Error::missing_field("clientDataJSON") 2337 .to_string() 2338 .into_bytes(); 2339 assert_eq!( 2340 serde_json::from_str::<Registration>( 2341 serde_json::json!({ 2342 "id": "AAAAAAAAAAAAAAAAAAAAAA", 2343 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 2344 "response": { 2345 "authenticatorData": b64_adata, 2346 "transports": [], 2347 "publicKey": b64_key, 2348 "publicKeyAlgorithm": -8, 2349 "attestationObject": b64_aobj, 2350 }, 2351 "clientExtensionResults": {}, 2352 "type": "public-key" 2353 }) 2354 .to_string() 2355 .as_str() 2356 ) 2357 .unwrap_err() 2358 .to_string() 2359 .into_bytes()[..err.len()], 2360 err 2361 ); 2362 // `null` `clientDataJSON`. 2363 err = Error::invalid_type(Unexpected::Other("null"), &"base64url-encoded data") 2364 .to_string() 2365 .into_bytes(); 2366 assert_eq!( 2367 serde_json::from_str::<Registration>( 2368 serde_json::json!({ 2369 "id": "AAAAAAAAAAAAAAAAAAAAAA", 2370 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 2371 "response": { 2372 "clientDataJSON": null, 2373 "authenticatorData": b64_adata, 2374 "transports": [], 2375 "publicKey": b64_key, 2376 "publicKeyAlgorithm": -8, 2377 "attestationObject": b64_aobj, 2378 }, 2379 "clientExtensionResults": {}, 2380 "type": "public-key" 2381 }) 2382 .to_string() 2383 .as_str() 2384 ) 2385 .unwrap_err() 2386 .to_string() 2387 .into_bytes()[..err.len()], 2388 err 2389 ); 2390 // Missing `attestationObject`. 2391 err = Error::missing_field("attestationObject") 2392 .to_string() 2393 .into_bytes(); 2394 assert_eq!( 2395 serde_json::from_str::<Registration>( 2396 serde_json::json!({ 2397 "id": "AAAAAAAAAAAAAAAAAAAAAA", 2398 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 2399 "response": { 2400 "clientDataJSON": b64_cdata, 2401 "authenticatorData": b64_adata, 2402 "transports": [], 2403 "publicKey": b64_key, 2404 "publicKeyAlgorithm": -8, 2405 }, 2406 "clientExtensionResults": {}, 2407 "type": "public-key" 2408 }) 2409 .to_string() 2410 .as_str() 2411 ) 2412 .unwrap_err() 2413 .to_string() 2414 .into_bytes()[..err.len()], 2415 err 2416 ); 2417 // `null` `attestationObject`. 2418 err = Error::invalid_type( 2419 Unexpected::Other("null"), 2420 &"base64url-encoded attestation object", 2421 ) 2422 .to_string() 2423 .into_bytes(); 2424 assert_eq!( 2425 serde_json::from_str::<Registration>( 2426 serde_json::json!({ 2427 "id": "AAAAAAAAAAAAAAAAAAAAAA", 2428 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 2429 "response": { 2430 "clientDataJSON": b64_cdata, 2431 "authenticatorData": b64_adata, 2432 "transports": [], 2433 "publicKey": b64_key, 2434 "publicKeyAlgorithm": -8, 2435 "attestationObject": null, 2436 }, 2437 "clientExtensionResults": {}, 2438 "type": "public-key" 2439 }) 2440 .to_string() 2441 .as_str() 2442 ) 2443 .unwrap_err() 2444 .to_string() 2445 .into_bytes()[..err.len()], 2446 err 2447 ); 2448 // Missing `response`. 2449 err = Error::missing_field("response").to_string().into_bytes(); 2450 assert_eq!( 2451 serde_json::from_str::<Registration>( 2452 serde_json::json!({ 2453 "id": "AAAAAAAAAAAAAAAAAAAAAA", 2454 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 2455 "clientExtensionResults": {}, 2456 "type": "public-key" 2457 }) 2458 .to_string() 2459 .as_str() 2460 ) 2461 .unwrap_err() 2462 .to_string() 2463 .into_bytes()[..err.len()], 2464 err 2465 ); 2466 // `null` `response`. 2467 err = Error::invalid_type(Unexpected::Other("null"), &"AuthenticatorAttestation") 2468 .to_string() 2469 .into_bytes(); 2470 assert_eq!( 2471 serde_json::from_str::<Registration>( 2472 serde_json::json!({ 2473 "id": "AAAAAAAAAAAAAAAAAAAAAA", 2474 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 2475 "response": null, 2476 "clientExtensionResults": {}, 2477 "type": "public-key" 2478 }) 2479 .to_string() 2480 .as_str() 2481 ) 2482 .unwrap_err() 2483 .to_string() 2484 .into_bytes()[..err.len()], 2485 err 2486 ); 2487 // Empty `response`. 2488 err = Error::missing_field("clientDataJSON") 2489 .to_string() 2490 .into_bytes(); 2491 assert_eq!( 2492 serde_json::from_str::<Registration>( 2493 serde_json::json!({ 2494 "id": "AAAAAAAAAAAAAAAAAAAAAA", 2495 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 2496 "response": {}, 2497 "clientExtensionResults": {}, 2498 "type": "public-key" 2499 }) 2500 .to_string() 2501 .as_str() 2502 ) 2503 .unwrap_err() 2504 .to_string() 2505 .into_bytes()[..err.len()], 2506 err 2507 ); 2508 // Missing `clientExtensionResults`. 2509 err = Error::missing_field("clientExtensionResults") 2510 .to_string() 2511 .into_bytes(); 2512 assert_eq!( 2513 serde_json::from_str::<Registration>( 2514 serde_json::json!({ 2515 "id": "AAAAAAAAAAAAAAAAAAAAAA", 2516 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 2517 "response": { 2518 "clientDataJSON": b64_cdata, 2519 "authenticatorData": b64_adata, 2520 "transports": [], 2521 "publicKey": b64_key, 2522 "publicKeyAlgorithm": -8, 2523 "attestationObject": b64_aobj, 2524 }, 2525 "type": "public-key" 2526 }) 2527 .to_string() 2528 .as_str() 2529 ) 2530 .unwrap_err() 2531 .to_string() 2532 .into_bytes()[..err.len()], 2533 err 2534 ); 2535 // `null` `clientExtensionResults`. 2536 err = Error::invalid_type( 2537 Unexpected::Other("null"), 2538 &"clientExtensionResults to be a map of allowed client extensions", 2539 ) 2540 .to_string() 2541 .into_bytes(); 2542 assert_eq!( 2543 serde_json::from_str::<Registration>( 2544 serde_json::json!({ 2545 "id": "AAAAAAAAAAAAAAAAAAAAAA", 2546 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 2547 "response": { 2548 "clientDataJSON": b64_cdata, 2549 "authenticatorData": b64_adata, 2550 "transports": [], 2551 "publicKey": b64_key, 2552 "publicKeyAlgorithm": -8, 2553 "attestationObject": b64_aobj, 2554 }, 2555 "clientExtensionResults": null, 2556 "type": "public-key" 2557 }) 2558 .to_string() 2559 .as_str() 2560 ) 2561 .unwrap_err() 2562 .to_string() 2563 .into_bytes()[..err.len()], 2564 err 2565 ); 2566 // Missing `type`. 2567 err = Error::missing_field("type").to_string().into_bytes(); 2568 assert_eq!( 2569 serde_json::from_str::<Registration>( 2570 serde_json::json!({ 2571 "id": "AAAAAAAAAAAAAAAAAAAAAA", 2572 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 2573 "response": { 2574 "clientDataJSON": b64_cdata, 2575 "authenticatorData": b64_adata, 2576 "transports": [], 2577 "publicKey": b64_key, 2578 "publicKeyAlgorithm": -8, 2579 "attestationObject": b64_aobj, 2580 }, 2581 "clientExtensionResults": {}, 2582 }) 2583 .to_string() 2584 .as_str() 2585 ) 2586 .unwrap_err() 2587 .to_string() 2588 .into_bytes()[..err.len()], 2589 err 2590 ); 2591 // `null` `type`. 2592 err = Error::invalid_type(Unexpected::Other("null"), &"public-key") 2593 .to_string() 2594 .into_bytes(); 2595 assert_eq!( 2596 serde_json::from_str::<Registration>( 2597 serde_json::json!({ 2598 "id": "AAAAAAAAAAAAAAAAAAAAAA", 2599 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 2600 "response": { 2601 "clientDataJSON": b64_cdata, 2602 "authenticatorData": b64_adata, 2603 "transports": [], 2604 "publicKey": b64_key, 2605 "publicKeyAlgorithm": -8, 2606 "attestationObject": b64_aobj, 2607 }, 2608 "clientExtensionResults": {}, 2609 "type": null 2610 }) 2611 .to_string() 2612 .as_str() 2613 ) 2614 .unwrap_err() 2615 .to_string() 2616 .into_bytes()[..err.len()], 2617 err 2618 ); 2619 // Not exactly `public-type` `type`. 2620 err = Error::invalid_value(Unexpected::Str("Public-key"), &"public-key") 2621 .to_string() 2622 .into_bytes(); 2623 assert_eq!( 2624 serde_json::from_str::<Registration>( 2625 serde_json::json!({ 2626 "id": "AAAAAAAAAAAAAAAAAAAAAA", 2627 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 2628 "response": { 2629 "clientDataJSON": b64_cdata, 2630 "authenticatorData": b64_adata, 2631 "transports": [], 2632 "publicKey": b64_key, 2633 "publicKeyAlgorithm": -8, 2634 "attestationObject": b64_aobj, 2635 }, 2636 "clientExtensionResults": {}, 2637 "type": "Public-key" 2638 }) 2639 .to_string() 2640 .as_str() 2641 ) 2642 .unwrap_err() 2643 .to_string() 2644 .into_bytes()[..err.len()], 2645 err 2646 ); 2647 // `null`. 2648 err = Error::invalid_type(Unexpected::Other("null"), &"PublicKeyCredential") 2649 .to_string() 2650 .into_bytes(); 2651 assert_eq!( 2652 serde_json::from_str::<Registration>(serde_json::json!(null).to_string().as_str()) 2653 .unwrap_err() 2654 .to_string() 2655 .into_bytes()[..err.len()], 2656 err 2657 ); 2658 // Empty. 2659 err = Error::missing_field("response").to_string().into_bytes(); 2660 assert_eq!( 2661 serde_json::from_str::<Registration>(serde_json::json!({}).to_string().as_str()) 2662 .unwrap_err() 2663 .to_string() 2664 .into_bytes()[..err.len()], 2665 err 2666 ); 2667 // Unknown field in `response`. 2668 err = Error::unknown_field( 2669 "foo", 2670 [ 2671 "clientDataJSON", 2672 "attestationObject", 2673 "authenticatorData", 2674 "transports", 2675 "publicKey", 2676 "publicKeyAlgorithm", 2677 ] 2678 .as_slice(), 2679 ) 2680 .to_string() 2681 .into_bytes(); 2682 assert_eq!( 2683 serde_json::from_str::<Registration>( 2684 serde_json::json!({ 2685 "id": "AAAAAAAAAAAAAAAAAAAAAA", 2686 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 2687 "response": { 2688 "clientDataJSON": b64_cdata, 2689 "authenticatorData": b64_adata, 2690 "transports": [], 2691 "publicKey": b64_key, 2692 "publicKeyAlgorithm": -8, 2693 "attestationObject": b64_aobj, 2694 "foo": true, 2695 }, 2696 "clientExtensionResults": {}, 2697 "type": "public-key" 2698 }) 2699 .to_string() 2700 .as_str() 2701 ) 2702 .unwrap_err() 2703 .to_string() 2704 .into_bytes()[..err.len()], 2705 err 2706 ); 2707 // Duplicate field in `response`. 2708 err = Error::duplicate_field("transports") 2709 .to_string() 2710 .into_bytes(); 2711 assert_eq!( 2712 serde_json::from_str::<Registration>( 2713 format!( 2714 "{{ 2715 \"id\": \"AAAAAAAAAAAAAAAAAAAAAA\", 2716 \"rawId\": \"AAAAAAAAAAAAAAAAAAAAAA\", 2717 \"response\": {{ 2718 \"clientDataJSON\": \"{b64_cdata}\", 2719 \"authenticatorData\": \"{b64_adata}\", 2720 \"transports\": [], 2721 \"publicKey\": \"{b64_key}\", 2722 \"publicKeyAlgorithm\": -8, 2723 \"attestationObject\": \"{b64_aobj}\", 2724 \"transports\": [] 2725 }}, 2726 \"clientExtensionResults\": {{}}, 2727 \"type\": \"public-key\" 2728 2729 }}" 2730 ) 2731 .as_str() 2732 ) 2733 .unwrap_err() 2734 .to_string() 2735 .into_bytes()[..err.len()], 2736 err 2737 ); 2738 // Unknown field in `PublicKeyCredential`. 2739 err = Error::unknown_field( 2740 "foo", 2741 [ 2742 "id", 2743 "type", 2744 "rawId", 2745 "response", 2746 "authenticatorAttachment", 2747 "clientExtensionResults", 2748 ] 2749 .as_slice(), 2750 ) 2751 .to_string() 2752 .into_bytes(); 2753 assert_eq!( 2754 serde_json::from_str::<Registration>( 2755 serde_json::json!({ 2756 "id": "AAAAAAAAAAAAAAAAAAAAAA", 2757 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 2758 "response": { 2759 "clientDataJSON": b64_cdata, 2760 "authenticatorData": b64_adata, 2761 "transports": [], 2762 "publicKey": b64_key, 2763 "publicKeyAlgorithm": -8, 2764 "attestationObject": b64_aobj 2765 }, 2766 "clientExtensionResults": {}, 2767 "type": "public-key", 2768 "foo": true, 2769 }) 2770 .to_string() 2771 .as_str() 2772 ) 2773 .unwrap_err() 2774 .to_string() 2775 .into_bytes()[..err.len()], 2776 err 2777 ); 2778 // Duplicate field in `PublicKeyCredential`. 2779 err = Error::duplicate_field("id").to_string().into_bytes(); 2780 assert_eq!( 2781 serde_json::from_str::<Registration>( 2782 format!( 2783 "{{ 2784 \"id\": \"AAAAAAAAAAAAAAAAAAAAAA\", 2785 \"id\": \"AAAAAAAAAAAAAAAAAAAAAA\", 2786 \"rawId\": \"AAAAAAAAAAAAAAAAAAAAAA\", 2787 \"response\": {{ 2788 \"clientDataJSON\": \"{b64_cdata}\", 2789 \"authenticatorData\": \"{b64_adata}\", 2790 \"transports\": [], 2791 \"publicKey\": \"{b64_key}\", 2792 \"publicKeyAlgorithm\": -8, 2793 \"attestationObject\": \"{b64_aobj}\" 2794 }}, 2795 \"clientExtensionResults\": {{}}, 2796 \"type\": \"public-key\" 2797 2798 }}" 2799 ) 2800 .as_str() 2801 ) 2802 .unwrap_err() 2803 .to_string() 2804 .into_bytes()[..err.len()], 2805 err 2806 ); 2807 } 2808 #[test] 2809 fn client_extensions() { 2810 let c_data_json = serde_json::json!({}).to_string(); 2811 let att_obj = [ 2812 cbor::MAP_3, 2813 cbor::TEXT_3, 2814 b'f', 2815 b'm', 2816 b't', 2817 cbor::TEXT_4, 2818 b'n', 2819 b'o', 2820 b'n', 2821 b'e', 2822 cbor::TEXT_7, 2823 b'a', 2824 b't', 2825 b't', 2826 b'S', 2827 b't', 2828 b'm', 2829 b't', 2830 cbor::MAP_0, 2831 cbor::TEXT_8, 2832 b'a', 2833 b'u', 2834 b't', 2835 b'h', 2836 b'D', 2837 b'a', 2838 b't', 2839 b'a', 2840 cbor::BYTES_INFO_24, 2841 113, 2842 // `rpIdHash`. 2843 0, 2844 0, 2845 0, 2846 0, 2847 0, 2848 0, 2849 0, 2850 0, 2851 0, 2852 0, 2853 0, 2854 0, 2855 0, 2856 0, 2857 0, 2858 0, 2859 0, 2860 0, 2861 0, 2862 0, 2863 0, 2864 0, 2865 0, 2866 0, 2867 0, 2868 0, 2869 0, 2870 0, 2871 0, 2872 0, 2873 0, 2874 0, 2875 // `flags`. 2876 0b0100_0101, 2877 // `signCount`. 2878 0, 2879 0, 2880 0, 2881 0, 2882 // `aaguid`. 2883 0, 2884 0, 2885 0, 2886 0, 2887 0, 2888 0, 2889 0, 2890 0, 2891 0, 2892 0, 2893 0, 2894 0, 2895 0, 2896 0, 2897 0, 2898 0, 2899 // `credentialIdLength`. 2900 0, 2901 16, 2902 // `credentialId`. 2903 0, 2904 0, 2905 0, 2906 0, 2907 0, 2908 0, 2909 0, 2910 0, 2911 0, 2912 0, 2913 0, 2914 0, 2915 0, 2916 0, 2917 0, 2918 0, 2919 // Ed25519 COSE key. 2920 cbor::MAP_4, 2921 KTY, 2922 OKP, 2923 ALG, 2924 EDDSA, 2925 // `crv`. 2926 cbor::NEG_ONE, 2927 // `Ed25519`. 2928 cbor::SIX, 2929 // `x`. 2930 cbor::NEG_TWO, 2931 cbor::BYTES_INFO_24, 2932 32, 2933 // Compressed y-coordinate. 2934 1, 2935 1, 2936 1, 2937 1, 2938 1, 2939 1, 2940 1, 2941 1, 2942 1, 2943 1, 2944 1, 2945 1, 2946 1, 2947 1, 2948 1, 2949 1, 2950 1, 2951 1, 2952 1, 2953 1, 2954 1, 2955 1, 2956 1, 2957 1, 2958 1, 2959 1, 2960 1, 2961 1, 2962 1, 2963 1, 2964 1, 2965 1, 2966 ]; 2967 let pub_key = VerifyingKey::from_bytes(&[1; 32]) 2968 .unwrap() 2969 .to_public_key_der() 2970 .unwrap(); 2971 let b64_cdata = base64url_nopad::encode(c_data_json.as_bytes()); 2972 let b64_adata = base64url_nopad::encode(&att_obj[att_obj.len() - 113..]); 2973 let b64_key = base64url_nopad::encode(pub_key.as_bytes()); 2974 let b64_aobj = base64url_nopad::encode(att_obj.as_slice()); 2975 // Base case is valid. 2976 assert!( 2977 serde_json::from_str::<Registration>( 2978 serde_json::json!({ 2979 "id": "AAAAAAAAAAAAAAAAAAAAAA", 2980 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 2981 "response": { 2982 "clientDataJSON": b64_cdata, 2983 "authenticatorData": b64_adata, 2984 "transports": [], 2985 "publicKey": b64_key, 2986 "publicKeyAlgorithm": -8, 2987 "attestationObject": b64_aobj, 2988 }, 2989 "clientExtensionResults": {}, 2990 "type": "public-key" 2991 }) 2992 .to_string() 2993 .as_str() 2994 ) 2995 .map_or(false, |reg| reg.response.client_data_json 2996 == c_data_json.as_bytes() 2997 && reg.response.attestation_object_and_c_data_hash[..att_obj.len()] 2998 == att_obj 2999 && reg.response.attestation_object_and_c_data_hash[att_obj.len()..] 3000 == *Sha256::digest(c_data_json.as_bytes()).as_slice() 3001 && reg.response.transports.is_empty() 3002 && matches!(reg.authenticator_attachment, AuthenticatorAttachment::None) 3003 && reg.client_extension_results.cred_props.is_none() 3004 && reg.client_extension_results.prf.is_none()) 3005 ); 3006 // `null` `credProps`. 3007 assert!( 3008 serde_json::from_str::<Registration>( 3009 serde_json::json!({ 3010 "id": "AAAAAAAAAAAAAAAAAAAAAA", 3011 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 3012 "response": { 3013 "clientDataJSON": b64_cdata, 3014 "authenticatorData": b64_adata, 3015 "transports": [], 3016 "publicKey": b64_key, 3017 "publicKeyAlgorithm": -8, 3018 "attestationObject": b64_aobj, 3019 }, 3020 "clientExtensionResults": { 3021 "credProps": null 3022 }, 3023 "type": "public-key" 3024 }) 3025 .to_string() 3026 .as_str() 3027 ) 3028 .map_or(false, |reg| reg 3029 .client_extension_results 3030 .cred_props 3031 .is_none() 3032 && reg.client_extension_results.prf.is_none()) 3033 ); 3034 // `null` `prf`. 3035 assert!( 3036 serde_json::from_str::<Registration>( 3037 serde_json::json!({ 3038 "id": "AAAAAAAAAAAAAAAAAAAAAA", 3039 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 3040 "response": { 3041 "clientDataJSON": b64_cdata, 3042 "authenticatorData": b64_adata, 3043 "transports": [], 3044 "publicKey": b64_key, 3045 "publicKeyAlgorithm": -8, 3046 "attestationObject": b64_aobj, 3047 }, 3048 "clientExtensionResults": { 3049 "prf": null 3050 }, 3051 "type": "public-key" 3052 }) 3053 .to_string() 3054 .as_str() 3055 ) 3056 .map_or(false, |reg| reg 3057 .client_extension_results 3058 .cred_props 3059 .is_none() 3060 && reg.client_extension_results.prf.is_none()) 3061 ); 3062 // Unknown `clientExtensionResults`. 3063 let mut err = Error::unknown_field("CredProps", ["credProps", "prf"].as_slice()) 3064 .to_string() 3065 .into_bytes(); 3066 assert_eq!( 3067 serde_json::from_str::<Registration>( 3068 serde_json::json!({ 3069 "id": "AAAAAAAAAAAAAAAAAAAAAA", 3070 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 3071 "response": { 3072 "clientDataJSON": b64_cdata, 3073 "authenticatorData": b64_adata, 3074 "transports": [], 3075 "publicKey": b64_key, 3076 "publicKeyAlgorithm": -8, 3077 "attestationObject": b64_aobj, 3078 }, 3079 "clientExtensionResults": { 3080 "CredProps": { 3081 "rk": true 3082 } 3083 }, 3084 "type": "public-key" 3085 }) 3086 .to_string() 3087 .as_str() 3088 ) 3089 .unwrap_err() 3090 .to_string() 3091 .into_bytes()[..err.len()], 3092 err 3093 ); 3094 // Duplicate field. 3095 err = Error::duplicate_field("credProps").to_string().into_bytes(); 3096 assert_eq!( 3097 serde_json::from_str::<Registration>( 3098 format!( 3099 "{{ 3100 \"id\": \"AAAAAAAAAAAAAAAAAAAAAA\", 3101 \"rawId\": \"AAAAAAAAAAAAAAAAAAAAAA\", 3102 \"response\": {{ 3103 \"clientDataJSON\": \"{b64_cdata}\", 3104 \"authenticatorData\": \"{b64_adata}\", 3105 \"transports\": [], 3106 \"publicKey\": \"{b64_key}\", 3107 \"publicKeyAlgorithm\": -8, 3108 \"attestationObject\": \"{b64_aobj}\" 3109 }}, 3110 \"clientExtensionResults\": {{ 3111 \"credProps\": null, 3112 \"credProps\": null 3113 }}, 3114 \"type\": \"public-key\" 3115 }}" 3116 ) 3117 .as_str() 3118 ) 3119 .unwrap_err() 3120 .to_string() 3121 .into_bytes()[..err.len()], 3122 err 3123 ); 3124 // `null` `rk`. 3125 assert!( 3126 serde_json::from_str::<Registration>( 3127 serde_json::json!({ 3128 "id": "AAAAAAAAAAAAAAAAAAAAAA", 3129 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 3130 "response": { 3131 "clientDataJSON": b64_cdata, 3132 "authenticatorData": b64_adata, 3133 "transports": [], 3134 "publicKey": b64_key, 3135 "publicKeyAlgorithm": -8, 3136 "attestationObject": b64_aobj, 3137 }, 3138 "clientExtensionResults": { 3139 "credProps": { 3140 "rk": null 3141 } 3142 }, 3143 "type": "public-key" 3144 }) 3145 .to_string() 3146 .as_str() 3147 ) 3148 .map_or(false, |reg| reg 3149 .client_extension_results 3150 .cred_props 3151 .map_or(false, |props| props.rk.is_none()) 3152 && reg.client_extension_results.prf.is_none()) 3153 ); 3154 // Missing `rk`. 3155 assert!( 3156 serde_json::from_str::<Registration>( 3157 serde_json::json!({ 3158 "id": "AAAAAAAAAAAAAAAAAAAAAA", 3159 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 3160 "response": { 3161 "clientDataJSON": b64_cdata, 3162 "authenticatorData": b64_adata, 3163 "transports": [], 3164 "publicKey": b64_key, 3165 "publicKeyAlgorithm": -8, 3166 "attestationObject": b64_aobj, 3167 }, 3168 "clientExtensionResults": { 3169 "credProps": {} 3170 }, 3171 "type": "public-key" 3172 }) 3173 .to_string() 3174 .as_str() 3175 ) 3176 .map_or(false, |reg| reg 3177 .client_extension_results 3178 .cred_props 3179 .map_or(false, |props| props.rk.is_none()) 3180 && reg.client_extension_results.prf.is_none()) 3181 ); 3182 // `true` rk`. 3183 assert!( 3184 serde_json::from_str::<Registration>( 3185 serde_json::json!({ 3186 "id": "AAAAAAAAAAAAAAAAAAAAAA", 3187 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 3188 "response": { 3189 "clientDataJSON": b64_cdata, 3190 "authenticatorData": b64_adata, 3191 "transports": [], 3192 "publicKey": b64_key, 3193 "publicKeyAlgorithm": -8, 3194 "attestationObject": b64_aobj, 3195 }, 3196 "clientExtensionResults": { 3197 "credProps": { 3198 "rk": true 3199 } 3200 }, 3201 "type": "public-key" 3202 }) 3203 .to_string() 3204 .as_str() 3205 ) 3206 .map_or(false, |reg| reg 3207 .client_extension_results 3208 .cred_props 3209 .map_or(false, |props| props.rk.unwrap_or_default()) 3210 && reg.client_extension_results.prf.is_none()) 3211 ); 3212 // `false` rk`. 3213 assert!( 3214 serde_json::from_str::<Registration>( 3215 serde_json::json!({ 3216 "id": "AAAAAAAAAAAAAAAAAAAAAA", 3217 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 3218 "response": { 3219 "clientDataJSON": b64_cdata, 3220 "authenticatorData": b64_adata, 3221 "transports": [], 3222 "publicKey": b64_key, 3223 "publicKeyAlgorithm": -8, 3224 "attestationObject": b64_aobj, 3225 }, 3226 "clientExtensionResults": { 3227 "credProps": { 3228 "rk": false 3229 } 3230 }, 3231 "type": "public-key" 3232 }) 3233 .to_string() 3234 .as_str() 3235 ) 3236 .map_or(false, |reg| reg 3237 .client_extension_results 3238 .cred_props 3239 .map_or(false, |props| props.rk.map_or(false, |rk| !rk)) 3240 && reg.client_extension_results.prf.is_none()) 3241 ); 3242 // Invalid `rk`. 3243 err = Error::invalid_type(Unexpected::Unsigned(3), &"a boolean") 3244 .to_string() 3245 .into_bytes(); 3246 assert_eq!( 3247 serde_json::from_str::<Registration>( 3248 serde_json::json!({ 3249 "id": "AAAAAAAAAAAAAAAAAAAAAA", 3250 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 3251 "response": { 3252 "clientDataJSON": b64_cdata, 3253 "authenticatorData": b64_adata, 3254 "transports": [], 3255 "publicKey": b64_key, 3256 "publicKeyAlgorithm": -8, 3257 "attestationObject": b64_aobj, 3258 }, 3259 "clientExtensionResults": { 3260 "credProps": { 3261 "rk": 3 3262 } 3263 }, 3264 "type": "public-key" 3265 }) 3266 .to_string() 3267 .as_str() 3268 ) 3269 .unwrap_err() 3270 .to_string() 3271 .into_bytes()[..err.len()], 3272 err 3273 ); 3274 // Unknown `credProps` field. 3275 err = Error::unknown_field("Rk", ["rk"].as_slice()) 3276 .to_string() 3277 .into_bytes(); 3278 assert_eq!( 3279 serde_json::from_str::<Registration>( 3280 serde_json::json!({ 3281 "id": "AAAAAAAAAAAAAAAAAAAAAA", 3282 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 3283 "response": { 3284 "clientDataJSON": b64_cdata, 3285 "authenticatorData": b64_adata, 3286 "transports": [], 3287 "publicKey": b64_key, 3288 "publicKeyAlgorithm": -8, 3289 "attestationObject": b64_aobj, 3290 }, 3291 "clientExtensionResults": { 3292 "credProps": { 3293 "Rk": true, 3294 } 3295 }, 3296 "type": "public-key" 3297 }) 3298 .to_string() 3299 .as_str() 3300 ) 3301 .unwrap_err() 3302 .to_string() 3303 .into_bytes()[..err.len()], 3304 err 3305 ); 3306 // Duplicate field in `credProps`. 3307 err = Error::duplicate_field("rk").to_string().into_bytes(); 3308 assert_eq!( 3309 serde_json::from_str::<Registration>( 3310 format!( 3311 "{{ 3312 \"id\": \"AAAAAAAAAAAAAAAAAAAAAA\", 3313 \"rawId\": \"AAAAAAAAAAAAAAAAAAAAAA\", 3314 \"response\": {{ 3315 \"clientDataJSON\": \"{b64_cdata}\", 3316 \"authenticatorData\": \"{b64_adata}\", 3317 \"transports\": [], 3318 \"publicKey\": \"{b64_key}\", 3319 \"publicKeyAlgorithm\": -8, 3320 \"attestationObject\": \"{b64_aobj}\" 3321 }}, 3322 \"clientExtensionResults\": {{ 3323 \"credProps\": {{ 3324 \"rk\": true, 3325 \"rk\": true 3326 }} 3327 }}, 3328 \"type\": \"public-key\" 3329 }}" 3330 ) 3331 .as_str() 3332 ) 3333 .unwrap_err() 3334 .to_string() 3335 .into_bytes()[..err.len()], 3336 err 3337 ); 3338 // `null` `enabled`. 3339 err = Error::invalid_type(Unexpected::Other("null"), &"a boolean") 3340 .to_string() 3341 .into_bytes(); 3342 assert_eq!( 3343 serde_json::from_str::<Registration>( 3344 serde_json::json!({ 3345 "id": "AAAAAAAAAAAAAAAAAAAAAA", 3346 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 3347 "response": { 3348 "clientDataJSON": b64_cdata, 3349 "authenticatorData": b64_adata, 3350 "transports": [], 3351 "publicKey": b64_key, 3352 "publicKeyAlgorithm": -8, 3353 "attestationObject": b64_aobj, 3354 }, 3355 "clientExtensionResults": { 3356 "prf": { 3357 "enabled": null 3358 } 3359 }, 3360 "type": "public-key" 3361 }) 3362 .to_string() 3363 .as_str() 3364 ) 3365 .unwrap_err() 3366 .to_string() 3367 .into_bytes()[..err.len()], 3368 err 3369 ); 3370 // Missing `enabled`. 3371 err = Error::missing_field("enabled").to_string().into_bytes(); 3372 assert_eq!( 3373 serde_json::from_str::<Registration>( 3374 serde_json::json!({ 3375 "id": "AAAAAAAAAAAAAAAAAAAAAA", 3376 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 3377 "response": { 3378 "clientDataJSON": b64_cdata, 3379 "authenticatorData": b64_adata, 3380 "transports": [], 3381 "publicKey": b64_key, 3382 "publicKeyAlgorithm": -8, 3383 "attestationObject": b64_aobj, 3384 }, 3385 "clientExtensionResults": { 3386 "prf": {} 3387 }, 3388 "type": "public-key" 3389 }) 3390 .to_string() 3391 .as_str() 3392 ) 3393 .unwrap_err() 3394 .to_string() 3395 .into_bytes()[..err.len()], 3396 err 3397 ); 3398 // `true` `enabled`. 3399 assert!( 3400 serde_json::from_str::<Registration>( 3401 serde_json::json!({ 3402 "id": "AAAAAAAAAAAAAAAAAAAAAA", 3403 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 3404 "response": { 3405 "clientDataJSON": b64_cdata, 3406 "authenticatorData": b64_adata, 3407 "transports": [], 3408 "publicKey": b64_key, 3409 "publicKeyAlgorithm": -8, 3410 "attestationObject": b64_aobj, 3411 }, 3412 "clientExtensionResults": { 3413 "prf": { 3414 "enabled": true 3415 } 3416 }, 3417 "type": "public-key" 3418 }) 3419 .to_string() 3420 .as_str() 3421 ) 3422 .map_or(false, |reg| reg 3423 .client_extension_results 3424 .cred_props 3425 .is_none() 3426 && reg 3427 .client_extension_results 3428 .prf 3429 .map_or(false, |prf| prf.enabled)) 3430 ); 3431 // `false` `enabled`. 3432 assert!( 3433 serde_json::from_str::<Registration>( 3434 serde_json::json!({ 3435 "id": "AAAAAAAAAAAAAAAAAAAAAA", 3436 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 3437 "response": { 3438 "clientDataJSON": b64_cdata, 3439 "authenticatorData": b64_adata, 3440 "transports": [], 3441 "publicKey": b64_key, 3442 "publicKeyAlgorithm": -8, 3443 "attestationObject": b64_aobj, 3444 }, 3445 "clientExtensionResults": { 3446 "prf": { 3447 "enabled": false, 3448 } 3449 }, 3450 "type": "public-key" 3451 }) 3452 .to_string() 3453 .as_str() 3454 ) 3455 .map_or(false, |reg| reg 3456 .client_extension_results 3457 .cred_props 3458 .is_none() 3459 && reg 3460 .client_extension_results 3461 .prf 3462 .map_or(false, |prf| !prf.enabled)) 3463 ); 3464 // Invalid `enabled`. 3465 err = Error::invalid_type(Unexpected::Unsigned(3), &"a boolean") 3466 .to_string() 3467 .into_bytes(); 3468 assert_eq!( 3469 serde_json::from_str::<Registration>( 3470 serde_json::json!({ 3471 "id": "AAAAAAAAAAAAAAAAAAAAAA", 3472 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 3473 "response": { 3474 "clientDataJSON": b64_cdata, 3475 "authenticatorData": b64_adata, 3476 "transports": [], 3477 "publicKey": b64_key, 3478 "publicKeyAlgorithm": -8, 3479 "attestationObject": b64_aobj, 3480 }, 3481 "clientExtensionResults": { 3482 "prf": { 3483 "enabled": 3 3484 } 3485 }, 3486 "type": "public-key" 3487 }) 3488 .to_string() 3489 .as_str() 3490 ) 3491 .unwrap_err() 3492 .to_string() 3493 .into_bytes()[..err.len()], 3494 err 3495 ); 3496 // `null` `results` with `enabled` `true`. 3497 assert!( 3498 serde_json::from_str::<Registration>( 3499 serde_json::json!({ 3500 "id": "AAAAAAAAAAAAAAAAAAAAAA", 3501 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 3502 "response": { 3503 "clientDataJSON": b64_cdata, 3504 "authenticatorData": b64_adata, 3505 "transports": [], 3506 "publicKey": b64_key, 3507 "publicKeyAlgorithm": -8, 3508 "attestationObject": b64_aobj, 3509 }, 3510 "clientExtensionResults": { 3511 "prf": { 3512 "enabled": true, 3513 "results": null, 3514 } 3515 }, 3516 "type": "public-key" 3517 }) 3518 .to_string() 3519 .as_str() 3520 ) 3521 .map_or(false, |reg| reg 3522 .client_extension_results 3523 .cred_props 3524 .is_none() 3525 && reg 3526 .client_extension_results 3527 .prf 3528 .map_or(false, |prf| prf.enabled)) 3529 ); 3530 // `null` `results` with `enabled` `false`. 3531 err = Error::custom( 3532 "prf must not have 'results', including a null 'results', if 'enabled' is false", 3533 ) 3534 .to_string() 3535 .into_bytes(); 3536 assert_eq!( 3537 serde_json::from_str::<Registration>( 3538 serde_json::json!({ 3539 "id": "AAAAAAAAAAAAAAAAAAAAAA", 3540 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 3541 "response": { 3542 "clientDataJSON": b64_cdata, 3543 "authenticatorData": b64_adata, 3544 "transports": [], 3545 "publicKey": b64_key, 3546 "publicKeyAlgorithm": -8, 3547 "attestationObject": b64_aobj, 3548 }, 3549 "clientExtensionResults": { 3550 "prf": { 3551 "enabled": false, 3552 "results": null 3553 } 3554 }, 3555 "type": "public-key" 3556 }) 3557 .to_string() 3558 .as_str() 3559 ) 3560 .unwrap_err() 3561 .to_string() 3562 .into_bytes()[..err.len()], 3563 err 3564 ); 3565 // Duplicate field in `prf`. 3566 err = Error::duplicate_field("enabled").to_string().into_bytes(); 3567 assert_eq!( 3568 serde_json::from_str::<Registration>( 3569 format!( 3570 "{{ 3571 \"id\": \"AAAAAAAAAAAAAAAAAAAAAA\", 3572 \"rawId\": \"AAAAAAAAAAAAAAAAAAAAAA\", 3573 \"response\": {{ 3574 \"clientDataJSON\": \"{b64_cdata}\", 3575 \"authenticatorData\": \"{b64_adata}\", 3576 \"transports\": [], 3577 \"publicKey\": \"{b64_key}\", 3578 \"publicKeyAlgorithm\": -8, 3579 \"attestationObject\": \"{b64_aobj}\" 3580 }}, 3581 \"clientExtensionResults\": {{ 3582 \"prf\": {{ 3583 \"enabled\": true, 3584 \"enabled\": true 3585 }} 3586 }}, 3587 \"type\": \"public-key\" 3588 }}" 3589 ) 3590 .as_str() 3591 ) 3592 .unwrap_err() 3593 .to_string() 3594 .into_bytes()[..err.len()], 3595 err 3596 ); 3597 // Missing `first`. 3598 err = Error::missing_field("first").to_string().into_bytes(); 3599 assert_eq!( 3600 serde_json::from_str::<Registration>( 3601 serde_json::json!({ 3602 "id": "AAAAAAAAAAAAAAAAAAAAAA", 3603 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 3604 "response": { 3605 "clientDataJSON": b64_cdata, 3606 "authenticatorData": b64_adata, 3607 "transports": [], 3608 "publicKey": b64_key, 3609 "publicKeyAlgorithm": -8, 3610 "attestationObject": b64_aobj, 3611 }, 3612 "clientExtensionResults": { 3613 "prf": { 3614 "enabled": true, 3615 "results": {}, 3616 } 3617 }, 3618 "type": "public-key" 3619 }) 3620 .to_string() 3621 .as_str() 3622 ) 3623 .unwrap_err() 3624 .to_string() 3625 .into_bytes()[..err.len()], 3626 err 3627 ); 3628 // `null` `first`. 3629 assert!( 3630 serde_json::from_str::<Registration>( 3631 serde_json::json!({ 3632 "id": "AAAAAAAAAAAAAAAAAAAAAA", 3633 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 3634 "response": { 3635 "clientDataJSON": b64_cdata, 3636 "authenticatorData": b64_adata, 3637 "transports": [], 3638 "publicKey": b64_key, 3639 "publicKeyAlgorithm": -8, 3640 "attestationObject": b64_aobj, 3641 }, 3642 "clientExtensionResults": { 3643 "prf": { 3644 "enabled": true, 3645 "results": { 3646 "first": null 3647 }, 3648 } 3649 }, 3650 "type": "public-key" 3651 }) 3652 .to_string() 3653 .as_str() 3654 ) 3655 .map_or(false, |reg| reg 3656 .client_extension_results 3657 .cred_props 3658 .is_none() 3659 && reg 3660 .client_extension_results 3661 .prf 3662 .map_or(false, |prf| prf.enabled)) 3663 ); 3664 // `null` `second`. 3665 assert!( 3666 serde_json::from_str::<Registration>( 3667 serde_json::json!({ 3668 "id": "AAAAAAAAAAAAAAAAAAAAAA", 3669 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 3670 "response": { 3671 "clientDataJSON": b64_cdata, 3672 "authenticatorData": b64_adata, 3673 "transports": [], 3674 "publicKey": b64_key, 3675 "publicKeyAlgorithm": -8, 3676 "attestationObject": b64_aobj, 3677 }, 3678 "clientExtensionResults": { 3679 "prf": { 3680 "enabled": true, 3681 "results": { 3682 "first": null, 3683 "second": null 3684 }, 3685 } 3686 }, 3687 "type": "public-key" 3688 }) 3689 .to_string() 3690 .as_str() 3691 ) 3692 .map_or(false, |reg| reg 3693 .client_extension_results 3694 .cred_props 3695 .is_none() 3696 && reg 3697 .client_extension_results 3698 .prf 3699 .map_or(false, |prf| prf.enabled)) 3700 ); 3701 // Non-`null` `first`. 3702 err = Error::invalid_type(Unexpected::Option, &"null") 3703 .to_string() 3704 .into_bytes(); 3705 assert_eq!( 3706 serde_json::from_str::<Registration>( 3707 serde_json::json!({ 3708 "id": "AAAAAAAAAAAAAAAAAAAAAA", 3709 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 3710 "response": { 3711 "clientDataJSON": b64_cdata, 3712 "authenticatorData": b64_adata, 3713 "transports": [], 3714 "publicKey": b64_key, 3715 "publicKeyAlgorithm": -8, 3716 "attestationObject": b64_aobj, 3717 }, 3718 "clientExtensionResults": { 3719 "prf": { 3720 "enabled": true, 3721 "results": { 3722 "first": "" 3723 }, 3724 } 3725 }, 3726 "type": "public-key" 3727 }) 3728 .to_string() 3729 .as_str() 3730 ) 3731 .unwrap_err() 3732 .to_string() 3733 .into_bytes()[..err.len()], 3734 err 3735 ); 3736 // Non-`null` `second`. 3737 err = Error::invalid_type(Unexpected::Option, &"null") 3738 .to_string() 3739 .into_bytes(); 3740 assert_eq!( 3741 serde_json::from_str::<Registration>( 3742 serde_json::json!({ 3743 "id": "AAAAAAAAAAAAAAAAAAAAAA", 3744 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 3745 "response": { 3746 "clientDataJSON": b64_cdata, 3747 "authenticatorData": b64_adata, 3748 "transports": [], 3749 "publicKey": b64_key, 3750 "publicKeyAlgorithm": -8, 3751 "attestationObject": b64_aobj, 3752 }, 3753 "clientExtensionResults": { 3754 "prf": { 3755 "enabled": true, 3756 "results": { 3757 "first": null, 3758 "second": "" 3759 }, 3760 } 3761 }, 3762 "type": "public-key" 3763 }) 3764 .to_string() 3765 .as_str() 3766 ) 3767 .unwrap_err() 3768 .to_string() 3769 .into_bytes()[..err.len()], 3770 err 3771 ); 3772 // Unknown `prf` field. 3773 err = Error::unknown_field("Results", ["enabled", "results"].as_slice()) 3774 .to_string() 3775 .into_bytes(); 3776 assert_eq!( 3777 serde_json::from_str::<Registration>( 3778 serde_json::json!({ 3779 "id": "AAAAAAAAAAAAAAAAAAAAAA", 3780 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 3781 "response": { 3782 "clientDataJSON": b64_cdata, 3783 "authenticatorData": b64_adata, 3784 "transports": [], 3785 "publicKey": b64_key, 3786 "publicKeyAlgorithm": -8, 3787 "attestationObject": b64_aobj, 3788 }, 3789 "clientExtensionResults": { 3790 "prf": { 3791 "enabled": true, 3792 "Results": null 3793 } 3794 }, 3795 "type": "public-key" 3796 }) 3797 .to_string() 3798 .as_str() 3799 ) 3800 .unwrap_err() 3801 .to_string() 3802 .into_bytes()[..err.len()], 3803 err 3804 ); 3805 // Unknown `results` field. 3806 err = Error::unknown_field("Second", ["first", "second"].as_slice()) 3807 .to_string() 3808 .into_bytes(); 3809 assert_eq!( 3810 serde_json::from_str::<Registration>( 3811 serde_json::json!({ 3812 "id": "AAAAAAAAAAAAAAAAAAAAAA", 3813 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 3814 "response": { 3815 "clientDataJSON": b64_cdata, 3816 "authenticatorData": b64_adata, 3817 "transports": [], 3818 "publicKey": b64_key, 3819 "publicKeyAlgorithm": -8, 3820 "attestationObject": b64_aobj, 3821 }, 3822 "clientExtensionResults": { 3823 "prf": { 3824 "enabled": true, 3825 "results": { 3826 "first": null, 3827 "Second": null 3828 } 3829 } 3830 }, 3831 "type": "public-key" 3832 }) 3833 .to_string() 3834 .as_str() 3835 ) 3836 .unwrap_err() 3837 .to_string() 3838 .into_bytes()[..err.len()], 3839 err 3840 ); 3841 // Duplicate field in `results`. 3842 err = Error::duplicate_field("first").to_string().into_bytes(); 3843 assert_eq!( 3844 serde_json::from_str::<Registration>( 3845 format!( 3846 "{{ 3847 \"id\": \"AAAAAAAAAAAAAAAAAAAAAA\", 3848 \"rawId\": \"AAAAAAAAAAAAAAAAAAAAAA\", 3849 \"response\": {{ 3850 \"clientDataJSON\": \"{b64_cdata}\", 3851 \"authenticatorData\": \"{b64_adata}\", 3852 \"transports\": [], 3853 \"publicKey\": \"{b64_key}\", 3854 \"publicKeyAlgorithm\": -8, 3855 \"attestationObject\": \"{b64_aobj}\" 3856 }}, 3857 \"clientExtensionResults\": {{ 3858 \"prf\": {{ 3859 \"enabled\": true, 3860 \"results\": {{ 3861 \"first\": null, 3862 \"first\": null 3863 }} 3864 }} 3865 }}, 3866 \"type\": \"public-key\" 3867 }}" 3868 ) 3869 .as_str() 3870 ) 3871 .unwrap_err() 3872 .to_string() 3873 .into_bytes()[..err.len()], 3874 err 3875 ); 3876 } 3877 #[test] 3878 fn es256_registration_deserialize_data_mismatch() { 3879 let c_data_json = serde_json::json!({}).to_string(); 3880 let mut att_obj = [ 3881 cbor::MAP_3, 3882 cbor::TEXT_3, 3883 b'f', 3884 b'm', 3885 b't', 3886 cbor::TEXT_4, 3887 b'n', 3888 b'o', 3889 b'n', 3890 b'e', 3891 cbor::TEXT_7, 3892 b'a', 3893 b't', 3894 b't', 3895 b'S', 3896 b't', 3897 b'm', 3898 b't', 3899 cbor::MAP_0, 3900 cbor::TEXT_8, 3901 b'a', 3902 b'u', 3903 b't', 3904 b'h', 3905 b'D', 3906 b'a', 3907 b't', 3908 b'a', 3909 cbor::BYTES_INFO_24, 3910 148, 3911 // `rpIdHash`. 3912 0, 3913 0, 3914 0, 3915 0, 3916 0, 3917 0, 3918 0, 3919 0, 3920 0, 3921 0, 3922 0, 3923 0, 3924 0, 3925 0, 3926 0, 3927 0, 3928 0, 3929 0, 3930 0, 3931 0, 3932 0, 3933 0, 3934 0, 3935 0, 3936 0, 3937 0, 3938 0, 3939 0, 3940 0, 3941 0, 3942 0, 3943 0, 3944 // `flags`. 3945 0b0100_0101, 3946 // `signCount`. 3947 0, 3948 0, 3949 0, 3950 0, 3951 // `aaguid`. 3952 0, 3953 0, 3954 0, 3955 0, 3956 0, 3957 0, 3958 0, 3959 0, 3960 0, 3961 0, 3962 0, 3963 0, 3964 0, 3965 0, 3966 0, 3967 0, 3968 // `credentialIdLength`. 3969 0, 3970 16, 3971 // `credentialId`. 3972 0, 3973 0, 3974 0, 3975 0, 3976 0, 3977 0, 3978 0, 3979 0, 3980 0, 3981 0, 3982 0, 3983 0, 3984 0, 3985 0, 3986 0, 3987 0, 3988 // P-256 COSE key. 3989 cbor::MAP_5, 3990 KTY, 3991 EC2, 3992 ALG, 3993 ES256, 3994 // `crv`. 3995 cbor::NEG_ONE, 3996 // `P-256`. 3997 cbor::ONE, 3998 // `x`. 3999 cbor::NEG_TWO, 4000 cbor::BYTES_INFO_24, 4001 32, 4002 // x-coordinate. This will be overwritten later. 4003 0, 4004 0, 4005 0, 4006 0, 4007 0, 4008 0, 4009 0, 4010 0, 4011 0, 4012 0, 4013 0, 4014 0, 4015 0, 4016 0, 4017 0, 4018 0, 4019 0, 4020 0, 4021 0, 4022 0, 4023 0, 4024 0, 4025 0, 4026 0, 4027 0, 4028 0, 4029 0, 4030 0, 4031 0, 4032 0, 4033 0, 4034 0, 4035 // `y`. 4036 cbor::NEG_THREE, 4037 cbor::BYTES_INFO_24, 4038 32, 4039 // y-coordinate. This will be overwritten later. 4040 0, 4041 0, 4042 0, 4043 0, 4044 0, 4045 0, 4046 0, 4047 0, 4048 0, 4049 0, 4050 0, 4051 0, 4052 0, 4053 0, 4054 0, 4055 0, 4056 0, 4057 0, 4058 0, 4059 0, 4060 0, 4061 0, 4062 0, 4063 0, 4064 0, 4065 0, 4066 0, 4067 0, 4068 0, 4069 0, 4070 0, 4071 0, 4072 ]; 4073 let key = P256Key::from_bytes( 4074 &[ 4075 137, 133, 36, 206, 163, 47, 255, 5, 76, 144, 163, 141, 40, 109, 108, 240, 246, 115, 4076 178, 237, 169, 68, 6, 129, 92, 21, 238, 127, 55, 158, 207, 95, 4077 ] 4078 .into(), 4079 ) 4080 .unwrap() 4081 .public_key(); 4082 let enc_key = key.to_encoded_point(false); 4083 let pub_key = key.to_public_key_der().unwrap(); 4084 let att_obj_len = att_obj.len(); 4085 att_obj[att_obj_len - 67..att_obj_len - 35] 4086 .copy_from_slice(enc_key.x().unwrap().as_slice()); 4087 att_obj[att_obj_len - 32..].copy_from_slice(enc_key.y().unwrap().as_slice()); 4088 let b64_cdata = base64url_nopad::encode(c_data_json.as_bytes()); 4089 let b64_adata = base64url_nopad::encode(&att_obj[att_obj.len() - 148..]); 4090 let b64_key = base64url_nopad::encode(pub_key.as_bytes()); 4091 let b64_aobj = base64url_nopad::encode(att_obj.as_slice()); 4092 // Base case is valid. 4093 assert!( 4094 serde_json::from_str::<Registration>( 4095 serde_json::json!({ 4096 "id": "AAAAAAAAAAAAAAAAAAAAAA", 4097 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 4098 "response": { 4099 "clientDataJSON": b64_cdata, 4100 "authenticatorData": b64_adata, 4101 "transports": [], 4102 "publicKey": b64_key, 4103 "publicKeyAlgorithm": -7, 4104 "attestationObject": b64_aobj, 4105 }, 4106 "clientExtensionResults": {}, 4107 "type": "public-key" 4108 }) 4109 .to_string() 4110 .as_str() 4111 ) 4112 .map_or(false, |reg| reg.response.client_data_json 4113 == c_data_json.as_bytes() 4114 && reg.response.attestation_object_and_c_data_hash[..att_obj.len()] 4115 == att_obj 4116 && reg.response.attestation_object_and_c_data_hash[att_obj.len()..] 4117 == *Sha256::digest(c_data_json.as_bytes()).as_slice() 4118 && reg.response.transports.is_empty() 4119 && matches!(reg.authenticator_attachment, AuthenticatorAttachment::None) 4120 && reg.client_extension_results.cred_props.is_none() 4121 && reg.client_extension_results.prf.is_none()) 4122 ); 4123 // `publicKeyAlgorithm` mismatch. 4124 let mut err = Error::invalid_value( 4125 Unexpected::Other(&format!("{:?}", CoseAlgorithmIdentifier::Eddsa).as_str()), 4126 &format!("public key algorithm to match the algorithm associated with the public key within the attestation object: {:?}", CoseAlgorithmIdentifier::Es256).as_str() 4127 ) 4128 .to_string().into_bytes(); 4129 assert_eq!( 4130 serde_json::from_str::<Registration>( 4131 serde_json::json!({ 4132 "id": "AAAAAAAAAAAAAAAAAAAAAA", 4133 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 4134 "response": { 4135 "clientDataJSON": b64_cdata, 4136 "authenticatorData": b64_adata, 4137 "transports": [], 4138 "publicKey": b64_key, 4139 "publicKeyAlgorithm": -8, 4140 "attestationObject": b64_aobj, 4141 }, 4142 "clientExtensionResults": {}, 4143 "type": "public-key" 4144 }) 4145 .to_string() 4146 .as_str() 4147 ) 4148 .unwrap_err() 4149 .to_string() 4150 .into_bytes()[..err.len()], 4151 err 4152 ); 4153 // Missing `publicKeyAlgorithm`. 4154 err = Error::missing_field("publicKeyAlgorithm") 4155 .to_string() 4156 .into_bytes(); 4157 assert_eq!( 4158 serde_json::from_str::<Registration>( 4159 serde_json::json!({ 4160 "id": "AAAAAAAAAAAAAAAAAAAAAA", 4161 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 4162 "response": { 4163 "clientDataJSON": b64_cdata, 4164 "authenticatorData": b64_adata, 4165 "transports": [], 4166 "publicKey": b64_key, 4167 "attestationObject": b64_aobj, 4168 }, 4169 "clientExtensionResults": {}, 4170 "type": "public-key" 4171 }) 4172 .to_string() 4173 .as_str() 4174 ) 4175 .unwrap_err() 4176 .to_string() 4177 .into_bytes()[..err.len()], 4178 err 4179 ); 4180 // `null` `publicKeyAlgorithm`. 4181 err = Error::invalid_type(Unexpected::Other("null"), &"publicKeyAlgorithm") 4182 .to_string() 4183 .into_bytes(); 4184 assert_eq!( 4185 serde_json::from_str::<Registration>( 4186 serde_json::json!({ 4187 "id": "AAAAAAAAAAAAAAAAAAAAAA", 4188 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 4189 "response": { 4190 "clientDataJSON": b64_cdata, 4191 "authenticatorData": b64_adata, 4192 "transports": [], 4193 "publicKey": b64_key, 4194 "publicKeyAlgorithm": null, 4195 "attestationObject": b64_aobj, 4196 }, 4197 "clientExtensionResults": {}, 4198 "type": "public-key" 4199 }) 4200 .to_string() 4201 .as_str() 4202 ) 4203 .unwrap_err() 4204 .to_string() 4205 .into_bytes()[..err.len()], 4206 err 4207 ); 4208 // `publicKey` mismatch. 4209 let bad_pub_key = P256PubKey::from_encoded_point(&P256Pt::from_affine_coordinates( 4210 &[ 4211 66, 71, 188, 41, 125, 2, 226, 44, 148, 62, 63, 190, 172, 64, 33, 214, 6, 37, 148, 4212 23, 240, 235, 203, 84, 112, 219, 232, 197, 54, 182, 17, 235, 4213 ] 4214 .into(), 4215 &[ 4216 22, 172, 123, 13, 170, 242, 217, 248, 193, 209, 206, 163, 92, 4, 162, 168, 113, 63, 4217 2, 117, 16, 223, 239, 196, 109, 179, 10, 130, 43, 213, 205, 92, 4218 ] 4219 .into(), 4220 false, 4221 )) 4222 .unwrap(); 4223 err = Error::invalid_value( 4224 Unexpected::Bytes([0; 32].as_slice()), 4225 &format!( 4226 "DER-encoded public key to match the public key within the attestation object: P256(UncompressedP256PubKey({:?}, {:?}))", 4227 &att_obj[att_obj.len() - 67..att_obj.len() - 35], 4228 &att_obj[att_obj.len() - 32..], 4229 ) 4230 .as_str(), 4231 ) 4232 .to_string().into_bytes(); 4233 assert_eq!(serde_json::from_str::<Registration>( 4234 serde_json::json!({ 4235 "id": "AAAAAAAAAAAAAAAAAAAAAA", 4236 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 4237 "response": { 4238 "clientDataJSON": b64_cdata, 4239 "authenticatorData": b64_adata, 4240 "transports": [], 4241 "publicKey": base64url_nopad::encode(bad_pub_key.to_public_key_der().unwrap().as_bytes()), 4242 "publicKeyAlgorithm": -7, 4243 "attestationObject": b64_aobj, 4244 }, 4245 "clientExtensionResults": {}, 4246 "type": "public-key" 4247 }) 4248 .to_string() 4249 .as_str() 4250 ) 4251 .unwrap_err().to_string().into_bytes()[..err.len()], 4252 err 4253 ); 4254 // Missing `publicKey` when using EdDSA, ES256, or RS256. 4255 err = Error::missing_field("publicKey").to_string().into_bytes(); 4256 assert_eq!( 4257 serde_json::from_str::<Registration>( 4258 serde_json::json!({ 4259 "id": "AAAAAAAAAAAAAAAAAAAAAA", 4260 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 4261 "response": { 4262 "clientDataJSON": b64_cdata, 4263 "authenticatorData": b64_adata, 4264 "transports": [], 4265 "publicKeyAlgorithm": -7, 4266 "attestationObject": b64_aobj, 4267 }, 4268 "clientExtensionResults": {}, 4269 "type": "public-key" 4270 }) 4271 .to_string() 4272 .as_str() 4273 ) 4274 .unwrap_err() 4275 .to_string() 4276 .into_bytes()[..err.len()], 4277 err 4278 ); 4279 // `null` `publicKey` when using EdDSA, ES256, or RS256. 4280 err = Error::invalid_type(Unexpected::Other("null"), &"publicKey") 4281 .to_string() 4282 .into_bytes(); 4283 assert_eq!( 4284 serde_json::from_str::<Registration>( 4285 serde_json::json!({ 4286 "id": "AAAAAAAAAAAAAAAAAAAAAA", 4287 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 4288 "response": { 4289 "clientDataJSON": b64_cdata, 4290 "authenticatorData": b64_adata, 4291 "transports": [], 4292 "publicKey": null, 4293 "publicKeyAlgorithm": -7, 4294 "attestationObject": b64_aobj, 4295 }, 4296 "clientExtensionResults": {}, 4297 "type": "public-key" 4298 }) 4299 .to_string() 4300 .as_str() 4301 ) 4302 .unwrap_err() 4303 .to_string() 4304 .into_bytes()[..err.len()], 4305 err 4306 ); 4307 } 4308 #[test] 4309 fn es384_registration_deserialize_data_mismatch() { 4310 let c_data_json = serde_json::json!({}).to_string(); 4311 let mut att_obj = [ 4312 cbor::MAP_3, 4313 cbor::TEXT_3, 4314 b'f', 4315 b'm', 4316 b't', 4317 cbor::TEXT_4, 4318 b'n', 4319 b'o', 4320 b'n', 4321 b'e', 4322 cbor::TEXT_7, 4323 b'a', 4324 b't', 4325 b't', 4326 b'S', 4327 b't', 4328 b'm', 4329 b't', 4330 cbor::MAP_0, 4331 cbor::TEXT_8, 4332 b'a', 4333 b'u', 4334 b't', 4335 b'h', 4336 b'D', 4337 b'a', 4338 b't', 4339 b'a', 4340 cbor::BYTES_INFO_24, 4341 181, 4342 // `rpIdHash`. 4343 0, 4344 0, 4345 0, 4346 0, 4347 0, 4348 0, 4349 0, 4350 0, 4351 0, 4352 0, 4353 0, 4354 0, 4355 0, 4356 0, 4357 0, 4358 0, 4359 0, 4360 0, 4361 0, 4362 0, 4363 0, 4364 0, 4365 0, 4366 0, 4367 0, 4368 0, 4369 0, 4370 0, 4371 0, 4372 0, 4373 0, 4374 0, 4375 // `flags`. 4376 0b0100_0101, 4377 // `signCount`. 4378 0, 4379 0, 4380 0, 4381 0, 4382 // `aaguid`. 4383 0, 4384 0, 4385 0, 4386 0, 4387 0, 4388 0, 4389 0, 4390 0, 4391 0, 4392 0, 4393 0, 4394 0, 4395 0, 4396 0, 4397 0, 4398 0, 4399 // `credentialIdLength`. 4400 0, 4401 16, 4402 // `credentialId`. 4403 0, 4404 0, 4405 0, 4406 0, 4407 0, 4408 0, 4409 0, 4410 0, 4411 0, 4412 0, 4413 0, 4414 0, 4415 0, 4416 0, 4417 0, 4418 0, 4419 // P-384 COSE key. 4420 cbor::MAP_5, 4421 KTY, 4422 EC2, 4423 ALG, 4424 cbor::NEG_INFO_24, 4425 ES384, 4426 // `crv`. 4427 cbor::NEG_ONE, 4428 // `P-384`. 4429 cbor::TWO, 4430 // `x`. 4431 cbor::NEG_TWO, 4432 cbor::BYTES_INFO_24, 4433 48, 4434 // x-coordinate. This will be overwritten later. 4435 0, 4436 0, 4437 0, 4438 0, 4439 0, 4440 0, 4441 0, 4442 0, 4443 0, 4444 0, 4445 0, 4446 0, 4447 0, 4448 0, 4449 0, 4450 0, 4451 0, 4452 0, 4453 0, 4454 0, 4455 0, 4456 0, 4457 0, 4458 0, 4459 0, 4460 0, 4461 0, 4462 0, 4463 0, 4464 0, 4465 0, 4466 0, 4467 0, 4468 0, 4469 0, 4470 0, 4471 0, 4472 0, 4473 0, 4474 0, 4475 0, 4476 0, 4477 0, 4478 0, 4479 0, 4480 0, 4481 0, 4482 0, 4483 // `y`. 4484 cbor::NEG_THREE, 4485 cbor::BYTES_INFO_24, 4486 48, 4487 // y-coordinate. This will be overwritten later. 4488 0, 4489 0, 4490 0, 4491 0, 4492 0, 4493 0, 4494 0, 4495 0, 4496 0, 4497 0, 4498 0, 4499 0, 4500 0, 4501 0, 4502 0, 4503 0, 4504 0, 4505 0, 4506 0, 4507 0, 4508 0, 4509 0, 4510 0, 4511 0, 4512 0, 4513 0, 4514 0, 4515 0, 4516 0, 4517 0, 4518 0, 4519 0, 4520 0, 4521 0, 4522 0, 4523 0, 4524 0, 4525 0, 4526 0, 4527 0, 4528 0, 4529 0, 4530 0, 4531 0, 4532 0, 4533 0, 4534 0, 4535 0, 4536 ]; 4537 let key = P384Key::from_bytes( 4538 &[ 4539 158, 99, 156, 49, 190, 211, 85, 167, 28, 2, 80, 57, 31, 22, 17, 38, 85, 78, 232, 4540 42, 45, 199, 154, 243, 136, 251, 84, 34, 5, 120, 208, 91, 61, 248, 64, 144, 87, 1, 4541 32, 86, 220, 68, 182, 11, 105, 223, 75, 70, 4542 ] 4543 .into(), 4544 ) 4545 .unwrap() 4546 .public_key(); 4547 let enc_key = key.to_encoded_point(false); 4548 let pub_key = key.to_public_key_der().unwrap(); 4549 let att_obj_len = att_obj.len(); 4550 att_obj[att_obj_len - 99..att_obj_len - 51] 4551 .copy_from_slice(enc_key.x().unwrap().as_slice()); 4552 att_obj[att_obj_len - 48..].copy_from_slice(enc_key.y().unwrap().as_slice()); 4553 let b64_cdata = base64url_nopad::encode(c_data_json.as_bytes()); 4554 let b64_adata = base64url_nopad::encode(&att_obj[att_obj.len() - 181..]); 4555 let b64_key = base64url_nopad::encode(pub_key.as_bytes()); 4556 let b64_aobj = base64url_nopad::encode(att_obj.as_slice()); 4557 // Base case is valid. 4558 assert!( 4559 serde_json::from_str::<Registration>( 4560 serde_json::json!({ 4561 "id": "AAAAAAAAAAAAAAAAAAAAAA", 4562 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 4563 "response": { 4564 "clientDataJSON": b64_cdata, 4565 "authenticatorData": b64_adata, 4566 "transports": [], 4567 "publicKey": b64_key, 4568 "publicKeyAlgorithm": -35, 4569 "attestationObject": b64_aobj, 4570 }, 4571 "clientExtensionResults": {}, 4572 "type": "public-key" 4573 }) 4574 .to_string() 4575 .as_str() 4576 ) 4577 .map_or(false, |reg| reg.response.client_data_json 4578 == c_data_json.as_bytes() 4579 && reg.response.attestation_object_and_c_data_hash[..att_obj.len()] 4580 == att_obj 4581 && reg.response.attestation_object_and_c_data_hash[att_obj.len()..] 4582 == *Sha256::digest(c_data_json.as_bytes()).as_slice() 4583 && reg.response.transports.is_empty() 4584 && matches!(reg.authenticator_attachment, AuthenticatorAttachment::None) 4585 && reg.client_extension_results.cred_props.is_none() 4586 && reg.client_extension_results.prf.is_none()) 4587 ); 4588 // `publicKeyAlgorithm` mismatch. 4589 let mut err = Error::invalid_value( 4590 Unexpected::Other(&format!("{:?}", CoseAlgorithmIdentifier::Es256).as_str()), 4591 &format!("public key algorithm to match the algorithm associated with the public key within the attestation object: {:?}", CoseAlgorithmIdentifier::Es384).as_str() 4592 ) 4593 .to_string().into_bytes(); 4594 assert_eq!( 4595 serde_json::from_str::<Registration>( 4596 serde_json::json!({ 4597 "id": "AAAAAAAAAAAAAAAAAAAAAA", 4598 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 4599 "response": { 4600 "clientDataJSON": b64_cdata, 4601 "authenticatorData": b64_adata, 4602 "transports": [], 4603 "publicKey": b64_key, 4604 "publicKeyAlgorithm": -7, 4605 "attestationObject": b64_aobj, 4606 }, 4607 "clientExtensionResults": {}, 4608 "type": "public-key" 4609 }) 4610 .to_string() 4611 .as_str() 4612 ) 4613 .unwrap_err() 4614 .to_string() 4615 .into_bytes()[..err.len()], 4616 err 4617 ); 4618 // Missing `publicKeyAlgorithm`. 4619 err = Error::missing_field("publicKeyAlgorithm") 4620 .to_string() 4621 .into_bytes(); 4622 assert_eq!( 4623 serde_json::from_str::<Registration>( 4624 serde_json::json!({ 4625 "id": "AAAAAAAAAAAAAAAAAAAAAA", 4626 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 4627 "response": { 4628 "clientDataJSON": b64_cdata, 4629 "authenticatorData": b64_adata, 4630 "transports": [], 4631 "publicKey": b64_key, 4632 "attestationObject": b64_aobj, 4633 }, 4634 "clientExtensionResults": {}, 4635 "type": "public-key" 4636 }) 4637 .to_string() 4638 .as_str() 4639 ) 4640 .unwrap_err() 4641 .to_string() 4642 .into_bytes()[..err.len()], 4643 err 4644 ); 4645 // `null` `publicKeyAlgorithm`. 4646 err = Error::invalid_type(Unexpected::Other("null"), &"publicKeyAlgorithm") 4647 .to_string() 4648 .into_bytes(); 4649 assert_eq!( 4650 serde_json::from_str::<Registration>( 4651 serde_json::json!({ 4652 "id": "AAAAAAAAAAAAAAAAAAAAAA", 4653 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 4654 "response": { 4655 "clientDataJSON": b64_cdata, 4656 "authenticatorData": b64_adata, 4657 "transports": [], 4658 "publicKey": b64_key, 4659 "publicKeyAlgorithm": null, 4660 "attestationObject": b64_aobj, 4661 }, 4662 "clientExtensionResults": {}, 4663 "type": "public-key" 4664 }) 4665 .to_string() 4666 .as_str() 4667 ) 4668 .unwrap_err() 4669 .to_string() 4670 .into_bytes()[..err.len()], 4671 err 4672 ); 4673 // `publicKey` mismatch. 4674 let bad_pub_key = P384PubKey::from_encoded_point(&P384Pt::from_affine_coordinates( 4675 &[ 4676 192, 10, 27, 46, 66, 67, 80, 98, 33, 230, 156, 95, 1, 135, 150, 110, 64, 243, 22, 4677 118, 5, 255, 107, 44, 234, 111, 217, 105, 125, 114, 39, 7, 126, 2, 191, 111, 48, 4678 93, 234, 175, 18, 172, 59, 28, 97, 106, 178, 152, 4679 ] 4680 .into(), 4681 &[ 4682 57, 36, 196, 12, 109, 129, 253, 115, 88, 154, 6, 43, 195, 85, 169, 5, 230, 51, 28, 4683 205, 142, 28, 150, 35, 24, 222, 170, 253, 14, 248, 84, 151, 109, 191, 152, 111, 4684 222, 70, 134, 247, 109, 171, 211, 33, 214, 217, 200, 111, 4685 ] 4686 .into(), 4687 false, 4688 )) 4689 .unwrap(); 4690 err = Error::invalid_value( 4691 Unexpected::Bytes([0; 32].as_slice()), 4692 &format!( 4693 "DER-encoded public key to match the public key within the attestation object: P384(UncompressedP384PubKey({:?}, {:?}))", 4694 &att_obj[att_obj.len() - 99..att_obj.len() - 51], 4695 &att_obj[att_obj.len() - 48..], 4696 ) 4697 .as_str(), 4698 ) 4699 .to_string().into_bytes(); 4700 assert_eq!(serde_json::from_str::<Registration>( 4701 serde_json::json!({ 4702 "id": "AAAAAAAAAAAAAAAAAAAAAA", 4703 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 4704 "response": { 4705 "clientDataJSON": b64_cdata, 4706 "authenticatorData": b64_adata, 4707 "transports": [], 4708 "publicKey": base64url_nopad::encode(bad_pub_key.to_public_key_der().unwrap().as_bytes()), 4709 "publicKeyAlgorithm": -35, 4710 "attestationObject": b64_aobj, 4711 }, 4712 "clientExtensionResults": {}, 4713 "type": "public-key" 4714 }) 4715 .to_string() 4716 .as_str() 4717 ) 4718 .unwrap_err().to_string().into_bytes()[..err.len()], 4719 err 4720 ); 4721 // Missing `publicKey` is allowed when not using EdDSA, ES256, or RS256. 4722 assert!( 4723 serde_json::from_str::<Registration>( 4724 serde_json::json!({ 4725 "id": "AAAAAAAAAAAAAAAAAAAAAA", 4726 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 4727 "response": { 4728 "clientDataJSON": b64_cdata, 4729 "authenticatorData": b64_adata, 4730 "transports": [], 4731 "publicKeyAlgorithm": -35, 4732 "attestationObject": b64_aobj, 4733 }, 4734 "clientExtensionResults": {}, 4735 "type": "public-key" 4736 }) 4737 .to_string() 4738 .as_str() 4739 ) 4740 .is_ok() 4741 ); 4742 // `publicKeyAlgorithm` mismatch when `publicKey` does not exist. 4743 err = Error::invalid_value( 4744 Unexpected::Other(&format!("{:?}", CoseAlgorithmIdentifier::Es256).as_str()), 4745 &format!("public key algorithm to match the algorithm associated with the public key within the attestation object: {:?}", CoseAlgorithmIdentifier::Es384).as_str() 4746 ) 4747 .to_string().into_bytes(); 4748 assert_eq!( 4749 serde_json::from_str::<Registration>( 4750 serde_json::json!({ 4751 "id": "AAAAAAAAAAAAAAAAAAAAAA", 4752 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 4753 "response": { 4754 "clientDataJSON": b64_cdata, 4755 "authenticatorData": b64_adata, 4756 "transports": [], 4757 "publicKeyAlgorithm": -7, 4758 "attestationObject": b64_aobj, 4759 }, 4760 "clientExtensionResults": {}, 4761 "type": "public-key" 4762 }) 4763 .to_string() 4764 .as_str() 4765 ) 4766 .unwrap_err() 4767 .to_string() 4768 .into_bytes()[..err.len()], 4769 err 4770 ); 4771 // `null` `publicKey` is allowed when not using EdDSA, ES256, or RS256. 4772 assert!( 4773 serde_json::from_str::<Registration>( 4774 serde_json::json!({ 4775 "id": "AAAAAAAAAAAAAAAAAAAAAA", 4776 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 4777 "response": { 4778 "clientDataJSON": b64_cdata, 4779 "authenticatorData": b64_adata, 4780 "transports": [], 4781 "publicKey": null, 4782 "publicKeyAlgorithm": -35, 4783 "attestationObject": b64_aobj, 4784 }, 4785 "clientExtensionResults": {}, 4786 "type": "public-key" 4787 }) 4788 .to_string() 4789 .as_str() 4790 ) 4791 .is_ok() 4792 ); 4793 // `publicKeyAlgorithm` mismatch when `publicKey` is null. 4794 err = Error::invalid_value( 4795 Unexpected::Other(&format!("{:?}", CoseAlgorithmIdentifier::Es256).as_str()), 4796 &format!("public key algorithm to match the algorithm associated with the public key within the attestation object: {:?}", CoseAlgorithmIdentifier::Es384).as_str() 4797 ) 4798 .to_string().into_bytes(); 4799 assert_eq!( 4800 serde_json::from_str::<Registration>( 4801 serde_json::json!({ 4802 "id": "AAAAAAAAAAAAAAAAAAAAAA", 4803 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 4804 "response": { 4805 "clientDataJSON": b64_cdata, 4806 "authenticatorData": b64_adata, 4807 "transports": [], 4808 "publicKey": null, 4809 "publicKeyAlgorithm": -7, 4810 "attestationObject": b64_aobj, 4811 }, 4812 "clientExtensionResults": {}, 4813 "type": "public-key" 4814 }) 4815 .to_string() 4816 .as_str() 4817 ) 4818 .unwrap_err() 4819 .to_string() 4820 .into_bytes()[..err.len()], 4821 err 4822 ); 4823 } 4824 #[test] 4825 fn rs256_registration_deserialize_data_mismatch() { 4826 let c_data_json = serde_json::json!({}).to_string(); 4827 let mut att_obj = [ 4828 cbor::MAP_3, 4829 cbor::TEXT_3, 4830 b'f', 4831 b'm', 4832 b't', 4833 cbor::TEXT_4, 4834 b'n', 4835 b'o', 4836 b'n', 4837 b'e', 4838 cbor::TEXT_7, 4839 b'a', 4840 b't', 4841 b't', 4842 b'S', 4843 b't', 4844 b'm', 4845 b't', 4846 cbor::MAP_0, 4847 cbor::TEXT_8, 4848 b'a', 4849 b'u', 4850 b't', 4851 b'h', 4852 b'D', 4853 b'a', 4854 b't', 4855 b'a', 4856 cbor::BYTES_INFO_25, 4857 1, 4858 87, 4859 // `rpIdHash`. 4860 0, 4861 0, 4862 0, 4863 0, 4864 0, 4865 0, 4866 0, 4867 0, 4868 0, 4869 0, 4870 0, 4871 0, 4872 0, 4873 0, 4874 0, 4875 0, 4876 0, 4877 0, 4878 0, 4879 0, 4880 0, 4881 0, 4882 0, 4883 0, 4884 0, 4885 0, 4886 0, 4887 0, 4888 0, 4889 0, 4890 0, 4891 0, 4892 // `flags`. 4893 0b0100_0101, 4894 // `signCount`. 4895 0, 4896 0, 4897 0, 4898 0, 4899 // `aaguid`. 4900 0, 4901 0, 4902 0, 4903 0, 4904 0, 4905 0, 4906 0, 4907 0, 4908 0, 4909 0, 4910 0, 4911 0, 4912 0, 4913 0, 4914 0, 4915 0, 4916 // `credentialIdLength`. 4917 0, 4918 16, 4919 // `credentialId`. 4920 0, 4921 0, 4922 0, 4923 0, 4924 0, 4925 0, 4926 0, 4927 0, 4928 0, 4929 0, 4930 0, 4931 0, 4932 0, 4933 0, 4934 0, 4935 0, 4936 // RSA COSE key. 4937 cbor::MAP_4, 4938 KTY, 4939 RSA, 4940 ALG, 4941 cbor::NEG_INFO_25, 4942 // RS256. 4943 1, 4944 0, 4945 // `n`. 4946 cbor::NEG_ONE, 4947 cbor::BYTES_INFO_25, 4948 1, 4949 0, 4950 // n. This will be overwritten later. 4951 0, 4952 0, 4953 0, 4954 0, 4955 0, 4956 0, 4957 0, 4958 0, 4959 0, 4960 0, 4961 0, 4962 0, 4963 0, 4964 0, 4965 0, 4966 0, 4967 0, 4968 0, 4969 0, 4970 0, 4971 0, 4972 0, 4973 0, 4974 0, 4975 0, 4976 0, 4977 0, 4978 0, 4979 0, 4980 0, 4981 0, 4982 0, 4983 0, 4984 0, 4985 0, 4986 0, 4987 0, 4988 0, 4989 0, 4990 0, 4991 0, 4992 0, 4993 0, 4994 0, 4995 0, 4996 0, 4997 0, 4998 0, 4999 0, 5000 0, 5001 0, 5002 0, 5003 0, 5004 0, 5005 0, 5006 0, 5007 0, 5008 0, 5009 0, 5010 0, 5011 0, 5012 0, 5013 0, 5014 0, 5015 0, 5016 0, 5017 0, 5018 0, 5019 0, 5020 0, 5021 0, 5022 0, 5023 0, 5024 0, 5025 0, 5026 0, 5027 0, 5028 0, 5029 0, 5030 0, 5031 0, 5032 0, 5033 0, 5034 0, 5035 0, 5036 0, 5037 0, 5038 0, 5039 0, 5040 0, 5041 0, 5042 0, 5043 0, 5044 0, 5045 0, 5046 0, 5047 0, 5048 0, 5049 0, 5050 0, 5051 0, 5052 0, 5053 0, 5054 0, 5055 0, 5056 0, 5057 0, 5058 0, 5059 0, 5060 0, 5061 0, 5062 0, 5063 0, 5064 0, 5065 0, 5066 0, 5067 0, 5068 0, 5069 0, 5070 0, 5071 0, 5072 0, 5073 0, 5074 0, 5075 0, 5076 0, 5077 0, 5078 0, 5079 0, 5080 0, 5081 0, 5082 0, 5083 0, 5084 0, 5085 0, 5086 0, 5087 0, 5088 0, 5089 0, 5090 0, 5091 0, 5092 0, 5093 0, 5094 0, 5095 0, 5096 0, 5097 0, 5098 0, 5099 0, 5100 0, 5101 0, 5102 0, 5103 0, 5104 0, 5105 0, 5106 0, 5107 0, 5108 0, 5109 0, 5110 0, 5111 0, 5112 0, 5113 0, 5114 0, 5115 0, 5116 0, 5117 0, 5118 0, 5119 0, 5120 0, 5121 0, 5122 0, 5123 0, 5124 0, 5125 0, 5126 0, 5127 0, 5128 0, 5129 0, 5130 0, 5131 0, 5132 0, 5133 0, 5134 0, 5135 0, 5136 0, 5137 0, 5138 0, 5139 0, 5140 0, 5141 0, 5142 0, 5143 0, 5144 0, 5145 0, 5146 0, 5147 0, 5148 0, 5149 0, 5150 0, 5151 0, 5152 0, 5153 0, 5154 0, 5155 0, 5156 0, 5157 0, 5158 0, 5159 0, 5160 0, 5161 0, 5162 0, 5163 0, 5164 0, 5165 0, 5166 0, 5167 0, 5168 0, 5169 0, 5170 0, 5171 0, 5172 0, 5173 0, 5174 0, 5175 0, 5176 0, 5177 0, 5178 0, 5179 0, 5180 0, 5181 0, 5182 0, 5183 0, 5184 0, 5185 0, 5186 0, 5187 0, 5188 0, 5189 0, 5190 0, 5191 0, 5192 0, 5193 0, 5194 0, 5195 0, 5196 0, 5197 0, 5198 0, 5199 0, 5200 0, 5201 0, 5202 0, 5203 0, 5204 0, 5205 0, 5206 0, 5207 // `e`. 5208 cbor::NEG_TWO, 5209 cbor::BYTES | 3, 5210 // e. 5211 1, 5212 0, 5213 1, 5214 ]; 5215 let n = [ 5216 111, 183, 124, 133, 38, 167, 70, 148, 44, 50, 30, 60, 121, 14, 38, 37, 96, 114, 107, 5217 195, 248, 64, 79, 36, 237, 140, 43, 27, 94, 74, 102, 152, 135, 102, 184, 150, 186, 206, 5218 185, 19, 165, 209, 48, 98, 98, 9, 3, 205, 208, 82, 250, 105, 132, 201, 73, 62, 60, 165, 5219 100, 128, 153, 9, 41, 118, 66, 95, 236, 214, 73, 135, 197, 68, 184, 10, 27, 116, 204, 5220 145, 50, 174, 58, 42, 183, 181, 119, 232, 126, 252, 217, 96, 162, 190, 103, 122, 64, 5221 87, 145, 45, 32, 207, 17, 239, 223, 3, 35, 14, 112, 119, 124, 141, 123, 208, 239, 105, 5222 81, 217, 151, 162, 190, 17, 88, 182, 176, 158, 81, 200, 42, 166, 133, 48, 23, 236, 55, 5223 117, 248, 233, 151, 203, 122, 155, 231, 46, 177, 20, 20, 151, 64, 222, 239, 226, 7, 21, 5224 254, 81, 202, 64, 232, 161, 235, 22, 51, 246, 207, 213, 0, 229, 138, 46, 222, 205, 157, 5225 108, 139, 253, 230, 80, 50, 2, 122, 212, 163, 100, 180, 114, 12, 113, 52, 56, 99, 188, 5226 42, 198, 212, 23, 182, 222, 56, 221, 200, 79, 96, 239, 221, 135, 10, 17, 106, 183, 56, 5227 104, 68, 94, 198, 196, 35, 200, 83, 204, 26, 185, 204, 212, 31, 183, 19, 111, 233, 13, 5228 72, 93, 53, 65, 111, 59, 242, 122, 160, 244, 162, 126, 38, 235, 156, 47, 88, 39, 132, 5229 153, 79, 0, 133, 78, 7, 218, 165, 241, 5230 ]; 5231 let e = 65537u32; 5232 let d = [ 5233 145, 79, 21, 97, 233, 3, 192, 194, 177, 68, 181, 80, 120, 197, 23, 44, 185, 74, 144, 0, 5234 132, 149, 139, 11, 16, 224, 4, 112, 236, 94, 238, 97, 121, 124, 213, 145, 24, 253, 168, 5235 35, 190, 205, 132, 115, 33, 201, 38, 253, 246, 180, 66, 155, 165, 46, 3, 254, 68, 108, 5236 154, 247, 246, 45, 187, 0, 204, 96, 185, 157, 249, 174, 158, 38, 62, 244, 183, 76, 102, 5237 6, 219, 92, 212, 138, 59, 147, 163, 219, 111, 39, 105, 21, 236, 196, 38, 255, 114, 247, 5238 82, 104, 113, 204, 29, 152, 209, 219, 48, 239, 74, 129, 19, 247, 33, 239, 119, 166, 5239 216, 152, 94, 138, 238, 164, 242, 129, 50, 150, 57, 20, 53, 224, 56, 241, 138, 97, 111, 5240 215, 107, 212, 195, 146, 108, 143, 0, 229, 181, 171, 73, 152, 105, 146, 25, 243, 242, 5241 140, 252, 248, 162, 247, 63, 168, 180, 20, 153, 120, 10, 248, 211, 1, 71, 127, 212, 5242 249, 237, 203, 202, 48, 26, 216, 226, 228, 186, 13, 204, 70, 255, 240, 89, 255, 59, 83, 5243 31, 253, 55, 43, 158, 90, 248, 83, 32, 159, 105, 57, 134, 34, 96, 18, 255, 245, 153, 5244 162, 60, 91, 99, 220, 51, 44, 85, 114, 67, 125, 202, 65, 217, 245, 40, 8, 81, 165, 142, 5245 24, 245, 127, 122, 247, 152, 212, 75, 45, 59, 90, 184, 234, 31, 147, 36, 8, 212, 45, 5246 50, 23, 3, 25, 253, 87, 227, 79, 119, 161, 5247 ]; 5248 let p = BigUint::from_bytes_le( 5249 [ 5250 215, 166, 5, 21, 11, 179, 41, 77, 198, 92, 165, 48, 77, 162, 42, 41, 206, 141, 60, 5251 69, 47, 164, 19, 92, 46, 72, 100, 238, 100, 53, 214, 197, 163, 185, 6, 140, 229, 5252 250, 195, 77, 8, 12, 5, 236, 178, 173, 86, 201, 43, 213, 165, 51, 108, 101, 161, 5253 99, 76, 240, 14, 234, 76, 197, 137, 53, 198, 168, 135, 205, 212, 198, 120, 29, 16, 5254 82, 98, 233, 236, 177, 12, 171, 141, 100, 107, 146, 33, 176, 125, 202, 172, 79, 5255 147, 179, 30, 62, 247, 206, 169, 19, 168, 114, 26, 73, 108, 178, 105, 84, 89, 191, 5256 168, 253, 228, 214, 54, 16, 212, 199, 111, 72, 3, 41, 247, 227, 165, 244, 32, 188, 5257 24, 247, 5258 ] 5259 .as_slice(), 5260 ); 5261 let p_2 = BigUint::from_bytes_le( 5262 [ 5263 41, 25, 198, 240, 134, 206, 121, 57, 11, 5, 134, 192, 212, 77, 229, 197, 14, 78, 5264 85, 212, 190, 114, 179, 188, 21, 171, 174, 12, 104, 74, 15, 164, 136, 173, 62, 177, 5265 141, 213, 93, 102, 147, 83, 59, 124, 146, 59, 175, 213, 55, 27, 25, 248, 154, 29, 5266 39, 85, 50, 235, 134, 60, 203, 106, 186, 195, 190, 185, 71, 169, 142, 236, 92, 11, 5267 250, 187, 198, 8, 201, 184, 120, 178, 227, 87, 63, 243, 89, 227, 234, 184, 28, 252, 5268 112, 211, 193, 69, 23, 92, 5, 72, 93, 53, 69, 159, 73, 160, 105, 244, 249, 94, 214, 5269 173, 9, 236, 4, 255, 129, 11, 224, 140, 252, 168, 57, 143, 176, 241, 60, 219, 90, 5270 250, 5271 ] 5272 .as_slice(), 5273 ); 5274 let key = RsaPrivateKey::from_components( 5275 BigUint::from_bytes_le(n.as_slice()), 5276 e.into(), 5277 BigUint::from_bytes_le(d.as_slice()), 5278 vec![p, p_2], 5279 ) 5280 .unwrap() 5281 .to_public_key(); 5282 let pub_key = key.to_public_key_der().unwrap(); 5283 let att_obj_len = att_obj.len(); 5284 att_obj[att_obj_len - 261..att_obj_len - 5] 5285 .copy_from_slice(key.n().to_bytes_be().as_slice()); 5286 let b64_cdata = base64url_nopad::encode(c_data_json.as_bytes()); 5287 let b64_adata = base64url_nopad::encode(&att_obj[att_obj.len() - 343..]); 5288 let b64_key = base64url_nopad::encode(pub_key.as_bytes()); 5289 let b64_aobj = base64url_nopad::encode(att_obj.as_slice()); 5290 // Base case is valid. 5291 assert!( 5292 serde_json::from_str::<Registration>( 5293 serde_json::json!({ 5294 "id": "AAAAAAAAAAAAAAAAAAAAAA", 5295 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 5296 "response": { 5297 "clientDataJSON": b64_cdata, 5298 "authenticatorData": b64_adata, 5299 "transports": [], 5300 "publicKey": b64_key, 5301 "publicKeyAlgorithm": -257, 5302 "attestationObject": b64_aobj, 5303 }, 5304 "clientExtensionResults": {}, 5305 "type": "public-key" 5306 }) 5307 .to_string() 5308 .as_str() 5309 ) 5310 .map_or(false, |reg| reg.response.client_data_json 5311 == c_data_json.as_bytes() 5312 && reg.response.attestation_object_and_c_data_hash[..att_obj.len()] 5313 == att_obj 5314 && reg.response.attestation_object_and_c_data_hash[att_obj.len()..] 5315 == *Sha256::digest(c_data_json.as_bytes()).as_slice() 5316 && reg.response.transports.is_empty() 5317 && matches!(reg.authenticator_attachment, AuthenticatorAttachment::None) 5318 && reg.client_extension_results.cred_props.is_none() 5319 && reg.client_extension_results.prf.is_none()) 5320 ); 5321 // `publicKeyAlgorithm` mismatch. 5322 let mut err = Error::invalid_value( 5323 Unexpected::Other(&format!("{:?}", CoseAlgorithmIdentifier::Eddsa).as_str()), 5324 &format!("public key algorithm to match the algorithm associated with the public key within the attestation object: {:?}", CoseAlgorithmIdentifier::Rs256).as_str() 5325 ) 5326 .to_string().into_bytes(); 5327 assert_eq!( 5328 serde_json::from_str::<Registration>( 5329 serde_json::json!({ 5330 "id": "AAAAAAAAAAAAAAAAAAAAAA", 5331 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 5332 "response": { 5333 "clientDataJSON": b64_cdata, 5334 "authenticatorData": b64_adata, 5335 "transports": [], 5336 "publicKey": b64_key, 5337 "publicKeyAlgorithm": -8, 5338 "attestationObject": b64_aobj, 5339 }, 5340 "clientExtensionResults": {}, 5341 "type": "public-key" 5342 }) 5343 .to_string() 5344 .as_str() 5345 ) 5346 .unwrap_err() 5347 .to_string() 5348 .into_bytes()[..err.len()], 5349 err 5350 ); 5351 // Missing `publicKeyAlgorithm`. 5352 err = Error::missing_field("publicKeyAlgorithm") 5353 .to_string() 5354 .into_bytes(); 5355 assert_eq!( 5356 serde_json::from_str::<Registration>( 5357 serde_json::json!({ 5358 "id": "AAAAAAAAAAAAAAAAAAAAAA", 5359 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 5360 "response": { 5361 "clientDataJSON": b64_cdata, 5362 "authenticatorData": b64_adata, 5363 "transports": [], 5364 "publicKey": b64_key, 5365 "attestationObject": b64_aobj, 5366 }, 5367 "clientExtensionResults": {}, 5368 "type": "public-key" 5369 }) 5370 .to_string() 5371 .as_str() 5372 ) 5373 .unwrap_err() 5374 .to_string() 5375 .into_bytes()[..err.len()], 5376 err 5377 ); 5378 // `null` `publicKeyAlgorithm`. 5379 err = Error::invalid_type(Unexpected::Other("null"), &"publicKeyAlgorithm") 5380 .to_string() 5381 .into_bytes(); 5382 assert_eq!( 5383 serde_json::from_str::<Registration>( 5384 serde_json::json!({ 5385 "id": "AAAAAAAAAAAAAAAAAAAAAA", 5386 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 5387 "response": { 5388 "clientDataJSON": b64_cdata, 5389 "authenticatorData": b64_adata, 5390 "transports": [], 5391 "publicKey": b64_key, 5392 "publicKeyAlgorithm": null, 5393 "attestationObject": b64_aobj, 5394 }, 5395 "clientExtensionResults": {}, 5396 "type": "public-key" 5397 }) 5398 .to_string() 5399 .as_str() 5400 ) 5401 .unwrap_err() 5402 .to_string() 5403 .into_bytes()[..err.len()], 5404 err 5405 ); 5406 // `publicKey` mismatch. 5407 let bad_pub_key = RsaPrivateKey::from_components( 5408 BigUint::from_bytes_le( 5409 [ 5410 175, 161, 161, 75, 52, 244, 72, 168, 29, 119, 33, 120, 3, 222, 231, 152, 222, 5411 119, 112, 83, 221, 237, 74, 174, 79, 216, 147, 251, 245, 94, 234, 114, 254, 21, 5412 17, 254, 8, 115, 75, 127, 150, 87, 59, 109, 230, 116, 85, 90, 11, 160, 63, 217, 5413 9, 38, 187, 250, 226, 183, 38, 164, 182, 218, 22, 19, 58, 189, 83, 219, 11, 5414 144, 15, 99, 151, 166, 46, 57, 17, 111, 189, 131, 142, 113, 85, 122, 188, 238, 5415 52, 21, 116, 125, 102, 195, 182, 165, 29, 156, 213, 182, 125, 156, 88, 56, 221, 5416 2, 98, 43, 210, 115, 32, 4, 105, 88, 181, 158, 207, 236, 162, 250, 253, 240, 5417 72, 8, 253, 50, 220, 247, 76, 170, 143, 68, 225, 231, 113, 64, 244, 17, 138, 5418 162, 233, 33, 2, 67, 11, 223, 188, 232, 152, 193, 20, 32, 243, 52, 64, 43, 2, 5419 243, 8, 77, 150, 232, 109, 148, 95, 127, 55, 71, 162, 34, 54, 83, 135, 52, 172, 5420 191, 32, 42, 106, 43, 211, 206, 100, 104, 110, 232, 5, 43, 120, 180, 166, 40, 5421 144, 233, 239, 103, 134, 103, 255, 224, 138, 184, 208, 137, 127, 36, 189, 143, 5422 248, 201, 2, 218, 51, 232, 96, 30, 83, 124, 109, 241, 23, 179, 247, 151, 238, 5423 212, 204, 44, 43, 223, 148, 241, 172, 10, 235, 155, 94, 68, 116, 24, 116, 191, 5424 86, 53, 127, 35, 133, 198, 204, 59, 76, 110, 16, 1, 15, 148, 135, 157, 5425 ] 5426 .as_slice(), 5427 ), 5428 65537u32.into(), 5429 BigUint::from_bytes_le( 5430 [ 5431 129, 93, 123, 251, 104, 29, 84, 203, 116, 100, 75, 237, 111, 160, 12, 100, 172, 5432 76, 57, 178, 144, 235, 81, 61, 115, 243, 28, 40, 183, 22, 56, 150, 68, 38, 220, 5433 62, 233, 110, 48, 174, 35, 197, 244, 109, 148, 109, 36, 69, 69, 82, 225, 113, 5434 175, 6, 239, 27, 193, 101, 50, 239, 122, 102, 7, 46, 98, 79, 195, 116, 155, 5435 158, 138, 147, 51, 93, 24, 237, 246, 82, 14, 109, 144, 250, 239, 93, 63, 214, 5436 96, 130, 226, 134, 198, 145, 161, 11, 231, 97, 214, 180, 255, 95, 158, 88, 108, 5437 254, 243, 177, 133, 184, 92, 95, 148, 88, 55, 124, 245, 244, 84, 86, 4, 121, 5438 44, 231, 97, 176, 190, 29, 155, 40, 57, 69, 165, 80, 168, 9, 56, 43, 233, 6, 5439 14, 157, 112, 223, 64, 88, 141, 7, 65, 23, 64, 208, 6, 83, 61, 8, 182, 248, 5440 126, 84, 179, 163, 80, 238, 90, 133, 4, 14, 71, 177, 175, 27, 29, 151, 211, 5441 108, 162, 195, 7, 157, 167, 86, 169, 3, 87, 235, 89, 158, 237, 216, 31, 243, 5442 197, 62, 5, 84, 131, 230, 186, 248, 49, 12, 93, 244, 61, 135, 180, 17, 162, 5443 241, 13, 115, 241, 138, 219, 98, 155, 166, 191, 63, 12, 37, 1, 165, 178, 84, 5444 200, 72, 80, 41, 77, 136, 217, 141, 246, 209, 31, 243, 159, 71, 43, 246, 159, 5445 182, 171, 116, 12, 3, 142, 235, 218, 164, 70, 90, 147, 238, 42, 75, 5446 ] 5447 .as_slice(), 5448 ), 5449 vec![ 5450 BigUint::from_bytes_le( 5451 [ 5452 215, 199, 110, 28, 64, 16, 16, 109, 106, 152, 150, 124, 52, 166, 121, 92, 5453 242, 13, 0, 69, 7, 152, 72, 172, 118, 63, 156, 180, 140, 39, 53, 29, 197, 5454 224, 177, 48, 41, 221, 102, 65, 17, 185, 55, 62, 219, 152, 227, 7, 78, 219, 5455 14, 139, 71, 204, 144, 152, 14, 39, 247, 244, 165, 224, 234, 60, 213, 74, 5456 237, 30, 102, 177, 242, 138, 168, 31, 122, 47, 206, 155, 225, 113, 103, 5457 175, 152, 244, 27, 233, 112, 223, 248, 38, 215, 178, 20, 244, 8, 121, 26, 5458 11, 70, 122, 16, 85, 167, 87, 64, 216, 228, 227, 173, 57, 250, 8, 221, 38, 5459 12, 203, 212, 1, 112, 43, 72, 91, 225, 97, 228, 57, 154, 193, 5460 ] 5461 .as_slice(), 5462 ), 5463 BigUint::from_bytes_le( 5464 [ 5465 233, 89, 204, 152, 31, 242, 8, 110, 38, 190, 111, 159, 105, 105, 45, 85, 5466 15, 244, 30, 250, 174, 226, 219, 111, 107, 191, 196, 135, 17, 123, 186, 5467 167, 85, 13, 120, 197, 159, 129, 78, 237, 152, 31, 230, 26, 229, 253, 197, 5468 211, 105, 204, 126, 142, 250, 55, 26, 172, 65, 160, 45, 6, 99, 86, 66, 238, 5469 107, 6, 98, 171, 93, 224, 201, 160, 31, 204, 82, 120, 228, 158, 238, 6, 5470 190, 12, 150, 153, 239, 95, 57, 71, 100, 239, 235, 155, 73, 200, 5, 225, 5471 127, 185, 46, 48, 243, 84, 33, 142, 17, 19, 20, 23, 215, 16, 114, 58, 211, 5472 14, 73, 148, 168, 252, 159, 252, 125, 57, 101, 211, 188, 12, 77, 208, 5473 ] 5474 .as_slice(), 5475 ), 5476 ], 5477 ) 5478 .unwrap() 5479 .to_public_key(); 5480 err = Error::invalid_value( 5481 Unexpected::Bytes([0; 32].as_slice()), 5482 &format!( 5483 "DER-encoded public key to match the public key within the attestation object: Rsa(RsaPubKey({:?}, 65537))", 5484 &att_obj[att_obj.len() - 261..att_obj.len() - 5], 5485 ) 5486 .as_str(), 5487 ) 5488 .to_string().into_bytes(); 5489 assert_eq!(serde_json::from_str::<Registration>( 5490 serde_json::json!({ 5491 "id": "AAAAAAAAAAAAAAAAAAAAAA", 5492 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 5493 "response": { 5494 "clientDataJSON": b64_cdata, 5495 "authenticatorData": b64_adata, 5496 "transports": [], 5497 "publicKey": base64url_nopad::encode(bad_pub_key.to_public_key_der().unwrap().as_bytes()), 5498 "publicKeyAlgorithm": -257, 5499 "attestationObject": b64_aobj, 5500 }, 5501 "clientExtensionResults": {}, 5502 "type": "public-key" 5503 }) 5504 .to_string() 5505 .as_str() 5506 ) 5507 .unwrap_err().to_string().into_bytes()[..err.len()], 5508 err 5509 ); 5510 // Missing `publicKey` when using EdDSA, ES256, or RS256. 5511 err = Error::missing_field("publicKey").to_string().into_bytes(); 5512 assert_eq!( 5513 serde_json::from_str::<Registration>( 5514 serde_json::json!({ 5515 "id": "AAAAAAAAAAAAAAAAAAAAAA", 5516 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 5517 "response": { 5518 "clientDataJSON": b64_cdata, 5519 "authenticatorData": b64_adata, 5520 "transports": [], 5521 "publicKeyAlgorithm": -257, 5522 "attestationObject": b64_aobj, 5523 }, 5524 "clientExtensionResults": {}, 5525 "type": "public-key" 5526 }) 5527 .to_string() 5528 .as_str() 5529 ) 5530 .unwrap_err() 5531 .to_string() 5532 .into_bytes()[..err.len()], 5533 err 5534 ); 5535 // `null` `publicKey` when using EdDSA, ES256, or RS256. 5536 err = Error::invalid_type(Unexpected::Other("null"), &"publicKey") 5537 .to_string() 5538 .into_bytes(); 5539 assert_eq!( 5540 serde_json::from_str::<Registration>( 5541 serde_json::json!({ 5542 "id": "AAAAAAAAAAAAAAAAAAAAAA", 5543 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 5544 "response": { 5545 "clientDataJSON": b64_cdata, 5546 "authenticatorData": b64_adata, 5547 "transports": [], 5548 "publicKey": null, 5549 "publicKeyAlgorithm": -257, 5550 "attestationObject": b64_aobj, 5551 }, 5552 "clientExtensionResults": {}, 5553 "type": "public-key" 5554 }) 5555 .to_string() 5556 .as_str() 5557 ) 5558 .unwrap_err() 5559 .to_string() 5560 .into_bytes()[..err.len()], 5561 err 5562 ); 5563 } 5564 }