Beispiel #1
0
        /// <summary>
        /// Returns AssertionOptions including a challenge to the browser/authr to assert existing credentials and authenticate a user.
        /// </summary>
        /// <returns></returns>
        public AssertionOptions GetAssertionOptions(IEnumerable <PublicKeyCredentialDescriptor> allowedCredentials, UserVerificationRequirement?userVerification, AuthenticationExtensionsClientInputs extensions = null)
        {
            var challenge = new byte[_config.ChallengeSize];

            _crypto.GetBytes(challenge);

            var options = AssertionOptions.Create(_config, challenge, allowedCredentials, userVerification, extensions);

            return(options);
        }
        /// <summary>
        /// SetLoginAssertionResult method implementation
        /// </summary>
        private int SetLoginAssertionResult(AuthenticationContext ctx, string jsonResponse)
        {
            try
            {
                string jsonOptions = ctx.AssertionOptions;
                if (string.IsNullOrEmpty(jsonOptions))
                {
                    throw new ArgumentNullException(jsonOptions);
                }
                if (string.IsNullOrEmpty(jsonResponse))
                {
                    throw new ArgumentNullException(jsonResponse);
                }

                MFAWebAuthNUser user = RuntimeRepository.GetUser(Config, ctx.UPN);
                if (user != null)
                {
                    AssertionOptions options = AssertionOptions.FromJson(jsonOptions);
                    AuthenticatorAssertionRawResponse clientResponse = JsonConvert.DeserializeObject <AuthenticatorAssertionRawResponse>(jsonResponse);


                    MFAUserCredential creds = RuntimeRepository.GetCredentialById(Config, user, clientResponse.Id);

                    if (creds == null)
                    {
                        throw new Exception("Unknown credentials");
                    }

                    uint storedCounter = creds.SignatureCounter;

                    bool callback(IsUserHandleOwnerOfCredentialIdParams args)
                    {
                        var storedCreds = RuntimeRepository.GetCredentialsByUserHandle(Config, user, args.UserHandle);

                        return(storedCreds.Exists(c => c.Descriptor.Id.SequenceEqual(args.CredentialId)));
                    }
                    AssertionVerificationResult res = _webathn.SetAssertionResult(clientResponse, options, creds.PublicKey, storedCounter, callback);
                    RuntimeRepository.UpdateCounter(Config, user, res.CredentialId, res.Counter);
                    return((int)AuthenticationResponseKind.Biometrics);
                }
                else
                {
                    Log.WriteEntry(string.Format("{0}\r\n{1}", ctx.UPN, "User does not exists !"), System.Diagnostics.EventLogEntryType.Error, 5000);
                    return((int)AuthenticationResponseKind.Error);
                }
            }
            catch (Exception e)
            {
                Log.WriteEntry(string.Format("{0}\r\n{1}", ctx.UPN, e.Message), System.Diagnostics.EventLogEntryType.Error, 5000);
                return((int)AuthenticationResponseKind.Error);
            }
        }
