11/*!
22 * Fast "async" scrypt implementation in JavaScript.
3- * Copyright (c) 2013-2015 Dmitry Chestnykh | BSD License
3+ * Copyright (c) 2013-2016 Dmitry Chestnykh | BSD License
44 * https://github.com/dchest/scrypt-async-js
55 */
66
7- /*
8- * Limitation: doesn't support parallelization parameter greater than 1.
9- */
10-
117/**
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+ * }
1327 *
1428 * Derives a key from password and salt and calls callback
1529 * with derived key as the only argument.
1933 * given, it defaults to 1000. If it's zero, the callback is called immediately
2034 * after the calculation, avoiding setImmediate.
2135 *
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])
3039 *
3140 */
3241function scrypt ( password , salt , logN , r , dkLen , interruptStep , callback , encoding ) {
@@ -347,20 +356,53 @@ function scrypt(password, salt, logN, r, dkLen, interruptStep, callback, encodin
347356
348357 // Generate key.
349358
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' ) ;
352394
353395 if ( r <= 0 )
354396 throw new Error ( 'scrypt: invalid r' ) ;
355397
356398 if ( logN < 1 || logN > 31 )
357399 throw new Error ( 'scrypt: logN not be between 1 and 31' ) ;
358400
359- var MAX_INT = ( 1 << 31 ) >>> 0 ,
360- N = ( 1 << logN ) >>> 0 ,
401+
402+ var N = ( 1 << logN ) >>> 0 ,
361403 XY , V , B , tmp ;
362404
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 )
364406 throw new Error ( 'scrypt: parameters are too large' ) ;
365407
366408 // Decode strings.
@@ -383,9 +425,9 @@ function scrypt(password, salt, logN, r, dkLen, interruptStep, callback, encodin
383425
384426 var xi = 0 , yi = 32 * r ;
385427
386- function smixStart ( ) {
428+ function smixStart ( pos ) {
387429 for ( var i = 0 ; i < 32 * r ; i ++ ) {
388- var j = i * 4 ;
430+ var j = pos + i * 4 ;
389431 XY [ xi + i ] = ( ( B [ j + 3 ] & 0xff ) << 24 ) | ( ( B [ j + 2 ] & 0xff ) << 16 ) |
390432 ( ( B [ j + 1 ] & 0xff ) << 8 ) | ( ( B [ j + 0 ] & 0xff ) << 0 ) ;
391433 }
@@ -413,13 +455,13 @@ function scrypt(password, salt, logN, r, dkLen, interruptStep, callback, encodin
413455 }
414456 }
415457
416- function smixFinish ( ) {
458+ function smixFinish ( pos ) {
417459 for ( var i = 0 ; i < 32 * r ; i ++ ) {
418460 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 ;
423465 }
424466 }
425467
@@ -448,6 +490,32 @@ function scrypt(password, salt, logN, r, dkLen, interruptStep, callback, encodin
448490 return result ;
449491 }
450492
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+
451519 if ( typeof interruptStep === 'function' ) {
452520 // Called as: scrypt(..., callback, [encoding])
453521 // shifting: scrypt(..., interruptStep, callback, [encoding])
@@ -457,26 +525,9 @@ function scrypt(password, salt, logN, r, dkLen, interruptStep, callback, encodin
457525 }
458526
459527 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 ( ) ;
469529 } 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 ) ;
480531 }
481532}
482533
0 commit comments