CryptUsingOpensshBcryptHash() private method

Encyrypt password using openssh bcrypt_hash function. Similar to CryptRaw.
private CryptUsingOpensshBcryptHash ( byte sha2pass, byte sha2salt ) : byte[]
sha2pass byte The SHA512 hash of the passphrase.
sha2salt byte The SHA512 hash of the salt.
return byte[]
Exemplo n.º 1
0
        /// <summary>
        /// Compute hash using bcrypt_pdkdf function from Openssh.
        /// </summary>
        /// <param name="pass">The passphrase.</param>
        /// <param name="salt">The salt.</param>
        /// <param name="key">The key. Must be initalized to the size needed.</param>
        /// <param name="rounds">The number of rounds.</param>
        /// <remarks>
        /// pkcs #5 pbkdf2 implementation using the "bcrypt" hash
        ///
        /// The bcrypt hash function is derived from the bcrypt password hashing
        /// function with the following modifications:
        /// 1. The input password and salt are preprocessed with SHA512.
        /// 2. The output length is expanded to 256 bits.
        /// 3. Subsequently the magic string to be encrypted is lengthened and modifed
        ///    to "OxychromaticBlowfishSwatDynamite"
        /// 4. The hash function is defined to perform 64 rounds of initial state
        ///    expansion. (More rounds are performed by iterating the hash.)
        ///
        /// One modification from official pbkdf2. Instead of outputting key material
        /// linearly, we mix it. pbkdf2 has a known weakness where if one uses it to
        /// generate (e.g.) 512 bits of key material for use as two 256 bit keys, an
        /// attacker can merely run once through the outer loop, but the user
        /// always runs it twice. Shuffling output bytes requires computing the
        /// entirety of the key material to assemble any subkey. This is something a
        /// wise caller could do; we just do it for you.
        /// </remarks>
        public static void HashUsingOpensshBCryptPbkdf(char[] pass, byte[] salt, ref byte[] key, uint rounds)
        {
            if (pass == null) {
                throw new ArgumentNullException("pass");
            }
            if (salt == null) {
                throw new ArgumentNullException("salt");
            }
            if (key == null) {
                throw new ArgumentNullException("key");
            }
            if (rounds < 1) {
                throw new ArgumentException("Must have at least one round", "rounds");
            }
            if (pass.Length == 0) {
                throw new ArgumentException("Empty password not allowed.", "pass");
            }
            if (salt.Length == 0 || salt.Length > (1 << 20)) {
                throw new ArgumentException("Bad salt size.", "salt");
            }
            if (key.Length == 0 || key.Length > (BCRYPT_HASHSIZE * BCRYPT_HASHSIZE)) {
                throw new ArgumentException("Bad key size.", "key");
            }

            var @out = new byte[BCRYPT_HASHSIZE];
            var countsalt = new byte[salt.Length + 4];
            var stride = (key.Length + @out.Length - 1) / @out.Length;
            var amt = (key.Length + stride - 1) / stride;

            Array.Copy(salt, countsalt, salt.Length);

            var sha512 = SHA512.Create();
            var bcrypt = new BCrypt();

            /* collapse password */
            var passBytes = Encoding.UTF8.GetBytes(pass);
            var sha2pass = sha512.ComputeHash(passBytes);
            Array.Clear(passBytes, 0, passBytes.Length);

            /* generate key, sizeof(out) at a time */
            int keylen = key.Length;
            for (uint count = 1; keylen > 0; count++) {
                countsalt[salt.Length + 0] = (byte)((count >> 24) & 0xff);
                countsalt[salt.Length + 1] = (byte)((count >> 16) & 0xff);
                countsalt[salt.Length + 2] = (byte)((count >> 8) & 0xff);
                countsalt[salt.Length + 3] = (byte)(count & 0xff);

                /* first round, salt is salt */
                var sha2salt = sha512.ComputeHash(countsalt);

                var tmpout = bcrypt.CryptUsingOpensshBcryptHash(sha2pass, sha2salt);
                Array.Copy(tmpout, @out, @out.Length);

                int i;
                for (i = 1; i < rounds; i++) {
                    /* subsequent rounds, salt is previous output */
                    sha2salt = sha512.ComputeHash(tmpout);
                    tmpout = bcrypt.CryptUsingOpensshBcryptHash(sha2pass, sha2salt);
                    for (int j = 0; j < @out.Length; j++)
                        @out[j] ^= tmpout[j];
                }

                /*
                 * pbkdf2 deviation: output the key material non-linearly.
                 */
                amt = Math.Min(amt, keylen);
                for (i = 0; i < amt; i++) {
                    var dest = i * stride + (count - 1);
                    if (dest >= key.Length)
                        break;
                    key[dest] = @out[i];
                }
                keylen -= i;
            }

            Array.Clear(@out, 0, @out.Length);
        }