Beispiel #3
0
        /// <summary>
        /// GetLoginAssertionsOptions method implementation
        /// </summary>
        private string GetLoginAssertionsOptions(AuthenticationContext ctx)
        {
            try
            {
                List <MFAPublicKeyCredentialDescriptor> existingCredentials = new List <MFAPublicKeyCredentialDescriptor>();

                if (!string.IsNullOrEmpty(ctx.UPN))
                {
                    var user = RuntimeRepository.GetUser(Config, ctx.UPN);
                    if (user == null)
                    {
                        throw new ArgumentException("Username was not registered");
                    }
                    existingCredentials = RuntimeRepository.GetCredentialsByUser(Config, user).Select(c => c.Descriptor).ToList();
                }

                AuthenticationExtensionsClientInputs exts = new AuthenticationExtensionsClientInputs()
                {
                    SimpleTransactionAuthorization  = "FIDO",
                    GenericTransactionAuthorization = new TxAuthGenericArg
                    {
                        ContentType = "text/plain",
                        Content     = new byte[] { 0x46, 0x49, 0x44, 0x4F }
                    },
                    UserVerificationIndex = this.UserVerificationIndex,
                    Location = this.Location,
                    UserVerificationMethod = this.UserVerificationMethod,
                    EnforceCredProtect     = this.EnforceCredProtect,
                    CredProtect            = this.CredProtect,
                    HmacSecret             = this.HmacSecret
                };

                UserVerificationRequirement uv      = this.UserVerificationRequirement.ToEnum <UserVerificationRequirement>();
                AssertionOptions            options = _webathn.GetAssertionOptions(existingCredentials.ToCore(), uv, exts);
                string result = options.ToJson();
                ctx.AssertionOptions = result;
                return(result);
            }
            catch (Exception e)
            {
                Log.WriteEntry(string.Format("{0}\r\n{1}", ctx.UPN, e.Message), EventLogEntryType.Error, 5000);
                string result = (new AssertionOptions {
                    Status = "error", ErrorMessage = string.Format("{0}{1}", e.Message, e.InnerException != null ? " (" + e.InnerException.Message + ")" : "")
                }).ToJson();
                ctx.AssertionOptions = result;
                return(result);
            }
        }
        /// <summary>
        /// GetLoginAssertionsOptions method implementation
        /// </summary>
        private string GetLoginAssertionsOptions(AuthenticationContext ctx)
        {
            try
            {
                List <MFAPublicKeyCredentialDescriptor> existingCredentials = new List <MFAPublicKeyCredentialDescriptor>();
                if (!string.IsNullOrEmpty(ctx.UPN))
                {
                    var user = RuntimeRepository.GetUser(Config, ctx.UPN);
                    if (user == null)
                    {
                        throw new ArgumentException("Username was not registered");
                    }
                    existingCredentials = RuntimeRepository.GetCredentialsByUser(Config, user).Select(c => c.Descriptor).ToList();
                }

                AuthenticationExtensionsClientInputs exts = new AuthenticationExtensionsClientInputs()
                {
                    Extensions             = this.Extentions,
                    UserVerificationMethod = this.UserVerificationMethod,
                };

                UserVerificationRequirement uv      = this.UserVerificationRequirement;
                AssertionOptions            options = null;
                if (existingCredentials.Count > 0)
                {
                    options = _webathn.GetAssertionOptions(existingCredentials.ToCore(), uv, exts);
                }
                else
                {
                    options = _webathn.GetAssertionOptions(null, uv, exts);
                }

                string result = options.ToJson();
                ctx.AssertionOptions = result;
                return(result);
            }
            catch (Exception e)
            {
                Log.WriteEntry(string.Format("{0}\r\n{1}", ctx.UPN, e.Message), EventLogEntryType.Error, 5000);
                string result = (new AssertionOptions {
                    Status = "error", ErrorMessage = string.Format("{0} / {1}", e.Message, e.InnerException != null ? " (" + e.InnerException.Message + ")" : "")
                }).ToJson();
                ctx.AssertionOptions = result;
                return(result);
            }
        }
