diff --git a/.gitignore b/.gitignore index 8aa11f5f..c66d1cc4 100644 --- a/.gitignore +++ b/.gitignore @@ -334,3 +334,5 @@ ASALocalRun/ /Test/coverage.netcoreapp3.1.cobertura.xml .DS_Store /testEnvironments.json + +Demo/Conformance/ \ No newline at end of file diff --git a/Demo/TestController.cs b/Demo/TestController.cs index d0d67163..5967a8af 100644 --- a/Demo/TestController.cs +++ b/Demo/TestController.cs @@ -1,6 +1,5 @@ using System.Text; using System.Text.Json; -using System.Text.Json.Serialization; using Fido2NetLib; using Fido2NetLib.Development; using Fido2NetLib.Objects; diff --git a/Src/Fido2.Models/AuthenticatorAttestationRawResponse.cs b/Src/Fido2.Models/AuthenticatorAttestationRawResponse.cs index 278e8eb2..80337144 100644 --- a/Src/Fido2.Models/AuthenticatorAttestationRawResponse.cs +++ b/Src/Fido2.Models/AuthenticatorAttestationRawResponse.cs @@ -15,7 +15,7 @@ public sealed class AuthenticatorAttestationRawResponse public byte[] RawId { get; set; } [JsonPropertyName("type")] - public PublicKeyCredentialType Type { get; set; } = PublicKeyCredentialType.PublicKey; + public PublicKeyCredentialType? Type { get; set; } [JsonPropertyName("response")] public AttestationResponse Response { get; set; } diff --git a/Src/Fido2.Models/CredentialCreateOptions.cs b/Src/Fido2.Models/CredentialCreateOptions.cs index 1e5b2239..43de046b 100644 --- a/Src/Fido2.Models/CredentialCreateOptions.cs +++ b/Src/Fido2.Models/CredentialCreateOptions.cs @@ -134,6 +134,7 @@ public static CredentialCreateOptions Create( PubKeyCredParam.ES512, PubKeyCredParam.RS512, PubKeyCredParam.PS512, + PubKeyCredParam.RS1 ], AuthenticatorSelection = authenticatorSelection, Attestation = attestationConveyancePreference, @@ -185,6 +186,7 @@ public sealed class PubKeyCredParam( public static readonly PubKeyCredParam PS384 = new(COSE.Algorithm.PS384); public static readonly PubKeyCredParam PS512 = new(COSE.Algorithm.PS512); public static readonly PubKeyCredParam Ed25519 = new(COSE.Algorithm.EdDSA); + public static readonly PubKeyCredParam RS1 = new(COSE.Algorithm.RS1); } /// diff --git a/Src/Fido2.Models/Objects/AuthenticationExtensionsClientInputs.cs b/Src/Fido2.Models/Objects/AuthenticationExtensionsClientInputs.cs index db10e328..f6f8e4d1 100644 --- a/Src/Fido2.Models/Objects/AuthenticationExtensionsClientInputs.cs +++ b/Src/Fido2.Models/Objects/AuthenticationExtensionsClientInputs.cs @@ -12,14 +12,14 @@ public sealed class AuthenticationExtensionsClientInputs /// [JsonPropertyName("example.extension.bool")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - public object Example { get; set; } + public bool? Example { get; set; } /// /// This extension allows WebAuthn Relying Parties that have previously registered a credential using the legacy FIDO JavaScript APIs to request an assertion. /// https://www.w3.org/TR/webauthn/#sctn-appid-extension /// [JsonPropertyName("appid")] - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonIgnore(Condition = JsonIgnoreCondition.Always)] public string AppID { get; set; } /// @@ -33,10 +33,11 @@ public sealed class AuthenticationExtensionsClientInputs /// /// This extension enables use of a user verification method. /// https://www.w3.org/TR/webauthn/#sctn-uvm-extension + /// TODO: Remove this completely as it's removed in L3 /// [JsonPropertyName("uvm")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - public bool? UserVerificationMethod { get; set; } + public bool? UserVerificationMethod { private get; set; } #nullable enable /// diff --git a/Src/Fido2.Models/Objects/AuthenticationExtensionsClientOutputs.cs b/Src/Fido2.Models/Objects/AuthenticationExtensionsClientOutputs.cs index edcf8059..b5f15b59 100644 --- a/Src/Fido2.Models/Objects/AuthenticationExtensionsClientOutputs.cs +++ b/Src/Fido2.Models/Objects/AuthenticationExtensionsClientOutputs.cs @@ -9,7 +9,7 @@ public class AuthenticationExtensionsClientOutputs /// [JsonPropertyName("example.extension.bool")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - public object Example { get; set; } + public bool? Example { get; set; } #nullable enable diff --git a/Src/Fido2/AuthenticatorAssertionResponse.cs b/Src/Fido2/AuthenticatorAssertionResponse.cs index e2b30b13..166901a2 100644 --- a/Src/Fido2/AuthenticatorAssertionResponse.cs +++ b/Src/Fido2/AuthenticatorAssertionResponse.cs @@ -52,6 +52,7 @@ public static AuthenticatorAssertionResponse Parse(AuthenticatorAssertionRawResp /// The stored counter value for this CredentialId /// A function that returns if user handle is owned by the credential ID. /// + /// DO NOT USE - Deprecated, but kept in code due to conformance testing tool /// The used to propagate notifications that the operation should be canceled. public async Task VerifyAsync( AssertionOptions options, @@ -61,9 +62,10 @@ public async Task VerifyAsync( uint storedSignatureCounter, IsUserHandleOwnerOfCredentialIdAsync isUserHandleOwnerOfCredId, IMetadataService? metadataService, + byte[]? requestTokenBindingId, CancellationToken cancellationToken = default) { - BaseVerify(config.FullyQualifiedOrigins, options.Challenge); + BaseVerify(config.FullyQualifiedOrigins, options.Challenge, requestTokenBindingId); if (Raw.Type != PublicKeyCredentialType.PublicKey) throw new Fido2VerificationException(Fido2ErrorCode.InvalidAssertionResponse, Fido2ErrorMessages.AssertionResponseNotPublicKey); @@ -115,15 +117,20 @@ public async Task VerifyAsync( // https://www.w3.org/TR/webauthn/#sctn-appid-extension // FIDO AppID Extension: // If true, the AppID was used and thus, when verifying an assertion, the Relying Party MUST expect the rpIdHash to be the hash of the AppID, not the RP ID. + var rpid = Raw.ClientExtensionResults?.AppID ?? false ? options.Extensions?.AppID : options.RpId; + byte[] hashedRpId = SHA256.HashData(Encoding.UTF8.GetBytes(rpid ?? string.Empty)); byte[] hash = SHA256.HashData(Raw.Response.ClientDataJson); if (!authData.RpIdHash.SequenceEqual(hashedRpId)) throw new Fido2VerificationException(Fido2ErrorCode.InvalidRpidHash, Fido2ErrorMessages.InvalidRpidHash); + var conformanceTesting = metadataService != null && metadataService.ConformanceTesting(); + // 14. Verify that the UP bit of the flags in authData is set. - if (!authData.UserPresent) + // Todo: Conformance testing verifies the UVP flags differently than W3C spec, simplify this by removing the mention of conformanceTesting when conformance tools are updated) + if (!authData.UserPresent && !conformanceTesting) throw new Fido2VerificationException(Fido2ErrorCode.UserPresentFlagNotSet, Fido2ErrorMessages.UserPresentFlagNotSet); // 15. If the Relying Party requires user verification for this assertion, verify that the UV bit of the flags in authData is set. @@ -174,6 +181,7 @@ public async Task VerifyAsync( if (authData.SignCount > 0 && authData.SignCount <= storedSignatureCounter) throw new Fido2VerificationException(Fido2ErrorCode.InvalidSignCount, Fido2ErrorMessages.SignCountIsLessThanSignatureCounter); + return new VerifyAssertionResult { CredentialId = Raw.Id, diff --git a/Src/Fido2/AuthenticatorAttestationResponse.cs b/Src/Fido2/AuthenticatorAttestationResponse.cs index d5665ae8..1522e54b 100644 --- a/Src/Fido2/AuthenticatorAttestationResponse.cs +++ b/Src/Fido2/AuthenticatorAttestationResponse.cs @@ -60,6 +60,7 @@ public async Task VerifyAsync( Fido2Configuration config, IsCredentialIdUniqueToUserAsyncDelegate isCredentialIdUniqueToUser, IMetadataService? metadataService, + byte[]? requestTokenBindingId, CancellationToken cancellationToken = default) { // https://www.w3.org/TR/webauthn/#registering-a-new-credential @@ -74,7 +75,10 @@ public async Task VerifyAsync( // 8. Verify that the value of C.challenge matches the challenge that was sent to the authenticator in the create() call. // 9. Verify that the value of C.origin matches the Relying Party's origin. - BaseVerify(config.FullyQualifiedOrigins, originalOptions.Challenge); + // 9.5. Verify that the value of C.tokenBinding.status matches the state of Token Binding for the TLS connection over which the attestation was obtained. + // If Token Binding was used on that TLS connection, also verify that C.tokenBinding.id matches the base64url encoding of the Token Binding ID for the connection. + // Validated in BaseVerify. + BaseVerify(config.FullyQualifiedOrigins, originalOptions.Challenge, requestTokenBindingId); if (Raw.Id is null || Raw.Id.Length == 0) throw new Fido2VerificationException(Fido2ErrorCode.InvalidAttestationResponse, Fido2ErrorMessages.AttestationResponseIdMissing); @@ -149,7 +153,7 @@ public async Task VerifyAsync( if (metadataService?.ConformanceTesting() is true && metadataEntry is null && attType != AttestationType.None && AttestationObject.Fmt is not "fido-u2f") throw new Fido2VerificationException(Fido2ErrorCode.AaGuidNotFound, "AAGUID not found in MDS test metadata"); - TrustAnchor.Verify(metadataEntry, trustPath); + TrustAnchor.Verify(metadataEntry, trustPath, metadataService?.ConformanceTesting() is true ? FidoValidationMode.FidoConformance2024 : FidoValidationMode.Default); // 22. Assess the attestation trustworthiness using the outputs of the verification procedure in step 14, as follows: // If self attestation was used, check if self attestation is acceptable under Relying Party policy. @@ -186,7 +190,7 @@ public async Task VerifyAsync( return new RegisteredPublicKeyCredential { - Type = Raw.Type, + Type = Raw.Type.Value, Id = authData.AttestedCredentialData.CredentialId, PublicKey = authData.AttestedCredentialData.CredentialPublicKey.GetBytes(), SignCount = authData.SignCount, @@ -253,7 +257,7 @@ private async Task DevicePublicKeyRegistrationAsync( if (metadataService?.ConformanceTesting() is true && metadataEntry is null && attType != AttestationType.None && devicePublicKeyAuthenticatorOutput.Fmt is not "fido-u2f") throw new Fido2VerificationException(Fido2ErrorCode.AaGuidNotFound, "AAGUID not found in MDS test metadata"); - TrustAnchor.Verify(metadataEntry, trustPath); + TrustAnchor.Verify(metadataEntry, trustPath, metadataService?.ConformanceTesting() is true ? FidoValidationMode.FidoConformance2024 : FidoValidationMode.Default); // Check status reports for authenticator with undesirable status var latestStatusReport = metadataEntry?.GetLatestStatusReport(); diff --git a/Src/Fido2/AuthenticatorResponse.cs b/Src/Fido2/AuthenticatorResponse.cs index 22c31635..eddee047 100644 --- a/Src/Fido2/AuthenticatorResponse.cs +++ b/Src/Fido2/AuthenticatorResponse.cs @@ -48,6 +48,7 @@ protected AuthenticatorResponse(ReadOnlySpan utf8EncodedJson) Type = response.Type; Challenge = response.Challenge; Origin = response.Origin; + TokenBinding = response.TokenBinding; } public const int MAX_ORIGINS_TO_PRINT = 5; @@ -62,7 +63,11 @@ protected AuthenticatorResponse(ReadOnlySpan utf8EncodedJson) [JsonPropertyName("origin")] public string Origin { get; } - protected void BaseVerify(IReadOnlySet fullyQualifiedExpectedOrigins, ReadOnlySpan originalChallenge) + // [Obsolete("This property is not used and will be removed in a future version once the conformance tool stops testing for it.")] + [JsonPropertyName("tokenBinding")] + public TokenBindingDto? TokenBinding { get; set; } + + protected void BaseVerify(IReadOnlySet fullyQualifiedExpectedOrigins, ReadOnlySpan originalChallenge, byte[]? requestTokenBindingId) { if (Type is not "webauthn.create" && Type is not "webauthn.get") throw new Fido2VerificationException(Fido2ErrorCode.InvalidAuthenticatorResponse, $"Type must be 'webauthn.create' or 'webauthn.get'. Was '{Type}'"); @@ -79,6 +84,10 @@ protected void BaseVerify(IReadOnlySet fullyQualifiedExpectedOrigins, Re // 12. Verify that the value of C.origin matches the Relying Party's origin. if (!fullyQualifiedExpectedOrigins.Contains(fullyQualifiedOrigin)) throw new Fido2VerificationException($"Fully qualified origin {fullyQualifiedOrigin} of {Origin} not equal to fully qualified original origin {string.Join(", ", fullyQualifiedExpectedOrigins.Take(MAX_ORIGINS_TO_PRINT))} ({fullyQualifiedExpectedOrigins.Count})"); + + // 13?. Verify that the value of C.tokenBinding.status matches the state of Token Binding for the TLS connection over which the assertion was obtained. + // If Token Binding was used on that TLS connection, also verify that C.tokenBinding.id matches the base64url encoding of the Token Binding ID for the connection. + TokenBinding?.Verify(requestTokenBindingId); } /* diff --git a/Src/Fido2/Extensions/CryptoUtils.cs b/Src/Fido2/Extensions/CryptoUtils.cs index 719d5eaf..8714becf 100644 --- a/Src/Fido2/Extensions/CryptoUtils.cs +++ b/Src/Fido2/Extensions/CryptoUtils.cs @@ -49,7 +49,7 @@ public static HashAlgorithmName HashAlgFromCOSEAlg(COSE.Algorithm alg) }; } - public static bool ValidateTrustChain(X509Certificate2[] trustPath, X509Certificate2[] attestationRootCertificates) + public static bool ValidateTrustChain(X509Certificate2[] trustPath, X509Certificate2[] attestationRootCertificates, FidoValidationMode validationMode = FidoValidationMode.Default) { // https://fidoalliance.org/specs/fido-v2.0-id-20180227/fido-metadata-statement-v2.0-id-20180227.html#widl-MetadataStatement-attestationRootCertificates @@ -59,6 +59,8 @@ public static bool ValidateTrustChain(X509Certificate2[] trustPath, X509Certific // A trust anchor can be a root certificate, an intermediate CA certificate or even the attestation certificate itself. // Let's check the simplest case first. If subject and issuer are the same, and the attestation cert is in the list, that's all the validation we need + + // We have the same singular root cert in trustpath and it is in attestationRootCertificates if (trustPath.Length == 1 && trustPath[0].Subject.Equals(trustPath[0].Issuer, StringComparison.Ordinal)) { foreach (X509Certificate2 cert in attestationRootCertificates) @@ -68,7 +70,6 @@ public static bool ValidateTrustChain(X509Certificate2[] trustPath, X509Certific return true; } } - return false; } // If the attestation cert is not self signed, we will need to build a chain @@ -101,7 +102,7 @@ public static bool ValidateTrustChain(X509Certificate2[] trustPath, X509Certific chain.ChainPolicy.TrustMode = X509ChainTrustMode.CustomRootTrust; // if the attestation cert has a CDP extension, go ahead and turn on online revocation checking - if (!string.IsNullOrEmpty(CDPFromCertificateExts(trustPath[0].Extensions))) + if (!string.IsNullOrEmpty(CDPFromCertificateExts(trustPath[0].Extensions)) && validationMode != FidoValidationMode.FidoConformance2024) chain.ChainPolicy.RevocationMode = X509RevocationMode.Online; // don't allow unknown root now that we have a custom root diff --git a/Src/Fido2/Fido2.cs b/Src/Fido2/Fido2.cs index 11dc1b93..8ebf338b 100644 --- a/Src/Fido2/Fido2.cs +++ b/Src/Fido2/Fido2.cs @@ -65,16 +65,18 @@ public CredentialCreateOptions RequestNewCredential( /// The attestation response from the authenticator. /// The original options that was sent to the client. /// The delegate used to validate that the CredentialID is unique to this user. + /// DO NOT USE - Deprecated, but kept in code due to conformance testing tool /// The used to propagate notifications that the operation should be canceled. /// public async Task MakeNewCredentialAsync( AuthenticatorAttestationRawResponse attestationResponse, CredentialCreateOptions originalOptions, IsCredentialIdUniqueToUserAsyncDelegate isCredentialIdUniqueToUser, + byte[]? requestTokenBindingId = null, CancellationToken cancellationToken = default) { var parsedResponse = AuthenticatorAttestationResponse.Parse(attestationResponse); - var credential = await parsedResponse.VerifyAsync(originalOptions, _config, isCredentialIdUniqueToUser, _metadataService, cancellationToken); + var credential = await parsedResponse.VerifyAsync(originalOptions, _config, isCredentialIdUniqueToUser, _metadataService, requestTokenBindingId, cancellationToken); return credential; } @@ -105,6 +107,7 @@ public AssertionOptions GetAssertionOptions( /// The stored device public keys. /// The stored value of the signature counter. /// The delegate used to validate that the user handle is indeed owned of the CredentialId. + /// DO NOT USE - Deprecated, but kept in code due to conformance testing tool /// The used to propagate notifications that the operation should be canceled. /// public async Task MakeAssertionAsync( @@ -114,6 +117,7 @@ public async Task MakeAssertionAsync( IReadOnlyList storedDevicePublicKeys, uint storedSignatureCounter, IsUserHandleOwnerOfCredentialIdAsync isUserHandleOwnerOfCredentialIdCallback, + byte[]? requestTokenBindingId = null, CancellationToken cancellationToken = default) { var parsedResponse = AuthenticatorAssertionResponse.Parse(assertionResponse); @@ -125,6 +129,7 @@ public async Task MakeAssertionAsync( storedSignatureCounter, isUserHandleOwnerOfCredentialIdCallback, _metadataService, + requestTokenBindingId, cancellationToken); return result; diff --git a/Src/Fido2/FidoValidationMode.cs b/Src/Fido2/FidoValidationMode.cs new file mode 100644 index 00000000..fbaf736f --- /dev/null +++ b/Src/Fido2/FidoValidationMode.cs @@ -0,0 +1,6 @@ +public enum FidoValidationMode +{ + WebAuthNLevel3, + FidoConformance2024, + Default = WebAuthNLevel3 +} diff --git a/Src/Fido2/IFido2.cs b/Src/Fido2/IFido2.cs index a536bbef..8b58de02 100644 --- a/Src/Fido2/IFido2.cs +++ b/Src/Fido2/IFido2.cs @@ -20,12 +20,14 @@ Task MakeAssertionAsync( IReadOnlyList storedDevicePublicKeys, uint storedSignatureCounter, IsUserHandleOwnerOfCredentialIdAsync isUserHandleOwnerOfCredentialIdCallback, + byte[]? requestTokenBindingId = null, CancellationToken cancellationToken = default); Task MakeNewCredentialAsync( AuthenticatorAttestationRawResponse attestationResponse, CredentialCreateOptions originalOptions, IsCredentialIdUniqueToUserAsyncDelegate isCredentialIdUniqueToUser, + byte[]? requestTokenBindingId = null, CancellationToken cancellationToken = default); CredentialCreateOptions RequestNewCredential( diff --git a/Src/Fido2/TokenBindingDto.cs b/Src/Fido2/TokenBindingDto.cs new file mode 100644 index 00000000..3651b301 --- /dev/null +++ b/Src/Fido2/TokenBindingDto.cs @@ -0,0 +1,39 @@ +using System.Text.Json.Serialization; +namespace Fido2NetLib; + +public class TokenBindingDto +{ + /// + /// Either "present" or "supported". https://www.w3.org/TR/webauthn/#enumdef-tokenbindingstatus + /// supported: Indicates the client supports token binding, but it was not negotiated when communicating with the Relying Party. + /// present: Indicates token binding was used when communicating with the Relying Party. In this case, the id member MUST be present + /// + [JsonPropertyName("status")] + public string? Status { get; set; } + + /// + /// This member MUST be present if status is present, and MUST a base64url encoding of the Token Binding ID that was used when communicating with the Relying Party. + /// + [JsonPropertyName("id")] + public string? Id { get; set; } + + public void Verify(byte[]? requestTokenbinding) + { + // validation by the FIDO conformance tool (more than spec says) + switch (Status) + { + case "present": + if (string.IsNullOrEmpty(Id)) + throw new Fido2VerificationException("TokenBinding status was present but Id is missing"); + var b64 = Base64Url.Encode(requestTokenbinding); + if (Id != b64) + throw new Fido2VerificationException("Tokenbinding Id does not match"); + break; + case "supported": + case "not-supported": + break; + default: + throw new Fido2VerificationException("Malformed tokenbinding status field"); + } + } +} diff --git a/Src/Fido2/TrustAnchor.cs b/Src/Fido2/TrustAnchor.cs index 6a02daed..cbdbcdb3 100644 --- a/Src/Fido2/TrustAnchor.cs +++ b/Src/Fido2/TrustAnchor.cs @@ -1,60 +1,65 @@ -using System; -using System.Linq; -using System.Security.Cryptography.X509Certificates; - -using Fido2NetLib.Exceptions; - -namespace Fido2NetLib; - -public static class TrustAnchor -{ - public static void Verify(MetadataBLOBPayloadEntry? metadataEntry, X509Certificate2[] trustPath) - { - if (trustPath != null && metadataEntry?.MetadataStatement?.AttestationTypes is not null) - { - static bool ContainsAttestationType(MetadataBLOBPayloadEntry entry, MetadataAttestationType type) - { - return entry.MetadataStatement.AttestationTypes.Contains(type.ToEnumMemberValue()); - } - - // If the authenticator's metadata requires basic full attestation, build and verify the chain - if (ContainsAttestationType(metadataEntry, MetadataAttestationType.ATTESTATION_BASIC_FULL) || - ContainsAttestationType(metadataEntry, MetadataAttestationType.ATTESTATION_PRIVACY_CA)) - { - string[] certStrings = metadataEntry.MetadataStatement.AttestationRootCertificates; - var attestationRootCertificates = new X509Certificate2[certStrings.Length]; - - for (int i = 0; i < attestationRootCertificates.Length; i++) - { - attestationRootCertificates[i] = new X509Certificate2(Convert.FromBase64String(certStrings[i])); - } - - if (!CryptoUtils.ValidateTrustChain(trustPath, attestationRootCertificates)) - { - throw new Fido2VerificationException(Fido2ErrorMessages.InvalidCertificateChain); - } - } - - else if (ContainsAttestationType(metadataEntry, MetadataAttestationType.ATTESTATION_ANONCA)) - { - // skip verification for Anonymization CA (AnonCA) - } - else // otherwise, ensure the certificate is self signed - { - var trustPath0 = trustPath[0]; - - if (!string.Equals(trustPath0.Subject, trustPath0.Issuer, StringComparison.Ordinal)) - { - // TODO: Improve this error message - throw new Fido2VerificationException("Attestation with full attestation from authenticator that does not support full attestation"); - } - } - - // TODO: Verify all MetadataAttestationTypes are correctly handled - - // [ ] ATTESTATION_ECDAA "ecdaa" | currently handled as self signed w/ no test coverage - // [ ] ATTESTATION_ANONCA "anonca" | currently not verified w/ no test coverage - // [ ] ATTESTATION_NONE "none" | currently handled as self signed w/ no test coverage - } - } -} +using System; +using System.Linq; +using System.Security.Cryptography.X509Certificates; + +using Fido2NetLib.Exceptions; + +namespace Fido2NetLib; + +public static class TrustAnchor +{ + public static void Verify(MetadataBLOBPayloadEntry? metadataEntry, X509Certificate2[] trustPath, FidoValidationMode validationMode = FidoValidationMode.Default) + { + if (trustPath != null && metadataEntry?.MetadataStatement?.AttestationTypes is not null) + { + static bool ContainsAttestationType(MetadataBLOBPayloadEntry entry, MetadataAttestationType type) + { + return entry.MetadataStatement.AttestationTypes.Contains(type.ToEnumMemberValue()); + } + + // If the authenticator's metadata requires basic full attestation, build and verify the chain + if (ContainsAttestationType(metadataEntry, MetadataAttestationType.ATTESTATION_BASIC_FULL) || + ContainsAttestationType(metadataEntry, MetadataAttestationType.ATTESTATION_PRIVACY_CA)) + { + string[] certStrings = metadataEntry.MetadataStatement.AttestationRootCertificates; + var attestationRootCertificates = new X509Certificate2[certStrings.Length]; + + for (int i = 0; i < attestationRootCertificates.Length; i++) + { + attestationRootCertificates[i] = new X509Certificate2(Convert.FromBase64String(certStrings[i])); + } + + if (trustPath.Length > 1 && attestationRootCertificates.Any(c => string.Equals(c.Thumbprint, trustPath[^1].Thumbprint, StringComparison.Ordinal))) + { + throw new Fido2VerificationException(Fido2ErrorMessages.InvalidCertificateChain); + } + + if (!CryptoUtils.ValidateTrustChain(trustPath, attestationRootCertificates, validationMode)) + { + throw new Fido2VerificationException(Fido2ErrorMessages.InvalidCertificateChain); + } + } + + else if (ContainsAttestationType(metadataEntry, MetadataAttestationType.ATTESTATION_ANONCA)) + { + // skip verification for Anonymization CA (AnonCA) + } + else // otherwise, ensure the certificate is self signed + { + var trustPath0 = trustPath[0]; + + if (!string.Equals(trustPath0.Subject, trustPath0.Issuer, StringComparison.Ordinal)) + { + // TODO: Improve this error message + throw new Fido2VerificationException("Attestation with full attestation from authenticator that does not support full attestation"); + } + } + + // TODO: Verify all MetadataAttestationTypes are correctly handled + + // [ ] ATTESTATION_ECDAA "ecdaa" | currently handled as self signed w/ no test coverage + // [ ] ATTESTATION_ANONCA "anonca" | currently not verified w/ no test coverage + // [ ] ATTESTATION_NONE "none" | currently handled as self signed w/ no test coverage + } + } +} diff --git a/Test/AuthenticatorResponse.cs b/Test/AuthenticatorResponse.cs index 50ff27d0..93b6a064 100644 --- a/Test/AuthenticatorResponse.cs +++ b/Test/AuthenticatorResponse.cs @@ -253,7 +253,7 @@ public void TestAuthenticatorAttestationRawResponse() { AppID = true, Extensions = ["foo", "bar"], - Example = "test", + Example = true, UserVerificationMethod = new ulong[][] { new ulong[] @@ -279,7 +279,7 @@ public void TestAuthenticatorAttestationRawResponse() Assert.Equal(clientDataJson, rawResponse.Response.ClientDataJson); Assert.True(rawResponse.ClientExtensionResults.AppID); Assert.Equal(new string[] { "foo", "bar" }, rawResponse.ClientExtensionResults.Extensions); - Assert.Equal("test", rawResponse.ClientExtensionResults.Example); + Assert.True(rawResponse.ClientExtensionResults.Example); Assert.Equal((ulong)4, rawResponse.ClientExtensionResults.UserVerificationMethod[0][0]); Assert.True(rawResponse.ClientExtensionResults.PRF.Enabled); Assert.Equal(rawResponse.ClientExtensionResults.PRF.Results.First, [0xf1, 0xd0]); @@ -1212,7 +1212,7 @@ public void TestAuthenticatorAssertionRawResponse() { AppID = true, Extensions = ["foo", "bar"], - Example = "test", + Example = true, UserVerificationMethod = new ulong[][] { new ulong[] @@ -1240,11 +1240,12 @@ public void TestAuthenticatorAssertionRawResponse() Assert.Equal([0xf1, 0xd0], assertionResponse.Response.UserHandle); Assert.True(assertionResponse.ClientExtensionResults.AppID); Assert.Equal(new string[] { "foo", "bar" }, assertionResponse.ClientExtensionResults.Extensions); - Assert.Equal("test", assertionResponse.ClientExtensionResults.Example); + Assert.True(assertionResponse.ClientExtensionResults.Example); Assert.Equal((ulong)4, assertionResponse.ClientExtensionResults.UserVerificationMethod[0][0]); Assert.True(assertionResponse.ClientExtensionResults.PRF.Enabled); Assert.Equal([0xf1, 0xd0], assertionResponse.ClientExtensionResults.PRF.Results.First); Assert.Equal([0xf1, 0xd0], assertionResponse.ClientExtensionResults.PRF.Results.Second); + } [Fact] @@ -1288,7 +1289,7 @@ public async Task TestAuthenticatorAssertionTypeNotPublicKey() { AppID = false, Extensions = ["foo", "bar"], - Example = "test", + Example = true, UserVerificationMethod = new ulong[][] { new ulong[] @@ -1356,7 +1357,7 @@ public async Task TestAuthenticatorAssertionIdMissing() { AppID = false, Extensions = ["foo", "bar"], - Example = "test", + Example = true, UserVerificationMethod = new ulong[][] { new ulong[] @@ -1425,7 +1426,7 @@ public async Task TestAuthenticatorAssertionRawIdMissing() { AppID = false, Extensions = ["foo", "bar"], - Example = "test", + Example = true, UserVerificationMethod = new ulong[][] { new ulong[] @@ -1494,7 +1495,7 @@ public async Task TestAuthenticatorAssertionUserHandleEmpty() { AppID = false, Extensions = ["foo", "bar"], - Example = "test", + Example = true, UserVerificationMethod = new ulong[][] { new ulong[] @@ -1563,7 +1564,7 @@ public async Task TestAuthenticatorAssertionUserHandleNotOwnerOfPublicKey() { AppID = false, Extensions = ["foo", "bar"], - Example = "test", + Example = true, UserVerificationMethod = new ulong[][] { new ulong[] @@ -1632,7 +1633,7 @@ public async Task TestAuthenticatorAssertionTypeNotWebAuthnGet() { AppID = false, Extensions = ["foo", "bar"], - Example = "test", + Example = true, UserVerificationMethod = new ulong[][] { new ulong[] @@ -1703,7 +1704,7 @@ public async Task TestAuthenticatorAssertionAppId() { AppID = true, Extensions = ["foo", "bar"], - Example = "test", + Example = true, UserVerificationMethod = new ulong[][] { new ulong[] @@ -1773,7 +1774,7 @@ public async Task TestAuthenticatorAssertionInvalidRpIdHash() { AppID = false, Extensions = ["foo", "bar"], - Example = "test", + Example = true, UserVerificationMethod = new ulong[][] { new ulong[] @@ -1844,7 +1845,7 @@ public async Task TestAuthenticatorAssertionUPRequirementNotMet() { AppID = false, Extensions = ["foo", "bar"], - Example = "test", + Example = true, UserVerificationMethod = new ulong[][] { new ulong[] @@ -1914,7 +1915,7 @@ public async Task TestAuthenticatorAssertionUVPolicyNotMet() { AppID = false, Extensions = ["foo", "bar"], - Example = "test", + Example = true, UserVerificationMethod = new ulong[][] { new ulong[] @@ -1982,7 +1983,7 @@ public async Task TestAuthenticatorAssertionBEPolicyRequired() { AppID = false, Extensions = ["foo", "bar"], - Example = "test", + Example = true, UserVerificationMethod = new ulong[][] { new ulong[] @@ -2051,7 +2052,7 @@ public async Task TestAuthenticatorAssertionBEPolicyDisallow() { AppID = false, Extensions = ["foo", "bar"], - Example = "test", + Example = true, UserVerificationMethod = new ulong[][] { new ulong[] @@ -2120,7 +2121,7 @@ public async Task TestAuthenticatorAssertionBSPolicyRequired() { AppID = false, Extensions = ["foo", "bar"], - Example = "test", + Example = true, UserVerificationMethod = new ulong[][] { new ulong[] @@ -2189,7 +2190,7 @@ public async Task TestAuthenticatorAssertionBSPolicyDisallow() { AppID = false, Extensions = ["foo", "bar"], - Example = "test", + Example = true, UserVerificationMethod = new ulong[][] { new ulong[] @@ -2259,7 +2260,7 @@ public async Task TestAuthenticatorAssertionStoredPublicKeyMissing() { AppID = false, Extensions = ["foo", "bar"], - Example = "test", + Example = true, UserVerificationMethod = new ulong[][] { new ulong[] @@ -2328,7 +2329,7 @@ public async Task TestAuthenticatorAssertionInvalidSignature() { AppID = false, Extensions = ["foo", "bar"], - Example = "test", + Example = true, UserVerificationMethod = new ulong[][] { new ulong[] @@ -2404,7 +2405,7 @@ public async Task TestAuthenticatorAssertionSignCountSignature() { AppID = false, Extensions = ["foo", "bar"], - Example = "test", + Example = true, UserVerificationMethod = new ulong[][] { new ulong[] diff --git a/Test/Fido2Tests.cs b/Test/Fido2Tests.cs index a6b578d7..db69354c 100644 --- a/Test/Fido2Tests.cs +++ b/Test/Fido2Tests.cs @@ -16,7 +16,7 @@ using Moq; using NSec.Cryptography; - +using Test; using static Fido2NetLib.AuthenticatorAttestationResponse; namespace fido2_net_lib.Test; @@ -76,6 +76,17 @@ static Fido2Tests() ]; } + private TestMetadataService CreateMetadataService(string cacheDir) + { + var repos = new List + { + new FileSystemMetadataRepository(cacheDir) + }; + var simpleService = new TestMetadataService(repos); + simpleService.InitializeAsync().Wait(); + return simpleService; + } + private async Task GetAsync(string filename) { return JsonSerializer.Deserialize(await File.ReadAllTextAsync(filename)); @@ -168,7 +179,7 @@ public async Task MakeAttestationResponseAsync() { AppID = true, Extensions = ["foo", "bar"], - Example = "test", + Example = true, UserVerificationMethod = new ulong[][] { new ulong[] @@ -411,7 +422,7 @@ public async Task TestFido2AssertionAsync() var response = JsonSerializer.Deserialize(await File.ReadAllTextAsync("./attestationNoneResponse.json")); var o = AuthenticatorAttestationResponse.Parse(response); - await o.VerifyAsync(options, _config, (x, cancellationToken) => Task.FromResult(true), _metadataService, CancellationToken.None); + await o.VerifyAsync(options, _config, (x, cancellationToken) => Task.FromResult(true), _metadataService, null, CancellationToken.None); var credId = "F1-3C-7F-08-3C-A2-29-E0-B4-03-E8-87-34-6E-FC-7F-98-53-10-3A-30-91-75-67-39-7A-D1-D8-AF-87-04-61-87-EF-95-31-85-60-F3-5A-1A-2A-CF-7D-B0-1D-06-B9-69-F9-AB-F4-EC-F3-07-3E-CF-0F-71-E8-84-E8-41-20"; var allowedCreds = new List() { @@ -480,7 +491,7 @@ public async Task TestParsingAsync() Assert.NotNull(jsonPost); var o = AuthenticatorAttestationResponse.Parse(jsonPost); - await o.VerifyAsync(options, _config, (x, cancellationToken) => Task.FromResult(true), _metadataService, CancellationToken.None); + await o.VerifyAsync(options, _config, (x, cancellationToken) => Task.FromResult(true), _metadataService, null, CancellationToken.None); } [Fact] @@ -531,7 +542,60 @@ public async Task TestU2FAttestationAsync() var jsonPost = JsonSerializer.Deserialize(await File.ReadAllTextAsync("./attestationResultsU2F.json")); var options = JsonSerializer.Deserialize(await File.ReadAllTextAsync("./attestationOptionsU2F.json")); var o = AuthenticatorAttestationResponse.Parse(jsonPost); - await o.VerifyAsync(options, _config, (x, cancellationToken) => Task.FromResult(true), _metadataService, CancellationToken.None); + await o.VerifyAsync(options, _config, (x, cancellationToken) => Task.FromResult(true), _metadataService, null, CancellationToken.None); + } + + [Fact] + public async Task TestPackedttestationAsyncFailTrustAnchorOnRootCertInTrustPath() + { + if (!OperatingSystem.IsWindows()) + return; + + var targetGuid = new Guid("42383245-4437-3343-3846-423445354132"); + var metadataService = CreateMetadataService("./metadata"); + metadataService.ChangeEntryGuid(new Guid("00000000-0000-0000-0000-000000000004"), targetGuid); + var jsonPost = JsonSerializer.Deserialize(await File.ReadAllTextAsync("./attestationResultsPacked.json")); + var options = JsonSerializer.Deserialize(await File.ReadAllTextAsync("./attestationOptionsPacked.json")); + var o = AuthenticatorAttestationResponse.Parse(jsonPost); + CborArray X5c = o.AttestationObject.AttStmt["x5c"] as CborArray; + var entry = await metadataService.GetEntryAsync(targetGuid); + foreach (var attRootCert in entry.MetadataStatement.AttestationRootCertificates) + X5c.Add(Encoding.UTF8.GetBytes(attRootCert)); + + await Assert.ThrowsAsync(() => o.VerifyAsync(options, _config, (x, cancellationToken) => Task.FromResult(true), metadataService, null, CancellationToken.None)); + } + + [Fact] + public async Task TestU2FAttestationAsyncFailTrustAnchorBasicFull() + { + var metadataService = CreateMetadataService("./metadata"); + metadataService.ChangeEntryGuid(new Guid("00000000-0000-0000-0000-000000000001"), new Guid("00000000-0000-0000-0000-000000000000")); + var jsonPost = JsonSerializer.Deserialize(await File.ReadAllTextAsync("./attestationResultsU2F.json")); + var options = JsonSerializer.Deserialize(await File.ReadAllTextAsync("./attestationOptionsU2F.json")); + var o = AuthenticatorAttestationResponse.Parse(jsonPost); + await Assert.ThrowsAsync(() => o.VerifyAsync(options, _config, (x, cancellationToken) => Task.FromResult(true), metadataService, null, CancellationToken.None)); + } + + [Fact] + public async Task TestU2FAttestationAsyncCantFailTrustAnchorAnonca() + { + var metadataService = CreateMetadataService("./metadata"); + metadataService.ChangeEntryGuid(new Guid("00000000-0000-0000-0000-000000000002"), new Guid("00000000-0000-0000-0000-000000000000")); + var jsonPost = JsonSerializer.Deserialize(await File.ReadAllTextAsync("./attestationResultsU2F.json")); + var options = JsonSerializer.Deserialize(await File.ReadAllTextAsync("./attestationOptionsU2F.json")); + var o = AuthenticatorAttestationResponse.Parse(jsonPost); + await o.VerifyAsync(options, _config, (x, cancellationToken) => Task.FromResult(true), metadataService, null, CancellationToken.None); + } + + [Fact] + public async Task TestU2FAttestationAsyncFailTrustAnchorBasicSurrogate() + { + var metadataService = CreateMetadataService("./metadata"); + metadataService.ChangeEntryGuid(new Guid("00000000-0000-0000-0000-000000000003"), new Guid("00000000-0000-0000-0000-000000000000")); + var jsonPost = JsonSerializer.Deserialize(await File.ReadAllTextAsync("./attestationResultsU2F.json")); + var options = JsonSerializer.Deserialize(await File.ReadAllTextAsync("./attestationOptionsU2F.json")); + var o = AuthenticatorAttestationResponse.Parse(jsonPost); + await Assert.ThrowsAsync(() => o.VerifyAsync(options, _config, (x, cancellationToken) => Task.FromResult(true), metadataService, null, CancellationToken.None)); } [Fact] @@ -541,7 +605,7 @@ public async Task TestPackedAttestationAsync() var options = JsonSerializer.Deserialize(await File.ReadAllTextAsync("./attestationOptionsPacked.json")); var o = AuthenticatorAttestationResponse.Parse(jsonPost); options.PubKeyCredParams.Add(new PubKeyCredParam(COSE.Algorithm.RS1, PublicKeyCredentialType.PublicKey)); - await o.VerifyAsync(options, _config, (x, cancellationToken) => Task.FromResult(true), _metadataService, CancellationToken.None); + await o.VerifyAsync(options, _config, (x, cancellationToken) => Task.FromResult(true), _metadataService, null, CancellationToken.None); var authData = o.AttestationObject.AuthData; var acdBytes = authData.AttestedCredentialData.ToByteArray(); var acd = AttestedCredentialData.Parse(acdBytes); @@ -555,7 +619,7 @@ public async Task TestNoneAttestationAsync() var options = JsonSerializer.Deserialize(await File.ReadAllTextAsync("./attestationOptionsNone.json")); var o = AuthenticatorAttestationResponse.Parse(jsonPost); - await o.VerifyAsync(options, _config, (x, cancellationToken) => Task.FromResult(true), _metadataService, CancellationToken.None); + await o.VerifyAsync(options, _config, (x, cancellationToken) => Task.FromResult(true), _metadataService, null, CancellationToken.None); } [Fact] @@ -564,7 +628,7 @@ public async Task TestTPMSHA256AttestationAsync() var jsonPost = JsonSerializer.Deserialize(await File.ReadAllTextAsync("./attestationTPMSHA256Response.json")); var options = JsonSerializer.Deserialize(await File.ReadAllTextAsync("./attestationTPMSHA256Options.json")); var o = AuthenticatorAttestationResponse.Parse(jsonPost); - await o.VerifyAsync(options, _config, (x, cancellationToken) => Task.FromResult(true), _metadataService, CancellationToken.None); + await o.VerifyAsync(options, _config, (x, cancellationToken) => Task.FromResult(true), _metadataService, null, CancellationToken.None); } [Fact] @@ -574,7 +638,7 @@ public async Task TestTPMSHA1AttestationAsync() var options = JsonSerializer.Deserialize(await File.ReadAllTextAsync("./attestationTPMSHA1Options.json")); var o = AuthenticatorAttestationResponse.Parse(jsonPost); options.PubKeyCredParams.Add(new PubKeyCredParam(COSE.Algorithm.RS1, PublicKeyCredentialType.PublicKey)); - await o.VerifyAsync(options, _config, (x, cancellationToken) => Task.FromResult(true), _metadataService, CancellationToken.None); + await o.VerifyAsync(options, _config, (x, cancellationToken) => Task.FromResult(true), _metadataService, null, CancellationToken.None); } [Fact] @@ -583,7 +647,7 @@ public async Task TestAndroidKeyAttestationAsync() var jsonPost = JsonSerializer.Deserialize(await File.ReadAllTextAsync("./attestationAndroidKeyResponse.json")); var options = JsonSerializer.Deserialize(await File.ReadAllTextAsync("./attestationAndroidKeyOptions.json")); var o = AuthenticatorAttestationResponse.Parse(jsonPost); - await o.VerifyAsync(options, _config, (x, cancellationToken) => Task.FromResult(true), _metadataService, CancellationToken.None); + await o.VerifyAsync(options, _config, (x, cancellationToken) => Task.FromResult(true), _metadataService, null, CancellationToken.None); } [Fact] @@ -592,7 +656,7 @@ public async Task TaskPackedAttestation512() var jsonPost = JsonSerializer.Deserialize(await File.ReadAllTextAsync("./attestationResultsPacked512.json")); var options = JsonSerializer.Deserialize(await File.ReadAllTextAsync("./attestationOptionsPacked512.json")); var o = AuthenticatorAttestationResponse.Parse(jsonPost); - await o.VerifyAsync(options, _config, (x, cancellationToken) => Task.FromResult(true), _metadataService, CancellationToken.None); + await o.VerifyAsync(options, _config, (x, cancellationToken) => Task.FromResult(true), _metadataService, null, CancellationToken.None); } [Fact] @@ -601,7 +665,7 @@ public async Task TestTrustKeyAttestationAsync() var jsonPost = JsonSerializer.Deserialize(await File.ReadAllTextAsync("./attestationResultTrustKeyT110.json")); var options = JsonSerializer.Deserialize(await File.ReadAllTextAsync("./attestationOptionsTrustKeyT110.json")); var o = AuthenticatorAttestationResponse.Parse(jsonPost); - await o.VerifyAsync(options, _config, (x, cancellationToken) => Task.FromResult(true), _metadataService, CancellationToken.None); + await o.VerifyAsync(options, _config, (x, cancellationToken) => Task.FromResult(true), _metadataService, null, CancellationToken.None); var authData = o.AttestationObject.AuthData; var acdBytes = authData.AttestedCredentialData.ToByteArray(); var acd = AttestedCredentialData.Parse(acdBytes); @@ -618,7 +682,7 @@ public async Task TestInvalidU2FAttestationAsync() var jsonPost = JsonSerializer.Deserialize(await File.ReadAllTextAsync("./attestationResultsATKey.json")); var options = JsonSerializer.Deserialize(await File.ReadAllTextAsync("./attestationOptionsATKey.json")); var o = AuthenticatorAttestationResponse.Parse(jsonPost); - await o.VerifyAsync(options, _config, (x, cancellationToken) => Task.FromResult(true), _metadataService, CancellationToken.None); + await o.VerifyAsync(options, _config, (x, cancellationToken) => Task.FromResult(true), _metadataService, null, CancellationToken.None); var authData = o.AttestationObject.AuthData; var acdBytes = authData.AttestedCredentialData.ToByteArray(); var acd = AttestedCredentialData.Parse(acdBytes); @@ -643,7 +707,7 @@ public async Task TestMdsStatusReportsSuccessAsync() mockMetadataService.Setup(m => m.ConformanceTesting()).Returns(false); var o = AuthenticatorAttestationResponse.Parse(response); - await o.VerifyAsync(options, _config, (x, cancellationToken) => Task.FromResult(true), mockMetadataService.Object, CancellationToken.None); + await o.VerifyAsync(options, _config, (x, cancellationToken) => Task.FromResult(true), mockMetadataService.Object, null, CancellationToken.None); } [Fact] @@ -666,7 +730,7 @@ public async Task TestMdsStatusReportsUndesiredAsync() var o = AuthenticatorAttestationResponse.Parse(response); await Assert.ThrowsAsync(() => - o.VerifyAsync(options, _config, (x, cancellationToken) => Task.FromResult(true), mockMetadataService.Object, CancellationToken.None)); + o.VerifyAsync(options, _config, (x, cancellationToken) => Task.FromResult(true), mockMetadataService.Object, null, CancellationToken.None)); } [Fact] @@ -689,7 +753,7 @@ public async Task TestMdsStatusReportsUndesiredFixedAsync() mockMetadataService.Setup(m => m.ConformanceTesting()).Returns(false); var o = AuthenticatorAttestationResponse.Parse(response); - await o.VerifyAsync(options, _config, (x, cancellationToken) => Task.FromResult(true), mockMetadataService.Object, CancellationToken.None); + await o.VerifyAsync(options, _config, (x, cancellationToken) => Task.FromResult(true), mockMetadataService.Object, null, CancellationToken.None); } [Fact] @@ -703,7 +767,7 @@ public async Task TestMdsStatusReportsNullAsync() mockMetadataService.Setup(m => m.ConformanceTesting()).Returns(false); var o = AuthenticatorAttestationResponse.Parse(response); - await o.VerifyAsync(options, _config, (x, cancellationToken) => Task.FromResult(true), mockMetadataService.Object, CancellationToken.None); + await o.VerifyAsync(options, _config, (x, cancellationToken) => Task.FromResult(true), mockMetadataService.Object, null, CancellationToken.None); } //public void TestHasCorrentAAguid() diff --git a/Test/Test.csproj b/Test/Test.csproj index 5fb30e72..12faf566 100644 --- a/Test/Test.csproj +++ b/Test/Test.csproj @@ -17,6 +17,7 @@ + diff --git a/Test/TestFiles/metadata/256K1 U2F Authenticator anonca.json b/Test/TestFiles/metadata/256K1 U2F Authenticator anonca.json new file mode 100644 index 00000000..e844de94 --- /dev/null +++ b/Test/TestFiles/metadata/256K1 U2F Authenticator anonca.json @@ -0,0 +1,68 @@ +{ + "legalHeader": "https://fidoalliance.org/metadata/metadata-statement-legal-header/", + "description": "Virtual Secp256K1 U2F authenticator", + "aaguid": "00000000-0000-0000-0000-000000000002", + "alternativeDescriptions": { + "en-GB": "Virtual Secp256K1 U2F authenticator" + }, + "attestationCertificateKeyIdentifiers": [ + "564df7c0f8c655b6a11f6c4d19f3bf41e2fd0179" + ], + "protocolFamily": "u2f", + "schema": 3, + "authenticatorVersion": 2, + "upv": [ + { + "major": 1, + "minor": 0 + }, + { + "major": 1, + "minor": 1 + }, + { + "major": 1, + "minor": 2 + } + ], + "authenticationAlgorithms": [ + "secp256r1_ecdsa_sha256_raw" + ], + "publicKeyAlgAndEncodings": [ + "ecc_x962_raw" + ], + "attestationTypes": [ + "anonca" + ], + "userVerificationDetails": [ + [ + { + "userVerificationMethod": "none" + } + ], + [ + { + "userVerificationMethod": "presence_internal" + } + ] + ], + "keyProtection": [ + "hardware", + "secure_element" + ], + "matcherProtection": [ + "on_chip" + ], + "cryptoStrength": 128, + "attachmentHint": [ + "external", + "wired", + "nfc", + "wireless" + ], + "tcDisplay": [], + "attestationRootCertificates": [ + "MIIFwDCCA6gCCQCNm1u56oRwXTANBgkqhkiG9w0BAQsFADCBoTEYMBYGA1UEAwwPRklETzIgVEVTVCBST09UMTEwLwYJKoZIhvcNAQkBFiJjb25mb3JtYW5jZS10b29sc0BmaWRvYWxsaWFuY2Uub3JnMRYwFAYDVQQKDA1GSURPIEFsbGlhbmNlMQwwCgYDVQQLDANDV0cxCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJNWTESMBAGA1UEBwwJV2FrZWZpZWxkMB4XDTE4MDMxNjE0MzUyN1oXDTQ1MDgwMTE0MzUyN1owgaExGDAWBgNVBAMMD0ZJRE8yIFRFU1QgUk9PVDExMC8GCSqGSIb3DQEJARYiY29uZm9ybWFuY2UtdG9vbHNAZmlkb2FsbGlhbmNlLm9yZzEWMBQGA1UECgwNRklETyBBbGxpYW5jZTEMMAoGA1UECwwDQ1dHMQswCQYDVQQGEwJVUzELMAkGA1UECAwCTVkxEjAQBgNVBAcMCVdha2VmaWVsZDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAL11U5yAIVLMrL3xS8u8ysMSdOkDeoTO+RcAy+uXXp6k4SC+jOy37gICEtYI+MKQV1EMeMMf3rM1ueZAO3iPFa0NEdi/oQ7npnGjBNI8wMzD8FfNe6rWtzkDaHpsZW///MwWDpGyJR+Xyjcq6U4vS9bS6zZ7jslw0Oczx4UsYgOsIUXSSBaGOrRbxJ/JC5gnDYEYvtNM+PDPczLNKAyhdvBZWNWHr7MZ0P5TeJQcXsAoShRX2Y8U8fRNJm7SeiFKDP0Nn/QKxOSt7zGP4xt9nMasE1q2ZTdar2+W13CRz37RI0ZWpq/+YquoEbZ7Uj7NmBTcqhb260nmDER2FpwwYwPSark92IZbamozB8d7OEI1jJgsrjJhKan0EmRaWVBpHT4xYKdEu7r09S0JhKyU+52WDmmVQTMpYLrm4Xl7hRxyPyBYkalrozsGmPs8vlhNq3VsVbyBSMSpEmUaeAa7LLE9/Vh0agJLVFHh1ehYKJpzHnmmBXUqx0Fz3afmDm1NX0sr3O/6xIx1VSTViT3KNxBYpVH1qjHATLzuxcWmm+75fcJMiPYPSMXVmRb3Q1l91AM4BBeWhlP3Fbc7gDy0r+s7m0sGS6PT2J2rGog2rUxnJ+zCM11M7DeO0XM2nny4uRYPPk9w2EXzfvtdvieYU/5RB4RDm5TGxHhGXVZUgac5AgMBAAEwDQYJKoZIhvcNAQELBQADggIBAFt2XGd3k5GpbO1EUm3u60zT1fE6u6pOscp156k5VnsHgaHRHdIAPNLeLNmR7y5OnrXbh13CrGwU1q84jjJXpv+v14xUCc5i01yopFTQFLr4A7NHp2nNYfNhhIVSFAgW43EflJflbLEelCJzxLlWb5BoDsZeeNmEQsXIM1mJ26R3r0dzsHBb0uy+8LNR1gdVqdjhC8BLy3gh4+BWuidyZNt07LveDsSFW5rcj5wRrSx9hXPIyVpjQSljNvY7MVTouqJzNAAQMsTKkXPkTXldCop9Qo9UPkHRRm0l7LLtdaOoXrct0Ymocf8zxf9bFNiw9f4WRYQM6sMhzt8+s/oDilo4QhcUgeJEiEPESi6ynYTV62SHA4eMunUJ5dlCaRnFiR9DTImFa5IRzie326/nW/SPCaKc/yrFIihMMjJoSAPhpTb/K6yHOUG8r+KiQut7NzqGV301pQ9u62dGL5Oi1VXmCFlE2ramZs15BNOUyAo2CBbRJg3jKcdu/8QC6ojjDvQ863+7LPtn74wJC5RpUJsS0GhQWgq5pAXO3wA61Uobxi6MkOpCC0zBWx/d4CqpS4j4hFgxWBTXX48ihPu+hIxIF/AxbqtPvqLMExW/xZITn6ArpWyQ9e4SUVr3n3F33ap1XdDyZ0vwFcm18JQAtsvXT6qCLrWOXnHUgfn/+Viu" + ], + "icon": "data:image/png;base64,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" +} \ No newline at end of file diff --git a/Test/TestFiles/metadata/256K1 U2F Authenticator basic_full.json b/Test/TestFiles/metadata/256K1 U2F Authenticator basic_full.json new file mode 100644 index 00000000..c09f42b7 --- /dev/null +++ b/Test/TestFiles/metadata/256K1 U2F Authenticator basic_full.json @@ -0,0 +1,68 @@ +{ + "legalHeader": "https://fidoalliance.org/metadata/metadata-statement-legal-header/", + "description": "Virtual Secp256K1 U2F authenticator", + "aaguid": "00000000-0000-0000-0000-000000000001", + "alternativeDescriptions": { + "en-GB": "Virtual Secp256K1 U2F authenticator" + }, + "attestationCertificateKeyIdentifiers": [ + "564df7c0f8c655b6a11f6c4d19f3bf41e2fd0179" + ], + "protocolFamily": "u2f", + "schema": 3, + "authenticatorVersion": 2, + "upv": [ + { + "major": 1, + "minor": 0 + }, + { + "major": 1, + "minor": 1 + }, + { + "major": 1, + "minor": 2 + } + ], + "authenticationAlgorithms": [ + "secp256r1_ecdsa_sha256_raw" + ], + "publicKeyAlgAndEncodings": [ + "ecc_x962_raw" + ], + "attestationTypes": [ + "basic_full" + ], + "userVerificationDetails": [ + [ + { + "userVerificationMethod": "none" + } + ], + [ + { + "userVerificationMethod": "presence_internal" + } + ] + ], + "keyProtection": [ + "hardware", + "secure_element" + ], + "matcherProtection": [ + "on_chip" + ], + "cryptoStrength": 128, + "attachmentHint": [ + "external", + "wired", + "nfc", + "wireless" + ], + "tcDisplay": [], + "attestationRootCertificates": [ + "MIIFwDCCA6gCCQCNm1u56oRwXTANBgkqhkiG9w0BAQsFADCBoTEYMBYGA1UEAwwPRklETzIgVEVTVCBST09UMTEwLwYJKoZIhvcNAQkBFiJjb25mb3JtYW5jZS10b29sc0BmaWRvYWxsaWFuY2Uub3JnMRYwFAYDVQQKDA1GSURPIEFsbGlhbmNlMQwwCgYDVQQLDANDV0cxCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJNWTESMBAGA1UEBwwJV2FrZWZpZWxkMB4XDTE4MDMxNjE0MzUyN1oXDTQ1MDgwMTE0MzUyN1owgaExGDAWBgNVBAMMD0ZJRE8yIFRFU1QgUk9PVDExMC8GCSqGSIb3DQEJARYiY29uZm9ybWFuY2UtdG9vbHNAZmlkb2FsbGlhbmNlLm9yZzEWMBQGA1UECgwNRklETyBBbGxpYW5jZTEMMAoGA1UECwwDQ1dHMQswCQYDVQQGEwJVUzELMAkGA1UECAwCTVkxEjAQBgNVBAcMCVdha2VmaWVsZDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAL11U5yAIVLMrL3xS8u8ysMSdOkDeoTO+RcAy+uXXp6k4SC+jOy37gICEtYI+MKQV1EMeMMf3rM1ueZAO3iPFa0NEdi/oQ7npnGjBNI8wMzD8FfNe6rWtzkDaHpsZW///MwWDpGyJR+Xyjcq6U4vS9bS6zZ7jslw0Oczx4UsYgOsIUXSSBaGOrRbxJ/JC5gnDYEYvtNM+PDPczLNKAyhdvBZWNWHr7MZ0P5TeJQcXsAoShRX2Y8U8fRNJm7SeiFKDP0Nn/QKxOSt7zGP4xt9nMasE1q2ZTdar2+W13CRz37RI0ZWpq/+YquoEbZ7Uj7NmBTcqhb260nmDER2FpwwYwPSark92IZbamozB8d7OEI1jJgsrjJhKan0EmRaWVBpHT4xYKdEu7r09S0JhKyU+52WDmmVQTMpYLrm4Xl7hRxyPyBYkalrozsGmPs8vlhNq3VsVbyBSMSpEmUaeAa7LLE9/Vh0agJLVFHh1ehYKJpzHnmmBXUqx0Fz3afmDm1NX0sr3O/6xIx1VSTViT3KNxBYpVH1qjHATLzuxcWmm+75fcJMiPYPSMXVmRb3Q1l91AM4BBeWhlP3Fbc7gDy0r+s7m0sGS6PT2J2rGog2rUxnJ+zCM11M7DeO0XM2nny4uRYPPk9w2EXzfvtdvieYU/5RB4RDm5TGxHhGXVZUgac5AgMBAAEwDQYJKoZIhvcNAQELBQADggIBAFt2XGd3k5GpbO1EUm3u60zT1fE6u6pOscp156k5VnsHgaHRHdIAPNLeLNmR7y5OnrXbh13CrGwU1q84jjJXpv+v14xUCc5i01yopFTQFLr4A7NHp2nNYfNhhIVSFAgW43EflJflbLEelCJzxLlWb5BoDsZeeNmEQsXIM1mJ26R3r0dzsHBb0uy+8LNR1gdVqdjhC8BLy3gh4+BWuidyZNt07LveDsSFW5rcj5wRrSx9hXPIyVpjQSljNvY7MVTouqJzNAAQMsTKkXPkTXldCop9Qo9UPkHRRm0l7LLtdaOoXrct0Ymocf8zxf9bFNiw9f4WRYQM6sMhzt8+s/oDilo4QhcUgeJEiEPESi6ynYTV62SHA4eMunUJ5dlCaRnFiR9DTImFa5IRzie326/nW/SPCaKc/yrFIihMMjJoSAPhpTb/K6yHOUG8r+KiQut7NzqGV301pQ9u62dGL5Oi1VXmCFlE2ramZs15BNOUyAo2CBbRJg3jKcdu/8QC6ojjDvQ863+7LPtn74wJC5RpUJsS0GhQWgq5pAXO3wA61Uobxi6MkOpCC0zBWx/d4CqpS4j4hFgxWBTXX48ihPu+hIxIF/AxbqtPvqLMExW/xZITn6ArpWyQ9e4SUVr3n3F33ap1XdDyZ0vwFcm18JQAtsvXT6qCLrWOXnHUgfn/+Viu" + ], + "icon": "data:image/png;base64,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" +} \ No newline at end of file diff --git a/Test/TestFiles/metadata/256K1 U2F Authenticator basic_surrogate.json b/Test/TestFiles/metadata/256K1 U2F Authenticator basic_surrogate.json new file mode 100644 index 00000000..c2c55c06 --- /dev/null +++ b/Test/TestFiles/metadata/256K1 U2F Authenticator basic_surrogate.json @@ -0,0 +1,68 @@ +{ + "legalHeader": "https://fidoalliance.org/metadata/metadata-statement-legal-header/", + "description": "Virtual Secp256K1 U2F authenticator", + "aaguid": "00000000-0000-0000-0000-000000000003", + "alternativeDescriptions": { + "en-GB": "Virtual Secp256K1 U2F authenticator" + }, + "attestationCertificateKeyIdentifiers": [ + "564df7c0f8c655b6a11f6c4d19f3bf41e2fd0179" + ], + "protocolFamily": "u2f", + "schema": 3, + "authenticatorVersion": 2, + "upv": [ + { + "major": 1, + "minor": 0 + }, + { + "major": 1, + "minor": 1 + }, + { + "major": 1, + "minor": 2 + } + ], + "authenticationAlgorithms": [ + "secp256r1_ecdsa_sha256_raw" + ], + "publicKeyAlgAndEncodings": [ + "ecc_x962_raw" + ], + "attestationTypes": [ + "basic_surrogate" + ], + "userVerificationDetails": [ + [ + { + "userVerificationMethod": "none" + } + ], + [ + { + "userVerificationMethod": "presence_internal" + } + ] + ], + "keyProtection": [ + "hardware", + "secure_element" + ], + "matcherProtection": [ + "on_chip" + ], + "cryptoStrength": 128, + "attachmentHint": [ + "external", + "wired", + "nfc", + "wireless" + ], + "tcDisplay": [], + "attestationRootCertificates": [ + "MIIFwDCCA6gCCQCNm1u56oRwXTANBgkqhkiG9w0BAQsFADCBoTEYMBYGA1UEAwwPRklETzIgVEVTVCBST09UMTEwLwYJKoZIhvcNAQkBFiJjb25mb3JtYW5jZS10b29sc0BmaWRvYWxsaWFuY2Uub3JnMRYwFAYDVQQKDA1GSURPIEFsbGlhbmNlMQwwCgYDVQQLDANDV0cxCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJNWTESMBAGA1UEBwwJV2FrZWZpZWxkMB4XDTE4MDMxNjE0MzUyN1oXDTQ1MDgwMTE0MzUyN1owgaExGDAWBgNVBAMMD0ZJRE8yIFRFU1QgUk9PVDExMC8GCSqGSIb3DQEJARYiY29uZm9ybWFuY2UtdG9vbHNAZmlkb2FsbGlhbmNlLm9yZzEWMBQGA1UECgwNRklETyBBbGxpYW5jZTEMMAoGA1UECwwDQ1dHMQswCQYDVQQGEwJVUzELMAkGA1UECAwCTVkxEjAQBgNVBAcMCVdha2VmaWVsZDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAL11U5yAIVLMrL3xS8u8ysMSdOkDeoTO+RcAy+uXXp6k4SC+jOy37gICEtYI+MKQV1EMeMMf3rM1ueZAO3iPFa0NEdi/oQ7npnGjBNI8wMzD8FfNe6rWtzkDaHpsZW///MwWDpGyJR+Xyjcq6U4vS9bS6zZ7jslw0Oczx4UsYgOsIUXSSBaGOrRbxJ/JC5gnDYEYvtNM+PDPczLNKAyhdvBZWNWHr7MZ0P5TeJQcXsAoShRX2Y8U8fRNJm7SeiFKDP0Nn/QKxOSt7zGP4xt9nMasE1q2ZTdar2+W13CRz37RI0ZWpq/+YquoEbZ7Uj7NmBTcqhb260nmDER2FpwwYwPSark92IZbamozB8d7OEI1jJgsrjJhKan0EmRaWVBpHT4xYKdEu7r09S0JhKyU+52WDmmVQTMpYLrm4Xl7hRxyPyBYkalrozsGmPs8vlhNq3VsVbyBSMSpEmUaeAa7LLE9/Vh0agJLVFHh1ehYKJpzHnmmBXUqx0Fz3afmDm1NX0sr3O/6xIx1VSTViT3KNxBYpVH1qjHATLzuxcWmm+75fcJMiPYPSMXVmRb3Q1l91AM4BBeWhlP3Fbc7gDy0r+s7m0sGS6PT2J2rGog2rUxnJ+zCM11M7DeO0XM2nny4uRYPPk9w2EXzfvtdvieYU/5RB4RDm5TGxHhGXVZUgac5AgMBAAEwDQYJKoZIhvcNAQELBQADggIBAFt2XGd3k5GpbO1EUm3u60zT1fE6u6pOscp156k5VnsHgaHRHdIAPNLeLNmR7y5OnrXbh13CrGwU1q84jjJXpv+v14xUCc5i01yopFTQFLr4A7NHp2nNYfNhhIVSFAgW43EflJflbLEelCJzxLlWb5BoDsZeeNmEQsXIM1mJ26R3r0dzsHBb0uy+8LNR1gdVqdjhC8BLy3gh4+BWuidyZNt07LveDsSFW5rcj5wRrSx9hXPIyVpjQSljNvY7MVTouqJzNAAQMsTKkXPkTXldCop9Qo9UPkHRRm0l7LLtdaOoXrct0Ymocf8zxf9bFNiw9f4WRYQM6sMhzt8+s/oDilo4QhcUgeJEiEPESi6ynYTV62SHA4eMunUJ5dlCaRnFiR9DTImFa5IRzie326/nW/SPCaKc/yrFIihMMjJoSAPhpTb/K6yHOUG8r+KiQut7NzqGV301pQ9u62dGL5Oi1VXmCFlE2ramZs15BNOUyAo2CBbRJg3jKcdu/8QC6ojjDvQ863+7LPtn74wJC5RpUJsS0GhQWgq5pAXO3wA61Uobxi6MkOpCC0zBWx/d4CqpS4j4hFgxWBTXX48ihPu+hIxIF/AxbqtPvqLMExW/xZITn6ArpWyQ9e4SUVr3n3F33ap1XdDyZ0vwFcm18JQAtsvXT6qCLrWOXnHUgfn/+Viu" + ], + "icon": "data:image/png;base64,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" +} \ No newline at end of file diff --git a/Test/TestFiles/metadata/Secp256R1 Packed Authenticator.json b/Test/TestFiles/metadata/Secp256R1 Packed Authenticator.json new file mode 100644 index 00000000..db5b0b68 --- /dev/null +++ b/Test/TestFiles/metadata/Secp256R1 Packed Authenticator.json @@ -0,0 +1,128 @@ +{ + "legalHeader": "https://fidoalliance.org/metadata/metadata-statement-legal-header/", + "description": "Virtual Packed authenticator", + "aaguid": "00000000-0000-0000-0000-000000000004", + "alternativeDescriptions": { + "en-GB": "Virtual Packed authenticator" + }, + "protocolFamily": "fido2", + "authenticatorVersion": 2, + "upv": [ + { + "major": 1, + "minor": 0 + } + ], + "authenticationAlgorithms": [ + "secp256r1_ecdsa_sha256_raw" + ], + "publicKeyAlgAndEncodings": [ + "cose" + ], + "attestationTypes": [ + "basic_full", + "basic_surrogate" + ], + "schema": 3, + "userVerificationDetails": [ + [ + { + "userVerificationMethod": "none" + } + ], + [ + { + "userVerificationMethod": "presence_internal" + } + ], + [ + { + "userVerificationMethod": "passcode_external", + "caDesc": { + "base": 10, + "minLength": 4 + } + } + ], + [ + { + "userVerificationMethod": "passcode_external", + "caDesc": { + "base": 10, + "minLength": 4 + } + }, + { + "userVerificationMethod": "presence_internal" + } + ] + ], + "keyProtection": [ + "hardware", + "secure_element" + ], + "matcherProtection": [ + "on_chip" + ], + "cryptoStrength": 128, + "attachmentHint": [ + "external", + "wired", + "wireless", + "nfc" + ], + "tcDisplay": [], + "attestationRootCertificates": [ + "MIIFwDCCA6gCCQCNm1u56oRwXTANBgkqhkiG9w0BAQsFADCBoTEYMBYGA1UEAwwPRklETzIgVEVTVCBST09UMTEwLwYJKoZIhvcNAQkBFiJjb25mb3JtYW5jZS10b29sc0BmaWRvYWxsaWFuY2Uub3JnMRYwFAYDVQQKDA1GSURPIEFsbGlhbmNlMQwwCgYDVQQLDANDV0cxCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJNWTESMBAGA1UEBwwJV2FrZWZpZWxkMB4XDTE4MDMxNjE0MzUyN1oXDTQ1MDgwMTE0MzUyN1owgaExGDAWBgNVBAMMD0ZJRE8yIFRFU1QgUk9PVDExMC8GCSqGSIb3DQEJARYiY29uZm9ybWFuY2UtdG9vbHNAZmlkb2FsbGlhbmNlLm9yZzEWMBQGA1UECgwNRklETyBBbGxpYW5jZTEMMAoGA1UECwwDQ1dHMQswCQYDVQQGEwJVUzELMAkGA1UECAwCTVkxEjAQBgNVBAcMCVdha2VmaWVsZDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAL11U5yAIVLMrL3xS8u8ysMSdOkDeoTO+RcAy+uXXp6k4SC+jOy37gICEtYI+MKQV1EMeMMf3rM1ueZAO3iPFa0NEdi/oQ7npnGjBNI8wMzD8FfNe6rWtzkDaHpsZW///MwWDpGyJR+Xyjcq6U4vS9bS6zZ7jslw0Oczx4UsYgOsIUXSSBaGOrRbxJ/JC5gnDYEYvtNM+PDPczLNKAyhdvBZWNWHr7MZ0P5TeJQcXsAoShRX2Y8U8fRNJm7SeiFKDP0Nn/QKxOSt7zGP4xt9nMasE1q2ZTdar2+W13CRz37RI0ZWpq/+YquoEbZ7Uj7NmBTcqhb260nmDER2FpwwYwPSark92IZbamozB8d7OEI1jJgsrjJhKan0EmRaWVBpHT4xYKdEu7r09S0JhKyU+52WDmmVQTMpYLrm4Xl7hRxyPyBYkalrozsGmPs8vlhNq3VsVbyBSMSpEmUaeAa7LLE9/Vh0agJLVFHh1ehYKJpzHnmmBXUqx0Fz3afmDm1NX0sr3O/6xIx1VSTViT3KNxBYpVH1qjHATLzuxcWmm+75fcJMiPYPSMXVmRb3Q1l91AM4BBeWhlP3Fbc7gDy0r+s7m0sGS6PT2J2rGog2rUxnJ+zCM11M7DeO0XM2nny4uRYPPk9w2EXzfvtdvieYU/5RB4RDm5TGxHhGXVZUgac5AgMBAAEwDQYJKoZIhvcNAQELBQADggIBAFt2XGd3k5GpbO1EUm3u60zT1fE6u6pOscp156k5VnsHgaHRHdIAPNLeLNmR7y5OnrXbh13CrGwU1q84jjJXpv+v14xUCc5i01yopFTQFLr4A7NHp2nNYfNhhIVSFAgW43EflJflbLEelCJzxLlWb5BoDsZeeNmEQsXIM1mJ26R3r0dzsHBb0uy+8LNR1gdVqdjhC8BLy3gh4+BWuidyZNt07LveDsSFW5rcj5wRrSx9hXPIyVpjQSljNvY7MVTouqJzNAAQMsTKkXPkTXldCop9Qo9UPkHRRm0l7LLtdaOoXrct0Ymocf8zxf9bFNiw9f4WRYQM6sMhzt8+s/oDilo4QhcUgeJEiEPESi6ynYTV62SHA4eMunUJ5dlCaRnFiR9DTImFa5IRzie326/nW/SPCaKc/yrFIihMMjJoSAPhpTb/K6yHOUG8r+KiQut7NzqGV301pQ9u62dGL5Oi1VXmCFlE2ramZs15BNOUyAo2CBbRJg3jKcdu/8QC6ojjDvQ863+7LPtn74wJC5RpUJsS0GhQWgq5pAXO3wA61Uobxi6MkOpCC0zBWx/d4CqpS4j4hFgxWBTXX48ihPu+hIxIF/AxbqtPvqLMExW/xZITn6ArpWyQ9e4SUVr3n3F33ap1XdDyZ0vwFcm18JQAtsvXT6qCLrWOXnHUgfn/+Viu" + ], + "icon": "data:image/png;base64,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", + "supportedExtensions": [ + { + "id": "hmac-secret", + "fail_if_unknown": false + }, + { + "id": "credProtect", + "fail_if_unknown": false + } + ], + "authenticatorGetInfo": { + "versions": [ + "U2F_V2", + "FIDO_2_0" + ], + "extensions": [ + "credProtect", + "hmac-secret" + ], + "aaguid": "326adcf00cef46d0939298d6c4a84a72", + "options": { + "plat": false, + "rk": true, + "clientPin": true, + "up": true, + "uv": true, + "uvToken": false, + "config": false + }, + "maxMsgSize": 1200, + "pinUvAuthProtocols": [ + 1 + ], + "maxCredentialCountInList": 16, + "maxCredentialIdLength": 128, + "transports": [ + "usb", + "nfc" + ], + "algorithms": [ + { + "type": "public-key", + "alg": -7 + } + ], + "maxAuthenticatorConfigLength": 1024, + "defaultCredProtect": 2, + "firmwareVersion": 5 + } +} \ No newline at end of file diff --git a/Test/TestMetadataService.cs b/Test/TestMetadataService.cs new file mode 100644 index 00000000..760656cc --- /dev/null +++ b/Test/TestMetadataService.cs @@ -0,0 +1,21 @@ +using Fido2NetLib; + +namespace Test +{ + public class TestMetadataService : ConformanceMetadataService + { + public TestMetadataService(IEnumerable repositories) : base(repositories) + { + } + + public void ChangeEntryGuid(Guid oldGuid, Guid newGuid) + { + if (!_entries.ContainsKey(oldGuid)) + return; + + _entries.Remove(oldGuid, out var entry); + entry.AaGuid = newGuid; + _entries.TryAdd(newGuid, entry); + } + } +}