SCrypt
Implementation of the scrypt a password-based key derivation function.
using Org.BouncyCastle.Crypto.Digests;
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Crypto.Utilities;
using Org.BouncyCastle.Math.Raw;
using Org.BouncyCastle.Utilities;
using System;
namespace Org.BouncyCastle.Crypto.Generators
{
public class SCrypt
{
public static byte[] Generate(byte[] P, byte[] S, int N, int r, int p, int dkLen)
{
if (P == null)
throw new ArgumentNullException("Passphrase P must be provided.");
if (S == null)
throw new ArgumentNullException("Salt S must be provided.");
if (N <= 1 || !IsPowerOf2(N))
throw new ArgumentException("Cost parameter N must be > 1 and a power of 2.");
if (r == 1 && N >= 65536)
throw new ArgumentException("Cost parameter N must be > 1 and < 65536.");
if (r < 1)
throw new ArgumentException("Block size r must be >= 1.");
int num = 2147483647 / (128 * r * 8);
if (p < 1 || p > num)
throw new ArgumentException("Parallelisation parameter p must be >= 1 and <= " + num.ToString() + " (based on block size r of " + r.ToString() + ")");
if (dkLen < 1)
throw new ArgumentException("Generated key length dkLen must be >= 1.");
return MFcrypt(P, S, N, r, p, dkLen);
}
private static byte[] MFcrypt(byte[] P, byte[] S, int N, int r, int p, int dkLen)
{
int num = r * 128;
byte[] array = SingleIterationPBKDF2(P, S, p * num);
uint[] array2 = null;
try {
int num2 = array.Length >> 2;
array2 = new uint[num2];
Pack.LE_To_UInt32(array, 0, array2);
int num3 = 0;
int num4 = N * r;
while (N - num3 > 2 && num4 > 1024) {
num3++;
num4 >>= 1;
}
int num5 = num >> 2;
for (int i = 0; i < num2; i += num5) {
SMix(array2, i, N, num3, r);
}
Pack.UInt32_To_LE(array2, array, 0);
return SingleIterationPBKDF2(P, array, dkLen);
} finally {
ClearAll(array, array2);
}
}
private static byte[] SingleIterationPBKDF2(byte[] P, byte[] S, int dkLen)
{
Pkcs5S2ParametersGenerator pkcs5S2ParametersGenerator = new Pkcs5S2ParametersGenerator(new Sha256Digest());
pkcs5S2ParametersGenerator.Init(P, S, 1);
return ((KeyParameter)pkcs5S2ParametersGenerator.GenerateDerivedMacParameters(dkLen * 8)).GetKey();
}
private static void SMix(uint[] B, int BOff, int N, int d, int r)
{
int num = Integers.NumberOfTrailingZeros(N);
int num2 = N >> d;
int num3 = 1 << d;
int num4 = num2 - 1;
int num5 = num - d;
int num6 = r * 32;
uint[] array = new uint[num6];
uint[][] array2 = new uint[num3][];
try {
Span<uint> span = B.AsSpan(BOff, num6);
for (int i = 0; i < num3; i++) {
uint[] array3 = array2[i] = new uint[num2 * num6];
Nat.Copy(num6, span, array3);
int num7 = 0;
for (int j = 1; j < num2; j++) {
BlockMix(array3.AsSpan(num7, num6), array3.AsSpan(num7 + num6));
num7 += num6;
}
Span<uint> span2 = array3.AsSpan();
int num8 = num6;
int length = span2.Length;
int num9 = length - num8;
BlockMix(span2.Slice(num9, length - num9), span);
}
uint num10 = (uint)(N - 1);
for (int k = 0; k < N; k++) {
int num11 = (int)(span[num6 - 16] & num10);
uint[] array4 = array2[num11 >> num5];
int start = (num11 & num4) * num6;
Nat.Xor(num6, array4.AsSpan(start), span, array);
BlockMix(array, span);
}
} finally {
Array[] arrays = array2;
ClearAll(arrays);
Clear(array);
}
}
private static void BlockMix(Span<uint> B, Span<uint> Y)
{
int length = B.Length;
int num = length >> 1;
int length2 = B.Length;
int num2 = length2 - 16;
Span<uint> span = B.Slice(num2, length2 - num2);
for (int i = 0; i < length; i += 32) {
num2 = i;
Span<uint> span2 = B.Slice(num2, B.Length - num2);
num2 = i >> 1;
Span<uint> span3 = Y.Slice(num2, Y.Length - num2);
Nat512.Xor(span, span2, span3);
Salsa20Engine.SalsaCore(8, span3, span3);
Span<uint> span4 = span2.Slice(16, span2.Length - 16);
num2 = num;
span = span3.Slice(num2, span3.Length - num2);
Nat512.Xor(span3, span4, span);
Salsa20Engine.SalsaCore(8, span, span);
}
}
private static void Clear(Array array)
{
if (array != null)
Array.Clear(array, 0, array.Length);
}
private static void ClearAll(params Array[] arrays)
{
for (int i = 0; i < arrays.Length; i++) {
Clear(arrays[i]);
}
}
private static bool IsPowerOf2(int x)
{
return (x & (x - 1)) == 0;
}
}
}