diff --git a/lib/bcoin/scrypt.js b/lib/bcoin/scrypt.js new file mode 100644 index 00000000..0d49d1b8 --- /dev/null +++ b/lib/bcoin/scrypt.js @@ -0,0 +1,194 @@ +/*! + * scrypt.js - scrypt for bcoin + * Copyright (c) 2016, Christopher Jeffrey (MIT License). + * https://github.com/indutny/bcoin + * + * 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. + */ + +var utils = require('./utils'); + +/** + * Javascript scrypt implementation. Scrypt is + * used in bip38. BCoin doesn't support bip38 + * yet, but here it is, just in case. + * @param {Buffer} passwd + * @param {Buffer} salt + * @param {Number} N + * @param {Number} r + * @param {Number} p + * @param {Number} len + * @returns {Buffer} + */ + +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 = utils.pbkdf2(passwd, salt, 1, p * 128 * r, 'sha256'); + + for (i = 0; i < p; i++) + smix(B.slice(i * 128 * r), r, N, V, XY); + + return utils.pbkdf2(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] = utils.readU32(B, 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++) + utils.writeU32(B, 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 utils.readU32(B, (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]; +} + +/* + * Expose + */ + +module.exports = scrypt;