@usearete/sdk 0.2.0 → 0.4.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 (39) hide show
  1. package/README.md +118 -0
  2. package/dist/index.cjs +7393 -0
  3. package/dist/index.cjs.map +1 -0
  4. package/dist/index.d.ts +802 -129
  5. package/dist/index.esm.js +3447 -526
  6. package/dist/index.esm.js.map +1 -1
  7. package/dist/ssr/handlers.cjs +794 -0
  8. package/dist/ssr/handlers.cjs.map +1 -0
  9. package/dist/ssr/handlers.esm.js +523 -4
  10. package/dist/ssr/handlers.esm.js.map +1 -1
  11. package/dist/ssr/index.cjs +795 -0
  12. package/dist/ssr/index.cjs.map +1 -0
  13. package/dist/ssr/index.esm.js +523 -4
  14. package/dist/ssr/index.esm.js.map +1 -1
  15. package/dist/ssr/nextjs-app.cjs +859 -0
  16. package/dist/ssr/nextjs-app.cjs.map +1 -0
  17. package/dist/ssr/nextjs-app.esm.js +523 -4
  18. package/dist/ssr/nextjs-app.esm.js.map +1 -1
  19. package/dist/ssr/tanstack-start.cjs +881 -0
  20. package/dist/ssr/tanstack-start.cjs.map +1 -0
  21. package/dist/ssr/tanstack-start.esm.js +523 -4
  22. package/dist/ssr/tanstack-start.esm.js.map +1 -1
  23. package/dist/ssr/vite.cjs +887 -0
  24. package/dist/ssr/vite.cjs.map +1 -0
  25. package/dist/ssr/vite.esm.js +523 -4
  26. package/dist/ssr/vite.esm.js.map +1 -1
  27. package/package.json +10 -8
  28. package/dist/index.js +0 -4448
  29. package/dist/index.js.map +0 -1
  30. package/dist/ssr/handlers.js +0 -294
  31. package/dist/ssr/handlers.js.map +0 -1
  32. package/dist/ssr/index.js +0 -295
  33. package/dist/ssr/index.js.map +0 -1
  34. package/dist/ssr/nextjs-app.js +0 -359
  35. package/dist/ssr/nextjs-app.js.map +0 -1
  36. package/dist/ssr/tanstack-start.js +0 -381
  37. package/dist/ssr/tanstack-start.js.map +0 -1
  38. package/dist/ssr/vite.js +0 -387
  39. package/dist/ssr/vite.js.map +0 -1
