/// <summary>
        /// Verify the given Argon2 hash as being that of the given password.
        /// </summary>
        /// <param name="encoded">
        /// The Argon2 hash string. This has the actual hash along with other parameters used in the hash.
        /// </param>
        /// <param name="password">
        /// The password to verify
        /// </param>
        /// <returns>
        /// True on success; false otherwise.
        /// </returns>
        public static bool Verify(
            string encoded,
            byte[] password)
        {
            SecureArray <byte> hash = null;

            try
            {
                var configToVerify = new Argon2Config {
                    Password = password
                };
                if (!configToVerify.DecodeString(encoded, out hash) || hash == null)
                {
                    return(false);
                }

                var hasherToVerify = new Argon2(configToVerify);
                var hashToVerify   = hasherToVerify.Hash();
                return(!hash.Buffer.Where((b, i) => b != hashToVerify[i]).Any());
            }
            finally
            {
                hash?.Dispose();
            }
        }
Beispiel #2
0
        /// <summary>
        /// Verify the given Argon2 hash as being that of the given password.
        /// </summary>
        /// <param name="encoded">
        /// The Argon2 hash string. This has the actual hash along with other parameters used in the hash.
        /// </param>
        /// <param name="configToVerify">
        /// The configuration that contains the values used to created <paramref name="encoded"/>.
        /// </param>
        /// <returns>
        /// True on success; false otherwise.
        /// </returns>
        public static bool Verify(
            string encoded,
            Argon2Config configToVerify)
        {
            SecureArray <byte> hash = null;

            try
            {
                if (!configToVerify.DecodeString(encoded, out hash) || hash == null)
                {
                    return(false);
                }

                using (var hasherToVerify = new Argon2(configToVerify))
                {
                    using (var hashToVerify = hasherToVerify.Hash())
                    {
                        return(!hash.Buffer.Where((b, i) => b != hashToVerify[i]).Any());
                    }
                }
            }
            finally
            {
                hash?.Dispose();
            }
        }
        /// <summary>
        /// Compares a hash, salt and plain password and sets IsValid
        /// </summary>
        public SecuredPassword(string plainPassword, byte[] hash, byte[] salt, HashStrategyKind hashStrategy)
        {
            _hash = hash;
            _salt = salt;
            SetHashStrategy(hashStrategy);
            byte[] newKey;
            switch (hashStrategy)
            {
            case HashStrategyKind.Pbkdf210001Iterations:
                var numberOfIterations = (int)_hashingParameter;
                if (numberOfIterations <= 10000)
                {
                    throw new ArgumentException("Iterations must be greater than 10000");
                }
                using (var deriveBytes = new Rfc2898DeriveBytes(plainPassword, salt, numberOfIterations, HashAlgorithmName.SHA256))
                {
                    newKey  = deriveBytes.GetBytes(_saltSize);
                    IsValid = newKey.SequenceEqual(hash);
                }
                break;

            case HashStrategyKind.Argon2WorkCost:
                SecureArray <byte> hashB = null;
                try
                {
                    var passwordBytes            = Encoding.ASCII.GetBytes(plainPassword);
                    var configOfPasswordToVerify = new Argon2Config
                    {
                        Type       = Argon2Type.DataIndependentAddressing,
                        Version    = Argon2Version.Nineteen,
                        TimeCost   = 10,
                        MemoryCost = (int)_hashingParameter,
                        Lanes      = 5,
                        Threads    = Environment.ProcessorCount,
                        Salt       = _salt,
                        Password   = passwordBytes,
                        HashLength = 20
                    };
                    var hashString = Encoding.ASCII.GetString(_hash);
                    if (configOfPasswordToVerify.DecodeString(hashString, out hashB) && hashB != null)
                    {
                        var argon2ToVerify = new Argon2(configOfPasswordToVerify);
                        using (var hashToVerify = argon2ToVerify.Hash())
                        {
                            if (!hashB.Buffer.Where((b, i) => b != hashToVerify[i]).Any())
                            {
                                IsValid = true;
                            }
                        }
                    }
                }
                finally
                {
                    hashB?.Dispose();
                }
                break;
            }
        }
