@bcts/envelope 1.0.0-alpha.10

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.
Files changed (44) hide show
  1. package/LICENSE +48 -0
  2. package/README.md +23 -0
  3. package/dist/index.cjs +2646 -0
  4. package/dist/index.cjs.map +1 -0
  5. package/dist/index.d.cts +782 -0
  6. package/dist/index.d.cts.map +1 -0
  7. package/dist/index.d.mts +782 -0
  8. package/dist/index.d.mts.map +1 -0
  9. package/dist/index.iife.js +2644 -0
  10. package/dist/index.iife.js.map +1 -0
  11. package/dist/index.mjs +2552 -0
  12. package/dist/index.mjs.map +1 -0
  13. package/package.json +84 -0
  14. package/src/base/assertion.ts +179 -0
  15. package/src/base/assertions.ts +153 -0
  16. package/src/base/cbor.ts +122 -0
  17. package/src/base/digest.ts +204 -0
  18. package/src/base/elide.ts +390 -0
  19. package/src/base/envelope-decodable.ts +186 -0
  20. package/src/base/envelope-encodable.ts +71 -0
  21. package/src/base/envelope.ts +988 -0
  22. package/src/base/error.ts +421 -0
  23. package/src/base/index.ts +56 -0
  24. package/src/base/leaf.ts +147 -0
  25. package/src/base/queries.ts +244 -0
  26. package/src/base/walk.ts +215 -0
  27. package/src/base/wrap.ts +26 -0
  28. package/src/extension/attachment.ts +280 -0
  29. package/src/extension/compress.ts +176 -0
  30. package/src/extension/encrypt.ts +297 -0
  31. package/src/extension/expression.ts +404 -0
  32. package/src/extension/index.ts +72 -0
  33. package/src/extension/proof.ts +227 -0
  34. package/src/extension/recipient.ts +440 -0
  35. package/src/extension/salt.ts +114 -0
  36. package/src/extension/signature.ts +398 -0
  37. package/src/extension/types.ts +92 -0
  38. package/src/format/diagnostic.ts +116 -0
  39. package/src/format/hex.ts +25 -0
  40. package/src/format/index.ts +13 -0
  41. package/src/format/tree.ts +168 -0
  42. package/src/index.ts +32 -0
  43. package/src/utils/index.ts +8 -0
  44. package/src/utils/string.ts +48 -0
