1
1
/*!
2
2
* Fast "async" scrypt implementation in JavaScript.
3
- * Copyright (c) 2013-2015 Dmitry Chestnykh | BSD License
3
+ * Copyright (c) 2013-2016 Dmitry Chestnykh | BSD License
4
4
* https://github.com/dchest/scrypt-async-js
5
5
*/
6
6
7
- /*
8
- * Limitation: doesn't support parallelization parameter greater than 1.
9
- */
10
-
11
7
/**
12
- * scrypt(password, salt, logN, r, dkLen, [interruptStep], callback, [encoding])
8
+ * scrypt(password, salt, options, callback)
9
+ *
10
+ * where
11
+ *
12
+ * password and salt are strings or arrays of bytes (Array of Uint8Array)
13
+ * options is
14
+ *
15
+ * {
16
+ * N: // CPU/memory cost parameter, must be power of two
17
+ * // (alternatively, you can specify logN)
18
+ * r: // block size
19
+ * p: // parallelization parameter
20
+ * dkLen: // length of derived key, default = 32
21
+ * encoding: // optional encoding:
22
+ * "base64" - standard Base64 encoding
23
+ * "hex" — hex encoding,
24
+ * undefined/null - Array of bytes
25
+ * interruptStep: // optional, steps to split calculations (default is 0)
26
+ * }
13
27
*
14
28
* Derives a key from password and salt and calls callback
15
29
* with derived key as the only argument.
19
33
* given, it defaults to 1000. If it's zero, the callback is called immediately
20
34
* after the calculation, avoiding setImmediate.
21
35
*
22
- * @param {string|Array.<number> } password Password.
23
- * @param {string|Array.<number> } salt Salt.
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).
36
+ * Legacy way (only supports p = 1) to call this function is:
37
+ *
38
+ * scrypt(password, salt, logN, r, dkLen, [interruptStep], callback, [encoding])
30
39
*
31
40
*/
32
41
function scrypt ( password , salt , logN , r , dkLen , interruptStep , callback , encoding ) {
@@ -347,20 +356,53 @@ function scrypt(password, salt, logN, r, dkLen, interruptStep, callback, encodin
347
356
348
357
// Generate key.
349
358
350
- // Set parallelization parameter to 1.
351
- var p = 1 ;
359
+ var MAX_UINT = ( - 1 ) >>> 0 ,
360
+ p = 1 ;
361
+
362
+ if ( typeof logN === "object" ) {
363
+ // Called as: scrypt(password, salt, opts, callback)
364
+ if ( arguments . count > 4 ) {
365
+ throw new Error ( 'scrypt: incorrect number of arguments' ) ;
366
+ }
367
+
368
+ var opts = logN ;
369
+
370
+ callback = r ;
371
+ logN = opts . logN ;
372
+ if ( typeof logN === 'undefined' ) {
373
+ if ( typeof opts . N !== 'undefined' ) {
374
+ if ( opts . N < 0 || opts . N > MAX_UINT )
375
+ throw new Error ( 'scrypt: N is out of range' ) ;
376
+
377
+ if ( opts . N & ( opts . N - 1 ) !== 0 )
378
+ throw new Error ( 'scrypt: N is not a power of 2' ) ;
379
+
380
+ logN = Math . log ( opts . N ) / Math . LN2 ;
381
+ } else {
382
+ throw new Error ( 'scrypt: missing N parameter' ) ;
383
+ }
384
+ }
385
+ p = opts . p || 1 ;
386
+ r = opts . r ;
387
+ dkLen = opts . dkLen || 32 ;
388
+ interruptStep = opts . interruptStep || 0 ;
389
+ encoding = opts . encoding ;
390
+ }
391
+
392
+ if ( p < 1 )
393
+ throw new Error ( 'scrypt: invalid p' ) ;
352
394
353
395
if ( r <= 0 )
354
396
throw new Error ( 'scrypt: invalid r' ) ;
355
397
356
398
if ( logN < 1 || logN > 31 )
357
399
throw new Error ( 'scrypt: logN not be between 1 and 31' ) ;
358
400
359
- var MAX_INT = ( 1 << 31 ) >>> 0 ,
360
- N = ( 1 << logN ) >>> 0 ,
401
+
402
+ var N = ( 1 << logN ) >>> 0 ,
361
403
XY , V , B , tmp ;
362
404
363
- if ( r * p >= 1 << 30 || r > MAX_INT / 128 / p || r > MAX_INT / 256 || N > MAX_INT / 128 / r )
405
+ if ( r * p >= 1 << 30 || r > MAX_UINT / 128 / p || r > MAX_UINT / 256 || N > MAX_UINT / 128 / r )
364
406
throw new Error ( 'scrypt: parameters are too large' ) ;
365
407
366
408
// Decode strings.
@@ -383,9 +425,9 @@ function scrypt(password, salt, logN, r, dkLen, interruptStep, callback, encodin
383
425
384
426
var xi = 0 , yi = 32 * r ;
385
427
386
- function smixStart ( ) {
428
+ function smixStart ( pos ) {
387
429
for ( var i = 0 ; i < 32 * r ; i ++ ) {
388
- var j = i * 4 ;
430
+ var j = pos + i * 4 ;
389
431
XY [ xi + i ] = ( ( B [ j + 3 ] & 0xff ) << 24 ) | ( ( B [ j + 2 ] & 0xff ) << 16 ) |
390
432
( ( B [ j + 1 ] & 0xff ) << 8 ) | ( ( B [ j + 0 ] & 0xff ) << 0 ) ;
391
433
}
@@ -413,13 +455,13 @@ function scrypt(password, salt, logN, r, dkLen, interruptStep, callback, encodin
413
455
}
414
456
}
415
457
416
- function smixFinish ( ) {
458
+ function smixFinish ( pos ) {
417
459
for ( var i = 0 ; i < 32 * r ; i ++ ) {
418
460
var j = XY [ xi + i ] ;
419
- B [ i * 4 + 0 ] = ( j >>> 0 ) & 0xff ;
420
- B [ i * 4 + 1 ] = ( j >>> 8 ) & 0xff ;
421
- B [ i * 4 + 2 ] = ( j >>> 16 ) & 0xff ;
422
- B [ i * 4 + 3 ] = ( j >>> 24 ) & 0xff ;
461
+ B [ pos + i * 4 + 0 ] = ( j >>> 0 ) & 0xff ;
462
+ B [ pos + i * 4 + 1 ] = ( j >>> 8 ) & 0xff ;
463
+ B [ pos + i * 4 + 2 ] = ( j >>> 16 ) & 0xff ;
464
+ B [ pos + i * 4 + 3 ] = ( j >>> 24 ) & 0xff ;
423
465
}
424
466
}
425
467
@@ -448,6 +490,32 @@ function scrypt(password, salt, logN, r, dkLen, interruptStep, callback, encodin
448
490
return result ;
449
491
}
450
492
493
+ // Blocking variant.
494
+ function calculateSync ( ) {
495
+ for ( var i = 0 ; i < p ; i ++ ) {
496
+ smixStart ( i * 128 * r ) ;
497
+ smixStep1 ( 0 , N ) ;
498
+ smixStep2 ( 0 , N ) ;
499
+ smixFinish ( i * 128 * r ) ;
500
+ }
501
+ callback ( getResult ( encoding ) ) ;
502
+ }
503
+
504
+ // Async variant.
505
+ function calculateAsync ( i ) {
506
+ smixStart ( i * 128 * r ) ;
507
+ interruptedFor ( 0 , N , interruptStep * 2 , smixStep1 , function ( ) {
508
+ interruptedFor ( 0 , N , interruptStep * 2 , smixStep2 , function ( ) {
509
+ smixFinish ( i * 128 * r ) ;
510
+ if ( i + 1 < p ) {
511
+ nextTick ( function ( ) { calculateAsync ( i + 1 ) ; } ) ;
512
+ } else {
513
+ callback ( getResult ( encoding ) ) ;
514
+ }
515
+ } ) ;
516
+ } ) ;
517
+ }
518
+
451
519
if ( typeof interruptStep === 'function' ) {
452
520
// Called as: scrypt(..., callback, [encoding])
453
521
// shifting: scrypt(..., interruptStep, callback, [encoding])
@@ -457,26 +525,9 @@ function scrypt(password, salt, logN, r, dkLen, interruptStep, callback, encodin
457
525
}
458
526
459
527
if ( interruptStep <= 0 ) {
460
- //
461
- // Blocking async variant, calls callback.
462
- //
463
- smixStart ( ) ;
464
- smixStep1 ( 0 , N ) ;
465
- smixStep2 ( 0 , N ) ;
466
- smixFinish ( ) ;
467
- callback ( getResult ( encoding ) ) ;
468
-
528
+ calculateSync ( ) ;
469
529
} else {
470
- //
471
- // Async variant with interruptions, calls callback.
472
- //
473
- smixStart ( ) ;
474
- interruptedFor ( 0 , N , interruptStep * 2 , smixStep1 , function ( ) {
475
- interruptedFor ( 0 , N , interruptStep * 2 , smixStep2 , function ( ) {
476
- smixFinish ( ) ;
477
- callback ( getResult ( encoding ) ) ;
478
- } ) ;
479
- } ) ;
530
+ calculateAsync ( 0 ) ;
480
531
}
481
532
}
482
533
0 commit comments