@calimero-network/mero-js 25.3.0 → 25.3.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.
package/dist/index.cjs CHANGED
@@ -331,6 +331,244 @@ function createTimeoutSignal(timeoutMs) {
331
331
  return controller.signal;
332
332
  }
333
333
 
334
+ // src/crypto/internal.ts
335
+ var PKCS8_ED25519_PREFIX = new Uint8Array([
336
+ 48,
337
+ 46,
338
+ 2,
339
+ 1,
340
+ 0,
341
+ 48,
342
+ 5,
343
+ 6,
344
+ 3,
345
+ 43,
346
+ 101,
347
+ 112,
348
+ 4,
349
+ 34,
350
+ 4,
351
+ 32
352
+ ]);
353
+ function u32le(value) {
354
+ const out = new Uint8Array(4);
355
+ new DataView(out.buffer).setUint32(0, value, true);
356
+ return out;
357
+ }
358
+ function hex(bytes) {
359
+ return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
360
+ }
361
+ function fromHex(value, label2, expectedBytes) {
362
+ const clean = value.trim();
363
+ if (!/^[0-9a-fA-F]*$/.test(clean) || clean.length !== expectedBytes * 2) {
364
+ throw new Error(
365
+ `${label2} must be ${expectedBytes * 2} hex characters, got ${clean.length}`
366
+ );
367
+ }
368
+ const bytes = new Uint8Array(expectedBytes);
369
+ for (let i = 0; i < expectedBytes; i += 1) {
370
+ bytes[i] = Number.parseInt(clean.slice(i * 2, i * 2 + 2), 16);
371
+ }
372
+ return bytes;
373
+ }
374
+ function fromHexUnsized(value, label2) {
375
+ const clean = value.trim();
376
+ if (!/^[0-9a-fA-F]*$/.test(clean) || clean.length % 2 !== 0) {
377
+ throw new Error(`${label2} must be an even number of hex characters`);
378
+ }
379
+ const bytes = new Uint8Array(clean.length / 2);
380
+ for (let i = 0; i < bytes.length; i += 1) {
381
+ bytes[i] = Number.parseInt(clean.slice(i * 2, i * 2 + 2), 16);
382
+ }
383
+ return bytes;
384
+ }
385
+ function u64le(value) {
386
+ const out = new Uint8Array(8);
387
+ new DataView(out.buffer).setBigUint64(0, BigInt(value), true);
388
+ return out;
389
+ }
390
+ function concat(...parts) {
391
+ const out = new Uint8Array(parts.reduce((total, p) => total + p.length, 0));
392
+ let offset = 0;
393
+ for (const part of parts) {
394
+ out.set(part, offset);
395
+ offset += part.length;
396
+ }
397
+ return out;
398
+ }
399
+ async function domainHash(domain, parts) {
400
+ const chunks = [u64le(domain.length), domain];
401
+ for (const part of parts) {
402
+ chunks.push(u64le(part.length), part);
403
+ }
404
+ return new Uint8Array(
405
+ await crypto.subtle.digest("SHA-256", concat(...chunks))
406
+ );
407
+ }
408
+ async function importSigningKey(secretHex) {
409
+ const seed = fromHex(secretHex, "deviceSecret", 32);
410
+ const pkcs8 = concat(PKCS8_ED25519_PREFIX, seed);
411
+ try {
412
+ return await crypto.subtle.importKey(
413
+ "pkcs8",
414
+ pkcs8,
415
+ { name: "Ed25519" },
416
+ false,
417
+ ["sign"]
418
+ );
419
+ } catch (cause) {
420
+ const detail = cause instanceof Error ? `: ${cause.message}` : "";
421
+ throw new Error(
422
+ "this runtime has no WebCrypto Ed25519, so a warrant cannot be signed here. Node 18.4+, Safari 17+, Firefox 129+ and Chrome 137+ have it; otherwise mint the warrant with `merod account warrant`" + detail
423
+ );
424
+ }
425
+ }
426
+ async function verifySignature(publicKey, signature, payload) {
427
+ const key = await crypto.subtle.importKey(
428
+ "raw",
429
+ fromHex(publicKey, "publicKey", 32),
430
+ { name: "Ed25519" },
431
+ false,
432
+ ["verify"]
433
+ );
434
+ return crypto.subtle.verify({ name: "Ed25519" }, key, signature, payload);
435
+ }
436
+ async function derivePublicKey(secretHex) {
437
+ const seed = fromHex(secretHex, "deviceSecret", 32);
438
+ const pkcs8 = concat(PKCS8_ED25519_PREFIX, seed);
439
+ const key = await crypto.subtle.importKey(
440
+ "pkcs8",
441
+ pkcs8,
442
+ { name: "Ed25519" },
443
+ true,
444
+ ["sign"]
445
+ );
446
+ const jwk = await crypto.subtle.exportKey("jwk", key);
447
+ const raw = jwk.x;
448
+ return Uint8Array.from(
449
+ atob(raw.replace(/-/g, "+").replace(/_/g, "/")),
450
+ (c) => c.charCodeAt(0)
451
+ );
452
+ }
453
+
454
+ // src/request/request-sig.ts
455
+ var SIGN_DOMAIN = new TextEncoder().encode("calimero.auth.request.v2");
456
+ var BODY_DOMAIN = new TextEncoder().encode("calimero.auth.request.body.v1");
457
+ function borshString(value) {
458
+ const bytes = new TextEncoder().encode(value);
459
+ return concat(u32le(bytes.length), bytes);
460
+ }
461
+ async function requestBodyHash(body = new Uint8Array(0)) {
462
+ const bytes = typeof body === "string" ? new TextEncoder().encode(body) : body;
463
+ return domainHash(BODY_DOMAIN, [bytes]);
464
+ }
465
+ async function signRequest(input) {
466
+ const key = await importSigningKey(input.signerSecret);
467
+ const bodyHash = await requestBodyHash(input.body);
468
+ const issuedAt = u64le(input.issuedAt);
469
+ const expiresAt = u64le(input.expiresAt);
470
+ const encoder = new TextEncoder();
471
+ const preimage = await domainHash(SIGN_DOMAIN, [
472
+ encoder.encode(input.method),
473
+ encoder.encode(input.path),
474
+ bodyHash,
475
+ issuedAt,
476
+ expiresAt
477
+ ]);
478
+ const signature = new Uint8Array(
479
+ await crypto.subtle.sign({ name: "Ed25519" }, key, preimage)
480
+ );
481
+ return hex(
482
+ concat(
483
+ borshString(input.method),
484
+ borshString(input.path),
485
+ bodyHash,
486
+ issuedAt,
487
+ expiresAt,
488
+ signature
489
+ )
490
+ );
491
+ }
492
+
493
+ // src/request-proof/request-proof.ts
494
+ var utf8 = (text) => new TextEncoder().encode(text);
495
+ var REQUEST_PROOF_HEADER = "X-Calimero-Proof";
496
+ var DEFAULT_VALID_FOR = 300;
497
+ async function signRequestProof(input) {
498
+ const { method, path, credential } = input;
499
+ if (!method) throw new Error("signRequestProof needs the method the request will use");
500
+ if (!path.startsWith("/")) {
501
+ throw new Error(`signRequestProof needs a path, got ${JSON.stringify(path)}`);
502
+ }
503
+ const bodyHash = await requestBodyHash(input.body);
504
+ const issuedAt = Math.floor(input.now ?? Date.now() / 1e3);
505
+ const expiresAt = issuedAt + (input.validForSeconds ?? DEFAULT_VALID_FOR);
506
+ const payload = await domainHash(SIGN_DOMAIN, [
507
+ utf8(method),
508
+ utf8(path),
509
+ bodyHash,
510
+ u64le(issuedAt),
511
+ u64le(expiresAt)
512
+ ]);
513
+ const signature = await signPayload(payload, input);
514
+ if (signature.length !== 64) {
515
+ throw new Error(`the signer returned ${signature.length} bytes, not a 64-byte signature`);
516
+ }
517
+ const requestSig = concat(
518
+ u32le(utf8(method).length),
519
+ utf8(method),
520
+ u32le(utf8(path).length),
521
+ utf8(path),
522
+ bodyHash,
523
+ u64le(issuedAt),
524
+ u64le(expiresAt),
525
+ signature
526
+ );
527
+ const accountProof = hexToBytes(credential, "credential");
528
+ return hex(concat(accountProof, new Uint8Array([0]), requestSig));
529
+ }
530
+ async function signPayload(payload, input) {
531
+ if (input.signer) return input.signer.sign(payload);
532
+ if (!input.deviceSecret) {
533
+ throw new Error("signRequestProof needs either `deviceSecret` or `signer`");
534
+ }
535
+ const key = await importSigningKey(input.deviceSecret);
536
+ return new Uint8Array(await crypto.subtle.sign({ name: "Ed25519" }, key, payload));
537
+ }
538
+ function hexToBytes(value, label2) {
539
+ const clean = value.trim();
540
+ if (!/^[0-9a-fA-F]*$/.test(clean) || clean.length % 2 !== 0) {
541
+ throw new Error(`${label2} is not an even-length hex string`);
542
+ }
543
+ return fromHex(clean, label2, clean.length / 2);
544
+ }
545
+
546
+ // src/request-proof/authorizer.ts
547
+ function createProofAuthorizer(credential) {
548
+ if (!credential.credential) {
549
+ throw new Error("a proof authorizer needs the `credential` from signDeviceCert");
550
+ }
551
+ if (!credential.deviceSecret && !credential.signer) {
552
+ throw new Error("a proof authorizer needs either `deviceSecret` or `signer`");
553
+ }
554
+ return async ({ method, path, body }) => {
555
+ const proof = await signRequestProof({
556
+ method,
557
+ path,
558
+ body,
559
+ credential: credential.credential,
560
+ deviceSecret: credential.deviceSecret,
561
+ signer: credential.signer,
562
+ validForSeconds: credential.validForSeconds
563
+ });
564
+ return { [REQUEST_PROOF_HEADER]: proof };
565
+ };
566
+ }
567
+ function signedPath(url2) {
568
+ const parsed = new URL(url2, globalThis.location?.href ?? "http://localhost");
569
+ return parsed.pathname + parsed.search;
570
+ }
571
+
334
572
  // src/http-client/web-client.ts
