@stryke/crypto 0.5.44 → 0.6.1

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 (95) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/dist/cloudflare.cjs +960 -0
  3. package/dist/cloudflare.d.cts +251 -0
  4. package/dist/cloudflare.d.cts.map +1 -0
  5. package/dist/cloudflare.d.mts +251 -0
  6. package/dist/cloudflare.d.mts.map +1 -0
  7. package/dist/cloudflare.mjs +942 -0
  8. package/dist/cloudflare.mjs.map +1 -0
  9. package/dist/index.cjs +16 -12
  10. package/dist/index.d.cts +2 -4
  11. package/dist/index.d.mts +2 -4
  12. package/dist/index.mjs +2 -4
  13. package/dist/neutral-BISXAuIW.cjs +868 -0
  14. package/dist/{encryption.d.mts → neutral-BJmyEdJq.d.mts} +77 -2
  15. package/dist/neutral-BJmyEdJq.d.mts.map +1 -0
  16. package/dist/neutral-BtZoJSl-.mjs +779 -0
  17. package/dist/neutral-BtZoJSl-.mjs.map +1 -0
  18. package/dist/{encryption.d.cts → neutral-CW6EXiYo.d.cts} +77 -2
  19. package/dist/neutral-CW6EXiYo.d.cts.map +1 -0
  20. package/dist/neutral.cjs +16 -16
  21. package/dist/neutral.d.cts +2 -4
  22. package/dist/neutral.d.mts +2 -4
  23. package/dist/neutral.mjs +2 -4
  24. package/dist/node.cjs +663 -0
  25. package/dist/node.d.cts +117 -0
  26. package/dist/node.d.cts.map +1 -0
  27. package/dist/node.d.mts +117 -0
  28. package/dist/node.d.mts.map +1 -0
  29. package/dist/node.mjs +654 -0
  30. package/dist/node.mjs.map +1 -0
  31. package/package.json +55 -20
  32. package/dist/base-64.cjs +0 -440
  33. package/dist/base-64.d.cts +0 -40
  34. package/dist/base-64.d.cts.map +0 -1
  35. package/dist/base-64.d.mts +0 -40
  36. package/dist/base-64.d.mts.map +0 -1
  37. package/dist/base-64.mjs +0 -438
  38. package/dist/base-64.mjs.map +0 -1
  39. package/dist/convert/src/array-buffer-to-string.cjs +0 -19
  40. package/dist/convert/src/array-buffer-to-string.mjs +0 -19
  41. package/dist/convert/src/array-buffer-to-string.mjs.map +0 -1
  42. package/dist/convert/src/neutral.cjs +0 -7
  43. package/dist/convert/src/neutral.mjs +0 -9
  44. package/dist/convert/src/parse-type-definition.cjs +0 -1
  45. package/dist/convert/src/parse-type-definition.mjs +0 -3
  46. package/dist/convert/src/string-to-uint8-array.cjs +0 -24
  47. package/dist/convert/src/string-to-uint8-array.mjs +0 -23
  48. package/dist/convert/src/string-to-uint8-array.mjs.map +0 -1
  49. package/dist/convert/src/string-to-utf8-array.cjs +0 -15
  50. package/dist/convert/src/string-to-utf8-array.mjs +0 -15
  51. package/dist/convert/src/string-to-utf8-array.mjs.map +0 -1
  52. package/dist/convert/src/uint8-array-to-stream.cjs +0 -22
  53. package/dist/convert/src/uint8-array-to-stream.mjs +0 -22
  54. package/dist/convert/src/uint8-array-to-stream.mjs.map +0 -1
  55. package/dist/convert/src/uint8-array-to-string.cjs +0 -15
  56. package/dist/convert/src/uint8-array-to-string.mjs +0 -16
  57. package/dist/convert/src/uint8-array-to-string.mjs.map +0 -1
  58. package/dist/convert/src/utf8-array-to-string.cjs +0 -15
  59. package/dist/convert/src/utf8-array-to-string.mjs +0 -15
  60. package/dist/convert/src/utf8-array-to-string.mjs.map +0 -1
  61. package/dist/encryption.cjs +0 -134
  62. package/dist/encryption.d.cts.map +0 -1
  63. package/dist/encryption.d.mts.map +0 -1
  64. package/dist/encryption.mjs +0 -129
  65. package/dist/encryption.mjs.map +0 -1
  66. package/dist/encryption.node.cjs +0 -63
  67. package/dist/encryption.node.d.cts +0 -42
  68. package/dist/encryption.node.d.cts.map +0 -1
  69. package/dist/encryption.node.d.mts +0 -42
  70. package/dist/encryption.node.d.mts.map +0 -1
  71. package/dist/encryption.node.mjs +0 -62
  72. package/dist/encryption.node.mjs.map +0 -1
  73. package/dist/hex.cjs +0 -62
  74. package/dist/hex.d.cts +0 -18
  75. package/dist/hex.d.cts.map +0 -1
  76. package/dist/hex.d.mts +0 -18
  77. package/dist/hex.d.mts.map +0 -1
  78. package/dist/hex.mjs +0 -61
  79. package/dist/hex.mjs.map +0 -1
  80. package/dist/type-checks/src/index.cjs +0 -5
  81. package/dist/type-checks/src/index.mjs +0 -7
  82. package/dist/type-checks/src/is-buffer.cjs +0 -12
  83. package/dist/type-checks/src/is-buffer.mjs +0 -12
  84. package/dist/type-checks/src/is-buffer.mjs.map +0 -1
  85. package/dist/type-checks/src/is-collection.cjs +0 -1
  86. package/dist/type-checks/src/is-collection.mjs +0 -3
  87. package/dist/type-checks/src/is-string.cjs +0 -12
  88. package/dist/type-checks/src/is-string.mjs +0 -12
  89. package/dist/type-checks/src/is-string.mjs.map +0 -1
  90. package/dist/type-checks/src/is-undefined.cjs +0 -8
  91. package/dist/type-checks/src/is-undefined.mjs +0 -8
  92. package/dist/type-checks/src/is-undefined.mjs.map +0 -1
  93. package/dist/type-checks/src/type-detect.cjs +0 -15
  94. package/dist/type-checks/src/type-detect.mjs +0 -17
  95. package/dist/type-checks/src/type-detect.mjs.map +0 -1