Beispiel #5
0
        /// <summary>
        /// Verifies the assertion response from the browser/authr to assert existing credentials and authenticate a user.
        /// </summary>
        /// <returns></returns>
        public AssertionVerificationResult SetAssertionResult(
            AuthenticatorAssertionRawResponse assertionResponse,
            AssertionOptions originalOptions,
            byte[] storedPublicKey,
            uint storedSignatureCounter,
            IsUserHandleOwnerOfCredentialId isUserHandleOwnerOfCredentialIdCallback,
            byte[] requestTokenBindingId = null)
        {
            var parsedResponse = AuthenticatorAssertionResponse.Parse(assertionResponse);

            var result = parsedResponse.Verify(originalOptions,
                                               _config.Origin,
                                               storedPublicKey,
                                               storedSignatureCounter,
                                               isUserHandleOwnerOfCredentialIdCallback,
                                               requestTokenBindingId);

            return(result);
        }
        /// <summary>
        /// Implements alghoritm from https://www.w3.org/TR/webauthn/#verifying-assertion
        /// </summary>
        /// <param name="options">The assertionoptions that was sent to the client</param>
        /// <param name="expectedOrigin">
        /// The expected server origin, used to verify that the signature is sent to the expected server
        /// </param>
        /// <param name="storedPublicKey">The stored public key for this CredentialId</param>
        /// <param name="storedSignatureCounter">The stored counter value for this CredentialId</param>
        /// <param name="isUserHandleOwnerOfCredId">A function that returns <see langword="true"/> if user handle is owned by the credential ID</param>
        /// <param name="requestTokenBindingId"></param>
        public AssertionVerificationResult Verify(
            AssertionOptions options,
            string expectedOrigin,
            byte[] storedPublicKey,
            uint storedSignatureCounter,
            IsUserHandleOwnerOfCredentialId isUserHandleOwnerOfCredId,
            byte[] requestTokenBindingId)
        {
            BaseVerify(expectedOrigin, options.Challenge, requestTokenBindingId);

            if (Raw.Type != PublicKeyCredentialType.PublicKey)
            {
                throw new VerificationException("AssertionResponse Type is not set to public-key");
            }

            if (Raw.Id == null)
            {
                throw new VerificationException("Id is missing");
            }
            if (Raw.RawId == null)
            {
                throw new VerificationException("RawId is missing");
            }

            // 1. If the allowCredentials option was given when this authentication ceremony was initiated, verify that credential.id identifies one of the public key credentials that were listed in allowCredentials.
            if (options.AllowCredentials != null && options.AllowCredentials.Count() > 0)
            {
                // might need to transform x.Id and raw.id as described in https://www.w3.org/TR/webauthn/#publickeycredential
                if (!options.AllowCredentials.Any(x => x.Id.SequenceEqual(Raw.Id)))
                {
                    throw new VerificationException("Invalid");
                }
            }

            // 2. If credential.response.userHandle is present, verify that the user identified by this value is the owner of the public key credential identified by credential.id.
            if (UserHandle != null)
            {
                if (UserHandle.Length == 0)
                {
                    throw new VerificationException("Userhandle was empty DOMString. It should either be null or have a value.");
                }

                if (false == isUserHandleOwnerOfCredId(new IsUserHandleOwnerOfCredentialIdParams(Raw.Id, UserHandle)))
                {
                    throw new VerificationException("User is not owner of the public key identitief by the credential id");
                }
            }

            // 3. Using credential’s id attribute(or the corresponding rawId, if base64url encoding is inappropriate for your use case), look up the corresponding credential public key.
            // public key inserted via parameter.

            // 4. Let cData, authData and sig denote the value of credential’s response's clientDataJSON, authenticatorData, and signature respectively.
            //var cData = Raw.Response.ClientDataJson;
            var authData = new AuthenticatorData(Raw.Response.AuthenticatorData);

            //var sig = Raw.Response.Signature;

            // 5. Let JSONtext be the result of running UTF-8 decode on the value of cData.
            //var JSONtext = Encoding.UTF8.GetBytes(cData.ToString());


            // 7. Verify that the value of C.type is the string webauthn.get.
            if (Type != "webauthn.get")
            {
                throw new VerificationException();
            }

            // 8. Verify that the value of C.challenge matches the challenge that was sent to the authenticator in the PublicKeyCredentialRequestOptions passed to the get() call.
            // 9. Verify that the value of C.origin matches the Relying Party's origin.
            // done in base class

            //10. Verify that the value of C.tokenBinding.status matches the state of Token Binding for the TLS connection over which the attestation was obtained.If Token Binding was used on that TLS connection, also verify that C.tokenBinding.id matches the base64url encoding of the Token Binding ID for the connection.
            // Validated in BaseVerify.
            // todo: Needs testing

            // 11. Verify that the rpIdHash in aData is the SHA - 256 hash of the RP ID expected by the Relying Party.

            byte[] hashedClientDataJson;
            byte[] hashedRpId;
            using (var sha = SHA256.Create())
            {
                // 11
                // https://www.w3.org/TR/webauthn/#sctn-appid-extension
                // FIDO AppID Extension:
                // If true, the AppID was used and thus, when verifying an assertion, the Relying Party MUST expect the rpIdHash to be the hash of the AppID, not the RP ID.
                var rpid = Raw.Extensions?.AppID ?? false ? options.Extensions?.AppID : options.RpId;
                hashedRpId = sha.ComputeHash(Encoding.UTF8.GetBytes(rpid ?? string.Empty));
                // 15
                hashedClientDataJson = sha.ComputeHash(Raw.Response.ClientDataJson);
            }

            if (false == authData.RpIdHash.SequenceEqual(hashedRpId))
            {
                throw new VerificationException("Hash mismatch RPID");
            }
            // 12. Verify that the User Present bit of the flags in authData is set.
            // UNLESS...userVerification is set to preferred or discouraged?
            // See Server-ServerAuthenticatorAssertionResponse-Resp3 Test server processing authenticatorData
            // P-5 Send a valid ServerAuthenticatorAssertionResponse both authenticatorData.flags.UV and authenticatorData.flags.UP are not set, for userVerification set to "preferred", and check that server succeeds
            // P-8 Send a valid ServerAuthenticatorAssertionResponse both authenticatorData.flags.UV and authenticatorData.flags.UP are not set, for userVerification set to "discouraged", and check that server succeeds
            //if ((false == authData.UserPresent) && (options.UserVerification != UserVerificationRequirement.Discouraged && options.UserVerification != UserVerificationRequirement.Preferred)) throw new Fido2VerificationException("User Present flag not set in authenticator data");

            // 13 If user verification is required for this assertion, verify that the User Verified bit of the flags in aData is set.
            // UNLESS...userPresent is true?
            // see ee Server-ServerAuthenticatorAssertionResponse-Resp3 Test server processing authenticatorData
            // P-8 Send a valid ServerAuthenticatorAssertionResponse both authenticatorData.flags.UV and authenticatorData.flags.UP are not set, for userVerification set to "discouraged", and check that server succeeds
            if (UserVerificationRequirement.Required == options.UserVerification && false == authData.UserVerified)
            {
                throw new VerificationException("User verification is required");
            }

            // 14. Verify that the values of the client extension outputs in clientExtensionResults and the authenticator extension outputs in the extensions in authData are as expected, considering the client extension input values that were given as the extensions option in the get() call.In particular, any extension identifier values in the clientExtensionResults and the extensions in authData MUST be also be present as extension identifier values in the extensions member of options, i.e., no extensions are present that were not requested. In the general case, the meaning of "are as expected" is specific to the Relying Party and which extensions are in use.
            // todo: Verify this (and implement extensions on options)
            if (true == authData.HasExtensionsData && ((null == authData.Extensions) || (0 == authData.Extensions.Length)))
            {
                throw new VerificationException("Extensions flag present, malformed extensions detected");
            }
            if (false == authData.HasExtensionsData && (null != authData.Extensions))
            {
                throw new VerificationException("Extensions flag not present, but extensions detected");
            }

            // 15.
            // Done earlier, hashedClientDataJson

            // 16. Using the credential public key looked up in step 3, verify that sig is a valid signature over the binary concatenation of aData and hash.
            byte[] data = new byte[Raw.Response.AuthenticatorData.Length + hashedClientDataJson.Length];
            Buffer.BlockCopy(Raw.Response.AuthenticatorData, 0, data, 0, Raw.Response.AuthenticatorData.Length);
            Buffer.BlockCopy(hashedClientDataJson, 0, data, Raw.Response.AuthenticatorData.Length, hashedClientDataJson.Length);

            if (null == storedPublicKey || 0 == storedPublicKey.Length)
            {
                throw new VerificationException("Stored public key is null or empty");
            }
            var cpk = new CredentialPublicKey(storedPublicKey);

            if (true != cpk.Verify(data, Signature))
            {
                throw new VerificationException("Signature did not match");
            }

            // 17.
            if (authData.SignCount > 0 && authData.SignCount <= storedSignatureCounter)
            {
                throw new VerificationException("SignatureCounter was not greater than stored SignatureCounter");
            }

            return(new AssertionVerificationResult()
            {
                Status = "ok",
                ErrorMessage = string.Empty,
                CredentialId = Raw.Id,
                Counter = authData.SignCount,
            });
        }
