Created
November 23, 2012 13:51
-
-
Save dchest/4135716 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
<!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