PasswordStretch.cs

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