Skip to content

Instantly share code, notes, and snippets.

@prajaybasu
Created July 7, 2016 23:37
Show Gist options
  • Save prajaybasu/0bc1c9e7471ba9080042d3841743d142 to your computer and use it in GitHub Desktop.
Save prajaybasu/0bc1c9e7471ba9080042d3841743d142 to your computer and use it in GitHub Desktop.
using System;
using System.Security.Cryptography;
using System.Text;
using System.Runtime.InteropServices;
using System.Numerics;
using System.Runtime.CompilerServices;
namespace Scrypt
{
public static class Scrypt
{
public static byte[] CryptoScrypt(byte[] password, byte[] salt, int N, int r, int p, int dkLen = 32)
{
var B = new byte[128 * r * p + 63];
var XY = new byte[256 * r + 63];
var V = new byte[128 * r * N + 63];
var buf = new byte[dkLen];
var mac = new HMACSHA256(password);
/* 1: (B_0 ... B_{p-1}) <-- PBKDF2(P, S, 1, p * MFLen) */
PBKDF2_SHA256(mac, salt, salt.Length, 1, B, p * 128 * r);
/* 2: for i = 0 to p - 1 do */
for (var i = 0; i < p; i++)
{
/* 3: B_i <-- MF(B_i, N) */
SMix(B,i * 128 * r, r, N, V, XY);
}
/* 5: DK <-- PBKDF2(P, B, 1, dkLen) */
PBKDF2_SHA256(mac, B, p * 128 * r, 1, buf, buf.Length);
return buf;
}
public static void SMix(byte[] B, int Bi, int r, int N, byte[] V, byte[] XY)
{
int Xi = 0;
int Yi = 128 * r;
int i;
Array.Copy(B, Bi, XY, Xi, 128 * r);
for (i = 0; i < N; i++)
{
Array.Copy(XY, Xi, V, i * (128 * r), 128 * r);
BlockMix(XY, Xi, Yi, r);
}
for (i = 0; i < N; i++)
{
int j = Integerify(XY, Xi, r) & (N - 1);
BlockXOR(V, j * (128 * r), XY, Xi, 128 * r);
BlockMix(XY, Xi, Yi, r);
}
Array.Copy(XY, Xi, B, Bi, 128 * r);
}
public static void BlockMix(byte[] BY, int Bi, int Yi, int r)
{
byte[] X = new byte[64];
int i;
Array.Copy(BY, Bi + (2 * r - 1) * 64, X, 0, 64);
for (i = 0; i < 2 * r; i++)
{
Array.Copy(BY, i * 64, X, 0, 64);
Salsa20_8(X);
Array.Copy(X, 0, BY, Yi + (i * 64), 64);
}
for (i = 0; i < r; i++)
{
Array.Copy(BY, Yi + (i * 2) * 64, BY, Bi + (i * 64), 64);
}
for (i = 0; i < r; i++)
{
Array.Copy(BY, Yi + (i * 2 + 1) * 64, BY, Bi + (i + r) * 64, 64);
}
}
public static int Integerify(byte[] B, int Bi, int r)
{
int n;
Bi += (2 * r - 1) * 64;
n = (B[Bi + 0] & 0xff) << 0;
n |= (B[Bi + 1] & 0xff) << 8;
n |= (B[Bi + 2] & 0xff) << 16;
n |= (B[Bi + 3] & 0xff) << 24;
return n;
}
public static void BlockXOR(byte[] S, int Si, byte[] D, int Di, int len)
{
for (int i = 0; i < len; i++)
{
D[Di + i] ^= S[Si + i];
}
}
public static void Salsa20_8(byte[] B)
{
int[] B32 = new int[16];
int[] x = new int[16];
int i;
for (i = 0; i < 16; i++)
{
B32[i] = (B[i * 4 + 0] & 0xff) << 0;
B32[i] |= (B[i * 4 + 1] & 0xff) << 8;
B32[i] |= (B[i * 4 + 2] & 0xff) << 16;
B32[i] |= (B[i * 4 + 3] & 0xff) << 24;
}
Array.Copy(B32, 0, x, 0, 16);
for (i = 8; i > 0; i -= 2)
{
x[4] ^= R(x[0] + x[12], 7); x[8] ^= R(x[4] + x[0], 9);
x[12] ^= R(x[8] + x[4], 13); x[0] ^= R(x[12] + x[8], 18);
x[9] ^= R(x[5] + x[1], 7); x[13] ^= R(x[9] + x[5], 9);
x[1] ^= R(x[13] + x[9], 13); x[5] ^= R(x[1] + x[13], 18);
x[14] ^= R(x[10] + x[6], 7); x[2] ^= R(x[14] + x[10], 9);
x[6] ^= R(x[2] + x[14], 13); x[10] ^= R(x[6] + x[2], 18);
x[3] ^= R(x[15] + x[11], 7); x[7] ^= R(x[3] + x[15], 9);
x[11] ^= R(x[7] + x[3], 13); x[15] ^= R(x[11] + x[7], 18);
x[1] ^= R(x[0] + x[3], 7); x[2] ^= R(x[1] + x[0], 9);
x[3] ^= R(x[2] + x[1], 13); x[0] ^= R(x[3] + x[2], 18);
x[6] ^= R(x[5] + x[4], 7); x[7] ^= R(x[6] + x[5], 9);
x[4] ^= R(x[7] + x[6], 13); x[5] ^= R(x[4] + x[7], 18);
x[11] ^= R(x[10] + x[9], 7); x[8] ^= R(x[11] + x[10], 9);
x[9] ^= R(x[8] + x[11], 13); x[10] ^= R(x[9] + x[8], 18);
x[12] ^= R(x[15] + x[14], 7); x[13] ^= R(x[12] + x[15], 9);
x[14] ^= R(x[13] + x[12], 13); x[15] ^= R(x[14] + x[13], 18);
}
for (i = 0; i < 16; ++i) B32[i] = x[i] + B32[i];
for (i = 0; i < 16; i++)
{
B[i * 4 + 0] = (byte)(B32[i] >> 0 & 0xff);
B[i * 4 + 1] = (byte)(B32[i] >> 8 & 0xff);
B[i * 4 + 2] = (byte)(B32[i] >> 16 & 0xff);
B[i * 4 + 3] = (byte)(B32[i] >> 24 & 0xff);
}
}
public static int R(int a, int b)
{
return (a << b) | a >> (32 - b);
}
/// <summary>
/// Compute PBKDF2 using HMAC-SHA256 as the PRF, and write the output to derivedKey.
/// TODO : Use corefx implementation once SHA256 is implemented. See https://github.com/dotnet/corefx/issues/9438
/// </summary>
static void PBKDF2_SHA256(HMACSHA256 mac, byte[] salt, int saltLength, long iterationCount, byte[] derivedKey, int derivedKeyLength)
{
if (derivedKeyLength > (Math.Pow(2, 32) - 1) * 32)
{
throw new ArgumentException("Requested key length too long");
}
var U = new byte[32];
var T = new byte[32];
var saltBuffer = new byte[saltLength + 4];
var blockCount = (int)Math.Ceiling((double)derivedKeyLength / 32);
var r = derivedKeyLength - (blockCount - 1) * 32;
Buffer.BlockCopy(salt, 0, saltBuffer, 0, saltLength);
for (int i = 1; i <= blockCount; i++)
{
saltBuffer[saltLength + 0] = (byte)(i >> 24);
saltBuffer[saltLength + 1] = (byte)(i >> 16);
saltBuffer[saltLength + 2] = (byte)(i >> 8);
saltBuffer[saltLength + 3] = (byte)(i);
mac.Initialize();
using (var incrementalHasher = IncrementalHash.CreateHMAC(HashAlgorithmName.SHA256, mac.Key))
{
incrementalHasher.AppendData(saltBuffer, 0, saltBuffer.Length);
Buffer.BlockCopy(incrementalHasher.GetHashAndReset(), 0, U, 0, U.Length);
Buffer.BlockCopy(U, 0, T, 0, 32);
for (long j = 1; j < iterationCount; j++)
{
incrementalHasher.AppendData(U, 0, U.Length);
Buffer.BlockCopy(incrementalHasher.GetHashAndReset(), 0, U, 0, U.Length);
for (int k = 0; k < 32; k++)
{
T[k] ^= U[k];
}
}
}
Buffer.BlockCopy(T, 0, derivedKey, (i - 1) * 32, (i == blockCount ? r : 32));
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment