From 71291eb21c40000e565cceeb9485e8c06bea0ed9 Mon Sep 17 00:00:00 2001 From: Marlon Regenhardt Date: Mon, 30 Dec 2024 01:06:30 +0100 Subject: [PATCH] Change Id to be received as string instead of decoded RawId is decoded to the raw byte value, while Id is the same value in base64url-encoded form. #513 --- .../Server/Controllers/UserController.cs | 2 +- Demo/Controller.cs | 2 +- Demo/TestController.cs | 2 +- .../AuthenticatorAssertionRawResponse.cs | 3 +- Src/Fido2/AuthenticatorAssertionResponse.cs | 6 ++-- Test/AuthenticatorResponse.cs | 36 +++++++++---------- Test/ExistingU2fRegistrationDataTests.cs | 5 +-- Test/Fido2Tests.cs | 2 +- 8 files changed, 29 insertions(+), 29 deletions(-) diff --git a/BlazorWasmDemo/Server/Controllers/UserController.cs b/BlazorWasmDemo/Server/Controllers/UserController.cs index 26165673..5ac22018 100644 --- a/BlazorWasmDemo/Server/Controllers/UserController.cs +++ b/BlazorWasmDemo/Server/Controllers/UserController.cs @@ -268,7 +268,7 @@ public async Task MakeAssertionAsync([FromBody] AuthenticatorAssertionRa _pendingAssertions.Remove(key); // 2. Get registered credential from database - var creds = _demoStorage.GetCredentialById(clientResponse.Id) ?? throw new Exception("Unknown credentials"); + var creds = _demoStorage.GetCredentialById(clientResponse.RawId) ?? throw new Exception("Unknown credentials"); // 3. Make the assertion var res = await _fido2.MakeAssertionAsync( diff --git a/Demo/Controller.cs b/Demo/Controller.cs index 6f3b1091..6c47dd3d 100644 --- a/Demo/Controller.cs +++ b/Demo/Controller.cs @@ -192,7 +192,7 @@ public async Task MakeAssertion([FromBody] AuthenticatorAssertionRaw var options = AssertionOptions.FromJson(jsonOptions); // 2. Get registered credential from database - var creds = DemoStorage.GetCredentialById(clientResponse.Id) ?? throw new Exception("Unknown credentials"); + var creds = DemoStorage.GetCredentialById(clientResponse.RawId) ?? throw new Exception("Unknown credentials"); // 3. Get credential counter from database var storedCounter = creds.SignCount; diff --git a/Demo/TestController.cs b/Demo/TestController.cs index 60429bac..0f24cef4 100644 --- a/Demo/TestController.cs +++ b/Demo/TestController.cs @@ -154,7 +154,7 @@ public async Task MakeAssertionTestAsync([FromBody] AuthenticatorAss var options = AssertionOptions.FromJson(jsonOptions); // 2. Get registered credential from database - var creds = _demoStorage.GetCredentialById(clientResponse.Id); + var creds = _demoStorage.GetCredentialById(clientResponse.RawId); // 3. Get credential counter from database var storedCounter = creds.SignCount; diff --git a/Src/Fido2.Models/AuthenticatorAssertionRawResponse.cs b/Src/Fido2.Models/AuthenticatorAssertionRawResponse.cs index 472d6928..c2d00b72 100644 --- a/Src/Fido2.Models/AuthenticatorAssertionRawResponse.cs +++ b/Src/Fido2.Models/AuthenticatorAssertionRawResponse.cs @@ -11,9 +11,8 @@ namespace Fido2NetLib; /// public class AuthenticatorAssertionRawResponse { - [JsonConverter(typeof(Base64UrlConverter))] [JsonPropertyName("id")] - public byte[] Id { get; set; } + public string Id { get; set; } // might be wrong to base64url encode this... [JsonConverter(typeof(Base64UrlConverter))] diff --git a/Src/Fido2/AuthenticatorAssertionResponse.cs b/Src/Fido2/AuthenticatorAssertionResponse.cs index c917c4f8..2b6f4421 100644 --- a/Src/Fido2/AuthenticatorAssertionResponse.cs +++ b/Src/Fido2/AuthenticatorAssertionResponse.cs @@ -78,7 +78,7 @@ public async Task VerifyAsync( if (options.AllowCredentials != null && options.AllowCredentials.Any()) { // might need to transform x.Id and raw.id as described in https://www.w3.org/TR/webauthn/#publickeycredential - if (!options.AllowCredentials.Any(x => x.Id.SequenceEqual(Raw.Id))) + if (!options.AllowCredentials.Any(x => x.Id.SequenceEqual(Raw.RawId))) throw new Fido2VerificationException(Fido2ErrorCode.InvalidAssertionResponse, Fido2ErrorMessages.CredentialIdNotInAllowedCredentials); } @@ -88,7 +88,7 @@ public async Task VerifyAsync( if (UserHandle.Length is 0) throw new Fido2VerificationException(Fido2ErrorMessages.UserHandleIsEmpty); - if (await isUserHandleOwnerOfCredId(new IsUserHandleOwnerOfCredentialIdParams(Raw.Id, UserHandle), cancellationToken) is false) + if (await isUserHandleOwnerOfCredId(new IsUserHandleOwnerOfCredentialIdParams(Raw.RawId, UserHandle), cancellationToken) is false) { throw new Fido2VerificationException(Fido2ErrorCode.InvalidAssertionResponse, Fido2ErrorMessages.UserHandleNotOwnerOfPublicKey); } @@ -177,7 +177,7 @@ public async Task VerifyAsync( return new VerifyAssertionResult { Status = "ok", - CredentialId = Raw.Id, + CredentialId = Raw.RawId, SignCount = authData.SignCount, IsBackedUp = authData.IsBackedUp, DevicePublicKey = devicePublicKeyResult, diff --git a/Test/AuthenticatorResponse.cs b/Test/AuthenticatorResponse.cs index 9046c07e..d8d43c90 100644 --- a/Test/AuthenticatorResponse.cs +++ b/Test/AuthenticatorResponse.cs @@ -1232,7 +1232,7 @@ public void TestAuthenticatorAssertionRawResponse() { Response = assertion, Type = PublicKeyCredentialType.PublicKey, - Id = [0xf1, 0xd0], + Id = "8dA", RawId = [0xf1, 0xd0], ClientExtensionResults = new AuthenticationExtensionsClientOutputs { @@ -1258,7 +1258,7 @@ public void TestAuthenticatorAssertionRawResponse() } }; Assert.Equal(PublicKeyCredentialType.PublicKey, assertionResponse.Type); - Assert.Equal([0xf1, 0xd0], assertionResponse.Id); + Assert.Equal("8dA", assertionResponse.Id); Assert.Equal([0xf1, 0xd0], assertionResponse.RawId); Assert.Equal([0xf1, 0xd0], assertionResponse.Response.AuthenticatorData); Assert.Equal([0xf1, 0xd0], assertionResponse.Response.Signature); @@ -1308,7 +1308,7 @@ public async Task TestAuthenticatorAssertionTypeNotPublicKey() { Response = assertion, Type = PublicKeyCredentialType.Invalid, - Id = [0xf1, 0xd0], + Id = "8dA", RawId = [0xf1, 0xd0], ClientExtensionResults = new AuthenticationExtensionsClientOutputs { @@ -1446,7 +1446,7 @@ public async Task TestAuthenticatorAssertionRawIdMissing() { Response = assertion, Type = PublicKeyCredentialType.PublicKey, - Id = [0xf1, 0xd0], + Id = "8dA", ClientExtensionResults = new AuthenticationExtensionsClientOutputs() { AppID = false, @@ -1514,7 +1514,7 @@ public async Task TestAuthenticatorAssertionUserHandleEmpty() { Response = assertion, Type = PublicKeyCredentialType.PublicKey, - Id = [0xf1, 0xd0], + Id = "8dA", RawId = [0xf1, 0xd0], ClientExtensionResults = new AuthenticationExtensionsClientOutputs() { @@ -1583,7 +1583,7 @@ public async Task TestAuthenticatorAssertionUserHandleNotOwnerOfPublicKey() { Response = assertion, Type = PublicKeyCredentialType.PublicKey, - Id = [0xf1, 0xd0], + Id = "8dA", RawId = [0xf1, 0xd0], ClientExtensionResults = new AuthenticationExtensionsClientOutputs() { @@ -1652,7 +1652,7 @@ public async Task TestAuthenticatorAssertionTypeNotWebAuthnGet() { Response = assertion, Type = PublicKeyCredentialType.PublicKey, - Id = [0xf1, 0xd0], + Id = "8dA", RawId = [0xf1, 0xd0], ClientExtensionResults = new AuthenticationExtensionsClientOutputs { @@ -1723,7 +1723,7 @@ public async Task TestAuthenticatorAssertionAppId() { Response = assertion, Type = PublicKeyCredentialType.PublicKey, - Id = [0xf1, 0xd0], + Id = "8dA", RawId = [0xf1, 0xd0], ClientExtensionResults = new AuthenticationExtensionsClientOutputs() { @@ -1793,7 +1793,7 @@ public async Task TestAuthenticatorAssertionInvalidRpIdHash() { Response = assertion, Type = PublicKeyCredentialType.PublicKey, - Id = [0xf1, 0xd0], + Id = "8dA", RawId = [0xf1, 0xd0], ClientExtensionResults = new AuthenticationExtensionsClientOutputs() { @@ -1864,7 +1864,7 @@ public async Task TestAuthenticatorAssertionUPRequirementNotMet() { Response = assertion, Type = PublicKeyCredentialType.PublicKey, - Id = [0xf1, 0xd0], + Id = "8dA", RawId = [0xf1, 0xd0], ClientExtensionResults = new AuthenticationExtensionsClientOutputs { @@ -1934,7 +1934,7 @@ public async Task TestAuthenticatorAssertionUVPolicyNotMet() { Response = assertion, Type = PublicKeyCredentialType.PublicKey, - Id = [0xf1, 0xd0], + Id = "8dA", RawId = [0xf1, 0xd0], ClientExtensionResults = new AuthenticationExtensionsClientOutputs { @@ -2002,7 +2002,7 @@ public async Task TestAuthenticatorAssertionBEPolicyRequired() { Response = assertion, Type = PublicKeyCredentialType.PublicKey, - Id = [0xf1, 0xd0], + Id = "8dA", RawId = [0xf1, 0xd0], ClientExtensionResults = new AuthenticationExtensionsClientOutputs() { @@ -2071,7 +2071,7 @@ public async Task TestAuthenticatorAssertionBEPolicyDisallow() { Response = assertion, Type = PublicKeyCredentialType.PublicKey, - Id = [0xf1, 0xd0], + Id = "8dA", RawId = [0xf1, 0xd0], ClientExtensionResults = new AuthenticationExtensionsClientOutputs { @@ -2140,7 +2140,7 @@ public async Task TestAuthenticatorAssertionBSPolicyRequired() { Response = assertion, Type = PublicKeyCredentialType.PublicKey, - Id = [0xf1, 0xd0], + Id = "8dA", RawId = [0xf1, 0xd0], ClientExtensionResults = new AuthenticationExtensionsClientOutputs { @@ -2209,7 +2209,7 @@ public async Task TestAuthenticatorAssertionBSPolicyDisallow() { Response = assertion, Type = PublicKeyCredentialType.PublicKey, - Id = [0xf1, 0xd0], + Id = "8dA", RawId = [0xf1, 0xd0], ClientExtensionResults = new AuthenticationExtensionsClientOutputs { @@ -2279,7 +2279,7 @@ public async Task TestAuthenticatorAssertionStoredPublicKeyMissing() { Response = assertion, Type = PublicKeyCredentialType.PublicKey, - Id = [0xf1, 0xd0], + Id = "8dA", RawId = [0xf1, 0xd0], ClientExtensionResults = new AuthenticationExtensionsClientOutputs() { @@ -2348,7 +2348,7 @@ public async Task TestAuthenticatorAssertionInvalidSignature() { Response = assertion, Type = PublicKeyCredentialType.PublicKey, - Id = [0xf1, 0xd0], + Id = "8dA", RawId = [0xf1, 0xd0], ClientExtensionResults = new AuthenticationExtensionsClientOutputs() { @@ -2424,7 +2424,7 @@ public async Task TestAuthenticatorAssertionSignCountSignature() { Response = assertion, Type = PublicKeyCredentialType.PublicKey, - Id = [0xf1, 0xd0], + Id = "8dA", RawId = [0xf1, 0xd0], ClientExtensionResults = new AuthenticationExtensionsClientOutputs() { diff --git a/Test/ExistingU2fRegistrationDataTests.cs b/Test/ExistingU2fRegistrationDataTests.cs index 4a61e46e..3a69521c 100644 --- a/Test/ExistingU2fRegistrationDataTests.cs +++ b/Test/ExistingU2fRegistrationDataTests.cs @@ -13,7 +13,8 @@ public async Task TestFido2AssertionWithExistingU2fRegistrationWithAppId() { // u2f registration with appId var appId = "https://localhost:44336"; - var keyHandleData = Base64Url.Decode("2uzGTqu9XGoDQpRBhkv3qDYWzEEZrDjOHT94fHe3J9VXl6KpaY6jL1C4gCAVSBCWZejOn-EYSyXfiG7RDQqgKw"); + var keyHandleB64Data = "2uzGTqu9XGoDQpRBhkv3qDYWzEEZrDjOHT94fHe3J9VXl6KpaY6jL1C4gCAVSBCWZejOn-EYSyXfiG7RDQqgKw"; + var keyHandleData = Base64Url.Decode(keyHandleB64Data); var publicKeyData = Base64Url.Decode("BEKJkJiDzo8wlrYbAHmyz5a5vShbkStO58ZO7F-hy4fvBp6TowCZoV2dNGcxIN1yT18799bb_WuP0Yq_DSv5a-U"); //key as cbor @@ -35,7 +36,7 @@ public async Task TestFido2AssertionWithExistingU2fRegistrationWithAppId() var authResponse = new AuthenticatorAssertionRawResponse { - Id = keyHandleData, + Id = keyHandleB64Data, RawId = keyHandleData, Type = PublicKeyCredentialType.PublicKey, ClientExtensionResults = new AuthenticationExtensionsClientOutputs diff --git a/Test/Fido2Tests.cs b/Test/Fido2Tests.cs index afb9163b..98d9cde5 100644 --- a/Test/Fido2Tests.cs +++ b/Test/Fido2Tests.cs @@ -921,7 +921,7 @@ internal static async Task MakeAssertionResponseAsync( { Response = assertion, Type = PublicKeyCredentialType.PublicKey, - Id = [0xf1, 0xd0], + Id = "8dA", RawId = [0xf1, 0xd0], }; IsUserHandleOwnerOfCredentialIdAsync callback = (args, cancellationToken) =>