Skip to content

Commit

Permalink
Cleanup.
Browse files Browse the repository at this point in the history
  • Loading branch information
KristofferStrube committed Jul 19, 2024
1 parent bddd142 commit 77912d0
Show file tree
Hide file tree
Showing 9 changed files with 142 additions and 101 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
builder.Services.AddSwaggerGen();

builder.Services.AddCors(o => o.AddPolicy("default",
builder =>
builder =>
builder.WithOrigins("https://localhost:7203",
"https://kristofferstrube.github.io")
.AllowAnyMethod()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,7 @@
using Microsoft.AspNetCore.Http.HttpResults;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.WebUtilities;
using System.Formats.Cbor;
using System.Net;
using System.Security.Cryptography;
using System.Text;
using System.Web;

namespace KristofferStrube.Blazor.WebAuthentication.API;

Expand Down Expand Up @@ -49,32 +45,32 @@ public static Ok<bool> Register(string userName, [FromBody] RegistrationResponse
{
CollectedClientData? clientData = System.Text.Json.JsonSerializer.Deserialize<CollectedClientData>(Convert.FromBase64String(registration.Response.ClientDataJSON));
if (clientData is null)
{
return TypedResults.Ok(false);

Console.WriteLine("0");
}

if (!Challenges.TryGetValue(userName, out byte[]? originalChallenge)
|| !originalChallenge.SequenceEqual(WebEncoders.Base64UrlDecode(clientData.Challenge)))
{
return TypedResults.Ok(false);

Console.WriteLine("1");
}

if (registration.Response.PublicKey is null)
{
return TypedResults.Ok(false);

Console.WriteLine("2");
}

var attestationStatement = PackedAttestationFormat.ReadFromBase64EncodedAttestationStatement(registration.Response.AttestationObject);

if (attestationStatement.Algorithm != (COSEAlgorithm)registration.Response.PublicKeyAlgorithm)
{
return TypedResults.Ok(false);

Console.WriteLine("3");
}

if (!VerifySignature(attestationStatement.Algorithm, Convert.FromBase64String(registration.Response.PublicKey), registration.Response.AuthenticatorData, registration.Response.ClientDataJSON, attestationStatement.Signature))
{
return TypedResults.Ok(false);

Console.WriteLine("4");
}

if (Credentials.TryGetValue(userName, out List<byte[]>? credentialList))
{
Expand All @@ -96,7 +92,7 @@ public static Ok<ValidateCredentials> ValidateChallenge(string userName)
}
byte[] challenge = RandomNumberGenerator.GetBytes(32);
Challenges[userName] = challenge;
return TypedResults.Ok<ValidateCredentials>(new (challenge, credentialList));
return TypedResults.Ok<ValidateCredentials>(new(challenge, credentialList));
}

public class ValidateCredentials(byte[] challenge, List<byte[]> credentials)
Expand All @@ -109,14 +105,20 @@ public static Ok<bool> Validate(string userName, [FromBody] AuthenticationRespon
{
CollectedClientData? clientData = System.Text.Json.JsonSerializer.Deserialize<CollectedClientData>(Convert.FromBase64String(authentication.Response.ClientDataJSON));
if (clientData is null)
{
return TypedResults.Ok(false);
}

if (!Challenges.TryGetValue(userName, out byte[]? originalChallenge)
|| !originalChallenge.SequenceEqual(WebEncoders.Base64UrlDecode(clientData.Challenge)))
{
return TypedResults.Ok(false);
}

if (!PublicKeys.TryGetValue(authentication.RawId, out (COSEAlgorithm algorithm, byte[] key) publicKey))
{
return TypedResults.Ok(false);
}

return TypedResults.Ok(VerifySignature(publicKey.algorithm, publicKey.key, authentication.Response.AuthenticatorData, authentication.Response.ClientDataJSON, Convert.FromBase64String(authentication.Response.Signature)));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,7 @@
using KristofferStrube.Blazor.WebIDL.Exceptions;
using Microsoft.AspNetCore.Components;
using Microsoft.JSInterop;
using System;
using System.Text;
using System.Threading.Channels;
using static KristofferStrube.Blazor.WebAuthentication.WasmExample.WebAuthenticationClient;

namespace KristofferStrube.Blazor.WebAuthentication.WasmExample.Pages;
Expand All @@ -31,13 +29,20 @@ public partial class Index : ComponentBase
protected override async Task OnInitializedAsync()
{
isSupported = await CredentialsService.IsSupportedAsync();
if (!isSupported) return;
if (!isSupported)
{
return;
}

container = await CredentialsService.GetCredentialsAsync();
}

private async Task CreateCredential()
{
if (username.Length == 0) username = "default";
if (username.Length == 0)
{
username = "default";
}

byte[] userId = Encoding.ASCII.GetBytes(username);
challenge = await WebAuthenticationClient.RegisterChallenge(username);
Expand Down Expand Up @@ -119,7 +124,10 @@ private async Task CreateCredential()

private async Task GetCredential()
{
if (username.Length == 0) username = "default";
if (username.Length == 0)
{
username = "default";
}

ValidateCredentials? setup = await WebAuthenticationClient.ValidateChallenge(username);
if (setup is not { Challenge: { Length: > 0 } challenge, Credentials: { Count: > 0 } credentials })
Expand All @@ -134,10 +142,10 @@ private async Task GetCredential()
foreach (byte[] credential in credentials)
{
allowCredentials.Add(new PublicKeyCredentialDescriptor()
{
Type = PublicKeyCredentialType.PublicKey,
Id = await JSRuntime.InvokeAsync<IJSObjectReference>("buffer", credential)
});
{
Type = PublicKeyCredentialType.PublicKey,
Id = await JSRuntime.InvokeAsync<IJSObjectReference>("buffer", credential)
});
}

CredentialRequestOptions options = new()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
using KristofferStrube.Blazor.WebIDL;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
using Microsoft.Extensions.DependencyInjection;

var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.RootComponents.Add<App>("#app");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ public class WebAuthenticationClient
{
private readonly HttpClient httpClient;

public WebAuthenticationClient([FromKeyedServices(typeof(WebAuthenticationClient))]HttpClient httpClient)
public WebAuthenticationClient([FromKeyedServices(typeof(WebAuthenticationClient))] HttpClient httpClient)
{
this.httpClient = httpClient;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,69 +27,101 @@ public static PackedAttestationFormat ReadFromBase64EncodedAttestationStatement(
CborReaderState state = cborReader.PeekState();

if (state is not CborReaderState.StartMap)
{
throw new FormatException("Attestation Statement did not start with a map.");
}

int? mapSize = cborReader.ReadStartMap();
if (mapSize is not 3)
{
throw new FormatException($"Attestation Statement had '{mapSize}' entries in its first map but '3' was expected.");
}

state = cborReader.PeekState();
if (state is not CborReaderState.TextString)
{
throw new FormatException($"Attestation Statement's first key was of type '{state}' but '{CborReaderState.TextString}' was expected.");
}

string label = cborReader.ReadTextString();
if (label is not "fmt")
{
throw new FormatException($"Attestation Statement's first key was '{label}' but 'fmt' was expected.");
}

state = cborReader.PeekState();
if (state is not CborReaderState.TextString)
{
throw new FormatException($"Attestation Statement's first value was of type '{state}' but '{CborReaderState.TextString}' was expected.");
}

string fmt = cborReader.ReadTextString();
if (fmt is not "packed")
{
throw new FormatException($"Attestation Statement had format '{fmt}' but 'packed' was expected.");
}

state = cborReader.PeekState();
if (state is not CborReaderState.TextString)
{
throw new FormatException($"Attestation Statement's second key was of type '{state}' but '{CborReaderState.TextString}' was expected.");
}

label = cborReader.ReadTextString();
if (label is not "attStmt")
{
throw new FormatException($"Attestation Statement's second key was '{label}' but 'attStmt' was expected.");
}

state = cborReader.PeekState();
if (state is not CborReaderState.StartMap)
{
throw new FormatException($"Attestation Statement's 'attStmt' was of type '{state}' but '{CborReaderState.StartMap}' was expected.");
}

mapSize = cborReader.ReadStartMap();
if (mapSize is not 2 or 3)
{
throw new FormatException($"Attestation Statement's packed format had '{mapSize}' entries but '2' or '3' was expected.");
}

state = cborReader.PeekState();
if (state is not CborReaderState.TextString)
{
throw new FormatException($"Attestation Statement's packed format's first key was of type '{state}' but '{CborReaderState.TextString}' was expected.");
}

label = cborReader.ReadTextString();
if (label is not "alg")
{
throw new FormatException($"Attestation Statement's packed format's first key was '{label}' but 'alg' was expected.");
}

state = cborReader.PeekState();
if (state is not CborReaderState.NegativeInteger)
{
throw new FormatException($"Attestation Statement's packed format's 'alg' was of type '{state}' but '{CborReaderState.NegativeInteger}' was expected.");
}

ulong negativeAlg = cborReader.ReadCborNegativeIntegerRepresentation();

state = cborReader.PeekState();
if (state is not CborReaderState.TextString)
{
throw new FormatException($"Attestation Statement's packed format's second key was of type '{state}' but '{CborReaderState.TextString}' was expected.");
}

label = cborReader.ReadTextString();
if (label is not "sig")
{
throw new FormatException($"Attestation Statement's packed format's second key was '{label}' but 'sig' was expected.");
}

state = cborReader.PeekState();
if (state is not CborReaderState.ByteString)
{
throw new FormatException($"Attestation Statement's packed format's 'sig' was of type '{state}' but '{CborReaderState.ByteString}' was expected.");
}

byte[] signature = cborReader.ReadByteString();

Expand Down
Loading

0 comments on commit 77912d0

Please sign in to comment.