コード例 #1
0
        public static WebAuthnAssertion VerifyAttestationResult(Fido2Configuration fido2Config, string requestJSON, string resultJSON)
        {
            var fido2 = new Fido2(fido2Config);
            IsCredentialIdUniqueToUserAsyncDelegate callback = (IsCredentialIdUniqueToUserParams args) =>
            {
                var id = args.CredentialId; // generated ID by authenticator(should be always unique for each authenticator, equivalent to client cert)
                var u  = args.User;         // user info, kind of useless as this can be changed by js at client side
                return(Task.FromResult(true));
            };
            var request = Newtonsoft.Json.JsonConvert.DeserializeObject <CredentialCreateOptions>(requestJSON);
            AuthenticatorAttestationRawResponse regResponse = Newtonsoft.Json.JsonConvert.DeserializeObject <AuthenticatorAttestationRawResponse>(resultJSON);
            var success        = fido2.MakeNewCredentialAsync(regResponse, request, callback).Result;
            var clientDataJson = Newtonsoft.Json.JsonConvert.DeserializeObject <AuthenticatorResponse>(System.Text.UTF8Encoding.UTF8.GetString(regResponse.Response.ClientDataJson));
            var challenge      = System.Convert.ToBase64String(clientDataJson.Challenge);
            var credentialId   = System.Convert.ToBase64String(success.Result.CredentialId);
            var publicKey      = System.Convert.ToBase64String(success.Result.PublicKey);
            var signingCounter = success.Result.Counter; // designed for replay attact prevention but useless for a multiple node situation
            var user           = success.Result.User;
            var aaguid         = success.Result.Aaguid;

            return(new WebAuthnAssertion
            {
                verified = true,
                challenge = challenge,
                credentialId = credentialId,
                publicKey = publicKey,
                signingCounter = signingCounter,
                aaguid = aaguid,
                userHandle = request.User.Id
            });
        }
コード例 #2
0
 public EnrollAuthenticatorDeviceCommand(string name, AuthenticatorAttestationRawResponse authenticatorAttestationRawResponse,
                                         CredentialCreateOptions credentialCreateOptions)
 {
     this.AuthenticatorAttestationRawResponse = authenticatorAttestationRawResponse;
     this.CredentialCreateOptions             = credentialCreateOptions;
     this.Name = name;
 }
コード例 #3
0
        public async Task <JsonResult> MakeCredentialResultTest([FromBody] AuthenticatorAttestationRawResponse attestationResponse)
        {
            // 1. get the options we sent the client
            var jsonOptions = HttpContext.Session.GetString("fido2.attestationOptions");
            var options     = CredentialCreateOptions.FromJson(jsonOptions);

            // 2. Create callback so that lib can verify credential id is unique to this user
            IsCredentialIdUniqueToUserAsyncDelegate callback = async(IsCredentialIdUniqueToUserParams args) =>
            {
                var users = await DemoStorage.GetUsersByCredentialIdAsync(args.CredentialId);

                if (users.Count > 0)
                {
                    return(false);
                }

                return(true);
            };

            // 2. Verify and make the credentials
            var success = await _lib.MakeNewCredentialAsync(attestationResponse, options, callback);

            // 3. Store the credentials in db
            DemoStorage.AddCredentialToUser(options.User, new StoredCredential
            {
                Descriptor       = new PublicKeyCredentialDescriptor(success.Result.CredentialId),
                PublicKey        = success.Result.PublicKey,
                UserHandle       = success.Result.User.Id,
                SignatureCounter = success.Result.Counter
            });

            // 4. return "ok" to the client
            return(Json(success));
        }
コード例 #4
0
        public void Constructor_GiveValidArguments_PropertiesAreSet()
        {
            var authenticatorAttestationRawResponse = new AuthenticatorAttestationRawResponse();
            var credentialCreateOptions             = new CredentialCreateOptions();
            var command = new EnrollAuthenticatorDeviceCommand("name", authenticatorAttestationRawResponse, credentialCreateOptions);

            Assert.Equal("name", command.Name);
            Assert.Equal(authenticatorAttestationRawResponse, command.AuthenticatorAttestationRawResponse);
            Assert.Equal(credentialCreateOptions, command.CredentialCreateOptions);
        }
