From f54e915d46802a8f4227d7582d2637a69ed3754a Mon Sep 17 00:00:00 2001 From: Dmitry Chestnykh Date: Sun, 10 May 2015 17:07:05 +0200 Subject: [PATCH] Optimize for zero or negative interruptStep. If interruptStep is <= 0, avoid calling setTimeout, and calculate result immediately, passing it to callback. --- README.md | 33 +++++--- scrypt-async.js | 79 +++++++++++++------ scrypt-async.min.js | 4 +- test/unittests.js | 181 +++++++++++++++++++++++++++++++++----------- 4 files changed, 213 insertions(+), 84 deletions(-) diff --git a/README.md b/README.md index 05be485..7646e5b 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/scrypt-async.js b/scrypt-async.js index 2b8ef7b..443eed4 100644 --- a/scrypt-async.js +++ b/scrypt-async.js @@ -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 */ @@ -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.} password Password. * @param {string|Array.} 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.)} 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'; @@ -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(''); } @@ -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') { @@ -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; diff --git a/scrypt-async.min.js b/scrypt-async.min.js index f70b701..83639da 100644 --- a/scrypt-async.min.js +++ b/scrypt-async.min.js @@ -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 */ -function scrypt(a,b,c,d,e,f,g,h){"use strict";function i(a){function b(a){for(var b=0,m=a.length;m>=64;){var n,o,p,q,r,s=d,t=e,u=f,v=g,w=h,x=i,y=j,z=k;for(o=0;16>o;o++)p=b+4*o,l[o]=(255&a[p])<<24|(255&a[p+1])<<16|(255&a[p+2])<<8|255&a[p+3];for(o=16;64>o;o++)n=l[o-2],q=(n>>>17|n<<15)^(n>>>19|n<<13)^n>>>10,n=l[o-15],r=(n>>>7|n<<25)^(n>>>18|n<<14)^n>>>3,l[o]=(q+l[o-7]|0)+(r+l[o-16]|0)|0;for(o=0;64>o;o++)q=(((w>>>6|w<<26)^(w>>>11|w<<21)^(w>>>25|w<<7))+(w&x^~w&y)|0)+(z+(c[o]+l[o]|0)|0)|0,r=((s>>>2|s<<30)^(s>>>13|s<<19)^(s>>>22|s<<10))+(s&t^s&u^t&u)|0,z=y,y=x,x=w,w=v+q|0,v=u,u=t,t=s,s=q+r|0;d=d+s|0,e=e+t|0,f=f+u|0,g=g+v|0,h=h+w|0,i=i+x|0,j=j+y|0,k=k+z|0,b+=64,m-=64}}var c=[1116352408,1899447441,3049323471,3921009573,961987163,1508970993,2453635748,2870763221,3624381080,310598401,607225278,1426881987,1925078388,2162078206,2614888103,3248222580,3835390401,4022224774,264347078,604807628,770255983,1249150122,1555081692,1996064986,2554220882,2821834349,2952996808,3210313671,3336571891,3584528711,113926993,338241895,666307205,773529912,1294757372,1396182291,1695183700,1986661051,2177026350,2456956037,2730485921,2820302411,3259730800,3345764771,3516065817,3600352804,4094571909,275423344,430227734,506948616,659060556,883997877,958139571,1322822218,1537002063,1747873779,1955562222,2024104815,2227730452,2361852424,2428436474,2756734187,3204031479,3329325298],d=1779033703,e=3144134277,f=1013904242,g=2773480762,h=1359893119,i=2600822924,j=528734635,k=1541459225,l=new Array(64);b(a);var m,n=a.length%64,o=a.length/536870912|0,p=a.length<<3,q=56>n?56:120,r=a.slice(a.length-n,a.length);for(r.push(128),m=n+1;q>m;m++)r.push(0);return r.push(o>>>24&255),r.push(o>>>16&255),r.push(o>>>8&255),r.push(o>>>0&255),r.push(p>>>24&255),r.push(p>>>16&255),r.push(p>>>8&255),r.push(p>>>0&255),b(r),[d>>>24&255,d>>>16&255,d>>>8&255,d>>>0&255,e>>>24&255,e>>>16&255,e>>>8&255,e>>>0&255,f>>>24&255,f>>>16&255,f>>>8&255,f>>>0&255,g>>>24&255,g>>>16&255,g>>>8&255,g>>>0&255,h>>>24&255,h>>>16&255,h>>>8&255,h>>>0&255,i>>>24&255,i>>>16&255,i>>>8&255,i>>>0&255,j>>>24&255,j>>>16&255,j>>>8&255,j>>>0&255,k>>>24&255,k>>>16&255,k>>>8&255,k>>>0&255]}function j(a,b,c){function d(){for(var a=f-1;a>=f-4;a--){if(g[a]++,g[a]<=255)return;g[a]=0}}a=a.length<=64?a:i(a);var e,f=64+b.length+4,g=new Array(f),h=new Array(64),j=[];for(e=0;64>e;e++)g[e]=54;for(e=0;ee;e++)g[e]=0;for(e=0;64>e;e++)h[e]=92;for(e=0;e=32;)d(),j=j.concat(i(h.concat(i(g)))),c-=32;return c>0&&(d(),j=j.concat(i(h.concat(i(g))).slice(0,c))),j}function k(a,b,c,d){var e,f,g=a[0]^b[c++],h=a[1]^b[c++],i=a[2]^b[c++],j=a[3]^b[c++],k=a[4]^b[c++],l=a[5]^b[c++],m=a[6]^b[c++],n=a[7]^b[c++],o=a[8]^b[c++],p=a[9]^b[c++],q=a[10]^b[c++],r=a[11]^b[c++],s=a[12]^b[c++],t=a[13]^b[c++],u=a[14]^b[c++],v=a[15]^b[c++],w=g,x=h,y=i,z=j,A=k,B=l,C=m,D=n,E=o,F=p,G=q,H=r,I=s,J=t,K=u,L=v;for(f=0;8>f;f+=2)e=w+I,A^=e<<7|e>>>25,e=A+w,E^=e<<9|e>>>23,e=E+A,I^=e<<13|e>>>19,e=I+E,w^=e<<18|e>>>14,e=B+x,F^=e<<7|e>>>25,e=F+B,J^=e<<9|e>>>23,e=J+F,x^=e<<13|e>>>19,e=x+J,B^=e<<18|e>>>14,e=G+C,K^=e<<7|e>>>25,e=K+G,y^=e<<9|e>>>23,e=y+K,C^=e<<13|e>>>19,e=C+y,G^=e<<18|e>>>14,e=L+H,z^=e<<7|e>>>25,e=z+L,D^=e<<9|e>>>23,e=D+z,H^=e<<13|e>>>19,e=H+D,L^=e<<18|e>>>14,e=w+z,x^=e<<7|e>>>25,e=x+w,y^=e<<9|e>>>23,e=y+x,z^=e<<13|e>>>19,e=z+y,w^=e<<18|e>>>14,e=B+A,C^=e<<7|e>>>25,e=C+B,D^=e<<9|e>>>23,e=D+C,A^=e<<13|e>>>19,e=A+D,B^=e<<18|e>>>14,e=G+F,H^=e<<7|e>>>25,e=H+G,E^=e<<9|e>>>23,e=E+H,F^=e<<13|e>>>19,e=F+E,G^=e<<18|e>>>14,e=L+K,I^=e<<7|e>>>25,e=I+L,J^=e<<9|e>>>23,e=J+I,K^=e<<13|e>>>19,e=K+J,L^=e<<18|e>>>14;b[d++]=a[0]=w+g|0,b[d++]=a[1]=x+h|0,b[d++]=a[2]=y+i|0,b[d++]=a[3]=z+j|0,b[d++]=a[4]=A+k|0,b[d++]=a[5]=B+l|0,b[d++]=a[6]=C+m|0,b[d++]=a[7]=D+n|0,b[d++]=a[8]=E+o|0,b[d++]=a[9]=F+p|0,b[d++]=a[10]=G+q|0,b[d++]=a[11]=H+r|0,b[d++]=a[12]=I+s|0,b[d++]=a[13]=J+t|0,b[d++]=a[14]=K+u|0,b[d++]=a[15]=L+v|0}function l(a,b,c,d,e){for(;e--;)a[b++]=c[d++]}function m(a,b,c,d,e){for(;e--;)a[b++]^=c[d++]}function n(a,b,c,d,e){l(a,0,b,c+16*(2*e-1),16);for(var f=0;2*e>f;f+=2)k(a,b,c+16*f,d+8*f),k(a,b,c+16*f+16,d+8*f+16*e)}function o(a,b,c){return a[b+16*(2*c-1)]}function p(a){for(var b=[],c=0;cd?b.push(d):d>127&&2048>d?(b.push(d>>6|192),b.push(63&d|128)):(b.push(d>>12|224),b.push(d>>6&63|128),b.push(63&d|128))}return b}function q(a){for(var b="0123456789abcdef".split(""),c=a.length,d=[],e=0;c>e;e++)d.push(b[a[e]>>>4&15]),d.push(b[a[e]>>>0&15]);return d.join("")}function r(a){for(var b,c,d,e,f="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".split(""),g=a.length,h=[],i=0;g>i;)b=g>i?a[i++]:0,c=g>i?a[i++]:0,d=g>i?a[i++]:0,e=(b<<16)+(c<<8)+d,h.push(f[e>>>18&63]),h.push(f[e>>>12&63]),h.push(f[e>>>6&63]),h.push(f[e>>>0&63]);return g%3>0&&(h[h.length-1]="=",g%3==1&&(h[h.length-2]="=")),h.join("")}function s(){for(var a=0;32*d>a;a++){var b=4*a;y[E+a]=(255&A[b+3])<<24|(255&A[b+2])<<16|(255&A[b+1])<<8|(255&A[b+0])<<0}}function t(a,b){for(var c=a;b>c;c+=2)l(z,32*c*d,y,E,32*d),n(B,y,E,F,d),l(z,32*(c+1)*d,y,F,32*d),n(B,y,F,E,d)}function u(a,b){for(var c=a;b>c;c+=2){var e=o(y,E,d)&D-1;m(y,E,z,32*e*d,32*d),n(B,y,E,F,d),e=o(y,F,d)&D-1,m(y,F,z,32*e*d,32*d),n(B,y,F,E,d)}}function v(){for(var a=0;32*d>a;a++){var b=y[E+a];A[4*a+0]=b>>>0&255,A[4*a+1]=b>>>8&255,A[4*a+2]=b>>>16&255,A[4*a+3]=b>>>24&255}}function w(a,b,c,d,e){!function f(){setTimeout(function(){d(a,b>a+c?a+c:b),a+=c,b>a?f():e()},0)}()}var x=1;if(1>c||c>31)throw new Error("scrypt: logN not be between 1 and 31");var y,z,A,B,C=1<<31>>>0,D=1<>>0;if(d*x>=1<<30||d>C/128/x||d>C/256||D>C/128/d)throw new Error("scrypt: parameters are too large");"string"==typeof a&&(a=p(a)),"string"==typeof b&&(b=p(b)),"undefined"!=typeof Int32Array?(y=new Int32Array(64*d),z=new Int32Array(32*D*d),B=new Int32Array(16)):(y=[],z=[],B=new Array(16)),A=j(a,b,128*x*d);var E=0,F=32*d;f||(f=1e3),s(),w(0,D,2*f,t,function(){w(0,D,2*f,u,function(){v();var b=j(a,A,e);g("base64"==h?r(b):"hex"==h?q(b):b)})})}"undefined"!=typeof module&&(module.exports=scrypt); \ No newline at end of file +function scrypt(a,b,c,d,e,f,g,h){"use strict";function i(a){function b(a){for(var b=0,m=a.length;m>=64;){var n,o,p,q,r,s=d,t=e,u=f,v=g,w=h,x=i,y=j,z=k;for(o=0;16>o;o++)p=b+4*o,l[o]=(255&a[p])<<24|(255&a[p+1])<<16|(255&a[p+2])<<8|255&a[p+3];for(o=16;64>o;o++)n=l[o-2],q=(n>>>17|n<<15)^(n>>>19|n<<13)^n>>>10,n=l[o-15],r=(n>>>7|n<<25)^(n>>>18|n<<14)^n>>>3,l[o]=(q+l[o-7]|0)+(r+l[o-16]|0)|0;for(o=0;64>o;o++)q=(((w>>>6|w<<26)^(w>>>11|w<<21)^(w>>>25|w<<7))+(w&x^~w&y)|0)+(z+(c[o]+l[o]|0)|0)|0,r=((s>>>2|s<<30)^(s>>>13|s<<19)^(s>>>22|s<<10))+(s&t^s&u^t&u)|0,z=y,y=x,x=w,w=v+q|0,v=u,u=t,t=s,s=q+r|0;d=d+s|0,e=e+t|0,f=f+u|0,g=g+v|0,h=h+w|0,i=i+x|0,j=j+y|0,k=k+z|0,b+=64,m-=64}}var c=[1116352408,1899447441,3049323471,3921009573,961987163,1508970993,2453635748,2870763221,3624381080,310598401,607225278,1426881987,1925078388,2162078206,2614888103,3248222580,3835390401,4022224774,264347078,604807628,770255983,1249150122,1555081692,1996064986,2554220882,2821834349,2952996808,3210313671,3336571891,3584528711,113926993,338241895,666307205,773529912,1294757372,1396182291,1695183700,1986661051,2177026350,2456956037,2730485921,2820302411,3259730800,3345764771,3516065817,3600352804,4094571909,275423344,430227734,506948616,659060556,883997877,958139571,1322822218,1537002063,1747873779,1955562222,2024104815,2227730452,2361852424,2428436474,2756734187,3204031479,3329325298],d=1779033703,e=3144134277,f=1013904242,g=2773480762,h=1359893119,i=2600822924,j=528734635,k=1541459225,l=new Array(64);b(a);var m,n=a.length%64,o=a.length/536870912|0,p=a.length<<3,q=56>n?56:120,r=a.slice(a.length-n,a.length);for(r.push(128),m=n+1;q>m;m++)r.push(0);return r.push(o>>>24&255),r.push(o>>>16&255),r.push(o>>>8&255),r.push(o>>>0&255),r.push(p>>>24&255),r.push(p>>>16&255),r.push(p>>>8&255),r.push(p>>>0&255),b(r),[d>>>24&255,d>>>16&255,d>>>8&255,d>>>0&255,e>>>24&255,e>>>16&255,e>>>8&255,e>>>0&255,f>>>24&255,f>>>16&255,f>>>8&255,f>>>0&255,g>>>24&255,g>>>16&255,g>>>8&255,g>>>0&255,h>>>24&255,h>>>16&255,h>>>8&255,h>>>0&255,i>>>24&255,i>>>16&255,i>>>8&255,i>>>0&255,j>>>24&255,j>>>16&255,j>>>8&255,j>>>0&255,k>>>24&255,k>>>16&255,k>>>8&255,k>>>0&255]}function j(a,b,c){function d(){for(var a=f-1;a>=f-4;a--){if(g[a]++,g[a]<=255)return;g[a]=0}}a=a.length<=64?a:i(a);var e,f=64+b.length+4,g=new Array(f),h=new Array(64),j=[];for(e=0;64>e;e++)g[e]=54;for(e=0;ee;e++)g[e]=0;for(e=0;64>e;e++)h[e]=92;for(e=0;e=32;)d(),j=j.concat(i(h.concat(i(g)))),c-=32;return c>0&&(d(),j=j.concat(i(h.concat(i(g))).slice(0,c))),j}function k(a,b,c,d){var e,f,g=a[0]^b[c++],h=a[1]^b[c++],i=a[2]^b[c++],j=a[3]^b[c++],k=a[4]^b[c++],l=a[5]^b[c++],m=a[6]^b[c++],n=a[7]^b[c++],o=a[8]^b[c++],p=a[9]^b[c++],q=a[10]^b[c++],r=a[11]^b[c++],s=a[12]^b[c++],t=a[13]^b[c++],u=a[14]^b[c++],v=a[15]^b[c++],w=g,x=h,y=i,z=j,A=k,B=l,C=m,D=n,E=o,F=p,G=q,H=r,I=s,J=t,K=u,L=v;for(f=0;8>f;f+=2)e=w+I,A^=e<<7|e>>>25,e=A+w,E^=e<<9|e>>>23,e=E+A,I^=e<<13|e>>>19,e=I+E,w^=e<<18|e>>>14,e=B+x,F^=e<<7|e>>>25,e=F+B,J^=e<<9|e>>>23,e=J+F,x^=e<<13|e>>>19,e=x+J,B^=e<<18|e>>>14,e=G+C,K^=e<<7|e>>>25,e=K+G,y^=e<<9|e>>>23,e=y+K,C^=e<<13|e>>>19,e=C+y,G^=e<<18|e>>>14,e=L+H,z^=e<<7|e>>>25,e=z+L,D^=e<<9|e>>>23,e=D+z,H^=e<<13|e>>>19,e=H+D,L^=e<<18|e>>>14,e=w+z,x^=e<<7|e>>>25,e=x+w,y^=e<<9|e>>>23,e=y+x,z^=e<<13|e>>>19,e=z+y,w^=e<<18|e>>>14,e=B+A,C^=e<<7|e>>>25,e=C+B,D^=e<<9|e>>>23,e=D+C,A^=e<<13|e>>>19,e=A+D,B^=e<<18|e>>>14,e=G+F,H^=e<<7|e>>>25,e=H+G,E^=e<<9|e>>>23,e=E+H,F^=e<<13|e>>>19,e=F+E,G^=e<<18|e>>>14,e=L+K,I^=e<<7|e>>>25,e=I+L,J^=e<<9|e>>>23,e=J+I,K^=e<<13|e>>>19,e=K+J,L^=e<<18|e>>>14;b[d++]=a[0]=w+g|0,b[d++]=a[1]=x+h|0,b[d++]=a[2]=y+i|0,b[d++]=a[3]=z+j|0,b[d++]=a[4]=A+k|0,b[d++]=a[5]=B+l|0,b[d++]=a[6]=C+m|0,b[d++]=a[7]=D+n|0,b[d++]=a[8]=E+o|0,b[d++]=a[9]=F+p|0,b[d++]=a[10]=G+q|0,b[d++]=a[11]=H+r|0,b[d++]=a[12]=I+s|0,b[d++]=a[13]=J+t|0,b[d++]=a[14]=K+u|0,b[d++]=a[15]=L+v|0}function l(a,b,c,d,e){for(;e--;)a[b++]=c[d++]}function m(a,b,c,d,e){for(;e--;)a[b++]^=c[d++]}function n(a,b,c,d,e){l(a,0,b,c+16*(2*e-1),16);for(var f=0;2*e>f;f+=2)k(a,b,c+16*f,d+8*f),k(a,b,c+16*f+16,d+8*f+16*e)}function o(a,b,c){return a[b+16*(2*c-1)]}function p(a){for(var b=[],c=0;cd?b.push(d):d>127&&2048>d?(b.push(d>>6|192),b.push(63&d|128)):(b.push(d>>12|224),b.push(d>>6&63|128),b.push(63&d|128))}return b}function q(a){for(var b="0123456789abcdef".split(""),c=a.length,d=[],e=0;c>e;e++)d.push(b[a[e]>>>4&15]),d.push(b[a[e]>>>0&15]);return d.join("")}function r(a){for(var b,c,d,e,f="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".split(""),g=a.length,h=[],i=0;g>i;)b=g>i?a[i++]:0,c=g>i?a[i++]:0,d=g>i?a[i++]:0,e=(b<<16)+(c<<8)+d,h.push(f[e>>>18&63]),h.push(f[e>>>12&63]),h.push(f[e>>>6&63]),h.push(f[e>>>0&63]);return g%3>0&&(h[h.length-1]="=",g%3===1&&(h[h.length-2]="=")),h.join("")}function s(){for(var a=0;32*d>a;a++){var b=4*a;z[F+a]=(255&B[b+3])<<24|(255&B[b+2])<<16|(255&B[b+1])<<8|(255&B[b+0])<<0}}function t(a,b){for(var c=a;b>c;c+=2)l(A,32*c*d,z,F,32*d),n(C,z,F,G,d),l(A,32*(c+1)*d,z,G,32*d),n(C,z,G,F,d)}function u(a,b){for(var c=a;b>c;c+=2){var e=o(z,F,d)&E-1;m(z,F,A,32*e*d,32*d),n(C,z,F,G,d),e=o(z,G,d)&E-1,m(z,G,A,32*e*d,32*d),n(C,z,G,F,d)}}function v(){for(var a=0;32*d>a;a++){var b=z[F+a];B[4*a+0]=b>>>0&255,B[4*a+1]=b>>>8&255,B[4*a+2]=b>>>16&255,B[4*a+3]=b>>>24&255}}function w(a,b,c,d,e){!function f(){setTimeout(function(){d(a,b>a+c?a+c:b),a+=c,b>a?f():e()},0)}()}function x(b){var c=j(a,B,e);return"base64"===b?r(c):"hex"===b?q(c):c}var y=1;if(1>c||c>31)throw new Error("scrypt: logN not be between 1 and 31");var z,A,B,C,D=1<<31>>>0,E=1<>>0;if(d*y>=1<<30||d>D/128/y||d>D/256||E>D/128/d)throw new Error("scrypt: parameters are too large");"string"==typeof a&&(a=p(a)),"string"==typeof b&&(b=p(b)),"undefined"!=typeof Int32Array?(z=new Int32Array(64*d),A=new Int32Array(32*E*d),C=new Int32Array(16)):(z=[],A=[],C=new Array(16)),B=j(a,b,128*y*d);var F=0,G=32*d;"function"==typeof f&&(h=g,g=f,f=1e3),0>=f?(s(),t(0,E),u(0,E),v(),g(x(h))):(s(),w(0,E,2*f,t,function(){w(0,E,2*f,u,function(){v(),g(x(h))})}))}"undefined"!=typeof module&&(module.exports=scrypt); \ No newline at end of file diff --git a/test/unittests.js b/test/unittests.js index bad256c..19d53ca 100644 --- a/test/unittests.js +++ b/test/unittests.js @@ -2,15 +2,6 @@ var scrypt = require('../scrypt-async.js'); var assert = require("assert"); var inputs = [ - { - password: 'this is a long \x00 password', - salt: 'and this is a long \x00 salt', - logN: 14, - r: 8, - dkLen: 256, - encoding: 'hex', - result: 'c3f182ee2dec846e70a6942fb529985a3a09765ef04c612923b17f18555a37076deb2b9830d69de5492651e4506ae5776d96d40f67aaee37e1777b8ad5c3111432bb3b6f7e1264401879e641aea2bd0a21a124fd5c1ece0891338d2c44ba312e497bd93660fc053a5df35ade0ca48fd0f3c6c0f6143bb3548420a7cbf6ce7c82bc6b56c8e33adbf6fbac9e0ffc4aa9fb9fcd97fd393700b7d8eac55d45d4651bdb1a270c35c8d40a22e1b2429d6521c4c673e4ba7e7f4a9638ec3b1adbc6dcab64e211b5a26df8f274511be41228cd9a4fae3ada5236ebf39dfc6cd1864652a16516fb622502205d9fdbf09dc6fa964b57cc468ee8d98e4a00bf064222dafec8' - }, { password: 'p', salt: 's', @@ -47,15 +38,6 @@ var inputs = [ encoding: 'base64', result: 'd9ZXYjhleyA7GcpCwYoEl/FrSETjB0ro39/6P+3iFEL80Aad7QlI+DJqdToPyB8X6NPg+y4NNijPNeIMONGJBs5zIGZWz4werX9PZjDQra4f2IeLd8O0aduRnwFZf2E6wveK7FpcZ8JVgzEZ6z5mtpd+bn4y4IV7eW37vCfj4HbldcVfZhVjQy1EUkJ9iiGkv8D095UyGB0BHsvZ8hDjAbkl6CytuuwBXS2ksRIvUestefIuKLfhIEoV50H+v5uUUm1X5yd9eqEumsilj9TEL2kiRwFWy9J57BYZxXTUXEF3KGMigDxgRtixTB9sVrbi4i0HfgoxdLqddlqE9SsRzQ==' }, - { - password: 'pleaseletmein', - salt: 'SodiumChloride', - logN: 14, - r: 8, - dkLen: 256, - encoding: 'hex', - result: '7023bdcb3afd7348461c06cd81fd38ebfda8fbba904f8e3ea9b543f6545da1f2d5432955613f0fcf62d49705242a9af9e61e85dc0d651e40dfcf017b45575887c3b5417f26036e90e9c1fe355d24ee3623c8b8bad9b9aa93286c6429dbc0bfa2e69326c0806f7dc5f825a6b9cc32d18483a117c1ea78e2f38675579c811c0b67c262dbab7fe2b6d989d07fac3443c859cf7b34fed8cc5b279f7bdbf6e0cd8d90a82fe56f3ac7a5b81f98275c9c5cb69f19c8bddc33db6bd7da847ec3197e13f33f70b00a46836b7c0a0379559160ef42c8332097f7ca265fe6ff972ce1ffb515ff7e4e715e4c92839113ea67f2515f311549fc7eee49804136fb0830abb943d3' - }, { password: 'пароль', salt: 'соль', @@ -74,18 +56,6 @@ var inputs = [ encoding: 'base64', result: 'FYjFuH0fb+qfG0Q0n5WFVQ==' }, - { - password: 'hello', - salt: 'world', - logN: 5, - r: 8, - dkLen: 48, - encoding: null, - result: [212, 108, 63, 108, 230, 193, 1, 5, 181, 168, 169, 234, 8, 53, 241, - 76, 44, 108, 85, 218, 223, 158, 113, 64, 94, 114, 7, 160, 1, 160, 174, - 43, 11, 22, 144, 102, 217, 198, 114, 226, 91, 245, 240, 80, 28, 210, 107, - 239] - }, { password: [104, 101, 108, 108, 111], // "hello" salt: [208, 188, 208, 184, 209, 128], // "мир" @@ -239,52 +209,173 @@ var inputs = [ dkLen: 16, encoding: 'hex', result: '518a355ac6468a4d98708adf03577df6' + }, + { + password: 'hello', + salt: 'world', + logN: 5, + r: 8, + dkLen: 48, + encoding: null, + result: [212, 108, 63, 108, 230, 193, 1, 5, 181, 168, 169, 234, 8, 53, 241, + 76, 44, 108, 85, 218, 223, 158, 113, 64, 94, 114, 7, 160, 1, 160, 174, + 43, 11, 22, 144, 102, 217, 198, 114, 226, 91, 245, 240, 80, 28, 210, 107, + 239] + }, + { + password: 'pleaseletmein', + salt: 'SodiumChloride', + logN: 14, + r: 8, + dkLen: 256, + encoding: 'hex', + result: '7023bdcb3afd7348461c06cd81fd38ebfda8fbba904f8e3ea9b543f6545da1f2d5432955613f0fcf62d49705242a9af9e61e85dc0d651e40dfcf017b45575887c3b5417f26036e90e9c1fe355d24ee3623c8b8bad9b9aa93286c6429dbc0bfa2e69326c0806f7dc5f825a6b9cc32d18483a117c1ea78e2f38675579c811c0b67c262dbab7fe2b6d989d07fac3443c859cf7b34fed8cc5b279f7bdbf6e0cd8d90a82fe56f3ac7a5b81f98275c9c5cb69f19c8bddc33db6bd7da847ec3197e13f33f70b00a46836b7c0a0379559160ef42c8332097f7ca265fe6ff972ce1ffb515ff7e4e715e4c92839113ea67f2515f311549fc7eee49804136fb0830abb943d3' + }, + { + password: 'this is a long \x00 password', + salt: 'and this is a long \x00 salt', + logN: 14, + r: 8, + dkLen: 256, + encoding: 'hex', + result: 'c3f182ee2dec846e70a6942fb529985a3a09765ef04c612923b17f18555a37076deb2b9830d69de5492651e4506ae5776d96d40f67aaee37e1777b8ad5c3111432bb3b6f7e1264401879e641aea2bd0a21a124fd5c1ece0891338d2c44ba312e497bd93660fc053a5df35ade0ca48fd0f3c6c0f6143bb3548420a7cbf6ce7c82bc6b56c8e33adbf6fbac9e0ffc4aa9fb9fcd97fd393700b7d8eac55d45d4651bdb1a270c35c8d40a22e1b2429d6521c4c673e4ba7e7f4a9638ec3b1adbc6dcab64e211b5a26df8f274511be41228cd9a4fae3ada5236ebf39dfc6cd1864652a16516fb622502205d9fdbf09dc6fa964b57cc468ee8d98e4a00bf064222dafec8' } -] +]; + +var shortInput = { + password: 'password', + salt: 'salt', + logN: 1, + r: 1, + dkLen: 16, + hexResult: '6d1bb878eee9ce4a7b77d7a44103574d', + result: [109, 27, 184, 120, 238, 233, 206, 74, 123, 119, 215, 164, 65, 3, 87, 77] +}; + +describe('limits test', function() { + var v = shortInput; -var input_output_test = function(i, done) { + it('should throw with too small logN', function() { + assert.throws(function() { + scrypt(v.password, v.salt, 0, v.r, v.dkLen); + }, Error); + }); + + it('should throw with too big logN', function() { + assert.throws(function() { + scrypt(v.password, v.salt, 32, v.r, v.dkLen); + }, Error); + }); + + it('should throw with too large parameters', function() { + assert.throws(function() { + scrypt(v.password, v.salt, v.logN, 1<<31, v.dkLen); + }, Error); + }); + +}); + +describe('argument order test', function() { + this.timeout(100000); + + var v = shortInput; + + it('all arguments', function(done) { + scrypt(v.password, v.salt, v.logN, v.r, v.dkLen, 1000, 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) { + 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) { var v = inputs[i]; - 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, interruptStep, function(out) { assert.deepEqual(v.result, out); done(); }, v.encoding); } -describe('input/output test', function(){ +describe('async input/output test', function() { this.timeout(100000); + var step = 1000; + it('input 0', function(done) { - input_output_test(0, done); + async_test(0, step, done); }); it('input 1', function(done) { - input_output_test(1, done); + async_test(1, step, done); }); it('input 2', function(done) { - input_output_test(2, done); + async_test(2, step, done); }); it('input 3', function(done) { - input_output_test(3, done); + async_test(3, step, done); }); it('input 4', function(done) { - input_output_test(4, done); + async_test(4, step, done); }); it('input 5', function(done) { - input_output_test(5, done); + async_test(5, step, done); }); it('input 6', function(done) { - input_output_test(6, done); + async_test(6, step, done); }); it('input 7', function(done) { - input_output_test(7, done); + async_test(7, step, done); }); it('input 8', function(done) { - input_output_test(8, done); + async_test(8, step, done); }); it('input 9', function(done) { - input_output_test(9, done); + async_test(9, step, done); }); it('input 10', function(done) { - input_output_test(10, 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); }); });