Skip to content

Commit

Permalink
Merge pull request #15 from dchest/notimeout
Browse files Browse the repository at this point in the history
Optimize for zero or negative interruptStep.
  • Loading branch information
dchest committed May 13, 2015
2 parents bd1b0a4 + f54e915 commit f23bd91
Show file tree
Hide file tree
Showing 4 changed files with 213 additions and 84 deletions.
33 changes: 21 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,28 +30,37 @@ You can install it via a package manager:
or [download source code](https://github.com/dchest/scrypt-async-js/releases).


Limitation
----------
Usage
-----

Doesn't support parallelization parameter greater than 1.
### scrypt(password, salt, logN, r, dkLen, [interruptStep], callback, [encoding])

Derives a key from password and salt and calls callback
with derived key as the only argument.

Usage
-----
Calculations are interrupted with 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
calculation, avoiding setTimeout.

### scrypt(password, salt, logN, r, dkLen, interruptStep, callback, encoding)

Derives a key from password and salt and calls callback with derived
key as the only argument.
#### Arguments:

* *password* - password (string or array of bytes)
* *salt* - salt (string or array of bytes)
* *logN* - CPU/memory cost parameter (1 to 31)
* *r* - block size parameter
* *dkLen* - length of derived key
* *interruptStep* - steps to split calculation with timeouts (default 1000)
* *callback* - callback function (`function (string)`)
* *encoding* - (optional) result encoding (`"base64"`, `"hex"`, or `null`).
* *interruptStep* - (optional) steps to split calculation with timeouts (defaults to 1000)
* *callback* - callback function (`function (array|string)`)
* *encoding* - (optional) result encoding (`"base64"`, `"hex"`, or `null`/`undefined`).

When encoding is not set, the result is an `Array` of bytes.


Limitation
----------

Doesn't support parallelization parameter greater than 1.


License
Expand Down
79 changes: 54 additions & 25 deletions scrypt-async.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*!
* Fast "async" scrypt implementation in JavaScript.
* Copyright (c) 2013-2014 Dmitry Chestnykh | BSD License
* Copyright (c) 2013-2015 Dmitry Chestnykh | BSD License
* https://github.com/dchest/scrypt-async-js
*/

Expand All @@ -9,19 +9,25 @@
*/

/**
* scrypt(password, salt, logN, r, dkLen, interruptStep, callback, encoding)
* scrypt(password, salt, logN, r, dkLen, [interruptStep], callback, [encoding])
*
* Derives a key from password and salt and calls callback
* with derived key as the only argument.
*
* Calculations are interrupted with 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
* calculation, avoiding setTimeout.
*
* @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 Steps to split calculation with timeouts (default 1000).
* @param {function(string)} callback Callback function.
* @param {string?} encoding Result encoding ("base64", "hex", or null).
* @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) {
'use strict';
Expand Down Expand Up @@ -333,7 +339,7 @@ function scrypt(password, salt, logN, r, dkLen, interruptStep, callback, encodin
}
if (len % 3 > 0) {
arr[arr.length-1] = '=';
if (len % 3 == 1) arr[arr.length-2] = '=';
if (len % 3 === 1) arr[arr.length-2] = '=';
}
return arr.join('');
}
Expand All @@ -355,9 +361,9 @@ function scrypt(password, salt, logN, r, dkLen, interruptStep, callback, encodin
throw new Error('scrypt: parameters are too large');

// Decode strings.
if (typeof password == 'string')
if (typeof password === 'string')
password = stringToUTF8Bytes(password);
if (typeof salt == 'string')
if (typeof salt === 'string')
salt = stringToUTF8Bytes(salt);

if (typeof Int32Array !== 'undefined') {
Expand Down Expand Up @@ -427,23 +433,46 @@ function scrypt(password, salt, logN, r, dkLen, interruptStep, callback, encodin
})();
}

// Note: step argument for interruptedFor must be divisible by
// two, since smixStepX work in increments of 2.
if (!interruptStep) interruptStep = 1000;

smixStart();
interruptedFor(0, N, interruptStep*2, smixStep1, function() {
interruptedFor(0, N, interruptStep*2, smixStep2, function () {
smixFinish();
function getResult(enc) {
var result = PBKDF2_HMAC_SHA256_OneIter(password, B, dkLen);
if (encoding == "base64")
callback(bytesToBase64(result));
else if (encoding == "hex")
callback(bytesToHex(result));
if (enc === 'base64')
return bytesToBase64(result);
else if (enc === 'hex')
return bytesToHex(result);
else
callback(result);
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));
});
});
});
}
}

if (typeof module !== 'undefined') module.exports = scrypt;
4 changes: 2 additions & 2 deletions scrypt-async.min.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit f23bd91

Please sign in to comment.