Skip to content

Commit 99dc99c

Browse files
authored
Update AttestationVerifier api to Async (#458)
* Update AttestationVerifier api to Async * Configure awaits * Apply code formatting rules
1 parent 43e2cd3 commit 99dc99c

14 files changed

+82
-63
lines changed

Src/Fido2/AttestationFormat/AndroidKey.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using System.Linq;
33
using System.Security.Cryptography;
44
using System.Security.Cryptography.X509Certificates;
5+
using System.Threading.Tasks;
56

67
using Fido2NetLib.Cbor;
78
using Fido2NetLib.Exceptions;
@@ -131,7 +132,7 @@ public static bool IsPurposeSign(byte[] attExtBytes)
131132
return (softwareEnforcedPurposeValue is 2 && teeEnforcedPurposeValue is 2);
132133
}
133134

134-
public override (AttestationType, X509Certificate2[]) Verify(VerifyAttestationRequest request)
135+
public override ValueTask<VerifyAttestationResult> VerifyAsync(VerifyAttestationRequest request)
135136
{
136137
// 1. Verify that attStmt is valid CBOR conforming to the syntax defined above and perform CBOR decoding on it to extract the contained fields
137138
// (handled in base class)
@@ -220,6 +221,6 @@ public override (AttestationType, X509Certificate2[]) Verify(VerifyAttestationRe
220221
if (!IsPurposeSign(attExtBytes))
221222
throw new Fido2VerificationException(Fido2ErrorCode.InvalidAttestation, "Found purpose field not set to KM_PURPOSE_SIGN in android key attestation certificate extension");
222223

223-
return (AttestationType.Basic, trustPath);
224+
return new(new VerifyAttestationResult(AttestationType.Basic, trustPath));
224225
}
225226
}

Src/Fido2/AttestationFormat/AndroidSafetyNet.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
using System.Security.Cryptography.X509Certificates;
88
using System.Text;
99
using System.Text.Json;
10+
using System.Threading.Tasks;
1011

