Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Demo/wwwroot/js/custom.register.js
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ async function registerNewCredential(newCredential) {
response: {
AttestationObject: coerceToBase64Url(attestationObject),
clientDataJSON: coerceToBase64Url(clientDataJSON),
transports: newCredential.response.getTransports(),
transports: newCredential.response.getTransports()
},
};

Expand Down
3 changes: 2 additions & 1 deletion Demo/wwwroot/js/mfa.register.js
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,8 @@ async function registerNewCredential(newCredential) {
extensions: newCredential.getClientExtensionResults(),
response: {
AttestationObject: coerceToBase64Url(attestationObject),
clientDataJSON: coerceToBase64Url(clientDataJSON)
clientDataJSON: coerceToBase64Url(clientDataJSON),
transports: newCredential.response.getTransports()
}
};

Expand Down
3 changes: 2 additions & 1 deletion Demo/wwwroot/js/passwordless.register.js
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,8 @@ async function registerNewCredential(newCredential) {
extensions: newCredential.getClientExtensionResults(),
response: {
AttestationObject: coerceToBase64Url(attestationObject),
clientDataJSON: coerceToBase64Url(clientDataJSON)
clientDataJSON: coerceToBase64Url(clientDataJSON),
transports: newCredential.response.getTransports()
}
};

Expand Down
3 changes: 2 additions & 1 deletion Demo/wwwroot/js/usernameless.register.js
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,8 @@ async function registerNewCredential(newCredential) {
extensions: newCredential.getClientExtensionResults(),
response: {
attestationObject: coerceToBase64Url(attestationObject),
clientDataJSON: coerceToBase64Url(clientDataJSON)
clientDataJSON: coerceToBase64Url(clientDataJSON),
transports: newCredential.response.getTransports()
}
};

Expand Down
2 changes: 1 addition & 1 deletion Src/Fido2.BlazorWebAssembly/wwwroot/js/WebAuthn.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export async function createCreds(options: PublicKeyCredentialCreationOptions) {
response: {
attestationObject: toBase64Url(response.attestationObject),
clientDataJSON: toBase64Url(response.clientDataJSON),
transports: response.getTransports ? response.getTransports() : [],
transports: response.getTransports ? response.getTransports() : []
}
};
return retval;
Expand Down
3 changes: 3 additions & 0 deletions Src/Fido2.Models/AuthenticatorAttestationRawResponse.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,8 @@ public sealed class AttestationResponse
[JsonConverter(typeof(Base64UrlConverter))]
[JsonPropertyName("clientDataJSON")]
public byte[] ClientDataJson { get; set; }

[JsonPropertyName("transports")]
public AuthenticatorTransport[] Transports { get; set; }
}
}
2 changes: 1 addition & 1 deletion Src/Fido2/AuthenticatorAttestationResponse.cs
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ public async Task<RegisteredPublicKeyCredential> VerifyAsync(
Id = authData.AttestedCredentialData.CredentialId,
PublicKey = authData.AttestedCredentialData.CredentialPublicKey.GetBytes(),
SignCount = authData.SignCount,
// Transports = result of response.getTransports();
Transports = Raw.Response.Transports,
IsBackupEligible = authData.IsBackupEligible,
IsBackedUp = authData.IsBackedUp,
AttestationObject = Raw.Response.AttestationObject,
Expand Down
1 change: 1 addition & 0 deletions Test/Attestation/AndroidKey.cs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ public async Task TestAndroidKey()
Assert.Equal("Test User", res.Result.User.DisplayName);
Assert.Equal("testuser"u8.ToArray(), res.Result.User.Id);
Assert.Equal("testuser", res.Result.User.Name);
Assert.Equal(new[] { AuthenticatorTransport.Internal }, res.Result.Transports);
}

[Fact]
Expand Down
1 change: 1 addition & 0 deletions Test/Attestation/AndroidSafetyNet.cs
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ public async Task TestAndroidSafetyNet()
Assert.Equal("Test User", res.Result.User.DisplayName);
Assert.Equal("testuser"u8.ToArray(), res.Result.User.Id);
Assert.Equal("testuser", res.Result.User.Name);
Assert.Equal(new[] { AuthenticatorTransport.Internal }, res.Result.Transports);
}

[Fact]
Expand Down
1 change: 1 addition & 0 deletions Test/Attestation/FidoU2f.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ public async Task TestU2f()
Assert.Equal("Test User", res.Result.User.DisplayName);
Assert.Equal("testuser"u8.ToArray(), res.Result.User.Id);
Assert.Equal("testuser", res.Result.User.Name);
Assert.Equal(new[] { AuthenticatorTransport.Internal }, res.Result.Transports);
}