Beispiel #4
0
        /// <summary>
        /// Decodes an Argon2 hash string into an Argon2 class instance.
        /// </summary>
        /// <param name="config">
        /// The configuration to populate with the data found in <paramref name="str"/>.
        /// </param>
        /// <param name="str">
        /// The string to decode.
        /// </param>
        /// <param name="hash">
        /// Loaded with the hash found in <paramref name="str"/>; set to null if
        /// <paramref name="str"/> does not contain a hash.
        /// </param>
        /// <returns>
        /// True on success; false otherwise. <paramref name="hash"/> set to
        /// null on failure.
        /// </returns>
        /// <remarks>
        /// <para>
        /// Expected format:
        /// </para>
        /// <para>
        /// $argon2&lt;T>[$v=&lt;num>]$m=&lt;num>,t=&lt;num>,p=&lt;num>[,keyid=&lt;bin>][,data=&lt;bin>][$&lt;bin>[$&lt;bin>]].
        /// </para>
        /// <para>
        /// where &lt;T> is either 'd' or 'i', &lt;num> is a decimal integer (positive, fits in
        /// an 'unsigned long'), and &lt;bin> is Base64-encoded data (no '=' padding
        /// characters, no newline or whitespace).
        /// The "keyid" is a binary identifier for a key (up to 8 bytes);
        /// "data" is associated data (up to 32 bytes). When the 'keyid'
        /// (resp. the 'data') is empty, then it is ommitted from the output.
        /// </para>
        /// <para>
        /// The last two binary chunks (encoded in Base64) are, in that order,
        /// the salt and the output. Both are optional, but you cannot have an
        /// output without a salt. The binary salt length is between 8 and 48 bytes.
        /// The output length is always exactly 32 bytes.
        /// </para>
        /// </remarks>
        public static bool DecodeString(this Argon2Config config, string str, out SecureArray <byte> hash)
        {
            int        pos;
            Argon2Type type;

            if (str.StartsWith("$argon2id"))
            {
                type = Argon2Type.HybridAddressing;
                pos  = 9;
            }
            else if (str.StartsWith("$argon2i"))
            {
                type = Argon2Type.DataIndependentAddressing;
                pos  = 8;
            }
            else if (str.StartsWith("$argon2d"))
            {
                type = Argon2Type.DataDependentAddressing;
                pos  = 8;
            }
            else
            {
                hash = null;
                return(false);
            }

            var version = Argon2Version.Sixteen;

            /* Reading the version number if the default is suppressed */
            {
                var check = "$v=";
                if (string.Compare(str, pos, check, 0, check.Length) == 0)
                {
                    pos += check.Length;
                    pos  = DecodeDecimal(str, pos, out var decX);
                    if (pos < 0)
                    {
                        hash = null;
                        return(false);
                    }

                    version = (Argon2Version)decX;
                }
            }

            pos = DecodeDecimal(out var memoryCost, "$m=", str, pos);
            if (pos < 0)
            {
                hash = null;
                return(false);
            }

            pos = DecodeDecimal(out var timeCost, ",t=", str, pos);
            if (pos < 0)
            {
                hash = null;
                return(false);
            }

            pos = DecodeDecimal(out var lanes, ",p=", str, pos);
            if (pos < 0)
            {
                hash = null;
                return(false);
            }

            byte[] associatedData = null;
            {
                var check = ",data=";
                if (string.Compare(str, pos, check, 0, check.Length) == 0)
                {
                    pos += check.Length;
                    pos  = FromBase64(out associatedData, str, pos);
                    if (pos < 0)
                    {
                        hash = null;
                        return(false);
                    }
                }
            }

            var validator = new Argon2Config();

            if (pos == str.Length)
            {
                try
                {
                    validator.Type           = type;
                    validator.TimeCost       = (int)timeCost;
                    validator.MemoryCost     = (int)memoryCost;
                    validator.Lanes          = (int)lanes;
                    validator.AssociatedData = associatedData;
                    validator.Version        = version;
                }
                catch (Exception)
                {
                    hash = null;
                    return(false);
                }

                config.Type           = type;
                config.TimeCost       = (int)timeCost;
                config.MemoryCost     = (int)memoryCost;
                config.Lanes          = (int)lanes;
                config.AssociatedData = associatedData;
                config.Version        = version;
                hash = null;
                return(true);
            }

            pos = DecodeBase64(out var salt, "$", str, pos);
            if (pos < 0)
            {
                hash = null;
                return(false);
            }

            if (pos == str.Length)
            {
                try
                {
                    validator.Type           = type;
                    validator.TimeCost       = (int)timeCost;
                    validator.MemoryCost     = (int)memoryCost;
                    validator.Lanes          = (int)lanes;
                    validator.Salt           = salt;
                    validator.AssociatedData = associatedData;
                    validator.Version        = version;
                }
                catch (Exception)
                {
                    hash = null;
                    return(false);
                }

                config.Type           = type;
                config.TimeCost       = (int)timeCost;
                config.MemoryCost     = (int)memoryCost;
                config.Lanes          = (int)lanes;
                config.Salt           = salt;
                config.AssociatedData = associatedData;
                config.Version        = version;
                hash = null;
                return(true);
            }

            if (str[pos] != '$')
            {
                hash = null;
                return(false);
            }

            ++pos;
            int hashlen = Base64Length(str, pos);

            if (hashlen < 0)
            {
                hash = null;
                return(false);
            }

            SecureArray <byte> output;

            try
            {
                output = new SecureArray <byte>(hashlen, SecureArrayType.ZeroedPinnedAndNoSwap, config.SecureArrayCall);
            }
            catch (LockFailException)
            {
                output = new SecureArray <byte>(hashlen, SecureArrayType.ZeroedAndPinned, config.SecureArrayCall);
            }

            bool success = false;

            try
            {
                pos = FromBase64(output.Buffer, str, pos);
                if (pos < 0)
                {
                    hash = null;
                    return(false);
                }

                if (pos != str.Length)
                {
                    hash = null;
                    return(false);
                }

                try
                {
                    validator.Type           = type;
                    validator.TimeCost       = (int)timeCost;
                    validator.MemoryCost     = (int)memoryCost;
                    validator.Lanes          = (int)lanes;
                    validator.Salt           = salt;
                    validator.AssociatedData = associatedData;
                    validator.Version        = version;
                }
                catch (Exception)
                {
                    hash = null;
                    return(false);
                }

                config.Type           = type;
                config.TimeCost       = (int)timeCost;
                config.MemoryCost     = (int)memoryCost;
                config.Lanes          = (int)lanes;
                config.Salt           = salt;
                config.AssociatedData = associatedData;
                config.Version        = version;
                hash    = output;
                success = true;
                return(true);
            }
            finally
            {
                if (!success)
                {
                    output.Dispose();
                }
            }
        }
