@lakutata/security 3.0.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/LICENSE +23 -0
  2. package/README.md +143 -0
  3. package/THIRD_PARTY_NOTICES.md +36 -0
  4. package/dist/cjs/exceptions/DecryptionFailedException.d.ts +13 -0
  5. package/dist/cjs/exceptions/DecryptionFailedException.js +20 -0
  6. package/dist/cjs/exceptions/InvalidSignatureException.d.ts +13 -0
  7. package/dist/cjs/exceptions/InvalidSignatureException.js +20 -0
  8. package/dist/cjs/exceptions/PasswordTooLongException.d.ts +12 -0
  9. package/dist/cjs/exceptions/PasswordTooLongException.js +19 -0
  10. package/dist/cjs/exports/Security.d.ts +19 -0
  11. package/dist/cjs/exports/Security.js +38 -0
  12. package/dist/cjs/functions/Encryption.d.ts +95 -0
  13. package/dist/cjs/functions/Encryption.js +208 -0
  14. package/dist/cjs/functions/GenerateSecret.d.ts +20 -0
  15. package/dist/cjs/functions/GenerateSecret.js +26 -0
  16. package/dist/cjs/functions/HMAC.d.ts +50 -0
  17. package/dist/cjs/functions/HMAC.js +58 -0
  18. package/dist/cjs/functions/TimingSafeEqual.d.ts +18 -0
  19. package/dist/cjs/functions/TimingSafeEqual.js +30 -0
  20. package/dist/cjs/lib/AssertKey.d.ts +5 -0
  21. package/dist/cjs/lib/AssertKey.js +11 -0
  22. package/dist/cjs/lib/bcrypt/constants.d.ts +13 -0
  23. package/dist/cjs/lib/bcrypt/constants.js +213 -0
  24. package/dist/cjs/lib/bcrypt/index.d.ts +84 -0
  25. package/dist/cjs/lib/bcrypt/index.js +658 -0
  26. package/dist/cjs/package.json +1 -0
  27. package/dist/cjs/providers/Crypt.d.ts +71 -0
  28. package/dist/cjs/providers/Crypt.js +88 -0
  29. package/dist/cjs/providers/Mac.d.ts +86 -0
  30. package/dist/cjs/providers/Mac.js +114 -0
  31. package/dist/cjs/providers/PasswordHash.d.ts +78 -0
  32. package/dist/cjs/providers/PasswordHash.js +146 -0
  33. package/dist/esm/exceptions/DecryptionFailedException.js +16 -0
  34. package/dist/esm/exceptions/InvalidSignatureException.js +16 -0
  35. package/dist/esm/exceptions/PasswordTooLongException.js +15 -0
  36. package/dist/esm/exports/Security.js +19 -0
  37. package/dist/esm/functions/Encryption.js +202 -0
  38. package/dist/esm/functions/GenerateSecret.js +23 -0
  39. package/dist/esm/functions/HMAC.js +54 -0
  40. package/dist/esm/functions/TimingSafeEqual.js +27 -0
  41. package/dist/esm/lib/AssertKey.js +8 -0
  42. package/dist/esm/lib/bcrypt/constants.js +202 -0
  43. package/dist/esm/lib/bcrypt/index.js +645 -0
  44. package/dist/esm/providers/Crypt.js +84 -0
  45. package/dist/esm/providers/Mac.js +110 -0
  46. package/dist/esm/providers/PasswordHash.js +109 -0
  47. package/dist/types/exceptions/DecryptionFailedException.d.ts +13 -0
  48. package/dist/types/exceptions/InvalidSignatureException.d.ts +13 -0
  49. package/dist/types/exceptions/PasswordTooLongException.d.ts +12 -0
  50. package/dist/types/exports/Security.d.ts +19 -0
  51. package/dist/types/functions/Encryption.d.ts +95 -0
  52. package/dist/types/functions/GenerateSecret.d.ts +20 -0
  53. package/dist/types/functions/HMAC.d.ts +50 -0
  54. package/dist/types/functions/TimingSafeEqual.d.ts +18 -0
  55. package/dist/types/lib/AssertKey.d.ts +5 -0
  56. package/dist/types/lib/bcrypt/constants.d.ts +13 -0
  57. package/dist/types/lib/bcrypt/index.d.ts +84 -0
  58. package/dist/types/providers/Crypt.d.ts +71 -0
  59. package/dist/types/providers/Mac.d.ts +86 -0
  60. package/dist/types/providers/PasswordHash.d.ts +78 -0
  61. package/package.json +45 -0