@@ -0,0 +1,960 @@
1
+
2
+ //#region ../json/src/canonical.ts
3
+ /**
4
+ * Converts a JavaScript value to a canonical JSON string representation. This function is used for signing JSON objects in a consistent way, ensuring that the same input will always produce the same output string. The canonicalization process includes:
5
+ * - Sorting object keys in lexicographical order.
6
+ * - Removing whitespace and line breaks.
7
+ * - Representing primitive values (null, boolean, number, string) in their standard JSON format.
8
+ * - Recursively applying these rules to nested objects and arrays.
9
+ *
10
+ * This function is designed to produce a deterministic string representation of a JSON value, which is essential for cryptographic signing and verification processes where the exact byte representation of the data must be consistent across different environments and implementations.
11
+ *
12
+ * @param obj - The JavaScript value to convert to a canonical JSON string.
13
+ * @returns A canonical JSON string representation of the input value.
14
+ */
15
+ function canonicalJson(obj) {
16
+ if (obj === null || obj === void 0) return "null";
17
+ if (typeof obj === "boolean" || typeof obj === "number") return JSON.stringify(obj);
18
+ if (typeof obj === "string") return JSON.stringify(obj);
19
+ if (Array.isArray(obj)) return `[${obj.map((item) => canonicalJson(item)).join(",")}]`;
20
+ if (typeof obj === "object") return `{${Object.keys(obj).sort().map((key) => {
21
+ const value = canonicalJson(obj[key]);
22
+ return `${JSON.stringify(key)}:${value}`;
23
+ }).join(",")}}`;
24
+ return "null";
25
+ }
26
+
27
+ //#endregion
28
+ //#region ../convert/src/array-buffer-to-string.ts
29
+ /**
30
+ * Convert an ArrayBuffer or Uint8Array to a string
31
+ *
32
+ * @param buffer - The ArrayBuffer or Uint8Array to convert
33
+ * @returns The converted string
34
+ */
35
+ function arrayBufferToString(buffer) {
36
+ const bytes = buffer instanceof Uint8Array ? buffer : new Uint8Array(buffer);
37
+ const len = bytes.byteLength;
38
+ if (len < 65535) return String.fromCharCode.apply(null, bytes);
39
+ let binary = "";
40
+ for (let i = 0; i < len; i++) binary += String.fromCharCode(bytes[i]);
41
+ return binary;
42
+ }
43
+
44
+ //#endregion
45
+ //#region ../type-checks/src/is-undefined.ts
46
+ const isUndefined = (value) => {
47
+ return value === void 0;
48
+ };
49
+
50
+ //#endregion
51
+ //#region ../type-checks/src/is-buffer.ts
52
+ const isBufferExists = typeof Buffer !== "undefined";
53
+ /**
54
+ * Check if the provided value's type is `Buffer`
55
+ */
56
+ const isBuffer = isBufferExists ? Buffer.isBuffer.bind(Buffer) : function isBuffer$1(value) {
57
+ return false;
58
+ };
59
+
60
+ //#endregion
61
+ //#region ../type-checks/src/type-detect.ts
62
+ const globalObject = ((Obj) => {
63
+ if (typeof globalThis === "object") return globalThis;
64
+ Object.defineProperty(Obj, "typeDetectGlobalObject", {
65
+ get() {
66
+ return this;
67
+ },
68
+ configurable: true
69
+ });
70
+ return globalThis;
71
+ })(Object.prototype);
72
+
73
+ //#endregion
74
+ //#region ../type-checks/src/is-string.ts
75
+ const isString = (value) => {
76
+ try {
77
+ return typeof value === "string";
78
+ } catch {
79
+ return false;
80
+ }
81
+ };
82
+
83
+ //#endregion
84
+ //#region ../convert/src/string-to-uint8-array.ts
85
+ /**
86
+ * Convert a string to Uint8Array
87
+ *
88
+ * @param text - The text to convert
89
+ * @returns The converted Uint8Array
90
+ */
91
+ function stringToUint8Array(text) {
92
+ return Uint8Array.from([...encodeURIComponent(text)].map((letter) => letter.codePointAt(0)));
93
+ }
94
+ /**
95
+ * Convert a base64 string to a Uint8Array
96
+ *
97
+ * @param data - The base64 string to convert
98
+ * @returns The converted Uint8Array
99
+ */
100
+ function base64StringToUint8Array(data) {
101
+ return stringToUint8Array(atob(data));
102
+ }
103
+
104
+ //#endregion
105
+ //#region ../convert/src/string-to-utf8-array.ts
106
+ const encoder = new TextEncoder();
107
+ /**
108
+ * Convert a string to a utf-8 array
109
+ *
110
+ * @param input - The string to convert
111
+ * @returns The converted utf-8 array
112
+ */
113
+ function stringToUtf8Array(input) {
114
+ return encoder.encode(input);
115
+ }
116
+
117
+ //#endregion
118
+ //#region ../convert/src/uint8-array-to-stream.ts
119
+ /**
120
+ * Concatenate an array of Uint8Array chunks into a single Uint8Array
121
+ *
122
+ * @param chunks - Array of Uint8Array chunks to concatenate
123
+ * @returns The concatenated Uint8Array
124
+ */
125
+ function concatUint8Array(chunks) {
126
+ let total = 0;
127
+ for (const chunk of chunks) total += chunk.length;
128
+ const result = new Uint8Array(total);
129
+ let offset = 0;
130
+ for (const chunk of chunks) {
131
+ result.set(chunk, offset);
132
+ offset += chunk.length;
133
+ }
134
+ return result;
135
+ }
136
+
137
+ //#endregion
138
+ //#region ../convert/src/uint8-array-to-string.ts
139
+ /**
140
+ * Convert a Uint8Array to a base64 string
141
+ *
142
+ * @param buffer - The Uint8Array to convert
143
+ * @returns The converted base64 string
144
+ */
145
+ function uint8ArrayToString(buffer) {
146
+ return btoa(arrayBufferToString(buffer));
147
+ }
148
+
149
+ //#endregion
150
+ //#region ../convert/src/utf8-array-to-string.ts
151
+ const decoder = new TextDecoder();
152
+ /**
153
+ * Convert a utf-8 array to string
154
+ *
155
+ * @param input - Utf-8 Array
156
+ * @returns The converted string
157
+ */
158
+ function utf8ArrayToString(input) {
159
+ return decoder.decode(input);
160
+ }
161
+
162
+ //#endregion
163
+ //#region src/base-64.ts
164
+ const ENCODE_MAP = new Uint8Array([
165
+ 65,
166
+ 66,
167
+ 67,
168
+ 68,
169
+ 69,
170
+ 70,
171
+ 71,
172
+ 72,
173
+ 73,
174
+ 74,
175
+ 75,
176
+ 76,
177
+ 77,
178
+ 78,
179
+ 79,
180
+ 80,
181
+ 81,
182
+ 82,
183
+ 83,
184
+ 84,
185
+ 85,
186
+ 86,
187
+ 87,
188
+ 88,
189
+ 89,
190
+ 90,
191
+ 97,
192
+ 98,
193
+ 99,
194
+ 100,
195
+ 101,
196
+ 102,
197
+ 103,
198
+ 104,
199
+ 105,
200
+ 106,
201
+ 107,
202
+ 108,
203
+ 109,
204
+ 110,
205
+ 111,
206
+ 112,
207
+ 113,
208
+ 114,
209
+ 115,
210
+ 116,
211
+ 117,
212
+ 118,
213
+ 119,
214
+ 120,
215
+ 121,
216
+ 122,
217
+ 48,
218
+ 49,
219
+ 50,
220
+ 51,
221
+ 52,
222
+ 53,
223
+ 54,
224
+ 55,
225
+ 56,
226
+ 57,
227
+ 43,
228
+ 47
229
+ ]);
230
+ const ENCODE_PAD = 61;
231
+ const DECODE_MAP$1 = new Uint8Array([
232
+ 100,
233
+ 100,
234
+ 100,
235
+ 100,
236
+ 100,
237
+ 100,
238
+ 100,
239
+ 100,
240
+ 100,
241
+ 100,
242
+ 100,
243
+ 100,
244
+ 100,
245
+ 100,
246
+ 100,
247
+ 100,
248
+ 100,
249
+ 100,
250
+ 100,
251
+ 100,
252
+ 100,
253
+ 100,
254
+ 100,
255
+ 100,
256
+ 100,
257
+ 100,
258
+ 100,
259
+ 100,
260
+ 100,
261
+ 100,
262
+ 100,
263
+ 100,
264
+ 100,
265
+ 100,
266
+ 100,
267
+ 100,
268
+ 100,
269
+ 100,
270
+ 100,
271
+ 100,
272
+ 100,
273
+ 100,
274
+ 100,
275
+ 62,
276
+ 100,
277
+ 100,
278
+ 100,
279
+ 63,
280
+ 52,
281
+ 53,
282
+ 54,
283
+ 55,
284
+ 56,
285
+ 57,
286
+ 58,
287
+ 59,
288
+ 60,
289
+ 61,
290
+ 100,
291
+ 100,
292
+ 100,
293
+ 100,
294
+ 100,
295
+ 100,
296
+ 100,
297
+ 0,
298
+ 1,
299
+ 2,
300
+ 3,
301
+ 4,
302
+ 5,
303
+ 6,
304
+ 7,
305
+ 8,
306
+ 9,
307
+ 10,
308
+ 11,
309
+ 12,
310
+ 13,
311
+ 14,
312
+ 15,
313
+ 16,
314
+ 17,
315
+ 18,
316
+ 19,
317
+ 20,
318
+ 21,
319
+ 22,
320
+ 23,
321
+ 24,
322
+ 25,
323
+ 100,
324
+ 100,
325
+ 100,
326
+ 100,
327
+ 100,
328
+ 100,
329
+ 26,
330
+ 27,
331
+ 28,
332
+ 29,
333
+ 30,
334
+ 31,
335
+ 32,
336
+ 33,
337
+ 34,
338
+ 35,
339
+ 36,
340
+ 37,
341
+ 38,
342
+ 39,
343
+ 40,
344
+ 41,
345
+ 42,
346
+ 43,
347
+ 44,
348
+ 45,
349
+ 46,
350
+ 47,
351
+ 48,
352
+ 49,
353
+ 50,
354
+ 51,
355
+ 100,
356
+ 100,
357
+ 100,
358
+ 100,
359
+ 100,
360
+ 100,
361
+ 100,
362
+ 100,
363
+ 100,
364
+ 100,
365
+ 100,
366
+ 100,
367
+ 100,
368
+ 100,
369
+ 100,
370
+ 100,
371
+ 100,
372
+ 100,
373
+ 100,
374
+ 100,
375
+ 100,
376
+ 100,
377
+ 100,
378
+ 100,
379
+ 100,
380
+ 100,
381
+ 100,
382
+ 100,
383
+ 100,
384
+ 100,
385
+ 100,
386
+ 100,
387
+ 100,
388
+ 100,
389
+ 100,
390
+ 100,
391
+ 100,
392
+ 100,
393
+ 100,
394
+ 100,
395
+ 100,
396
+ 100,
397
+ 100,
398
+ 100,
399
+ 100,
400
+ 100,
401
+ 100,
402
+ 100,
403
+ 100,
404
+ 100,
405
+ 100,
406
+ 100,
407
+ 100,
408
+ 100,
409
+ 100,
410
+ 100,
411
+ 100,
412
+ 100,
413
+ 100,
414
+ 100,
415
+ 100,
416
+ 100,
417
+ 100,
418
+ 100,
419
+ 100,
420
+ 100,
421
+ 100,
422
+ 100,
423
+ 100,
424
+ 100,
425
+ 100,
426
+ 100,
427
+ 100,
428
+ 100,
429
+ 100,
430
+ 100,
431
+ 100,
432
+ 100,
433
+ 100,
434
+ 100,
435
+ 100,
436
+ 100,
437
+ 100,
438
+ 100,
439
+ 100,
440
+ 100,
441
+ 100,
442
+ 100,
443
+ 100,
444
+ 100,
445
+ 100,
446
+ 100,
447
+ 100,
448
+ 100,
449
+ 100,
450
+ 100,
451
+ 100,
452
+ 100,
453
+ 100,
454
+ 100,
455
+ 100,
456
+ 100,
457
+ 100,
458
+ 100,
459
+ 100,
460
+ 100,
461
+ 100,
462
+ 100,
463
+ 100,
464
+ 100,
465
+ 100,
466
+ 100,
467
+ 100,
468
+ 100,
469
+ 100,
470
+ 100,
471
+ 100,
472
+ 100,
473
+ 100,
474
+ 100,
475
+ 100,
476
+ 100,
477
+ 100,
478
+ 100,
479
+ 100,
480
+ 100,
481
+ 100,
482
+ 100,
483
+ 100,
484
+ 100,
485
+ 100,
486
+ 100
487
+ ]);
488
+ /**
489
+ * Encodes a Uint8Array into a Base64 encoded Uint8Array.
490
+ *
491
+ * @credit https://github.com/hi-ogawa/js-utils
492
+ *
493
+ * @param input - The input Uint8Array or string to encode.
494
+ * @returns The Base64 encoded Uint8Array.
495
+ */
496
+ function encodeBase64(input) {
497
+ if (isString(input)) input = stringToUint8Array(input);
498
+ const xLen = input.length;
499
+ const result = new Uint8Array(Math.ceil(xLen / 3) * 4);
500
+ const chunkLen = Math.floor(xLen / 3);
501
+ for (let i$1 = 0; i$1 < chunkLen; i$1++) {
502
+ const chunk = input[3 * i$1 + 0] << 16 | input[3 * i$1 + 1] << 8 | input[3 * i$1 + 2];
503
+ result[4 * i$1 + 0] = ENCODE_MAP[chunk >> 18 & 63];
504
+ result[4 * i$1 + 1] = ENCODE_MAP[chunk >> 12 & 63];
505
+ result[4 * i$1 + 2] = ENCODE_MAP[chunk >> 6 & 63];
506
+ result[4 * i$1 + 3] = ENCODE_MAP[chunk >> 0 & 63];
507
+ }
508
+ const i = chunkLen;
509
+ switch (xLen % 3) {
510
+ case 1: {
511
+ const chunk = input[3 * i + 0] << 16;
512
+ result[4 * i + 0] = ENCODE_MAP[chunk >> 18 & 63];
513
+ result[4 * i + 1] = ENCODE_MAP[chunk >> 12 & 63];
514
+ result[4 * i + 2] = ENCODE_PAD;
515
+ result[4 * i + 3] = ENCODE_PAD;
516
+ break;
517
+ }
518
+ case 2: {
519
+ const chunk = input[3 * i + 0] << 16 | input[3 * i + 1] << 8;
520
+ result[4 * i + 0] = ENCODE_MAP[chunk >> 18 & 63];
521
+ result[4 * i + 1] = ENCODE_MAP[chunk >> 12 & 63];
522
+ result[4 * i + 2] = ENCODE_MAP[chunk >> 6 & 63];
523
+ result[4 * i + 3] = ENCODE_PAD;
524
+ break;
525
+ }
526
+ }
527
+ return utf8ArrayToString(result);
528
+ }
529
+ /**
530
+ * Decodes a Base64 encoded Uint8Array into a Uint8Array.
531
+ *
532
+ * @credit https://github.com/hi-ogawa/js-utils
533
+ *
534
+ * @param input - The Base64 encoded Uint8Array or string to decode.
535
+ * @returns The decoded Uint8Array.
536
+ */
537
+ function decodeBase64(input) {
538
+ if (isString(input)) input = stringToUint8Array(input);
539
+ const yLen = input.length;
540
+ if (yLen % 4 !== 0) throw new Error("invalid length");
541
+ let padLen = 0;
542
+ while (padLen < 2 && padLen < input.length && input[input.length - 1 - padLen] === ENCODE_PAD) padLen++;
543
+ for (let i$1 = 0; i$1 < input.length - padLen; i$1++) if (isUndefined(input[i$1]) || isUndefined(DECODE_MAP$1[input[i$1]]) || DECODE_MAP$1[input[i$1]] >= 64) throw new Error("invalid data");
544
+ const chunkLen = Math.floor((yLen - padLen) / 4);
545
+ const xLen = 3 * chunkLen + (3 - padLen) % 3;
546
+ const result = new Uint8Array(xLen);
547
+ for (let i$1 = 0; i$1 < chunkLen; i$1++) {
548
+ const chunk = DECODE_MAP$1[input[4 * i$1 + 0]] << 18 | DECODE_MAP$1[input[4 * i$1 + 1]] << 12 | DECODE_MAP$1[input[4 * i$1 + 2]] << 6 | DECODE_MAP$1[input[4 * i$1 + 3]] << 0;
549
+ result[3 * i$1] = chunk >> 16 & 255;
550
+ result[3 * i$1 + 1] = chunk >> 8 & 255;
551
+ result[3 * i$1 + 2] = chunk >> 0 & 255;
552
+ }
553
+ const i = chunkLen;
554
+ switch (xLen % 3) {
555
+ case 1: {
556
+ const chunk = DECODE_MAP$1[input[4 * i + 0]] << 18 | DECODE_MAP$1[input[4 * i + 1]] << 12;
557
+ result[3 * i] = chunk >> 16 & 255;
558
+ break;
559
+ }
560
+ case 2: {
561
+ const chunk = DECODE_MAP$1[input[4 * i + 0]] << 18 | DECODE_MAP$1[input[4 * i + 1]] << 12 | DECODE_MAP$1[input[4 * i + 2]] << 6;
562
+ result[3 * i] = chunk >> 16 & 255;
563
+ result[3 * i + 1] = chunk >> 8 & 255;
564
+ break;
565
+ }
566
+ }
567
+ return result;
568
+ }
569
+ /**
570
+ * Converts a Base64 encoded string to a [Base64url](https://datatracker.ietf.org/doc/html/rfc7515#appendix-C) encoded string.
571
+ *
572
+ * @see https://datatracker.ietf.org/doc/html/rfc7515#appendix-C
573
+ *
574
+ * @param base64 - The Base64 encoded string to convert.
575
+ * @returns The Base64url encoded string.
576
+ */
577
+ function base64UrlEncode(base64) {
578
+ return btoa(String.fromCharCode(...base64)).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
579
+ }
580
+ /**
581
+ * Converts a [Base64url](https://datatracker.ietf.org/doc/html/rfc7515#appendix-C) encoded string to a Base64 encoded string.
582
+ *
583
+ * @see https://datatracker.ietf.org/doc/html/rfc7515#appendix-C
584
+ *
585
+ * @param base64url - The Base64url encoded string to convert.
586
+ * @returns The Base64 encoded string.
587
+ */
588
+ function base64UrlDecode(base64url) {
589
+ const base64 = base64url.replace(/-/g, "+").replace(/_/g, "/");
590
+ return new Uint8Array([...atob(base64 + "=".repeat((4 - base64.length % 4) % 4))].map((c) => c.charCodeAt(0)));
591
+ }
592
+
593
+ //#endregion
594
+ //#region src/ed25519.ts
595
+ /**
596
+ * Generates an Ed25519 key pair using Cloudflare Workers' `NODE-ED25519` algorithm and returns the public key as a base64 string, the private key as a JWK object, and a key ID derived from the public key. The private key is returned as a JWK for compatibility with Web Crypto API operations.
597
+ *
598
+ * @remarks
599
+ * This function uses Cloudflare Workers' `NODE-ED25519` algorithm parameters, which are specific to Cloudflare Workers' implementation of Ed25519. The returned key pair is structured as follows:
600
+ * - The `publicKey` is returned as a base64url-encoded string of the raw public key bytes (the 'x' parameter from the JWK).
601
+ * - The `privateKeyJwk` is returned as a JWK object containing the private key parameters, which can be used for signing operations with the Web Crypto API.
602
+ * - The `keyId` is generated by hashing the raw public key bytes and taking the first 4 bytes of the hash, encoded as a hex string prefixed with "ed25519:". This provides a unique identifier for the key pair based on its public key.
603
+ *
604
+ * This function is not compatible with standard Web Crypto API Ed25519 implementations in other environments. The legacy function `generateSigningKeyPairLegacy` is also available for backwards compatibility, which returns the private key as a JSON stringified JWK. However, it is recommended to use this structured version for new code.
605
+ *
606
+ * @returns An object containing the `publicKey` as a base64 string, the `privateKeyJwk` as a JWK object, and a `keyId` derived from the public key.
607
+ * @throws {@link DOMException} If key generation or export fails in the Web Crypto API.
608
+ */
609
+ async function generateSigningKeyPair() {
610
+ const keyPair = await crypto.subtle.generateKey({
611
+ name: "NODE-ED25519",
612
+ namedCurve: "NODE-ED25519"
613
+ }, true, ["sign", "verify"]);
614
+ const publicKeyJwk = await crypto.subtle.exportKey("jwk", keyPair.publicKey);
615
+ const privateKeyJwk = await crypto.subtle.exportKey("jwk", keyPair.privateKey);
616
+ const publicKeyBytes = base64UrlDecode(publicKeyJwk.x);
617
+ const keyIdHash = new Uint8Array(await crypto.subtle.digest("SHA-256", publicKeyBytes)).slice(0, 4);
618
+ const keyId = `ed25519:${Array.from(keyIdHash).map((b) => b.toString(16).padStart(2, "0")).join("")}`;
619
+ return {
620
+ publicKey: base64UrlEncode(publicKeyBytes),
621
+ privateKeyJwk,
622
+ keyId
623
+ };
624
+ }
625
+ /**
626
+ * Generates an Ed25519 key pair and returns the public key as a base64 string, the private key as a JSON stringified JWK, and a key ID derived from the public key. The private key is returned in a legacy format for compatibility with existing code that expects a JSON string.
627
+ *
628
+ * @remarks
629
+ * Legacy function for backwards compatibility during migration. Returns the old format but with a proper key.
630
+ *
631
+ * @deprecated Use `generateSigningKeyPair` instead, which returns a structured key object and separate key ID. This legacy function is retained for backwards compatibility but may be removed in future releases.
632
+ *
633
+ * @returns An object containing the `publicKey` as a base64 string, the `privateKey` as a JSON stringified JWK, and a `keyId` derived from the public key.
634
+ * @throws {@link SyntaxError} If the generated private key cannot be stringified to JSON (should not occur under normal circumstances).
635
+ */
636
+ async function generateSigningKeyPairLegacy() {
637
+ const { publicKey, privateKeyJwk, keyId } = await generateSigningKeyPair();
638
+ return {
639
+ publicKey,
640
+ privateKey: JSON.stringify(privateKeyJwk),
641
+ keyId
642
+ };
643
+ }
644
+ /**
645
+ * Creates and attaches an [Ed25519 signature](https://matrix.org/docs/spec/client_server/latest#signing-json-objects) for a JSON object using [Matrix-style](https://matrix.org/docs/spec/client_server/latest#signing-json-objects) signing rules.
646
+ *
647
+ * @see https://matrix.org/docs/spec/client_server/latest#signing-json-objects
648
+ *
649
+ * @remarks
650
+ * - Signature output is encoded as un-padded base64url.
651
+ * - The original `obj` is not mutated.
652
+ * - Uses `NODE-ED25519` algorithm parameters for key import/sign operations.
653
+ *
654
+ * The function canonicalizes a copy of the input object after removing the `signatures`
655
+ * and `unsigned` properties, signs that canonical JSON payload with the provided private key,
656
+ * and returns a new object with the generated signature merged into `obj.signatures`.
657
+ *
658
+ * Existing signatures are preserved, including other keys under the same `serverName`.
659
+ *
660
+ * @param obj - The JSON object to sign.
661
+ * @param serverName - The signing entity name used as the top-level key in `signatures`.
662
+ * @param keyId - The key identifier used under `signatures[serverName]`.
663
+ * @param privateKeyJwk - The Ed25519 private key as a JWK object, or a JSON stringified JWK (legacy compatibility).
664
+ * @returns A new object containing all original fields plus an updated `signatures` map with the new signature.
665
+ * @throws {@link SyntaxError} If `privateKeyJwk` is a string that is not valid JSON.
666
+ * @throws {@link DOMException} If key import or signing fails in the Web Crypto API.
667
+ */
668
+ async function signJson(obj, serverName, keyId, privateKeyJwk) {
669
+ const jwk = typeof privateKeyJwk === "string" ? JSON.parse(privateKeyJwk) : privateKeyJwk;
670
+ const toSign = { ...obj };
671
+ delete toSign.signatures;
672
+ delete toSign.unsigned;
673
+ const privateKey = await crypto.subtle.importKey("jwk", jwk, {
674
+ name: "NODE-ED25519",
675
+ namedCurve: "NODE-ED25519"
676
+ }, false, ["sign"]);
677
+ const canonical = canonicalJson(toSign);
678
+ const signatureBytes = await crypto.subtle.sign({ name: "NODE-ED25519" }, privateKey, new TextEncoder().encode(canonical));
679
+ const signatureB64 = base64UrlEncode(new Uint8Array(signatureBytes));
680
+ const existingSignatures = obj.signatures ?? {};
681
+ return {
682
+ ...obj,
683
+ signatures: {
684
+ ...existingSignatures,
685
+ [serverName]: {
686
+ ...existingSignatures[serverName] ?? {},
687
+ [keyId]: signatureB64
688
+ }
689
+ }
690
+ };
691
+ }
692
+ /**
693
+ * Verifies an Ed25519 signature on a JSON object using [Matrix-style signing rules](https://matrix.org/docs/spec/client_server/latest#signing-json-objects). The function extracts the relevant signature from the `signatures` property of the input object, removes the `signatures` and `unsigned` properties to create a canonical JSON payload, and then verifies the signature against the provided public key. The public key is expected to be a base64url-encoded string of the raw public key bytes (the 'x' parameter from the JWK). The function returns `true` if the signature is valid and `false` otherwise. Any errors during the verification process are caught and logged, with a return value of `false` in case of failure.
694
+ *
695
+ * @see https://matrix.org/docs/spec/client_server/latest#signing-json-objects
696
+ *
697
+ * @remarks
698
+ * - Signature input is expected to be un-padded base64url.
699
+ * - The original `obj` is not mutated.
700
+ * - Uses `NODE-ED25519` algorithm parameters for key import/verify operations.
701
+ * - The function does not throw on verification failure; it returns `false` instead. Errors during key import or verification are logged to the console for debugging purposes.
702
+ *
703
+ * @param obj - The JSON object containing the signature to verify.
704
+ * @param serverName - The signing entity name used as the top-level key in `signatures`.
705
+ * @param keyId - The key identifier used under `signatures[serverName]` to locate the specific signature to verify.
706
+ * @param publicKeyB64 - The Ed25519 public key as a base64url-encoded string of the raw public key bytes (the 'x' parameter from the JWK).
707
+ * @returns A boolean indicating whether the signature is valid (`true`) or not (`false`). Returns `false` if the signature is missing, invalid, or if any errors occur during the verification process.
708
+ */
709
+ async function verifySignature(obj, serverName, keyId, publicKeyB64) {
710
+ try {
711
+ const signature = obj.signatures?.[serverName]?.[keyId];
712
+ if (!signature) return false;
713
+ const toVerify = { ...obj };
714
+ delete toVerify.signatures;
715
+ delete toVerify.unsigned;
716
+ const publicKeyBytes = base64UrlDecode(publicKeyB64);
717
+ const publicKey = await crypto.subtle.importKey("raw", publicKeyBytes, {
718
+ name: "NODE-ED25519",
719
+ namedCurve: "NODE-ED25519"
720
+ }, false, ["verify"]);
721
+ const signatureBytes = base64UrlDecode(signature);
722
+ const canonical = canonicalJson(toVerify);
723
+ return await crypto.subtle.verify({ name: "NODE-ED25519" }, publicKey, signatureBytes, new TextEncoder().encode(canonical));
724
+ } catch (error) {
725
+ console.error("Signature verification failed:", error);
726
+ return false;
727
+ }
728
+ }
729
+
730
+ //#endregion
731
+ //#region src/hex.ts
732
+ const ALPHABET = "0123456789ABCDEF";
733
+ const DECODE_MAP = {
734
+ "0": 0,
735
+ "1": 1,
736
+ "2": 2,
737
+ "3": 3,
738
+ "4": 4,
739
+ "5": 5,
740
+ "6": 6,
741
+ "7": 7,
742
+ "8": 8,
743
+ "9": 9,
744
+ a: 10,
745
+ A: 10,
746
+ b: 11,
747
+ B: 11,
748
+ c: 12,
749
+ C: 12,
750
+ d: 13,
751
+ D: 13,
752
+ e: 14,
753
+ E: 14,
754
+ f: 15,
755
+ F: 15
756
+ };
757
+ /**
758
+ * Encodes a Uint8Array into a hexadecimal string.
759
+ *
760
+ * @param input - The input Uint8Array.
761
+ * @returns The hexadecimal string.
762
+ */
763
+ function encodeHex(input) {
764
+ let result = "";
765
+ for (let i = 0; i < input.length; i++) {
766
+ result += ALPHABET[input[i] >> 4];
767
+ result += ALPHABET[input[i] & 15];
768
+ }
769
+ return result;
770
+ }
771
+ /**
772
+ * Encodes a Uint8Array into an uppercase hexadecimal string.
773
+ *
774
+ * @param input - The input Uint8Array.
775
+ * @returns The uppercase hexadecimal string.
776
+ */
777
+ function decodeHex(input) {
778
+ if (input.length % 2 !== 0) throw new Error("Invalid hex string");
779
+ const result = new Uint8Array(input.length / 2);
780
+ for (let i = 0; i < input.length; i += 2) {
781
+ if (!(input[i] in DECODE_MAP)) throw new Error("Invalid character");
782
+ if (!(input[i + 1] in DECODE_MAP)) throw new Error("Invalid character");
783
+ result[i / 2] |= DECODE_MAP[input[i]] << 4;
784
+ result[i / 2] |= DECODE_MAP[input[i + 1]];
785
+ }
786
+ return result;
787
+ }
788
+
789
+ //#endregion
790
+ //#region src/encryption.ts
791
+ /**
792
+ * Creates a CryptoKey object that can be used to encrypt any string.
793
+ *
794
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/generateKey
795
+ *
796
+ * @returns A promise that resolves to a CryptoKey object that can be used to encrypt and decrypt strings.
797
+ */
798
+ async function createKey() {
799
+ return crypto.subtle.generateKey({
800
+ name: "AES-GCM",
801
+ length: 256
802
+ }, true, ["encrypt", "decrypt"]);
803
+ }
804
+ /**
805
+ * Encodes a CryptoKey to base64 string, so that it can be embedded in JSON / JavaScript
806
+ *
807
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/exportKey
808
+ *
809
+ * @param key - The CryptoKey to encode
810
+ * @returns A promise that resolves to a base64 string representing the key
811
+ */
812
+ async function encodeKey(key) {
813
+ const exported = await crypto.subtle.exportKey("raw", key);
814
+ return encodeBase64(new Uint8Array(exported)).toString();
815
+ }
816
+ /**
817
+ * Decodes a base64 string into bytes and then imports the key.
818
+ *
819
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/importKey
820
+ *
821
+ * @param encoded - The base64 encoded key
822
+ * @returns A promise that resolves to a CryptoKey object that can be used to encrypt and decrypt strings
823
+ */
824
+ async function decodeKey(encoded) {
825
+ const bytes = decodeBase64(encoded);
826
+ return crypto.subtle.importKey("raw", bytes.buffer, "AES-GCM", true, ["encrypt", "decrypt"]);
827
+ }
828
+ const IV_LENGTH = 24;
829
+ /**
830
+ * Using a CryptoKey, use AES-GCM to encrypt a string into a base64 string.
831
+ *
832
+ * @remarks
833
+ * The initialization vector is randomly generated and prepended to the encrypted string. The IV is required for decryption, so it must be stored alongside the encrypted data.
834
+ *
835
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/encrypt
836
+ *
837
+ * @param key - The CryptoKey to use for encryption
838
+ * @param plaintext - The plaintext string to encrypt
839
+ * @returns A promise that resolves to a base64 string representing the encrypted data
840
+ */
841
+ async function encrypt(key, plaintext) {
842
+ const iv = crypto.getRandomValues(new Uint8Array(IV_LENGTH / 2));
843
+ const encrypted = await crypto.subtle.encrypt({
844
+ name: "AES-GCM",
845
+ iv
846
+ }, key, stringToUtf8Array(plaintext));
847
+ return encodeHex(iv) + encodeBase64(new Uint8Array(encrypted));
848
+ }
849
+ /**
850
+ * Takes a base64 encoded string, decodes it and returns the AES-GCM decrypted text.
851
+ *
852
+ * @remarks
853
+ * The initialization vector is expected to be prepended to the encrypted string. The IV is required for decryption, so it must be extracted from the start of the string.
854
+ *
855
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/decrypt
856
+ *
857
+ * @param key - The CryptoKey to use for decryption
858
+ * @param encrypted - The encrypted base64 encoded string to decrypt
859
+ * @returns A promise that resolves to the decrypted string
860
+ */
861
+ async function decrypt(key, encrypted) {
862
+ return utf8ArrayToString(await crypto.subtle.decrypt({
863
+ name: "AES-GCM",
864
+ iv: decodeHex(encrypted.slice(0, IV_LENGTH))
865
+ }, key, decodeBase64(encrypted.slice(IV_LENGTH))));
866
+ }
867
+ /**
868
+ * Encrypts a buffer using AES-GCM with a given CryptoKey.
869
+ *
870
+ * @remarks
871
+ * The initialization vector (IV) is randomly generated and prepended to the encrypted data. The resulting data is then encoded as a base64 string for easy storage/transmission.
872
+ *
873
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/encrypt
874
+ *
875
+ * @param key - The CryptoKey to use for encryption
876
+ * @param buffer - The buffer to encrypt
877
+ * @returns A promise that resolves to a base64 string representing the encrypted data
878
+ */
879
+ async function encryptBuffer(key, buffer) {
880
+ const iv = crypto.getRandomValues(new Uint8Array(16));
881
+ const encrypted = await crypto.subtle.encrypt({
882
+ name: "AES-GCM",
883
+ iv
884
+ }, key, buffer);
885
+ return uint8ArrayToString(concatUint8Array([iv, new Uint8Array(encrypted)]));
886
+ }
887
+ /**
888
+ * Decrypts a buffer using AES-GCM with a given CryptoKey.
889
+ *
890
+ * @remarks
891
+ * The initialization vector (IV) is expected to be prepended to the encrypted data. The IV is required for decryption, so it must be extracted from the start of the buffer.
892
+ *
893
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/decrypt
894
+ *
895
+ * @param key - The CryptoKey to use for decryption
896
+ * @param encrypted - The encrypted base64 encoded string to decrypt
897
+ * @returns A promise that resolves to the decrypted string
898
+ */
899
+ async function decryptBuffer(key, encrypted) {
900
+ const concatenated = base64StringToUint8Array(encrypted);
901
+ return crypto.subtle.decrypt({
902
+ name: "AES-GCM",
903
+ iv: concatenated.slice(0, 16)
904
+ }, key, concatenated.slice(16));
905
+ }
906
+
907
+ //#endregion
908
+ //#region src/random.ts
909
+ /**
910
+ * Generate a random byte array of the specified length using the Web Crypto API.
911
+ *
912
+ * @param length - The length of the random byte array to generate (default is 32 bytes)
913
+ * @returns A Uint8Array containing random bytes of the specified length
914
+ */
915
+ function generateRandomBytes(length = 32) {
916
+ return crypto.getRandomValues(new Uint8Array(length));
917
+ }
918
+ /**
919
+ * Generate a random string of the specified length using characters A-Z, a-z, and 0-9 for CSRF tokens, etc.
920
+ *
921
+ * @remarks
922
+ * This function uses the Web Crypto API's `crypto.getRandomValues` to generate secure random bytes,
923
+ * and then maps those bytes to characters in the specified character set. It uses rejection sampling
924
+ * to ensure a uniform distribution of characters without modulo bias.
925
+ *
926
+ * @param length - The length of the random string to generate (default is 32 characters)
927
+ * @returns A random string of the specified length
928
+ */
929
+ function generateRandomString(length = 32) {
930
+ const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
931
+ const charsLen = 62;
932
+ const maxValid = 256 - 256 % charsLen;
933
+ const result = [];
934
+ while (result.length < length) {
935
+ const bytes = generateRandomBytes(length - result.length);
936
+ for (const b of bytes) if (b < maxValid && result.length < length && chars[b % charsLen]) result.push(chars[b % charsLen]);
937
+ }
938
+ return result.join("");
939
+ }
940
+
941
+ //#endregion
942
+ exports.base64UrlDecode = base64UrlDecode;
943
+ exports.base64UrlEncode = base64UrlEncode;
944
+ exports.createKey = createKey;
945
+ exports.decodeBase64 = decodeBase64;
946
+ exports.decodeHex = decodeHex;
947
+ exports.decodeKey = decodeKey;
948
+ exports.decrypt = decrypt;
949
+ exports.decryptBuffer = decryptBuffer;
950
+ exports.encodeBase64 = encodeBase64;
951
+ exports.encodeHex = encodeHex;
952
+ exports.encodeKey = encodeKey;
953
+ exports.encrypt = encrypt;
954
+ exports.encryptBuffer = encryptBuffer;
955
+ exports.generateRandomBytes = generateRandomBytes;
956
+ exports.generateRandomString = generateRandomString;
957
+ exports.generateSigningKeyPair = generateSigningKeyPair;
958
+ exports.generateSigningKeyPairLegacy = generateSigningKeyPairLegacy;
959
+ exports.signJson = signJson;
960
+ exports.verifySignature = verifySignature;