[Fact]
Expand Down
1 change: 1 addition & 0 deletions Test/Attestation/Packed.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ public async Task TestSelf()
Assert.Equal("testuser"u8.ToArray(), res.Result.User.Id);
Assert.Equal("testuser", res.Result.User.Name);
_attestationObject = new CborMap { { "fmt", "packed" } };
Assert.Equal(new[] { AuthenticatorTransport.Internal }, res.Result.Transports);
}
}

Expand Down
3 changes: 3 additions & 0 deletions Test/Attestation/Tpm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,7 @@ public async Task TestTPM()
Assert.Equal("testuser"u8.ToArray(), res.Result.User.Id);
Assert.Equal("testuser", res.Result.User.Name);
_attestationObject = new CborMap { { "fmt", "tpm" } };
Assert.Equal(new[] { AuthenticatorTransport.Internal }, res.Result.Transports);
}
}

Expand Down Expand Up @@ -422,6 +423,7 @@ public async Task TestTPMAikCertSANTCGConformant()
Assert.Equal("Test User", res.Result.User.DisplayName);
Assert.Equal("testuser"u8.ToArray(), res.Result.User.Id);
Assert.Equal("testuser", res.Result.User.Name);
Assert.Equal(new[] { AuthenticatorTransport.Internal }, res.Result.Transports);
}

[Fact]
Expand Down Expand Up @@ -5060,6 +5062,7 @@ public async Task TestTPMAikCertMisingAAGUID()
Assert.Equal("Test User", res.Result.User.DisplayName);
Assert.Equal("testuser"u8.ToArray(), res.Result.User.Id);
Assert.Equal("testuser", res.Result.User.Name);
Assert.Equal(new[] { AuthenticatorTransport.Internal }, res.Result.Transports);
}

[Fact]
Expand Down
59 changes: 30 additions & 29 deletions Test/Fido2Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,8 @@ static Fido2Tests()
new (COSE.KeyType.OKP, COSE.Algorithm.EdDSA, COSE.EllipticCurve.Ed25519),
new (COSE.KeyType.EC2, COSE.Algorithm.ES256K, COSE.EllipticCurve.P256K)
};
}

}
private async Task<T> GetAsync<T>(string filename)
{
return JsonSerializer.Deserialize<T>(await File.ReadAllTextAsync(filename));
Expand Down Expand Up @@ -117,23 +117,23 @@ public byte[] _clientDataJson

public byte[] _attToBeSignedHash(HashAlgorithmName alg)
{
return CryptoUtils.HashData(alg, _attToBeSigned);
return CryptoUtils.HashData(alg, _attToBeSigned);
}

public byte[] _credentialID;
public const AuthenticatorFlags _flags = AuthenticatorFlags.AT | AuthenticatorFlags.ED | AuthenticatorFlags.UP | AuthenticatorFlags.UV;
public ushort _signCount;
protected Guid _aaguid = new("F1D0F1D0-F1D0-F1D0-F1D0-F1D0F1D0F1D0");
public Extensions GetExtensions()
{
{
var extBytes = new CborMap { { "testing", true } }.Encode();
return new Extensions(extBytes);
return new Extensions(extBytes);
}

public AuthenticatorData _authData => new(_rpIdHash, _flags, _signCount, _acd, GetExtensions());

public AttestedCredentialData _acd => new(_aaguid, _credentialID, _credentialPublicKey);

public AuthenticatorData _authData => new(_rpIdHash, _flags, _signCount, _acd, GetExtensions());
public AttestedCredentialData _acd => new(_aaguid, _credentialID, _credentialPublicKey);
public Attestation()
{
_credentialID = RandomNumberGenerator.GetBytes(16);
Expand Down Expand Up @@ -163,6 +163,7 @@ public async Task<MakeNewCredentialResult> MakeAttestationResponseAsync()
{
AttestationObject = _attestationObject.Encode(),
ClientDataJson = _clientDataJson,
Transports = new[] { AuthenticatorTransport.Internal }
},
Extensions = new AuthenticationExtensionsClientOutputs()
{
Expand Down Expand Up @@ -226,8 +227,8 @@ public async Task<MakeNewCredentialResult> MakeAttestationResponseAsync()
ServerDomain = rp,
ServerName = rp,
Origins = new HashSet<string> { rp },
});

});
var credentialMakeResult = await lib.MakeNewCredentialAsync(attestationResponse, origChallenge, callback);