@@ -0,0 +1,794 @@
1
+ 'use strict';
2
+
3
+ /*! noble-ed25519 - MIT License (c) 2019 Paul Miller (paulmillr.com) */
4
+ /**
5
+ * 4KB JS implementation of ed25519 EdDSA signatures.
6
+ * Compliant with RFC8032, FIPS 186-5 & ZIP215.
7
+ * @module
8
+ * @example
9
+ * ```js
10
+ import * as ed from '@noble/ed25519';
11
+ (async () => {
12
+ const privKey = ed.utils.randomPrivateKey();
13
+ const message = Uint8Array.from([0xab, 0xbc, 0xcd, 0xde]);
14
+ const pubKey = await ed.getPublicKeyAsync(privKey); // Sync methods are also present
15
+ const signature = await ed.signAsync(message, privKey);
16
+ const isValid = await ed.verifyAsync(signature, message, pubKey);
17
+ })();
18
+ ```
19
+ */
20
+ /**
21
+ * Curve params. ed25519 is twisted edwards curve. Equation is −x² + y² = -a + dx²y².
22
+ * * P = `2n**255n - 19n` // field over which calculations are done
23
+ * * N = `2n**252n + 27742317777372353535851937790883648493n` // group order, amount of curve points
24
+ * * h = 8 // cofactor
25
+ * * a = `Fp.create(BigInt(-1))` // equation param
26
+ * * d = -121665/121666 a.k.a. `Fp.neg(121665 * Fp.inv(121666))` // equation param
27
+ * * Gx, Gy are coordinates of Generator / base point
28
+ */
29
+ const ed25519_CURVE = {
30
+ p: 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffedn,
31
+ n: 0x1000000000000000000000000000000014def9dea2f79cd65812631a5cf5d3edn,
32
+ h: 8n,
33
+ a: 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffecn,
34
+ d: 0x52036cee2b6ffe738cc740797779e89800700a4d4141d8ab75eb4dca135978a3n,
35
+ Gx: 0x216936d3cd6e53fec0a4e231fdd6dc5c692cc7609525a7b2c9562d608f25d51an,
36
+ Gy: 0x6666666666666666666666666666666666666666666666666666666666666658n,
37
+ };
38
+ const { p: P, n: N, Gx, Gy, a: _a, d: _d } = ed25519_CURVE;
39
+ const h = 8n; // cofactor
40
+ const L = 32; // field / group byte length
41
+ const L2 = 64;
42
+ // Helpers and Precomputes sections are reused between libraries
43
+ // ## Helpers
44
+ // ----------
45
+ // error helper, messes-up stack trace
46
+ const err = (m = '') => {
47
+ throw new Error(m);
48
+ };
49
+ const isBig = (n) => typeof n === 'bigint'; // is big integer
50
+ const isStr = (s) => typeof s === 'string'; // is string
51
+ const isBytes = (a) => a instanceof Uint8Array || (ArrayBuffer.isView(a) && a.constructor.name === 'Uint8Array');
52
+ /** assert is Uint8Array (of specific length) */
53
+ const abytes = (a, l) => !isBytes(a) || (typeof l === 'number' && l > 0 && a.length !== l)
54
+ ? err('Uint8Array expected')
55
+ : a;
56
+ /** create Uint8Array */
57
+ const u8n = (len) => new Uint8Array(len);
58
+ const u8fr = (buf) => Uint8Array.from(buf);
59
+ const padh = (n, pad) => n.toString(16).padStart(pad, '0');
60
+ const bytesToHex = (b) => Array.from(abytes(b))
61
+ .map((e) => padh(e, 2))
62
+ .join('');
63
+ const C = { _0: 48, _9: 57, A: 65, F: 70, a: 97, f: 102 }; // ASCII characters
64
+ const _ch = (ch) => {
65
+ if (ch >= C._0 && ch <= C._9)
66
+ return ch - C._0; // '2' => 50-48
67
+ if (ch >= C.A && ch <= C.F)
68
+ return ch - (C.A - 10); // 'B' => 66-(65-10)
69
+ if (ch >= C.a && ch <= C.f)
70
+ return ch - (C.a - 10); // 'b' => 98-(97-10)
71
+ return;
72
+ };
73
+ const hexToBytes = (hex) => {
74
+ const e = 'hex invalid';
75
+ if (!isStr(hex))
76
+ return err(e);
77
+ const hl = hex.length;
78
+ const al = hl / 2;
79
+ if (hl % 2)
80
+ return err(e);
81
+ const array = u8n(al);
82
+ for (let ai = 0, hi = 0; ai < al; ai++, hi += 2) {
83
+ // treat each char as ASCII
84
+ const n1 = _ch(hex.charCodeAt(hi)); // parse first char, multiply it by 16
85
+ const n2 = _ch(hex.charCodeAt(hi + 1)); // parse second char
86
+ if (n1 === undefined || n2 === undefined)
87
+ return err(e);
88
+ array[ai] = n1 * 16 + n2; // example: 'A9' => 10*16 + 9
89
+ }
90
+ return array;
91
+ };
92
+ /** normalize hex or ui8a to ui8a */
93
+ const toU8 = (a, len) => abytes(isStr(a) ? hexToBytes(a) : u8fr(abytes(a)), len);
94
+ const cr = () => globalThis?.crypto; // WebCrypto is available in all modern environments
95
+ const subtle = () => cr()?.subtle ?? err('crypto.subtle must be defined');
96
+ // prettier-ignore
97
+ const concatBytes = (...arrs) => {
98
+ const r = u8n(arrs.reduce((sum, a) => sum + abytes(a).length, 0)); // create u8a of summed length
99
+ let pad = 0; // walk through each array,
100
+ arrs.forEach(a => { r.set(a, pad); pad += a.length; }); // ensure they have proper type
101
+ return r;
102
+ };
103
+ /** WebCrypto OS-level CSPRNG (random number generator). Will throw when not available. */
104
+ const randomBytes = (len = L) => {
105
+ const c = cr();
106
+ return c.getRandomValues(u8n(len));
107
+ };
108
+ const big = BigInt;
109
+ const arange = (n, min, max, msg = 'bad number: out of range') => isBig(n) && min <= n && n < max ? n : err(msg);
110
+ /** modular division */
111
+ const M = (a, b = P) => {
112
+ const r = a % b;
113
+ return r >= 0n ? r : b + r;
114
+ };
115
+ const modN = (a) => M(a, N);
116
+ /** Modular inversion using eucledian GCD (non-CT). No negative exponent for now. */
117
+ // prettier-ignore
118
+ const invert = (num, md) => {
119
+ if (num === 0n || md <= 0n)
120
+ err('no inverse n=' + num + ' mod=' + md);
121
+ let a = M(num, md), b = md, x = 0n, u = 1n;
122
+ while (a !== 0n) {
123
+ const q = b / a, r = b % a;
124
+ const m = x - u * q;
125
+ b = a, a = r, x = u, u = m;
126
+ }
127
+ return b === 1n ? M(x, md) : err('no inverse'); // b is gcd at this point
128
+ };
129
+ const apoint = (p) => (p instanceof Point ? p : err('Point expected'));
130
+ // ## End of Helpers
131
+ // -----------------
132
+ const B256 = 2n ** 256n;
133
+ /** Point in XYZT extended coordinates. */
134
+ class Point {
135
+ static BASE;
136
+ static ZERO;
137
+ ex;
138
+ ey;
139
+ ez;
140
+ et;
141
+ constructor(ex, ey, ez, et) {
142
+ const max = B256;
143
+ this.ex = arange(ex, 0n, max);
144
+ this.ey = arange(ey, 0n, max);
145
+ this.ez = arange(ez, 1n, max);
146
+ this.et = arange(et, 0n, max);
147
+ Object.freeze(this);
148
+ }
149
+ static fromAffine(p) {
150
+ return new Point(p.x, p.y, 1n, M(p.x * p.y));
151
+ }
152
+ /** RFC8032 5.1.3: Uint8Array to Point. */
153
+ static fromBytes(hex, zip215 = false) {
154
+ const d = _d;
155
+ // Copy array to not mess it up.
156
+ const normed = u8fr(abytes(hex, L));
157
+ // adjust first LE byte = last BE byte
158
+ const lastByte = hex[31];
159
+ normed[31] = lastByte & ~0x80;
160
+ const y = bytesToNumLE(normed);
161
+ // zip215=true: 0 <= y < 2^256
162
+ // zip215=false, RFC8032: 0 <= y < 2^255-19
163
+ const max = zip215 ? B256 : P;
164
+ arange(y, 0n, max);
165
+ const y2 = M(y * y); // y²
166
+ const u = M(y2 - 1n); // u=y²-1
167
+ const v = M(d * y2 + 1n); // v=dy²+1
168
+ let { isValid, value: x } = uvRatio(u, v); // (uv³)(uv⁷)^(p-5)/8; square root
169
+ if (!isValid)
170
+ err('bad point: y not sqrt'); // not square root: bad point
171
+ const isXOdd = (x & 1n) === 1n; // adjust sign of x coordinate
172
+ const isLastByteOdd = (lastByte & 0x80) !== 0; // x_0, last bit
173
+ if (!zip215 && x === 0n && isLastByteOdd)
174
+ err('bad point: x==0, isLastByteOdd'); // x=0, x_0=1
175
+ if (isLastByteOdd !== isXOdd)
176
+ x = M(-x);
177
+ return new Point(x, y, 1n, M(x * y)); // Z=1, T=xy
178
+ }
179
+ /** Checks if the point is valid and on-curve. */
180
+ assertValidity() {
181
+ const a = _a;
182
+ const d = _d;
183
+ const p = this;
184
+ if (p.is0())
185
+ throw new Error('bad point: ZERO'); // TODO: optimize, with vars below?
186
+ // Equation in affine coordinates: ax² + y² = 1 + dx²y²
187
+ // Equation in projective coordinates (X/Z, Y/Z, Z): (aX² + Y²)Z² = Z⁴ + dX²Y²
188
+ const { ex: X, ey: Y, ez: Z, et: T } = p;
189
+ const X2 = M(X * X); // X²
190
+ const Y2 = M(Y * Y); // Y²
191
+ const Z2 = M(Z * Z); // Z²
192
+ const Z4 = M(Z2 * Z2); // Z⁴
193
+ const aX2 = M(X2 * a); // aX²
194
+ const left = M(Z2 * M(aX2 + Y2)); // (aX² + Y²)Z²
195
+ const right = M(Z4 + M(d * M(X2 * Y2))); // Z⁴ + dX²Y²
196
+ if (left !== right)
197
+ throw new Error('bad point: equation left != right (1)');
198
+ // In Extended coordinates we also have T, which is x*y=T/Z: check X*Y == Z*T
199
+ const XY = M(X * Y);
200
+ const ZT = M(Z * T);
201
+ if (XY !== ZT)
202
+ throw new Error('bad point: equation left != right (2)');
203
+ return this;
204
+ }
205
+ /** Equality check: compare points P&Q. */
206
+ equals(other) {
207
+ const { ex: X1, ey: Y1, ez: Z1 } = this;
208
+ const { ex: X2, ey: Y2, ez: Z2 } = apoint(other); // checks class equality
209
+ const X1Z2 = M(X1 * Z2);
210
+ const X2Z1 = M(X2 * Z1);
211
+ const Y1Z2 = M(Y1 * Z2);
212
+ const Y2Z1 = M(Y2 * Z1);
213
+ return X1Z2 === X2Z1 && Y1Z2 === Y2Z1;
214
+ }
215
+ is0() {
216
+ return this.equals(I);
217
+ }
218
+ /** Flip point over y coordinate. */
219
+ negate() {
220
+ return new Point(M(-this.ex), this.ey, this.ez, M(-this.et));
221
+ }
222
+ /** Point doubling. Complete formula. Cost: `4M + 4S + 1*a + 6add + 1*2`. */
223
+ double() {
224
+ const { ex: X1, ey: Y1, ez: Z1 } = this;
225
+ const a = _a;
226
+ // https://hyperelliptic.org/EFD/g1p/auto-twisted-extended.html#doubling-dbl-2008-hwcd
227
+ const A = M(X1 * X1);
228
+ const B = M(Y1 * Y1);
229
+ const C = M(2n * M(Z1 * Z1));
230
+ const D = M(a * A);
231
+ const x1y1 = X1 + Y1;
232
+ const E = M(M(x1y1 * x1y1) - A - B);
233
+ const G = D + B;
234
+ const F = G - C;
235
+ const H = D - B;
236
+ const X3 = M(E * F);
237
+ const Y3 = M(G * H);
238
+ const T3 = M(E * H);
239
+ const Z3 = M(F * G);
240
+ return new Point(X3, Y3, Z3, T3);
241
+ }
242
+ /** Point addition. Complete formula. Cost: `8M + 1*k + 8add + 1*2`. */
243
+ add(other) {
244
+ const { ex: X1, ey: Y1, ez: Z1, et: T1 } = this;
245
+ const { ex: X2, ey: Y2, ez: Z2, et: T2 } = apoint(other); // doesn't check if other on-curve
246
+ const a = _a;
247
+ const d = _d;
248
+ // https://hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html#addition-add-2008-hwcd-3
249
+ const A = M(X1 * X2);
250
+ const B = M(Y1 * Y2);
251
+ const C = M(T1 * d * T2);
252
+ const D = M(Z1 * Z2);
253
+ const E = M((X1 + Y1) * (X2 + Y2) - A - B);
254
+ const F = M(D - C);
255
+ const G = M(D + C);
256
+ const H = M(B - a * A);
257
+ const X3 = M(E * F);
258
+ const Y3 = M(G * H);
259
+ const T3 = M(E * H);
260
+ const Z3 = M(F * G);
261
+ return new Point(X3, Y3, Z3, T3);
262
+ }
263
+ /**
264
+ * Point-by-scalar multiplication. Scalar must be in range 1 <= n < CURVE.n.
265
+ * Uses {@link wNAF} for base point.
266
+ * Uses fake point to mitigate side-channel leakage.
267
+ * @param n scalar by which point is multiplied
268
+ * @param safe safe mode guards against timing attacks; unsafe mode is faster
269
+ */
270
+ multiply(n, safe = true) {
271
+ if (!safe && (n === 0n || this.is0()))
272
+ return I;
273
+ arange(n, 1n, N);
274
+ if (n === 1n)
275
+ return this;
276
+ if (this.equals(G))
277
+ return wNAF(n).p;
278
+ // init result point & fake point
279
+ let p = I;
280
+ let f = G;
281
+ for (let d = this; n > 0n; d = d.double(), n >>= 1n) {
282
+ // if bit is present, add to point
283
+ // if not present, add to fake, for timing safety
284
+ if (n & 1n)
285
+ p = p.add(d);
286
+ else if (safe)
287
+ f = f.add(d);
288
+ }
289
+ return p;
290
+ }
291
+ /** Convert point to 2d xy affine point. (X, Y, Z) ∋ (x=X/Z, y=Y/Z) */
292
+ toAffine() {
293
+ const { ex: x, ey: y, ez: z } = this;
294
+ // fast-paths for ZERO point OR Z=1
295
+ if (this.equals(I))
296
+ return { x: 0n, y: 1n };
297
+ const iz = invert(z, P);
298
+ // (Z * Z^-1) must be 1, otherwise bad math
299
+ if (M(z * iz) !== 1n)
300
+ err('invalid inverse');
301
+ // x = X*Z^-1; y = Y*Z^-1
302
+ return { x: M(x * iz), y: M(y * iz) };
303
+ }
304
+ toBytes() {
305
+ const { x, y } = this.assertValidity().toAffine();
306
+ const b = numTo32bLE(y);
307
+ // store sign in first LE byte
308
+ b[31] |= x & 1n ? 0x80 : 0;
309
+ return b;
310
+ }
311
+ toHex() {
312
+ return bytesToHex(this.toBytes());
313
+ } // encode to hex string
314
+ clearCofactor() {
315
+ return this.multiply(big(h), false);
316
+ }
317
+ isSmallOrder() {
318
+ return this.clearCofactor().is0();
319
+ }
320
+ isTorsionFree() {
321
+ // multiply by big number CURVE.n
322
+ let p = this.multiply(N / 2n, false).double(); // ensures the point is not "bad".
323
+ if (N % 2n)
324
+ p = p.add(this); // P^(N+1) // P*N == (P*(N/2))*2+P
325
+ return p.is0();
326
+ }
327
+ static fromHex(hex, zip215) {
328
+ return Point.fromBytes(toU8(hex), zip215);
329
+ }
330
+ get x() {
331
+ return this.toAffine().x;
332
+ }
333
+ get y() {
334
+ return this.toAffine().y;
335
+ }
336
+ toRawBytes() {
337
+ return this.toBytes();
338
+ }
339
+ }
340
+ /** Generator / base point */
341
+ const G = new Point(Gx, Gy, 1n, M(Gx * Gy));
342
+ /** Identity / zero point */
343
+ const I = new Point(0n, 1n, 1n, 0n);
344
+ // Static aliases
345
+ Point.BASE = G;
346
+ Point.ZERO = I;
347
+ const numTo32bLE = (num) => hexToBytes(padh(arange(num, 0n, B256), L2)).reverse();
348
+ const bytesToNumLE = (b) => big('0x' + bytesToHex(u8fr(abytes(b)).reverse()));
349
+ const pow2 = (x, power) => {
350
+ // pow2(x, 4) == x^(2^4)
351
+ let r = x;
352
+ while (power-- > 0n) {
353
+ r *= r;
354
+ r %= P;
355
+ }
356
+ return r;
357
+ };
358
+ // prettier-ignore
359
+ const pow_2_252_3 = (x) => {
360
+ const x2 = (x * x) % P; // x^2, bits 1
361
+ const b2 = (x2 * x) % P; // x^3, bits 11
362
+ const b4 = (pow2(b2, 2n) * b2) % P; // x^(2^4-1), bits 1111
363
+ const b5 = (pow2(b4, 1n) * x) % P; // x^(2^5-1), bits 11111
364
+ const b10 = (pow2(b5, 5n) * b5) % P; // x^(2^10)
365
+ const b20 = (pow2(b10, 10n) * b10) % P; // x^(2^20)
366
+ const b40 = (pow2(b20, 20n) * b20) % P; // x^(2^40)
367
+ const b80 = (pow2(b40, 40n) * b40) % P; // x^(2^80)
368
+ const b160 = (pow2(b80, 80n) * b80) % P; // x^(2^160)
369
+ const b240 = (pow2(b160, 80n) * b80) % P; // x^(2^240)
370
+ const b250 = (pow2(b240, 10n) * b10) % P; // x^(2^250)
371
+ const pow_p_5_8 = (pow2(b250, 2n) * x) % P; // < To pow to (p+3)/8, multiply it by x.
372
+ return { pow_p_5_8, b2 };
373
+ };
374
+ const RM1 = 0x2b8324804fc1df0b2b4d00993dfbd7a72f431806ad2fe478c4ee1b274a0ea0b0n; // √-1
375
+ // for sqrt comp
376
+ // prettier-ignore
377
+ const uvRatio = (u, v) => {
378
+ const v3 = M(v * v * v); // v³
379
+ const v7 = M(v3 * v3 * v); // v⁷
380
+ const pow = pow_2_252_3(u * v7).pow_p_5_8; // (uv⁷)^(p-5)/8
381
+ let x = M(u * v3 * pow); // (uv³)(uv⁷)^(p-5)/8
382
+ const vx2 = M(v * x * x); // vx²
383
+ const root1 = x; // First root candidate
384
+ const root2 = M(x * RM1); // Second root candidate; RM1 is √-1
385
+ const useRoot1 = vx2 === u; // If vx² = u (mod p), x is a square root
386
+ const useRoot2 = vx2 === M(-u); // If vx² = -u, set x <-- x * 2^((p-1)/4)
387
+ const noRoot = vx2 === M(-u * RM1); // There is no valid root, vx² = -u√-1
388
+ if (useRoot1)
389
+ x = root1;
390
+ if (useRoot2 || noRoot)
391
+ x = root2; // We return root2 anyway, for const-time
392
+ if ((M(x) & 1n) === 1n)
393
+ x = M(-x); // edIsNegative
394
+ return { isValid: useRoot1 || useRoot2, value: x };
395
+ };
396
+ // N == L, just weird naming
397
+ const modL_LE = (hash) => modN(bytesToNumLE(hash)); // modulo L; but little-endian
398
+ const sha512a = (...m) => etc.sha512Async(...m); // Async SHA512
399
+ // RFC8032 5.1.5
400
+ const hash2extK = (hashed) => {
401
+ // slice creates a copy, unlike subarray
402
+ const head = hashed.slice(0, L);
403
+ head[0] &= 248; // Clamp bits: 0b1111_1000
404
+ head[31] &= 127; // 0b0111_1111
405
+ head[31] |= 64; // 0b0100_0000
406
+ const prefix = hashed.slice(L, L2); // private key "prefix"
407
+ const scalar = modL_LE(head); // modular division over curve order
408
+ const point = G.multiply(scalar); // public key point
409
+ const pointBytes = point.toBytes(); // point serialized to Uint8Array
410
+ return { head, prefix, scalar, point, pointBytes };
411
+ };
412
+ // RFC8032 5.1.5; getPublicKey async, sync. Hash priv key and extract point.
413
+ const getExtendedPublicKeyAsync = (priv) => sha512a(toU8(priv, L)).then(hash2extK);
414
+ /** Creates 32-byte ed25519 public key from 32-byte private key. Async. */
415
+ const getPublicKeyAsync = (priv) => getExtendedPublicKeyAsync(priv).then((p) => p.pointBytes);
416
+ const hashFinishA = (res) => sha512a(res.hashable).then(res.finish);
417
+ const _sign = (e, rBytes, msg) => {
418
+ // sign() shared code
419
+ const { pointBytes: P, scalar: s } = e;
420
+ const r = modL_LE(rBytes); // r was created outside, reduce it modulo L
421
+ const R = G.multiply(r).toBytes(); // R = [r]B
422
+ const hashable = concatBytes(R, P, msg); // dom2(F, C) || R || A || PH(M)
423
+ const finish = (hashed) => {
424
+ // k = SHA512(dom2(F, C) || R || A || PH(M))
425
+ const S = modN(r + modL_LE(hashed) * s); // S = (r + k * s) mod L; 0 <= s < l
426
+ return abytes(concatBytes(R, numTo32bLE(S)), L2); // 64-byte sig: 32b R.x + 32b LE(S)
427
+ };
428
+ return { hashable, finish };
429
+ };
430
+ /**
431
+ * Signs message (NOT message hash) using private key. Async.
432
+ * Follows RFC8032 5.1.6.
433
+ */
434
+ const signAsync = async (msg, privKey) => {
435
+ const m = toU8(msg);
436
+ const e = await getExtendedPublicKeyAsync(privKey);
437
+ const rBytes = await sha512a(e.prefix, m); // r = SHA512(dom2(F, C) || prefix || PH(M))
438
+ return hashFinishA(_sign(e, rBytes, m)); // gen R, k, S, then 64-byte signature
439
+ };
440
+ /** Math, hex, byte helpers. Not in `utils` because utils share API with noble-curves. */
441
+ const etc = {
442
+ sha512Async: async (...messages) => {
443
+ const s = subtle();
444
+ const m = concatBytes(...messages);
445
+ return u8n(await s.digest('SHA-512', m.buffer));
446
+ },
447
+ sha512Sync: undefined,
448
+ bytesToHex: bytesToHex,
449
+ hexToBytes: hexToBytes,
450
+ concatBytes: concatBytes,
451
+ mod: M,
452
+ invert: invert,
453
+ randomBytes: randomBytes,
454
+ };
455
+ // ## Precomputes
456
+ // --------------
457
+ const W = 8; // W is window size
458
+ const scalarBits = 256;
459
+ const pwindows = Math.ceil(scalarBits / W) + 1; // 33 for W=8
460
+ const pwindowSize = 2 ** (W - 1); // 128 for W=8
461
+ const precompute = () => {
462
+ const points = [];
463
+ let p = G;
464
+ let b = p;
465
+ for (let w = 0; w < pwindows; w++) {
466
+ b = p;
467
+ points.push(b);
468
+ for (let i = 1; i < pwindowSize; i++) {
469
+ b = b.add(p);
470
+ points.push(b);
471
+ } // i=1, bc we skip 0
472
+ p = b.double();
473
+ }
474
+ return points;
475
+ };
476
+ let Gpows = undefined; // precomputes for base point G
477
+ // const-time negate
478
+ const ctneg = (cnd, p) => {
479
+ const n = p.negate();
480
+ return cnd ? n : p;
481
+ };
482
+ /**
483
+ * Precomputes give 12x faster getPublicKey(), 10x sign(), 2x verify() by
484
+ * caching multiples of G (base point). Cache is stored in 32MB of RAM.
485
+ * Any time `G.multiply` is done, precomputes are used.
486
+ * Not used for getSharedSecret, which instead multiplies random pubkey `P.multiply`.
487
+ *
488
+ * w-ary non-adjacent form (wNAF) precomputation method is 10% slower than windowed method,
489
+ * but takes 2x less RAM. RAM reduction is possible by utilizing `.subtract`.
490
+ *
491
+ * !! Precomputes can be disabled by commenting-out call of the wNAF() inside Point#multiply().
492
+ */
493
+ const wNAF = (n) => {
494
+ const comp = Gpows || (Gpows = precompute());
495
+ let p = I;
496
+ let f = G; // f must be G, or could become I in the end
497
+ const pow_2_w = 2 ** W; // 256 for W=8
498
+ const maxNum = pow_2_w; // 256 for W=8
499
+ const mask = big(pow_2_w - 1); // 255 for W=8 == mask 0b11111111
500
+ const shiftBy = big(W); // 8 for W=8
501
+ for (let w = 0; w < pwindows; w++) {
502
+ let wbits = Number(n & mask); // extract W bits.
503
+ n >>= shiftBy; // shift number by W bits.
504
+ if (wbits > pwindowSize) {
505
+ wbits -= maxNum;
506
+ n += 1n;
507
+ } // split if bits > max: +224 => 256-32
508
+ const off = w * pwindowSize;
509
+ const offF = off; // offsets, evaluate both
510
+ const offP = off + Math.abs(wbits) - 1;
511
+ const isEven = w % 2 !== 0; // conditions, evaluate both
512
+ const isNeg = wbits < 0;
513
+ if (wbits === 0) {
514
+ // off == I: can't add it. Adding random offF instead.
515
+ f = f.add(ctneg(isEven, comp[offF])); // bits are 0: add garbage to fake point
516
+ }
517
+ else {
518
+ p = p.add(ctneg(isNeg, comp[offP])); // bits are 1: add to result point
519
+ }
520
+ }
521
+ return { p, f }; // return both real and fake points for JIT
522
+ };
523
+
524
+ /**
525
+ * Base64URL encoding/decoding utilities
526
+ */
527
+ /**
528
+ * Encode Uint8Array to base64url string (RFC 4648)
529
+ */
530
+ function encode(bytes) {
531
+ // Convert to regular base64
532
+ const base64 = Buffer.from(bytes).toString('base64');
533
+ // Convert to base64url: replace + with -, / with _, remove padding
534
+ return base64
535
+ .replace(/\+/g, '-')
536
+ .replace(/\//g, '_')
537
+ .replace(/=/g, '');
538
+ }
539
+ /**
540
+ * Decode base64url string to Uint8Array
541
+ */
542
+ function decode(base64url) {
543
+ // Convert from base64url to regular base64
544
+ let base64 = base64url
545
+ .replace(/-/g, '+')
546
+ .replace(/_/g, '/');
547
+ // Add padding if needed
548
+ const padding = 4 - (base64.length % 4);
549
+ if (padding !== 4) {
550
+ base64 += '='.repeat(padding);
551
+ }
552
+ return new Uint8Array(Buffer.from(base64, 'base64'));
553
+ }
554
+ const base64url = {
555
+ encode,
556
+ decode,
557
+ };
558
+
559
+ /**
560
+ * Arete Auth Server - Drop-in Endpoint Handlers
561
+ *
562
+ * These are framework-agnostic API route handlers that users can mount however they like.
563
+ * They handle token minting and JWKS serving directly using Ed25519 signing.
564
+ *
565
+ * @example
566
+ * ```typescript
567
+ * // app/api/arete/sessions/route.ts (Next.js App Router)
568
+ * import { handleSessionRequest, handleJwksRequest } from '@usearete/sdk/ssr/handlers';
569
+ *
570
+ * export async function POST(request: Request) {
571
+ * const user = await getAuthenticatedUser(request);
572
+ * if (!user) return new Response('Unauthorized', { status: 401 });
573
+ * return handleSessionRequest({}, user.id);
574
+ * }
575
+ *
576
+ * export async function GET() {
577
+ * return handleJwksRequest();
578
+ * }
579
+ * ```
580
+ */
581
+ /**
582
+ * Decode base64 to Uint8Array
583
+ */
584
+ function decodeBase64(base64) {
585
+ const binary = Buffer.from(base64, 'base64');
586
+ return new Uint8Array(binary);
587
+ }
588
+ /**
589
+ * Encode Uint8Array to base64url
590
+ */
591
+ function encodeBase64url(bytes) {
592
+ return base64url.encode(bytes);
593
+ }
594
+ /**
595
+ * Generate a key ID from public key bytes
596
+ */
597
+ function deriveKeyId(publicKey) {
598
+ // Use first 8 bytes of public key as hex for kid
599
+ return Array.from(publicKey.slice(0, 8))
600
+ .map(b => b.toString(16).padStart(2, '0'))
601
+ .join('');
602
+ }
603
+ /**
604
+ * Create JWT header for Ed25519
605
+ */
606
+ function createJwtHeader(keyId) {
607
+ const header = {
608
+ alg: 'EdDSA',
609
+ typ: 'JWT',
610
+ kid: keyId,
611
+ };
612
+ return encodeBase64url(new TextEncoder().encode(JSON.stringify(header)));
613
+ }
614
+ /**
615
+ * Create JWT payload from claims
616
+ */
617
+ function createJwtPayload(claims) {
618
+ return encodeBase64url(new TextEncoder().encode(JSON.stringify(claims)));
619
+ }
620
+ /**
621
+ * Sign data with Ed25519
622
+ */
623
+ async function signEd25519(data, privateKey) {
624
+ const messageBytes = new TextEncoder().encode(data);
625
+ return await signAsync(messageBytes, privateKey);
626
+ }
627
+ /**
628
+ * Mint a session token using Ed25519 signing
629
+ * `subject` must come from verified server-side auth or trusted service code.
630
+ */
631
+ async function mintSessionToken(config, subject = 'anonymous', scope = 'read', origin) {
632
+ const signingKeyBase64 = config.signingKey || process.env.ARETE_SIGNING_KEY;
633
+ if (!signingKeyBase64) {
634
+ throw new Error('ARETE_SIGNING_KEY not set. Generate with: node -e "console.log(require(\'crypto\').randomBytes(32).toString(\'base64\'))"');
635
+ }
636
+ const privateKeyBytes = decodeBase64(signingKeyBase64);
637
+ if (privateKeyBytes.length !== 32) {
638
+ throw new Error(`Invalid signing key length: expected 32 bytes, got ${privateKeyBytes.length}. ` +
639
+ 'Ed25519 signing key must be 32 bytes (base64-encoded).');
640
+ }
641
+ // Derive public key from private key
642
+ const publicKeyBytes = await getPublicKeyAsync(privateKeyBytes);
643
+ const keyId = config.keyId || deriveKeyId(publicKeyBytes);
644
+ const issuer = config.issuer || process.env.ARETE_ISSUER || 'arete';
645
+ const audience = config.audience || process.env.ARETE_AUDIENCE || 'arete';
646
+ const ttlSeconds = config.ttlSeconds || 300;
647
+ const now = Math.floor(Date.now() / 1000);
648
+ const expiresAt = now + ttlSeconds;
649
+ const claims = {
650
+ iss: issuer,
651
+ sub: subject,
652
+ aud: audience,
653
+ iat: now,
654
+ nbf: now,
655
+ exp: expiresAt,
656
+ jti: crypto.randomUUID(),
657
+ scope,
658
+ metering_key: `meter:${subject}`,
659
+ key_class: 'secret',
660
+ limits: config.limits || {
661
+ max_connections: 10,
662
+ max_subscriptions: 100,
663
+ max_snapshot_rows: 1000,
664
+ max_messages_per_minute: 10000,
665
+ max_bytes_per_minute: 100 * 1024 * 1024,
666
+ },
667
+ };
668
+ // Add origin binding if provided
669
+ if (origin) {
670
+ claims.origin = origin;
671
+ }
672
+ // Create JWT
673
+ const header = createJwtHeader(keyId);
674
+ const payload = createJwtPayload(claims);
675
+ const signingInput = `${header}.${payload}`;
676
+ const signature = await signEd25519(signingInput, privateKeyBytes);
677
+ const signatureBase64 = encodeBase64url(signature);
678
+ const token = `${signingInput}.${signatureBase64}`;
679
+ return {
680
+ token,
681
+ expires_at: expiresAt,
682
+ };
683
+ }
684
+ /**
685
+ * Generate JWKS response from signing key
686
+ */
687
+ async function generateJwks(config) {
688
+ const signingKeyBase64 = config.signingKey || process.env.ARETE_SIGNING_KEY;
689
+ const publicKeyBase64 = config.publicKey || process.env.ARETE_PUBLIC_KEY;
690
+ if (!signingKeyBase64 && !publicKeyBase64) {
691
+ return { keys: [] };
692
+ }
693
+ let publicKeyBytes;
694
+ if (publicKeyBase64) {
695
+ // Use provided public key
696
+ publicKeyBytes = decodeBase64(publicKeyBase64);
697
+ if (publicKeyBytes.length !== 32) {
698
+ throw new Error(`Invalid public key length: expected 32 bytes, got ${publicKeyBytes.length}`);
699
+ }
700
+ }
701
+ else {
702
+ // Derive public key from private key
703
+ const privateKeyBytes = decodeBase64(signingKeyBase64);
704
+ if (privateKeyBytes.length !== 32) {
705
+ throw new Error(`Invalid signing key length: expected 32 bytes, got ${privateKeyBytes.length}`);
706
+ }
707
+ publicKeyBytes = await getPublicKeyAsync(privateKeyBytes);
708
+ }
709
+ const keyId = config.keyId ||
710
+ (publicKeyBase64 ? 'key-1' : deriveKeyId(publicKeyBytes));
711
+ return {
712
+ keys: [
713
+ {
714
+ kty: 'OKP',
715
+ crv: 'Ed25519',
716
+ kid: keyId,
717
+ use: 'sig',
718
+ alg: 'EdDSA',
719
+ x: encodeBase64url(publicKeyBytes),
720
+ },
721
+ ],
722
+ };
723
+ }
724
+ /**
725
+ * Framework-agnostic request handler for token minting
726
+ * Returns a Response object that can be used with any framework
727
+ * The `subject` must be derived from verified server-side auth.
728
+ */
729
+ async function handleSessionRequest(config = {}, subject = 'anonymous', scope = 'read', origin) {
730
+ try {
731
+ const tokenData = await mintSessionToken(config, subject, scope, origin);
732
+ return new Response(JSON.stringify(tokenData), {
733
+ status: 200,
734
+ headers: {
735
+ 'Content-Type': 'application/json',
736
+ },
737
+ });
738
+ }
739
+ catch (error) {
740
+ return new Response(JSON.stringify({
741
+ error: error instanceof Error ? error.message : 'Failed to mint token',
742
+ }), {
743
+ status: 500,
744
+ headers: {
745
+ 'Content-Type': 'application/json',
746
+ },
747
+ });
748
+ }
749
+ }
750
+ /**
751
+ * Framework-agnostic request handler for JWKS endpoint
752
+ */
753
+ async function handleJwksRequest(config = {}) {
754
+ try {
755
+ const jwks = await generateJwks(config);
756
+ return new Response(JSON.stringify(jwks), {
757
+ status: 200,
758
+ headers: {
759
+ 'Content-Type': 'application/json',
760
+ },
761
+ });
762
+ }
763
+ catch (error) {
764
+ return new Response(JSON.stringify({
765
+ error: error instanceof Error ? error.message : 'Failed to generate JWKS',
766
+ }), {
767
+ status: 500,
768
+ headers: {
769
+ 'Content-Type': 'application/json',
770
+ },
771
+ });
772
+ }
773
+ }
774
+ /**
775
+ * Framework-agnostic health check handler
776
+ */
777
+ function handleHealthRequest() {
778
+ return new Response(JSON.stringify({
779
+ status: 'healthy',
780
+ version: '0.5.10',
781
+ }), {
782
+ status: 200,
783
+ headers: {
784
+ 'Content-Type': 'application/json',
785
+ },
786
+ });
787
+ }
788
+
789
+ exports.generateJwks = generateJwks;
790
+ exports.handleHealthRequest = handleHealthRequest;
791
+ exports.handleJwksRequest = handleJwksRequest;
792
+ exports.handleSessionRequest = handleSessionRequest;
793
+ exports.mintSessionToken = mintSessionToken;
794
+ //# sourceMappingURL=handlers.cjs.map