@@ -0,0 +1,72 @@
1
+ /// Extension module exports for Gordian Envelope.
2
+ ///
3
+ /// This module provides extended functionality for working with Gordian
4
+ /// Envelopes, including type system support, encryption, compression,
5
+ /// signatures, and more.
6
+
7
+ // Type system
8
+ export { IS_A } from "./types";
9
+
10
+ // Salt support
11
+ export { SALT } from "./salt";
12
+
13
+ // Compression support
14
+ export { Compressed, registerCompressExtension } from "./compress";
15
+
16
+ // Encryption support
17
+ export { SymmetricKey, EncryptedMessage, registerEncryptExtension } from "./encrypt";
18
+
19
+ // Signature support
20
+ export {
21
+ Signature,
22
+ SigningPrivateKey,
23
+ SigningPublicKey,
24
+ SignatureMetadata,
25
+ type Signer,
26
+ type Verifier,
27
+ SIGNED,
28
+ VERIFIED_BY,
29
+ NOTE,
30
+ } from "./signature";
31
+
32
+ // Attachment support
33
+ export { Attachments, ATTACHMENT, VENDOR, CONFORMS_TO } from "./attachment";
34
+
35
+ // Recipient support (public-key encryption)
36
+ export { PublicKeyBase, PrivateKeyBase, SealedMessage, HAS_RECIPIENT } from "./recipient";
37
+
38
+ // Expression support
39
+ export {
40
+ Function,
41
+ Parameter,
42
+ Expression,
43
+ FUNCTION_IDS,
44
+ PARAMETER_IDS,
45
+ CBOR_TAG_FUNCTION,
46
+ CBOR_TAG_PARAMETER,
47
+ CBOR_TAG_PLACEHOLDER,
48
+ CBOR_TAG_REPLACEMENT,
49
+ add,
50
+ sub,
51
+ mul,
52
+ div,
53
+ neg,
54
+ lt,
55
+ gt,
56
+ eq,
57
+ and,
58
+ or,
59
+ not,
60
+ type FunctionID,
61
+ type ParameterID,
62
+ } from "./expression";
63
+
64
+ // Import side-effect modules to register prototype extensions
65
+ import "./types";
66
+ import "./salt";
67
+ import "./compress";
68
+ import "./encrypt";
69
+ import "./signature";
70
+ import "./attachment";
71
+ import "./recipient";
72
+ import "./proof";
@@ -0,0 +1,227 @@
1
+ import { Envelope } from "../base/envelope";
2
+ import { type Digest } from "../base/digest";
3
+
4
+ /// Extension for envelope inclusion proofs.
5
+ ///
6
+ /// Inclusion proofs allow a holder of an envelope to prove that specific
7
+ /// elements exist within the envelope without revealing the entire contents.
8
+ /// This is particularly useful for selective disclosure of information in
9
+ /// privacy-preserving scenarios.
10
+ ///
11
+ /// ## How Inclusion Proofs Work
12
+ ///
13
+ /// The inclusion proof mechanism leverages the Merkle-like digest tree
14
+ /// structure of envelopes:
15
+ /// - The holder creates a minimal structure containing only the digests
16
+ /// necessary to validate the proof
17
+ /// - A verifier with a trusted root digest can confirm that the specific
18
+ /// elements exist in the original envelope
19
+ /// - All other content can remain elided, preserving privacy
20
+ ///
21
+ /// For enhanced privacy, elements can be salted to prevent correlation attacks.
22
+ ///
23
+ /// @example
24
+ /// ```typescript
25
+ /// // Create an envelope with multiple assertions
26
+ /// const aliceFriends = Envelope.new('Alice')
27
+ /// .addAssertion('knows', 'Bob')
28
+ /// .addAssertion('knows', 'Carol')
29
+ /// .addAssertion('knows', 'Dan');
30
+ ///
31
+ /// // Create a representation of just the root digest
32
+ /// const aliceFriendsRoot = aliceFriends.elideRevealingSet(new Set());
33
+ ///
34
+ /// // Create the target we want to prove exists
35
+ /// const knowsBobAssertion = Envelope.newAssertion('knows', 'Bob');
36
+ ///
37
+ /// // Generate a proof that Alice knows Bob
38
+ /// const aliceKnowsBobProof = aliceFriends.proofContainsTarget(knowsBobAssertion);
39
+ ///
40
+ /// // A third party can verify the proof against the trusted root
41
+ /// if (aliceKnowsBobProof) {
42
+ /// const isValid = aliceFriendsRoot.confirmContainsTarget(
43
+ /// knowsBobAssertion,
44
+ /// aliceKnowsBobProof
45
+ /// );
46
+ /// console.log('Proof is valid:', isValid);
47
+ /// }
48
+ /// ```
49
+
50
+ /// Implementation of proof methods on Envelope prototype
51
+ // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
52
+ if (Envelope?.prototype) {
53
+ Envelope.prototype.proofContainsSet = function (target: Set<Digest>): Envelope | undefined {
54
+ const revealSet = revealSetOfSet(this, target);
55
+
56
+ // Check if all targets can be revealed
57
+ if (!isSubset(target, revealSet)) {
58
+ return undefined;
59
+ }
60
+
61
+ // Create a proof by revealing only what's necessary, then eliding the targets
62
+ const revealed = this.elideRevealingSet(revealSet);
63
+ return revealed.elideRemovingSet(target);
64
+ };
65
+
66
+ Envelope.prototype.proofContainsTarget = function (target: Envelope): Envelope | undefined {
67
+ const targetSet = new Set<Digest>([target.digest()]);
68
+ return this.proofContainsSet(targetSet);
69
+ };
70
+
71
+ Envelope.prototype.confirmContainsSet = function (target: Set<Digest>, proof: Envelope): boolean {
72
+ // Verify the proof has the same digest as this envelope
73
+ if (this.digest().hex() !== proof.digest().hex()) {
74
+ return false;
75
+ }
76
+
77
+ // Verify the proof contains all target elements
78
+ return containsAll(proof, target);
79
+ };
80
+
81
+ Envelope.prototype.confirmContainsTarget = function (target: Envelope, proof: Envelope): boolean {
82
+ const targetSet = new Set<Digest>([target.digest()]);
83
+ return this.confirmContainsSet(targetSet, proof);
84
+ };
85
+ }
86
+
87
+ /// Internal helper functions
88
+
89
+ /// Builds a set of all digests needed to reveal the target set.
90
+ ///
91
+ /// This collects all digests in the path from the envelope's root to each
92
+ /// target element.
93
+ function revealSetOfSet(envelope: Envelope, target: Set<Digest>): Set<Digest> {
94
+ const result = new Set<Digest>();
95
+ revealSets(envelope, target, new Set<Digest>(), result);
96
+ return result;
97
+ }
98
+
99
+ /// Recursively traverses the envelope to collect all digests needed to
100
+ /// reveal the target set.
101
+ ///
102
+ /// Builds the set of digests forming the path from the root to each target element.
103
+ function revealSets(
104
+ envelope: Envelope,
105
+ target: Set<Digest>,
106
+ current: Set<Digest>,
107
+ result: Set<Digest>,
108
+ ): void {
109
+ // Add current envelope's digest to the path
110
+ const newCurrent = new Set(current);
111
+ newCurrent.add(envelope.digest());
112
+
113
+ // If this is a target, add the entire path to the result
114
+ if (containsDigest(target, envelope.digest())) {
115
+ for (const digest of newCurrent) {
116
+ result.add(digest);
117
+ }
118
+ }
119
+
120
+ // Traverse the envelope structure
121
+ const envelopeCase = envelope.case();
122
+
123
+ if (envelopeCase.type === "node") {
124
+ // Traverse subject
125
+ revealSets(envelopeCase.subject, target, newCurrent, result);
126
+
127
+ // Traverse all assertions
128
+ for (const assertion of envelopeCase.assertions) {
129
+ revealSets(assertion, target, newCurrent, result);
130
+ }
131
+ } else if (envelopeCase.type === "wrapped") {
132
+ // Traverse wrapped envelope
133
+ revealSets(envelopeCase.envelope, target, newCurrent, result);
134
+ } else if (envelopeCase.type === "assertion") {
135
+ // Traverse predicate and object
136
+ const predicate = envelopeCase.assertion.predicate();
137
+ const object = envelopeCase.assertion.object();
138
+ revealSets(predicate, target, newCurrent, result);
139
+ revealSets(object, target, newCurrent, result);
140
+ }
141
+ // For leaf envelopes (elided, encrypted, compressed, leaf), no further traversal needed
142
+ }
143
+
144
+ /// Checks if this envelope contains all elements in the target set.
145
+ ///
146
+ /// Used during proof verification to confirm all target elements exist in the proof.
147
+ function containsAll(envelope: Envelope, target: Set<Digest>): boolean {
148
+ const targetCopy = new Set(target);
149
+ removeAllFound(envelope, targetCopy);
150
+ return targetCopy.size === 0;
151
+ }
152
+
153
+ /// Recursively traverses the envelope and removes found target elements from the set.
154
+ ///
155
+ /// Used during proof verification to confirm all target elements are present.
156
+ function removeAllFound(envelope: Envelope, target: Set<Digest>): void {
157
+ // Check if this envelope's digest is in the target set
158
+ if (containsDigest(target, envelope.digest())) {
159
+ removeDigest(target, envelope.digest());
160
+ }
161
+
162
+ // Early exit if all targets found
163
+ if (target.size === 0) {
164
+ return;
165
+ }
166
+
167
+ // Traverse the envelope structure
168
+ const envelopeCase = envelope.case();
169
+
170
+ if (envelopeCase.type === "node") {
171
+ // Traverse subject
172
+ removeAllFound(envelopeCase.subject, target);
173
+
174
+ // Traverse all assertions
175
+ for (const assertion of envelopeCase.assertions) {
176
+ removeAllFound(assertion, target);
177
+ if (target.size === 0) break;
178
+ }
179
+ } else if (envelopeCase.type === "wrapped") {
180
+ // Traverse wrapped envelope
181
+ removeAllFound(envelopeCase.envelope, target);
182
+ } else if (envelopeCase.type === "assertion") {
183
+ // Traverse predicate and object
184
+ const predicate = envelopeCase.assertion.predicate();
185
+ const object = envelopeCase.assertion.object();
186
+ removeAllFound(predicate, target);
187
+ if (target.size > 0) {
188
+ removeAllFound(object, target);
189
+ }
190
+ }
191
+ // For leaf envelopes (elided, encrypted, compressed, leaf), no further traversal needed
192
+ }
193
+
194
+ /// Helper function to check if a set contains a digest (by hex comparison)
195
+ function containsDigest(set: Set<Digest>, digest: Digest): boolean {
196
+ const hexToFind = digest.hex();
197
+ for (const d of set) {
198
+ if (d.hex() === hexToFind) {
199
+ return true;
200
+ }
201
+ }
202
+ return false;
203
+ }
204
+
205
+ /// Helper function to remove a digest from a set (by hex comparison)
206
+ function removeDigest(set: Set<Digest>, digest: Digest): void {
207
+ const hexToFind = digest.hex();
208
+ for (const d of set) {
209
+ if (d.hex() === hexToFind) {
210
+ set.delete(d);
211
+ return;
212
+ }
213
+ }
214
+ }
215
+
216
+ /// Helper function to check if one set is a subset of another (by hex comparison)
217
+ function isSubset(subset: Set<Digest>, superset: Set<Digest>): boolean {
218
+ for (const digest of subset) {
219
+ if (!containsDigest(superset, digest)) {
220
+ return false;
221
+ }
222
+ }
223
+ return true;
224
+ }
225
+
226
+ // Export empty object to make this a module
227
+ export {};