@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.mjs CHANGED
@@ -31,6 +31,244 @@ function createTimeoutSignal(timeoutMs) {
31
31
  return controller.signal;
32
32
  }
33
33
 
34
+ // src/crypto/internal.ts
35
+ var PKCS8_ED25519_PREFIX = new Uint8Array([
36
+ 48,
37
+ 46,
38
+ 2,
39
+ 1,
40
+ 0,
41
+ 48,
42
+ 5,
43
+ 6,
44
+ 3,
45
+ 43,
46
+ 101,
47
+ 112,
48
+ 4,
49
+ 34,
50
+ 4,
51
+ 32
52
+ ]);
53
+ function u32le(value) {
54
+ const out = new Uint8Array(4);
55
+ new DataView(out.buffer).setUint32(0, value, true);
56
+ return out;
57
+ }
58
+ function hex(bytes) {
59
+ return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
60
+ }
61
+ function fromHex(value, label2, expectedBytes) {
62
+ const clean = value.trim();
63
+ if (!/^[0-9a-fA-F]*$/.test(clean) || clean.length !== expectedBytes * 2) {
64
+ throw new Error(
65
+ `${label2} must be ${expectedBytes * 2} hex characters, got ${clean.length}`
66
+ );
67
+ }
68
+ const bytes = new Uint8Array(expectedBytes);
69
+ for (let i = 0; i < expectedBytes; i += 1) {
70
+ bytes[i] = Number.parseInt(clean.slice(i * 2, i * 2 + 2), 16);
71
+ }
72
+ return bytes;
73
+ }
74
+ function fromHexUnsized(value, label2) {
75
+ const clean = value.trim();
76
+ if (!/^[0-9a-fA-F]*$/.test(clean) || clean.length % 2 !== 0) {
77
+ throw new Error(`${label2} must be an even number of hex characters`);
78
+ }
79
+ const bytes = new Uint8Array(clean.length / 2);
80
+ for (let i = 0; i < bytes.length; i += 1) {
81
+ bytes[i] = Number.parseInt(clean.slice(i * 2, i * 2 + 2), 16);
82
+ }
83
+ return bytes;
84
+ }
85
+ function u64le(value) {
86
+ const out = new Uint8Array(8);
87
+ new DataView(out.buffer).setBigUint64(0, BigInt(value), true);
88
+ return out;
89
+ }
90
+ function concat(...parts) {
91
+ const out = new Uint8Array(parts.reduce((total, p) => total + p.length, 0));
92
+ let offset = 0;
93
+ for (const part of parts) {
94
+ out.set(part, offset);
95
+ offset += part.length;
96
+ }
97
+ return out;
98
+ }
99
+ async function domainHash(domain, parts) {
100
+ const chunks = [u64le(domain.length), domain];
101
+ for (const part of parts) {
102
+ chunks.push(u64le(part.length), part);
103
+ }
104
+ return new Uint8Array(
105
+ await crypto.subtle.digest("SHA-256", concat(...chunks))
106
+ );
107
+ }
108
+ async function importSigningKey(secretHex) {
109
+ const seed = fromHex(secretHex, "deviceSecret", 32);
110
+ const pkcs8 = concat(PKCS8_ED25519_PREFIX, seed);
111
+ try {
112
+ return await crypto.subtle.importKey(
113
+ "pkcs8",
114
+ pkcs8,
115
+ { name: "Ed25519" },
116
+ false,
117
+ ["sign"]
118
+ );
119
+ } catch (cause) {
120
+ const detail = cause instanceof Error ? `: ${cause.message}` : "";
121
+ throw new Error(
122
+ "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
123
+ );
124
+ }
125
+ }
126
+ async function verifySignature(publicKey, signature, payload) {
127
+ const key = await crypto.subtle.importKey(
128
+ "raw",
129
+ fromHex(publicKey, "publicKey", 32),
130
+ { name: "Ed25519" },
131
+ false,
132
+ ["verify"]
133
+ );
134
+ return crypto.subtle.verify({ name: "Ed25519" }, key, signature, payload);
135
+ }
136
+ async function derivePublicKey(secretHex) {
137
+ const seed = fromHex(secretHex, "deviceSecret", 32);
138
+ const pkcs8 = concat(PKCS8_ED25519_PREFIX, seed);
139
+ const key = await crypto.subtle.importKey(
140
+ "pkcs8",
141
+ pkcs8,
142
+ { name: "Ed25519" },
143
+ true,
144
+ ["sign"]
145
+ );
146
+ const jwk = await crypto.subtle.exportKey("jwk", key);
147
+ const raw = jwk.x;
148
+ return Uint8Array.from(
149
+ atob(raw.replace(/-/g, "+").replace(/_/g, "/")),
150
+ (c) => c.charCodeAt(0)
151
+ );
152
+ }
153
+
154
+ // src/request/request-sig.ts
155
+ var SIGN_DOMAIN = new TextEncoder().encode("calimero.auth.request.v2");
156
+ var BODY_DOMAIN = new TextEncoder().encode("calimero.auth.request.body.v1");
157
+ function borshString(value) {
158
+ const bytes = new TextEncoder().encode(value);
159
+ return concat(u32le(bytes.length), bytes);
160
+ }
161
+ async function requestBodyHash(body = new Uint8Array(0)) {
162
+ const bytes = typeof body === "string" ? new TextEncoder().encode(body) : body;
163
+ return domainHash(BODY_DOMAIN, [bytes]);
164
+ }
165
+ async function signRequest(input) {
166
+ const key = await importSigningKey(input.signerSecret);
167
+ const bodyHash = await requestBodyHash(input.body);
168
+ const issuedAt = u64le(input.issuedAt);
169
+ const expiresAt = u64le(input.expiresAt);
170
+ const encoder = new TextEncoder();
171
+ const preimage = await domainHash(SIGN_DOMAIN, [
172
+ encoder.encode(input.method),
173
+ encoder.encode(input.path),
174
+ bodyHash,
175
+ issuedAt,
176
+ expiresAt
177
+ ]);
178
+ const signature = new Uint8Array(
179
+ await crypto.subtle.sign({ name: "Ed25519" }, key, preimage)
180
+ );
181
+ return hex(
182
+ concat(
183
+ borshString(input.method),
184
+ borshString(input.path),
185
+ bodyHash,
186
+ issuedAt,
187
+ expiresAt,
188
+ signature
189
+ )
190
+ );
191
+ }
192
+
193
+ // src/request-proof/request-proof.ts
194
+ var utf8 = (text) => new TextEncoder().encode(text);
195
+ var REQUEST_PROOF_HEADER = "X-Calimero-Proof";
196
+ var DEFAULT_VALID_FOR = 300;
197
+ async function signRequestProof(input) {
198
+ const { method, path, credential } = input;
199
+ if (!method) throw new Error("signRequestProof needs the method the request will use");
200
+ if (!path.startsWith("/")) {
201
+ throw new Error(`signRequestProof needs a path, got ${JSON.stringify(path)}`);
202
+ }
203
+ const bodyHash = await requestBodyHash(input.body);
204
+ const issuedAt = Math.floor(input.now ?? Date.now() / 1e3);
205
+ const expiresAt = issuedAt + (input.validForSeconds ?? DEFAULT_VALID_FOR);
206
+ const payload = await domainHash(SIGN_DOMAIN, [
207
+ utf8(method),
208
+ utf8(path),
209
+ bodyHash,
210
+ u64le(issuedAt),
211
+ u64le(expiresAt)
212
+ ]);
213
+ const signature = await signPayload(payload, input);
214
+ if (signature.length !== 64) {
215
+ throw new Error(`the signer returned ${signature.length} bytes, not a 64-byte signature`);
216
+ }
217
+ const requestSig = concat(
218
+ u32le(utf8(method).length),
219
+ utf8(method),
220
+ u32le(utf8(path).length),
221
+ utf8(path),
222
+ bodyHash,
223
+ u64le(issuedAt),
224
+ u64le(expiresAt),
225
+ signature
226
+ );
227
+ const accountProof = hexToBytes(credential, "credential");
228
+ return hex(concat(accountProof, new Uint8Array([0]), requestSig));
229
+ }
230
+ async function signPayload(payload, input) {
231
+ if (input.signer) return input.signer.sign(payload);
232
+ if (!input.deviceSecret) {
233
+ throw new Error("signRequestProof needs either `deviceSecret` or `signer`");
234
+ }
235
+ const key = await importSigningKey(input.deviceSecret);
236
+ return new Uint8Array(await crypto.subtle.sign({ name: "Ed25519" }, key, payload));
237
+ }
238
+ function hexToBytes(value, label2) {
239
+ const clean = value.trim();
240
+ if (!/^[0-9a-fA-F]*$/.test(clean) || clean.length % 2 !== 0) {
241
+ throw new Error(`${label2} is not an even-length hex string`);
242
+ }
243
+ return fromHex(clean, label2, clean.length / 2);
244
+ }
245
+
246
+ // src/request-proof/authorizer.ts
247
+ function createProofAuthorizer(credential) {
248
+ if (!credential.credential) {
249
+ throw new Error("a proof authorizer needs the `credential` from signDeviceCert");
250
+ }
251
+ if (!credential.deviceSecret && !credential.signer) {
252
+ throw new Error("a proof authorizer needs either `deviceSecret` or `signer`");
253
+ }
254
+ return async ({ method, path, body }) => {
255
+ const proof = await signRequestProof({
256
+ method,
257
+ path,
258
+ body,
259
+ credential: credential.credential,
260
+ deviceSecret: credential.deviceSecret,
261
+ signer: credential.signer,
262
+ validForSeconds: credential.validForSeconds
263
+ });
264
+ return { [REQUEST_PROOF_HEADER]: proof };
265
+ };
266
+ }
267
+ function signedPath(url2) {
268
+ const parsed = new URL(url2, globalThis.location?.href ?? "http://localhost");
269
+ return parsed.pathname + parsed.search;
270
+ }
271
+
34
272
  // src/http-client/web-client.ts