コード例 #5
0
        public async Task <JsonResult> MakeCredential([FromBody] AuthenticatorAttestationRawResponse attestationResponse, CancellationToken cancellationToken)
        {
            try
            {
                // 1. get the options we sent the client
                var jsonOptions = HttpContext.Session.GetString("fido2.attestationOptions");
                var options     = CredentialCreateOptions.FromJson(jsonOptions);

                // 2. Create callback so that lib can verify credential id is unique to this user
                IsCredentialIdUniqueToUserAsyncDelegate callback = static async(args, cancellationToken) =>
コード例 #6
0
        public async Task <CacheItem> ExecuteAsync(string fido2Payload, CacheItem relatedItem)
        {
            var request = OwnIdSerializer.Deserialize <Fido2RegisterRequest>(fido2Payload);

            if (string.IsNullOrWhiteSpace(request.AttestationObject))
            {
                throw new CommandValidationException("Incorrect Fido2 register request: AttestationObject is missing");
            }

            if (string.IsNullOrWhiteSpace(request.ClientDataJson))
            {
                throw new CommandValidationException("Incorrect Fido2 register request: ClientDataJson is missing");
            }

            var fido2Response = new AuthenticatorAttestationRawResponse
            {
                Id       = _encodingService.Base64UrlDecode(NewUserId),
                RawId    = _encodingService.Base64UrlDecode(NewUserId),
                Type     = PublicKeyCredentialType.PublicKey,
                Response = new AuthenticatorAttestationRawResponse.ResponseData
                {
                    AttestationObject = _encodingService.Base64UrlDecode(request.AttestationObject),
                    ClientDataJson    = _encodingService.Base64UrlDecode(request.ClientDataJson)
                }
            };

            var options = new CredentialCreateOptions
            {
                Challenge = _encodingService.ASCIIDecode(relatedItem.Context),
                Rp        = new PublicKeyCredentialRpEntity(
                    _configuration.Fido2.RelyingPartyId,
                    _configuration.Fido2.RelyingPartyName,
                    null),
                User = new Fido2User
                {
                    DisplayName = _configuration.Fido2.UserDisplayName,
                    Name        = _configuration.Fido2.UserName,
                    Id          = _encodingService.Base64UrlDecode(NewUserId)
                }
            };

            var result = await _fido2.MakeNewCredentialAsync(fido2Response, options, args => Task.FromResult(true));

            if (result == null)
            {
                throw new InternalLogicException("Cannot verify fido2 register request");
            }

            var publicKey = _encodingService.Base64UrlEncode(result.Result.PublicKey);

            return(await ProcessFido2RegisterResponseAsync(relatedItem, publicKey, result.Result.Counter,
                                                           _encodingService.Base64UrlEncode(result.Result.CredentialId)));
        }
コード例 #7
0
        public async Task <ActionResult <CredentialMakeResult> > MakeCredential(AuthenticatorAttestationRawResponse attestationResponse)
        {
            try
            {
                // 1. get the options we sent the client
                var jsonOptions = HttpContext.Session.GetString("fido2.attestationOptions");
                var options     = CredentialCreateOptions.FromJson(jsonOptions);

                // 2. Create callback so that lib can verify credential id is unique to this user
                IsCredentialIdUniqueToUserAsyncDelegate callback = async(IsCredentialIdUniqueToUserParams args) =>
                {
                    var credentialIdString = Base64Url.Encode(args.CredentialId);
                    var cred = await context.StoredCredentials.Where(x => x.DescriptorJson.Contains(credentialIdString)).FirstOrDefaultAsync();

                    if (cred == null)
                    {
                        return(true);
                    }
                    var users = await context.Members.Where(x => x.UserId == cred.UserId).Select(u => new Fido2User
                    {
                        DisplayName = u.DisplayName,
                        Name        = u.UserName,
                        Id          = u.UserId
                    }).ToListAsync();

                    return(users.Count == 0);
                };

                // 2. Verify and make the credentials
                var success = await _fido2.MakeNewCredentialAsync(attestationResponse, options, callback);

                context.StoredCredentials.Add(new StoredCredential
                {
                    Descriptor       = new PublicKeyCredentialDescriptor(success.Result.CredentialId),
                    UserId           = options.User.Id,
                    PublicKey        = success.Result.PublicKey,
                    UserHandle       = success.Result.User.Id,
                    SignatureCounter = success.Result.Counter,
                    CredType         = success.Result.CredType,
                    RegDate          = DateTime.Now,
                    AaGuid           = success.Result.Aaguid
                });
                await context.SaveChangesAsync();

                return(Ok(success));
            }
            catch (Exception e)
            {
                return(BadRequest(new CredentialMakeResult {
                    Status = "error", ErrorMessage = FormatException(e)
                }));
            }
        }
コード例 #8
0
        public async Task <IActionResult> MakeCredential([FromBody] AuthenticatorAttestationRawResponse attestationResponse)
        {
            var o = new
            {
                challenge = string.Empty,
                origin    = string.Empty,
                type      = string.Empty
            };

            var username = string.Empty;

            try
            {
                o = JsonConvert.DeserializeAnonymousType((Encoding.UTF8.GetString(attestationResponse.Response.ClientDataJson)), o);
                var jsonOptions = _memoryCache.Get <string>(o.challenge);
                var options     = CredentialCreateOptions.FromJson(jsonOptions);
                username = options.User.Name;

                async Task <bool> Callback(IsCredentialIdUniqueToUserParams args)
                {
                    var users = await _dataStore.GetUsersByCredentialIdAsync(args.CredentialId);

                    return(users.Count <= 0);
                }

                var success = await _lib.MakeNewCredentialAsync(attestationResponse, options, Callback);

                _dataStore.AddCredentialToUser(options.User, new StoredCredential
                {
                    Descriptor       = new PublicKeyCredentialDescriptor(success.Result.CredentialId),
                    PublicKey        = success.Result.PublicKey,
                    UserHandle       = success.Result.User.Id,
                    SignatureCounter = success.Result.Counter,
                    CredType         = success.Result.CredType,
                    RegDate          = DateTime.Now,
                    AaGuid           = success.Result.Aaguid
                });

                var ev = new Event(username, "Successfully logged the person in", nameof(RegistrationController), nameof(MakeCredential));
                await _elasticClient.IndexAsync(ev, i => i.Index(GetIndexName(nameof(Ok))));

                return(Ok(success));
            }
            catch (Exception e)
            {
                var errorEvent = new ErrorEvent(e, username, nameof(RegistrationController), nameof(MakeCredential));
                await _elasticClient.IndexAsync(errorEvent, i => i.Index(GetIndexName(nameof(Exception))));

                return(Ok(new Fido2.CredentialMakeResult {
                    Status = "error", ErrorMessage = FormatException(e) + $"ClientDataJson = {Encoding.UTF8.GetString(attestationResponse.Response.ClientDataJson)}"
                }));
            }
        }
コード例 #9
0
        public void TestAuthenticatorAttestationResponseNull()
        {
            var rawResponse = new AuthenticatorAttestationRawResponse
            {
                Type     = PublicKeyCredentialType.PublicKey,
                Id       = new byte[] { 0xf1, 0xd0 },
                RawId    = new byte[] { 0xf1, 0xd0 },
                Response = null,
            };
            var ex = Assert.Throws <Fido2VerificationException>(() => AuthenticatorAttestationResponse.Parse(rawResponse));

            Assert.Equal("Expected rawResponse, got null", ex.Message);
        }
コード例 #10
0
        public void TestAuthenticatorAttestationReponseAttestationObjectNull(byte[] value)
        {
            var rawResponse = new AuthenticatorAttestationRawResponse
            {
                Response = new AuthenticatorAttestationRawResponse.ResponseData()
                {
                    AttestationObject = value,
                }
            };
            var ex = Assert.Throws <Fido2VerificationException>(() => AuthenticatorAttestationResponse.Parse(rawResponse));

            Assert.Equal("Missing AttestationObject", ex.Message);
        }
コード例 #11
0
        public void TestAuthenticatorAttestationRawResponse()
        {
            var challenge      = RandomGenerator.Default.GenerateBytes(128);
            var clientDataJson = JsonSerializer.SerializeToUtf8Bytes(new
            {
                Type      = "webauthn.create",
                Challenge = challenge,
                Origin    = "https://www.passwordless.dev",
            });

            var rawResponse = new AuthenticatorAttestationRawResponse
            {
                Type     = PublicKeyCredentialType.PublicKey,
                Id       = new byte[] { 0xf1, 0xd0 },
                RawId    = new byte[] { 0xf1, 0xd0 },
                Response = new AuthenticatorAttestationRawResponse.ResponseData()
                {
                    AttestationObject = new CborMap().Encode(),
                    ClientDataJson    = clientDataJson
                },
                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, rawResponse.Type);
            Assert.True(rawResponse.Id.SequenceEqual(new byte[] { 0xf1, 0xd0 }));
            Assert.True(rawResponse.RawId.SequenceEqual(new byte[] { 0xf1, 0xd0 }));
            Assert.True(rawResponse.Response.AttestationObject.SequenceEqual(new byte[] { 0xa0 }));
            Assert.True(rawResponse.Response.ClientDataJson.SequenceEqual(clientDataJson));
            Assert.True(rawResponse.Extensions.AppID);
            Assert.True(rawResponse.Extensions.AuthenticatorSelection);
            Assert.Equal(rawResponse.Extensions.Extensions, new string[] { "foo", "bar" });
            Assert.Equal("test", rawResponse.Extensions.Example);
            Assert.Equal((ulong)4, rawResponse.Extensions.UserVerificationMethod[0][0]);
        }
コード例 #12
0
        public async Task <ActionResult> MakeCredential(
            [FromBody] AuthenticatorAttestationRawResponse attestationResponse)
        {
            try
            {
                // 1. get the options we sent the client
                var jsonOptions = HttpContext.Session.GetString("fido2.attestationOptions");
                var options     = CredentialCreateOptions.FromJson(jsonOptions);

                // 2. Create callback so that lib can verify credential id is unique to this user
                IsCredentialIdUniqueToUserAsyncDelegate callback = async(IsCredentialIdUniqueToUserParams args) =>
                {
                    var users = await fidoStore.GetUsersByCredentialIdAsync(args.CredentialId);

                    if (users.Count > 0)
                    {
                        return(false);
                    }

                    return(true);
                };

                // 2. Verify and make the credentials
                var success = await fido2.MakeNewCredentialAsync(attestationResponse, options, callback);

                // 3. Store the credentials in db
                fidoStore.AddCredentialToUser(options.User, new StoredCredential
                {
                    Descriptor       = new PublicKeyCredentialDescriptor(success.Result.CredentialId),
                    PublicKey        = success.Result.PublicKey,
                    UserHandle       = success.Result.User.Id,
                    SignatureCounter = success.Result.Counter,
                    CredType         = success.Result.CredType,
                    RegDate          = DateTime.Now,
                    AaGuid           = success.Result.Aaguid
                });

                // 4. return "ok" to the client
                return(Ok(success));
            }
            catch (Exception e)
            {
                return(BadRequest(new Fido2.CredentialMakeResult {
                    Status = "error", ErrorMessage = FormatException(e)
                }));
            }
        }
コード例 #13
0
        public void TestAuthenticatorAttestationObjectBadCBOR(byte[] value)
        {
            var rawResponse = new AuthenticatorAttestationRawResponse
            {
                Response = new AuthenticatorAttestationRawResponse.ResponseData()
                {
                    AttestationObject = value,
                }
            };

            var ex = Assert.Throws <Fido2VerificationException>(() => AuthenticatorAttestationResponse.Parse(rawResponse));

            Assert.Equal("AttestationObject invalid CBOR", ex.Message);

            var innerEx = (CborContentException)ex.InnerException;

            Assert.Equal("Declared definite length of CBOR data item exceeds available buffer size.", innerEx.Message);
        }
コード例 #14
0
        public async Task <JsonResult> SaveCredentials([FromBody] AuthenticatorAttestationRawResponse attestationResponse)
        {
            try
            {
                var jsonOptions = HttpContext.Session.GetString("fido2.attestationOptions");
                var options     = CredentialCreateOptions.FromJson(jsonOptions);

                var fidoCredentials = await fido2.MakeNewCredentialAsync(attestationResponse, options, IsCredentialUnique);

                var storedCredential = new StoredCredential
                {
                    Descriptor       = new PublicKeyCredentialDescriptor(fidoCredentials.Result.CredentialId),
                    PublicKey        = fidoCredentials.Result.PublicKey,
                    UserHandle       = fidoCredentials.Result.User.Id,
                    SignatureCounter = fidoCredentials.Result.Counter,
                    CredType         = fidoCredentials.Result.CredType,
                    RegDate          = DateTime.Now,
                    AaGuid           = fidoCredentials.Result.Aaguid
                };

                var names  = options.User.DisplayName.Split(' ');
                var result = await oktaClient.Users.CreateUserAsync(new CreateUserWithoutCredentialsOptions
                {
                    Profile = new UserProfile
                    {
                        Login                     = options.User.Name,
                        Email                     = options.User.Name,
                        DisplayName               = options.User.DisplayName,
                        FirstName                 = names[0],
                        LastName                  = names[1],
                        ["CredentialId"]          = Convert.ToBase64String(fidoCredentials.Result.CredentialId),
                        ["PasswordlessPublicKey"] = JsonConvert.SerializeObject(storedCredential)
                    }
                });

                return(Json(fidoCredentials));
            }
            catch (Exception e)
            {
                return(Json(new Fido2.CredentialMakeResult {
                    Status = "error", ErrorMessage = e.Message
                }));
            }
        }
コード例 #15
0
        public async Task <JsonResult> MakeCredential([FromBody] AuthenticatorAttestationRawResponse attestationResponse, [FromQuery] CredentialCreateOptions options)
        {
            var user = await GetCurrentLocalUserAsync();

            try
            {
                // 2. Create callback so that lib can verify credential id is unique to this user
                IsCredentialIdUniqueToUserAsyncDelegate callback = async(IsCredentialIdUniqueToUserParams args) =>
                {
                    var users = await _auth.GetUserIdsByCredentialIdAsync(args.CredentialId);

                    if (users.Count > 0)
                    {
                        return(false);
                    }

                    return(true);
                };

                // 2. Verify and make the credentials
                var success = await _fido2.MakeNewCredentialAsync(attestationResponse, options, callback);

                // 3. Store the credentials in db
                await _auth.AddCredential(user, new StoredCredential
                {
                    Descriptor       = new PublicKeyCredentialDescriptor(success.Result.CredentialId),
                    PublicKey        = success.Result.PublicKey,
                    UserId           = BitConverter.GetBytes(user.Id),
                    UserHandle       = success.Result.User.Id,
                    SignatureCounter = success.Result.Counter,
                    CredType         = success.Result.CredType,
                    RegDate          = DateTime.Now,
                    AaGuid           = success.Result.Aaguid
                });

                // 4. return "ok" to the client
                return(Json(success));
            }
            catch (Exception e)
            {
                return(Json(new { Status = "error", ErrorMessage = e.Message }));
            }
        }
コード例 #16
0
        public async Task <bool> CompleteCreation(string userId, string name, AuthenticatorAttestationRawResponse attestationResponse)
        {
            await using var dbContext = _contextFactory.CreateContext();
            var user = await dbContext.Users.Include(applicationUser => applicationUser.Fido2Credentials)
                       .FirstOrDefaultAsync(applicationUser => applicationUser.Id == userId);

            if (user == null || !CreationStore.TryGetValue(userId, out var options))
            {
                return(false);
            }


            // 2. Verify and make the credentials
            var success = await _fido2.MakeNewCredentialAsync(attestationResponse, options, args => Task.FromResult(true));

            // 3. Store the credentials in db
            var newCredential = new Fido2Credential()
            {
                Name = name,
                ApplicationUserId = userId
            };

            newCredential.SetBlob(new Fido2CredentialBlob()
            {
                Descriptor       = new PublicKeyCredentialDescriptor(success.Result.CredentialId),
                PublicKey        = success.Result.PublicKey,
                UserHandle       = success.Result.User.Id,
                SignatureCounter = success.Result.Counter,
                CredType         = success.Result.CredType,
                AaGuid           = success.Result.Aaguid.ToString(),
            });

            await dbContext.Fido2Credentials.AddAsync(newCredential);

            await dbContext.SaveChangesAsync();

            CreationStore.Remove(userId, out _);
            return(true);
        }
コード例 #17
0
        public async Task <Result <Fido2.CredentialMakeResult> > MakeCredentialAsync(AuthenticatorAttestationRawResponse attestationResponse, int userId, long loginId, string deviceName)
        {
            try
            {
                // 1. get the options we sent the client
                var cacheKey = $"CredentialOptions:{loginId}";
                if (!_cache.TryGetValue(cacheKey, out CredentialCreateOptions options))
                {
                    _logger.LogError($"Failed to find credential options for user {userId} login {loginId}");
                    return(Result.Failure <Fido2.CredentialMakeResult>("No challenge found"));
                }
                _cache.Remove(cacheKey);

                // 2. Verify and make the credentials
                var cmr = await _fido2.MakeNewCredentialAsync(attestationResponse, options,
                                                              x => Task.FromResult(true));

                // 3. Store the credentials in db
                _db.DeviceAuthorizations.Add(new DeviceAuthorization
                {
                    UserId           = userId,
                    PublicKey        = cmr.Result.PublicKey,
                    CredentialId     = cmr.Result.CredentialId,
                    SignatureCounter = cmr.Result.Counter,
                    DeviceName       = deviceName
                });
                await _db.SaveChangesAsync();

                // 4. return "ok" to the client
                _logger.LogInformation($"Created credential for user {userId} login {loginId}");
                return(Result.Success(cmr));
            }
            catch (Exception e)
            {
                _logger.LogError(e, $"Failed to make credential for user {userId} login {loginId}");
                return(Result.Failure <Fido2.CredentialMakeResult>("Error making credential"));
            }
        }
コード例 #18
0
        public async Task <JsonResult> MakeCredential([FromBody] AuthenticatorAttestationRawResponse attestationResponse)
        {
            try
            {
                // 1. get the options we sent the client
                //var jsonOptions = HttpContext.Session.GetString("fido2.attestationOptions");
                var jsonOptions = await _distributedCache.GetStringAsync(UniqueId);

                if (string.IsNullOrEmpty(jsonOptions))
                {
                    throw new Exception("Can't get CredentialOptions from cache");
                }
                var options = CredentialCreateOptions.FromJson(jsonOptions);

                // 2. Create callback so that lib can verify credential id is unique to this user
                IsCredentialIdUniqueToUserAsyncDelegate callback = async(IsCredentialIdUniqueToUserParams args) =>
                {
                    var users = await _fido2Storage.GetUsersByCredentialIdAsync(args.CredentialId);

                    if (users.Count > 0)
                    {
                        return(false);
                    }

                    return(true);
                };

                // 2. Verify and make the credentials
                var success = await _lib.MakeNewCredentialAsync(attestationResponse, options, callback);

                // 3. Store the credentials in db
                await _fido2Storage.AddCredentialToUser(options.User, new FidoStoredCredential
                {
                    Username         = options.User.Name,
                    Descriptor       = new PublicKeyCredentialDescriptor(success.Result.CredentialId),
                    PublicKey        = success.Result.PublicKey,
                    UserHandle       = success.Result.User.Id,
                    SignatureCounter = success.Result.Counter,
                    CredType         = success.Result.CredType,
                    RegDate          = DateTime.Now,
                    AaGuid           = success.Result.Aaguid
                });

                // 4. return "ok" to the client
                var user = await _userManager.GetUserAsync(User);

                if (user == null)
                {
                    return(Json(new CredentialMakeResult {
                        Status = "error", ErrorMessage = $"Unable to load user with ID '{_userManager.GetUserId(User)}'."
                    }));
                }

                await _userManager.SetTwoFactorEnabledAsync(user, true);

                if (await _userManager.CountRecoveryCodesAsync(user) == 0)
                {
                    var recoveryCodes = await _userManager.GenerateNewTwoFactorRecoveryCodesAsync(user, 10);

                    RecoveryCodes = recoveryCodes.ToArray();
                }

                return(Json(success));
            }
            catch (Exception e)
            {
                return(Json(new CredentialMakeResult {
                    Status = "error", ErrorMessage = FormatException(e)
                }));
            }
        }
コード例 #19
0
        public async Task <JsonResult> MakeCredential([FromBody] AuthenticatorAttestationRawResponse attestationResponse)
        {
            try
            {
                // 1. get the options we sent the client
                var jsonOptions = HttpContext.Session.GetString("fido2.attestationOptions");
                var options     = CredentialCreateOptions.FromJson(jsonOptions);

                // 2. Create callback so that lib can verify credential id is unique to this user
                IsCredentialIdUniqueToUserAsyncDelegate callback = async(IsCredentialIdUniqueToUserParams args) =>
                {
                    var users = await _fido2Storage.GetUsersByCredentialIdAsync(args.CredentialId);

                    if (users.Count > 0)
                    {
                        return(false);
                    }

                    return(true);
                };

                // 2. Verify and make the credentials
                var success = await _lib.MakeNewCredentialAsync(attestationResponse, options, callback);

                // 3. Store the credentials in db
                await _fido2Storage.AddCredentialToUser(options.User, new FidoStoredCredential
                {
                    Username         = options.User.Name,
                    Descriptor       = new PublicKeyCredentialDescriptor(success.Result.CredentialId),
                    PublicKey        = success.Result.PublicKey,
                    UserHandle       = success.Result.User.Id,
                    SignatureCounter = success.Result.Counter,
                    CredType         = success.Result.CredType,
                    RegDate          = DateTime.Now,
                    AaGuid           = success.Result.Aaguid
                });

                // 4. return "ok" to the client

                var user = await _userManager.GetUserAsync(User);

                if (user == null)
                {
                    return(Json(new CredentialMakeResult {
                        Status = "error", ErrorMessage = _sharedLocalizer["FIDO2_USER_NOTFOUND", _userManager.GetUserId(User)]
                    }));
                }

                await _userManager.SetTwoFactorEnabledAsync(user, true);

                var userId = await _userManager.GetUserIdAsync(user);

                return(Json(success));
            }
            catch (Exception e)
            {
                return(Json(new CredentialMakeResult {
                    Status = "error", ErrorMessage = FormatException(e)
                }));
            }
        }
コード例 #20
0
        public async Task TestAuthenticatorOrigins(string origin, string expectedOrigin)
        {
            var challenge = RandomGenerator.Default.GenerateBytes(128);
            var rp        = origin;
            var acd       = new AttestedCredentialData(("00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-40-FE-6A-32-63-BE-37-D1-01-B1-2E-57-CA-96-6C-00-22-93-E4-19-C8-CD-01-06-23-0B-C6-92-E8-CC-77-12-21-F1-DB-11-5D-41-0F-82-6B-DB-98-AC-64-2E-B1-AE-B5-A8-03-D1-DB-C1-47-EF-37-1C-FD-B1-CE-B0-48-CB-2C-A5-01-02-03-26-20-01-21-58-20-A6-D1-09-38-5A-C7-8E-5B-F0-3D-1C-2E-08-74-BE-6D-BB-A4-0B-4F-2A-5F-2F-11-82-45-65-65-53-4F-67-28-22-58-20-43-E1-08-2A-F3-13-5B-40-60-93-79-AC-47-42-58-AA-B3-97-B8-86-1D-E4-41-B4-4E-83-08-5D-1C-6B-E0-D0").Split('-').Select(c => Convert.ToByte(c, 16)).ToArray());
            var authData  = new AuthenticatorData(
                SHA256.Create().ComputeHash(Encoding.UTF8.GetBytes(origin)),
                AuthenticatorFlags.UP | AuthenticatorFlags.AT,
                0,
                acd,
                null
                ).ToByteArray();
            var clientDataJson = Encoding.UTF8.GetBytes(
                JsonConvert.SerializeObject
                (
                    new
            {
                Type      = "webauthn.create",
                Challenge = challenge,
                Origin    = rp,
            }
                )
                );
            var rawResponse = new AuthenticatorAttestationRawResponse
            {
                Type     = PublicKeyCredentialType.PublicKey,
                Id       = new byte[] { 0xf1, 0xd0 },
                RawId    = new byte[] { 0xf1, 0xd0 },
                Response = new AuthenticatorAttestationRawResponse.ResponseData()
                {
                    AttestationObject = CBORObject.NewMap().Add("fmt", "none").Add("attStmt", CBORObject.NewMap()).Add("authData", authData).EncodeToBytes(),
                    ClientDataJson    = clientDataJson
                },
            };

            var origChallenge = new CredentialCreateOptions
            {
                Attestation            = AttestationConveyancePreference.Direct,
                AuthenticatorSelection = new AuthenticatorSelection
                {
                    AuthenticatorAttachment = AuthenticatorAttachment.CrossPlatform,
                    RequireResidentKey      = true,
                    UserVerification        = UserVerificationRequirement.Required,
                },
                Challenge        = challenge,
                ErrorMessage     = "",
                PubKeyCredParams = new List <PubKeyCredParam>()
                {
                    new PubKeyCredParam
                    {
                        Alg  = COSE.Algorithm.ES256,
                        Type = PublicKeyCredentialType.PublicKey,
                    }
                },
                Rp     = new PublicKeyCredentialRpEntity(rp, rp, ""),
                Status = "ok",
                User   = new Fido2User
                {
                    Name        = "testuser",
                    Id          = Encoding.UTF8.GetBytes("testuser"),
                    DisplayName = "Test User",
                },
                Timeout = 60000,
            };

            IsCredentialIdUniqueToUserAsyncDelegate callback = (args) =>
            {
                return(Task.FromResult(true));
            };

            var lib = new Fido2(new Fido2Configuration()
            {
                ServerDomain = rp,
                ServerName   = rp,
                Origin       = expectedOrigin,
            });

            var result = await lib.MakeNewCredentialAsync(rawResponse, origChallenge, callback);
        }
コード例 #21
0
        public void TestAuthenticatorAttestationResponseNotUserPresent()
        {
            var challenge = RandomGenerator.Default.GenerateBytes(128);
            var rp        = "https://www.passwordless.dev";
            var authData  = new AuthenticatorData(
                SHA256.Create().ComputeHash(Encoding.UTF8.GetBytes(rp)),
                AuthenticatorFlags.UV,
                0,
                null,
                null
                ).ToByteArray();
            var clientDataJson = Encoding.UTF8.GetBytes(
                JsonConvert.SerializeObject
                (
                    new
            {
                Type      = "webauthn.create",
                Challenge = challenge,
                Origin    = rp,
            }
                )
                );
            var rawResponse = new AuthenticatorAttestationRawResponse
            {
                Type     = PublicKeyCredentialType.PublicKey,
                Id       = new byte[] { 0xf1, 0xd0 },
                RawId    = new byte[] { 0xf1, 0xd0 },
                Response = new AuthenticatorAttestationRawResponse.ResponseData()
                {
                    AttestationObject = CBORObject.NewMap().Add("fmt", "testing").Add("attStmt", CBORObject.NewMap()).Add("authData", authData).EncodeToBytes(),
                    ClientDataJson    = clientDataJson
                },
            };

            var origChallenge = new CredentialCreateOptions
            {
                Attestation            = AttestationConveyancePreference.Direct,
                AuthenticatorSelection = new AuthenticatorSelection
                {
                    AuthenticatorAttachment = AuthenticatorAttachment.CrossPlatform,
                    RequireResidentKey      = true,
                    UserVerification        = UserVerificationRequirement.Required,
                },
                Challenge        = challenge,
                ErrorMessage     = "",
                PubKeyCredParams = new List <PubKeyCredParam>()
                {
                    new PubKeyCredParam
                    {
                        Alg  = COSE.Algorithm.ES256,
                        Type = PublicKeyCredentialType.PublicKey,
                    }
                },
                Rp     = new PublicKeyCredentialRpEntity(rp, rp, ""),
                Status = "ok",
                User   = new Fido2User
                {
                    Name        = "testuser",
                    Id          = Encoding.UTF8.GetBytes("testuser"),
                    DisplayName = "Test User",
                },
                Timeout = 60000,
            };

            IsCredentialIdUniqueToUserAsyncDelegate callback = (args) =>
            {
                return(Task.FromResult(true));
            };

            var lib = new Fido2(new Fido2Configuration()
            {
                ServerDomain = rp,
                ServerName   = rp,
                Origin       = rp,
            });

            var ex = Assert.ThrowsAsync <Fido2VerificationException>(() => lib.MakeNewCredentialAsync(rawResponse, origChallenge, callback));

            Assert.Equal("User Present flag not set in authenticator data", ex.Result.Message);
        }
コード例 #22
0
        /// <summary>
        /// 注册:验证用户凭证
        /// <para>当客户端返回响应时,我们验证并注册凭据。</para>
        /// </summary>
        /// <param name="attestationResponse"></param>
        public async Task <CredentialMakeResult> RegisterCredentials(User user, string fido2Name, AuthenticatorAttestationRawResponse attestationResponse)
        {
            // 1. get the options we sent the client
            var jsonOptions = distributedCache.GetString(user.UserId.ToString() + "attestationOptions");

            if (string.IsNullOrEmpty(jsonOptions))
            {
                return(null);
            }
            var options = CredentialCreateOptions.FromJson(jsonOptions);

            // 2. Create callback so that lib can verify credential id is unique to this user
            IsCredentialIdUniqueToUserAsyncDelegate callback = async(IsCredentialIdUniqueToUserParams args) =>
            {
                //var users = await DemoStorage.GetUsersByCredentialIdAsync(args.CredentialId);
                //if (users.Count > 0)
                //    return false;
                var argUserId = args.CredentialId;
                if (this.dataContext.FIDO2Repository.Where(b => b.CredentialId.Equals(argUserId)).Any())
                {
                    return(false);
                }
                return(true);
            };
            // 2. Verify and make the credentials
            var success = await _fido2.MakeNewCredentialAsync(attestationResponse, options, callback);

            // 3. Store the credentials in db
            //var user = dataContext.User.Where(b => b.UserId == userId).FirstOrDefault();
            if (user == null)
            {
                return(null);
            }
            var fido2 = new FIDO2Item()
            {
                Id               = IdGenerator.NextId(),
                UserId           = user.UserId,
                FIDO2Name        = fido2Name,
                CredentialId     = success.Result.CredentialId,
                PublicKey        = success.Result.PublicKey,
                UserHandle       = success.Result.User.Id,
                SignatureCounter = success.Result.Counter,
                CredType         = success.Result.CredType,
                RegDate          = DateTime.Now,
                AaGuid           = success.Result.Aaguid
            };

            dataContext.FIDO2Repository.Add(fido2);

            dataContext.SaveChanges();

            return(success);
        }
コード例 #23
0
        public async Task <IActionResult> RegisterCallback([FromBody] AuthenticatorAttestationRawResponse model)
        {
            var sub = HttpContext.User.Claims.FirstOrDefault(x => x.Type == "sub")?.Value;

            if (string.IsNullOrEmpty(sub))
            {
                return(RedirectToAction("Index", "Home"));
            }
            var user = await _users.FindByIdAsync(sub);

            if (user == null)
            {
                return(RedirectToAction("Index", "Home"));
            }

            try
            {
                // 1. get the options we sent the client
                var jsonOptions       = HttpContext.Session.GetString("fido2.attestationOptions");
                var options           = CredentialCreateOptions.FromJson(jsonOptions);
                var authenticatorName = HttpContext.Session.GetString("fido2.attestationOptions.authenticatorType");
                // 2. Create callback so that lib can verify credential id is unique to this user
                IsCredentialIdUniqueToUserAsyncDelegate callback = async(IsCredentialIdUniqueToUserParams args) =>
                {
                    var users = _authContext.FidoLogins.Where(l => l.PublicKeyIdBytes.SequenceEqual(args.CredentialId));
                    if (users.Count() > 0)
                    {
                        return(false);
                    }

                    return(true);
                };

                // 2. Verify and make the credentials
                var success = await _lib.MakeNewCredentialAsync(model, options, callback);

                var parsedResponse = AuthenticatorAttestationResponse.Parse(model);;
                var authData       = new AuthenticatorData(parsedResponse.AttestationObject.AuthData);
                var dbUser         = _authContext.Users.First(x => x.Id == user.Id);
                dbUser.TwoFactorEnabled = true;
                var login = new FidoLogin()
                {
                    PublicKeyIdBytes  = success.Result.CredentialId,
                    PublicKeyId       = Fido2NetLib.Base64Url.Encode(success.Result.CredentialId),
                    AaGuid            = success.Result.Aaguid.ToString(),
                    PublicKey         = success.Result.PublicKey,
                    SignatureCounter  = success.Result.Counter,
                    CredType          = success.Result.CredType,
                    RegistrationDate  = DateTime.Now,
                    User              = dbUser,
                    UserHandle        = success.Result.User.Id,
                    AuthenticatorName = authenticatorName
                };
                _authContext.FidoLogins.Add(login);
                _authContext.SaveChanges();


                // 4. return "ok" to the client
                return(Json(new { success = true }));
            }
            catch (Exception e)
            {
                return(Json(new { error = true }));
            }
        }
コード例 #24
0
            public async Task <Fido2.CredentialMakeResult> MakeAttestationResponse()
            {
                _attestationObject.Set("authData", _authData);

                var attestationResponse = new AuthenticatorAttestationRawResponse
                {
                    Type     = PublicKeyCredentialType.PublicKey,
                    Id       = new byte[] { 0xf1, 0xd0 },
                    RawId    = new byte[] { 0xf1, 0xd0 },
                    Response = new AuthenticatorAttestationRawResponse.ResponseData()
                    {
                        AttestationObject = _attestationObject.EncodeToBytes(),
                        ClientDataJson    = _clientDataJson,
                    },
                    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
                            },
                        },
                    }
                };

                var origChallenge = new CredentialCreateOptions
                {
                    Attestation            = AttestationConveyancePreference.Direct,
                    AuthenticatorSelection = new AuthenticatorSelection
                    {
                        AuthenticatorAttachment = AuthenticatorAttachment.CrossPlatform,
                        RequireResidentKey      = true,
                        UserVerification        = UserVerificationRequirement.Required,
                    },
                    Challenge        = _challenge,
                    ErrorMessage     = "",
                    PubKeyCredParams = new List <PubKeyCredParam>()
                    {
                        new PubKeyCredParam
                        {
                            Alg  = COSE.Algorithm.ES256,
                            Type = PublicKeyCredentialType.PublicKey,
                        }
                    },
                    Rp     = new PublicKeyCredentialRpEntity(rp, rp, ""),
                    Status = "ok",
                    User   = new Fido2User
                    {
                        Name        = "testuser",
                        Id          = Encoding.UTF8.GetBytes("testuser"),
                        DisplayName = "Test User",
                    },
                    Timeout = 60000,
                };

                IsCredentialIdUniqueToUserAsyncDelegate callback = (args) =>
                {
                    return(Task.FromResult(true));
                };

                var lib = new Fido2(new Fido2Configuration()
                {
                    ServerDomain = rp,
                    ServerName   = rp,
                    Origin       = rp,
                });

                var credentialMakeResult = await lib.MakeNewCredentialAsync(attestationResponse, origChallenge, callback);

                return(credentialMakeResult);
            }
