Password Stretch using Sha3
Make passwords more secure by increasing the time and space taken to test each password.
Example Code: var b1 = new byte[0]; var b2 = new byte[0]; var salt = new byte[0]; using (var db = new PasswordStretch("1234567890".ToSecureString(), 16)) { b1 = (byte[]) db.GetBytes(0, 64).Clone(); salt = db.Salt; } using (var db = new PasswordStretch("1234567891".ToSecureString(), salt)) { b2 = (byte[]) db.GetBytes(0, 64).Clone(); }
using System; using System.Security; using System.Security.Cryptography; public class PasswordStretch : IDisposable { private const int PacketCount = 4; private readonly byte[] _buffer; private readonly byte[] _salt; private readonly SHA3Managed _sha; public PasswordStretch(SecureString password) : this(password, 0, 1000) { } public PasswordStretch(SecureString password, int saltSize) : this(password, saltSize, 1000) { } public PasswordStretch(SecureString password, int saltSize, int iterations) { Iterations = iterations; var temp = new byte[0]; if (saltSize != 0) { var data = new byte[saltSize]; new RNGCryptoServiceProvider().GetBytes(data); _salt = data; _sha = new SHA3Managed(512); _buffer = new byte[_sha.HashLength * PacketCount]; _sha.TransformBlock(_salt, 0, _salt.Length, null, 0); var pwb = password.GetBytes(); _sha.TransformBlock(pwb, 0, pwb.Length, null, 0); _sha.Finalize(); temp = _sha.HashValue; } else { _sha = new SHA3Managed(512); _buffer = new byte[_sha.HashLength * PacketCount]; temp = _sha.ComputeHash(password.GetBytes()); } for (var i = 0; i < PacketCount; i++) { for (var j = 0; j < Iterations; j++) temp = _sha.ComputeHash(temp); Buffer.BlockCopy(temp, 0, _buffer, i * _sha.HashLength, _sha.HashLength); } } public PasswordStretch(SecureString password, byte[] salt) : this(password, salt, 1000) { } public PasswordStretch(SecureString password, byte[] salt, int iterations = 1000) { Iterations = iterations; _sha = new SHA3Managed(512); _buffer = new byte[_sha.HashLength * PacketCount]; _sha.TransformBlock(salt, 0, salt.Length, null, 0); var pwb = password.GetBytes(); _sha.TransformBlock(pwb, 0, pwb.Length, null, 0); _sha.Finalize(); var temp = _sha.HashValue; for (var i = 0; i < PacketCount; i++) { for (var j = 0; j < Iterations; j++) temp = _sha.ComputeHash(temp); Buffer.BlockCopy(temp, 0, _buffer, i * _sha.HashLength, _sha.HashLength); } } public byte[] Salt { get { if (_salt != null) return (byte[]) _salt.Clone(); return default; } } public int Iterations { get; } = 1000; public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } ~PasswordStretch() { Dispose(); } public byte[] GetBytes(int offset, int psize) { if (offset + psize > _buffer.Length) throw new Exception("Offset and Size Exceed Buffer Length."); var passpart = new byte[psize]; Buffer.BlockCopy(_buffer, offset, passpart, 0, passpart.Length); return passpart; } private void Dispose(bool disposing) { if (!disposing) return; if (_sha != null) _sha.Dispose(); if (_buffer != null) Array.Clear(_buffer, 0, _buffer.Length); if (_salt == null) return; Array.Clear(_salt, 0, _salt.Length); } }