private byte[] HashPassword(string password, RandomNumberGenerator rng, Pbkdf2Provider pbkdf2Provider)
 {
     return(HashPasswordV3(password,
                           iterCount: _pbkdf2IterCount,
                           saltSize: _saltSize,
                           numBytesRequested: _pbkdf2SubkeyLength,
                           rng: rng,
                           pbkdf2Provider: pbkdf2Provider));
 }
        private static byte[] HashPasswordV3(string password, int iterCount, int saltSize, int numBytesRequested, RandomNumberGenerator rng, Pbkdf2Provider pbkdf2Provider)
        {
            // Produce a version 2 (see comment above) text hash.
            byte[] salt = new byte[_saltSize];
            rng.GetBytes(salt);
            byte[] subkey = pbkdf2Provider.DeriveKey(password, salt, iterCount, numBytesRequested);

            var outputBytes = new byte[13 + salt.Length + subkey.Length];

            outputBytes[0] = 0x01; // format marker
            WriteNetworkByteOrder(outputBytes, 1, (uint)KeyDerivationPrf.HMACSHA256);
            WriteNetworkByteOrder(outputBytes, 5, (uint)iterCount);
            WriteNetworkByteOrder(outputBytes, 9, (uint)saltSize);
            Buffer.BlockCopy(salt, 0, outputBytes, 13, salt.Length);
            Buffer.BlockCopy(subkey, 0, outputBytes, 13 + saltSize, subkey.Length);
            return(outputBytes);
        }
        private const int _saltSize           = 128 / 8; // 128 bits


        /// <summary>
        /// Creates a new instance of <see cref="PasswordHasher{TUser}"/>.
        /// </summary>
        /// <param name="optionsAccessor">The options for this instance.</param>
        public SimplePasswordHasher()
        {
            _rng            = RandomNumberGenerator.Create();
            _pbkdf2Provider = new Pbkdf2Provider();
        }
        private static bool VerifyHashedPassword(byte[] hashedPassword, string password, Pbkdf2Provider pbkdf2Provider)
        {
            try
            {
                // Read header information
                //KeyDerivationPrf prf = (KeyDerivationPrf)ReadNetworkByteOrder(hashedPassword, 1);
                var iterCount  = (int)ReadNetworkByteOrder(hashedPassword, 5);
                int saltLength = (int)ReadNetworkByteOrder(hashedPassword, 9);

                // Lower iteration count will be denied.
                if (iterCount < _pbkdf2IterCount)
                {
                    return(false);
                }
                // Read the salt: must be >= 128 bits
                if (saltLength < 128 / 8)
                {
                    return(false);
                }
                byte[] salt = new byte[saltLength];
                Buffer.BlockCopy(hashedPassword, 13, salt, 0, salt.Length);

                // Read the subkey (the rest of the payload): must be >= 128 bits
                int subkeyLength = hashedPassword.Length - 13 - salt.Length;
                if (subkeyLength < 128 / 8)
                {
                    return(false);
                }
                byte[] expectedSubkey = new byte[subkeyLength];
                Buffer.BlockCopy(hashedPassword, 13 + salt.Length, expectedSubkey, 0, expectedSubkey.Length);

                // Hash the incoming password and verify it
                byte[] actualSubkey = pbkdf2Provider.DeriveKey(password, salt, iterCount, subkeyLength);
                return(ByteArraysEqual(actualSubkey, expectedSubkey));
            }
            catch
            {
                // This should never occur except in the case of a malformed payload, where
                // we might go off the end of the array. Regardless, a malformed payload
                // implies verification failed.
                return(false);
            }
        }