11// Licensed to the .NET Foundation under one or more agreements.
22// The .NET Foundation licenses this file to you under the MIT license.
33
4- using System ;
5- using System . Buffers . Binary ;
6- using System . Collections . Generic ;
74using System . Diagnostics ;
85using System . Linq ;
96using System . Security . Cryptography ;
107using System . Text ;
118using System . Text . Json ;
12- using System . Threading . Tasks ;
13- using Microsoft . Extensions . Logging ;
149using Microsoft . Extensions . Options ;
1510
1611namespace Microsoft . AspNetCore . Identity ;
@@ -22,7 +17,7 @@ public sealed partial class DefaultPasskeyHandler<TUser> : IPasskeyHandler<TUser
2217 where TUser : class
2318{
2419 private readonly IPasskeyOriginValidator _originValidator ;
25- private readonly IPasskeyAttestationStatementVerifier ? _attestationStatementVerifier ;
20+ private readonly IPasskeyAttestationStatementVerifier _attestationStatementVerifier ;
2621 private readonly PasskeyOptions _passkeyOptions ;
2722
2823 /// <summary>
@@ -34,7 +29,7 @@ public sealed partial class DefaultPasskeyHandler<TUser> : IPasskeyHandler<TUser
3429 public DefaultPasskeyHandler (
3530 IOptions < IdentityOptions > options ,
3631 IPasskeyOriginValidator originValidator ,
37- IPasskeyAttestationStatementVerifier ? attestationStatementVerifier = null )
32+ IPasskeyAttestationStatementVerifier attestationStatementVerifier )
3833 {
3934 _originValidator = originValidator ;
4035 _attestationStatementVerifier = attestationStatementVerifier ;
@@ -151,7 +146,7 @@ private async Task<PasskeyAttestationResult> PerformAttestationCoreAsync(
151146 }
152147
153148 // 12. Let clientDataHash be the result of computing a hash over response.clientDataJSON using SHA-256.
154- var clientDataHash = ComputeSHA256Hash ( response . ClientDataJSON ) ;
149+ var clientDataHash = SHA256 . HashData ( response . ClientDataJSON . AsSpan ( ) ) ;
155150
156151 // 13. Perform CBOR decoding on the attestationObject field of the AuthenticatorAttestationResponse structure and obtain the
157152 // the authenticator data authenticatorData.
@@ -166,7 +161,7 @@ private async Task<PasskeyAttestationResult> PerformAttestationCoreAsync(
166161 }
167162
168163 // 14. Verify that the rpIdHash in authenticatorData is the SHA-256 hash of the RP ID expected by the Relying Party.
169- var rpIdHash = ComputeSHA256Hash ( Encoding . UTF8 . GetBytes ( originalOptions . Rp . Id ?? string . Empty ) ) ;
164+ var rpIdHash = SHA256 . HashData ( Encoding . UTF8 . GetBytes ( originalOptions . Rp . Id ?? string . Empty ) ) ;
170165 if ( ! authenticatorData . RpIdHash . Span . SequenceEqual ( rpIdHash . AsSpan ( ) ) )
171166 {
172167 throw PasskeyException . InvalidRelyingPartyIDHash ( ) ;
@@ -230,14 +225,11 @@ private async Task<PasskeyAttestationResult> PerformAttestationCoreAsync(
230225
231226 // 21-24. Determine the attestation statement format by performing a USASCII case-sensitive match on fmt against the set of supported WebAuthn
232227 // Attestation Statement Format Identifier values...
233- if ( _attestationStatementVerifier is not null )
228+ // Handles all validation related to the attestation statement (21-24).
229+ var isAttestationStatementValid = await _attestationStatementVerifier . VerifyAsync ( attestationObjectMemory , clientDataHash ) . ConfigureAwait ( false ) ;
230+ if ( ! isAttestationStatementValid )
234231 {
235- // Handles all validation related to the attestation statement (21-24).
236- var isAttestationStatementValid = await _attestationStatementVerifier . VerifyAsync ( attestationObjectMemory , clientDataHash ) . ConfigureAwait ( false ) ;
237- if ( ! isAttestationStatementValid )
238- {
239- throw PasskeyException . InvalidAttestationStatement ( ) ;
240- }
232+ throw PasskeyException . InvalidAttestationStatement ( ) ;
241233 }
242234
243235 // 25. Verify that the credentialId is <= 1023 bytes.
@@ -410,7 +402,7 @@ private async Task<PasskeyAssertionResult<TUser>> PerformAssertionCoreAsync(
410402 }
411403
412404 // 15. Verify that the rpIdHash in authData is the SHA-256 hash of the RP ID expected by the Relying Party.
413- var rpIdHash = ComputeSHA256Hash ( Encoding . UTF8 . GetBytes ( originalOptions . RpId ?? string . Empty ) ) ;
405+ var rpIdHash = SHA256 . HashData ( Encoding . UTF8 . GetBytes ( originalOptions . RpId ?? string . Empty ) ) ;
414406 if ( ! authenticatorData . RpIdHash . Span . SequenceEqual ( rpIdHash . AsSpan ( ) ) )
415407 {
416408 throw PasskeyException . InvalidRelyingPartyIDHash ( ) ;
@@ -459,7 +451,7 @@ private async Task<PasskeyAssertionResult<TUser>> PerformAssertionCoreAsync(
459451 }
460452
461453 // 20. Let clientDataHash be the result of computing a hash over the cData using SHA-256.
462- var clientDataHash = ComputeSHA256Hash ( response . ClientDataJSON ) ;
454+ var clientDataHash = SHA256 . HashData ( response . ClientDataJSON . AsSpan ( ) ) ;
463455
464456 // 21. Using credentialRecord.publicKey, verify that sig is a valid signature over the binary concatenation of authData and hash.
465457 byte [ ] data = [ .. response . AuthenticatorData . AsSpan ( ) , .. clientDataHash ] ;
@@ -502,24 +494,4 @@ private async Task<PasskeyAssertionResult<TUser>> PerformAssertionCoreAsync(
502494 // 25. If all the above steps are successful, continue the authentication ceremony as appropriate.
503495 return PasskeyAssertionResult . Success ( storedPasskey , user ) ;
504496 }
505-
506- private static byte [ ] ComputeSHA256Hash ( byte [ ] data )
507- {
508- #if NETCOREAPP
509- return SHA256 . HashData ( data ) ;
510- #else
511- using var sha256 = SHA256 . Create ( ) ;
512- return sha256 . ComputeHash ( data ) ;
513- #endif
514- }
515-
516- private static byte [ ] ComputeSHA256Hash ( BufferSource data )
517- {
518- #if NETCOREAPP
519- return SHA256 . HashData ( data . AsSpan ( ) ) ;
520- #else
521- using var sha256 = SHA256 . Create ( ) ;
522- return sha256 . ComputeHash ( data . ToArray ( ) ) ;
523- #endif
524- }
525497}
0 commit comments