コード例 #25
0
ファイル: UserService.cs プロジェクト: nitinj19/server
        public async Task <bool> CompleteWebAuthRegistrationAsync(User user, int id, string name, AuthenticatorAttestationRawResponse attestationResponse)
        {
            var keyId = $"Key{id}";

            var provider = user.GetTwoFactorProvider(TwoFactorProviderType.WebAuthn);

            if (!provider?.MetaData?.ContainsKey("pending") ?? true)
            {
                return(false);
            }

            var options = CredentialCreateOptions.FromJson((string)provider.MetaData["pending"]);

            // Callback to ensure credential id is unique. Always return true since we don't care if another
            // account uses the same 2fa key.
            IsCredentialIdUniqueToUserAsyncDelegate callback = args => Task.FromResult(true);

            var success = await _fido2.MakeNewCredentialAsync(attestationResponse, options, callback);

            provider.MetaData.Remove("pending");
            provider.MetaData[keyId] = new TwoFactorProvider.WebAuthnData
            {
                Name             = name,
                Descriptor       = new PublicKeyCredentialDescriptor(success.Result.CredentialId),
                PublicKey        = success.Result.PublicKey,
                UserHandle       = success.Result.User.Id,
                SignatureCounter = success.Result.Counter,
                CredType         = success.Result.CredType,
                RegDate          = DateTime.Now,
                AaGuid           = success.Result.Aaguid
            };

            var providers = user.GetTwoFactorProviders();

            providers[TwoFactorProviderType.WebAuthn] = provider;
            user.SetTwoFactorProviders(providers);
            await UpdateTwoFactorProviderAsync(user, TwoFactorProviderType.WebAuthn);

            return(true);
        }
