コード例 #1
0
        private String CalcCredentialHash(String username, String password, String salt,
                                          PasswordHashSpecification specification)
        {
            String passwordHash = Hashing.CalcPasswordHash(specification.HashType, password, specification.Salt);

            if (passwordHash != null)
            {
                String argon2Hash = Hashing.CalcArgon2(username + "$" + passwordHash, salt);

                return(argon2Hash.Substring(argon2Hash.LastIndexOf('$') + 1));
            }
            else
            {
                return(null);
            }
        }
コード例 #2
0
        /// <summary>
        /// Calls the Enzoic CheckCredentials API in a secure fashion to check whether the provided username and password
        /// are known to be compromised.
        /// This call is made securely to the server - only a salted and hashed representation of the credentials are passed and
        /// the salt value is not passed along with it.
        /// @see <a href="https://www.enzoic.com/docs/credentials-api">https://www.enzoic.com/docs/credentials-api</a>
        /// </summary>
        /// <param name="username">the username to check - may be an email address or username</param>
        /// <param name="password">the password to check</param>
        /// <param name="lastCheckDate">(Optional) The timestamp for the last check you performed for this user.  If the date/time you provide
        /// for the last check is greater than the timestamp Enzoic has for the last breach affecting this user, the check will
        /// not be performed.This can be used to substantially increase performance.Can be set to null if no last check was performed
        /// or the credentials have changed since.</param>
        /// <param name="excludeHashTypes">(Optional) An array of PasswordTypes to ignore when calculating hashes for the credentials check.
        /// By excluding computationally expensive PasswordTypes, such as BCrypt, it is possible to balance the performance of this
        /// call against security.Can be set to null if you don't wish to exclude any hash types.</param>
        /// <returns>true if the credentials are known to be compromised, false otherwise</returns>
        /// <param name="useRawCredentials">(Optional) Pass true in for this parameter to use the Raw Credentials
        /// variant of the Credentials API.  The Raw Credentials version of the Credentials API allows you to
        /// check usernames and passwords for compromise without passing even a hashed version to Enzoic.
        /// This works be pulling down all of the Credentials Hashes Enzoic has for a given username and
        /// calculating/comparing locally.  The only thing that gets passed to Enzoic in this case is a hash of
        /// the username.  Raw Credentials requires a separate approval to unlock.  If you're interested in getting
        /// approved, please contact us through our website.</param>
        /// <returns>true if the credentials are known to be compromised, false otherwise</returns>///
        public bool CheckCredentials(string username, string password, DateTime?lastCheckDate = null,
                                     PasswordType[] excludeHashTypes = null, bool useRawCredentials = false)
        {
            String response = MakeRestCall(
                BaseURL + ACCOUNTS_API_PATH + "?username="******"&includeHashes=1" : ""),
                "GET", null);

            if (response == "404")
            {
                // this is all we needed to check for this - email wasn't even in the DB
                return(false);
            }

            // deserialize response
            AccountsResponse accountsResponse = JsonConvert.DeserializeObject <AccountsResponse>(response);

            // see if the lastCheckDate was later than the lastBreachDate - if so bail out
            if (lastCheckDate.HasValue && lastCheckDate.Value >= accountsResponse.lastBreachDate)
            {
                return(false);
            }

            if (accountsResponse.CredentialsHashes != null)
            {
                int bcryptCount = 0;
                foreach (CredentialsHashSpecification credHashSpec in accountsResponse.CredentialsHashes)
                {
                    PasswordHashSpecification hashSpec = new PasswordHashSpecification
                    {
                        Salt     = credHashSpec.Salt,
                        HashType = credHashSpec.HashType
                    };
                    if (excludeHashTypes != null && excludeHashTypes.Contains(hashSpec.HashType))
                    {
                        // this type is excluded
                        continue;
                    }

                    // bcrypt gets far too expensive for good response time if there are many of them to calculate.
                    // some mostly garbage accounts have accumulated a number of them in our DB and if we happen to hit one it
                    // kills performance, so short circuit out after at most 2 BCrypt hashes
                    if (hashSpec.HashType != PasswordType.BCrypt || bcryptCount <= 2)
                    {
                        if (hashSpec.HashType == PasswordType.BCrypt)
                        {
                            bcryptCount++;
                        }
                        String credentialHash = CalcCredentialHash(username, password, accountsResponse.Salt, hashSpec);

                        if (credentialHash != null)
                        {
                            if (credentialHash == credHashSpec.CredentialsHash)
                            {
                                return(true);
                            }
                        }
                    }
                }
            }
            else if (accountsResponse.PasswordHashesRequired != null)
            {
                int bcryptCount = 0;

                List <string> credentialHashes = new List <string>();
                StringBuilder queryString      = new StringBuilder();
                foreach (PasswordHashSpecification hashSpec in accountsResponse.PasswordHashesRequired)
                {
                    if (excludeHashTypes != null && excludeHashTypes.Contains(hashSpec.HashType))
                    {
                        // this type is excluded
                        continue;
                    }

                    // bcrypt gets far too expensive for good response time if there are many of them to calculate.
                    // some mostly garbage accounts have accumulated a number of them in our DB and if we happen to hit one it
                    // kills performance, so short circuit out after at most 2 BCrypt hashes
                    if (hashSpec.HashType != PasswordType.BCrypt || bcryptCount <= 2)
                    {
                        if (hashSpec.HashType == PasswordType.BCrypt)
                        {
                            bcryptCount++;
                        }

                        String credentialHash = CalcCredentialHash(username, password, accountsResponse.Salt, hashSpec);

                        if (credentialHash != null)
                        {
                            credentialHashes.Add(credentialHash);
                            if (queryString.Length == 0)
                            {
                                queryString.Append("?partialHashes=").Append(credentialHash.Substring(0, 10));
                            }
                            else
                            {
                                queryString.Append("&partialHashes=").Append(credentialHash.Substring(0, 10));
                            }
                        }
                    }
                }

                if (queryString.Length > 0)
                {
                    String credsResponse = MakeRestCall(
                        BaseURL + CREDENTIALS_API_PATH + queryString, "GET", null);

                    if (credsResponse != "404")
                    {
                        // loop through candidate hashes returned and see if we have a match with the exact hash
                        dynamic responseObj = JObject.Parse(credsResponse);

                        foreach (dynamic candidate in responseObj.candidateHashes)
                        {
                            if (credentialHashes.FirstOrDefault(hash => hash == candidate.ToString()) != null)
                            {
                                return(true);
                            }
                        }
                    }
                }
            }

            return(false);
        }