335
573
  var TERMINAL_AUTH_ERRORS = /* @__PURE__ */ new Set(["token_reuse", "token_revoked"]);
336
574
  function extractErrorMessage(bodyText) {
@@ -386,13 +624,31 @@ function headersToRecord(headers) {
386
624
  });
387
625
  return record;
388
626
  }
389
- function pathForProof(url2) {
627
+ function withoutBareQuery(url2) {
628
+ let parsed;
390
629
  try {
391
- return new URL(url2).pathname;
630
+ parsed = new URL(url2, globalThis.location?.href);
392
631
  } catch {
393
- const query = url2.indexOf("?");
394
- return query === -1 ? url2 : url2.slice(0, query);
632
+ return url2;
395
633
  }
634
+ if (parsed.search === "") parsed.search = "";
635
+ return parsed.href;
636
+ }
637
+ function carriesProof(headers) {
638
+ return Object.keys(headers).some(
639
+ (name) => name.toLowerCase() === REQUEST_PROOF_HEADER.toLowerCase()
640
+ );
641
+ }
642
+ function refuseProofRedirect(response, url2) {
643
+ const redirected = response.type === "opaqueredirect" || response.status >= 300 && response.status < 400;
644
+ if (!redirected) return;
645
+ throw new HTTPError(
646
+ 0,
647
+ "Redirect Not Followed",
648
+ url2,
649
+ response.headers,
650
+ `the node answered with a redirect; a request carrying ${REQUEST_PROOF_HEADER} is not followed to another location`
651
+ );
396
652
  }
397
653
  var WebHttpClient = class {
398
654
  constructor(transport2) {
@@ -457,7 +713,7 @@ var WebHttpClient = class {
457
713
  }
458
714
  async makeRequest(path, init, retryCount = 0, requestStartTime) {
459
715
  const MAX_RETRY_ATTEMPTS = 1;
460
- const url2 = this.buildUrl(path);
716
+ const url2 = withoutBareQuery(this.buildUrl(path));
461
717
  const startTime = requestStartTime ?? Date.now();
462
718
  const signal = this.createAbortSignal(init);
463
719
  const headers = await this.buildHeaders(init?.headers);
@@ -482,7 +738,7 @@ var WebHttpClient = class {
482
738
  headersObj,
483
739
  await this.transport.authorizeRequest({
484
740
  method,
485
- path: new URL(url2).pathname,
741
+ path: signedPath(url2),
486
742
  // Narrowed by the guard above; `BodyInit` is wider than what can be
487
743
  // signed.
488
744
  body
@@ -542,15 +798,20 @@ var WebHttpClient = class {
542
798
  if (this.transport.getProof) {
543
799
  const proof = await this.transport.getProof({
544
800
  method: requestInit.method ?? "GET",
545
- path: pathForProof(url2),
801
+ path: signedPath(url2),
546
802
  body: requestInit.body
547
803
  });
548
804
  if (proof) {
549
805
  headersObj["X-Calimero-Proof"] = proof;
550
806
  }
551
807
  }
808
+ const proofAttached = carriesProof(headersObj);
809
+ if (proofAttached && requestInit.redirect !== "error") {
810
+ requestInit.redirect = "manual";
811
+ }
552
812
  try {
553
813
  const response = await this.transport.fetch(url2, requestInit);
814
+ if (proofAttached) refuseProofRedirect(response, url2);
554
815
  if (!response.ok) {
555
816
  const bodyText = await this.getBodyText(response);
556
817
  const httpError = new HTTPError(
@@ -2764,165 +3025,6 @@ function buildAuthLoginUrl(nodeUrl, opts) {
2764
3025
  return `${base}/auth/login?${params.toString()}`;
2765
3026
  }
2766
3027
 
2767
- // src/crypto/internal.ts
2768
- var PKCS8_ED25519_PREFIX = new Uint8Array([
2769
- 48,
2770
- 46,
2771
- 2,
2772
- 1,
2773
- 0,
2774
- 48,
2775
- 5,
2776
- 6,
2777
- 3,
2778
- 43,
2779
- 101,
2780
- 112,
2781
- 4,
2782
- 34,
2783
- 4,
2784
- 32
2785
- ]);
2786
- function u32le(value) {
2787
- const out = new Uint8Array(4);
2788
- new DataView(out.buffer).setUint32(0, value, true);
2789
- return out;
2790
- }
2791
- function hex(bytes) {
2792
- return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
2793
- }
2794
- function fromHex(value, label2, expectedBytes) {
2795
- const clean = value.trim();
2796
- if (!/^[0-9a-fA-F]*$/.test(clean) || clean.length !== expectedBytes * 2) {
2797
- throw new Error(
2798
- `${label2} must be ${expectedBytes * 2} hex characters, got ${clean.length}`
2799
- );
2800
- }
2801
- const bytes = new Uint8Array(expectedBytes);
2802
- for (let i = 0; i < expectedBytes; i += 1) {
2803
- bytes[i] = Number.parseInt(clean.slice(i * 2, i * 2 + 2), 16);
2804
- }
2805
- return bytes;
2806
- }
2807
- function fromHexUnsized(value, label2) {
2808
- const clean = value.trim();
2809
- if (!/^[0-9a-fA-F]*$/.test(clean) || clean.length % 2 !== 0) {
2810
- throw new Error(`${label2} must be an even number of hex characters`);
2811
- }
2812
- const bytes = new Uint8Array(clean.length / 2);
2813
- for (let i = 0; i < bytes.length; i += 1) {
2814
- bytes[i] = Number.parseInt(clean.slice(i * 2, i * 2 + 2), 16);
2815
- }
2816
- return bytes;
2817
- }
2818
- function u64le(value) {
2819
- const out = new Uint8Array(8);
2820
- new DataView(out.buffer).setBigUint64(0, BigInt(value), true);
2821
- return out;
2822
- }
2823
- function concat(...parts) {
2824
- const out = new Uint8Array(parts.reduce((total, p) => total + p.length, 0));
2825
- let offset = 0;
2826
- for (const part of parts) {
2827
- out.set(part, offset);
2828
- offset += part.length;
2829
- }
2830
- return out;
2831
- }
2832
- async function domainHash(domain, parts) {
2833
- const chunks = [u64le(domain.length), domain];
2834
- for (const part of parts) {
2835
- chunks.push(u64le(part.length), part);
2836
- }
2837
- return new Uint8Array(
2838
- await crypto.subtle.digest("SHA-256", concat(...chunks))
2839
- );
2840
- }
2841
- async function importSigningKey(secretHex) {
2842
- const seed = fromHex(secretHex, "deviceSecret", 32);
2843
- const pkcs8 = concat(PKCS8_ED25519_PREFIX, seed);
2844
- try {
2845
- return await crypto.subtle.importKey(
2846
- "pkcs8",
2847
- pkcs8,
2848
- { name: "Ed25519" },
2849
- false,
2850
- ["sign"]
2851
- );
2852
- } catch (cause) {
2853
- const detail = cause instanceof Error ? `: ${cause.message}` : "";
2854
- throw new Error(
2855
- "this runtime has no WebCrypto Ed25519, so a warrant cannot be signed here. Node 18.4+, Safari 17+, Firefox 129+ and Chrome 137+ have it; otherwise mint the warrant with `merod account warrant`" + detail
2856
- );
2857
- }
2858
- }
2859
- async function verifySignature(publicKey, signature, payload) {
2860
- const key = await crypto.subtle.importKey(
2861
- "raw",
2862
- fromHex(publicKey, "publicKey", 32),
2863
- { name: "Ed25519" },
2864
- false,
2865
- ["verify"]
2866
- );
2867
- return crypto.subtle.verify({ name: "Ed25519" }, key, signature, payload);
2868
- }
2869
- async function derivePublicKey(secretHex) {
2870
- const seed = fromHex(secretHex, "deviceSecret", 32);
2871
- const pkcs8 = concat(PKCS8_ED25519_PREFIX, seed);
2872
- const key = await crypto.subtle.importKey(
2873
- "pkcs8",
2874
- pkcs8,
2875
- { name: "Ed25519" },
2876
- true,
2877
- ["sign"]
2878
- );
2879
- const jwk = await crypto.subtle.exportKey("jwk", key);
2880
- const raw = jwk.x;
2881
- return Uint8Array.from(
2882
- atob(raw.replace(/-/g, "+").replace(/_/g, "/")),
2883
- (c) => c.charCodeAt(0)
2884
- );
2885
- }
2886
-
2887
- // src/request/request-sig.ts
2888
- var SIGN_DOMAIN = new TextEncoder().encode("calimero.auth.request.v1");
2889
- var BODY_DOMAIN = new TextEncoder().encode("calimero.auth.request.body.v1");
2890
- function borshString(value) {
2891
- const bytes = new TextEncoder().encode(value);
2892
- return concat(u32le(bytes.length), bytes);
2893
- }
2894
- async function requestBodyHash(body = new Uint8Array(0)) {
2895
- const bytes = typeof body === "string" ? new TextEncoder().encode(body) : body;
2896
- return domainHash(BODY_DOMAIN, [bytes]);
2897
- }
2898
- async function signRequest(input) {
2899
- const key = await importSigningKey(input.signerSecret);
2900
- const bodyHash = await requestBodyHash(input.body);
2901
- const issuedAt = u64le(input.issuedAt);
2902
- const expiresAt = u64le(input.expiresAt);
2903
- const encoder = new TextEncoder();
2904
- const preimage = await domainHash(SIGN_DOMAIN, [
2905
- encoder.encode(input.method),
2906
- encoder.encode(input.path),
2907
- bodyHash,
2908
- issuedAt,
2909
- expiresAt
2910
- ]);
2911
- const signature = new Uint8Array(
2912
- await crypto.subtle.sign({ name: "Ed25519" }, key, preimage)
2913
- );
2914
- return hex(
2915
- concat(
2916
- borshString(input.method),
2917
- borshString(input.path),
2918
- bodyHash,
2919
- issuedAt,
2920
- expiresAt,
2921
- signature
2922
- )
2923
- );
2924
- }
2925
-
2926
3028
  // src/request/caller-proof.ts
2927
3029
  function callerProof(input) {
2928
3030
  const credential = fromHexUnsized(input.credential, "credential");
@@ -3151,13 +3253,17 @@ var SseClient = class {
3151
3253
  this.abortController = new AbortController();
3152
3254
  try {
3153
3255
  const url2 = `${this.baseUrl}/sse`;
3256
+ const headers = {
3257
+ ...await this.authorize({ method: "GET", path: signedPath(url2) }),
3258
+ "Accept": "text/event-stream"
3259
+ };
3260
+ const proofAttached = carriesProof(headers);
3154
3261
  const response = await this.fetchImpl(url2, {
3155
- headers: {
3156
- ...await this.authorize({ method: "GET", path: new URL(url2).pathname }),
3157
- "Accept": "text/event-stream"
3158
- },
3159
- signal: this.abortController.signal
3262
+ headers,
3263
+ signal: this.abortController.signal,
3264
+ ...proofAttached && { redirect: "manual" }
3160
3265
  });
3266
+ if (proofAttached) refuseProofRedirect(response, url2);
3161
3267
  if (!response.ok) {
3162
3268
  const terminal = isTerminalAuthFailure(response);
3163
3269
  const bodyText = await response.text().catch(() => void 0);
@@ -3355,15 +3461,19 @@ var SseClient = class {
3355
3461
  if (ids.groupIds && ids.groupIds.length > 0) params.groupIds = ids.groupIds;
3356
3462
  const url2 = `${this.baseUrl}/sse/subscription`;
3357
3463
  const body = JSON.stringify({ id: this.sessionId, method, params });
3464
+ const headers = {
3465
+ ...await this.authorize({ method: "POST", path: signedPath(url2), body }),
3466
+ "Content-Type": "application/json"
3467
+ };
3468
+ const proofAttached = carriesProof(headers);
3358
3469
  const response = await this.fetchImpl(url2, {
3359
3470
  method: "POST",
3360
- headers: {
3361
- ...await this.authorize({ method: "POST", path: new URL(url2).pathname, body }),
3362
- "Content-Type": "application/json"
3363
- },
3471
+ headers,
3364
3472
  body,
3365
- signal: timeout.signal
3473
+ signal: timeout.signal,
3474
+ ...proofAttached && { redirect: "manual" }
3366
3475
  });
3476
+ if (proofAttached) refuseProofRedirect(response, url2);
3367
3477
  if (!response.ok) {
3368
3478
  const terminal = isTerminalAuthFailure(response);
3369
3479
  const bodyText = await response.text().catch(() => void 0);
@@ -6483,7 +6593,7 @@ var MAX_HANDSHAKE_REPLY = 1024;
6483
6593
  var RENEW_BEFORE_MS = 6e4;
6484
6594
  var TRANSPORT_BINDING_DOMAIN = "calimero.tee-attest.transport-key.v1";
6485
6595
  var NULL_BODY_STATUSES = [101, 204, 205, 304];
6486
- var utf8 = new TextEncoder();
6596
+ var utf82 = new TextEncoder();
6487
6597
  var StaleTransportKeyError = class extends Error {
6488
6598
  constructor() {
6489
6599
  super("The node no longer holds the attested transport key; attest again for the current one");
@@ -6502,7 +6612,7 @@ var SealedTransportError = class extends Error {
6502
6612
  async function transportKeyBinding(inner, transportKey) {
6503
6613
  const digest = await crypto.subtle.digest(
6504
6614
  "SHA-256",
6505
- concat(utf8.encode(TRANSPORT_BINDING_DOMAIN), inner, transportKey)
6615
+ concat(utf82.encode(TRANSPORT_BINDING_DOMAIN), inner, transportKey)
6506
6616
  );
6507
6617
  return new Uint8Array(digest);
6508
6618
  }
@@ -6706,7 +6816,7 @@ function nonce(requestId, index) {
6706
6816
  return bytes;
6707
6817
  }
6708
6818
  function joinInner(head, body) {
6709
- const encoded = utf8.encode(JSON.stringify(head));
6819
+ const encoded = utf82.encode(JSON.stringify(head));
6710
6820
  const len = new Uint8Array(4);
6711
6821
  new DataView(len.buffer).setUint32(0, encoded.length, false);
6712
6822
  return concat(len, encoded, body);
@@ -7196,7 +7306,7 @@ var FULCIO_ISSUER_V1 = "1.3.6.1.4.1.57264.1.1";
7196
7306
  var SAN_URI = 134;
7197
7307
  var UTF8_STRING = 12;
7198
7308
  var SEQUENCE2 = 48;
7199
- var utf82 = new TextEncoder();
7309
+ var utf83 = new TextEncoder();
7200
7310
  var fromUtf8 = (bytes) => new TextDecoder().decode(bytes);
7201
7311
  async function verifyCosignBundle(body, bundle, signer, signature) {
7202
7312
  const { base64Signature, cert, rekorBundle } = cosignBundle(bundle);
@@ -7209,7 +7319,7 @@ async function verifyCosignBundle(body, bundle, signer, signature) {
7209
7319
  const leaf = parseCertificate(leafDer);
7210
7320
  const log = REKOR_LOGS.find((candidate) => candidate.logId === payload.logID.toLowerCase());
7211
7321
  if (!log) throw new Error(`The bundle names Rekor log ${payload.logID}, which the Sigstore trust root does not know`);
7212
- const canonical = utf82.encode(
7322
+ const canonical = utf83.encode(
7213
7323
  `{"body":${JSON.stringify(payload.body)},"integratedTime":${payload.integratedTime},"logID":${JSON.stringify(payload.logID)},"logIndex":${payload.logIndex}}`
7214
7324
  );
7215
7325
  const setVerifies = await verifyEcdsa(
@@ -8947,92 +9057,6 @@ var RelayObserver = class {
8947
9057
  }
8948
9058
  };
8949
9059
 
8950
- // src/request-proof/request-proof.ts
8951
- var utf83 = (text) => new TextEncoder().encode(text);
8952
- var REQUEST_SIGN_DOMAIN = utf83("calimero.auth.request.v1");
8953
- var REQUEST_BODY_DOMAIN = utf83("calimero.auth.request.body.v1");
8954
- var REQUEST_PROOF_HEADER = "X-Calimero-Proof";
8955
- var DEFAULT_VALID_FOR = 300;
8956
- async function signRequestProof(input) {
8957
- const { method, path, credential } = input;
8958
- if (!method) throw new Error("signRequestProof needs the method the request will use");
8959
- if (!path.startsWith("/")) {
8960
- throw new Error(`signRequestProof needs a path, got ${JSON.stringify(path)}`);
8961
- }
8962
- if (path.includes("?")) {
8963
- throw new Error(
8964
- `the path must carry no query string, got ${JSON.stringify(path)} \u2014 a query is excluded from the signature on purpose, because a proxy may rewrite one. Anything that must be bound belongs in the body.`
8965
- );
8966
- }
8967
- const bodyBytes2 = input.body === void 0 ? new Uint8Array() : typeof input.body === "string" ? utf83(input.body) : input.body;
8968
- const bodyHash = await domainHash(REQUEST_BODY_DOMAIN, [bodyBytes2]);
8969
- const issuedAt = Math.floor(input.now ?? Date.now() / 1e3);
8970
- const expiresAt = issuedAt + (input.validForSeconds ?? DEFAULT_VALID_FOR);
8971
- const payload = await domainHash(REQUEST_SIGN_DOMAIN, [
8972
- utf83(method),
8973
- utf83(path),
8974
- bodyHash,
8975
- u64le(issuedAt),
8976
- u64le(expiresAt)
8977
- ]);
8978
- const signature = await signPayload(payload, input);
8979
- if (signature.length !== 64) {
8980
- throw new Error(`the signer returned ${signature.length} bytes, not a 64-byte signature`);
8981
- }
8982
- const requestSig = concat(
8983
- u32le(utf83(method).length),
8984
- utf83(method),
8985
- u32le(utf83(path).length),
8986
- utf83(path),
8987
- bodyHash,
8988
- u64le(issuedAt),
8989
- u64le(expiresAt),
8990
- signature
8991
- );
8992
- const accountProof = hexToBytes(credential, "credential");
8993
- return hex(concat(accountProof, new Uint8Array([0]), requestSig));
8994
- }
8995
- async function signPayload(payload, input) {
8996
- if (input.signer) return input.signer.sign(payload);
8997
- if (!input.deviceSecret) {
8998
- throw new Error("signRequestProof needs either `deviceSecret` or `signer`");
8999
- }
9000
- const key = await importSigningKey(input.deviceSecret);
9001
- return new Uint8Array(await crypto.subtle.sign({ name: "Ed25519" }, key, payload));
9002
- }
9003
- function hexToBytes(value, label2) {
9004
- const clean = value.trim();
9005
- if (!/^[0-9a-fA-F]*$/.test(clean) || clean.length % 2 !== 0) {
9006
- throw new Error(`${label2} is not an even-length hex string`);
9007
- }
9008
- return fromHex(clean, label2, clean.length / 2);
9009
- }
9010
-
9011
- // src/request-proof/authorizer.ts
9012
- function createProofAuthorizer(credential) {
9013
- if (!credential.credential) {
9014
- throw new Error("a proof authorizer needs the `credential` from signDeviceCert");
9015
- }
9016
- if (!credential.deviceSecret && !credential.signer) {
9017
- throw new Error("a proof authorizer needs either `deviceSecret` or `signer`");
9018
- }
9019
- return async ({ method, path, body }) => {
9020
- const proof = await signRequestProof({
9021
- method,
9022
- path,
9023
- body,
9024
- credential: credential.credential,
9025
- deviceSecret: credential.deviceSecret,
9026
- signer: credential.signer,
9027
- validForSeconds: credential.validForSeconds
9028
- });
9029
- return { [REQUEST_PROOF_HEADER]: proof };
9030
- };
9031
- }
9032
- function signedPath(url2) {
9033
- return new URL(url2).pathname;
9034
- }
9035
-
9036
9060
  // src/presence/statement.ts
9037
9061
  var PRESENCE_DOMAIN = new TextEncoder().encode("calimero/presence/1");
9038
9062
  async function stateHash(state) {