Skip to content

Commit f23bd91

Browse files
committed
Merge pull request #15 from dchest/notimeout
Optimize for zero or negative interruptStep.
2 parents bd1b0a4 + f54e915 commit f23bd91

File tree

4 files changed

+213
-84
lines changed

4 files changed

+213
-84
lines changed

README.md

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -30,28 +30,37 @@ You can install it via a package manager:
3030
or [download source code](https://github.com/dchest/scrypt-async-js/releases).
3131

3232

33-
Limitation
34-
----------
33+
Usage
34+
-----
3535

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

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

39-
Usage
40-
-----
41+
Calculations are interrupted with zero setTimeout at the given
42+
interruptSteps to avoid freezing the browser. If interruptStep is not given,
43+
it defaults to 1000. If it's zero, the callback is called immediately after
44+
calculation, avoiding setTimeout.
4145

42-
### scrypt(password, salt, logN, r, dkLen, interruptStep, callback, encoding)
43-
44-
Derives a key from password and salt and calls callback with derived
45-
key as the only argument.
46+
#### Arguments:
4647

4748
* *password* - password (string or array of bytes)
4849
* *salt* - salt (string or array of bytes)
4950
* *logN* - CPU/memory cost parameter (1 to 31)
5051
* *r* - block size parameter
5152
* *dkLen* - length of derived key
52-
* *interruptStep* - steps to split calculation with timeouts (default 1000)
53-
* *callback* - callback function (`function (string)`)
54-
* *encoding* - (optional) result encoding (`"base64"`, `"hex"`, or `null`).
53+
* *interruptStep* - (optional) steps to split calculation with timeouts (defaults to 1000)
54+
* *callback* - callback function (`function (array|string)`)
55+
* *encoding* - (optional) result encoding (`"base64"`, `"hex"`, or `null`/`undefined`).
56+
57+
When encoding is not set, the result is an `Array` of bytes.
58+
59+
60+
Limitation
61+
----------
62+
63+
Doesn't support parallelization parameter greater than 1.
5564

5665

5766
License

scrypt-async.js

Lines changed: 54 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/*!
22
* Fast "async" scrypt implementation in JavaScript.
3-
* Copyright (c) 2013-2014 Dmitry Chestnykh | BSD License
3+
* Copyright (c) 2013-2015 Dmitry Chestnykh | BSD License
44
* https://github.com/dchest/scrypt-async-js
55
*/
66

@@ -9,19 +9,25 @@
99
*/
1010

1111
/**
12-
* scrypt(password, salt, logN, r, dkLen, interruptStep, callback, encoding)
12+
* scrypt(password, salt, logN, r, dkLen, [interruptStep], callback, [encoding])
1313
*
1414
* Derives a key from password and salt and calls callback
1515
* with derived key as the only argument.
1616
*
17+
* Calculations are interrupted with zero setTimeout at the given
18+
* interruptSteps to avoid freezing the browser. If interruptStep is not given,
19+
* it defaults to 1000. If it's zero, the callback is called immediately after
20+
* calculation, avoiding setTimeout.
21+
*
1722
* @param {string|Array.<number>} password Password.
1823
* @param {string|Array.<number>} salt Salt.
19-
* @param {number} logN CPU/memory cost parameter (1 to 31).
20-
* @param {number} r Block size parameter.
21-
* @param {number} dkLen Length of derived key.
22-
* @param {number} interruptStep Steps to split calculation with timeouts (default 1000).
23-
* @param {function(string)} callback Callback function.
24-
* @param {string?} encoding Result encoding ("base64", "hex", or null).
24+
* @param {number} logN CPU/memory cost parameter (1 to 31).
25+
* @param {number} r Block size parameter.
26+
* @param {number} dkLen Length of derived key.
27+
* @param {number?} interruptStep (optional) Steps to split calculation with timeouts (default 1000).
28+
* @param {function(string|Array.<number>)} callback Callback function.
29+
* @param {string?} encoding (optional) Result encoding ("base64", "hex", or null).
30+
*
2531
*/
2632
function scrypt(password, salt, logN, r, dkLen, interruptStep, callback, encoding) {
2733
'use strict';
@@ -333,7 +339,7 @@ function scrypt(password, salt, logN, r, dkLen, interruptStep, callback, encodin
333339
}
334340
if (len % 3 > 0) {
335341
arr[arr.length-1] = '=';
336-
if (len % 3 == 1) arr[arr.length-2] = '=';
342+
if (len % 3 === 1) arr[arr.length-2] = '=';
337343
}
338344
return arr.join('');
339345
}
@@ -355,9 +361,9 @@ function scrypt(password, salt, logN, r, dkLen, interruptStep, callback, encodin
355361
throw new Error('scrypt: parameters are too large');
356362

357363
// Decode strings.
358-
if (typeof password == 'string')
364+
if (typeof password === 'string')
359365
password = stringToUTF8Bytes(password);
360-
if (typeof salt == 'string')
366+
if (typeof salt === 'string')
361367
salt = stringToUTF8Bytes(salt);
362368

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

430-
// Note: step argument for interruptedFor must be divisible by
431-
// two, since smixStepX work in increments of 2.
432-
if (!interruptStep) interruptStep = 1000;
433-
434-
smixStart();
435-
interruptedFor(0, N, interruptStep*2, smixStep1, function() {
436-
interruptedFor(0, N, interruptStep*2, smixStep2, function () {
437-
smixFinish();
436+
function getResult(enc) {
438437
var result = PBKDF2_HMAC_SHA256_OneIter(password, B, dkLen);
439-
if (encoding == "base64")
440-
callback(bytesToBase64(result));
441-
else if (encoding == "hex")
442-
callback(bytesToHex(result));
438+
if (enc === 'base64')
439+
return bytesToBase64(result);
440+
else if (enc === 'hex')
441+
return bytesToHex(result);
443442
else
444-
callback(result);
443+
return result;
444+
}
445+
446+
if (typeof interruptStep === 'function') {
447+
// Called as: scrypt(..., callback, [encoding])
448+
// shifting: scrypt(..., interruptStep, callback, [encoding])
449+
encoding = callback;
450+
callback = interruptStep;
451+
interruptStep = 1000;
452+
}
453+
454+
if (interruptStep <= 0) {
455+
//
456+
// Blocking async variant, calls callback.
457+
//
458+
smixStart();
459+
smixStep1(0, N);
460+
smixStep2(0, N);
461+
smixFinish();
462+
callback(getResult(encoding));
463+
464+
} else {
465+
//
466+
// Async variant with interruptions, calls callback.
467+
//
468+
smixStart();
469+
interruptedFor(0, N, interruptStep*2, smixStep1, function() {
470+
interruptedFor(0, N, interruptStep*2, smixStep2, function () {
471+
smixFinish();
472+
callback(getResult(encoding));
473+
});
445474
});
446-
});
475+
}
447476
}
448477

449478
if (typeof module !== 'undefined') module.exports = scrypt;

scrypt-async.min.js

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)