Esempio n. 1
0
        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
            });
        }
Esempio n. 3
0
        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
                }));
            }
        }
Esempio n. 4
0
        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));
        }
Esempio n. 5
0
        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);
        }
Esempio n. 6
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 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)
                }));
            }
        }
Esempio n. 7
0
        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 }));
            }
        }
Esempio n. 8
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 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)
                }));
            }
        }
Esempio n. 9
0
        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);
        }
Esempio n. 10
0
        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]);
        }
Esempio n. 13
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)
                }));
            }
        }
Esempio n. 14
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 = 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)
                }));
            }
        }
Esempio n. 15
0
        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)
                }));
            }
        }
Esempio n. 16
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 = 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)
                }));
            }
        }
Esempio n. 19
0
        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
                }));
            }
        }
Esempio n. 21
0
        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,
            }));
        }
Esempio n. 22
0
 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]);
        }
Esempio n. 24
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)
                }));
            }
        }
Esempio n. 25
0
        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);
        }
Esempio n. 26
0
        /// <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);
        }
Esempio n. 27
0
        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));
        }
Esempio n. 29
0
        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();
        }
Esempio n. 30
0
 public ValidateDeviceMfaAgainstCurrentUserCommand(
     AuthenticatorAssertionRawResponse authenticatorAssertionRawResponse, AssertionOptions assertionOptions)
 {
     this.AuthenticatorAssertionRawResponse = authenticatorAssertionRawResponse;
     this.AssertionOptions = assertionOptions;
 }