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/// The output of authenticatorGetInfo. Some fields are hidden as they are duplicated
584/// in the metadata statement.
585#[derive(Debug, Clone, Serialize, Deserialize)]
586#[serde(deny_unknown_fields, rename_all = "camelCase")]
587pub struct AuthenticatorGetInfo {
588    /// The list of supported CTAP versions
589    pub versions: Vec<AuthenticatorVersion>,
590    /// The list of supported extension identifiers.
591    #[serde(default)]
592    pub extensions: Vec<String>,
593    /// The AAGUID (UUID) of this device
594    pub aaguid: Uuid,
595    /// List of supported Options
596    #[serde(default)]
597    pub options: BTreeMap<String, bool>,
598    /// The largest ctap message size this device supports
599    pub max_msg_size: Option<u32>,
600    /// The list of PIN UV auth protocols
601    #[serde(default)]
602    pub pin_uv_auth_protocols: Vec<u32>,
603    /// The maximum number of credentials ID's that can be listed and provided to this device.
604    pub max_credential_count_in_list: Option<u32>,
605    /// The maximum length of a credential ID that can be provided to this device.
606    pub max_credential_id_length: Option<u32>,
607    /// The list of transports this device supports
608    #[serde(default)]
609    pub transports: Vec<AuthenticatorTransport>,
610    #[serde(default)]
611    pub(crate) algorithms: Vec<serde_json::Value>,
612    /// The maximum size of large blob array this device can store, if the extension is supported.
613    pub max_serialized_large_blob_array: Option<u32>,
614    #[serde(rename = "forcePINChange")]
615    force_pin_change: Option<bool>,
616    /// The minimum pin length that this device requires.
617    #[serde(rename = "minPINLength")]
618    pub min_pin_length: Option<u32>,
619    firmware_version: Option<u32>,
620    /// The maximum size of the credBlob if supported
621    pub max_cred_blob_length: Option<u32>,
622    /// The maximum number of discoverable (resident) keys this device supports.
623    #[serde(rename = "maxRPIDsForSetMinPINLength")]
624    pub max_rpids_for_set_min_pin_length: Option<u32>,
625    /// _
626    pub preferred_platform_uv_attempts: Option<u32>,
627    uv_modality: Option<u32>,
628    #[serde(default)]
629    pub certifications: BTreeMap<String, u32>,
630    /// The number of remaining resident keys on this device.
631    pub remaining_discoverable_credentials: Option<u32>,
632    /// Vendor specific details
633    #[serde(default)]
634    pub vendor_prototype_config_commands: Vec<u32>,
635}
636
637#[derive(Debug, Clone, Serialize, Deserialize)]
638#[serde(deny_unknown_fields, rename_all = "camelCase")]
639/// A statement describing a device and it's associated properties.
640pub struct MetadataStatement {
641    /// Legal Header
642    pub legal_header: Option<String>,
643    /// The Authenticator Attestation ID. See UAFProtocol for the definition of the AAID structure.
644    /// This field must be set if the authenticator implements FIDO UAF.
645    pub aaid: Option<String>,
646    /// The Authenticator Attestation GUID. See FIDOKeyAttestation for the definition of the
647    /// AAGUID structure. This field must be set if the authenticator implements FIDO 2.
648    pub aaguid: Option<Uuid>,
649    /// A list of the attestation certificate public key identifiers encoded as hex string. This
650    /// value must be calculated according to method 1 for computing the keyIdentifier as defined
651    /// in RFC5280 section 4.2.1.2. The hex string must not contain any non-hex characters
652    /// (e.g. spaces). All hex letters must be lower case. This field must be set if neither aaid
653    /// nor aaguid are set. Setting this field implies that the attestation certificate(s) are
654    /// dedicated to a single authenticator model.
655    ///
656    /// All attestationCertificateKeyIdentifier values should be unique within the scope of the Metadata Service.
657    pub attestation_certificate_key_identifiers: Option<Vec<String>>,
658    /// A human-readable, short description of the authenticator, in English.
659    pub description: String,
660    /// A list of human-readable short descriptions of the authenticator in different languages.
661    #[serde(default)]
662    pub alternative_descriptions: BTreeMap<String, String>,
663    /// Earliest (i.e. lowest) trustworthy authenticatorVersion meeting the requirements specified
664    /// in this metadata statement.
665    ///
666    /// Adding new StatusReport entries with status UPDATE_AVAILABLE to the metadata TOC object
667    /// FIDOMetadataService must also change this authenticatorVersion if the update fixes severe
668    /// security issues, e.g. the ones reported by preceding StatusReport entries with status code
669    /// USER_VERIFICATION_BYPASS, ATTESTATION_KEY_COMPROMISE, USER_KEY_REMOTE_COMPROMISE, USER_KEY_PHYSICAL_COMPROMISE, REVOKED.
670    ///
671    /// It is recommended to assume increased risk if this version is higher (newer) than the
672    /// firmware version present in an authenticator. For example, if a StatusReport entry with
673    /// status USER_VERIFICATION_BYPASS or USER_KEY_REMOTE_COMPROMISE precedes the UPDATE_AVAILABLE
674    /// entry, than any firmware version lower (older) than the one specified in the metadata statement
675    /// is assumed to be vulnerable.
676    pub authenticator_version: u32,
677    /// The FIDO protocol family. The values "uaf", "u2f", and "fido2" are supported. If this field
678    /// is missing, the assumed protocol family is "uaf". Metadata Statements for U2F authenticators
679    /// must set the value of protocolFamily to "u2f" and FIDO 2.0/WebAuthentication Authenticator
680    /// implementations must set the value of protocolFamily to "fido2".
681    ///
682    /// ❗️ This is represented as an enum in this project to help you understand the possible values
683    /// that *may* exist. This contradicts the MDS spec, but it's better for you the consumer.
684    #[serde(default)]
685    pub protocol_family: ProtocolFamily,
686    /// Version of this structure
687    pub schema: u16,
688    /// ⚠️  WARNING - Content of this value is not well documented to it's intent or usage!
689    ///
690    /// The FIDO unified protocol version(s) (related to the specific protocol family) supported by
691    /// this authenticator. See UAFProtocol for the definition of the Version structure.
692    pub upv: Vec<Upv>,
693    /// The list of authentication algorithms supported by the authenticator. Must be set to the
694    /// complete list of the supported ALG_ constants defined in the FIDO Registry of Predefined
695    /// Values FIDORegistry if the authenticator supports multiple algorithms. Each value must be non-zero.
696    ///
697    /// ❗️ This is represented as an enum in this project to help you understand the possible values
698    /// that *may* exist. This contradicts the MDS spec, but it's better for you the consumer.
699    #[serde(default)]
700    pub authentication_algorithms: Vec<AuthenticationAlgorithm>,
701    /// The list of public key formats supported by the authenticator during registration operations.
702    /// Must be set to the complete list of the supported ALG_KEY constants defined in the FIDO Registry
703    /// of Predefined Values FIDORegistry if the authenticator model supports multiple encodings.
704    /// Because this information is not present in APIs related to authenticator discovery or policy,
705    /// a FIDO server must be prepared to accept and process any and all key representations defined
706    /// for any public key algorithm it supports. Each value must be non-zero.
707    ///
708    /// ❗️ This is represented as an enum in this project to help you understand the possible values
709    /// that *may* exist. This contradicts the MDS spec, but it's better for you the consumer.
710    #[serde(default)]
711    pub public_key_alg_and_encodings: Vec<PublicKeyAlg>,
712    /// The supported attestation type(s). (e.g. TAG_ATTESTATION_BASIC_FULL(0x3E07), TAG_ATTESTATION_BASIC_SURROGATE(0x3E08)).
713    ///
714    /// ❗️ This is represented as an enum in this project to help you understand the possible values
715    /// that *may* exist. This contradicts the MDS spec, but it's better for you the consumer.
716    pub attestation_types: Vec<AttestationType>,
717    /// A list of alternative VerificationMethodANDCombinations.
718    ///
719    /// userVerificationDetails is a two dimensional array, that informs RP what
720    /// VerificationMethodANDCombinations user may be required to perform in order to pass user
721    /// verification, e.g User need to pass fingerprint, or faceprint, or password and palm print, etc.
722    ///
723    /// The outer array is an OR over the values, and the inner arrays are ANDs. For example:
724    ///
725    ///
726    /// [
727    ///   [
728    ///      { user_verification_method: ... }
729    ///   ],
730    ///   // OR
731    ///   [
732    ///      { user_verification_method: ... },
733    ///      // AND
734    ///      { user_verification_method: ... }
735    ///   ],
736    /// ]
737    ///
738    pub user_verification_details: Vec<Vec<VerificationMethodAndCombinations>>,
739    /// The list of key protection types supported by the authenticator. Must be set to the complete
740    /// list of the supported KEY_PROTECTION_ constant case-sensitive string names.
741    ///
742    /// ❗️ This is represented as an enum in this project to help you understand the possible values
743    /// that *may* exist. This contradicts the MDS spec, but it's better for you the consumer.
744    pub key_protection: Vec<KeyProtection>,
745    /// This entry is set to true, if the Uauth private key is restricted by the authenticator to
746    /// only sign valid FIDO signature assertions.
747    ///
748    /// This entry is set to false, if the authenticator doesn't restrict the Uauth key to only
749    /// sign valid FIDO signature assertions. In this case, the calling application could potentially
750    /// get any hash value signed by the authenticator.
751    ///
752    /// If this field is missing, the assumed value is isKeyRestricted=true
753    #[serde(default = "assume_true")]
754    pub is_key_restricted: bool,
755    /// This entry is set to true, if Uauth key usage always requires a fresh user verification.
756    ///
757    /// If this field is missing, the assumed value is isFreshUserVerificationRequired=true.
758    ///
759    /// This entry is set to false, if the Uauth key can be used without requiring a fresh user
760    /// verification, e.g. without any additional user interaction, if the user was verified a
761    /// (potentially configurable) caching time ago.
762    ///
763    /// In the case of isFreshUserVerificationRequired=false, the FIDO server must verify the
764    /// registration response and/or authentication response and verify that the (maximum) caching
765    /// time (sometimes also called "authTimeout") is acceptable.
766    ///
767    /// This entry solely refers to the user verification. In the case of transaction confirmation,
768    /// the authenticator must always ask the user to authorize the specific transaction.
769    #[serde(default = "assume_true")]
770    pub is_fresh_user_verification_required: bool,
771    /// ⚠️  WARNING - Content of this value is not well documented to it's intent or usage!
772    ///
773    /// ❗️ This is represented as an enum in this project to help you understand the possible values
774    /// that *may* exist. This contradicts the MDS spec, but it's better for you the consumer.
775    pub matcher_protection: Vec<MatcherProtection>,
776    /// The authenticator's overall claimed cryptographic strength in bits (sometimes also called
777    /// security strength or security level). This is the minimum of the cryptographic strength of
778    /// all involved cryptographic methods (e.g. RNG, underlying hash, key wrapping algorithm,
779    /// signing algorithm, attestation algorithm), e.g. see FIPS180-4, FIPS186-4, FIPS198-1,
780    /// SP800-38B, SP800-38C, SP800-38D, SP800-38F, SP800-90C, SP800-90ar1, FIPS140-2 etc.
781    ///
782    /// If this value is absent, the cryptographic strength is unknown. If the cryptographic strength
783    /// of one of the involved cryptographic methods is unknown the overall claimed cryptographic
784    /// strength is also unknown.
785    pub crypto_strength: Option<u16>,
786    /// 🤔 Authors Note: Generally attachment hint is a very problematic thing to provide to browsers
787    /// as they are not always used or expected in work flows. It's likely this field is "useless"
788    /// as a result.
789    ///
790    /// A list of attachment hints that MAY be used with this device.
791    ///
792    /// ❗️ This is represented as an enum in this project to help you understand the possible values
793    /// that *may* exist. This contradicts the MDS spec, but it's better for you the consumer.
794    pub attachment_hint: Vec<AttachmentHint>,
795    /// ⚠️  WARNING - Content of this value is not well documented to it's intent or usage!
796    ///
797    /// A 16-bit number representing a combination of the bit flags defined by the
798    /// TRANSACTION_CONFIRMATION_DISPLAY constants in the FIDO Registry of Predefined Values FIDORegistry.
799    /// This value must be 0, if transaction confirmation is not supported by the authenticator.
800    pub tc_display: Vec<String>,
801    /// ⚠️  WARNING - Content of this value is not well documented to it's intent or usage!
802    ///
803    pub tc_display_content_type: Option<String>,
804    /// ⚠️  WARNING - Content of this value is not well documented to it's intent or usage!
805    ///
806    #[serde(rename = "tcDisplayPNGCharacteristics")]
807    pub tc_display_png_characteristics: Option<serde_json::Value>,
808    /// ❌ NOTE - The FIDO metadata specification defines this as a an array of base64 values, however
809    /// many values are INVALID and contain leading/trailing whitespace that MAY confuse your base64 parser
810    ///
811    /// ⚠️  WARNING - Content of this value MAY have extra leading or trailing whitespace which MAY
812    /// cause issues when parsing.
813    ///
814    /// Each element of this array represents a PKIX RFC5280 X.509 certificate that is a valid
815    /// trust anchor for this authenticator model. Multiple certificates might be used for different
816    /// batches of the same model. The array does not represent a certificate chain, but only the
817    /// trust anchor of that chain. A trust anchor can be a root certificate, an intermediate CA
818    /// certificate or even the attestation certificate itself.
819    ///
820    /// Each array element is a base64-encoded (section 4 of RFC4648), DER-encoded ITU-X690-2008
821    /// PKIX certificate value. Each element must be dedicated for authenticator attestation.
822    ///
823    // /// ❗️ This is decoded from Base64 for you so that you can directly access the DER of the certificate.
824    // attestation_root_certificates: Vec<Base64UrlSafeData>,
825    pub attestation_root_certificates: Vec<String>,
826    /// A list of trust anchors used for ECDAA attestation. This entry must be present if and only
827    /// if attestationType includes TAG_ATTESTATION_ECDAA. The entries in attestationRootCertificates
828    /// have no relevance for ECDAA attestation. Each ecdaaTrustAnchor must be dedicated to a single
829    /// authenticator model (e.g as identified by its AAID/AAGUID).
830    #[serde(default)]
831    pub ecdaa_trust_anchors: Vec<EcdaaAnchor>,
832    /// An icon representing this device.
833    pub icon: Option<serde_json::Value>,
834    /// The list of supported extensions of this authenticator
835    #[serde(default)]
836    pub supported_extensions: Vec<ExtensionDescriptor>,
837    /// Describes supported versions, extensions, AAGUID of the device and its capabilities.
838    ///
839    /// The information is the same reported by an authenticator when invoking the 'authenticatorGetInfo'
840    /// method, see FIDOCTAP.
841    pub authenticator_get_info: Option<AuthenticatorGetInfo>,
842
843    #[serde(default)]
844    /// Defines if credentials in this authenticator can be accessed on multiple
845    /// devices. If set to `unsupported` this means that it is a single device
846    /// credential.
847    pub multi_device_credential_support: MultiDeviceCredentialSupport,
848}
849
850#[derive(Debug, Clone, Default, Serialize, Deserialize)]
851#[serde(rename_all = "camelCase")]
852/// See `BiometricsStatusReport`
853pub enum BiometricModality {
854    /// No valid biometric modality was provided.
855    #[default]
856    Unknown,
857}
858
859#[derive(Debug, Clone, Serialize, Deserialize)]
860#[serde(deny_unknown_fields, rename_all = "camelCase")]
861/// A status report for the device if it provides biometrics
862pub struct BiometricsStatusReport {
863    /// ⚠️  WARNING - Content of this value is not well documented to it's intent or usage!
864    ///
865    /// Achieved level of the biometric certification of this biometric component of the authenticator
866    pub cert_level: u16,
867    /// A single a single USER_VERIFY short form case-sensitive string name constant, representing
868    /// biometric modality. See section "User Verification Methods" in FIDORegistry
869    /// (e.g. "fingerprint_internal").
870    ///
871    /// ❗️ This is represented as an enum in this project to help you understand the possible values
872    /// that *may* exist. This contradicts the MDS spec, but it's better for you the consumer.
873    #[serde(default)]
874    pub modality: BiometricModality,
875    /// ISO-8601 formatted date since when the certLevel achieved, if applicable. If no date is
876    /// given, the status is assumed to be effective while present.
877    pub effective_date: Option<String>,
878    /// ⚠️  WARNING - Content of this value is not well documented to it's intent or usage!
879    ///
880    /// Describes the externally visible aspects of the Biometric Certification evaluation.
881    ///
882    /// For example, if it states "on chip" this means the biometrics are kept inside the device only
883    pub certification_descriptor: Option<String>,
884    /// The unique identifier for the issued Biometric Certification.
885    pub certificate_number: Option<String>,
886    /// The version of the Biometric Certification Policy the implementation is Certified to, e.g. "1.0.0".
887    pub certification_policy_version: Option<String>,
888    /// The version of the Biometric Requirements FIDOBiometricsRequirements the implementation is certified to, e.g. "1.0.0".
889    pub certification_requirements_version: Option<String>,
890}
891
892/// The fido certification status of the device associated to this aaid/aaguid.
893#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
894pub enum AuthenticatorStatus {
895    /// The device is NOT fido certified
896    #[serde(rename = "NOT_FIDO_CERTIFIED")]
897    NotFidoCertified,
898    /// The divec is fido certified. This will be phased out in favour of the FIDO_CERTIFIED_L1
899    /// field.
900    #[serde(rename = "FIDO_CERTIFIED")]
901    FidoCertified,
902    /// Indicates that malware is able to bypass the user verification. This means that the
903    /// authenticator could be used without the user’s consent and potentially even without the
904    /// user’s knowledge.
905    #[serde(rename = "USER_VERIFICATION_BYPASS")]
906    UserVerificationBypass,
907    /// Indicates that an attestation key for this authenticator is known to be compromised.
908    /// The relying party SHOULD check the certificate field and use it to identify the compromised
909    /// authenticator batch. If the certificate field is not set, the relying party should reject
910    /// all new registrations of the compromised authenticator. The Authenticator manufacturer
911    /// should set the date to the date when compromise has occurred.
912    #[serde(rename = "ATTESTATION_KEY_COMPROMISE")]
913    AttestationKeyCompromise,
914    /// This authenticator has identified weaknesses that allow registered keys to be compromised
915    /// and should not be trusted. This would include both, e.g. weak entropy that causes
916    /// predictable keys to be generated or side channels that allow keys or signatures to be
917    /// forged, guessed or extracted.
918    #[serde(rename = "USER_KEY_REMOTE_COMPROMISE")]
919    UserKeyRemoteCompromise,
920    /// This authenticator has known weaknesses in its key protection mechanism(s) that allow user
921    /// keys to be extracted by an adversary in physical possession of the device.
922    #[serde(rename = "USER_KEY_PHYSICAL_COMPROMISE")]
923    UserKeyPhysicalCompromise,
924    /// A software or firmware update is available for the device. The Authenticator manufacturer
925    /// should set the url to the URL where users can obtain an update and the date the update was
926    /// published. When this status code is used, then the field authenticatorVersion in the
927    /// authenticator Metadata Statement FIDOMetadataStatement MUST be updated, if the update fixes
928    /// severe security issues, e.g. the ones reported by preceding StatusReport entries with status
929    /// code USER_VERIFICATION_BYPASS, ATTESTATION_KEY_COMPROMISE, USER_KEY_REMOTE_COMPROMISE,
930    /// USER_KEY_PHYSICAL_COMPROMISE, REVOKED. The Relying party MUST reject the Metadata Statement
931    /// if the authenticatorVersion has not increased
932    #[serde(rename = "UPDATE_AVAILABLE")]
933    UpdateAvailable,
934    /// The FIDO Alliance has determined that this authenticator should not be trusted for any
935    /// reason. For example if it is known to be a fraudulent product or contain a deliberate
936    /// backdoor. Relying parties SHOULD reject any future registration of this authenticator model.
937    #[serde(rename = "REVOKED")]
938    Revoked,
939    /// The authenticator vendor has completed and submitted the self-certification checklist to
940    /// the FIDO Alliance. If this completed checklist is publicly available, the URL will be
941    /// specified in url.
942    #[serde(rename = "SELF_ASSERTION_SUBMITTED")]
943    SelfAssertionSubmitted,
944    /// This device is certified at level 1
945    #[serde(rename = "FIDO_CERTIFIED_L1")]
946    FidoCertifiedL1,
947    /// This device is certified at level 1 plus
948    #[serde(rename = "FIDO_CERTIFIED_L1plus")]
949    FidoCertifiedL1Plus,
950    /// This device is certified at level 2
951    #[serde(rename = "FIDO_CERTIFIED_L2")]
952    FidoCertifiedL2,
953    /// This device is certified at level 2 plus
954    #[serde(rename = "FIDO_CERTIFIED_L2plus")]
955    FidoCertifiedL2Plus,
956    /// This device is certified at level 3
957    #[serde(rename = "FIDO_CERTIFIED_L3")]
958    FidoCertifiedL3,
959    /// This device is certified at level 3 plus
960    #[serde(rename = "FIDO_CERTIFIED_L3plus")]
961    FidoCertifiedL3Plus,
962}
963
964impl FromStr for AuthenticatorStatus {
965    type Err = ();
966
967    fn from_str(s: &str) -> Result<Self, Self::Err> {
968        match s.to_lowercase().as_str() {
969            "not-certified" => Ok(AuthenticatorStatus::NotFidoCertified),
970            "uv-bypass" => Ok(AuthenticatorStatus::UserVerificationBypass),
971            "key-compromise" => Ok(AuthenticatorStatus::AttestationKeyCompromise),
972            "remote-exploit" => Ok(AuthenticatorStatus::UserKeyRemoteCompromise),
973            "physical-compromise" => Ok(AuthenticatorStatus::UserKeyPhysicalCompromise),
974            "update-available" => Ok(AuthenticatorStatus::UpdateAvailable),
975            "revoked" => Ok(AuthenticatorStatus::Revoked),
976            "self-asserted" => Ok(AuthenticatorStatus::SelfAssertionSubmitted),
977            "valid" | "l1" => Ok(AuthenticatorStatus::FidoCertifiedL1),
978            "l1+" => Ok(AuthenticatorStatus::FidoCertifiedL1Plus),
979            "l2" => Ok(AuthenticatorStatus::FidoCertifiedL2),
980            "l2+" => Ok(AuthenticatorStatus::FidoCertifiedL2Plus),
981            "l3" => Ok(AuthenticatorStatus::FidoCertifiedL3),
982            "l3+" => Ok(AuthenticatorStatus::FidoCertifiedL3Plus),
983            _ => Err(()),
984        }
985    }
986}
987
988impl AuthenticatorStatus {
989    pub(crate) fn numeric(&self) -> u8 {
990        match self {
991            AuthenticatorStatus::NotFidoCertified
992            | AuthenticatorStatus::UserVerificationBypass
993            | AuthenticatorStatus::AttestationKeyCompromise
994            | AuthenticatorStatus::UserKeyRemoteCompromise
995            | AuthenticatorStatus::UserKeyPhysicalCompromise
996            | AuthenticatorStatus::UpdateAvailable
997            | AuthenticatorStatus::Revoked
998            | AuthenticatorStatus::SelfAssertionSubmitted => 0,
999            AuthenticatorStatus::FidoCertified | AuthenticatorStatus::FidoCertifiedL1 => 10,
1000            AuthenticatorStatus::FidoCertifiedL1Plus => 11,
1001            AuthenticatorStatus::FidoCertifiedL2 => 20,
1002            AuthenticatorStatus::FidoCertifiedL2Plus => 21,
1003            AuthenticatorStatus::FidoCertifiedL3 => 30,
1004            AuthenticatorStatus::FidoCertifiedL3Plus => 31,
1005        }
1006    }
1007}
1008
1009#[derive(Debug, Clone, Serialize, Deserialize)]
1010#[serde(deny_unknown_fields, rename_all = "camelCase")]
1011/// Contains an AuthenticatorStatus and additional data associated with it, if any.
1012///
1013/// The latest StatusReport entry MUST reflect the "current" status. For example, if the latest
1014/// entry has status USER_VERIFICATION_BYPASS, then it is recommended assuming an increased risk
1015/// associated with all authenticators of this AAID; if the latest entry has status UPDATE_AVAILABLE,
1016/// then the update is intended to address at least all previous issues reported in this StatusReport
1017/// dictionary.
1018pub struct StatusReport {
1019    /// The status of the authenticator.
1020    pub status: AuthenticatorStatus,
1021    /// ISO-8601 formatted date since when the status code was set, if applicable. If no date is
1022    /// given, the status is assumed to be effective while present.
1023    pub effective_date: Option<String>,
1024    /// The authenticatorVersion that this status report relates to. In the case of FIDO_CERTIFIED*
1025    /// status values, the status applies to higher authenticatorVersions until there is a new statusReport.
1026    pub authenticator_version: Option<u32>,
1027    /// ⚠️  WARNING - Content of this value is not well documented to it's intent or usage!
1028    ///
1029    /// Base64-encoded RFC4648 (not base64url!) DER ITU-X690-2008 PKIX certificate value related
1030    /// to the current status, if applicable.
1031    pub certificate: Option<String>,
1032    /// HTTPS URL where additional information may be found related to the current status, if applicable.
1033    pub url: Option<String>,
1034    /// Describes the externally visible aspects of the Authenticator Certification evaluation.
1035    ///
1036    /// For example it could state that the authenticator is a "SecurityKey based on a CC EAL 5
1037    /// certified chip hardware".
1038    pub certification_descriptor: Option<String>,
1039    /// The unique identifier for the issued Certification.
1040    pub certificate_number: Option<String>,
1041    /// ⚠️  WARNING - Content of this value is not well documented to it's intent or usage!
1042    ///
1043    /// The version of the Authenticator Certification Policy the implementation is Certified to, e.g. "1.0.0".
1044    pub certification_policy_version: Option<String>,
1045    /// The Document Version of the Authenticator Security Requirements (DV)
1046    /// FIDOAuthenticatorSecurityRequirements the implementation is certified to, e.g. "1.2.0".
1047    pub certification_requirements_version: Option<String>,
1048
1049    /// ⚠️  WARNING - CONTENT AND USE OF THIS VALUE IS NOT DOCUMENTED BY FIDO
1050    certification_profiles: Option<serde_json::Value>,
1051}
1052
1053#[derive(Debug, Clone, Serialize, Deserialize)]
1054#[serde(deny_unknown_fields, rename_all = "camelCase")]
1055/// A statement describing a device and it's associated properties.
1056pub struct FidoDevice {
1057    /// If this device is identified by an aaid, it will be listed here.
1058    pub aaid: Option<String>,
1059    /// Alternately, if the device is identified by an AAGUID it will be set here. Generally all
1060    /// FIDO2 devices will use aaguid.
1061    pub aaguid: Option<Uuid>,
1062    /// A list of the attestation certificate public key identifiers encoded as hex string. This
1063    /// value MUST be calculated according to method 1 for computing the keyIdentifier as defined
1064    /// in RFC5280 section 4.2.1.2.
1065    pub attestation_certificate_key_identifiers: Option<Vec<String>>,
1066    /// The FIDOMetadataStatement pertaining to this device.
1067    pub metadata_statement: MetadataStatement,
1068    /// ⚠️  WARNING - 2022-08-11 - no biometric status reports have been published, so we are unable
1069    /// to validate the correctness of this type and it's parser.
1070    ///
1071    /// Status of the FIDO Biometric Certification of one or more biometric components of the Authenticator
1072    #[serde(default)]
1073    pub biometric_status_reports: Vec<BiometricsStatusReport>,
1074    /// An array of status reports applicable to this authenticator.
1075    pub status_reports: Vec<StatusReport>,
1076    /// ISO-8601 formatted date since when the status report array was set to the current value.
1077    pub time_of_last_status_change: String, // iso 8601 time.
1078    /// ⚠️  WARNING - Content of this value is not well documented to it's intent or usage!
1079    ///
1080    /// URL of a list of rogue (i.e. untrusted) individual authenticators.
1081    #[serde(rename = "rogueListURL")]
1082    pub rogue_list_url: Option<String>,
1083    /// ⚠️  WARNING - Content of this value is not well documented to it's intent or usage!
1084    ///
1085    /// The hash value computed over the Base64url encoding of the UTF-8 representation of the JSON
1086    /// encoded rogueList available at rogueListURL (with type rogueListEntry). The hash algorithm
1087    /// related to the signature algorithm specified in the JWTHeader (see Metadata BLOB) MUST be used.
1088    ///
1089    /// This hash value MUST be present and non-empty whenever rogueListURL is present.
1090    pub rogue_list_hash: Option<String>,
1091}
1092
1093impl fmt::Display for FidoDevice {
1094    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1095        let s = serde_json::to_string_pretty(self).map_err(|_| fmt::Error)?;
1096        write!(f, "FidoDevice {s}")
1097    }
1098}
1099
1100/// The parsed content of the Fido Metadata Server. This content can be retrieved from it's online
1101/// url at <https://mds.fidoalliance.org/> . It's recommended you re-download this content every few weeks. This can be parsed
1102/// from it's str representation - for from_str to suceed, the metadata jwt content MUST be correctly
1103/// signed be pinned root certificate authority, the full chain is verified, and the content of the
1104/// JWT is signed correctly.
1105///
1106/// The fido metadata specification listed at <https://fidoalliance.org/specs/mds/fido-metadata-service-v3.0-ps-20210518.html>
1107/// has a number of errors (~20 deviations). When they are found, the "true implementation" of the content of the JWT
1108/// is used, rather than the specification.
1109///
1110/// Generally the use of meanings, and definitions of many fields is fuzzy at best. This makes it
1111/// harder for you to fully rely on all the content of this document and what it implies for your
1112/// use case. When a field is un-clear, a `⚠️  WARNING` will be listed.
1113#[derive(Debug, Clone, Serialize, Deserialize)]
1114#[serde(deny_unknown_fields, rename_all = "camelCase")]
1115pub struct FidoMds {
1116    /// The set of device metadata
1117    pub entries: Vec<FidoDevice>,
1118    /// legal header
1119    pub legal_header: String,
1120    /// ISO-8601 formatted date when the next update will be provided at latest.
1121    pub next_update: String,
1122    /// The serial number of this UAF Metadata BLOB Payload. Serial numbers MUST be consecutive and
1123    /// strictly monotonic, i.e. the successor BLOB will have a no value exactly incremented by one.
1124    pub no: u32,
1125}
1126
1127impl FromStr for FidoMds {
1128    type Err = JwtError;
1129
1130    fn from_str(s: &str) -> Result<Self, Self::Err> {
1131        // Setup the trusted CA store so that we can validate the authenticity of the MDS blob.
1132        let root_ca = x509::X509::from_pem(GLOBAL_SIGN_ROOT_CA_R3.as_bytes())
1133            .map_err(|_| JwtError::OpenSSLError)?;
1134
1135        let jws = JwsCompact::from_str(s)?;
1136
1137        let fullchain = jws
1138            .get_x5c_chain()
1139            .and_then(|chain| chain.ok_or(JwtError::InvalidHeaderFormat))?;
1140
1141        let verifier = JwsX509VerifierBuilder::new()
1142            .add_fullchain(fullchain)
1143            .add_trust_root(root_ca)
1144            .build()
1145            .map_err(|_| JwtError::OpenSSLError)?;
1146
1147        // Now we can release the embedded cert, since we have asserted the trust in the chain
1148        // that has signed this metadata.
1149        let released = verifier.verify(&jws)?;
1150
1151        let metadata: FidoMds = released.from_json().map_err(|serde_err| {
1152            tracing::error!(?serde_err);
1153            JwtError::Serde
1154        })?;
1155
1156        // trace!(?metadata);
1157
1158        Ok(metadata)
1159    }
1160}