コード例 #26
0
        public async Task <JsonResult> MakeCredential([FromBody] AuthenticatorAttestationRawResponse authenticatorAttestationRawResponse)
        {
            try
            {
                var user = await _userManager.GetUserAsync(User);

                if (user == null)
                {
                    throw new Exception("Unable to retrieve user.");
                }

                var jsonOptions = await _distributedCache.GetStringAsync(UniqueId);

                if (string.IsNullOrEmpty(jsonOptions))
                {
                    throw new Exception("Cant get Credential options from cache.");
                }
                var options = CredentialCreateOptions.FromJson(jsonOptions);

                IsCredentialIdUniqueToUserAsyncDelegate isCredentialIdUniqueToUserAsyncDelegate = async(IsCredentialIdUniqueToUserParams isCredentialIdUniqueToUserParams) =>
                {
                    var fido2Users = await _fido2Service.GetFido2UsersByCredentialIdAsync(isCredentialIdUniqueToUserParams.CredentialId);

                    if (fido2Users.Count > 0)
                    {
                        return(false);
                    }
                    return(true);
                };
                var result = await _fido2.MakeNewCredentialAsync(authenticatorAttestationRawResponse, options, isCredentialIdUniqueToUserAsyncDelegate);

                if (result.Status != "ok")
                {
                    throw new Exception("Unable to create credential.");
                }


                var newFido2StoredCredential = new Fido2StoredCredential {
                };
                newFido2StoredCredential.UserName         = options.User.Name;
                newFido2StoredCredential.UserId           = options.User.Id;
                newFido2StoredCredential.PublicKey        = result.Result.PublicKey;
                newFido2StoredCredential.UserHandle       = result.Result.User.Id;
                newFido2StoredCredential.SignatureCounter = result.Result.Counter;
                newFido2StoredCredential.CredType         = result.Result.CredType;
                newFido2StoredCredential.RegDate          = DateTime.Now;
                newFido2StoredCredential.AaGuid           = Guid.NewGuid();
                newFido2StoredCredential.Descriptor       = new PublicKeyCredentialDescriptor(result.Result.CredentialId);

                _fido2Service.AddFido2StoredCredential(newFido2StoredCredential);

                return(Json(result));
            }
            catch (Exception exception)
            {
                return(Json(new CredentialCreateOptions()
                {
                    Status = "error", ErrorMessage = CommonFunctions.FormatException(exception)
                }));
            }
        }