@@ -0,0 +1,658 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.genSaltSync = genSaltSync;
4
+ exports.genSalt = genSalt;
5
+ exports.hashSync = hashSync;
6
+ exports.hash = hash;
7
+ exports.compareSync = compareSync;
8
+ exports.compare = compare;
9
+ exports.getRounds = getRounds;
10
+ exports.getSalt = getSalt;
11
+ exports.truncates = truncates;
12
+ exports.encodeBase64 = encodeBase64;
13
+ exports.decodeBase64 = decodeBase64;
14
+ /**
15
+ * Derived from the upstream index.js (see UPSTREAM.md; BSD-3-Clause, see THIRD_PARTY_NOTICES.md)
16
+ *
17
+ * The bcrypt password hashing (Blowfish based), compatible with the other bcrypt implementations ($2a$, $2b$, $2y$).
18
+ */
19
+ const node_crypto_1 = require("node:crypto");
20
+ const constants_js_1 = require("./constants.js");
21
+ /**
22
+ * Continues after yielding to the event loop
23
+ */
24
+ const nextTick = setImmediate;
25
+ /**
26
+ * Synchronously generates a salt.
27
+ * @param rounds Number of rounds to use, defaults to 10 if omitted (from 4 to 31)
28
+ * @returns Resulting salt
29
+ */
30
+ function genSaltSync(rounds) {
31
+ rounds = rounds || constants_js_1.GENSALT_DEFAULT_LOG2_ROUNDS;
32
+ if (typeof rounds !== 'number')
33
+ throw Error('Illegal arguments: ' + typeof rounds + ', undefined');
34
+ if (rounds < 4) {
35
+ rounds = 4;
36
+ }
37
+ else if (rounds > 31) {
38
+ rounds = 31;
39
+ }
40
+ const salt = [];
41
+ salt.push('$2b$');
42
+ if (rounds < 10)
43
+ salt.push('0');
44
+ salt.push(rounds.toString());
45
+ salt.push('$');
46
+ salt.push(base64Encode((0, node_crypto_1.randomBytes)(constants_js_1.BCRYPT_SALT_LEN), constants_js_1.BCRYPT_SALT_LEN));
47
+ return salt.join('');
48
+ }
49
+ /**
50
+ * Asynchronously generates a salt.
51
+ * @param rounds Number of rounds to use, defaults to 10 if omitted
52
+ * @returns Resulting salt
53
+ */
54
+ function genSalt(rounds) {
55
+ if (typeof rounds === 'undefined') {
56
+ rounds = constants_js_1.GENSALT_DEFAULT_LOG2_ROUNDS;
57
+ }
58
+ else if (typeof rounds !== 'number') {
59
+ throw Error('illegal arguments: ' + typeof rounds);
60
+ }
61
+ const saltRounds = rounds;
62
+ return new Promise((resolve, reject) => nextTick(() => {
63
+ //Pretty thin, but salting is fast enough
64
+ try {
65
+ resolve(genSaltSync(saltRounds));
66
+ }
67
+ catch (error) {
68
+ reject(error);
69
+ }
70
+ }));
71
+ }
72
+ /**
73
+ * Synchronously generates a hash for the given password.
74
+ * @param password Password to hash
75
+ * @param salt Salt length to generate or salt to use, default to 10
76
+ * @returns Resulting hash
77
+ */
78
+ function hashSync(password, salt = constants_js_1.GENSALT_DEFAULT_LOG2_ROUNDS) {
79
+ if (typeof salt === 'number')
80
+ salt = genSaltSync(salt);
81
+ if (typeof password !== 'string' || typeof salt !== 'string')
82
+ throw Error('Illegal arguments: ' + typeof password + ', ' + typeof salt);
83
+ return hashPassword(password, salt);
84
+ }
85
+ /**
86
+ * Asynchronously generates a hash for the given password.
87
+ * @param password Password to hash
88
+ * @param salt Salt length to generate or salt to use
89
+ * @param progressCallback Callback successively called with the percentage of rounds completed (0.0 - 1.0), maximally
90
+ * once per `MAX_EXECUTION_TIME = 100` ms.
91
+ * @returns Resulting hash
92
+ */
93
+ async function hash(password, salt, progressCallback) {
94
+ if (typeof password === 'string' && typeof salt === 'number')
95
+ salt = await genSalt(salt);
96
+ if (typeof password !== 'string' || typeof salt !== 'string') {
97
+ const error = Error('Illegal arguments: ' + typeof password + ', ' + typeof salt);
98
+ await new Promise((resolve) => nextTick(resolve));
99
+ throw error;
100
+ }
101
+ return await hashPasswordAsync(password, salt, progressCallback);
102
+ }
103
+ /**
104
+ * Compares two strings of the same length in constant time.
105
+ * @param known Must be of the correct length
106
+ * @param unknown Must be the same length as `known`
107
+ */
108
+ function safeStringCompare(known, unknown) {
109
+ let diff = known.length ^ unknown.length;
110
+ for (let i = 0; i < known.length; ++i) {
111
+ diff |= known.charCodeAt(i) ^ unknown.charCodeAt(i);
112
+ }
113
+ return diff === 0;
114
+ }
115
+ /**
116
+ * Synchronously tests a password against a hash.
117
+ * @param password Password to compare
118
+ * @param hash Hash to test against
119
+ * @returns true if matching, otherwise false
120
+ */
121
+ function compareSync(password, hash) {
122
+ if (typeof password !== 'string' || typeof hash !== 'string')
123
+ throw Error('Illegal arguments: ' + typeof password + ', ' + typeof hash);
124
+ if (hash.length !== 60)
125
+ return false;
126
+ return safeStringCompare(hashSync(password, hash.substring(0, hash.length - 31)), hash);
127
+ }
128
+ /**
129
+ * Asynchronously tests a password against a hash.
130
+ * @param password Password to compare
131
+ * @param hashValue Hash to test against
132
+ * @param progressCallback Callback successively called with the percentage of rounds completed (0.0 - 1.0), maximally
133
+ * once per `MAX_EXECUTION_TIME = 100` ms.
134
+ * @returns true if matching, otherwise false
135
+ */
136
+ async function compare(password, hashValue, progressCallback) {
137
+ if (typeof password !== 'string' || typeof hashValue !== 'string') {
138
+ const error = Error('Illegal arguments: ' + typeof password + ', ' + typeof hashValue);
139
+ await new Promise((resolve) => nextTick(resolve));
140
+ throw error;
141
+ }
142
+ if (hashValue.length !== 60) {
143
+ await new Promise((resolve) => nextTick(resolve));
144
+ return false;
145
+ }
146
+ return safeStringCompare(await hash(password, hashValue.substring(0, 29), progressCallback), hashValue);
147
+ }
148
+ /**
149
+ * Gets the number of rounds used to encrypt the specified hash.
150
+ * @param hash Hash to extract the used number of rounds from
151
+ * @returns Number of rounds used
152
+ */
153
+ function getRounds(hash) {
154
+ if (typeof hash !== 'string')
155
+ throw Error('Illegal arguments: ' + typeof hash);
156
+ return parseInt(hash.split('$')[2], 10);
157
+ }
158
+ /**
159
+ * Gets the salt portion from a hash. Does not validate the hash.
160
+ * @param hash Hash to extract the salt from
161
+ * @returns Extracted salt part
162
+ */
163
+ function getSalt(hash) {
164
+ if (typeof hash !== 'string')
165
+ throw Error('Illegal arguments: ' + typeof hash);
166
+ if (hash.length !== 60)
167
+ throw Error('Illegal hash length: ' + hash.length + ' != 60');
168
+ return hash.substring(0, 29);
169
+ }
170
+ /**
171
+ * Tests if a password will be truncated when hashed, that is its length is greater than 72 bytes when converted to
172
+ * UTF-8.
173
+ * @param password The password to test
174
+ * @returns `true` if truncated, otherwise `false`
175
+ */
176
+ function truncates(password) {
177
+ if (typeof password !== 'string')
178
+ throw Error('Illegal arguments: ' + typeof password);
179
+ return utf8Length(password) > 72;
180
+ }
181
+ /**
182
+ * Calculates the byte length of a string encoded as UTF8.
183
+ * @param string
184
+ */
185
+ function utf8Length(string) {
186
+ let length = 0;
187
+ for (let i = 0; i < string.length; ++i) {
188
+ const c = string.charCodeAt(i);
189
+ if (c < 128) {
190
+ length += 1;
191
+ }
192
+ else if (c < 2048) {
193
+ length += 2;
194
+ }
195
+ else if ((c & 0xfc00) === 0xd800 && (string.charCodeAt(i + 1) & 0xfc00) === 0xdc00) {
196
+ ++i;
197
+ length += 4;
198
+ }
199
+ else {
200
+ length += 3;
201
+ }
202
+ }
203
+ return length;
204
+ }
205
+ /**
206
+ * Converts a string to an array of UTF8 bytes.
207
+ * @param string
208
+ */
209
+ function utf8Array(string) {
210
+ let offset = 0;
211
+ const buffer = new Array(utf8Length(string));
212
+ for (let i = 0, k = string.length; i < k; ++i) {
213
+ let c1 = string.charCodeAt(i);
214
+ let c2;
215
+ if (c1 < 128) {
216
+ buffer[offset++] = c1;
217
+ }
218
+ else if (c1 < 2048) {
219
+ buffer[offset++] = (c1 >> 6) | 192;
220
+ buffer[offset++] = (c1 & 63) | 128;
221
+ }
222
+ else if ((c1 & 0xfc00) === 0xd800 && ((c2 = string.charCodeAt(i + 1)) & 0xfc00) === 0xdc00) {
223
+ c1 = 0x10000 + ((c1 & 0x03ff) << 10) + (c2 & 0x03ff);
224
+ ++i;
225
+ buffer[offset++] = (c1 >> 18) | 240;
226
+ buffer[offset++] = ((c1 >> 12) & 63) | 128;
227
+ buffer[offset++] = ((c1 >> 6) & 63) | 128;
228
+ buffer[offset++] = (c1 & 63) | 128;
229
+ }
230
+ else {
231
+ buffer[offset++] = (c1 >> 12) | 224;
232
+ buffer[offset++] = ((c1 >> 6) & 63) | 128;
233
+ buffer[offset++] = (c1 & 63) | 128;
234
+ }
235
+ }
236
+ return buffer;
237
+ }
238
+ /**
239
+ * Encodes a byte array to base64 with up to len bytes of input, using the bcrypt alphabet.
240
+ * @param b Byte array
241
+ * @param len Maximum input length
242
+ */
243
+ function base64Encode(b, len) {
244
+ let off = 0;
245
+ const rs = [];
246
+ if (len <= 0 || len > b.length)
247
+ throw Error('Illegal len: ' + len);
248
+ while (off < len) {
249
+ let c1 = b[off++] & 0xff;
250
+ rs.push(constants_js_1.BASE64_CODE[(c1 >> 2) & 0x3f]);
251
+ c1 = (c1 & 0x03) << 4;
252
+ if (off >= len) {
253
+ rs.push(constants_js_1.BASE64_CODE[c1 & 0x3f]);
254
+ break;
255
+ }
256
+ let c2 = b[off++] & 0xff;
257
+ c1 |= (c2 >> 4) & 0x0f;
258
+ rs.push(constants_js_1.BASE64_CODE[c1 & 0x3f]);
259
+ c1 = (c2 & 0x0f) << 2;
260
+ if (off >= len) {
261
+ rs.push(constants_js_1.BASE64_CODE[c1 & 0x3f]);
262
+ break;
263
+ }
264
+ c2 = b[off++] & 0xff;
265
+ c1 |= (c2 >> 6) & 0x03;
266
+ rs.push(constants_js_1.BASE64_CODE[c1 & 0x3f]);
267
+ rs.push(constants_js_1.BASE64_CODE[c2 & 0x3f]);
268
+ }
269
+ return rs.join('');
270
+ }
271
+ /**
272
+ * Decodes a base64 encoded string (bcrypt alphabet) to up to len bytes of output.
273
+ * @param s String to decode
274
+ * @param len Maximum output length
275
+ */
276
+ function base64Decode(s, len) {
277
+ let off = 0;
278
+ const slen = s.length;
279
+ let olen = 0;
280
+ const rs = [];
281
+ if (len <= 0)
282
+ throw Error('Illegal len: ' + len);
283
+ while (off < slen - 1 && olen < len) {
284
+ let code = s.charCodeAt(off++);
285
+ const c1 = code < constants_js_1.BASE64_INDEX.length ? constants_js_1.BASE64_INDEX[code] : -1;
286
+ code = s.charCodeAt(off++);
287
+ const c2 = code < constants_js_1.BASE64_INDEX.length ? constants_js_1.BASE64_INDEX[code] : -1;
288
+ if (c1 == -1 || c2 == -1)
289
+ break;
290
+ let o = (c1 << 2) >>> 0;
291
+ o |= (c2 & 0x30) >> 4;
292
+ rs.push(String.fromCharCode(o));
293
+ if (++olen >= len || off >= slen)
294
+ break;
295
+ code = s.charCodeAt(off++);
296
+ const c3 = code < constants_js_1.BASE64_INDEX.length ? constants_js_1.BASE64_INDEX[code] : -1;
297
+ if (c3 == -1)
298
+ break;
299
+ o = ((c2 & 0x0f) << 4) >>> 0;
300
+ o |= (c3 & 0x3c) >> 2;
301
+ rs.push(String.fromCharCode(o));
302
+ if (++olen >= len || off >= slen)
303
+ break;
304
+ code = s.charCodeAt(off++);
305
+ const c4 = code < constants_js_1.BASE64_INDEX.length ? constants_js_1.BASE64_INDEX[code] : -1;
306
+ o = ((c3 & 0x03) << 6) >>> 0;
307
+ o |= c4;
308
+ rs.push(String.fromCharCode(o));
309
+ ++olen;
310
+ }
311
+ const res = [];
312
+ for (off = 0; off < olen; off++)
313
+ res.push(rs[off].charCodeAt(0));
314
+ return res;
315
+ }
316
+ /**
317
+ * Blowfish encryption of the pair of words at off (the bottleneck: the loop is unrolled)
318
+ * @param lr
319
+ * @param off
320
+ * @param P
321
+ * @param S
322
+ */
323
+ function encipher(lr, off, P, S) {
324
+ let n;
325
+ let l = lr[off];
326
+ let r = lr[off + 1];
327
+ l ^= P[0];
328
+ //Iteration 0
329
+ n = S[l >>> 24];
330
+ n += S[0x100 | ((l >> 16) & 0xff)];
331
+ n ^= S[0x200 | ((l >> 8) & 0xff)];
332
+ n += S[0x300 | (l & 0xff)];
333
+ r ^= n ^ P[1];
334
+ n = S[r >>> 24];
335
+ n += S[0x100 | ((r >> 16) & 0xff)];
336
+ n ^= S[0x200 | ((r >> 8) & 0xff)];
337
+ n += S[0x300 | (r & 0xff)];
338
+ l ^= n ^ P[2];
339
+ //Iteration 1
340
+ n = S[l >>> 24];
341
+ n += S[0x100 | ((l >> 16) & 0xff)];
342
+ n ^= S[0x200 | ((l >> 8) & 0xff)];
343
+ n += S[0x300 | (l & 0xff)];
344
+ r ^= n ^ P[3];
345
+ n = S[r >>> 24];
346
+ n += S[0x100 | ((r >> 16) & 0xff)];
347
+ n ^= S[0x200 | ((r >> 8) & 0xff)];
348
+ n += S[0x300 | (r & 0xff)];
349
+ l ^= n ^ P[4];
350
+ //Iteration 2
351
+ n = S[l >>> 24];
352
+ n += S[0x100 | ((l >> 16) & 0xff)];
353
+ n ^= S[0x200 | ((l >> 8) & 0xff)];
354
+ n += S[0x300 | (l & 0xff)];
355
+ r ^= n ^ P[5];
356
+ n = S[r >>> 24];
357
+ n += S[0x100 | ((r >> 16) & 0xff)];
358
+ n ^= S[0x200 | ((r >> 8) & 0xff)];
359
+ n += S[0x300 | (r & 0xff)];
360
+ l ^= n ^ P[6];
361
+ //Iteration 3
362
+ n = S[l >>> 24];
363
+ n += S[0x100 | ((l >> 16) & 0xff)];
364
+ n ^= S[0x200 | ((l >> 8) & 0xff)];
365
+ n += S[0x300 | (l & 0xff)];
366
+ r ^= n ^ P[7];
367
+ n = S[r >>> 24];
368
+ n += S[0x100 | ((r >> 16) & 0xff)];
369
+ n ^= S[0x200 | ((r >> 8) & 0xff)];
370
+ n += S[0x300 | (r & 0xff)];
371
+ l ^= n ^ P[8];
372
+ //Iteration 4
373
+ n = S[l >>> 24];
374
+ n += S[0x100 | ((l >> 16) & 0xff)];
375
+ n ^= S[0x200 | ((l >> 8) & 0xff)];
376
+ n += S[0x300 | (l & 0xff)];
377
+ r ^= n ^ P[9];
378
+ n = S[r >>> 24];
379
+ n += S[0x100 | ((r >> 16) & 0xff)];
380
+ n ^= S[0x200 | ((r >> 8) & 0xff)];
381
+ n += S[0x300 | (r & 0xff)];
382
+ l ^= n ^ P[10];
383
+ //Iteration 5
384
+ n = S[l >>> 24];
385
+ n += S[0x100 | ((l >> 16) & 0xff)];
386
+ n ^= S[0x200 | ((l >> 8) & 0xff)];
387
+ n += S[0x300 | (l & 0xff)];
388
+ r ^= n ^ P[11];
389
+ n = S[r >>> 24];
390
+ n += S[0x100 | ((r >> 16) & 0xff)];
391
+ n ^= S[0x200 | ((r >> 8) & 0xff)];
392
+ n += S[0x300 | (r & 0xff)];
393
+ l ^= n ^ P[12];
394
+ //Iteration 6
395
+ n = S[l >>> 24];
396
+ n += S[0x100 | ((l >> 16) & 0xff)];
397
+ n ^= S[0x200 | ((l >> 8) & 0xff)];
398
+ n += S[0x300 | (l & 0xff)];
399
+ r ^= n ^ P[13];
400
+ n = S[r >>> 24];
401
+ n += S[0x100 | ((r >> 16) & 0xff)];
402
+ n ^= S[0x200 | ((r >> 8) & 0xff)];
403
+ n += S[0x300 | (r & 0xff)];
404
+ l ^= n ^ P[14];
405
+ //Iteration 7
406
+ n = S[l >>> 24];
407
+ n += S[0x100 | ((l >> 16) & 0xff)];
408
+ n ^= S[0x200 | ((l >> 8) & 0xff)];
409
+ n += S[0x300 | (l & 0xff)];
410
+ r ^= n ^ P[15];
411
+ n = S[r >>> 24];
412
+ n += S[0x100 | ((r >> 16) & 0xff)];
413
+ n ^= S[0x200 | ((r >> 8) & 0xff)];
414
+ n += S[0x300 | (r & 0xff)];
415
+ l ^= n ^ P[16];
416
+ lr[off] = r ^ P[constants_js_1.BLOWFISH_NUM_ROUNDS + 1];
417
+ lr[off + 1] = l;
418
+ return lr;
419
+ }
420
+ /**
421
+ * The next word of a cyclic byte stream
422
+ * @param data
423
+ * @param offp
424
+ */
425
+ function streamToWord(data, offp) {
426
+ let word = 0;
427
+ for (let i = 0; i < 4; ++i) {
428
+ word = (word << 8) | (data[offp] & 0xff);
429
+ offp = (offp + 1) % data.length;
430
+ }
431
+ return { key: word, offp };
432
+ }
433
+ /**
434
+ * Blowfish key schedule
435
+ * @param key
436
+ * @param P
437
+ * @param S
438
+ */
439
+ function blowfishKey(key, P, S) {
440
+ let offset = 0;
441
+ let lr = [0, 0];
442
+ const plen = P.length;
443
+ const slen = S.length;
444
+ for (let i = 0; i < plen; i++) {
445
+ const sw = streamToWord(key, offset);
446
+ offset = sw.offp;
447
+ P[i] = P[i] ^ sw.key;
448
+ }
449
+ for (let i = 0; i < plen; i += 2) {
450
+ lr = encipher(lr, 0, P, S);
451
+ P[i] = lr[0];
452
+ P[i + 1] = lr[1];
453
+ }
454
+ for (let i = 0; i < slen; i += 2) {
455
+ lr = encipher(lr, 0, P, S);
456
+ S[i] = lr[0];
457
+ S[i + 1] = lr[1];
458
+ }
459
+ }
460
+ /**
461
+ * Expensive key schedule Blowfish.
462
+ * @param data
463
+ * @param key
464
+ * @param P
465
+ * @param S
466
+ */
467
+ function expensiveKey(data, key, P, S) {
468
+ let offp = 0;
469
+ let lr = [0, 0];
470
+ const plen = P.length;
471
+ const slen = S.length;
472
+ let sw;
473
+ for (let i = 0; i < plen; i++) {
474
+ sw = streamToWord(key, offp);
475
+ offp = sw.offp;
476
+ P[i] = P[i] ^ sw.key;
477
+ }
478
+ offp = 0;
479
+ for (let i = 0; i < plen; i += 2) {
480
+ sw = streamToWord(data, offp);
481
+ offp = sw.offp;
482
+ lr[0] ^= sw.key;
483
+ sw = streamToWord(data, offp);
484
+ offp = sw.offp;
485
+ lr[1] ^= sw.key;
486
+ lr = encipher(lr, 0, P, S);
487
+ P[i] = lr[0];
488
+ P[i + 1] = lr[1];
489
+ }
490
+ for (let i = 0; i < slen; i += 2) {
491
+ sw = streamToWord(data, offp);
492
+ offp = sw.offp;
493
+ lr[0] ^= sw.key;
494
+ sw = streamToWord(data, offp);
495
+ offp = sw.offp;
496
+ lr[1] ^= sw.key;
497
+ lr = encipher(lr, 0, P, S);
498
+ S[i] = lr[0];
499
+ S[i + 1] = lr[1];
500
+ }
501
+ }
502
+ /**
503
+ * Starts crypting bytes (validates the rounds and the salt)
504
+ * @param b Bytes to crypt
505
+ * @param salt Salt bytes to use
506
+ * @param rounds Number of rounds (log2)
507
+ */
508
+ function startCrypt(b, salt, rounds) {
509
+ if (rounds < 4 || rounds > 31)
510
+ throw Error('Illegal number of rounds (4-31): ' + rounds);
511
+ if (salt.length !== constants_js_1.BCRYPT_SALT_LEN)
512
+ throw Error('Illegal salt length: ' + salt.length + ' != ' + constants_js_1.BCRYPT_SALT_LEN);
513
+ const state = { b, salt, rounds: (1 << rounds) >>> 0, P: new Int32Array(constants_js_1.P_ORIG), S: new Int32Array(constants_js_1.S_ORIG), i: 0 };
514
+ expensiveKey(salt, b, state.P, state.S);
515
+ return state;
516
+ }
517
+ /**
518
+ * Runs rounds of a crypt, for at most a time (Infinity: all the rounds)
519
+ * @param state
520
+ * @param maxTime
521
+ */
522
+ function runRounds(state, maxTime) {
523
+ const start = Date.now();
524
+ while (state.i < state.rounds) {
525
+ state.i = state.i + 1;
526
+ blowfishKey(state.b, state.P, state.S);
527
+ blowfishKey(state.salt, state.P, state.S);
528
+ if (Date.now() - start > maxTime)
529
+ break;
530
+ }
531
+ }
532
+ /**
533
+ * Finishes a crypt: the bytes of the encrypted magic value
534
+ * @param state
535
+ */
536
+ function finishCrypt(state) {
537
+ const cdata = constants_js_1.C_ORIG.slice();
538
+ const clen = cdata.length;
539
+ for (let i = 0; i < 64; i++) {
540
+ for (let j = 0; j < clen >> 1; j++)
541
+ encipher(cdata, j << 1, state.P, state.S);
542
+ }
543
+ const ret = [];
544
+ for (let i = 0; i < clen; i++) {
545
+ ret.push(((cdata[i] >> 24) & 0xff) >>> 0);
546
+ ret.push(((cdata[i] >> 16) & 0xff) >>> 0);
547
+ ret.push(((cdata[i] >> 8) & 0xff) >>> 0);
548
+ ret.push((cdata[i] & 0xff) >>> 0);
549
+ }
550
+ return ret;
551
+ }
552
+ /**
553
+ * Parses a salt, and prepares the password bytes
554
+ * @param password
555
+ * @param salt
556
+ */
557
+ function parseSalt(password, salt) {
558
+ if (typeof password !== 'string' || typeof salt !== 'string')
559
+ throw Error('Invalid string / salt: Not a string');
560
+ //Validate the salt
561
+ let minor;
562
+ let offset;
563
+ if (salt.charAt(0) !== '$' || salt.charAt(1) !== '2')
564
+ throw Error('Invalid salt version: ' + salt.substring(0, 2));
565
+ if (salt.charAt(2) === '$') {
566
+ minor = String.fromCharCode(0);
567
+ offset = 3;
568
+ }
569
+ else {
570
+ minor = salt.charAt(2);
571
+ if ((minor !== 'a' && minor !== 'b' && minor !== 'y') || salt.charAt(3) !== '$')
572
+ throw Error('Invalid salt revision: ' + salt.substring(2, 4));
573
+ offset = 4;
574
+ }
575
+ //Extract number of rounds
576
+ if (salt.charAt(offset + 2) > '$')
577
+ throw Error('Missing salt rounds');
578
+ const r1 = parseInt(salt.substring(offset, offset + 1), 10) * 10;
579
+ const r2 = parseInt(salt.substring(offset + 1, offset + 2), 10);
580
+ const rounds = r1 + r2;
581
+ const realSalt = salt.substring(offset + 3, offset + 25);
582
+ password += minor >= 'a' ? '\x00' : '';
583
+ return { minor, rounds, passwordBytes: utf8Array(password), saltBytes: base64Decode(realSalt, constants_js_1.BCRYPT_SALT_LEN) };
584
+ }
585
+ /**
586
+ * The hash string of the crypted bytes
587
+ * @param parts
588
+ * @param bytes
589
+ */
590
+ function formatHash(parts, bytes) {
591
+ const res = [];
592
+ res.push('$2');
593
+ if (parts.minor >= 'a')
594
+ res.push(parts.minor);
595
+ res.push('$');
596
+ if (parts.rounds < 10)
597
+ res.push('0');
598
+ res.push(parts.rounds.toString());
599
+ res.push('$');
600
+ res.push(base64Encode(parts.saltBytes, parts.saltBytes.length));
601
+ res.push(base64Encode(bytes, constants_js_1.C_ORIG.length * 4 - 1));
602
+ return res.join('');
603
+ }
604
+ /**
605
+ * Hashes a password synchronously.
606
+ * @param password Password to hash
607
+ * @param salt Salt to use
608
+ */
609
+ function hashPassword(password, salt) {
610
+ const parts = parseSalt(password, salt);
611
+ const state = startCrypt(parts.passwordBytes, parts.saltBytes, parts.rounds);
612
+ runRounds(state, Infinity);
613
+ return formatHash(parts, finishCrypt(state));
614
+ }
615
+ /**
616
+ * Hashes a password asynchronously: the rounds run at most 100 ms at a time, yielding to the event loop in between.
617
+ * @param password Password to hash
618
+ * @param salt Salt to use
619
+ * @param progressCallback
620
+ */
621
+ async function hashPasswordAsync(password, salt, progressCallback) {
622
+ let parts;
623
+ let state;
624
+ try {
625
+ parts = parseSalt(password, salt);
626
+ state = startCrypt(parts.passwordBytes, parts.saltBytes, parts.rounds);
627
+ }
628
+ catch (error) {
629
+ //The errors are reported asynchronously
630
+ await new Promise((resolve) => nextTick(resolve));
631
+ throw error;
632
+ }
633
+ for (;;) {
634
+ if (progressCallback)
635
+ progressCallback(state.i / state.rounds);
636
+ if (state.i >= state.rounds)
637
+ break;
638
+ runRounds(state, constants_js_1.MAX_EXECUTION_TIME);
639
+ await new Promise((resolve) => nextTick(resolve));
640
+ }
641
+ return formatHash(parts, finishCrypt(state));
642
+ }
643
+ /**
644
+ * Encodes a byte array to base64 with up to len bytes of input, using the custom bcrypt alphabet.
645
+ * @param bytes Byte array
646
+ * @param length Maximum input length
647
+ */
648
+ function encodeBase64(bytes, length) {
649
+ return base64Encode(bytes, length);
650
+ }
651
+ /**
652
+ * Decodes a base64 encoded string to up to len bytes of output, using the custom bcrypt alphabet.
653
+ * @param string String to decode
654
+ * @param length Maximum output length
655
+ */
656
+ function decodeBase64(string, length) {
657
+ return base64Decode(string, length);
658
+ }
@@ -0,0 +1 @@
1
+ {"type":"commonjs"}