ser.rs (96781B)
1 #[cfg(test)] 2 mod tests; 3 use super::{ 4 super::{ 5 super::request::register::CoseAlgorithmIdentifier, 6 ser::{ 7 AuthenticationExtensionsPrfOutputsHelper, AuthenticationExtensionsPrfValues, 8 Base64DecodedVal, ClientExtensions, PublicKeyCredential, 9 }, 10 }, 11 AttestationObject, AttestedCredentialData, AuthTransports, AuthenticationExtensionsPrfOutputs, 12 AuthenticatorAttestation, ClientExtensionsOutputs, CredentialPropertiesOutput, FromCbor as _, 13 Registration, UncompressedPubKey, 14 }; 15 #[cfg(doc)] 16 use super::{AuthenticatorAttachment, CredentialId}; 17 use core::{ 18 fmt::{self, Formatter}, 19 marker::PhantomData, 20 str, 21 }; 22 use rsa::sha2::{Sha256, digest::OutputSizeUser as _}; 23 use serde::de::{Deserialize, Deserializer, Error, IgnoredAny, MapAccess, Unexpected, Visitor}; 24 /// Functionality for deserializing DER-encoded `SubjectPublicKeyInfo` _without_ making copies of data or 25 /// verifying the key is valid. This exists purely to ensure that the public key we receive in JSON is the same as 26 /// the public key in the attestation object. 27 mod spki { 28 use super::super::{ 29 Ed25519PubKey, MlDsa44PubKey, MlDsa65PubKey, MlDsa87PubKey, RsaPubKey, RsaPubKeyErr, 30 UncompressedP256PubKey, UncompressedP384PubKey, UncompressedPubKey, 31 }; 32 use core::fmt::{self, Display, Formatter}; 33 use p256::{ 34 NistP256, 35 elliptic_curve::{Curve, common::typenum::type_operators::ToInt as _}, 36 }; 37 use p384::NistP384; 38 /// Value assigned to the integer type under the universal tag class per 39 /// [ITU-T X.680](https://www.itu.int/rec/T-REC-X.680-202102-I/en). 40 const INTEGER: u8 = 2; 41 /// Value assigned to the bitstring type under the universal tag class per 42 /// [ITU-T X.680](https://www.itu.int/rec/T-REC-X.680-202102-I/en). 43 const BITSTRING: u8 = 3; 44 /// Value assigned to the null type under the universal tag class per 45 /// [ITU-T X.680](https://www.itu.int/rec/T-REC-X.680-202102-I/en). 46 const NULL: u8 = 5; 47 /// Value assigned to the object identifier type under the universal tag class per 48 /// [ITU-T X.680](https://www.itu.int/rec/T-REC-X.680-202102-I/en). 49 const OID: u8 = 6; 50 /// Value assigned to the sequence type under the universal tag class per 51 /// [ITU-T X.680](https://www.itu.int/rec/T-REC-X.680-202102-I/en). 52 const SEQUENCE: u8 = 16; 53 /// Value assigned to a constructed [`SEQUENCE`] per 54 /// [ITU-T X.690](https://www.itu.int/rec/T-REC-X.690-202102-I/en). 55 /// 56 /// All sequences are constructed once encoded, so this will likely always be used instead of 57 /// `SEQUENCE`. 58 const CONSTRUCTED_SEQUENCE: u8 = SEQUENCE | 0b0010_0000; 59 /// Length of the header before the encoded key in a DER-encoded ASN.1 `SubjectPublicKeyInfo` 60 /// for an ML-DSA-87 public key. 61 const MLDSA87_HEADER_LEN: usize = 22; 62 /// Length of a DER-encoded ASN.1 `SubjectPublicKeyInfo` for ML-DSA-87 public key. 63 const MLDSA87_LEN: usize = MLDSA87_HEADER_LEN + 2592; 64 /// Length of the header before the encoded key in a DER-encoded ASN.1 `SubjectPublicKeyInfo` 65 /// for an ML-DSA-65 public key. 66 const MLDSA65_HEADER_LEN: usize = 22; 67 /// Length of a DER-encoded ASN.1 `SubjectPublicKeyInfo` for ML-DSA-65 public key. 68 const MLDSA65_LEN: usize = MLDSA65_HEADER_LEN + 1952; 69 /// Length of the header before the encoded key in a DER-encoded ASN.1 `SubjectPublicKeyInfo` 70 /// for an ML-DSA-44 public key. 71 const MLDSA44_HEADER_LEN: usize = 22; 72 /// Length of a DER-encoded ASN.1 `SubjectPublicKeyInfo` for ML-DSA-44 public key. 73 const MLDSA44_LEN: usize = MLDSA44_HEADER_LEN + 1312; 74 /// Length of the header before the compressed y-coordinate in a DER-encoded ASN.1 `SubjectPublicKeyInfo` 75 /// for an Ed25519 public key. 76 const ED25519_HEADER_LEN: usize = 12; 77 /// Length of a DER-encoded ASN.1 `SubjectPublicKeyInfo` for Ed25519 public key. 78 const ED25519_LEN: usize = ED25519_HEADER_LEN + ed25519_dalek::PUBLIC_KEY_LENGTH; 79 /// `ED25519_LEN` as a `u8`. 80 // `44 as u8` is clearly OK. 81 #[expect( 82 clippy::as_conversions, 83 clippy::cast_possible_truncation, 84 reason = "comments above justify their correctness" 85 )] 86 const ED25519_LEN_U8: u8 = ED25519_LEN as u8; 87 /// Length of the header before the uncompressed SEC- 1 pubic key in a DER-encoded ASN.1 `SubjectPublicKeyInfo` 88 /// for an uncompressed ECDSA public key based on secp256r1/P-256. 89 const P256_HEADER_LEN: usize = 27; 90 /// Number of bytes the x-coordinate takes in an uncompressed P-256 public key. 91 const P256_X_LEN: usize = <NistP256 as Curve>::FieldBytesSize::INT; 92 /// Number of bytes the y-coordinate takes in an uncompressed P-256 public key. 93 const P256_Y_LEN: usize = <NistP256 as Curve>::FieldBytesSize::INT; 94 /// Number of bytes the x and y coordinates take in an uncompressed P-256 public key when concatenated together. 95 const P256_COORD_LEN: usize = P256_X_LEN + P256_Y_LEN; 96 /// Length of a DER-encoded ASN.1 `SubjectPublicKeyInfo` for an uncompressed SEC-1 ECDSA public key 97 /// based on secp256r1/P-256. 98 const P256_LEN: usize = P256_HEADER_LEN + P256_COORD_LEN; 99 /// `P256_LEN` as a `u8`. 100 // `91 as u8` is clearly OK. 101 #[expect( 102 clippy::as_conversions, 103 clippy::cast_possible_truncation, 104 reason = "comments above justify their correctness" 105 )] 106 const P256_LEN_U8: u8 = P256_LEN as u8; 107 /// Length of the header before the uncompressed SEC- 1 pubic key in a DER-encoded ASN.1 `SubjectPublicKeyInfo` 108 /// for an uncompressed ECDSA public key based on secp384r1/P-384. 109 const P384_HEADER_LEN: usize = 24; 110 /// Number of bytes the x-coordinate takes in an uncompressed P-384 public key. 111 const P384_X_LEN: usize = <NistP384 as Curve>::FieldBytesSize::INT; 112 /// Number of bytes the y-coordinate takes in an uncompressed P-384 public key. 113 const P384_Y_LEN: usize = <NistP384 as Curve>::FieldBytesSize::INT; 114 /// Number of bytes the x and y coordinates take in an uncompressed P-384 public key when concatenated together. 115 const P384_COORD_LEN: usize = P384_X_LEN + P384_Y_LEN; 116 /// Length of a DER-encoded ASN.1 `SubjectPublicKeyInfo` for an uncompressed SEC-1 ECDSA public key 117 /// based on secp384r1/P-384. 118 const P384_LEN: usize = P384_HEADER_LEN + P384_COORD_LEN; 119 /// `P384_LEN` as a `u8`. 120 // `120 as u8` is clearly OK. 121 #[expect( 122 clippy::as_conversions, 123 clippy::cast_possible_truncation, 124 reason = "comments above justify their correctness" 125 )] 126 const P384_LEN_U8: u8 = P384_LEN as u8; 127 /// Error returned from [`SubjectPublicKeyInfo::from_der`]. 128 pub(super) enum SubjectPublicKeyInfoErr { 129 /// The length of the DER-encoded ML-DSA-87 key was invalid. 130 MlDsa87Len, 131 /// The header of the DER-encoded ML-DSA-87 key was invalid. 132 MlDsa87Header, 133 /// The length of the DER-encoded ML-DSA-65 key was invalid. 134 MlDsa65Len, 135 /// The header of the DER-encoded ML-DSA-65 key was invalid. 136 MlDsa65Header, 137 /// The length of the DER-encoded ML-DSA-44 key was invalid. 138 MlDsa44Len, 139 /// The header of the DER-encoded ML-DSA-44 key was invalid. 140 MlDsa44Header, 141 /// The length of the DER-encoded Ed25519 key was invalid. 142 Ed25519Len, 143 /// The header of the DER-encoded Ed25519 key was invalid. 144 Ed25519Header, 145 /// The length of the DER-encoded P-256 key was invalid. 146 P256Len, 147 /// The header of the DER-encoded P-256 key was invalid. 148 P256Header, 149 /// The length of the DER-encoded P-384 key was invalid. 150 P384Len, 151 /// The header of the DER-encoded P-384 key was invalid. 152 P384Header, 153 /// The length of the DER-encoded RSA key was invalid. 154 RsaLen, 155 /// The DER-encoding of the RSA key was invalid. 156 RsaEncoding, 157 /// The exponent of the DER-encoded RSA key was too large. 158 RsaExponentTooLarge, 159 /// The DER-encoded RSA key was not a valid [`RsaPubKey`]. 160 RsaPubKey(RsaPubKeyErr), 161 } 162 impl Display for SubjectPublicKeyInfoErr { 163 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 164 match *self { 165 Self::MlDsa87Len => { 166 f.write_str("length of the DER-encoded ML-DSA-87 key was invalid") 167 } 168 Self::MlDsa87Header => { 169 f.write_str("header of the DER-encoded ML-DSA-87 key was invalid") 170 } 171 Self::MlDsa65Len => { 172 f.write_str("length of the DER-encoded ML-DSA-65 key was invalid") 173 } 174 Self::MlDsa65Header => { 175 f.write_str("header of the DER-encoded ML-DSA-65 key was invalid") 176 } 177 Self::MlDsa44Len => { 178 f.write_str("length of the DER-encoded ML-DSA-44 key was invalid") 179 } 180 Self::MlDsa44Header => { 181 f.write_str("header of the DER-encoded ML-DSA-44 key was invalid") 182 } 183 Self::Ed25519Len => { 184 f.write_str("length of the DER-encoded Ed25519 key was invalid") 185 } 186 Self::Ed25519Header => { 187 f.write_str("header of the DER-encoded Ed25519 key was invalid") 188 } 189 Self::P256Len => f.write_str("length of the DER-encoded P-256 key was invalid"), 190 Self::P256Header => f.write_str("header of the DER-encoded P-256 key was invalid"), 191 Self::P384Len => f.write_str("length of the DER-encoded P-384 key was invalid"), 192 Self::P384Header => f.write_str("header of the DER-encoded P-384 key was invalid"), 193 Self::RsaLen => f.write_str("length of the DER-encoded RSA key was invalid"), 194 Self::RsaEncoding => { 195 f.write_str("the DER-encoding of the RSA public key was invalid") 196 } 197 Self::RsaExponentTooLarge => { 198 f.write_str("the DER-encoded RSA public key had an exponent that was too large") 199 } 200 Self::RsaPubKey(err) => { 201 write!(f, "the DER-encoded RSA public was not valid: {err}") 202 } 203 } 204 } 205 } 206 /// Types that can be deserialized from the DER-encoded ASN.1 `SubjectPublicKeyInfo` as defined in 207 /// [RFC 5280](https://datatracker.ietf.org/doc/html/rfc5280#appendix-A.1) and other applicable RFCs 208 /// and documents (e.g., [ITU-T X.690](https://www.itu.int/rec/T-REC-X.690-202102-I/en)). 209 pub(super) trait SubjectPublicKeyInfo<'a>: Sized { 210 /// Transforms the DER-encoded ASN.1 `SubjectPublicKeyInfo` into `Self`. 211 /// 212 /// # Errors 213 /// 214 /// Errors iff `der` does not conform. 215 #[expect(single_use_lifetimes, reason = "false positive")] 216 fn from_der<'b: 'a>(der: &'b [u8]) -> Result<Self, SubjectPublicKeyInfoErr>; 217 } 218 impl<'a> SubjectPublicKeyInfo<'a> for MlDsa87PubKey<&'a [u8]> { 219 #[expect(single_use_lifetimes, reason = "false positive")] 220 fn from_der<'b: 'a>(der: &'b [u8]) -> Result<Self, SubjectPublicKeyInfoErr> { 221 /// ```asn 222 /// SubjectPublicKeyInfo ::= SEQUENCE { 223 /// algorithm AlgorithmIdentifier, 224 /// subjectPublicKey BIT STRING 225 /// } 226 /// 227 /// AlgorithmIdentifier ::= SEQUENCE { 228 /// algorithm OBJECT IDENTIFIER, 229 /// parameters ANY DEFINED BY algorithm OPTIONAL 230 /// } 231 /// ``` 232 /// 233 /// [RFC 9882](https://www.rfc-editor.org/rfc/rfc9882.html) requires parameters to not exist 234 /// in `AlgorithmIdentifier`. 235 /// 236 /// RFC 9882 defines the OID as 2.16.840.1.101.3.4.3.19 which is encoded as 96.134.72.1.101.3.4.3.19 237 /// per [X.690](https://www.itu.int/rec/T-REC-X.690-202102-I/en). 238 /// 239 /// [FIPS 204](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.204.pdf) defines the bitstring as a reinterpretation of the byte string. 240 const HEADER: [u8; MLDSA87_HEADER_LEN] = [ 241 CONSTRUCTED_SEQUENCE, 242 130, 243 10, 244 50, 245 CONSTRUCTED_SEQUENCE, 246 9 + 2, 247 OID, 248 9, 249 96, 250 134, 251 72, 252 1, 253 101, 254 3, 255 4, 256 3, 257 19, 258 BITSTRING, 259 130, 260 10, 261 33, 262 // The number of unused bits. 263 0, 264 ]; 265 der.split_at_checked(HEADER.len()) 266 .ok_or(SubjectPublicKeyInfoErr::MlDsa87Len) 267 .and_then(|(header, rem)| { 268 if header == HEADER { 269 if rem.len() == 2592 { 270 Ok(Self(rem)) 271 } else { 272 Err(SubjectPublicKeyInfoErr::MlDsa87Len) 273 } 274 } else { 275 Err(SubjectPublicKeyInfoErr::MlDsa87Header) 276 } 277 }) 278 } 279 } 280 impl<'a> SubjectPublicKeyInfo<'a> for MlDsa65PubKey<&'a [u8]> { 281 #[expect(single_use_lifetimes, reason = "false positive")] 282 fn from_der<'b: 'a>(der: &'b [u8]) -> Result<Self, SubjectPublicKeyInfoErr> { 283 /// ```asn 284 /// SubjectPublicKeyInfo ::= SEQUENCE { 285 /// algorithm AlgorithmIdentifier, 286 /// subjectPublicKey BIT STRING 287 /// } 288 /// 289 /// AlgorithmIdentifier ::= SEQUENCE { 290 /// algorithm OBJECT IDENTIFIER, 291 /// parameters ANY DEFINED BY algorithm OPTIONAL 292 /// } 293 /// ``` 294 /// 295 /// [RFC 9882](https://www.rfc-editor.org/rfc/rfc9882.html) requires parameters to not exist 296 /// in `AlgorithmIdentifier`. 297 /// 298 /// RFC 9882 defines the OID as 2.16.840.1.101.3.4.3.18 which is encoded as 96.134.72.1.101.3.4.3.18 299 /// per [X.690](https://www.itu.int/rec/T-REC-X.690-202102-I/en). 300 /// 301 /// [FIPS 204](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.204.pdf) defines the bitstring as a reinterpretation of the byte string. 302 const HEADER: [u8; MLDSA65_HEADER_LEN] = [ 303 CONSTRUCTED_SEQUENCE, 304 130, 305 7, 306 178, 307 CONSTRUCTED_SEQUENCE, 308 9 + 2, 309 OID, 310 9, 311 96, 312 134, 313 72, 314 1, 315 101, 316 3, 317 4, 318 3, 319 18, 320 BITSTRING, 321 130, 322 7, 323 161, 324 // The number of unused bits. 325 0, 326 ]; 327 der.split_at_checked(HEADER.len()) 328 .ok_or(SubjectPublicKeyInfoErr::MlDsa65Len) 329 .and_then(|(header, rem)| { 330 if header == HEADER { 331 if rem.len() == 1952 { 332 Ok(Self(rem)) 333 } else { 334 Err(SubjectPublicKeyInfoErr::MlDsa65Len) 335 } 336 } else { 337 Err(SubjectPublicKeyInfoErr::MlDsa65Header) 338 } 339 }) 340 } 341 } 342 impl<'a> SubjectPublicKeyInfo<'a> for MlDsa44PubKey<&'a [u8]> { 343 #[expect(single_use_lifetimes, reason = "false positive")] 344 fn from_der<'b: 'a>(der: &'b [u8]) -> Result<Self, SubjectPublicKeyInfoErr> { 345 /// ```asn 346 /// SubjectPublicKeyInfo ::= SEQUENCE { 347 /// algorithm AlgorithmIdentifier, 348 /// subjectPublicKey BIT STRING 349 /// } 350 /// 351 /// AlgorithmIdentifier ::= SEQUENCE { 352 /// algorithm OBJECT IDENTIFIER, 353 /// parameters ANY DEFINED BY algorithm OPTIONAL 354 /// } 355 /// ``` 356 /// 357 /// [RFC 9882](https://www.rfc-editor.org/rfc/rfc9882.html) requires parameters to not exist 358 /// in `AlgorithmIdentifier`. 359 /// 360 /// RFC 9882 defines the OID as 2.16.840.1.101.3.4.3.17 which is encoded as 96.134.72.1.101.3.4.3.17 361 /// per [X.690](https://www.itu.int/rec/T-REC-X.690-202102-I/en). 362 /// 363 /// [FIPS 204](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.204.pdf) defines the bitstring as a reinterpretation of the byte string. 364 const HEADER: [u8; MLDSA44_HEADER_LEN] = [ 365 CONSTRUCTED_SEQUENCE, 366 130, 367 5, 368 50, 369 CONSTRUCTED_SEQUENCE, 370 9 + 2, 371 OID, 372 9, 373 96, 374 134, 375 72, 376 1, 377 101, 378 3, 379 4, 380 3, 381 17, 382 BITSTRING, 383 130, 384 5, 385 33, 386 // The number of unused bits. 387 0, 388 ]; 389 der.split_at_checked(HEADER.len()) 390 .ok_or(SubjectPublicKeyInfoErr::MlDsa44Len) 391 .and_then(|(header, rem)| { 392 if header == HEADER { 393 if rem.len() == 1312 { 394 Ok(Self(rem)) 395 } else { 396 Err(SubjectPublicKeyInfoErr::MlDsa44Len) 397 } 398 } else { 399 Err(SubjectPublicKeyInfoErr::MlDsa44Header) 400 } 401 }) 402 } 403 } 404 impl<'a> SubjectPublicKeyInfo<'a> for Ed25519PubKey<&'a [u8]> { 405 #[expect(single_use_lifetimes, reason = "false positive")] 406 fn from_der<'b: 'a>(der: &'b [u8]) -> Result<Self, SubjectPublicKeyInfoErr> { 407 /// ```asn 408 /// SubjectPublicKeyInfo ::= SEQUENCE { 409 /// algorithm AlgorithmIdentifier, 410 /// subjectPublicKey BIT STRING 411 /// } 412 /// 413 /// AlgorithmIdentifier ::= SEQUENCE { 414 /// algorithm OBJECT IDENTIFIER, 415 /// parameters ANY DEFINED BY algorithm OPTIONAL 416 /// } 417 /// ``` 418 /// 419 /// [RFC 8410](https://www.rfc-editor.org/rfc/rfc8410#section-3) requires parameters to not exist 420 /// in `AlgorithmIdentifier`. 421 /// 422 /// RFC 8410 defines the OID as 1.3.101.112 which is encoded as 43.101.112 423 /// per [X.690](https://www.itu.int/rec/T-REC-X.690-202102-I/en). 424 /// 425 /// RFC 8410 defines the bitstring as a reinterpretation of the byte string. 426 const HEADER: [u8; ED25519_HEADER_LEN] = [ 427 CONSTRUCTED_SEQUENCE, 428 // `ED25519_LEN_U8` is the length of the entire payload; thus we subtract 429 // the "consumed" length. 430 ED25519_LEN_U8 - 2, 431 CONSTRUCTED_SEQUENCE, 432 // AlgorithmIdentifier only contains the OID; thus it's length is 5. 433 3 + 2, 434 OID, 435 3, 436 43, 437 101, 438 112, 439 BITSTRING, 440 // `ED25519_LEN_U8` is the length of the entire payload; thus we subtract 441 // the "consumed" length. 442 ED25519_LEN_U8 - 11, 443 // The number of unused bits. 444 0, 445 ]; 446 der.split_at_checked(HEADER.len()) 447 .ok_or(SubjectPublicKeyInfoErr::Ed25519Len) 448 .and_then(|(header, rem)| { 449 if header == HEADER { 450 if rem.len() == ed25519_dalek::PUBLIC_KEY_LENGTH { 451 Ok(Self(rem)) 452 } else { 453 Err(SubjectPublicKeyInfoErr::Ed25519Len) 454 } 455 } else { 456 Err(SubjectPublicKeyInfoErr::Ed25519Header) 457 } 458 }) 459 } 460 } 461 impl<'a> SubjectPublicKeyInfo<'a> for UncompressedP256PubKey<'a> { 462 #[expect(single_use_lifetimes, reason = "false positive")] 463 fn from_der<'b: 'a>(der: &'b [u8]) -> Result<Self, SubjectPublicKeyInfoErr> { 464 // ```asn 465 // SubjectPublicKeyInfo ::= SEQUENCE { 466 // algorithm AlgorithmIdentifier, 467 // subjectPublicKey BIT STRING 468 // } 469 // 470 // AlgorithmIdentifier ::= SEQUENCE { 471 // algorithm OBJECT IDENTIFIER, 472 // parameters ANY DEFINED BY algorithm OPTIONAL 473 // } 474 // 475 // ECParameters ::= CHOICE { 476 // namedCurve OBJECT IDENTIFIER 477 // -- implicitCurve NULL 478 // -- specifiedCurve SpecifiedECDomain 479 // } 480 // ``` 481 // [RFC 5480](https://www.rfc-editor.org/rfc/rfc5480#section-2.1.1) requires parameters to exist and 482 // be of the `ECParameters` form with the requirement that `namedCurve` is chosen. 483 // 484 // RFC 5480 defines the OID for id-ecPublicKey as 1.2.840.10045.2.1 and the OID for the namedCurve 485 // 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 486 // 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). 487 // 488 // [RFC 5480](https://www.rfc-editor.org/rfc/rfc5480#section-5) only requires support for the 489 // uncompressed form and only states that the compressed form MAY be supported. In practice this means 490 // DER-encoded payloads almost always are of the uncompressed form for compatibility reasons. This in 491 // conjunction with the fact the COSE key is required to be in the uncompressed form means we only support 492 // DER-encoded payloads containing uncompressed keys. 493 // 494 // [SEC 1](https://secg.org/sec1-v2.pdf) defines the point as an octet string, and the conversion 495 // to a bitstring simply requires reinterpreting the octet string as a bitstring. 496 497 /// Header of the DER-encoded payload before the public key. 498 const HEADER: [u8; P256_HEADER_LEN] = [ 499 CONSTRUCTED_SEQUENCE, 500 // `P256_LEN_U8` is the length of the entire payload; thus we subtract 501 // the "consumed" length. 502 P256_LEN_U8 - 2, 503 CONSTRUCTED_SEQUENCE, 504 7 + 2 + 8 + 2, 505 OID, 506 7, 507 42, 508 134, 509 72, 510 206, 511 61, 512 2, 513 1, 514 OID, 515 8, 516 42, 517 134, 518 72, 519 206, 520 61, 521 3, 522 1, 523 7, 524 BITSTRING, 525 // `P256_LEN_U8` is the length of the entire payload; thus we subtract 526 // the "consumed" length. 527 P256_LEN_U8 - 25, 528 // The number of unused bits. 529 0, 530 // SEC-1 tag for an uncompressed key. 531 4, 532 ]; 533 der.split_at_checked(HEADER.len()) 534 .ok_or(SubjectPublicKeyInfoErr::P256Len) 535 .and_then(|(header, header_rem)| { 536 if header == HEADER { 537 header_rem 538 .split_at_checked(P256_X_LEN) 539 .ok_or(SubjectPublicKeyInfoErr::P256Len) 540 .and_then(|(x, y)| { 541 if y.len() == P256_Y_LEN { 542 Ok(Self(x, y)) 543 } else { 544 Err(SubjectPublicKeyInfoErr::P256Len) 545 } 546 }) 547 } else { 548 Err(SubjectPublicKeyInfoErr::P256Header) 549 } 550 }) 551 } 552 } 553 impl<'a> SubjectPublicKeyInfo<'a> for UncompressedP384PubKey<'a> { 554 #[expect(single_use_lifetimes, reason = "false positive")] 555 fn from_der<'b: 'a>(der: &'b [u8]) -> Result<Self, SubjectPublicKeyInfoErr> { 556 // ```asn 557 // SubjectPublicKeyInfo ::= SEQUENCE { 558 // algorithm AlgorithmIdentifier, 559 // subjectPublicKey BIT STRING 560 // } 561 // 562 // AlgorithmIdentifier ::= SEQUENCE { 563 // algorithm OBJECT IDENTIFIER, 564 // parameters ANY DEFINED BY algorithm OPTIONAL 565 // } 566 // 567 // ECParameters ::= CHOICE { 568 // namedCurve OBJECT IDENTIFIER 569 // -- implicitCurve NULL 570 // -- specifiedCurve SpecifiedECDomain 571 // } 572 // ``` 573 // [RFC 5480](https://www.rfc-editor.org/rfc/rfc5480#section-2.1.1) requires parameters to exist and 574 // be of the `ECParameters` form with the requirement that `namedCurve` is chosen. 575 // 576 // RFC 5480 defines the OID for id-ecPublicKey as 1.2.840.10045.2.1 and the OID for the namedCurve 577 // secp384r1 as 1.3.132.0.34. The former OID is encoded as 42.134.72.206.61.2.1 and the latter 578 // is encoded as 43.129.4.0.34 per [X.690](https://www.itu.int/rec/T-REC-X.690-202102-I/en). 579 // 580 // [RFC 5480](https://www.rfc-editor.org/rfc/rfc5480#section-5) only requires support for the 581 // uncompressed form and only states that the compressed form MAY be supported. In practice this means 582 // DER-encoded payloads almost always are of the uncompressed form for compatibility reasons. This in 583 // conjunction with the fact the COSE key is required to be in the uncompressed form means we only support 584 // DER-encoded payloads containing uncompressed keys. 585 // 586 // [SEC 1](https://secg.org/sec1-v2.pdf) defines the point as an octet string, and the conversion 587 // to a bitstring simply requires reinterpreting the octet string as a bitstring. 588 589 /// Header of the DER-encoded payload before the public key. 590 const HEADER: [u8; P384_HEADER_LEN] = [ 591 CONSTRUCTED_SEQUENCE, 592 // `P384_LEN_U8` is the length of the entire payload; thus we subtract 593 // the "consumed" length. 594 P384_LEN_U8 - 2, 595 CONSTRUCTED_SEQUENCE, 596 7 + 2 + 5 + 2, 597 OID, 598 7, 599 42, 600 134, 601 72, 602 206, 603 61, 604 2, 605 1, 606 OID, 607 5, 608 43, 609 129, 610 4, 611 0, 612 34, 613 BITSTRING, 614 // `P384_LEN_U8` is the length of the entire payload; thus we subtract 615 // the "consumed" length. 616 P384_LEN_U8 - 22, 617 // The number of unused bits. 618 0, 619 // SEC-1 tag for an uncompressed key. 620 4, 621 ]; 622 der.split_at_checked(HEADER.len()) 623 .ok_or(SubjectPublicKeyInfoErr::P384Len) 624 .and_then(|(header, header_rem)| { 625 if header == HEADER { 626 header_rem 627 .split_at_checked(P384_X_LEN) 628 .ok_or(SubjectPublicKeyInfoErr::P384Len) 629 .and_then(|(x, y)| { 630 if y.len() == P384_Y_LEN { 631 Ok(Self(x, y)) 632 } else { 633 Err(SubjectPublicKeyInfoErr::P384Len) 634 } 635 }) 636 } else { 637 Err(SubjectPublicKeyInfoErr::P384Header) 638 } 639 }) 640 } 641 } 642 impl<'a> SubjectPublicKeyInfo<'a> for RsaPubKey<&'a [u8]> { 643 #[expect(single_use_lifetimes, reason = "false positive")] 644 #[expect( 645 clippy::arithmetic_side_effects, 646 clippy::big_endian_bytes, 647 clippy::indexing_slicing, 648 clippy::missing_asserts_for_indexing, 649 reason = "comments justify their correctness" 650 )] 651 #[expect( 652 clippy::too_many_lines, 653 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." 654 )] 655 fn from_der<'b: 'a>(der: &'b [u8]) -> Result<Self, SubjectPublicKeyInfoErr> { 656 // ```asn 657 // SubjectPublicKeyInfo ::= SEQUENCE { 658 // algorithm AlgorithmIdentifier, 659 // subjectPublicKey BIT STRING 660 // } 661 // 662 // AlgorithmIdentifier ::= SEQUENCE { 663 // algorithm OBJECT IDENTIFIER, 664 // parameters ANY DEFINED BY algorithm OPTIONAL 665 // } 666 // 667 // RSAPublicKey ::= SEQUENCE { 668 // modulus INTEGER, -- n 669 // publicExponent INTEGER --e 670 // } 671 // 672 // pkcs-1 OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840) 673 // rsadsi(113549) pkcs(1) 1 674 // } 675 // 676 // rsaEncryption OBJECT IDENTIFIER ::= { pkcs-1 1} 677 // ``` 678 // [RFC 3279](https://www.rfc-editor.org/rfc/rfc3279#section-2.3.1) requires parameters to exist and 679 // be null. 680 // 681 // RFC 3279 defines the OID for rsaEncryption as 1.2.840.113549.1.1.1 which is encoded as 682 // 42.134.72.134.247.13.1.1.1 per [X.690](https://www.itu.int/rec/T-REC-X.690-202102-I/en). 683 // 684 // Note we only allow moduli that are 256 to 2048 bytes in length inclusively. Additionally 685 // we only allow `u32` exponents; consequently all lengths that include the modulus will always be 686 // encoded with two bytes. 687 688 /// `AlgorithmIdentifier` header including the `BITSTRING` and number of bytes the length 689 /// is encoded in of the `BITSTRING` type of `subjectPublicKey` 690 /// (130-128 = 2 bytes to encode the length). 691 const ALG_OID_HEADER: [u8; 17] = [ 692 CONSTRUCTED_SEQUENCE, 693 9 + 2 + 2, 694 OID, 695 9, 696 42, 697 134, 698 72, 699 134, 700 247, 701 13, 702 1, 703 1, 704 1, 705 NULL, 706 0, 707 BITSTRING, 708 130, 709 ]; 710 /// `CONSTRUCTED_SEQUENCE` whose length is encoded in two bytes. 711 const SEQ_LONG: [u8; 2] = [CONSTRUCTED_SEQUENCE, 130]; 712 /// `INTEGER` whose length is encoded in two bytes. 713 const INT_LONG: [u8; 2] = [INTEGER, 130]; 714 der.split_at_checked(SEQ_LONG.len()) 715 .ok_or(SubjectPublicKeyInfoErr::RsaLen) 716 .and_then(|(seq, seq_rem)| { 717 if seq == SEQ_LONG { 718 seq_rem 719 .split_at_checked(2) 720 .ok_or(SubjectPublicKeyInfoErr::RsaLen) 721 .and_then(|(seq_len, seq_len_rem)| { 722 let mut len = [0; 2]; 723 len.copy_from_slice(seq_len); 724 let rem_len = usize::from(u16::from_be_bytes(len)); 725 if rem_len == seq_len_rem.len() { 726 if rem_len > 255 { 727 // We can safely split here since we know `seq_len_rem` is at least 728 // 256 which is greater than `ALG_OID_HEADER.len()`. 729 let (a_oid, a_oid_rem) = seq_len_rem.split_at(ALG_OID_HEADER.len()); 730 if a_oid == ALG_OID_HEADER { 731 // `a_oid_rem.len()` is at least 239, so splitting is fine. 732 let (bit_str_len_enc, bit_str_val) = a_oid_rem.split_at(2); 733 let mut bit_string_len = [0; 2]; 734 bit_string_len.copy_from_slice(bit_str_len_enc); 735 let bit_str_val_len = usize::from(u16::from_be_bytes(bit_string_len)); 736 if bit_str_val_len == bit_str_val.len() { 737 if bit_str_val_len > 255 { 738 // `bit_str_val.len() > 255`, so splitting is fine. 739 let (unused_bits, bits_rem) = bit_str_val.split_at(1); 740 if unused_bits == [0] { 741 // We can safely split here since we know `bits_rem.len()` is at least 742 // 255. 743 let (rsa_seq, rsa_seq_rem) = bits_rem.split_at(SEQ_LONG.len()); 744 if rsa_seq == SEQ_LONG { 745 // `rsa_seq_rem.len()` is at least 253, so splitting is fine. 746 let (rsa_seq_len_enc, rsa_seq_len_enc_rem) = rsa_seq_rem.split_at(2); 747 let mut rsa_seq_len = [0; 2]; 748 rsa_seq_len.copy_from_slice(rsa_seq_len_enc); 749 let rsa_key_info_len = usize::from(u16::from_be_bytes(rsa_seq_len)); 750 if rsa_key_info_len == rsa_seq_len_enc_rem.len() { 751 if rsa_key_info_len > 255 { 752 // We can safely split here since we know `rsa_seq_len_enc_rem.len()` 753 // is at least 256. 754 let (n_meta, n_meta_rem) = rsa_seq_len_enc_rem.split_at(INT_LONG.len()); 755 if n_meta == INT_LONG { 756 // `n_meta_rem.len()` is at least 254, so splitting is fine. 757 let (n_len_enc, n_len_enc_rem) = n_meta_rem.split_at(2); 758 let mut n_len = [0; 2]; 759 n_len.copy_from_slice(n_len_enc); 760 let mod_len = usize::from(u16::from_be_bytes(n_len)); 761 if mod_len > 255 { 762 n_len_enc_rem.split_at_checked(mod_len).ok_or(SubjectPublicKeyInfoErr::RsaLen).and_then(|(mut n, n_rem)| { 763 // `n.len() > 255`, so indexing is fine. 764 let n_first = n[0]; 765 // DER integers are signed; thus the most significant bit must be 0. 766 // DER integers are minimally encoded; thus when a leading 0 exists, 767 // the second byte must be at least 128. 768 // `n.len() > 255`, so indexing is fine. 769 if n_first < 128 && (n_first != 0 || n[1] > 127) { 770 if n_first == 0 { 771 // `n.len() > 255`, so indexing is fine. 772 // We must remove the leading 0. 773 n = &n[1..]; 774 } 775 n_rem.split_first().ok_or(SubjectPublicKeyInfoErr::RsaLen).and_then(|(e_type, e_type_rem)| { 776 if *e_type == INTEGER { 777 e_type_rem.split_first().ok_or(SubjectPublicKeyInfoErr::RsaLen).and_then(|(e_len, e_len_rem)| { 778 let e_len_usize = usize::from(*e_len); 779 if e_len_usize == e_len_rem.len() { 780 e_len_rem.first().ok_or(SubjectPublicKeyInfoErr::RsaLen).and_then(|&e_first| { 781 // DER integers are signed; thus the most significant bit must be 0. 782 if e_first < 128 { 783 // `RsaPubKey` only allows `u32` exponents, which means we only care 784 // about lengths up to 5. 785 match e_len_usize { 786 1 => Ok(u32::from(e_first)), 787 2..=5 => if e_first == 0 { 788 // DER integers are minimally encoded; thus when a leading 789 // 0 exists, the second byte must be at least 128. 790 // We know the length is at least 2; thus this won't `panic`. 791 if e_len_rem[1] > 127 { 792 let mut e = [0; 4]; 793 if e_len_usize == 5 { 794 // We know the length is at least 2; thus this won't `panic`. 795 e.copy_from_slice(&e_len_rem[1..]); 796 } else { 797 // `e.len() == 4` and `e_len_usize` is at most 4; thus underflow 798 // won't occur nor will indexing `panic`. `e` is big-endian, 799 // so we start from the right. 800 e[4 - e_len_usize..].copy_from_slice(e_len_rem); 801 } 802 Ok(u32::from_be_bytes(e)) 803 } else { 804 Err(SubjectPublicKeyInfoErr::RsaEncoding) 805 } 806 } else if e_len_usize == 5 { 807 // 5 bytes are only possible for `INTEGER`s that 808 // are greater than `i32::MAX`, which will be encoded 809 // with a leading 0. 810 Err(SubjectPublicKeyInfoErr::RsaEncoding) 811 } else { 812 let mut e = [0; 4]; 813 // `e.len() == 4` and `e_len_usize` is at most 4; thus underflow 814 // won't occur nor will indexing `panic`. `e` is big-endian, 815 // so we start from the right. 816 e[4 - e_len_usize..].copy_from_slice(e_len_rem); 817 Ok(u32::from_be_bytes(e)) 818 }, 819 _ => Err(SubjectPublicKeyInfoErr::RsaExponentTooLarge), 820 }.and_then(|e| Self::try_from((n, e)).map_err(SubjectPublicKeyInfoErr::RsaPubKey)) 821 } else { 822 Err(SubjectPublicKeyInfoErr::RsaEncoding) 823 } 824 }) 825 } else { 826 Err(SubjectPublicKeyInfoErr::RsaLen) 827 } 828 }) 829 } else { 830 Err(SubjectPublicKeyInfoErr::RsaEncoding) 831 } 832 }) 833 } else { 834 Err(SubjectPublicKeyInfoErr::RsaEncoding) 835 } 836 }) 837 } else { 838 Err(SubjectPublicKeyInfoErr::RsaEncoding) 839 } 840 } else { 841 Err(SubjectPublicKeyInfoErr::RsaEncoding) 842 } 843 } else { 844 Err(SubjectPublicKeyInfoErr::RsaEncoding) 845 } 846 } else { 847 Err(SubjectPublicKeyInfoErr::RsaLen) 848 } 849 } else { 850 Err(SubjectPublicKeyInfoErr::RsaEncoding) 851 } 852 } else { 853 Err(SubjectPublicKeyInfoErr::RsaEncoding) 854 } 855 } else { 856 Err(SubjectPublicKeyInfoErr::RsaEncoding) 857 } 858 } else { 859 Err(SubjectPublicKeyInfoErr::RsaLen) 860 } 861 } else { 862 Err(SubjectPublicKeyInfoErr::RsaEncoding) 863 } 864 } else { 865 Err(SubjectPublicKeyInfoErr::RsaEncoding) 866 } 867 } else { 868 Err(SubjectPublicKeyInfoErr::RsaLen) 869 } 870 }) 871 } else { 872 Err(SubjectPublicKeyInfoErr::RsaEncoding) 873 } 874 }) 875 } 876 } 877 impl<'a> SubjectPublicKeyInfo<'a> for UncompressedPubKey<'a> { 878 #[expect(clippy::indexing_slicing, reason = "comments justify correctness")] 879 #[expect(single_use_lifetimes, reason = "false positive")] 880 fn from_der<'b: 'a>(der: &'b [u8]) -> Result<Self, SubjectPublicKeyInfoErr> { 881 /// Index in a DER-encoded payload of the ML-DSA-* public key that corresponds 882 /// to the OID length. 883 const ML_DSA_OID_LEN_IDX: usize = 5; 884 /// Length of the OID for the DER-encoded ML-DSA-* public keys. 885 /// 886 /// Note this is _not_ the same as the OID length for an RSA public key (i.e., 13). 887 const ML_DSA_OID_LEN: u8 = 11; 888 // The only lengths that overlap is RSA with ML-DSA-44 and ML-DSA-65. 889 match der.len() { 890 // The minimum modulus we support for RSA is 2048 bits which is 256 bytes; 891 // thus clearly its encoding will be at least 256 which is greater than 892 // all of the non-ML-DSA-* values and less than the ML-DSA-* values. 893 // The maximum modulus we support for RSA is 16K bits which is 2048 bytes and the maximum 894 // exponent we support for RSA is 4 bytes which is hundreds of bytes less than 2614 895 // (i.e., the length of a DER-encoded ML-DSAS-87 public key). 896 ED25519_LEN => Ed25519PubKey::from_der(der).map(Self::Ed25519), 897 P256_LEN => UncompressedP256PubKey::from_der(der).map(Self::P256), 898 P384_LEN => UncompressedP384PubKey::from_der(der).map(Self::P384), 899 MLDSA44_LEN => { 900 // `ML_DSA_OID_LEN_IDX < MLDSA44_LEN = der.len()` so indexing 901 // won't `panic`. 902 if der[ML_DSA_OID_LEN_IDX] == ML_DSA_OID_LEN { 903 MlDsa44PubKey::from_der(der).map(Self::MlDsa44) 904 } else { 905 RsaPubKey::from_der(der).map(Self::Rsa) 906 } 907 } 908 MLDSA65_LEN => { 909 // `ML_DSA_OID_LEN_IDX < MLDSA65_LEN = der.len()` so indexing 910 // won't `panic`. 911 if der[ML_DSA_OID_LEN_IDX] == ML_DSA_OID_LEN { 912 MlDsa65PubKey::from_der(der).map(Self::MlDsa65) 913 } else { 914 RsaPubKey::from_der(der).map(Self::Rsa) 915 } 916 } 917 MLDSA87_LEN => MlDsa87PubKey::from_der(der).map(Self::MlDsa87), 918 _ => RsaPubKey::from_der(der).map(Self::Rsa), 919 } 920 } 921 } 922 } 923 /// Helper type returned from [`AuthenticatorAttestationVisitor::visit_map`]. 924 /// 925 /// The purpose of this type is to hopefully avoid re-parsing the raw attestation object multiple times. In 926 /// particular [`Registration`] and [`super::ser_relaxed::RegistrationRelaxed`] will attempt to validate `id` is the 927 /// same as the [`CredentialId`] within the attestation object. 928 pub(super) struct AuthAttest { 929 /// The data we care about. 930 pub attest: AuthenticatorAttestation, 931 /// [`CredentialId`] information. This is `None` iff `authenticatorData`, `publicKey`, and 932 /// `publicKeyAlgorithm` do not exist and we are performing a `RELAXED` parsing. When `Some`, the first 933 /// `usize` is the starting index of `CredentialId` within the attestation object; and the second `usize` is 934 /// 1 past the last index of `CredentialId`. 935 pub cred_info: Option<(usize, usize)>, 936 } 937 /// Fields in `AuthenticatorAttestationResponseJSON`. 938 enum AttestField<const IGNORE_UNKNOWN: bool> { 939 /// `clientDataJSON`. 940 ClientDataJson, 941 /// `attestationObject`. 942 AttestationObject, 943 /// `authenticatorData`. 944 AuthenticatorData, 945 /// `transports`. 946 Transports, 947 /// `publicKey`. 948 PublicKey, 949 /// `publicKeyAlgorithm`. 950 PublicKeyAlgorithm, 951 /// Unknown fields. 952 Other, 953 } 954 impl<'e, const I: bool> Deserialize<'e> for AttestField<I> { 955 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 956 where 957 D: Deserializer<'e>, 958 { 959 /// `Visitor` for `AttestField`. 960 struct AttestFieldVisitor<const IGNORE_UNKNOWN: bool>; 961 impl<const IG: bool> Visitor<'_> for AttestFieldVisitor<IG> { 962 type Value = AttestField<IG>; 963 fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result { 964 write!( 965 formatter, 966 "'{CLIENT_DATA_JSON}', '{ATTESTATION_OBJECT}', '{AUTHENTICATOR_DATA}', '{TRANSPORTS}', '{PUBLIC_KEY}', or '{PUBLIC_KEY_ALGORITHM}'" 967 ) 968 } 969 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E> 970 where 971 E: Error, 972 { 973 match v { 974 CLIENT_DATA_JSON => Ok(AttestField::ClientDataJson), 975 ATTESTATION_OBJECT => Ok(AttestField::AttestationObject), 976 AUTHENTICATOR_DATA => Ok(AttestField::AuthenticatorData), 977 TRANSPORTS => Ok(AttestField::Transports), 978 PUBLIC_KEY => Ok(AttestField::PublicKey), 979 PUBLIC_KEY_ALGORITHM => Ok(AttestField::PublicKeyAlgorithm), 980 _ => { 981 if IG { 982 Ok(AttestField::Other) 983 } else { 984 Err(E::unknown_field(v, AUTH_ATTEST_FIELDS)) 985 } 986 } 987 } 988 } 989 } 990 deserializer.deserialize_identifier(AttestFieldVisitor::<I>) 991 } 992 } 993 /// Attestation object. We use this instead of `Base64DecodedVal` since we want to manually 994 /// allocate the `Vec` in order to avoid re-allocation. Internally `AuthenticatorAttestation::new` 995 /// appends the SHA-256 hash to the passed attestation object `Vec` to avoid temporarily allocating 996 /// a `Vec` that contains the attestation object and hash for signature verification. Calling code 997 /// can avoid any reallocation that would occur when the capacity is not large enough by ensuring the 998 /// passed `Vec` has at least 32 bytes of available capacity. 999 pub(super) struct AttObj(pub Vec<u8>); 1000 impl<'e> Deserialize<'e> for AttObj { 1001 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 1002 where 1003 D: Deserializer<'e>, 1004 { 1005 /// `Visitor` for `AttObj`. 1006 struct AttObjVisitor; 1007 impl Visitor<'_> for AttObjVisitor { 1008 type Value = AttObj; 1009 fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result { 1010 formatter.write_str("base64url-encoded attestation object") 1011 } 1012 #[expect( 1013 clippy::arithmetic_side_effects, 1014 reason = "comment justifies their correctness" 1015 )] 1016 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E> 1017 where 1018 E: Error, 1019 { 1020 base64url_nopad::decode_len(v.len()) 1021 .ok_or_else(|| E::invalid_value(Unexpected::Str(v), &"base64url-encoded value")) 1022 .and_then(|len| { 1023 // The decoded length is 3/4 of the encoded length, so overflow could only occur 1024 // if usize::MAX / 4 < 32 => usize::MAX < 128 < u8::MAX; thus overflow is not 1025 // possible. We add 32 since the SHA-256 hash of `clientDataJSON` will be added to 1026 // the raw attestation object by `AuthenticatorAttestation::new`. 1027 let mut att_obj = vec![0; len + Sha256::output_size()]; 1028 att_obj.truncate(len); 1029 base64url_nopad::decode_buffer_exact(v.as_bytes(), &mut att_obj) 1030 .map_err(E::custom) 1031 .map(|()| AttObj(att_obj)) 1032 }) 1033 } 1034 } 1035 deserializer.deserialize_str(AttObjVisitor) 1036 } 1037 } 1038 /// `Visitor` for `AuthenticatorAttestation`. 1039 /// 1040 /// Unknown fields are ignored and only `clientDataJSON` and `attestationObject` are required iff `RELAXED`. 1041 pub(super) struct AuthenticatorAttestationVisitor<const RELAXED: bool>; 1042 impl<'d, const R: bool> Visitor<'d> for AuthenticatorAttestationVisitor<R> { 1043 type Value = AuthAttest; 1044 fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result { 1045 formatter.write_str("AuthenticatorAttestation") 1046 } 1047 #[expect(clippy::too_many_lines, reason = "find it easier to reason about")] 1048 #[expect( 1049 clippy::arithmetic_side_effects, 1050 clippy::indexing_slicing, 1051 reason = "comments justify their correctness" 1052 )] 1053 fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error> 1054 where 1055 A: MapAccess<'d>, 1056 { 1057 use spki::SubjectPublicKeyInfo as _; 1058 let mut client_data = None; 1059 let mut attest = None; 1060 let mut auth = None; 1061 let mut pub_key = None; 1062 let mut key_alg = None; 1063 let mut trans = None; 1064 while let Some(key) = map.next_key::<AttestField<R>>()? { 1065 match key { 1066 AttestField::ClientDataJson => { 1067 if client_data.is_some() { 1068 return Err(Error::duplicate_field(CLIENT_DATA_JSON)); 1069 } 1070 client_data = map 1071 .next_value::<Base64DecodedVal>() 1072 .map(|c_data| Some(c_data.0))?; 1073 } 1074 AttestField::AttestationObject => { 1075 if attest.is_some() { 1076 return Err(Error::duplicate_field(ATTESTATION_OBJECT)); 1077 } 1078 attest = map.next_value::<AttObj>().map(|att_obj| Some(att_obj.0))?; 1079 } 1080 AttestField::AuthenticatorData => { 1081 if auth.is_some() { 1082 return Err(Error::duplicate_field(AUTHENTICATOR_DATA)); 1083 } 1084 auth = map.next_value::<Option<Base64DecodedVal>>().map(Some)?; 1085 } 1086 AttestField::Transports => { 1087 if trans.is_some() { 1088 return Err(Error::duplicate_field(TRANSPORTS)); 1089 } 1090 trans = map.next_value::<Option<_>>().map(Some)?; 1091 } 1092 AttestField::PublicKey => { 1093 if pub_key.is_some() { 1094 return Err(Error::duplicate_field(PUBLIC_KEY)); 1095 } 1096 pub_key = map.next_value::<Option<Base64DecodedVal>>().map(Some)?; 1097 } 1098 AttestField::PublicKeyAlgorithm => { 1099 if key_alg.is_some() { 1100 return Err(Error::duplicate_field(PUBLIC_KEY_ALGORITHM)); 1101 } 1102 key_alg = map 1103 .next_value::<Option<CoseAlgorithmIdentifier>>() 1104 .map(Some)?; 1105 } 1106 AttestField::Other => map.next_value::<IgnoredAny>().map(|_| ())?, 1107 } 1108 } 1109 // Note the order of this matters from a performance perspective. In particular `auth` must be evaluated 1110 // before `pub_key` which must be evaluated before `key_alg` as this allows us to parse the attestation 1111 // object at most once and allow us to prioritize parsing `authenticatorData` over the attestation object. 1112 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| { 1113 trans.ok_or(false).and_then(|opt_trans| opt_trans.ok_or(true)).or_else( 1114 |flag| { 1115 if R { 1116 Ok(AuthTransports::new()) 1117 } else if flag { 1118 Err(Error::invalid_type(Unexpected::Other("null"), &format!("{TRANSPORTS} to be a sequence of AuthenticatorTransports").as_str())) 1119 } else { 1120 Err(Error::missing_field(TRANSPORTS)) 1121 } 1122 }, 1123 ).and_then(|transports| { 1124 auth.ok_or(false).and_then(|opt_auth| opt_auth.ok_or(true)).as_ref().map_or_else( 1125 |flag| { 1126 if R { 1127 Ok(None) 1128 } else if *flag { 1129 Err(Error::invalid_type(Unexpected::Other("null"), &format!("{AUTHENTICATOR_DATA} to be a base64url-encoded AuthenticatorData").as_str())) 1130 } else { 1131 Err(Error::missing_field(AUTHENTICATOR_DATA)) 1132 } 1133 }, 1134 |a_data| { 1135 if a_data.0.len() > 37 { 1136 // The last portion of attestation object is always authenticator data. 1137 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| { 1138 // Indexing is fine; otherwise the above check would have returned `None`. 1139 if *a_data.0 == attestation_object[idx..] { 1140 // We know `a_data.len() > 37`; thus indexing is fine. 1141 // We start at 37 since that is the beginning of `attestedCredentialData`. 1142 // Recall the first 32 bytes are `rpIdHash`, then a 1 byte `flags`, then a 1143 // 4-byte big-endian integer `signCount`. 1144 // The starting index of `credentialId` is 18 within `attestedCredentialData`. 1145 // Recall the first 16 bytes are `aaguid`, then a 2-byte big-endian integer 1146 // `credentialIdLength`. Consequently the starting index within 1147 // `attestation_object` is `idx + 37 + 18` = `idx + 55`. Overflow cannot occur 1148 // since we successfully parsed `AttestedCredentialData`. 1149 AttestedCredentialData::from_cbor(&a_data.0[37..]).map_err(Error::custom).map(|success| Some((success.value, idx + 55))) 1150 } else { 1151 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())) 1152 } 1153 }) 1154 } else { 1155 Err(Error::invalid_value(Unexpected::Bytes(a_data.0.as_slice()), &"authenticator data to be long enough to contain attested credential data")) 1156 } 1157 } 1158 ).and_then(|attested_info| { 1159 pub_key.ok_or(false).and_then(|opt_key| opt_key.ok_or(true)).map_or_else( 1160 |flag| { 1161 if R { 1162 attested_info.as_ref().map_or(Ok(None), |&(ref attested_data, cred_id_start)| Ok(Some((match attested_data.credential_public_key { 1163 UncompressedPubKey::MlDsa87(_) => CoseAlgorithmIdentifier::Mldsa87, 1164 UncompressedPubKey::MlDsa65(_) => CoseAlgorithmIdentifier::Mldsa65, 1165 UncompressedPubKey::MlDsa44(_) => CoseAlgorithmIdentifier::Mldsa44, 1166 UncompressedPubKey::Ed25519(_) => CoseAlgorithmIdentifier::Eddsa, 1167 UncompressedPubKey::P256(_) => CoseAlgorithmIdentifier::Es256, 1168 UncompressedPubKey::P384(_) => CoseAlgorithmIdentifier::Es384, 1169 UncompressedPubKey::Rsa(_) => CoseAlgorithmIdentifier::Rs256, 1170 // Overflow won't occur since this is correct as 1171 // `AttestedCredentialData::from_cbor` would have erred if not. 1172 }, cred_id_start, cred_id_start + attested_data.credential_id.0.len())))) 1173 } else { 1174 // `publicKey` is only allowed to not exist when `CoseAlgorithmIdentifier::Eddsa`, 1175 // `CoseAlgorithmIdentifier::Es256`, or `CoseAlgorithmIdentifier::Rs256` is not 1176 // used. 1177 attested_info.as_ref().map_or_else( 1178 || AttestationObject::parse_data(attestation_object.as_slice()).map_err(Error::custom).and_then(|(att_obj, auth_idx)| { 1179 match att_obj.auth_data.attested_credential_data.credential_public_key { 1180 UncompressedPubKey::MlDsa87(_) => { 1181 // This won't overflow since `AttestationObject::parse_data` succeeded and `auth_idx` 1182 // is the start of the raw authenticator data which itself contains the raw Credential ID. 1183 Ok(Some((CoseAlgorithmIdentifier::Mldsa87, auth_idx, auth_idx + att_obj.auth_data.attested_credential_data.credential_id.0.len()))) 1184 } 1185 UncompressedPubKey::MlDsa65(_) => { 1186 // This won't overflow since `AttestationObject::parse_data` succeeded and `auth_idx` 1187 // is the start of the raw authenticator data which itself contains the raw Credential ID. 1188 Ok(Some((CoseAlgorithmIdentifier::Mldsa65, auth_idx, auth_idx + att_obj.auth_data.attested_credential_data.credential_id.0.len()))) 1189 } 1190 UncompressedPubKey::MlDsa44(_) => { 1191 // This won't overflow since `AttestationObject::parse_data` succeeded and `auth_idx` 1192 // is the start of the raw authenticator data which itself contains the raw Credential ID. 1193 Ok(Some((CoseAlgorithmIdentifier::Mldsa44, auth_idx, auth_idx + att_obj.auth_data.attested_credential_data.credential_id.0.len()))) 1194 } 1195 UncompressedPubKey::P384(_) => { 1196 // This won't overflow since `AttestationObject::parse_data` succeeded and `auth_idx` 1197 // is the start of the raw authenticator data which itself contains the raw Credential ID. 1198 Ok(Some((CoseAlgorithmIdentifier::Es384, auth_idx, auth_idx + att_obj.auth_data.attested_credential_data.credential_id.0.len()))) 1199 } 1200 UncompressedPubKey::Ed25519(_) | UncompressedPubKey::P256(_) | UncompressedPubKey::Rsa(_) => Err(Error::missing_field(PUBLIC_KEY)), 1201 } 1202 }), 1203 |&(ref attested_data, cred_id_start)| { 1204 match attested_data.credential_public_key { 1205 UncompressedPubKey::MlDsa87(_) => { 1206 // Overflow won't occur since this is correct. This is correct since we successfully parsed 1207 // `AttestedCredentialData` and calculated `cred_id_start` from it. 1208 Ok(Some((CoseAlgorithmIdentifier::Mldsa87, cred_id_start, cred_id_start + attested_data.credential_id.0.len()))) 1209 } 1210 UncompressedPubKey::MlDsa65(_) => { 1211 // Overflow won't occur since this is correct. This is correct since we successfully parsed 1212 // `AttestedCredentialData` and calculated `cred_id_start` from it. 1213 Ok(Some((CoseAlgorithmIdentifier::Mldsa65, cred_id_start, cred_id_start + attested_data.credential_id.0.len()))) 1214 } 1215 UncompressedPubKey::MlDsa44(_) => { 1216 // Overflow won't occur since this is correct. This is correct since we successfully parsed 1217 // `AttestedCredentialData` and calculated `cred_id_start` from it. 1218 Ok(Some((CoseAlgorithmIdentifier::Mldsa44, cred_id_start, cred_id_start + attested_data.credential_id.0.len()))) 1219 } 1220 UncompressedPubKey::P384(_) => { 1221 // Overflow won't occur since this is correct. This is correct since we successfully parsed 1222 // `AttestedCredentialData` and calculated `cred_id_start` from it. 1223 Ok(Some((CoseAlgorithmIdentifier::Es384, cred_id_start, cred_id_start + attested_data.credential_id.0.len()))) 1224 } 1225 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)) }, 1226 } 1227 } 1228 ) 1229 } 1230 }, 1231 |der| { 1232 UncompressedPubKey::from_der(der.0.as_slice()).map_err(Error::custom).and_then(|key| { 1233 attested_info.as_ref().map_or_else( 1234 || AttestationObject::parse_data(attestation_object.as_slice()).map_err(Error::custom).and_then(|(att_obj, auth_idx)| { 1235 if key == att_obj.auth_data.attested_credential_data.credential_public_key { 1236 let alg = match att_obj.auth_data.attested_credential_data.credential_public_key { 1237 UncompressedPubKey::MlDsa87(_) => CoseAlgorithmIdentifier::Mldsa87, 1238 UncompressedPubKey::MlDsa65(_) => CoseAlgorithmIdentifier::Mldsa65, 1239 UncompressedPubKey::MlDsa44(_) => CoseAlgorithmIdentifier::Mldsa44, 1240 UncompressedPubKey::Ed25519(_) => CoseAlgorithmIdentifier::Eddsa, 1241 UncompressedPubKey::P256(_) => CoseAlgorithmIdentifier::Es256, 1242 UncompressedPubKey::P384(_) => CoseAlgorithmIdentifier::Es384, 1243 UncompressedPubKey::Rsa(_) => CoseAlgorithmIdentifier::Rs256, 1244 }; 1245 // This won't overflow since `AttestationObject::parse_data` succeeded and `auth_idx` 1246 // is the start of the raw authenticator data which itself contains the raw Credential ID. 1247 Ok(Some((alg, auth_idx, auth_idx+ att_obj.auth_data.attested_credential_data.credential_id.0.len()))) 1248 } else { 1249 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())) 1250 } 1251 }), 1252 |&(ref attested_data, cred_id_start)| { 1253 if key == attested_data.credential_public_key { 1254 let alg = match attested_data.credential_public_key { 1255 UncompressedPubKey::MlDsa87(_) => CoseAlgorithmIdentifier::Mldsa87, 1256 UncompressedPubKey::MlDsa65(_) => CoseAlgorithmIdentifier::Mldsa65, 1257 UncompressedPubKey::MlDsa44(_) => CoseAlgorithmIdentifier::Mldsa44, 1258 UncompressedPubKey::Ed25519(_) => CoseAlgorithmIdentifier::Eddsa, 1259 UncompressedPubKey::P256(_) => CoseAlgorithmIdentifier::Es256, 1260 UncompressedPubKey::P384(_) => CoseAlgorithmIdentifier::Es384, 1261 UncompressedPubKey::Rsa(_) => CoseAlgorithmIdentifier::Rs256, 1262 }; 1263 // Overflow won't occur since this is correct. This is correct since we successfully parsed 1264 // `AttestedCredentialData` and calculated `cred_id_start` from it. 1265 Ok(Some((alg, cred_id_start, cred_id_start + attested_data.credential_id.0.len()))) 1266 } else { 1267 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())) 1268 } 1269 } 1270 ) 1271 }) 1272 } 1273 ).and_then(|cred_key_alg_cred_info| { 1274 key_alg.ok_or(false).and_then(|opt_alg| opt_alg.ok_or(true)).map_or_else( 1275 |flag| { 1276 if R { 1277 Ok(cred_key_alg_cred_info.map(|info| (info.1, info.2))) 1278 } else if flag { 1279 Err(Error::invalid_type(Unexpected::Other("null"), &format!("{PUBLIC_KEY_ALGORITHM} to be a base64url-encoded DER-encoded SubjectPublicKeyInfo").as_str())) 1280 } else { 1281 Err(Error::missing_field(PUBLIC_KEY_ALGORITHM)) 1282 } 1283 }, 1284 |alg| { 1285 cred_key_alg_cred_info.map_or_else( 1286 || AttestationObject::parse_data(attestation_object.as_slice()).map_err(Error::custom).and_then(|(att_obj, auth_idx)| { 1287 let att_obj_alg = match att_obj.auth_data.attested_credential_data.credential_public_key { 1288 UncompressedPubKey::MlDsa87(_) => CoseAlgorithmIdentifier::Mldsa87, 1289 UncompressedPubKey::MlDsa65(_) => CoseAlgorithmIdentifier::Mldsa65, 1290 UncompressedPubKey::MlDsa44(_) => CoseAlgorithmIdentifier::Mldsa44, 1291 UncompressedPubKey::Ed25519(_) => CoseAlgorithmIdentifier::Eddsa, 1292 UncompressedPubKey::P256(_) => CoseAlgorithmIdentifier::Es256, 1293 UncompressedPubKey::P384(_) => CoseAlgorithmIdentifier::Es384, 1294 UncompressedPubKey::Rsa(_) => CoseAlgorithmIdentifier::Rs256, 1295 }; 1296 if alg == att_obj_alg { 1297 // This won't overflow since `AttestationObject::parse_data` succeeded and `auth_idx` 1298 // is the start of the raw authenticator data which itself contains the raw Credential ID. 1299 Ok(Some((auth_idx, auth_idx + att_obj.auth_data.attested_credential_data.credential_id.0.len()))) 1300 } else { 1301 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())) 1302 } 1303 }), 1304 |(a, start, last)| if alg == a { 1305 Ok(Some((start, last))) 1306 } else { 1307 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())) 1308 }, 1309 ) 1310 } 1311 ).map(|cred_info| AuthAttest{ attest: AuthenticatorAttestation::new(client_data_json, attestation_object, transports), cred_info, }) 1312 }) 1313 }) 1314 }) 1315 })) 1316 } 1317 } 1318 /// `"clientDataJSON"` 1319 const CLIENT_DATA_JSON: &str = "clientDataJSON"; 1320 /// `"attestationObject"` 1321 const ATTESTATION_OBJECT: &str = "attestationObject"; 1322 /// `"authenticatorData"` 1323 const AUTHENTICATOR_DATA: &str = "authenticatorData"; 1324 /// `"transports"` 1325 const TRANSPORTS: &str = "transports"; 1326 /// `"publicKey"` 1327 const PUBLIC_KEY: &str = "publicKey"; 1328 /// `"publicKeyAlgorithm"` 1329 const PUBLIC_KEY_ALGORITHM: &str = "publicKeyAlgorithm"; 1330 /// Fields in `AuthenticatorAttestationResponseJSON`. 1331 pub(super) const AUTH_ATTEST_FIELDS: &[&str; 6] = &[ 1332 CLIENT_DATA_JSON, 1333 ATTESTATION_OBJECT, 1334 AUTHENTICATOR_DATA, 1335 TRANSPORTS, 1336 PUBLIC_KEY, 1337 PUBLIC_KEY_ALGORITHM, 1338 ]; 1339 impl<'de> Deserialize<'de> for AuthAttest { 1340 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 1341 where 1342 D: Deserializer<'de>, 1343 { 1344 deserializer.deserialize_struct( 1345 "AuthenticatorAttestation", 1346 AUTH_ATTEST_FIELDS, 1347 AuthenticatorAttestationVisitor::<false>, 1348 ) 1349 } 1350 } 1351 impl<'de> Deserialize<'de> for AuthenticatorAttestation { 1352 /// Deserializes a `struct` based on 1353 /// [`AuthenticatorAttestationResponseJSON`](https://www.w3.org/TR/webauthn-3/#dictdef-authenticatorattestationresponsejson). 1354 /// 1355 /// Note unknown keys and duplicate keys are forbidden; 1356 /// [`clientDataJSON`](https://www.w3.org/TR/webauthn-3/#dom-authenticatorattestationresponsejson-clientdatajson), 1357 /// [`authenticatorData`](https://www.w3.org/TR/webauthn-3/#dom-authenticatorattestationresponsejson-authenticatordata), 1358 /// [`publicKey`](https://www.w3.org/TR/webauthn-3/#dom-authenticatorattestationresponsejson-publickey) 1359 /// and 1360 /// [`attestationObject`](https://www.w3.org/TR/webauthn-3/#dom-authenticatorattestationresponsejson-attestationobject) 1361 /// are base64url-decoded; 1362 /// [`transports`](https://www.w3.org/TR/webauthn-3/#dom-authenticatorattestationresponsejson-transports) 1363 /// is deserialized via [`AuthTransports::deserialize`]; the decoded `publicKey` is parsed according to the 1364 /// applicable DER-encoded ASN.1 `SubjectPublicKeyInfo` schema; 1365 /// [`publicKeyAlgorithm`](https://www.w3.org/TR/webauthn-3/#dom-authenticatorattestationresponsejson-publickeyalgorithm) 1366 /// is deserialized according to 1367 /// [`CoseAlgorithmIdentifier`](https://www.w3.org/TR/webauthn-3/#typedefdef-cosealgorithmidentifier); all `required` 1368 /// fields in the `AuthenticatorAttestationResponseJSON` Web IDL `dictionary` exist (and must not be `null`); `publicKey` 1369 /// exists when Ed25519, P-256 with SHA-256, or RSASSA-PKCS1-v1_5 with SHA-256 is used (and must not be `null`) 1370 /// [per WebAuthn](https://www.w3.org/TR/webauthn-3/#sctn-public-key-easy); the `publicKeyAlgorithm` aligns 1371 /// with 1372 /// [`credentialPublicKey`](https://www.w3.org/TR/webauthn-3/#authdata-attestedcredentialdata-credentialpublickey) 1373 /// within 1374 /// [`attestedCredentialData`](https://www.w3.org/TR/webauthn-3/#authdata-attestedcredentialdata) within the 1375 /// decoded `authenticatorData`; the decoded `publicKey` is the same as `credentialPublicKey` within 1376 /// `attestedCredentialData` within the decoded `authenticatorData`; and the decoded `authenticatorData` is the 1377 /// same as [`authData`](https://www.w3.org/TR/webauthn-3/#attestation-object) within the decoded 1378 /// `attestationObject`. 1379 #[inline] 1380 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 1381 where 1382 D: Deserializer<'de>, 1383 { 1384 AuthAttest::deserialize(deserializer).map(|val| val.attest) 1385 } 1386 } 1387 /// `Visitor` for `CredentialPropertiesOutput`. 1388 /// 1389 /// Unknown fields are ignored iff `RELAXED`. 1390 pub(super) struct CredentialPropertiesOutputVisitor<const RELAXED: bool>; 1391 impl<'d, const R: bool> Visitor<'d> for CredentialPropertiesOutputVisitor<R> { 1392 type Value = CredentialPropertiesOutput; 1393 fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result { 1394 formatter.write_str("CredentialPropertiesOutput") 1395 } 1396 fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error> 1397 where 1398 A: MapAccess<'d>, 1399 { 1400 /// Allowed fields. 1401 enum Field<const IGNORE_UNKNOWN: bool> { 1402 /// `rk` field. 1403 Rk, 1404 /// Unknown field. 1405 Other, 1406 } 1407 impl<'e, const I: bool> Deserialize<'e> for Field<I> { 1408 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 1409 where 1410 D: Deserializer<'e>, 1411 { 1412 /// `Visitor` for `Field`. 1413 /// 1414 /// Unknown fields are ignored iff `IGNORE_UNKNOWN`. 1415 struct FieldVisitor<const IGNORE_UNKNOWN: bool>; 1416 impl<const IG: bool> Visitor<'_> for FieldVisitor<IG> { 1417 type Value = Field<IG>; 1418 fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result { 1419 write!(formatter, "'{RK}'") 1420 } 1421 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E> 1422 where 1423 E: Error, 1424 { 1425 match v { 1426 RK => Ok(Field::Rk), 1427 _ => { 1428 if IG { 1429 Ok(Field::Other) 1430 } else { 1431 Err(E::unknown_field(v, PROPS_FIELDS)) 1432 } 1433 } 1434 } 1435 } 1436 } 1437 deserializer.deserialize_identifier(FieldVisitor) 1438 } 1439 } 1440 let mut rk = None; 1441 while let Some(key) = map.next_key::<Field<R>>()? { 1442 match key { 1443 Field::Rk => { 1444 if rk.is_some() { 1445 return Err(Error::duplicate_field(RK)); 1446 } 1447 rk = map.next_value().map(Some)?; 1448 } 1449 Field::Other => map.next_value::<IgnoredAny>().map(|_| ())?, 1450 } 1451 } 1452 Ok(CredentialPropertiesOutput { rk: rk.flatten() }) 1453 } 1454 } 1455 /// `"rk"` 1456 const RK: &str = "rk"; 1457 /// `CredentialPropertiesOutput` fields. 1458 pub(super) const PROPS_FIELDS: &[&str; 1] = &[RK]; 1459 impl<'de> Deserialize<'de> for CredentialPropertiesOutput { 1460 /// Deserializes a `struct` based on 1461 /// [`CredentialPropertiesOutput`](https://www.w3.org/TR/webauthn-3/#dictdef-credentialpropertiesoutput). 1462 /// 1463 /// Note unknown and duplicate keys are forbidden. 1464 #[inline] 1465 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 1466 where 1467 D: Deserializer<'de>, 1468 { 1469 deserializer.deserialize_struct( 1470 "CredentialPropertiesOutput", 1471 PROPS_FIELDS, 1472 CredentialPropertiesOutputVisitor::<false>, 1473 ) 1474 } 1475 } 1476 impl<'de> Deserialize<'de> for AuthenticationExtensionsPrfOutputs { 1477 /// Deserializes a `struct` based on 1478 /// [`AuthenticationExtensionsPRFOutputsJSON`](https://www.w3.org/TR/webauthn-3/#dictdef-authenticationextensionsprfoutputsjson). 1479 /// 1480 /// Note unknown and duplicate keys are forbidden; 1481 /// [`enabled`](https://www.w3.org/TR/webauthn-3/#dom-authenticationextensionsprfoutputs-enabled) 1482 /// must exist (and not be `null`); and 1483 /// [`results`](https://www.w3.org/TR/webauthn-3/#dom-authenticationextensionsprfoutputs-results) must not exist, 1484 /// be `null`, or be an 1485 /// [`AuthenticationExtensionsPRFValues`](https://www.w3.org/TR/webauthn-3/#dictdef-authenticationextensionsprfvalues) 1486 /// with no unknown or duplicate keys, 1487 /// [`first`](https://www.w3.org/TR/webauthn-3/#dom-authenticationextensionsprfvalues-first) must exist but be 1488 /// `null`, and 1489 /// [`second`](https://www.w3.org/TR/webauthn-3/#dom-authenticationextensionsprfvalues-second) can exist but 1490 /// must be `null` if so. 1491 #[inline] 1492 #[expect(clippy::unreachable, reason = "we want to crash when there is a bug")] 1493 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 1494 where 1495 D: Deserializer<'de>, 1496 { 1497 AuthenticationExtensionsPrfOutputsHelper::<false, true, AuthenticationExtensionsPrfValues>::deserialize(deserializer).map(|val| Self { 1498 enabled: val.0.unwrap_or_else(|| { 1499 unreachable!( 1500 "there is a bug in AuthenticationExtensionsPrfOutputsHelper::deserialize" 1501 ) 1502 }), 1503 }) 1504 } 1505 } 1506 /// `Visitor` for `ClientExtensionsOutputs`. 1507 /// 1508 /// Unknown fields are ignored iff `RELAXED`. 1509 pub(super) struct ClientExtensionsOutputsVisitor<const RELAXED: bool, PROPS, PRF>( 1510 pub PhantomData<fn() -> (PROPS, PRF)>, 1511 ); 1512 impl<'d, const R: bool, C, P> Visitor<'d> for ClientExtensionsOutputsVisitor<R, C, P> 1513 where 1514 C: for<'a> Deserialize<'a> + Into<CredentialPropertiesOutput>, 1515 P: for<'a> Deserialize<'a> + Into<AuthenticationExtensionsPrfOutputs>, 1516 { 1517 type Value = ClientExtensionsOutputs; 1518 fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result { 1519 formatter.write_str("ClientExtensionsOutputs") 1520 } 1521 fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error> 1522 where 1523 A: MapAccess<'d>, 1524 { 1525 /// Allowed fields. 1526 enum Field<const IGNORE_UNKNOWN: bool> { 1527 /// `credProps` field. 1528 CredProps, 1529 /// `prf` field. 1530 Prf, 1531 /// Unknown field. 1532 Other, 1533 } 1534 impl<'e, const I: bool> Deserialize<'e> for Field<I> { 1535 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 1536 where 1537 D: Deserializer<'e>, 1538 { 1539 /// `Visitor` for `Field`. 1540 /// 1541 /// Unknown fields are ignored iff `IGNORE_UNKNOWN`. 1542 struct FieldVisitor<const IGNORE_UNKNOWN: bool>; 1543 impl<const IG: bool> Visitor<'_> for FieldVisitor<IG> { 1544 type Value = Field<IG>; 1545 fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result { 1546 write!(formatter, "'{CRED_PROPS}' or '{PRF}'") 1547 } 1548 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E> 1549 where 1550 E: Error, 1551 { 1552 match v { 1553 CRED_PROPS => Ok(Field::CredProps), 1554 PRF => Ok(Field::Prf), 1555 _ => { 1556 if IG { 1557 Ok(Field::Other) 1558 } else { 1559 Err(E::unknown_field(v, EXT_FIELDS)) 1560 } 1561 } 1562 } 1563 } 1564 } 1565 deserializer.deserialize_identifier(FieldVisitor) 1566 } 1567 } 1568 let mut cred_props = None; 1569 let mut prf = None; 1570 while let Some(key) = map.next_key::<Field<R>>()? { 1571 match key { 1572 Field::CredProps => { 1573 if cred_props.is_some() { 1574 return Err(Error::duplicate_field(CRED_PROPS)); 1575 } 1576 cred_props = map.next_value::<Option<C>>().map(Some)?; 1577 } 1578 Field::Prf => { 1579 if prf.is_some() { 1580 return Err(Error::duplicate_field(PRF)); 1581 } 1582 prf = map.next_value::<Option<P>>().map(Some)?; 1583 } 1584 Field::Other => map.next_value::<IgnoredAny>().map(|_| ())?, 1585 } 1586 } 1587 Ok(ClientExtensionsOutputs { 1588 cred_props: cred_props.flatten().map(Into::into), 1589 prf: prf.flatten().map(Into::into), 1590 }) 1591 } 1592 } 1593 impl ClientExtensions for ClientExtensionsOutputs { 1594 fn empty() -> Self { 1595 Self { 1596 prf: None, 1597 cred_props: None, 1598 } 1599 } 1600 } 1601 /// `"credProps"` 1602 const CRED_PROPS: &str = "credProps"; 1603 /// `"prf"` 1604 const PRF: &str = "prf"; 1605 /// `AuthenticationExtensionsClientOutputsJSON` fields. 1606 pub(super) const EXT_FIELDS: &[&str; 2] = &[CRED_PROPS, PRF]; 1607 impl<'de> Deserialize<'de> for ClientExtensionsOutputs { 1608 /// Deserializes a `struct` based on 1609 /// [`AuthenticationExtensionsClientOutputsJSON`](https://www.w3.org/TR/webauthn-3/#dictdef-authenticationextensionsclientoutputsjson). 1610 /// 1611 /// Note that unknown and duplicate keys are forbidden; 1612 /// [`credProps`](https://www.w3.org/TR/webauthn-3/#dom-authenticationextensionsclientoutputs-credprops) is 1613 /// `null` or deserialized via [`CredentialPropertiesOutput::deserialize`]; and 1614 /// [`prf`](https://www.w3.org/TR/webauthn-3/#dom-authenticationextensionsclientoutputs-prf) is `null` 1615 /// or deserialized via [`AuthenticationExtensionsPrfOutputs::deserialize`]. 1616 #[inline] 1617 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 1618 where 1619 D: Deserializer<'de>, 1620 { 1621 deserializer.deserialize_struct( 1622 "ClientExtensionsOutputs", 1623 EXT_FIELDS, 1624 ClientExtensionsOutputsVisitor::< 1625 false, 1626 CredentialPropertiesOutput, 1627 AuthenticationExtensionsPrfOutputs, 1628 >(PhantomData), 1629 ) 1630 } 1631 } 1632 impl<'de> Deserialize<'de> for Registration { 1633 /// Deserializes a `struct` based on 1634 /// [`RegistrationResponseJSON`](https://www.w3.org/TR/webauthn-3/#dictdef-registrationresponsejson). 1635 /// 1636 /// Note that unknown and duplicate keys are forbidden; 1637 /// [`id`](https://www.w3.org/TR/webauthn-3/#dom-registrationresponsejson-id) and 1638 /// [`rawId`](https://www.w3.org/TR/webauthn-3/#dom-registrationresponsejson-rawid) are deserialized 1639 /// via [`CredentialId::deserialize`]; 1640 /// [`response`](https://www.w3.org/TR/webauthn-3/#dom-registrationresponsejson-response) is deserialized 1641 /// via [`AuthenticatorAttestation::deserialize`]; 1642 /// [`authenticatorAttachment`](https://www.w3.org/TR/webauthn-3/#dom-registrationresponsejson-authenticatorattachment) 1643 /// is `null` or deserialized via [`AuthenticatorAttachment::deserialize`]; 1644 /// [`clientExtensionResults`](https://www.w3.org/TR/webauthn-3/#dom-registrationresponsejson-clientextensionresults) 1645 /// is deserialized via [`ClientExtensionsOutputs::deserialize`]; all `required` fields in the 1646 /// `RegistrationResponseJSON` Web IDL `dictionary` exist (and are not `null`); 1647 /// [`type`](https://www.w3.org/TR/webauthn-3/#dom-registrationresponsejson-type) is `"public-key"`; 1648 /// and the decoded `id`, decoded `rawId`, and 1649 /// [`credentialId`](https://www.w3.org/TR/webauthn-3/#authdata-attestedcredentialdata-credentialid) within 1650 /// [`attestedCredentialData`](https://www.w3.org/TR/webauthn-3/#authdata-attestedcredentialdata) within 1651 /// [`authData`](https://www.w3.org/TR/webauthn-3/#attestation-object) within the decoded 1652 /// [`attestationObject`](https://www.w3.org/TR/webauthn-3/#dom-authenticatorattestationresponsejson-attestationobject) 1653 /// are all the same. 1654 #[expect(clippy::unreachable, reason = "when there is a bug, we want to crash")] 1655 #[expect(clippy::indexing_slicing, reason = "comment justifies its correctness")] 1656 #[inline] 1657 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 1658 where 1659 D: Deserializer<'de>, 1660 { 1661 PublicKeyCredential::<false, true, AuthAttest, ClientExtensionsOutputs>::deserialize(deserializer).and_then(|cred| { 1662 let id = cred.id.unwrap_or_else(|| unreachable!("there is a bug in PublicKeyCredential::deserialize")); 1663 cred.response.cred_info.map_or_else( 1664 || AttestationObject::try_from(cred.response.attest.attestation_object()).map_err(Error::custom).and_then(|att_obj| { 1665 if id.as_ref() == att_obj.auth_data.attested_credential_data.credential_id.as_ref() { 1666 Ok(()) 1667 } else { 1668 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())) 1669 } 1670 }), 1671 // `start` and `last` were calculated based on `cred.response.attest.attestation_object()` 1672 // and represent the starting and ending index of the `CredentialId`; therefore this is correct 1673 // let alone won't `panic`. 1674 |(start, last)| if *id.0 == cred.response.attest.attestation_object()[start..last] { 1675 Ok(()) 1676 } else { 1677 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())) 1678 }, 1679 ).map(|()| Self { response: cred.response.attest, authenticator_attachment: cred.authenticator_attachment, client_extension_results: cred.client_extension_results, }) 1680 }) 1681 } 1682 }