@bcts/envelope 1.0.0-alpha.5
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 +978 -0
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.mts +978 -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 +85 -0
- package/src/base/assertion.ts +179 -0
- package/src/base/assertions.ts +304 -0
- package/src/base/cbor.ts +122 -0
- package/src/base/digest.ts +204 -0
- package/src/base/elide.ts +526 -0
- package/src/base/envelope-decodable.ts +229 -0
- package/src/base/envelope-encodable.ts +71 -0
- package/src/base/envelope.ts +790 -0
- package/src/base/error.ts +421 -0
- package/src/base/index.ts +56 -0
- package/src/base/leaf.ts +226 -0
- package/src/base/queries.ts +374 -0
- package/src/base/walk.ts +241 -0
- package/src/base/wrap.ts +72 -0
- package/src/extension/attachment.ts +369 -0
- package/src/extension/compress.ts +293 -0
- package/src/extension/encrypt.ts +379 -0
- package/src/extension/expression.ts +404 -0
- package/src/extension/index.ts +72 -0
- package/src/extension/proof.ts +276 -0
- package/src/extension/recipient.ts +557 -0
- package/src/extension/salt.ts +223 -0
- package/src/extension/signature.ts +463 -0
- package/src/extension/types.ts +222 -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,204 @@
|
|
|
1
|
+
import { sha256 } from "@bcts/crypto";
|
|
2
|
+
|
|
3
|
+
/// A cryptographic digest used to uniquely identify digital objects.
|
|
4
|
+
///
|
|
5
|
+
/// Digests in Gordian Envelope are always SHA-256 hashes (32 bytes).
|
|
6
|
+
/// This is a fundamental building block for the Merkle-like digest tree
|
|
7
|
+
/// that enables privacy features while maintaining integrity.
|
|
8
|
+
///
|
|
9
|
+
/// Based on BCR-2021-002: Digests for Digital Objects
|
|
10
|
+
/// @see https://github.com/BlockchainCommons/Research/blob/master/papers/bcr-2021-002-digest.md
|
|
11
|
+
export class Digest {
|
|
12
|
+
readonly #data: Uint8Array;
|
|
13
|
+
|
|
14
|
+
/// Creates a new Digest from raw bytes.
|
|
15
|
+
///
|
|
16
|
+
/// @param data - The 32-byte digest data
|
|
17
|
+
/// @throws {Error} If data is not exactly 32 bytes
|
|
18
|
+
constructor(data: Uint8Array) {
|
|
19
|
+
if (data.length !== 32) {
|
|
20
|
+
throw new Error(`Digest must be exactly 32 bytes, got ${data.length} bytes`);
|
|
21
|
+
}
|
|
22
|
+
this.#data = data;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/// Returns the raw digest bytes.
|
|
26
|
+
///
|
|
27
|
+
/// @returns A Uint8Array containing the 32-byte digest
|
|
28
|
+
data(): Uint8Array {
|
|
29
|
+
return this.#data;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/// Creates a digest from an image (arbitrary byte array).
|
|
33
|
+
///
|
|
34
|
+
/// This is the primary way to create a digest from data. The data is
|
|
35
|
+
/// hashed using SHA-256 to produce a 32-byte digest.
|
|
36
|
+
///
|
|
37
|
+
/// @param image - The data to hash
|
|
38
|
+
/// @returns A new Digest instance
|
|
39
|
+
///
|
|
40
|
+
/// @example
|
|
41
|
+
/// ```typescript
|
|
42
|
+
/// const digest = Digest.fromImage(new TextEncoder().encode("Hello, world!"));
|
|
43
|
+
/// ```
|
|
44
|
+
static fromImage(image: Uint8Array): Digest {
|
|
45
|
+
const hash = sha256(image);
|
|
46
|
+
return new Digest(hash);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/// Creates a digest from multiple digests.
|
|
50
|
+
///
|
|
51
|
+
/// This is used to combine digests in the Merkle-like tree structure.
|
|
52
|
+
/// The digests are concatenated and then hashed.
|
|
53
|
+
///
|
|
54
|
+
/// @param digests - An array of digests to combine
|
|
55
|
+
/// @returns A new Digest instance representing the combined digests
|
|
56
|
+
///
|
|
57
|
+
/// @example
|
|
58
|
+
/// ```typescript
|
|
59
|
+
/// const digest1 = Digest.fromImage(data1);
|
|
60
|
+
/// const digest2 = Digest.fromImage(data2);
|
|
61
|
+
/// const combined = Digest.fromDigests([digest1, digest2]);
|
|
62
|
+
/// ```
|
|
63
|
+
static fromDigests(digests: Digest[]): Digest {
|
|
64
|
+
const totalLength = digests.length * 32;
|
|
65
|
+
const combined = new Uint8Array(totalLength);
|
|
66
|
+
let offset = 0;
|
|
67
|
+
for (const digest of digests) {
|
|
68
|
+
combined.set(digest.data(), offset);
|
|
69
|
+
offset += 32;
|
|
70
|
+
}
|
|
71
|
+
return Digest.fromImage(combined);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/// Returns the hexadecimal string representation of the digest.
|
|
75
|
+
///
|
|
76
|
+
/// @returns A 64-character hexadecimal string
|
|
77
|
+
///
|
|
78
|
+
/// @example
|
|
79
|
+
/// ```typescript
|
|
80
|
+
/// const digest = Digest.fromImage(data);
|
|
81
|
+
/// console.log(digest.hex()); // "5feceb66ffc86f38d952786c6d696c79c2dbc239dd4e91b46729d73a27fb57e9"
|
|
82
|
+
/// ```
|
|
83
|
+
hex(): string {
|
|
84
|
+
return Array.from(this.#data)
|
|
85
|
+
.map((b) => b.toString(16).padStart(2, "0"))
|
|
86
|
+
.join("");
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/// Returns an abbreviated hexadecimal representation for visual comparison.
|
|
90
|
+
///
|
|
91
|
+
/// Following Blockchain Commons conventions, this returns the first 7
|
|
92
|
+
/// hexadecimal digits of the digest, which provides sufficient entropy
|
|
93
|
+
/// for human visual comparison while being easy to read.
|
|
94
|
+
///
|
|
95
|
+
/// @returns A 7-character hexadecimal string
|
|
96
|
+
///
|
|
97
|
+
/// @example
|
|
98
|
+
/// ```typescript
|
|
99
|
+
/// const digest = Digest.fromImage(data);
|
|
100
|
+
/// console.log(digest.short()); // "5feceb6"
|
|
101
|
+
/// ```
|
|
102
|
+
short(): string {
|
|
103
|
+
return this.hex().substring(0, 7);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/// Creates a digest from a hexadecimal string.
|
|
107
|
+
///
|
|
108
|
+
/// @param hex - A 64-character hexadecimal string
|
|
109
|
+
/// @returns A new Digest instance
|
|
110
|
+
/// @throws {Error} If the hex string is not exactly 64 characters
|
|
111
|
+
///
|
|
112
|
+
/// @example
|
|
113
|
+
/// ```typescript
|
|
114
|
+
/// const digest = Digest.fromHex("5feceb66ffc86f38d952786c6d696c79c2dbc239dd4e91b46729d73a27fb57e9");
|
|
115
|
+
/// ```
|
|
116
|
+
static fromHex(hex: string): Digest {
|
|
117
|
+
if (hex.length !== 64) {
|
|
118
|
+
throw new Error(`Hex string must be exactly 64 characters, got ${hex.length}`);
|
|
119
|
+
}
|
|
120
|
+
const data = new Uint8Array(32);
|
|
121
|
+
for (let i = 0; i < 32; i++) {
|
|
122
|
+
data[i] = parseInt(hex.substring(i * 2, i * 2 + 2), 16);
|
|
123
|
+
}
|
|
124
|
+
return new Digest(data);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/// Checks if two digests are equal.
|
|
128
|
+
///
|
|
129
|
+
/// @param other - The other digest to compare with
|
|
130
|
+
/// @returns `true` if the digests are equal, `false` otherwise
|
|
131
|
+
equals(other: Digest): boolean {
|
|
132
|
+
if (this.#data.length !== other.#data.length) {
|
|
133
|
+
return false;
|
|
134
|
+
}
|
|
135
|
+
for (let i = 0; i < this.#data.length; i++) {
|
|
136
|
+
if (this.#data[i] !== other.#data[i]) {
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return true;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/// Returns a string representation of the digest (short form).
|
|
144
|
+
///
|
|
145
|
+
/// @returns The short hexadecimal representation
|
|
146
|
+
toString(): string {
|
|
147
|
+
return this.short();
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/// Creates a deep copy of the digest.
|
|
151
|
+
///
|
|
152
|
+
/// @returns A new Digest instance with the same data
|
|
153
|
+
clone(): Digest {
|
|
154
|
+
return new Digest(new Uint8Array(this.#data));
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/// Trait for types that can provide a digest.
|
|
159
|
+
///
|
|
160
|
+
/// This is equivalent to Rust's `DigestProvider` trait. Types that
|
|
161
|
+
/// implement this interface can be used in contexts where a digest
|
|
162
|
+
/// is needed for identity or integrity verification.
|
|
163
|
+
export interface DigestProvider {
|
|
164
|
+
/// Returns the digest of this object.
|
|
165
|
+
///
|
|
166
|
+
/// The digest uniquely identifies the semantic content of the object,
|
|
167
|
+
/// regardless of whether parts of it are elided, encrypted, or compressed.
|
|
168
|
+
digest(): Digest;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/// Helper function to create a digest from a string.
|
|
172
|
+
///
|
|
173
|
+
/// This is a convenience function for creating digests from text strings,
|
|
174
|
+
/// which are encoded as UTF-8 before hashing.
|
|
175
|
+
///
|
|
176
|
+
/// @param text - The text to hash
|
|
177
|
+
/// @returns A new Digest instance
|
|
178
|
+
///
|
|
179
|
+
/// @example
|
|
180
|
+
/// ```typescript
|
|
181
|
+
/// const digest = digestFromString("Hello, world!");
|
|
182
|
+
/// ```
|
|
183
|
+
export function digestFromString(text: string): Digest {
|
|
184
|
+
const encoder = new TextEncoder();
|
|
185
|
+
return Digest.fromImage(encoder.encode(text));
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/// Helper function to create a digest from a number.
|
|
189
|
+
///
|
|
190
|
+
/// The number is converted to a big-endian byte representation before hashing.
|
|
191
|
+
///
|
|
192
|
+
/// @param num - The number to hash
|
|
193
|
+
/// @returns A new Digest instance
|
|
194
|
+
///
|
|
195
|
+
/// @example
|
|
196
|
+
/// ```typescript
|
|
197
|
+
/// const digest = digestFromNumber(42);
|
|
198
|
+
/// ```
|
|
199
|
+
export function digestFromNumber(num: number): Digest {
|
|
200
|
+
const buffer = new ArrayBuffer(8);
|
|
201
|
+
const view = new DataView(buffer);
|
|
202
|
+
view.setFloat64(0, num, false); // big-endian
|
|
203
|
+
return Digest.fromImage(new Uint8Array(buffer));
|
|
204
|
+
}
|