-
-
Save prajaybasu/0bc1c9e7471ba9080042d3841743d142 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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