/// <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(); } }
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); }
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); }
/// <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); }
/// <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(); } }
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); }
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); }