Beispiel #7
0
        /// <summary>
        /// SetLoginAssertionResult method implementation
        /// </summary>
        private int SetLoginAssertionResult(AuthenticationContext ctx, string jsonResponse, out string error)
        {
            bool isDeserialized = false;

            try
            {
                string jsonOptions = ctx.AssertionOptions;
                if (string.IsNullOrEmpty(jsonOptions))
                {
                    throw new ArgumentNullException(jsonOptions);
                }
                if (string.IsNullOrEmpty(jsonResponse))
                {
                    throw new ArgumentNullException(jsonResponse);
                }

                MFAWebAuthNUser user = RuntimeRepository.GetUser(Config, ctx.UPN);
                if (user != null)
                {
                    AssertionOptions options = AssertionOptions.FromJson(jsonOptions);
                    try
                    {
                        AuthenticatorAssertionRawResponse clientResponse = JsonConvert.DeserializeObject <AuthenticatorAssertionRawResponse>(jsonResponse);
                        isDeserialized = true;

                        MFAUserCredential creds = RuntimeRepository.GetCredentialById(Config, user, clientResponse.Id);
                        if (creds == null)
                        {
                            throw new Exception("Unknown credentials");
                        }

                        // Check Replay
                        AuthenticatorData authData = new AuthenticatorData(clientResponse.Response.AuthenticatorData);
                        uint authCounter           = authData.SignCount;
                        uint storedCounter         = creds.SignatureCounter;
                        if ((authCounter <= 0) || (authCounter <= storedCounter))
                        {
                            ResourcesLocale Resources = new ResourcesLocale(ctx.Lcid);
                            throw new Exception(Resources.GetString(ResourcesLocaleKind.Html, "BIOERRORAUTHREPLAY"));
                        }

                        bool callback(IsUserHandleOwnerOfCredentialIdParams args)
                        {
                            var storedCreds = RuntimeRepository.GetCredentialsByUserHandle(Config, user, args.UserHandle);

                            return(storedCreds.Exists(c => c.Descriptor.Id.SequenceEqual(args.CredentialId)));
                        }
                        AssertionVerificationResult res = _webathn.SetAssertionResult(clientResponse, options, creds.PublicKey, storedCounter, callback);
                        RuntimeRepository.UpdateCounter(Config, user, res.CredentialId, res.Counter);

                        if (!authData.UserPresent || !authData.UserVerified)
                        {
                            switch (creds.CredType)
                            {
                            case "none":
                                ctx.PinRequirements = (this.PinRequirements.HasFlag(WebAuthNPinRequirements.None));
                                break;

                            case "android-key":
                                ctx.PinRequirements = (this.PinRequirements.HasFlag(WebAuthNPinRequirements.AndroidKey));
                                break;

                            case "android-safetynet":
                                ctx.PinRequirements = (this.PinRequirements.HasFlag(WebAuthNPinRequirements.AndroidSafetyNet));
                                break;

                            case "fido-u2f":
                                ctx.PinRequirements = (this.PinRequirements.HasFlag(WebAuthNPinRequirements.Fido2U2f));
                                break;

                            case "packed":
                                ctx.PinRequirements = (this.PinRequirements.HasFlag(WebAuthNPinRequirements.Packed));
                                break;

                            case "tpm":
                                ctx.PinRequirements = (this.PinRequirements.HasFlag(WebAuthNPinRequirements.TPM));
                                break;

                            case "apple":
                                ctx.PinRequirements = (this.PinRequirements.HasFlag(WebAuthNPinRequirements.Apple));
                                break;

                            default:
                                ctx.PinRequirements = false;
                                break;
                            }
                        }
                        else
                        {
                            ctx.PinRequirements = false;
                        }
                        error = string.Empty;
                        return((int)AuthenticationResponseKind.Biometrics);
                    }
                    catch (Exception ex)
                    {
                        if (isDeserialized)
                        {
                            Log.WriteEntry(string.Format("{0}\r\n{1}", ctx.UPN, ex.Message), EventLogEntryType.Error, 5000);
                        }
                        else
                        {
                            Log.WriteEntry(string.Format("{0}\r\n{1}", ctx.UPN, jsonResponse), EventLogEntryType.Error, 5000);
                        }
                        error = ex.Message;
                        return((int)AuthenticationResponseKind.Error);
                    }
                }
                else
                {
                    Log.WriteEntry(string.Format("{0}\r\n{1}", ctx.UPN, "User does not exists !"), EventLogEntryType.Error, 5000);
                    error = string.Format("{0}\r\n{1}", ctx.UPN, "User does not exists !");
                    return((int)AuthenticationResponseKind.Error);
                }
            }
            catch (Exception e)
            {
                Log.WriteEntry(string.Format("{0}\r\n{1}", ctx.UPN, e.Message), EventLogEntryType.Error, 5000);
                error = e.Message;
                return((int)AuthenticationResponseKind.Error);
            }
        }
        /// <summary>
        /// SetLoginAssertionResult method implementation
        /// </summary>
        private int SetLoginAssertionResult(AuthenticationContext ctx, string jsonResponse, out string error)
        {
            bool isDeserialized = false;

            try
            {
                string jsonOptions = ctx.AssertionOptions;
                if (string.IsNullOrEmpty(jsonOptions))
                {
                    throw new ArgumentNullException(jsonOptions);
                }
                if (string.IsNullOrEmpty(jsonResponse))
                {
                    throw new ArgumentNullException(jsonResponse);
                }

                MFAWebAuthNUser user = RuntimeRepository.GetUser(Config, ctx.UPN);
                if (user != null)
                {
                    AssertionOptions options = AssertionOptions.FromJson(jsonOptions);
                    try
                    {
                        AuthenticatorAssertionRawResponse clientResponse = JsonConvert.DeserializeObject <AuthenticatorAssertionRawResponse>(jsonResponse);
                        isDeserialized = true;

                        MFAUserCredential creds = RuntimeRepository.GetCredentialById(Config, user, clientResponse.Id);
                        if (creds == null)
                        {
                            throw new Exception("Unknown credentials");
                        }

                        AuthenticatorData authData = new AuthenticatorData(clientResponse.Response.AuthenticatorData);
                        bool isnocount             = Utilities.IsNoCounterDevice(this.Config.WebAuthNProvider.Configuration, ctx);
                        uint authCounter           = 0;
                        uint storedCounter         = 0;

                        if (!isnocount)
                        {
                            authCounter   = authData.SignCount;
                            storedCounter = creds.SignatureCounter;
                        }
                        else
                        if ((authCounter > 0) && (authCounter <= storedCounter))
                        {
                            ResourcesLocale Resources = new ResourcesLocale(ctx.Lcid);
                            throw new Exception(Resources.GetString(ResourcesLocaleKind.FIDOHtml, "BIOERRORAUTHREPLAY"));
                        }

#pragma warning disable CS1998 // Cette méthode async n'a pas d'opérateur 'await' et elle s'exécutera de façon synchrone
                        IsUserHandleOwnerOfCredentialIdAsync callback = async(args) =>
#pragma warning restore CS1998 // Cette méthode async n'a pas d'opérateur 'await' et elle s'exécutera de façon synchrone
                        {
                            var storedCreds = RuntimeRepository.GetCredentialsByUserHandle(Config, user, args.UserHandle);
                            return(storedCreds.Exists(c => c.Descriptor.Id.SequenceEqual(args.CredentialId)));
                        };

                        // Apple counter always 0
                        AssertionVerificationResult res = _webathn.SetAssertionResult(clientResponse, options, creds.PublicKey, storedCounter, callback).Result;
                        if (!isnocount)
                        {
                            RuntimeRepository.UpdateCounter(Config, user, res.CredentialId, res.Counter);
                        }
                        else
                        {
                            RuntimeRepository.UpdateCounter(Config, user, res.CredentialId, 0);
                        }

                        if (!authData.UserPresent || !authData.UserVerified)
                        {
                            switch (creds.CredType)
                            {
                            case "none":
                                ctx.PinRequirements = (this.PinRequirements.HasFlag(WebAuthNPinRequirements.None));
                                break;

                            case "android-key":
                                ctx.PinRequirements = (this.PinRequirements.HasFlag(WebAuthNPinRequirements.AndroidKey));
                                break;

                            case "android-safetynet":
                                ctx.PinRequirements = (this.PinRequirements.HasFlag(WebAuthNPinRequirements.AndroidSafetyNet));
                                break;

                            case "fido-u2f":
                                ctx.PinRequirements = (this.PinRequirements.HasFlag(WebAuthNPinRequirements.Fido2U2f));
                                break;

                            case "packed":
                                ctx.PinRequirements = (this.PinRequirements.HasFlag(WebAuthNPinRequirements.Packed));
                                break;

                            case "tpm":
                                ctx.PinRequirements = (this.PinRequirements.HasFlag(WebAuthNPinRequirements.TPM));
                                break;

                            case "apple":
                                ctx.PinRequirements = (this.PinRequirements.HasFlag(WebAuthNPinRequirements.Apple));
                                break;

                            default:
                                ctx.PinRequirements = false;
                                break;
                            }
                        }
                        else
                        {
                            ctx.PinRequirements = false;
                        }
                        error = string.Empty;
                        return((int)AuthenticationResponseKind.Biometrics);
                    }
                    catch (Exception ex)
                    {
                        if (isDeserialized)
                        {
                            Log.WriteEntry(string.Format("{0}\r\n{1}", ctx.UPN, ex.Message), EventLogEntryType.Error, 5000);
                        }
                        else
                        {
                            Log.WriteEntry(string.Format("{0}\r\n{1}", ctx.UPN, jsonResponse), EventLogEntryType.Error, 5000);
                        }
                        error = ex.Message;
                        return((int)AuthenticationResponseKind.Error);
                    }
                }
                else
                {
                    Log.WriteEntry(string.Format("{0}\r\n{1}", ctx.UPN, "User does not exists !"), EventLogEntryType.Error, 5000);
                    error = string.Format("{0}\r\n{1}", ctx.UPN, "User does not exists !");
                    return((int)AuthenticationResponseKind.Error);
                }
            }
            catch (Exception e)
            {
                Log.WriteEntry(string.Format("{0}\r\n{1}", ctx.UPN, e.Message), EventLogEntryType.Error, 5000);
                error = e.Message;
                return((int)AuthenticationResponseKind.Error);
            }
        }
