Skip to content

Instantly share code, notes, and snippets.

@dchest
Created November 23, 2012 13:51
Show Gist options
  • Save dchest/4135716 to your computer and use it in GitHub Desktop.
Save dchest/4135716 to your computer and use it in GitHub Desktop.
<!doctype html>
<html>
<script>
var Crypto;
(function (Crypto) {
var SHA256 = (function () {
function SHA256() {
this.blockLength = 64;
this.digestLength = 32;
this.h = new Array(8);
this.x = new Array(64);
this.tmp = new Array(64);
this.reset();
}
SHA256.K = [
1116352408,
1899447441,
3049323471,
3921009573,
961987163,
1508970993,
2453635748,
2870763221,
3624381080,
310598401,
607225278,
1426881987,
1925078388,
2162078206,
2614888103,
3248222580,
3835390401,
4022224774,
264347078,
604807628,
770255983,
1249150122,
1555081692,
1996064986,
2554220882,
2821834349,
2952996808,
3210313671,
3336571891,
3584528711,
113926993,
338241895,
666307205,
773529912,
1294757372,
1396182291,
1695183700,
1986661051,
2177026350,
2456956037,
2730485921,
2820302411,
3259730800,
3345764771,
3516065817,
3600352804,
4094571909,
275423344,
430227734,
506948616,
659060556,
883997877,
958139571,
1322822218,
1537002063,
1747873779,
1955562222,
2024104815,
2227730452,
2361852424,
2428436474,
2756734187,
3204031479,
3329325298
];
SHA256.prototype.reset = function () {
this.h[0] = 1779033703;
this.h[1] = 3144134277;
this.h[2] = 1013904242;
this.h[3] = 2773480762;
this.h[4] = 1359893119;
this.h[5] = 2600822924;
this.h[6] = 528734635;
this.h[7] = 1541459225;
this.nx = 0;
this.len = 0;
};
SHA256.prototype.blocks = function (p, offset, length) {
var w = this.tmp;
var h0 = this.h[0];
var h1 = this.h[1];
var h2 = this.h[2];
var h3 = this.h[3];
var h4 = this.h[4];
var h5 = this.h[5];
var h6 = this.h[6];
var h7 = this.h[7];
var h8 = this.h[8];
var n = 0;
while(length >= 64) {
var a = h0;
var b = h1;
var c = h2;
var d = h3;
var e = h4;
var f = h5;
var g = h6;
var h = h7;
var u, t1, t2;
for(var i = 0; i < 16; i++) {
var j = offset + i * 4;
w[i] = ((p[j] & 255) << 24) | ((p[j + 1] & 255) << 16) | ((p[j + 2] & 255) << 8) | (p[j + 3] & 255);
}
for(var i = 16; i < 64; i++) {
u = w[i - 2];
t1 = ((u >>> 17) | (u << (32 - 17))) ^ ((u >>> 19) | (u << (32 - 19))) ^ (u >>> 10);
u = w[i - 15];
t2 = ((u >>> 7) | (u << (32 - 7))) ^ ((u >>> 18) | (u << (32 - 18))) ^ (u >>> 3);
w[i] = (t1 + w[i - 7] + t2 + w[i - 16]) | 0;
}
for(var i = 0; i < 64; i++) {
t1 = (((e >>> 6) | (e << (32 - 6))) ^ ((e >>> 11) | (e << (32 - 11))) ^ ((e >>> 25) | (e << (32 - 25)))) + ((e & f) ^ (~e & g)) + h + SHA256.K[i] + w[i];
t2 = ((((a >>> 2) | (a << (32 - 2))) ^ ((a >>> 13) | (a << (32 - 13))) ^ ((a >>> 22) | (a << (32 - 22)))) + ((a & b) ^ (a & c) ^ (b & c)));
h = g;
g = f;
f = e;
e = (d + t1) | 0;
d = c;
c = b;
b = a;
a = (t1 + t2) | 0;
}
h0 = (h0 + a) | 0;
h1 = (h1 + b) | 0;
h2 = (h2 + c) | 0;
h3 = (h3 + d) | 0;
h4 = (h4 + e) | 0;
h5 = (h5 + f) | 0;
h6 = (h6 + g) | 0;
h7 = (h7 + h) | 0;
n += 64;
offset += 64;
length -= 64;
}
this.h[0] = h0;
this.h[1] = h1;
this.h[2] = h2;
this.h[3] = h3;
this.h[4] = h4;
this.h[5] = h5;
this.h[6] = h6;
this.h[7] = h7;
return n;
};
SHA256.prototype.update = function (p, offset, length) {
var n;
this.len += length;
if(this.nx > 0) {
if(length > 64 - this.nx) {
n = 64 - this.nx;
} else {
n = length;
}
for(var i = 0; i < n; i++) {
this.x[this.nx + i] = p[offset + i];
}
this.nx += n;
if(this.nx == 64) {
this.blocks(this.x, 0, 64);
this.nx = 0;
}
offset += n;
length -= n;
}
n = this.blocks(p, offset, length);
offset += n;
length -= n;
if(length > 0) {
for(i = 0; i < length; i++) {
this.x[this.nx + i] = p[offset + i];
}
this.nx += length;
}
};
SHA256.prototype.digest = function (out) {
if (typeof out === "undefined") { out = null; }
var len = this.len;
var tmp = this.tmp;
tmp[0] = 128;
for(var i = 1; i < 64; i++) {
tmp[i] = 0;
}
if(len % 64 < 56) {
this.update(tmp, 0, 56 - len % 64);
} else {
this.update(tmp, 0, 64 + 56 - len % 64);
}
len *= 8;
tmp[0] = 0;
tmp[1] = 0;
tmp[2] = 0;
tmp[3] = 0;
tmp[4] = (len >>> 24) & 255;
tmp[5] = (len >>> 16) & 255;
tmp[6] = (len >>> 8) & 255;
tmp[7] = (len >>> 0) & 255;
this.update(tmp, 0, 8);
if(!out) {
out = new Array(32);
}
for(var i = 0; i < 8; i++) {
var h = this.h[i];
out[i * 4 + 0] = (h >>> 24) & 255;
out[i * 4 + 1] = (h >>> 16) & 255;
out[i * 4 + 2] = (h >>> 8) & 255;
out[i * 4 + 3] = (h >>> 0) & 255;
}
return out;
};
return SHA256;
})();
Crypto.SHA256 = SHA256;
var HMAC = (function () {
function HMAC(hashfn, key) {
this.inner = hashfn();
this.outer = hashfn();
this.blockLength = this.inner.blockLength;
this.digestLength = this.inner.digestLength;
this.tmp = new Array(this.blockLength);
if(key.length > this.blockLength) {
this.outer.update(key, 0, key.length);
this.key = this.outer.digest();
} else {
this.key = key;
}
this.reset();
}
HMAC.prototype.reset = function () {
this.inner.reset();
var i = 0;
for(; i < this.key.length; i++) {
this.tmp[i] = 54 ^ (this.key[i] & 255);
}
for(; i < this.blockLength; i++) {
this.tmp[i] = 54;
}
this.inner.update(this.tmp, 0, this.blockLength);
};
HMAC.prototype.update = function (p, offset, length) {
this.inner.update(p, offset, length);
};
HMAC.prototype.digest = function (out) {
var i = 0;
for(; i < this.key.length; i++) {
this.tmp[i] = 92 ^ (this.key[i] & 255);
}
for(; i < this.blockLength; i++) {
this.tmp[i] = 92;
}
this.outer.reset();
this.outer.update(this.tmp, 0, this.blockLength);
this.inner.digest(this.tmp);
this.outer.update(this.tmp, 0, this.digestLength);
return this.outer.digest(out);
};
return HMAC;
})();
Crypto.HMAC = HMAC;
var PBKDF2OneIter = (function () {
function PBKDF2OneIter(hashfn, password, salt) {
this.hmac = new HMAC(hashfn, password);
this.salt = salt;
this.counter = [
0,
0,
0,
1
];
}
PBKDF2OneIter.prototype.incrementCounter = function () {
for(var i = 3; i >= 0; i--) {
this.counter[i]++;
if(this.counter[i] > 255) {
this.counter[i] = 0;
} else {
return;
}
}
};
PBKDF2OneIter.prototype.eachBlock = function (callback) {
var block = new Array(this.hmac.digestLength);
do {
this.hmac.reset();
this.hmac.update(this.salt, 0, this.salt.length);
this.hmac.update(this.counter, 0, 4);
this.incrementCounter();
this.hmac.digest(block);
}while(callback(block))
};
PBKDF2OneIter.prototype.getNextBlock = function (out) {
this.hmac.reset();
this.hmac.update(this.salt, 0, this.salt.length);
this.hmac.update(this.counter, 0, 4);
this.incrementCounter();
return this.hmac.digest(out);
};
return PBKDF2OneIter;
})();
Crypto.PBKDF2OneIter = PBKDF2OneIter;
function PBKDF2_HMAC_SHA256_OneIter(password, salt, dkLen) {
var kdf = new PBKDF2OneIter(function () {
return new SHA256();
}, password, salt);
var block = new Array(32);
var out = new Array(dkLen);
var oi = 0;
while(dkLen >= 32) {
kdf.getNextBlock(block);
for(var i = 0; i < 32; i++) {
out[oi++] = block[i];
}
dkLen -= 32;
}
if(dkLen > 0) {
kdf.getNextBlock(block);
for(var i = 0; i < dkLen; i++) {
out[oi++] = block[i];
}
}
return out;
}
Crypto.PBKDF2_HMAC_SHA256_OneIter = PBKDF2_HMAC_SHA256_OneIter;
(function (Scrypt) {
function salsa20_8_xor(x, xi, bin, bout) {
var j0 = x[xi++] ^ x[bin++];
var j1 = x[xi++] ^ x[bin++];
var j2 = x[xi++] ^ x[bin++];
var j3 = x[xi++] ^ x[bin++];
var j4 = x[xi++] ^ x[bin++];
var j5 = x[xi++] ^ x[bin++];
var j6 = x[xi++] ^ x[bin++];
var j7 = x[xi++] ^ x[bin++];
var j8 = x[xi++] ^ x[bin++];
var j9 = x[xi++] ^ x[bin++];
var j10 = x[xi++] ^ x[bin++];
var j11 = x[xi++] ^ x[bin++];
var j12 = x[xi++] ^ x[bin++];
var j13 = x[xi++] ^ x[bin++];
var j14 = x[xi++] ^ x[bin++];
var j15 = x[xi++] ^ x[bin++];
var x0 = j0, x1 = j1, x2 = j2, x3 = j3, x4 = j4, x5 = j5, x6 = j6, x7 = j7, x8 = j8, x9 = j9, x10 = j10, x11 = j11, x12 = j12, x13 = j13, x14 = j14, x15 = j15;
var u;
for(var i = 0; i < 8; i += 2) {
u = x0 + x12;
x4 ^= (u << 7) | (u >>> (32 - 7));
u = x4 + x0;
x8 ^= (u << 9) | (u >>> (32 - 9));
u = x8 + x4;
x12 ^= (u << 13) | (u >>> (32 - 13));
u = x12 + x8;
x0 ^= (u << 18) | (u >>> (32 - 18));
u = x5 + x1;
x9 ^= (u << 7) | (u >>> (32 - 7));
u = x9 + x5;
x13 ^= (u << 9) | (u >>> (32 - 9));
u = x13 + x9;
x1 ^= (u << 13) | (u >>> (32 - 13));
u = x1 + x13;
x5 ^= (u << 18) | (u >>> (32 - 18));
u = x10 + x6;
x14 ^= (u << 7) | (u >>> (32 - 7));
u = x14 + x10;
x2 ^= (u << 9) | (u >>> (32 - 9));
u = x2 + x14;
x6 ^= (u << 13) | (u >>> (32 - 13));
u = x6 + x2;
x10 ^= (u << 18) | (u >>> (32 - 18));
u = x15 + x11;
x3 ^= (u << 7) | (u >>> (32 - 7));
u = x3 + x15;
x7 ^= (u << 9) | (u >>> (32 - 9));
u = x7 + x3;
x11 ^= (u << 13) | (u >>> (32 - 13));
u = x11 + x7;
x15 ^= (u << 18) | (u >>> (32 - 18));
u = x0 + x3;
x1 ^= (u << 7) | (u >>> (32 - 7));
u = x1 + x0;
x2 ^= (u << 9) | (u >>> (32 - 9));
u = x2 + x1;
x3 ^= (u << 13) | (u >>> (32 - 13));
u = x3 + x2;
x0 ^= (u << 18) | (u >>> (32 - 18));
u = x5 + x4;
x6 ^= (u << 7) | (u >>> (32 - 7));
u = x6 + x5;
x7 ^= (u << 9) | (u >>> (32 - 9));
u = x7 + x6;
x4 ^= (u << 13) | (u >>> (32 - 13));
u = x4 + x7;
x5 ^= (u << 18) | (u >>> (32 - 18));
u = x10 + x9;
x11 ^= (u << 7) | (u >>> (32 - 7));
u = x11 + x10;
x8 ^= (u << 9) | (u >>> (32 - 9));
u = x8 + x11;
x9 ^= (u << 13) | (u >>> (32 - 13));
u = x9 + x8;
x10 ^= (u << 18) | (u >>> (32 - 18));
u = x15 + x14;
x12 ^= (u << 7) | (u >>> (32 - 7));
u = x12 + x15;
x13 ^= (u << 9) | (u >>> (32 - 9));
u = x13 + x12;
x14 ^= (u << 13) | (u >>> (32 - 13));
u = x14 + x13;
x15 ^= (u << 18) | (u >>> (32 - 18));
}
xi -= 16;
x[bout++] = x[xi++] = (x0 + j0) | 0;
x[bout++] = x[xi++] = (x1 + j1) | 0;
x[bout++] = x[xi++] = (x2 + j2) | 0;
x[bout++] = x[xi++] = (x3 + j3) | 0;
x[bout++] = x[xi++] = (x4 + j4) | 0;
x[bout++] = x[xi++] = (x5 + j5) | 0;
x[bout++] = x[xi++] = (x6 + j6) | 0;
x[bout++] = x[xi++] = (x7 + j7) | 0;
x[bout++] = x[xi++] = (x8 + j8) | 0;
x[bout++] = x[xi++] = (x9 + j9) | 0;
x[bout++] = x[xi++] = (x10 + j10) | 0;
x[bout++] = x[xi++] = (x11 + j11) | 0;
x[bout++] = x[xi++] = (x12 + j12) | 0;
x[bout++] = x[xi++] = (x13 + j13) | 0;
x[bout++] = x[xi++] = (x14 + j14) | 0;
x[bout++] = x[xi++] = (x15 + j15) | 0;
}
function blockCopy(dst, di, src, si, len) {
while(len--) {
dst[di++] = src[si++];
}
}
function blockXOR(dst, di, src, si, len) {
while(len--) {
dst[di++] ^= src[si++];
}
}
function blockMix(B, bin, bout, x, r) {
blockCopy(B, x, B, bin + (2 * r - 1) * 16, 16);
for(var i = 0; i < 2 * r; i += 2) {
salsa20_8_xor(B, x, bin + i * 16, bout + i * 8);
salsa20_8_xor(B, x, bin + i * 16 + 16, bout + i * 8 + r * 16);
}
}
function integerify(B, bi, r) {
return (B[bi + (2 * r - 1) * 16]);
}
function smix(B, bi, r, N, V, XY) {
var xi = 0;
var yi = 32 * r;
var zi = 64 * r;
var i, j;
for(i = 0; i < 32 * r; i++) {
j = bi + i * 4;
XY[xi + i] = ((B[j + 3] & 255) << 24) | ((B[j + 2] & 255) << 16) | ((B[j + 1] & 255) << 8) | (B[j] & 255);
}
for(i = 0; i < N; i += 2) {
blockCopy(V, i * (32 * r), XY, xi, 32 * r);
blockMix(XY, xi, yi, zi, r);
blockCopy(V, (i + 1) * (32 * r), XY, yi, 32 * r);
blockMix(XY, yi, xi, zi, r);
}
for(i = 0; i < N; i += 2) {
j = integerify(XY, xi, r) & (N - 1);
blockXOR(XY, xi, V, j * (32 * r), 32 * r);
blockMix(XY, xi, yi, zi, r);
j = integerify(XY, yi, r) & (N - 1);
blockXOR(XY, yi, V, j * (32 * r), 32 * r);
blockMix(XY, yi, xi, zi, r);
}
for(i = 0; i < 32 * r; i++) {
j = XY[xi + i];
B[i * 4 + 0] = (j >>> 0) & 255;
B[i * 4 + 1] = (j >>> 8) & 255;
B[i * 4 + 2] = (j >>> 16) & 255;
B[i * 4 + 3] = (j >>> 24) & 255;
}
}
function generateKey(password, salt, N, r, p, dkLen) {
if(N <= 1 || N & (N - 1)) {
throw new Error("scrypt: N must be > 1 and a power of 2");
}
var MAX_INT = (1 << 31) >>> 0;
if(r * p >= 1 << 30 || r > MAX_INT / 128 / p || r > MAX_INT / 256 || N > MAX_INT / 128 / r) {
throw new Error("scrypt: parameters are too large");
}
var XY = new Int32Array(64 * r + 16);
var V = new Int32Array(32 * N * r);
var B = PBKDF2_HMAC_SHA256_OneIter(password, salt, p * 128 * r);
for(var i = 0; i < p; i++) {
smix(B, i * 128 * r, r, N, V, XY);
}
return PBKDF2_HMAC_SHA256_OneIter(password, B, dkLen);
}
Scrypt.generateKey = generateKey;
})(Crypto.Scrypt || (Crypto.Scrypt = {}));
var Scrypt = Crypto.Scrypt;
function getRandomBytes(length) {
var buf = null;
var tmp;
if(typeof window != "undefined" && window.hasOwnProperty("crypto")) {
buf = new Uint8Array(new ArrayBuffer(length));
window.crypto.getRandomValues(buf);
} else {
if(typeof require != "undefined" && (tmp = require('crypto'))) {
buf = tmp.randomBytes(length);
} else {
throw new Error("no random number generator");
}
}
if(buf == null || buf.length != length) {
throw new Error("no random bytes generated");
}
var out = new Array(length);
for(var i = 0; i < length; i++) {
out[i] = buf[i];
buf[i] = 0;
}
return out;
}
Crypto.getRandomBytes = getRandomBytes;
})(Crypto || (Crypto = {}));
function testMe() {
var t1=(new Date()).getTime();
//console.log('hashing...');
var res = Crypto.Scrypt.generateKey([1,2,3], [4,5,6], 1<<14, 8, 1, 14);
var t2 = ((new Date()).getTime()-t1);
document.querySelector('#out').innerHTML = 'scrypt: <b>'+t2+' ms</b> ' + res.join(",");
}
</script>
<button onclick="testMe()">Test</button>
<div id=out></div>
<p><small>
Notes: PBKDF2_HMAC_SHA256_OneIter outputs Array. XY, V use typed arrays.
</small>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment