/// <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. This gets UTF-8 encoded.
        /// </param>
        /// <param name="secret">
        /// The secret used in the creation of <paramref name="encoded"/>. UTF-8 encoded to create the byte-buffer actually used in the verification.
        /// May be null for no secret. <see cref="string"/>.<see cref="string.Empty"/> is treated as null.
        /// </param>
        /// <param name="secureArrayCall">
        /// The methods that get called to secure arrays. A null value defaults to <see cref="SecureArray"/>.<see cref="SecureArray.DefaultCall"/>.
        /// </param>
        /// <returns>
        /// True on success; false otherwise.
        /// </returns>
        public static bool Verify(
            string encoded,
            string password,
            string secret,
            SecureArrayCall secureArrayCall = null)
        {
            var secretBuf = string.IsNullOrEmpty(secret)
                                ? null
                                : SecureArray <byte> .Best(Encoding.UTF8.GetByteCount(secret), secureArrayCall);

            try
            {
                if (secretBuf != null)
                {
                    Encoding.UTF8.GetBytes(secret, 0, secret.Length, secretBuf.Buffer, 0);
                }

                using var passwordBuf = SecureArray <byte> .Best(Encoding.UTF8.GetByteCount(password), secureArrayCall);

                Encoding.UTF8.GetBytes(password, 0, password.Length, passwordBuf.Buffer, 0);
                return(Verify(encoded, passwordBuf.Buffer, secretBuf?.Buffer, secureArrayCall));
            }
            finally
            {
                secretBuf?.Dispose();
            }
        }
Exemple #2
0
        private void Initialize()
        {
            using var blockHash = SecureArray <byte> .Best(PrehashSeedLength, this.config.SecureArrayCall);

            using (var initialHash = this.InitialHash())
            {
                Array.Copy(initialHash.Buffer, blockHash.Buffer, PrehashDigestLength);
            }

            InitialKat(blockHash.Buffer, this);
            this.FillFirstBlocks(blockHash.Buffer);
        }
Exemple #3
0
        private void FillFirstBlocks(byte[] blockHash)
        {
            using var blockHashBytes = SecureArray <byte> .Best(BlockSize, this.config.SecureArrayCall);

            for (int l = 0; l < this.config.Lanes; ++l)
            {
                Store32(blockHash, PrehashDigestLength, 0);
                Store32(blockHash, PrehashDigestLength + 4, l);
                Blake2BLong(blockHashBytes.Buffer, blockHash, this.config.SecureArrayCall);
                LoadBlock(this.Memory[l * this.LaneLength], blockHashBytes.Buffer);
                Store32(blockHash, PrehashDigestLength, 1);
                Blake2BLong(blockHashBytes.Buffer, blockHash, this.config.SecureArrayCall);
                LoadBlock(this.Memory[(l * this.LaneLength) + 1], blockHashBytes.Buffer);
            }
        }
        /// <summary>
        /// Does a Blake2 hash with the ability to truncate or extend the hash to any length.
        /// </summary>
        /// <param name="hash">
        /// The buffer to fill with the hash.
        /// </param>
        /// <param name="inputBuffer">
        /// What to hash.
        /// </param>
        /// <param name="secureArrayCall">
        /// The methods that get called to secure arrays. A null value defaults to <see cref="SecureArray"/>.<see cref="SecureArray.DefaultCall"/>.
        /// </param>
        private static void Blake2BLong(byte[] hash, byte[] inputBuffer, SecureArrayCall secureArrayCall)
        {
            var outputLengthBytes = new byte[4];

            using var intermediateHash = SecureArray <byte> .Best(Blake2B.OutputLength, secureArrayCall);

            var config = new Blake2BConfig
            {
                Result64ByteBuffer = intermediateHash.Buffer,
                OutputSizeInBytes  = hash.Length > 64 ? 64 : hash.Length,
            };

            Store32(outputLengthBytes, hash.Length);
            using (var blakeHash = Blake2B.Create(config, secureArrayCall))
            {
                blakeHash.Update(outputLengthBytes);
                blakeHash.Update(inputBuffer);
                blakeHash.Finish();
            }

            if (hash.Length <= intermediateHash.Buffer.Length)
            {
                Array.Copy(intermediateHash.Buffer, hash, hash.Length);
                return;
            }

            const int b2B2 = Blake2B.OutputLength / 2;

            Array.Copy(intermediateHash.Buffer, hash, b2B2);
            int pos           = b2B2;
            int lastHashIndex = hash.Length - Blake2B.OutputLength;
            var toHash        = new byte[Blake2B.OutputLength];

            while (pos < lastHashIndex)
            {
                Array.Copy(intermediateHash.Buffer, toHash, intermediateHash.Buffer.Length);
                Blake2B.ComputeHash(toHash, config, secureArrayCall);
                Array.Copy(intermediateHash.Buffer, 0, hash, pos, b2B2);
                pos += b2B2;
            }

            Array.Copy(intermediateHash.Buffer, toHash, intermediateHash.Buffer.Length);
            Blake2B.ComputeHash(toHash, config, secureArrayCall);
            Array.Copy(intermediateHash.Buffer, 0, hash, pos, hash.Length - pos);
        }
