Skip to content

Instantly share code, notes, and snippets.

@chjj
Last active November 18, 2018 04:09
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save chjj/2bedd29aed535d3a76d891ea7808450c to your computer and use it in GitHub Desktop.
Save chjj/2bedd29aed535d3a76d891ea7808450c to your computer and use it in GitHub Desktop.
javascript scrypt implementation
/*!
* scrypt
* Copyright (c) 2016, Christopher Jeffrey (MIT License).
*
* Ported from:
* https://github.com/Tarsnap/scrypt/blob/master/lib/crypto/crypto_scrypt-ref.c
*
* Copyright 2009 Colin Percival
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* This file was originally written by Colin Percival as part of the Tarsnap
* online backup system.
*/
var crypto = require('crypto');
function scrypt(passwd, salt, N, r, p, len) {
var i, B, V, XY;
if (r * p >= (1 << 30))
throw new Error('EFBIG');
if ((N & (N - 1)) !== 0 || N === 0)
throw new Error('EINVAL');
if (N > 0xffffffff)
throw new Error('EINVAL');
XY = new Buffer(256 * r);
V = new Buffer(128 * r * N);
B = crypto.pbkdf2Sync(passwd, salt, 1, p * 128 * r, 'sha256');
for (i = 0; i < p; i++)
smix(B.slice(i * 128 * r), r, N, V, XY);
return crypto.pbkdf2Sync(passwd, B, 1, len, 'sha256');
}
function salsa20_8(B) {
var B32 = new Array(16);
var x = new Array(16);
var i;
for (i = 0; i < 16; i++)
B32[i] = B.readUInt32LE(i * 4);
for (i = 0; i < 16; i++)
x[i] = B32[i];
for (i = 0; i < 8; 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];
for (i = 0; i < 16; i++)
B.writeUInt32LE(B32[i], 4 * i);
}
function R(a, b) {
return (a << b) | (a >>> (32 - b));
}
function blockmix_salsa8(B, Y, r) {
var X = new Buffer(64);
var i;
blkcpy(X, B.slice((2 * r - 1) * 64), 64);
for (i = 0; i < 2 * r; i++) {
blkxor(X, B.slice(i * 64), 64);
salsa20_8(X);
blkcpy(Y.slice(i * 64), X, 64);
}
for (i = 0; i < r; i++)
blkcpy(B.slice(i * 64), Y.slice((i * 2) * 64), 64);
for (i = 0; i < r; i++)
blkcpy(B.slice((i + r) * 64), Y.slice((i * 2 + 1) * 64), 64);
}
function integerify(B, r) {
return B.readUInt32LE((2 * r - 1) * 64);
}
function smix(B, r, N, V, XY) {
var X = XY;
var Y = XY.slice(128 * r);
var i;
var j;
blkcpy(X, B, 128 * r);
for (i = 0; i < N; i++) {
blkcpy(V.slice(i * (128 * r)), X, 128 * r);
blockmix_salsa8(X, Y, r);
}
for (i = 0; i < N; i++) {
j = integerify(X, r) & (N - 1);
blkxor(X, V.slice(j * (128 * r)), 128 * r);
blockmix_salsa8(X, Y, r);
}
blkcpy(B, X, 128 * r);
}
function blkcpy(dest, src, len) {
src.copy(dest, 0, 0, len);
}
function blkxor(dest, src, len) {
for (var i = 0; i < len; i++)
dest[i] ^= src[i];
}
// console.log(scrypt(new Buffer(''), new Buffer(''), 16, 1, 1, 64));
// console.log(scrypt(new Buffer('password'), new Buffer('NaCl'), 1024, 8, 16, 64));
// console.log(scrypt(new Buffer('pleaseletmein'), new Buffer('SodiumChloride'), 16384, 8, 1, 64));
// console.log(scrypt(new Buffer('pleaseletmein'), new Buffer('SodiumChloride'), 1048576, 8, 1, 64));
module.exports = scrypt;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment