Repository: go-webauthn/webauthn
Pull request #454 (2025-07-18 12:15:15)

feat(metadata): update schema to 3.1


Patch:
diff --git a/metadata/const.go b/metadata/const.go
index 76e6a33..4853ec8 100644
--- a/metadata/const.go
+++ b/metadata/const.go
@@ -10,7 +10,7 @@ const (
 	// https://mds3.fido.tools/pki/MDS3ROOT.crt
 	ConformanceMDSRoot = "MIICaDCCAe6gAwIBAgIPBCqih0DiJLW7+UHXx/o1MAoGCCqGSM49BAMDMGcxCzAJBgNVBAYTAlVTMRYwFAYDVQQKDA1GSURPIEFsbGlhbmNlMScwJQYDVQQLDB5GQUtFIE1ldGFkYXRhIDMgQkxPQiBST09UIEZBS0UxFzAVBgNVBAMMDkZBS0UgUm9vdCBGQUtFMB4XDTE3MDIwMTAwMDAwMFoXDTQ1MDEzMTIzNTk1OVowZzELMAkGA1UEBhMCVVMxFjAUBgNVBAoMDUZJRE8gQWxsaWFuY2UxJzAlBgNVBAsMHkZBS0UgTWV0YWRhdGEgMyBCTE9CIFJPT1QgRkFLRTEXMBUGA1UEAwwORkFLRSBSb290IEZBS0UwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAASKYiz3YltC6+lmxhPKwA1WFZlIqnX8yL5RybSLTKFAPEQeTD9O6mOz+tg8wcSdnVxHzwnXiQKJwhrav70rKc2ierQi/4QUrdsPes8TEirZOkCVJurpDFbXZOgs++pa4XmjYDBeMAsGA1UdDwQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBQGcfeCs0Y8D+lh6U5B2xSrR74eHTAfBgNVHSMEGDAWgBQGcfeCs0Y8D+lh6U5B2xSrR74eHTAKBggqhkjOPQQDAwNoADBlAjEA/xFsgri0xubSa3y3v5ormpPqCwfqn9s0MLBAtzCIgxQ/zkzPKctkiwoPtDzI51KnAjAmeMygX2S5Ht8+e+EQnezLJBJXtnkRWY+Zt491wgt/AwSs5PHHMv5QgjELOuMxQBc="
 
-	// Example from https://fidoalliance.org/specs/mds/fido-metadata-service-v3.0-ps-20210518.html
+	// Example from https://fidoalliance.org/specs/mds/fido-metadata-service-v3.1-ps-20250521.html#sctn-examples
 	ExampleMDSRoot = "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"
 )
 
diff --git a/metadata/decode.go b/metadata/decode.go
index 7d4b351..47e74c4 100644
--- a/metadata/decode.go
+++ b/metadata/decode.go
@@ -45,7 +45,8 @@ type Decoder struct {
 	ignoreEntryParsingErrors bool
 }
 