Beispiel #9
0
        /// <summary>
        /// SetLoginAssertionResult method implementation
        /// </summary>
        private int SetLoginAssertionResult(AuthenticationContext ctx, string jsonResponse)
        {
            try
            {
                string jsonOptions = ctx.AssertionOptions;
                if (string.IsNullOrEmpty(jsonOptions))
                {
                    throw new ArgumentNullException(jsonOptions);
                }
                if (string.IsNullOrEmpty(jsonResponse))
                {
                    throw new ArgumentNullException(jsonResponse);
                }

                MFAWebAuthNUser user = RuntimeRepository.GetUser(Config, ctx.UPN);
                if (user != null)
                {
                    AssertionOptions options = AssertionOptions.FromJson(jsonOptions);
                    AuthenticatorAssertionRawResponse clientResponse = JsonConvert.DeserializeObject <AuthenticatorAssertionRawResponse>(jsonResponse);

                    // AuthData Flags
                    byte flag          = clientResponse.Response.AuthenticatorData[32];
                    var  userpresent   = (flag & (1 << 0)) != 0;
                    var  userverified  = (flag & (1 << 2)) != 0;
                    var  attestedcred  = (flag & (1 << 6)) != 0;
                    var  hasextenddata = (flag & (1 << 7)) != 0;

                    MFAUserCredential creds = RuntimeRepository.GetCredentialById(Config, user, clientResponse.Id);

                    if (creds == null)
                    {
                        throw new Exception("Unknown credentials");
                    }

                    uint storedCounter = creds.SignatureCounter;

                    bool callback(IsUserHandleOwnerOfCredentialIdParams args)
                    {
                        var storedCreds = RuntimeRepository.GetCredentialsByUserHandle(Config, user, args.UserHandle);

                        return(storedCreds.Exists(c => c.Descriptor.Id.SequenceEqual(args.CredentialId)));
                    }
                    AssertionVerificationResult res = _webathn.SetAssertionResult(clientResponse, options, creds.PublicKey, storedCounter, callback);
                    RuntimeRepository.UpdateCounter(Config, user, res.CredentialId, res.Counter);
                    if (!userpresent || !userverified)
                    {
                        switch (creds.CredType)
                        {
                        case "none":
                            ctx.PinRequirements = (this.PinRequirements.HasFlag(WebAuthNPinRequirements.None));
                            break;

                        case "android-key":
                            ctx.PinRequirements = (this.PinRequirements.HasFlag(WebAuthNPinRequirements.AndroidKey));
                            break;

                        case "android-safetynet":
                            ctx.PinRequirements = (this.PinRequirements.HasFlag(WebAuthNPinRequirements.AndroidSafetyNet));
                            break;

                        case "fido-u2f":
                            ctx.PinRequirements = (this.PinRequirements.HasFlag(WebAuthNPinRequirements.Fido2U2f));
                            break;

                        case "packed":
                            ctx.PinRequirements = (this.PinRequirements.HasFlag(WebAuthNPinRequirements.Packed));
                            break;

                        case "tpm":
                            ctx.PinRequirements = (this.PinRequirements.HasFlag(WebAuthNPinRequirements.TPM));
                            break;

                        case "apple":
                            ctx.PinRequirements = (this.PinRequirements.HasFlag(WebAuthNPinRequirements.Apple));
                            break;

                        default:
                            ctx.PinRequirements = false;
                            break;
                        }
                    }
                    else
                    {
                        ctx.PinRequirements = false;
                    }
                    return((int)AuthenticationResponseKind.Biometrics);
                }
                else
                {
                    Log.WriteEntry(string.Format("{0}\r\n{1}", ctx.UPN, "User does not exists !"), System.Diagnostics.EventLogEntryType.Error, 5000);
                    return((int)AuthenticationResponseKind.Error);
                }
            }
            catch (Exception e)
            {
                Log.WriteEntry(string.Format("{0}\r\n{1}", ctx.UPN, e.Message), System.Diagnostics.EventLogEntryType.Error, 5000);
                return((int)AuthenticationResponseKind.Error);
            }
        }