@calimero-network/mero-js 19.24.0 → 19.25.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.
- package/dist/admin-api/admin-client.d.ts +29 -1
- package/dist/admin-api/admin-client.js +43 -0
- package/dist/admin-api/admin-types.d.ts +78 -0
- package/dist/index.browser.mjs +2 -2
- package/dist/index.browser.mjs.map +3 -3
- package/dist/index.cjs +86 -3
- package/dist/index.cjs.map +2 -2
- package/dist/index.mjs +86 -3
- package/dist/index.mjs.map +2 -2
- package/dist/sealed/sealed.d.ts +5 -1
- package/dist/sealed/sealed.js +50 -3
- package/package.json +1 -1
package/dist/sealed/sealed.d.ts
CHANGED
|
@@ -52,8 +52,12 @@ export declare class StaleTransportKeyError extends Error {
|
|
|
52
52
|
export declare class SealedTransportError extends Error {
|
|
53
53
|
status: number;
|
|
54
54
|
code: string | undefined;
|
|
55
|
+
/** How long the node asked to wait before retrying (`Retry-After`), if it did. */
|
|
56
|
+
retryAfterMs?: number;
|
|
55
57
|
name: string;
|
|
56
|
-
constructor(status: number, code: string | undefined, message: string
|
|
58
|
+
constructor(status: number, code: string | undefined, message: string,
|
|
59
|
+
/** How long the node asked to wait before retrying (`Retry-After`), if it did. */
|
|
60
|
+
retryAfterMs?: number);
|
|
57
61
|
}
|
|
58
62
|
/**
|
|
59
63
|
* What the attest endpoint puts in report data bytes `32..64` when asked to
|
package/dist/sealed/sealed.js
CHANGED
|
@@ -50,6 +50,14 @@ const HEADER_LEN = 1 + SESSION_ID_LEN + 8;
|
|
|
50
50
|
const FRAME_HEAD = 0;
|
|
51
51
|
const FRAME_DATA = 1;
|
|
52
52
|
const FRAME_END = 2;
|
|
53
|
+
/**
|
|
54
|
+
* The largest frame the node sends: a kind byte, at most 64 KiB of data, a tag.
|
|
55
|
+
* Anything larger is refused before it is buffered, so a proxy cannot make the
|
|
56
|
+
* client hold an unbounded frame it has yet to authenticate.
|
|
57
|
+
*/
|
|
58
|
+
const MAX_FRAME_LEN = 1 + 64 * 1024 + 16;
|
|
59
|
+
/** Far more than a handshake reply (69 bytes) ever is. */
|
|
60
|
+
const MAX_HANDSHAKE_REPLY = 1024;
|
|
53
61
|
/** Open a new session this long before the node would drop the current one. */
|
|
54
62
|
const RENEW_BEFORE_MS = 60000;
|
|
55
63
|
const TRANSPORT_BINDING_DOMAIN = 'calimero.tee-attest.transport-key.v1';
|
|
@@ -64,10 +72,13 @@ export class StaleTransportKeyError extends Error {
|
|
|
64
72
|
}
|
|
65
73
|
/** The node refused the envelope itself, before anything inside it ran. */
|
|
66
74
|
export class SealedTransportError extends Error {
|
|
67
|
-
constructor(status, code, message
|
|
75
|
+
constructor(status, code, message,
|
|
76
|
+
/** How long the node asked to wait before retrying (`Retry-After`), if it did. */
|
|
77
|
+
retryAfterMs) {
|
|
68
78
|
super(message);
|
|
69
79
|
this.status = status;
|
|
70
80
|
this.code = code;
|
|
81
|
+
this.retryAfterMs = retryAfterMs;
|
|
71
82
|
this.name = 'SealedTransportError';
|
|
72
83
|
}
|
|
73
84
|
}
|
|
@@ -205,6 +216,19 @@ async function checkedKey(key) {
|
|
|
205
216
|
return bytes;
|
|
206
217
|
}
|
|
207
218
|
async function handshake(baseFetch, nodeUrl, transportKey) {
|
|
219
|
+
try {
|
|
220
|
+
return await handshakeOnce(baseFetch, nodeUrl, transportKey);
|
|
221
|
+
}
|
|
222
|
+
catch (error) {
|
|
223
|
+
// The node limits how fast sessions open. A refused handshake opened
|
|
224
|
+
// nothing, so trying once more after the pause it asks for is safe.
|
|
225
|
+
if (!(error instanceof SealedTransportError) || error.code !== 'busy')
|
|
226
|
+
throw error;
|
|
227
|
+
await new Promise((resolve) => setTimeout(resolve, error.retryAfterMs ?? 1000));
|
|
228
|
+
return handshakeOnce(baseFetch, nodeUrl, transportKey);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
async function handshakeOnce(baseFetch, nodeUrl, transportKey) {
|
|
208
232
|
const noise = await initiate(transportKey, PROLOGUE);
|
|
209
233
|
const response = await baseFetch(`${nodeUrl}${HANDSHAKE_PATH}`, {
|
|
210
234
|
method: 'POST',
|
|
@@ -214,7 +238,7 @@ async function handshake(baseFetch, nodeUrl, transportKey) {
|
|
|
214
238
|
if (response.headers.get('content-type') !== SEALED_CONTENT_TYPE) {
|
|
215
239
|
throw await envelopeRefusal(response);
|
|
216
240
|
}
|
|
217
|
-
const reply =
|
|
241
|
+
const reply = await readAtMost(response, MAX_HANDSHAKE_REPLY);
|
|
218
242
|
return sessionFrom(noise, reply);
|
|
219
243
|
}
|
|
220
244
|
/** Finish the handshake with the node's reply. Exported for the published vectors. */
|
|
@@ -353,6 +377,10 @@ function frameReader(reader) {
|
|
|
353
377
|
if (!(await fill(4)))
|
|
354
378
|
return null;
|
|
355
379
|
const length = new DataView(buffer.buffer, buffer.byteOffset).getUint32(0, false);
|
|
380
|
+
if (length > MAX_FRAME_LEN) {
|
|
381
|
+
await reader.cancel();
|
|
382
|
+
throw new Error('The sealed response is malformed: a frame is larger than the node sends');
|
|
383
|
+
}
|
|
356
384
|
if (!(await fill(4 + length)))
|
|
357
385
|
return null;
|
|
358
386
|
const frame = buffer.slice(4, 4 + length);
|
|
@@ -362,6 +390,23 @@ function frameReader(reader) {
|
|
|
362
390
|
cancel: () => reader.cancel(),
|
|
363
391
|
};
|
|
364
392
|
}
|
|
393
|
+
/** Read a body, refusing one longer than `limit` rather than buffering it. */
|
|
394
|
+
async function readAtMost(response, limit) {
|
|
395
|
+
if (!response.body)
|
|
396
|
+
return new Uint8Array();
|
|
397
|
+
const reader = response.body.getReader();
|
|
398
|
+
let bytes = new Uint8Array();
|
|
399
|
+
for (;;) {
|
|
400
|
+
const { done, value } = await reader.read();
|
|
401
|
+
if (done)
|
|
402
|
+
return bytes;
|
|
403
|
+
bytes = concat(bytes, value);
|
|
404
|
+
if (bytes.length > limit) {
|
|
405
|
+
await reader.cancel();
|
|
406
|
+
throw new Error('The handshake reply is malformed: it is too long');
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
}
|
|
365
410
|
async function envelopeRefusal(response) {
|
|
366
411
|
let code;
|
|
367
412
|
let message = `Sealed request refused: HTTP ${response.status}`;
|
|
@@ -377,5 +422,7 @@ async function envelopeRefusal(response) {
|
|
|
377
422
|
}
|
|
378
423
|
if (code === 'stale_transport_key')
|
|
379
424
|
return new StaleTransportKeyError();
|
|
380
|
-
|
|
425
|
+
const retryAfter = Number(response.headers.get('retry-after'));
|
|
426
|
+
const retryAfterMs = Number.isFinite(retryAfter) && retryAfter > 0 ? Math.min(retryAfter, 5) * 1000 : undefined;
|
|
427
|
+
return new SealedTransportError(response.status, code, message, retryAfterMs);
|
|
381
428
|
}
|