1112
using Fido2NetLib.Cbor;
1213
using Fido2NetLib.Exceptions;
@@ -20,7 +21,7 @@ internal sealed class AndroidSafetyNet : AttestationVerifier
2021
{
2122
private const int _driftTolerance = 0;
2223

23-
public override (AttestationType, X509Certificate2[]) Verify(VerifyAttestationRequest request)
24+
public override ValueTask<VerifyAttestationResult> VerifyAsync(VerifyAttestationRequest request)
2425
{
2526
// 1. Verify that attStmt is valid CBOR conforming to the syntax defined above and perform
2627
// CBOR decoding on it to extract the contained fields
@@ -157,8 +158,7 @@ public override (AttestationType, X509Certificate2[]) Verify(VerifyAttestationRe
157158
throw new Fido2VerificationException(Fido2ErrorCode.InvalidAttestation, "Nonce value not base64string in SafetyNet attestation", ex);
158159
}
159160

160-
Span<byte> dataHash = stackalloc byte[32];
161-
SHA256.HashData(request.Data, dataHash);
161+
byte[] dataHash = SHA256.HashData(request.Data);
162162

163163
if (!dataHash.SequenceEqual(nonceHash))
164164
{
@@ -180,6 +180,6 @@ public override (AttestationType, X509Certificate2[]) Verify(VerifyAttestationRe
180180
if (true != ctsProfileMatch)
181181
throw new Fido2VerificationException(Fido2ErrorCode.InvalidAttestation, "SafetyNet response ctsProfileMatch false");
182182

183-
return (AttestationType.Basic, new X509Certificate2[] { attestationCert });
183+
return new(new VerifyAttestationResult(AttestationType.Basic, new X509Certificate2[] { attestationCert }));
184184
}
185185
}

Src/Fido2/AttestationFormat/Apple.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using System.Linq;
44
using System.Security.Cryptography;
55
using System.Security.Cryptography.X509Certificates;
6+
using System.Threading.Tasks;
67

78
using Fido2NetLib.Cbor;
89
using Fido2NetLib.Exceptions;
@@ -40,7 +41,7 @@ public static byte[] GetAppleAttestationExtensionValue(X509ExtensionCollection e
4041
}
4142
}
4243

43-
public override (AttestationType, X509Certificate2[]) Verify(VerifyAttestationRequest request)
44+
public override ValueTask<VerifyAttestationResult> VerifyAsync(VerifyAttestationRequest request)
4445
{
4546
// 1. Verify that attStmt is valid CBOR conforming to the syntax defined above and perform CBOR decoding on it to extract the contained fields.
4647
if (!(request.X5c is CborArray { Length: >= 2 } x5cArray && x5cArray[0] is CborByteString { Length: > 0 }))
@@ -86,6 +87,6 @@ public override (AttestationType, X509Certificate2[]) Verify(VerifyAttestationRe
8687
throw new Fido2VerificationException(Fido2ErrorCode.InvalidAttestation, "Credential public key in Apple attestation does not match subject public key of credCert");
8788

8889
// 7. If successful, return implementation-specific values representing attestation type Anonymous CA and attestation trust path x5c.
89-
return (AttestationType.Basic, trustPath);
90+
return new(new VerifyAttestationResult(AttestationType.Basic, trustPath));
9091
}
9192
}

Src/Fido2/AttestationFormat/AppleAppAttest.cs

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@
33
using System.Linq;
44
using System.Security.Cryptography;
55
using System.Security.Cryptography.X509Certificates;
6+
using System.Threading.Tasks;
67

78
using Fido2NetLib.Cbor;
8-
using Fido2NetLib.Objects;
99

1010
namespace Fido2NetLib;
1111

@@ -47,7 +47,7 @@ public static byte[] GetAppleAppIdFromCredCertExtValue(X509ExtensionCollection e
4747
// 61707061-7474-6573-7400-000000000000
4848
public static readonly Guid prodAaguid = new("61707061-7474-6573-7400-000000000000");
4949

50-
public override (AttestationType, X509Certificate2[]) Verify(VerifyAttestationRequest request)
50+
public override async ValueTask<VerifyAttestationResult> VerifyAsync(VerifyAttestationRequest request)
5151
{
5252
// 1. Verify that the x5c array contains the intermediate and leaf certificates for App Attest, starting from the credential certificate in the first data buffer in the array (credcert).
5353
if (!(request.X5c is CborArray { Length: 2 } x5cArray && x5cArray[0] is CborByteString { Length: > 0 } && x5cArray[1] is CborByteString { Length: > 0 }))
@@ -81,22 +81,20 @@ public override (AttestationType, X509Certificate2[]) Verify(VerifyAttestationRe
8181
// 4. Obtain the value of the credCert extension with OID 1.2.840.113635.100.8.2, which is a DER - encoded ASN.1 sequence.Decode the sequence and extract the single octet string that it contains. Verify that the string equals nonce.
8282
// Steps 2 - 4 done in the "apple" format verifier
8383
var apple = new Apple();
84-
(var attType, var trustPath) = apple.Verify(request);
84+
(var attType, var trustPath) = await apple.VerifyAsync(request).ConfigureAwait(false);
8585

8686
// 5. Create the SHA256 hash of the public key in credCert, and verify that it matches the key identifier from your app.
87-
Span<byte> credCertPKHash = stackalloc byte[32];
88-
SHA256.HashData(credCert.GetPublicKey(), credCertPKHash);
89-
ReadOnlySpan<byte> keyIdentifier = Convert.FromHexString(credCert.GetNameInfo(X509NameType.SimpleName, false));
90-
if (!credCertPKHash.SequenceEqual(keyIdentifier))
87+
byte[] credCertPKHash = SHA256.HashData(credCert.GetPublicKey());
88+
byte[] keyIdentifier = Convert.FromHexString(credCert.GetNameInfo(X509NameType.SimpleName, false));
89+
if (!credCertPKHash.AsSpan().SequenceEqual(keyIdentifier))
9190
{
9291
throw new Fido2VerificationException("Public key hash does not match key identifier in Apple AppAttest attestation");
9392
}
9493

9594
// 6. Compute the SHA256 hash of your app's App ID, and verify that it’s the same as the authenticator data's RP ID hash.
9695
var appId = GetAppleAppIdFromCredCertExtValue(credCert.Extensions);
97-
Span<byte> appIdHash = stackalloc byte[32];
98-
SHA256.HashData(appId, appIdHash);
99-
if (!appIdHash.SequenceEqual(request.AuthData.RpIdHash))
96+
byte[] appIdHash = SHA256.HashData(appId);
97+
if (!appIdHash.AsSpan().SequenceEqual(request.AuthData.RpIdHash))
10098
{
10199
throw new Fido2VerificationException("App ID hash does not match RP ID hash in Apple AppAttest attestation");
102100
}
@@ -119,6 +117,6 @@ public override (AttestationType, X509Certificate2[]) Verify(VerifyAttestationRe
119117
throw new Fido2VerificationException("Mismatch between credentialId and keyIdentifier in Apple AppAttest attestation");
120118
}
121119

122-
return (attType, trustPath);
120+
return new VerifyAttestationResult(attType, trustPath);
123121
}
124122
}

Src/Fido2/AttestationFormat/AttestationVerifier.cs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System.Formats.Asn1;
22
using System.Linq;
33
using System.Security.Cryptography.X509Certificates;
4+
using System.Threading.Tasks;
45

56
using Fido2NetLib.Cbor;
67
using Fido2NetLib.Exceptions;
@@ -10,12 +11,12 @@ namespace Fido2NetLib;
1011

1112
public abstract class AttestationVerifier
1213
{
13-
public (AttestationType, X509Certificate2[]) Verify(CborMap attStmt, AuthenticatorData authenticatorData, byte[] clientDataHash)
14+
public ValueTask<VerifyAttestationResult> VerifyAsync(CborMap attStmt, AuthenticatorData authenticatorData, byte[] clientDataHash)
1415
{
15-
return Verify(new VerifyAttestationRequest(attStmt, authenticatorData, clientDataHash));
16+
return VerifyAsync(new VerifyAttestationRequest(attStmt, authenticatorData, clientDataHash));
1617
}
1718

18-
public abstract (AttestationType, X509Certificate2[]) Verify(VerifyAttestationRequest request);
19+
public abstract ValueTask<VerifyAttestationResult> VerifyAsync(VerifyAttestationRequest request);
1920

2021
public static AttestationVerifier Create(string formatIdentifier)
2122
{

Src/Fido2/AttestationFormat/FidoU2f.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System;
22
using System.Security.Cryptography;
33
using System.Security.Cryptography.X509Certificates;
4+
using System.Threading.Tasks;
45

56
using Fido2NetLib.Cbor;
67
using Fido2NetLib.Exceptions;
@@ -10,7 +11,7 @@ namespace Fido2NetLib;
1011

1112
internal sealed class FidoU2f : AttestationVerifier
1213
{
13-
public override (AttestationType, X509Certificate2[]) Verify(VerifyAttestationRequest request)
14+
public override ValueTask<VerifyAttestationResult> VerifyAsync(VerifyAttestationRequest request)
1415
{
1516
// verify that aaguid is 16 empty bytes (note: required by fido2 conformance testing, could not find this in spec?)
1617
if (request.AuthData.AttestedCredentialData!.AaGuid.CompareTo(Guid.Empty) != 0)
@@ -86,6 +87,6 @@ public override (AttestationType, X509Certificate2[]) Verify(VerifyAttestationRe
8687

8788
var trustPath = new X509Certificate2[1] { attCert };
8889

89-
return (AttestationType.AttCa, trustPath);
90+
return new(new VerifyAttestationResult(AttestationType.AttCa, trustPath));
9091
}
9192
}
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using System.Security.Cryptography.X509Certificates;
1+
using System.Threading.Tasks;
22

33
using Fido2NetLib.Exceptions;
44
using Fido2NetLib.Objects;
@@ -7,11 +7,11 @@ namespace Fido2NetLib;
77

88
public sealed class None : AttestationVerifier
99
{
10-
public override (AttestationType, X509Certificate2[]) Verify(VerifyAttestationRequest request)
10+
public override ValueTask<VerifyAttestationResult> VerifyAsync(VerifyAttestationRequest request)
1111
{
1212
if (request.AttStmt.Count != 0)
1313
throw new Fido2VerificationException(Fido2ErrorCode.InvalidAttestation, "Attestation format none should have no attestation statement");
1414

15-
return (AttestationType.None, null!);
15+
return new(new VerifyAttestationResult(AttestationType.None, null!));
1616
}
1717
}

Src/Fido2/AttestationFormat/Packed.cs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System;
22
using System.Collections.Generic;
33
using System.Security.Cryptography.X509Certificates;
4+
using System.Threading.Tasks;
45

56
using Fido2NetLib.Cbor;
67
using Fido2NetLib.Exceptions;
@@ -36,7 +37,7 @@ public static bool IsValidPackedAttnCertSubject(string attnCertSubj)
3637
&& subjectMap.TryGetValue("CN", out var cn) && cn.Length > 0;
3738
}
3839

39-
public override (AttestationType, X509Certificate2[]) Verify(VerifyAttestationRequest request)
40+
public override ValueTask<VerifyAttestationResult> VerifyAsync(VerifyAttestationRequest request)
4041
{
4142
// 1. Verify that attStmt is valid CBOR conforming to the syntax defined above and
4243
// perform CBOR decoding on it to extract the contained fields.
@@ -117,7 +118,7 @@ public override (AttestationType, X509Certificate2[]) Verify(VerifyAttestationRe
117118

118119
// 2d. Optionally, inspect x5c and consult externally provided knowledge to determine whether attStmt conveys a Basic or AttCA attestation
119120

120-
return (AttestationType.AttCa, trustPath);
121+
return new(new VerifyAttestationResult(AttestationType.AttCa, trustPath));
121122
}
122123

123124
// 3. If ecdaaKeyId is present, then the attestation type is ECDAA
@@ -145,7 +146,7 @@ public override (AttestationType, X509Certificate2[]) Verify(VerifyAttestationRe
145146
if (!request.AuthData.AttestedCredentialData.CredentialPublicKey.Verify(request.Data, sig))
146147
throw new Fido2VerificationException(Fido2ErrorCode.InvalidAttestation, "Failed to validate signature");
147148

148-
return (AttestationType.Self, null!);
149+
return new(new VerifyAttestationResult(AttestationType.Self, null!));
149150
}
150151
}
151152
}

Src/Fido2/AttestationFormat/Tpm.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using System.Linq;
66
using System.Security.Cryptography;
77
using System.Security.Cryptography.X509Certificates;
8+
using System.Threading.Tasks;
89

910
using Fido2NetLib.Cbor;
1011
using Fido2NetLib.Exceptions;
@@ -43,7 +44,7 @@ internal sealed class Tpm : AttestationVerifier
4344
"id:474F4F47", // 'GOOG' Google
4445
};
4546

46-
public override (AttestationType, X509Certificate2[]) Verify(VerifyAttestationRequest request)
47+
public override ValueTask<VerifyAttestationResult> VerifyAsync(VerifyAttestationRequest request)
4748
{
4849
// 1. Verify that attStmt is valid CBOR conforming to the syntax defined above and perform CBOR decoding on it to extract the contained fields.
4950
// (handled in base class)
@@ -202,7 +203,7 @@ public override (AttestationType, X509Certificate2[]) Verify(VerifyAttestationRe
202203
throw new Fido2VerificationException($"aaguid malformed, expected {request.AuthData.AttestedCredentialData.AaGuid}, got {new Guid(aaguid)}");
203204
}
204205

205-
return (AttestationType.AttCa, trustPath);
206+
return new(new VerifyAttestationResult(AttestationType.AttCa, trustPath));
206207
}
207208
// If ecdaaKeyId is present, then the attestation type is ECDAA
208209
else if (request.EcdaaKeyId != null)
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
using System.Security.Cryptography.X509Certificates;
2+
3+
using Fido2NetLib.Objects;
4+
5+
namespace Fido2NetLib;
6+
7+
public sealed class VerifyAttestationResult
8+
{
9+
public VerifyAttestationResult(AttestationType type, X509Certificate2[] certificates)
10+
{
11+
Type = type;
12+
Certificates = certificates;
13+
}
14+
15+
public AttestationType Type { get; }
16+
17+
public X509Certificate2[] Certificates { get; }
18+
19+
public void Deconstruct(out AttestationType type, out X509Certificate2[] certificates)
20+
{
21+
(type, certificates) = (Type, Certificates);
22+
}
23+
}

0 commit comments

Comments
 (0)