コード例 #27
0
        public async Task TestApplePublicKeyMismatch()
        {
            var cpkBytes = new byte[] { 0xa5, 0x01, 0x02, 0x03, 0x26, 0x20, 0x01, 0x21, 0x58, 0x20, 0x79, 0xfe, 0x59, 0x08, 0xbb, 0x51, 0x29, 0xc8, 0x09, 0x38, 0xb7, 0x54, 0xc0, 0x4d, 0x2b, 0x34, 0x0e, 0xfa, 0x66, 0x15, 0xb9, 0x87, 0x69, 0x8b, 0xf5, 0x9d, 0xa4, 0xe5, 0x3e, 0xa3, 0xe6, 0xfe, 0x22, 0x58, 0x20, 0xfb, 0x03, 0xda, 0xa1, 0x27, 0x0d, 0x58, 0x04, 0xe8, 0xab, 0x61, 0xc1, 0x5a, 0xac, 0xa2, 0x43, 0x5c, 0x7d, 0xbf, 0x36, 0x9d, 0x71, 0xca, 0x15, 0xc5, 0x23, 0xb0, 0x00, 0x4a, 0x1b, 0x75, 0xb7 };

            _credentialPublicKey = new CredentialPublicKey(cpkBytes);

            var authData = new AuthenticatorData(_rpIdHash, _flags, _signCount, _acd, _exts).ToByteArray();

            _attestationObject.Set("authData", new CborByteString(authData));
            var clientData = new
            {
                type      = "webauthn.create",
                challenge = _challenge,
                origin    = "https://www.passwordless.dev",
            };
            var clientDataJson = JsonSerializer.SerializeToUtf8Bytes(clientData);

            var invalidX5cStrings = StackAllocSha256(authData, clientDataJson);

            var trustPath = invalidX5cStrings
                            .Select(x => new X509Certificate2(Convert.FromBase64String(x)))
                            .ToArray();

            var X5c = new CborArray {
                { trustPath[0].RawData },
                { trustPath[1].RawData }
            };

            ((CborMap)_attestationObject["attStmt"]).Set("x5c", X5c);

            var attestationResponse = new AuthenticatorAttestationRawResponse
            {
                Type     = PublicKeyCredentialType.PublicKey,
                Id       = new byte[] { 0xf1, 0xd0 },
                RawId    = new byte[] { 0xf1, 0xd0 },
                Response = new AuthenticatorAttestationRawResponse.ResponseData()
                {
                    AttestationObject = _attestationObject.Encode(),
                    ClientDataJson    = clientDataJson,
                }
            };

            var origChallenge = new CredentialCreateOptions
            {
                Attestation            = AttestationConveyancePreference.Direct,
                AuthenticatorSelection = new AuthenticatorSelection
                {
                    AuthenticatorAttachment = AuthenticatorAttachment.CrossPlatform,
                    RequireResidentKey      = true,
                    UserVerification        = UserVerificationRequirement.Discouraged,
                },
                Challenge        = _challenge,
                ErrorMessage     = "",
                PubKeyCredParams = new List <PubKeyCredParam>()
                {
                    new PubKeyCredParam(COSE.Algorithm.ES256)
                },
                Rp     = new PublicKeyCredentialRpEntity("https://www.passwordless.dev", "6cc3c9e7967a.ngrok.io", ""),
                Status = "ok",
                User   = new Fido2User
                {
                    Name        = "testuser",
                    Id          = Encoding.UTF8.GetBytes("testuser"),
                    DisplayName = "Test User",
                },
                Timeout = 60000,
            };

            IsCredentialIdUniqueToUserAsyncDelegate callback = (args, cancellationToken) =>
            {
                return(Task.FromResult(true));
            };

            IFido2 lib = new Fido2(new Fido2Configuration()
            {
                ServerDomain = "6cc3c9e7967a.ngrok.io",
                ServerName   = "6cc3c9e7967a.ngrok.io",
                Origins      = new HashSet <string> {
                    "https://www.passwordless.dev"
                },
            });

            var credentialMakeResult = await lib.MakeNewCredentialAsync(attestationResponse, origChallenge, callback);
        }