35
273
  var TERMINAL_AUTH_ERRORS = /* @__PURE__ */ new Set(["token_reuse", "token_revoked"]);
36
274
  function extractErrorMessage(bodyText) {
@@ -86,13 +324,31 @@ function headersToRecord(headers) {
86
324
  });
87
325
  return record;
88
326
  }
89
- function pathForProof(url2) {
327
+ function withoutBareQuery(url2) {
328
+ let parsed;
90
329
  try {
91
- return new URL(url2).pathname;
330
+ parsed = new URL(url2, globalThis.location?.href);
92
331
  } catch {
93
- const query = url2.indexOf("?");
94
- return query === -1 ? url2 : url2.slice(0, query);
332
+ return url2;
95
333
  }
334
+ if (parsed.search === "") parsed.search = "";
335
+ return parsed.href;
336
+ }
337
+ function carriesProof(headers) {
338
+ return Object.keys(headers).some(
339
+ (name) => name.toLowerCase() === REQUEST_PROOF_HEADER.toLowerCase()
340
+ );
341
+ }
342
+ function refuseProofRedirect(response, url2) {
343
+ const redirected = response.type === "opaqueredirect" || response.status >= 300 && response.status < 400;
344
+ if (!redirected) return;
345
+ throw new HTTPError(
346
+ 0,
347
+ "Redirect Not Followed",
348
+ url2,
349
+ response.headers,
350
+ `the node answered with a redirect; a request carrying ${REQUEST_PROOF_HEADER} is not followed to another location`
351
+ );
96
352
  }
97
353
  var WebHttpClient = class {
98
354
  constructor(transport2) {
@@ -157,7 +413,7 @@ var WebHttpClient = class {
157
413
  }
158
414
  async makeRequest(path, init, retryCount = 0, requestStartTime) {
159
415
  const MAX_RETRY_ATTEMPTS = 1;
160
- const url2 = this.buildUrl(path);
416
+ const url2 = withoutBareQuery(this.buildUrl(path));
161
417
  const startTime = requestStartTime ?? Date.now();
162
418
  const signal = this.createAbortSignal(init);
163
419
  const headers = await this.buildHeaders(init?.headers);
@@ -182,7 +438,7 @@ var WebHttpClient = class {
182
438
  headersObj,
183
439
  await this.transport.authorizeRequest({
184
440
  method,
185
- path: new URL(url2).pathname,
441
+ path: signedPath(url2),
186
442
  // Narrowed by the guard above; `BodyInit` is wider than what can be
187
443
  // signed.
188
444
  body
@@ -242,15 +498,20 @@ var WebHttpClient = class {
242
498
  if (this.transport.getProof) {
243
499
  const proof = await this.transport.getProof({
244
500
  method: requestInit.method ?? "GET",
245
- path: pathForProof(url2),
501
+ path: signedPath(url2),
246
502
  body: requestInit.body
247
503
  });
248
504
  if (proof) {
249
505
  headersObj["X-Calimero-Proof"] = proof;
250
506
  }
251
507
  }
508
+ const proofAttached = carriesProof(headersObj);
509
+ if (proofAttached && requestInit.redirect !== "error") {
510
+ requestInit.redirect = "manual";
511
+ }
252
512
  try {
253
513
  const response = await this.transport.fetch(url2, requestInit);
514
+ if (proofAttached) refuseProofRedirect(response, url2);
254
515
  if (!response.ok) {
255
516
  const bodyText = await this.getBodyText(response);
256
517
  const httpError = new HTTPError(
@@ -2464,165 +2725,6 @@ function buildAuthLoginUrl(nodeUrl, opts) {
2464
2725
  return `${base}/auth/login?${params.toString()}`;
2465
2726
  }
2466
2727
 
2467
- // src/crypto/internal.ts
2468
- var PKCS8_ED25519_PREFIX = new Uint8Array([
2469
- 48,
2470
- 46,
2471
- 2,
2472
- 1,
2473
- 0,
2474
- 48,
2475
- 5,
2476
- 6,
2477
- 3,
2478
- 43,
2479
- 101,
2480
- 112,
2481
- 4,
2482
- 34,
2483
- 4,
2484
- 32
2485
- ]);
2486
- function u32le(value) {
2487
- const out = new Uint8Array(4);
2488
- new DataView(out.buffer).setUint32(0, value, true);
2489
- return out;
2490
- }
2491
- function hex(bytes) {
2492
- return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
2493
- }
2494
- function fromHex(value, label2, expectedBytes) {
2495
- const clean = value.trim();
2496
- if (!/^[0-9a-fA-F]*$/.test(clean) || clean.length !== expectedBytes * 2) {
2497
- throw new Error(
2498
- `${label2} must be ${expectedBytes * 2} hex characters, got ${clean.length}`
2499
- );
2500
- }
2501
- const bytes = new Uint8Array(expectedBytes);
2502
- for (let i = 0; i < expectedBytes; i += 1) {
2503
- bytes[i] = Number.parseInt(clean.slice(i * 2, i * 2 + 2), 16);
2504
- }
2505
- return bytes;
2506
- }
2507
- function fromHexUnsized(value, label2) {
2508
- const clean = value.trim();
2509
- if (!/^[0-9a-fA-F]*$/.test(clean) || clean.length % 2 !== 0) {
2510
- throw new Error(`${label2} must be an even number of hex characters`);
2511
- }
2512
- const bytes = new Uint8Array(clean.length / 2);
2513
- for (let i = 0; i < bytes.length; i += 1) {
2514
- bytes[i] = Number.parseInt(clean.slice(i * 2, i * 2 + 2), 16);
2515
- }
2516
- return bytes;
2517
- }
2518
- function u64le(value) {
2519
- const out = new Uint8Array(8);
2520
- new DataView(out.buffer).setBigUint64(0, BigInt(value), true);
2521
- return out;
2522
- }
2523
- function concat(...parts) {
2524
- const out = new Uint8Array(parts.reduce((total, p) => total + p.length, 0));
2525
- let offset = 0;
2526
- for (const part of parts) {
2527
- out.set(part, offset);
2528
- offset += part.length;
2529
- }
2530
- return out;
2531
- }
2532
- async function domainHash(domain, parts) {
2533
- const chunks = [u64le(domain.length), domain];
2534
- for (const part of parts) {
2535
- chunks.push(u64le(part.length), part);
2536
- }
2537
- return new Uint8Array(
2538
- await crypto.subtle.digest("SHA-256", concat(...chunks))
2539
- );
2540
- }
2541
- async function importSigningKey(secretHex) {
2542
- const seed = fromHex(secretHex, "deviceSecret", 32);
2543
- const pkcs8 = concat(PKCS8_ED25519_PREFIX, seed);
2544
- try {
2545
- return await crypto.subtle.importKey(
2546
- "pkcs8",
2547
- pkcs8,
2548
- { name: "Ed25519" },
2549
- false,
2550
- ["sign"]
2551
- );
2552
- } catch (cause) {
2553
- const detail = cause instanceof Error ? `: ${cause.message}` : "";
2554
- throw new Error(
2555
- "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
2556
- );
2557
- }
2558
- }
2559
- async function verifySignature(publicKey, signature, payload) {
2560
- const key = await crypto.subtle.importKey(
2561
- "raw",
2562
- fromHex(publicKey, "publicKey", 32),
2563
- { name: "Ed25519" },
2564
- false,
2565
- ["verify"]
2566
- );
2567
- return crypto.subtle.verify({ name: "Ed25519" }, key, signature, payload);
2568
- }
2569
- async function derivePublicKey(secretHex) {
2570
- const seed = fromHex(secretHex, "deviceSecret", 32);
2571
- const pkcs8 = concat(PKCS8_ED25519_PREFIX, seed);
2572
- const key = await crypto.subtle.importKey(
2573
- "pkcs8",
2574
- pkcs8,
2575
- { name: "Ed25519" },
2576
- true,
2577
- ["sign"]
2578
- );
2579
- const jwk = await crypto.subtle.exportKey("jwk", key);
2580
- const raw = jwk.x;
2581
- return Uint8Array.from(
2582
- atob(raw.replace(/-/g, "+").replace(/_/g, "/")),
2583
- (c) => c.charCodeAt(0)
2584
- );
2585
- }
2586
-
2587
- // src/request/request-sig.ts
2588
- var SIGN_DOMAIN = new TextEncoder().encode("calimero.auth.request.v1");
2589
- var BODY_DOMAIN = new TextEncoder().encode("calimero.auth.request.body.v1");
2590
- function borshString(value) {
2591
- const bytes = new TextEncoder().encode(value);
2592
- return concat(u32le(bytes.length), bytes);
2593
- }
2594
- async function requestBodyHash(body = new Uint8Array(0)) {
2595
- const bytes = typeof body === "string" ? new TextEncoder().encode(body) : body;
2596
- return domainHash(BODY_DOMAIN, [bytes]);
2597
- }
2598
- async function signRequest(input) {
2599
- const key = await importSigningKey(input.signerSecret);
2600
- const bodyHash = await requestBodyHash(input.body);
2601
- const issuedAt = u64le(input.issuedAt);
2602
- const expiresAt = u64le(input.expiresAt);
2603
- const encoder = new TextEncoder();
2604
- const preimage = await domainHash(SIGN_DOMAIN, [
2605
- encoder.encode(input.method),
2606
- encoder.encode(input.path),
2607
- bodyHash,
2608
- issuedAt,
2609
- expiresAt
2610
- ]);
2611
- const signature = new Uint8Array(
2612
- await crypto.subtle.sign({ name: "Ed25519" }, key, preimage)
2613
- );
2614
- return hex(
2615
- concat(
2616
- borshString(input.method),
2617
- borshString(input.path),
2618
- bodyHash,
2619
- issuedAt,
2620
- expiresAt,
2621
- signature
2622
- )
2623
- );
2624
- }
2625
-
2626
2728
  // src/request/caller-proof.ts
2627
2729
  function callerProof(input) {
2628
2730
  const credential = fromHexUnsized(input.credential, "credential");
@@ -2851,13 +2953,17 @@ var SseClient = class {
2851
2953
  this.abortController = new AbortController();
2852
2954
  try {
2853
2955
  const url2 = `${this.baseUrl}/sse`;
2956
+ const headers = {
2957
+ ...await this.authorize({ method: "GET", path: signedPath(url2) }),
2958
+ "Accept": "text/event-stream"
2959
+ };
2960
+ const proofAttached = carriesProof(headers);
2854
2961
  const response = await this.fetchImpl(url2, {
2855
- headers: {
2856
- ...await this.authorize({ method: "GET", path: new URL(url2).pathname }),
2857
- "Accept": "text/event-stream"
2858
- },
2859
- signal: this.abortController.signal
2962
+ headers,
2963
+ signal: this.abortController.signal,
2964
+ ...proofAttached && { redirect: "manual" }
2860
2965
  });
2966
+ if (proofAttached) refuseProofRedirect(response, url2);
2861
2967
  if (!response.ok) {
2862
2968
  const terminal = isTerminalAuthFailure(response);
2863
2969
  const bodyText = await response.text().catch(() => void 0);
@@ -3055,15 +3161,19 @@ var SseClient = class {
3055
3161
  if (ids.groupIds && ids.groupIds.length > 0) params.groupIds = ids.groupIds;
3056
3162
  const url2 = `${this.baseUrl}/sse/subscription`;
3057
3163
  const body = JSON.stringify({ id: this.sessionId, method, params });
3164
+ const headers = {
3165
+ ...await this.authorize({ method: "POST", path: signedPath(url2), body }),
3166
+ "Content-Type": "application/json"
3167
+ };
3168
+ const proofAttached = carriesProof(headers);
3058
3169
  const response = await this.fetchImpl(url2, {
3059
3170
  method: "POST",
3060
- headers: {
3061
- ...await this.authorize({ method: "POST", path: new URL(url2).pathname, body }),
3062
- "Content-Type": "application/json"
3063
- },
3171
+ headers,
3064
3172
  body,
3065
- signal: timeout.signal
3173
+ signal: timeout.signal,
3174
+ ...proofAttached && { redirect: "manual" }
3066
3175
  });
3176
+ if (proofAttached) refuseProofRedirect(response, url2);
3067
3177
  if (!response.ok) {
3068
3178
  const terminal = isTerminalAuthFailure(response);
3069
3179
  const bodyText = await response.text().catch(() => void 0);
@@ -6183,7 +6293,7 @@ var MAX_HANDSHAKE_REPLY = 1024;
6183
6293
  var RENEW_BEFORE_MS = 6e4;
6184
6294
  var TRANSPORT_BINDING_DOMAIN = "calimero.tee-attest.transport-key.v1";
6185
6295
  var NULL_BODY_STATUSES = [101, 204, 205, 304];
6186
- var utf8 = new TextEncoder();
6296
+ var utf82 = new TextEncoder();
6187
6297
  var StaleTransportKeyError = class extends Error {
6188
6298
  constructor() {
6189
6299
  super("The node no longer holds the attested transport key; attest again for the current one");
@@ -6202,7 +6312,7 @@ var SealedTransportError = class extends Error {
6202
6312
  async function transportKeyBinding(inner, transportKey) {
6203
6313
  const digest = await crypto.subtle.digest(
6204
6314
  "SHA-256",
6205
- concat(utf8.encode(TRANSPORT_BINDING_DOMAIN), inner, transportKey)
6315
+ concat(utf82.encode(TRANSPORT_BINDING_DOMAIN), inner, transportKey)
6206
6316
  );
6207
6317
  return new Uint8Array(digest);
6208
6318
  }
@@ -6406,7 +6516,7 @@ function nonce(requestId, index) {
6406
6516
  return bytes;
6407
6517
  }
6408
6518
  function joinInner(head, body) {
6409
- const encoded = utf8.encode(JSON.stringify(head));
6519
+ const encoded = utf82.encode(JSON.stringify(head));
6410
6520
  const len = new Uint8Array(4);
6411
6521
  new DataView(len.buffer).setUint32(0, encoded.length, false);
6412
6522
  return concat(len, encoded, body);
@@ -6896,7 +7006,7 @@ var FULCIO_ISSUER_V1 = "1.3.6.1.4.1.57264.1.1";
6896
7006
  var SAN_URI = 134;
6897
7007
  var UTF8_STRING = 12;
6898
7008
  var SEQUENCE2 = 48;
6899
- var utf82 = new TextEncoder();
7009
+ var utf83 = new TextEncoder();
6900
7010
  var fromUtf8 = (bytes) => new TextDecoder().decode(bytes);
6901
7011
  async function verifyCosignBundle(body, bundle, signer, signature) {
6902
7012
  const { base64Signature, cert, rekorBundle } = cosignBundle(bundle);
@@ -6909,7 +7019,7 @@ async function verifyCosignBundle(body, bundle, signer, signature) {
6909
7019
  const leaf = parseCertificate(leafDer);
6910
7020
  const log = REKOR_LOGS.find((candidate) => candidate.logId === payload.logID.toLowerCase());
6911
7021
  if (!log) throw new Error(`The bundle names Rekor log ${payload.logID}, which the Sigstore trust root does not know`);
6912
- const canonical = utf82.encode(
7022
+ const canonical = utf83.encode(
6913
7023
  `{"body":${JSON.stringify(payload.body)},"integratedTime":${payload.integratedTime},"logID":${JSON.stringify(payload.logID)},"logIndex":${payload.logIndex}}`
6914
7024
  );
6915
7025
  const setVerifies = await verifyEcdsa(
@@ -8647,92 +8757,6 @@ var RelayObserver = class {
8647
8757
  }
8648
8758
  };
8649
8759
 
8650
- // src/request-proof/request-proof.ts
8651
- var utf83 = (text) => new TextEncoder().encode(text);
8652
- var REQUEST_SIGN_DOMAIN = utf83("calimero.auth.request.v1");
8653
- var REQUEST_BODY_DOMAIN = utf83("calimero.auth.request.body.v1");
8654
- var REQUEST_PROOF_HEADER = "X-Calimero-Proof";
8655
- var DEFAULT_VALID_FOR = 300;
8656
- async function signRequestProof(input) {
8657
- const { method, path, credential } = input;
8658
- if (!method) throw new Error("signRequestProof needs the method the request will use");
8659
- if (!path.startsWith("/")) {
8660
- throw new Error(`signRequestProof needs a path, got ${JSON.stringify(path)}`);
8661
- }
8662
- if (path.includes("?")) {
8663
- throw new Error(
8664
- `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.`
8665
- );
8666
- }
8667
- const bodyBytes2 = input.body === void 0 ? new Uint8Array() : typeof input.body === "string" ? utf83(input.body) : input.body;
8668
- const bodyHash = await domainHash(REQUEST_BODY_DOMAIN, [bodyBytes2]);
8669
- const issuedAt = Math.floor(input.now ?? Date.now() / 1e3);
8670
- const expiresAt = issuedAt + (input.validForSeconds ?? DEFAULT_VALID_FOR);
8671
- const payload = await domainHash(REQUEST_SIGN_DOMAIN, [
8672
- utf83(method),
8673
- utf83(path),
8674
- bodyHash,
8675
- u64le(issuedAt),
8676
- u64le(expiresAt)
8677
- ]);
8678
- const signature = await signPayload(payload, input);
8679
- if (signature.length !== 64) {
8680
- throw new Error(`the signer returned ${signature.length} bytes, not a 64-byte signature`);
8681
- }
8682
- const requestSig = concat(
8683
- u32le(utf83(method).length),
8684
- utf83(method),
8685
- u32le(utf83(path).length),
8686
- utf83(path),
8687
- bodyHash,
8688
- u64le(issuedAt),
8689
- u64le(expiresAt),
8690
- signature
8691
- );
8692
- const accountProof = hexToBytes(credential, "credential");
8693
- return hex(concat(accountProof, new Uint8Array([0]), requestSig));
8694
- }
8695
- async function signPayload(payload, input) {
8696
- if (input.signer) return input.signer.sign(payload);
8697
- if (!input.deviceSecret) {
8698
- throw new Error("signRequestProof needs either `deviceSecret` or `signer`");
8699
- }
8700
- const key = await importSigningKey(input.deviceSecret);
8701
- return new Uint8Array(await crypto.subtle.sign({ name: "Ed25519" }, key, payload));
8702
- }
8703
- function hexToBytes(value, label2) {
8704
- const clean = value.trim();
8705
- if (!/^[0-9a-fA-F]*$/.test(clean) || clean.length % 2 !== 0) {
8706
- throw new Error(`${label2} is not an even-length hex string`);
8707
- }
8708
- return fromHex(clean, label2, clean.length / 2);
8709
- }
8710
-
8711
- // src/request-proof/authorizer.ts
8712
- function createProofAuthorizer(credential) {
8713
- if (!credential.credential) {
8714
- throw new Error("a proof authorizer needs the `credential` from signDeviceCert");
8715
- }
8716
- if (!credential.deviceSecret && !credential.signer) {
8717
- throw new Error("a proof authorizer needs either `deviceSecret` or `signer`");
8718
- }
8719
- return async ({ method, path, body }) => {
8720
- const proof = await signRequestProof({
8721
- method,
8722
- path,
8723
- body,
8724
- credential: credential.credential,
8725
- deviceSecret: credential.deviceSecret,
8726
- signer: credential.signer,
8727
- validForSeconds: credential.validForSeconds
8728
- });
8729
- return { [REQUEST_PROOF_HEADER]: proof };
8730
- };
8731
- }
8732
- function signedPath(url2) {
8733
- return new URL(url2).pathname;
8734
- }
8735
-
8736
8760
  // src/presence/statement.ts
8737
8761
  var PRESENCE_DOMAIN = new TextEncoder().encode("calimero/presence/1");
8738
8762
  async function stateHash(state) {