@yielded/crypto 0.0.0-stage → 0.1.0-beta.19

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 (88) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +39 -2
  3. package/THIRD_PARTY_NOTICES.md +378 -0
  4. package/dist/Aead.d.mts +34 -0
  5. package/dist/Aead.mjs +28 -0
  6. package/dist/Aead.mjs.map +1 -0
  7. package/dist/Errors.d.mts +20 -0
  8. package/dist/Errors.mjs +19 -0
  9. package/dist/Errors.mjs.map +1 -0
  10. package/dist/Hmac.d.mts +39 -0
  11. package/dist/Hmac.mjs +26 -0
  12. package/dist/Hmac.mjs.map +1 -0
  13. package/dist/Kdf.d.mts +54 -0
  14. package/dist/Kdf.mjs +78 -0
  15. package/dist/Kdf.mjs.map +1 -0
  16. package/dist/KdfAdmission.d.mts +24 -0
  17. package/dist/KdfAdmission.mjs +48 -0
  18. package/dist/KdfAdmission.mjs.map +1 -0
  19. package/dist/Portable.d.mts +11 -0
  20. package/dist/Portable.mjs +13 -0
  21. package/dist/Portable.mjs.map +1 -0
  22. package/dist/Signature.d.mts +70 -0
  23. package/dist/Signature.mjs +73 -0
  24. package/dist/Signature.mjs.map +1 -0
  25. package/dist/WebCrypto.d.mts +20 -0
  26. package/dist/WebCrypto.mjs +33 -0
  27. package/dist/WebCrypto.mjs.map +1 -0
  28. package/dist/index.d.mts +7 -0
  29. package/dist/index.mjs +7 -0
  30. package/dist/internal/aead.mjs +81 -0
  31. package/dist/internal/aead.mjs.map +1 -0
  32. package/dist/internal/argon2-core.mjs +293 -0
  33. package/dist/internal/argon2-core.mjs.map +1 -0
  34. package/dist/internal/argon2.mjs +16 -0
  35. package/dist/internal/argon2.mjs.map +1 -0
  36. package/dist/internal/blake2b.mjs +359 -0
  37. package/dist/internal/blake2b.mjs.map +1 -0
  38. package/dist/internal/common.mjs +20 -0
  39. package/dist/internal/common.mjs.map +1 -0
  40. package/dist/internal/hmac.mjs +68 -0
  41. package/dist/internal/hmac.mjs.map +1 -0
  42. package/dist/internal/kdf.mjs +99 -0
  43. package/dist/internal/kdf.mjs.map +1 -0
  44. package/dist/internal/layer.mjs +26 -0
  45. package/dist/internal/layer.mjs.map +1 -0
  46. package/dist/internal/node.d.mts +11 -0
  47. package/dist/internal/node.mjs +34 -0
  48. package/dist/internal/node.mjs.map +1 -0
  49. package/dist/internal/poly1305.mjs +247 -0
  50. package/dist/internal/poly1305.mjs.map +1 -0
  51. package/dist/internal/signature.mjs +140 -0
  52. package/dist/internal/signature.mjs.map +1 -0
  53. package/dist/internal/word64.mjs +25 -0
  54. package/dist/internal/word64.mjs.map +1 -0
  55. package/dist/internal/xchacha-core.mjs +285 -0
  56. package/dist/internal/xchacha-core.mjs.map +1 -0
  57. package/dist/internal/xchacha.mjs +21 -0
  58. package/dist/internal/xchacha.mjs.map +1 -0
  59. package/dist/platform-bun.d.mts +2 -0
  60. package/dist/platform-bun.mjs +2 -0
  61. package/dist/platform-node.d.mts +2 -0
  62. package/dist/platform-node.mjs +2 -0
  63. package/package.json +1 -6
  64. package/src/Aead.ts +39 -0
  65. package/src/Errors.ts +27 -0
  66. package/src/Hmac.ts +41 -0
  67. package/src/Kdf.ts +87 -0
  68. package/src/KdfAdmission.ts +87 -0
  69. package/src/Portable.ts +8 -0
  70. package/src/Signature.ts +87 -0
  71. package/src/WebCrypto.ts +46 -0
  72. package/src/index.ts +6 -0
  73. package/src/internal/aead.ts +119 -0
  74. package/src/internal/argon2-core.ts +470 -0
  75. package/src/internal/argon2.ts +23 -0
  76. package/src/internal/blake2b.ts +230 -0
  77. package/src/internal/common.ts +44 -0
  78. package/src/internal/hmac.ts +111 -0
  79. package/src/internal/kdf.ts +150 -0
  80. package/src/internal/layer.ts +32 -0
  81. package/src/internal/node.ts +46 -0
  82. package/src/internal/poly1305.ts +289 -0
  83. package/src/internal/signature.ts +188 -0
  84. package/src/internal/word64.ts +48 -0
  85. package/src/internal/xchacha-core.ts +397 -0
  86. package/src/internal/xchacha.ts +26 -0
  87. package/src/platform-bun.ts +2 -0
  88. package/src/platform-node.ts +2 -0
