Skip to content

Commit

Permalink
Make use of JS Promises
Browse files Browse the repository at this point in the history
- Least invasive changes at first
- Redundant tests were removed
  • Loading branch information
Max13 committed Aug 16, 2016
1 parent 1723f1f commit 0ce7a5c
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 110 deletions.
50 changes: 10 additions & 40 deletions scrypt-async.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*!
* Fast "async" scrypt implementation in JavaScript.
* Fast scrypt implementation in JavaScript, using Promises.
* Copyright (c) 2013-2015 Dmitry Chestnykh | BSD License
* https://github.com/dchest/scrypt-async-js
*/
Expand All @@ -9,29 +9,23 @@
*/

/**
* scrypt(password, salt, logN, r, dkLen, [interruptStep], callback, [encoding])
* scrypt(password, salt, logN, r, dkLen, [encoding])
*
* Derives a key from password and salt and calls callback
* Derives a key from password and salt and returns a Promise
* with derived key as the only argument.
*
* Calculations are interrupted with setImmediate (or zero setTimeout) at the
* given interruptSteps to avoid freezing the browser. If interruptStep is not
* given, it defaults to 1000. If it's zero, the callback is called immediately
* after the calculation, avoiding setImmediate.
*
* @param {string|Array.<number>} password Password.
* @param {string|Array.<number>} salt Salt.
* @param {number} logN CPU/memory cost parameter (1 to 31).
* @param {number} r Block size parameter.
* @param {number} dkLen Length of derived key.
* @param {number?} interruptStep (optional) Steps to split calculation with timeouts (default 1000).
* @param {function(string|Array.<number>)} callback Callback function.
* @param {string?} encoding (optional) Result encoding ("base64", "hex", or null).
*
*/
function scrypt(password, salt, logN, r, dkLen, interruptStep, callback, encoding) {
function scrypt(password, salt, logN, r, dkLen, encoding) {
'use strict';

return new Promise(function (callback, reject) {
function SHA256(m) {
/** @const */ var K = [
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b,
Expand Down Expand Up @@ -351,17 +345,17 @@ function scrypt(password, salt, logN, r, dkLen, interruptStep, callback, encodin
var p = 1;

if (r <= 0)
throw new Error('scrypt: invalid r');
return reject('scrypt: invalid r');

if (logN < 1 || logN > 31)
throw new Error('scrypt: logN not be between 1 and 31');
return reject('scrypt: logN not be between 1 and 31');

var MAX_INT = (1<<31)>>>0,
N = (1<<logN)>>>0,
XY, V, B, tmp;

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');
return reject('scrypt: parameters are too large');

// Decode strings.
if (typeof password === 'string')
Expand Down Expand Up @@ -448,36 +442,12 @@ function scrypt(password, salt, logN, r, dkLen, interruptStep, callback, encodin
return result;
}

if (typeof interruptStep === 'function') {
// Called as: scrypt(..., callback, [encoding])
// shifting: scrypt(..., interruptStep, callback, [encoding])
encoding = callback;
callback = interruptStep;
interruptStep = 1000;
}

if (interruptStep <= 0) {
//
// Blocking async variant, calls callback.
//
smixStart();
smixStep1(0, N);
smixStep2(0, N);
smixFinish();
callback(getResult(encoding));

} else {
//
// Async variant with interruptions, calls callback.
//
smixStart();
interruptedFor(0, N, interruptStep*2, smixStep1, function() {
interruptedFor(0, N, interruptStep*2, smixStep2, function () {
smixFinish();
callback(getResult(encoding));
});
});
}
return callback(getResult(encoding));
});
}

if (typeof module !== 'undefined') module.exports = scrypt;
110 changes: 40 additions & 70 deletions test/unittests.js
Original file line number Diff line number Diff line change
Expand Up @@ -255,28 +255,32 @@ var shortInput = {
describe('limits test', function() {
var v = shortInput;

it('should throw with too small logN', function() {
assert.throws(function() {
scrypt(v.password, v.salt, 0, v.r, v.dkLen);
}, Error);
it('should reject with too small logN', function() {
scrypt(v.password, v.salt, 0, v.r, v.dkLen)
.catch(function (err) {
assert.equal('should reject with too small logN', err);
});
});

it('should throw with too big logN', function() {
assert.throws(function() {
scrypt(v.password, v.salt, 32, v.r, v.dkLen);
}, Error);
it('should reject with too big logN', function() {
scrypt(v.password, v.salt, 32, v.r, v.dkLen)
.catch(function (err) {
assert.equal('should reject with too big logN', err);
});
});

it('should throw with too large parameters', function() {
assert.throws(function() {
scrypt(v.password, v.salt, v.logN, 1<<31, v.dkLen);
}, Error);
it('should reject with too large parameters', function() {
scrypt(v.password, v.salt, v.logN, 1<<31, v.dkLen)
.catch(function (err) {
assert.equal('should reject with too large parameters', err);
});
});

it('should throw when r = 0', function() {
assert.throws(function() {
scrypt(v.password, v.salt, v.logN, 0, v.dkLen);
}, Error);
it('should reject when r = 0', function() {
scrypt(v.password, v.salt, v.logN, 0, v.dkLen)
.catch(function (err) {
assert.equal('should reject when r = 0', err);
});
});

});
Expand All @@ -287,101 +291,67 @@ describe('argument order test', function() {
var v = shortInput;

it('all arguments', function(done) {
scrypt(v.password, v.salt, v.logN, v.r, v.dkLen, 1000, function(out) {
scrypt(v.password, v.salt, v.logN, v.r, v.dkLen, "hex")
.then(function (out) {
assert.equal(v.hexResult, out);
done();
}, "hex");
});

it('all arguments, zero interruptStep', function(done) {
scrypt(v.password, v.salt, v.logN, v.r, v.dkLen, 0, function(out) {
assert.equal(v.hexResult, out);
done();
}, "hex");
});
});

it('drop encoding', function(done) {
scrypt(v.password, v.salt, v.logN, v.r, v.dkLen, 1000, function(out) {
scrypt(v.password, v.salt, v.logN, v.r, v.dkLen)
.then(function (out) {
assert.deepEqual(v.result, out);
done();
});
});

it('drop interruptStep, keep encoding', function(done) {
scrypt(v.password, v.salt, v.logN, v.r, v.dkLen, function(out) {
assert.equal(v.hexResult, out);
done();
}, 'hex');
});

});

function async_test(i, interruptStep, done) {
function async_test(i, done) {
var v = inputs[i];
scrypt(v.password, v.salt, v.logN, v.r, v.dkLen, interruptStep, function(out) {
scrypt(v.password, v.salt, v.logN, v.r, v.dkLen, v.encoding)
.then(function(out) {
assert.deepEqual(v.result, out);
done();
}, v.encoding);
});
}

describe('async input/output test', function() {
this.timeout(100000);

var step = 1000;

it('input 0', function(done) {
async_test(0, step, done);
async_test(0, done);
});
it('input 1', function(done) {
async_test(1, step, done);
async_test(1, done);
});
it('input 2', function(done) {
async_test(2, step, done);
async_test(2, done);
});
it('input 3', function(done) {
async_test(3, step, done);
async_test(3, done);
});
it('input 4', function(done) {
async_test(4, step, done);
async_test(4, done);
});
it('input 5', function(done) {
async_test(5, step, done);
async_test(5, done);
});
it('input 6', function(done) {
async_test(6, step, done);
async_test(6, done);
});
it('input 7', function(done) {
async_test(7, step, done);
async_test(7, done);
});
it('input 8', function(done) {
async_test(8, step, done);
async_test(8, done);
});
it('input 9', function(done) {
async_test(9, step, done);
async_test(9, done);
});
it('input 10', function(done) {
async_test(10, step, done);
});

});

describe('async input/output test with zero interruptStep', function() {
this.timeout(100000);

// Only shorter tests:
var step = 0;

it('input 0', function(done) {
async_test(0, step, done);
});
it('input 1', function(done) {
async_test(1, step, done);
});
it('input 2', function(done) {
async_test(2, step, done);
});
it('input 3', function(done) {
async_test(3, step, done);
async_test(10, done);
});

});

0 comments on commit 0ce7a5c

Please sign in to comment.