コード例 #28
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();
        }
コード例 #29
0
        public void TestAuthenticatorAttestationResponseUVRequired()
        {
            var challenge = RandomGenerator.Default.GenerateBytes(128);
            var rp        = "https://www.passwordless.dev";
            var acd       = new AttestedCredentialData(("00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-40-FE-6A-32-63-BE-37-D1-01-B1-2E-57-CA-96-6C-00-22-93-E4-19-C8-CD-01-06-23-0B-C6-92-E8-CC-77-12-21-F1-DB-11-5D-41-0F-82-6B-DB-98-AC-64-2E-B1-AE-B5-A8-03-D1-DB-C1-47-EF-37-1C-FD-B1-CE-B0-48-CB-2C-A5-01-02-03-26-20-01-21-58-20-A6-D1-09-38-5A-C7-8E-5B-F0-3D-1C-2E-08-74-BE-6D-BB-A4-0B-4F-2A-5F-2F-11-82-45-65-65-53-4F-67-28-22-58-20-43-E1-08-2A-F3-13-5B-40-60-93-79-AC-47-42-58-AA-B3-97-B8-86-1D-E4-41-B4-4E-83-08-5D-1C-6B-E0-D0").Split('-').Select(c => Convert.ToByte(c, 16)).ToArray());
            var authData  = new AuthenticatorData(
                SHA256.HashData(Encoding.UTF8.GetBytes(rp)),
                AuthenticatorFlags.AT | AuthenticatorFlags.UP,
                0,
                acd
                ).ToByteArray();
            var clientDataJson = JsonSerializer.SerializeToUtf8Bytes(new
            {
                type      = "webauthn.create",
                challenge = challenge,
                origin    = rp,
            });

            var rawResponse = new AuthenticatorAttestationRawResponse
            {
                Type     = PublicKeyCredentialType.PublicKey,
                Id       = new byte[] { 0xf1, 0xd0 },
                RawId    = new byte[] { 0xf1, 0xd0 },
                Response = new AuthenticatorAttestationRawResponse.ResponseData()
                {
                    AttestationObject = new CborMap {
                        { "fmt", "none" },
                        { "attStmt", new CborMap() },
                        { "authData", authData }
                    }.Encode(),
                ClientDataJson = clientDataJson
                },
            };

            var origChallenge = new CredentialCreateOptions
            {
                Attestation            = AttestationConveyancePreference.Direct,
                AuthenticatorSelection = new AuthenticatorSelection
                {
                    AuthenticatorAttachment = AuthenticatorAttachment.CrossPlatform,
                    RequireResidentKey      = true,
                    UserVerification        = UserVerificationRequirement.Required,
                },
                Challenge        = challenge,
                ErrorMessage     = "",
                PubKeyCredParams = new List <PubKeyCredParam>()
                {
                    new PubKeyCredParam(COSE.Algorithm.ES256)
                },
                Rp     = new PublicKeyCredentialRpEntity(rp, rp, ""),
                Status = "ok",
                User   = new Fido2User
                {
                    Name        = "testuser",
                    Id          = Encoding.UTF8.GetBytes("testuser"),
                    DisplayName = "Test User",
                },
                Timeout = 60000,
            };

            IsCredentialIdUniqueToUserAsyncDelegate callback = (args, cancellationToken) =>
            {
                return(Task.FromResult(true));
            };

            IFido2 lib = new Fido2(new Fido2Configuration()
            {
                ServerDomain = rp,
                ServerName   = rp,
                Origins      = new HashSet <string> {
                    rp
                },
            });

            var ex = Assert.ThrowsAsync <Fido2VerificationException>(() => lib.MakeNewCredentialAsync(rawResponse, origChallenge, callback));

            Assert.Equal("User Verified flag not set in authenticator data and user verification was required", ex.Result.Message);
        }
