diff --git a/.editorconfig b/.editorconfig index 1266d6a9..c538bd10 100644 --- a/.editorconfig +++ b/.editorconfig @@ -60,7 +60,7 @@ dotnet_style_explicit_tuple_names = true:suggestion [*.cs] # Prefer "var" when the type is apparent on the right side of the assignment; otherwise, avoid it. csharp_style_var_when_type_is_apparent = true:suggestion -csharp_style_var_elsewhere = true:suggestion +csharp_style_var_elsewhere = false:none # Prefer method-like constructs to have a block body csharp_style_expression_bodied_methods = false:none diff --git a/BlazorWasmDemo/Client/Shared/UserService.cs b/BlazorWasmDemo/Client/Shared/UserService.cs index 55967f40..7812550c 100644 --- a/BlazorWasmDemo/Client/Shared/UserService.cs +++ b/BlazorWasmDemo/Client/Shared/UserService.cs @@ -14,7 +14,7 @@ public class UserService private const string _routeRegister = "credential"; private const string _routeAssertionOpts = "assertion-options"; private const string _routeLogin = "assertion"; - + private readonly JsonSerializerOptions _jsonOptions = new FidoBlazorSerializerContext().Options; private readonly HttpClient _httpClient; private readonly WebAuthn _webAuthn; @@ -103,7 +103,7 @@ public async Task RegisterAsync(string? username, string? displayName = { Console.WriteLine(e); var errorMessage = e.Message; - if(options.ExcludeCredentials?.Count > 0) + if (options.ExcludeCredentials?.Count > 0) { errorMessage += " (You may have already registered this device)"; } diff --git a/BlazorWasmDemo/Server/Controllers/UserController.cs b/BlazorWasmDemo/Server/Controllers/UserController.cs index 68b945f0..6e61df64 100644 --- a/BlazorWasmDemo/Server/Controllers/UserController.cs +++ b/BlazorWasmDemo/Server/Controllers/UserController.cs @@ -213,16 +213,16 @@ public AssertionOptions MakeAssertionOptions([FromRoute] string? username, [From var exts = new AuthenticationExtensionsClientInputs() { - UserVerificationMethod = true, - Extensions = true, - DevicePubKey = new AuthenticationExtensionsDevicePublicKeyInputs() + UserVerificationMethod = true, + Extensions = true, + DevicePubKey = new AuthenticationExtensionsDevicePublicKeyInputs() }; - // 2. Create options (usernameless users will be prompted by their device to select a credential from their own list) - var options = _fido2.GetAssertionOptions( - existingKeys, - userVerification ?? UserVerificationRequirement.Discouraged, - exts); + // 2. Create options (usernameless users will be prompted by their device to select a credential from their own list) + var options = _fido2.GetAssertionOptions( + existingKeys, + userVerification ?? UserVerificationRequirement.Discouraged, + exts); // 4. Temporarily store options, session/in-memory cache/redis/db _pendingAssertions[new string(options.Challenge.Select(b => (char)b).ToArray())] = options; @@ -300,7 +300,7 @@ public async Task MakeAssertionAsync([FromBody] AuthenticatorAssertionRa var token = handler.CreateEncodedJwt( HttpContext.Request.Host.Host, HttpContext.Request.Headers.Referer, - new ClaimsIdentity(new Claim[]{new(ClaimTypes.Actor, Encoding.UTF8.GetString(creds.UserHandle))}), + new ClaimsIdentity(new Claim[] { new(ClaimTypes.Actor, Encoding.UTF8.GetString(creds.UserHandle)) }), DateTime.Now.Subtract(TimeSpan.FromMinutes(1)), DateTime.Now.AddDays(1), DateTime.Now, diff --git a/Demo/ConformanceTesting.cs b/Demo/ConformanceTesting.cs index c16dc57c..d1dcd9f1 100644 --- a/Demo/ConformanceTesting.cs +++ b/Demo/ConformanceTesting.cs @@ -6,7 +6,7 @@ namespace Fido2Demo; public static class ConformanceTesting { - private static readonly object _syncRoot = new (); + private static readonly object _syncRoot = new(); private static IMetadataService _instance; diff --git a/Demo/Pages/_options.cshtml.cs b/Demo/Pages/_options.cshtml.cs index c58163fd..32482130 100644 --- a/Demo/Pages/_options.cshtml.cs +++ b/Demo/Pages/_options.cshtml.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; @@ -13,4 +13,4 @@ public void OnGet() { } } -} \ No newline at end of file +} diff --git a/Demo/TestController.cs b/Demo/TestController.cs index 0e382cda..8f2ea227 100644 --- a/Demo/TestController.cs +++ b/Demo/TestController.cs @@ -17,7 +17,7 @@ namespace Fido2Demo; public class TestController : Controller { /* CONFORMANCE TESTING ENDPOINTS */ - private static readonly DevelopmentInMemoryStore _demoStorage = new (); + private static readonly DevelopmentInMemoryStore _demoStorage = new(); private readonly IFido2 _fido2; private readonly string _origin; @@ -31,7 +31,7 @@ public TestController(IOptions fido2Configuration) ServerDomain = fido2Configuration.Value.ServerDomain, ServerName = fido2Configuration.Value.ServerName, Origins = fido2Configuration.Value.FullyQualifiedOrigins, - }, + }, ConformanceTesting.MetadataServiceInstance( System.IO.Path.Combine(fido2Configuration.Value.MDSCacheDirPath, @"Conformance"), _origin) ); @@ -130,7 +130,7 @@ public IActionResult AssertionOptionsTest([FromBody] TEST_AssertionClientParams uv = assertionClientParams.authenticatorSelection.UserVerification; var exts = new AuthenticationExtensionsClientInputs - { + { AppID = _origin, UserVerificationMethod = true }; diff --git a/Src/Fido2.AspNet/DistributedCacheMetadataService.cs b/Src/Fido2.AspNet/DistributedCacheMetadataService.cs index a1f0f2a1..f380e79f 100644 --- a/Src/Fido2.AspNet/DistributedCacheMetadataService.cs +++ b/Src/Fido2.AspNet/DistributedCacheMetadataService.cs @@ -76,7 +76,8 @@ protected virtual DateTimeOffset GetMemoryCacheAbsoluteExpiryTime(DateTimeOffset var expiryTime = _systemClock.UtcNow.GetNextIncrement(_defaultMemoryCacheInterval); //Ensure that memory cache expiry time never exceeds the next update time from the service - if(nextUpdateTime.HasValue && expiryTime > nextUpdateTime.Value) expiryTime = nextUpdateTime.Value; + if (nextUpdateTime.HasValue && expiryTime > nextUpdateTime.Value) + expiryTime = nextUpdateTime.Value; return expiryTime; } @@ -98,7 +99,7 @@ protected virtual async Task GetRepositoryPayloadWithErrorH { return await repository.GetBLOBAsync(cancellationToken); } - catch(Exception ex) + catch (Exception ex) { _logger.LogError(ex, "Could not fetch metadata from {0}", repository.GetType().Name); return null; @@ -113,7 +114,7 @@ await _distributedCache.SetStringAsync( new DistributedCacheEntryOptions() { AbsoluteExpiration = GetDistributedCacheAbsoluteExpiryTime(GetNextUpdateTimeFromPayload(payload)) - }, + }, cancellationToken); } @@ -165,7 +166,7 @@ protected virtual async Task GetMemoryCachedPayload(IMetada { var distributedCacheBlob = await GetDistributedCachedBlob(repository, cancellationToken); - if(distributedCacheBlob != null) + if (distributedCacheBlob != null) { var nextUpdateTime = GetNextUpdateTimeFromPayload(distributedCacheBlob); diff --git a/Src/Fido2.AspNet/Fido2NetLibBuilderExtensions.cs b/Src/Fido2.AspNet/Fido2NetLibBuilderExtensions.cs index ebdd7cb5..b4d2a10c 100644 --- a/Src/Fido2.AspNet/Fido2NetLibBuilderExtensions.cs +++ b/Src/Fido2.AspNet/Fido2NetLibBuilderExtensions.cs @@ -62,7 +62,7 @@ public static IFido2MetadataServiceBuilder AddFileSystemMetadataRepository(this public static IFido2MetadataServiceBuilder AddConformanceMetadataRepository( this IFido2MetadataServiceBuilder builder, - HttpClient client = null, + HttpClient client = null, string origin = "") { builder.Services.AddTransient(provider => @@ -77,7 +77,8 @@ public static IFido2MetadataServiceBuilder AddFidoMetadataRepository(this IFido2 { var httpClientBuilder = builder.Services.AddHttpClient(nameof(Fido2MetadataServiceRepository)); - if(clientBuilder != null) clientBuilder(httpClientBuilder); + if (clientBuilder != null) + clientBuilder(httpClientBuilder); builder.Services.AddTransient(); diff --git a/Src/Fido2.Ctap2/Commands/AuthenticatorClientPinCommand.cs b/Src/Fido2.Ctap2/Commands/AuthenticatorClientPinCommand.cs index 22b05e80..87f92ff0 100644 --- a/Src/Fido2.Ctap2/Commands/AuthenticatorClientPinCommand.cs +++ b/Src/Fido2.Ctap2/Commands/AuthenticatorClientPinCommand.cs @@ -13,12 +13,12 @@ public AuthenticatorClientPinCommand( byte[]? newPinEnc = null, byte[]? pinHashEnc = null) { - + PinProtocol = pinProtocol; SubCommand = subCommand; KeyAgreement = keyAgreement; PinAuth = pinAuth; - NewPinEnc = newPinEnc; + NewPinEnc = newPinEnc; PinHashEnc = pinHashEnc; } @@ -96,6 +96,7 @@ public AuthenticatorClientPinCommand( public enum AuthenticatorClientPinSubCommand { + #pragma warning disable format GetRetries = 0x01, GetKeyAgreement = 0x02, SetPin = 0x03, diff --git a/Src/Fido2.Ctap2/Commands/AuthenticatorGetAssertionCommand.cs b/Src/Fido2.Ctap2/Commands/AuthenticatorGetAssertionCommand.cs index 25947af6..027f98b4 100644 --- a/Src/Fido2.Ctap2/Commands/AuthenticatorGetAssertionCommand.cs +++ b/Src/Fido2.Ctap2/Commands/AuthenticatorGetAssertionCommand.cs @@ -6,7 +6,7 @@ namespace Fido2NetLib.Ctap2; public sealed class AuthenticatorGetAssertionCommand : CtapCommand { public AuthenticatorGetAssertionCommand( - string rpId, + string rpId, byte[] clientDataHash, PublicKeyCredentialDescriptor[] allowList, CborMap? extensions = null, diff --git a/Src/Fido2.Ctap2/Commands/AuthenticatorMakeCredentialCommand.cs b/Src/Fido2.Ctap2/Commands/AuthenticatorMakeCredentialCommand.cs index ffdba95a..ebb446b0 100644 --- a/Src/Fido2.Ctap2/Commands/AuthenticatorMakeCredentialCommand.cs +++ b/Src/Fido2.Ctap2/Commands/AuthenticatorMakeCredentialCommand.cs @@ -83,7 +83,7 @@ public AuthenticatorMakeCredentialCommand( }; var pubKeyCredParams = new CborArray(); - + foreach (PubKeyCredParam pubKeyCredParam in PubKeyCredParams) { pubKeyCredParams.Add(pubKeyCredParam.ToCborObject()); diff --git a/Src/Fido2.Ctap2/Commands/CtapCommand.cs b/Src/Fido2.Ctap2/Commands/CtapCommand.cs index 49c286e5..9eada65a 100644 --- a/Src/Fido2.Ctap2/Commands/CtapCommand.cs +++ b/Src/Fido2.Ctap2/Commands/CtapCommand.cs @@ -16,7 +16,7 @@ public byte[] GetPayload() { return new byte[] { (byte)Type }; } - + var encodedObject = parameters.Encode(); var result = new byte[encodedObject.Length + 1]; @@ -25,6 +25,6 @@ public byte[] GetPayload() encodedObject.AsSpan().CopyTo(result.AsSpan(1)); - return result; + return result; } } diff --git a/Src/Fido2.Ctap2/Commands/CtapCommandType.cs b/Src/Fido2.Ctap2/Commands/CtapCommandType.cs index d6bfac93..c42f53f6 100644 --- a/Src/Fido2.Ctap2/Commands/CtapCommandType.cs +++ b/Src/Fido2.Ctap2/Commands/CtapCommandType.cs @@ -1,5 +1,6 @@ namespace Fido2NetLib.Ctap2; +#pragma warning disable format public enum CtapCommandType : byte { // | value | has parameters diff --git a/Src/Fido2.Ctap2/Devices/FidoAuthenticator.cs b/Src/Fido2.Ctap2/Devices/FidoAuthenticator.cs index 07f08315..36fd02f0 100644 --- a/Src/Fido2.Ctap2/Devices/FidoAuthenticator.cs +++ b/Src/Fido2.Ctap2/Devices/FidoAuthenticator.cs @@ -32,7 +32,7 @@ public async ValueTask GetInfoAsync() return AuthenticatorGetInfoResponse.FromCborObject(result.GetCborObject()); } - + public async ValueTask ExecuteClientPinCommandAsync(AuthenticatorClientPinCommand command) { @@ -87,14 +87,14 @@ public async ValueTask SetNewPinAsync(string newPinUnicode, CredentialPublicKey byte[] newPinEnc = CryptoHelper.AesCbcDefaultIvNoPadding(sharedSecret, CryptoHelper.ZeroPadRight(newPin, 64)); // LEFT(HMAC-SHA-256(sharedSecret, newPinEnc), 16) - var pinAuth = HMACSHA256.HashData(sharedSecret, newPinEnc).AsSpan(0, 16).ToArray(); + var pinAuth = HMACSHA256.HashData(sharedSecret, newPinEnc).AsSpan(0, 16).ToArray(); var command = new AuthenticatorClientPinCommand( - pinProtocol : 0x01, - subCommand : AuthenticatorClientPinSubCommand.SetPin, - keyAgreement : platformKey, - pinAuth : pinAuth, - newPinEnc : newPinEnc + pinProtocol: 0x01, + subCommand: AuthenticatorClientPinSubCommand.SetPin, + keyAgreement: platformKey, + pinAuth: pinAuth, + newPinEnc: newPinEnc ); _ = await ExecuteClientPinCommandAsync(command).ConfigureAwait(false); @@ -122,15 +122,15 @@ public async ValueTask ChangePinAsync(string curPinUnicode, string newPinUnicode byte[] pinAuth = HMACSHA256.HashData(sharedSecret, newPinEnc).AsSpan(0, 16).ToArray(); var command = new AuthenticatorClientPinCommand( - pinProtocol : 0x01, - subCommand : AuthenticatorClientPinSubCommand.ChangePin, - keyAgreement : platformKey, - pinAuth : pinAuth, - newPinEnc : newPinEnc, - pinHashEnc : pinHashEnc + pinProtocol: 0x01, + subCommand: AuthenticatorClientPinSubCommand.ChangePin, + keyAgreement: platformKey, + pinAuth: pinAuth, + newPinEnc: newPinEnc, + pinHashEnc: pinHashEnc ); - _= await ExecuteClientPinCommandAsync(command).ConfigureAwait(false); + _ = await ExecuteClientPinCommandAsync(command).ConfigureAwait(false); } public async ValueTask GetPinTokenAsync(string pin, CredentialPublicKey platformKey, byte[] sharedSecret) @@ -143,10 +143,10 @@ public async ValueTask GetPinTokenAsync(string pin, CredentialPublicKey byte[] pinHashEnc = CryptoHelper.AesCbcDefaultIvNoPadding(sharedSecret, SHA256.HashData(curPin).AsSpan(0, 16)); var command = new AuthenticatorClientPinCommand( - pinProtocol : 0x01, - subCommand : AuthenticatorClientPinSubCommand.GetPinToken, - keyAgreement : platformKey, - pinHashEnc : pinHashEnc + pinProtocol: 0x01, + subCommand: AuthenticatorClientPinSubCommand.GetPinToken, + keyAgreement: platformKey, + pinHashEnc: pinHashEnc ); var result = await ExecuteClientPinCommandAsync(command).ConfigureAwait(false); diff --git a/Src/Fido2.Ctap2/Extensions/PubKeyCredParamExtensions.cs b/Src/Fido2.Ctap2/Extensions/PubKeyCredParamExtensions.cs index a4e1b50c..bfe67af5 100644 --- a/Src/Fido2.Ctap2/Extensions/PubKeyCredParamExtensions.cs +++ b/Src/Fido2.Ctap2/Extensions/PubKeyCredParamExtensions.cs @@ -19,6 +19,7 @@ public static class AuthenticatorTransportExtensions { public static string Canonicalize(this AuthenticatorTransport value) { + #pragma warning disable format return value switch { AuthenticatorTransport.Usb => "usb", @@ -27,5 +28,6 @@ public static string Canonicalize(this AuthenticatorTransport value) AuthenticatorTransport.Internal => "internal", _ => value.ToString() }; + #pragma warning restore format } } diff --git a/Src/Fido2.Ctap2/Extensions/PublicKeyCredentialRpEntityExtensions.cs b/Src/Fido2.Ctap2/Extensions/PublicKeyCredentialRpEntityExtensions.cs index c63d736e..25698688 100644 --- a/Src/Fido2.Ctap2/Extensions/PublicKeyCredentialRpEntityExtensions.cs +++ b/Src/Fido2.Ctap2/Extensions/PublicKeyCredentialRpEntityExtensions.cs @@ -14,7 +14,7 @@ public static CborMap ToCborObject(this PublicKeyCredentialRpEntity rp) if (rp.Icon is string icon) { result.Add("icon", icon); - } + } return result; } diff --git a/Src/Fido2.Ctap2/Helpers/CryptoHelper.cs b/Src/Fido2.Ctap2/Helpers/CryptoHelper.cs index 6ee7c1c9..64c10f3d 100644 --- a/Src/Fido2.Ctap2/Helpers/CryptoHelper.cs +++ b/Src/Fido2.Ctap2/Helpers/CryptoHelper.cs @@ -17,7 +17,7 @@ internal static byte[] AesCbcDefaultIvNoPadding(byte[] key, ReadOnlySpan d // AES256-CBC(sharedSecret, IV = 0, data). return aes.EncryptCbc(data, iv: DefaultIV, PaddingMode.None); } - + public static byte[] GenerateSharedSecret(CredentialPublicKey authenticatorKeyAgreementKey, out CredentialPublicKey platformKeyAgreementKey) { using var authenticatorKey = authenticatorKeyAgreementKey.CreateECDsa(); // public key diff --git a/Src/Fido2.Ctap2/Responses/AuthenticatorGetInfoResponse.cs b/Src/Fido2.Ctap2/Responses/AuthenticatorGetInfoResponse.cs index 6243432d..e830b7c9 100644 --- a/Src/Fido2.Ctap2/Responses/AuthenticatorGetInfoResponse.cs +++ b/Src/Fido2.Ctap2/Responses/AuthenticatorGetInfoResponse.cs @@ -24,7 +24,7 @@ public sealed class AuthenticatorGetInfoResponse /// [CborMember(0x03)] public byte[] Aaguid { get; set; } - + /// /// List of supported options. /// diff --git a/Src/Fido2.Ctap2/Responses/AuthenticatorMakeCredentialResponse.cs b/Src/Fido2.Ctap2/Responses/AuthenticatorMakeCredentialResponse.cs index dfb17f58..f7801ffd 100644 --- a/Src/Fido2.Ctap2/Responses/AuthenticatorMakeCredentialResponse.cs +++ b/Src/Fido2.Ctap2/Responses/AuthenticatorMakeCredentialResponse.cs @@ -46,11 +46,13 @@ public static AuthenticatorMakeCredentialResponse FromCborObject(CborObject cbor { switch ((int)key) { + #pragma warning disable format case 0x01: result.Fmt = (string)value; break; case 0x02: result.AuthData = (byte[])value; break; case 0x03: result.AttStmt = (CborMap)value; break; case 0x04: result.EpAtt = (bool)value; break; case 0x05: result.LargeBlobKey = (byte[])value; break; + #pragma warning restore format } } diff --git a/Src/Fido2.Ctap2/Responses/CtapStatusCode.cs b/Src/Fido2.Ctap2/Responses/CtapStatusCode.cs index 44a30dcf..0e25283e 100644 --- a/Src/Fido2.Ctap2/Responses/CtapStatusCode.cs +++ b/Src/Fido2.Ctap2/Responses/CtapStatusCode.cs @@ -1,5 +1,6 @@ namespace Fido2NetLib.Ctap2; +#pragma warning disable format public enum CtapStatusCode { OK = 0x00, // Indicates successful response diff --git a/Src/Fido2.Ctap2/Responses/NegotiateSharedSecretResult.cs b/Src/Fido2.Ctap2/Responses/NegotiateSharedSecretResult.cs index da646ab7..2c67cd50 100644 --- a/Src/Fido2.Ctap2/Responses/NegotiateSharedSecretResult.cs +++ b/Src/Fido2.Ctap2/Responses/NegotiateSharedSecretResult.cs @@ -5,8 +5,8 @@ namespace Fido2NetLib.Ctap2; public sealed class NegotiateSharedSecretResult { public NegotiateSharedSecretResult( - CredentialPublicKey authenticatorKey, - CredentialPublicKey platformKey, + CredentialPublicKey authenticatorKey, + CredentialPublicKey platformKey, byte[] sharedShared) { ArgumentNullException.ThrowIfNull(authenticatorKey); diff --git a/Src/Fido2.Models/Base64Url.cs b/Src/Fido2.Models/Base64Url.cs index c9dd9222..cb2df877 100644 --- a/Src/Fido2.Models/Base64Url.cs +++ b/Src/Fido2.Models/Base64Url.cs @@ -28,8 +28,12 @@ public static string Encode(ReadOnlySpan arg) switch (c) { - case '+': c = '-'; break; - case '/': c = '_'; break; + case '+': + c = '-'; + break; + case '/': + c = '_'; + break; } } @@ -71,8 +75,12 @@ public static byte[] Decode(ReadOnlySpan text) switch (c) { - case '-': c = '+'; break; - case '_': c = '/'; break; + case '-': + c = '+'; + break; + case '_': + c = '/'; + break; } } @@ -118,8 +126,12 @@ public static byte[] DecodeUtf8(ReadOnlySpan text) switch ((char)c) { - case '-': c = (byte)'+'; break; - case '_': c = (byte)'/'; break; + case '-': + c = (byte)'+'; + break; + case '_': + c = (byte)'/'; + break; } } diff --git a/Src/Fido2.Models/Converters/EnumNameMapper.cs b/Src/Fido2.Models/Converters/EnumNameMapper.cs index 9e155bee..e1a49164 100644 --- a/Src/Fido2.Models/Converters/EnumNameMapper.cs +++ b/Src/Fido2.Models/Converters/EnumNameMapper.cs @@ -7,7 +7,7 @@ namespace Fido2NetLib; public static class EnumNameMapper<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields)] TEnum> - where TEnum: struct, Enum + where TEnum : struct, Enum { private static readonly Dictionary s_valueToNames = GetIdToNameMap(); private static readonly Dictionary s_namesToValues = Invert(s_valueToNames); diff --git a/Src/Fido2.Models/Converters/FidoEnumConverter.cs b/Src/Fido2.Models/Converters/FidoEnumConverter.cs index 57282038..7dc42480 100644 --- a/Src/Fido2.Models/Converters/FidoEnumConverter.cs +++ b/Src/Fido2.Models/Converters/FidoEnumConverter.cs @@ -6,7 +6,7 @@ namespace Fido2NetLib; public sealed class FidoEnumConverter<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields)] T> : JsonConverter - where T: struct, Enum + where T : struct, Enum { public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { diff --git a/Src/Fido2.Models/CredentialCreateOptions.cs b/Src/Fido2.Models/CredentialCreateOptions.cs index abfd3635..1625486c 100644 --- a/Src/Fido2.Models/CredentialCreateOptions.cs +++ b/Src/Fido2.Models/CredentialCreateOptions.cs @@ -49,8 +49,8 @@ public sealed class CredentialCreateOptions : Fido2ResponseBase /// This member is intended for use by Relying Parties that wish to express their preference for attestation conveyance.The default is none. /// [JsonPropertyName("attestation")] - public AttestationConveyancePreference Attestation { get; set; } = AttestationConveyancePreference.None; - + public AttestationConveyancePreference Attestation { get; set; } = AttestationConveyancePreference.None; + /// /// This member is intended for use by Relying Parties that wish to select the appropriate authenticators to participate in the create() operation. /// @@ -138,15 +138,15 @@ public PubKeyCredParam(COSE.Algorithm alg, PublicKeyCredentialType type = Public [JsonPropertyName("alg")] public COSE.Algorithm Alg { get; } - public static readonly PubKeyCredParam ES256 = new(COSE.Algorithm.ES256); // External authenticators support the ES256 algorithm - public static readonly PubKeyCredParam ES384 = new(COSE.Algorithm.ES384); - public static readonly PubKeyCredParam ES512 = new(COSE.Algorithm.ES512); - public static readonly PubKeyCredParam RS256 = new(COSE.Algorithm.RS256); // Supported by windows hello - public static readonly PubKeyCredParam RS384 = new(COSE.Algorithm.RS384); - public static readonly PubKeyCredParam RS512 = new(COSE.Algorithm.RS512); - public static readonly PubKeyCredParam PS256 = new(COSE.Algorithm.PS256); - public static readonly PubKeyCredParam PS384 = new(COSE.Algorithm.PS384); - public static readonly PubKeyCredParam PS512 = new(COSE.Algorithm.PS512); + public static readonly PubKeyCredParam ES256 = new(COSE.Algorithm.ES256); // External authenticators support the ES256 algorithm + public static readonly PubKeyCredParam ES384 = new(COSE.Algorithm.ES384); + public static readonly PubKeyCredParam ES512 = new(COSE.Algorithm.ES512); + public static readonly PubKeyCredParam RS256 = new(COSE.Algorithm.RS256); // Supported by windows hello + public static readonly PubKeyCredParam RS384 = new(COSE.Algorithm.RS384); + public static readonly PubKeyCredParam RS512 = new(COSE.Algorithm.RS512); + public static readonly PubKeyCredParam PS256 = new(COSE.Algorithm.PS256); + public static readonly PubKeyCredParam PS384 = new(COSE.Algorithm.PS384); + public static readonly PubKeyCredParam PS512 = new(COSE.Algorithm.PS512); public static readonly PubKeyCredParam Ed25519 = new(COSE.Algorithm.EdDSA); } @@ -225,7 +225,7 @@ public ResidentKeyRequirement ResidentKey /// [Obsolete("Use property ResidentKey.")] [JsonPropertyName("requireResidentKey")] - public bool RequireResidentKey + public bool RequireResidentKey { get => _requireResidentKey; set diff --git a/Src/Fido2.Models/Metadata/CodeAccuracyDescriptor.cs b/Src/Fido2.Models/Metadata/CodeAccuracyDescriptor.cs index e45354aa..467ed775 100644 --- a/Src/Fido2.Models/Metadata/CodeAccuracyDescriptor.cs +++ b/Src/Fido2.Models/Metadata/CodeAccuracyDescriptor.cs @@ -15,14 +15,14 @@ public sealed class CodeAccuracyDescriptor /// Gets or sets the numeric system base (radix) of the code, e.g. 10 in the case of decimal digits. /// [JsonPropertyName("base"), Required] - public ushort Base { get; set; } - + public ushort Base { get; set; } + /// /// Gets or sets the minimum number of digits of the given base required for that code, e.g. 4 in the case of 4 digits. /// [JsonPropertyName("minLength"), Required] - public ushort MinLength { get; set; } - + public ushort MinLength { get; set; } + /// /// Gets or sets the maximum number of false attempts before the authenticator will block this method (at least for some time). /// Zero (0) means it will never block. diff --git a/Src/Fido2.Models/Metadata/DisplayPNGCharacteristicsDescriptor.cs b/Src/Fido2.Models/Metadata/DisplayPNGCharacteristicsDescriptor.cs index 4a3a772f..4affbf17 100644 --- a/Src/Fido2.Models/Metadata/DisplayPNGCharacteristicsDescriptor.cs +++ b/Src/Fido2.Models/Metadata/DisplayPNGCharacteristicsDescriptor.cs @@ -15,8 +15,8 @@ public sealed class DisplayPNGCharacteristicsDescriptor /// Gets or sets the image width. /// [JsonPropertyName("width"), Required] - public ulong Width { get; set; } - + public ulong Width { get; set; } + /// /// Gets or sets the image height. /// diff --git a/Src/Fido2.Models/Metadata/MetadataBLOBPayload.cs b/Src/Fido2.Models/Metadata/MetadataBLOBPayload.cs index 275e79af..32da3307 100644 --- a/Src/Fido2.Models/Metadata/MetadataBLOBPayload.cs +++ b/Src/Fido2.Models/Metadata/MetadataBLOBPayload.cs @@ -34,7 +34,7 @@ public sealed class MetadataBLOBPayload /// [JsonPropertyName("nextUpdate"), Required] public string NextUpdate { get; set; } - + /// /// Gets or sets a list of zero or more entries of . /// diff --git a/Src/Fido2.Models/Objects/AttestationConveyancePreference.cs b/Src/Fido2.Models/Objects/AttestationConveyancePreference.cs index de7427fe..517fd21e 100644 --- a/Src/Fido2.Models/Objects/AttestationConveyancePreference.cs +++ b/Src/Fido2.Models/Objects/AttestationConveyancePreference.cs @@ -28,10 +28,10 @@ public enum AttestationConveyancePreference /// [EnumMember(Value = "direct")] Direct, - + /// /// This value indicates that the Relying Party wants to receive an attestation statement that may include uniquely identifying information. /// - [EnumMember(Value ="enterprise")] + [EnumMember(Value = "enterprise")] Enterprise } diff --git a/Src/Fido2.Models/Objects/AttestationVerificationSuccess.cs b/Src/Fido2.Models/Objects/AttestationVerificationSuccess.cs index c2c14f91..a678aa07 100644 --- a/Src/Fido2.Models/Objects/AttestationVerificationSuccess.cs +++ b/Src/Fido2.Models/Objects/AttestationVerificationSuccess.cs @@ -11,7 +11,7 @@ public class AttestationVerificationSuccess : AssertionVerificationResult public Fido2User User { get; set; } public string CredType { get; set; } - + public Guid AaGuid { get; set; } /// /// The type of the public key credential source. diff --git a/Src/Fido2.Models/Objects/AuthenticationExtensionsClientInputs.cs b/Src/Fido2.Models/Objects/AuthenticationExtensionsClientInputs.cs index 5b797b7b..9d89fef6 100644 --- a/Src/Fido2.Models/Objects/AuthenticationExtensionsClientInputs.cs +++ b/Src/Fido2.Models/Objects/AuthenticationExtensionsClientInputs.cs @@ -61,7 +61,7 @@ public sealed class AuthenticationExtensionsClientInputs [JsonPropertyName("credProps")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public bool? CredProps { get; set; } - + /// /// This extension allows a Relying Party to evaluate outputs from a pseudo-random function (PRF) associated with a credential. /// https://w3c.github.io/webauthn/#prf-extension diff --git a/Src/Fido2.Models/Objects/AuthenticationExtensionsClientOutputs.cs b/Src/Fido2.Models/Objects/AuthenticationExtensionsClientOutputs.cs index 52148310..00548c5e 100644 --- a/Src/Fido2.Models/Objects/AuthenticationExtensionsClientOutputs.cs +++ b/Src/Fido2.Models/Objects/AuthenticationExtensionsClientOutputs.cs @@ -19,7 +19,7 @@ public class AuthenticationExtensionsClientOutputs /// [JsonPropertyName("appid")] public bool AppID { get; set; } - + /// /// This extension allows a WebAuthn Relying Party to guide the selection of the authenticator that will be leveraged when creating the credential. It is intended primarily for Relying Parties that wish to tightly control the experience around credential creation. /// https://www.w3.org/TR/webauthn/#sctn-authenticator-selection-extension @@ -57,7 +57,7 @@ public class AuthenticationExtensionsClientOutputs [JsonPropertyName("credProps")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public CredentialPropertiesOutput? CredProps { get; set; } - + /// /// This extension allows a Relying Party to evaluate outputs from a pseudo-random function (PRF) associated with a credential. /// https://w3c.github.io/webauthn/#prf-extension @@ -65,4 +65,4 @@ public class AuthenticationExtensionsClientOutputs [JsonPropertyName("prf")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public AuthenticationExtensionsPRFOutputs? PRF { get; set; } -} \ No newline at end of file +} diff --git a/Src/Fido2.Models/Objects/CredentialPropertiesOutput.cs b/Src/Fido2.Models/Objects/CredentialPropertiesOutput.cs index 9243b5d4..0b7d8ccb 100644 --- a/Src/Fido2.Models/Objects/CredentialPropertiesOutput.cs +++ b/Src/Fido2.Models/Objects/CredentialPropertiesOutput.cs @@ -1,4 +1,4 @@ -#nullable enable +#nullable enable using System.Text.Json.Serialization; namespace Fido2NetLib.Objects; diff --git a/Src/Fido2/Asn1Element.cs b/Src/Fido2/Asn1Element.cs index 86fa2c59..f0433d82 100644 --- a/Src/Fido2/Asn1Element.cs +++ b/Src/Fido2/Asn1Element.cs @@ -12,8 +12,8 @@ internal readonly struct Asn1Element private readonly List? _elements; // set | sequence public Asn1Element( - Asn1Tag tag, - ReadOnlyMemory encodedValue, + Asn1Tag tag, + ReadOnlyMemory encodedValue, List? elements = null) { _tag = tag; @@ -91,7 +91,7 @@ internal void CheckPrimitive() internal string GetOID() { - return AsnDecoder.ReadObjectIdentifier(_encodedValue.Span, AsnEncodingRules.DER, out int _); + return AsnDecoder.ReadObjectIdentifier(_encodedValue.Span, AsnEncodingRules.DER, out int _); } internal string GetString() @@ -169,7 +169,7 @@ private static List ReadElements(AsnReader reader) Asn1Tag tag = reader.PeekTag(); Asn1Element el; - + if (tag == Asn1Tag.Sequence) { el = new Asn1Element(tag, Array.Empty(), ReadElements(reader.ReadSequence())); diff --git a/Src/Fido2/AttestationFormat/AndroidKey.cs b/Src/Fido2/AttestationFormat/AndroidKey.cs index ba8f05d5..3549f9f1 100644 --- a/Src/Fido2/AttestationFormat/AndroidKey.cs +++ b/Src/Fido2/AttestationFormat/AndroidKey.cs @@ -77,7 +77,7 @@ public static bool IsOriginGenerated(byte[] attExtBytes) break; } } - + var teeEnforced = keyDescription[7].Sequence; foreach (Asn1Element s in teeEnforced) { @@ -90,7 +90,7 @@ public static bool IsOriginGenerated(byte[] attExtBytes) break; } } - + return (softwareEnforcedOriginValue is 0 && teeEnforcedOriginValue is 0); } @@ -172,7 +172,7 @@ public override (AttestationType, X509Certificate2[]) Verify(VerifyAttestationRe X509Certificate2 androidKeyCert = trustPath[0]; ECDsa androidKeyPubKey = androidKeyCert.GetECDsaPublicKey()!; // attestation public key - + byte[] ecSignature; try { diff --git a/Src/Fido2/AttestationFormat/AndroidSafetyNet.cs b/Src/Fido2/AttestationFormat/AndroidSafetyNet.cs index d810edc6..c18a437f 100644 --- a/Src/Fido2/AttestationFormat/AndroidSafetyNet.cs +++ b/Src/Fido2/AttestationFormat/AndroidSafetyNet.cs @@ -62,7 +62,7 @@ public override (AttestationType, X509Certificate2[]) Verify(VerifyAttestationRe } if (!x5cEl.TryDecodeArrayOfBase64EncodedBytes(out var x5cRawKeys)) - { + { throw new Fido2VerificationException("SafetyNet response JWT header has a malformed x5c value"); } @@ -101,7 +101,7 @@ public override (AttestationType, X509Certificate2[]) Verify(VerifyAttestationRe var tokenHandler = new JwtSecurityTokenHandler(); SecurityToken validatedToken; try - { + { tokenHandler.ValidateToken(responseJwt, validationParameters, out validatedToken); } catch (SecurityTokenException ex) @@ -176,7 +176,7 @@ public override (AttestationType, X509Certificate2[]) Verify(VerifyAttestationRe // 6. Verify that the ctsProfileMatch attribute in the payload of response is true if (ctsProfileMatch is null) throw new Fido2VerificationException(Fido2ErrorCode.InvalidAttestation, "SafetyNet response ctsProfileMatch missing"); - + if (true != ctsProfileMatch) throw new Fido2VerificationException(Fido2ErrorCode.InvalidAttestation, "SafetyNet response ctsProfileMatch false"); diff --git a/Src/Fido2/AttestationFormat/Apple.cs b/Src/Fido2/AttestationFormat/Apple.cs index f0878264..d0ea4515 100644 --- a/Src/Fido2/AttestationFormat/Apple.cs +++ b/Src/Fido2/AttestationFormat/Apple.cs @@ -15,7 +15,7 @@ internal sealed class Apple : AttestationVerifier public static byte[] GetAppleAttestationExtensionValue(X509ExtensionCollection exts) { var appleExtension = exts.FirstOrDefault(static e => e.Oid?.Value is "1.2.840.113635.100.8.2"); - + if (appleExtension is null || appleExtension.RawData is null || appleExtension.RawData.Length < 0x26) throw new Fido2VerificationException(Fido2ErrorCode.InvalidAttestation, "Extension with OID 1.2.840.113635.100.8.2 not found on Apple attestation credCert"); diff --git a/Src/Fido2/AttestationFormat/AppleAppAttest.cs b/Src/Fido2/AttestationFormat/AppleAppAttest.cs index c8a58688..b23d658e 100644 --- a/Src/Fido2/AttestationFormat/AppleAppAttest.cs +++ b/Src/Fido2/AttestationFormat/AppleAppAttest.cs @@ -18,18 +18,18 @@ public static byte[] GetAppleAppIdFromCredCertExtValue(X509ExtensionCollection e if (appleExtension is null || appleExtension.RawData is null) throw new Fido2VerificationException("Extension with OID 1.2.840.113635.100.8.5 not found on Apple AppAttest credCert"); - var appleAttestationASN = Asn1Element.Decode(appleExtension.RawData); - appleAttestationASN.CheckTag(Asn1Tag.Sequence); - foreach (Asn1Element s in appleAttestationASN.Sequence) + var appleAttestationASN = Asn1Element.Decode(appleExtension.RawData); + appleAttestationASN.CheckTag(Asn1Tag.Sequence); + foreach (Asn1Element s in appleAttestationASN.Sequence) + { + if (s.TagValue is 1204) { - if (s.TagValue is 1204) - { - // App ID is the concatenation of your 10-digit team identifier, a period, and your app's CFBundleIdentifier value - s.CheckExactSequenceLength(1); - s[0].CheckTag(Asn1Tag.PrimitiveOctetString); - return s[0].GetOctetString(); - } + // App ID is the concatenation of your 10-digit team identifier, a period, and your app's CFBundleIdentifier value + s.CheckExactSequenceLength(1); + s[0].CheckTag(Asn1Tag.PrimitiveOctetString); + return s[0].GetOctetString(); } + } throw new Fido2VerificationException("Apple AppAttest attestation extension 1.2.840.113635.100.8.5 has invalid data"); } diff --git a/Src/Fido2/AttestationFormat/AttestationVerifier.cs b/Src/Fido2/AttestationFormat/AttestationVerifier.cs index 983057a2..d87a2c44 100644 --- a/Src/Fido2/AttestationFormat/AttestationVerifier.cs +++ b/Src/Fido2/AttestationFormat/AttestationVerifier.cs @@ -19,6 +19,7 @@ public abstract class AttestationVerifier public static AttestationVerifier Create(string formatIdentifier) { + #pragma warning disable format return formatIdentifier switch { "none" => new None(), // https://www.w3.org/TR/webauthn-2/#sctn-none-attestation @@ -31,6 +32,7 @@ public static AttestationVerifier Create(string formatIdentifier) "apple-appattest" => new AppleAppAttest(), // https://developer.apple.com/documentation/devicecheck/validating_apps_that_connect_to_your_server _ => throw new Fido2VerificationException(Fido2ErrorCode.UnknownAttestationType, $"Unknown attestation type. Was '{formatIdentifier}'") }; + #pragma warning restore format } internal static bool IsAttnCertCACert(X509ExtensionCollection exts) diff --git a/Src/Fido2/AttestationFormat/MetadataAttestationType.cs b/Src/Fido2/AttestationFormat/MetadataAttestationType.cs index 306ed6bb..d160d6be 100644 --- a/Src/Fido2/AttestationFormat/MetadataAttestationType.cs +++ b/Src/Fido2/AttestationFormat/MetadataAttestationType.cs @@ -43,7 +43,7 @@ internal enum MetadataAttestationType /// [EnumMember(Value = "anonca")] ATTESTATION_ANONCA = 0x3e0c, - + [EnumMember(Value = "none")] ATTESTATION_NONE = 0x3e0b } diff --git a/Src/Fido2/AttestationFormat/Packed.cs b/Src/Fido2/AttestationFormat/Packed.cs index 0d93de6f..d3db7665 100644 --- a/Src/Fido2/AttestationFormat/Packed.cs +++ b/Src/Fido2/AttestationFormat/Packed.cs @@ -73,7 +73,7 @@ public override (AttestationType, X509Certificate2[]) Verify(VerifyAttestationRe else { throw new Fido2VerificationException(Fido2ErrorCode.InvalidAttestation, "Malformed x5c cert found in packed attestation statement"); - } + } } // The attestation certificate attestnCert MUST be the first element in the array. @@ -113,10 +113,10 @@ public override (AttestationType, X509Certificate2[]) Verify(VerifyAttestationRe } // id-fido-u2f-ce-transports - byte u2fTransports = U2FTransportsFromAttnCert(attestnCert.Extensions); - - // 2d. Optionally, inspect x5c and consult externally provided knowledge to determine whether attStmt conveys a Basic or AttCA attestation - + byte u2fTransports = U2FTransportsFromAttnCert(attestnCert.Extensions); + + // 2d. Optionally, inspect x5c and consult externally provided knowledge to determine whether attStmt conveys a Basic or AttCA attestation + return (AttestationType.AttCa, trustPath); } diff --git a/Src/Fido2/AttestationFormat/Tpm.cs b/Src/Fido2/AttestationFormat/Tpm.cs index 7f4f75eb..e37c3b5a 100644 --- a/Src/Fido2/AttestationFormat/Tpm.cs +++ b/Src/Fido2/AttestationFormat/Tpm.cs @@ -14,7 +14,7 @@ namespace Fido2NetLib; internal sealed class Tpm : AttestationVerifier { - public static readonly HashSet TPMManufacturers = new () + public static readonly HashSet TPMManufacturers = new() { "id:FFFFF1D0", // FIDO testing TPM // From https://trustedcomputinggroup.org/wp-content/uploads/TCG-TPM-Vendor-ID-Registry-Version-1.02-Revision-1.00.pdf @@ -42,7 +42,7 @@ internal sealed class Tpm : AttestationVerifier "id:524F4343", // 'ROCC' Fuzhou Rockchip "id:474F4F47", // 'GOOG' Google }; - + public override (AttestationType, X509Certificate2[]) Verify(VerifyAttestationRequest request) { // 1. Verify that attStmt is valid CBOR conforming to the syntax defined above and perform CBOR decoding on it to extract the contained fields. @@ -59,7 +59,7 @@ public override (AttestationType, X509Certificate2[]) Verify(VerifyAttestationRe if (request.AttStmt["pubArea"] is CborByteString { Length: > 0 } pubAreaObject) { pubArea = new PubArea(pubAreaObject.Value); - } + } if (pubArea is null || pubArea.Unique is null || pubArea.Unique.Length is 0) throw new Fido2VerificationException(Fido2ErrorCode.InvalidAttestation, "Missing or malformed pubArea"); @@ -222,7 +222,7 @@ public override (AttestationType, X509Certificate2[]) Verify(VerifyAttestationRe } } - private static readonly Dictionary CoseCurveToTpm = new () + private static readonly Dictionary CoseCurveToTpm = new() { { 1, TpmEccCurve.TPM_ECC_NIST_P256}, { 2, TpmEccCurve.TPM_ECC_NIST_P384}, @@ -308,8 +308,8 @@ This detects this condition and repacks each devices attributes SEQUENCE into it foreach (Asn1Element o in deviceAttributes[0].Sequence) { - wrappedElements.Add(Asn1Element.CreateSetOf(new List(1) { - Asn1Element.CreateSequence((List)o.Sequence) + wrappedElements.Add(Asn1Element.CreateSetOf(new List(1) { + Asn1Element.CreateSequence((List)o.Sequence) })); } @@ -480,9 +480,9 @@ public CertInfo(byte[] data) public byte[] ExtraData { get; } public byte[] Clock { get; } public byte[] ResetCount { get; } - public byte[] RestartCount { get; } + public byte[] RestartCount { get; } public byte[] Safe { get; } - public byte[] FirmwareVersion { get; } + public byte[] FirmwareVersion { get; } public ushort Alg { get; } public byte[] AttestedName { get; } public byte[] AttestedQualifiedNameBuffer { get; } @@ -584,7 +584,7 @@ public PubArea(byte[] data) } // TPMS_ASYM_PARMS, for TPM_ALG_RSA and TPM_ALG_ECC - if (tpmAlg is TpmAlg.TPM_ALG_RSA or TpmAlg.TPM_ALG_ECC) + if (tpmAlg is TpmAlg.TPM_ALG_RSA or TpmAlg.TPM_ALG_ECC) { Symmetric = AuthDataHelper.GetSizedByteArray(data, ref offset, 2); Scheme = AuthDataHelper.GetSizedByteArray(data, ref offset, 2); @@ -645,13 +645,13 @@ public PubArea(byte[] data) public byte[] Type { get; } public byte[] Alg { get; } public byte[] Attributes { get; } - public byte[] Policy { get; } + public byte[] Policy { get; } public byte[]? Symmetric { get; } public byte[]? Scheme { get; } public byte[]? KeyBits { get; } public uint Exponent { get; } public byte[]? CurveID { get; } - public byte[]? KDF { get;} + public byte[]? KDF { get; } public byte[]? Unique { get; } public TpmEccCurve EccCurve => (TpmEccCurve)Enum.ToObject(typeof(TpmEccCurve), BinaryPrimitives.ReadUInt16BigEndian(CurveID)); public ECPoint ECPoint { get; } diff --git a/Src/Fido2/AuthenticatorAssertionResponse.cs b/Src/Fido2/AuthenticatorAssertionResponse.cs index 92a9764f..2e192926 100644 --- a/Src/Fido2/AuthenticatorAssertionResponse.cs +++ b/Src/Fido2/AuthenticatorAssertionResponse.cs @@ -20,7 +20,7 @@ public sealed class AuthenticatorAssertionResponse : AuthenticatorResponse { private readonly AuthenticatorAssertionRawResponse _raw; - private AuthenticatorAssertionResponse(AuthenticatorAssertionRawResponse raw, AuthenticatorData authenticatorData) + private AuthenticatorAssertionResponse(AuthenticatorAssertionRawResponse raw, AuthenticatorData authenticatorData) : base(raw.Response.ClientDataJson) { _raw = raw; @@ -40,8 +40,8 @@ private AuthenticatorAssertionResponse(AuthenticatorAssertionRawResponse raw, Au public static AuthenticatorAssertionResponse Parse(AuthenticatorAssertionRawResponse rawResponse) { return new AuthenticatorAssertionResponse( - raw : rawResponse, - authenticatorData : AuthenticatorData.Parse(rawResponse.Response.AuthenticatorData) + raw: rawResponse, + authenticatorData: AuthenticatorData.Parse(rawResponse.Response.AuthenticatorData) ); } @@ -153,10 +153,10 @@ public async Task VerifyAsync( } // Pretty sure these conditions are not able to be met due to the AuthenticatorData constructor implementation - if (authData.HasExtensionsData && (authData.Extensions is null || authData.Extensions.Length is 0)) + if (authData.HasExtensionsData && (authData.Extensions is null || authData.Extensions.Length is 0)) throw new Fido2VerificationException(Fido2ErrorCode.MalformedExtensionsDetected, Fido2ErrorMessages.MalformedExtensionsDetected); - if (!authData.HasExtensionsData && authData.Extensions != null) + if (!authData.HasExtensionsData && authData.Extensions != null) throw new Fido2VerificationException(Fido2ErrorCode.UnexpectedExtensionsDetected, Fido2ErrorMessages.UnexpectedExtensionsDetected); // 18. Let hash be the result of computing a hash over the cData using SHA-256. @@ -164,13 +164,13 @@ public async Task VerifyAsync( // 19. Using credentialRecord.publicKey, verify that sig is a valid signature over the binary concatenation of authData and hash. byte[] data = DataHelper.Concat(Raw.Response.AuthenticatorData, hash); - - if (storedPublicKey is null || storedPublicKey.Length is 0) + + if (storedPublicKey is null || storedPublicKey.Length is 0) throw new Fido2VerificationException(Fido2ErrorCode.MissingStoredPublicKey, Fido2ErrorMessages.MissingStoredPublicKey); var cpk = new CredentialPublicKey(storedPublicKey); - if (!cpk.Verify(data, Signature)) + if (!cpk.Verify(data, Signature)) throw new Fido2VerificationException(Fido2ErrorCode.InvalidSignature, Fido2ErrorMessages.InvalidSignature); // 20. If authData.signCount is nonzero or credentialRecord.signCount is nonzero @@ -263,7 +263,7 @@ public async Task VerifyAsync( if (storedDevicePublicKeys.Count > 0) { var matchedDpkRecords = new List(); - + foreach (var storedDevicePublicKey in storedDevicePublicKeys) { var dpkRecord = DevicePublicKeyAuthenticatorOutput.Parse(storedDevicePublicKey); diff --git a/Src/Fido2/AuthenticatorAttestationResponse.cs b/Src/Fido2/AuthenticatorAttestationResponse.cs index d7aa1563..4a75b3ef 100644 --- a/Src/Fido2/AuthenticatorAttestationResponse.cs +++ b/Src/Fido2/AuthenticatorAttestationResponse.cs @@ -86,7 +86,7 @@ public async Task VerifyAsync( // 10. Let hash be the result of computing a hash over response.clientDataJSON using SHA-256. byte[] clientDataHash = SHA256.HashData(Raw.Response.ClientDataJson); - byte[] rpIdHash = SHA256.HashData(Encoding.UTF8.GetBytes(originalOptions.Rp.Id)); + byte[] rpIdHash = SHA256.HashData(Encoding.UTF8.GetBytes(originalOptions.Rp.Id)); // 11. Perform CBOR decoding on the attestationObject field of the AuthenticatorAttestationResponse structure to obtain the attestation statement format fmt, // the authenticator data authData, and the attestation statement attStmt. @@ -142,9 +142,9 @@ public async Task VerifyAsync( // For example, the FIDO Metadata Service [FIDOMetadataService] provides one way to obtain such information, using the aaguid in the attestedCredentialData in authData. MetadataBLOBPayloadEntry? metadataEntry = null; - if(metadataService != null) + if (metadataService != null) metadataEntry = await metadataService.GetEntryAsync(authData.AttestedCredentialData.AaGuid, cancellationToken); - + // while conformance testing, we must reject any authenticator that we cannot get metadata for if (metadataService?.ConformanceTesting() is true && metadataEntry is null && attType != AttestationType.None && AttestationObject.Fmt is not "fido-u2f") throw new Fido2VerificationException(Fido2ErrorCode.AaGuidNotFound, "AAGUID not found in MDS test metadata"); @@ -275,7 +275,7 @@ public ParsedAttestationObject(string fmt, CborMap attStmt, AuthenticatorData au } public string Fmt { get; } - + public CborMap AttStmt { get; } public AuthenticatorData AuthData { get; } @@ -291,9 +291,9 @@ internal static ParsedAttestationObject FromCbor(CborMap cbor) } return new ParsedAttestationObject( - fmt : fmt, - attStmt : attStmt, - authData : AuthenticatorData.Parse(authData) + fmt: fmt, + attStmt: attStmt, + authData: AuthenticatorData.Parse(authData) ); } } diff --git a/Src/Fido2/AuthenticatorResponse.cs b/Src/Fido2/AuthenticatorResponse.cs index fd11ed03..22c31635 100644 --- a/Src/Fido2/AuthenticatorResponse.cs +++ b/Src/Fido2/AuthenticatorResponse.cs @@ -28,7 +28,7 @@ protected AuthenticatorResponse(ReadOnlySpan utf8EncodedJson) throw new Fido2VerificationException(Fido2ErrorCode.InvalidAuthenticatorResponse, "utf8EncodedJson may not be empty"); // 1. Let JSONtext be the result of running UTF-8 decode on the value of response.clientDataJSON - + // 2. Let C, the client data claimed as collected during the credential creation, be the result of running an implementation-specific JSON parser on JSONtext // Note: C may be any implementation-specific data structure representation, as long as C’s components are referenceable, as required by this algorithm. // We call this AuthenticatorResponse diff --git a/Src/Fido2/Cbor/CborBoolean.cs b/Src/Fido2/Cbor/CborBoolean.cs index a91cc335..af96e7ea 100644 --- a/Src/Fido2/Cbor/CborBoolean.cs +++ b/Src/Fido2/Cbor/CborBoolean.cs @@ -4,7 +4,7 @@ namespace Fido2NetLib.Cbor; public sealed class CborBoolean : CborObject { - public static readonly CborBoolean True = new(true); + public static readonly CborBoolean True = new(true); public static readonly CborBoolean False = new(false); public CborBoolean(bool value) diff --git a/Src/Fido2/Cbor/CborMap.cs b/Src/Fido2/Cbor/CborMap.cs index e66d921e..b2f85334 100644 --- a/Src/Fido2/Cbor/CborMap.cs +++ b/Src/Fido2/Cbor/CborMap.cs @@ -45,11 +45,11 @@ public IEnumerable Values yield return item.Value; } } - } + } public void Add(string key, CborObject value) { - _items.Add(new (new CborTextString(key), value)); + _items.Add(new(new CborTextString(key), value)); } public void Add(string key, bool value) @@ -59,7 +59,7 @@ public void Add(string key, bool value) public void Add(long key, CborObject value) { - _items.Add(new (new CborInteger(key), value)); + _items.Add(new(new CborInteger(key), value)); } public void Add(long key, byte[] value) @@ -94,7 +94,7 @@ public void Add(string key, byte[] value) internal void Add(CborObject key, CborObject value) { - _items.Add(new (key, value)); + _items.Add(new(key, value)); } internal void Add(string key, COSE.Algorithm value) diff --git a/Src/Fido2/Cbor/CborObject.cs b/Src/Fido2/Cbor/CborObject.cs index 6863ffaa..6309a173 100644 --- a/Src/Fido2/Cbor/CborObject.cs +++ b/Src/Fido2/Cbor/CborObject.cs @@ -61,15 +61,15 @@ private static CborObject Read(CborReader reader) return s switch { - CborReaderState.StartMap => ReadMap(reader), - CborReaderState.StartArray => ReadArray(reader), - CborReaderState.TextString => new CborTextString(reader.ReadTextString()), - CborReaderState.Boolean => (CborBoolean)reader.ReadBoolean(), - CborReaderState.ByteString => new CborByteString(reader.ReadByteString()), + CborReaderState.StartMap => ReadMap(reader), + CborReaderState.StartArray => ReadArray(reader), + CborReaderState.TextString => new CborTextString(reader.ReadTextString()), + CborReaderState.Boolean => (CborBoolean)reader.ReadBoolean(), + CborReaderState.ByteString => new CborByteString(reader.ReadByteString()), CborReaderState.UnsignedInteger => new CborInteger(reader.ReadInt64()), CborReaderState.NegativeInteger => new CborInteger(reader.ReadInt64()), - CborReaderState.Null => ReadNull(reader), - _ => throw new Exception($"Unhandled state. Was {s}") + CborReaderState.Null => ReadNull(reader), + _ => throw new Exception($"Unhandled state. Was {s}") }; } diff --git a/Src/Fido2/Cbor/CborType.cs b/Src/Fido2/Cbor/CborType.cs index 71e3c120..22d5c2ea 100644 --- a/Src/Fido2/Cbor/CborType.cs +++ b/Src/Fido2/Cbor/CborType.cs @@ -1,7 +1,7 @@ namespace Fido2NetLib.Cbor; public enum CborType -{ +{ Map, Array, Boolean, diff --git a/Src/Fido2/Extensions/AuthDataHelper.cs b/Src/Fido2/Extensions/AuthDataHelper.cs index 511949a2..a1990d2a 100644 --- a/Src/Fido2/Extensions/AuthDataHelper.cs +++ b/Src/Fido2/Extensions/AuthDataHelper.cs @@ -16,7 +16,7 @@ public static byte[] GetSizedByteArray(ReadOnlySpan ab, ref int offset, us offset += 2; } byte[] result = null!; - if ((0 < len) && ((offset + len) <= ab.Length)) + if ((0 < len) && ((offset + len) <= ab.Length)) { result = ab.Slice(offset, len).ToArray(); offset += len; diff --git a/Src/Fido2/Extensions/CryptoUtils.cs b/Src/Fido2/Extensions/CryptoUtils.cs index 2969ea9d..9f895c10 100644 --- a/Src/Fido2/Extensions/CryptoUtils.cs +++ b/Src/Fido2/Extensions/CryptoUtils.cs @@ -13,6 +13,7 @@ internal static class CryptoUtils { public static byte[] HashData(HashAlgorithmName hashName, ReadOnlySpan data) { + #pragma warning disable format return hashName.Name switch { "SHA1" => SHA1.HashData(data), @@ -21,28 +22,29 @@ public static byte[] HashData(HashAlgorithmName hashName, ReadOnlySpan dat "SHA512" or "HS512" or "RS512" or "ES512" or "PS512" => SHA512.HashData(data), _ => throw new ArgumentOutOfRangeException(nameof(hashName)), }; + #pragma warning restore format } public static HashAlgorithmName HashAlgFromCOSEAlg(COSE.Algorithm alg) { return alg switch { - COSE.Algorithm.RS1 => HashAlgorithmName.SHA1, - COSE.Algorithm.ES256 => HashAlgorithmName.SHA256, - COSE.Algorithm.ES384 => HashAlgorithmName.SHA384, - COSE.Algorithm.ES512 => HashAlgorithmName.SHA512, - COSE.Algorithm.PS256 => HashAlgorithmName.SHA256, - COSE.Algorithm.PS384 => HashAlgorithmName.SHA384, - COSE.Algorithm.PS512 => HashAlgorithmName.SHA512, - COSE.Algorithm.RS256 => HashAlgorithmName.SHA256, - COSE.Algorithm.RS384 => HashAlgorithmName.SHA384, - COSE.Algorithm.RS512 => HashAlgorithmName.SHA512, + COSE.Algorithm.RS1 => HashAlgorithmName.SHA1, + COSE.Algorithm.ES256 => HashAlgorithmName.SHA256, + COSE.Algorithm.ES384 => HashAlgorithmName.SHA384, + COSE.Algorithm.ES512 => HashAlgorithmName.SHA512, + COSE.Algorithm.PS256 => HashAlgorithmName.SHA256, + COSE.Algorithm.PS384 => HashAlgorithmName.SHA384, + COSE.Algorithm.PS512 => HashAlgorithmName.SHA512, + COSE.Algorithm.RS256 => HashAlgorithmName.SHA256, + COSE.Algorithm.RS384 => HashAlgorithmName.SHA384, + COSE.Algorithm.RS512 => HashAlgorithmName.SHA512, COSE.Algorithm.ES256K => HashAlgorithmName.SHA256, - (COSE.Algorithm)4 => HashAlgorithmName.SHA1, - (COSE.Algorithm)11 => HashAlgorithmName.SHA256, - (COSE.Algorithm)12 => HashAlgorithmName.SHA384, - (COSE.Algorithm)13 => HashAlgorithmName.SHA512, - COSE.Algorithm.EdDSA => HashAlgorithmName.SHA512, + (COSE.Algorithm)4 => HashAlgorithmName.SHA1, + (COSE.Algorithm)11 => HashAlgorithmName.SHA256, + (COSE.Algorithm)12 => HashAlgorithmName.SHA384, + (COSE.Algorithm)13 => HashAlgorithmName.SHA512, + COSE.Algorithm.EdDSA => HashAlgorithmName.SHA512, _ => throw new Fido2VerificationException(Fido2ErrorMessages.InvalidCoseAlgorithmValue), }; } @@ -216,7 +218,7 @@ public static bool IsCertInCRL(byte[] crl, X509Certificate2 cert) Array.Reverse(certificateSerialNumber); // convert to big-endian order var revokedAsnSequence = asnData[0][5].Sequence; - + for (int i = 0; i < revokedAsnSequence.Count; i++) { ReadOnlySpan revokedSerialNumber = revokedAsnSequence[i][0].GetIntegerBytes(); @@ -226,7 +228,7 @@ public static bool IsCertInCRL(byte[] crl, X509Certificate2 cert) return true; } } - + return false; } } diff --git a/Src/Fido2/Extensions/DataHelper.cs b/Src/Fido2/Extensions/DataHelper.cs index 4204a69d..28543633 100644 --- a/Src/Fido2/Extensions/DataHelper.cs +++ b/Src/Fido2/Extensions/DataHelper.cs @@ -32,7 +32,7 @@ public static byte[] Concat(ReadOnlySpan a, ReadOnlySpan b, ReadOnly var position = 0; a.CopyTo(result); position += a.Length; - + b.CopyTo(result.AsSpan(position)); position += b.Length; diff --git a/Src/Fido2/Extensions/EcCurveExtensions.cs b/Src/Fido2/Extensions/EcCurveExtensions.cs index 14649e1a..b2544cdf 100644 --- a/Src/Fido2/Extensions/EcCurveExtensions.cs +++ b/Src/Fido2/Extensions/EcCurveExtensions.cs @@ -20,7 +20,7 @@ public static COSE.EllipticCurve ToCoseCurve(this ECCurve curve) else if (curve.Oid.Value.Equals(ECCurve.NamedCurves.nistP521.Oid.Value, StringComparison.Ordinal)) return COSE.EllipticCurve.P521; - + throw new Exception($"Invalid ECCurve. Was {curve.Oid}"); } } diff --git a/Src/Fido2/Fido2ErrorMessages.cs b/Src/Fido2/Fido2ErrorMessages.cs index 2b5827c7..0a03fc44 100644 --- a/Src/Fido2/Fido2ErrorMessages.cs +++ b/Src/Fido2/Fido2ErrorMessages.cs @@ -2,6 +2,7 @@ namespace Fido2NetLib.Exceptions; +#pragma warning disable format internal static class Fido2ErrorMessages { public static readonly string AssertionResponseTypeNotWebAuthnGet = "AssertionResponse type must be 'webauthn.get'"; diff --git a/Src/Fido2/Fido2NetLib.cs b/Src/Fido2/Fido2NetLib.cs index 1eef6dc0..7cb1494d 100644 --- a/Src/Fido2/Fido2NetLib.cs +++ b/Src/Fido2/Fido2NetLib.cs @@ -73,9 +73,9 @@ public async Task MakeNewCredentialAsync( // todo: Set Errormessage etc. return new CredentialMakeResult( - status : "ok", - errorMessage : string.Empty, - result : success + status: "ok", + errorMessage: string.Empty, + result: success ); } diff --git a/Src/Fido2/IFido2.cs b/Src/Fido2/IFido2.cs index d9fd52ee..62a40611 100644 --- a/Src/Fido2/IFido2.cs +++ b/Src/Fido2/IFido2.cs @@ -8,8 +8,8 @@ namespace Fido2NetLib; public interface IFido2 { AssertionOptions GetAssertionOptions( - IEnumerable allowedCredentials, - UserVerificationRequirement? userVerification, + IEnumerable allowedCredentials, + UserVerificationRequirement? userVerification, AuthenticationExtensionsClientInputs? extensions = null); Task MakeAssertionAsync( diff --git a/Src/Fido2/Metadata/ConformanceMetadataRepository.cs b/Src/Fido2/Metadata/ConformanceMetadataRepository.cs index 83172b57..d6594669 100644 --- a/Src/Fido2/Metadata/ConformanceMetadataRepository.cs +++ b/Src/Fido2/Metadata/ConformanceMetadataRepository.cs @@ -18,7 +18,7 @@ namespace Fido2NetLib; public sealed class ConformanceMetadataRepository : IMetadataRepository { - private static ReadOnlySpan ROOT_CERT => + private static ReadOnlySpan ROOT_CERT => "MIICaDCCAe6gAwIBAgIPBCqih0DiJLW7+UHXx/o1MAoGCCqGSM49BAMDMGcxCzAJ"u8 + "BgNVBAYTAlVTMRYwFAYDVQQKDA1GSURPIEFsbGlhbmNlMScwJQYDVQQLDB5GQUtF"u8 + "IE1ldGFkYXRhIDMgQkxPQiBST09UIEZBS0UxFzAVBgNVBAMMDkZBS0UgUm9vdCBG"u8 + @@ -92,7 +92,7 @@ public async Task GetBLOBAsync(CancellationToken cancellati { continue; } - + if (string.Compare(blob.NextUpdate, combinedBlob.NextUpdate, StringComparison.InvariantCulture) < 0) combinedBlob.NextUpdate = blob.NextUpdate; @@ -100,7 +100,7 @@ public async Task GetBLOBAsync(CancellationToken cancellati combinedBlob.Number = blob.Number; entries.AddRange(blob.Entries); - + combinedBlob.JwtAlg = blob.JwtAlg; } @@ -147,7 +147,7 @@ public async Task DeserializeAndValidateBlobAsync(string ra } var rootCert = X509CertificateHelper.CreateFromBase64String(ROOT_CERT); - var blobCertificates = new X509Certificate2[x5cRawKeys.Length]; + var blobCertificates = new X509Certificate2[x5cRawKeys.Length]; var blobPublicKeys = new List(x5cRawKeys.Length); for (int i = 0; i < x5cRawKeys.Length; i++) @@ -157,7 +157,7 @@ public async Task DeserializeAndValidateBlobAsync(string ra if (cert.GetECDsaPublicKey() is ECDsa ecdsaPublicKey) blobPublicKeys.Add(new ECDsaSecurityKey(ecdsaPublicKey)); - + else if (cert.GetRSAPublicKey() is RSA rsa) blobPublicKeys.Add(new RsaSecurityKey(rsa)); @@ -176,7 +176,7 @@ public async Task DeserializeAndValidateBlobAsync(string ra IssuerSigningKeys = blobPublicKeys, }; - var tokenHandler = new JwtSecurityTokenHandler() + var tokenHandler = new JwtSecurityTokenHandler() { // 250k isn't enough bytes for conformance test tool // https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/issues/1097 @@ -188,13 +188,13 @@ public async Task DeserializeAndValidateBlobAsync(string ra validationParameters, out var validatedToken); - if(blobCertificates.Length > 1) + if (blobCertificates.Length > 1) { certChain.ChainPolicy.ExtraStore.AddRange(blobCertificates.Skip(1).ToArray()); } - + var certChainIsValid = certChain.Build(blobCertificates[0]); - + // if the root is trusted in the context we are running in, valid should be true here if (!certChainIsValid) { @@ -214,7 +214,7 @@ public async Task DeserializeAndValidateBlobAsync(string ra // and that the number of elements in the chain accounts for what was in x5c plus the root we added certChain.ChainElements.Count == (x5cRawKeys.Length + 1) && // and that the root cert has exactly one status with the value of UntrustedRoot - certChain.ChainElements[^1].ChainElementStatus is [ { Status: X509ChainStatusFlags.UntrustedRoot } ]) + certChain.ChainElements[^1].ChainElementStatus is [{ Status: X509ChainStatusFlags.UntrustedRoot }]) { // if we are good so far, that is a good sign certChainIsValid = true; diff --git a/Src/Fido2/Metadata/Fido2MetadataServiceRepository.cs b/Src/Fido2/Metadata/Fido2MetadataServiceRepository.cs index 414015bd..b78df1ca 100644 --- a/Src/Fido2/Metadata/Fido2MetadataServiceRepository.cs +++ b/Src/Fido2/Metadata/Fido2MetadataServiceRepository.cs @@ -160,11 +160,11 @@ private async Task DeserializeAndValidateBlobAsync(string r validationParameters, out var validatedToken); - if(blobCerts.Length > 1) + if (blobCerts.Length > 1) { certChain.ChainPolicy.ExtraStore.AddRange(blobCerts.Skip(1).ToArray()); - } - + } + var certChainIsValid = certChain.Build(blobCerts[0]); // if the root is trusted in the context we are running in, valid should be true here if (!certChainIsValid) @@ -181,11 +181,11 @@ private async Task DeserializeAndValidateBlobAsync(string r } // otherwise we have to manually validate that the root in the chain we are testing is the root we downloaded - if (rootCert.Thumbprint == certChain.ChainElements[^1].Certificate.Thumbprint && - // and that the number of elements in the chain accounts for what was in x5c plus the root we added - certChain.ChainElements.Count == (x5cRawKeys.Length + 1) && - // and that the root cert has exactly one status with the value of UntrustedRoot - certChain.ChainElements[^1].ChainElementStatus is [ { Status: X509ChainStatusFlags.UntrustedRoot } ]) + if (rootCert.Thumbprint == certChain.ChainElements[^1].Certificate.Thumbprint && + // and that the number of elements in the chain accounts for what was in x5c plus the root we added + certChain.ChainElements.Count == (x5cRawKeys.Length + 1) && + // and that the root cert has exactly one status with the value of UntrustedRoot + certChain.ChainElements[^1].ChainElementStatus is [{ Status: X509ChainStatusFlags.UntrustedRoot }]) { // if we are good so far, that is a good sign certChainIsValid = true; diff --git a/Src/Fido2/Metadata/FileSystemMetadataRepository.cs b/Src/Fido2/Metadata/FileSystemMetadataRepository.cs index fbd81e6a..50fd4895 100644 --- a/Src/Fido2/Metadata/FileSystemMetadataRepository.cs +++ b/Src/Fido2/Metadata/FileSystemMetadataRepository.cs @@ -42,20 +42,21 @@ public async Task GetBLOBAsync(CancellationToken cancellati foreach (var filename in Directory.GetFiles(_path)) { await using var fileStream = new FileStream(filename, FileMode.Open, FileAccess.Read); - MetadataStatement statement = await JsonSerializer.DeserializeAsync(fileStream, FidoModelSerializerContext.Default.MetadataStatement, cancellationToken:cancellationToken) ?? throw new NullReferenceException(nameof(statement)); + MetadataStatement statement = await JsonSerializer.DeserializeAsync(fileStream, FidoModelSerializerContext.Default.MetadataStatement, cancellationToken: cancellationToken) ?? throw new NullReferenceException(nameof(statement)); var conformanceEntry = new MetadataBLOBPayloadEntry { AaGuid = statement.AaGuid, MetadataStatement = statement, - StatusReports = new StatusReport[] - { - new StatusReport - { - Status = AuthenticatorStatus.NOT_FIDO_CERTIFIED - } + StatusReports = new StatusReport[] + { + new StatusReport + { + Status = AuthenticatorStatus.NOT_FIDO_CERTIFIED + } } }; - if (null != conformanceEntry.AaGuid) _entries.Add(conformanceEntry.AaGuid.Value, conformanceEntry); + if (null != conformanceEntry.AaGuid) + _entries.Add(conformanceEntry.AaGuid.Value, conformanceEntry); } } diff --git a/Src/Fido2/Objects/AttestationType.cs b/Src/Fido2/Objects/AttestationType.cs index bc4c553b..03884276 100644 --- a/Src/Fido2/Objects/AttestationType.cs +++ b/Src/Fido2/Objects/AttestationType.cs @@ -8,11 +8,11 @@ namespace Fido2NetLib.Objects; [JsonConverter(typeof(AttestationTypeConverter))] public sealed class AttestationType : IEquatable { - public static readonly AttestationType None = new ("none"); - public static readonly AttestationType Basic = new ("basic"); - public static readonly AttestationType Self = new ("self"); - public static readonly AttestationType AttCa = new ("attca"); - public static readonly AttestationType ECDAA = new ("ecdaa"); + public static readonly AttestationType None = new("none"); + public static readonly AttestationType Basic = new("basic"); + public static readonly AttestationType Self = new("self"); + public static readonly AttestationType AttCa = new("attca"); + public static readonly AttestationType ECDAA = new("ecdaa"); private readonly string _value; @@ -49,7 +49,7 @@ public bool Equals(AttestationType? other) return true; if (other is null) - return false; + return false; return string.Equals(Value, other.Value, StringComparison.Ordinal); } @@ -62,12 +62,12 @@ internal static AttestationType Get(string value) { return value switch { - "none" => None, + "none" => None, "basic" => Basic, - "self" => Self, + "self" => Self, "attca" => AttCa, "ecdaa" => ECDAA, - _ => new AttestationType(value) + _ => new AttestationType(value) }; } } diff --git a/Src/Fido2/Objects/CredentialIdUserHandleParams.cs b/Src/Fido2/Objects/CredentialIdUserHandleParams.cs index f7ee4f79..7d36d923 100644 --- a/Src/Fido2/Objects/CredentialIdUserHandleParams.cs +++ b/Src/Fido2/Objects/CredentialIdUserHandleParams.cs @@ -4,7 +4,7 @@ /// Parameters used for callback function /// public sealed class IsUserHandleOwnerOfCredentialIdParams -{ +{ public IsUserHandleOwnerOfCredentialIdParams(byte[] credentialId, byte[] userHandle) { CredentialId = credentialId; diff --git a/Src/Fido2/Objects/CredentialPublicKey.cs b/Src/Fido2/Objects/CredentialPublicKey.cs index b3a0e864..e9a2d186 100644 --- a/Src/Fido2/Objects/CredentialPublicKey.cs +++ b/Src/Fido2/Objects/CredentialPublicKey.cs @@ -15,7 +15,7 @@ public sealed class CredentialPublicKey internal readonly COSE.Algorithm _alg; internal readonly CborMap _cpk; - public CredentialPublicKey(byte[] cpk) + public CredentialPublicKey(byte[] cpk) : this((CborMap)CborObject.Decode(cpk)) { } public CredentialPublicKey(CborMap cpk) @@ -75,7 +75,7 @@ public bool Verify(ReadOnlySpan data, ReadOnlySpan signature) switch (_type) { case COSE.KeyType.EC2: - using(ECDsa ecdsa = CreateECDsa()) + using (ECDsa ecdsa = CreateECDsa()) { var ecsig = CryptoUtils.SigFromEcDsaSig(signature.ToArray(), ecdsa.KeySize); return ecdsa.VerifyData(data, ecsig, CryptoUtils.HashAlgFromCOSEAlg(_alg)); @@ -104,7 +104,7 @@ internal RSA CreateRSA() { Modulus = (byte[])_cpk[COSE.KeyTypeParameter.N], Exponent = (byte[])_cpk[COSE.KeyTypeParameter.E] - }); + }); } public ECDsa CreateECDsa() @@ -113,7 +113,7 @@ public ECDsa CreateECDsa() { throw new InvalidOperationException($"Must be a EC2 key. Was {_type}"); } - + var point = new ECPoint { X = (byte[])_cpk[COSE.KeyTypeParameter.X], @@ -137,11 +137,11 @@ public ECDsa CreateECDsa() curve = ECCurve.CreateFromFriendlyName("secP256k1"); break; case (COSE.Algorithm.ES256, COSE.EllipticCurve.P256): - curve = ECCurve.NamedCurves.nistP256; + curve = ECCurve.NamedCurves.nistP256; break; case (COSE.Algorithm.ES384, COSE.EllipticCurve.P384): curve = ECCurve.NamedCurves.nistP384; - break; + break; case (COSE.Algorithm.ES512, COSE.EllipticCurve.P521): curve = ECCurve.NamedCurves.nistP521; break; @@ -191,7 +191,7 @@ internal NSec.Cryptography.PublicKey EdDSAPublicKey { throw new InvalidOperationException($"Must be a OKP key. Was {_type}"); } - + switch (_alg) // https://www.iana.org/assignments/cose/cose.xhtml#algorithms { case COSE.Algorithm.EdDSA: diff --git a/Test/Asn1Tests.cs b/Test/Asn1Tests.cs index 3f7de8f2..474ca93f 100644 --- a/Test/Asn1Tests.cs +++ b/Test/Asn1Tests.cs @@ -12,9 +12,9 @@ public class Asn1Tests public void EncodeTpmSan() { Assert.Equal("MG2kazBpMRYwFAYFZ4EFAgEMC2lkOkZGRkZGMUQwMTcwNQYFZ4EFAgIMLEZJRE8yLU5FVC1MSUItVGVzdFRQTUFpa0NlcnRTQU5UQ0dDb25mb3JtYW50MRYwFAYFZ4EFAgMMC2lkOkYxRDAwMDAy", Convert.ToBase64String(TpmSanEncoder.Encode( - ( new Oid("2.23.133.2.1"), "id:FFFFF1D0" ), - ( new Oid("2.23.133.2.2"), "FIDO2-NET-LIB-TestTPMAikCertSANTCGConformant" ), - ( new Oid("2.23.133.2.3"), "id:F1D00002") + (new Oid("2.23.133.2.1"), "id:FFFFF1D0"), + (new Oid("2.23.133.2.2"), "FIDO2-NET-LIB-TestTPMAikCertSANTCGConformant"), + (new Oid("2.23.133.2.3"), "id:F1D00002") ))); } @@ -25,7 +25,7 @@ public void DecodeObjectIdentifierAsOctetString() var decoded = Asn1Element.Decode(data); - Assert.Equal(new Asn1Tag(TagClass.ContextSpecific, (int) UniversalTagNumber.ObjectIdentifier), decoded[0][0][0][0].Tag); + Assert.Equal(new Asn1Tag(TagClass.ContextSpecific, (int)UniversalTagNumber.ObjectIdentifier), decoded[0][0][0][0].Tag); var cdp = Encoding.ASCII.GetString(decoded[0][0][0][0].GetOctetString(decoded[0][0][0][0].Tag)); @@ -101,14 +101,14 @@ public void Decode() Assert.Equal(8, element.Sequence.Count); Assert.Equal(new[] { 2, 10, 2, 10, 4, 4, 16, 16 }, element.Sequence.Select(element => element.TagValue).ToArray()); - Assert.Equal(Asn1Tag.Integer, element[0].Tag); - Assert.Equal(Asn1Tag.Enumerated, element[1].Tag); - Assert.Equal(Asn1Tag.Integer, element[2].Tag); - Assert.Equal(Asn1Tag.Enumerated, element[3].Tag); - Assert.Equal(Asn1Tag.PrimitiveOctetString, element[4].Tag); - Assert.Equal(Asn1Tag.PrimitiveOctetString, element[5].Tag); - Assert.Equal(Asn1Tag.Sequence, element[6].Tag); - Assert.Equal(Asn1Tag.Sequence, element[7].Tag); + Assert.Equal(Asn1Tag.Integer, element[0].Tag); + Assert.Equal(Asn1Tag.Enumerated, element[1].Tag); + Assert.Equal(Asn1Tag.Integer, element[2].Tag); + Assert.Equal(Asn1Tag.Enumerated, element[3].Tag); + Assert.Equal(Asn1Tag.PrimitiveOctetString, element[4].Tag); + Assert.Equal(Asn1Tag.PrimitiveOctetString, element[5].Tag); + Assert.Equal(Asn1Tag.Sequence, element[6].Tag); + Assert.Equal(Asn1Tag.Sequence, element[7].Tag); Assert.True(element[0].IsInteger); Assert.Equal(2, element[0].GetInt32()); @@ -131,9 +131,9 @@ public void DecodeContextSpecificConstructedSet() Assert.Equal(8, element.Sequence.Count); Assert.Equal(new[] { 2, 0, 2, 0, 4, 4, 16, 16 }, element.Sequence.Select(element => element.TagValue).ToArray()); - var element6 = element[6]; - var element6_1 = element[6][1]; - var element6_1_0 = element[6][1][0]; + var element6 = element[6]; + var element6_1 = element[6][1]; + var element6_1_0 = element[6][1][0]; var element6_1_0_0 = element[6][1][0][0]; Assert.True(element6.IsSequence); @@ -147,7 +147,7 @@ public void DecodeContextSpecificConstructedSet() Assert.Equal(Asn1Tag.SetOf, element6_1_0.Tag); - Assert.Equal(2, element6_1_0_0.TagValue); - Assert.Equal(1, element6_1_0_0.GetInt32()); + Assert.Equal(2, element6_1_0_0.TagValue); + Assert.Equal(1, element6_1_0_0.GetInt32()); } } diff --git a/Test/Attestation/AndroidSafetyNet.cs b/Test/Attestation/AndroidSafetyNet.cs index 6985cf48..4b19e354 100644 --- a/Test/Attestation/AndroidSafetyNet.cs +++ b/Test/Attestation/AndroidSafetyNet.cs @@ -63,7 +63,7 @@ public AndroidSafetyNet() var attToBeSigned = _attToBeSignedHash(HashAlgorithmName.SHA256); - var claims = new List + var claims = new List { new Claim("nonce", Convert.ToBase64String(attToBeSigned) , ClaimValueTypes.String), new Claim("ctsProfileMatch", bool.TrueString, ClaimValueTypes.Boolean), @@ -93,7 +93,7 @@ public AndroidSafetyNet() }); } } - + [Fact] public async void TestAndroidSafetyNet() { @@ -298,7 +298,7 @@ public void TestAndroidSafetyNetResponseJWTX5cNoKeys() var jwtParts = Encoding.UTF8.GetString(response).Split('.'); var jwtHeaderJSON = JObject.Parse(Encoding.UTF8.GetString(Base64Url.Decode(jwtParts.First()))); jwtHeaderJSON.Remove("x5c"); - jwtHeaderJSON.Add("x5c", JToken.FromObject(new List { })); + jwtHeaderJSON.Add("x5c", JToken.FromObject(new List { })); jwtParts[0] = Base64Url.Encode(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(jwtHeaderJSON))); response = Encoding.UTF8.GetBytes(string.Join(".", jwtParts)); var attStmt = (CborMap)_attestationObject["attStmt"]; @@ -314,7 +314,7 @@ public void TestAndroidSafetyNetResponseJWTX5cInvalidString() var jwtParts = Encoding.UTF8.GetString(response).Split('.'); var jwtHeaderJSON = JObject.Parse(Encoding.UTF8.GetString(Base64Url.Decode(jwtParts.First()))); jwtHeaderJSON.Remove("x5c"); - jwtHeaderJSON.Add("x5c", JToken.FromObject(new List { "RjFEMA=="})); + jwtHeaderJSON.Add("x5c", JToken.FromObject(new List { "RjFEMA==" })); jwtParts[0] = Base64Url.Encode(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(jwtHeaderJSON))); response = Encoding.UTF8.GetBytes(string.Join(".", jwtParts)); var attStmt = (CborMap)_attestationObject["attStmt"]; @@ -457,7 +457,7 @@ public void TestAndroidSafetyNetResponseClaimTimestampNotYetValid() byte[] serial = RandomNumberGenerator.GetBytes(12); - using (X509Certificate2 publicOnly = attRequest.Create( root, notBefore, notAfter, serial)) + using (X509Certificate2 publicOnly = attRequest.Create(root, notBefore, notAfter, serial)) { attestnCert = publicOnly.CopyWithPrivateKey(ecdsaAtt); } @@ -474,7 +474,7 @@ public void TestAndroidSafetyNetResponseClaimTimestampNotYetValid() }; var x = (byte[])cpk[COSE.KeyTypeParameter.X]; - var y = (byte[])cpk[COSE.KeyTypeParameter.Y]; + var y = (byte[])cpk[COSE.KeyTypeParameter.Y]; _credentialPublicKey = new CredentialPublicKey(cpk); @@ -1052,7 +1052,7 @@ public void TestAndroidSafetyNetCtsProfileMatchFalse() }; var x = (byte[])cpk[COSE.KeyTypeParameter.X]; - var y = (byte[])cpk[COSE.KeyTypeParameter.Y]; + var y = (byte[])cpk[COSE.KeyTypeParameter.Y]; _credentialPublicKey = new CredentialPublicKey(cpk); diff --git a/Test/Attestation/Apple.cs b/Test/Attestation/Apple.cs index 2f635c8e..8c0fae06 100644 --- a/Test/Attestation/Apple.cs +++ b/Test/Attestation/Apple.cs @@ -182,7 +182,7 @@ public void TestAppleInvalidNonce() .Select(x => new X509Certificate2(Convert.FromBase64String(x))) .ToArray(); - var x5c = new CborArray { + var x5c = new CborArray { trustPath[0].RawData, trustPath[1].RawData }; diff --git a/Test/Attestation/AppleAppAttest.cs b/Test/Attestation/AppleAppAttest.cs index 2f8357d9..c1067bbe 100644 --- a/Test/Attestation/AppleAppAttest.cs +++ b/Test/Attestation/AppleAppAttest.cs @@ -92,8 +92,8 @@ public void TestAppleAppAttestMissingX5c() attStmt.Set("x5c", CborNull.Instance); var verifier = new Fido2NetLib.AppleAppAttest(); var ex = Assert.Throws( - () => {(AttestationType attType, X509Certificate[] trustPath) = verifier.Verify(attStmt, _authData, _clientDataJson);}); - + () => { (AttestationType attType, X509Certificate[] trustPath) = verifier.Verify(attStmt, _authData, _clientDataJson); }); + Assert.Equal("Malformed x5c in Apple AppAttest attestation", ex.Message); } diff --git a/Test/Attestation/Packed.cs b/Test/Attestation/Packed.cs index 7d13c465..ac0979ba 100644 --- a/Test/Attestation/Packed.cs +++ b/Test/Attestation/Packed.cs @@ -310,7 +310,8 @@ public async Task TestFull() break; case COSE.KeyType.OKP: { - var avr = new AssertionVerificationResult { + var avr = new AssertionVerificationResult + { CredentialId = new byte[] { 0xf1, 0xd0 }, ErrorMessage = string.Empty, Status = "ok", diff --git a/Test/Attestation/Tpm.cs b/Test/Attestation/Tpm.cs index 19973fdb..81a03781 100644 --- a/Test/Attestation/Tpm.cs +++ b/Test/Attestation/Tpm.cs @@ -16,7 +16,7 @@ namespace Test.Attestation; public class Tpm : Fido2Tests.Attestation { - private readonly X500DistinguishedName attDN = new (""); + private readonly X500DistinguishedName attDN = new(""); private X509Certificate2 attestnCert; private readonly DateTimeOffset notBefore, notAfter; private readonly X509EnhancedKeyUsageExtension tcgKpAIKCertExt; @@ -24,7 +24,7 @@ public class Tpm : Fido2Tests.Attestation private byte[] unique, exponent, curveId, kdf; private byte[] tpmAlg; - private static readonly Dictionary TpmAlgToDigestSizeMap = new () + private static readonly Dictionary TpmAlgToDigestSizeMap = new() { { TpmAlg.TPM_ALG_SHA1, (160/8) }, { TpmAlg.TPM_ALG_SHA256, (256/8) }, @@ -32,7 +32,7 @@ public class Tpm : Fido2Tests.Attestation { TpmAlg.TPM_ALG_SHA512, (512/8) } }; - private static readonly Dictionary CoseCurveToTpm = new () + private static readonly Dictionary CoseCurveToTpm = new() { { 1, TpmEccCurve.TPM_ECC_NIST_P256}, { 2, TpmEccCurve.TPM_ECC_NIST_P384}, @@ -61,9 +61,9 @@ public Tpm() false); byte[] asnEncodedSAN = TpmSanEncoder.Encode( - manufacturer : "id:FFFFF1D0", - model : "FIDO2-NET-LIB-TEST-TPM", - version : "id:F1D00002" + manufacturer: "id:FFFFF1D0", + model: "FIDO2-NET-LIB-TEST-TPM", + version: "id:F1D00002" ); aikCertSanExt = new X509Extension("2.5.29.17", asnEncodedSAN, false); @@ -718,7 +718,7 @@ public void TestTPMVersionNot2() if (alg is COSE.Algorithm.ES512 or COSE.Algorithm.PS512 or COSE.Algorithm.RS512) tpmAlg = TpmAlg.TPM_ALG_SHA512.ToUInt16BigEndianBytes(); if (alg is COSE.Algorithm.RS1) - tpmAlg = TpmAlg.TPM_ALG_SHA1.ToUInt16BigEndianBytes(); + tpmAlg = TpmAlg.TPM_ALG_SHA1.ToUInt16BigEndianBytes(); using RSA rsaRoot = RSA.Create(); RSASignaturePadding padding = GetRSASignaturePaddingForCoseAlgorithm(alg); @@ -1999,7 +1999,7 @@ public void TestTPMCertInfoByteStringZeroLen() if (alg is COSE.Algorithm.ES512 or COSE.Algorithm.PS512 or COSE.Algorithm.RS512) tpmAlg = TpmAlg.TPM_ALG_SHA512.ToUInt16BigEndianBytes(); if (alg is COSE.Algorithm.RS1) - tpmAlg = TpmAlg.TPM_ALG_SHA1.ToUInt16BigEndianBytes(); + tpmAlg = TpmAlg.TPM_ALG_SHA1.ToUInt16BigEndianBytes(); using RSA rsaRoot = RSA.Create(); RSASignaturePadding padding = GetRSASignaturePaddingForCoseAlgorithm(alg); @@ -3978,7 +3978,7 @@ public void TestTPMBadSignature() Assert.Equal("Bad signature in TPM with aikCert", ex.Result.Message); } - [Fact] + [Fact] public void TestTPMAikCertNotV3() { var (type, alg, _) = Fido2Tests._validCOSEParameters[3]; diff --git a/Test/AttestationTypeTests.cs b/Test/AttestationTypeTests.cs index 02d6fcbc..619e8481 100644 --- a/Test/AttestationTypeTests.cs +++ b/Test/AttestationTypeTests.cs @@ -20,7 +20,7 @@ public void CanSerialize() [Fact] public void CanDeserialize() { - Assert.Same(AttestationType.None, JsonSerializer.Deserialize("\"none\"")); + Assert.Same(AttestationType.None, JsonSerializer.Deserialize("\"none\"")); Assert.Same(AttestationType.ECDAA, JsonSerializer.Deserialize("\"ecdaa\"")); } } diff --git a/Test/AuthenticatorResponse.cs b/Test/AuthenticatorResponse.cs index f4fb331d..b6b2c9fa 100644 --- a/Test/AuthenticatorResponse.cs +++ b/Test/AuthenticatorResponse.cs @@ -434,7 +434,8 @@ public async Task TestAuthenticatorAttestationResponseInvalidType() return Task.FromResult(true); }; - var lib = new Fido2(new Fido2Configuration { + var lib = new Fido2(new Fido2Configuration + { ServerDomain = rp, ServerName = rp, Origins = new HashSet { rp }, @@ -463,7 +464,8 @@ public void TestAuthenticatorAttestationResponseInvalidRawId(byte[] value) Type = PublicKeyCredentialType.PublicKey, Id = value, RawId = value, - Response = new AuthenticatorAttestationRawResponse.ResponseData { + Response = new AuthenticatorAttestationRawResponse.ResponseData + { AttestationObject = new CborMap { { "fmt", "testing" }, { "attStmt", new CborMap() }, @@ -1281,9 +1283,9 @@ public void TestAuthenticatorAssertionTypeNotPublicKey() var challenge = RandomNumberGenerator.GetBytes(128); var rp = "https://www.passwordless.dev"; var authenticatorResponse = new AuthenticatorResponse( - type : "webauthn.get", - challenge : challenge, - origin : rp + type: "webauthn.get", + challenge: challenge, + origin: rp ); byte[] clientDataJson = JsonSerializer.SerializeToUtf8Bytes(authenticatorResponse, FidoSerializerContext.Default.AuthenticatorResponse); @@ -1351,9 +1353,9 @@ public void TestAuthenticatorAssertionIdMissing() var challenge = RandomNumberGenerator.GetBytes(128); var rp = "https://www.passwordless.dev"; var authenticatorResponse = new AuthenticatorResponse( - type : "webauthn.get", - challenge : challenge, - origin : rp + type: "webauthn.get", + challenge: challenge, + origin: rp ); byte[] clientDataJson = JsonSerializer.SerializeToUtf8Bytes(authenticatorResponse, FidoSerializerContext.Default.AuthenticatorResponse); @@ -1421,9 +1423,9 @@ public void TestAuthenticatorAssertionRawIdMissing() var rp = "https://www.passwordless.dev"; var authenticatorResponse = new AuthenticatorResponse( - type : "webauthn.get", - challenge : challenge, - origin : rp + type: "webauthn.get", + challenge: challenge, + origin: rp ); byte[] clientDataJson = JsonSerializer.SerializeToUtf8Bytes(authenticatorResponse, FidoSerializerContext.Default.AuthenticatorResponse); @@ -1490,9 +1492,9 @@ public void TestAuthenticatorAssertionUserHandleEmpty() var challenge = RandomNumberGenerator.GetBytes(128); var rp = "https://www.passwordless.dev"; var authenticatorResponse = new AuthenticatorResponse( - type : "webauthn.get", - challenge : challenge, - origin : rp + type: "webauthn.get", + challenge: challenge, + origin: rp ); byte[] clientDataJson = JsonSerializer.SerializeToUtf8Bytes(authenticatorResponse, FidoSerializerContext.Default.AuthenticatorResponse); @@ -1560,9 +1562,9 @@ public void TestAuthenticatorAssertionUserHandleNotOwnerOfPublicKey() var challenge = RandomNumberGenerator.GetBytes(128); var rp = "https://www.passwordless.dev"; var authenticatorResponse = new AuthenticatorResponse( - type : "webauthn.get", - challenge : challenge, - origin : rp + type: "webauthn.get", + challenge: challenge, + origin: rp ); byte[] clientDataJson = JsonSerializer.SerializeToUtf8Bytes(authenticatorResponse, FidoSerializerContext.Default.AuthenticatorResponse); @@ -1630,9 +1632,9 @@ public void TestAuthenticatorAssertionTypeNotWebAuthnGet() var challenge = RandomNumberGenerator.GetBytes(128); var rp = "https://www.passwordless.dev"; var authenticatorResponse = new AuthenticatorResponse( - type : "webauthn.create", - challenge : challenge, - origin : rp + type: "webauthn.create", + challenge: challenge, + origin: rp ); byte[] clientDataJson = JsonSerializer.SerializeToUtf8Bytes(authenticatorResponse, FidoSerializerContext.Default.AuthenticatorResponse); @@ -1701,9 +1703,9 @@ public void TestAuthenticatorAssertionAppId() var rp = "https://www.passwordless.dev"; var authenticatorResponse = new AuthenticatorResponse( - type : "webauthn.get", - challenge : challenge, - origin : rp + type: "webauthn.get", + challenge: challenge, + origin: rp ); byte[] clientDataJson = JsonSerializer.SerializeToUtf8Bytes(authenticatorResponse, FidoSerializerContext.Default.AuthenticatorResponse); @@ -1773,9 +1775,9 @@ public void TestAuthenticatorAssertionInvalidRpIdHash() var rp = "https://www.passwordless.dev"; var authenticatorResponse = new AuthenticatorResponse( - type : "webauthn.get", - challenge : challenge, - origin : rp + type: "webauthn.get", + challenge: challenge, + origin: rp ); byte[] clientDataJson = JsonSerializer.SerializeToUtf8Bytes(authenticatorResponse, FidoSerializerContext.Default.AuthenticatorResponse); @@ -1844,9 +1846,9 @@ public void TestAuthenticatorAssertionUPRequirementNotMet() var rp = "https://www.passwordless.dev"; var authenticatorResponse = new AuthenticatorResponse( - type : "webauthn.get", - challenge : challenge, - origin : rp + type: "webauthn.get", + challenge: challenge, + origin: rp ); byte[] clientDataJson = JsonSerializer.SerializeToUtf8Bytes(authenticatorResponse, FidoSerializerContext.Default.AuthenticatorResponse); @@ -1915,9 +1917,9 @@ public void TestAuthenticatorAssertionUVPolicyNotMet() var rp = "https://www.passwordless.dev"; var authenticatorResponse = new AuthenticatorResponse( - type : "webauthn.get", - challenge : challenge, - origin : rp + type: "webauthn.get", + challenge: challenge, + origin: rp ); byte[] clientDataJson = JsonSerializer.SerializeToUtf8Bytes(authenticatorResponse, FidoSerializerContext.Default.AuthenticatorResponse); @@ -1985,11 +1987,11 @@ public void TestAuthenticatorAssertionBEPolicyRequired() var challenge = RandomNumberGenerator.GetBytes(128); var rp = "https://www.passwordless.dev"; var authenticatorResponse = new AuthenticatorResponse( - type : "webauthn.get", - challenge : challenge, - origin : rp + type: "webauthn.get", + challenge: challenge, + origin: rp ); - + byte[] clientDataJson = JsonSerializer.SerializeToUtf8Bytes(authenticatorResponse, FidoSerializerContext.Default.AuthenticatorResponse); var options = new AssertionOptions @@ -2055,9 +2057,9 @@ public void TestAuthenticatorAssertionBEPolicyDisallow() var challenge = RandomNumberGenerator.GetBytes(128); var rp = "https://www.passwordless.dev"; var authenticatorResponse = new AuthenticatorResponse( - type : "webauthn.get", - challenge : challenge, - origin : rp + type: "webauthn.get", + challenge: challenge, + origin: rp ); byte[] clientDataJson = JsonSerializer.SerializeToUtf8Bytes(authenticatorResponse, FidoSerializerContext.Default.AuthenticatorResponse); @@ -2125,9 +2127,9 @@ public void TestAuthenticatorAssertionBSPolicyRequired() var challenge = RandomNumberGenerator.GetBytes(128); var rp = "https://www.passwordless.dev"; var authenticatorResponse = new AuthenticatorResponse( - type : "webauthn.get", - challenge : challenge, - origin : rp + type: "webauthn.get", + challenge: challenge, + origin: rp ); byte[] clientDataJson = JsonSerializer.SerializeToUtf8Bytes(authenticatorResponse, FidoSerializerContext.Default.AuthenticatorResponse); @@ -2195,9 +2197,9 @@ public void TestAuthenticatorAssertionBSPolicyDisallow() var challenge = RandomNumberGenerator.GetBytes(128); var rp = "https://www.passwordless.dev"; var authenticatorResponse = new AuthenticatorResponse( - type : "webauthn.get", - challenge : challenge, - origin : rp + type: "webauthn.get", + challenge: challenge, + origin: rp ); byte[] clientDataJson = JsonSerializer.SerializeToUtf8Bytes(authenticatorResponse, FidoSerializerContext.Default.AuthenticatorResponse); @@ -2265,15 +2267,16 @@ public void TestAuthenticatorAssertionStoredPublicKeyMissing() var challenge = RandomNumberGenerator.GetBytes(128); var rp = "https://www.passwordless.dev"; - var authenticatorResponse = new AuthenticatorResponse( - type : "webauthn.get", - challenge : challenge, - origin : rp - ); + var authenticatorResponse = new AuthenticatorResponse( + type: "webauthn.get", + challenge: challenge, + origin: rp + ); byte[] clientDataJson = JsonSerializer.SerializeToUtf8Bytes(authenticatorResponse, FidoSerializerContext.Default.AuthenticatorResponse); - var options = new AssertionOptions { + var options = new AssertionOptions + { Challenge = challenge, RpId = rp, AllowCredentials = new[] @@ -2334,11 +2337,11 @@ public void TestAuthenticatorAssertionInvalidSignature() var challenge = RandomNumberGenerator.GetBytes(128); var rp = "https://www.passwordless.dev"; - var authenticatorResponse = new AuthenticatorResponse( - type : "webauthn.get", - challenge : challenge, - origin : rp - ); + var authenticatorResponse = new AuthenticatorResponse( + type: "webauthn.get", + challenge: challenge, + origin: rp + ); byte[] clientDataJson = JsonSerializer.SerializeToUtf8Bytes(authenticatorResponse, FidoSerializerContext.Default.AuthenticatorResponse); @@ -2405,11 +2408,11 @@ public void TestAuthenticatorAssertionSignCountSignature() var challenge = RandomNumberGenerator.GetBytes(128); var rp = "https://www.passwordless.dev"; - var authenticatorResponse = new AuthenticatorResponse( - type : "webauthn.get", - challenge : challenge, - origin : rp - ); + var authenticatorResponse = new AuthenticatorResponse( + type: "webauthn.get", + challenge: challenge, + origin: rp + ); byte[] clientDataJson = JsonSerializer.SerializeToUtf8Bytes(authenticatorResponse, FidoSerializerContext.Default.AuthenticatorResponse); @@ -2428,7 +2431,7 @@ public void TestAuthenticatorAssertionSignCountSignature() fido2_net_lib.Test.Fido2Tests.MakeEdDSA(out _, out var publicKey, out var expandedPrivateKey); Key privateKey = Key.Import(SignatureAlgorithm.Ed25519, expandedPrivateKey, KeyBlobFormat.RawPrivateKey); var cpk = fido2_net_lib.Test.Fido2Tests.MakeCredentialPublicKey(COSE.KeyType.OKP, COSE.Algorithm.EdDSA, COSE.EllipticCurve.Ed25519, publicKey); - + var assertion = new AuthenticatorAssertionRawResponse.AssertionResponse { AuthenticatorData = authData, diff --git a/Test/Base64UrlTest.cs b/Test/Base64UrlTest.cs index 016a8401..30518149 100644 --- a/Test/Base64UrlTest.cs +++ b/Test/Base64UrlTest.cs @@ -13,7 +13,7 @@ public void EncodeAndDecodeResultsAreEqual(byte[] data) // Act var encodedString = Base64Url.Encode(data); var decodedBytes = Base64Url.Decode(encodedString); - + // Assert Assert.Equal(data, decodedBytes); diff --git a/Test/CborTests.cs b/Test/CborTests.cs index d241cc4c..5c67ceb8 100644 --- a/Test/CborTests.cs +++ b/Test/CborTests.cs @@ -10,13 +10,13 @@ public void CanRoundtripAttestationObject() var @object = (CborMap)CborObject.Decode(data); Assert.Equal("android-key", ((CborTextString)@object["fmt"]).Value); - Assert.Equal(158, ((CborByteString)@object["authData"]).Value.Length); + Assert.Equal(158, ((CborByteString)@object["authData"]).Value.Length); var attStmt = (CborMap)@object["attStmt"]; - Assert.Equal(-7, ((CborInteger)attStmt["alg"]).Value); - Assert.Equal(1, ((CborArray)attStmt["x5c"]).Length); - Assert.Equal(70, ((CborByteString)attStmt["sig"]).Value.Length); + Assert.Equal(-7, ((CborInteger)attStmt["alg"]).Value); + Assert.Equal(1, ((CborArray)attStmt["x5c"]).Length); + Assert.Equal(70, ((CborByteString)attStmt["sig"]).Value.Length); Assert.Equal(data, @object.Encode()); } @@ -33,11 +33,11 @@ public void CanReadObjectAndIgnoreRemainingData() var keys = map.Keys.Select(k => ((CborInteger)k).Value); - Assert.Equal(2, ((CborInteger)map[new CborInteger(1)]).Value); - Assert.Equal(-47, ((CborInteger)map[new CborInteger(3)]).Value); + Assert.Equal(2, ((CborInteger)map[new CborInteger(1)]).Value); + Assert.Equal(-47, ((CborInteger)map[new CborInteger(3)]).Value); Assert.Equal("y6rAanAqDpv7VJqZVJWgUVG6jIQvURLOH9Ylf/+nyes=", Convert.ToBase64String(((CborByteString)map[new CborInteger(-2)]).Value)); Assert.Equal("2++Vd74HG5uz9DNaSOeXHOhJKKQ8VVe0VykH5v8TZTc=", Convert.ToBase64String(((CborByteString)map[new CborInteger(-3)]).Value)); - Assert.Equal(8, ((CborInteger)map[new CborInteger(-1)]).Value); + Assert.Equal(8, ((CborInteger)map[new CborInteger(-1)]).Value); // Ensure we ignored the remaining data diff --git a/Test/CredentialPublicKeyTests.cs b/Test/CredentialPublicKeyTests.cs index ab37c4b3..2433c893 100644 --- a/Test/CredentialPublicKeyTests.cs +++ b/Test/CredentialPublicKeyTests.cs @@ -8,10 +8,10 @@ namespace fido2_net_lib.Test; public class CredentialPublicKeyTests { [Theory] - [InlineData("1.3.132.0.10", COSE.Algorithm.ES256K)] // secP256k1 + [InlineData("1.3.132.0.10", COSE.Algorithm.ES256K)] // secP256k1 [InlineData("1.2.840.10045.3.1.7", COSE.Algorithm.ES256)] // P256 - [InlineData("1.3.132.0.34", COSE.Algorithm.ES384)] // P384 - [InlineData("1.3.132.0.35", COSE.Algorithm.ES512)] // P512 + [InlineData("1.3.132.0.34", COSE.Algorithm.ES384)] // P384 + [InlineData("1.3.132.0.35", COSE.Algorithm.ES512)] // P512 public void CanUseECCurves(string oid, COSE.Algorithm alg) { if (OperatingSystem.IsMacOS() && alg is COSE.Algorithm.ES256K) @@ -40,7 +40,7 @@ public void CanUseECCurves(string oid, COSE.Algorithm alg) Assert.Equal(oid, decodedEcDsaParams.Curve.Oid.Value); } - + Assert.True(credentialPublicKey.Verify(signedData, signature)); } diff --git a/Test/CryptoUtilsTests.cs b/Test/CryptoUtilsTests.cs index 17732af0..ea254a26 100644 --- a/Test/CryptoUtilsTests.cs +++ b/Test/CryptoUtilsTests.cs @@ -34,8 +34,8 @@ public void TestCertInCRLTrueCase() [Fact] public void TestValidateTrustChainRootAnchor() { - var attestationRootCertificates = new X509Certificate2[3] - { + var attestationRootCertificates = new X509Certificate2[3] + { new X509Certificate2(Convert.FromBase64String("MIIBfjCCASWgAwIBAgIBATAKBggqhkjOPQQDAjAXMRUwEwYDVQQDDAxGVCBGSURPIDAyMDAwIBcNMTYwNTAxMDAwMDAwWhgPMjA1MDA1MDEwMDAwMDBaMBcxFTATBgNVBAMMDEZUIEZJRE8gMDIwMDBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABNBmrRqVOxztTJVN19vtdqcL7tKQeol2nnM2/yYgvksZnr50SKbVgIEkzHQVOu80LVEE3lVheO1HjggxAlT6o4WjYDBeMB0GA1UdDgQWBBRJFWQt1bvG3jM6XgmV/IcjNtO/CzAfBgNVHSMEGDAWgBRJFWQt1bvG3jM6XgmV/IcjNtO/CzAMBgNVHRMEBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAKBggqhkjOPQQDAgNHADBEAiAwfPqgIWIUB+QBBaVGsdHy0s5RMxlkzpSX/zSyTZmUpQIgB2wJ6nZRM8oX/nA43Rh6SJovM2XwCCH//+LirBAbB0M=")), new X509Certificate2(Convert.FromBase64String("MIIB2DCCAX6gAwIBAgIQFZ97ws2JGPEoa5NI+p8z1jAKBggqhkjOPQQDAjBLMQswCQYDVQQGEwJDTjEdMBsGA1UECgwURmVpdGlhbiBUZWNobm9sb2dpZXMxHTAbBgNVBAMMFEZlaXRpYW4gRklETyBSb290IENBMCAXDTE4MDQwMTAwMDAwMFoYDzIwNDgwMzMxMjM1OTU5WjBLMQswCQYDVQQGEwJDTjEdMBsGA1UECgwURmVpdGlhbiBUZWNobm9sb2dpZXMxHTAbBgNVBAMMFEZlaXRpYW4gRklETyBSb290IENBMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEnfAKbjvMX1Ey1b6k+WQQdNVMt9JgGWyJ3PvM4BSK5XqTfo++0oAj/4tnwyIL0HFBR9St+ktjqSXDfjiXAurs86NCMEAwHQYDVR0OBBYEFNGhmE2Bf8O5a/YHZ71QEv6QRfFUMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMAoGCCqGSM49BAMCA0gAMEUCIQC3sT1lBjGeF+xKTpzV1KYU2ckahTd4mLJyzYOhaHv4igIgD2JYkfyH5Q4Bpo8rroO0It7oYjF2kgy/eSZ3U9Glaqw=")), new X509Certificate2(Convert.FromBase64String("MIIB2DCCAX6gAwIBAgIQGBUrQbdDrm20FZnDsX2CBTAKBggqhkjOPQQDAjBLMQswCQYDVQQGEwJVUzEdMBsGA1UECgwURmVpdGlhbiBUZWNobm9sb2dpZXMxHTAbBgNVBAMMFEZlaXRpYW4gRklETyBSb290IENBMCAXDTE4MDQwMTAwMDAwMFoYDzIwNDgwMzMxMjM1OTU5WjBLMQswCQYDVQQGEwJVUzEdMBsGA1UECgwURmVpdGlhbiBUZWNobm9sb2dpZXMxHTAbBgNVBAMMFEZlaXRpYW4gRklETyBSb290IENBMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEsFYEEhiJuqqnMgQjSiivBjV7DGCTf4XBBH/B7uvZsKxXShF0L8uDISWUvcExixRs6gB3oldSrjox6L8T94NOzqNCMEAwHQYDVR0OBBYEFEu9hyYRrRyJzwRYvnDSCIxrFiO3MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMAoGCCqGSM49BAMCA0gAMEUCIDHSb2mbNDAUNXvpPU0oWKeNye0fQ2l9D01AR2+sLZdhAiEAo3wz684IFMVsCCRmuJqxH6FQRESNqezuo1E+KkGxWuM=")) @@ -77,7 +77,7 @@ public void TestValidateTrustChainSelf() { byte[] certBytes = Convert.FromBase64String("MIIBzTCCAXOgAwIBAgIJALS3SibGDXTPMAoGCCqGSM49BAMCMDsxIDAeBgNVBAMMF0dvVHJ1c3QgRklETzIgUm9vdCBDQSAxMRcwFQYDVQQKDA5Hb1RydXN0SUQgSW5jLjAeFw0xOTEyMDQwNjU5NDBaFw00OTExMjYwNjU5NDBaMDsxIDAeBgNVBAMMF0dvVHJ1c3QgRklETzIgUm9vdCBDQSAxMRcwFQYDVQQKDA5Hb1RydXN0SUQgSW5jLjBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABA5mjYsjowAI0jnpi//CJ3KnzhGbTUmstNWqN78ioG1CTK9gPgPl9UiFOJO/v+FfFK+Pxv10c604dvlIDAbKw+ijYDBeMAwGA1UdEwEB/wQCMAAwDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBSgWtY0nEcmPmGDLuCwceKeJPScozAfBgNVHSMEGDAWgBSgWtY0nEcmPmGDLuCwceKeJPScozAKBggqhkjOPQQDAgNIADBFAiAxoVs6qj7DX2xixCjjcDUdxBTJmSTLb0f1rRGwrABzTQIhAPt0P32qzAeepF4//tgzxqNoKkWDcaPPSXrg+xzrlVHw"); var certs = new X509Certificate2[1] { new X509Certificate2(certBytes) }; - + byte[] otherCertBytes = Convert.FromBase64String("MIIDRjCCAu2gAwIBAgIUZPhSDtxI5lg2qgy+7IGDJhGqPOgwCgYIKoZIzj0EAwIwgYcxCzAJBgNVBAYTAlRXMQ8wDQYDVQQIDAZUYWlwZWkxEjAQBgNVBAcMCVNvbWV3aGVyZTEWMBQGA1UECgwNV2lTRUNVUkUgSW5jLjEgMB4GCSqGSIb3DQEJARYRYWRtaW5AZXhhbXBsZS5vcmcxGTAXBgNVBAMMEFdpU0VDVVJFIFJvb3QgQ0EwHhcNMjEwMTI4MDgyNzIwWhcNMzEwMTI2MDgyNzIwWjCBhzELMAkGA1UEBhMCVFcxDzANBgNVBAgMBlRhaXBlaTESMBAGA1UEBwwJU29tZXdoZXJlMRYwFAYDVQQKDA1XaVNFQ1VSRSBJbmMuMSAwHgYJKoZIhvcNAQkBFhFhZG1pbkBleGFtcGxlLm9yZzEZMBcGA1UEAwwQV2lTRUNVUkUgUm9vdCBDQTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABBiWvFaf/IhFMOWNqlweqr4GfO0mu/1B18J03OG+pSltRix9GjRojBya4LARyXMP8nw2Xh9PvwOBm9QedMC66XGjggEzMIIBLzAdBgNVHQ4EFgQUd+Yvj6I3Y8cKH3QRNLlC8/Op97cwgccGA1UdIwSBvzCBvIAUd+Yvj6I3Y8cKH3QRNLlC8/Op97ehgY2kgYowgYcxCzAJBgNVBAYTAlRXMQ8wDQYDVQQIDAZUYWlwZWkxEjAQBgNVBAcMCVNvbWV3aGVyZTEWMBQGA1UECgwNV2lTRUNVUkUgSW5jLjEgMB4GCSqGSIb3DQEJARYRYWRtaW5AZXhhbXBsZS5vcmcxGTAXBgNVBAMMEFdpU0VDVVJFIFJvb3QgQ0GCFGT4Ug7cSOZYNqoMvuyBgyYRqjzoMAwGA1UdEwEB/wQCMAAwNgYDVR0fBC8wLTAroCmgJ4YlaHR0cDovL3d3dy5leGFtcGxlLm9yZy9leGFtcGxlX2NhLmNybDAKBggqhkjOPQQDAgNHADBEAiBf3p8LJ3PlfMsxTzWgjHaal6uzIo5tx3o+EUybdDY4ogIgV6nR1MUE1wKz1uC7/kENg/FpJOetFaJePcgoneEwsKA="); var otherCerts = new X509Certificate2[1] { new X509Certificate2(otherCertBytes) }; diff --git a/Test/EnumExtensionTest.cs b/Test/EnumExtensionTest.cs index 756dc37a..63ba51db 100644 --- a/Test/EnumExtensionTest.cs +++ b/Test/EnumExtensionTest.cs @@ -43,7 +43,7 @@ public void TestToEnumWithIgnoringCase(string value, bool shouldThrow) // invalid [InlineData("cross_platform", true)] [InlineData("cross-platforms", true)] - [InlineData("CROSS_PLATFORM", true)] + [InlineData("CROSS_PLATFORM", true)] public void TestToEnumWithDashes(string value, bool shouldThrow) { var exception = Record.Exception(() => value.ToEnum()); diff --git a/Test/Extensions/TpmSanEncoder.cs b/Test/Extensions/TpmSanEncoder.cs index be58fee9..23265ea3 100644 --- a/Test/Extensions/TpmSanEncoder.cs +++ b/Test/Extensions/TpmSanEncoder.cs @@ -7,9 +7,9 @@ internal static class TpmSanEncoder { internal static class Oids { - public static readonly Oid Manufacturer = new ("2.23.133.2.1"); - public static readonly Oid Model = new ("2.23.133.2.2"); - public static readonly Oid Version = new ("2.23.133.2.3"); + public static readonly Oid Manufacturer = new("2.23.133.2.1"); + public static readonly Oid Model = new("2.23.133.2.2"); + public static readonly Oid Version = new("2.23.133.2.3"); } public static byte[] Encode(string manufacturer, string model, string version) diff --git a/Test/Fido2Tests.cs b/Test/Fido2Tests.cs index 87be4509..0ba4b020 100644 --- a/Test/Fido2Tests.cs +++ b/Test/Fido2Tests.cs @@ -61,7 +61,7 @@ static Fido2Tests() var noCurve = COSE.EllipticCurve.Reserved; - _validCOSEParameters = new () + _validCOSEParameters = new() { new (COSE.KeyType.EC2, COSE.Algorithm.ES256, COSE.EllipticCurve.P256), new (COSE.KeyType.EC2, COSE.Algorithm.ES384, COSE.EllipticCurve.P384), @@ -75,8 +75,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 GetAsync(string filename) { return JsonSerializer.Deserialize(await File.ReadAllTextAsync(filename)); @@ -118,7 +118,7 @@ public byte[] _clientDataJson public byte[] _attToBeSignedHash(HashAlgorithmName alg) { - return CryptoUtils.HashData(alg, _attToBeSigned); + return CryptoUtils.HashData(alg, _attToBeSigned); } public byte[] _credentialID; @@ -126,15 +126,15 @@ public byte[] _attToBeSignedHash(HashAlgorithmName alg) 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); @@ -228,8 +228,8 @@ public Attestation() ServerDomain = rp, ServerName = rp, Origins = new HashSet { rp }, - }); - + }); + var credentialMakeResult = await lib.MakeNewCredentialAsync(attestationResponse, origChallenge, callback); return credentialMakeResult; @@ -355,8 +355,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() { @@ -389,11 +389,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); @@ -433,7 +433,7 @@ public void TestAppleAppAttestDev() { var b64 = "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"; var cbor = Convert.FromBase64String(b64); - var json = (CborMap) CborObject.Decode(cbor); + var json = (CborMap)CborObject.Decode(cbor); var AttestationObject = new ParsedAttestationObject ( @@ -458,21 +458,22 @@ public void TestAppleAppAttestProd() var AttestationObject = new ParsedAttestationObject ( - fmt : (string)json["fmt"], - attStmt : (CborMap)json["attStmt"], - authData : AuthenticatorData.Parse((byte[])json["authData"]) + fmt: (string)json["fmt"], + attStmt: (CborMap)json["attStmt"], + authData: AuthenticatorData.Parse((byte[])json["authData"]) ); var clientDataJson = SHA256.HashData(Encoding.UTF8.GetBytes("1234567890abcdefgh")); var verifier = new AppleAppAttest(); - var ex = Assert.Throws(() => { - (AttestationType attType, X509Certificate[] trustPath) = verifier.Verify(AttestationObject.AttStmt, AttestationObject.AuthData, clientDataJson); + var ex = Assert.Throws(() => + { + (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."; const string cryptoKitErrorMessage = "Failed to build chain in Apple AppAttest attestation: An expired certificate was detected."; - const string linuxErrorMessage = "Failed to build chain in Apple AppAttest attestation: certificate has expired"; + const string linuxErrorMessage = "Failed to build chain in Apple AppAttest attestation: certificate has expired"; Assert.True(ex.Message is windowsErrorMessage or cryptoKitErrorMessage or linuxErrorMessage); } @@ -508,7 +509,7 @@ public void MetadataBLOBPayloadEntry_Can_Be_JSON_Roundtripped() input.MetadataStatement.AttestationTypes = new string[] { "bar" }; input.MetadataStatement.AuthenticationAlgorithms = new string[] { "alg0", "alg1" }; input.MetadataStatement.PublicKeyAlgAndEncodings = new string[] { "example0", "example1" }; - input.MetadataStatement.TcDisplay = new string[] { "transaction","confirmation" }; + input.MetadataStatement.TcDisplay = new string[] { "transaction", "confirmation" }; input.MetadataStatement.KeyProtection = new string[] { "protector" }; input.MetadataStatement.MatcherProtection = new string[] { "stuff", "things" }; input.MetadataStatement.UserVerificationDetails = Array.Empty(); @@ -823,8 +824,8 @@ public async Task TestAssertionResponse() Assert.Equal(new byte[] { 0xf1, 0xd0 }, avr.CredentialId); Assert.Equal("1", avr.Counter.ToString("X")); } - } - + } + internal static async Task MakeAssertionResponseAsync( COSE.KeyType kty, COSE.Algorithm alg, @@ -847,16 +848,16 @@ internal static async Task MakeAssertionResponseAsy { 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; @@ -895,7 +896,7 @@ internal static async Task MakeAssertionResponseAsy 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]; @@ -936,12 +937,12 @@ internal static async Task MakeAssertionResponseAsy } 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) diff --git a/Test/MetadataServiceTests.cs b/Test/MetadataServiceTests.cs index e99ac539..521bbe8e 100644 --- a/Test/MetadataServiceTests.cs +++ b/Test/MetadataServiceTests.cs @@ -15,8 +15,8 @@ public async Task ConformanceTestClient() { var client = new ConformanceMetadataRepository(null, "http://localhost:80"); - var cancellationToken = CancellationToken.None; - + var cancellationToken = CancellationToken.None; + var blob = await client.GetBLOBAsync(cancellationToken); Assert.NotEmpty(blob.Entries); diff --git a/Tests/Fido2.Ctap2.Tests/Commands/AuthenticatorGetAssertionCommandTests.cs b/Tests/Fido2.Ctap2.Tests/Commands/AuthenticatorGetAssertionCommandTests.cs index 937502dc..fbcb81c2 100644 --- a/Tests/Fido2.Ctap2.Tests/Commands/AuthenticatorGetAssertionCommandTests.cs +++ b/Tests/Fido2.Ctap2.Tests/Commands/AuthenticatorGetAssertionCommandTests.cs @@ -10,15 +10,15 @@ public void GetPayload() var request = new AuthenticatorGetAssertionCommand( rpId: "example.com", clientDataHash: Convert.FromHexString("687134968222ec17202e42505f8ed2b16ae22f16bb05b88c25db9e602645f141"), - allowList: new [] + allowList: new[] { new PublicKeyCredentialDescriptor(Convert.FromHexString("f22006de4f905af68a43942f024f2a5ece603d9c6d4b3df8be08ed01fc442646d034858ac75bed3fd580bf9808d94fcbee82b9b2ef6677af0adcc35852ea6b9e")), new PublicKeyCredentialDescriptor(Convert.FromHexString("0303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303")) }, options: new AuthenticatorGetAssertionOptions { UserVerification = true } - + ); - + string expect = """ 02 # authenticatorGetAssertion command a4 # map(4) diff --git a/Tests/Fido2.Ctap2.Tests/Commands/AuthenticatorMakeCredentialCommandTests.cs b/Tests/Fido2.Ctap2.Tests/Commands/AuthenticatorMakeCredentialCommandTests.cs index 555d5309..c0bb125e 100644 --- a/Tests/Fido2.Ctap2.Tests/Commands/AuthenticatorMakeCredentialCommandTests.cs +++ b/Tests/Fido2.Ctap2.Tests/Commands/AuthenticatorMakeCredentialCommandTests.cs @@ -10,20 +10,21 @@ public void GetPayload() var request = new AuthenticatorMakeCredentialCommand( clientDataHash: Convert.FromHexString("687134968222ec17202e42505f8ed2b16ae22f16bb05b88c25db9e602645f141"), rpEntity: new PublicKeyCredentialRpEntity(id: "example.com", name: "Acme", null), - user: new PublicKeyCredentialUserEntity { - Id = Convert.FromHexString("3082019330820138a0030201023082019330820138a003020102308201933082"), - Icon = "https://pics.example.com/00/p/aBjjjpqPb.png", - Name = "johnpsmith@example.com", - DisplayName = "John P. Smith" - + user: new PublicKeyCredentialUserEntity + { + Id = Convert.FromHexString("3082019330820138a0030201023082019330820138a003020102308201933082"), + Icon = "https://pics.example.com/00/p/aBjjjpqPb.png", + Name = "johnpsmith@example.com", + DisplayName = "John P. Smith" + }, - pubKeyCredParams: new[] { - new PubKeyCredParam(COSE.Algorithm.ES256), + pubKeyCredParams: new[] { + new PubKeyCredParam(COSE.Algorithm.ES256), new PubKeyCredParam(COSE.Algorithm.RS256) }, - options: new AuthenticatorMakeCredentialOptions { ResidentKey = true } + options: new AuthenticatorMakeCredentialOptions { ResidentKey = true } ); - + string expect = """ 01 # authenticatorMakeCredential command a5 # map(5) diff --git a/Tests/Fido2.Ctap2.Tests/Responses/AuthenticatorGetAssertionResponseTests.cs b/Tests/Fido2.Ctap2.Tests/Responses/AuthenticatorGetAssertionResponseTests.cs index 01d93a0c..214ec0d5 100644 --- a/Tests/Fido2.Ctap2.Tests/Responses/AuthenticatorGetAssertionResponseTests.cs +++ b/Tests/Fido2.Ctap2.Tests/Responses/AuthenticatorGetAssertionResponseTests.cs @@ -72,10 +72,10 @@ public void Deserialize() Assert.Equal(37, response.AuthData.Length); Assert.Equal(71, response.Signature.Length); - Assert.Equal(32, response.User!.Id.Length); + Assert.Equal(32, response.User!.Id.Length); Assert.Equal("https://pics.example.com/00/p/aBjjjpqPb.png", response.User!.Icon); - Assert.Equal("johnpsmith@example.com", response.User!.Name); - Assert.Equal("John P. Smith", response.User!.DisplayName); + Assert.Equal("johnpsmith@example.com", response.User!.Name); + Assert.Equal("John P. Smith", response.User!.DisplayName); Assert.Equal(1, response.NumberOfCredentials!.Value); diff --git a/Tests/Fido2.Ctap2.Tests/Responses/AuthenticatorMakeCredentialResponseTests.cs b/Tests/Fido2.Ctap2.Tests/Responses/AuthenticatorMakeCredentialResponseTests.cs index 36866e5f..3d7d9f4d 100644 --- a/Tests/Fido2.Ctap2.Tests/Responses/AuthenticatorMakeCredentialResponseTests.cs +++ b/Tests/Fido2.Ctap2.Tests/Responses/AuthenticatorMakeCredentialResponseTests.cs @@ -79,8 +79,8 @@ public void Deserialize() var response = AuthenticatorMakeCredentialResponse.FromCborObject(TestHelper.GetResponse(hexEncodedCborData).GetCborObject()); Assert.Equal("packed", response.Fmt); - Assert.Equal(154, response.AuthData.Length); - Assert.Equal(3, response.AttStmt.Count); - Assert.Equal(1, ((CborArray)response.AttStmt["x5c"]!).Length); + Assert.Equal(154, response.AuthData.Length); + Assert.Equal(3, response.AttStmt.Count); + Assert.Equal(1, ((CborArray)response.AttStmt["x5c"]!).Length); } } diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 38c82dda..0192b169 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -81,7 +81,7 @@ jobs: displayName: 'Install .NET 7.0 SDK' inputs: packageType: 'sdk' - version: '7.0.x' + version: '7.0.x' - task: DotNetCoreCLI@2 displayName: 'dotnet restore' inputs: @@ -145,6 +145,12 @@ jobs: inputs: command: restore projects: 'Test/Test.csproj' + - task: DotNetCoreCLI@2 + displayName: 'dotnet format' + inputs: + command: custom + custom: 'format' + arguments: '--verify-no-changes --no-restore' - task: DotNetCoreCLI@2 displayName: 'dotnet build $(buildConfiguration)' inputs: