Skip to main content

fido_mds/
mds.rs

1//! An implementation of the types for the fido metadata service as defined by
2//! <https://fidoalliance.org/specs/mds/fido-metadata-service-v3.0-ps-20210518.html>
3//!
4//! This allows parsing the fido metadata blob and consuming it's content. See `FidoMds`
5//! for more.
6
7use compact_jwt::{crypto::JwsX509VerifierBuilder, JwsCompact, JwsVerifier, JwtError};
8use openssl::x509;
9use serde::{Deserialize, Serialize};
10use std::fmt;
11use std::str::FromStr;
12
13use std::collections::BTreeMap;
14use std::hash::{Hash, Hasher};
15use uuid::Uuid;
16
17static GLOBAL_SIGN_ROOT_CA_R3: &str = r#"
18-----BEGIN CERTIFICATE-----
19MIIDXzCCAkegAwIBAgILBAAAAAABIVhTCKIwDQYJKoZIhvcNAQELBQAwTDEgMB4G
20A1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjMxEzARBgNVBAoTCkdsb2JhbFNp
21Z24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMDkwMzE4MTAwMDAwWhcNMjkwMzE4
22MTAwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMzETMBEG
23A1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCASIwDQYJKoZI
24hvcNAQEBBQADggEPADCCAQoCggEBAMwldpB5BngiFvXAg7aEyiie/QV2EcWtiHL8
25RgJDx7KKnQRfJMsuS+FggkbhUqsMgUdwbN1k0ev1LKMPgj0MK66X17YUhhB5uzsT
26gHeMCOFJ0mpiLx9e+pZo34knlTifBtc+ycsmWQ1z3rDI6SYOgxXG71uL0gRgykmm
27KPZpO/bLyCiR5Z2KYVc3rHQU3HTgOu5yLy6c+9C7v/U9AOEGM+iCK65TpjoWc4zd
28QQ4gOsC0p6Hpsk+QLjJg6VfLuQSSaGjlOCZgdbKfd/+RFO+uIEn8rUAVSNECMWEZ
29XriX7613t2Saer9fwRPvm2L7DWzgVGkWqQPabumDk3F2xmmFghcCAwEAAaNCMEAw
30DgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFI/wS3+o
31LkUkrk1Q+mOai97i3Ru8MA0GCSqGSIb3DQEBCwUAA4IBAQBLQNvAUKr+yAzv95ZU
32RUm7lgAJQayzE4aGKAczymvmdLm6AC2upArT9fHxD4q/c2dKg8dEe3jgr25sbwMp
33jjM5RcOO5LlXbKr8EpbsU8Yt5CRsuZRj+9xTaGdWPoO4zzUhw8lo/s7awlOqzJCK
346fBdRoyV3XpYKBovHd7NADdBj+1EbddTKJd+82cEHhXXipa0095MJ6RMG3NzdvQX
35mcIfeg7jLQitChws/zyrVQ4PkX4268NXSb7hLi18YIvDQVETI53O9zJrlAGomecs
36Mx86OyXShkDOOyyGeMlhLxS67ttVb9+E7gUJTb0o2HLO02JQZR7rkpeDMdmztcpH
37WD9f
38-----END CERTIFICATE-----
39"#;
40
41// https://fidoalliance.org/specs/mds/fido-metadata-service-v3.0-ps-20210518.html#metadata-blob-format
42
43fn assume_true() -> bool {
44    true
45}
46
47#[derive(Debug, Clone, Serialize, Deserialize)]
48#[serde(deny_unknown_fields, rename_all = "camelCase")]
49/// Unclear
50pub struct Upv {
51    /// Major
52    pub major: u16,
53    /// Minor
54    pub minor: u16,
55}
56
57#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Hash)]
58#[serde(deny_unknown_fields, rename_all = "camelCase")]
59/// The CodeAccuracyDescriptor describes the relevant accuracy/complexity aspects of passcode user verification methods.
60pub struct CodeAccuracyDescriptor {
61    /// ⚠️  WARNING - Content of this value is not well documented to it's intent or usage!
62    ///
63    /// The numeric system base (radix) of the code, e.g. 10 in the case of decimal digits.
64    pub base: u16,
65    /// The minimum number of digits of the given base required for that code, e.g. 4 in the case of 4 digits.
66    pub min_length: u16,
67    /// Maximum number of false attempts before the authenticator will block this method (at least
68    /// for some time). 0 means it will never block.
69    pub max_retries: Option<u16>,
70    /// Enforced minimum number of seconds wait time after blocking (e.g. due to forced reboot or
71    /// similar). 0 means this user verification method will be blocked, either permanently or until
72    /// an alternative user verification method method succeeded. All alternative user verification
73    /// methods must be specified appropriately in the Metadata in userVerificationDetails.
74    pub block_slowdown: Option<u16>,
75}
76
77#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
78#[serde(deny_unknown_fields, rename_all = "camelCase")]
79/// The BiometricAccuracyDescriptor describes relevant accuracy/complexity aspects in the case of a biometric user verification method.
80pub struct BiometricAccuracyDescriptor {
81    /// The false rejection rate [ISOIEC-19795-1] for a single template, i.e. the percentage of
82    /// verification transactions with truthful claims of identity that are incorrectly denied. For
83    /// example a FRR of 10% would be encoded as 0.1.
84    ///
85    /// This value is self attested and, if the authenticator passed biometric certification, the
86    /// data is an independently verified FRR as measured when meeting the FRR target specified in
87    /// the biometric certification requirements FIDOBiometricsRequirements for the indicated
88    /// biometric certification level (see certLevel in related biometricStatusReport as specified
89    /// in FIDOMetadataService).
90    #[serde(rename = "selfAttestedFRR")]
91    pub self_attested_frr: Option<f32>,
92    /// The false acceptance rate ISOIEC-19795-1 for a single template, i.e. the percentage of
93    /// verification transactions with wrongful claims of identity that are incorrectly confirmed.
94    /// For example a FAR of 0.002% would be encoded as 0.00002.
95    ///
96    /// This value is self attested and, if the authenticator passed biometric certification, the
97    /// data is an independently verified FAR specified in the biometric certification requirements
98    /// FIDOBiometricsRequirements for the indicated biomeric certification level (see certLevel
99    /// in related biometricStatusReport as specified in FIDOMetadataService).
100    #[serde(rename = "selfAttestedFAR")]
101    pub self_attested_far: Option<f32>,
102
103    /// Maximum number of alternative templates from different fingers allowed (for other modalities,
104    /// multiple parts of the body that can be used interchangeably), e.g. 3 if the user is allowed
105    /// to enroll up to 3 different fingers to a fingerprint based authenticator.
106    pub max_templates: Option<u16>,
107    /// Maximum number of false attempts before the authenticator will block this method (at least
108    /// for some time). 0 means it will never block.
109    pub max_retries: Option<u16>,
110    /// Enforced minimum number of seconds wait time after blocking (e.g. due to forced reboot or
111    /// similar). 0 means that this user verification method will be blocked either permanently or
112    /// until an alternative user verification method succeeded. All alternative user verification
113    /// methods must be specified appropriately in the metadata in userVerificationDetails.
114    pub block_slowdown: Option<u16>,
115
116    /// ⚠️  WARNING - CONTENT AND USE OF THIS VALUE IS NOT DOCUMENTED BY FIDO
117    #[serde(rename = "iAPARThreshold")]
118    pub iapar_threshold: Option<serde_json::Value>,
119}
120
121impl Hash for BiometricAccuracyDescriptor {
122    fn hash<H: Hasher>(&self, state: &mut H) {
123        self.self_attested_frr
124            .map(|f| f.to_le_bytes())
125            .unwrap_or([0; 4])
126            .hash(state);
127        self.self_attested_far
128            .map(|f| f.to_le_bytes())
129            .unwrap_or([0; 4])
130            .hash(state);
131        self.max_templates.hash(state);
132        self.max_retries.hash(state);
133        self.block_slowdown.hash(state);
134    }
135}
136
137#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
138#[serde(deny_unknown_fields, rename_all = "camelCase")]
139/// The PatternAccuracyDescriptor describes relevant accuracy/complexity aspects in the case that a
140/// pattern is used as the user verification method.
141pub struct PatternAccuracyDescriptor {
142    /// ❌ NOTE - The FIDO metadata values for this value are broken and can not be parsed.
143    ///
144    /// Number of possible patterns (having the minimum length) out of which exactly one would be
145    /// the right one, i.e. 1/probability in the case of equal distribution.
146    pub min_complexity: serde_json::Value,
147    /// Maximum number of false attempts before the authenticator will block authentication using
148    /// this method (at least temporarily). 0 means it will never block.
149    pub max_retries: Option<u16>,
150    /// Enforced minimum number of seconds wait time after blocking (due to forced reboot or similar
151    /// mechanism). 0 means this user verification method will be blocked, either permanently or
152    /// until an alternative user verification method method succeeded. All alternative user
153    /// verification methods must be specified appropriately in the metadata under
154    /// userVerificationDetails.
155    pub block_slowdown: Option<u16>,
156}
157
158impl Hash for PatternAccuracyDescriptor {
159    fn hash<H: Hasher>(&self, state: &mut H) {
160        self.max_retries
161            .map(|f| f.to_le_bytes())
162            .unwrap_or([0; 2])
163            .hash(state);
164        self.block_slowdown
165            .map(|f| f.to_le_bytes())
166            .unwrap_or([0; 2])
167            .hash(state);
168    }
169}
170
171/// User Verification Methods
172#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
173pub enum UserVerificationMethod {
174    /// None
175    #[serde(rename = "none")]
176    None,
177    /// All. MUST NOT APPEAR IN ANY UVM.
178    #[serde(rename = "all")]
179    All,
180    /// presence_internal
181    #[serde(rename = "presence_internal")]
182    PresenceInternal,
183    /// passcode_internal
184    #[serde(rename = "passcode_internal")]
185    PasscodeInternal,
186    /// passcode_external
187    #[serde(rename = "passcode_external")]
188    PasscodeExternal,
189    /// fingerprint_internal
190    #[serde(rename = "fingerprint_internal")]
191    FingerprintInternal,
192    /// handprint_internal
193    #[serde(rename = "handprint_internal")]
194    HandprintInternal,
195    /// eyeprint_internal
196    #[serde(rename = "eyeprint_internal")]
197    EyeprintInternal,
198    /// pattern_internal
199    #[serde(rename = "pattern_internal")]
200    PatternInternal,
201    /// voiceprint_internal
202    #[serde(rename = "voiceprint_internal")]
203    VoiceprintInternal,
204    /// location_internal
205    #[serde(rename = "location_internal")]
206    LocationInternal,
207    /// faceprint_internal
208    #[serde(rename = "faceprint_internal")]
209    FaceprintInternal,
210}
211
212#[derive(Debug, Clone, Serialize, Deserialize)]
213#[serde(deny_unknown_fields, rename_all = "camelCase")]
214/// A descriptor for a specific base user verification method as implemented by the authenticator.
215pub struct VerificationMethodAndCombinations {
216    /// a single USER_VERIFY constant case-sensitive string name. See section "User Verification
217    /// Methods" in FIDORegistry (e.g. "presence_internal"). This value MUST NOT be empty.
218    pub user_verification_method: UserVerificationMethod,
219    /// May optionally be used in the case of method USER_VERIFY_PASSCODE_INTERNAL or USER_VERIFY_PASSCODE_EXTERNAL.
220    pub ca_desc: Option<CodeAccuracyDescriptor>,
221    /// May optionally be used in the case of method USER_VERIFY_FINGERPRINT_INTERNAL,
222    /// USER_VERIFY_VOICEPRINT_INTERNAL, USER_VERIFY_FACEPRINT_INTERNAL,
223    /// USER_VERIFY_EYEPRINT_INTERNAL, or USER_VERIFY_HANDPRINT_INTERNAL.
224    pub ba_desc: Option<BiometricAccuracyDescriptor>,
225    /// May optionally be used in case of method USER_VERIFY_PATTERN_INTERNAL or USER_VERIFY_PATTERN_EXTERNAL
226    pub pa_desc: Option<PatternAccuracyDescriptor>,
227}
228
229#[derive(Debug, Clone, Serialize, Deserialize)]
230#[serde(deny_unknown_fields, rename_all = "camelCase")]
231/// In the case of ECDAA attestation, the ECDAA-Issuer's trust anchor must be specified in this field.
232pub struct EcdaaAnchor {
233    /// base64url encoding of the result of ECPoint2ToB of the ECPoint2 X = P_2^xX=P
234    /// See FIDOEcdaaAlgorithm for the definition of ECPoint2ToB.
235    #[serde(rename = "X")]
236    pub x: String,
237    /// base64url encoding of the result of ECPoint2ToB of the ECPoint2 Y = P_2^yY=P
238    /// See FIDOEcdaaAlgorithm for the definition of ECPoint2ToB.
239    #[serde(rename = "Y")]
240    pub y: String,
241    /// base64url encoding of the result of BigNumberToB(cc). See section "Issuer Specific ECDAA Parameters" in FIDOEcdaaAlgorithm for an explanation of cc. See FIDOEcdaaAlgorithm for the definition of BigNumberToB.
242    pub c: String,
243    /// base64url encoding of the result of BigNumberToB(sxsx). See section "Issuer Specific ECDAA Parameters" in FIDOEcdaaAlgorithm for an explanation of sxsx. See FIDOEcdaaAlgorithm for the definition of BigNumberToB.
244    pub sx: String,
245    /// base64url encoding of the result of BigNumberToB(sysy). See section "Issuer Specific ECDAA Parameters" in FIDOEcdaaAlgorithm for an explanation of sysy. See FIDOEcdaaAlgorithm for the definition of BigNumberToB.
246    pub sy: String,
247    /// Name of the Barreto-Naehrig elliptic curve for G1. "BN_P256", "BN_P638", "BN_ISOP256", and "BN_ISOP512" are supported. See section "Supported Curves for ECDAA" in FIDOEcdaaAlgorithm for details.
248    #[serde(rename = "G1Curve")]
249    pub g1curve: String,
250}
251
252#[derive(Debug, Clone, Serialize, Deserialize)]
253#[serde(deny_unknown_fields)]
254/// This descriptor contains an extension supported by the authenticator.
255pub struct ExtensionDescriptor {
256    /// Identifies the extension.
257    pub id: String,
258    /// The TAG of the extension if this was assigned. TAGs are assigned to extensions if they could appear in an assertion.
259    pub tag: Option<u16>,
260    /// Contains arbitrary data further describing the extension and/or data needed to correctly process the extension.
261    pub data: Option<String>,
262    /// Indicates whether unknown extensions must be ignored (false) or must lead to an error (true)
263    /// when the extension is to be processed by the FIDO Server, FIDO Client, ASM, or FIDO Authenticator.
264    ///
265    /// A value of false indicates that unknown extensions must be ignored
266    ///
267    /// A value of true indicates that unknown extensions must result in an error.
268    pub fail_if_unknown: bool,
269}
270
271/// The assertion scheme in use by this device.
272#[derive(Debug, Clone, Default, Serialize, Deserialize)]
273#[serde(deny_unknown_fields)]
274pub enum AssertionScheme {
275    /// No assertion scheme was provided
276    #[default]
277    Unknown,
278    /// Fido 2
279    #[serde(rename = "FIDOV2")]
280    FidoV2,
281}
282
283/// The family of protocols this device belongs to.
284#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)]
285#[serde(deny_unknown_fields)]
286pub enum ProtocolFamily {
287    /// No protocol family was provided
288    #[default]
289    Unknown,
290    /// Uaf
291    #[serde(rename = "uaf")]
292    Uaf,
293    /// Universal Second Factor
294    #[serde(rename = "u2f")]
295    U2f,
296    /// Fido 2. This is the preferred type
297    #[serde(rename = "fido2")]
298    Fido2,
299}
300
301#[derive(Debug, Clone, Serialize, Deserialize)]
302#[serde(deny_unknown_fields)]
303/// The list of authentication algorithms supported by the authenticator.
304pub enum AuthenticationAlgorithm {
305    /// secp256r1_ecdsa_sha256_raw
306    #[serde(rename = "secp256r1_ecdsa_sha256_raw")]
307    Secp256r1EcdsaSha256Raw,
308    /// secp256r1_ecdsa_sha256_der
309    #[serde(rename = "secp256r1_ecdsa_sha256_der")]
310    Secp256r1EcdsaSha256Der,
311    /// secp256k1_ecdsa_sha256_raw
312    #[serde(rename = "secp256k1_ecdsa_sha256_raw")]
313    Secp256K1EcdsaSha256Raw,
314    /// rsa_emsa_pkcs1_sha256_raw
315    #[serde(rename = "rsa_emsa_pkcs1_sha256_raw")]
316    RsaEmsaPkcs1Sha256Raw,
317    /// ed25519_eddsa_sha512_raw
318    #[serde(rename = "ed25519_eddsa_sha512_raw")]
319    Ed25519EddsaSha512Raw,
320    /// secp384r1_ecdsa_sha384_raw
321    #[serde(rename = "secp384r1_ecdsa_sha384_raw")]
322    Secp384r1EcdsaSha384Raw,
323    /// secp521r1_ecdsa_sha512_raw
324    #[serde(rename = "secp521r1_ecdsa_sha512_raw")]
325    Secp521r1EcdsaSha512Raw,
326    /// rsassa_pkcsv15_sha256_raw
327    #[serde(rename = "rsassa_pkcsv15_sha256_raw")]
328    RsassaPkcsv15Sha256Raw,
329    /// rsassa_pkcsv15_sha1_raw
330    #[serde(rename = "rsassa_pkcsv15_sha1_raw")]
331    RsassaPkcsv15Sha1Raw,
332    /// rsassa_pss_sha256_raw
333    #[serde(rename = "rsassa_pss_sha256_raw")]
334    RsassaPssSha256Raw,
335}
336
337impl fmt::Display for AuthenticationAlgorithm {
338    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
339        match self {
340            AuthenticationAlgorithm::Secp256r1EcdsaSha256Raw => {
341                write!(f, "secp256r1_ecdsa_sha256_raw")
342            }
343            AuthenticationAlgorithm::Secp256r1EcdsaSha256Der => {
344                write!(f, "secp256r1_ecdsa_sha256_der")
345            }
346            AuthenticationAlgorithm::Secp256K1EcdsaSha256Raw => {
347                write!(f, "secp256k1_ecdsa_sha256_raw")
348            }
349            AuthenticationAlgorithm::RsaEmsaPkcs1Sha256Raw => {
350                write!(f, "rsa_emsa_pkcs1_sha256_raw")
351            }
352            AuthenticationAlgorithm::Ed25519EddsaSha512Raw => write!(f, "ed25519_eddsa_sha512_raw"),
353            AuthenticationAlgorithm::Secp384r1EcdsaSha384Raw => {
354                write!(f, "secp384r1_ecdsa_sha384_raw")
355            }
356            AuthenticationAlgorithm::Secp521r1EcdsaSha512Raw => {
357                write!(f, "secp521r1_ecdsa_sha512_raw")
358            }
359            AuthenticationAlgorithm::RsassaPkcsv15Sha256Raw => {
360                write!(f, "rsassa_pkcsv15_sha256_raw")
361            }
362            AuthenticationAlgorithm::RsassaPkcsv15Sha1Raw => {
363                write!(f, "rsassa_pkcsv15_sha1_raw")
364            }
365            AuthenticationAlgorithm::RsassaPssSha256Raw => {
366                write!(f, "rsassa_pss_sha256_raw")
367            }
368        }
369    }
370}
371
372#[derive(Debug, Clone, Serialize, Deserialize)]
373#[serde(deny_unknown_fields)]
374/// The public key format used by the authenticator during registration operations.
375pub enum PublicKeyAlg {
376    /// ecc_x962_raw
377    #[serde(rename = "ecc_x962_raw")]
378    EccX962Raw,
379    /// ecc_x962_der
380    #[serde(rename = "ecc_x962_der")]
381    EccX962Der,
382    /// rsa_2048_raw
383    #[serde(rename = "rsa_2048_raw")]
384    Rsa2048Raw,
385    /// cose
386    #[serde(rename = "cose")]
387    Cose,
388}
389
390#[derive(Debug, Clone, Serialize, Deserialize)]
391#[serde(deny_unknown_fields)]
392/// A type of attestation
393pub enum AttestationType {
394    /// basic_full
395    #[serde(rename = "basic_full")]
396    BasicFull,
397    /// basic_surrogate
398    #[serde(rename = "basic_surrogate")]
399    BasicSurrogate,
400    /// attca
401    #[serde(rename = "attca")]
402    AttCa,
403}
404
405#[derive(Debug, Clone, Serialize, Deserialize)]
406#[serde(deny_unknown_fields)]
407/// The class of key protection.
408pub enum KeyProtection {
409    /// The key is stored in hardware. This is exclusive to `software`
410    #[serde(rename = "hardware")]
411    Hardware,
412    /// Secure Element
413    #[serde(rename = "secure_element")]
414    SecureElement,
415    /// The private key is stored in a key-wrapped-key. This can still be "hardware" backed
416    /// where the KWK can only be decrypted by a specific device's hardware.
417    #[serde(rename = "remote_handle")]
418    RemoteHandle,
419    /// Trusted Execution Environment.
420    #[serde(rename = "tee")]
421    Tee,
422    /// The key is stored in software. This is exclusive to `hardware`
423    #[serde(rename = "software")]
424    Software,
425}
426
427/// ⚠️  WARNING - Content of this value is not well documented to it's intent or usage!
428///
429#[derive(Debug, Clone, Serialize, Deserialize)]
430#[serde(deny_unknown_fields)]
431pub enum MatcherProtection {
432    /// on_chip
433    #[serde(rename = "on_chip")]
434    OnChip,
435    /// tee
436    #[serde(rename = "tee")]
437    Tee,
438    /// software
439    #[serde(rename = "software")]
440    Software,
441}
442
443/// Attachment hint
444#[derive(Debug, Clone, Serialize, Deserialize)]
445#[serde(deny_unknown_fields)]
446pub enum AttachmentHint {
447    /// external
448    #[serde(rename = "external")]
449    External,
450    /// wired
451    #[serde(rename = "wired")]
452    Wired,
453    /// wireless
454    #[serde(rename = "wireless")]
455    Wireless,
456    /// nfc
457    #[serde(rename = "nfc")]
458    Nfc,
459    /// internal
460    #[serde(rename = "internal")]
461    Internal,
462    /// bluetooth
463    #[serde(rename = "bluetooth")]
464    Bluetooth,
465    /// network
466    #[serde(rename = "network")]
467    Network,
468    /// wifi-direct
469    #[serde(rename = "wifi_direct")]
470    WifiDirect,
471}
472
473/// The authenticator versions this device supports
474#[derive(Debug, Clone, Serialize, Deserialize)]
475pub enum AuthenticatorVersion {
476    /// U2F
477    #[serde(rename = "U2F_V2")]
478    U2fV2,
479    /// FIDO 2.0
480    #[serde(rename = "FIDO_2_0")]
481    Fido2_0,
482    /// FIDO 2.1 PRE
483    #[serde(rename = "FIDO_2_1_PRE")]
484    Fido2_1Pre,
485    /// FIDO 2.1
486    #[serde(rename = "FIDO_2_1")]
487    Fido2_1,
488}
489
490impl fmt::Display for AuthenticatorVersion {
491    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
492        match self {
493            AuthenticatorVersion::U2fV2 => write!(f, "U2F V2"),
494            AuthenticatorVersion::Fido2_0 => write!(f, "FIDO 2.0"),
495            AuthenticatorVersion::Fido2_1Pre => write!(f, "FIDO 2.1 PRE"),
496            AuthenticatorVersion::Fido2_1 => write!(f, "FIDO 2.1"),
497        }
498    }
499}
500
501/// The authenticator transports that this device supports
502#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
503pub enum AuthenticatorTransport {
504    /// usb
505    #[serde(rename = "usb")]
506    Usb,
507    /// nfc
508    #[serde(rename = "nfc")]
509    Nfc,
510    /// lightning
511    #[serde(rename = "lightning")]
512    Lightning,
513    /// ble
514    #[serde(rename = "ble")]
515    Ble,
516    /// internal
517    #[serde(rename = "internal")]
518    Internal,
519    /// wireless
520    #[serde(rename = "wireless")]
521    Wireless,
522    /// hybrid (formerly caBLE)
523    #[serde(rename = "hybrid")]
524    Hybrid,
525}
526
527impl fmt::Display for AuthenticatorTransport {
528    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
529        match self {
530            AuthenticatorTransport::Usb => write!(f, "usb"),
531            AuthenticatorTransport::Nfc => write!(f, "nfc"),
532            AuthenticatorTransport::Lightning => write!(f, "lightning"),
533            AuthenticatorTransport::Ble => write!(f, "ble"),
534            AuthenticatorTransport::Internal => write!(f, "internal"),
535            AuthenticatorTransport::Wireless => write!(f, "wireless"),
536            AuthenticatorTransport::Hybrid => write!(f, "hybrid (caBLE)"),
537        }
538    }
539}
540
541impl FromStr for AuthenticatorTransport {
542    type Err = ();
543
544    fn from_str(s: &str) -> Result<Self, Self::Err> {
545        match s {
546            "usb" => Ok(AuthenticatorTransport::Usb),
547            "nfc" => Ok(AuthenticatorTransport::Nfc),
548            "lightning" => Ok(AuthenticatorTransport::Lightning),
549            "ble" => Ok(AuthenticatorTransport::Ble),
550            "internal" => Ok(AuthenticatorTransport::Internal),
551            "wireless" => Ok(AuthenticatorTransport::Wireless),
552            _ => Err(()),
553        }
554    }
555}
556
557/// Describes this device's capability to allow credentials to be
558/// accessible to a single device, or multiple devices.
559#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
560#[serde(rename_all = "lowercase")]
561pub enum MultiDeviceCredentialSupport {
562    /// Multiple devices are not supported, this credential is bound to one
563    /// device.
564    #[default]
565    Unsupported,
566    /// The authenticator will provide its multiple device support during
567    /// registration through setting the flag "backup eligible".
568    Explicit,
569    /// This authenticator always is multi device
570    Implicit,
571}
572
573impl fmt::Display for MultiDeviceCredentialSupport {
574    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
575        match self {
576            Self::Unsupported => write!(f, "unsupported (no)"),
577            Self::Explicit => write!(f, "explicit (see backup_eligible flag)"),
578            Self::Implicit => write!(f, "implicit (yes)"),
579        }
580    }
581}
582
583#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
584#[serde(rename_all = "lowercase")]
585pub enum AttestationFormat {
586    Packed,
587}
588
589/// The output of authenticatorGetInfo. Some fields are hidden as they are duplicated
590/// in the metadata statement.
591#[derive(Debug, Clone, Serialize, Deserialize)]
592#[serde(deny_unknown_fields, rename_all = "camelCase")]
593pub struct AuthenticatorGetInfo {
594    /// The list of supported CTAP versions
595    pub versions: Vec<AuthenticatorVersion>,
596    /// The list of supported extension identifiers.
597    #[serde(default)]
598    pub extensions: Vec<String>,
599    /// The AAGUID (UUID) of this device
600    pub aaguid: Uuid,
601    /// List of supported Options
602    #[serde(default)]
603    pub options: BTreeMap<String, bool>,
604    /// The largest ctap message size this device supports
605    pub max_msg_size: Option<u32>,
606    /// The list of PIN UV auth protocols
607    #[serde(default)]
608    pub pin_uv_auth_protocols: Vec<u32>,
609    /// The maximum number of credentials ID's that can be listed and provided to this device.
610    pub max_credential_count_in_list: Option<u32>,
611    /// The maximum length of a credential ID that can be provided to this device.
612    pub max_credential_id_length: Option<u32>,
613    /// The list of transports this device supports
614    #[serde(default)]
615    pub transports: Vec<AuthenticatorTransport>,
616    #[serde(default)]
617    pub(crate) algorithms: Vec<serde_json::Value>,
618    /// The maximum size of large blob array this device can store, if the extension is supported.
619    pub max_serialized_large_blob_array: Option<u32>,
620    #[serde(rename = "forcePINChange")]
621    force_pin_change: Option<bool>,
622    /// The minimum pin length that this device requires.
623    #[serde(rename = "minPINLength")]
624    pub min_pin_length: Option<u32>,
625    firmware_version: Option<u32>,
626    /// The maximum size of the credBlob if supported
627    pub max_cred_blob_length: Option<u32>,
628    /// The maximum number of discoverable (resident) keys this device supports.
629    #[serde(rename = "maxRPIDsForSetMinPINLength")]
630    pub max_rpids_for_set_min_pin_length: Option<u32>,
631    /// _
632    pub preferred_platform_uv_attempts: Option<u32>,
633    uv_modality: Option<u32>,
634    #[serde(default)]
635    pub certifications: BTreeMap<String, u32>,
636    /// The number of remaining resident keys on this device.
637    pub remaining_discoverable_credentials: Option<u32>,
638    /// Vendor specific details
639    #[serde(default)]
640    pub vendor_prototype_config_commands: Vec<u32>,
641
642    /// Supported attestation formats
643    #[serde(default)]
644    pub attestation_formats: Vec<AttestationFormat>,
645}
646
647#[derive(Debug, Clone, Serialize, Deserialize)]
648#[serde(deny_unknown_fields, rename_all = "camelCase")]
649/// A statement describing a device and it's associated properties.
650pub struct MetadataStatement {
651    /// Legal Header
652    pub legal_header: Option<String>,
653    /// The Authenticator Attestation ID. See UAFProtocol for the definition of the AAID structure.
654    /// This field must be set if the authenticator implements FIDO UAF.
655    pub aaid: Option<String>,
656    /// The Authenticator Attestation GUID. See FIDOKeyAttestation for the definition of the
657    /// AAGUID structure. This field must be set if the authenticator implements FIDO 2.
658    pub aaguid: Option<Uuid>,
659    /// A list of the attestation certificate public key identifiers encoded as hex string. This
660    /// value must be calculated according to method 1 for computing the keyIdentifier as defined
661    /// in RFC5280 section 4.2.1.2. The hex string must not contain any non-hex characters
662    /// (e.g. spaces). All hex letters must be lower case. This field must be set if neither aaid
663    /// nor aaguid are set. Setting this field implies that the attestation certificate(s) are
664    /// dedicated to a single authenticator model.
665    ///
666    /// All attestationCertificateKeyIdentifier values should be unique within the scope of the Metadata Service.
667    pub attestation_certificate_key_identifiers: Option<Vec<String>>,
668    /// A human-readable, short description of the authenticator, in English.
669    pub description: String,
670    /// A list of human-readable short descriptions of the authenticator in different languages.
671    #[serde(default)]
672    pub alternative_descriptions: BTreeMap<String, String>,
673
674    /// A list of friendly names describing the device.
675    pub friendly_names: BTreeMap<String, String>,
676
677    /// Earliest (i.e. lowest) trustworthy authenticatorVersion meeting the requirements specified
678    /// in this metadata statement.
679    ///
680    /// Adding new StatusReport entries with status UPDATE_AVAILABLE to the metadata TOC object
681    /// FIDOMetadataService must also change this authenticatorVersion if the update fixes severe
682    /// security issues, e.g. the ones reported by preceding StatusReport entries with status code
683    /// USER_VERIFICATION_BYPASS, ATTESTATION_KEY_COMPROMISE, USER_KEY_REMOTE_COMPROMISE, USER_KEY_PHYSICAL_COMPROMISE, REVOKED.
684    ///
685    /// It is recommended to assume increased risk if this version is higher (newer) than the
686    /// firmware version present in an authenticator. For example, if a StatusReport entry with
687    /// status USER_VERIFICATION_BYPASS or USER_KEY_REMOTE_COMPROMISE precedes the UPDATE_AVAILABLE
688    /// entry, than any firmware version lower (older) than the one specified in the metadata statement
689    /// is assumed to be vulnerable.
690    pub authenticator_version: u32,
691    /// The FIDO protocol family. The values "uaf", "u2f", and "fido2" are supported. If this field
692    /// is missing, the assumed protocol family is "uaf". Metadata Statements for U2F authenticators
693    /// must set the value of protocolFamily to "u2f" and FIDO 2.0/WebAuthentication Authenticator
694    /// implementations must set the value of protocolFamily to "fido2".
695    ///
696    /// ❗️ This is represented as an enum in this project to help you understand the possible values
697    /// that *may* exist. This contradicts the MDS spec, but it's better for you the consumer.
698    #[serde(default)]
699    pub protocol_family: ProtocolFamily,
700    /// Version of this structure
701    pub schema: u16,
702    /// ⚠️  WARNING - Content of this value is not well documented to it's intent or usage!
703    ///
704    /// The FIDO unified protocol version(s) (related to the specific protocol family) supported by
705    /// this authenticator. See UAFProtocol for the definition of the Version structure.
706    pub upv: Vec<Upv>,
707    /// The list of authentication algorithms supported by the authenticator. Must be set to the
708    /// complete list of the supported ALG_ constants defined in the FIDO Registry of Predefined
709    /// Values FIDORegistry if the authenticator supports multiple algorithms. Each value must be non-zero.
710    ///
711    /// ❗️ This is represented as an enum in this project to help you understand the possible values
712    /// that *may* exist. This contradicts the MDS spec, but it's better for you the consumer.
713    #[serde(default)]
714    pub authentication_algorithms: Vec<AuthenticationAlgorithm>,
715    /// The list of public key formats supported by the authenticator during registration operations.
716    /// Must be set to the complete list of the supported ALG_KEY constants defined in the FIDO Registry
717    /// of Predefined Values FIDORegistry if the authenticator model supports multiple encodings.
718    /// Because this information is not present in APIs related to authenticator discovery or policy,
719    /// a FIDO server must be prepared to accept and process any and all key representations defined
720    /// for any public key algorithm it supports. Each value must be non-zero.
721    ///
722    /// ❗️ This is represented as an enum in this project to help you understand the possible values
723    /// that *may* exist. This contradicts the MDS spec, but it's better for you the consumer.
724    #[serde(default)]
725    pub public_key_alg_and_encodings: Vec<PublicKeyAlg>,
726    /// The supported attestation type(s). (e.g. TAG_ATTESTATION_BASIC_FULL(0x3E07), TAG_ATTESTATION_BASIC_SURROGATE(0x3E08)).
727    ///
728    /// ❗️ This is represented as an enum in this project to help you understand the possible values
729    /// that *may* exist. This contradicts the MDS spec, but it's better for you the consumer.
730    pub attestation_types: Vec<AttestationType>,
731    /// A list of alternative VerificationMethodANDCombinations.
732    ///
733    /// userVerificationDetails is a two dimensional array, that informs RP what
734    /// VerificationMethodANDCombinations user may be required to perform in order to pass user
735    /// verification, e.g User need to pass fingerprint, or faceprint, or password and palm print, etc.
736    ///
737    /// The outer array is an OR over the values, and the inner arrays are ANDs. For example:
738    ///
739    ///
740    /// [
741    ///   [
742    ///      { user_verification_method: ... }
743    ///   ],
744    ///   // OR
745    ///   [
746    ///      { user_verification_method: ... },
747    ///      // AND
748    ///      { user_verification_method: ... }
749    ///   ],
750    /// ]
751    ///
752    pub user_verification_details: Vec<Vec<VerificationMethodAndCombinations>>,
753    /// The list of key protection types supported by the authenticator. Must be set to the complete
754    /// list of the supported KEY_PROTECTION_ constant case-sensitive string names.
755    ///
756    /// ❗️ This is represented as an enum in this project to help you understand the possible values
757    /// that *may* exist. This contradicts the MDS spec, but it's better for you the consumer.
758    pub key_protection: Vec<KeyProtection>,
759    /// This entry is set to true, if the Uauth private key is restricted by the authenticator to
760    /// only sign valid FIDO signature assertions.
761    ///
762    /// This entry is set to false, if the authenticator doesn't restrict the Uauth key to only
763    /// sign valid FIDO signature assertions. In this case, the calling application could potentially
764    /// get any hash value signed by the authenticator.
765    ///
766    /// If this field is missing, the assumed value is isKeyRestricted=true
767    #[serde(default = "assume_true")]
768    pub is_key_restricted: bool,
769    /// This entry is set to true, if Uauth key usage always requires a fresh user verification.
770    ///
771    /// If this field is missing, the assumed value is isFreshUserVerificationRequired=true.
772    ///
773    /// This entry is set to false, if the Uauth key can be used without requiring a fresh user
774    /// verification, e.g. without any additional user interaction, if the user was verified a
775    /// (potentially configurable) caching time ago.
776    ///
777    /// In the case of isFreshUserVerificationRequired=false, the FIDO server must verify the
778    /// registration response and/or authentication response and verify that the (maximum) caching
779    /// time (sometimes also called "authTimeout") is acceptable.
780    ///
781    /// This entry solely refers to the user verification. In the case of transaction confirmation,
782    /// the authenticator must always ask the user to authorize the specific transaction.
783    #[serde(default = "assume_true")]
784    pub is_fresh_user_verification_required: bool,
785    /// ⚠️  WARNING - Content of this value is not well documented to it's intent or usage!
786    ///
787    /// ❗️ This is represented as an enum in this project to help you understand the possible values
788    /// that *may* exist. This contradicts the MDS spec, but it's better for you the consumer.
789    pub matcher_protection: Vec<MatcherProtection>,
790    /// The authenticator's overall claimed cryptographic strength in bits (sometimes also called
791    /// security strength or security level). This is the minimum of the cryptographic strength of
792    /// all involved cryptographic methods (e.g. RNG, underlying hash, key wrapping algorithm,
793    /// signing algorithm, attestation algorithm), e.g. see FIPS180-4, FIPS186-4, FIPS198-1,
794    /// SP800-38B, SP800-38C, SP800-38D, SP800-38F, SP800-90C, SP800-90ar1, FIPS140-2 etc.
795    ///
796    /// If this value is absent, the cryptographic strength is unknown. If the cryptographic strength
797    /// of one of the involved cryptographic methods is unknown the overall claimed cryptographic
798    /// strength is also unknown.
799    pub crypto_strength: Option<u16>,
800    /// 🤔 Authors Note: Generally attachment hint is a very problematic thing to provide to browsers
801    /// as they are not always used or expected in work flows. It's likely this field is "useless"
802    /// as a result.
803    ///
804    /// A list of attachment hints that MAY be used with this device.
805    ///
806    /// ❗️ This is represented as an enum in this project to help you understand the possible values
807    /// that *may* exist. This contradicts the MDS spec, but it's better for you the consumer.
808    pub attachment_hint: Vec<AttachmentHint>,
809    /// ⚠️  WARNING - Content of this value is not well documented to it's intent or usage!
810    ///
811    /// A 16-bit number representing a combination of the bit flags defined by the
812    /// TRANSACTION_CONFIRMATION_DISPLAY constants in the FIDO Registry of Predefined Values FIDORegistry.
813    /// This value must be 0, if transaction confirmation is not supported by the authenticator.
814    pub tc_display: Vec<String>,
815    /// ⚠️  WARNING - Content of this value is not well documented to it's intent or usage!
816    ///
817    pub tc_display_content_type: Option<String>,
818    /// ⚠️  WARNING - Content of this value is not well documented to it's intent or usage!
819    ///
820    #[serde(rename = "tcDisplayPNGCharacteristics")]
821    pub tc_display_png_characteristics: Option<serde_json::Value>,
822    /// ❌ NOTE - The FIDO metadata specification defines this as a an array of base64 values, however
823    /// many values are INVALID and contain leading/trailing whitespace that MAY confuse your base64 parser
824    ///
825    /// ⚠️  WARNING - Content of this value MAY have extra leading or trailing whitespace which MAY
826    /// cause issues when parsing.
827    ///
828    /// Each element of this array represents a PKIX RFC5280 X.509 certificate that is a valid
829    /// trust anchor for this authenticator model. Multiple certificates might be used for different
830    /// batches of the same model. The array does not represent a certificate chain, but only the
831    /// trust anchor of that chain. A trust anchor can be a root certificate, an intermediate CA
832    /// certificate or even the attestation certificate itself.
833    ///
834    /// Each array element is a base64-encoded (section 4 of RFC4648), DER-encoded ITU-X690-2008
835    /// PKIX certificate value. Each element must be dedicated for authenticator attestation.
836    ///
837    // /// ❗️ This is decoded from Base64 for you so that you can directly access the DER of the certificate.
838    // attestation_root_certificates: Vec<Base64UrlSafeData>,
839    pub attestation_root_certificates: Vec<String>,
840    /// A list of trust anchors used for ECDAA attestation. This entry must be present if and only
841    /// if attestationType includes TAG_ATTESTATION_ECDAA. The entries in attestationRootCertificates
842    /// have no relevance for ECDAA attestation. Each ecdaaTrustAnchor must be dedicated to a single
843    /// authenticator model (e.g as identified by its AAID/AAGUID).
844    #[serde(default)]
845    pub ecdaa_trust_anchors: Vec<EcdaaAnchor>,
846    /// An icon representing this device.
847    pub icon: Option<serde_json::Value>,
848    /// The list of supported extensions of this authenticator
849    #[serde(default)]
850    pub supported_extensions: Vec<ExtensionDescriptor>,
851    /// Describes supported versions, extensions, AAGUID of the device and its capabilities.
852    ///
853    /// The information is the same reported by an authenticator when invoking the 'authenticatorGetInfo'
854    /// method, see FIDOCTAP.
855    pub authenticator_get_info: Option<AuthenticatorGetInfo>,
856
857    #[serde(default)]
858    /// Defines if credentials in this authenticator can be accessed on multiple
859    /// devices. If set to `unsupported` this means that it is a single device
860    /// credential.
861    pub multi_device_credential_support: MultiDeviceCredentialSupport,
862}
863
864#[derive(Debug, Clone, Default, Serialize, Deserialize)]
865#[serde(rename_all = "camelCase")]
866/// See `BiometricsStatusReport`
867pub enum BiometricModality {
868    /// No valid biometric modality was provided.
869    #[default]
870    Unknown,
871}
872
873#[derive(Debug, Clone, Serialize, Deserialize)]
874#[serde(deny_unknown_fields, rename_all = "camelCase")]
875/// A status report for the device if it provides biometrics
876pub struct BiometricsStatusReport {
877    /// ⚠️  WARNING - Content of this value is not well documented to it's intent or usage!
878    ///
879    /// Achieved level of the biometric certification of this biometric component of the authenticator
880    pub cert_level: u16,
881    /// A single a single USER_VERIFY short form case-sensitive string name constant, representing
882    /// biometric modality. See section "User Verification Methods" in FIDORegistry
883    /// (e.g. "fingerprint_internal").
884    ///
885    /// ❗️ This is represented as an enum in this project to help you understand the possible values
886    /// that *may* exist. This contradicts the MDS spec, but it's better for you the consumer.
887    #[serde(default)]
888    pub modality: BiometricModality,
889    /// ISO-8601 formatted date since when the certLevel achieved, if applicable. If no date is
890    /// given, the status is assumed to be effective while present.
891    pub effective_date: Option<String>,
892    /// ⚠️  WARNING - Content of this value is not well documented to it's intent or usage!
893    ///
894    /// Describes the externally visible aspects of the Biometric Certification evaluation.
895    ///
896    /// For example, if it states "on chip" this means the biometrics are kept inside the device only
897    pub certification_descriptor: Option<String>,
898    /// The unique identifier for the issued Biometric Certification.
899    pub certificate_number: Option<String>,
900    /// The version of the Biometric Certification Policy the implementation is Certified to, e.g. "1.0.0".
901    pub certification_policy_version: Option<String>,
902    /// The version of the Biometric Requirements FIDOBiometricsRequirements the implementation is certified to, e.g. "1.0.0".
903    pub certification_requirements_version: Option<String>,
904}
905
906/// The fido certification status of the device associated to this aaid/aaguid.
907#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
908pub enum AuthenticatorStatus {
909    /// The device is NOT fido certified
910    #[serde(rename = "NOT_FIDO_CERTIFIED")]
911    NotFidoCertified,
912    /// The divec is fido certified. This will be phased out in favour of the FIDO_CERTIFIED_L1
913    /// field.
914    #[serde(rename = "FIDO_CERTIFIED")]
915    FidoCertified,
916    /// Indicates that malware is able to bypass the user verification. This means that the
917    /// authenticator could be used without the user’s consent and potentially even without the
918    /// user’s knowledge.
919    #[serde(rename = "USER_VERIFICATION_BYPASS")]
920    UserVerificationBypass,
921    /// Indicates that an attestation key for this authenticator is known to be compromised.
922    /// The relying party SHOULD check the certificate field and use it to identify the compromised
923    /// authenticator batch. If the certificate field is not set, the relying party should reject
924    /// all new registrations of the compromised authenticator. The Authenticator manufacturer
925    /// should set the date to the date when compromise has occurred.
926    #[serde(rename = "ATTESTATION_KEY_COMPROMISE")]
927    AttestationKeyCompromise,
928    /// This authenticator has identified weaknesses that allow registered keys to be compromised
929    /// and should not be trusted. This would include both, e.g. weak entropy that causes
930    /// predictable keys to be generated or side channels that allow keys or signatures to be
931    /// forged, guessed or extracted.
932    #[serde(rename = "USER_KEY_REMOTE_COMPROMISE")]
933    UserKeyRemoteCompromise,
934    /// This authenticator has known weaknesses in its key protection mechanism(s) that allow user
935    /// keys to be extracted by an adversary in physical possession of the device.
936    #[serde(rename = "USER_KEY_PHYSICAL_COMPROMISE")]
937    UserKeyPhysicalCompromise,
938    /// A software or firmware update is available for the device. The Authenticator manufacturer
939    /// should set the url to the URL where users can obtain an update and the date the update was
940    /// published. When this status code is used, then the field authenticatorVersion in the
941    /// authenticator Metadata Statement FIDOMetadataStatement MUST be updated, if the update fixes
942    /// severe security issues, e.g. the ones reported by preceding StatusReport entries with status
943    /// code USER_VERIFICATION_BYPASS, ATTESTATION_KEY_COMPROMISE, USER_KEY_REMOTE_COMPROMISE,
944    /// USER_KEY_PHYSICAL_COMPROMISE, REVOKED. The Relying party MUST reject the Metadata Statement
945    /// if the authenticatorVersion has not increased
946    #[serde(rename = "UPDATE_AVAILABLE")]
947    UpdateAvailable,
948    /// The FIDO Alliance has determined that this authenticator should not be trusted for any
949    /// reason. For example if it is known to be a fraudulent product or contain a deliberate
950    /// backdoor. Relying parties SHOULD reject any future registration of this authenticator model.
951    #[serde(rename = "REVOKED")]
952    Revoked,
953    /// The authenticator vendor has completed and submitted the self-certification checklist to
954    /// the FIDO Alliance. If this completed checklist is publicly available, the URL will be
955    /// specified in url.
956    #[serde(rename = "SELF_ASSERTION_SUBMITTED")]
957    SelfAssertionSubmitted,
958    /// This device is certified at level 1
959    #[serde(rename = "FIDO_CERTIFIED_L1")]
960    FidoCertifiedL1,
961    /// This device is certified at level 1 plus
962    #[serde(rename = "FIDO_CERTIFIED_L1plus")]
963    FidoCertifiedL1Plus,
964    /// This device is certified at level 2
965    #[serde(rename = "FIDO_CERTIFIED_L2")]
966    FidoCertifiedL2,
967    /// This device is certified at level 2 plus
968    #[serde(rename = "FIDO_CERTIFIED_L2plus")]
969    FidoCertifiedL2Plus,
970    /// This device is certified at level 3
971    #[serde(rename = "FIDO_CERTIFIED_L3")]
972    FidoCertifiedL3,
973    /// This device is certified at level 3 plus
974    #[serde(rename = "FIDO_CERTIFIED_L3plus")]
975    FidoCertifiedL3Plus,
976}
977
978impl FromStr for AuthenticatorStatus {
979    type Err = ();
980
981    fn from_str(s: &str) -> Result<Self, Self::Err> {
982        match s.to_lowercase().as_str() {
983            "not-certified" => Ok(AuthenticatorStatus::NotFidoCertified),
984            "uv-bypass" => Ok(AuthenticatorStatus::UserVerificationBypass),
985            "key-compromise" => Ok(AuthenticatorStatus::AttestationKeyCompromise),
986            "remote-exploit" => Ok(AuthenticatorStatus::UserKeyRemoteCompromise),
987            "physical-compromise" => Ok(AuthenticatorStatus::UserKeyPhysicalCompromise),
988            "update-available" => Ok(AuthenticatorStatus::UpdateAvailable),
989            "revoked" => Ok(AuthenticatorStatus::Revoked),
990            "self-asserted" => Ok(AuthenticatorStatus::SelfAssertionSubmitted),
991            "valid" | "l1" => Ok(AuthenticatorStatus::FidoCertifiedL1),
992            "l1+" => Ok(AuthenticatorStatus::FidoCertifiedL1Plus),
993            "l2" => Ok(AuthenticatorStatus::FidoCertifiedL2),
994            "l2+" => Ok(AuthenticatorStatus::FidoCertifiedL2Plus),
995            "l3" => Ok(AuthenticatorStatus::FidoCertifiedL3),
996            "l3+" => Ok(AuthenticatorStatus::FidoCertifiedL3Plus),
997            _ => Err(()),
998        }
999    }
1000}
1001
1002impl AuthenticatorStatus {
1003    pub(crate) fn numeric(&self) -> u8 {
1004        match self {
1005            AuthenticatorStatus::NotFidoCertified
1006            | AuthenticatorStatus::UserVerificationBypass
1007            | AuthenticatorStatus::AttestationKeyCompromise
1008            | AuthenticatorStatus::UserKeyRemoteCompromise
1009            | AuthenticatorStatus::UserKeyPhysicalCompromise
1010            | AuthenticatorStatus::UpdateAvailable
1011            | AuthenticatorStatus::Revoked
1012            | AuthenticatorStatus::SelfAssertionSubmitted => 0,
1013            AuthenticatorStatus::FidoCertified | AuthenticatorStatus::FidoCertifiedL1 => 10,
1014            AuthenticatorStatus::FidoCertifiedL1Plus => 11,
1015            AuthenticatorStatus::FidoCertifiedL2 => 20,
1016            AuthenticatorStatus::FidoCertifiedL2Plus => 21,
1017            AuthenticatorStatus::FidoCertifiedL3 => 30,
1018            AuthenticatorStatus::FidoCertifiedL3Plus => 31,
1019        }
1020    }
1021}
1022
1023#[derive(Debug, Clone, Serialize, Deserialize)]
1024#[serde(deny_unknown_fields, rename_all = "camelCase")]
1025/// Contains an AuthenticatorStatus and additional data associated with it, if any.
1026///
1027/// The latest StatusReport entry MUST reflect the "current" status. For example, if the latest
1028/// entry has status USER_VERIFICATION_BYPASS, then it is recommended assuming an increased risk
1029/// associated with all authenticators of this AAID; if the latest entry has status UPDATE_AVAILABLE,
1030/// then the update is intended to address at least all previous issues reported in this StatusReport
1031/// dictionary.
1032pub struct StatusReport {
1033    /// The status of the authenticator.
1034    pub status: AuthenticatorStatus,
1035    /// ISO-8601 formatted date since when the status code was set, if applicable. If no date is
1036    /// given, the status is assumed to be effective while present.
1037    pub effective_date: Option<String>,
1038    /// The authenticatorVersion that this status report relates to. In the case of FIDO_CERTIFIED*
1039    /// status values, the status applies to higher authenticatorVersions until there is a new statusReport.
1040    pub authenticator_version: Option<u32>,
1041    /// ⚠️  WARNING - Content of this value is not well documented to it's intent or usage!
1042    ///
1043    /// Base64-encoded RFC4648 (not base64url!) DER ITU-X690-2008 PKIX certificate value related
1044    /// to the current status, if applicable.
1045    pub certificate: Option<String>,
1046    /// HTTPS URL where additional information may be found related to the current status, if applicable.
1047    pub url: Option<String>,
1048    /// Describes the externally visible aspects of the Authenticator Certification evaluation.
1049    ///
1050    /// For example it could state that the authenticator is a "SecurityKey based on a CC EAL 5
1051    /// certified chip hardware".
1052    pub certification_descriptor: Option<String>,
1053    /// The unique identifier for the issued Certification.
1054    pub certificate_number: Option<String>,
1055    /// ⚠️  WARNING - Content of this value is not well documented to it's intent or usage!
1056    ///
1057    /// The version of the Authenticator Certification Policy the implementation is Certified to, e.g. "1.0.0".
1058    pub certification_policy_version: Option<String>,
1059    /// The Document Version of the Authenticator Security Requirements (DV)
1060    /// FIDOAuthenticatorSecurityRequirements the implementation is certified to, e.g. "1.2.0".
1061    pub certification_requirements_version: Option<String>,
1062
1063    /// ⚠️  WARNING - CONTENT AND USE OF THIS VALUE IS NOT DOCUMENTED BY FIDO
1064    certification_profiles: Option<serde_json::Value>,
1065}
1066
1067#[derive(Debug, Clone, Serialize, Deserialize)]
1068#[serde(deny_unknown_fields, rename_all = "camelCase")]
1069/// A statement describing a device and it's associated properties.
1070pub struct FidoDevice {
1071    /// If this device is identified by an aaid, it will be listed here.
1072    pub aaid: Option<String>,
1073    /// Alternately, if the device is identified by an AAGUID it will be set here. Generally all
1074    /// FIDO2 devices will use aaguid.
1075    pub aaguid: Option<Uuid>,
1076    /// A list of the attestation certificate public key identifiers encoded as hex string. This
1077    /// value MUST be calculated according to method 1 for computing the keyIdentifier as defined
1078    /// in RFC5280 section 4.2.1.2.
1079    pub attestation_certificate_key_identifiers: Option<Vec<String>>,
1080    /// The FIDOMetadataStatement pertaining to this device.
1081    pub metadata_statement: MetadataStatement,
1082    /// ⚠️  WARNING - 2022-08-11 - no biometric status reports have been published, so we are unable
1083    /// to validate the correctness of this type and it's parser.
1084    ///
1085    /// Status of the FIDO Biometric Certification of one or more biometric components of the Authenticator
1086    #[serde(default)]
1087    pub biometric_status_reports: Vec<BiometricsStatusReport>,
1088    /// An array of status reports applicable to this authenticator.
1089    pub status_reports: Vec<StatusReport>,
1090    /// ISO-8601 formatted date since when the status report array was set to the current value.
1091    pub time_of_last_status_change: String, // iso 8601 time.
1092    /// ⚠️  WARNING - Content of this value is not well documented to it's intent or usage!
1093    ///
1094    /// URL of a list of rogue (i.e. untrusted) individual authenticators.
1095    #[serde(rename = "rogueListURL")]
1096    pub rogue_list_url: Option<String>,
1097    /// ⚠️  WARNING - Content of this value is not well documented to it's intent or usage!
1098    ///
1099    /// The hash value computed over the Base64url encoding of the UTF-8 representation of the JSON
1100    /// encoded rogueList available at rogueListURL (with type rogueListEntry). The hash algorithm
1101    /// related to the signature algorithm specified in the JWTHeader (see Metadata BLOB) MUST be used.
1102    ///
1103    /// This hash value MUST be present and non-empty whenever rogueListURL is present.
1104    pub rogue_list_hash: Option<String>,
1105}
1106
1107impl fmt::Display for FidoDevice {
1108    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1109        let s = serde_json::to_string_pretty(self).map_err(|_| fmt::Error)?;
1110        write!(f, "FidoDevice {s}")
1111    }
1112}
1113
1114/// The parsed content of the Fido Metadata Server. This content can be retrieved from it's online
1115/// url at <https://mds.fidoalliance.org/> . It's recommended you re-download this content every few weeks. This can be parsed
1116/// from it's str representation - for from_str to suceed, the metadata jwt content MUST be correctly
1117/// signed be pinned root certificate authority, the full chain is verified, and the content of the
1118/// JWT is signed correctly.
1119///
1120/// The fido metadata specification listed at <https://fidoalliance.org/specs/mds/fido-metadata-service-v3.0-ps-20210518.html>
1121/// has a number of errors (~20 deviations). When they are found, the "true implementation" of the content of the JWT
1122/// is used, rather than the specification.
1123///
1124/// Generally the use of meanings, and definitions of many fields is fuzzy at best. This makes it
1125/// harder for you to fully rely on all the content of this document and what it implies for your
1126/// use case. When a field is un-clear, a `⚠️  WARNING` will be listed.
1127#[derive(Debug, Clone, Serialize, Deserialize)]
1128#[serde(deny_unknown_fields, rename_all = "camelCase")]
1129pub struct FidoMds {
1130    /// The set of device metadata
1131    pub entries: Vec<FidoDevice>,
1132    /// legal header
1133    pub legal_header: String,
1134    /// ISO-8601 formatted date when the next update will be provided at latest.
1135    pub next_update: String,
1136    /// The serial number of this UAF Metadata BLOB Payload. Serial numbers MUST be consecutive and
1137    /// strictly monotonic, i.e. the successor BLOB will have a no value exactly incremented by one.
1138    pub no: u32,
1139}
1140
1141impl FromStr for FidoMds {
1142    type Err = JwtError;
1143
1144    fn from_str(s: &str) -> Result<Self, Self::Err> {
1145        // Setup the trusted CA store so that we can validate the authenticity of the MDS blob.
1146        let root_ca = x509::X509::from_pem(GLOBAL_SIGN_ROOT_CA_R3.as_bytes())
1147            .map_err(|_| JwtError::OpenSSLError)?;
1148
1149        let jws = JwsCompact::from_str(s)?;
1150
1151        let fullchain = jws
1152            .get_x5c_chain()
1153            .and_then(|chain| chain.ok_or(JwtError::InvalidHeaderFormat))?;
1154
1155        let verifier = JwsX509VerifierBuilder::new()
1156            .add_fullchain(fullchain)
1157            .add_trust_root(root_ca)
1158            .build()
1159            .map_err(|_| JwtError::OpenSSLError)?;
1160
1161        // Now we can release the embedded cert, since we have asserted the trust in the chain
1162        // that has signed this metadata.
1163        let released = verifier.verify(&jws)?;
1164
1165        let metadata: FidoMds = released.from_json().map_err(|serde_err| {
1166            tracing::error!(?serde_err);
1167            JwtError::Serde
1168        })?;
1169
1170        // trace!(?metadata);
1171
1172        Ok(metadata)
1173    }
1174}