Exemple #5
0
        /// <summary>
        /// Initializes a new instance of the <see cref="Argon2"/> class.
        /// </summary>
        /// <param name="config">
        /// The configuration to use.
        /// </param>
        public Argon2(Argon2Config config)
        {
            this.config = config;
            uint memoryBlocks = (uint)config.MemoryCost;

            if (memoryBlocks < 2 * SyncPoints * config.Lanes)
            {
                memoryBlocks = 2 * SyncPoints * (uint)config.Lanes;
            }

            this.SegmentLength = (int)(memoryBlocks / (config.Lanes * SyncPoints));

            // ensure that all segments have equal length
            this.LaneLength       = this.SegmentLength * SyncPoints;
            this.MemoryBlockCount = this.LaneLength * this.config.Lanes;
            this.memory           = SecureArray <ulong> .Best(BlockSize *this.MemoryBlockCount / 8, config.SecureArrayCall);

            this.Memory = new Blocks(this.memory.Buffer, this.MemoryBlockCount);
        }
Exemple #6
0
        /// <summary>
        /// Hash the given password to a Argon2 hash string.
        /// </summary>
        /// <param name="password">
        /// The password to hash. Gets UTF-8 encoded before hashing.
        /// </param>
        /// <param name="secret">
        /// The secret to use in creating the hash. UTF-8 encoded before hashing. May be null. A
        /// <see cref="string"/>.<see cref="string.Empty"/> is treated the same as null.
        /// </param>
        /// <param name="timeCost">
        /// The time cost to use. Defaults to 3.
        /// </param>
        /// <param name="memoryCost">
        /// The memory cost to use. Defaults to 65536 (64K).
        /// </param>
        /// <param name="parallelism">
        /// The parallelism to use. Default to 1 (single threaded).
        /// </param>
        /// <param name="type">
        /// Data-dependent, data-independent, or hybrid. Defaults to hybrid
        /// (as recommended for password hashing).
        /// </param>
        /// <param name="hashLength">
        /// The length of the hash in bytes. Note, the string returned base-64
        /// encodes this with other parameters so the resulting string is
        /// significantly longer.
        /// </param>
        /// <param name="secureArrayCall">
        /// The methods that get called to secure arrays. A null value defaults to <see cref="SecureArray"/>.<see cref="SecureArray.DefaultCall"/>.
        /// </param>
        /// <returns>
        /// The Argon2 hash of the given password.
        /// </returns>
        public static string Hash(
            string password,
            string secret,
            int timeCost    = 3,
            int memoryCost  = 65536,
            int parallelism = 1,
            Argon2Type type = Argon2Type.HybridAddressing,
            int hashLength  = 32,
            SecureArrayCall secureArrayCall = null)
        {
            var secretBuf = string.IsNullOrEmpty(secret)
                                ? null
                                : SecureArray <byte> .Best(Encoding.UTF8.GetByteCount(secret), secureArrayCall);

            try
            {
                if (secretBuf != null)
                {
                    Encoding.UTF8.GetBytes(secret, 0, secret.Length, secretBuf.Buffer, 0);
                }

                using (var passwordBuf = SecureArray <byte> .Best(Encoding.UTF8.GetByteCount(password), secureArrayCall))
                {
                    Encoding.UTF8.GetBytes(password, 0, password.Length, passwordBuf.Buffer, 0);
                    return(Hash(
                               passwordBuf.Buffer,
                               secretBuf?.Buffer,
                               timeCost,
                               memoryCost,
                               parallelism,
                               type,
                               hashLength,
                               secureArrayCall));
                }
            }
            finally
            {
                secretBuf?.Dispose();
            }
        }