Exemplo n.º 2
0
        /// <summary>
        /// Compute hash using bcrypt_pdkdf function from Openssh.
        /// </summary>
        /// <param name="pass">The passphrase.</param>
        /// <param name="salt">The salt.</param>
        /// <param name="key">The key. Must be initalized to the size needed.</param>
        /// <param name="rounds">The number of rounds.</param>
        /// <remarks>
        /// pkcs #5 pbkdf2 implementation using the "bcrypt" hash
        ///
        /// The bcrypt hash function is derived from the bcrypt password hashing
        /// function with the following modifications:
        /// 1. The input password and salt are preprocessed with SHA512.
        /// 2. The output length is expanded to 256 bits.
        /// 3. Subsequently the magic string to be encrypted is lengthened and modifed
        ///    to "OxychromaticBlowfishSwatDynamite"
        /// 4. The hash function is defined to perform 64 rounds of initial state
        ///    expansion. (More rounds are performed by iterating the hash.)
        ///
        /// One modification from official pbkdf2. Instead of outputting key material
        /// linearly, we mix it. pbkdf2 has a known weakness where if one uses it to
        /// generate (e.g.) 512 bits of key material for use as two 256 bit keys, an
        /// attacker can merely run once through the outer loop, but the user
        /// always runs it twice. Shuffling output bytes requires computing the
        /// entirety of the key material to assemble any subkey. This is something a
        /// wise caller could do; we just do it for you.
        /// </remarks>
        public static void HashUsingOpensshBCryptPbkdf(char[] pass, byte[] salt, ref byte[] key, uint rounds)
        {
            if (pass == null)
            {
                throw new ArgumentNullException("pass");
            }
            if (salt == null)
            {
                throw new ArgumentNullException("salt");
            }
            if (key == null)
            {
                throw new ArgumentNullException("key");
            }
            if (rounds < 1)
            {
                throw new ArgumentException("Must have at least one round", "rounds");
            }
            if (pass.Length == 0)
            {
                throw new ArgumentException("Empty password not allowed.", "pass");
            }
            if (salt.Length == 0 || salt.Length > (1 << 20))
            {
                throw new ArgumentException("Bad salt size.", "salt");
            }
            if (key.Length == 0 || key.Length > (BCRYPT_HASHSIZE * BCRYPT_HASHSIZE))
            {
                throw new ArgumentException("Bad key size.", "key");
            }

            var @out      = new byte[BCRYPT_HASHSIZE];
            var countsalt = new byte[salt.Length + 4];
            var stride    = (key.Length + @out.Length - 1) / @out.Length;
            var amt       = (key.Length + stride - 1) / stride;

            Array.Copy(salt, countsalt, salt.Length);

            var sha512 = SHA512.Create();
            var bcrypt = new BCrypt();

            /* collapse password */
            var passBytes = Encoding.UTF8.GetBytes(pass);
            var sha2pass  = sha512.ComputeHash(passBytes);

            Array.Clear(passBytes, 0, passBytes.Length);

            /* generate key, sizeof(out) at a time */
            int keylen = key.Length;

            for (uint count = 1; keylen > 0; count++)
            {
                countsalt[salt.Length + 0] = (byte)((count >> 24) & 0xff);
                countsalt[salt.Length + 1] = (byte)((count >> 16) & 0xff);
                countsalt[salt.Length + 2] = (byte)((count >> 8) & 0xff);
                countsalt[salt.Length + 3] = (byte)(count & 0xff);

                /* first round, salt is salt */
                var sha2salt = sha512.ComputeHash(countsalt);

                var tmpout = bcrypt.CryptUsingOpensshBcryptHash(sha2pass, sha2salt);
                Array.Copy(tmpout, @out, @out.Length);

                int i;
                for (i = 1; i < rounds; i++)
                {
                    /* subsequent rounds, salt is previous output */
                    sha2salt = sha512.ComputeHash(tmpout);
                    tmpout   = bcrypt.CryptUsingOpensshBcryptHash(sha2pass, sha2salt);
                    for (int j = 0; j < @out.Length; j++)
                    {
                        @out[j] ^= tmpout[j];
                    }
                }

                /*
                 * pbkdf2 deviation: output the key material non-linearly.
                 */
                amt = Math.Min(amt, keylen);
                for (i = 0; i < amt; i++)
                {
                    var dest = i * stride + (count - 1);
                    if (dest >= key.Length)
                    {
                        break;
                    }
                    key[dest] = @out[i];
                }
                keylen -= i;
            }

            Array.Clear(@out, 0, @out.Length);
        }