return credentialMakeResult;
Expand Down Expand Up @@ -353,8 +354,8 @@ internal static byte[] SignData(COSE.KeyType kty, COSE.Algorithm alg, byte[] dat
default:
throw new ArgumentOutOfRangeException(nameof(kty), $"Missing or unknown kty {kty}");
}
}

}
[Fact]
public void TestStringIsSerializable()
{
Expand Down Expand Up @@ -387,11 +388,11 @@ public void TestStringIsSerializable()
Assert.Equal(AuthenticatorAttachment.CrossPlatform, y2);

// test list of typed strings
var z1 = new[] {
AuthenticatorTransport.Ble,
AuthenticatorTransport.Usb,
var z1 = new[] {
AuthenticatorTransport.Ble,
AuthenticatorTransport.Usb,
AuthenticatorTransport.Nfc,
AuthenticatorTransport.Internal
AuthenticatorTransport.Internal
};

var zjson = JsonSerializer.Serialize(z1);
Expand Down Expand Up @@ -464,9 +465,9 @@ public void TestAppleAppAttestProd()
var clientDataJson = SHA256.HashData(Encoding.UTF8.GetBytes("1234567890abcdefgh"));

var verifier = new AppleAppAttest();
var ex = Assert.Throws<Fido2VerificationException>(() =>
{
(AttestationType attType, X509Certificate[] trustPath) = verifier.Verify(AttestationObject.AttStmt, AttestationObject.AuthData, clientDataJson);
var ex = Assert.Throws<Fido2VerificationException>(() =>
{
(AttestationType attType, X509Certificate[] trustPath) = verifier.Verify(AttestationObject.AttStmt, AttestationObject.AuthData, clientDataJson);
});

const string windowsErrorMessage = "Failed to build chain in Apple AppAttest attestation: A required certificate is not within its validity period when verifying against the current system clock or the timestamp in the signed file.";
Expand Down Expand Up @@ -822,8 +823,8 @@ public async Task TestAssertionResponse()
Assert.Equal(new byte[] { 0xf1, 0xd0 }, avr.CredentialId);
Assert.Equal("1", avr.SignCount.ToString("X"));
}
}

}
internal static async Task<VerifyAssertionResult> MakeAssertionResponseAsync(
COSE.KeyType kty,
COSE.Algorithm alg,
Expand All @@ -846,16 +847,16 @@ internal static async Task<VerifyAssertionResult> MakeAssertionResponseAsync(
{
case COSE.KeyType.EC2:
{
ecdsa ??= MakeECDsa(alg, crv);

ecdsa ??= MakeECDsa(alg, crv);
var ecParams = ecdsa.ExportParameters(true);
cpk = MakeCredentialPublicKey(kty, alg, crv, ecParams.Q.X, ecParams.Q.Y);
break;
}
case COSE.KeyType.RSA:
{
rsa ??= RSA.Create();

rsa ??= RSA.Create();
var rsaParams = rsa.ExportParameters(true);
cpk = MakeCredentialPublicKey(kty, alg, rsaParams.Modulus, rsaParams.Exponent);
break;
Expand Down Expand Up @@ -894,7 +895,7 @@ internal static async Task<VerifyAssertionResult> MakeAssertionResponseAsync(
var clientDataJson = JsonSerializer.SerializeToUtf8Bytes(clientData);

var hashedClientDataJson = SHA256.HashData(clientDataJson);
byte[] data = DataHelper.Concat(authData, hashedClientDataJson);
byte[] data = DataHelper.Concat(authData, hashedClientDataJson);
byte[] signature = SignData(kty, alg, data, ecdsa, rsa, expandedPrivateKey);

var userHandle = new byte[16];
Expand Down Expand Up @@ -935,12 +936,12 @@ internal static async Task<VerifyAssertionResult> MakeAssertionResponseAsync(
}

internal static void MakeEdDSA(out byte[] privateKeySeed, out byte[] publicKey, out byte[] expandedPrivateKey)
{
{
privateKeySeed = new byte[32];
RandomNumberGenerator.Fill(privateKeySeed);
var key = Key.Create(SignatureAlgorithm.Ed25519, new KeyCreationParameters() { ExportPolicy = KeyExportPolicies.AllowPlaintextExport });
expandedPrivateKey = key.Export(KeyBlobFormat.RawPrivateKey);
publicKey = key.Export(KeyBlobFormat.RawPublicKey);
publicKey = key.Export(KeyBlobFormat.RawPublicKey);
}

internal static ECDsa MakeECDsa(COSE.Algorithm alg, COSE.EllipticCurve crv)
Expand Down