Exemple #7
0
        private SecureArray <byte> Final()
        {
            using var blockHashBuffer = SecureArray <ulong> .Best(BlockSize / 8, this.config.SecureArrayCall);

            var blockHash = new BlockValues(blockHashBuffer.Buffer, 0);

            blockHash.Copy(this.Memory[this.LaneLength - 1]);

            // XOR last blocks
            for (int l = 1; l < this.config.Lanes; ++l)
            {
                blockHash.Xor(this.Memory[(l * this.LaneLength) + (this.LaneLength - 1)]);
            }

            using var blockHashBytes = SecureArray <byte> .Best(BlockSize, this.config.SecureArrayCall);

            StoreBlock(blockHashBytes.Buffer, blockHash);
            var ret = SecureArray <byte> .Best(this.config.HashLength, this.config.SecureArrayCall);

            Blake2BLong(ret.Buffer, blockHashBytes.Buffer, this.config.SecureArrayCall);
            PrintTag(ret.Buffer);
            return(ret);
        }
Exemple #8
0
        private SecureArray <byte> InitialHash()
        {
            var ret = SecureArray <byte> .Best(Blake2B.OutputLength, this.config.SecureArrayCall);

            using (var blakeHash =
                       Blake2B.Create(
                           new Blake2BConfig
            {
                OutputSizeInBytes = PrehashDigestLength,
                Result64ByteBuffer = ret.Buffer,
            },
                           this.config.SecureArrayCall))
            {
                var value = new byte[4];
                Store32(value, this.config.Lanes);
                blakeHash.Update(value);
                Store32(value, this.config.HashLength);
                blakeHash.Update(value);
                Store32(value, this.config.MemoryCost);
                blakeHash.Update(value);
                Store32(value, this.config.TimeCost);
                blakeHash.Update(value);
                Store32(value, (uint)this.config.Version);
                blakeHash.Update(value);
                Store32(value, (uint)this.config.Type);
                blakeHash.Update(value);
                Store32(value, this.config.Password?.Length ?? 0);
                blakeHash.Update(value);
                if (this.config.Password != null)
                {
                    blakeHash.Update(this.config.Password);
                    if (this.config.ClearPassword)
                    {
                        SecureArray.Zero(this.config.Password);
                    }
                }

                Store32(value, this.config.Salt?.Length ?? 0);
                blakeHash.Update(value);
                if (this.config.Salt != null)
                {
                    blakeHash.Update(this.config.Salt);
                }

                Store32(value, this.config.Secret?.Length ?? 0);
                blakeHash.Update(value);
                if (this.config.Secret != null)
                {
                    blakeHash.Update(this.config.Secret);
                    if (this.config.ClearSecret)
                    {
                        SecureArray.Zero(this.config.Secret);
                    }
                }

                Store32(value, this.config.AssociatedData?.Length ?? 0);
                blakeHash.Update(value);
                if (this.config.AssociatedData != null)
                {
                    blakeHash.Update(this.config.AssociatedData);
                }

                blakeHash.Finish();
            }

            return(ret);
        }