From ae3b46a1c0d8fc67c79a15faa5a95e053e53c7b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20=C3=85berg?= Date: Wed, 30 Oct 2024 14:30:25 +0100 Subject: [PATCH 1/6] Removed DevicePubKey p1 --- .../Server/Controllers/UserController.cs | 8 +- Demo/Controller.cs | 9 +- Demo/TestController.cs | 6 +- Src/Fido2.Development/StoredCredential.cs | 2 - .../Objects/VerifyAssertionResult.cs | 5 - Src/Fido2/AuthenticatorAssertionResponse.cs | 194 +----------------- Src/Fido2/Fido2.cs | 1 - Src/Fido2/MakeAssertionParams.cs | 5 - Tests/Fido2.Tests/AuthenticatorResponse.cs | 51 ++--- .../ExistingU2fRegistrationDataTests.cs | 3 +- 10 files changed, 25 insertions(+), 259 deletions(-) diff --git a/BlazorWasmDemo/Server/Controllers/UserController.cs b/BlazorWasmDemo/Server/Controllers/UserController.cs index 1ec1719e..ba62abe6 100644 --- a/BlazorWasmDemo/Server/Controllers/UserController.cs +++ b/BlazorWasmDemo/Server/Controllers/UserController.cs @@ -168,7 +168,6 @@ public async Task CreateCredentialAsync([FromRoute] string username, [Fr SignCount = credential.SignCount, RegDate = DateTimeOffset.UtcNow, AaGuid = credential.AaGuid, - DevicePublicKeys = [credential.DevicePublicKey], Transports = credential.Transports, IsBackupEligible = credential.IsBackupEligible, IsBackedUp = credential.IsBackedUp, @@ -277,16 +276,11 @@ public async Task MakeAssertionAsync([FromBody] AuthenticatorAssertionRa OriginalOptions = options, StoredPublicKey = creds.PublicKey, StoredSignatureCounter = creds.SignCount, - IsUserHandleOwnerOfCredentialIdCallback = UserHandleOwnerOfCredentialIdAsync, - StoredDevicePublicKeys = creds.DevicePublicKeys + IsUserHandleOwnerOfCredentialIdCallback = UserHandleOwnerOfCredentialIdAsync }, cancellationToken: cancellationToken); // 4. Store the updated counter _demoStorage.UpdateCounter(res.CredentialId, res.SignCount); - if (res.DevicePublicKey is not null) - { - creds.DevicePublicKeys.Add(res.DevicePublicKey); - } // 5. return result to client diff --git a/Demo/Controller.cs b/Demo/Controller.cs index b0a0273b..5722adc9 100644 --- a/Demo/Controller.cs +++ b/Demo/Controller.cs @@ -127,8 +127,7 @@ public async Task MakeCredential([FromBody] AuthenticatorAttestation IsBackupEligible = credential.IsBackupEligible, IsBackedUp = credential.IsBackedUp, AttestationObject = credential.AttestationObject, - AttestationClientDataJson = credential.AttestationClientDataJson, - DevicePublicKeys = [credential.DevicePublicKey] + AttestationClientDataJson = credential.AttestationClientDataJson }); // 4. return "ok" to the client @@ -215,16 +214,12 @@ public async Task MakeAssertion([FromBody] AuthenticatorAssertionRaw OriginalOptions = options, StoredPublicKey = creds.PublicKey, StoredSignatureCounter = storedCounter, - IsUserHandleOwnerOfCredentialIdCallback = callback, - StoredDevicePublicKeys = creds.DevicePublicKeys + IsUserHandleOwnerOfCredentialIdCallback = callback }, cancellationToken: cancellationToken); // 6. Store the updated counter DemoStorage.UpdateCounter(res.CredentialId, res.SignCount); - if (res.DevicePublicKey is not null) - creds.DevicePublicKeys.Add(res.DevicePublicKey); - // 7. return OK to client return Json(res); } diff --git a/Demo/TestController.cs b/Demo/TestController.cs index 2ccf4fdc..eca9ea6b 100644 --- a/Demo/TestController.cs +++ b/Demo/TestController.cs @@ -188,16 +188,12 @@ public async Task MakeAssertionTestAsync([FromBody] AuthenticatorAss OriginalOptions = options, StoredPublicKey = creds.PublicKey, StoredSignatureCounter = storedCounter, - IsUserHandleOwnerOfCredentialIdCallback = callback, - StoredDevicePublicKeys = creds.DevicePublicKeys + IsUserHandleOwnerOfCredentialIdCallback = callback }, cancellationToken: cancellationToken); // 6. Store the updated counter _demoStorage.UpdateCounter(res.CredentialId, res.SignCount); - if (res.DevicePublicKey is not null) - creds.DevicePublicKeys.Add(res.DevicePublicKey); - // 7. return OK to client return Json(new { diff --git a/Src/Fido2.Development/StoredCredential.cs b/Src/Fido2.Development/StoredCredential.cs index 7d0e048f..6314e606 100644 --- a/Src/Fido2.Development/StoredCredential.cs +++ b/Src/Fido2.Development/StoredCredential.cs @@ -48,8 +48,6 @@ public class StoredCredential /// public byte[] AttestationClientDataJson { get; set; } - public List DevicePublicKeys { get; set; } - public byte[] UserId { get; set; } /// diff --git a/Src/Fido2.Models/Objects/VerifyAssertionResult.cs b/Src/Fido2.Models/Objects/VerifyAssertionResult.cs index dc397c73..812507c7 100644 --- a/Src/Fido2.Models/Objects/VerifyAssertionResult.cs +++ b/Src/Fido2.Models/Objects/VerifyAssertionResult.cs @@ -16,9 +16,4 @@ public class VerifyAssertionResult /// The latest value of the BS flag in the authenticator data from any ceremony using the public key credential source. /// public bool IsBackedUp { get; init; } - - /// - /// The public key portion of a hardware-bound device key pair - /// - public byte[] DevicePublicKey { get; init; } } diff --git a/Src/Fido2/AuthenticatorAssertionResponse.cs b/Src/Fido2/AuthenticatorAssertionResponse.cs index a47d4be3..62d1f425 100644 --- a/Src/Fido2/AuthenticatorAssertionResponse.cs +++ b/Src/Fido2/AuthenticatorAssertionResponse.cs @@ -48,7 +48,6 @@ public static AuthenticatorAssertionResponse Parse(AuthenticatorAssertionRawResp /// The original assertion options that was sent to the client. /// /// The stored public key for this CredentialId. - /// The stored device public key for this CredentialId. /// The stored counter value for this CredentialId /// A function that returns if user handle is owned by the credential ID. /// @@ -58,7 +57,6 @@ public async Task VerifyAsync( AssertionOptions options, Fido2Configuration config, byte[] storedPublicKey, - IReadOnlyList storedDevicePublicKeys, uint storedSignatureCounter, IsUserHandleOwnerOfCredentialIdAsync isUserHandleOwnerOfCredId, IMetadataService? metadataService, @@ -147,14 +145,7 @@ public async Task VerifyAsync( !authData.IsBackedUp && config.BackedUpCredentialPolicy is Fido2Configuration.CredentialBackupPolicy.Required) throw new Fido2VerificationException(Fido2ErrorCode.BackupStateRequirementNotMet, Fido2ErrorMessages.BackupStateRequirementNotMet); - // 17. Verify that the values of the client extension outputs in clientExtensionResults and the authenticator extension outputs in the extensions in authData are as expected, - // considering the client extension input values that were given in options.extensions and any specific policy of the Relying Party regarding unsolicited extensions, - // i.e., those that were not specified as part of options.extensions. In the general case, the meaning of "are as expected" is specific to the Relying Party and which extensions are in use. - byte[]? devicePublicKeyResult = null; - if (Raw.ClientExtensionResults?.DevicePubKey is not null) - { - devicePublicKeyResult = await DevicePublicKeyAuthenticationAsync(storedDevicePublicKeys, Raw.ClientExtensionResults, AuthenticatorData, hash).ConfigureAwait(false); - } + // Pretty sure these conditions are not able to be met due to the AuthenticatorData constructor implementation if (authData.HasExtensionsData && (authData.Extensions is null || authData.Extensions.Length is 0)) @@ -186,187 +177,8 @@ public async Task VerifyAsync( { CredentialId = Raw.Id, SignCount = authData.SignCount, - IsBackedUp = authData.IsBackedUp, - DevicePublicKey = devicePublicKeyResult, + IsBackedUp = authData.IsBackedUp + }; } - - /// - /// If the devicePubKey extension was included on a navigator.credentials.get() call, then the below - /// verification steps are performed in the context of this step of § 7.2 Verifying an Authentication Assertion using - /// these variables established therein: credential, clientExtensionResults, authData, and hash. Relying Party policy - /// may specify whether a response without a devicePubKey is acceptable. - /// - /// - /// - /// - /// - /// - private static async ValueTask DevicePublicKeyAuthenticationAsync( - IReadOnlyList storedDevicePublicKeys, - AuthenticationExtensionsClientOutputs clientExtensionResults, - AuthenticatorData authData, - byte[] hash) - { - // 1. Let attObjForDevicePublicKey be the value of the devicePubKey member of clientExtensionResults. - var attObjForDevicePublicKey = clientExtensionResults.DevicePubKey!; - - // 2. Verify that attObjForDevicePublicKey is valid CBOR conforming to the syntax defined above and - // perform CBOR decoding on it to extract the contained fields: aaguid, dpk, scope, nonce, fmt, attStmt. - var devicePublicKeyAuthenticatorOutput = DevicePublicKeyAuthenticatorOutput.Parse(attObjForDevicePublicKey.AuthenticatorOutput); - - // 3. Verify that signature is a valid signature over the assertion signature input (i.e. authData and hash) by the device public key dpk. - if (!devicePublicKeyAuthenticatorOutput.DevicePublicKey.Verify([.. authData.ToByteArray(), .. hash], attObjForDevicePublicKey.Signature)) - throw new Fido2VerificationException(Fido2ErrorCode.InvalidSignature, Fido2ErrorMessages.InvalidSignature); - - // 4. If the Relying Party's user account mapped to the credential.id in play (i.e., for the user being - // authenticated) holds aaguid, dpk and scope values corresponding to the extracted attObjForDevicePublicKey - // fields, then perform binary equality checks between the corresponding stored values and the extracted field - // values. The Relying Party MAY have more than one set of {aaguid, dpk, scope} values mapped to the user - // account and credential.id pair and each set MUST be checked. - if (storedDevicePublicKeys.Count > 0) - { - var matchedDpkRecords = new List(); - - foreach (var storedDevicePublicKey in storedDevicePublicKeys) - { - var dpkRecord = DevicePublicKeyAuthenticatorOutput.Parse(storedDevicePublicKey); - if (dpkRecord.GetAuthenticationMatcher().SequenceEqual(devicePublicKeyAuthenticatorOutput.GetAuthenticationMatcher()) - && dpkRecord.Scope.Equals(devicePublicKeyAuthenticatorOutput.Scope)) - { - matchedDpkRecords.Add(dpkRecord); - } - } - - // more than one match - if (matchedDpkRecords.Count > 1) - { - // Some form of error has occurred. It is indeterminate whether this is a known device. Terminate these verification steps. - throw new Fido2VerificationException(Fido2ErrorCode.DevicePublicKeyAuthentication, Fido2ErrorMessages.NonUniqueDevicePublicKey); - } - // exactly one match - else if (matchedDpkRecords.Count is 1) - { - // This is likely a known device. - // If fmt's value is "none" then there is no attestation signature to verify and this is a known device public key with a valid signature and thus a known device. Terminate these verification steps. - if (devicePublicKeyAuthenticatorOutput.Fmt is "none") - { - return null; - } - // Otherwise, check attObjForDevicePublicKey's attStmt by performing a binary equality check between the corresponding stored and extracted attStmt values. - else if (devicePublicKeyAuthenticatorOutput.AttStmt.Encode().SequenceEqual(matchedDpkRecords.First().AttStmt.Encode())) - { - // Note: This authenticator is not generating a fresh per-response random nonce. - return null; - } - else - { - // Optionally, if attestation was requested and the RP wishes to verify it, verify that attStmt - // is a correct attestation statement, conveying a valid attestation signature, by using the - // attestation statement format fmt’s verification procedure given attStmt. See § 10.2.2.2.2 - // Attestation calculations. Relying Party policy may specify which attestations are acceptable. - // https://www.w3.org/TR/webauthn/#defined-attestation-formats - var verifier = AttestationVerifier.Create(devicePublicKeyAuthenticatorOutput.Fmt); - - // https://w3c.github.io/webauthn/#sctn-device-publickey-attestation-calculations - try - { - // This is a known device public key with a valid signature and valid attestation and thus a known device. Terminate these verification steps. - _ = await verifier.VerifyAsync(devicePublicKeyAuthenticatorOutput.AttStmt, devicePublicKeyAuthenticatorOutput.GetAuthenticatorData(), devicePublicKeyAuthenticatorOutput.GetHash()).ConfigureAwait(false); - } - catch (Exception ex) - { - // Some form of error has occurred. It is indeterminate whether this is a known device. Terminate these verification steps. - throw new Fido2VerificationException(Fido2ErrorCode.DevicePublicKeyAuthentication, Fido2ErrorMessages.InvalidDevicePublicKeyAttestation, ex); - } - } - } - // This is possibly a new device public key signifying a new device. - else if (matchedDpkRecords.Count == 0) - { - // Let matchedDpkKeys be a new empty set - List matchedDpkKeys = new(); - - // For each dpkRecord in credentialRecord.devicePubKeys - foreach (var storedDevicePublicKey in storedDevicePublicKeys) - { - var dpkRecord = DevicePublicKeyAuthenticatorOutput.Parse(storedDevicePublicKey); - - // If dpkRecord.dpk equals dpk - if (dpkRecord.DevicePublicKey.GetBytes().SequenceEqual(devicePublicKeyAuthenticatorOutput.DevicePublicKey.GetBytes())) - { - // Append dpkRecord to matchedDpkKeys. - matchedDpkKeys.Add(dpkRecord); - } - } - - // If matchedDpkKeys is empty - if (matchedDpkKeys.Count == 0) - { - // If fmt’s value is "none" - if (devicePublicKeyAuthenticatorOutput.Fmt.Equals("none")) - // There is no attestation signature to verify and this is a new device. - // Unless Relying Party policy specifies that this attestation is unacceptable, Create a new device-bound key record and then terminate these verification steps. - return devicePublicKeyAuthenticatorOutput.Encode(); - - // Otherwise - else - { - // Optionally, if attestation was requested and the RP wishes to verify it, verify that attStmt is a correct attestation statement, conveying a valid attestation signature, by using the attestation statement format fmt’s verification procedure given attStmt. See § 10.2.2.2.2 Attestation calculations. - // Relying Party policy may specify which attestations are acceptable. - var verifier = AttestationVerifier.Create(devicePublicKeyAuthenticatorOutput.Fmt); - // https://w3c.github.io/webauthn/#sctn-device-publickey-attestation-calculations - try - { - // This is a known device public key with a valid signature and valid attestation and thus a known device. Terminate these verification steps. - _ = await verifier.VerifyAsync(devicePublicKeyAuthenticatorOutput.AttStmt, devicePublicKeyAuthenticatorOutput.GetAuthenticatorData(), devicePublicKeyAuthenticatorOutput.GetHash()).ConfigureAwait(false); - return devicePublicKeyAuthenticatorOutput.Encode(); - } - catch (Exception ex) - { - // Some form of error has occurred. It is indeterminate whether this is a known device. Terminate these verification steps. - throw new Fido2VerificationException(Fido2ErrorCode.DevicePublicKeyAuthentication, Fido2ErrorMessages.InvalidDevicePublicKeyAttestation, ex); - } - } - } - else - { - // Otherwise there is some form of error: we received a known dpk value, but one or more of the - // accompanying aaguid, scope, or fmt values did not match what the Relying Party has stored - // along with that dpk value. Terminate these verification steps. - throw new Fido2VerificationException(Fido2ErrorCode.DevicePublicKeyAuthentication, Fido2ErrorMessages.MissingStoredPublicKey); - } - } - } - - // Otherwise, the Relying Party does not have attObjForDevicePublicKey fields presently mapped to this user account and credential.id pair: - else - { - // If fmt’s value is "none" there is no attestation signature to verify. - // Complete the steps in § 7.2 Verifying an Authentication Assertion and, if those steps are successful, store the extracted aaguid, dpk, scope, fmt, attStmt values indexed to the credential.id in the user account. - // Terminate these verification steps. - if (devicePublicKeyAuthenticatorOutput.Fmt.Equals("none")) - return devicePublicKeyAuthenticatorOutput.Encode(); - // Otherwise, verify that attStmt is a correct attestation statement, conveying a valid attestation signature, by using the attestation statement format fmt’s verification procedure given attStmt. See § 10.2.2.2.2 Attestation calculations. - // Relying Party policy may specify which attestations are acceptable. - else - { - var verifier = AttestationVerifier.Create(devicePublicKeyAuthenticatorOutput.Fmt); - // https://w3c.github.io/webauthn/#sctn-device-publickey-attestation-calculations - try - { - // This is a known device public key with a valid signature and valid attestation and thus a known device. Terminate these verification steps. - _ = await verifier.VerifyAsync(devicePublicKeyAuthenticatorOutput.AttStmt, devicePublicKeyAuthenticatorOutput.GetAuthenticatorData(), devicePublicKeyAuthenticatorOutput.GetHash()).ConfigureAwait(false); - return devicePublicKeyAuthenticatorOutput.Encode(); - } - catch - { - // Some form of error has occurred. It is indeterminate whether this is a known device. Terminate these verification steps. - throw new Fido2VerificationException(Fido2ErrorCode.MissingStoredPublicKey, Fido2ErrorMessages.MissingStoredPublicKey); - } - } - } - - return null; - } } diff --git a/Src/Fido2/Fido2.cs b/Src/Fido2/Fido2.cs index 665252a8..d2081ff5 100644 --- a/Src/Fido2/Fido2.cs +++ b/Src/Fido2/Fido2.cs @@ -105,7 +105,6 @@ public async Task MakeAssertionAsync(MakeAssertionParams var result = await parsedResponse.VerifyAsync(makeAssertionParams.OriginalOptions, _config, makeAssertionParams.StoredPublicKey, - makeAssertionParams.StoredDevicePublicKeys, makeAssertionParams.StoredSignatureCounter, makeAssertionParams.IsUserHandleOwnerOfCredentialIdCallback, _metadataService, diff --git a/Src/Fido2/MakeAssertionParams.cs b/Src/Fido2/MakeAssertionParams.cs index 3403f4eb..312f9b2b 100644 --- a/Src/Fido2/MakeAssertionParams.cs +++ b/Src/Fido2/MakeAssertionParams.cs @@ -34,11 +34,6 @@ public sealed class MakeAssertionParams /// public required IsUserHandleOwnerOfCredentialIdAsync IsUserHandleOwnerOfCredentialIdCallback { get; init; } - /// - /// The stored device public keys. - /// - public IReadOnlyList StoredDevicePublicKeys { get; init; } = Array.Empty(); - /// /// DO NOT USE - Deprecated, but kept in code due to conformance testing tool. /// diff --git a/Tests/Fido2.Tests/AuthenticatorResponse.cs b/Tests/Fido2.Tests/AuthenticatorResponse.cs index 6528ab2c..de9c0af5 100644 --- a/Tests/Fido2.Tests/AuthenticatorResponse.cs +++ b/Tests/Fido2.Tests/AuthenticatorResponse.cs @@ -1384,8 +1384,7 @@ public async Task TestAuthenticatorAssertionTypeNotPublicKey() OriginalOptions = options, StoredPublicKey = null, StoredSignatureCounter = 0, - IsUserHandleOwnerOfCredentialIdCallback = callback, - StoredDevicePublicKeys = null + IsUserHandleOwnerOfCredentialIdCallback = callback })); Assert.Equal(Fido2ErrorMessages.AssertionResponseNotPublicKey, ex.Message); } @@ -1460,8 +1459,7 @@ public async Task TestAuthenticatorAssertionIdMissing() OriginalOptions = options, StoredPublicKey = null, StoredSignatureCounter = 0, - IsUserHandleOwnerOfCredentialIdCallback = callback, - StoredDevicePublicKeys = null + IsUserHandleOwnerOfCredentialIdCallback = callback })); Assert.Equal(Fido2ErrorMessages.AssertionResponseIdMissing, ex.Message); } @@ -1537,8 +1535,7 @@ public async Task TestAuthenticatorAssertionRawIdMissing() OriginalOptions = options, StoredPublicKey = null, StoredSignatureCounter = 0, - IsUserHandleOwnerOfCredentialIdCallback = callback, - StoredDevicePublicKeys = null + IsUserHandleOwnerOfCredentialIdCallback = callback })); Assert.Equal(Fido2ErrorMessages.AssertionResponseRawIdMissing, ex.Message); } @@ -1614,8 +1611,7 @@ public async Task TestAuthenticatorAssertionUserHandleEmpty() OriginalOptions = options, StoredPublicKey = null, StoredSignatureCounter = 0, - IsUserHandleOwnerOfCredentialIdCallback = callback, - StoredDevicePublicKeys = null + IsUserHandleOwnerOfCredentialIdCallback = callback })); Assert.Equal(Fido2ErrorMessages.UserHandleIsEmpty, ex.Message); } @@ -1691,8 +1687,7 @@ public async Task TestAuthenticatorAssertionUserHandleNotOwnerOfPublicKey() OriginalOptions = options, StoredPublicKey = null, StoredSignatureCounter = 0, - IsUserHandleOwnerOfCredentialIdCallback = callback, - StoredDevicePublicKeys = null + IsUserHandleOwnerOfCredentialIdCallback = callback })); Assert.Equal(Fido2ErrorMessages.UserHandleNotOwnerOfPublicKey, ex.Message); } @@ -1768,8 +1763,7 @@ public async Task TestAuthenticatorAssertionTypeNotWebAuthnGet() OriginalOptions = options, StoredPublicKey = null, StoredSignatureCounter = 0, - IsUserHandleOwnerOfCredentialIdCallback = callback, - StoredDevicePublicKeys = null + IsUserHandleOwnerOfCredentialIdCallback = callback })); Assert.Equal(Fido2ErrorMessages.AssertionResponseTypeNotWebAuthnGet, ex.Message); } @@ -1847,8 +1841,7 @@ public async Task TestAuthenticatorAssertionAppId() OriginalOptions = options, StoredPublicKey = null, StoredSignatureCounter = 0, - IsUserHandleOwnerOfCredentialIdCallback = callback, - StoredDevicePublicKeys = null + IsUserHandleOwnerOfCredentialIdCallback = callback })); Assert.Equal(Fido2ErrorMessages.InvalidRpidHash, ex.Message); } @@ -1925,8 +1918,7 @@ public async Task TestAuthenticatorAssertionInvalidRpIdHash() OriginalOptions = options, StoredPublicKey = null, StoredSignatureCounter = 0, - IsUserHandleOwnerOfCredentialIdCallback = callback, - StoredDevicePublicKeys = null + IsUserHandleOwnerOfCredentialIdCallback = callback })); Assert.Equal(Fido2ErrorMessages.InvalidRpidHash, ex.Message); } @@ -2003,8 +1995,7 @@ public async Task TestAuthenticatorAssertionUPRequirementNotMet() OriginalOptions = options, StoredPublicKey = null, StoredSignatureCounter = 0, - IsUserHandleOwnerOfCredentialIdCallback = callback, - StoredDevicePublicKeys = null + IsUserHandleOwnerOfCredentialIdCallback = callback })); Assert.Equal(Fido2ErrorMessages.UserPresentFlagNotSet, ex.Message); } @@ -2081,8 +2072,7 @@ public async Task TestAuthenticatorAssertionUVPolicyNotMet() OriginalOptions = options, StoredPublicKey = null, StoredSignatureCounter = 0, - IsUserHandleOwnerOfCredentialIdCallback = callback, - StoredDevicePublicKeys = null + IsUserHandleOwnerOfCredentialIdCallback = callback })); Assert.Equal(Fido2ErrorMessages.UserVerificationRequirementNotMet, ex.Message); } @@ -2158,8 +2148,7 @@ public async Task TestAuthenticatorAssertionBEPolicyRequired() OriginalOptions = options, StoredPublicKey = null, StoredSignatureCounter = 0, - IsUserHandleOwnerOfCredentialIdCallback = callback, - StoredDevicePublicKeys = null + IsUserHandleOwnerOfCredentialIdCallback = callback })); Assert.Equal(Fido2ErrorMessages.BackupEligibilityRequirementNotMet, ex.Message); } @@ -2235,8 +2224,7 @@ public async Task TestAuthenticatorAssertionBEPolicyDisallow() OriginalOptions = options, StoredPublicKey = null, StoredSignatureCounter = 0, - IsUserHandleOwnerOfCredentialIdCallback = callback, - StoredDevicePublicKeys = null + IsUserHandleOwnerOfCredentialIdCallback = callback })); Assert.Equal(Fido2ErrorMessages.BackupEligibilityRequirementNotMet, ex.Message); } @@ -2312,8 +2300,7 @@ public async Task TestAuthenticatorAssertionBSPolicyRequired() OriginalOptions = options, StoredPublicKey = null, StoredSignatureCounter = 0, - IsUserHandleOwnerOfCredentialIdCallback = callback, - StoredDevicePublicKeys = null + IsUserHandleOwnerOfCredentialIdCallback = callback })); Assert.Equal(Fido2ErrorMessages.BackupStateRequirementNotMet, ex.Message); } @@ -2389,8 +2376,7 @@ public async Task TestAuthenticatorAssertionBSPolicyDisallow() OriginalOptions = options, StoredPublicKey = null, StoredSignatureCounter = 0, - IsUserHandleOwnerOfCredentialIdCallback = callback, - StoredDevicePublicKeys = null + IsUserHandleOwnerOfCredentialIdCallback = callback })); Assert.Equal(Fido2ErrorMessages.BackupStateRequirementNotMet, ex.Message); } @@ -2466,8 +2452,7 @@ public async Task TestAuthenticatorAssertionStoredPublicKeyMissing() OriginalOptions = options, StoredPublicKey = null, StoredSignatureCounter = 0, - IsUserHandleOwnerOfCredentialIdCallback = callback, - StoredDevicePublicKeys = null + IsUserHandleOwnerOfCredentialIdCallback = callback })); Assert.Equal(Fido2ErrorMessages.MissingStoredPublicKey, ex.Message); } @@ -2544,8 +2529,7 @@ public async Task TestAuthenticatorAssertionInvalidSignature() OriginalOptions = options, StoredPublicKey = fido2_net_lib.Test.Fido2Tests.MakeCredentialPublicKey(COSE.KeyType.OKP, COSE.Algorithm.EdDSA, COSE.EllipticCurve.Ed25519, publicKey).GetBytes(), StoredSignatureCounter = 0, - IsUserHandleOwnerOfCredentialIdCallback = callback, - StoredDevicePublicKeys = null + IsUserHandleOwnerOfCredentialIdCallback = callback })); Assert.Equal(Fido2ErrorMessages.InvalidSignature, ex.Message); } @@ -2627,8 +2611,7 @@ public async Task TestAuthenticatorAssertionSignCountSignature() OriginalOptions = options, StoredPublicKey = cpk.GetBytes(), StoredSignatureCounter = 2, - IsUserHandleOwnerOfCredentialIdCallback = callback, - StoredDevicePublicKeys = null + IsUserHandleOwnerOfCredentialIdCallback = callback })); Assert.Equal(Fido2ErrorMessages.SignCountIsLessThanSignatureCounter, ex.Message); } diff --git a/Tests/Fido2.Tests/ExistingU2fRegistrationDataTests.cs b/Tests/Fido2.Tests/ExistingU2fRegistrationDataTests.cs index f740bbbb..d031fbc1 100644 --- a/Tests/Fido2.Tests/ExistingU2fRegistrationDataTests.cs +++ b/Tests/Fido2.Tests/ExistingU2fRegistrationDataTests.cs @@ -62,8 +62,7 @@ public async Task TestFido2AssertionWithExistingU2fRegistrationWithAppId() OriginalOptions = options, StoredPublicKey = publicKey.Encode(), StoredSignatureCounter = 0, - IsUserHandleOwnerOfCredentialIdCallback = null, - StoredDevicePublicKeys = null + IsUserHandleOwnerOfCredentialIdCallback = null }); Assert.NotEmpty(credential.CredentialId); From a7c347ba69823bc415bee07ce4d0920163e178bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20=C3=85berg?= Date: Wed, 30 Oct 2024 14:35:52 +0100 Subject: [PATCH 2/6] Removed DevicePubkey p2 --- .../Server/Controllers/UserController.cs | 9 +- Demo/Controller.cs | 4 +- .../AuthenticationExtensionsClientInputs.cs | 8 -- .../AuthenticationExtensionsClientOutputs.cs | 8 -- ...ticationExtensionsDevicePublicKeyInputs.cs | 16 --- ...icationExtensionsDevicePublicKeyOutputs.cs | 19 ---- .../Objects/RegisteredPublicKeyCredential.cs | 5 - Src/Fido2/AuthenticatorAttestationResponse.cs | 74 +------------- .../DevicePublicKeyAuthenticatorOutput.cs | 97 ------------------- .../Attestation/DevicePublicKey.cs | 20 ---- 10 files changed, 5 insertions(+), 255 deletions(-) delete mode 100644 Src/Fido2.Models/Objects/AuthenticationExtensionsDevicePublicKeyInputs.cs delete mode 100644 Src/Fido2.Models/Objects/AuthenticationExtensionsDevicePublicKeyOutputs.cs delete mode 100644 Src/Fido2/Objects/DevicePublicKeyAuthenticatorOutput.cs delete mode 100644 Tests/Fido2.Tests/Attestation/DevicePublicKey.cs diff --git a/BlazorWasmDemo/Server/Controllers/UserController.cs b/BlazorWasmDemo/Server/Controllers/UserController.cs index ba62abe6..437e9ed8 100644 --- a/BlazorWasmDemo/Server/Controllers/UserController.cs +++ b/BlazorWasmDemo/Server/Controllers/UserController.cs @@ -112,11 +112,7 @@ public CredentialCreateOptions GetCredentialOptions( { Extensions = true, UserVerificationMethod = true, - CredProps = true, - DevicePubKey = new AuthenticationExtensionsDevicePublicKeyInputs - { - Attestation = attestationType?.ToString() ?? AttestationConveyancePreference.None.ToString() - }, + CredProps = true } ); @@ -212,8 +208,7 @@ public AssertionOptions MakeAssertionOptions([FromRoute] string? username, [From var exts = new AuthenticationExtensionsClientInputs { UserVerificationMethod = true, - Extensions = true, - DevicePubKey = new AuthenticationExtensionsDevicePublicKeyInputs() + Extensions = true }; // 2. Create options (usernameless users will be prompted by their device to select a credential from their own list) diff --git a/Demo/Controller.cs b/Demo/Controller.cs index 5722adc9..d2e64357 100644 --- a/Demo/Controller.cs +++ b/Demo/Controller.cs @@ -67,7 +67,6 @@ public JsonResult MakeCredentialOptions([FromForm] string username, { Extensions = true, UserVerificationMethod = true, - DevicePubKey = new AuthenticationExtensionsDevicePublicKeyInputs() { Attestation = attType }, CredProps = true }; @@ -159,8 +158,7 @@ public ActionResult AssertionOptionsPost([FromForm] string username, [FromForm] var exts = new AuthenticationExtensionsClientInputs() { Extensions = true, - UserVerificationMethod = true, - DevicePubKey = new AuthenticationExtensionsDevicePublicKeyInputs() + UserVerificationMethod = true }; // 3. Create options diff --git a/Src/Fido2.Models/Objects/AuthenticationExtensionsClientInputs.cs b/Src/Fido2.Models/Objects/AuthenticationExtensionsClientInputs.cs index f6f8e4d1..308eefac 100644 --- a/Src/Fido2.Models/Objects/AuthenticationExtensionsClientInputs.cs +++ b/Src/Fido2.Models/Objects/AuthenticationExtensionsClientInputs.cs @@ -40,14 +40,6 @@ public sealed class AuthenticationExtensionsClientInputs public bool? UserVerificationMethod { private get; set; } #nullable enable - /// - /// This extension enables use of a user verification method. - /// https://www.w3.org/TR/webauthn/#sctn-uvm-extension - /// - [JsonPropertyName("devicePubKey")] - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - public AuthenticationExtensionsDevicePublicKeyInputs? DevicePubKey { get; set; } - /// /// This client registration extension facilitates reporting certain credential properties known by the client to the requesting WebAuthn Relying Party upon creation of a public key credential source as a result of a registration ceremony. /// diff --git a/Src/Fido2.Models/Objects/AuthenticationExtensionsClientOutputs.cs b/Src/Fido2.Models/Objects/AuthenticationExtensionsClientOutputs.cs index b5f15b59..0d516287 100644 --- a/Src/Fido2.Models/Objects/AuthenticationExtensionsClientOutputs.cs +++ b/Src/Fido2.Models/Objects/AuthenticationExtensionsClientOutputs.cs @@ -36,14 +36,6 @@ public class AuthenticationExtensionsClientOutputs [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public ulong[][]? UserVerificationMethod { get; set; } - /// - /// This authenticator registration extension and authentication extension provides a Relying Party with a "device continuity" signal for backup eligible credentials. - /// https://w3c.github.io/webauthn/#sctn-device-publickey-extension - /// - [JsonPropertyName("devicePubKey")] - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - public AuthenticationExtensionsDevicePublicKeyOutputs? DevicePubKey { get; set; } - /// /// This client registration extension facilitates reporting certain credential properties known by the client to the requesting WebAuthn Relying Party upon creation of a public key credential source as a result of a registration ceremony. /// diff --git a/Src/Fido2.Models/Objects/AuthenticationExtensionsDevicePublicKeyInputs.cs b/Src/Fido2.Models/Objects/AuthenticationExtensionsDevicePublicKeyInputs.cs deleted file mode 100644 index be2624ee..00000000 --- a/Src/Fido2.Models/Objects/AuthenticationExtensionsDevicePublicKeyInputs.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace Fido2NetLib.Objects; - -using System; -using System.Text.Json.Serialization; - -/// -/// Deprecated: DevicePublickeyKey has been deprecated but is kept around in the code base because of conformance testing tools. -/// -public sealed class AuthenticationExtensionsDevicePublicKeyInputs -{ - [JsonPropertyName("attestation")] - public string Attestation { get; set; } = "none"; - - [JsonPropertyName("attestationFormats")] - public IReadOnlyList AttestationFormats { get; set; } = Array.Empty(); -} diff --git a/Src/Fido2.Models/Objects/AuthenticationExtensionsDevicePublicKeyOutputs.cs b/Src/Fido2.Models/Objects/AuthenticationExtensionsDevicePublicKeyOutputs.cs deleted file mode 100644 index 1b799be7..00000000 --- a/Src/Fido2.Models/Objects/AuthenticationExtensionsDevicePublicKeyOutputs.cs +++ /dev/null @@ -1,19 +0,0 @@ -#nullable enable - -namespace Fido2NetLib.Objects; - -using System.Text.Json.Serialization; - -[method: JsonConstructor] -public sealed class AuthenticationExtensionsDevicePublicKeyOutputs( - byte[] authenticatorOutput, - byte[] signature) -{ - [JsonConverter(typeof(Base64UrlConverter))] - [JsonPropertyName("authenticatorOutput")] - public byte[] AuthenticatorOutput { get; } = authenticatorOutput; - - [JsonConverter(typeof(Base64UrlConverter))] - [JsonPropertyName("signature")] - public byte[] Signature { get; } = signature; -} diff --git a/Src/Fido2.Models/Objects/RegisteredPublicKeyCredential.cs b/Src/Fido2.Models/Objects/RegisteredPublicKeyCredential.cs index c7865597..cd8aee9c 100644 --- a/Src/Fido2.Models/Objects/RegisteredPublicKeyCredential.cs +++ b/Src/Fido2.Models/Objects/RegisteredPublicKeyCredential.cs @@ -43,11 +43,6 @@ public class RegisteredPublicKeyCredential /// public bool IsBackedUp { get; init; } - /// - /// The public key portion of a hardware-bound device key pair - /// - public byte[] DevicePublicKey { get; init; } - public Guid AaGuid { get; init; } public Fido2User User { get; init; } diff --git a/Src/Fido2/AuthenticatorAttestationResponse.cs b/Src/Fido2/AuthenticatorAttestationResponse.cs index 075229b4..ca9b7ee8 100644 --- a/Src/Fido2/AuthenticatorAttestationResponse.cs +++ b/Src/Fido2/AuthenticatorAttestationResponse.cs @@ -125,12 +125,6 @@ public async Task VerifyAsync( // in the clientExtensionResults and the extensions in authData MUST be also be present as extension identifier values in the extensions member of options, i.e., // no extensions are present that were not requested. In the general case, the meaning of "are as expected" is specific to the Relying Party and which extensions are in use. // TODO?: Implement sort of like this: ClientExtensions.Keys.Any(x => options.extensions.contains(x); - byte[]? devicePublicKeyResult = null; - - if (Raw.ClientExtensionResults?.DevicePubKey is not null) - { - devicePublicKeyResult = await DevicePublicKeyRegistrationAsync(config, metadataService, Raw.ClientExtensionResults, AttestationObject.AuthData, clientDataHash, cancellationToken).ConfigureAwait(false); - } // 19. Determine the attestation statement format by performing a USASCII case-sensitive match on fmt // against the set of supported WebAuthn Attestation Statement Format Identifier values. @@ -201,74 +195,10 @@ public async Task VerifyAsync( AttestationClientDataJson = Raw.Response.ClientDataJson, User = originalOptions.User, AttestationFormat = AttestationObject.Fmt, - AaGuid = authData.AttestedCredentialData.AaGuid, - DevicePublicKey = devicePublicKeyResult + AaGuid = authData.AttestedCredentialData.AaGuid }; } - - /// - /// If the devicePubKey extension was included on a navigator.credentials.create() call, - /// then the below verification steps are performed in the context of this step of § 7.1 - /// Registering a New Credential using these variables established therein: - /// credential, clientExtensionResults, authData, and hash. - /// Relying Party policy may specify whether a response without a devicePubKey is acceptable. - /// - /// - /// - /// - /// - /// - /// The used to propagate notifications that the operation should be canceled. - /// - private async Task DevicePublicKeyRegistrationAsync( - Fido2Configuration config, - IMetadataService? metadataService, - AuthenticationExtensionsClientOutputs clientExtensionResults, - AuthenticatorData authData, - byte[] hash, - CancellationToken cancellationToken) - { - // 1. Let attObjForDevicePublicKey be the value of the devicePubKey member of clientExtensionResults. - var attObjForDevicePublicKey = clientExtensionResults.DevicePubKey!; - - // 2. Verify that attObjForDevicePublicKey is valid CBOR conforming to the syntax defined above and - // perform CBOR decoding on it to extract the contained fields: aaguid, dpk, scope, nonce, fmt, attStmt. - var devicePublicKeyAuthenticatorOutput = DevicePublicKeyAuthenticatorOutput.Parse(attObjForDevicePublicKey.AuthenticatorOutput); - - // 3. Verify that signature is a valid signature over the assertion signature input (i.e. authData and hash) by the device public key dpk. - if (!devicePublicKeyAuthenticatorOutput.DevicePublicKey.Verify([.. authData.ToByteArray(), .. hash], attObjForDevicePublicKey.Signature)) - throw new Fido2VerificationException(Fido2ErrorCode.InvalidSignature, Fido2ErrorMessages.InvalidSignature); - - // 4. Optionally, if attestation was requested and the Relying Party wishes to verify it, verify that attStmt is a correct attestation statement, conveying a valid attestation signature, - // by using the attestation statement format fmt's verification procedure given attStmt. - // https://www.w3.org/TR/webauthn/#defined-attestation-formats - var verifier = AttestationVerifier.Create(devicePublicKeyAuthenticatorOutput.Fmt); - - // https://w3c.github.io/webauthn/#sctn-device-publickey-attestation-calculations - (var attType, var trustPath) = await verifier.VerifyAsync(devicePublicKeyAuthenticatorOutput.AttStmt, devicePublicKeyAuthenticatorOutput.GetAuthenticatorData(), devicePublicKeyAuthenticatorOutput.GetHash()).ConfigureAwait(false); - - // 5. Complete the steps from § 7.1 Registering a New Credential and, if those steps are successful, - // store the aaguid, dpk, scope, fmt, attStmt values indexed to the credential.id in the user account. - MetadataBLOBPayloadEntry? metadataEntry = null; - if (metadataService != null) - metadataEntry = await metadataService.GetEntryAsync(devicePublicKeyAuthenticatorOutput.AaGuid, cancellationToken); - - // while conformance testing, we must reject any authenticator that we cannot get metadata for - 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, metadataService?.ConformanceTesting() is true ? FidoValidationMode.FidoConformance2024 : FidoValidationMode.Default); - - // Check status reports for authenticator with undesirable status - var latestStatusReport = metadataEntry?.GetLatestStatusReport(); - if (latestStatusReport != null && config.UndesiredAuthenticatorMetadataStatuses.Contains(latestStatusReport.Status)) - { - throw new UndesiredMetadataStatusFido2VerificationException(latestStatusReport); - } - - return devicePublicKeyAuthenticatorOutput.Encode(); - } - + /// /// The AttestationObject after CBOR parsing /// diff --git a/Src/Fido2/Objects/DevicePublicKeyAuthenticatorOutput.cs b/Src/Fido2/Objects/DevicePublicKeyAuthenticatorOutput.cs deleted file mode 100644 index b96516e0..00000000 --- a/Src/Fido2/Objects/DevicePublicKeyAuthenticatorOutput.cs +++ /dev/null @@ -1,97 +0,0 @@ -namespace Fido2NetLib.Objects; - -using System; - -using Fido2NetLib.Cbor; - -public sealed class DevicePublicKeyAuthenticatorOutput -{ - #pragma warning disable format - // https://w3c.github.io/webauthn/#sctn-device-publickey-attestation-calculations - internal static ReadOnlySpan _dpkAuthDataPrefix => [ - 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x20, - 0x6b, 0x65, 0x79, 0x20, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x20, 0x73, 0x69, 0x67, 0x00, 0xff, 0xff, 0xff, 0xff - ]; - #pragma warning restore format - - private readonly byte[] _nonce; - - internal CborMap _map; - - internal DevicePublicKeyAuthenticatorOutput(CborMap map) - { - AaGuid = new Guid((byte[])map["aaguid"]!); - DevicePublicKey = new CredentialPublicKey((byte[])map["dpk"]!); - Scope = (uint)map["scope"]!; - _nonce = (byte[])map["nonce"]!; - Fmt = (string)map["fmt"]!; - AttStmt = (CborMap)map["attStmt"]!; - EpAtt = false; - if ((Fmt is "enterprise") && map["epAtt"] is not null) - EpAtt = (bool)map["epAtt"]!; - _map = map; - } - - /// - /// The AAGUID of the authenticator. Can be used to identify the make and model of the authenticator. - /// - /// - public Guid AaGuid { get; } - - /// - /// The credential public key encoded in COSE_Key format, as defined in - /// Section 7 of RFC8152, using the CTAP2 canonical CBOR encoding form. - /// - /// - public CredentialPublicKey DevicePublicKey { get; } - - /// - /// Whether this key is scoped to the entire device, or a loosely-defined, narrower scope called "app". - /// For example, a "device"-scoped key is expected to be the same between an app and a browser on the same device, while an "app"-scoped key would probably not be. - /// Whatever the scope, a device key is still specific to a given credential and does not provide any ability to link credentials. - /// Whether device-scoped or not, keys are still device-bound. I.e.an app-scoped key does not enjoy lesser protection from extraction. - /// A value of 0x00 means "entire device" ("all apps") scope. - /// 0x01 means "per-app" scope. Values other than 0x00 or 0x01 are reserved for future use. - /// - public uint Scope { get; } - - /// - /// An authenticator-generated random nonce for inclusion in the attestation signature. - /// If the authenticator chooses to not generate a nonce, it sets this to a zero-length byte string. - /// See the note below about "randomNonce" for a discussion on the nonce's purpose. - /// - public ReadOnlySpan Nonce => _nonce; - - /// - /// Attestation statement formats are identified by a string, called an attestation statement format identifier, chosen by the author of the attestation statement format. - /// - /// - public string Fmt { get; } - - /// - /// A CborMap encoded attestation statement. - /// - public CborMap AttStmt { get; } - - /// - /// An optional boolean that indicates whether the attestation statement contains uniquely identifying information. - /// This can only be true when the `attestation` field of the extension input is "enterprise" and either the user-agent or the authenticator permits uniquely identifying attestation for the requested RP ID. - /// - public bool? EpAtt { get; } - - public AuthenticatorData GetAuthenticatorData() => AuthenticatorData.Parse([.. _dpkAuthDataPrefix, .. AaGuid.ToByteArray()]); - - public byte[] GetHash() => [.. DevicePublicKey.GetBytes(), .. Nonce]; - - public ReadOnlySpan GetAuthenticationMatcher() => (byte[])[.. AaGuid.ToByteArray(), .. DevicePublicKey.GetBytes()]; - - public byte[] Encode() => _map.Encode(); - - public static DevicePublicKeyAuthenticatorOutput Parse(byte[] attObjForDevicePublicKey) - { - var cbor = (CborMap)CborObject.Decode(attObjForDevicePublicKey); - - return new DevicePublicKeyAuthenticatorOutput(cbor); - } -} diff --git a/Tests/Fido2.Tests/Attestation/DevicePublicKey.cs b/Tests/Fido2.Tests/Attestation/DevicePublicKey.cs deleted file mode 100644 index 28994f97..00000000 --- a/Tests/Fido2.Tests/Attestation/DevicePublicKey.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System.Text.Json; - -using fido2_net_lib.Test; - -using Fido2NetLib.Objects; - -namespace Test.Attestation; - -public class DevicePublicKey : Fido2Tests.Attestation -{ - [Fact] - public void TestDevicePublicKey() - { - string json = """{"authenticatorOutput":"pmNkcGtYTaUBAgMmIAEhWCBNwZidDC8QQNAffsFaxUKxTbVLxepdV-1_azg-u0-rsCJYIFtht9l1L8g2hqQOo8omnBd9fRj2byJzn1JQqnp19oVbY2ZtdGRub25lZW5vbmNlQGVzY29wZQBmYWFndWlkUAAAAAAAAAAAAAAAAAAAAABnYXR0U3RtdKA=","signature":"MEUCIQDTf2ImngEOi3qHws6gxf6CpquI97oDIl8m_4T2xQO-YwIgdWN7elqNuU-yMZtGpy8hQtL_E-qmZ1_rM2u2nhXYw7A="}"""; - - var model = JsonSerializer.Deserialize(json); - var devicePublicKeyAuthenticatorOutput = DevicePublicKeyAuthenticatorOutput.Parse(model.AuthenticatorOutput); - Assert.Equal("none", devicePublicKeyAuthenticatorOutput.Fmt); - } -} From c174399e0b0cf1b493c59f3249b998088ef76476 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20=C3=85berg?= Date: Wed, 30 Oct 2024 14:37:12 +0100 Subject: [PATCH 3/6] Clean up crumbs --- Src/Fido2.Models/Exceptions/Fido2ErrorCode.cs | 3 +-- Src/Fido2/Fido2ErrorMessages.cs | 3 --- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/Src/Fido2.Models/Exceptions/Fido2ErrorCode.cs b/Src/Fido2.Models/Exceptions/Fido2ErrorCode.cs index 92af43b1..9ecdb9fa 100644 --- a/Src/Fido2.Models/Exceptions/Fido2ErrorCode.cs +++ b/Src/Fido2.Models/Exceptions/Fido2ErrorCode.cs @@ -33,6 +33,5 @@ public enum Fido2ErrorCode UnimplementedAlgorithm, BackupEligibilityRequirementNotMet, BackupStateRequirementNotMet, - CredentialAlgorithmRequirementNotMet, - DevicePublicKeyAuthentication + CredentialAlgorithmRequirementNotMet } diff --git a/Src/Fido2/Fido2ErrorMessages.cs b/Src/Fido2/Fido2ErrorMessages.cs index 0a03fc44..41d80dc2 100644 --- a/Src/Fido2/Fido2ErrorMessages.cs +++ b/Src/Fido2/Fido2ErrorMessages.cs @@ -39,10 +39,7 @@ internal static class Fido2ErrorMessages public static readonly string BackupStateRequirementNotMet = "Backup state does not match policy requirement"; public static readonly string CredentialAlgorithmRequirementNotMet = "Credential algorithm does not match policy requirement"; public static readonly string NonUniqueCredentialId = "CredentialId is not unique to this user"; - public static readonly string MissingAttestationType = "Missing attestation type"; public static readonly string InvalidAttestationCertSubject = "Invalid attestation cert subject"; - public static readonly string InvalidDevicePublicKeyAttestation = "Invalid devicePublicKey attestation"; - public static readonly string NonUniqueDevicePublicKey = "More than one devicePublicKey match"; public static readonly string CredentialIdNotInAllowedCredentials = "Credential ID not in allowed credentials"; public static readonly string UserHandleNotOwnerOfPublicKey = "User is not owner of the public key identified by the credential id"; From d97e99bc55dd65b92be0a231eb36a3c3d8623b40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20=C3=85berg?= Date: Wed, 30 Oct 2024 14:37:42 +0100 Subject: [PATCH 4/6] format --- Src/Fido2/AuthenticatorAssertionResponse.cs | 4 ++-- Src/Fido2/AuthenticatorAttestationResponse.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Src/Fido2/AuthenticatorAssertionResponse.cs b/Src/Fido2/AuthenticatorAssertionResponse.cs index 62d1f425..61f8100b 100644 --- a/Src/Fido2/AuthenticatorAssertionResponse.cs +++ b/Src/Fido2/AuthenticatorAssertionResponse.cs @@ -145,7 +145,7 @@ public async Task VerifyAsync( !authData.IsBackedUp && config.BackedUpCredentialPolicy is Fido2Configuration.CredentialBackupPolicy.Required) throw new Fido2VerificationException(Fido2ErrorCode.BackupStateRequirementNotMet, Fido2ErrorMessages.BackupStateRequirementNotMet); - + // Pretty sure these conditions are not able to be met due to the AuthenticatorData constructor implementation if (authData.HasExtensionsData && (authData.Extensions is null || authData.Extensions.Length is 0)) @@ -178,7 +178,7 @@ public async Task VerifyAsync( CredentialId = Raw.Id, SignCount = authData.SignCount, IsBackedUp = authData.IsBackedUp - + }; } } diff --git a/Src/Fido2/AuthenticatorAttestationResponse.cs b/Src/Fido2/AuthenticatorAttestationResponse.cs index ca9b7ee8..54b9d00a 100644 --- a/Src/Fido2/AuthenticatorAttestationResponse.cs +++ b/Src/Fido2/AuthenticatorAttestationResponse.cs @@ -198,7 +198,7 @@ public async Task VerifyAsync( AaGuid = authData.AttestedCredentialData.AaGuid }; } - + /// /// The AttestationObject after CBOR parsing /// From 7ac70e568962174e8493a9065ecfd7bdcf880e14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20=C3=85berg?= Date: Wed, 30 Oct 2024 20:49:36 +0100 Subject: [PATCH 5/6] Add the comment back in --- Src/Fido2/AuthenticatorAssertionResponse.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Src/Fido2/AuthenticatorAssertionResponse.cs b/Src/Fido2/AuthenticatorAssertionResponse.cs index 61f8100b..5f3e6635 100644 --- a/Src/Fido2/AuthenticatorAssertionResponse.cs +++ b/Src/Fido2/AuthenticatorAssertionResponse.cs @@ -145,7 +145,10 @@ public async Task VerifyAsync( !authData.IsBackedUp && config.BackedUpCredentialPolicy is Fido2Configuration.CredentialBackupPolicy.Required) throw new Fido2VerificationException(Fido2ErrorCode.BackupStateRequirementNotMet, Fido2ErrorMessages.BackupStateRequirementNotMet); - + + // 17. Verify that the values of the client extension outputs in clientExtensionResults and the authenticator extension outputs in the extensions in authData are as expected, + // considering the client extension input values that were given in options.extensions and any specific policy of the Relying Party regarding unsolicited extensions, + // i.e., those that were not specified as part of options.extensions. In the general case, the meaning of "are as expected" is specific to the Relying Party and which extensions are in use. // Pretty sure these conditions are not able to be met due to the AuthenticatorData constructor implementation if (authData.HasExtensionsData && (authData.Extensions is null || authData.Extensions.Length is 0)) From b79a9ab6e1629887b3a4cb4b9a44d4e4b7ebe38e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20=C3=85berg?= Date: Wed, 30 Oct 2024 20:53:36 +0100 Subject: [PATCH 6/6] format --- Src/Fido2/AuthenticatorAssertionResponse.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Src/Fido2/AuthenticatorAssertionResponse.cs b/Src/Fido2/AuthenticatorAssertionResponse.cs index 5f3e6635..30dc5f6f 100644 --- a/Src/Fido2/AuthenticatorAssertionResponse.cs +++ b/Src/Fido2/AuthenticatorAssertionResponse.cs @@ -145,7 +145,7 @@ public async Task VerifyAsync( !authData.IsBackedUp && config.BackedUpCredentialPolicy is Fido2Configuration.CredentialBackupPolicy.Required) throw new Fido2VerificationException(Fido2ErrorCode.BackupStateRequirementNotMet, Fido2ErrorMessages.BackupStateRequirementNotMet); - + // 17. Verify that the values of the client extension outputs in clientExtensionResults and the authenticator extension outputs in the extensions in authData are as expected, // considering the client extension input values that were given in options.extensions and any specific policy of the Relying Party regarding unsolicited extensions, // i.e., those that were not specified as part of options.extensions. In the general case, the meaning of "are as expected" is specific to the Relying Party and which extensions are in use.