@bcts/gstp 1.0.0-alpha.23 → 1.0.0-beta.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/index.cjs +130 -44
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +38 -3
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +38 -3
- package/dist/index.d.mts.map +1 -1
- package/dist/index.iife.js +131 -46
- package/dist/index.iife.js.map +1 -1
- package/dist/index.mjs +130 -44
- package/dist/index.mjs.map +1 -1
- package/package.json +7 -7
- package/src/continuation.ts +81 -41
- package/src/sealed-event.ts +9 -6
- package/src/sealed-request.ts +69 -27
- package/src/sealed-response.ts +7 -6
package/dist/index.cjs
CHANGED
|
@@ -13,6 +13,7 @@ var __exportAll = (all, no_symbols) => {
|
|
|
13
13
|
//#endregion
|
|
14
14
|
let _bcts_components = require("@bcts/components");
|
|
15
15
|
let _bcts_envelope = require("@bcts/envelope");
|
|
16
|
+
let _bcts_dcbor = require("@bcts/dcbor");
|
|
16
17
|
let _bcts_known_values = require("@bcts/known-values");
|
|
17
18
|
let _bcts_xid = require("@bcts/xid");
|
|
18
19
|
//#region src/error.ts
|
|
@@ -133,7 +134,22 @@ var GstpError = class GstpError extends Error {
|
|
|
133
134
|
* eliminating the need for local state storage and enhancing security
|
|
134
135
|
* for devices with limited storage or requiring distributed state management.
|
|
135
136
|
*
|
|
136
|
-
* Ported from gstp-rust/src/continuation.rs
|
|
137
|
+
* Ported from gstp-rust/src/continuation.rs.
|
|
138
|
+
*
|
|
139
|
+
* Wire shape — mirrors Rust:
|
|
140
|
+
* ```
|
|
141
|
+
* {
|
|
142
|
+
* <state envelope>
|
|
143
|
+
* } [
|
|
144
|
+
* 'id': ARID(...)
|
|
145
|
+
* 'validUntil': Date(...) ← CBOR tag 1, not ISO 8601 text
|
|
146
|
+
* ]
|
|
147
|
+
* ```
|
|
148
|
+
*
|
|
149
|
+
* The `state` envelope is **wrapped** before assertions are attached,
|
|
150
|
+
* matching Rust `self.state.wrap().add_optional_assertion(...)`. The
|
|
151
|
+
* earlier port attached the assertions directly to the un-wrapped state,
|
|
152
|
+
* producing a different digest tree.
|
|
137
153
|
*/
|
|
138
154
|
/**
|
|
139
155
|
* Represents an encrypted state continuation.
|
|
@@ -240,8 +256,10 @@ var Continuation = class Continuation {
|
|
|
240
256
|
/**
|
|
241
257
|
* Checks if the continuation is valid at the given time.
|
|
242
258
|
*
|
|
243
|
-
*
|
|
244
|
-
*
|
|
259
|
+
* Mirrors Rust `is_valid_date(now)`: at the exact `valid_until`
|
|
260
|
+
* instant, the continuation is **expired** (returns `false`). The
|
|
261
|
+
* earlier port used `<=` here, which differed from Rust by one
|
|
262
|
+
* millisecond at the boundary.
|
|
245
263
|
*
|
|
246
264
|
* @param now - The time to check against, or undefined to skip time validation
|
|
247
265
|
* @returns true if the continuation is valid at the given time
|
|
@@ -249,7 +267,7 @@ var Continuation = class Continuation {
|
|
|
249
267
|
isValidDate(now) {
|
|
250
268
|
if (this._validUntil === void 0) return true;
|
|
251
269
|
if (now === void 0) return true;
|
|
252
|
-
return now.getTime()
|
|
270
|
+
return now.getTime() < this._validUntil.getTime();
|
|
253
271
|
}
|
|
254
272
|
/**
|
|
255
273
|
* Checks if the continuation has the expected ID.
|
|
@@ -278,21 +296,39 @@ var Continuation = class Continuation {
|
|
|
278
296
|
/**
|
|
279
297
|
* Converts the continuation to an envelope.
|
|
280
298
|
*
|
|
281
|
-
*
|
|
299
|
+
* Mirrors Rust `Continuation::to_envelope`:
|
|
300
|
+
*
|
|
301
|
+
* ```rust
|
|
302
|
+
* self.state.wrap()
|
|
303
|
+
* .add_optional_assertion(ID, self.valid_id)
|
|
304
|
+
* .add_optional_assertion(VALID_UNTIL, self.valid_until)
|
|
305
|
+
* ```
|
|
306
|
+
*
|
|
307
|
+
* The state is wrapped first; the optional assertions then live on
|
|
308
|
+
* the wrap node. `valid_until` is encoded as a CBOR-tagged Date
|
|
309
|
+
* (tag 1) — never as a plain ISO 8601 string.
|
|
282
310
|
*
|
|
283
311
|
* @param recipient - Optional recipient to encrypt the envelope to
|
|
284
312
|
* @returns The continuation as an envelope
|
|
285
313
|
*/
|
|
286
314
|
toEnvelope(recipient) {
|
|
287
|
-
let envelope = this._state;
|
|
315
|
+
let envelope = this._state.wrap();
|
|
288
316
|
if (this._validId !== void 0) envelope = envelope.addAssertion(_bcts_known_values.ID, this._validId);
|
|
289
|
-
if (this._validUntil !== void 0) envelope = envelope.addAssertion(_bcts_known_values.VALID_UNTIL, this._validUntil
|
|
317
|
+
if (this._validUntil !== void 0) envelope = envelope.addAssertion(_bcts_known_values.VALID_UNTIL, _bcts_dcbor.CborDate.fromDatetime(this._validUntil));
|
|
290
318
|
if (recipient !== void 0) envelope = envelope.encryptToRecipients([recipient]);
|
|
291
319
|
return envelope;
|
|
292
320
|
}
|
|
293
321
|
/**
|
|
294
322
|
* Parses a continuation from an envelope.
|
|
295
323
|
*
|
|
324
|
+
* Mirrors Rust `Continuation::try_from_envelope`:
|
|
325
|
+
*
|
|
326
|
+
* ```rust
|
|
327
|
+
* state: envelope.try_unwrap()?, // unwrap
|
|
328
|
+
* valid_id: envelope.extract_optional_object_for_predicate(ID)?,
|
|
329
|
+
* valid_until: envelope.extract_optional_object_for_predicate(VALID_UNTIL)?,
|
|
330
|
+
* ```
|
|
331
|
+
*
|
|
296
332
|
* @param encryptedEnvelope - The envelope to parse
|
|
297
333
|
* @param expectedId - Optional ID to validate against
|
|
298
334
|
* @param now - Optional time to validate against
|
|
@@ -307,23 +343,32 @@ var Continuation = class Continuation {
|
|
|
307
343
|
} catch (e) {
|
|
308
344
|
throw GstpError.envelope(e instanceof Error ? e : new Error(String(e)));
|
|
309
345
|
}
|
|
310
|
-
|
|
311
|
-
let validId;
|
|
346
|
+
let state;
|
|
312
347
|
try {
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
348
|
+
state = envelope.tryUnwrap();
|
|
349
|
+
} catch (e) {
|
|
350
|
+
throw GstpError.envelope(e instanceof Error ? e : new Error(String(e)));
|
|
351
|
+
}
|
|
352
|
+
let validId;
|
|
353
|
+
const idObj = envelope.optionalObjectForPredicate(_bcts_known_values.ID);
|
|
354
|
+
if (idObj !== void 0) {
|
|
355
|
+
const leafCbor = idObj.asLeaf();
|
|
356
|
+
if (leafCbor !== void 0) try {
|
|
357
|
+
validId = _bcts_components.ARID.fromTaggedCbor(leafCbor);
|
|
358
|
+
} catch (e) {
|
|
359
|
+
throw GstpError.envelope(e instanceof Error ? e : new Error(String(e)));
|
|
317
360
|
}
|
|
318
|
-
}
|
|
361
|
+
}
|
|
319
362
|
let validUntil;
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
363
|
+
const validUntilObj = envelope.optionalObjectForPredicate(_bcts_known_values.VALID_UNTIL);
|
|
364
|
+
if (validUntilObj !== void 0) {
|
|
365
|
+
const leafCbor = validUntilObj.asLeaf();
|
|
366
|
+
if (leafCbor !== void 0) try {
|
|
367
|
+
validUntil = _bcts_dcbor.CborDate.fromTaggedCbor(leafCbor).datetime();
|
|
368
|
+
} catch (e) {
|
|
369
|
+
throw GstpError.envelope(e instanceof Error ? e : new Error(String(e)));
|
|
325
370
|
}
|
|
326
|
-
}
|
|
371
|
+
}
|
|
327
372
|
const continuation = new Continuation(state, validId, validUntil);
|
|
328
373
|
if (!continuation.isValidDate(now)) throw GstpError.continuationExpired();
|
|
329
374
|
if (!continuation.isValidId(expectedId)) throw GstpError.continuationIdInvalid();
|
|
@@ -360,6 +405,47 @@ var Continuation = class Continuation {
|
|
|
360
405
|
//#endregion
|
|
361
406
|
//#region src/sealed-request.ts
|
|
362
407
|
/**
|
|
408
|
+
* Copyright © 2023-2026 Blockchain Commons, LLC
|
|
409
|
+
* Copyright © 2025-2026 Parity Technologies
|
|
410
|
+
*
|
|
411
|
+
*
|
|
412
|
+
* SealedRequest - Sealed request messages for GSTP
|
|
413
|
+
*
|
|
414
|
+
* A SealedRequest wraps a Request with sender information and state
|
|
415
|
+
* continuations for secure, authenticated request messages.
|
|
416
|
+
*
|
|
417
|
+
* Ported from gstp-rust/src/sealed_request.rs
|
|
418
|
+
*/
|
|
419
|
+
/**
|
|
420
|
+
* Decode a CBOR value into a typed JS value.
|
|
421
|
+
*
|
|
422
|
+
* Mirrors the type-driven `T: TryFrom<CBOR>` dispatch Rust's
|
|
423
|
+
* `extract_object_for_parameter` relies on. TS lacks compile-time
|
|
424
|
+
* trait dispatch, so we hand-roll the most common cases:
|
|
425
|
+
* - tag 1 (`Date`) → JS `Date`,
|
|
426
|
+
* - tag 40012 (`ARID`) → `ARID`,
|
|
427
|
+
* - integer / text / bool / number / byte-string primitives,
|
|
428
|
+
* - everything else → the raw `Cbor` value.
|
|
429
|
+
*
|
|
430
|
+
* Callers needing other typed extraction should use
|
|
431
|
+
* `objectForParameter()` directly and decode the envelope themselves.
|
|
432
|
+
*/
|
|
433
|
+
function extractCborAsT(cbor) {
|
|
434
|
+
const tagged = cbor.asTagged();
|
|
435
|
+
if (tagged !== void 0) {
|
|
436
|
+
const [tag] = tagged;
|
|
437
|
+
const tagNumber = Number(tag.value);
|
|
438
|
+
if (tagNumber === 1) return _bcts_dcbor.CborDate.fromTaggedCbor(cbor).datetime();
|
|
439
|
+
if (tagNumber === 40012) return _bcts_components.ARID.fromTaggedCbor(cbor);
|
|
440
|
+
}
|
|
441
|
+
if (cbor.isInteger()) return cbor.toInteger();
|
|
442
|
+
if (cbor.isText()) return cbor.toText();
|
|
443
|
+
if (cbor.isBool()) return cbor.toBool();
|
|
444
|
+
if (cbor.isNumber()) return cbor.toNumber();
|
|
445
|
+
if (cbor.isByteString()) return cbor.toByteString();
|
|
446
|
+
return cbor;
|
|
447
|
+
}
|
|
448
|
+
/**
|
|
363
449
|
* A sealed request that combines a Request with sender information and
|
|
364
450
|
* state continuations for secure communication.
|
|
365
451
|
*
|
|
@@ -451,25 +537,32 @@ var SealedRequest = class SealedRequest {
|
|
|
451
537
|
}
|
|
452
538
|
/**
|
|
453
539
|
* Returns all objects for a parameter.
|
|
540
|
+
*
|
|
541
|
+
* Mirrors Rust `SealedRequest::objects_for_parameter` which delegates
|
|
542
|
+
* to `Expression::objects_for_parameter`. GSTP requests can carry
|
|
543
|
+
* multiple parameters with the same ID — e.g. a DKG invite has
|
|
544
|
+
* one `participant` per group member — and a decoder must see
|
|
545
|
+
* every one of them.
|
|
454
546
|
*/
|
|
455
547
|
objectsForParameter(param) {
|
|
456
|
-
|
|
457
|
-
return obj !== void 0 ? [obj] : [];
|
|
548
|
+
return this._request.body().objectsForParameter(param);
|
|
458
549
|
}
|
|
459
550
|
/**
|
|
460
551
|
* Extracts an object for a parameter as a specific type.
|
|
552
|
+
*
|
|
553
|
+
* Mirrors Rust `SealedRequest::extract_object_for_parameter` — Rust
|
|
554
|
+
* uses a `T: TryFrom<CBOR>` constraint and dispatches to whatever
|
|
555
|
+
* `From<CBOR> for T` impl is in scope (e.g. tag-1 CBOR decodes to
|
|
556
|
+
* `chrono::DateTime`, tag-40012 to `ARID`, etc.). TS lacks that
|
|
557
|
+
* trait dispatch, so we recognise the most common tagged types
|
|
558
|
+
* (`Date` via tag 1, `ARID` via tag 40012) plus the primitive
|
|
559
|
+
* fall-through. Callers needing other typed extraction should use
|
|
560
|
+
* `objectForParameter()` directly and decode the envelope themselves.
|
|
461
561
|
*/
|
|
462
562
|
extractObjectForParameter(param) {
|
|
463
563
|
const envelope = this.objectForParameter(param);
|
|
464
564
|
if (envelope === void 0) throw GstpError.envelope(/* @__PURE__ */ new Error(`Parameter not found: ${param}`));
|
|
465
|
-
return envelope.extractSubject((cbor) =>
|
|
466
|
-
if (cbor.isInteger()) return cbor.toInteger();
|
|
467
|
-
if (cbor.isText()) return cbor.toText();
|
|
468
|
-
if (cbor.isBool()) return cbor.toBool();
|
|
469
|
-
if (cbor.isNumber()) return cbor.toNumber();
|
|
470
|
-
if (cbor.isByteString()) return cbor.toByteString();
|
|
471
|
-
return cbor;
|
|
472
|
-
});
|
|
565
|
+
return envelope.extractSubject((cbor) => extractCborAsT(cbor));
|
|
473
566
|
}
|
|
474
567
|
/**
|
|
475
568
|
* Extracts an optional object for a parameter.
|
|
@@ -477,14 +570,7 @@ var SealedRequest = class SealedRequest {
|
|
|
477
570
|
extractOptionalObjectForParameter(param) {
|
|
478
571
|
const envelope = this.objectForParameter(param);
|
|
479
572
|
if (envelope === void 0) return;
|
|
480
|
-
return envelope.extractSubject((cbor) =>
|
|
481
|
-
if (cbor.isInteger()) return cbor.toInteger();
|
|
482
|
-
if (cbor.isText()) return cbor.toText();
|
|
483
|
-
if (cbor.isBool()) return cbor.toBool();
|
|
484
|
-
if (cbor.isNumber()) return cbor.toNumber();
|
|
485
|
-
if (cbor.isByteString()) return cbor.toByteString();
|
|
486
|
-
return cbor;
|
|
487
|
-
});
|
|
573
|
+
return envelope.extractSubject((cbor) => extractCborAsT(cbor));
|
|
488
574
|
}
|
|
489
575
|
/**
|
|
490
576
|
* Extracts all objects for a parameter as a specific type.
|
|
@@ -616,7 +702,7 @@ var SealedRequest = class SealedRequest {
|
|
|
616
702
|
*/
|
|
617
703
|
toEnvelopeForRecipients(validUntil, signer, recipients) {
|
|
618
704
|
const continuation = new Continuation(this._state ?? _bcts_envelope.Envelope.new(null), this.id(), validUntil);
|
|
619
|
-
const senderEncryptionKey = this._sender.
|
|
705
|
+
const senderEncryptionKey = this._sender.encryptionKey();
|
|
620
706
|
if (senderEncryptionKey === void 0) throw GstpError.senderMissingEncryptionKey();
|
|
621
707
|
const senderContinuation = continuation.toEnvelope(senderEncryptionKey);
|
|
622
708
|
let result = this._request.toEnvelope();
|
|
@@ -658,7 +744,7 @@ var SealedRequest = class SealedRequest {
|
|
|
658
744
|
} catch (e) {
|
|
659
745
|
throw GstpError.xid(e instanceof Error ? e : new Error(String(e)));
|
|
660
746
|
}
|
|
661
|
-
const senderVerificationKey = sender.
|
|
747
|
+
const senderVerificationKey = sender.verificationKey();
|
|
662
748
|
if (senderVerificationKey === void 0) throw GstpError.senderMissingVerificationKey();
|
|
663
749
|
let requestEnvelope;
|
|
664
750
|
try {
|
|
@@ -920,7 +1006,7 @@ var SealedResponse = class SealedResponse {
|
|
|
920
1006
|
let senderContinuation;
|
|
921
1007
|
if (this._state !== void 0) {
|
|
922
1008
|
const continuation = new Continuation(this._state, void 0, validUntil);
|
|
923
|
-
const senderEncryptionKey = this._sender.
|
|
1009
|
+
const senderEncryptionKey = this._sender.encryptionKey();
|
|
924
1010
|
if (senderEncryptionKey === void 0) throw GstpError.senderMissingEncryptionKey();
|
|
925
1011
|
senderContinuation = continuation.toEnvelope(senderEncryptionKey);
|
|
926
1012
|
}
|
|
@@ -963,7 +1049,7 @@ var SealedResponse = class SealedResponse {
|
|
|
963
1049
|
} catch (e) {
|
|
964
1050
|
throw GstpError.xid(e instanceof Error ? e : new Error(String(e)));
|
|
965
1051
|
}
|
|
966
|
-
const senderVerificationKey = sender.
|
|
1052
|
+
const senderVerificationKey = sender.verificationKey();
|
|
967
1053
|
if (senderVerificationKey === void 0) throw GstpError.senderMissingVerificationKey();
|
|
968
1054
|
let responseEnvelope;
|
|
969
1055
|
try {
|
|
@@ -1182,7 +1268,7 @@ var SealedEvent = class SealedEvent {
|
|
|
1182
1268
|
* @returns The sealed event as an envelope
|
|
1183
1269
|
*/
|
|
1184
1270
|
toEnvelopeForRecipients(validUntil, signer, recipients) {
|
|
1185
|
-
const senderEncryptionKey = this._sender.
|
|
1271
|
+
const senderEncryptionKey = this._sender.encryptionKey();
|
|
1186
1272
|
if (senderEncryptionKey === void 0) throw GstpError.senderMissingEncryptionKey();
|
|
1187
1273
|
let senderContinuation;
|
|
1188
1274
|
if (this._state !== void 0) senderContinuation = new Continuation(this._state, void 0, validUntil).toEnvelope(senderEncryptionKey);
|
|
@@ -1227,7 +1313,7 @@ var SealedEvent = class SealedEvent {
|
|
|
1227
1313
|
} catch (e) {
|
|
1228
1314
|
throw GstpError.xid(e instanceof Error ? e : new Error(String(e)));
|
|
1229
1315
|
}
|
|
1230
|
-
const senderVerificationKey = sender.
|
|
1316
|
+
const senderVerificationKey = sender.verificationKey();
|
|
1231
1317
|
if (senderVerificationKey === void 0) throw GstpError.senderMissingVerificationKey();
|
|
1232
1318
|
let eventEnvelope;
|
|
1233
1319
|
try {
|