Beispiel #5
0
        internal static ObjectResult VerifyUser(string username, string password)
        {
            if (string.IsNullOrWhiteSpace(username) || string.IsNullOrWhiteSpace(password))
            {
                return(new InvalidResult("Error: Username/Password are required."));
            }

            try
            {
                var result = from user in context.users
                             where user.username == username
                             select user;

                if (result.Count() == 0)
                {
                    return(new NotFoundResult("Error: User does not exist."));
                }

                string passwordHash = result.First().passwordHash;
                bool?  isAdmin      = result.First().isAdmin;
                //START CODE BLOCK
                //-- Code within used from
                //https://github.com/mheyman/Isopoh.Cryptography.Argon2/blob/master/README.md
                Argon2Config config = new Argon2Config
                {
                    Type       = Argon2Type.DataIndependentAddressing,
                    Version    = Argon2Version.Nineteen,
                    TimeCost   = 3,
                    MemoryCost = 32768,
                    Lanes      = 4,
                    Threads    = Environment.ProcessorCount,
                    Password   = Encoding.ASCII.GetBytes(password),
                    Salt       = Convert.FromBase64String(Properties.Settings.Default.Salt),
                    HashLength = 20
                };
                SecureArray <byte> hashB = null;
                try
                {
                    if (config.DecodeString(passwordHash, out hashB) && hashB != null)
                    {
                        var argon2ToVerify = new Argon2(config);
                        using (var hashToVerify = argon2ToVerify.Hash())
                        {
                            if (!hashB.Buffer.Where((b, i) => b != hashToVerify[i]).Any())
                            {
                                return(new OkResult(isAdmin.ToString()));
                            }
                            else
                            {
                                return(new WrongResult("Error: Username/Password is incorrect."));
                            }
                        }
                    }
                }
                catch (Exception ex)
                {
                    return(new InvalidResult("Error: Failed to verify password."));
                }
                finally
                {
                    hashB?.Dispose();
                }
                //END CODE BLOCK
            }
            catch (Exception ex)
            {
                return(new InvalidResult("Error: Failed retrieving data from the database."));
            }
            return(new InvalidResult("Error: Failed to process verification."));
        }
 public void Dispose() => _privateKey?.Dispose();