@@ -0,0 +1,470 @@
1
+ import { Blake2b } from "./blake2b";
2
+ import type { Argon2Parameters } from "./kdf";
3
+ /**
4
+ * Adapted from noble-hashes, Copyright (c) 2022 Paul Miller.
5
+ * MIT; source revision 8060cc83bbab6681d23c9eed071db3f3a6b442da.
6
+ * See ../../THIRD_PARTY_NOTICES.md for source mapping and license.
7
+ */
8
+ import { rotr32H, rotr32L, rotrBH, rotrBL, rotrSH, rotrSL } from "./word64";
9
+
10
+ const ARGON2_SYNC_POINTS = 4;
11
+
12
+ // Unsigned `u32 * u32 = { h, l }`, returned as split 64-bit halves.
13
+ function mul(a: number, b: number) {
14
+ // Split into 16-bit limbs so each partial product stays exact under `Math.imul`.
15
+ const aL = a & 0xffff;
16
+ const aH = a >>> 16;
17
+ const bL = b & 0xffff;
18
+ const bH = b >>> 16;
19
+ const ll = Math.imul(aL, bL);
20
+ const hl = Math.imul(aH, bL);
21
+ const lh = Math.imul(aL, bH);
22
+ const hh = Math.imul(aH, bH);
23
+ const carry = (ll >>> 16) + (hl & 0xffff) + lh;
24
+ const high = (hh + (hl >>> 16) + (carry >>> 16)) | 0;
25
+ const low = (carry << 16) | (ll & 0xffff);
26
+
27
+ return { h: high, l: low };
28
+ }
29
+
30
+ // High 32 bits of unsigned u32 multiply, via the same 16-bit limb split as `mul`.
31
+ function mulHi(a: number, b: number): number {
32
+ const aL = a & 0xffff,
33
+ aH = a >>> 16,
34
+ bL = b & 0xffff,
35
+ bH = b >>> 16;
36
+
37
+ const carry = (Math.imul(aL, bL) >>> 16) + (Math.imul(aH, bL) & 0xffff) + Math.imul(aL, bH);
38
+
39
+ return (Math.imul(aH, bH) + (Math.imul(aH, bL) >>> 16) + (carry >>> 16)) | 0;
40
+ }
41
+
42
+ // 1024-byte block: 256 u32 = 128 interleaved low/high halves = RFC's
43
+ // 8x8 matrix of 16-byte registers.
44
+
45
+ // Quarter-round over 64-bit word indices into `A2_BUF`; each index maps to adjacent low/high u32s.
46
+ // Each BlaMka step `X = X + Y + 2 * trunc(X) * trunc(Y)` (trunc = low 32 bits) is three lines:
47
+ // `Math.imul` is the low product half, `mulHi` the high half, then a split 64-bit add with the
48
+ // doubling folded in. RFC 9106 Figure 19 GB rotates by 32, 24, 16, and 63 bits after each XOR.
49
+ function G(A2_BUF: Uint32Array, a: number, b: number, c: number, d: number) {
50
+ let Al = A2_BUF[2 * a],
51
+ Ah = A2_BUF[2 * a + 1];
52
+
53
+ let Bl = A2_BUF[2 * b],
54
+ Bh = A2_BUF[2 * b + 1];
55
+
56
+ let Cl = A2_BUF[2 * c],
57
+ Ch = A2_BUF[2 * c + 1];
58
+
59
+ let Dl = A2_BUF[2 * d],
60
+ Dh = A2_BUF[2 * d + 1];
61
+
62
+ let ml = 0,
63
+ mh = 0,
64
+ rl = 0,
65
+ xh = 0,
66
+ xl = 0;
67
+
68
+ // A = blamka(A, B); D = rotr64(D ^ A, 32)
69
+ ml = Math.imul(Al, Bl);
70
+ mh = mulHi(Al, Bl);
71
+ rl = (Al >>> 0) + (Bl >>> 0) + ((ml << 1) >>> 0);
72
+ Ah = (Ah + Bh + ((mh << 1) | (ml >>> 31)) + ((rl / 0x100000000) | 0)) | 0;
73
+ Al = rl | 0;
74
+ xh = Dh ^ Ah;
75
+ xl = Dl ^ Al;
76
+ Dh = rotr32H(xh, xl);
77
+ Dl = rotr32L(xh, xl);
78
+
79
+ // C = blamka(C, D); B = rotr64(B ^ C, 24)
80
+ ml = Math.imul(Cl, Dl);
81
+ mh = mulHi(Cl, Dl);
82
+ rl = (Cl >>> 0) + (Dl >>> 0) + ((ml << 1) >>> 0);
83
+ Ch = (Ch + Dh + ((mh << 1) | (ml >>> 31)) + ((rl / 0x100000000) | 0)) | 0;
84
+ Cl = rl | 0;
85
+ xh = Bh ^ Ch;
86
+ xl = Bl ^ Cl;
87
+ Bh = rotrSH(xh, xl, 24);
88
+ Bl = rotrSL(xh, xl, 24);
89
+
90
+ // A = blamka(A, B); D = rotr64(D ^ A, 16)
91
+ ml = Math.imul(Al, Bl);
92
+ mh = mulHi(Al, Bl);
93
+ rl = (Al >>> 0) + (Bl >>> 0) + ((ml << 1) >>> 0);
94
+ Ah = (Ah + Bh + ((mh << 1) | (ml >>> 31)) + ((rl / 0x100000000) | 0)) | 0;
95
+ Al = rl | 0;
96
+ xh = Dh ^ Ah;
97
+ xl = Dl ^ Al;
98
+ Dh = rotrSH(xh, xl, 16);
99
+ Dl = rotrSL(xh, xl, 16);
100
+
101
+ // C = blamka(C, D); B = rotr64(B ^ C, 63)
102
+ ml = Math.imul(Cl, Dl);
103
+ mh = mulHi(Cl, Dl);
104
+ rl = (Cl >>> 0) + (Dl >>> 0) + ((ml << 1) >>> 0);
105
+ Ch = (Ch + Dh + ((mh << 1) | (ml >>> 31)) + ((rl / 0x100000000) | 0)) | 0;
106
+ Cl = rl | 0;
107
+ xh = Bh ^ Ch;
108
+ xl = Bl ^ Cl;
109
+ Bh = rotrBH(xh, xl, 63);
110
+ Bl = rotrBL(xh, xl, 63);
111
+
112
+ A2_BUF[2 * a] = Al;
113
+ A2_BUF[2 * a + 1] = Ah;
114
+ A2_BUF[2 * b] = Bl;
115
+ A2_BUF[2 * b + 1] = Bh;
116
+ A2_BUF[2 * c] = Cl;
117
+ A2_BUF[2 * c + 1] = Ch;
118
+ A2_BUF[2 * d] = Dl;
119
+ A2_BUF[2 * d + 1] = Dh;
120
+ }
121
+
122
+ // Argon2 permutation over 16 register indices into `A2_BUF`, not the register values themselves.
123
+ // RFC 9106 Figure 17: these arguments are the 16 `v0..v15` 64-bit word
124
+ // indices inside eight 16-byte inputs, not copied word values.
125
+ function P(
126
+ A2_BUF: Uint32Array,
127
+ v00: number,
128
+ v01: number,
129
+ v02: number,
130
+ v03: number,
131
+ v04: number,
132
+ v05: number,
133
+ v06: number,
134
+ v07: number,
135
+ v08: number,
136
+ v09: number,
137
+ v10: number,
138
+ v11: number,
139
+ v12: number,
140
+ v13: number,
141
+ v14: number,
142
+ v15: number,
143
+ ) {
144
+ // RFC 9106 Figure 18: first apply GB across rows, then across columns of the 8x8 register matrix.
145
+ G(A2_BUF, v00, v04, v08, v12);
146
+ G(A2_BUF, v01, v05, v09, v13);
147
+ G(A2_BUF, v02, v06, v10, v14);
148
+ G(A2_BUF, v03, v07, v11, v15);
149
+ G(A2_BUF, v00, v05, v10, v15);
150
+ G(A2_BUF, v01, v06, v11, v12);
151
+ G(A2_BUF, v02, v07, v08, v13);
152
+ G(A2_BUF, v03, v04, v09, v14);
153
+ }
154
+
155
+ function block(
156
+ A2_BUF: Uint32Array,
157
+ x: Uint32Array,
158
+ xPos: number,
159
+ yPos: number,
160
+ outPos: number,
161
+ needXor: boolean,
162
+ ) {
163
+ for (let i = 0; i < 256; i++) A2_BUF[i] = x[xPos + i] ^ x[yPos + i];
164
+ // rows (8 consecutive 16-register groups)
165
+ for (let i = 0; i < 128; i += 16) {
166
+ P(
167
+ A2_BUF,
168
+ i,
169
+ i + 1,
170
+ i + 2,
171
+ i + 3,
172
+ i + 4,
173
+ i + 5,
174
+ i + 6,
175
+ i + 7,
176
+ i + 8,
177
+ i + 9,
178
+ i + 10,
179
+ i + 11,
180
+ i + 12,
181
+ i + 13,
182
+ i + 14,
183
+ i + 15,
184
+ );
185
+ }
186
+ // columns (8 strided 16-register groups)
187
+ for (let i = 0; i < 16; i += 2) {
188
+ P(
189
+ A2_BUF,
190
+ i,
191
+ i + 1,
192
+ i + 16,
193
+ i + 17,
194
+ i + 32,
195
+ i + 33,
196
+ i + 48,
197
+ i + 49,
198
+ i + 64,
199
+ i + 65,
200
+ i + 80,
201
+ i + 81,
202
+ i + 96,
203
+ i + 97,
204
+ i + 112,
205
+ i + 113,
206
+ );
207
+ }
208
+
209
+ // RFC 9106 step 6: passes after the first XOR the old destination block into the new G(X, Y).
210
+ if (needXor) for (let i = 0; i < 256; i++) x[outPos + i] ^= A2_BUF[i] ^ x[xPos + i] ^ x[yPos + i];
211
+ else for (let i = 0; i < 256; i++) x[outPos + i] = A2_BUF[i] ^ x[xPos + i] ^ x[yPos + i];
212
+ A2_BUF.fill(0);
213
+ }
214
+
215
+ // Used only inside argon2Blocks!
216
+ function indexAlpha(
217
+ r: number,
218
+ s: number,
219
+ laneLen: number,
220
+ segmentLen: number,
221
+ index: number,
222
+ randL: number,
223
+ sameLane: boolean = false,
224
+ ) {
225
+ // RFC 9106 §3.4.2 Figures 12-13: map `J1` / `J2` into the current lane's reference area `W`.
226
+ let area: number;
227
+
228
+ if (r === 0) {
229
+ if (s === 0) area = index - 1;
230
+ else if (sameLane) area = s * segmentLen + index - 1;
231
+ else area = s * segmentLen + (index === 0 ? -1 : 0);
232
+ } else if (sameLane) area = laneLen - segmentLen + index - 1;
233
+ else area = laneLen - segmentLen + (index === 0 ? -1 : 0);
234
+ const startPos = r !== 0 && s !== ARGON2_SYNC_POINTS - 1 ? (s + 1) * segmentLen : 0;
235
+ // RFC 9106 Figure 13: `mul(randL, randL).h` is `floor(J_1^2 / 2^32)`, and the outer high-half
236
+ // multiply computes `floor(|W| * x / 2^32)` without floating-point math.
237
+ const rel = area - 1 - mul(area, mul(randL, randL).h).h;
238
+
239
+ return (startPos + rel) % laneLen;
240
+ }
241
+
242
+ const bytesOfWords = (words: Uint32Array): Uint8Array => {
243
+ const bytes = new Uint8Array(words.length * 4);
244
+ const view = new DataView(bytes.buffer);
245
+
246
+ for (let i = 0; i < words.length; i++) view.setUint32(i * 4, words[i], true);
247
+
248
+ return bytes;
249
+ };
250
+
251
+ const hash = (data: Uint8Array, length = 64) => {
252
+ const state = new Blake2b(length);
253
+
254
+ try {
255
+ return state.update(data).digest();
256
+ } finally {
257
+ state.destroy();
258
+ }
259
+ };
260
+
261
+ // RFC 9106 H': preserve byte tails, including output lengths not divisible by four.
262
+ function variableHash(words: Uint32Array, length: number): Uint8Array {
263
+ const data = bytesOfWords(words);
264
+ const prefix = new Uint8Array(4);
265
+
266
+ new DataView(prefix.buffer).setUint32(0, length, true);
267
+ const state = new Blake2b(Math.min(length, 64));
268
+ let previous: Uint8Array | undefined;
269
+ let out: Uint8Array | undefined;
270
+
271
+ try {
272
+ previous = state.update(prefix).update(data).digest();
273
+ if (length <= 64) {
274
+ const result = previous;
275
+
276
+ previous = undefined;
277
+
278
+ return result;
279
+ }
280
+ out = new Uint8Array(length);
281
+ out.set(previous.subarray(0, 32));
282
+ let position = 32;
283
+
284
+ for (; length - position > 64; position += 32) {
285
+ const next = hash(previous);
286
+
287
+ previous.fill(0);
288
+ previous = next;
289
+ out.set(previous.subarray(0, 32), position);
290
+ }
291
+ const last = hash(previous, length - position);
292
+
293
+ out.set(last, position);
294
+ last.fill(0);
295
+
296
+ return out;
297
+ } catch (error) {
298
+ out?.fill(0);
299
+ throw error;
300
+ } finally {
301
+ state.destroy();
302
+ previous?.fill(0);
303
+ prefix.fill(0);
304
+ data.fill(0);
305
+ }
306
+ }
307
+
308
+ export interface State {
309
+ readonly memory: Uint32Array;
310
+ readonly scratch: Uint32Array;
311
+ readonly address: Uint32Array;
312
+ readonly initial: Uint32Array;
313
+ readonly final: Uint32Array;
314
+ readonly blocks: number;
315
+ readonly laneLength: number;
316
+ readonly segmentLength: number;
317
+ }
318
+
319
+ // Allocate before absorbing secrets. Kdf has already applied Schema/resource limits.
320
+ export const allocate = (input: Argon2Parameters): State => {
321
+ const blocks = 4 * input.parallelism * Math.floor(input.memoryKiB / (4 * input.parallelism));
322
+ const laneLength = blocks / input.parallelism;
323
+
324
+ return {
325
+ memory: new Uint32Array(blocks * 256),
326
+ scratch: new Uint32Array(256),
327
+ address: new Uint32Array(3 * 256),
328
+ initial: new Uint32Array(18),
329
+ final: new Uint32Array(256),
330
+ blocks,
331
+ laneLength,
332
+ segmentLength: laneLength / 4,
333
+ };
334
+ };
335
+
336
+ export const destroy = (state: State): void => {
337
+ state.memory.fill(0);
338
+ state.scratch.fill(0);
339
+ state.address.fill(0);
340
+ state.initial.fill(0);
341
+ state.final.fill(0);
342
+ };
343
+
344
+ export const initialize = (state: State, input: Argon2Parameters): void => {
345
+ const h = new Blake2b();
346
+ const scalar = new Uint8Array(4);
347
+ const view = new DataView(scalar.buffer);
348
+
349
+ const number = (value: number) => {
350
+ view.setUint32(0, value, true);
351
+ h.update(scalar);
352
+ };
353
+
354
+ try {
355
+ for (const value of [input.parallelism, input.length, input.memoryKiB, input.passes, 19, 2])
356
+ number(value);
357
+ for (const data of [
358
+ input.password,
359
+ input.salt,
360
+ input.secret ?? new Uint8Array(),
361
+ input.associatedData,
362
+ ]) {
363
+ number(data.length);
364
+ h.update(data);
365
+ }
366
+ const initial = h.digest();
367
+
368
+ try {
369
+ const hashView = new DataView(initial.buffer, initial.byteOffset, initial.byteLength);
370
+
371
+ for (let i = 0; i < 16; i++) state.initial[i] = hashView.getUint32(i * 4, true);
372
+ } finally {
373
+ initial.fill(0);
374
+ }
375
+ for (let lane = 0; lane < input.parallelism; lane++) {
376
+ state.initial[17] = lane;
377
+ for (let first = 0; first < 2; first++) {
378
+ state.initial[16] = first;
379
+ const expanded = variableHash(state.initial, 1024);
380
+
381
+ try {
382
+ const expandedView = new DataView(
383
+ expanded.buffer,
384
+ expanded.byteOffset,
385
+ expanded.byteLength,
386
+ );
387
+
388
+ const offset = (lane * state.laneLength + first) * 256;
389
+
390
+ for (let i = 0; i < 256; i++)
391
+ state.memory[offset + i] = expandedView.getUint32(i * 4, true);
392
+ } finally {
393
+ expanded.fill(0);
394
+ }
395
+ }
396
+ }
397
+ } finally {
398
+ h.destroy();
399
+ scalar.fill(0);
400
+ state.initial.fill(0);
401
+ }
402
+ };
403
+
404
+ /** Yield block boundaries to the Effect driver; all scratch belongs to this derivation. */
405
+ export function* fill(state: State, input: Argon2Parameters): Generator<void, void> {
406
+ const { memory: B, scratch, address, laneLength: laneLen, segmentLength: segmentLen } = state;
407
+ const p = input.parallelism;
408
+
409
+ address[256 + 6] = state.blocks;
410
+ address[256 + 8] = input.passes;
411
+ address[256 + 10] = 2;
412
+ for (let r = 0; r < input.passes; r++) {
413
+ const needXor = r !== 0;
414
+
415
+ address[256] = r;
416
+ for (let s = 0; s < ARGON2_SYNC_POINTS; s++) {
417
+ address[256 + 4] = s;
418
+ const dataIndependent = r === 0 && s < 2;
419
+
420
+ for (let lane = 0; lane < p; lane++) {
421
+ address[256 + 2] = lane;
422
+ address[256 + 12] = 0;
423
+ const start = r === 0 && s === 0 ? 2 : 0;
424
+
425
+ if (start === 2 && dataIndependent) {
426
+ address[256 + 12]++;
427
+ block(scratch, address, 256, 512, 0, false);
428
+ block(scratch, address, 0, 512, 0, false);
429
+ }
430
+ let offset = lane * laneLen + s * segmentLen + start;
431
+
432
+ for (let index = start; index < segmentLen; index++, offset++) {
433
+ const prev = offset % laneLen ? offset - 1 : offset + laneLen - 1;
434
+ let randL: number;
435
+ let randH: number;
436
+
437
+ if (dataIndependent) {
438
+ const addressIndex = index % 128;
439
+
440
+ if (addressIndex === 0) {
441
+ address[256 + 12]++;
442
+ block(scratch, address, 256, 512, 0, false);
443
+ block(scratch, address, 0, 512, 0, false);
444
+ }
445
+ randL = address[2 * addressIndex];
446
+ randH = address[2 * addressIndex + 1];
447
+ } else {
448
+ randL = B[256 * prev];
449
+ randH = B[256 * prev + 1];
450
+ }
451
+ const refLane = r === 0 && s === 0 ? lane : randH % p;
452
+ const refPos = indexAlpha(r, s, laneLen, segmentLen, index, randL, refLane === lane);
453
+
454
+ block(scratch, B, 256 * prev, 256 * (laneLen * refLane + refPos), offset * 256, needXor);
455
+ yield;
456
+ }
457
+ }
458
+ }
459
+ }
460
+ }
461
+
462
+ export const output = (state: State, input: Argon2Parameters): Uint8Array => {
463
+ for (let lane = 0; lane < input.parallelism; lane++) {
464
+ const offset = 256 * (state.laneLength * lane + state.laneLength - 1);
465
+
466
+ for (let word = 0; word < 256; word++) state.final[word] ^= state.memory[offset + word];
467
+ }
468
+
469
+ return variableHash(state.final, input.length);
470
+ };
@@ -0,0 +1,23 @@
1
+ import { Effect } from "effect";
2
+
3
+ import { allocate, destroy, fill, initialize, output } from "./argon2-core";
4
+ import type { Argon2 } from "./kdf";
5
+
6
+ /** Effect schedules bounded batches; admission remains held through buffer cleanup. */
7
+ export const argon2: Argon2 = (input) =>
8
+ Effect.acquireUseRelease(
9
+ Effect.sync(() => allocate(input)),
10
+ (state) =>
11
+ Effect.gen(function* () {
12
+ initialize(state, input);
13
+ const blocks = fill(state, input);
14
+
15
+ for (;;) {
16
+ for (let batch = 0; batch < 256; batch++) {
17
+ if (blocks.next().done) return output(state, input);
18
+ }
19
+ yield* Effect.yieldNow;
20
+ }
21
+ }),
22
+ (state) => Effect.sync(() => destroy(state)),
23
+ );
@@ -0,0 +1,230 @@
1
+ /**
2
+ * Adapted from noble-hashes, Copyright (c) 2022 Paul Miller.
3
+ * MIT; source revision 8060cc83bbab6681d23c9eed071db3f3a6b442da.
4
+ * See ../../THIRD_PARTY_NOTICES.md for source mapping and license.
5
+ */
6
+ import * as u64 from "./word64";
7
+
8
+ const B2B_IV = /* @__PURE__ */ Uint32Array.from([
9
+ 0xf3bcc908, 0x6a09e667, 0x84caa73b, 0xbb67ae85, 0xfe94f82b, 0x3c6ef372, 0x5f1d36f1, 0xa54ff53a,
10
+ 0xade682d1, 0x510e527f, 0x2b3e6c1f, 0x9b05688c, 0xfb41bd6b, 0x1f83d9ab, 0x137e2179, 0x5be0cd19,
11
+ ]);
12
+
13
+ const BSIGMA = /* @__PURE__ */ Uint8Array.from([
14
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11,
15
+ 7, 5, 3, 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4, 7, 9, 3, 1, 13, 12, 11, 14, 2, 6,
16
+ 5, 10, 4, 0, 15, 8, 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13, 2, 12, 6, 10, 0, 11, 8,
17
+ 3, 4, 13, 7, 5, 15, 14, 1, 9, 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11, 13, 11, 7, 14,
18
+ 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10, 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5, 10,
19
+ 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
20
+ 14, 15, 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3,
21
+ ]);
22
+
23
+ // BLAKE2b G mix split into two half-rounds over LE u32 low/high limbs.
24
+ function G1b(
25
+ BBUF: Uint32Array,
26
+ a: number,
27
+ b: number,
28
+ c: number,
29
+ d: number,
30
+ msg: Uint32Array,
31
+ x: number,
32
+ ) {
33
+ // NOTE: V is LE here
34
+ const Xl = msg[x],
35
+ Xh = msg[x + 1];
36
+
37
+ let Al = BBUF[2 * a],
38
+ Ah = BBUF[2 * a + 1];
39
+
40
+ let Bl = BBUF[2 * b],
41
+ Bh = BBUF[2 * b + 1];
42
+
43
+ let Cl = BBUF[2 * c],
44
+ Ch = BBUF[2 * c + 1];
45
+
46
+ let Dl = BBUF[2 * d],
47
+ Dh = BBUF[2 * d + 1];
48
+
49
+ // v[a] = (v[a] + v[b] + x) | 0;
50
+ const ll = u64.add3L(Al, Bl, Xl);
51
+
52
+ Ah = u64.add3H(ll, Ah, Bh, Xh);
53
+ Al = ll | 0;
54
+
55
+ // v[d] = rotr(v[d] ^ v[a], 32)
56
+ let xh = Dh ^ Ah,
57
+ xl = Dl ^ Al;
58
+
59
+ Dh = u64.rotr32H(xh, xl);
60
+ Dl = u64.rotr32L(xh, xl);
61
+ // v[c] = (v[c] + v[d]) | 0;
62
+ ({ h: Ch, l: Cl } = u64.add(Ch, Cl, Dh, Dl));
63
+ // v[b] = rotr(v[b] ^ v[c], 24)
64
+ xh = Bh ^ Ch;
65
+ xl = Bl ^ Cl;
66
+ Bh = u64.rotrSH(xh, xl, 24);
67
+ Bl = u64.rotrSL(xh, xl, 24);
68
+ BBUF[2 * a] = Al;
69
+ BBUF[2 * a + 1] = Ah;
70
+ BBUF[2 * b] = Bl;
71
+ BBUF[2 * b + 1] = Bh;
72
+ BBUF[2 * c] = Cl;
73
+ BBUF[2 * c + 1] = Ch;
74
+ BBUF[2 * d] = Dl;
75
+ BBUF[2 * d + 1] = Dh;
76
+ }
77
+
78
+ // Second half-round of the same LE-limb BLAKE2b G mix; `x` is the message word offset.
79
+ function G2b(
80
+ BBUF: Uint32Array,
81
+ a: number,
82
+ b: number,
83
+ c: number,
84
+ d: number,
85
+ msg: Uint32Array,
86
+ x: number,
87
+ ) {
88
+ // NOTE: V is LE here
89
+ const Xl = msg[x],
90
+ Xh = msg[x + 1];
91
+
92
+ let Al = BBUF[2 * a],
93
+ Ah = BBUF[2 * a + 1];
94
+
95
+ let Bl = BBUF[2 * b],
96
+ Bh = BBUF[2 * b + 1];
97
+
98
+ let Cl = BBUF[2 * c],
99
+ Ch = BBUF[2 * c + 1];
100
+
101
+ let Dl = BBUF[2 * d],
102
+ Dh = BBUF[2 * d + 1];
103
+
104
+ // v[a] = (v[a] + v[b] + x) | 0;
105
+ const ll = u64.add3L(Al, Bl, Xl);
106
+
107
+ Ah = u64.add3H(ll, Ah, Bh, Xh);
108
+ Al = ll | 0;
109
+
110
+ // v[d] = rotr(v[d] ^ v[a], 16)
111
+ let xh = Dh ^ Ah,
112
+ xl = Dl ^ Al;
113
+
114
+ Dh = u64.rotrSH(xh, xl, 16);
115
+ Dl = u64.rotrSL(xh, xl, 16);
116
+ // v[c] = (v[c] + v[d]) | 0;
117
+ ({ h: Ch, l: Cl } = u64.add(Ch, Cl, Dh, Dl));
118
+ // v[b] = rotr(v[b] ^ v[c], 63)
119
+ xh = Bh ^ Ch;
120
+ xl = Bl ^ Cl;
121
+ Bh = u64.rotrBH(xh, xl, 63);
122
+ Bl = u64.rotrBL(xh, xl, 63);
123
+ BBUF[2 * a] = Al;
124
+ BBUF[2 * a + 1] = Ah;
125
+ BBUF[2 * b] = Bl;
126
+ BBUF[2 * b + 1] = Bh;
127
+ BBUF[2 * c] = Cl;
128
+ BBUF[2 * c + 1] = Ch;
129
+ BBUF[2 * d] = Dl;
130
+ BBUF[2 * d + 1] = Dh;
131
+ }
132
+
133
+ /** Unkeyed BLAKE2b used by Argon2's H0 and variable-length H'. */
134
+ export class Blake2b {
135
+ private readonly state = B2B_IV.slice();
136
+ private readonly buffer = new Uint8Array(128);
137
+ private readonly words = new Uint32Array(32);
138
+ private readonly mixed = new Uint32Array(32);
139
+ private readonly view = new DataView(this.buffer.buffer);
140
+ private length = 0;
141
+ private position = 0;
142
+
143
+ constructor(private readonly outputLength = 64) {
144
+ this.state[0] ^= 0x01010000 ^ outputLength;
145
+ }
146
+
147
+ update(data: Uint8Array): this {
148
+ for (let position = 0; position < data.length;) {
149
+ // BLAKE2 must retain the last block until its finalization flag is known.
150
+ if (this.position === 128) {
151
+ this.compress(false);
152
+ this.position = 0;
153
+ }
154
+ const take = Math.min(128 - this.position, data.length - position);
155
+
156
+ this.buffer.set(data.subarray(position, position + take), this.position);
157
+ this.position += take;
158
+ this.length += take;
159
+ position += take;
160
+ }
161
+
162
+ return this;
163
+ }
164
+
165
+ private compress(last: boolean): void {
166
+ const BBUF = this.mixed;
167
+ const msg = this.words;
168
+
169
+ for (let i = 0; i < 32; i++) msg[i] = this.view.getUint32(i * 4, true);
170
+ BBUF.set(this.state);
171
+ BBUF.set(B2B_IV, 16);
172
+ BBUF[24] ^= this.length >>> 0;
173
+ BBUF[25] ^= (this.length / 0x100000000) | 0;
174
+ if (last) {
175
+ BBUF[28] = ~BBUF[28];
176
+ BBUF[29] = ~BBUF[29];
177
+ }
178
+ let j = 0;
179
+ const s = BSIGMA;
180
+
181
+ // SIGMA selects 64-bit message words; multiply by 2 because `msg` stores
182
+ // each word as [low32, high32].
183
+ for (let i = 0; i < 12; i++) {
184
+ G1b(BBUF, 0, 4, 8, 12, msg, 2 * s[j++]);
185
+ G2b(BBUF, 0, 4, 8, 12, msg, 2 * s[j++]);
186
+ G1b(BBUF, 1, 5, 9, 13, msg, 2 * s[j++]);
187
+ G2b(BBUF, 1, 5, 9, 13, msg, 2 * s[j++]);
188
+ G1b(BBUF, 2, 6, 10, 14, msg, 2 * s[j++]);
189
+ G2b(BBUF, 2, 6, 10, 14, msg, 2 * s[j++]);
190
+ G1b(BBUF, 3, 7, 11, 15, msg, 2 * s[j++]);
191
+ G2b(BBUF, 3, 7, 11, 15, msg, 2 * s[j++]);
192
+
193
+ G1b(BBUF, 0, 5, 10, 15, msg, 2 * s[j++]);
194
+ G2b(BBUF, 0, 5, 10, 15, msg, 2 * s[j++]);
195
+ G1b(BBUF, 1, 6, 11, 12, msg, 2 * s[j++]);
196
+ G2b(BBUF, 1, 6, 11, 12, msg, 2 * s[j++]);
197
+ G1b(BBUF, 2, 7, 8, 13, msg, 2 * s[j++]);
198
+ G2b(BBUF, 2, 7, 8, 13, msg, 2 * s[j++]);
199
+ G1b(BBUF, 3, 4, 9, 14, msg, 2 * s[j++]);
200
+ G2b(BBUF, 3, 4, 9, 14, msg, 2 * s[j++]);
201
+ }
202
+
203
+ for (let i = 0; i < 16; i++) this.state[i] ^= BBUF[i] ^ BBUF[i + 16];
204
+ BBUF.fill(0);
205
+ msg.fill(0);
206
+ }
207
+
208
+ digest(): Uint8Array {
209
+ try {
210
+ this.buffer.fill(0, this.position);
211
+ this.compress(true);
212
+ const out = new Uint8Array(this.outputLength);
213
+
214
+ for (let i = 0; i < out.length; i++) out[i] = this.state[i >>> 2] >>> (8 * (i & 3));
215
+
216
+ return out;
217
+ } finally {
218
+ this.destroy();
219
+ }
220
+ }
221
+
222
+ destroy(): void {
223
+ this.state.fill(0);
224
+ this.buffer.fill(0);
225
+ this.words.fill(0);
226
+ this.mixed.fill(0);
227
+ this.length = 0;
228
+ this.position = 0;
229
+ }
230
+ }