107 lines
2.5 KiB
C#
107 lines
2.5 KiB
C#
using System;
|
|
using System.Security.Cryptography;
|
|
|
|
namespace Intelix.Helper.Hashing;
|
|
|
|
public class PBKDF2
|
|
{
|
|
private readonly int _blockSize;
|
|
|
|
private uint _blockIndex = 1u;
|
|
|
|
private byte[] _bufferBytes;
|
|
|
|
private int _bufferStartIndex;
|
|
|
|
private int _bufferEndIndex;
|
|
|
|
private HMAC Algorithm { get; }
|
|
|
|
private byte[] Salt { get; }
|
|
|
|
private int IterationCount { get; }
|
|
|
|
public PBKDF2(HMAC algorithm, byte[] password, byte[] salt, int iterations)
|
|
{
|
|
Algorithm = algorithm ?? throw new ArgumentNullException("algorithm", "Algorithm cannot be null.");
|
|
Algorithm.Key = password ?? throw new ArgumentNullException("password", "Password cannot be null.");
|
|
Salt = salt ?? throw new ArgumentNullException("salt", "Salt cannot be null.");
|
|
IterationCount = iterations;
|
|
_blockSize = Algorithm.HashSize / 8;
|
|
_bufferBytes = new byte[_blockSize];
|
|
}
|
|
|
|
public byte[] GetBytes(int count)
|
|
{
|
|
byte[] array = new byte[count];
|
|
int i = 0;
|
|
int num = _bufferEndIndex - _bufferStartIndex;
|
|
if (num > 0)
|
|
{
|
|
if (count < num)
|
|
{
|
|
Buffer.BlockCopy(_bufferBytes, _bufferStartIndex, array, 0, count);
|
|
_bufferStartIndex += count;
|
|
return array;
|
|
}
|
|
Buffer.BlockCopy(_bufferBytes, _bufferStartIndex, array, 0, num);
|
|
_bufferStartIndex = (_bufferEndIndex = 0);
|
|
i += num;
|
|
}
|
|
for (; i < count; i += _blockSize)
|
|
{
|
|
int num2 = count - i;
|
|
_bufferBytes = Func();
|
|
if (num2 > _blockSize)
|
|
{
|
|
Buffer.BlockCopy(_bufferBytes, 0, array, i, _blockSize);
|
|
continue;
|
|
}
|
|
Buffer.BlockCopy(_bufferBytes, 0, array, i, num2);
|
|
_bufferStartIndex = num2;
|
|
_bufferEndIndex = _blockSize;
|
|
return array;
|
|
}
|
|
return array;
|
|
}
|
|
|
|
private byte[] Func()
|
|
{
|
|
byte[] array = new byte[Salt.Length + 4];
|
|
Buffer.BlockCopy(Salt, 0, array, 0, Salt.Length);
|
|
Buffer.BlockCopy(GetBytesFromInt(_blockIndex), 0, array, Salt.Length, 4);
|
|
byte[] array2 = Algorithm.ComputeHash(array);
|
|
byte[] array3 = array2;
|
|
for (int i = 2; i <= IterationCount; i++)
|
|
{
|
|
array2 = Algorithm.ComputeHash(array2, 0, array2.Length);
|
|
for (int j = 0; j < _blockSize; j++)
|
|
{
|
|
array3[j] ^= array2[j];
|
|
}
|
|
}
|
|
if (_blockIndex == uint.MaxValue)
|
|
{
|
|
throw new InvalidOperationException("Derived key too long.");
|
|
}
|
|
_blockIndex++;
|
|
return array3;
|
|
}
|
|
|
|
private static byte[] GetBytesFromInt(uint i)
|
|
{
|
|
byte[] bytes = BitConverter.GetBytes(i);
|
|
if (!BitConverter.IsLittleEndian)
|
|
{
|
|
return bytes;
|
|
}
|
|
return new byte[4]
|
|
{
|
|
bytes[3],
|
|
bytes[2],
|
|
bytes[1],
|
|
bytes[0]
|
|
};
|
|
}
|
|
}
|