@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.
- package/LICENSE +48 -0
- package/README.md +23 -0
- package/dist/index.cjs +2646 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +782 -0
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.mts +782 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.iife.js +2644 -0
- package/dist/index.iife.js.map +1 -0
- package/dist/index.mjs +2552 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +84 -0
- package/src/base/assertion.ts +179 -0
- package/src/base/assertions.ts +153 -0
- package/src/base/cbor.ts +122 -0
- package/src/base/digest.ts +204 -0
- package/src/base/elide.ts +390 -0
- package/src/base/envelope-decodable.ts +186 -0
- package/src/base/envelope-encodable.ts +71 -0
- package/src/base/envelope.ts +988 -0
- package/src/base/error.ts +421 -0
- package/src/base/index.ts +56 -0
- package/src/base/leaf.ts +147 -0
- package/src/base/queries.ts +244 -0
- package/src/base/walk.ts +215 -0
- package/src/base/wrap.ts +26 -0
- package/src/extension/attachment.ts +280 -0
- package/src/extension/compress.ts +176 -0
- package/src/extension/encrypt.ts +297 -0
- package/src/extension/expression.ts +404 -0
- package/src/extension/index.ts +72 -0
- package/src/extension/proof.ts +227 -0
- package/src/extension/recipient.ts +440 -0
- package/src/extension/salt.ts +114 -0
- package/src/extension/signature.ts +398 -0
- package/src/extension/types.ts +92 -0
- package/src/format/diagnostic.ts +116 -0
- package/src/format/hex.ts +25 -0
- package/src/format/index.ts +13 -0
- package/src/format/tree.ts +168 -0
- package/src/index.ts +32 -0
- package/src/utils/index.ts +8 -0
- 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 {};
|