コード例 #30
0
        public void TestAuthenticatorAttestationResponseNoAttestedCredentialData()
        {
            var challenge = RandomGenerator.Default.GenerateBytes(128);
            var rp        = "https://www.passwordless.dev";
            var authData  = new AuthenticatorData(
                SHA256.HashData(Encoding.UTF8.GetBytes(rp)),
                AuthenticatorFlags.UP | AuthenticatorFlags.UV,
                0,
                null
                ).ToByteArray();

            var clientDataJson = JsonSerializer.SerializeToUtf8Bytes(new
            {
                type      = "webauthn.create",
                challenge = challenge,
                origin    = rp,
            });

            var rawResponse = new AuthenticatorAttestationRawResponse
            {
                Type     = PublicKeyCredentialType.PublicKey,
                Id       = new byte[] { 0xf1, 0xd0 },
                RawId    = new byte[] { 0xf1, 0xd0 },
                Response = new AuthenticatorAttestationRawResponse.ResponseData()
                {
                    AttestationObject = new CborMap {
                        { "fmt", "testing" },
                        { "attStmt", new CborMap() },
                        { "authData", authData }
                    }.Encode(),
                ClientDataJson = clientDataJson
                },
            };

            var origChallenge = new CredentialCreateOptions
            {
                Attestation            = AttestationConveyancePreference.Direct,
                AuthenticatorSelection = new AuthenticatorSelection
                {
                    AuthenticatorAttachment = AuthenticatorAttachment.CrossPlatform,
                    RequireResidentKey      = true,
                    UserVerification        = UserVerificationRequirement.Discouraged,
                },
                Challenge        = challenge,
                ErrorMessage     = "",
                PubKeyCredParams = new List <PubKeyCredParam>()
                {
                    new PubKeyCredParam(COSE.Algorithm.ES256)
                },
                Rp     = new PublicKeyCredentialRpEntity(rp, rp, ""),
                Status = "ok",
                User   = new Fido2User
                {
                    Name        = "testuser",
                    Id          = Encoding.UTF8.GetBytes("testuser"),
                    DisplayName = "Test User",
                },
                Timeout = 60000,
            };

            IsCredentialIdUniqueToUserAsyncDelegate callback = (args, cancellationToken) =>
            {
                return(Task.FromResult(true));
            };

            IFido2 lib = new Fido2(new Fido2Configuration()
            {
                ServerDomain = rp,
                ServerName   = rp,
                Origins      = new HashSet <string> {
                    rp
                },
            });

            var ex = Assert.ThrowsAsync <Fido2VerificationException>(() => lib.MakeNewCredentialAsync(rawResponse, origChallenge, callback));

            Assert.Equal("Attestation flag not set on attestation data", ex.Result.Message);
        }