-// Parse handles parsing of the raw JSON values of the metadata blob. Should be used after using Decode or DecodeBytes.
+// Parse handles parsing of the raw JSON values of the metadata blob. Should be used after using [Decoder.Decode] or
+// [Decoder.DecodeBytes].
 func (d *Decoder) Parse(payload *PayloadJSON) (metadata *Metadata, err error) {
 	metadata = &Metadata{
 		Parsed: Parsed{
@@ -80,7 +81,7 @@ func (d *Decoder) Parse(payload *PayloadJSON) (metadata *Metadata, err error) {
 	return metadata, nil
 }
 
-// Decode the blob from an io.Reader. This function will close the io.ReadCloser after completing.
+// Decode the blob from an [io.Reader]. This function will close the [io.ReadCloser] after completing.
 func (d *Decoder) Decode(r io.Reader) (payload *PayloadJSON, err error) {
 	bytes, err := io.ReadAll(r)
 	if err != nil {
@@ -90,7 +91,7 @@ func (d *Decoder) Decode(r io.Reader) (payload *PayloadJSON, err error) {
 	return d.DecodeBytes(bytes)
 }
 
-// DecodeBytes handles decoding raw bytes. If you have a read closer it's suggested to use Decode.
+// DecodeBytes handles decoding raw bytes. If you have a read closer it's suggested to use [Decoder.Decode].
 func (d *Decoder) DecodeBytes(bytes []byte) (payload *PayloadJSON, err error) {
 	var token *jwt.Token
 
@@ -272,3 +273,17 @@ func mdsParseX509Certificate(value string) (certificate *x509.Certificate, err e
 
 	return certificate, nil
 }
+
+func mdsParseTimePointer(format, value string) (parsed *time.Time, err error) {
+	if value == "" {
+		return nil, nil
+	}
+
+	var p time.Time
+
+	if p, err = time.Parse(format, value); err != nil {
+		return nil, err
+	}
+
+	return &p, nil
+}
diff --git a/metadata/metadata.go b/metadata/metadata.go
index 3b1612a..18cd479 100644
--- a/metadata/metadata.go
+++ b/metadata/metadata.go
@@ -38,6 +38,7 @@ func Fetch() (metadata *Metadata, err error) {
 	return decoder.Parse(payload)
 }
 
+// Metadata represents a MDS3.1 blob in either a fully parsed or partially parsed state.
 type Metadata struct {
 	Parsed   Parsed
 	Unparsed []EntryError
@@ -55,9 +56,9 @@ func (m *Metadata) ToMap() (metadata map[uuid.UUID]*Entry) {
 	return metadata
 }
 
-// Parsed is a structure representing the Parsed MDS3 dictionary.
+// Parsed is a structure representing the MDS3.1 Metadata BLOB Payload dictionary.
 //
-// See: https://fidoalliance.org/specs/mds/fido-metadata-service-v3.0-ps-20210518.html#metadata-blob-payload-entry-dictionary
+// See: https://fidoalliance.org/specs/mds/fido-metadata-service-v3.1-ps-20250521.html#sctn-mds-payload-blob
 type Parsed struct {
 	// The legalHeader, if present, contains a legal guide for accessing and using metadata, which itself MAY contain URL(s) pointing to further information, such as a full Terms and Conditions statement.
 	LegalHeader string
@@ -72,7 +73,7 @@ type Parsed struct {
 	Entries []Entry
 }
 
-// PayloadJSON is an intermediary JSON/JWT representation of the Parsed.
+// PayloadJSON is an intermediary JSON/JWT representation of the [Parsed] struct.
 type PayloadJSON struct {
 	LegalHeader string `json:"legalHeader"`
 	Number      int    `json:"no"`
@@ -106,27 +107,27 @@ func (j PayloadJSON) Parse() (payload Parsed, err error) {
 	}, nil
 }
 
-// Entry is a structure representing the Entry MDS3 dictionary.
+// Entry is a structure representing the MDS3.1 Metadata BLOB Payload Entry dictionary.
 //
-// See: https://fidoalliance.org/specs/mds/fido-metadata-service-v3.0-ps-20210518.html#metadata-blob-payload-entry-dictionary
+// See: https://fidoalliance.org/specs/mds/fido-metadata-service-v3.1-ps-20250521.html#sctn-mds-blob-pe
 type Entry struct {
 	// The Authenticator Attestation ID.
-	Aaid string `json:"aaid"`
+	Aaid string
 
 	// The Authenticator Attestation GUID.
-	AaGUID uuid.UUID `json:"aaguid"`
+	AaGUID uuid.UUID
 
 	// A list of the attestation certificate public key identifiers encoded as hex string.
-	AttestationCertificateKeyIdentifiers []string `json:"attestationCertificateKeyIdentifiers"`
+	AttestationCertificateKeyIdentifiers []string
 
 	// The metadataStatement JSON object as defined in FIDOMetadataStatement.
-	MetadataStatement Statement `json:"metadataStatement"`
+	MetadataStatement Statement
 
 	// Status of the FIDO Biometric Certification of one or more biometric components of the Authenticator
-	BiometricStatusReports []BiometricStatusReport `json:"biometricStatusReports"`
+	BiometricStatusReports []BiometricStatusReport
 
 	// An array of status reports applicable to this authenticator.
-	StatusReports []StatusReport `json:"statusReports"`
+	StatusReports []StatusReport
 
 	// ISO-8601 formatted date since when the status report array was set to the current value.
 	TimeOfLastStatusChange time.Time
@@ -138,9 +139,10 @@ type Entry struct {
 	RogueListHash string
 }
 
-// EntryJSON is an intermediary JSON/JWT structure representing the Entry MDS3 dictionary.
+// EntryJSON is an intermediary JSON/JWT structure representing the MDS3.1 Metadata BLOB Payload Entry dictionary and
+// JSON representation of the [Entry] struct.
 //
-// See: https://fidoalliance.org/specs/mds/fido-metadata-service-v3.0-ps-20210518.html#metadata-blob-payload-entry-dictionary
+// See: https://fidoalliance.org/specs/mds/fido-metadata-service-v3.1-ps-20250521.html#sctn-mds-blob-pe
 type EntryJSON struct {
 	Aaid                                 string   `json:"aaid"`
 	AaGUID                               string   `json:"aaguid"`
@@ -223,11 +225,11 @@ func (j EntryJSON) Parse() (entry Entry, err error) {
 	}, nil
 }
 
-// Statement is a structure representing the Statement MDS3 dictionary.
+// Statement is a structure representing the Statement MDS3.1 dictionary.
 // Authenticator metadata statements are used directly by the FIDO server at a relying party, but the information
 // contained in the authoritative statement is used in several other places.
 //
-// See: https://fidoalliance.org/specs/mds/fido-metadata-statement-v3.0-ps-20210518.html#metadata-keys
+// See: https://fidoalliance.org/specs/mds/fido-metadata-statement-v3.1-ps-20250521.html#sctn-md-keys
 type Statement struct {
 	// The legalHeader, if present, contains a legal guide for accessing and using metadata, which itself MAY contain URL(s) pointing to further information, such as a full Terms and Conditions statement.
 	LegalHeader string
@@ -241,6 +243,9 @@ type Statement struct {
 	// A list of the attestation certificate public key identifiers encoded as hex string.
 	AttestationCertificateKeyIdentifiers []string
 
+	// This descriptor contains friendly names (e.g., public trade name) of the authenticator in multiple languages.
+	FriendlyNames map[string]string
+
 	// A human-readable, short description of the authenticator, in English.
 	Description string
 
@@ -309,14 +314,40 @@ type Statement struct {
 	// A list of trust anchors used for ECDAA attestation. This entry MUST be present if and only if attestationType includes ATTESTATION_ECDAA.
 	EcdaaTrustAnchors []EcdaaTrustAnchor
 
-	// A data: url [RFC2397] encoded PNG [PNG] icon for the Authenticator.
+	// A data: url [RFC2397] encoded [PNG] or [SVG11] (light mode) icon for the Authenticator (e.g., depicting the
+	// security key). This icon is intended to be shown to users by RPs. Use of [SVG11] format is mandatory if any of
+	// the iconDark, providerLogoLight and/or providerLogoDark is used in addition to icon. Use of [SVG11] is
+	// recommended if only icon is used. The icon is more specific than the provider logo and should be shown if present.
 	Icon *url.URL
 
+	// A data: url [RFC2397] encoded [SVG11] dark mode icon for the Authenticator (e.g., depicting the security key).
+	// This icon is intended to be shown to users by RPs. The icon is more specific than the provider logo and should be
+	// shown if present.
+	IconDark *url.URL
+
+	// A data: url [RFC2397] encoded [SVG11] light mode icon for the provider (e.g., logomark of the passkey provider).
+	// The SVG MUST meet all of the requirements defined in § 4.1 SVG requirements. This icon is intended to be shown
+	// to users by RPs.
+	ProviderLogoLight *url.URL
+
+	// A data: url [RFC2397] encoded [SVG11] dark mode icon for the provider (e.g., logomark of the passkey provider).
+	// The SVG MUST meet all of the requirements defined in § 4.1 SVG requirements. This icon is intended to be shown
+	// to users by RPs.
+	ProviderLogoDark *url.URL
+
 	// List of extensions supported by the authenticator.
 	SupportedExtensions []ExtensionDescriptor
 
+	// Scope of keys generated and maintained by this authenticator model.
+	KeyScope KeyScope
+
+	MultiDeviceCredentialSupport MultiDeviceCredentialSupport
+
 	// Describes supported versions, extensions, AAGUID of the device and its capabilities
 	AuthenticatorGetInfo AuthenticatorGetInfo
+
+	// Specifies the URL for retrieving the configuration details for the credential export protocol (CXP).
+	CredentialExportProtocolConfigURL *url.URL
 }
 
 func (s *Statement) Verifier(x5cis []*x509.Certificate) (opts x509.VerifyOptions) {
@@ -342,16 +373,18 @@ func (s *Statement) Verifier(x5cis []*x509.Certificate) (opts x509.VerifyOptions
 	}
 }
 
-// StatementJSON is an intermediary JSON/JWT structure representing the Statement MDS3 dictionary.
+// StatementJSON is an intermediary JSON/JWT structure representing the MetadataStatement MDS3.1 dictionary and the JSON
+// representation of the [Statement] struct.
 // Authenticator metadata statements are used directly by the FIDO server at a relying party, but the information
 // contained in the authoritative statement is used in several other places.
 //
-// See: https://fidoalliance.org/specs/mds/fido-metadata-statement-v3.0-ps-20210518.html#metadata-keys
+// See: https://fidoalliance.org/specs/mds/fido-metadata-statement-v3.1-ps-20250521.html#sctn-md-keys
 type StatementJSON struct {
 	LegalHeader                          string                                `json:"legalHeader"`
 	Aaid                                 string                                `json:"aaid"`
 	AaGUID                               string                                `json:"aaguid"`
 	AttestationCertificateKeyIdentifiers []string                              `json:"attestationCertificateKeyIdentifiers"`
+	FriendlyNames                        map[string]string                     `json:"friendlyNames"`
 	Description                          string                                `json:"description"`
 	AlternativeDescriptions              map[string]string                     `json:"alternativeDescriptions"`
 	AuthenticatorVersion                 uint32                                `json:"authenticatorVersion"`
@@ -374,8 +407,14 @@ type StatementJSON struct {
 	AttestationRootCertificates          []string                              `json:"attestationRootCertificates"`
 	EcdaaTrustAnchors                    []EcdaaTrustAnchor                    `json:"ecdaaTrustAnchors"`
 	Icon                                 string                                `json:"icon"`
+	IconDark                             string                                `json:"iconDark"`
+	ProviderLogoLight                    string                                `json:"providerLogoLight"`
+	ProviderLogoDark                     string                                `json:"providerLogoDark"`
 	SupportedExtensions                  []ExtensionDescriptor                 `json:"supportedExtensions"`
+	KeyScope                             KeyScope                              `json:"keyScope"`
+	MultiDeviceCredentialSupport         MultiDeviceCredentialSupport          `json:"multiDeviceCredentialSupport"`
 	AuthenticatorGetInfo                 AuthenticatorGetInfoJSON              `json:"authenticatorGetInfo"`
+	CredentialExportProtocolConfigURL    string                                `json:"cxpConfigURL"`
 }
 
 func (j StatementJSON) Parse() (statement Statement, err error) {
@@ -397,7 +436,13 @@ func (j StatementJSON) Parse() (statement Statement, err error) {
 		}
 	}
 
-	var icon *url.URL
+	var (
+		icon, iconDark *url.URL
+
+		logoLight, logoDark *url.URL
+
+		cxpConfigURL *url.URL
+	)
 
 	if len(j.Icon) != 0 {
 		if icon, err = url.ParseRequestURI(j.Icon); err != nil {
@@ -405,6 +450,30 @@ func (j StatementJSON) Parse() (statement Statement, err error) {
 		}
 	}
 
+	if len(j.IconDark) != 0 {
+		if iconDark, err = url.ParseRequestURI(j.IconDark); err != nil {
+			return statement, fmt.Errorf("error occurred parsing statement with description '%s': error occurred parsing icon dark value: %w", j.Description, err)
+		}
+	}
+
+	if len(j.ProviderLogoLight) != 0 {
+		if logoLight, err = url.ParseRequestURI(j.ProviderLogoLight); err != nil {
+			return statement, fmt.Errorf("error occurred parsing statement with description '%s': error occurred parsing provider logo light value: %w", j.Description, err)
+		}
+	}
+
+	if len(j.ProviderLogoDark) != 0 {
+		if logoDark, err = url.ParseRequestURI(j.ProviderLogoDark); err != nil {
+			return statement, fmt.Errorf("error occurred parsing statement with description '%s': error occurred parsing provider logo dark value: %w", j.Description, err)
+		}
+	}
+
+	if len(j.CredentialExportProtocolConfigURL) != 0 {
+		if cxpConfigURL, err = url.ParseRequestURI(j.CredentialExportProtocolConfigURL); err != nil {
+			return statement, fmt.Errorf("error occurred parsing statement with description '%s': error occurred parsing cxp config url value: %w", j.Description, err)
+		}
+	}
+
 	var info AuthenticatorGetInfo
 
 	if info, err = j.AuthenticatorGetInfo.Parse(); err != nil {
@@ -416,6 +485,7 @@ func (j StatementJSON) Parse() (statement Statement, err error) {
 		Aaid:                                 j.Aaid,
 		AaGUID:                               aaguid,
 		AttestationCertificateKeyIdentifiers: j.AttestationCertificateKeyIdentifiers,
+		FriendlyNames:                        j.FriendlyNames,
 		Description:                          j.Description,
 		AlternativeDescriptions:              j.AlternativeDescriptions,
 		AuthenticatorVersion:                 j.AuthenticatorVersion,
@@ -438,15 +508,21 @@ func (j StatementJSON) Parse() (statement Statement, err error) {
 		AttestationRootCertificates:          certificates,
 		EcdaaTrustAnchors:                    j.EcdaaTrustAnchors,
 		Icon:                                 icon,
+		IconDark:                             iconDark,
+		ProviderLogoLight:                    logoLight,
+		ProviderLogoDark:                     logoDark,
 		SupportedExtensions:                  j.SupportedExtensions,
+		KeyScope:                             j.KeyScope,
+		MultiDeviceCredentialSupport:         j.MultiDeviceCredentialSupport,
 		AuthenticatorGetInfo:                 info,
+		CredentialExportProtocolConfigURL:    cxpConfigURL,
 	}, nil
 }
 
-// BiometricStatusReport is a structure representing the BiometricStatusReport MDS3 dictionary.
+// BiometricStatusReport is a structure representing the BiometricStatusReport MDS3.1 dictionary.
 // Contains the current status of the authenticator's biometric component.
 //
-// See: https://fidoalliance.org/specs/mds/fido-metadata-service-v3.0-ps-20210518.html#biometricstatusreport-dictionary
+// See: https://fidoalliance.org/specs/mds/fido-metadata-service-v3.1-ps-20250521.html#sctn-bio-stat-rep
 type BiometricStatusReport struct {
 	// Achieved level of the biometric certification of this biometric component of the authenticator
 	CertLevel uint16
@@ -470,10 +546,11 @@ type BiometricStatusReport struct {
 	CertificationRequirementsVersion string
 }
 
-// BiometricStatusReportJSON is a structure representing the BiometricStatusReport MDS3 dictionary.
+// BiometricStatusReportJSON is a structure representing the BiometricStatusReport MDS3.1 dictionary and the JSON
+// representation of the [BiometricStatusReport] struct.
 // Contains the current status of the authenticator's biometric component.
 //
-// See: https://fidoalliance.org/specs/mds/fido-metadata-service-v3.0-ps-20210518.html#biometricstatusreport-dictionary
+// See: https://fidoalliance.org/specs/mds/fido-metadata-service-v3.1-ps-20250521.html#sctn-bio-stat-rep
 type BiometricStatusReportJSON struct {
 	CertLevel               uint16 `json:"certLevel"`
 	Modality                string `json:"modality"`
@@ -503,9 +580,9 @@ func (j BiometricStatusReportJSON) Parse() (report BiometricStatusReport, err er
 	}, nil
 }
 
-// StatusReport is a structure representing the StatusReport MDS3 dictionary.
+// StatusReport is a structure representing the StatusReport MDS3.1 dictionary.
 //
-// See: https://fidoalliance.org/specs/mds/fido-metadata-service-v3.0-ps-20210518.html#statusreport-dictionary
+// See: https://fidoalliance.org/specs/mds/fido-metadata-service-v3.1-ps-20250521.html#sctn-stat-rep
 type StatusReport struct {
 	// Status of the authenticator. Additional fields MAY be set depending on this value.
 	Status AuthenticatorStatus
@@ -517,6 +594,9 @@ type StatusReport struct {
 	AuthenticatorVersion uint32
 
 	// Base64-encoded [RFC4648] (not base64url!) DER [ITU-X690-2008] PKIX certificate value related to the current status, if applicable.
+	BatchCertificate *x509.Certificate
+
+	// Base64-encoded [RFC4648] (not base64url!) DER [ITU-X690-2008] PKIX certificate value related to the current status, if applicable. This field will typically not be present if field batchCertificate is present.
 	Certificate *x509.Certificate
 
 	// HTTPS URL where additional information may be found related to the current status, if applicable.
@@ -533,25 +613,45 @@ type StatusReport struct {
 
 	// The Document Version of the Authenticator Security Requirements (DV) [FIDOAuthenticatorSecurityRequirements] the implementation is certified to, e.g. "1.2.0".
 	CertificationRequirementsVersion string
+
+	// ISO-8601 formatted date since when the status wil expire, if applicable. If no date is given, the status is
+	// assumed to not have a scheduled expiry.
+	SunsetDate *time.Time
+
+	// The revision number of the FIPS 140 specification, e.g. "3" in the case of FIPS 140-3. This entry MUST be present
+	// if and only if the status entry is one of FIPS140_CERTIFIED_L*.
+	FIPSRevision uint32
+
+	// In the case the status represents a FIPS certification, this field contains the "physical security level" of the
+	// FIPS certification. This entry MUST be present if and only if the status entry is one of FIPS140_CERTIFIED_L*. It
+	// MUST reflect the physical security level which might deviate from the overall level.
+	FIPSPhysicalSecurityLevel uint32
 }
 
-// StatusReportJSON is an intermediary JSON/JWT structure representing the StatusReport MDS3 dictionary.
+// StatusReportJSON is an intermediary JSON/JWT structure representing the StatusReport MDS3.1 dictionary and the JSON
+// representation of the [StatusReport] struct.
 //
-// See: https://fidoalliance.org/specs/mds/fido-metadata-service-v3.0-ps-20210518.html#statusreport-dictionary
+// See: https://fidoalliance.org/specs/mds/fido-metadata-service-v3.1-ps-20250521.html#sctn-stat-rep
 type StatusReportJSON struct {
 	Status                           AuthenticatorStatus `json:"status"`
 	EffectiveDate                    string              `json:"effectiveDate"`
 	AuthenticatorVersion             uint32              `json:"authenticatorVersion"`
+	BatchCertificate                 string              `json:"batchCertificate"`
 	Certificate                      string              `json:"certificate"`
 	URL                              string              `json:"url"`
 	CertificationDescriptor          string              `json:"certificationDescriptor"`
 	CertificateNumber                string              `json:"certificateNumber"`
 	CertificationPolicyVersion       string              `json:"certificationPolicyVersion"`
 	CertificationRequirementsVersion string              `json:"certificationRequirementsVersion"`
+	SunsetDate                       string              `json:"sunsetDate"`
+	FIPSRevision                     uint32              `json:"fipsRevision"`
+	FIPSPhysicalSecurityLevel        uint32              `json:"fipsPhysicalSecurityLevel"`
 }
 
 func (j StatusReportJSON) Parse() (report StatusReport, err error) {
-	var certificate *x509.Certificate
+	var (
+		certificate, batchCertificate *x509.Certificate
+	)
 
 	if len(j.Certificate) != 0 {
 		if certificate, err = mdsParseX509Certificate(j.Certificate); err != nil {
@@ -559,12 +659,25 @@ func (j StatusReportJSON) Parse() (report StatusReport, err error) {
 		}
 	}
 
-	var effective time.Time
+	if len(j.BatchCertificate) != 0 {
+		if batchCertificate, err = mdsParseX509Certificate(j.BatchCertificate); err != nil {
+			return report, fmt.Errorf("error occurred parsing batch certificate value: %w", err)
+		}
+	}
+
+	var (
+		effective time.Time
+		sunset    *time.Time
+	)
 
 	if effective, err = time.Parse(time.DateOnly, j.EffectiveDate); err != nil {
 		return report, fmt.Errorf("error occurred parsing effective date value: %w", err)
 	}
 
+	if sunset, err = mdsParseTimePointer(time.DateOnly, j.SunsetDate); err != nil {
+		return report, fmt.Errorf("error occurred parsing sunset date value: %w", err)
+	}
+
 	var uri *url.URL
 
 	if len(j.URL) != 0 {
@@ -583,18 +696,22 @@ func (j StatusReportJSON) Parse() (report StatusReport, err error) {
 		Status:                           j.Status,
 		EffectiveDate:                    effective,
 		AuthenticatorVersion:             j.AuthenticatorVersion,
+		BatchCertificate:                 batchCertificate,
 		Certificate:                      certificate,
 		URL:                              uri,
 		CertificationDescriptor:          j.CertificationDescriptor,
 		CertificateNumber:                j.CertificateNumber,
 		CertificationPolicyVersion:       j.CertificationPolicyVersion,
 		CertificationRequirementsVersion: j.CertificationRequirementsVersion,
+		SunsetDate:                       sunset,
+		FIPSRevision:                     j.FIPSRevision,
+		FIPSPhysicalSecurityLevel:        j.FIPSPhysicalSecurityLevel,
 	}, nil
 }
 
-// RogueListEntry is a structure representing the RogueListEntry MDS3 dictionary.
+// RogueListEntry is a structure representing the RogueListEntry MDS3.1 dictionary.
 //
-// See: https://fidoalliance.org/specs/mds/fido-metadata-service-v3.0-ps-20210518.html#roguelistentry-dictionary
+// See: https://fidoalliance.org/specs/mds/fido-metadata-service-v3.1-ps-20250521.html#sctn-rogue-list-entry
 type RogueListEntry struct {
 	// Base64url encoding of the rogue authenticator's secret key
 	Sk string `json:"sk"`
@@ -603,10 +720,10 @@ type RogueListEntry struct {
 	Date string `json:"date"`
 }
 
-// CodeAccuracyDescriptor is a structure representing the CodeAccuracyDescriptor MDS3 dictionary.
+// CodeAccuracyDescriptor is a structure representing the CodeAccuracyDescriptor MDS3.1 dictionary.
 // It describes the relevant accuracy/complexity aspects of passcode user verification methods.
 //
-// See: https://fidoalliance.org/specs/mds/fido-metadata-statement-v3.0-ps-20210518.html#codeaccuracydescriptor-dictionary
+// See: https://fidoalliance.org/specs/mds/fido-metadata-statement-v3.1-ps-20250521.html#sctn-type-cad
 type CodeAccuracyDescriptor struct {
 	// The numeric system base (radix) of the code, e.g. 10 in the case of decimal digits.
 	Base uint16 `json:"base"`
@@ -623,10 +740,10 @@ type CodeAccuracyDescriptor struct {
 	BlockSlowdown uint16 `json:"blockSlowdown"`
 }
 
-// BiometricAccuracyDescriptor is a structure representing the BiometricAccuracyDescriptor MDS3 dictionary.
+// BiometricAccuracyDescriptor is a structure representing the BiometricAccuracyDescriptor MDS3.1 dictionary.
 // It describes relevant accuracy/complexity aspects in the case of a biometric user verification method.
 //
-// See: https://fidoalliance.org/specs/mds/fido-metadata-statement-v3.0-ps-20210518.html#biometricaccuracydescriptor-dictionary
+// See: https://fidoalliance.org/specs/mds/fido-metadata-statement-v3.1-ps-20250521.html#sctn-type-bad
 type BiometricAccuracyDescriptor struct {
 	// The false rejection rate [ISO19795-1] for a single template, i.e. the percentage of verification transactions with truthful claims of identity that are incorrectly denied.
 	SelfAttestedFRR float64 `json:"selfAttestedFRR"`
@@ -634,6 +751,12 @@ type BiometricAccuracyDescriptor struct {
 	// The false acceptance rate [ISO19795-1] for a single template, i.e. the percentage of verification transactions with wrongful claims of identity that are incorrectly confirmed.
 	SelfAttestedFAR float64 `json:"selfAttestedFAR"`
 
+	// Impostor Attack Presentation Accept Rate (IAPAR) is the proportion of impostor attack presentations using the
+	// same presentation attack instrument (PAI) species that result in accept [isoiec-30107-3]. For biometric
+	// certification requirements [FIDOBiometricsRequirements], certification can be achieved for an IAPAR threshold of
+	// less than 7% OR less than 15% for each of the PAI species tested.
+	ImposterAttackPresentationAcceptRateThreshold float64 `json:"iAPARThreshold"`
+
 	// Maximum number of alternative templates from different fingers allowed.
 	MaxTemplates uint16 `json:"maxTemplates"`
 
@@ -646,10 +769,10 @@ type BiometricAccuracyDescriptor struct {
 	BlockSlowdown uint16 `json:"blockSlowdown"`
 }
 
-// PatternAccuracyDescriptor is a structure representing the PatternAccuracyDescriptor MDS3 dictionary.
+// PatternAccuracyDescriptor is a structure representing the PatternAccuracyDescriptor MDS3.1 dictionary.
 // It describes relevant accuracy/complexity aspects in the case that a pattern is used as the user verification method.
 //
-// See: https://fidoalliance.org/specs/mds/fido-metadata-statement-v3.0-ps-20210518.html#patternaccuracydescriptor-dictionary
+// See: https://fidoalliance.org/specs/mds/fido-metadata-statement-v3.1-ps-20250521.html#sctn-type-pad
 type PatternAccuracyDescriptor struct {
 	// Number of possible patterns (having the minimum length) out of which exactly one would be the right one, i.e. 1/probability in the case of equal distribution.
 	MinComplexity uint32 `json:"minComplexity"`
@@ -663,10 +786,10 @@ type PatternAccuracyDescriptor struct {
 	BlockSlowdown uint16 `json:"blockSlowdown"`
 }
 
-// VerificationMethodDescriptor is a structure representing the VerificationMethodDescriptor MDS3 dictionary.
+// VerificationMethodDescriptor is a structure representing the VerificationMethodDescriptor MDS3.1 dictionary.
 // It describes a descriptor for a specific base user verification method as implemented by the authenticator.
 //
-// See: https://fidoalliance.org/specs/mds/fido-metadata-statement-v3.0-ps-20210518.html#verificationmethoddescriptor-dictionary
+// See: https://fidoalliance.org/specs/mds/fido-metadata-statement-v3.1-ps-20250521.html#sctn-type-vmd
 type VerificationMethodDescriptor struct {
 	// a single USER_VERIFY constant (see [FIDORegistry]), not a bit flag combination. This value MUST be non-zero.
 	UserVerificationMethod string `json:"userVerificationMethod"`
@@ -681,10 +804,10 @@ type VerificationMethodDescriptor struct {
 	PaDesc PatternAccuracyDescriptor `json:"paDesc"`
 }
 
-// RGBPaletteEntry is a structure representing the RGBPaletteEntry MDS3 dictionary.
+// RGBPaletteEntry is a structure representing the RGBPaletteEntry MDS3.1 dictionary.
 // It describes an RGB three-sample tuple palette entry.
 //
-// See: https://fidoalliance.org/specs/mds/fido-metadata-statement-v3.0-ps-20210518.html#rgbpaletteentry-dictionary
+// See: https://fidoalliance.org/specs/mds/fido-metadata-statement-v3.1-ps-20250521.html#sctn-type-rgbpe
 type RGBPaletteEntry struct {
 	// Red channel sample value
 	R uint16 `json:"r"`
@@ -696,10 +819,10 @@ type RGBPaletteEntry struct {
 	B uint16 `json:"b"`
 }
 
-// DisplayPNGCharacteristicsDescriptor is a structure representing the DisplayPNGCharacteristicsDescriptor MDS3 dictionary.
+// DisplayPNGCharacteristicsDescriptor is a structure representing the DisplayPNGCharacteristicsDescriptor MDS3.1 dictionary.
 // It describes a PNG image characteristics as defined in the PNG [PNG] spec for IHDR (image header) and PLTE (palette table)/
 //
-// See: https://fidoalliance.org/specs/mds/fido-metadata-statement-v3.0-ps-20210518.html#displaypngcharacteristicsdescriptor-dictionary
+// See: https://fidoalliance.org/specs/mds/fido-metadata-statement-v3.1-ps-20250521.html#sctn-type-dpngcd
 type DisplayPNGCharacteristicsDescriptor struct {
 	// image width
 	Width uint32 `json:"width"`
@@ -726,10 +849,10 @@ type DisplayPNGCharacteristicsDescriptor struct {
 	Plte []RGBPaletteEntry `json:"plte"`
 }
 
-// EcdaaTrustAnchor is a structure representing the EcdaaTrustAnchor MDS3 dictionary.
+// EcdaaTrustAnchor is a structure representing the EcdaaTrustAnchor MDS3.1 dictionary.
 // In the case of ECDAA attestation, the ECDAA-Issuer's trust anchor MUST be specified in this field.
 //
-// See: https://fidoalliance.org/specs/mds/fido-metadata-statement-v3.0-ps-20210518.html#ecdaatrustanchor-dictionary
+// See: https://fidoalliance.org/specs/mds/fido-metadata-statement-v3.1-ps-20250521.html#sctn-type-ecdaata
 type EcdaaTrustAnchor struct {
 	// base64url encoding of the result of ECPoint2ToB of the ECPoint2 X
 	X string `json:"X"`
@@ -750,10 +873,10 @@ type EcdaaTrustAnchor struct {
 	G1Curve string `json:"G1Curve"`
 }
 
-// ExtensionDescriptor is a structure representing the ExtensionDescriptor MDS3 dictionary.
+// ExtensionDescriptor is a structure representing the ExtensionDescriptor MDS3.1 dictionary.
 // This descriptor contains an extension supported by the authenticator.
 //
-// See: https://fidoalliance.org/specs/mds/fido-metadata-statement-v3.0-ps-20210518.html#extensiondescriptor-dictionary
+// See: https://fidoalliance.org/specs/mds/fido-metadata-statement-v3.1-ps-20250521.html#sctn-type-ed
 type ExtensionDescriptor struct {
 	// Identifies the extension.
 	ID string `json:"id"`
@@ -768,7 +891,10 @@ type ExtensionDescriptor struct {
 	FailIfUnknown bool `json:"fail_if_unknown"`
 }
 
-// Version represents a generic version with major and minor fields.
+// Version is a structure representing the Version FIDO UAF Protocol 1.2 dictionary and represents a generic version
+// with major and minor fields.
+//
+// See: https://fidoalliance.org/specs/fido-uaf-v1.2-ps-20201020/fido-uaf-protocol-v1.2-ps-20201020.html#version-interface
 type Version struct {
 	// Major version.
 	Major uint16 `json:"major"`
@@ -777,6 +903,9 @@ type Version struct {
 	Minor uint16 `json:"minor"`
 }
 
+// AuthenticatorGetInfo is a structure representing the AuthenticatorGetInfo MDS3.1 dictionary.
+//
+// See: https://fidoalliance.org/specs/mds/fido-metadata-statement-v3.1-ps-20250521.html#sctn-type-agid
 type AuthenticatorGetInfo struct {
 	// List of supported versions.
 	Versions []string
diff --git a/metadata/passkey_authenticator.go b/metadata/passkey_authenticator.go
index bd993d0..48798b6 100644
--- a/metadata/passkey_authenticator.go
+++ b/metadata/passkey_authenticator.go
@@ -5,8 +5,8 @@ package metadata
 // See: https://github.com/passkeydeveloper/passkey-authenticator-aaguids
 type PasskeyAuthenticator map[string]PassKeyAuthenticatorAAGUID
 
-// PassKeyAuthenticatorAAGUID is a type that represents the indivudal schema entry from the Passkey Developer AAGUID
-// listing. Used with PasskeyAuthenticator.
+// PassKeyAuthenticatorAAGUID is a type that represents the individual schema entry from the Passkey Developer AAGUID
+// listing. Used with [PasskeyAuthenticator].
 //
 // See: https://github.com/passkeydeveloper/passkey-authenticator-aaguids
 type PassKeyAuthenticatorAAGUID struct {
diff --git a/metadata/providers/cached/doc.go b/metadata/providers/cached/doc.go
index cb18e91..d76aeb5 100644
--- a/metadata/providers/cached/doc.go
+++ b/metadata/providers/cached/doc.go
@@ -1,6 +1,6 @@
-// Package cached handles a metadata.Provider implementation that both downloads and caches the MDS3 blob. This
+// Package cached handles a [metadata.Provider] implementation that both downloads and caches the MDS3 blob. This
 // effectively is the recommended provider in most instances as it's fairly robust. Alternatively we suggest
-// implementing a similar provider that leverages the memory.Provider as an underlying element.
+// implementing a similar provider that leverages the [memory.Provider] as an underlying element.
 //
 // This provider only specifically performs updates at the time it's initialized. It has no automatic update
 // functionality. This may change in the future however if you want this functionality at this time we recommend making
diff --git a/metadata/providers/cached/options.go b/metadata/providers/cached/options.go
index 3abd763..031161f 100644
--- a/metadata/providers/cached/options.go
+++ b/metadata/providers/cached/options.go
@@ -40,7 +40,7 @@ func WithForceUpdate(force bool) Option {
 	}
 }
 
-// WithNew customizes the NewFunc. By default we just create a fairly standard memory.Provider with strict defaults.
+// WithNew customizes the NewFunc. By default we just create a fairly standard [memory.Provider] with strict defaults.
 func WithNew(newup NewFunc) Option {
 	return func(provider *Provider) (err error) {
 		provider.newup = newup
@@ -60,7 +60,7 @@ func WithDecoder(decoder *metadata.Decoder) Option {
 }
 
 // WithMetadataURL configures the URL to get the metadata from. This shouldn't be modified unless you know what you're
-// doing as we use the metadata.ProductionMDSURL which is safe in most instances.
+// doing as we use the [metadata.ProductionMDSURL] which is safe in most instances.
 func WithMetadataURL(uri string) Option {
 	return func(provider *Provider) (err error) {
 		if _, err = url.ParseRequestURI(uri); err != nil {
@@ -73,7 +73,7 @@ func WithMetadataURL(uri string) Option {
 	}
 }
 
-// WithClient configures the *http.Client used to get the MDS3 blob.
+// WithClient configures the [*http.Client] used to get the MDS3 blob.
 func WithClient(client *http.Client) Option {
 	return func(provider *Provider) (err error) {
 		provider.client = client
@@ -82,7 +82,7 @@ func WithClient(client *http.Client) Option {
 	}
 }
 
-// WithClock allows injection of a metadata.Clock to check the up-to-date status of a blob.
+// WithClock allows injection of a [metadata.Clock] to check the up-to-date status of a blob.
 func WithClock(clock metadata.Clock) Option {
 	return func(provider *Provider) (err error) {
 		provider.clock = clock
diff --git a/metadata/providers/cached/provider.go b/metadata/providers/cached/provider.go
index d8a0dd1..28fb70e 100644
--- a/metadata/providers/cached/provider.go
+++ b/metadata/providers/cached/provider.go
@@ -9,7 +9,7 @@ import (
 	"github.com/go-webauthn/webauthn/metadata"
 )
 
-// New returns a new cached Provider given a set of functional Option's. This provider will download a new version and
+// New returns a new cached Provider given a set of functional [Option]'s. This provider will download a new version and
 // save it to the configured file path if it doesn't exist or if it's out of date by default.
 func New(opts ...Option) (provider metadata.Provider, err error) {
 	p := &Provider{
@@ -48,7 +48,7 @@ func New(opts ...Option) (provider metadata.Provider, err error) {
 	return p, nil
 }
 
-// Provider implements a metadata.Provider with a file-based cache.
+// Provider implements a [metadata.Provider] with a file-based cache.
 type Provider struct {
 	metadata.Provider
 
diff --git a/metadata/providers/memory/doc.go b/metadata/providers/memory/doc.go
index bc2679f..7044aa2 100644
--- a/metadata/providers/memory/doc.go
+++ b/metadata/providers/memory/doc.go
@@ -1,4 +1,4 @@
-// Package memory handles a metadata.Provider implementation that solely exists in memory. It's intended as a basis
+// Package memory handles a [metadata.Provider] implementation that solely exists in memory. It's intended as a basis
 // for other providers and generally not recommended to use directly unless you're implementing your own logic to handle
 // the download and potential caching of the MDS3 blob yourself.
 package memory
diff --git a/metadata/providers/memory/options.go b/metadata/providers/memory/options.go
index 87c0b90..15ab258 100644
--- a/metadata/providers/memory/options.go
+++ b/metadata/providers/memory/options.go
@@ -19,7 +19,7 @@ func WithMetadata(mds map[uuid.UUID]*metadata.Entry) Option {
 }
 
 // WithValidateEntry requires that the provided metadata has an entry for the given authenticator to be considered
-// valid. By default an AAGUID which has a zero value should fail validation if WithValidateEntryPermitZeroAAGUID is not
+// valid. By default an AAGUID which has a zero value should fail validation if [WithValidateEntryPermitZeroAAGUID] is not
 // provided with the value of true. Default is true.
 func WithValidateEntry(require bool) Option {
 	return func(provider *Provider) (err error) {
@@ -30,7 +30,7 @@ func WithValidateEntry(require bool) Option {
 }
 
 // WithValidateEntryPermitZeroAAGUID is an option that permits a zero'd AAGUID from an attestation statement to
-// automatically pass metadata validations. Generally helpful to use with WithValidateEntry. Default is false.
+// automatically pass metadata validations. Generally helpful to use with [WithValidateEntry]. Default is false.
 func WithValidateEntryPermitZeroAAGUID(permit bool) Option {
 	return func(provider *Provider) (err error) {
 		provider.entryPermitZero = permit
@@ -50,7 +50,7 @@ func WithValidateTrustAnchor(validate bool) Option {
 }
 
 // WithValidateStatus when set to true enables the validation of the attestation statements AAGUID against the desired
-// and undesired metadata.AuthenticatorStatus lists. Default is true.
+// and undesired [metadata.AuthenticatorStatus] lists. Default is true.
 func WithValidateStatus(validate bool) Option {
 	return func(provider *Provider) (err error) {
 		provider.status = validate
@@ -70,7 +70,7 @@ func WithValidateAttestationTypes(validate bool) Option {
 }
 
 // WithStatusUndesired provides the list of statuses which are considered undesirable for status report validation
-// purposes. Should be used with WithValidateStatus set to true.
+// purposes. Should be used with [WithValidateStatus] set to true.
 func WithStatusUndesired(statuses []metadata.AuthenticatorStatus) Option {
 	return func(provider *Provider) (err error) {
 		provider.undesired = statuses
@@ -80,7 +80,7 @@ func WithStatusUndesired(statuses []metadata.AuthenticatorStatus) Option {
 }
 
 // WithStatusDesired provides the list of statuses which are considered desired and will be required for status report
-// validation purposes. Should be used with WithValidateStatus set to true.
+// validation purposes. Should be used with [WithValidateStatus] set to true.
 func WithStatusDesired(statuses []metadata.AuthenticatorStatus) Option {
 	return func(provider *Provider) (err error) {
 		provider.desired = statuses
diff --git a/metadata/providers/memory/provider.go b/metadata/providers/memory/provider.go
index 27a8443..45e3d2c 100644
--- a/metadata/providers/memory/provider.go
+++ b/metadata/providers/memory/provider.go
@@ -32,7 +32,7 @@ func New(opts ...Option) (provider metadata.Provider, err error) {
 	return p, nil
 }
 
-// Provider is a concrete implementation of the metadata.Provider that utilizes memory for validation. This provider is
+// Provider is a concrete implementation of the [metadata.Provider] that utilizes memory for validation. This provider is
 // a simple one-shot that doesn't perform any locking, provide dynamic functionality, or download the metadata at any
 // stage (it expects it's provided via one of the Option's).
 type Provider struct {
diff --git a/metadata/status.go b/metadata/status.go
index f20d716..c1b012a 100644
--- a/metadata/status.go
+++ b/metadata/status.go
@@ -5,7 +5,7 @@ import (
 	"strings"
 )
 
-// ValidateStatusReports checks a list of StatusReport's against a list of desired and undesired AuthenticatorStatus
+// ValidateStatusReports checks a list of [StatusReport] structs against a list of desired and undesired [AuthenticatorStatus]
 // values. If the reports contain all of the desired and none of the undesired status reports then no error is returned
 // otherwise an error describing the issue is returned.
 func ValidateStatusReports(reports []StatusReport, desired, undesired []AuthenticatorStatus) (err error) {
diff --git a/metadata/types.go b/metadata/types.go
index dcc8913..2097cd8 100644
--- a/metadata/types.go
+++ b/metadata/types.go
@@ -86,8 +86,25 @@ const (
 	None AuthenticatorAttestationType = "none"
 )
 
+type KeyScope string
+
+const (
+	KeyScopeNone                   KeyScope = ""
+	PublicKeyCredentialSource      KeyScope = "public-key-credential-source"
+	DeviceSupplementalPublicKeys   KeyScope = "device-spk"
+	ProviderSupplementalPublicKeys KeyScope = "provider-spk"
+)
+
+type MultiDeviceCredentialSupport string
+
+const (
+	MultiDeviceCredentialUnsupported MultiDeviceCredentialSupport = "unsupported"
+	MultiDeviceCredentialExplicit    MultiDeviceCredentialSupport = "explicit"
+	MultiDeviceCredentialImplicit    MultiDeviceCredentialSupport = "implicit"
+)
+
 // AuthenticatorStatus - This enumeration describes the status of an authenticator model as identified by its AAID and potentially some additional information (such as a specific attestation key).
-// https://fidoalliance.org/specs/mds/fido-metadata-service-v3.0-ps-20210518.html#authenticatorstatus-enum
+// https://fidoalliance.org/specs/mds/fido-metadata-service-v3.1-ps-20250521.html#sctn-authnr-stat
 type AuthenticatorStatus string
 
 const (
@@ -121,6 +138,14 @@ const (
 	FidoCertifiedL3 AuthenticatorStatus = "FIDO_CERTIFIED_L3"
 	// FidoCertifiedL3plus - The authenticator has passed FIDO Authenticator certification at level 3+. This level is more strict than level 3.
 	FidoCertifiedL3plus AuthenticatorStatus = "FIDO_CERTIFIED_L3plus"
+	// FIPS140CertifiedL1 - The authenticator has passed FIPS 140 certification at overall level 1.
+	FIPS140CertifiedL1 AuthenticatorStatus = "FIPS140_CERTIFIED_L1"
+	// FIPS140CertifiedL2 - The authenticator has passed FIPS 140 certification at overall level 2.
+	FIPS140CertifiedL2 AuthenticatorStatus = "FIPS140_CERTIFIED_L2"
+	// FIPS140CertifiedL3 - The authenticator has passed FIPS 140 certification at overall level 3.
+	FIPS140CertifiedL3 AuthenticatorStatus = "FIPS140_CERTIFIED_L3"
+	// FIPS140CertifiedL4 - The authenticator has passed FIPS 140 certification at overall level 4.
+	FIPS140CertifiedL4 AuthenticatorStatus = "FIPS140_CERTIFIED_L4"
 )
 
 // defaultUndesiredAuthenticatorStatus is an array of undesirable authenticator statuses


Test patch:
diff --git a/metadata/metadata_test.go b/metadata/metadata_test.go
index b0bfb80..1a1f9ed 100644
--- a/metadata/metadata_test.go
+++ b/metadata/metadata_test.go
@@ -213,6 +213,22 @@ func TestIsUndesiredAuthenticatorStatus(t *testing.T) {
 			FidoCertifiedL3plus,
 			false,
 		},
+		{
+			FIPS140CertifiedL1,
+			false,
+		},
+		{
+			FIPS140CertifiedL2,
+			false,
+		},
+		{
+			FIPS140CertifiedL3,
+			false,
+		},
+		{
+			FIPS140CertifiedL4,
+			false,
+		},
 	}
 
 	for _, tt := range tests {