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