public async Task TestFido2AssertionAsync() { //var existingKey = "45-43-53-31-20-00-00-00-0E-B4-F3-73-C2-AC-7D-F7-7E-7D-17-D3-A3-A2-CC-AB-E5-C6-B1-42-ED-10-AC-7C-15-72-39-8D-75-C6-5B-B9-76-09-33-A0-30-F2-44-51-C8-31-AF-72-9B-4F-7B-AB-4F-85-2D-7D-1F-E0-B5-BD-A3-3D-0E-D6-18-04-CD-98"; //var key2 = "45-43-53-31-20-00-00-00-1D-60-44-D7-92-A0-0C-1E-3B-F9-58-5A-28-43-92-FD-F6-4F-BB-7F-8E-86-33-38-30-A4-30-5D-4E-2C-71-E3-53-3C-7B-98-81-99-FE-A9-DA-D9-24-8E-04-BD-C7-86-40-D3-03-1E-6E-00-81-7D-85-C3-A2-19-C9-21-85-8D"; //var key2 = "45-43-53-31-20-00-00-00-A9-E9-12-2A-37-8A-F0-74-E7-BA-52-54-B0-91-55-46-DB-21-E5-2C-01-B8-FB-69-CD-E5-ED-02-B6-C3-16-E3-1A-59-16-C1-43-87-0D-04-B9-94-7F-CF-56-E5-AA-5E-96-8C-5B-27-8F-83-F4-E2-50-AB-B3-F6-28-A1-F8-9E"; var options = JsonConvert.DeserializeObject <CredentialCreateOptions>(File.ReadAllText("./AttestationNoneOptions.json")); var response = JsonConvert.DeserializeObject <AuthenticatorAttestationRawResponse>(File.ReadAllText("./AttestationNoneResponse.json")); var o = AuthenticatorAttestationResponse.Parse(response); await o.VerifyAsync(options, "https://localhost:44329", (x) => Task.FromResult(true), null, null); var credId = "F1-3C-7F-08-3C-A2-29-E0-B4-03-E8-87-34-6E-FC-7F-98-53-10-3A-30-91-75-67-39-7A-D1-D8-AF-87-04-61-87-EF-95-31-85-60-F3-5A-1A-2A-CF-7D-B0-1D-06-B9-69-F9-AB-F4-EC-F3-07-3E-CF-0F-71-E8-84-E8-41-20"; var allowedCreds = new List <PublicKeyCredentialDescriptor>() { new PublicKeyCredentialDescriptor() { Id = StringToByteArray(credId), Type = "public-key" } }; // assertion var aoptions = Get <AssertionOptions>("./assertionNoneOptions.json"); AuthenticatorAssertionRawResponse aresponse = Get <AuthenticatorAssertionRawResponse>("./assertionNoneResponse.json"); // signed assertion? //var cng = CngKey.Import(StringToByteArray(key2), CngKeyBlobFormat.EccPublicBlob); //var existingPublicKey = new ECDsaCng(cng); //fido2.MakeAssertion(aresponse, aoptions, response.); }
public static WebAuthnAssertion VerifyAssertionResult(Fido2Configuration fido2Config, string requestJSON, string resultJSON, Func <string, byte[]> getStoredPublicKey) { var fido2 = new Fido2(fido2Config); IsUserHandleOwnerOfCredentialIdAsync callback = (args) => { var u = args.UserHandle; var id = args.CredentialId; return(Task.FromResult(true)); }; var request = Newtonsoft.Json.JsonConvert.DeserializeObject <AssertionOptions>(requestJSON); AuthenticatorAssertionRawResponse assertionResponse = Newtonsoft.Json.JsonConvert.DeserializeObject <AuthenticatorAssertionRawResponse>(resultJSON); var credentialId = System.Convert.ToBase64String(assertionResponse.Id); uint storedCounter = 0; // 0 means always success var publicKey = getStoredPublicKey(credentialId); var success = fido2.MakeAssertionAsync(assertionResponse, request, publicKey, storedCounter, callback).Result; var clientDataJson = Newtonsoft.Json.JsonConvert.DeserializeObject <AuthenticatorResponse>(System.Text.UTF8Encoding.UTF8.GetString(assertionResponse.Response.ClientDataJson)); var challenge = clientDataJson.Challenge; return(new WebAuthnAssertion { verified = true, challenge = System.Convert.ToBase64String(clientDataJson.Challenge), credentialId = credentialId, publicKey = System.Convert.ToBase64String(publicKey), userHandle = assertionResponse.Response.UserHandle }); }
public async Task <JsonResult> SignIn([FromBody] AuthenticatorAssertionRawResponse clientResponse) { try { var jsonOptions = HttpContext.Session.GetString("fido2.assertionOptions"); var options = AssertionOptions.FromJson(jsonOptions); var user = await GetUserByCredentials(clientResponse.Id); var credential = JsonConvert.DeserializeObject <StoredCredential>(user.Profile["PasswordlessPublicKey"].ToString()); var result = await fido2.MakeAssertionAsync(clientResponse, options, credential.PublicKey, credential.SignatureCounter, args => Task.FromResult(credential.UserHandle.SequenceEqual(args.UserHandle))); await UpdateCounter(user, credential, result.Counter); var claims = new List <Claim> { new Claim(ClaimTypes.Name, user.Profile.Email) }; var claimsIdentity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme); await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(claimsIdentity)); return(Json(result)); } catch (Exception e) { return(Json(new AssertionVerificationResult { Status = "error", ErrorMessage = e.Message })); } }
public async Task <JsonResult> MakeAssertionTest([FromBody] AuthenticatorAssertionRawResponse clientResponse) { // 1. Get the assertion options we sent the client var jsonOptions = HttpContext.Session.GetString("fido2.assertionOptions"); var options = AssertionOptions.FromJson(jsonOptions); // 2. Get registered credential from database var creds = DemoStorage.GetCredentialById(clientResponse.Id); // 3. Get credential counter from database var storedCounter = creds.SignatureCounter; // 4. Create callback to check if userhandle owns the credentialId IsUserHandleOwnerOfCredentialIdAsync callback = async(args) => { var storedCreds = await DemoStorage.GetCredentialsByUserHandleAsync(args.UserHandle); return(storedCreds.Exists(c => c.Descriptor.Id.SequenceEqual(args.CredentialId))); }; // 5. Make the assertion var res = await _lib.MakeAssertionAsync(clientResponse, options, creds.PublicKey, storedCounter, callback); // 6. Store the updated counter DemoStorage.UpdateCounter(res.CredentialId, res.Counter); var testRes = new { status = "ok", errorMessage = "" }; // 7. return OK to client return(Json(testRes)); }
public async Task <bool> CompleteLogin(string userId, AuthenticatorAssertionRawResponse response) { await using var dbContext = _contextFactory.CreateContext(); var user = await dbContext.Users.Include(applicationUser => applicationUser.Fido2Credentials) .FirstOrDefaultAsync(applicationUser => applicationUser.Id == userId); if (user == null || !LoginStore.TryGetValue(userId, out var options)) { return(false); } var credential = user.Fido2Credentials .Where(fido2Credential => fido2Credential.Type is Fido2Credential.CredentialType.FIDO2) .Select(fido2Credential => (fido2Credential, fido2Credential.GetBlob())) .FirstOrDefault(fido2Credential => fido2Credential.Item2.Descriptor.Id.SequenceEqual(response.Id)); if (credential.Item2 is null) { return(false); } // 5. Make the assertion var res = await _fido2.MakeAssertionAsync(response, options, credential.Item2.PublicKey, credential.Item2.SignatureCounter, x => Task.FromResult(true)); // 6. Store the updated counter credential.Item2.SignatureCounter = res.Counter; credential.fido2Credential.SetBlob(credential.Item2); await dbContext.SaveChangesAsync(); LoginStore.Remove(userId, out _); // 7. return OK to client return(true); }
public async Task <JsonResult> MakeAssertion([FromBody] AuthenticatorAssertionRawResponse clientResponse) { try { // 1. Get the assertion options we sent the client //var jsonOptions = HttpContext.Session.GetString("fido2.assertionOptions"); var jsonOptions = await _distributedCache.GetStringAsync(UniqueId); if (string.IsNullOrEmpty(jsonOptions)) { throw new Exception("Can't get AssertionOptions from cache"); } var options = AssertionOptions.FromJson(jsonOptions); // 2. Get registered credential from database var creds = await _fido2Storage.GetCredentialById(clientResponse.Id); if (creds == null) { throw new Exception("Unknown credentials"); } // 3. Get credential counter from database var storedCounter = creds.SignatureCounter; // 4. Create callback to check if userhandle owns the credentialId IsUserHandleOwnerOfCredentialIdAsync callback = async(args) => { var storedCreds = await _fido2Storage.GetCredentialsByUserHandleAsync(args.UserHandle); return(storedCreds.Exists(c => c.Descriptor.Id.SequenceEqual(args.CredentialId))); }; // 5. Make the assertion var res = await _lib.MakeAssertionAsync(clientResponse, options, creds.PublicKey, storedCounter, callback); // 6. Store the updated counter await _fido2Storage.UpdateCounter(res.CredentialId, res.Counter); // complete sign-in var user = await _signInManager.GetTwoFactorAuthenticationUserAsync(); if (user == null) { throw new InvalidOperationException($"Unable to load two-factor authentication user."); } var result = await _signInManager.TwoFactorSignInAsync("FIDO2", string.Empty, false, false); // 7. return OK to client return(Json(res)); } catch (Exception e) { return(Json(new AssertionVerificationResult { Status = "error", ErrorMessage = FormatException(e) })); } }
public async Task <IActionResult> Fido2LoginCallback([FromBody] AuthenticatorAssertionRawResponse clientResponse, [FromHeader] string returnUrl) { var info = await HttpContext.AuthenticateAsync(_fido2AuthenticationScheme); var tempUser = info?.Principal; if (tempUser == null) { return(Json(new { success = false })); } var user = await _users.FindByIdAsync(tempUser.GetSubjectId()); try { // 1. Get the assertion options we sent the client var jsonOptions = HttpContext.Session.GetString("fido2.assertionOptions"); var options = AssertionOptions.FromJson(jsonOptions); // 2. Get registered credential from database var creds = _authenticationContext.FidoLogins.FirstOrDefault(x => x.PublicKeyIdBytes.SequenceEqual(clientResponse.Id)); //DemoStorage.GetCredentialById(clientResponse.Id); if (creds == null) { throw new Exception("Unknown credentials"); } if (creds.UserId != user.Id) { throw new Exception("User is not owner of credentials."); } // 3. Get credential counter from database var storedCounter = creds.SignatureCounter; // 4. Create callback to check if userhandle owns the credentialId IsUserHandleOwnerOfCredentialIdAsync callback = async(args) => { return(_authenticationContext.FidoLogins.FirstOrDefault(x => x.UserHandle.SequenceEqual(args.UserHandle) && x.PublicKeyIdBytes.SequenceEqual(args.CredentialId)) != null); }; // 5. Make the assertion var res = await _lib.MakeAssertionAsync(clientResponse, options, creds.PublicKey, storedCounter, callback); // 6. Store the updated counter creds.SignatureCounter = res.Counter; _authenticationContext.SaveChanges(); // 7. return OK to client return(Json(new { success = true })); } catch (Exception e) { return(Json(new { success = false })); } }
public async Task <JsonResult> MakeAssertion([FromBody] AuthenticatorAssertionRawResponse clientResponse) { try { // 1. Get the assertion options we sent the client var jsonOptions = HttpContext.Session.GetString("fido2.assertionOptions"); var options = AssertionOptions.FromJson(jsonOptions); // 2. Get registered credential from database var signInUser = await _signInManager.GetTwoFactorAuthenticationUserAsync(); var applicationUser = await _userManager.FindByNameAsync(signInUser.UserName); var creds = _fido2CredentialService.GetCredentialByUser(applicationUser); if (creds == null) { throw new Exception("Unknown credentials"); } // 3. Get credential counter from database var storedCounter = creds.SignatureCounter; // 4. Create callback to check if userhandle owns the credentialId IsUserHandleOwnerOfCredentialIdAsync callback = async(args) => { var storedCreds = await _fido2CredentialService.GetCredentialsByUserHandleAsync(args.UserHandle); return(storedCreds.Exists(c => new PublicKeyCredentialDescriptor(c.Descriptor).Id.SequenceEqual(args.CredentialId))); }; // 5. Make the assertion var res = await _fido2.MakeAssertionAsync(clientResponse, options, creds.PublicKey, storedCounter, callback); // 6. Store the updated counter var code = await _fido2CredentialService.UpdateCounter(res.CredentialId, res.Counter); // TODO Fix counter // TODO Allow more than one key to work // var result = await _signInManager.TwoFactorSignInAsync("Fido2", string.Empty, false, false); await _signInManager.SignInAsync(applicationUser, false); // 7. return OK to client return(Json(res)); } catch (Exception e) { return(Json(new AssertionVerificationResult { Status = "error", ErrorMessage = FormatException(e) })); } }
public async Task TestFido2AssertionWithExistingU2fRegistrationWithAppId() { // u2f registration with appId var appId = "https://localhost:44336"; var keyHandleData = Base64Url.Decode("2uzGTqu9XGoDQpRBhkv3qDYWzEEZrDjOHT94fHe3J9VXl6KpaY6jL1C4gCAVSBCWZejOn-EYSyXfiG7RDQqgKw"); var publicKeyData = Base64Url.Decode("BEKJkJiDzo8wlrYbAHmyz5a5vShbkStO58ZO7F-hy4fvBp6TowCZoV2dNGcxIN1yT18799bb_WuP0Yq_DSv5a-U"); //key as cbor var publicKey = CreatePublicKeyFromU2fRegistrationData(keyHandleData, publicKeyData); var options = new AssertionOptions { Challenge = Base64Url.Decode("mNxQVDWI8+ahBXeQJ8iS4jk5pDUd5KetZGVOwSkw2X0"), RpId = "localhost", AllowCredentials = new[] { new PublicKeyCredentialDescriptor { Id = keyHandleData, Type = PublicKeyCredentialType.PublicKey } }, Extensions = new AuthenticationExtensionsClientInputs { AppID = appId } }; var authResponse = new AuthenticatorAssertionRawResponse { Id = keyHandleData, RawId = keyHandleData, Type = PublicKeyCredentialType.PublicKey, Extensions = new AuthenticationExtensionsClientOutputs { AppID = true }, Response = new AuthenticatorAssertionRawResponse.AssertionResponse { AuthenticatorData = Base64Url.Decode("B6_fPoU4uitIYRHXuNfpbqt5mrDWz8cp7d1noAUrAucBAAABTQ"), ClientDataJson = Base64Url.Decode("eyJjaGFsbGVuZ2UiOiJtTnhRVkRXSTgtYWhCWGVRSjhpUzRqazVwRFVkNUtldFpHVk93U2t3MlgwIiwib3JpZ2luIjoiaHR0cHM6Ly9sb2NhbGhvc3Q6NDQzMzYiLCJ0eXBlIjoid2ViYXV0aG4uZ2V0In0"), Signature = Base64Url.Decode("MEQCICHV36RVY9DdFmKZgxF5Z_yScpPPsKcj__8KcPmngtGHAiAq_SzmTY8rZz4-5uNNiz3h6xO9osNTh7O7Mjqtoxul8w") } }; IFido2 fido2 = new Fido2(new Fido2Configuration { Origins = new System.Collections.Generic.HashSet <string> { "https://localhost:44336" } //data was generated with this origin }); var res = await fido2.MakeAssertionAsync(authResponse, options, publicKey.Encode(), 0, null); Assert.Equal("ok", res.Status); }
public void Constructor_GiveValidArguments_PropertiesAreSet() { var authenticatorAssertionRawResponse = new AuthenticatorAssertionRawResponse(); var assertionOptions = new AssertionOptions(); var command = new ValidateDeviceMfaAgainstCurrentUserCommand(authenticatorAssertionRawResponse, assertionOptions); Assert.Equal(authenticatorAssertionRawResponse, command.AuthenticatorAssertionRawResponse); Assert.Equal(assertionOptions, command.AssertionOptions); }
public async Task <CacheItem> ExecuteAsync(string fido2Payload, CacheItem relatedItem) { var request = OwnIdSerializer.Deserialize <Fido2LoginRequest>(fido2Payload); var storedFido2Info = await _userHandlerAdapter.FindFido2InfoAsync(request.CredentialId); if (storedFido2Info == null) { throw new OwnIdException(ErrorType.UserNotFound); } var options = new AssertionOptions { Challenge = Encoding.ASCII.GetBytes(relatedItem.Context), RpId = _configuration.Fido2.RelyingPartyId }; var fidoResponse = new AuthenticatorAssertionRawResponse { Extensions = new AuthenticationExtensionsClientOutputs(), Id = Base64Url.Decode(request.CredentialId), RawId = Base64Url.Decode(request.CredentialId), Response = new AuthenticatorAssertionRawResponse.AssertionResponse { AuthenticatorData = Base64Url.Decode(request.AuthenticatorData), ClientDataJson = Base64Url.Decode(request.ClientDataJson), Signature = Base64Url.Decode(request.Signature), UserHandle = Base64Url.Decode(storedFido2Info.UserId) }, Type = PublicKeyCredentialType.PublicKey }; var result = await _fido2.MakeAssertionAsync( fidoResponse, options, Base64Url.Decode(storedFido2Info.PublicKey), storedFido2Info.SignatureCounter, args => { var storedCredentialId = Base64Url.Decode(storedFido2Info.CredentialId); var storedCredDescriptor = new PublicKeyCredentialDescriptor(storedCredentialId); var credIdValidationResult = storedCredDescriptor.Id.SequenceEqual(args.CredentialId); return(Task.FromResult(credIdValidationResult)); }); return(await _cacheItemRepository.UpdateAsync(relatedItem.Context, item => { item.PublicKey = storedFido2Info.PublicKey; item.Fido2SignatureCounter = result.Counter; item.Fido2CredentialId = request.CredentialId; item.FinishFlow(storedFido2Info.UserId, storedFido2Info.PublicKey); })); }
public void TestAuthenticatorAssertionRawResponse() { var challenge = RandomGenerator.Default.GenerateBytes(128); var clientDataJson = JsonSerializer.SerializeToUtf8Bytes(new { Type = "webauthn.get", Challenge = challenge, Origin = "https://www.passwordless.dev", }); var assertion = new AuthenticatorAssertionRawResponse.AssertionResponse() { AuthenticatorData = new byte[] { 0xf1, 0xd0 }, Signature = new byte[] { 0xf1, 0xd0 }, ClientDataJson = clientDataJson, UserHandle = new byte[] { 0xf1, 0xd0 }, }; var assertionResponse = new AuthenticatorAssertionRawResponse() { Response = assertion, Type = PublicKeyCredentialType.PublicKey, Id = new byte[] { 0xf1, 0xd0 }, RawId = new byte[] { 0xf1, 0xd0 }, Extensions = new AuthenticationExtensionsClientOutputs() { AppID = true, AuthenticatorSelection = true, Extensions = new string[] { "foo", "bar" }, Example = "test", UserVerificationMethod = new ulong[][] { new ulong[] { 4 // USER_VERIFY_PASSCODE_INTERNAL }, }, } }; Assert.Equal(PublicKeyCredentialType.PublicKey, assertionResponse.Type); Assert.True(assertionResponse.Id.SequenceEqual(new byte[] { 0xf1, 0xd0 })); Assert.True(assertionResponse.RawId.SequenceEqual(new byte[] { 0xf1, 0xd0 })); Assert.True(assertionResponse.Response.AuthenticatorData.SequenceEqual(new byte[] { 0xf1, 0xd0 })); Assert.True(assertionResponse.Response.Signature.SequenceEqual(new byte[] { 0xf1, 0xd0 })); Assert.True(assertionResponse.Response.ClientDataJson.SequenceEqual(clientDataJson)); Assert.True(assertionResponse.Response.UserHandle.SequenceEqual(new byte[] { 0xf1, 0xd0 })); Assert.True(assertionResponse.Extensions.AppID); Assert.True(assertionResponse.Extensions.AuthenticatorSelection); Assert.Equal(assertionResponse.Extensions.Extensions, new string[] { "foo", "bar" }); Assert.Equal("test", assertionResponse.Extensions.Example); Assert.Equal((ulong)4, assertionResponse.Extensions.UserVerificationMethod[0][0]); }
public async Task <JsonResult> MakeAssertion([FromBody] AuthenticatorAssertionRawResponse clientResponse) { try { // 1. Get the assertion options we sent the client var jsonOptions = HttpContext.Session.GetString("fido2.assertionOptions"); var options = AssertionOptions.FromJson(jsonOptions); // 2. Get registered credential from database var creds = await _fido2Storage.GetCredentialById(clientResponse.Id); if (creds == null) { throw new Exception("Unknown credentials"); } // 3. Get credential counter from database var storedCounter = creds.SignatureCounter; // 4. Create callback to check if userhandle owns the credentialId IsUserHandleOwnerOfCredentialIdAsync callback = async(args) => { var storedCreds = await _fido2Storage.GetCredentialsByUserHandleAsync(args.UserHandle); return(storedCreds.Exists(c => c.Descriptor.Id.SequenceEqual(args.CredentialId))); }; // 5. Make the assertion var res = await _lib.MakeAssertionAsync(clientResponse, options, creds.PublicKey, storedCounter, callback); // 6. Store the updated counter await _fido2Storage.UpdateCounter(res.CredentialId, res.Counter); var identityUser = await _userManager.FindByNameAsync(creds.Username); if (identityUser == null) { throw new InvalidOperationException($"Unable to load user."); } await _signInManager.SignInAsync(identityUser, isPersistent : false); // 7. return OK to client return(Json(res)); } catch (Exception e) { return(Json(new AssertionVerificationResult { Status = "error", ErrorMessage = FormatException(e) })); } }
public async Task <JsonResult> MakeAssertion([FromBody] AuthenticatorAssertionRawResponse clientResponse) { try { // 1. Get the assertion options we sent the client var jsonOptions = HttpContext.Session.GetString("fido2.assertionOptions"); var options = AssertionOptions.FromJson(jsonOptions); // 2. Get registered credential from database var creds = DemoStorage.GetCredentialById(clientResponse.Id); if (creds == null) { throw new Exception("Unknown credentials"); } // 3. Get credential counter from database var storedCounter = creds.SignatureCounter; // 4. Create callback to check if userhandle owns the credentialId IsUserHandleOwnerOfCredentialIdAsync callback = async(args) => { var storedCreds = await DemoStorage.GetCredentialsByUserHandleAsync(args.UserHandle); return(storedCreds.Exists(c => c.Descriptor.Id.SequenceEqual(args.CredentialId))); }; // 5. Make the assertion var res = await _lib.MakeAssertionAsync(clientResponse, options, creds.PublicKey, storedCounter, callback); // 6. Store the updated counter DemoStorage.UpdateCounter(res.CredentialId, res.Counter); // Begin customization - Krishna // 7. At this point, the user has been authenticated successfully. // Set auth cookie through standard ASP.NET core's identity methods, using creds.userHandle as the UPN claim. var userHandleStr = Encoding.UTF8.GetString(creds.UserHandle); await SetClaimsPrincipalAsync(userHandleStr); // End customization - Krishna // 8. return OK to client return(Json(res)); } catch (Exception e) { return(Json(new AssertionVerificationResult { Status = "error", ErrorMessage = FormatException(e) })); } }
public async Task <JsonResult> MakeAssertion([FromBody] AuthenticatorAssertionRawResponse authenticatorAssertionRawResponse) { try { var jsonOptions = await _distributedCache.GetStringAsync(UniqueId); if (string.IsNullOrEmpty(jsonOptions)) { throw new Exception("Cant get Credential options from cache."); } var assertionOptions = AssertionOptions.FromJson(jsonOptions); var fidoStoredCredential = await _fido2Service.GetFido2StoredCredentialsByCredentialIdAsync(authenticatorAssertionRawResponse.Id); if (fidoStoredCredential == null) { throw new Exception("Unkown credentials."); } IsUserHandleOwnerOfCredentialIdAsync isUserHandleOwnerOfCredentialIdAsync = async(args) => { var storedCreds = await _fido2Service.GetFido2StoredCredentialsByUserHandleAsync(args.UserHandle); var storedCredExsist = storedCreds.Exists(c => c.Descriptor.Id.SequenceEqual(args.CredentialId)); return(storedCredExsist); }; var response = await _fido2.MakeAssertionAsync(authenticatorAssertionRawResponse, assertionOptions, fidoStoredCredential.PublicKey, fidoStoredCredential.SignatureCounter, isUserHandleOwnerOfCredentialIdAsync); if (response.Status != "ok") { throw new Exception("Failed to assertion."); } await _fido2Service.UpdateFido2StoredCredentialCounter(response.CredentialId, response.Counter); var identityUser = _applicationDbContext.Users.Where(c => c.UserName == fidoStoredCredential.UserName).FirstOrDefault(); if (identityUser == null) { throw new Exception($"Unable to load two factor authentication user."); } await _signInManager.SignInAsync(identityUser, true); return(Json(response)); } catch (Exception exception) { return(Json(new AssertionVerificationResult { Status = "error", ErrorMessage = CommonFunctions.FormatException(exception) })); } }
public async Task <JsonResult> MakeAssertion([FromBody] AuthenticatorAssertionRawResponse clientResponse) { try { // 1. Get the assertion options we sent the client var jsonOptions = HttpContext.Session.GetString("fido2.assertionOptions"); var options = AssertionOptions.FromJson(jsonOptions); // 2. Get registered credential from database var creds = PasswordlessStore.GetCredentialById(clientResponse.Id); if (creds == null) { throw new Exception("Unknown credentials"); } // 3. Get credential counter from database var storedCounter = creds.SignatureCounter; // 4. Create callback to check if userhandle owns the credentialId IsUserHandleOwnerOfCredentialIdAsync callback = async(args) => { var storedCreds = await PasswordlessStore.GetCredentialsByUserHandleAsync(args.UserHandle); return(storedCreds.Exists(c => c.Descriptor.Id.SequenceEqual(args.CredentialId))); }; // 5. Make the assertion var res = await _fido2.MakeAssertionAsync(clientResponse, options, creds.PublicKey, storedCounter, callback); // 6. Store the updated counter PasswordlessStore.UpdateCounter(res.CredentialId, res.Counter); if (res.Status == "ok") { var username = System.Text.Encoding.UTF8.GetString(creds.UserId); await SignInOidc(username); } // 7. return OK to client return(Json(res)); } catch (Exception e) { return(Json(new AssertionVerificationResult { Status = "error", ErrorMessage = FormatException(e) })); } }
public static WebAuthnAssertion GetAssertionIdInfo(string resultJSON) { AuthenticatorAssertionRawResponse assertionResponse = Newtonsoft.Json.JsonConvert.DeserializeObject <AuthenticatorAssertionRawResponse>(resultJSON); var clientData = Newtonsoft.Json.JsonConvert.DeserializeObject <AuthenticatorResponse>(System.Text.UTF8Encoding.UTF8.GetString(assertionResponse.Response.ClientDataJson)); var credentialId = assertionResponse.Id; var userHandle = assertionResponse.Response.UserHandle; var challenge = clientData.Challenge; return(new WebAuthnAssertion { verified = false, credentialId = System.Convert.ToBase64String(credentialId), challenge = System.Convert.ToBase64String(challenge), userHandle = userHandle }); }
public async Task <IActionResult> MakeAssertion(AuthenticatorAssertionRawResponse clientResponse) { try { // 1. Get the assertion options we sent the client var jsonOptions = HttpContext.Session.GetString("fido2.assertionOptions"); var options = AssertionOptions.FromJson(jsonOptions); // 2. Get registered credential from database var credentialIdString = Base64Url.Encode(clientResponse.Id); var cred = await context.StoredCredentials.Where(x => x.DescriptorJson.Contains(credentialIdString)).FirstOrDefaultAsync(); if (cred == null) { throw new Exception("Unknown credentials"); } // 3. Get credential counter from database var storedCounter = cred.SignatureCounter; // 4. Create callback to check if userhandle owns the credentialId IsUserHandleOwnerOfCredentialIdAsync callback = async(args) => { var storedCreds = await context.StoredCredentials.Where(x => x.UserHandle == args.UserHandle).ToListAsync(); return(storedCreds.Exists(c => c.Descriptor.Id.SequenceEqual(args.CredentialId))); }; // 5. Make the assertion var res = await _fido2.MakeAssertionAsync(clientResponse, options, cred.PublicKey, storedCounter, callback); // 6. Store the updated counter cred.SignatureCounter = res.Counter; await context.SaveChangesAsync(); // 7. return OK to client return(Ok(res)); } catch (Exception e) { return(BadRequest(new AssertionVerificationResult { Status = "error", ErrorMessage = FormatException(e) })); } }
public async Task <JsonResult> MakeAssertion([FromBody] AuthenticatorAssertionRawResponse clientResponse) { // 1. Get the assertion options we sent the client var jsonOptions = this.TempData["fido2.assertionOptions"] as string; var options = AssertionOptions.FromJson(jsonOptions); var result = await this._mediator.Send(new ValidateAuthenticatorDeviceCommand(clientResponse, options)); if (result.IsSuccess) { await this._authenticationService.SignInFromPartial(); return(this.Json(result.Value.AssertionVerificationResult)); } return(this.Json(new AssertionVerificationResult { Status = "error" })); }
public async Task <JsonResult> MakeAssertion([FromBody] AuthenticatorAssertionRawResponse clientResponse, [FromQuery] AssertionOptions options) { try { // 2. Get registered credential from database var creds = await _auth.GetCredentialById(clientResponse.Id); if (creds == null) { throw new Exception("Unknown credentials"); } // 3. Get credential counter from database var storedCounter = creds.SignatureCounter; // 4. Create callback to check if userhandle owns the credentialId IsUserHandleOwnerOfCredentialIdAsync callback = async(args) => { var storedCreds = await _auth.GetCredentialsForUser(BitConverter.ToInt32(args.UserHandle)); return(storedCreds.Exists(c => c.Id.SequenceEqual(args.CredentialId))); }; // 5. Make the assertion var res = await _fido2.MakeAssertionAsync(clientResponse, options, creds.PublicKey, storedCounter, callback); // 6. Store the updated counter await _auth.UpdateCredential(BitConverter.ToInt32(res.CredentialId), res.Counter); // 7. return OK to client return(Json(res)); } catch (Exception e) { return(Json(new AssertionVerificationResult { Status = "error", ErrorMessage = e.Message })); } }
public async Task <JsonResult> MakeAssertion([FromBody] AuthenticatorAssertionRawResponse clientResponse) { var jsonOptions = this.TempData["fido2.assertionOptions"] as string; var options = AssertionOptions.FromJson(jsonOptions); var result = await this._mediator.Send(new ValidateDeviceMfaAgainstCurrentUserCommand(clientResponse, options)); if (!result.IsSuccess) { return(this.Json(new AssertionVerificationResult { Status = "error" })); } var url = await this._authenticationService.SignInUserFromPartialStateAsync(result.Value.UserId); return(this.Json(new { url = string.IsNullOrWhiteSpace(url) ? this.Url.Page(PageLocations.AppDashboard) : this.LocalRedirect(url).Url, assertionVerificationResult = result.Value.AssertionVerificationResult, })); }
public ValidateAuthenticatorDeviceCommand(AuthenticatorAssertionRawResponse authenticatorAssertionRawResponse, AssertionOptions assertionOptions) { this.AuthenticatorAssertionRawResponse = authenticatorAssertionRawResponse; this.AssertionOptions = assertionOptions; }
public void TestAuthenticatorAssertionRawResponse() { var challenge = RandomGenerator.Default.GenerateBytes(128); var clientDataJson = Encoding.UTF8.GetBytes( JsonConvert.SerializeObject ( new { Type = "webauthn.get", Challenge = challenge, Origin = "https://www.passwordless.dev", } ) ); var assertion = new AuthenticatorAssertionRawResponse.AssertionResponse() { AuthenticatorData = new byte[] { 0xf1, 0xd0 }, Signature = new byte[] { 0xf1, 0xd0 }, ClientDataJson = clientDataJson, UserHandle = new byte[] { 0xf1, 0xd0 }, }; var assertionResponse = new AuthenticatorAssertionRawResponse() { Response = assertion, Type = PublicKeyCredentialType.PublicKey, Id = new byte[] { 0xf1, 0xd0 }, RawId = new byte[] { 0xf1, 0xd0 }, Extensions = new AuthenticationExtensionsClientOutputs() { AppID = true, AuthenticatorSelection = true, BiometricAuthenticatorPerformanceBounds = true, GenericTransactionAuthorization = new byte[] { 0xf1, 0xd0 }, SimpleTransactionAuthorization = "test", Extensions = new string[] { "foo", "bar" }, Example = "test", Location = new GeoCoordinatePortable.GeoCoordinate(42.523714, -71.040860), UserVerificationIndex = new byte[] { 0xf1, 0xd0 }, UserVerificationMethod = new ulong[][] { new ulong[] { 4 // USER_VERIFY_PASSCODE_INTERNAL }, }, } }; Assert.Equal(PublicKeyCredentialType.PublicKey, assertionResponse.Type); Assert.True(assertionResponse.Id.SequenceEqual(new byte[] { 0xf1, 0xd0 })); Assert.True(assertionResponse.RawId.SequenceEqual(new byte[] { 0xf1, 0xd0 })); Assert.True(assertionResponse.Response.AuthenticatorData.SequenceEqual(new byte[] { 0xf1, 0xd0 })); Assert.True(assertionResponse.Response.Signature.SequenceEqual(new byte[] { 0xf1, 0xd0 })); Assert.True(assertionResponse.Response.ClientDataJson.SequenceEqual(clientDataJson)); Assert.True(assertionResponse.Response.UserHandle.SequenceEqual(new byte[] { 0xf1, 0xd0 })); Assert.True(assertionResponse.Extensions.AppID); Assert.True(assertionResponse.Extensions.AuthenticatorSelection); Assert.True(assertionResponse.Extensions.BiometricAuthenticatorPerformanceBounds); Assert.True(assertionResponse.Extensions.GenericTransactionAuthorization.SequenceEqual(new byte[] { 0xf1, 0xd0 })); Assert.Equal("test", assertionResponse.Extensions.SimpleTransactionAuthorization); Assert.Equal(assertionResponse.Extensions.Extensions, new string[] { "foo", "bar" }); Assert.Equal("test", assertionResponse.Extensions.Example); Assert.Equal(42.523714, assertionResponse.Extensions.Location.Latitude); Assert.Equal(-71.040860, assertionResponse.Extensions.Location.Longitude); Assert.True(assertionResponse.Extensions.UserVerificationIndex.SequenceEqual(new byte[] { 0xf1, 0xd0 })); Assert.Equal((ulong)4, assertionResponse.Extensions.UserVerificationMethod[0][0]); }
public async Task <IActionResult> MakeAssertion([FromBody] AuthenticatorAssertionRawResponse clientResponse) { var o = new { challenge = string.Empty, origin = string.Empty, type = string.Empty }; var username = string.Empty; try { o = JsonConvert.DeserializeAnonymousType((Encoding.UTF8.GetString(clientResponse.Response.ClientDataJson)), o); var jsonOptions = _memoryCache.Get <string>(o.challenge); var parsedObject = JObject.Parse(jsonOptions); username = parsedObject["User"]?["Name"]?.ToString(); var options = Fido2NetLib.AssertionOptions.FromJson(jsonOptions); var credentials = _dataStore.GetCredentialById(clientResponse.Id); if (credentials == null) { throw new Exception("Unknown credentials"); } var storedCounter = credentials.SignatureCounter; async Task <bool> Callback(IsUserHandleOwnerOfCredentialIdParams args) { var storedCredentials = await _dataStore.GetCredentialsByUserHandleAsync(args.UserHandle); return(storedCredentials.Exists(c => c.Descriptor.Id.SequenceEqual(args.CredentialId))); } var res = await _lib.MakeAssertionAsync(clientResponse, options, credentials.PublicKey, storedCounter, Callback); _dataStore.UpdateCounter(res.CredentialId, res.Counter); var response = new AuthenticationResult { ErrorMessage = res.ErrorMessage, Status = res.Status }; var ev = new Event(username, "Successful assertion made", nameof(AuthenticationController), nameof(MakeAssertion)); await _elasticClient.IndexAsync(ev, i => i.Index(GetIndexName(nameof(Ok)))); return(Ok(response)); } catch (Exception e) { var errorEvent = new ErrorEvent(e, username, nameof(AuthenticationController), nameof(MakeAssertion)); await _elasticClient.IndexAsync(errorEvent, i => i.Index(GetIndexName(nameof(Exception)))); return(Ok(new AuthenticationResult { Status = "error", ErrorMessage = FormatException(e) })); } }
internal async void MakeAssertionResponse(COSE.KeyType kty, COSE.Algorithm alg, COSE.EllipticCurve crv = COSE.EllipticCurve.P256) { const string rp = "fido2.azurewebsites.net"; byte[] rpId = Encoding.UTF8.GetBytes(rp); var rpIdHash = SHA256.Create().ComputeHash(rpId); var flags = AuthenticatorFlags.AT | AuthenticatorFlags.ED | AuthenticatorFlags.UP | AuthenticatorFlags.UV; const ushort signCount = 0xf1d0; var aaguid = new Guid("F1D0F1D0-F1D0-F1D0-F1D0-F1D0F1D0F1D0"); var credentialID = new byte[] { 0xf1, 0xd0, 0xf1, 0xd0, 0xf1, 0xd0, 0xf1, 0xd0, 0xf1, 0xd0, 0xf1, 0xd0, 0xf1, 0xd0, 0xf1, 0xd0, }; CredentialPublicKey cpk = null; ECDsa ecdsa = null; RSA rsa = null; byte[] expandedPrivateKey = null; switch (kty) { case COSE.KeyType.EC2: { 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(); var rsaparams = rsa.ExportParameters(true); cpk = MakeCredentialPublicKey(kty, alg, rsaparams.Modulus, rsaparams.Exponent); break; } case COSE.KeyType.OKP: { MakeEdDSA(out var privateKeySeed, out var publicKey, out expandedPrivateKey); cpk = MakeCredentialPublicKey(kty, alg, COSE.EllipticCurve.Ed25519, publicKey); break; } throw new ArgumentOutOfRangeException(nameof(kty), $"Missing or unknown kty {kty}"); } var acd = new AttestedCredentialData(aaguid, credentialID, cpk); var extBytes = CBORObject.NewMap().Add("testing", true).EncodeToBytes(); var exts = new Extensions(extBytes); var ad = new AuthenticatorData(rpIdHash, flags, signCount, acd, exts); var authData = ad.ToByteArray(); var challenge = new byte[128]; var rng = RandomNumberGenerator.Create(); rng.GetBytes(challenge); var clientData = new { Type = "webauthn.get", Challenge = challenge, Origin = rp, }; var clientDataJson = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(clientData)); var sha = SHA256.Create(); var hashedClientDataJson = sha.ComputeHash(clientDataJson); byte[] data = new byte[authData.Length + hashedClientDataJson.Length]; Buffer.BlockCopy(authData, 0, data, 0, authData.Length); Buffer.BlockCopy(hashedClientDataJson, 0, data, authData.Length, hashedClientDataJson.Length); byte[] signature = null; switch (kty) { case COSE.KeyType.EC2: { signature = ecdsa.SignData(data, CryptoUtils.algMap[(int)alg]); break; } case COSE.KeyType.RSA: { RSASignaturePadding padding; switch (alg) // https://www.iana.org/assignments/cose/cose.xhtml#algorithms { case COSE.Algorithm.PS256: case COSE.Algorithm.PS384: case COSE.Algorithm.PS512: padding = RSASignaturePadding.Pss; break; case COSE.Algorithm.RS1: case COSE.Algorithm.RS256: case COSE.Algorithm.RS384: case COSE.Algorithm.RS512: padding = RSASignaturePadding.Pkcs1; break; default: throw new ArgumentOutOfRangeException(nameof(alg), $"Missing or unknown alg {alg}"); } signature = rsa.SignData(data, CryptoUtils.algMap[(int)alg], padding); break; } case COSE.KeyType.OKP: { signature = Ed25519.Sign(data, expandedPrivateKey); break; } default: throw new ArgumentOutOfRangeException(nameof(kty), $"Missing or unknown kty {kty}"); } if (kty == COSE.KeyType.EC2) { signature = EcDsaSigFromSig(signature, ecdsa.KeySize); } var userHandle = new byte[16]; rng.GetBytes(userHandle); var assertion = new AuthenticatorAssertionRawResponse.AssertionResponse() { AuthenticatorData = authData, Signature = signature, ClientDataJson = clientDataJson, UserHandle = userHandle, }; var lib = new Fido2(new Fido2Configuration() { ServerDomain = rp, ServerName = rp, Origin = rp, }); var existingCredentials = new List <PublicKeyCredentialDescriptor>(); var cred = new PublicKeyCredentialDescriptor { Type = PublicKeyCredentialType.PublicKey, Id = new byte[] { 0xf1, 0xd0 } }; existingCredentials.Add(cred); var options = lib.GetAssertionOptions(existingCredentials, null, null); options.Challenge = challenge; var response = new AuthenticatorAssertionRawResponse() { Response = assertion, Type = PublicKeyCredentialType.PublicKey, Id = new byte[] { 0xf1, 0xd0 }, RawId = new byte[] { 0xf1, 0xd0 }, }; IsUserHandleOwnerOfCredentialIdAsync callback = (args) => { return(Task.FromResult(true)); }; var res = await lib.MakeAssertionAsync(response, options, cpk.GetBytes(), signCount - 1, callback); }
/// <summary> /// 断言:验证断言响应 /// <para>当客户端返回响应时,我们对其进行验证并接受登录。</para> /// </summary> public async Task <AssertionVerificationResult> MakeAssertionAsync(User user, AuthenticatorAssertionRawResponse clientRespons) { if (user == null) { return(new AssertionVerificationResult() { Status = "bad" }); } // 1. Get the assertion options we sent the client var jsonOptions = distributedCache.GetString(user.UserId.ToString() + "assertionOptions"); if (string.IsNullOrEmpty(jsonOptions)) { return(new AssertionVerificationResult() { Status = "fail", ErrorMessage = "time out" }); } var options = AssertionOptions.FromJson(jsonOptions); // 2. Get registered credential from database var storedCredential = this.GetFIDO2ItemByUserId(user.UserId); // 3. Get credential counter from database var creds = user.FIDO2Items.Where(b => b.CredentialId.SequenceEqual(clientRespons.Id)).FirstOrDefault(); var storedCounter = creds.SignatureCounter; // 4. Create callback to check if userhandle owns the credentialId IsUserHandleOwnerOfCredentialIdAsync callback = async(args) => { return(storedCredential.Exists(c => c.CredentialId.SequenceEqual(args.CredentialId))); }; // 5. Make the assertion var res = await _fido2.MakeAssertionAsync(clientRespons, options, creds.PublicKey, storedCounter, callback); // 6. Store the updated counter creds.SignatureCounter = res.Counter; dataContext.SaveChanges(); return(res); }
public async Task <IActionResult> AssertDigitalSignatureResult([FromBody] AuthenticatorAssertionRawResponse clientResponse) { var sub = HttpContext.User.Claims.FirstOrDefault(x => x.Type == "sub")?.Value; if (string.IsNullOrEmpty(sub)) { return(Json(new { success = false })); } var user = await _users.FindByIdAsync(sub); if (user == null) { return(Json(new { success = false })); } try { // 1. Get the assertion options we sent the client var jsonOptions = HttpContext.Session.GetString("fido2.assertionOptions"); var options = AssertionOptions.FromJson(jsonOptions); // 2. Get registered credential from database var creds = _authContext.FidoLogins.FirstOrDefault(x => x.PublicKeyIdBytes.SequenceEqual(clientResponse.Id) && x.UserId == user.Id); //DemoStorage.GetCredentialById(clientResponse.Id); if (creds == null) { return(Json(new { success = false })); } // 3. Get credential counter from database var storedCounter = creds.SignatureCounter; // 4. Create callback to check if userhandle owns the credentialId IsUserHandleOwnerOfCredentialIdAsync callback = async(args) => { return(_authContext.FidoLogins.FirstOrDefault(x => x.UserHandle.SequenceEqual(args.UserHandle) && x.PublicKeyIdBytes.SequenceEqual(args.CredentialId)) != null); }; // 5. Make the assertion var res = await _lib.MakeAssertionAsync(clientResponse, options, creds.PublicKey, storedCounter, callback); if (!string.IsNullOrEmpty(res.ErrorMessage)) { return(Json(new { success = false, error = res.ErrorMessage })); } var paymentId = HttpContext.Session.GetString("fido2.paymentId"); var payment = _authContext.Payments.First(x => x.Id == paymentId); var signature = Fido2NetLib.Base64Url.Encode(clientResponse.Response.Signature); var paymentAuthorization = new PaymentAuthorization() { Payment = payment, PublicKeyId = creds.PublicKeyId, Signature = signature, Type = (int)DeviceType.FIDO2, AuthorizationDateTime = DateTime.Now, ClientData = Fido2NetLib.Base64Url.Encode(clientResponse.Response.ClientDataJson), AuthenticatorData = Fido2NetLib.Base64Url.Encode(clientResponse.Response.AuthenticatorData) }; _authContext.PaymentAuthorizations.Add(paymentAuthorization); payment.Status = "authorized"; // 6. Store the updated counter creds.SignatureCounter = res.Counter; _authContext.SaveChanges(); // 7. return OK to client return(Json(new { signature, success = true })); } catch (Exception e) { return(Json(new { success = false })); } }
internal static async Task <AssertionVerificationResult> MakeAssertionResponse(COSE.KeyType kty, COSE.Algorithm alg, COSE.EllipticCurve crv = COSE.EllipticCurve.P256, CredentialPublicKey cpk = null, ushort signCount = 0, ECDsa ecdsa = null, RSA rsa = null, byte[] expandedPrivateKey = null) { const string rp = "https://www.passwordless.dev"; byte[] rpId = Encoding.UTF8.GetBytes(rp); var rpIdHash = SHA256.Create().ComputeHash(rpId); var flags = AuthenticatorFlags.AT | AuthenticatorFlags.ED | AuthenticatorFlags.UP | AuthenticatorFlags.UV; var aaguid = new Guid("F1D0F1D0-F1D0-F1D0-F1D0-F1D0F1D0F1D0"); var credentialID = new byte[] { 0xf1, 0xd0, 0xf1, 0xd0, 0xf1, 0xd0, 0xf1, 0xd0, 0xf1, 0xd0, 0xf1, 0xd0, 0xf1, 0xd0, 0xf1, 0xd0, }; if (cpk == null) { switch (kty) { case COSE.KeyType.EC2: { if (ecdsa == null) { 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: { if (rsa == null) { rsa = RSA.Create(); } var rsaparams = rsa.ExportParameters(true); cpk = MakeCredentialPublicKey(kty, alg, rsaparams.Modulus, rsaparams.Exponent); break; } case COSE.KeyType.OKP: { byte[] publicKey = null; if (expandedPrivateKey == null) { MakeEdDSA(out var privateKeySeed, out publicKey, out expandedPrivateKey); } cpk = MakeCredentialPublicKey(kty, alg, COSE.EllipticCurve.Ed25519, publicKey); break; } throw new ArgumentOutOfRangeException(nameof(kty), $"Missing or unknown kty {kty}"); } } var acd = new AttestedCredentialData(aaguid, credentialID, cpk); var extBytes = CBORObject.NewMap().Add("testing", true).EncodeToBytes(); var exts = new Extensions(extBytes); var ad = new AuthenticatorData(rpIdHash, flags, (uint)(signCount + 1), acd, exts); var authData = ad.ToByteArray(); var challenge = new byte[128]; var rng = RandomNumberGenerator.Create(); rng.GetBytes(challenge); var clientData = new { Type = "webauthn.get", Challenge = challenge, Origin = rp, }; var clientDataJson = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(clientData)); var sha = SHA256.Create(); var hashedClientDataJson = sha.ComputeHash(clientDataJson); byte[] data = new byte[authData.Length + hashedClientDataJson.Length]; Buffer.BlockCopy(authData, 0, data, 0, authData.Length); Buffer.BlockCopy(hashedClientDataJson, 0, data, authData.Length, hashedClientDataJson.Length); byte[] signature = SignData(kty, alg, data, ecdsa, rsa, expandedPrivateKey); var userHandle = new byte[16]; rng.GetBytes(userHandle); var assertion = new AuthenticatorAssertionRawResponse.AssertionResponse() { AuthenticatorData = authData, Signature = signature, ClientDataJson = clientDataJson, UserHandle = userHandle, }; var lib = new Fido2(new Fido2Configuration() { ServerDomain = rp, ServerName = rp, Origin = rp, }); var existingCredentials = new List <PublicKeyCredentialDescriptor>(); var cred = new PublicKeyCredentialDescriptor { Type = PublicKeyCredentialType.PublicKey, Id = new byte[] { 0xf1, 0xd0 } }; existingCredentials.Add(cred); var options = lib.GetAssertionOptions(existingCredentials, null, null); options.Challenge = challenge; var response = new AuthenticatorAssertionRawResponse() { Response = assertion, Type = PublicKeyCredentialType.PublicKey, Id = new byte[] { 0xf1, 0xd0 }, RawId = new byte[] { 0xf1, 0xd0 }, }; IsUserHandleOwnerOfCredentialIdAsync callback = (args) => { return(Task.FromResult(true)); }; return(await lib.MakeAssertionAsync(response, options, cpk.GetBytes(), signCount, callback)); }
static void Main(string[] args) { Console.WriteLine($"webauthn.dll API version: {WebAuthnApi.GetApiVersionNumber()}"); WebAuthnApi.IsUserVerifyingPlatformAuthenticatorAvailable(out var vpa); Console.WriteLine($"Verifying Platform Authenticator available: {vpa}"); var config = new Fido2.Configuration { ChallengeSize = 32, Origin = "https://foo.net/bar", ServerDomain = "foo.net", ServerName = "Foo Server" }; var user1 = new User { Name = "Testuser1", Id = new byte[32] }; new Random().NextBytes(user1.Id); var fido2NetLib = new Fido2(config); var excl = new List <F2.PublicKeyCredentialDescriptor>(); var selection = new AuthenticatorSelection { AuthenticatorAttachment = F2.AuthenticatorAttachment.CrossPlatform, RequireResidentKey = false, UserVerification = F2.UserVerificationRequirement.Discouraged }; //generate valid Fido2 request with Fido2Net var f2req = fido2NetLib.RequestNewCredential(user1, excl, selection, F2.AttestationConveyancePreference.Direct); //translate objects into our own types var rp = new RelayingPartyInfo { Id = f2req.Rp.Id, Name = f2req.Rp.Name }; var user = new UserInfo { UserId = f2req.User.Id, Name = f2req.User.Name, DisplayName = f2req.User.DisplayName }; var json = $"{{\r\n\t\"type\" : \"webauthn.create\",\r\n\t\"challenge\" : \"{Convert.ToBase64String(f2req.Challenge)}\",\r\n\t\"origin\" : \"{config.Origin}\"\r\n}}"; var clientData = new ClientData { ClientDataJSON = Encoding.UTF8.GetBytes(json), HashAlgorithm = HashAlgorithm.Sha256 }; var coseParams = f2req.PubKeyCredParams.Select(p => new CoseCredentialParameter((CoseAlgorithm)p.Alg)).ToList(); var makeOptions = new AuthenticatorMakeCredentialOptions { TimeoutMilliseconds = (int)f2req.Timeout, UserVerificationRequirement = (UserVerificationRequirement)f2req.AuthenticatorSelection.UserVerification + 1, AuthenticatorAttachment = ((AuthenticatorAttachment?)f2req.AuthenticatorSelection.AuthenticatorAttachment + 1) ?? AuthenticatorAttachment.Any, RequireResidentKey = f2req.AuthenticatorSelection.RequireResidentKey, AttestationConveyancePreference = (AttestationConveyancePreference)f2req.Attestation + 1, ExcludeCredentialsEx = f2req.ExcludeCredentials .Select(ec => new CredentialEx(ec.Id, ec.Transports.Aggregate(CtapTransport.NoRestrictions, (acc, n) => acc | (CtapTransport)(1 << (int)n)))).ToList() }; //call WinAPI var windowHandle = Process.GetCurrentProcess().MainWindowHandle; var res = WebAuthnApi.AuthenticatorMakeCredential(windowHandle, rp, user, coseParams, clientData, makeOptions, out var credential); if (res != WebAuthnResult.Ok) { Console.WriteLine(WebAuthnApi.GetErrorName(res)); return; } //translate back and verify var resp = new AuthenticatorAttestationRawResponse { Id = credential.CredentialId, RawId = credential.CredentialId, Response = new AuthenticatorAttestationRawResponse.ResponseData { AttestationObject = credential.AttestationObject, ClientDataJson = Encoding.UTF8.GetBytes(json) }, Type = F2.PublicKeyCredentialType.PublicKey }; var makeRes = fido2NetLib.MakeNewCredentialAsync(resp, f2req, _ => Task.FromResult(true)).Result; Console.WriteLine($"Register Result: {makeRes.Result.Status}"); Console.WriteLine($"Attestation CN: {credential.CommonAttestation.Certificates.FirstOrDefault()?.Subject}"); for (; ;) { //--------------------- Assertion ---------------- var allowed = new List <F2.PublicKeyCredentialDescriptor> { new F2.PublicKeyCredentialDescriptor(credential.CredentialId) }; //generate valid Fido2 request with Fido2Net var f2reqA = fido2NetLib.GetAssertionOptions(allowed, F2.UserVerificationRequirement.Discouraged); //translate objects into our own types var jsonA = $"{{\r\n\t\"type\" : \"webauthn.get\",\r\n\t\"challenge\" : \"{Convert.ToBase64String(f2reqA.Challenge)}\",\r\n\t\"origin\" : \"{config.Origin}\"\r\n}}"; var clientDataA = new ClientData { ClientDataJSON = Encoding.UTF8.GetBytes(jsonA), HashAlgorithm = HashAlgorithm.Sha256 }; WebAuthnApi.GetCancellationId(out var cancelId); var getOptions = new AuthenticatorGetAssertionOptions() { CancellationId = cancelId, TimeoutMilliseconds = (int)f2reqA.Timeout, UserVerificationRequirement = (UserVerificationRequirement)f2reqA.UserVerification + 1, AllowedCredentialsEx = f2reqA.AllowCredentials .Select(ec => new CredentialEx(ec.Id, ec.Transports?.Aggregate(CtapTransport.NoRestrictions, (acc, n) => acc | (CtapTransport)(1 << (int)n)) ?? CtapTransport.NoRestrictions)).ToList() }; //call WinAPI Console.WriteLine("Canceling Assertion in 5sec..."); Task.Run(async() => { await Task.Delay(5000); WebAuthnApi.CancelCurrentOperation(cancelId); }); res = WebAuthnApi.AuthenticatorGetAssertion(windowHandle, config.ServerDomain, clientDataA, getOptions, out var assertion); if (res != WebAuthnResult.Ok) { if (res == WebAuthnResult.Canceled) { Console.WriteLine("Canceled"); Console.ReadKey(); continue; } Console.WriteLine($"0x{(int)res:X} " + WebAuthnApi.GetErrorName(res)); break; } //translate back and verify var respA = new AuthenticatorAssertionRawResponse() { Id = assertion.Credential.CredentialId, RawId = assertion.Credential.CredentialId, Type = F2.PublicKeyCredentialType.PublicKey, Response = new AuthenticatorAssertionRawResponse.AssertionResponse() { AuthenticatorData = assertion.AuthenticatorData, Signature = assertion.Signature, UserHandle = assertion.UserId, ClientDataJson = Encoding.UTF8.GetBytes(jsonA) }, }; var getResult = fido2NetLib.MakeAssertionAsync(respA, f2reqA, makeRes.Result.PublicKey, makeRes.Result.Counter, _ => Task.FromResult(true)).Result; Console.WriteLine($"Assertion Result: {getResult.Status}"); Console.WriteLine("Press any key to test assertion again"); Console.ReadKey(); } Console.ReadKey(); }
public ValidateDeviceMfaAgainstCurrentUserCommand( AuthenticatorAssertionRawResponse authenticatorAssertionRawResponse, AssertionOptions assertionOptions) { this.AuthenticatorAssertionRawResponse = authenticatorAssertionRawResponse; this.AssertionOptions = assertionOptions; }