scrypt.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. exports.scryptAsync = exports.scrypt = void 0;
  4. const _assert_js_1 = require("./_assert.js");
  5. const sha256_js_1 = require("./sha256.js");
  6. const pbkdf2_js_1 = require("./pbkdf2.js");
  7. const utils_js_1 = require("./utils.js");
  8. // RFC 7914 Scrypt KDF
  9. // The main Scrypt loop: uses Salsa extensively.
  10. // Six versions of the function were tried, this is the fastest one.
  11. // prettier-ignore
  12. function XorAndSalsa(prev, pi, input, ii, out, oi) {
  13. // Based on https://cr.yp.to/salsa20.html
  14. // Xor blocks
  15. let y00 = prev[pi++] ^ input[ii++], y01 = prev[pi++] ^ input[ii++];
  16. let y02 = prev[pi++] ^ input[ii++], y03 = prev[pi++] ^ input[ii++];
  17. let y04 = prev[pi++] ^ input[ii++], y05 = prev[pi++] ^ input[ii++];
  18. let y06 = prev[pi++] ^ input[ii++], y07 = prev[pi++] ^ input[ii++];
  19. let y08 = prev[pi++] ^ input[ii++], y09 = prev[pi++] ^ input[ii++];
  20. let y10 = prev[pi++] ^ input[ii++], y11 = prev[pi++] ^ input[ii++];
  21. let y12 = prev[pi++] ^ input[ii++], y13 = prev[pi++] ^ input[ii++];
  22. let y14 = prev[pi++] ^ input[ii++], y15 = prev[pi++] ^ input[ii++];
  23. // Save state to temporary variables (salsa)
  24. let x00 = y00, x01 = y01, x02 = y02, x03 = y03, x04 = y04, x05 = y05, x06 = y06, x07 = y07, x08 = y08, x09 = y09, x10 = y10, x11 = y11, x12 = y12, x13 = y13, x14 = y14, x15 = y15;
  25. // Main loop (salsa)
  26. for (let i = 0; i < 8; i += 2) {
  27. x04 ^= (0, utils_js_1.rotl)(x00 + x12 | 0, 7);
  28. x08 ^= (0, utils_js_1.rotl)(x04 + x00 | 0, 9);
  29. x12 ^= (0, utils_js_1.rotl)(x08 + x04 | 0, 13);
  30. x00 ^= (0, utils_js_1.rotl)(x12 + x08 | 0, 18);
  31. x09 ^= (0, utils_js_1.rotl)(x05 + x01 | 0, 7);
  32. x13 ^= (0, utils_js_1.rotl)(x09 + x05 | 0, 9);
  33. x01 ^= (0, utils_js_1.rotl)(x13 + x09 | 0, 13);
  34. x05 ^= (0, utils_js_1.rotl)(x01 + x13 | 0, 18);
  35. x14 ^= (0, utils_js_1.rotl)(x10 + x06 | 0, 7);
  36. x02 ^= (0, utils_js_1.rotl)(x14 + x10 | 0, 9);
  37. x06 ^= (0, utils_js_1.rotl)(x02 + x14 | 0, 13);
  38. x10 ^= (0, utils_js_1.rotl)(x06 + x02 | 0, 18);
  39. x03 ^= (0, utils_js_1.rotl)(x15 + x11 | 0, 7);
  40. x07 ^= (0, utils_js_1.rotl)(x03 + x15 | 0, 9);
  41. x11 ^= (0, utils_js_1.rotl)(x07 + x03 | 0, 13);
  42. x15 ^= (0, utils_js_1.rotl)(x11 + x07 | 0, 18);
  43. x01 ^= (0, utils_js_1.rotl)(x00 + x03 | 0, 7);
  44. x02 ^= (0, utils_js_1.rotl)(x01 + x00 | 0, 9);
  45. x03 ^= (0, utils_js_1.rotl)(x02 + x01 | 0, 13);
  46. x00 ^= (0, utils_js_1.rotl)(x03 + x02 | 0, 18);
  47. x06 ^= (0, utils_js_1.rotl)(x05 + x04 | 0, 7);
  48. x07 ^= (0, utils_js_1.rotl)(x06 + x05 | 0, 9);
  49. x04 ^= (0, utils_js_1.rotl)(x07 + x06 | 0, 13);
  50. x05 ^= (0, utils_js_1.rotl)(x04 + x07 | 0, 18);
  51. x11 ^= (0, utils_js_1.rotl)(x10 + x09 | 0, 7);
  52. x08 ^= (0, utils_js_1.rotl)(x11 + x10 | 0, 9);
  53. x09 ^= (0, utils_js_1.rotl)(x08 + x11 | 0, 13);
  54. x10 ^= (0, utils_js_1.rotl)(x09 + x08 | 0, 18);
  55. x12 ^= (0, utils_js_1.rotl)(x15 + x14 | 0, 7);
  56. x13 ^= (0, utils_js_1.rotl)(x12 + x15 | 0, 9);
  57. x14 ^= (0, utils_js_1.rotl)(x13 + x12 | 0, 13);
  58. x15 ^= (0, utils_js_1.rotl)(x14 + x13 | 0, 18);
  59. }
  60. // Write output (salsa)
  61. out[oi++] = (y00 + x00) | 0;
  62. out[oi++] = (y01 + x01) | 0;
  63. out[oi++] = (y02 + x02) | 0;
  64. out[oi++] = (y03 + x03) | 0;
  65. out[oi++] = (y04 + x04) | 0;
  66. out[oi++] = (y05 + x05) | 0;
  67. out[oi++] = (y06 + x06) | 0;
  68. out[oi++] = (y07 + x07) | 0;
  69. out[oi++] = (y08 + x08) | 0;
  70. out[oi++] = (y09 + x09) | 0;
  71. out[oi++] = (y10 + x10) | 0;
  72. out[oi++] = (y11 + x11) | 0;
  73. out[oi++] = (y12 + x12) | 0;
  74. out[oi++] = (y13 + x13) | 0;
  75. out[oi++] = (y14 + x14) | 0;
  76. out[oi++] = (y15 + x15) | 0;
  77. }
  78. function BlockMix(input, ii, out, oi, r) {
  79. // The block B is r 128-byte chunks (which is equivalent of 2r 64-byte chunks)
  80. let head = oi + 0;
  81. let tail = oi + 16 * r;
  82. for (let i = 0; i < 16; i++)
  83. out[tail + i] = input[ii + (2 * r - 1) * 16 + i]; // X ← B[2r−1]
  84. for (let i = 0; i < r; i++, head += 16, ii += 16) {
  85. // We write odd & even Yi at same time. Even: 0bXXXXX0 Odd: 0bXXXXX1
  86. XorAndSalsa(out, tail, input, ii, out, head); // head[i] = Salsa(blockIn[2*i] ^ tail[i-1])
  87. if (i > 0)
  88. tail += 16; // First iteration overwrites tmp value in tail
  89. XorAndSalsa(out, head, input, (ii += 16), out, tail); // tail[i] = Salsa(blockIn[2*i+1] ^ head[i])
  90. }
  91. }
  92. // Common prologue and epilogue for sync/async functions
  93. function scryptInit(password, salt, _opts) {
  94. // Maxmem - 1GB+1KB by default
  95. const opts = (0, utils_js_1.checkOpts)({
  96. dkLen: 32,
  97. asyncTick: 10,
  98. maxmem: 1024 ** 3 + 1024,
  99. }, _opts);
  100. const { N, r, p, dkLen, asyncTick, maxmem, onProgress } = opts;
  101. (0, _assert_js_1.number)(N);
  102. (0, _assert_js_1.number)(r);
  103. (0, _assert_js_1.number)(p);
  104. (0, _assert_js_1.number)(dkLen);
  105. (0, _assert_js_1.number)(asyncTick);
  106. (0, _assert_js_1.number)(maxmem);
  107. if (onProgress !== undefined && typeof onProgress !== 'function')
  108. throw new Error('progressCb should be function');
  109. const blockSize = 128 * r;
  110. const blockSize32 = blockSize / 4;
  111. if (N <= 1 || (N & (N - 1)) !== 0 || N >= 2 ** (blockSize / 8) || N > 2 ** 32) {
  112. // NOTE: we limit N to be less than 2**32 because of 32 bit variant of Integrify function
  113. // There is no JS engines that allows alocate more than 4GB per single Uint8Array for now, but can change in future.
  114. throw new Error('Scrypt: N must be larger than 1, a power of 2, less than 2^(128 * r / 8) and less than 2^32');
  115. }
  116. if (p < 0 || p > ((2 ** 32 - 1) * 32) / blockSize) {
  117. throw new Error('Scrypt: p must be a positive integer less than or equal to ((2^32 - 1) * 32) / (128 * r)');
  118. }
  119. if (dkLen < 0 || dkLen > (2 ** 32 - 1) * 32) {
  120. throw new Error('Scrypt: dkLen should be positive integer less than or equal to (2^32 - 1) * 32');
  121. }
  122. const memUsed = blockSize * (N + p);
  123. if (memUsed > maxmem) {
  124. throw new Error(`Scrypt: parameters too large, ${memUsed} (128 * r * (N + p)) > ${maxmem} (maxmem)`);
  125. }
  126. // [B0...Bp−1] ← PBKDF2HMAC-SHA256(Passphrase, Salt, 1, blockSize*ParallelizationFactor)
  127. // Since it has only one iteration there is no reason to use async variant
  128. const B = (0, pbkdf2_js_1.pbkdf2)(sha256_js_1.sha256, password, salt, { c: 1, dkLen: blockSize * p });
  129. const B32 = (0, utils_js_1.u32)(B);
  130. // Re-used between parallel iterations. Array(iterations) of B
  131. const V = (0, utils_js_1.u32)(new Uint8Array(blockSize * N));
  132. const tmp = (0, utils_js_1.u32)(new Uint8Array(blockSize));
  133. let blockMixCb = () => { };
  134. if (onProgress) {
  135. const totalBlockMix = 2 * N * p;
  136. // Invoke callback if progress changes from 10.01 to 10.02
  137. // Allows to draw smooth progress bar on up to 8K screen
  138. const callbackPer = Math.max(Math.floor(totalBlockMix / 10000), 1);
  139. let blockMixCnt = 0;
  140. blockMixCb = () => {
  141. blockMixCnt++;
  142. if (onProgress && (!(blockMixCnt % callbackPer) || blockMixCnt === totalBlockMix))
  143. onProgress(blockMixCnt / totalBlockMix);
  144. };
  145. }
  146. return { N, r, p, dkLen, blockSize32, V, B32, B, tmp, blockMixCb, asyncTick };
  147. }
  148. function scryptOutput(password, dkLen, B, V, tmp) {
  149. const res = (0, pbkdf2_js_1.pbkdf2)(sha256_js_1.sha256, password, B, { c: 1, dkLen });
  150. B.fill(0);
  151. V.fill(0);
  152. tmp.fill(0);
  153. return res;
  154. }
  155. /**
  156. * Scrypt KDF from RFC 7914.
  157. * @param password - pass
  158. * @param salt - salt
  159. * @param opts - parameters
  160. * - `N` is cpu/mem work factor (power of 2 e.g. 2**18)
  161. * - `r` is block size (8 is common), fine-tunes sequential memory read size and performance
  162. * - `p` is parallelization factor (1 is common)
  163. * - `dkLen` is output key length in bytes e.g. 32.
  164. * - `asyncTick` - (default: 10) max time in ms for which async function can block execution
  165. * - `maxmem` - (default: `1024 ** 3 + 1024` aka 1GB+1KB). A limit that the app could use for scrypt
  166. * - `onProgress` - callback function that would be executed for progress report
  167. * @returns Derived key
  168. */
  169. function scrypt(password, salt, opts) {
  170. const { N, r, p, dkLen, blockSize32, V, B32, B, tmp, blockMixCb } = scryptInit(password, salt, opts);
  171. if (!utils_js_1.isLE)
  172. (0, utils_js_1.byteSwap32)(B32);
  173. for (let pi = 0; pi < p; pi++) {
  174. const Pi = blockSize32 * pi;
  175. for (let i = 0; i < blockSize32; i++)
  176. V[i] = B32[Pi + i]; // V[0] = B[i]
  177. for (let i = 0, pos = 0; i < N - 1; i++) {
  178. BlockMix(V, pos, V, (pos += blockSize32), r); // V[i] = BlockMix(V[i-1]);
  179. blockMixCb();
  180. }
  181. BlockMix(V, (N - 1) * blockSize32, B32, Pi, r); // Process last element
  182. blockMixCb();
  183. for (let i = 0; i < N; i++) {
  184. // First u32 of the last 64-byte block (u32 is LE)
  185. const j = B32[Pi + blockSize32 - 16] % N; // j = Integrify(X) % iterations
  186. for (let k = 0; k < blockSize32; k++)
  187. tmp[k] = B32[Pi + k] ^ V[j * blockSize32 + k]; // tmp = B ^ V[j]
  188. BlockMix(tmp, 0, B32, Pi, r); // B = BlockMix(B ^ V[j])
  189. blockMixCb();
  190. }
  191. }
  192. if (!utils_js_1.isLE)
  193. (0, utils_js_1.byteSwap32)(B32);
  194. return scryptOutput(password, dkLen, B, V, tmp);
  195. }
  196. exports.scrypt = scrypt;
  197. /**
  198. * Scrypt KDF from RFC 7914.
  199. */
  200. async function scryptAsync(password, salt, opts) {
  201. const { N, r, p, dkLen, blockSize32, V, B32, B, tmp, blockMixCb, asyncTick } = scryptInit(password, salt, opts);
  202. if (!utils_js_1.isLE)
  203. (0, utils_js_1.byteSwap32)(B32);
  204. for (let pi = 0; pi < p; pi++) {
  205. const Pi = blockSize32 * pi;
  206. for (let i = 0; i < blockSize32; i++)
  207. V[i] = B32[Pi + i]; // V[0] = B[i]
  208. let pos = 0;
  209. await (0, utils_js_1.asyncLoop)(N - 1, asyncTick, () => {
  210. BlockMix(V, pos, V, (pos += blockSize32), r); // V[i] = BlockMix(V[i-1]);
  211. blockMixCb();
  212. });
  213. BlockMix(V, (N - 1) * blockSize32, B32, Pi, r); // Process last element
  214. blockMixCb();
  215. await (0, utils_js_1.asyncLoop)(N, asyncTick, () => {
  216. // First u32 of the last 64-byte block (u32 is LE)
  217. const j = B32[Pi + blockSize32 - 16] % N; // j = Integrify(X) % iterations
  218. for (let k = 0; k < blockSize32; k++)
  219. tmp[k] = B32[Pi + k] ^ V[j * blockSize32 + k]; // tmp = B ^ V[j]
  220. BlockMix(tmp, 0, B32, Pi, r); // B = BlockMix(B ^ V[j])
  221. blockMixCb();
  222. });
  223. }
  224. if (!utils_js_1.isLE)
  225. (0, utils_js_1.byteSwap32)(B32);
  226. return scryptOutput(password, dkLen, B, V, tmp);
  227. }
  228. exports.scryptAsync = scryptAsync;
  229. //# sourceMappingURL=scrypt.js.map