@bcts/xid 1.0.0-alpha.12 → 1.0.0-alpha.14

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.
@@ -6,14 +6,7 @@
6
6
  * Ported from bc-xid-rust/src/xid_document.rs
7
7
  */
8
8
 
9
- import {
10
- Envelope,
11
- PrivateKeyBase,
12
- type PublicKeyBase,
13
- type EnvelopeEncodable,
14
- type Signer,
15
- type EnvelopeEncodableValue,
16
- } from "@bcts/envelope";
9
+ import { Envelope, type EnvelopeEncodable, type EnvelopeEncodableValue } from "@bcts/envelope";
17
10
  import {
18
11
  KEY,
19
12
  DELEGATE,
@@ -25,7 +18,15 @@ import {
25
18
 
26
19
  // Helper to convert KnownValue to EnvelopeEncodableValue
27
20
  const kv = (v: KnownValue): EnvelopeEncodableValue => v as unknown as EnvelopeEncodableValue;
28
- import { Reference, XID } from "@bcts/components";
21
+ import {
22
+ Reference,
23
+ XID,
24
+ type PublicKeys,
25
+ PrivateKeyBase,
26
+ type PrivateKeys,
27
+ type Signer,
28
+ type EncapsulationPublicKey,
29
+ } from "@bcts/components";
29
30
  import {
30
31
  type ProvenanceMark,
31
32
  ProvenanceMarkGenerator,
@@ -38,7 +39,6 @@ import { Delegate, registerXIDDocumentClass } from "./delegate";
38
39
  import { Service } from "./service";
39
40
  import { Provenance, XIDGeneratorOptions, type XIDGeneratorOptionsValue } from "./provenance";
40
41
  import { XIDError } from "./error";
41
-
42
42
  // Raw values for predicate matching
43
43
  const KEY_RAW = KEY.value();
44
44
  const DELEGATE_RAW = DELEGATE.value();
@@ -51,8 +51,9 @@ const DEREFERENCE_VIA_RAW = DEREFERENCE_VIA.value();
51
51
  */
52
52
  export type XIDInceptionKeyOptions =
53
53
  | { type: "default" }
54
- | { type: "publicKeyBase"; publicKeyBase: PublicKeyBase }
55
- | { type: "privateKeyBase"; privateKeyBase: PrivateKeyBase };
54
+ | { type: "publicKeys"; publicKeys: PublicKeys }
55
+ | { type: "privateKeyBase"; privateKeyBase: PrivateKeyBase }
56
+ | { type: "privateKeys"; privateKeys: PrivateKeys; publicKeys: PublicKeys };
56
57
 
57
58
  /**
58
59
  * Options for creating the genesis mark.
@@ -80,7 +81,8 @@ export type XIDGenesisMarkOptions =
80
81
  export type XIDSigningOptions =
81
82
  | { type: "none" }
82
83
  | { type: "inception" }
83
- | { type: "privateKeyBase"; privateKeyBase: PrivateKeyBase };
84
+ | { type: "privateKeyBase"; privateKeyBase: PrivateKeyBase }
85
+ | { type: "privateKeys"; privateKeys: PrivateKeys };
84
86
 
85
87
  /**
86
88
  * Options for verifying the signature on an envelope when loading.
@@ -134,7 +136,9 @@ export class XIDDocument implements EnvelopeEncodable {
134
136
  const inceptionKey = XIDDocument.inceptionKeyForOptions(keyOptions);
135
137
  const provenance = XIDDocument.genesisMarkWithOptions(markOptions);
136
138
 
137
- const xid = XID.from(hashPublicKey(inceptionKey.publicKeyBase()));
139
+ // Use the reference from PublicKeys (which uses tagged CBOR hash)
140
+ // XID is created from the digest data of the reference
141
+ const xid = XID.from(inceptionKey.publicKeys().reference().getDigest().toData());
138
142
  const doc = new XIDDocument(xid, new Set(), new Map(), new Map(), new Map(), provenance);
139
143
 
140
144
  doc.addKey(inceptionKey);
@@ -144,13 +148,15 @@ export class XIDDocument implements EnvelopeEncodable {
144
148
  private static inceptionKeyForOptions(options: XIDInceptionKeyOptions): Key {
145
149
  switch (options.type) {
146
150
  case "default": {
147
- const privateKeyBase = PrivateKeyBase.generate();
151
+ const privateKeyBase = PrivateKeyBase.new();
148
152
  return Key.newWithPrivateKeyBase(privateKeyBase);
149
153
  }
150
- case "publicKeyBase":
151
- return Key.newAllowAll(options.publicKeyBase);
154
+ case "publicKeys":
155
+ return Key.newAllowAll(options.publicKeys);
152
156
  case "privateKeyBase":
153
157
  return Key.newWithPrivateKeyBase(options.privateKeyBase);
158
+ case "privateKeys":
159
+ return Key.newWithPrivateKeys(options.privateKeys, options.publicKeys);
154
160
  }
155
161
  }
156
162
 
@@ -229,10 +235,10 @@ export class XIDDocument implements EnvelopeEncodable {
229
235
  }
230
236
 
231
237
  /**
232
- * Find a key by its public key base.
238
+ * Find a key by its public keys.
233
239
  */
234
- findKeyByPublicKeyBase(publicKeyBase: PublicKeyBase): Key | undefined {
235
- const hashKey = publicKeyBase.hex();
240
+ findKeyByPublicKeys(publicKeys: PublicKeys): Key | undefined {
241
+ const hashKey = publicKeys.reference().toHex();
236
242
  return this._keys.get(hashKey);
237
243
  }
238
244
 
@@ -251,8 +257,8 @@ export class XIDDocument implements EnvelopeEncodable {
251
257
  /**
252
258
  * Take and remove a key.
253
259
  */
254
- takeKey(publicKeyBase: PublicKeyBase): Key | undefined {
255
- const hashKey = publicKeyBase.hex();
260
+ takeKey(publicKeys: PublicKeys): Key | undefined {
261
+ const hashKey = publicKeys.reference().toHex();
256
262
  const key = this._keys.get(hashKey);
257
263
  if (key !== undefined) {
258
264
  this._keys.delete(hashKey);
@@ -263,22 +269,23 @@ export class XIDDocument implements EnvelopeEncodable {
263
269
  /**
264
270
  * Remove a key.
265
271
  */
266
- removeKey(publicKeyBase: PublicKeyBase): void {
267
- if (this.servicesReferenceKey(publicKeyBase)) {
272
+ removeKey(publicKeys: PublicKeys): void {
273
+ if (this.servicesReferenceKey(publicKeys)) {
268
274
  throw XIDError.stillReferenced("key");
269
275
  }
270
- const hashKey = publicKeyBase.hex();
276
+ const hashKey = publicKeys.reference().toHex();
271
277
  if (!this._keys.delete(hashKey)) {
272
278
  throw XIDError.notFound("key");
273
279
  }
274
280
  }
275
281
 
276
282
  /**
277
- * Check if the given public key is the inception signing key.
283
+ * Check if the given public keys is the inception signing key.
278
284
  */
279
- isInceptionKey(publicKeyBase: PublicKeyBase): boolean {
280
- const xidData = hashPublicKey(publicKeyBase);
281
- return bytesEqual(xidData, this._xid.toData());
285
+ isInceptionKey(publicKeys: PublicKeys): boolean {
286
+ // The XID is derived from the reference of the inception PublicKeys
287
+ const xidReference = publicKeys.reference();
288
+ return bytesEqual(xidReference.getDigest().toData(), this._xid.toData());
282
289
  }
283
290
 
284
291
  /**
@@ -286,7 +293,7 @@ export class XIDDocument implements EnvelopeEncodable {
286
293
  */
287
294
  inceptionKey(): Key | undefined {
288
295
  for (const key of this._keys.values()) {
289
- if (this.isInceptionKey(key.publicKeyBase())) {
296
+ if (this.isInceptionKey(key.publicKeys())) {
290
297
  return key;
291
298
  }
292
299
  }
@@ -294,10 +301,30 @@ export class XIDDocument implements EnvelopeEncodable {
294
301
  }
295
302
 
296
303
  /**
297
- * Get the inception private key base, if available.
304
+ * Get the inception private keys, if available.
298
305
  */
299
- inceptionPrivateKeyBase(): PrivateKeyBase | undefined {
300
- return this.inceptionKey()?.privateKeyBase();
306
+ inceptionPrivateKeys(): PrivateKeys | undefined {
307
+ return this.inceptionKey()?.privateKeys();
308
+ }
309
+
310
+ /**
311
+ * Get the encryption key (encapsulation public key) for this document.
312
+ *
313
+ * Prefers the inception key for encryption. If no inception key is available,
314
+ * falls back to the first key in the document.
315
+ */
316
+ encryptionKey(): EncapsulationPublicKey | undefined {
317
+ // Prefer the inception key for encryption
318
+ const inceptionKey = this.inceptionKey();
319
+ if (inceptionKey !== undefined) {
320
+ return inceptionKey.publicKeys().encapsulationPublicKey();
321
+ }
322
+ // Fall back to first key
323
+ const firstKey = this._keys.values().next().value;
324
+ if (firstKey !== undefined) {
325
+ return firstKey.publicKeys().encapsulationPublicKey();
326
+ }
327
+ return undefined;
301
328
  }
302
329
 
303
330
  /**
@@ -448,16 +475,16 @@ export class XIDDocument implements EnvelopeEncodable {
448
475
  }
449
476
 
450
477
  for (const keyRef of service.keyReferences()) {
451
- const refBytes = hexToBytes(keyRef);
452
- const ref = Reference.hash(refBytes);
478
+ // keyRef is already a hex representation of a Reference, don't hash again
479
+ const ref = Reference.fromHex(keyRef);
453
480
  if (this.findKeyByReference(ref) === undefined) {
454
481
  throw XIDError.unknownKeyReference(keyRef, service.uri());
455
482
  }
456
483
  }
457
484
 
458
485
  for (const delegateRef of service.delegateReferences()) {
459
- const refBytes = hexToBytes(delegateRef);
460
- const ref = Reference.hash(refBytes);
486
+ // delegateRef is already a hex representation of a Reference, don't hash again
487
+ const ref = Reference.fromHex(delegateRef);
461
488
  if (this.findDelegateByReference(ref) === undefined) {
462
489
  throw XIDError.unknownDelegateReference(delegateRef, service.uri());
463
490
  }
@@ -471,8 +498,8 @@ export class XIDDocument implements EnvelopeEncodable {
471
498
  /**
472
499
  * Check if any service references the given key.
473
500
  */
474
- servicesReferenceKey(publicKeyBase: PublicKeyBase): boolean {
475
- const keyRef = Reference.hash(publicKeyBase.data()).toHex();
501
+ servicesReferenceKey(publicKeys: PublicKeys): boolean {
502
+ const keyRef = publicKeys.reference().toHex();
476
503
  for (const service of this._services.values()) {
477
504
  if (service.keyReferences().has(keyRef)) {
478
505
  return true;
@@ -627,27 +654,33 @@ export class XIDDocument implements EnvelopeEncodable {
627
654
  );
628
655
  }
629
656
 
630
- // Apply signing
657
+ // Apply signing (uses sign() which wraps the envelope first)
658
+ // PrivateKeys implements Signer from @bcts/components, which is compatible with envelope's sign()
631
659
  switch (signingOptions.type) {
632
660
  case "inception": {
633
661
  const inceptionKey = this.inceptionKey();
634
662
  if (inceptionKey === undefined) {
635
663
  throw XIDError.missingInceptionKey();
636
664
  }
637
- const privateKeyBase = inceptionKey.privateKeyBase();
638
- if (privateKeyBase === undefined) {
665
+ const privateKeys = inceptionKey.privateKeys();
666
+ if (privateKeys === undefined) {
639
667
  throw XIDError.missingInceptionKey();
640
668
  }
641
- envelope = (envelope as unknown as { addSignature(s: Signer): Envelope }).addSignature(
642
- privateKeyBase as unknown as Signer,
643
- );
669
+ envelope = (envelope as unknown as { sign(s: Signer): Envelope }).sign(privateKeys);
644
670
  break;
645
671
  }
646
- case "privateKeyBase":
647
- envelope = (envelope as unknown as { addSignature(s: Signer): Envelope }).addSignature(
648
- signingOptions.privateKeyBase as unknown as Signer,
672
+ case "privateKeyBase": {
673
+ // Derive PrivateKeys from PrivateKeyBase and use for signing
674
+ const privateKeys = signingOptions.privateKeyBase.ed25519PrivateKeys();
675
+ envelope = (envelope as unknown as { sign(s: Signer): Envelope }).sign(privateKeys);
676
+ break;
677
+ }
678
+ case "privateKeys": {
679
+ envelope = (envelope as unknown as { sign(s: Signer): Envelope }).sign(
680
+ signingOptions.privateKeys,
649
681
  );
650
682
  break;
683
+ }
651
684
  case "none":
652
685
  default:
653
686
  break;
@@ -693,13 +726,13 @@ export class XIDDocument implements EnvelopeEncodable {
693
726
  throw XIDError.missingInceptionKey();
694
727
  }
695
728
 
696
- // Verify signature
697
- if (!envelopeExt.hasSignatureFrom(inceptionKey.publicKeyBase() as unknown as Verifier)) {
729
+ // Verify signature using the PublicKeys (implements Verifier from @bcts/components)
730
+ if (!envelopeExt.hasSignatureFrom(inceptionKey.publicKeys())) {
698
731
  throw XIDError.signatureVerificationFailed();
699
732
  }
700
733
 
701
734
  // Verify XID matches inception key
702
- if (!doc.isInceptionKey(inceptionKey.publicKeyBase())) {
735
+ if (!doc.isInceptionKey(inceptionKey.publicKeys())) {
703
736
  throw XIDError.invalidXid();
704
737
  }
705
738
 
@@ -791,7 +824,7 @@ export class XIDDocument implements EnvelopeEncodable {
791
824
  const envelope = this.toEnvelope(XIDPrivateKeyOptions.Omit, XIDGeneratorOptions.Omit, {
792
825
  type: "none",
793
826
  });
794
- return (envelope as unknown as { addSignature(s: Signer): Envelope }).addSignature(signingKey);
827
+ return (envelope as unknown as { sign(s: Signer): Envelope }).sign(signingKey);
795
828
  }
796
829
 
797
830
  /**
@@ -834,20 +867,7 @@ export class XIDDocument implements EnvelopeEncodable {
834
867
  // Register XIDDocument class with Delegate to resolve circular dependency
835
868
  registerXIDDocumentClass(XIDDocument);
836
869
 
837
- // Helper interface for Verifier
838
- interface Verifier {
839
- verify(data: Uint8Array, signature: { data(): Uint8Array }): boolean;
840
- }
841
-
842
870
  // Helper functions
843
- function hexToBytes(hex: string): Uint8Array {
844
- const bytes = new Uint8Array(hex.length / 2);
845
- for (let i = 0; i < hex.length; i += 2) {
846
- bytes[i / 2] = parseInt(hex.substring(i, i + 2), 16);
847
- }
848
- return bytes;
849
- }
850
-
851
871
  function bytesEqual(a: Uint8Array, b: Uint8Array): boolean {
852
872
  if (a.length !== b.length) return false;
853
873
  for (let i = 0; i < a.length; i++) {
@@ -855,8 +875,3 @@ function bytesEqual(a: Uint8Array, b: Uint8Array): boolean {
855
875
  }
856
876
  return true;
857
877
  }
858
-
859
- function hashPublicKey(publicKeyBase: PublicKeyBase): Uint8Array {
860
- // SHA-256 hash of public key to get XID
861
- return Reference.hash(publicKeyBase.data()).getDigest().toData();
862
- }