@muhkoo/connect 0.14.0-alpha.1 → 0.14.0-alpha.3
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/package.json +1 -1
- package/dist/browser/blockEngine.worker.js +0 -349
- package/dist/browser/blockEngine.worker.js.map +0 -1
- package/dist/browser/index.js +0 -15716
- package/dist/browser/index.js.map +0 -1
- package/dist/connect.d.ts +0 -7518
- package/dist/server/index.js +0 -15917
- package/dist/server/index.js.map +0 -1
- package/dist/workers/index.js +0 -2670
- package/dist/workers/index.js.map +0 -1
package/dist/workers/index.js
DELETED
|
@@ -1,2670 +0,0 @@
|
|
|
1
|
-
const _isTty = (() => {
|
|
2
|
-
try {
|
|
3
|
-
const proc = globalThis.process;
|
|
4
|
-
return !!proc?.stdout?.isTTY;
|
|
5
|
-
}
|
|
6
|
-
catch {
|
|
7
|
-
return false;
|
|
8
|
-
}
|
|
9
|
-
})();
|
|
10
|
-
const _ansi = (code) => _isTty ? (s) => `\x1b[${code}m${s}\x1b[0m` : (s) => s;
|
|
11
|
-
const colors = {
|
|
12
|
-
gray: _ansi('90'),
|
|
13
|
-
red: _ansi('31'),
|
|
14
|
-
green: _ansi('32'),
|
|
15
|
-
yellow: _ansi('33'),
|
|
16
|
-
blue: _ansi('34'),
|
|
17
|
-
magenta: _ansi('35'),
|
|
18
|
-
cyan: _ansi('36'),
|
|
19
|
-
dim: _ansi('2'),
|
|
20
|
-
};
|
|
21
|
-
var LogLevel;
|
|
22
|
-
(function (LogLevel) {
|
|
23
|
-
LogLevel[LogLevel["VERBOSE"] = 0] = "VERBOSE";
|
|
24
|
-
LogLevel[LogLevel["DEBUG"] = 1] = "DEBUG";
|
|
25
|
-
LogLevel[LogLevel["INFO"] = 2] = "INFO";
|
|
26
|
-
LogLevel[LogLevel["SUCCESS"] = 3] = "SUCCESS";
|
|
27
|
-
LogLevel[LogLevel["WARN"] = 4] = "WARN";
|
|
28
|
-
LogLevel[LogLevel["ERROR"] = 5] = "ERROR";
|
|
29
|
-
})(LogLevel || (LogLevel = {}));
|
|
30
|
-
class Logger {
|
|
31
|
-
prefix;
|
|
32
|
-
logLevel;
|
|
33
|
-
console;
|
|
34
|
-
showCaller;
|
|
35
|
-
level = 'INFO';
|
|
36
|
-
constructor(prefix = 'CONNECT', LOGLEVEL = 'INFO', showCaller = false) {
|
|
37
|
-
this.prefix = colors.gray(`[${prefix}]`);
|
|
38
|
-
this.showCaller = showCaller;
|
|
39
|
-
let envLevel;
|
|
40
|
-
// Check environment: browser vs Node.js
|
|
41
|
-
if (typeof window !== 'undefined') {
|
|
42
|
-
// Browser environment
|
|
43
|
-
envLevel = (LOGLEVEL).toUpperCase();
|
|
44
|
-
}
|
|
45
|
-
else {
|
|
46
|
-
// Node.js environment - safely access process.env
|
|
47
|
-
const globalProcess = globalThis.process;
|
|
48
|
-
const nodeEnv = globalProcess?.env?.LOG_LEVEL;
|
|
49
|
-
envLevel = (nodeEnv || LOGLEVEL).toUpperCase();
|
|
50
|
-
}
|
|
51
|
-
this.level = envLevel;
|
|
52
|
-
this.logLevel = LogLevel[envLevel] ?? LogLevel.INFO;
|
|
53
|
-
this.console = globalThis.console;
|
|
54
|
-
}
|
|
55
|
-
shouldLog(level) {
|
|
56
|
-
return level >= this.logLevel;
|
|
57
|
-
}
|
|
58
|
-
/**
|
|
59
|
-
* Extract caller information from stack trace
|
|
60
|
-
*/
|
|
61
|
-
getCallerInfo() {
|
|
62
|
-
if (!this.showCaller)
|
|
63
|
-
return null;
|
|
64
|
-
try {
|
|
65
|
-
const stack = new Error().stack;
|
|
66
|
-
if (!stack)
|
|
67
|
-
return null;
|
|
68
|
-
// Split stack trace into lines
|
|
69
|
-
const lines = stack.split('\n');
|
|
70
|
-
// Skip first 3 lines (Error, getCallerInfo, the log method itself)
|
|
71
|
-
// The 4th line should be the actual caller
|
|
72
|
-
const callerLine = lines[3];
|
|
73
|
-
if (!callerLine)
|
|
74
|
-
return null;
|
|
75
|
-
// Parse stack trace line
|
|
76
|
-
// Format: " at ClassName.methodName (file:line:column)"
|
|
77
|
-
// or: " at file:line:column"
|
|
78
|
-
const match = callerLine.match(/at\s+(?:(.+?)\s+\()?(.+?):(\d+):(\d+)\)?/);
|
|
79
|
-
if (!match)
|
|
80
|
-
return null;
|
|
81
|
-
const [, functionName, filePath, line, column] = match;
|
|
82
|
-
// Extract just the filename from the full path
|
|
83
|
-
const file = filePath.split('/').pop()?.split('?')[0];
|
|
84
|
-
return {
|
|
85
|
-
file,
|
|
86
|
-
line: parseInt(line, 10),
|
|
87
|
-
column: parseInt(column, 10),
|
|
88
|
-
function: functionName || undefined,
|
|
89
|
-
};
|
|
90
|
-
}
|
|
91
|
-
catch (error) {
|
|
92
|
-
// If we can't parse the stack, just return null
|
|
93
|
-
return null;
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
/**
|
|
97
|
-
* Format caller info for display
|
|
98
|
-
*/
|
|
99
|
-
formatCallerInfo(info) {
|
|
100
|
-
if (!info)
|
|
101
|
-
return '';
|
|
102
|
-
const parts = [];
|
|
103
|
-
if (info.function) {
|
|
104
|
-
parts.push(colors.cyan(info.function));
|
|
105
|
-
}
|
|
106
|
-
if (info.file) {
|
|
107
|
-
const location = info.line ? `${info.file}:${info.line}` : info.file;
|
|
108
|
-
parts.push(colors.dim(`(${location})`));
|
|
109
|
-
}
|
|
110
|
-
return parts.length > 0 ? ' ' + parts.join(' ') : '';
|
|
111
|
-
}
|
|
112
|
-
setLevel(level) {
|
|
113
|
-
this.level = level.toUpperCase();
|
|
114
|
-
const envLevel = level.toUpperCase();
|
|
115
|
-
this.logLevel = LogLevel[envLevel] ?? LogLevel.INFO;
|
|
116
|
-
}
|
|
117
|
-
setShowCaller(show) {
|
|
118
|
-
this.showCaller = show;
|
|
119
|
-
}
|
|
120
|
-
// Logging methods with automatic caller detection
|
|
121
|
-
info(...args) {
|
|
122
|
-
if (this.shouldLog(LogLevel.INFO)) {
|
|
123
|
-
const caller = this.formatCallerInfo(this.getCallerInfo());
|
|
124
|
-
this.console.info(this.prefix, colors.blue('INFO') + caller, ...args);
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
log(...args) {
|
|
128
|
-
this.info(...args); // Alias for info
|
|
129
|
-
}
|
|
130
|
-
verbose(...args) {
|
|
131
|
-
if (this.shouldLog(LogLevel.VERBOSE)) {
|
|
132
|
-
const caller = this.formatCallerInfo(this.getCallerInfo());
|
|
133
|
-
this.console.log(this.prefix, colors.cyan('VERBOSE') + caller, ...args);
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
success(...args) {
|
|
137
|
-
if (this.shouldLog(LogLevel.SUCCESS)) {
|
|
138
|
-
const caller = this.formatCallerInfo(this.getCallerInfo());
|
|
139
|
-
this.console.debug(this.prefix, colors.green('SUCCESS') + caller, ...args);
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
warn(...args) {
|
|
143
|
-
if (this.shouldLog(LogLevel.WARN)) {
|
|
144
|
-
const caller = this.formatCallerInfo(this.getCallerInfo());
|
|
145
|
-
this.console.warn(this.prefix, colors.yellow('WARN') + caller, ...args);
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
error(...args) {
|
|
149
|
-
if (this.shouldLog(LogLevel.ERROR)) {
|
|
150
|
-
const caller = this.formatCallerInfo(this.getCallerInfo());
|
|
151
|
-
this.console.error(this.prefix, colors.red('ERROR') + caller, ...args);
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
debug(...args) {
|
|
155
|
-
if (this.shouldLog(LogLevel.DEBUG)) {
|
|
156
|
-
const caller = this.formatCallerInfo(this.getCallerInfo());
|
|
157
|
-
this.console.debug(this.prefix, colors.magenta('DEBUG') + caller, ...args);
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
// Rest of the methods remain unchanged
|
|
161
|
-
assert(condition, ...data) {
|
|
162
|
-
this.console.assert(condition, ...data);
|
|
163
|
-
}
|
|
164
|
-
clear() {
|
|
165
|
-
this.console.clear();
|
|
166
|
-
}
|
|
167
|
-
count(label) {
|
|
168
|
-
this.console.count(label);
|
|
169
|
-
}
|
|
170
|
-
countReset(label) {
|
|
171
|
-
this.console.countReset(label);
|
|
172
|
-
}
|
|
173
|
-
dir(item, options) {
|
|
174
|
-
this.console.dir(item, options);
|
|
175
|
-
}
|
|
176
|
-
dirxml(...data) {
|
|
177
|
-
this.console.dirxml(...data);
|
|
178
|
-
}
|
|
179
|
-
group(...data) {
|
|
180
|
-
this.console.group(...data);
|
|
181
|
-
}
|
|
182
|
-
groupCollapsed(...data) {
|
|
183
|
-
this.console.groupCollapsed(...data);
|
|
184
|
-
}
|
|
185
|
-
groupEnd() {
|
|
186
|
-
this.console.groupEnd();
|
|
187
|
-
}
|
|
188
|
-
table(tabularData, properties) {
|
|
189
|
-
this.console.table(tabularData, properties);
|
|
190
|
-
}
|
|
191
|
-
time(label) {
|
|
192
|
-
this.console.time(label);
|
|
193
|
-
}
|
|
194
|
-
timeEnd(label) {
|
|
195
|
-
this.console.timeEnd(label);
|
|
196
|
-
}
|
|
197
|
-
timeLog(label, ...data) {
|
|
198
|
-
this.console.timeLog(label, ...data);
|
|
199
|
-
}
|
|
200
|
-
trace(...data) {
|
|
201
|
-
this.console.trace(...data);
|
|
202
|
-
}
|
|
203
|
-
// Note: Console property removed as it's Node.js-specific and not needed
|
|
204
|
-
profile(label) {
|
|
205
|
-
if ('profile' in this.console) {
|
|
206
|
-
this.console.profile(label);
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
profileEnd(label) {
|
|
210
|
-
if ('profileEnd' in this.console) {
|
|
211
|
-
this.console.profileEnd(label);
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
timeStamp(label) {
|
|
215
|
-
if ('timeStamp' in this.console) {
|
|
216
|
-
this.console.timeStamp(label);
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
// Export default instance for general use
|
|
221
|
-
const log = new Logger('CONNECT', 'INFO', false);
|
|
222
|
-
|
|
223
|
-
/**
|
|
224
|
-
* Byte conversion helpers usable in both Node and the browser.
|
|
225
|
-
*
|
|
226
|
-
* Replaces Node's `Buffer` API for the small set of operations we actually
|
|
227
|
-
* need in the crypto layer: hex/base64/base64url encode + decode, and array
|
|
228
|
-
* concatenation. Lets `DoubleRatchet`, `KeyStore`, etc. compile against
|
|
229
|
-
* platforms without `Buffer` (browsers, CF Workers).
|
|
230
|
-
*/
|
|
231
|
-
const HEX_CHARS = "0123456789abcdef";
|
|
232
|
-
/** Encode a byte array as a lowercase hex string. */
|
|
233
|
-
function toHex(bytes) {
|
|
234
|
-
let out = "";
|
|
235
|
-
for (let i = 0; i < bytes.length; i++) {
|
|
236
|
-
const b = bytes[i];
|
|
237
|
-
out += HEX_CHARS[b >> 4] + HEX_CHARS[b & 0x0f];
|
|
238
|
-
}
|
|
239
|
-
return out;
|
|
240
|
-
}
|
|
241
|
-
/** Decode a hex string into a byte array. Accepts upper/lowercase; rejects odd-length input. */
|
|
242
|
-
function fromHex(hex) {
|
|
243
|
-
if (hex.length % 2 !== 0) {
|
|
244
|
-
throw new Error(`fromHex: input length must be even (got ${hex.length})`);
|
|
245
|
-
}
|
|
246
|
-
const ab = new ArrayBuffer(hex.length / 2);
|
|
247
|
-
const out = new Uint8Array(ab);
|
|
248
|
-
for (let i = 0; i < out.length; i++) {
|
|
249
|
-
const byte = parseInt(hex.substring(i * 2, i * 2 + 2), 16);
|
|
250
|
-
if (Number.isNaN(byte)) {
|
|
251
|
-
throw new Error(`fromHex: invalid hex character at offset ${i * 2}`);
|
|
252
|
-
}
|
|
253
|
-
out[i] = byte;
|
|
254
|
-
}
|
|
255
|
-
return out;
|
|
256
|
-
}
|
|
257
|
-
/** Encode a byte array as a standard base64 string. */
|
|
258
|
-
function toBase64(bytes) {
|
|
259
|
-
let binary = "";
|
|
260
|
-
for (let i = 0; i < bytes.length; i++) {
|
|
261
|
-
binary += String.fromCharCode(bytes[i]);
|
|
262
|
-
}
|
|
263
|
-
return btoa(binary);
|
|
264
|
-
}
|
|
265
|
-
/** Decode a standard base64 string to bytes. */
|
|
266
|
-
function fromBase64(b64) {
|
|
267
|
-
const binary = atob(b64);
|
|
268
|
-
const ab = new ArrayBuffer(binary.length);
|
|
269
|
-
const out = new Uint8Array(ab);
|
|
270
|
-
for (let i = 0; i < binary.length; i++)
|
|
271
|
-
out[i] = binary.charCodeAt(i);
|
|
272
|
-
return out;
|
|
273
|
-
}
|
|
274
|
-
/** Decode a base64url string (with or without padding) to bytes. */
|
|
275
|
-
function fromBase64Url(b64url) {
|
|
276
|
-
const padded = b64url.replace(/-/g, "+").replace(/_/g, "/") +
|
|
277
|
-
"=".repeat((4 - (b64url.length % 4)) % 4);
|
|
278
|
-
return fromBase64(padded);
|
|
279
|
-
}
|
|
280
|
-
/** Concatenate any number of byte arrays into a single Uint8Array. */
|
|
281
|
-
function concatBytes(...arrays) {
|
|
282
|
-
let total = 0;
|
|
283
|
-
for (const a of arrays)
|
|
284
|
-
total += a.byteLength;
|
|
285
|
-
const ab = new ArrayBuffer(total);
|
|
286
|
-
const out = new Uint8Array(ab);
|
|
287
|
-
let offset = 0;
|
|
288
|
-
for (const a of arrays) {
|
|
289
|
-
const view = a instanceof Uint8Array ? a : new Uint8Array(a);
|
|
290
|
-
out.set(view, offset);
|
|
291
|
-
offset += view.length;
|
|
292
|
-
}
|
|
293
|
-
return out;
|
|
294
|
-
}
|
|
295
|
-
/** Encode a UTF-8 string as bytes. Thin wrapper around TextEncoder for symmetry. */
|
|
296
|
-
function utf8Encode(s) {
|
|
297
|
-
// TextEncoder.encode returns Uint8Array<ArrayBufferLike>; widen to the
|
|
298
|
-
// ArrayBuffer variant explicitly so WebCrypto's BufferSource type accepts it.
|
|
299
|
-
const u8 = new TextEncoder().encode(s);
|
|
300
|
-
const ab = new ArrayBuffer(u8.byteLength);
|
|
301
|
-
const out = new Uint8Array(ab);
|
|
302
|
-
out.set(u8);
|
|
303
|
-
return out;
|
|
304
|
-
}
|
|
305
|
-
/** Decode UTF-8 bytes to a string. */
|
|
306
|
-
function utf8Decode(bytes) {
|
|
307
|
-
return new TextDecoder().decode(bytes);
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
const base58Alphabet = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
|
|
311
|
-
//0ba25dcb0db07dbac27435857ab55150d2a2e6c8f73a969c74b7659c7a8dd3a3
|
|
312
|
-
/**
|
|
313
|
-
* @public
|
|
314
|
-
* @remarks Asserts the basic type of a variable
|
|
315
|
-
* @param value - The value to check
|
|
316
|
-
* @param type - The expected type
|
|
317
|
-
*
|
|
318
|
-
* @throws Error - If the type of the value does not match the expected type
|
|
319
|
-
*
|
|
320
|
-
*/
|
|
321
|
-
function assertType(value, typeOrConstructors) {
|
|
322
|
-
{
|
|
323
|
-
// Single primitive type
|
|
324
|
-
if (typeof value !== typeOrConstructors) {
|
|
325
|
-
throw new Error(`Expected type '${typeOrConstructors}', received '${typeof value}'`);
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
/**
|
|
330
|
-
* @public
|
|
331
|
-
* @remarks Encodes a string to Base58
|
|
332
|
-
* @param input - the data to encode, must be a cloneable object
|
|
333
|
-
* @returns string
|
|
334
|
-
*/
|
|
335
|
-
function base58Encode(input) {
|
|
336
|
-
const bytes = typeof input === "string"
|
|
337
|
-
? new TextEncoder().encode(input)
|
|
338
|
-
: new Uint8Array(input);
|
|
339
|
-
let num = BigInt(0);
|
|
340
|
-
for (const byte of bytes) {
|
|
341
|
-
num = (num << BigInt(8)) + BigInt(byte);
|
|
342
|
-
}
|
|
343
|
-
let result = "";
|
|
344
|
-
while (num > 0) {
|
|
345
|
-
const remainder = Number(num % BigInt(58));
|
|
346
|
-
num = num / BigInt(58);
|
|
347
|
-
result = base58Alphabet[remainder] + result;
|
|
348
|
-
}
|
|
349
|
-
// Preserve leading zero bytes as "1" in Base58
|
|
350
|
-
for (const byte of bytes) {
|
|
351
|
-
if (byte === 0)
|
|
352
|
-
result = "1" + result;
|
|
353
|
-
else
|
|
354
|
-
break;
|
|
355
|
-
}
|
|
356
|
-
return result;
|
|
357
|
-
}
|
|
358
|
-
/**
|
|
359
|
-
* @public
|
|
360
|
-
* @remarks Decodes a Base58 string to a Uint8Array
|
|
361
|
-
* @param input - the Base58 string to decode
|
|
362
|
-
* @returns Uint8Array
|
|
363
|
-
*/
|
|
364
|
-
// Function to decode a Base58 string to ArrayBuffer
|
|
365
|
-
function base58Decode(input) {
|
|
366
|
-
try {
|
|
367
|
-
const base58Map = new Map(base58Alphabet.split("").map((char, index) => [char, index]));
|
|
368
|
-
let num = BigInt(0);
|
|
369
|
-
for (const char of input) {
|
|
370
|
-
const value = base58Map.get(char);
|
|
371
|
-
if (value === undefined)
|
|
372
|
-
throw new Error(`Invalid Base58 character: ${char}`);
|
|
373
|
-
num = num * BigInt(58) + BigInt(value);
|
|
374
|
-
}
|
|
375
|
-
const byteArray = [];
|
|
376
|
-
while (num > 0) {
|
|
377
|
-
byteArray.push(Number(num % BigInt(256)));
|
|
378
|
-
num = num / BigInt(256);
|
|
379
|
-
}
|
|
380
|
-
byteArray.reverse();
|
|
381
|
-
// Restore leading zero bytes from Base58 "1"
|
|
382
|
-
const leadingZeros = Array.from(input).filter((char) => char === "1").length;
|
|
383
|
-
return new Uint8Array([...Array(leadingZeros).fill(0), ...byteArray]).buffer;
|
|
384
|
-
}
|
|
385
|
-
catch (e) {
|
|
386
|
-
log.error(e);
|
|
387
|
-
throw new Error("Invalid input");
|
|
388
|
-
}
|
|
389
|
-
}
|
|
390
|
-
/**
|
|
391
|
-
* Simple checksum function using djb2 hash algorithm
|
|
392
|
-
* Works in both browser and Node.js environments
|
|
393
|
-
*/
|
|
394
|
-
function simpleChecksum(str) {
|
|
395
|
-
let hash = 5381;
|
|
396
|
-
for (let i = 0; i < str.length; i++) {
|
|
397
|
-
hash = ((hash << 5) + hash) + str.charCodeAt(i); // hash * 33 + c
|
|
398
|
-
}
|
|
399
|
-
return (hash >>> 0).toString(16); // Convert to unsigned 32-bit hex
|
|
400
|
-
}
|
|
401
|
-
/**
|
|
402
|
-
* @public
|
|
403
|
-
* @remarks Generates a checksum for a given input
|
|
404
|
-
* @param input - The input to generate a checksum for as a string
|
|
405
|
-
* @returns string
|
|
406
|
-
*/
|
|
407
|
-
function generateChecksum(input) {
|
|
408
|
-
assertType(input, "string");
|
|
409
|
-
return simpleChecksum(input);
|
|
410
|
-
}
|
|
411
|
-
/**
|
|
412
|
-
* @public
|
|
413
|
-
* @remarks Verifies a checksum for a given input
|
|
414
|
-
* @param input - The input to verify as a string
|
|
415
|
-
* @param checksum - The checksum to verify as a string
|
|
416
|
-
* @returns boolean
|
|
417
|
-
*/
|
|
418
|
-
function verifyChecksum(input, checksum) {
|
|
419
|
-
assertType(input, "string");
|
|
420
|
-
assertType(checksum, "string");
|
|
421
|
-
return !!(simpleChecksum(input) === checksum);
|
|
422
|
-
}
|
|
423
|
-
/**
|
|
424
|
-
* @internal
|
|
425
|
-
* @remarks Generates a random UUID for a given packet
|
|
426
|
-
* @returns string
|
|
427
|
-
*/
|
|
428
|
-
function _packetId() {
|
|
429
|
-
return "PKT" + generateId();
|
|
430
|
-
}
|
|
431
|
-
/**
|
|
432
|
-
* @internal
|
|
433
|
-
* @remarks Generates a random UUID for a message
|
|
434
|
-
* @returns string
|
|
435
|
-
*/
|
|
436
|
-
function _messageId() {
|
|
437
|
-
return "MSG" + generateId();
|
|
438
|
-
}
|
|
439
|
-
/**
|
|
440
|
-
* @public
|
|
441
|
-
* @remarks Generates a random UUID and base58 encodes it
|
|
442
|
-
* @returns string
|
|
443
|
-
*/
|
|
444
|
-
function generateId() {
|
|
445
|
-
// Web-standard UUID generator (available in every modern browser and
|
|
446
|
-
// Node 14+). Replaces the npm `uuid` dep so the browser bundle has one
|
|
447
|
-
// fewer external import to resolve.
|
|
448
|
-
return base58Encode(crypto.randomUUID());
|
|
449
|
-
}
|
|
450
|
-
/**
|
|
451
|
-
* @public
|
|
452
|
-
* @remarks Serializes data to a base58 encoded string
|
|
453
|
-
* @param data - The data to serialize
|
|
454
|
-
* @typeParam T - The type of the data to serialize
|
|
455
|
-
* @returns base58 encoded string
|
|
456
|
-
*/
|
|
457
|
-
function serialize(data) {
|
|
458
|
-
const arrayBuffer = structuredCloneToArrayBuffer(data);
|
|
459
|
-
return base58Encode(arrayBuffer);
|
|
460
|
-
}
|
|
461
|
-
/**
|
|
462
|
-
* @public
|
|
463
|
-
* @remarks Deserializes a base58 encoded string to data
|
|
464
|
-
* @param base58String - The base58 encoded string to deserialize
|
|
465
|
-
* @returns Deserialized data
|
|
466
|
-
*/
|
|
467
|
-
function deserialize(base58String) {
|
|
468
|
-
const arrayBuffer = base58Decode(base58String);
|
|
469
|
-
return deserializeWithStructuredClone(arrayBufferToData(arrayBuffer));
|
|
470
|
-
}
|
|
471
|
-
function deserializeWithStructuredClone(data) {
|
|
472
|
-
const deserializeHelper = (item) => {
|
|
473
|
-
if (item === null || typeof item !== 'object') {
|
|
474
|
-
return item; // Handle primitives directly
|
|
475
|
-
}
|
|
476
|
-
if (item.__type === "Symbol") {
|
|
477
|
-
return Symbol.for(item.value) || Symbol(item.value);
|
|
478
|
-
}
|
|
479
|
-
if (item.__type === 'Date') {
|
|
480
|
-
return new Date(item.value); // Rehydrate Date
|
|
481
|
-
}
|
|
482
|
-
if (item.__type === 'Map') {
|
|
483
|
-
return new Map(item.value); // Rehydrate Map
|
|
484
|
-
}
|
|
485
|
-
if (item.__type === 'Set') {
|
|
486
|
-
return new Set(item.value); // Rehydrate Set
|
|
487
|
-
}
|
|
488
|
-
if (Array.isArray(item)) {
|
|
489
|
-
return item.map(deserializeHelper); // Rehydrate Array recursively
|
|
490
|
-
}
|
|
491
|
-
// Generic object case
|
|
492
|
-
const deserializedObject = {};
|
|
493
|
-
for (const key of Object.keys(item)) {
|
|
494
|
-
deserializedObject[key] = deserializeHelper(item[key]);
|
|
495
|
-
}
|
|
496
|
-
return deserializedObject;
|
|
497
|
-
};
|
|
498
|
-
return deserializeHelper(data);
|
|
499
|
-
}
|
|
500
|
-
function structuredCloneToArrayBuffer(data) {
|
|
501
|
-
// Clone and transform the data for serialization
|
|
502
|
-
const serializeHelper = (item) => {
|
|
503
|
-
if (item === null || typeof item !== "object")
|
|
504
|
-
return item;
|
|
505
|
-
if (typeof item === "symbol") {
|
|
506
|
-
const key = Symbol.keyFor(item) || item.description || "";
|
|
507
|
-
return { __type: "Symbol", value: key };
|
|
508
|
-
}
|
|
509
|
-
if (item.constructor === Date)
|
|
510
|
-
return { __type: "Date", value: item.toISOString() };
|
|
511
|
-
if (item.constructor === Map)
|
|
512
|
-
return { __type: "Map", value: Array.from(item.entries()) };
|
|
513
|
-
if (item.constructor === Set)
|
|
514
|
-
return { __type: "Set", value: Array.from(item) };
|
|
515
|
-
if (Array.isArray(item))
|
|
516
|
-
return item.map(serializeHelper);
|
|
517
|
-
const serializedObject = {};
|
|
518
|
-
for (const key of Object.keys(item)) {
|
|
519
|
-
serializedObject[key] = serializeHelper(item[key]);
|
|
520
|
-
}
|
|
521
|
-
return serializedObject;
|
|
522
|
-
};
|
|
523
|
-
// Serialize the structured data into a JSON string
|
|
524
|
-
const jsonString = JSON.stringify(serializeHelper(data));
|
|
525
|
-
return new TextEncoder().encode(jsonString).buffer;
|
|
526
|
-
}
|
|
527
|
-
function arrayBufferToData(arrayBuffer) {
|
|
528
|
-
let jsonString = new TextDecoder().decode(new Uint8Array(arrayBuffer));
|
|
529
|
-
jsonString = jsonString.replace(/^\0+/, '');
|
|
530
|
-
return JSON.parse(jsonString);
|
|
531
|
-
}
|
|
532
|
-
/**
|
|
533
|
-
* Converts bytes into a more human-friendly format (e.g., KB, MB, GB).
|
|
534
|
-
* @internal
|
|
535
|
-
* @param bytes - The number of bytes to convert.
|
|
536
|
-
* @param decimals - The number of decimals to display (default is 2).
|
|
537
|
-
* @returns A formatted string representing the size in appropriate units.
|
|
538
|
-
*/
|
|
539
|
-
function _formatBytes(bytes, decimals = 2) {
|
|
540
|
-
if (bytes === 0)
|
|
541
|
-
return "0 Bytes";
|
|
542
|
-
const k = 1024; // 1 KB = 1024 Bytes
|
|
543
|
-
const sizes = ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
|
|
544
|
-
const i = Math.floor(Math.log(bytes) / Math.log(k)); // Determines the index for size
|
|
545
|
-
const sizeInUnit = bytes / Math.pow(k, i); // Converts bytes to appropriate unit
|
|
546
|
-
return `${sizeInUnit.toFixed(decimals)} ${sizes[i]}`;
|
|
547
|
-
}
|
|
548
|
-
|
|
549
|
-
/**
|
|
550
|
-
* @public
|
|
551
|
-
* @remarks
|
|
552
|
-
* The message class is a simple class that represents a message object.
|
|
553
|
-
* @sealed
|
|
554
|
-
*/
|
|
555
|
-
class Message {
|
|
556
|
-
id = _messageId();
|
|
557
|
-
timestamp = Date.now();
|
|
558
|
-
status = "pending";
|
|
559
|
-
checksum;
|
|
560
|
-
_body;
|
|
561
|
-
constructor(msg) {
|
|
562
|
-
if (typeof msg === 'object' && msg.hasOwnProperty('body')) {
|
|
563
|
-
this.id = msg.id || this.id;
|
|
564
|
-
this.timestamp = msg.timestamp || this.timestamp;
|
|
565
|
-
this.status = msg.status || this.status;
|
|
566
|
-
this.checksum = msg.checksum || this.checksum;
|
|
567
|
-
this.body = msg.body;
|
|
568
|
-
}
|
|
569
|
-
else {
|
|
570
|
-
this.body = msg;
|
|
571
|
-
}
|
|
572
|
-
this.validatebodySize();
|
|
573
|
-
}
|
|
574
|
-
get body() {
|
|
575
|
-
return deserialize(this._body);
|
|
576
|
-
}
|
|
577
|
-
set body(value) {
|
|
578
|
-
this._body = serialize(value);
|
|
579
|
-
if (!this.checksum) {
|
|
580
|
-
this.checksum = generateChecksum(this._body);
|
|
581
|
-
}
|
|
582
|
-
}
|
|
583
|
-
verifyChecksum = () => {
|
|
584
|
-
if (!this.checksum)
|
|
585
|
-
throw new Error("No message checksum");
|
|
586
|
-
const isValid = verifyChecksum(this._body, this.checksum);
|
|
587
|
-
if (!isValid)
|
|
588
|
-
throw new Error("Invalid message checksum");
|
|
589
|
-
};
|
|
590
|
-
validatebodySize(maxSize = 1024 * 1024 * 3) {
|
|
591
|
-
const sizeInBytes = new TextEncoder().encode(this._body).length;
|
|
592
|
-
if (sizeInBytes > maxSize) {
|
|
593
|
-
throw new Error(`body size exceeds the limit of ${_formatBytes(maxSize)}.`);
|
|
594
|
-
}
|
|
595
|
-
}
|
|
596
|
-
serialize = () => {
|
|
597
|
-
return JSON.stringify({
|
|
598
|
-
id: this.id,
|
|
599
|
-
timestamp: this.timestamp,
|
|
600
|
-
body: this.body,
|
|
601
|
-
status: this.status,
|
|
602
|
-
checksum: this.checksum,
|
|
603
|
-
});
|
|
604
|
-
};
|
|
605
|
-
static deserialize = (data) => {
|
|
606
|
-
if (typeof data !== 'string') {
|
|
607
|
-
log.warn('Message.deserialize: data is not a string', data);
|
|
608
|
-
}
|
|
609
|
-
const msg = JSON.parse(data);
|
|
610
|
-
return new Message(msg);
|
|
611
|
-
};
|
|
612
|
-
}
|
|
613
|
-
|
|
614
|
-
/**
|
|
615
|
-
* @public
|
|
616
|
-
* @remarks
|
|
617
|
-
* This class represents a message object that can be sent over the network
|
|
618
|
-
* @sealed
|
|
619
|
-
*/
|
|
620
|
-
class Packet {
|
|
621
|
-
id;
|
|
622
|
-
subject;
|
|
623
|
-
source;
|
|
624
|
-
target;
|
|
625
|
-
headers; // Optional: Additional headers for the packet
|
|
626
|
-
message; // By making the message optional, we can send packets without messages. Essentially performing a GET request or other bodyless requests
|
|
627
|
-
timestamp;
|
|
628
|
-
ttl; // Optional: Time-to-live in milliseconds
|
|
629
|
-
signature; // Optional: Cryptographic signature for verification
|
|
630
|
-
constructor({ id, subject, source, target, headers, message, ttl, signature, timestamp }) {
|
|
631
|
-
this.id = id || _packetId(); // Ensure the packet has a unique ID
|
|
632
|
-
this.subject = subject;
|
|
633
|
-
this.source = source;
|
|
634
|
-
this.target = target;
|
|
635
|
-
this.headers = headers || {};
|
|
636
|
-
this.message = message;
|
|
637
|
-
this.timestamp = Date.now(); // Automatically track packet creation time
|
|
638
|
-
this.ttl = ttl;
|
|
639
|
-
this.signature = signature;
|
|
640
|
-
this.timestamp = timestamp || Date.now();
|
|
641
|
-
}
|
|
642
|
-
// Serialize the packet for network transmission
|
|
643
|
-
serialize() {
|
|
644
|
-
return JSON.stringify({
|
|
645
|
-
id: this.id,
|
|
646
|
-
source: this.source,
|
|
647
|
-
subject: this.subject,
|
|
648
|
-
target: this.target,
|
|
649
|
-
headers: this.headers,
|
|
650
|
-
message: this.message && this.message.serialize(),
|
|
651
|
-
timestamp: this.timestamp,
|
|
652
|
-
ttl: this.ttl,
|
|
653
|
-
signature: this.signature,
|
|
654
|
-
});
|
|
655
|
-
}
|
|
656
|
-
// Deserialize a packet from a string
|
|
657
|
-
static deserialize(data) {
|
|
658
|
-
const obj = JSON.parse(data);
|
|
659
|
-
return new Packet({
|
|
660
|
-
id: obj.id,
|
|
661
|
-
subject: obj.subject,
|
|
662
|
-
source: obj.source,
|
|
663
|
-
target: obj.target,
|
|
664
|
-
headers: obj.headers,
|
|
665
|
-
message: obj.message && Message.deserialize(obj.message),
|
|
666
|
-
timestamp: obj.timestamp,
|
|
667
|
-
ttl: obj.ttl,
|
|
668
|
-
signature: obj.signature,
|
|
669
|
-
});
|
|
670
|
-
}
|
|
671
|
-
// Verify TTL: Checks if the packet is still valid based on TTL
|
|
672
|
-
isExpired() {
|
|
673
|
-
if (!this.ttl)
|
|
674
|
-
return false; // No TTL means no expiration
|
|
675
|
-
return Date.now() > this.timestamp + this.ttl;
|
|
676
|
-
}
|
|
677
|
-
}
|
|
678
|
-
|
|
679
|
-
function SerializeMessage(target, propertyKey, descriptor) {
|
|
680
|
-
const originalMethod = descriptor.value;
|
|
681
|
-
descriptor.value = function (...args) {
|
|
682
|
-
const [subject, message] = args;
|
|
683
|
-
if (typeof message !== "string") {
|
|
684
|
-
throw new Error("Message must be a string");
|
|
685
|
-
}
|
|
686
|
-
const serializedMessage = new Message({
|
|
687
|
-
subject,
|
|
688
|
-
payload: message,
|
|
689
|
-
status: "pending",
|
|
690
|
-
headers: new Map([["Content-Type", "application/json"]]),
|
|
691
|
-
}).serialize();
|
|
692
|
-
return originalMethod.apply(this, [subject, serializedMessage]);
|
|
693
|
-
};
|
|
694
|
-
return descriptor;
|
|
695
|
-
}
|
|
696
|
-
|
|
697
|
-
// Re-export Message and its types
|
|
698
|
-
const decorators = {
|
|
699
|
-
SerializeMessage
|
|
700
|
-
};
|
|
701
|
-
|
|
702
|
-
/**
|
|
703
|
-
* Shared zero-knowledge types and circuit constants.
|
|
704
|
-
*
|
|
705
|
-
* Used by both the browser/server builds (where snarkjs handles proof generation
|
|
706
|
-
* and verification) and the workers build (where verification runs through
|
|
707
|
-
* `bn128.wasm` via `src/workers/groth16-verifier.ts`). Keeping these here ensures
|
|
708
|
-
* proof shapes and verification keys stay identical across runtimes.
|
|
709
|
-
*/
|
|
710
|
-
/**
|
|
711
|
-
* Verification key for the `preimagePoK` circuit (Poseidon-commitment
|
|
712
|
-
* proof-of-knowledge with three public signals: commitment, nonce, ecdsaPubHash).
|
|
713
|
-
*
|
|
714
|
-
* Pinned here so worker-side and SDK-side verification agree on the key being
|
|
715
|
-
* checked. If the circuit is recompiled, regenerate this constant and
|
|
716
|
-
* `public/circuits/build/preimagePoK_verification_key.json` together.
|
|
717
|
-
*/
|
|
718
|
-
const PREIMAGE_POK_VERIFICATION_KEY = {
|
|
719
|
-
protocol: "groth16",
|
|
720
|
-
curve: "bn128",
|
|
721
|
-
nPublic: 3,
|
|
722
|
-
vk_alpha_1: [
|
|
723
|
-
"20491192805390485299153009773594534940189261866228447918068658471970481763042",
|
|
724
|
-
"9383485363053290200918347156157836566562967994039712273449902621266178545958",
|
|
725
|
-
"1"
|
|
726
|
-
],
|
|
727
|
-
vk_beta_2: [
|
|
728
|
-
[
|
|
729
|
-
"6375614351688725206403948262868962793625744043794305715222011528459656738731",
|
|
730
|
-
"4252822878758300859123897981450591353533073413197771768651442665752259397132"
|
|
731
|
-
],
|
|
732
|
-
[
|
|
733
|
-
"10505242626370262277552901082094356697409835680220590971873171140371331206856",
|
|
734
|
-
"21847035105528745403288232691147584728191162732299865338377159692350059136679"
|
|
735
|
-
],
|
|
736
|
-
[
|
|
737
|
-
"1",
|
|
738
|
-
"0"
|
|
739
|
-
]
|
|
740
|
-
],
|
|
741
|
-
vk_gamma_2: [
|
|
742
|
-
[
|
|
743
|
-
"10857046999023057135944570762232829481370756359578518086990519993285655852781",
|
|
744
|
-
"11559732032986387107991004021392285783925812861821192530917403151452391805634"
|
|
745
|
-
],
|
|
746
|
-
[
|
|
747
|
-
"8495653923123431417604973247489272438418190587263600148770280649306958101930",
|
|
748
|
-
"4082367875863433681332203403145435568316851327593401208105741076214120093531"
|
|
749
|
-
],
|
|
750
|
-
[
|
|
751
|
-
"1",
|
|
752
|
-
"0"
|
|
753
|
-
]
|
|
754
|
-
],
|
|
755
|
-
vk_delta_2: [
|
|
756
|
-
[
|
|
757
|
-
"16843627858994773805056502117979518680360297888097507527057721641174626133429",
|
|
758
|
-
"2669618170444825402403730985182974839894668798317345855172970345856260721653"
|
|
759
|
-
],
|
|
760
|
-
[
|
|
761
|
-
"20564952487570762020467138133408075499830271526522626813107251678961587215295",
|
|
762
|
-
"9630342855454833575580303045534431010412631371159617591163132980579520487965"
|
|
763
|
-
],
|
|
764
|
-
[
|
|
765
|
-
"1",
|
|
766
|
-
"0"
|
|
767
|
-
]
|
|
768
|
-
],
|
|
769
|
-
vk_alphabeta_12: [
|
|
770
|
-
[
|
|
771
|
-
[
|
|
772
|
-
"2029413683389138792403550203267699914886160938906632433982220835551125967885",
|
|
773
|
-
"21072700047562757817161031222997517981543347628379360635925549008442030252106"
|
|
774
|
-
],
|
|
775
|
-
[
|
|
776
|
-
"5940354580057074848093997050200682056184807770593307860589430076672439820312",
|
|
777
|
-
"12156638873931618554171829126792193045421052652279363021382169897324752428276"
|
|
778
|
-
],
|
|
779
|
-
[
|
|
780
|
-
"7898200236362823042373859371574133993780991612861777490112507062703164551277",
|
|
781
|
-
"7074218545237549455313236346927434013100842096812539264420499035217050630853"
|
|
782
|
-
]
|
|
783
|
-
],
|
|
784
|
-
[
|
|
785
|
-
[
|
|
786
|
-
"7077479683546002997211712695946002074877511277312570035766170199895071832130",
|
|
787
|
-
"10093483419865920389913245021038182291233451549023025229112148274109565435465"
|
|
788
|
-
],
|
|
789
|
-
[
|
|
790
|
-
"4595479056700221319381530156280926371456704509942304414423590385166031118820",
|
|
791
|
-
"19831328484489333784475432780421641293929726139240675179672856274388269393268"
|
|
792
|
-
],
|
|
793
|
-
[
|
|
794
|
-
"11934129596455521040620786944827826205713621633706285934057045369193958244500",
|
|
795
|
-
"8037395052364110730298837004334506829870972346962140206007064471173334027475"
|
|
796
|
-
]
|
|
797
|
-
]
|
|
798
|
-
],
|
|
799
|
-
IC: [
|
|
800
|
-
[
|
|
801
|
-
"6593633352362890744195366164011945207337919238085164401989523182031213441232",
|
|
802
|
-
"21183283244721822514187218730411278389501771108619619957207607994811554523781",
|
|
803
|
-
"1"
|
|
804
|
-
],
|
|
805
|
-
[
|
|
806
|
-
"19317830753800014286972931403860527733342849870141075528271937285218206220756",
|
|
807
|
-
"14626265811834596630857393417326981325583938311796888424299048456918853093257",
|
|
808
|
-
"1"
|
|
809
|
-
],
|
|
810
|
-
[
|
|
811
|
-
"7942727853353225551121019923926808347232536417992238353855978019969636961405",
|
|
812
|
-
"8824649763391398686420260814761862276544573369118345976741973565095405175062",
|
|
813
|
-
"1"
|
|
814
|
-
],
|
|
815
|
-
[
|
|
816
|
-
"8369441262422248167284752361555580367206817200973928868895308479466335503315",
|
|
817
|
-
"7836653245051994305208746627300011506099851195782496031197976259815728652585",
|
|
818
|
-
"1"
|
|
819
|
-
]
|
|
820
|
-
]
|
|
821
|
-
};
|
|
822
|
-
|
|
823
|
-
/**
|
|
824
|
-
* WebCrypto subtle-interface resolver.
|
|
825
|
-
*
|
|
826
|
-
* Available natively in every runtime this SDK targets (modern browsers,
|
|
827
|
-
* Node 16+, CF Workers). Centralized here so the runtime check + error
|
|
828
|
-
* message are consistent across every primitive — and so a future polyfill
|
|
829
|
-
* shim has one place to plug in.
|
|
830
|
-
*/
|
|
831
|
-
function getSubtle() {
|
|
832
|
-
const s = globalThis.crypto?.subtle;
|
|
833
|
-
if (!s) {
|
|
834
|
-
throw new Error("@muhkoo/connect crypto: `globalThis.crypto.subtle` is unavailable. " +
|
|
835
|
-
"Modern browsers, Node 16+, and CF Workers expose it natively.");
|
|
836
|
-
}
|
|
837
|
-
return s;
|
|
838
|
-
}
|
|
839
|
-
|
|
840
|
-
/**
|
|
841
|
-
* Cryptographically secure random bytes.
|
|
842
|
-
*
|
|
843
|
-
* Wraps `crypto.getRandomValues` with two small affordances every caller
|
|
844
|
-
* needs:
|
|
845
|
-
* - Allocates over a fresh, non-shared `ArrayBuffer` so the returned
|
|
846
|
-
* `Uint8Array` satisfies WebCrypto's `BufferSource` type under strict
|
|
847
|
-
* TS lib settings (where `Uint8Array<ArrayBufferLike>` isn't directly
|
|
848
|
-
* assignable).
|
|
849
|
-
* - Chunks requests larger than 65 KiB, the per-call cap browsers enforce
|
|
850
|
-
* on `getRandomValues`.
|
|
851
|
-
*/
|
|
852
|
-
const MAX_PER_CALL = 65536;
|
|
853
|
-
function randomBytes(n) {
|
|
854
|
-
if (!Number.isInteger(n) || n < 0) {
|
|
855
|
-
throw new Error(`randomBytes: n must be a non-negative integer (got ${n})`);
|
|
856
|
-
}
|
|
857
|
-
const buf = new Uint8Array(new ArrayBuffer(n));
|
|
858
|
-
for (let offset = 0; offset < n; offset += MAX_PER_CALL) {
|
|
859
|
-
const slice = buf.subarray(offset, Math.min(offset + MAX_PER_CALL, n));
|
|
860
|
-
globalThis.crypto.getRandomValues(slice);
|
|
861
|
-
}
|
|
862
|
-
return buf;
|
|
863
|
-
}
|
|
864
|
-
|
|
865
|
-
/**
|
|
866
|
-
* AES-256-GCM encryption / decryption with raw key bytes.
|
|
867
|
-
*
|
|
868
|
-
* The two functions here are the single canonical AES-GCM call site in the
|
|
869
|
-
* SDK. Earlier copies of this logic existed inline in `DoubleRatchet`, in the
|
|
870
|
-
* storage `ChunkCipher`, and in the passphrase-wrap helpers — they all now
|
|
871
|
-
* route through here so the algorithm, IV size, and tag handling stay in one
|
|
872
|
-
* place.
|
|
873
|
-
*
|
|
874
|
-
* Conventions:
|
|
875
|
-
* - 256-bit keys only. Passing 16- or 24-byte keys throws; callers that
|
|
876
|
-
* need AES-128 should add a dedicated primitive.
|
|
877
|
-
* - 96-bit IVs. WebCrypto's recommended GCM IV length; trades the 4-byte
|
|
878
|
-
* IV economy of 128-bit for compatibility with every spec-compliant
|
|
879
|
-
* implementation.
|
|
880
|
-
* - The 16-byte GCM authentication tag is appended to the ciphertext by
|
|
881
|
-
* WebCrypto's default behavior. Callers don't manage it separately.
|
|
882
|
-
*
|
|
883
|
-
* If a future need arises for AAD (associated data), add an optional `aad`
|
|
884
|
-
* parameter to both functions rather than introducing a parallel primitive.
|
|
885
|
-
*/
|
|
886
|
-
/** Length of the AES key in bytes. */
|
|
887
|
-
const AES_GCM_KEY_BYTES = 32;
|
|
888
|
-
/** Length of the AES-GCM IV in bytes (WebCrypto-recommended). */
|
|
889
|
-
const AES_GCM_IV_BYTES = 12;
|
|
890
|
-
/** Length of the AES-GCM authentication tag in bytes (appended to ciphertext). */
|
|
891
|
-
const AES_GCM_TAG_BYTES = 16;
|
|
892
|
-
/**
|
|
893
|
-
* Encrypt `plaintext` under a 32-byte AES-256 key with the supplied 12-byte
|
|
894
|
-
* IV. Returns ciphertext with the 16-byte GCM tag appended (so the output
|
|
895
|
-
* length is `plaintext.length + 16`).
|
|
896
|
-
*
|
|
897
|
-
* The IV must be unique per (key, message). Callers that generate the IV
|
|
898
|
-
* randomly should grab it via `randomBytes(AES_GCM_IV_BYTES)`; callers
|
|
899
|
-
* managing a counter pass their own.
|
|
900
|
-
*/
|
|
901
|
-
async function encryptAesGcm(key, iv, plaintext) {
|
|
902
|
-
assertKey(key);
|
|
903
|
-
assertIv(iv);
|
|
904
|
-
const cryptoKey = await importAesGcmKey(key, ["encrypt"]);
|
|
905
|
-
const ciphertext = await getSubtle().encrypt({ name: "AES-GCM", iv: iv }, cryptoKey, plaintext);
|
|
906
|
-
return new Uint8Array(ciphertext);
|
|
907
|
-
}
|
|
908
|
-
/**
|
|
909
|
-
* Inverse of {@link encryptAesGcm}. Throws if the GCM auth tag fails — this
|
|
910
|
-
* captures "wrong key", "wrong IV", or "ciphertext was tampered with",
|
|
911
|
-
* indistinguishably. Callers that care about distinguishing those should
|
|
912
|
-
* keep separate provenance bookkeeping, not invent a parallel MAC.
|
|
913
|
-
*/
|
|
914
|
-
async function decryptAesGcm(key, iv, ciphertext) {
|
|
915
|
-
assertKey(key);
|
|
916
|
-
assertIv(iv);
|
|
917
|
-
const cryptoKey = await importAesGcmKey(key, ["decrypt"]);
|
|
918
|
-
const plaintext = await getSubtle().decrypt({ name: "AES-GCM", iv: iv }, cryptoKey, ciphertext);
|
|
919
|
-
return new Uint8Array(plaintext);
|
|
920
|
-
}
|
|
921
|
-
async function importAesGcmKey(key, usages) {
|
|
922
|
-
return await getSubtle().importKey("raw", key, { name: "AES-GCM" }, false, usages);
|
|
923
|
-
}
|
|
924
|
-
function assertKey(key) {
|
|
925
|
-
if (key.byteLength !== AES_GCM_KEY_BYTES) {
|
|
926
|
-
throw new Error(`aes-gcm: key must be ${AES_GCM_KEY_BYTES} bytes (got ${key.byteLength})`);
|
|
927
|
-
}
|
|
928
|
-
}
|
|
929
|
-
function assertIv(iv) {
|
|
930
|
-
if (iv.byteLength !== AES_GCM_IV_BYTES) {
|
|
931
|
-
throw new Error(`aes-gcm: iv must be ${AES_GCM_IV_BYTES} bytes (got ${iv.byteLength})`);
|
|
932
|
-
}
|
|
933
|
-
}
|
|
934
|
-
|
|
935
|
-
/**
|
|
936
|
-
* Key-derivation primitives — PBKDF2 (for passphrase → key) and HKDF (for
|
|
937
|
-
* symmetric key material → expanded key material).
|
|
938
|
-
*
|
|
939
|
-
* PBKDF2 is the slow, salted KDF the passphrase-wrap helper uses to harden
|
|
940
|
-
* user passwords against offline guessing. HKDF is the fast, deterministic
|
|
941
|
-
* KDF the Double Ratchet uses to derive root + chain + message keys from
|
|
942
|
-
* shared secrets.
|
|
943
|
-
*
|
|
944
|
-
* Both are SHA-256-backed everywhere in this codebase — keep it that way
|
|
945
|
-
* unless there's a concrete reason to diverge.
|
|
946
|
-
*/
|
|
947
|
-
/**
|
|
948
|
-
* Iteration count default for PBKDF2 passphrase hardening. Tuned for
|
|
949
|
-
* 2024–2026 hardware: slow enough to deter offline guessing of weak
|
|
950
|
-
* passphrases (~100ms on commodity laptops), fast enough that interactive
|
|
951
|
-
* unlock stays sub-second.
|
|
952
|
-
*/
|
|
953
|
-
const PBKDF2_DEFAULT_ITERATIONS = 200_000;
|
|
954
|
-
/**
|
|
955
|
-
* Derive a 256-bit AES-GCM key from `passphrase + salt` via PBKDF2-SHA256.
|
|
956
|
-
*
|
|
957
|
-
* Returned as raw bytes (not a `CryptoKey`) so callers can hand it straight
|
|
958
|
-
* to {@link encryptAesGcm} — that's the convention everywhere else in the
|
|
959
|
-
* primitive layer. If a future caller needs the non-extractable
|
|
960
|
-
* `CryptoKey` form, add a parallel `deriveCryptoKeyFromPassphrase` variant
|
|
961
|
-
* rather than overloading this one.
|
|
962
|
-
*/
|
|
963
|
-
async function deriveAesKeyFromPassphrase(passphrase, salt, iterations = PBKDF2_DEFAULT_ITERATIONS) {
|
|
964
|
-
if (typeof passphrase !== "string" || passphrase.length === 0) {
|
|
965
|
-
throw new Error("kdf.deriveAesKeyFromPassphrase: passphrase must be a non-empty string");
|
|
966
|
-
}
|
|
967
|
-
const s = getSubtle();
|
|
968
|
-
const passphraseKey = await s.importKey("raw", new TextEncoder().encode(passphrase), { name: "PBKDF2" }, false, ["deriveBits"]);
|
|
969
|
-
const bits = await s.deriveBits({
|
|
970
|
-
name: "PBKDF2",
|
|
971
|
-
salt: salt,
|
|
972
|
-
iterations,
|
|
973
|
-
hash: "SHA-256",
|
|
974
|
-
}, passphraseKey, 256);
|
|
975
|
-
return new Uint8Array(bits);
|
|
976
|
-
}
|
|
977
|
-
/**
|
|
978
|
-
* HKDF-Expand with SHA-256, returning `lengthBytes` raw bytes of key material.
|
|
979
|
-
*
|
|
980
|
-
* `info` is the per-purpose label (e.g. `"DoubleRatchetMsg"`). Optional
|
|
981
|
-
* `salt` defaults to an empty buffer, matching the way the Double Ratchet
|
|
982
|
-
* code has always used it.
|
|
983
|
-
*
|
|
984
|
-
* Note: this is intentionally a one-shot Extract-then-Expand call rather than
|
|
985
|
-
* exposing Extract and Expand separately — every call site in this codebase
|
|
986
|
-
* wants the same shape, and a richer API can be added later if needed.
|
|
987
|
-
*/
|
|
988
|
-
async function deriveBitsHkdf(inputKeyMaterial, info, lengthBytes, salt = new Uint8Array(0)) {
|
|
989
|
-
if (lengthBytes <= 0) {
|
|
990
|
-
throw new Error(`kdf.deriveBitsHkdf: lengthBytes must be > 0 (got ${lengthBytes})`);
|
|
991
|
-
}
|
|
992
|
-
const s = getSubtle();
|
|
993
|
-
const key = await s.importKey("raw", inputKeyMaterial, { name: "HKDF" }, false, ["deriveBits"]);
|
|
994
|
-
const bits = await s.deriveBits({
|
|
995
|
-
name: "HKDF",
|
|
996
|
-
hash: "SHA-256",
|
|
997
|
-
salt: salt,
|
|
998
|
-
info: new TextEncoder().encode(info),
|
|
999
|
-
}, key, lengthBytes * 8);
|
|
1000
|
-
return new Uint8Array(bits);
|
|
1001
|
-
}
|
|
1002
|
-
|
|
1003
|
-
class KeyStore {
|
|
1004
|
-
static instance;
|
|
1005
|
-
_keys = new Map();
|
|
1006
|
-
_authKeys = new Map();
|
|
1007
|
-
constructor() { }
|
|
1008
|
-
static getInstance() {
|
|
1009
|
-
if (!KeyStore.instance) {
|
|
1010
|
-
KeyStore.instance = new KeyStore();
|
|
1011
|
-
}
|
|
1012
|
-
return KeyStore.instance;
|
|
1013
|
-
}
|
|
1014
|
-
get keys() {
|
|
1015
|
-
return this._keys;
|
|
1016
|
-
}
|
|
1017
|
-
get authKeys() {
|
|
1018
|
-
return this._authKeys;
|
|
1019
|
-
}
|
|
1020
|
-
async generateOwnKeyPair(id) {
|
|
1021
|
-
if (this._keys.has(id)) {
|
|
1022
|
-
throw new Error(`Key pair for ${id} already exists.`);
|
|
1023
|
-
}
|
|
1024
|
-
const ecdhKeyPair = await crypto.subtle.generateKey({ name: 'ECDH', namedCurve: 'P-384' }, true, ['deriveKey', 'deriveBits']);
|
|
1025
|
-
this._keys.set(id, { privateKey: ecdhKeyPair.privateKey, publicKey: ecdhKeyPair.publicKey });
|
|
1026
|
-
const ecdsaKeyPair = await crypto.subtle.generateKey({ name: 'ECDSA', namedCurve: 'P-384' }, true, ['sign', 'verify']);
|
|
1027
|
-
this._authKeys.set(id, { privateKey: ecdsaKeyPair.privateKey, publicKey: ecdsaKeyPair.publicKey });
|
|
1028
|
-
const dehydrated = await this.dehydrateKeyPair(id);
|
|
1029
|
-
appLogger.debug(`Generated key pair for ${id}: publicKey=${dehydrated.ecdhPub.slice(0, 16)}...`);
|
|
1030
|
-
return { privateKey: ecdhKeyPair.privateKey, publicKey: ecdhKeyPair.publicKey };
|
|
1031
|
-
}
|
|
1032
|
-
async storeRemotePublicKeys(id, ecdhPublicKey, ecdsaPublicKey) {
|
|
1033
|
-
const existing = this._keys.get(id);
|
|
1034
|
-
if (!existing || !existing.privateKey) {
|
|
1035
|
-
this._keys.set(id, { privateKey: null, publicKey: ecdhPublicKey });
|
|
1036
|
-
}
|
|
1037
|
-
const existingAuth = this._authKeys.get(id);
|
|
1038
|
-
if (!existingAuth || !existingAuth.privateKey) {
|
|
1039
|
-
this._authKeys.set(id, { privateKey: null, publicKey: ecdsaPublicKey });
|
|
1040
|
-
}
|
|
1041
|
-
const dehydrated = await this.dehydrateKeyPair(id);
|
|
1042
|
-
appLogger.debug(`Stored remote public keys for ${id}: ecdhPub=${dehydrated.ecdhPub.slice(0, 16)}...`);
|
|
1043
|
-
}
|
|
1044
|
-
async hydrateKeyPair(id, dehydrated) {
|
|
1045
|
-
const ecdhPubJwk = JSON.parse(deserialize(dehydrated.ecdhPub));
|
|
1046
|
-
const ecdhPublicKey = await crypto.subtle.importKey('jwk', ecdhPubJwk, { name: 'ECDH', namedCurve: 'P-384' }, true, []);
|
|
1047
|
-
const ecdsaPubJwk = JSON.parse(deserialize(dehydrated.ecdsaPub));
|
|
1048
|
-
const ecdsaPublicKey = await crypto.subtle.importKey('jwk', ecdsaPubJwk, { name: 'ECDSA', namedCurve: 'P-384' }, true, ['verify']);
|
|
1049
|
-
let ecdhPrivateKey = null;
|
|
1050
|
-
let ecdsaPrivateKey = null;
|
|
1051
|
-
if (dehydrated.ecdhPriv) {
|
|
1052
|
-
const ecdhPrivJwk = JSON.parse(deserialize(dehydrated.ecdhPriv));
|
|
1053
|
-
ecdhPrivateKey = await crypto.subtle.importKey('jwk', ecdhPrivJwk, { name: 'ECDH', namedCurve: 'P-384' }, true, ['deriveKey', 'deriveBits']);
|
|
1054
|
-
}
|
|
1055
|
-
if (dehydrated.ecdsaPriv) {
|
|
1056
|
-
const ecdsaPrivJwk = JSON.parse(deserialize(dehydrated.ecdsaPriv));
|
|
1057
|
-
ecdsaPrivateKey = await crypto.subtle.importKey('jwk', ecdsaPrivJwk, { name: 'ECDSA', namedCurve: 'P-384' }, true, ['sign']);
|
|
1058
|
-
}
|
|
1059
|
-
const existing = this._keys.get(id);
|
|
1060
|
-
if (!existing || !existing.privateKey) {
|
|
1061
|
-
this._keys.set(id, { privateKey: ecdhPrivateKey, publicKey: ecdhPublicKey });
|
|
1062
|
-
}
|
|
1063
|
-
const existingAuth = this._authKeys.get(id);
|
|
1064
|
-
if (!existingAuth || !existingAuth.privateKey) {
|
|
1065
|
-
this._authKeys.set(id, { privateKey: ecdsaPrivateKey, publicKey: ecdsaPublicKey });
|
|
1066
|
-
}
|
|
1067
|
-
}
|
|
1068
|
-
getKeyPair(id) {
|
|
1069
|
-
return this._keys.get(id) || null;
|
|
1070
|
-
}
|
|
1071
|
-
getAuthKeyPair(id) {
|
|
1072
|
-
return this._authKeys.get(id) || null;
|
|
1073
|
-
}
|
|
1074
|
-
async dehydrateKeyPair(id) {
|
|
1075
|
-
const keyPair = this._keys.get(id);
|
|
1076
|
-
const authKeyPair = this._authKeys.get(id);
|
|
1077
|
-
if (!keyPair || !authKeyPair) {
|
|
1078
|
-
throw new Error(`No key pair for ${id}`);
|
|
1079
|
-
}
|
|
1080
|
-
const ecdhPubJwk = await crypto.subtle.exportKey('jwk', keyPair.publicKey);
|
|
1081
|
-
const ecdhPrivJwk = keyPair.privateKey ? await crypto.subtle.exportKey('jwk', keyPair.privateKey) : null;
|
|
1082
|
-
const ecdsaPubJwk = await crypto.subtle.exportKey('jwk', authKeyPair.publicKey);
|
|
1083
|
-
const ecdsaPrivJwk = authKeyPair.privateKey ? await crypto.subtle.exportKey('jwk', authKeyPair.privateKey) : null;
|
|
1084
|
-
const ecdhPubBase64 = JSON.stringify(ecdhPubJwk).replace(/=+$/, '');
|
|
1085
|
-
const ecdhPrivBase64 = ecdhPrivJwk ? JSON.stringify(ecdhPrivJwk).replace(/=+$/, '') : '';
|
|
1086
|
-
const ecdsaPubBase64 = JSON.stringify(ecdsaPubJwk).replace(/=+$/, '');
|
|
1087
|
-
const ecdsaPrivBase64 = ecdsaPrivJwk ? JSON.stringify(ecdsaPrivJwk).replace(/=+$/, '') : '';
|
|
1088
|
-
return {
|
|
1089
|
-
ecdhPub: serialize(ecdhPubBase64),
|
|
1090
|
-
ecdhPriv: ecdhPrivBase64 ? serialize(ecdhPrivBase64) : '',
|
|
1091
|
-
ecdsaPub: serialize(ecdsaPubBase64),
|
|
1092
|
-
ecdsaPriv: ecdsaPrivBase64 ? serialize(ecdsaPrivBase64) : '',
|
|
1093
|
-
};
|
|
1094
|
-
}
|
|
1095
|
-
async getRawEcdsaPublicKey(id) {
|
|
1096
|
-
const authKeyPair = this._authKeys.get(id);
|
|
1097
|
-
if (!authKeyPair) {
|
|
1098
|
-
return null;
|
|
1099
|
-
}
|
|
1100
|
-
const jwk = await crypto.subtle.exportKey('jwk', authKeyPair.publicKey);
|
|
1101
|
-
const x = fromBase64Url(jwk.x);
|
|
1102
|
-
const y = fromBase64Url(jwk.y);
|
|
1103
|
-
// SEC1 uncompressed point: 0x04 || x || y
|
|
1104
|
-
return concatBytes(new Uint8Array([0x04]), x, y);
|
|
1105
|
-
}
|
|
1106
|
-
async packDehydratedKeys(id) {
|
|
1107
|
-
const dehydrated = await this.dehydrateKeyPair(id);
|
|
1108
|
-
const combined = JSON.stringify(dehydrated);
|
|
1109
|
-
return toBase64(new TextEncoder().encode(combined));
|
|
1110
|
-
}
|
|
1111
|
-
async hydrateFromPacked(id, masterKey) {
|
|
1112
|
-
const decoded = utf8Decode(fromBase64(masterKey));
|
|
1113
|
-
const dehydrated = JSON.parse(decoded);
|
|
1114
|
-
await this.hydrateKeyPair(id, dehydrated);
|
|
1115
|
-
}
|
|
1116
|
-
}
|
|
1117
|
-
|
|
1118
|
-
// We rely on the WebCrypto global (`crypto`/`globalThis.crypto`) — present in
|
|
1119
|
-
// browsers, CF Workers, and Node 19+. No `require('crypto')` Node fallback;
|
|
1120
|
-
// supported runtimes all expose WebCrypto natively.
|
|
1121
|
-
async function getPubKeyHex(pubKey) {
|
|
1122
|
-
const exported = await crypto.subtle.exportKey('raw', pubKey);
|
|
1123
|
-
return toHex(new Uint8Array(exported));
|
|
1124
|
-
}
|
|
1125
|
-
async function keysEqual(key1, key2) {
|
|
1126
|
-
const hex1 = await getPubKeyHex(key1);
|
|
1127
|
-
const hex2 = await getPubKeyHex(key2);
|
|
1128
|
-
return hex1 === hex2;
|
|
1129
|
-
}
|
|
1130
|
-
class DoubleRatchet {
|
|
1131
|
-
state;
|
|
1132
|
-
sessionType;
|
|
1133
|
-
maxSkip = 3000;
|
|
1134
|
-
overlapPeriod = 30000; // ms
|
|
1135
|
-
windowSize = 100; // messages per DH ratchet
|
|
1136
|
-
constructor(senderId, recipientId, sessionType, isClient = true) {
|
|
1137
|
-
this.sessionType = sessionType;
|
|
1138
|
-
const keyStore = KeyStore.getInstance();
|
|
1139
|
-
const senderKeyPair = keyStore.getKeyPair(senderId);
|
|
1140
|
-
const recipientKeyPair = keyStore.getKeyPair(recipientId);
|
|
1141
|
-
if (!senderKeyPair || !senderKeyPair.privateKey) {
|
|
1142
|
-
throw new Error(`Missing own key pair for sender ${senderId}`);
|
|
1143
|
-
}
|
|
1144
|
-
if (!recipientKeyPair || !recipientKeyPair.publicKey) {
|
|
1145
|
-
throw new Error(`Missing public key for recipient ${recipientId}`);
|
|
1146
|
-
}
|
|
1147
|
-
this.state = {
|
|
1148
|
-
clientDhPriv: isClient ? senderKeyPair.privateKey : null,
|
|
1149
|
-
clientDhPub: isClient ? senderKeyPair.publicKey : recipientKeyPair.publicKey,
|
|
1150
|
-
serverDhPriv: isClient ? null : senderKeyPair.privateKey,
|
|
1151
|
-
serverDhPub: isClient ? recipientKeyPair.publicKey : senderKeyPair.publicKey,
|
|
1152
|
-
rootKey: null,
|
|
1153
|
-
sendChainKey: null,
|
|
1154
|
-
recvChainKey: null,
|
|
1155
|
-
sendCount: 0,
|
|
1156
|
-
recvCount: 0,
|
|
1157
|
-
prevChainLength: 0,
|
|
1158
|
-
currentSkippedKeys: new Map(),
|
|
1159
|
-
oldSkippedMessageKeys: new Map(),
|
|
1160
|
-
};
|
|
1161
|
-
appLogger.debug(`Constructor: senderId=${senderId}, recipientId=${recipientId}, isClient=${isClient}`);
|
|
1162
|
-
}
|
|
1163
|
-
hkdf(input, info, length) {
|
|
1164
|
-
return deriveBitsHkdf(input, info, length);
|
|
1165
|
-
}
|
|
1166
|
-
async initializeSession(isClient = true) {
|
|
1167
|
-
appLogger.debug(`Initializing session: isClient=${isClient}, sessionType=${this.sessionType}`);
|
|
1168
|
-
const ownPriv = isClient ? this.state.clientDhPriv : this.state.serverDhPriv;
|
|
1169
|
-
if (!ownPriv) {
|
|
1170
|
-
throw new Error('Missing own private key');
|
|
1171
|
-
}
|
|
1172
|
-
const remotePub = isClient ? this.state.serverDhPub : this.state.clientDhPub;
|
|
1173
|
-
const sharedSecret = await crypto.subtle.deriveBits({ name: 'ECDH', public: remotePub }, ownPriv, 384);
|
|
1174
|
-
const keys = await this.hkdf(new Uint8Array(sharedSecret), 'DoubleRatchetInit', 32);
|
|
1175
|
-
this.state.rootKey = keys;
|
|
1176
|
-
this.state.sendChainKey = this.state.rootKey;
|
|
1177
|
-
this.state.recvChainKey = this.state.rootKey;
|
|
1178
|
-
appLogger.debug(`Initialized: rootKey set`);
|
|
1179
|
-
}
|
|
1180
|
-
async dhRatchet(newPubKey, isClient = true) {
|
|
1181
|
-
appLogger.debug(`DH ratchet: isClient=${isClient}`);
|
|
1182
|
-
const ownPriv = isClient ? this.state.clientDhPriv : this.state.serverDhPriv;
|
|
1183
|
-
if (!ownPriv) {
|
|
1184
|
-
throw new Error('Missing own private key');
|
|
1185
|
-
}
|
|
1186
|
-
const sharedSecret = await crypto.subtle.deriveBits({ name: 'ECDH', public: newPubKey }, ownPriv, 384);
|
|
1187
|
-
const input = concatBytes(this.state.rootKey, new Uint8Array(sharedSecret));
|
|
1188
|
-
const keys = await this.hkdf(input, 'DoubleRatchetDH', 64);
|
|
1189
|
-
this.state.rootKey = keys.subarray(0, 32);
|
|
1190
|
-
this.state.recvChainKey = keys.subarray(32);
|
|
1191
|
-
this.state.sendChainKey = isClient ? this.state.recvChainKey : this.state.rootKey;
|
|
1192
|
-
this.state.prevChainLength = this.state.sendCount;
|
|
1193
|
-
this.state.sendCount = 0;
|
|
1194
|
-
this.state.recvCount = 0;
|
|
1195
|
-
appLogger.debug(`DH ratchet complete`);
|
|
1196
|
-
}
|
|
1197
|
-
async symmetricRatchet(chainKey) {
|
|
1198
|
-
const keys = await this.hkdf(chainKey, 'DoubleRatchetMsg', 64);
|
|
1199
|
-
const messageKey = keys.subarray(0, 32);
|
|
1200
|
-
const newChainKey = keys.subarray(32);
|
|
1201
|
-
appLogger.debug(`Symmetric ratchet: messageKey generated`);
|
|
1202
|
-
return [messageKey, newChainKey];
|
|
1203
|
-
}
|
|
1204
|
-
async encrypt(plaintext, newDhKey = false, senderId, recipientId, sessionId, sessionType) {
|
|
1205
|
-
if (sessionType !== this.sessionType) {
|
|
1206
|
-
throw new Error(`Session type mismatch: expected ${this.sessionType}, got ${sessionType}`);
|
|
1207
|
-
}
|
|
1208
|
-
const keyStore = KeyStore.getInstance();
|
|
1209
|
-
if (this.sessionType === 'specific' && this.state.sendCount >= this.windowSize && this.state.sendCount > 0) {
|
|
1210
|
-
newDhKey = true;
|
|
1211
|
-
}
|
|
1212
|
-
if (newDhKey && this.sessionType === 'specific') {
|
|
1213
|
-
appLogger.debug(`Generating new DH key pair for ${senderId}`);
|
|
1214
|
-
const ecdhKeyPair = await crypto.subtle.generateKey({ name: 'ECDH', namedCurve: 'P-384' }, true, ['deriveKey', 'deriveBits']);
|
|
1215
|
-
this.state.clientDhPriv = ecdhKeyPair.privateKey;
|
|
1216
|
-
this.state.clientDhPub = ecdhKeyPair.publicKey;
|
|
1217
|
-
keyStore.keys.set(senderId, { privateKey: ecdhKeyPair.privateKey, publicKey: ecdhKeyPair.publicKey });
|
|
1218
|
-
await this.dhRatchet(this.state.serverDhPub, true);
|
|
1219
|
-
}
|
|
1220
|
-
const [messageKey, newChainKey] = await this.symmetricRatchet(this.state.sendChainKey);
|
|
1221
|
-
this.state.sendChainKey = newChainKey;
|
|
1222
|
-
const iv = randomBytes(AES_GCM_IV_BYTES);
|
|
1223
|
-
const encrypted = await encryptAesGcm(messageKey, iv, utf8Encode(plaintext));
|
|
1224
|
-
const cipherTextWithTag = toHex(encrypted);
|
|
1225
|
-
appLogger.debug(`Encrypt: plaintextLength=${plaintext.length}, ciphertextLength=${encrypted.byteLength}`);
|
|
1226
|
-
this.state.sendCount++;
|
|
1227
|
-
const dehydratedSender = await keyStore.dehydrateKeyPair(senderId);
|
|
1228
|
-
const header = {
|
|
1229
|
-
dhPub: dehydratedSender.ecdhPub,
|
|
1230
|
-
prevChainLength: this.state.prevChainLength,
|
|
1231
|
-
messageNumber: this.state.sendCount,
|
|
1232
|
-
sessionId,
|
|
1233
|
-
sessionType,
|
|
1234
|
-
senderId,
|
|
1235
|
-
recipientId,
|
|
1236
|
-
timestamp: Date.now(),
|
|
1237
|
-
signature: '',
|
|
1238
|
-
};
|
|
1239
|
-
appLogger.debug(`Message header:`, header);
|
|
1240
|
-
appLogger.debug(`Message header:`, keyStore.getAuthKeyPair(senderId).privateKey);
|
|
1241
|
-
const sign = await crypto.subtle.sign({ name: 'ECDSA', hash: 'SHA-256' }, keyStore.getAuthKeyPair(senderId).privateKey, utf8Encode(JSON.stringify({
|
|
1242
|
-
dhPub: header.dhPub,
|
|
1243
|
-
prevChainLength: header.prevChainLength,
|
|
1244
|
-
messageNumber: header.messageNumber,
|
|
1245
|
-
sessionId,
|
|
1246
|
-
sessionType,
|
|
1247
|
-
senderId,
|
|
1248
|
-
recipientId,
|
|
1249
|
-
timestamp: header.timestamp
|
|
1250
|
-
})));
|
|
1251
|
-
header.signature = toHex(new Uint8Array(sign));
|
|
1252
|
-
appLogger.debug(`Encrypted: sessionId=${sessionId}, messageNumber=${this.state.sendCount}, sender=${senderId}, recipient=${recipientId}`);
|
|
1253
|
-
return {
|
|
1254
|
-
header,
|
|
1255
|
-
ciphertext: cipherTextWithTag,
|
|
1256
|
-
nonce: toHex(iv),
|
|
1257
|
-
};
|
|
1258
|
-
}
|
|
1259
|
-
cleanOldSkippedKeys() {
|
|
1260
|
-
const now = Date.now();
|
|
1261
|
-
for (const skipped of this.state.oldSkippedMessageKeys.entries()) {
|
|
1262
|
-
// [pubHex, { created }]
|
|
1263
|
-
const pubHex = skipped[0];
|
|
1264
|
-
const data = skipped[1];
|
|
1265
|
-
const created = data.created;
|
|
1266
|
-
if (now > created + this.overlapPeriod) {
|
|
1267
|
-
this.state.oldSkippedMessageKeys.delete(pubHex);
|
|
1268
|
-
}
|
|
1269
|
-
}
|
|
1270
|
-
}
|
|
1271
|
-
async decrypt(message, isClient = true) {
|
|
1272
|
-
// const { header, ciphertext, nonce } = message;
|
|
1273
|
-
const header = message.header;
|
|
1274
|
-
const ciphertext = message.ciphertext;
|
|
1275
|
-
const nonce = message.nonce;
|
|
1276
|
-
if (header.sessionType !== this.sessionType) {
|
|
1277
|
-
throw new Error(`Session type mismatch: expected ${this.sessionType}, got ${header.sessionType}`);
|
|
1278
|
-
}
|
|
1279
|
-
const keyStore = KeyStore.getInstance();
|
|
1280
|
-
const verify = await crypto.subtle.verify({ name: 'ECDSA', hash: 'SHA-256' }, keyStore.getAuthKeyPair(header.senderId).publicKey, fromHex(header.signature), utf8Encode(JSON.stringify({
|
|
1281
|
-
dhPub: header.dhPub,
|
|
1282
|
-
prevChainLength: header.prevChainLength,
|
|
1283
|
-
messageNumber: header.messageNumber,
|
|
1284
|
-
sessionId: header.sessionId,
|
|
1285
|
-
sessionType: header.sessionType,
|
|
1286
|
-
senderId: header.senderId,
|
|
1287
|
-
recipientId: header.recipientId,
|
|
1288
|
-
timestamp: header.timestamp
|
|
1289
|
-
})));
|
|
1290
|
-
if (!verify) {
|
|
1291
|
-
throw new Error('Invalid signature');
|
|
1292
|
-
}
|
|
1293
|
-
const maxAge = 5 * 60 * 1000;
|
|
1294
|
-
if (Math.abs(Date.now() - header.timestamp) > maxAge) {
|
|
1295
|
-
throw new Error('CipherMessage too old, possible replay attack');
|
|
1296
|
-
}
|
|
1297
|
-
appLogger.debug(`Decrypting: sessionId=${header.sessionId}, messageNumber=${header.messageNumber}, sender=${header.senderId}, recipient=${header.recipientId}`);
|
|
1298
|
-
if (!ciphertext || !nonce || !header.dhPub) {
|
|
1299
|
-
throw new Error('Invalid message: missing ciphertext, nonce, or dhPub');
|
|
1300
|
-
}
|
|
1301
|
-
if (!/^[0-9a-fA-F]+$/.test(ciphertext) || ciphertext.length % 2 !== 0) {
|
|
1302
|
-
throw new Error(`Invalid ciphertext: not a valid hex string (length=${ciphertext.length})`);
|
|
1303
|
-
}
|
|
1304
|
-
if (!/^[0-9a-fA-F]+$/.test(nonce) || nonce.length % 2 !== 0) {
|
|
1305
|
-
throw new Error(`Invalid nonce: not a valid hex string (length=${nonce.length})`);
|
|
1306
|
-
}
|
|
1307
|
-
const ciphertextBuffer = fromHex(ciphertext);
|
|
1308
|
-
const nonceBuffer = fromHex(nonce);
|
|
1309
|
-
if (nonceBuffer.length !== 12) {
|
|
1310
|
-
throw new Error(`Invalid nonce: must be 12 bytes (length=${nonceBuffer.length})`);
|
|
1311
|
-
}
|
|
1312
|
-
const senderPubJwk = JSON.parse(deserialize(header.dhPub));
|
|
1313
|
-
const senderPubKey = await crypto.subtle.importKey('jwk', senderPubJwk, { name: 'ECDH', namedCurve: 'P-384' }, true, []);
|
|
1314
|
-
const senderPubHex = await getPubKeyHex(senderPubKey);
|
|
1315
|
-
const currentSenderPub = isClient ? this.state.serverDhPub : this.state.clientDhPub;
|
|
1316
|
-
const currentSenderPubHex = await getPubKeyHex(currentSenderPub);
|
|
1317
|
-
this.cleanOldSkippedKeys();
|
|
1318
|
-
let messageKey;
|
|
1319
|
-
if (senderPubHex === currentSenderPubHex) {
|
|
1320
|
-
// Current chain
|
|
1321
|
-
if (header.messageNumber > this.state.recvCount) {
|
|
1322
|
-
const numSkips = header.messageNumber - this.state.recvCount - 1;
|
|
1323
|
-
if (numSkips > this.maxSkip) {
|
|
1324
|
-
throw new Error('Too many skipped messages');
|
|
1325
|
-
}
|
|
1326
|
-
for (let i = 0; i < numSkips; i++) {
|
|
1327
|
-
const [skippedKey, newChainKey] = await this.symmetricRatchet(this.state.recvChainKey);
|
|
1328
|
-
this.state.currentSkippedKeys.set(this.state.recvCount + 1 + i, skippedKey);
|
|
1329
|
-
this.state.recvChainKey = newChainKey;
|
|
1330
|
-
}
|
|
1331
|
-
}
|
|
1332
|
-
if (this.state.currentSkippedKeys.has(header.messageNumber)) {
|
|
1333
|
-
messageKey = this.state.currentSkippedKeys.get(header.messageNumber);
|
|
1334
|
-
this.state.currentSkippedKeys.delete(header.messageNumber);
|
|
1335
|
-
appLogger.debug(`Using skipped key for messageNumber=${header.messageNumber}`);
|
|
1336
|
-
}
|
|
1337
|
-
else {
|
|
1338
|
-
const [msgKey, newChainKey] = await this.symmetricRatchet(this.state.recvChainKey);
|
|
1339
|
-
messageKey = msgKey;
|
|
1340
|
-
this.state.recvChainKey = newChainKey;
|
|
1341
|
-
}
|
|
1342
|
-
this.state.recvCount = Math.max(this.state.recvCount, header.messageNumber);
|
|
1343
|
-
}
|
|
1344
|
-
else {
|
|
1345
|
-
// Check if new DH pub
|
|
1346
|
-
const expectedPubKey = keyStore.getKeyPair(header.senderId).publicKey;
|
|
1347
|
-
if (!(await keysEqual(senderPubKey, expectedPubKey))) {
|
|
1348
|
-
// New DH key, handle skip on old chain before ratchet
|
|
1349
|
-
const oldSenderPubHex = currentSenderPubHex;
|
|
1350
|
-
const numSkips = header.prevChainLength - this.state.recvCount;
|
|
1351
|
-
if (numSkips > this.maxSkip) {
|
|
1352
|
-
throw new Error('Too many skipped messages on old chain');
|
|
1353
|
-
}
|
|
1354
|
-
let oldSkips = this.state.oldSkippedMessageKeys.get(oldSenderPubHex)?.skips || new Map();
|
|
1355
|
-
let tempRecvChainKey = this.state.recvChainKey;
|
|
1356
|
-
for (let i = 0; i < numSkips; i++) {
|
|
1357
|
-
const [skippedKey, newChainKey] = await this.symmetricRatchet(tempRecvChainKey);
|
|
1358
|
-
oldSkips.set(this.state.recvCount + 1 + i, skippedKey);
|
|
1359
|
-
tempRecvChainKey = newChainKey;
|
|
1360
|
-
}
|
|
1361
|
-
this.state.oldSkippedMessageKeys.set(oldSenderPubHex, { skips: oldSkips, created: Date.now() });
|
|
1362
|
-
// Clear current skipped
|
|
1363
|
-
this.state.currentSkippedKeys.clear();
|
|
1364
|
-
// Perform DH ratchet
|
|
1365
|
-
await this.dhRatchet(senderPubKey, isClient);
|
|
1366
|
-
// Update state sender pub
|
|
1367
|
-
if (isClient) {
|
|
1368
|
-
this.state.serverDhPub = senderPubKey;
|
|
1369
|
-
}
|
|
1370
|
-
else {
|
|
1371
|
-
this.state.clientDhPub = senderPubKey;
|
|
1372
|
-
}
|
|
1373
|
-
// Update keyStore
|
|
1374
|
-
keyStore.storeRemotePublicKeys(header.senderId, senderPubKey, keyStore.getAuthKeyPair(header.senderId).publicKey);
|
|
1375
|
-
// Now derive message key for new chain
|
|
1376
|
-
if (header.messageNumber > this.state.recvCount) {
|
|
1377
|
-
const numSkipsNew = header.messageNumber - this.state.recvCount - 1;
|
|
1378
|
-
if (numSkipsNew > this.maxSkip) {
|
|
1379
|
-
throw new Error('Too many skipped messages');
|
|
1380
|
-
}
|
|
1381
|
-
for (let i = 0; i < numSkipsNew; i++) {
|
|
1382
|
-
const [skippedKey, newChainKey] = await this.symmetricRatchet(this.state.recvChainKey);
|
|
1383
|
-
this.state.currentSkippedKeys.set(this.state.recvCount + 1 + i, skippedKey);
|
|
1384
|
-
this.state.recvChainKey = newChainKey;
|
|
1385
|
-
}
|
|
1386
|
-
}
|
|
1387
|
-
if (this.state.currentSkippedKeys.has(header.messageNumber)) {
|
|
1388
|
-
messageKey = this.state.currentSkippedKeys.get(header.messageNumber);
|
|
1389
|
-
this.state.currentSkippedKeys.delete(header.messageNumber);
|
|
1390
|
-
}
|
|
1391
|
-
else {
|
|
1392
|
-
const [msgKey, newChainKey] = await this.symmetricRatchet(this.state.recvChainKey);
|
|
1393
|
-
messageKey = msgKey;
|
|
1394
|
-
this.state.recvChainKey = newChainKey;
|
|
1395
|
-
}
|
|
1396
|
-
this.state.recvCount = Math.max(this.state.recvCount, header.messageNumber);
|
|
1397
|
-
}
|
|
1398
|
-
else {
|
|
1399
|
-
// Old chain
|
|
1400
|
-
const oldData = this.state.oldSkippedMessageKeys.get(senderPubHex);
|
|
1401
|
-
if (oldData && oldData.skips.has(header.messageNumber)) {
|
|
1402
|
-
messageKey = oldData.skips.get(header.messageNumber);
|
|
1403
|
-
oldData.skips.delete(header.messageNumber);
|
|
1404
|
-
if (oldData.skips.size === 0) {
|
|
1405
|
-
this.state.oldSkippedMessageKeys.delete(senderPubHex);
|
|
1406
|
-
}
|
|
1407
|
-
appLogger.debug(`Using old skipped key for messageNumber=${header.messageNumber} on old chain`);
|
|
1408
|
-
}
|
|
1409
|
-
else {
|
|
1410
|
-
throw new Error('No key for message on old chain');
|
|
1411
|
-
}
|
|
1412
|
-
}
|
|
1413
|
-
}
|
|
1414
|
-
try {
|
|
1415
|
-
const decrypted = await decryptAesGcm(messageKey, nonceBuffer, ciphertextBuffer);
|
|
1416
|
-
appLogger.debug(`Decrypted successfully: sessionId=${header.sessionId}, messageNumber=${header.messageNumber}`);
|
|
1417
|
-
return new TextDecoder().decode(decrypted);
|
|
1418
|
-
}
|
|
1419
|
-
catch (error) {
|
|
1420
|
-
appLogger.error(`Decryption failed: ${error instanceof Error ? error.message : 'unknown error'}`);
|
|
1421
|
-
throw new Error(`Decryption failed: ${error instanceof Error ? error.message : 'unknown error'}`);
|
|
1422
|
-
}
|
|
1423
|
-
}
|
|
1424
|
-
getState() {
|
|
1425
|
-
return { ...this.state };
|
|
1426
|
-
}
|
|
1427
|
-
setState(state) {
|
|
1428
|
-
this.state = { ...state };
|
|
1429
|
-
}
|
|
1430
|
-
}
|
|
1431
|
-
|
|
1432
|
-
/**
|
|
1433
|
-
* Per-chunk AES-256-GCM encryption with random keys and IVs.
|
|
1434
|
-
*
|
|
1435
|
-
* Each chunk of a stored file gets its own freshly generated 256-bit AES key
|
|
1436
|
-
* and 96-bit IV. The (key, IV) pair lives in the file manifest, which the
|
|
1437
|
-
* application is responsible for storing under whatever access-control regime
|
|
1438
|
-
* it uses (in this codebase: the `SharedSpaceClient` writes manifests into
|
|
1439
|
-
* ZK-gated multi-user spaces; the shard store itself is open).
|
|
1440
|
-
*
|
|
1441
|
-
* Per-chunk keys (rather than a per-file DEK) lets future versions revoke or
|
|
1442
|
-
* re-share individual chunks without re-encrypting the whole file. The
|
|
1443
|
-
* additional manifest size is dominated by the shard hash list anyway.
|
|
1444
|
-
*
|
|
1445
|
-
* The cipher itself delegates to the canonical `encryptAesGcm` / `decryptAesGcm`
|
|
1446
|
-
* primitives — this class only wraps them with key-material generation and
|
|
1447
|
-
* the JSON-friendly base64 serialization the manifest needs.
|
|
1448
|
-
*/
|
|
1449
|
-
class ChunkCipher {
|
|
1450
|
-
/**
|
|
1451
|
-
* Generate a fresh random (key, iv) pair encoded for the manifest.
|
|
1452
|
-
* Callers should generate one of these per chunk before calling
|
|
1453
|
-
* {@link encryptChunk}.
|
|
1454
|
-
*/
|
|
1455
|
-
generateKeyMaterial() {
|
|
1456
|
-
return {
|
|
1457
|
-
key: toBase64(randomBytes(AES_GCM_KEY_BYTES)),
|
|
1458
|
-
iv: toBase64(randomBytes(AES_GCM_IV_BYTES)),
|
|
1459
|
-
};
|
|
1460
|
-
}
|
|
1461
|
-
/**
|
|
1462
|
-
* Encrypt `plaintext` under the supplied key + IV. Returns the AES-GCM
|
|
1463
|
-
* ciphertext with the 16-byte auth tag appended (WebCrypto's default
|
|
1464
|
-
* GCM behavior).
|
|
1465
|
-
*/
|
|
1466
|
-
async encryptChunk(plaintext, material) {
|
|
1467
|
-
return encryptAesGcm(fromBase64(material.key), fromBase64(material.iv), plaintext);
|
|
1468
|
-
}
|
|
1469
|
-
/**
|
|
1470
|
-
* Inverse of {@link encryptChunk}. Throws if the auth tag fails — that
|
|
1471
|
-
* means the ciphertext was tampered with, the key/iv was wrong, or the
|
|
1472
|
-
* shards were reassembled incorrectly.
|
|
1473
|
-
*/
|
|
1474
|
-
async decryptChunk(ciphertext, material) {
|
|
1475
|
-
try {
|
|
1476
|
-
return await decryptAesGcm(fromBase64(material.key), fromBase64(material.iv), ciphertext);
|
|
1477
|
-
}
|
|
1478
|
-
catch {
|
|
1479
|
-
// WebCrypto throws a vague OperationError on GCM tag mismatch.
|
|
1480
|
-
// Surface a stable, readable message so callers can react to it.
|
|
1481
|
-
throw new Error("ChunkCipher.decryptChunk: decryption failed (key/iv mismatch, corrupted ciphertext, or reassembly error)");
|
|
1482
|
-
}
|
|
1483
|
-
}
|
|
1484
|
-
}
|
|
1485
|
-
|
|
1486
|
-
/**
|
|
1487
|
-
* Passphrase-based wrap / unwrap helpers using WebCrypto.
|
|
1488
|
-
*
|
|
1489
|
-
* Encrypt a payload with a user-supplied passphrase before persisting it
|
|
1490
|
-
* somewhere the server can see (e.g. the manifest blob in a ZK-gated
|
|
1491
|
-
* SharedSpace, or chat-key material in a PersonalSpace). The server never
|
|
1492
|
-
* sees the passphrase or the plaintext — both wrap and unwrap run entirely
|
|
1493
|
-
* client-side.
|
|
1494
|
-
*
|
|
1495
|
-
* Algorithm (the only one this codebase has ever shipped):
|
|
1496
|
-
* - PBKDF2-SHA256 with 200_000 iterations derives a 256-bit AES key from
|
|
1497
|
-
* `passphrase || salt`.
|
|
1498
|
-
* - AES-256-GCM encrypts the plaintext with a fresh random 12-byte IV.
|
|
1499
|
-
* - The auth tag is appended to the ciphertext by WebCrypto's default
|
|
1500
|
-
* GCM behaviour, so {@link WrappedPayload.ciphertext} carries it implicitly.
|
|
1501
|
-
*
|
|
1502
|
-
* The returned {@link WrappedPayload} is JSON-friendly (all base64 fields), so
|
|
1503
|
-
* it can be `JSON.stringify`-ed straight into a KV layer.
|
|
1504
|
-
*/
|
|
1505
|
-
/** Length of the PBKDF2 salt, in bytes. */
|
|
1506
|
-
const SALT_BYTES = 16;
|
|
1507
|
-
/** Encode a `Uint8Array` to standard base64 (with padding). */
|
|
1508
|
-
function bytesToBase64(bytes) {
|
|
1509
|
-
// Build chunk-wise to avoid blowing the call stack on large payloads.
|
|
1510
|
-
let binary = "";
|
|
1511
|
-
const CHUNK = 0x8000;
|
|
1512
|
-
for (let i = 0; i < bytes.length; i += CHUNK) {
|
|
1513
|
-
binary += String.fromCharCode(...bytes.subarray(i, i + CHUNK));
|
|
1514
|
-
}
|
|
1515
|
-
if (typeof btoa === "function")
|
|
1516
|
-
return btoa(binary);
|
|
1517
|
-
return Buffer.from(bytes).toString("base64");
|
|
1518
|
-
}
|
|
1519
|
-
/** Decode a base64 string back into a `Uint8Array`. */
|
|
1520
|
-
function base64ToBytes(b64) {
|
|
1521
|
-
if (typeof atob === "function") {
|
|
1522
|
-
const binary = atob(b64);
|
|
1523
|
-
const bytes = new Uint8Array(binary.length);
|
|
1524
|
-
for (let i = 0; i < binary.length; i++)
|
|
1525
|
-
bytes[i] = binary.charCodeAt(i);
|
|
1526
|
-
return bytes;
|
|
1527
|
-
}
|
|
1528
|
-
return new Uint8Array(Buffer.from(b64, "base64"));
|
|
1529
|
-
}
|
|
1530
|
-
/**
|
|
1531
|
-
* Encrypt `plaintext` under a key derived from `passphrase`. The returned
|
|
1532
|
-
* payload bundles the salt, IV, and ciphertext so unwrap only needs the
|
|
1533
|
-
* passphrase to reverse the operation.
|
|
1534
|
-
*/
|
|
1535
|
-
async function wrapWithPassphrase(passphrase, plaintext) {
|
|
1536
|
-
if (typeof passphrase !== "string" || passphrase.length === 0) {
|
|
1537
|
-
throw new Error("wrapWithPassphrase: passphrase must be a non-empty string");
|
|
1538
|
-
}
|
|
1539
|
-
const salt = randomBytes(SALT_BYTES);
|
|
1540
|
-
const iv = randomBytes(AES_GCM_IV_BYTES);
|
|
1541
|
-
const key = await deriveAesKeyFromPassphrase(passphrase, salt, PBKDF2_DEFAULT_ITERATIONS);
|
|
1542
|
-
const ciphertext = await encryptAesGcm(key, iv, plaintext);
|
|
1543
|
-
return {
|
|
1544
|
-
salt: bytesToBase64(salt),
|
|
1545
|
-
iv: bytesToBase64(iv),
|
|
1546
|
-
ciphertext: bytesToBase64(ciphertext),
|
|
1547
|
-
alg: "PBKDF2-SHA256/AES-256-GCM",
|
|
1548
|
-
iter: PBKDF2_DEFAULT_ITERATIONS,
|
|
1549
|
-
};
|
|
1550
|
-
}
|
|
1551
|
-
/**
|
|
1552
|
-
* Decrypt a {@link WrappedPayload} produced by {@link wrapWithPassphrase}.
|
|
1553
|
-
* Throws if the passphrase is wrong, the payload is tampered with, or the
|
|
1554
|
-
* algorithm marker is unrecognised.
|
|
1555
|
-
*/
|
|
1556
|
-
async function unwrapWithPassphrase(passphrase, wrapped) {
|
|
1557
|
-
if (typeof passphrase !== "string" || passphrase.length === 0) {
|
|
1558
|
-
throw new Error("unwrapWithPassphrase: passphrase must be a non-empty string");
|
|
1559
|
-
}
|
|
1560
|
-
if (wrapped.alg !== "PBKDF2-SHA256/AES-256-GCM") {
|
|
1561
|
-
throw new Error(`unwrapWithPassphrase: unsupported alg "${wrapped.alg}"`);
|
|
1562
|
-
}
|
|
1563
|
-
const salt = base64ToBytes(wrapped.salt);
|
|
1564
|
-
const iv = base64ToBytes(wrapped.iv);
|
|
1565
|
-
const ciphertext = base64ToBytes(wrapped.ciphertext);
|
|
1566
|
-
const key = await deriveAesKeyFromPassphrase(passphrase, salt, wrapped.iter ?? PBKDF2_DEFAULT_ITERATIONS);
|
|
1567
|
-
try {
|
|
1568
|
-
return await decryptAesGcm(key, iv, ciphertext);
|
|
1569
|
-
}
|
|
1570
|
-
catch {
|
|
1571
|
-
// WebCrypto throws a vague OperationError on GCM tag mismatch; turn it
|
|
1572
|
-
// into a stable, readable message so callers can react to it.
|
|
1573
|
-
throw new Error("unwrapWithPassphrase: decryption failed (wrong passphrase or tampered payload)");
|
|
1574
|
-
}
|
|
1575
|
-
}
|
|
1576
|
-
|
|
1577
|
-
var EventCoreEvents;
|
|
1578
|
-
(function (EventCoreEvents) {
|
|
1579
|
-
EventCoreEvents["CONNECTED"] = "connected";
|
|
1580
|
-
EventCoreEvents["DISCONNECTED"] = "disconnected";
|
|
1581
|
-
EventCoreEvents["RECONNECTING"] = "reconnecting";
|
|
1582
|
-
EventCoreEvents["ERROR"] = "error";
|
|
1583
|
-
EventCoreEvents["DATA_RECEIVED"] = "data_received";
|
|
1584
|
-
EventCoreEvents["DATA_SENT"] = "data_sent";
|
|
1585
|
-
EventCoreEvents["MESSAGE"] = "message";
|
|
1586
|
-
EventCoreEvents["GET_HISTORY"] = "get_history";
|
|
1587
|
-
EventCoreEvents["RECEIVED_HISTORY"] = "received_history";
|
|
1588
|
-
// Offline layer — connectivity + background sync lifecycle. Emitted by the
|
|
1589
|
-
// ConnectivityManager / SyncEngine; apps subscribe to drive a status UI.
|
|
1590
|
-
EventCoreEvents["ONLINE"] = "online";
|
|
1591
|
-
EventCoreEvents["OFFLINE"] = "offline";
|
|
1592
|
-
EventCoreEvents["SYNCING"] = "syncing";
|
|
1593
|
-
EventCoreEvents["SYNC_PROGRESS"] = "sync_progress";
|
|
1594
|
-
EventCoreEvents["SYNC_COMPLETE"] = "sync_complete";
|
|
1595
|
-
EventCoreEvents["SYNC_ERROR"] = "sync_error";
|
|
1596
|
-
})(EventCoreEvents || (EventCoreEvents = {}));
|
|
1597
|
-
/**
|
|
1598
|
-
* @public
|
|
1599
|
-
* EventCore is a static event emitter with singleton functionality for global event handling.
|
|
1600
|
-
*/
|
|
1601
|
-
class EventCore {
|
|
1602
|
-
static eventTarget = new EventTarget();
|
|
1603
|
-
static events = new Map();
|
|
1604
|
-
/**
|
|
1605
|
-
* @remarks Listen to an event.
|
|
1606
|
-
* @example EventCore.on('event', (e) =\> appLogger.debug(e));
|
|
1607
|
-
*/
|
|
1608
|
-
static on(event, handler) {
|
|
1609
|
-
if (!EventCore.events.has(event)) {
|
|
1610
|
-
EventCore.events.set(event, handler);
|
|
1611
|
-
}
|
|
1612
|
-
EventCore.eventTarget.addEventListener(event, handler);
|
|
1613
|
-
}
|
|
1614
|
-
/**
|
|
1615
|
-
* @remarks Stop listening to an event.
|
|
1616
|
-
* @example EventCore.off('event', handler);
|
|
1617
|
-
*/
|
|
1618
|
-
static off(event, handler) {
|
|
1619
|
-
if (EventCore.events.has(event)) {
|
|
1620
|
-
EventCore.events.delete(event);
|
|
1621
|
-
}
|
|
1622
|
-
EventCore.eventTarget.removeEventListener(event, handler);
|
|
1623
|
-
}
|
|
1624
|
-
/**
|
|
1625
|
-
* @remarks Emit an event.
|
|
1626
|
-
* @example EventCore.emit('event', data);
|
|
1627
|
-
*/
|
|
1628
|
-
static emit(event, data) {
|
|
1629
|
-
if (!EventCore.events.has(event)) {
|
|
1630
|
-
appLogger.debug(`Warning: No listeners for event: ${event}`);
|
|
1631
|
-
// appLogger.verbose(`Event data:`, data);
|
|
1632
|
-
}
|
|
1633
|
-
EventCore.eventTarget.dispatchEvent(new CustomEvent(event, { detail: data }));
|
|
1634
|
-
}
|
|
1635
|
-
}
|
|
1636
|
-
|
|
1637
|
-
/**
|
|
1638
|
-
* An end-to-end-encrypted communication session for one local identity
|
|
1639
|
-
* (`myId`) talking to an arbitrary set of peers. Wraps `KeyStore` +
|
|
1640
|
-
* per-pair `DoubleRatchet` instances and owns the protocol bookkeeping
|
|
1641
|
-
* (handshake reciprocation, dedup, role assignment, broadcast fan-out,
|
|
1642
|
-
* recipient filtering) so application code doesn't have to.
|
|
1643
|
-
*
|
|
1644
|
-
* Transport-agnostic by design: the Session never touches a WebSocket
|
|
1645
|
-
* (or HTTP, or anything else). Application code:
|
|
1646
|
-
* - calls `session.encrypt(text)` and gets back an array of frames it
|
|
1647
|
-
* ships over whatever transport it has;
|
|
1648
|
-
* - calls `session.receive(frame)` with each inbound frame and acts on
|
|
1649
|
-
* the typed result (plaintext, handshake-done, ignored).
|
|
1650
|
-
*
|
|
1651
|
-
* Frames are plain JSON objects with the same shape the existing chat
|
|
1652
|
-
* protocol uses (`{ keyExchange: {...} }` and `{ cipherMessage: {...} }`),
|
|
1653
|
-
* so this can drop into a chat app without changing the wire format.
|
|
1654
|
-
*/
|
|
1655
|
-
// ---------------------------------------------------------------------------
|
|
1656
|
-
// EncryptedSession
|
|
1657
|
-
// ---------------------------------------------------------------------------
|
|
1658
|
-
class EncryptedSession {
|
|
1659
|
-
myId;
|
|
1660
|
-
keyStore;
|
|
1661
|
-
/** One DoubleRatchet per peer pair. */
|
|
1662
|
-
ratchets = new Map();
|
|
1663
|
-
/** Peers we've already sent our keyExchange to in this Session. */
|
|
1664
|
-
sentHandshakeTo = new Set();
|
|
1665
|
-
/** Resolved when own keys are generated and ready to use. */
|
|
1666
|
-
_ready;
|
|
1667
|
-
_resolveReady;
|
|
1668
|
-
_initialized = false;
|
|
1669
|
-
constructor(opts) {
|
|
1670
|
-
if (!opts || !opts.myId) {
|
|
1671
|
-
throw new Error("EncryptedSession: `myId` is required");
|
|
1672
|
-
}
|
|
1673
|
-
this.myId = opts.myId;
|
|
1674
|
-
this.keyStore = KeyStore.getInstance();
|
|
1675
|
-
this._ready = new Promise((resolve) => {
|
|
1676
|
-
this._resolveReady = resolve;
|
|
1677
|
-
});
|
|
1678
|
-
}
|
|
1679
|
-
/** A promise that resolves once `initialize()` has completed. */
|
|
1680
|
-
get ready() {
|
|
1681
|
-
return this._ready;
|
|
1682
|
-
}
|
|
1683
|
-
/** Our local identifier. */
|
|
1684
|
-
get id() {
|
|
1685
|
-
return this.myId;
|
|
1686
|
-
}
|
|
1687
|
-
/**
|
|
1688
|
-
* Generate this client's ECDH + ECDSA keypair (idempotent — safe to call
|
|
1689
|
-
* multiple times across reconnects; only the first call generates).
|
|
1690
|
-
*/
|
|
1691
|
-
async initialize() {
|
|
1692
|
-
if (this._initialized)
|
|
1693
|
-
return;
|
|
1694
|
-
if (!this.keyStore.getKeyPair(this.myId)) {
|
|
1695
|
-
await this.keyStore.generateOwnKeyPair(this.myId);
|
|
1696
|
-
}
|
|
1697
|
-
this._initialized = true;
|
|
1698
|
-
this._resolveReady();
|
|
1699
|
-
}
|
|
1700
|
-
/**
|
|
1701
|
-
* Build the keyExchange frame this client should send to peers when it
|
|
1702
|
-
* comes online. Application is responsible for actually transmitting it
|
|
1703
|
-
* (e.g. ws.send(JSON.stringify(frame))).
|
|
1704
|
-
*/
|
|
1705
|
-
async getOwnKeyExchange() {
|
|
1706
|
-
await this._ready;
|
|
1707
|
-
const ownEcdh = this.keyStore.getKeyPair(this.myId);
|
|
1708
|
-
const ownEcdsa = this.keyStore.getAuthKeyPair(this.myId);
|
|
1709
|
-
if (!ownEcdh || !ownEcdsa) {
|
|
1710
|
-
throw new Error("EncryptedSession.getOwnKeyExchange: keys not initialized");
|
|
1711
|
-
}
|
|
1712
|
-
return {
|
|
1713
|
-
keyExchange: {
|
|
1714
|
-
type: "handshake",
|
|
1715
|
-
userId: this.myId,
|
|
1716
|
-
ecdhPublicKey: await exportJwkBase64(ownEcdh.publicKey),
|
|
1717
|
-
ecdsaPublicKey: await exportJwkBase64(ownEcdsa.publicKey),
|
|
1718
|
-
},
|
|
1719
|
-
};
|
|
1720
|
-
}
|
|
1721
|
-
/**
|
|
1722
|
-
* Encrypt a plaintext to every peer we currently have a ratchet for, and
|
|
1723
|
-
* return one cipherMessage frame per recipient. The application sends
|
|
1724
|
-
* each frame over its transport.
|
|
1725
|
-
*
|
|
1726
|
-
* If there are no peers yet (or all peers are still mid-handshake), the
|
|
1727
|
-
* returned array may be empty — the caller should typically render the
|
|
1728
|
-
* outgoing message locally in that case (since there's no one to send
|
|
1729
|
-
* it to yet).
|
|
1730
|
-
*/
|
|
1731
|
-
async encrypt(plaintext) {
|
|
1732
|
-
await this._ready;
|
|
1733
|
-
const frames = [];
|
|
1734
|
-
for (const peerId of this.ratchets.keys()) {
|
|
1735
|
-
const ratchet = this.ratchets.get(peerId);
|
|
1736
|
-
const sessionId = this._sessionIdFor(peerId);
|
|
1737
|
-
const cipherMessage = await ratchet.encrypt(plaintext,
|
|
1738
|
-
/* newDhKey */ false, this.myId, peerId, sessionId, "specific");
|
|
1739
|
-
frames.push({ cipherMessage });
|
|
1740
|
-
}
|
|
1741
|
-
return frames;
|
|
1742
|
-
}
|
|
1743
|
-
/**
|
|
1744
|
-
* Hand an inbound frame to the session. Returns a typed result the
|
|
1745
|
-
* application acts on.
|
|
1746
|
-
*
|
|
1747
|
-
* - `kind: 'plaintext'` — decrypted; render `result.text`
|
|
1748
|
-
* - `kind: 'handshake'` — a peer's keys are now stored, and if
|
|
1749
|
-
* `result.outbound` is set, send it
|
|
1750
|
-
* - `kind: 'ignored'` — not for us, malformed, duplicate, etc.
|
|
1751
|
-
*
|
|
1752
|
-
* Calling code should always handle `'ignored'` (no UI side effect).
|
|
1753
|
-
*/
|
|
1754
|
-
async receive(frame) {
|
|
1755
|
-
await this._ready;
|
|
1756
|
-
// -- handshake frame -----------------------------------------------------
|
|
1757
|
-
if (isKeyExchangeFrame(frame)) {
|
|
1758
|
-
const kx = frame.keyExchange;
|
|
1759
|
-
const peerId = kx.userId;
|
|
1760
|
-
if (peerId === this.myId) {
|
|
1761
|
-
return { kind: "ignored", reason: "own keyExchange echo" };
|
|
1762
|
-
}
|
|
1763
|
-
const isNew = !this.ratchets.has(peerId);
|
|
1764
|
-
// (Re-)store the peer's pubkeys + (re-)build the ratchet if needed.
|
|
1765
|
-
const peerEcdh = await importJwkBase64(kx.ecdhPublicKey, "ECDH");
|
|
1766
|
-
const peerEcdsa = await importJwkBase64(kx.ecdsaPublicKey, "ECDSA");
|
|
1767
|
-
await this.keyStore.storeRemotePublicKeys(peerId, peerEcdh, peerEcdsa);
|
|
1768
|
-
if (isNew) {
|
|
1769
|
-
const isClient = this.myId < peerId;
|
|
1770
|
-
const ratchet = new DoubleRatchet(this.myId, peerId, "specific", isClient);
|
|
1771
|
-
await ratchet.initializeSession(isClient);
|
|
1772
|
-
this.ratchets.set(peerId, ratchet);
|
|
1773
|
-
}
|
|
1774
|
-
// Reciprocate exactly once per peer per session lifetime.
|
|
1775
|
-
let outbound = null;
|
|
1776
|
-
if (isNew && !this.sentHandshakeTo.has(peerId)) {
|
|
1777
|
-
outbound = await this.getOwnKeyExchange();
|
|
1778
|
-
this.sentHandshakeTo.add(peerId);
|
|
1779
|
-
}
|
|
1780
|
-
return { kind: "handshake", peerId, outbound };
|
|
1781
|
-
}
|
|
1782
|
-
// -- ciphertext frame ----------------------------------------------------
|
|
1783
|
-
if (isCipherFrame(frame)) {
|
|
1784
|
-
const cipherMessage = frame.cipherMessage;
|
|
1785
|
-
const header = cipherMessage?.header;
|
|
1786
|
-
if (!header) {
|
|
1787
|
-
return { kind: "ignored", reason: "cipherMessage has no header" };
|
|
1788
|
-
}
|
|
1789
|
-
if (header.senderId === this.myId) {
|
|
1790
|
-
return { kind: "ignored", reason: "own outbound echo" };
|
|
1791
|
-
}
|
|
1792
|
-
if (header.recipientId && header.recipientId !== this.myId) {
|
|
1793
|
-
return { kind: "ignored", reason: "addressed to a different peer" };
|
|
1794
|
-
}
|
|
1795
|
-
const ratchet = this.ratchets.get(header.senderId);
|
|
1796
|
-
if (!ratchet) {
|
|
1797
|
-
return {
|
|
1798
|
-
kind: "ignored",
|
|
1799
|
-
reason: `no ratchet with sender ${header.senderId} (handshake hasn't completed)`,
|
|
1800
|
-
};
|
|
1801
|
-
}
|
|
1802
|
-
const isClient = this.myId < header.senderId;
|
|
1803
|
-
let text;
|
|
1804
|
-
try {
|
|
1805
|
-
text = await ratchet.decrypt(cipherMessage, isClient);
|
|
1806
|
-
}
|
|
1807
|
-
catch (err) {
|
|
1808
|
-
// DoubleRatchet's wallclock-based replay-protection check throws
|
|
1809
|
-
// "CipherMessage too old, possible replay attack" for any frame
|
|
1810
|
-
// older than 5 minutes. Real replays still fail downstream because
|
|
1811
|
-
// the message key has been consumed; the wallclock check is a
|
|
1812
|
-
// defense-in-depth heuristic, not a security boundary. Treating it
|
|
1813
|
-
// as `ignored` lets the channel survive legacy backlog replays and
|
|
1814
|
-
// CF WS-idle-timeout reconnect cycles without spamming the
|
|
1815
|
-
// channel's ERROR stream.
|
|
1816
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
1817
|
-
if (msg.includes("too old") || msg.includes("replay attack")) {
|
|
1818
|
-
return { kind: "ignored", reason: `stale cipherMessage rejected: ${msg}` };
|
|
1819
|
-
}
|
|
1820
|
-
throw err;
|
|
1821
|
-
}
|
|
1822
|
-
return { kind: "plaintext", from: header.senderId, text, cipherMessage };
|
|
1823
|
-
}
|
|
1824
|
-
return { kind: "ignored", reason: "unknown frame type" };
|
|
1825
|
-
}
|
|
1826
|
-
// -- introspection ---------------------------------------------------------
|
|
1827
|
-
/** List of peer ids we have an established ratchet for. */
|
|
1828
|
-
peers() {
|
|
1829
|
-
return Array.from(this.ratchets.keys());
|
|
1830
|
-
}
|
|
1831
|
-
/** Has the handshake completed for this peer? */
|
|
1832
|
-
hasRatchetFor(peerId) {
|
|
1833
|
-
return this.ratchets.has(peerId);
|
|
1834
|
-
}
|
|
1835
|
-
/**
|
|
1836
|
-
* Tear down a peer's ratchet (e.g. when they leave the room). The peer's
|
|
1837
|
-
* stored keys remain in `KeyStore` so a fresh handshake can be set up
|
|
1838
|
-
* later without re-fetching them, but the ratchet state is dropped.
|
|
1839
|
-
*/
|
|
1840
|
-
forgetPeer(peerId) {
|
|
1841
|
-
this.ratchets.delete(peerId);
|
|
1842
|
-
this.sentHandshakeTo.delete(peerId);
|
|
1843
|
-
}
|
|
1844
|
-
// -- internals -------------------------------------------------------------
|
|
1845
|
-
/** Deterministic per-pair session id — same string on both sides. */
|
|
1846
|
-
_sessionIdFor(peerId) {
|
|
1847
|
-
return [this.myId, peerId].sort().join(":");
|
|
1848
|
-
}
|
|
1849
|
-
}
|
|
1850
|
-
// ---------------------------------------------------------------------------
|
|
1851
|
-
// Helpers — JWK <-> base64 string for key transport
|
|
1852
|
-
// ---------------------------------------------------------------------------
|
|
1853
|
-
async function exportJwkBase64(key) {
|
|
1854
|
-
const jwk = await crypto.subtle.exportKey("jwk", key);
|
|
1855
|
-
return btoa(JSON.stringify(jwk));
|
|
1856
|
-
}
|
|
1857
|
-
async function importJwkBase64(b64, type) {
|
|
1858
|
-
const jwk = JSON.parse(atob(b64));
|
|
1859
|
-
const algorithm = type === "ECDH"
|
|
1860
|
-
? { name: "ECDH", namedCurve: "P-384" }
|
|
1861
|
-
: { name: "ECDSA", namedCurve: "P-384" };
|
|
1862
|
-
const usages = type === "ECDH" ? [] : ["verify"];
|
|
1863
|
-
return await crypto.subtle.importKey("jwk", jwk, algorithm, true, usages);
|
|
1864
|
-
}
|
|
1865
|
-
// ---------------------------------------------------------------------------
|
|
1866
|
-
// Type guards
|
|
1867
|
-
// ---------------------------------------------------------------------------
|
|
1868
|
-
function isKeyExchangeFrame(frame) {
|
|
1869
|
-
const kx = frame.keyExchange;
|
|
1870
|
-
if (!kx || typeof kx !== "object")
|
|
1871
|
-
return false;
|
|
1872
|
-
const k = kx;
|
|
1873
|
-
return (typeof k.userId === "string" &&
|
|
1874
|
-
typeof k.ecdhPublicKey === "string" &&
|
|
1875
|
-
typeof k.ecdsaPublicKey === "string");
|
|
1876
|
-
}
|
|
1877
|
-
function isCipherFrame(frame) {
|
|
1878
|
-
const cm = frame.cipherMessage;
|
|
1879
|
-
if (!cm || typeof cm !== "object")
|
|
1880
|
-
return false;
|
|
1881
|
-
const m = cm;
|
|
1882
|
-
return Boolean(m.header && m.ciphertext && m.nonce);
|
|
1883
|
-
}
|
|
1884
|
-
|
|
1885
|
-
/**
|
|
1886
|
-
* Pure WebSocket transport — owns the socket lifecycle and nothing else.
|
|
1887
|
-
*
|
|
1888
|
-
* Hands raw frame strings up (via the `MESSAGE` event) and accepts raw frame
|
|
1889
|
-
* strings down (via `send`). Anything frame-shape, encryption, or serialization
|
|
1890
|
-
* related belongs in the layer above (e.g. `Network` for client↔server
|
|
1891
|
-
* encrypted pipes, `BroadcastChannel` for multi-peer E2E rooms).
|
|
1892
|
-
*
|
|
1893
|
-
* Responsibilities:
|
|
1894
|
-
* - Open / close the WebSocket
|
|
1895
|
-
* - Auto-reconnect with linear backoff after an unexpected close
|
|
1896
|
-
* - Buffer outbound frames while disconnected (capped queue)
|
|
1897
|
-
* - Emit lifecycle events (CONNECTED / DISCONNECTED / RECONNECTING / ERROR)
|
|
1898
|
-
* plus raw inbound MESSAGE events
|
|
1899
|
-
*/
|
|
1900
|
-
/** Monotonic source of unique transport ids when a caller doesn't supply one. */
|
|
1901
|
-
let __wsInstanceSeq = 0;
|
|
1902
|
-
class WSTransport extends EventCore {
|
|
1903
|
-
/**
|
|
1904
|
-
* Per-instance event scope. `EventCore` is a process-global static emitter,
|
|
1905
|
-
* so without namespacing every WSTransport would hear every other one's
|
|
1906
|
-
* frames. We prefix each event with this transport's `id` and expose
|
|
1907
|
-
* `on`/`off`/`emit` that scope transparently — callers still use the plain
|
|
1908
|
-
* `EventCoreEvents` enum and only ever see their own connection's events.
|
|
1909
|
-
*/
|
|
1910
|
-
id;
|
|
1911
|
-
emit = (event, data) => {
|
|
1912
|
-
EventCore.emit(this.scopedEvent(event), data);
|
|
1913
|
-
};
|
|
1914
|
-
on = (event, handler) => {
|
|
1915
|
-
EventCore.on(this.scopedEvent(event), handler);
|
|
1916
|
-
};
|
|
1917
|
-
off = (event, handler) => {
|
|
1918
|
-
EventCore.off(this.scopedEvent(event), handler);
|
|
1919
|
-
};
|
|
1920
|
-
scopedEvent(event) {
|
|
1921
|
-
return `${this.id}:${event}`;
|
|
1922
|
-
}
|
|
1923
|
-
url;
|
|
1924
|
-
urlProvider;
|
|
1925
|
-
socket = null;
|
|
1926
|
-
_isConnected = false;
|
|
1927
|
-
_isConnecting = false;
|
|
1928
|
-
// Reconnect state
|
|
1929
|
-
autoReconnect;
|
|
1930
|
-
reconnectDelay;
|
|
1931
|
-
maxReconnectAttempts;
|
|
1932
|
-
reconnectAttempts = 0;
|
|
1933
|
-
reconnectTimeout = null;
|
|
1934
|
-
// Outbound queue (raw frame strings — already serialized by caller)
|
|
1935
|
-
outboundQueue = [];
|
|
1936
|
-
maxQueueSize;
|
|
1937
|
-
// Heartbeat / liveness state
|
|
1938
|
-
heartbeatInterval;
|
|
1939
|
-
heartbeatTimeout;
|
|
1940
|
-
pingFrame;
|
|
1941
|
-
pongFrame;
|
|
1942
|
-
heartbeatTimer = null;
|
|
1943
|
-
pongTimer = null;
|
|
1944
|
-
constructor(opts) {
|
|
1945
|
-
super();
|
|
1946
|
-
if (!opts || !opts.url) {
|
|
1947
|
-
throw new Error("WSTransport: `url` is required");
|
|
1948
|
-
}
|
|
1949
|
-
this.url = opts.url;
|
|
1950
|
-
this.urlProvider = opts.urlProvider;
|
|
1951
|
-
this.id = opts.id ?? `ws${++__wsInstanceSeq}`;
|
|
1952
|
-
this.autoReconnect = opts.autoReconnect ?? true;
|
|
1953
|
-
this.reconnectDelay = opts.reconnectDelay ?? 3000;
|
|
1954
|
-
this.maxReconnectAttempts = opts.maxReconnectAttempts ?? 5;
|
|
1955
|
-
this.maxQueueSize = opts.maxQueueSize ?? 100;
|
|
1956
|
-
this.heartbeatInterval = opts.heartbeatInterval ?? 30000;
|
|
1957
|
-
this.heartbeatTimeout = opts.heartbeatTimeout ?? 10000;
|
|
1958
|
-
this.pingFrame = opts.pingFrame ?? "ping";
|
|
1959
|
-
this.pongFrame = opts.pongFrame ?? "pong";
|
|
1960
|
-
}
|
|
1961
|
-
/** Open the socket. Resolves when CONNECTED fires. Rejects on first error. */
|
|
1962
|
-
async connect() {
|
|
1963
|
-
if (this._isConnected || this._isConnecting) {
|
|
1964
|
-
throw new Error("WSTransport: already connected or connecting");
|
|
1965
|
-
}
|
|
1966
|
-
this._isConnecting = true;
|
|
1967
|
-
// On a RECONNECT, refresh the URL (the baked-in single-use ticket is spent).
|
|
1968
|
-
// The first connect uses `url` as-is. If the provider throws (e.g. can't
|
|
1969
|
-
// mint a ticket), surface it and let scheduleReconnect retry.
|
|
1970
|
-
if (this.urlProvider && this.reconnectAttempts > 0) {
|
|
1971
|
-
try {
|
|
1972
|
-
this.url = await this.urlProvider();
|
|
1973
|
-
}
|
|
1974
|
-
catch (err) {
|
|
1975
|
-
this._isConnecting = false;
|
|
1976
|
-
throw err;
|
|
1977
|
-
}
|
|
1978
|
-
}
|
|
1979
|
-
return new Promise((resolve, reject) => {
|
|
1980
|
-
try {
|
|
1981
|
-
const socket = new WebSocket(this.url);
|
|
1982
|
-
this.socket = socket;
|
|
1983
|
-
socket.onopen = () => {
|
|
1984
|
-
this._isConnected = true;
|
|
1985
|
-
this._isConnecting = false;
|
|
1986
|
-
this.reconnectAttempts = 0;
|
|
1987
|
-
this.emit(EventCoreEvents.CONNECTED, undefined);
|
|
1988
|
-
this.flushQueue();
|
|
1989
|
-
this.startHeartbeat();
|
|
1990
|
-
resolve();
|
|
1991
|
-
};
|
|
1992
|
-
socket.onmessage = (event) => {
|
|
1993
|
-
// Heartbeat ack — swallow it (don't surface as a MESSAGE) and clear
|
|
1994
|
-
// the liveness deadline. The layer above never sees keep-alive frames.
|
|
1995
|
-
if (event.data === this.pongFrame) {
|
|
1996
|
-
if (this.pongTimer) {
|
|
1997
|
-
clearTimeout(this.pongTimer);
|
|
1998
|
-
this.pongTimer = null;
|
|
1999
|
-
}
|
|
2000
|
-
return;
|
|
2001
|
-
}
|
|
2002
|
-
// Raw inbound frame — layer above is responsible for parsing.
|
|
2003
|
-
this.emit(EventCoreEvents.MESSAGE, event.data);
|
|
2004
|
-
};
|
|
2005
|
-
socket.onerror = (err) => {
|
|
2006
|
-
this.emit(EventCoreEvents.ERROR, err);
|
|
2007
|
-
if (this._isConnecting) {
|
|
2008
|
-
this._isConnecting = false;
|
|
2009
|
-
reject(err);
|
|
2010
|
-
}
|
|
2011
|
-
};
|
|
2012
|
-
socket.onclose = () => {
|
|
2013
|
-
const wasConnected = this._isConnected;
|
|
2014
|
-
this._isConnected = false;
|
|
2015
|
-
this._isConnecting = false;
|
|
2016
|
-
this.stopHeartbeat();
|
|
2017
|
-
this.emit(EventCoreEvents.DISCONNECTED, undefined);
|
|
2018
|
-
if (this.autoReconnect && wasConnected) {
|
|
2019
|
-
this.scheduleReconnect();
|
|
2020
|
-
}
|
|
2021
|
-
};
|
|
2022
|
-
}
|
|
2023
|
-
catch (err) {
|
|
2024
|
-
this._isConnecting = false;
|
|
2025
|
-
reject(err);
|
|
2026
|
-
}
|
|
2027
|
-
});
|
|
2028
|
-
}
|
|
2029
|
-
/** Close the socket and stop reconnecting. */
|
|
2030
|
-
disconnect() {
|
|
2031
|
-
if (this.reconnectTimeout) {
|
|
2032
|
-
clearTimeout(this.reconnectTimeout);
|
|
2033
|
-
this.reconnectTimeout = null;
|
|
2034
|
-
}
|
|
2035
|
-
this.stopHeartbeat();
|
|
2036
|
-
this.autoReconnect = false;
|
|
2037
|
-
if (this.socket) {
|
|
2038
|
-
try {
|
|
2039
|
-
this.socket.close();
|
|
2040
|
-
}
|
|
2041
|
-
catch { /* ignore */ }
|
|
2042
|
-
this.socket = null;
|
|
2043
|
-
}
|
|
2044
|
-
this._isConnected = false;
|
|
2045
|
-
this._isConnecting = false;
|
|
2046
|
-
}
|
|
2047
|
-
/**
|
|
2048
|
-
* Send a raw frame. If currently disconnected, the frame is queued and sent
|
|
2049
|
-
* after the next successful reconnect (up to `maxQueueSize`).
|
|
2050
|
-
*/
|
|
2051
|
-
send(frame) {
|
|
2052
|
-
if (this._isConnected && this.socket) {
|
|
2053
|
-
this.socket.send(frame);
|
|
2054
|
-
return;
|
|
2055
|
-
}
|
|
2056
|
-
if (this.outboundQueue.length >= this.maxQueueSize) {
|
|
2057
|
-
throw new Error(`WSTransport: outbound queue full (${this.maxQueueSize}); drop or wait for reconnect`);
|
|
2058
|
-
}
|
|
2059
|
-
this.outboundQueue.push(frame);
|
|
2060
|
-
}
|
|
2061
|
-
isConnected() { return this._isConnected; }
|
|
2062
|
-
isConnecting() { return this._isConnecting; }
|
|
2063
|
-
queuedFrames() { return this.outboundQueue.length; }
|
|
2064
|
-
reconnectAttemptCount() { return this.reconnectAttempts; }
|
|
2065
|
-
// -- internals -------------------------------------------------------------
|
|
2066
|
-
/** Begin the keep-alive loop (no-op when disabled). */
|
|
2067
|
-
startHeartbeat() {
|
|
2068
|
-
if (this.heartbeatInterval <= 0)
|
|
2069
|
-
return;
|
|
2070
|
-
this.stopHeartbeat();
|
|
2071
|
-
this.heartbeatTimer = setInterval(() => this.sendHeartbeat(), this.heartbeatInterval);
|
|
2072
|
-
}
|
|
2073
|
-
/** Stop the keep-alive loop and clear any pending liveness deadline. */
|
|
2074
|
-
stopHeartbeat() {
|
|
2075
|
-
if (this.heartbeatTimer) {
|
|
2076
|
-
clearInterval(this.heartbeatTimer);
|
|
2077
|
-
this.heartbeatTimer = null;
|
|
2078
|
-
}
|
|
2079
|
-
if (this.pongTimer) {
|
|
2080
|
-
clearTimeout(this.pongTimer);
|
|
2081
|
-
this.pongTimer = null;
|
|
2082
|
-
}
|
|
2083
|
-
}
|
|
2084
|
-
/**
|
|
2085
|
-
* Send one ping and arm a liveness deadline. If no `pongFrame` arrives before
|
|
2086
|
-
* `heartbeatTimeout`, the connection is considered dead and we force a
|
|
2087
|
-
* reconnect — this is what catches a silently-dropped TCP connection that
|
|
2088
|
-
* never fired `onclose` (the failure mode that "loses connection with the
|
|
2089
|
-
* Accelerator" on long sessions).
|
|
2090
|
-
*/
|
|
2091
|
-
sendHeartbeat() {
|
|
2092
|
-
if (!this._isConnected || !this.socket)
|
|
2093
|
-
return;
|
|
2094
|
-
try {
|
|
2095
|
-
this.socket.send(this.pingFrame);
|
|
2096
|
-
}
|
|
2097
|
-
catch {
|
|
2098
|
-
this.forceReconnect();
|
|
2099
|
-
return;
|
|
2100
|
-
}
|
|
2101
|
-
// A still-pending deadline means the previous pong never came back.
|
|
2102
|
-
if (this.pongTimer)
|
|
2103
|
-
return;
|
|
2104
|
-
this.pongTimer = setTimeout(() => {
|
|
2105
|
-
this.pongTimer = null;
|
|
2106
|
-
this.forceReconnect();
|
|
2107
|
-
}, this.heartbeatTimeout);
|
|
2108
|
-
}
|
|
2109
|
-
/**
|
|
2110
|
-
* Tear down a connection we believe is dead and kick off reconnect, without
|
|
2111
|
-
* waiting on the OS/browser to eventually fire `onclose` (which a half-open
|
|
2112
|
-
* socket may never do). We detach the handlers first so the normal close path
|
|
2113
|
-
* can't double-fire the reconnect.
|
|
2114
|
-
*/
|
|
2115
|
-
forceReconnect() {
|
|
2116
|
-
this.stopHeartbeat();
|
|
2117
|
-
const sock = this.socket;
|
|
2118
|
-
const wasConnected = this._isConnected;
|
|
2119
|
-
this.socket = null;
|
|
2120
|
-
this._isConnected = false;
|
|
2121
|
-
this._isConnecting = false;
|
|
2122
|
-
if (sock) {
|
|
2123
|
-
sock.onopen = null;
|
|
2124
|
-
sock.onmessage = null;
|
|
2125
|
-
sock.onerror = null;
|
|
2126
|
-
sock.onclose = null;
|
|
2127
|
-
try {
|
|
2128
|
-
sock.close();
|
|
2129
|
-
}
|
|
2130
|
-
catch { /* already gone */ }
|
|
2131
|
-
}
|
|
2132
|
-
this.emit(EventCoreEvents.ERROR, new Error("WSTransport: heartbeat timeout"));
|
|
2133
|
-
this.emit(EventCoreEvents.DISCONNECTED, undefined);
|
|
2134
|
-
if (this.autoReconnect && wasConnected) {
|
|
2135
|
-
this.scheduleReconnect();
|
|
2136
|
-
}
|
|
2137
|
-
}
|
|
2138
|
-
scheduleReconnect() {
|
|
2139
|
-
if (this.maxReconnectAttempts > 0 &&
|
|
2140
|
-
this.reconnectAttempts >= this.maxReconnectAttempts) {
|
|
2141
|
-
this.emit(EventCoreEvents.ERROR, new Error("WSTransport: max reconnect attempts reached"));
|
|
2142
|
-
return;
|
|
2143
|
-
}
|
|
2144
|
-
this.reconnectAttempts++;
|
|
2145
|
-
this.emit(EventCoreEvents.RECONNECTING, { attempt: this.reconnectAttempts });
|
|
2146
|
-
this.reconnectTimeout = setTimeout(async () => {
|
|
2147
|
-
try {
|
|
2148
|
-
await this.connect();
|
|
2149
|
-
}
|
|
2150
|
-
catch {
|
|
2151
|
-
// A failed reconnect never opened the socket, so its `onclose` won't
|
|
2152
|
-
// re-schedule (that path is guarded by `wasConnected`). Re-schedule here
|
|
2153
|
-
// so we keep retrying (with a fresh ticket each time) until we succeed
|
|
2154
|
-
// or hit the attempt cap.
|
|
2155
|
-
if (this.autoReconnect)
|
|
2156
|
-
this.scheduleReconnect();
|
|
2157
|
-
}
|
|
2158
|
-
}, this.reconnectDelay);
|
|
2159
|
-
}
|
|
2160
|
-
flushQueue() {
|
|
2161
|
-
if (this.outboundQueue.length === 0 || !this.socket)
|
|
2162
|
-
return;
|
|
2163
|
-
const pending = this.outboundQueue;
|
|
2164
|
-
this.outboundQueue = [];
|
|
2165
|
-
for (const frame of pending) {
|
|
2166
|
-
try {
|
|
2167
|
-
this.socket.send(frame);
|
|
2168
|
-
}
|
|
2169
|
-
catch {
|
|
2170
|
-
// re-queue if a flush fails
|
|
2171
|
-
this.outboundQueue.push(frame);
|
|
2172
|
-
}
|
|
2173
|
-
}
|
|
2174
|
-
}
|
|
2175
|
-
}
|
|
2176
|
-
|
|
2177
|
-
/**
|
|
2178
|
-
* BroadcastChannel — a turnkey end-to-end encrypted channel for multi-peer
|
|
2179
|
-
* "room"-style apps (e.g. a chat). Wires together:
|
|
2180
|
-
*
|
|
2181
|
-
* - `WSTransport` for socket lifecycle, reconnect, and offline frame queue
|
|
2182
|
-
* - `EncryptedSession` for per-peer Double Ratchet state, handshake
|
|
2183
|
-
* reciprocation, fan-out encryption, and recipient filtering
|
|
2184
|
-
*
|
|
2185
|
-
* The host app only deals with high-level events and method calls; it never
|
|
2186
|
-
* touches `new WebSocket(...)` or builds cipherMessage frames by hand.
|
|
2187
|
-
*
|
|
2188
|
-
* Wire protocol (kept compatible with the existing chat protocol so this
|
|
2189
|
-
* drops into the deployed Accelerator without server changes):
|
|
2190
|
-
*
|
|
2191
|
-
* outbound: `{ keyExchange: {...} }` — produced by us on `announce()`
|
|
2192
|
-
* `{ cipherMessage: {...} }` — produced by `send(text)`,
|
|
2193
|
-
* one frame per peer
|
|
2194
|
-
* arbitrary JSON — sent via `sendRaw(obj)`
|
|
2195
|
-
*
|
|
2196
|
-
* inbound: `{ keyExchange: {...} }` — routed into EncryptedSession;
|
|
2197
|
-
* emits `peer_handshake`
|
|
2198
|
-
* `{ cipherMessage: {...} }` — decrypted; emits `message`
|
|
2199
|
-
* anything else — passes through as `raw_frame`
|
|
2200
|
-
* (the app decides what to do)
|
|
2201
|
-
*
|
|
2202
|
-
* **Event scoping.** Unlike `Network`, BroadcastChannel uses a per-instance
|
|
2203
|
-
* `EventTarget`, so two channels (e.g. two rooms in the same app) don't
|
|
2204
|
-
* cross-contaminate each other's events.
|
|
2205
|
-
*
|
|
2206
|
-
* Note: this class shadows the `BroadcastChannel` Web API global (cross-tab
|
|
2207
|
-
* messaging) only when imported by name. They're unrelated.
|
|
2208
|
-
*/
|
|
2209
|
-
// ---------------------------------------------------------------------------
|
|
2210
|
-
// Event names
|
|
2211
|
-
// ---------------------------------------------------------------------------
|
|
2212
|
-
/**
|
|
2213
|
-
* Event names BroadcastChannel emits. Lifecycle events reuse the
|
|
2214
|
-
* `EventCoreEvents` string values (`connected`, `disconnected`, etc.) so
|
|
2215
|
-
* apps can use shared constants if they want. Channel-specific events are
|
|
2216
|
-
* prefixed `channel:` to avoid colliding with other event systems in the
|
|
2217
|
-
* same app.
|
|
2218
|
-
*/
|
|
2219
|
-
const BroadcastChannelEvents = {
|
|
2220
|
-
CONNECTED: EventCoreEvents.CONNECTED,
|
|
2221
|
-
DISCONNECTED: EventCoreEvents.DISCONNECTED,
|
|
2222
|
-
RECONNECTING: EventCoreEvents.RECONNECTING,
|
|
2223
|
-
ERROR: EventCoreEvents.ERROR,
|
|
2224
|
-
/** A peer's ratchet is now ready. `detail.peerId` is the peer's id. */
|
|
2225
|
-
PEER_HANDSHAKE: "channel:peer_handshake",
|
|
2226
|
-
/** Decrypted message. `detail` is `{ from, text, cipherMessage }`. */
|
|
2227
|
-
MESSAGE: "channel:message",
|
|
2228
|
-
/** Inbound frame BroadcastChannel didn't recognize. App handles it. */
|
|
2229
|
-
RAW_FRAME: "channel:raw_frame",
|
|
2230
|
-
};
|
|
2231
|
-
// ---------------------------------------------------------------------------
|
|
2232
|
-
// BroadcastChannel
|
|
2233
|
-
// ---------------------------------------------------------------------------
|
|
2234
|
-
class BroadcastChannel {
|
|
2235
|
-
transport;
|
|
2236
|
-
session;
|
|
2237
|
-
myId;
|
|
2238
|
-
autoAnnounce;
|
|
2239
|
-
announced = false;
|
|
2240
|
-
/** Per-instance EventTarget — events do NOT leak across BroadcastChannels. */
|
|
2241
|
-
events = new EventTarget();
|
|
2242
|
-
constructor(opts) {
|
|
2243
|
-
if (!opts || !opts.myId) {
|
|
2244
|
-
throw new Error("BroadcastChannel: `myId` is required");
|
|
2245
|
-
}
|
|
2246
|
-
this.myId = opts.myId;
|
|
2247
|
-
this.autoAnnounce = opts.autoAnnounce ?? false;
|
|
2248
|
-
this.transport = new WSTransport(opts);
|
|
2249
|
-
this.session = new EncryptedSession({ myId: opts.myId });
|
|
2250
|
-
// Pass-through lifecycle events from the transport. `EventCore.on` is
|
|
2251
|
-
// static so it routes back through the shared singleton; we bridge it
|
|
2252
|
-
// into our instance-scoped event target.
|
|
2253
|
-
this.transport.on(EventCoreEvents.CONNECTED, () => {
|
|
2254
|
-
// Reset the announce flag on every (re)connect. CF Workers WebSockets
|
|
2255
|
-
// hit an idle timeout and force reconnects; without resetting, the
|
|
2256
|
-
// SDK would short-circuit announce() and any peer who joined the
|
|
2257
|
-
// room after our last announce would never see our pubkey.
|
|
2258
|
-
this.announced = false;
|
|
2259
|
-
this.emit(BroadcastChannelEvents.CONNECTED);
|
|
2260
|
-
if (this.autoAnnounce) {
|
|
2261
|
-
void this.announce();
|
|
2262
|
-
}
|
|
2263
|
-
});
|
|
2264
|
-
this.transport.on(EventCoreEvents.DISCONNECTED, () => {
|
|
2265
|
-
this.emit(BroadcastChannelEvents.DISCONNECTED);
|
|
2266
|
-
});
|
|
2267
|
-
this.transport.on(EventCoreEvents.RECONNECTING, (e) => {
|
|
2268
|
-
this.emit(BroadcastChannelEvents.RECONNECTING, e.detail);
|
|
2269
|
-
});
|
|
2270
|
-
this.transport.on(EventCoreEvents.ERROR, (e) => {
|
|
2271
|
-
this.emit(BroadcastChannelEvents.ERROR, e.detail);
|
|
2272
|
-
});
|
|
2273
|
-
// Route inbound raw frames through our framing/decryption logic.
|
|
2274
|
-
this.transport.on(EventCoreEvents.MESSAGE, (e) => {
|
|
2275
|
-
void this.handleInboundFrame(e.detail);
|
|
2276
|
-
});
|
|
2277
|
-
}
|
|
2278
|
-
// -- event API -------------------------------------------------------------
|
|
2279
|
-
/** Subscribe to a channel event. Handler receives a CustomEvent. */
|
|
2280
|
-
on(event, handler) {
|
|
2281
|
-
this.events.addEventListener(event, handler);
|
|
2282
|
-
}
|
|
2283
|
-
/** Unsubscribe. */
|
|
2284
|
-
off(event, handler) {
|
|
2285
|
-
this.events.removeEventListener(event, handler);
|
|
2286
|
-
}
|
|
2287
|
-
emit(event, detail) {
|
|
2288
|
-
this.events.dispatchEvent(new CustomEvent(event, { detail }));
|
|
2289
|
-
}
|
|
2290
|
-
// -- lifecycle -------------------------------------------------------------
|
|
2291
|
-
/** Generate our keys (idempotent) and open the WebSocket. */
|
|
2292
|
-
async connect() {
|
|
2293
|
-
await this.session.initialize();
|
|
2294
|
-
await this.transport.connect();
|
|
2295
|
-
}
|
|
2296
|
-
/** Close the WebSocket and stop reconnecting. */
|
|
2297
|
-
disconnect() {
|
|
2298
|
-
this.transport.disconnect();
|
|
2299
|
-
}
|
|
2300
|
-
/**
|
|
2301
|
-
* Send our `keyExchange` frame to the room. Idempotent — only sends on the
|
|
2302
|
-
* first call. Call this once your higher-level handshake (e.g. sending a
|
|
2303
|
-
* `{name}` identification frame) has completed.
|
|
2304
|
-
*/
|
|
2305
|
-
async announce() {
|
|
2306
|
-
if (this.announced)
|
|
2307
|
-
return;
|
|
2308
|
-
this.announced = true;
|
|
2309
|
-
const frame = await this.session.getOwnKeyExchange();
|
|
2310
|
-
this.transport.send(JSON.stringify(frame));
|
|
2311
|
-
}
|
|
2312
|
-
/**
|
|
2313
|
-
* Encrypt `plaintext` to every peer we have a ratchet with, and send each
|
|
2314
|
-
* cipherMessage as its own WS frame. Returns the number of peers we sent to
|
|
2315
|
-
* (0 if no peers are handshaken yet — in which case the caller should
|
|
2316
|
-
* typically render the message locally for the sender and try again later).
|
|
2317
|
-
*/
|
|
2318
|
-
async send(plaintext) {
|
|
2319
|
-
const frames = await this.session.encrypt(plaintext);
|
|
2320
|
-
for (const frame of frames) {
|
|
2321
|
-
this.transport.send(JSON.stringify(frame));
|
|
2322
|
-
}
|
|
2323
|
-
return frames.length;
|
|
2324
|
-
}
|
|
2325
|
-
/**
|
|
2326
|
-
* Send an arbitrary JSON-serializable frame as-is. The app uses this for
|
|
2327
|
-
* any frame that isn't channel-managed (e.g. a chat's `{name}`, `{file}`,
|
|
2328
|
-
* or unencrypted `{message}`).
|
|
2329
|
-
*/
|
|
2330
|
-
sendRaw(frame) {
|
|
2331
|
-
this.transport.send(JSON.stringify(frame));
|
|
2332
|
-
}
|
|
2333
|
-
// -- introspection ---------------------------------------------------------
|
|
2334
|
-
/** List of peer ids we have an established ratchet for. */
|
|
2335
|
-
peers() {
|
|
2336
|
-
return this.session.peers();
|
|
2337
|
-
}
|
|
2338
|
-
/** Drop a peer's ratchet (e.g. when they leave the room). */
|
|
2339
|
-
forgetPeer(peerId) {
|
|
2340
|
-
this.session.forgetPeer(peerId);
|
|
2341
|
-
}
|
|
2342
|
-
isConnected() {
|
|
2343
|
-
return this.transport.isConnected();
|
|
2344
|
-
}
|
|
2345
|
-
// -- internals -------------------------------------------------------------
|
|
2346
|
-
async handleInboundFrame(raw) {
|
|
2347
|
-
let parsed;
|
|
2348
|
-
try {
|
|
2349
|
-
parsed = JSON.parse(raw);
|
|
2350
|
-
}
|
|
2351
|
-
catch (err) {
|
|
2352
|
-
this.emit(BroadcastChannelEvents.ERROR, err);
|
|
2353
|
-
return;
|
|
2354
|
-
}
|
|
2355
|
-
if (!parsed || typeof parsed !== "object") {
|
|
2356
|
-
this.emit(BroadcastChannelEvents.RAW_FRAME, parsed);
|
|
2357
|
-
return;
|
|
2358
|
-
}
|
|
2359
|
-
const obj = parsed;
|
|
2360
|
-
// Channel-managed: keyExchange & cipherMessage. Anything else is RAW_FRAME.
|
|
2361
|
-
if ("keyExchange" in obj || "cipherMessage" in obj) {
|
|
2362
|
-
try {
|
|
2363
|
-
const result = await this.session.receive(obj);
|
|
2364
|
-
if (result.kind === "handshake") {
|
|
2365
|
-
if (result.outbound) {
|
|
2366
|
-
this.transport.send(JSON.stringify(result.outbound));
|
|
2367
|
-
}
|
|
2368
|
-
this.emit(BroadcastChannelEvents.PEER_HANDSHAKE, { peerId: result.peerId });
|
|
2369
|
-
}
|
|
2370
|
-
else if (result.kind === "plaintext") {
|
|
2371
|
-
this.emit(BroadcastChannelEvents.MESSAGE, {
|
|
2372
|
-
from: result.from,
|
|
2373
|
-
text: result.text,
|
|
2374
|
-
cipherMessage: result.cipherMessage,
|
|
2375
|
-
});
|
|
2376
|
-
}
|
|
2377
|
-
// `ignored` is intentionally swallowed; subscribe to ERROR for bad frames.
|
|
2378
|
-
}
|
|
2379
|
-
catch (err) {
|
|
2380
|
-
this.emit(BroadcastChannelEvents.ERROR, err);
|
|
2381
|
-
}
|
|
2382
|
-
return;
|
|
2383
|
-
}
|
|
2384
|
-
this.emit(BroadcastChannelEvents.RAW_FRAME, obj);
|
|
2385
|
-
}
|
|
2386
|
-
}
|
|
2387
|
-
|
|
2388
|
-
function _loadWasmModule (sync, filepath, src, imports) {
|
|
2389
|
-
function _instantiateOrCompile(source, imports, stream) {
|
|
2390
|
-
var instantiateFunc = stream ? WebAssembly.instantiateStreaming : WebAssembly.instantiate;
|
|
2391
|
-
var compileFunc = stream ? WebAssembly.compileStreaming : WebAssembly.compile;
|
|
2392
|
-
|
|
2393
|
-
if (imports) {
|
|
2394
|
-
return instantiateFunc(source, imports)
|
|
2395
|
-
} else {
|
|
2396
|
-
return compileFunc(source)
|
|
2397
|
-
}
|
|
2398
|
-
}
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
var buf = null;
|
|
2402
|
-
var isNode = typeof process !== 'undefined' && process.versions != null && process.versions.node != null;
|
|
2403
|
-
if (isNode) {
|
|
2404
|
-
|
|
2405
|
-
buf = Buffer.from(src, 'base64');
|
|
2406
|
-
|
|
2407
|
-
} else {
|
|
2408
|
-
|
|
2409
|
-
var raw = globalThis.atob(src);
|
|
2410
|
-
var rawLength = raw.length;
|
|
2411
|
-
buf = new Uint8Array(new ArrayBuffer(rawLength));
|
|
2412
|
-
for(var i = 0; i < rawLength; i++) {
|
|
2413
|
-
buf[i] = raw.charCodeAt(i);
|
|
2414
|
-
}
|
|
2415
|
-
|
|
2416
|
-
}
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
{
|
|
2420
|
-
return _instantiateOrCompile(buf, imports, false)
|
|
2421
|
-
}
|
|
2422
|
-
}
|
|
2423
|
-
|
|
2424
|
-
function loadBundledBn128(imports){return _loadWasmModule(0, null, 'AGFzbQEAAAABlAESYAJ/fwBgAX8AYAF/AX9gAn9/AX9gA39/fwF/YAN/f38AYAN/fn8AYAJ/fgBgBH9/f38AYAV/f39/fwBgBH9/f38Bf2AHf39/f39/fwBgBn9/f39/fwBgCn9/f39/f39/f38AYAV/f39/fwF/YAd/f39/f39/AX9gCX9/f39/f39/fwF/YAt/f39/f39/f39/fwF/Ag8BA2VudgZtZW1vcnkCABkDwQK/AgABAgEDAwQEBQAABgcIBQIFBQAABQAAAAACAgABBQgJBQUFCAgICAACAgUFAAAFAAAAAAICAAEFCAkFBQUICAgIAAIFAAACAgIBAQAAAAMDAwAABQUFAAAFBQUAAAAAAAICBQAFAAAAAAUFBQUFCgALCQoACwkICAMACAgCAAAJDAwFBQwACA0JCAICAQEABQUABQUAAAAAAwAIAgIJCAACAgIBAQAAAAMDAwAABQUFAAAFBQUAAAAAAAICBQAFAAAAAAUFBQUFCgALCQoACwkICAUDAAgIAgAACQwMBQUMBQMACAgCAAAJDAwFBQwFBQkJCQkJAAICAQEABQAFBQACAAADAAgCCQgAAgIBAQAFBQAFBQAAAAADAAgCAgkIAAIFAAAAAAgIBQAAAAAAAAAAAAAAAAAAAAAEDg8QEQUH9SSoAghpbnRfY29weQAACGludF96ZXJvAAEHaW50X29uZQADCmludF9pc1plcm8AAgZpbnRfZXEABAdpbnRfZ3RlAAUHaW50X2FkZAAGB2ludF9zdWIABwdpbnRfbXVsAAgKaW50X3NxdWFyZQAJDWludF9zcXVhcmVPbGQACgdpbnRfZGl2AA0OaW50X2ludmVyc2VNb2QADghmMW1fY29weQAACGYxbV96ZXJvAAEKZjFtX2lzWmVybwACBmYxbV9lcQAEB2YxbV9hZGQAEAdmMW1fc3ViABEHZjFtX25lZwASDmYxbV9pc05lZ2F0aXZlABkJZjFtX2lzT25lAA8IZjFtX3NpZ24AGgtmMW1fbVJlZHVjdAATB2YxbV9tdWwAFApmMW1fc3F1YXJlABUNZjFtX3NxdWFyZU9sZAAWEmYxbV9mcm9tTW9udGdvbWVyeQAYEGYxbV90b01vbnRnb21lcnkAFwtmMW1faW52ZXJzZQAbB2YxbV9vbmUAHAhmMW1fbG9hZAAdD2YxbV90aW1lc1NjYWxhcgAeB2YxbV9leHAAJhBmMW1fYmF0Y2hJbnZlcnNlAB8IZjFtX3NxcnQAJwxmMW1faXNTcXVhcmUAKBVmMW1fYmF0Y2hUb01vbnRnb21lcnkAIBdmMW1fYmF0Y2hGcm9tTW9udGdvbWVyeQAhCGZybV9jb3B5AAAIZnJtX3plcm8AAQpmcm1faXNaZXJvAAIGZnJtX2VxAAQHZnJtX2FkZAAqB2ZybV9zdWIAKwdmcm1fbmVnACwOZnJtX2lzTmVnYXRpdmUAMwlmcm1faXNPbmUAKQhmcm1fc2lnbgA0C2ZybV9tUmVkdWN0AC0HZnJtX211bAAuCmZybV9zcXVhcmUALw1mcm1fc3F1YXJlT2xkADASZnJtX2Zyb21Nb250Z29tZXJ5ADIQZnJtX3RvTW9udGdvbWVyeQAxC2ZybV9pbnZlcnNlADUHZnJtX29uZQA2CGZybV9sb2FkADcPZnJtX3RpbWVzU2NhbGFyADgHZnJtX2V4cABAEGZybV9iYXRjaEludmVyc2UAOQhmcm1fc3FydABBDGZybV9pc1NxdWFyZQBCFWZybV9iYXRjaFRvTW9udGdvbWVyeQA6F2ZybV9iYXRjaEZyb21Nb250Z29tZXJ5ADsGZnJfYWRkACoGZnJfc3ViACsGZnJfbmVnACwGZnJfbXVsAEMJZnJfc3F1YXJlAEQKZnJfaW52ZXJzZQBFDWZyX2lzTmVnYXRpdmUARgdmcl9jb3B5AAAHZnJfemVybwABBmZyX29uZQA2CWZyX2lzWmVybwACBWZyX2VxAAQMZzFtX211bHRpZXhwAHESZzFtX211bHRpZXhwX2NodW5rAHASZzFtX211bHRpZXhwQWZmaW5lAHUYZzFtX211bHRpZXhwQWZmaW5lX2NodW5rAHQKZzFtX2lzWmVybwBIEGcxbV9pc1plcm9BZmZpbmUARwZnMW1fZXEAUAtnMW1fZXFNaXhlZABPDGcxbV9lcUFmZmluZQBOCGcxbV9jb3B5AEwOZzFtX2NvcHlBZmZpbmUASwhnMW1femVybwBKDmcxbV96ZXJvQWZmaW5lAEkKZzFtX2RvdWJsZQBSEGcxbV9kb3VibGVBZmZpbmUAUQdnMW1fYWRkAFUMZzFtX2FkZE1peGVkAFQNZzFtX2FkZEFmZmluZQBTB2cxbV9uZWcAVw1nMW1fbmVnQWZmaW5lAFYHZzFtX3N1YgBaDGcxbV9zdWJNaXhlZABZDWcxbV9zdWJBZmZpbmUAWBJnMW1fZnJvbU1vbnRnb21lcnkAXBhnMW1fZnJvbU1vbnRnb21lcnlBZmZpbmUAWxBnMW1fdG9Nb250Z29tZXJ5AF4WZzFtX3RvTW9udGdvbWVyeUFmZmluZQBdD2cxbV90aW1lc1NjYWxhcgB2FWcxbV90aW1lc1NjYWxhckFmZmluZQB3DWcxbV9ub3JtYWxpemUAYwpnMW1fTEVNdG9VAGUKZzFtX0xFTXRvQwBmCmcxbV9VdG9MRU0AZwpnMW1fQ3RvTEVNAGgPZzFtX2JhdGNoTEVNdG9VAGkPZzFtX2JhdGNoTEVNdG9DAGoPZzFtX2JhdGNoVXRvTEVNAGsPZzFtX2JhdGNoQ3RvTEVNAGwMZzFtX3RvQWZmaW5lAF8OZzFtX3RvSmFjb2JpYW4ATRFnMW1fYmF0Y2hUb0FmZmluZQBiE2cxbV9iYXRjaFRvSmFjb2JpYW4AbQtnMW1faW5DdXJ2ZQBhEWcxbV9pbkN1cnZlQWZmaW5lAGAHZnJtX2ZmdAB9CGZybV9pZmZ0AH4KZnJtX3Jhd2ZmdAB7C2ZybV9mZnRKb2luAH8OZnJtX2ZmdEpvaW5FeHQAgAERZnJtX2ZmdEpvaW5FeHRJbnYAgQEKZnJtX2ZmdE1peACCAQxmcm1fZmZ0RmluYWwAgwEdZnJtX3ByZXBhcmVMYWdyYW5nZUV2YWx1YXRpb24AhAEIcG9sX3plcm8AhQEPcG9sX2NvbnN0cnVjdExDAIYBDHFhcF9idWlsZEFCQwCHAQtxYXBfam9pbkFCQwCIAQxxYXBfYmF0Y2hBZGQAiQEKZjJtX2lzWmVybwCKAQlmMm1faXNPbmUAiwEIZjJtX3plcm8AjAEHZjJtX29uZQCNAQhmMm1fY29weQCOAQdmMm1fbXVsAI8BCGYybV9tdWwxAJABCmYybV9zcXVhcmUAkQEHZjJtX2FkZACSAQdmMm1fc3ViAJMBB2YybV9uZWcAlAEIZjJtX3NpZ24AmwENZjJtX2Nvbmp1Z2F0ZQCVARJmMm1fZnJvbU1vbnRnb21lcnkAlwEQZjJtX3RvTW9udGdvbWVyeQCWAQZmMm1fZXEAmAELZjJtX2ludmVyc2UAmQEHZjJtX2V4cACeAQ9mMm1fdGltZXNTY2FsYXIAmgEQZjJtX2JhdGNoSW52ZXJzZQCdAQhmMm1fc3FydACfAQxmMm1faXNTcXVhcmUAoAEOZjJtX2lzTmVnYXRpdmUAnAEMZzJtX211bHRpZXhwAMsBEmcybV9tdWx0aWV4cF9jaHVuawDKARJnMm1fbXVsdGlleHBBZmZpbmUAzwEYZzJtX211bHRpZXhwQWZmaW5lX2NodW5rAM4BCmcybV9pc1plcm8AogEQZzJtX2lzWmVyb0FmZmluZQChAQZnMm1fZXEAqgELZzJtX2VxTWl4ZWQAqQEMZzJtX2VxQWZmaW5lAKgBCGcybV9jb3B5AKYBDmcybV9jb3B5QWZmaW5lAKUBCGcybV96ZXJvAKQBDmcybV96ZXJvQWZmaW5lAKMBCmcybV9kb3VibGUArAEQZzJtX2RvdWJsZUFmZmluZQCrAQdnMm1fYWRkAK8BDGcybV9hZGRNaXhlZACuAQ1nMm1fYWRkQWZmaW5lAK0BB2cybV9uZWcAsQENZzJtX25lZ0FmZmluZQCwAQdnMm1fc3ViALQBDGcybV9zdWJNaXhlZACzAQ1nMm1fc3ViQWZmaW5lALIBEmcybV9mcm9tTW9udGdvbWVyeQC2ARhnMm1fZnJvbU1vbnRnb21lcnlBZmZpbmUAtQEQZzJtX3RvTW9udGdvbWVyeQC4ARZnMm1fdG9Nb250Z29tZXJ5QWZmaW5lALcBD2cybV90aW1lc1NjYWxhcgDQARVnMm1fdGltZXNTY2FsYXJBZmZpbmUA0QENZzJtX25vcm1hbGl6ZQC9AQpnMm1fTEVNdG9VAL8BCmcybV9MRU10b0MAwAEKZzJtX1V0b0xFTQDBAQpnMm1fQ3RvTEVNAMIBD2cybV9iYXRjaExFTXRvVQDDAQ9nMm1fYmF0Y2hMRU10b0MAxAEPZzJtX2JhdGNoVXRvTEVNAMUBD2cybV9iYXRjaEN0b0xFTQDGAQxnMm1fdG9BZmZpbmUAuQEOZzJtX3RvSmFjb2JpYW4ApwERZzJtX2JhdGNoVG9BZmZpbmUAvAETZzJtX2JhdGNoVG9KYWNvYmlhbgDHAQtnMm1faW5DdXJ2ZQC7ARFnMm1faW5DdXJ2ZUFmZmluZQC6AQtnMW1fdGltZXNGcgDSAQdnMW1fZmZ0ANgBCGcxbV9pZmZ0ANkBCmcxbV9yYXdmZnQA1gELZzFtX2ZmdEpvaW4A2gEOZzFtX2ZmdEpvaW5FeHQA2wERZzFtX2ZmdEpvaW5FeHRJbnYA3AEKZzFtX2ZmdE1peADdAQxnMW1fZmZ0RmluYWwA3gEdZzFtX3ByZXBhcmVMYWdyYW5nZUV2YWx1YXRpb24A3wELZzJtX3RpbWVzRnIA4AEHZzJtX2ZmdADmAQhnMm1faWZmdADnAQpnMm1fcmF3ZmZ0AOQBC2cybV9mZnRKb2luAOgBDmcybV9mZnRKb2luRXh0AOkBEWcybV9mZnRKb2luRXh0SW52AOoBCmcybV9mZnRNaXgA6wEMZzJtX2ZmdEZpbmFsAOwBHWcybV9wcmVwYXJlTGFncmFuZ2VFdmFsdWF0aW9uAO0BEWcxbV90aW1lc0ZyQWZmaW5lAO4BEWcybV90aW1lc0ZyQWZmaW5lAO8BEWZybV9iYXRjaEFwcGx5S2V5APABEWcxbV9iYXRjaEFwcGx5S2V5APEBFmcxbV9iYXRjaEFwcGx5S2V5TWl4ZWQA8gERZzJtX2JhdGNoQXBwbHlLZXkA8wEWZzJtX2JhdGNoQXBwbHlLZXlNaXhlZAD0AQpmNm1faXNaZXJvAPYBCWY2bV9pc09uZQD3AQhmNm1femVybwD4AQdmNm1fb25lAPkBCGY2bV9jb3B5APoBB2Y2bV9tdWwA+wEKZjZtX3NxdWFyZQD8AQdmNm1fYWRkAP0BB2Y2bV9zdWIA/gEHZjZtX25lZwD/AQhmNm1fc2lnbgCAAhJmNm1fZnJvbU1vbnRnb21lcnkAggIQZjZtX3RvTW9udGdvbWVyeQCBAgZmNm1fZXEAgwILZjZtX2ludmVyc2UAhAIHZjZtX2V4cACIAg9mNm1fdGltZXNTY2FsYXIAhQIQZjZtX2JhdGNoSW52ZXJzZQCHAg5mNm1faXNOZWdhdGl2ZQCGAgpmdG1faXNaZXJvAIoCCWZ0bV9pc09uZQCLAghmdG1femVybwCMAgdmdG1fb25lAI0CCGZ0bV9jb3B5AI4CB2Z0bV9tdWwAjwIIZnRtX211bDEAkAIKZnRtX3NxdWFyZQCRAgdmdG1fYWRkAJICB2Z0bV9zdWIAkwIHZnRtX25lZwCUAghmdG1fc2lnbgCbAg1mdG1fY29uanVnYXRlAJUCEmZ0bV9mcm9tTW9udGdvbWVyeQCXAhBmdG1fdG9Nb250Z29tZXJ5AJYCBmZ0bV9lcQCYAgtmdG1faW52ZXJzZQCZAgdmdG1fZXhwAJ4CD2Z0bV90aW1lc1NjYWxhcgCaAhBmdG1fYmF0Y2hJbnZlcnNlAJ0CCGZ0bV9zcXJ0AJ8CDGZ0bV9pc1NxdWFyZQCgAg5mdG1faXNOZWdhdGl2ZQCcAhRibjEyOF9fZnJvYmVuaXVzTWFwMACpAhRibjEyOF9fZnJvYmVuaXVzTWFwMQCqAhRibjEyOF9fZnJvYmVuaXVzTWFwMgCrAhRibjEyOF9fZnJvYmVuaXVzTWFwMwCsAhRibjEyOF9fZnJvYmVuaXVzTWFwNACtAhRibjEyOF9fZnJvYmVuaXVzTWFwNQCuAhRibjEyOF9fZnJvYmVuaXVzTWFwNgCvAhRibjEyOF9fZnJvYmVuaXVzTWFwNwCwAhRibjEyOF9fZnJvYmVuaXVzTWFwOACxAhRibjEyOF9fZnJvYmVuaXVzTWFwOQCyAhBibjEyOF9wYWlyaW5nRXExALkCEGJuMTI4X3BhaXJpbmdFcTIAugIQYm4xMjhfcGFpcmluZ0VxMwC7AhBibjEyOF9wYWlyaW5nRXE0ALwCEGJuMTI4X3BhaXJpbmdFcTUAvQINYm4xMjhfcGFpcmluZwC+Ag9ibjEyOF9wcmVwYXJlRzEAowIPYm4xMjhfcHJlcGFyZUcyAKUCEGJuMTI4X21pbGxlckxvb3AAqAIZYm4xMjhfZmluYWxFeHBvbmVudGlhdGlvbgC4AhxibjEyOF9maW5hbEV4cG9uZW50aWF0aW9uT2xkALMCD2JuMTI4X19tdWxCeTAyNACmAhJibjEyOF9fbXVsQnkwMjRPbGQApwIXYm4xMjhfX2N5Y2xvdG9taWNTcXVhcmUAtQIXYm4xMjhfX2N5Y2xvdG9taWNFeHBfdzAAtgIK/d8DvwIqACABIAApAwA3AwAgASAAKQMINwMIIAEgACkDEDcDECABIAApAxg3AxgLHgAgAEIANwMAIABCADcDCCAAQgA3AxAgAEIANwMYCzMAIAApAxhQBEAgACkDEFAEQCAAKQMIUARAIAApAwBQDwVBAA8LBUEADwsFQQAPC0EADwseACAAQgE3AwAgAEIANwMIIABCADcDECAAQgA3AxgLRwAgACkDGCABKQMYUQRAIAApAxAgASkDEFEEQCAAKQMIIAEpAwhRBEAgACkDACABKQMAUQ8FQQAPCwVBAA8LBUEADwtBAA8LfQAgACkDGCABKQMYVARAQQAPBSAAKQMYIAEpAxhWBEBBAQ8FIAApAxAgASkDEFQEQEEADwUgACkDECABKQMQVgRAQQEPBSAAKQMIIAEpAwhUBEBBAA8FIAApAwggASkDCFYEQEEBDwUgACkDACABKQMAWg8LCwsLCwtBAA8L1AEBAX4gADUCACABNQIAfCEDIAIgAz4CACAANQIEIAE1AgR8IANCIIh8IQMgAiADPgIEIAA1AgggATUCCHwgA0IgiHwhAyACIAM+AgggADUCDCABNQIMfCADQiCIfCEDIAIgAz4CDCAANQIQIAE1AhB8IANCIIh8IQMgAiADPgIQIAA1AhQgATUCFHwgA0IgiHwhAyACIAM+AhQgADUCGCABNQIYfCADQiCIfCEDIAIgAz4CGCAANQIcIAE1Ahx8IANCIIh8IQMgAiADPgIcIANCIIinC4wCAQF+IAA1AgAgATUCAH0hAyACIANC/////w+DPgIAIAA1AgQgATUCBH0gA0Igh3whAyACIANC/////w+DPgIEIAA1AgggATUCCH0gA0Igh3whAyACIANC/////w+DPgIIIAA1AgwgATUCDH0gA0Igh3whAyACIANC/////w+DPgIMIAA1AhAgATUCEH0gA0Igh3whAyACIANC/////w+DPgIQIAA1AhQgATUCFH0gA0Igh3whAyACIANC/////w+DPgIUIAA1AhggATUCGH0gA0Igh3whAyACIANC/////w+DPgIYIAA1AhwgATUCHH0gA0Igh3whAyACIANC/////w+DPgIcIANCIIenC48QEgF+AX4BfgF+AX4BfgF+AX4BfgF+AX4BfgF+AX4BfgF+AX4BfiADQv////8PgyAANQIAIgUgATUCACIGfnwhAyAEIANCIIh8IQQgAiADPgIAIARCIIghAyAEQv////8PgyAFIAE1AgQiCH58IQQgAyAEQiCIfCEDIARC/////w+DIAA1AgQiByAGfnwhBCADIARCIIh8IQMgAiAEPgIEIANCIIghBCADQv////8PgyAFIAE1AggiCn58IQMgBCADQiCIfCEEIANC/////w+DIAcgCH58IQMgBCADQiCIfCEEIANC/////w+DIAA1AggiCSAGfnwhAyAEIANCIIh8IQQgAiADPgIIIARCIIghAyAEQv////8PgyAFIAE1AgwiDH58IQQgAyAEQiCIfCEDIARC/////w+DIAcgCn58IQQgAyAEQiCIfCEDIARC/////w+DIAkgCH58IQQgAyAEQiCIfCEDIARC/////w+DIAA1AgwiCyAGfnwhBCADIARCIIh8IQMgAiAEPgIMIANCIIghBCADQv////8PgyAFIAE1AhAiDn58IQMgBCADQiCIfCEEIANC/////w+DIAcgDH58IQMgBCADQiCIfCEEIANC/////w+DIAkgCn58IQMgBCADQiCIfCEEIANC/////w+DIAsgCH58IQMgBCADQiCIfCEEIANC/////w+DIAA1AhAiDSAGfnwhAyAEIANCIIh8IQQgAiADPgIQIARCIIghAyAEQv////8PgyAFIAE1AhQiEH58IQQgAyAEQiCIfCEDIARC/////w+DIAcgDn58IQQgAyAEQiCIfCEDIARC/////w+DIAkgDH58IQQgAyAEQiCIfCEDIARC/////w+DIAsgCn58IQQgAyAEQiCIfCEDIARC/////w+DIA0gCH58IQQgAyAEQiCIfCEDIARC/////w+DIAA1AhQiDyAGfnwhBCADIARCIIh8IQMgAiAEPgIUIANCIIghBCADQv////8PgyAFIAE1AhgiEn58IQMgBCADQiCIfCEEIANC/////w+DIAcgEH58IQMgBCADQiCIfCEEIANC/////w+DIAkgDn58IQMgBCADQiCIfCEEIANC/////w+DIAsgDH58IQMgBCADQiCIfCEEIANC/////w+DIA0gCn58IQMgBCADQiCIfCEEIANC/////w+DIA8gCH58IQMgBCADQiCIfCEEIANC/////w+DIAA1AhgiESAGfnwhAyAEIANCIIh8IQQgAiADPgIYIARCIIghAyAEQv////8PgyAFIAE1AhwiFH58IQQgAyAEQiCIfCEDIARC/////w+DIAcgEn58IQQgAyAEQiCIfCEDIARC/////w+DIAkgEH58IQQgAyAEQiCIfCEDIARC/////w+DIAsgDn58IQQgAyAEQiCIfCEDIARC/////w+DIA0gDH58IQQgAyAEQiCIfCEDIARC/////w+DIA8gCn58IQQgAyAEQiCIfCEDIARC/////w+DIBEgCH58IQQgAyAEQiCIfCEDIARC/////w+DIAA1AhwiEyAGfnwhBCADIARCIIh8IQMgAiAEPgIcIANCIIghBCADQv////8PgyAHIBR+fCEDIAQgA0IgiHwhBCADQv////8PgyAJIBJ+fCEDIAQgA0IgiHwhBCADQv////8PgyALIBB+fCEDIAQgA0IgiHwhBCADQv////8PgyANIA5+fCEDIAQgA0IgiHwhBCADQv////8PgyAPIAx+fCEDIAQgA0IgiHwhBCADQv////8PgyARIAp+fCEDIAQgA0IgiHwhBCADQv////8PgyATIAh+fCEDIAQgA0IgiHwhBCACIAM+AiAgBEIgiCEDIARC/////w+DIAkgFH58IQQgAyAEQiCIfCEDIARC/////w+DIAsgEn58IQQgAyAEQiCIfCEDIARC/////w+DIA0gEH58IQQgAyAEQiCIfCEDIARC/////w+DIA8gDn58IQQgAyAEQiCIfCEDIARC/////w+DIBEgDH58IQQgAyAEQiCIfCEDIARC/////w+DIBMgCn58IQQgAyAEQiCIfCEDIAIgBD4CJCADQiCIIQQgA0L/////D4MgCyAUfnwhAyAEIANCIIh8IQQgA0L/////D4MgDSASfnwhAyAEIANCIIh8IQQgA0L/////D4MgDyAQfnwhAyAEIANCIIh8IQQgA0L/////D4MgESAOfnwhAyAEIANCIIh8IQQgA0L/////D4MgEyAMfnwhAyAEIANCIIh8IQQgAiADPgIoIARCIIghAyAEQv////8PgyANIBR+fCEEIAMgBEIgiHwhAyAEQv////8PgyAPIBJ+fCEEIAMgBEIgiHwhAyAEQv////8PgyARIBB+fCEEIAMgBEIgiHwhAyAEQv////8PgyATIA5+fCEEIAMgBEIgiHwhAyACIAQ+AiwgA0IgiCEEIANC/////w+DIA8gFH58IQMgBCADQiCIfCEEIANC/////w+DIBEgEn58IQMgBCADQiCIfCEEIANC/////w+DIBMgEH58IQMgBCADQiCIfCEEIAIgAz4CMCAEQiCIIQMgBEL/////D4MgESAUfnwhBCADIARCIIh8IQMgBEL/////D4MgEyASfnwhBCADIARCIIh8IQMgAiAEPgI0IANCIIghBCADQv////8PgyATIBR+fCEDIAQgA0IgiHwhBCACIAM+AjggBEIgiCEDIAIgBD4CPAuMEgwBfgF+AX4BfgF+AX4BfgF+AX4BfgF+AX5CACECQgAhAyACQv////8Pg0IBhiECIANCAYYgAkIgiHwhAyACQv////8PgyAANQIAIgYgBn58IQIgAyACQiCIfCEDIAEgAj4CACADIQQgBEIgiCEFQgAhAkIAIQMgAkL/////D4MgBiAANQIEIgd+fCECIAMgAkIgiHwhAyACQv////8Pg0IBhiECIANCAYYgAkIgiHwhAyACQv////8PgyAEQv////8Pg3whAiADIAJCIIh8IAV8IQMgASACPgIEIAMhBCAEQiCIIQVCACECQgAhAyACQv////8PgyAGIAA1AggiCH58IQIgAyACQiCIfCEDIAJC/////w+DQgGGIQIgA0IBhiACQiCIfCEDIAJC/////w+DIAcgB358IQIgAyACQiCIfCEDIAJC/////w+DIARC/////w+DfCECIAMgAkIgiHwgBXwhAyABIAI+AgggAyEEIARCIIghBUIAIQJCACEDIAJC/////w+DIAYgADUCDCIJfnwhAiADIAJCIIh8IQMgAkL/////D4MgByAIfnwhAiADIAJCIIh8IQMgAkL/////D4NCAYYhAiADQgGGIAJCIIh8IQMgAkL/////D4MgBEL/////D4N8IQIgAyACQiCIfCAFfCEDIAEgAj4CDCADIQQgBEIgiCEFQgAhAkIAIQMgAkL/////D4MgBiAANQIQIgp+fCECIAMgAkIgiHwhAyACQv////8PgyAHIAl+fCECIAMgAkIgiHwhAyACQv////8Pg0IBhiECIANCAYYgAkIgiHwhAyACQv////8PgyAIIAh+fCECIAMgAkIgiHwhAyACQv////8PgyAEQv////8Pg3whAiADIAJCIIh8IAV8IQMgASACPgIQIAMhBCAEQiCIIQVCACECQgAhAyACQv////8PgyAGIAA1AhQiC358IQIgAyACQiCIfCEDIAJC/////w+DIAcgCn58IQIgAyACQiCIfCEDIAJC/////w+DIAggCX58IQIgAyACQiCIfCEDIAJC/////w+DQgGGIQIgA0IBhiACQiCIfCEDIAJC/////w+DIARC/////w+DfCECIAMgAkIgiHwgBXwhAyABIAI+AhQgAyEEIARCIIghBUIAIQJCACEDIAJC/////w+DIAYgADUCGCIMfnwhAiADIAJCIIh8IQMgAkL/////D4MgByALfnwhAiADIAJCIIh8IQMgAkL/////D4MgCCAKfnwhAiADIAJCIIh8IQMgAkL/////D4NCAYYhAiADQgGGIAJCIIh8IQMgAkL/////D4MgCSAJfnwhAiADIAJCIIh8IQMgAkL/////D4MgBEL/////D4N8IQIgAyACQiCIfCAFfCEDIAEgAj4CGCADIQQgBEIgiCEFQgAhAkIAIQMgAkL/////D4MgBiAANQIcIg1+fCECIAMgAkIgiHwhAyACQv////8PgyAHIAx+fCECIAMgAkIgiHwhAyACQv////8PgyAIIAt+fCECIAMgAkIgiHwhAyACQv////8PgyAJIAp+fCECIAMgAkIgiHwhAyACQv////8Pg0IBhiECIANCAYYgAkIgiHwhAyACQv////8PgyAEQv////8Pg3whAiADIAJCIIh8IAV8IQMgASACPgIcIAMhBCAEQiCIIQVCACECQgAhAyACQv////8PgyAHIA1+fCECIAMgAkIgiHwhAyACQv////8PgyAIIAx+fCECIAMgAkIgiHwhAyACQv////8PgyAJIAt+fCECIAMgAkIgiHwhAyACQv////8Pg0IBhiECIANCAYYgAkIgiHwhAyACQv////8PgyAKIAp+fCECIAMgAkIgiHwhAyACQv////8PgyAEQv////8Pg3whAiADIAJCIIh8IAV8IQMgASACPgIgIAMhBCAEQiCIIQVCACECQgAhAyACQv////8PgyAIIA1+fCECIAMgAkIgiHwhAyACQv////8PgyAJIAx+fCECIAMgAkIgiHwhAyACQv////8PgyAKIAt+fCECIAMgAkIgiHwhAyACQv////8Pg0IBhiECIANCAYYgAkIgiHwhAyACQv////8PgyAEQv////8Pg3whAiADIAJCIIh8IAV8IQMgASACPgIkIAMhBCAEQiCIIQVCACECQgAhAyACQv////8PgyAJIA1+fCECIAMgAkIgiHwhAyACQv////8PgyAKIAx+fCECIAMgAkIgiHwhAyACQv////8Pg0IBhiECIANCAYYgAkIgiHwhAyACQv////8PgyALIAt+fCECIAMgAkIgiHwhAyACQv////8PgyAEQv////8Pg3whAiADIAJCIIh8IAV8IQMgASACPgIoIAMhBCAEQiCIIQVCACECQgAhAyACQv////8PgyAKIA1+fCECIAMgAkIgiHwhAyACQv////8PgyALIAx+fCECIAMgAkIgiHwhAyACQv////8Pg0IBhiECIANCAYYgAkIgiHwhAyACQv////8PgyAEQv////8Pg3whAiADIAJCIIh8IAV8IQMgASACPgIsIAMhBCAEQiCIIQVCACECQgAhAyACQv////8PgyALIA1+fCECIAMgAkIgiHwhAyACQv////8Pg0IBhiECIANCAYYgAkIgiHwhAyACQv////8PgyAMIAx+fCECIAMgAkIgiHwhAyACQv////8PgyAEQv////8Pg3whAiADIAJCIIh8IAV8IQMgASACPgIwIAMhBCAEQiCIIQVCACECQgAhAyACQv////8PgyAMIA1+fCECIAMgAkIgiHwhAyACQv////8Pg0IBhiECIANCAYYgAkIgiHwhAyACQv////8PgyAEQv////8Pg3whAiADIAJCIIh8IAV8IQMgASACPgI0IAMhBCAEQiCIIQVCACECQgAhAyACQv////8Pg0IBhiECIANCAYYgAkIgiHwhAyACQv////8PgyANIA1+fCECIAMgAkIgiHwhAyACQv////8PgyAEQv////8Pg3whAiADIAJCIIh8IAV8IQMgASACPgI4IAMhBCAEQiCIIQUgASAEPgI8CwoAIAAgACABEAgLtgEBAX4gADUAACABfiEDIAIgAz4AACAANQAEIAF+IANCIIh8IQMgAiADPgAEIAA1AAggAX4gA0IgiHwhAyACIAM+AAggADUADCABfiADQiCIfCEDIAIgAz4ADCAANQAQIAF+IANCIIh8IQMgAiADPgAQIAA1ABQgAX4gA0IgiHwhAyACIAM+ABQgADUAGCABfiADQiCIfCEDIAIgAz4AGCAANQAcIAF+IANCIIh8IQMgAiADPgAcC04CAX4BfyAAIQMgAzUAACABfCECIAMgAj4AACACQiCIIQICQANAIAJQDQEgA0EEaiEDIAM1AAAgAnwhAiADIAI+AAAgAkIgiCECDAALCwurAgcBfwF/AX8BfwF+AX4BfyACBEAgAiEFBUHIACEFCyADBEAgAyEEBUHoACEECyAAIAQQACABQSgQACAFEAFBiAEQAUEf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LCADQiCIfEHoAzUCECAEfnwhAyAAIAM+AiwgADUCMCADQiCIfEHoAzUCFCAEfnwhAyAAIAM+AjAgADUCNCADQiCIfEHoAzUCGCAEfnwhAyAAIAM+AjQgADUCOCADQiCIfEHoAzUCHCAEfnwhAyAAIAM+AjhBiAYgA0IgiD4CHEGIBiAAQSBqIAEQEAu+HyMBfgF+AX4BfgF+AX4BfgF+AX4BfgF+AX4BfgF+AX4BfgF+AX4BfgF+AX4BfgF+AX4BfgF+AX4BfgF+AX4BfgF+AX4BfgF+QonHmaQOIQUgA0L/////D4MgADUCACIGIAE1AgAiB358IQMgBCADQiCIfCEEIANC/////w+DIAV+Qv////8PgyEIIANC/////w+DQQA1AugDIgkgCH58IQMgBCADQiCIfCEEIARCIIghAyAEQv////8PgyAGIAE1AgQiC358IQQgAyAEQiCIfCEDIARC/////w+DIAA1AgQiCiAHfnwhBCADIARCIIh8IQMgBEL/////D4NBADUC7AMiDSAIfnwhBCADIARCIIh8IQMgBEL/////D4MgBX5C/////w+DIQwgBEL/////D4MgCSAMfnwhBCADIARCIIh8IQMgA0IgiCEEIANC/////w+DIAYgATUCCCIPfnwhAyAEIANCIIh8IQQgA0L/////D4MgCiALfnwhAyAEIANCIIh8IQQgA0L/////D4MgADUCCCIOIAd+fCEDIAQgA0IgiHwhBCADQv////8PgyANIAx+fCEDIAQgA0IgiHwhBCADQv////8Pg0EANQLwAyIRIAh+fCEDIAQgA0IgiHwhBCADQv////8PgyAFfkL/////D4MhECADQv////8PgyAJIBB+fCEDIAQgA0IgiHwhBCAEQiCIIQMgBEL/////D4MgBiABNQIMIhN+fCEEIAMgBEIgiHwhAyAEQv////8PgyAKIA9+fCEEIAMgBEIgiHwhAyAEQv////8PgyAOIAt+fCEEIAMgBEIgiHwhAyAEQv////8PgyAANQIMIhIgB358IQQgAyAEQiCIfCEDIARC/////w+DIA0gEH58IQQgAyAEQiCIfCEDIARC/////w+DIBEgDH58IQQgAyAEQiCIfCEDIARC/////w+DQQA1AvQDIhUgCH58IQQgAyAEQiCIfCEDIARC/////w+DIAV+Qv////8PgyEUIARC/////w+DIAkgFH58IQQgAyAEQiCIfCEDIANCIIghBCADQv////8PgyAGIAE1AhAiF358IQMgBCADQiCIfCEEIANC/////w+DIAogE358IQMgBCADQiCIfCEEIANC/////w+DIA4gD358IQMgBCADQiCIfCEEIANC/////w+DIBIgC358IQMgBCADQiCIfCEEIANC/////w+DIAA1AhAiFiAHfnwhAyAEIANCIIh8IQQgA0L/////D4MgDSAUfnwhAyAEIANCIIh8IQQgA0L/////D4MgESAQfnwhAyAEIANCIIh8IQQgA0L/////D4MgFSAMfnwhAyAEIANCIIh8IQQgA0L/////D4NBADUC+AMiGSAIfnwhAyAEIANCIIh8IQQgA0L/////D4MgBX5C/////w+DIRggA0L/////D4MgCSAYfnwhAyAEIANCIIh8IQQgBEIgiCEDIARC/////w+DIAYgATUCFCIbfnwhBCADIARCIIh8IQMgBEL/////D4MgCiAXfnwhBCADIARCIIh8IQMgBEL/////D4MgDiATfnwhBCADIARCIIh8IQMgBEL/////D4MgEiAPfnwhBCADIARCIIh8IQMgBEL/////D4MgFiALfnwhBCADIARCIIh8IQMgBEL/////D4MgADUCFCIaIAd+fCEEIAMgBEIgiHwhAyAEQv////8PgyANIBh+fCEEIAMgBEIgiHwhAyAEQv////8PgyARIBR+fCEEIAMgBEIgiHwhAyAEQv////8PgyAVIBB+fCEEIAMgBEIgiHwhAyAEQv////8PgyAZIAx+fCEEIAMgBEIgiHwhAyAEQv////8Pg0EANQL8AyIdIAh+fCEEIAMgBEIgiHwhAyAEQv////8PgyAFfkL/////D4MhHCAEQv////8PgyAJIBx+fCEEIAMgBEIgiHwhAyADQiCIIQQgA0L/////D4MgBiABNQIYIh9+fCEDIAQgA0IgiHwhBCADQv////8PgyAKIBt+fCEDIAQgA0IgiHwhBCADQv////8PgyAOIBd+fCEDIAQgA0IgiHwhBCADQv////8PgyASIBN+fCEDIAQgA0IgiHwhBCADQv////8PgyAWIA9+fCEDIAQgA0IgiHwhBCADQv////8PgyAaIAt+fCEDIAQgA0IgiHwhBCADQv////8PgyAANQIYIh4gB358IQMgBCADQiCIfCEEIANC/////w+DIA0gHH58IQMgBCADQiCIfCEEIANC/////w+DIBEgGH58IQMgBCADQiCIfCEEIANC/////w+DIBUgFH58IQMgBCADQiCIfCEEIANC/////w+DIBkgEH58IQMgBCADQiCIfCEEIANC/////w+DIB0gDH58IQMgBCADQiCIfCEEIANC/////w+DQQA1AoAEIiEgCH58IQMgBCADQiCIfCEEIANC/////w+DIAV+Qv////8PgyEgIANC/////w+DIAkgIH58IQMgBCADQiCIfCEEIARCIIghAyAEQv////8PgyAGIAE1AhwiI358IQQgAyAEQiCIfCEDIARC/////w+DIAogH358IQQgAyAEQiCIfCEDIARC/////w+DIA4gG358IQQgAyAEQiCIfCEDIARC/////w+DIBIgF358IQQgAyAEQiCIfCEDIARC/////w+DIBYgE358IQQgAyAEQiCIfCEDIARC/////w+DIBogD358IQQgAyAEQiCIfCEDIARC/////w+DIB4gC358IQQgAyAEQiCIfCEDIARC/////w+DIAA1AhwiIiAHfnwhBCADIARCIIh8IQMgBEL/////D4MgDSAgfnwhBCADIARCIIh8IQMgBEL/////D4MgESAcfnwhBCADIARCIIh8IQMgBEL/////D4MgFSAYfnwhBCADIARCIIh8IQMgBEL/////D4MgGSAUfnwhBCADIARCIIh8IQMgBEL/////D4MgHSAQfnwhBCADIARCIIh8IQMgBEL/////D4MgISAMfnwhBCADIARCIIh8IQMgBEL/////D4NBADUChAQiJSAIfnwhBCADIARCIIh8IQMgBEL/////D4MgBX5C/////w+DISQgBEL/////D4MgCSAkfnwhBCADIARCIIh8IQMgA0IgiCEEIANC/////w+DIAogI358IQMgBCADQiCIfCEEIANC/////w+DIA4gH358IQMgBCADQiCIfCEEIANC/////w+DIBIgG358IQMgBCADQiCIfCEEIANC/////w+DIBYgF358IQMgBCADQiCIfCEEIANC/////w+DIBogE358IQMgBCADQiCIfCEEIANC/////w+DIB4gD358IQMgBCADQiCIfCEEIANC/////w+DICIgC358IQMgBCADQiCIfCEEIANC/////w+DIA0gJH58IQMgBCADQiCIfCEEIANC/////w+DIBEgIH58IQMgBCADQiCIfCEEIANC/////w+DIBUgHH58IQMgBCADQiCIfCEEIANC/////w+DIBkgGH58IQMgBCADQiCIfCEEIANC/////w+DIB0gFH58IQMgBCADQiCIfCEEIANC/////w+DICEgEH58IQMgBCADQiCIfCEEIANC/////w+DICUgDH58IQMgBCADQiCIfCEEIAIgAz4CACAEQiCIIQMgBEL/////D4MgDiAjfnwhBCADIARCIIh8IQMgBEL/////D4MgEiAffnwhBCADIARCIIh8IQMgBEL/////D4MgFiAbfnwhBCADIARCIIh8IQMgBEL/////D4MgGiAXfnwhBCADIARCIIh8IQMgBEL/////D4MgHiATfnwhBCADIARCIIh8IQMgBEL/////D4MgIiAPfnwhBCADIARCIIh8IQMgBEL/////D4MgESAkfnwhBCADIARCIIh8IQMgBEL/////D4MgFSAgfnwhBCADIARCIIh8IQMgBEL/////D4MgGSAcfnwhBCADIARCIIh8IQMgBEL/////D4MgHSAYfnwhBCADIARCIIh8IQMgBEL/////D4MgISAUfnwhBCADIARCIIh8IQMgBEL/////D4MgJSAQfnwhBCADIARCIIh8IQMgAiAEPgIEIANCIIghBCADQv////8PgyASICN+fCEDIAQgA0IgiHwhBCADQv////8PgyAWIB9+fCEDIAQgA0IgiHwhBCADQv////8PgyAaIBt+fCEDIAQgA0IgiHwhBCADQv////8PgyAeIBd+fCEDIAQgA0IgiHwhBCADQv////8PgyAiIBN+fCEDIAQgA0IgiHwhBCADQv////8PgyAVICR+fCEDIAQgA0IgiHwhBCADQv////8PgyAZICB+fCEDIAQgA0IgiHwhBCADQv////8PgyAdIBx+fCEDIAQgA0IgiHwhBCADQv////8PgyAhIBh+fCEDIAQgA0IgiHwhBCADQv////8PgyAlIBR+fCEDIAQgA0IgiHwhBCACIAM+AgggBEIgiCEDIARC/////w+DIBYgI358IQQgAyAEQiCIfCEDIARC/////w+DIBogH358IQQgAyAEQiCIfCEDIARC/////w+DIB4gG358IQQgAyAEQiCIfCEDIARC/////w+DICIgF358IQQgAyAEQiCIfCEDIARC/////w+DIBkgJH58IQQgAyAEQiCIfCEDIARC/////w+DIB0gIH58IQQgAyAEQiCIfCEDIARC/////w+DICEgHH58IQQgAyAEQiCIfCEDIARC/////w+DICUgGH58IQQgAyAEQiCIfCEDIAIgBD4CDCADQiCIIQQgA0L/////D4MgGiAjfnwhAyAEIANCIIh8IQQgA0L/////D4MgHiAffnwhAyAEIANCIIh8IQQgA0L/////D4MgIiAbfnwhAyAEIANCIIh8IQQgA0L/////D4MgHSAkfnwhAyAEIANCIIh8IQQgA0L/////D4MgISAgfnwhAyAEIANCIIh8IQQgA0L/////D4MgJSAcfnwhAyAEIANCIIh8IQQgAiADPgIQIARCIIghAyAEQv////8PgyAeICN+fCEEIAMgBEIgiHwhAyAEQv////8PgyAiIB9+fCEEIAMgBEIgiHwhAyAEQv////8PgyAhICR+fCEEIAMgBEIgiHwhAyAEQv////8PgyAlICB+fCEEIAMgBEIgiHwhAyACIAQ+AhQgA0IgiCEEIANC/////w+DICIgI358IQMgBCADQiCIfCEEIANC/////w+DICUgJH58IQMgBCADQiCIfCEEIAIgAz4CGCAEQiCIIQMgAiAEPgIcIAOnBEAgAkHoAyACEAcaBSACQegDEAUEQCACQegDIAIQBxoLCwu7IR0BfgF+AX4BfgF+AX4BfgF+AX4BfgF+AX4BfgF+AX4BfgF+AX4BfgF+AX4BfgF+AX4BfgF+AX4BfgF+QonHmaQOIQZCACECQgAhAyACQv////8Pg0IBhiECIANCAYYgAkIgiHwhAyACQv////8PgyAANQIAIgcgB358IQIgAyACQiCIfCEDIAJC/////w+DIAZ+Qv////8PgyEIIAJC/////w+DQQA1AugDIgkgCH58IQIgAyACQiCIfCEDIAMhBCAEQiCIIQVCACECQgAhAyACQv////8PgyAHIAA1AgQiCn58IQIgAyACQiCIfCEDIAJC/////w+DQgGGIQIgA0IBhiACQiCIfCEDIAJC/////w+DIARC/////w+DfCECIAMgAkIgiHwgBXwhAyACQv////8Pg0EANQLsAyIMIAh+fCECIAMgAkIgiHwhAyACQv////8PgyAGfkL/////D4MhCyACQv////8PgyAJIAt+fCECIAMgAkIgiHwhAyADIQQgBEIgiCEFQgAhAkIAIQMgAkL/////D4MgByAANQIIIg1+fCECIAMgAkIgiHwhAyACQv////8Pg0IBhiECIANCAYYgAkIgiHwhAyACQv////8PgyAKIAp+fCECIAMgAkIgiHwhAyACQv////8PgyAEQv////8Pg3whAiADIAJCIIh8IAV8IQMgAkL/////D4MgDCALfnwhAiADIAJCIIh8IQMgAkL/////D4NBADUC8AMiDyAIfnwhAiADIAJCIIh8IQMgAkL/////D4MgBn5C/////w+DIQ4gAkL/////D4MgCSAOfnwhAiADIAJCIIh8IQMgAyEEIARCIIghBUIAIQJCACEDIAJC/////w+DIAcgADUCDCIQfnwhAiADIAJCIIh8IQMgAkL/////D4MgCiANfnwhAiADIAJCIIh8IQMgAkL/////D4NCAYYhAiADQgGGIAJCIIh8IQMgAkL/////D4MgBEL/////D4N8IQIgAyACQiCIfCAFfCEDIAJC/////w+DIAwgDn58IQIgAyACQiCIfCEDIAJC/////w+DIA8gC358IQIgAyACQiCIfCEDIAJC/////w+DQQA1AvQDIhIgCH58IQIgAyACQiCIfCEDIAJC/////w+DIAZ+Qv////8PgyERIAJC/////w+DIAkgEX58IQIgAyACQiCIfCEDIAMhBCAEQiCIIQVCACECQgAhAyACQv////8PgyAHIAA1AhAiE358IQIgAyACQiCIfCEDIAJC/////w+DIAogEH58IQIgAyACQiCIfCEDIAJC/////w+DQgGGIQIgA0IBhiACQiCIfCEDIAJC/////w+DIA0gDX58IQIgAyACQiCIfCEDIAJC/////w+DIARC/////w+DfCECIAMgAkIgiHwgBXwhAyACQv////8PgyAMIBF+fCECIAMgAkIgiHwhAyACQv////8PgyAPIA5+fCECIAMgAkIgiHwhAyACQv////8PgyASIAt+fCECIAMgAkIgiHwhAyACQv////8Pg0EANQL4AyIVIAh+fCECIAMgAkIgiHwhAyACQv////8PgyAGfkL/////D4MhFCACQv////8PgyAJIBR+fCECIAMgAkIgiHwhAyADIQQgBEIgiCEFQgAhAkIAIQMgAkL/////D4MgByAANQIUIhZ+fCECIAMgAkIgiHwhAyACQv////8PgyAKIBN+fCECIAMgAkIgiHwhAyACQv////8PgyANIBB+fCECIAMgAkIgiHwhAyACQv////8Pg0IBhiECIANCAYYgAkIgiHwhAyACQv////8PgyAEQv////8Pg3whAiADIAJCIIh8IAV8IQMgAkL/////D4MgDCAUfnwhAiADIAJCIIh8IQMgAkL/////D4MgDyARfnwhAiADIAJCIIh8IQMgAkL/////D4MgEiAOfnwhAiADIAJCIIh8IQMgAkL/////D4MgFSALfnwhAiADIAJCIIh8IQMgAkL/////D4NBADUC/AMiGCAIfnwhAiADIAJCIIh8IQMgAkL/////D4MgBn5C/////w+DIRcgAkL/////D4MgCSAXfnwhAiADIAJCIIh8IQMgAyEEIARCIIghBUIAIQJCACEDIAJC/////w+DIAcgADUCGCIZfnwhAiADIAJCIIh8IQMgAkL/////D4MgCiAWfnwhAiADIAJCIIh8IQMgAkL/////D4MgDSATfnwhAiADIAJCIIh8IQMgAkL/////D4NCAYYhAiADQgGGIAJCIIh8IQMgAkL/////D4MgECAQfnwhAiADIAJCIIh8IQMgAkL/////D4MgBEL/////D4N8IQIgAyACQiCIfCAFfCEDIAJC/////w+DIAwgF358IQIgAyACQiCIfCEDIAJC/////w+DIA8gFH58IQIgAyACQiCIfCEDIAJC/////w+DIBIgEX58IQIgAyACQiCIfCEDIAJC/////w+DIBUgDn58IQIgAyACQiCIfCEDIAJC/////w+DIBggC358IQIgAyACQiCIfCEDIAJC/////w+DQQA1AoAEIhsgCH58IQIgAyACQiCIfCEDIAJC/////w+DIAZ+Qv////8PgyEaIAJC/////w+DIAkgGn58IQIgAyACQiCIfCEDIAMhBCAEQiCIIQVCACECQgAhAyACQv////8PgyAHIAA1AhwiHH58IQIgAyACQiCIfCEDIAJC/////w+DIAogGX58IQIgAyACQiCIfCEDIAJC/////w+DIA0gFn58IQIgAyACQiCIfCEDIAJC/////w+DIBAgE358IQIgAyACQiCIfCEDIAJC/////w+DQgGGIQIgA0IBhiACQiCIfCEDIAJC/////w+DIARC/////w+DfCECIAMgAkIgiHwgBXwhAyACQv////8PgyAMIBp+fCECIAMgAkIgiHwhAyACQv////8PgyAPIBd+fCECIAMgAkIgiHwhAyACQv////8PgyASIBR+fCECIAMgAkIgiHwhAyACQv////8PgyAVIBF+fCECIAMgAkIgiHwhAyACQv////8PgyAYIA5+fCECIAMgAkIgiHwhAyACQv////8PgyAbIAt+fCECIAMgAkIgiHwhAyACQv////8Pg0EANQKEBCIeIAh+fCECIAMgAkIgiHwhAyACQv////8PgyAGfkL/////D4MhHSACQv////8PgyAJIB1+fCECIAMgAkIgiHwhAyADIQQgBEIgiCEFQgAhAkIAIQMgAkL/////D4MgCiAcfnwhAiADIAJCIIh8IQMgAkL/////D4MgDSAZfnwhAiADIAJCIIh8IQMgAkL/////D4MgECAWfnwhAiADIAJCIIh8IQMgAkL/////D4NCAYYhAiADQgGGIAJCIIh8IQMgAkL/////D4MgEyATfnwhAiADIAJCIIh8IQMgAkL/////D4MgBEL/////D4N8IQIgAyACQiCIfCAFfCEDIAJC/////w+DIAwgHX58IQIgAyACQiCIfCEDIAJC/////w+DIA8gGn58IQIgAyACQiCIfCEDIAJC/////w+DIBIgF358IQIgAyACQiCIfCEDIAJC/////w+DIBUgFH58IQIgAyACQiCIfCEDIAJC/////w+DIBggEX58IQIgAyACQiCIfCEDIAJC/////w+DIBsgDn58IQIgAyACQiCIfCEDIAJC/////w+DIB4gC358IQIgAyACQiCIfCEDIAEgAj4CACADIQQgBEIgiCEFQgAhAkIAIQMgAkL/////D4MgDSAcfnwhAiADIAJCIIh8IQMgAkL/////D4MgECAZfnwhAiADIAJCIIh8IQMgAkL/////D4MgEyAWfnwhAiADIAJCIIh8IQMgAkL/////D4NCAYYhAiADQgGGIAJCIIh8IQMgAkL/////D4MgBEL/////D4N8IQIgAyACQiCIfCAFfCEDIAJC/////w+DIA8gHX58IQIgAyACQiCIfCEDIAJC/////w+DIBIgGn58IQIgAyACQiCIfCEDIAJC/////w+DIBUgF358IQIgAyACQiCIfCEDIAJC/////w+DIBggFH58IQIgAyACQiCIfCEDIAJC/////w+DIBsgEX58IQIgAyACQiCIfCEDIAJC/////w+DIB4gDn58IQIgAyACQiCIfCEDIAEgAj4CBCADIQQgBEIgiCEFQgAhAkIAIQMgAkL/////D4MgECAcfnwhAiADIAJCIIh8IQMgAkL/////D4MgEyAZfnwhAiADIAJCIIh8IQMgAkL/////D4NCAYYhAiADQgGGIAJCIIh8IQMgAkL/////D4MgFiAWfnwhAiADIAJCIIh8IQMgAkL/////D4MgBEL/////D4N8IQIgAyACQiCIfCAFfCEDIAJC/////w+DIBIgHX58IQIgAyACQiCIfCEDIAJC/////w+DIBUgGn58IQIgAyACQiCIfCEDIAJC/////w+DIBggF358IQIgAyACQiCIfCEDIAJC/////w+DIBsgFH58IQIgAyACQiCIfCEDIAJC/////w+DIB4gEX58IQIgAyACQiCIfCEDIAEgAj4CCCADIQQgBEIgiCEFQgAhAkIAIQMgAkL/////D4MgEyAcfnwhAiADIAJCIIh8IQMgAkL/////D4MgFiAZfnwhAiADIAJCIIh8IQMgAkL/////D4NCAYYhAiADQgGGIAJCIIh8IQMgAkL/////D4MgBEL/////D4N8IQIgAyACQiCIfCAFfCEDIAJC/////w+DIBUgHX58IQIgAyACQiCIfCEDIAJC/////w+DIBggGn58IQIgAyACQiCIfCEDIAJC/////w+DIBsgF358IQIgAyACQiCIfCEDIAJC/////w+DIB4gFH58IQIgAyACQiCIfCEDIAEgAj4CDCADIQQgBEIgiCEFQgAhAkIAIQMgAkL/////D4MgFiAcfnwhAiADIAJCIIh8IQMgAkL/////D4NCAYYhAiADQgGGIAJCIIh8IQMgAkL/////D4MgGSAZfnwhAiADIAJCIIh8IQMgAkL/////D4MgBEL/////D4N8IQIgAyACQiCIfCAFfCEDIAJC/////w+DIBggHX58IQIgAyACQiCIfCEDIAJC/////w+DIBsgGn58IQIgAyACQiCIfCEDIAJC/////w+DIB4gF358IQIgAyACQiCIfCEDIAEgAj4CECADIQQgBEIgiCEFQgAhAkIAIQMgAkL/////D4MgGSAcfnwhAiADIAJCIIh8IQMgAkL/////D4NCAYYhAiADQgGGIAJCIIh8IQMgAkL/////D4MgBEL/////D4N8IQIgAyACQiCIfCAFfCEDIAJC/////w+DIBsgHX58IQIgAyACQiCIfCEDIAJC/////w+DIB4gGn58IQIgAyACQiCIfCEDIAEgAj4CFCADIQQgBEIgiCEFQgAhAkIAIQMgAkL/////D4NCAYYhAiADQgGGIAJCIIh8IQMgAkL/////D4MgHCAcfnwhAiADIAJCIIh8IQMgAkL/////D4MgBEL/////D4N8IQIgAyACQiCIfCAFfCEDIAJC/////w+DIB4gHX58IQIgAyACQiCIfCEDIAEgAj4CGCADIQQgBEIgiCEFIAEgBD4CHCAFpwRAIAFB6AMgARAHGgUgAUHoAxAFBEAgAUHoAyABEAcaCwsLCgAgACAAIAEQFAsLACAAQYgEIAEQFAsVACAAQYgKEABBqAoQAUGICiABEBMLEQAgAEHIChAYQcgKQYgFEAULJAAgABACBEBBAA8LIABB6AoQGEHoCkGIBRAFBEBBfw8LQQEPCxcAIAAgARAYIAFB6AMgARAOIAEgARAXCwkAQagEIAAQAAvLAQQBfwF/AX8BfyACEAFBICEFIAAhAwJAA0AgBSABSw0BIAVBIEYEQEGICxAcBUGIC0GIBEGICxAUCyADQYgLQagLEBQgAkGoCyACEBAgA0EgaiEDIAVBIGohBQwACwsgAUEgcCEEIARFBEAPC0GoCxABQQAhBgJAA0AgBiAERg0BIAYgAy0AADoAqAsgA0EBaiEDIAZBAWohBgwACwsgBUEgRgRAQYgLEBwFQYgLQYgEQYgLEBQLQagLQYgLQagLEBQgAkGoCyACEBALHAAgASACQcgLEB1ByAtByAsQFyAAQcgLIAMQFAv4AQQBfwF/AX8Bf0EAKAIAIQVBACAFIAJBAWpBIGxqNgIAIAUQHCAAIQYgBUEgaiEFQQAhCAJAA0AgCCACRg0BIAYQAgRAIAVBIGsgBRAABSAGIAVBIGsgBRAUCyAGIAFqIQYgBUEgaiEFIAhBAWohCAwACwsgBiABayEGIAVBIGshBSADIAJBAWsgBGxqIQcgBSAFEBsCQANAIAhFDQEgBhACBEAgBSAFQSBrEAAgBxABBSAFQSBrQegLEAAgBSAGIAVBIGsQFCAFQegLIAcQFAsgBiABayEGIAcgBGshByAFQSBrIQUgCEEBayEIDAALC0EAIAU2AgALPgMBfwF/AX8gACEEIAIhBUEAIQMCQANAIAMgAUYNASAEIAUQFyAEQSBqIQQgBUEgaiEFIANBAWohAwwACwsLPgMBfwF/AX8gACEEIAIhBUEAIQMCQANAIAMgAUYNASAEIAUQGCAEQSBqIQQgBUEgaiEFIANBAWohAwwACwsLPgMBfwF/AX8gACEEIAIhBUEAIQMCQANAIAMgAUYNASAEIAUQEiAEQSBqIQQgBUEgaiEFIANBAWohAwwACwsLTQQBfwF/AX8BfyAAIQUgASEGIAMhB0EAIQQCQANAIAQgAkYNASAFIAYgBxAQIAVBIGohBSAGQSBqIQYgB0EgaiEHIARBAWohBAwACwsLTQQBfwF/AX8BfyAAIQUgASEGIAMhB0EAIQQCQANAIAQgAkYNASAFIAYgBxARIAVBIGohBSAGQSBqIQYgB0EgaiEHIARBAWohBAwACwsLTQQBfwF/AX8BfyAAIQUgASEGIAMhB0EAIQQCQANAIAQgAkYNASAFIAYgBxAUIAVBIGohBSAGQSBqIQYgB0EgaiEHIARBAWohBAwACwsLsgICAX8BfyACRQRAIAMQHA8LIABBiAwQACADEBwgAiEEAkADQCAEQQFrIQQgASAEai0AACEFIAMgAxAVIAVBgAFPBEAgBUGAAWshBSADQYgMIAMQFAsgAyADEBUgBUHAAE8EQCAFQcAAayEFIANBiAwgAxAUCyADIAMQFSAFQSBPBEAgBUEgayEFIANBiAwgAxAUCyADIAMQFSAFQRBPBEAgBUEQayEFIANBiAwgAxAUCyADIAMQFSAFQQhPBEAgBUEIayEFIANBiAwgAxAUCyADIAMQFSAFQQRPBEAgBUEEayEFIANBiAwgAxAUCyADIAMQFSAFQQJPBEAgBUECayEFIANBiAwgAxAUCyADIAMQFSAFQQFPBEAgBUEBayEFIANBiAwgAxAUCyAERQ0BDAALCwveAQMBfwF/AX8gABACBEAgARABDwtBASECQcgFQagMEAAgAEGoBUEgQcgMECYgAEHoBUEgQegMECYCQANAQcgMQagEEAQNAUHIDEGIDRAVQQEhAwJAA0BBiA1BqAQQBA0BQYgNQYgNEBUgA0EBaiEDDAALC0GoDEGoDRAAIAIgA2tBAWshBAJAA0AgBEUNAUGoDUGoDRAVIARBAWshBAwACwsgAyECQagNQagMEBVByAxBqAxByAwQFEHoDEGoDUHoDBAUDAALC0HoDBAZBEBB6AwgARASBUHoDCABEAALCyAAIAAQAgRAQQEPCyAAQegEQSBByA0QJkHIDUGoBBAECwoAIABBqA4QBA8LLAAgACABIAIQBgRAIAJB6A0gAhAHGgUgAkHoDRAFBEAgAkHoDSACEAcaCwsLFwAgACABIAIQBwRAIAJB6A0gAhAGGgsLCwBByA4gACABECsLnBEDAX4BfgF+Qv////8OIQJCACEDIAA1AgAgAn5C/////w+DIQQgADUCACADQiCIfEHoDTUCACAEfnwhAyAAIAM+AgAgADUCBCADQiCIfEHoDTUCBCAEfnwhAyAAIAM+AgQgADUCCCADQiCIfEHoDTUCCCAEfnwhAyAAIAM+AgggADUCDCADQiCIfEHoDTUCDCAEfnwhAyAAIAM+AgwgADUCECADQiCIfEHoDTUCECAEfnwhAyAAIAM+AhAgADUCFCADQiCIfEHoDTUCFCAEfnwhAyAAIAM+AhQgADUCGCADQiCIfEHoDTUCGCAEfnwhAyAAIAM+AhggADUCHCADQiCIfEHoDTUCHCAEfnwhAyAAIAM+AhxBiBAgA0IgiD4CAEIAIQMgADUCBCACfkL/////D4MhBCAANQIEIANCIIh8QegNNQIAIAR+fCEDIAAgAz4CBCAANQIIIANCIIh8QegNNQIEIAR+fCEDIAAgAz4CCCAANQIMIANCIIh8QegNNQIIIAR+fCEDIAAgAz4CDCAANQIQIANCIIh8QegNNQIMIAR+fCEDIAAgAz4CECAANQIUIANCIIh8QegNNQIQIAR+fCEDIAAgAz4CFCAANQIYIANCIIh8QegNNQIUIAR+fCEDIAAgAz4CGCAANQIcIANCIIh8QegNNQIYIAR+fCEDIAAgAz4CHCAANQIgIANCIIh8QegNNQIcIAR+fCEDIAAgAz4CIEGIECADQiCIPgIEQgAhAyAANQIIIAJ+Qv////8PgyEEIAA1AgggA0IgiHxB6A01AgAgBH58IQMgACADPgIIIAA1AgwgA0IgiHxB6A01AgQgBH58IQMgACADPgIMIAA1AhAgA0IgiHxB6A01AgggBH58IQMgACADPgIQIAA1AhQgA0IgiHxB6A01AgwgBH58IQMgACADPgIUIAA1AhggA0IgiHxB6A01AhAgBH58IQMgACADPgIYIAA1AhwgA0IgiHxB6A01AhQgBH58IQMgACADPgIcIAA1AiAgA0IgiHxB6A01AhggBH58IQMgACADPgIgIAA1AiQgA0IgiHxB6A01AhwgBH58IQMgACADPgIkQYgQIANCIIg+AghCACEDIAA1AgwgAn5C/////w+DIQQgADUCDCADQiCIfEHoDTUCACAEfnwhAyAAIAM+AgwgADUCECADQiCIfEHoDTUCBCAEfnwhAyAAIAM+AhAgADUCFCADQiCIfEHoDTUCCCAEfnwhAyAAIAM+AhQgADUCGCADQiCIfEHoDTUCDCAEfnwhAyAAIAM+AhggADUCHCADQiCIfEHoDTUCECAEfnwhAyAAIAM+AhwgADUCICADQiCIfEHoDTUCFCAEfnwhAyAAIAM+AiAgADUCJCADQiCIfEHoDTUCGCAEfnwhAyAAIAM+AiQgADUCKCADQiCIfEHoDTUCHCAEfnwhAyAAIAM+AihBiBAgA0IgiD4CDEIAIQMgADUCECACfkL/////D4MhBCAANQIQIANCIIh8QegNNQIAIAR+fCEDIAAgAz4CECAANQIUIANCIIh8QegNNQIEIAR+fCEDIAAgAz4CFCAANQIYIANCIIh8QegNNQIIIAR+fCEDIAAgAz4CGCAANQIcIANCIIh8QegNNQIMIAR+fCEDIAAgAz4CHCAANQIgIANCIIh8QegNNQIQIAR+fCEDIAAgAz4CICAANQIkIANCIIh8QegNNQIUIAR+fCEDIAAgAz4CJCAANQIoIANCIIh8QegNNQIYIAR+fCEDIAAgAz4CKCAANQIsIANCIIh8QegNNQIcIAR+fCEDIAAgAz4CLEGIECADQiCIPgIQQgAhAyAANQIUIAJ+Qv////8PgyEEIAA1AhQgA0IgiHxB6A01AgAgBH58IQMgACADPgIUIAA1AhggA0IgiHxB6A01AgQgBH58IQMgACADPgIYIAA1AhwgA0IgiHxB6A01AgggBH58IQMgACADPgIcIAA1AiAgA0IgiHxB6A01AgwgBH58IQMgACADPgIgIAA1AiQgA0IgiHxB6A01AhAgBH58IQMgACADPgIkIAA1AiggA0IgiHxB6A01AhQgBH58IQMgACADPgIoIAA1AiwgA0IgiHxB6A01AhggBH58IQMgACADPgIsIAA1AjAgA0IgiHxB6A01AhwgBH58IQMgACADPgIwQYgQIANCIIg+AhRCACEDIAA1AhggAn5C/////w+DIQQgADUCGCADQiCIfEHoDTUCACAEfnwhAyAAIAM+AhggADUCHCADQiCIfEHoDTUCBCAEfnwhAyAAIAM+AhwgADUCICADQiCIfEHoDTUCCCAEfnwhAyAAIAM+AiAgADUCJCADQiCIfEHoDTUCDCAEfnwhAyAAIAM+AiQgADUCKCADQiCIfEHoDTUCECAEfnwhAyAAIAM+AiggADUCLCADQiCIfEHoDTUCFCAEfnwhAyAAIAM+AiwgADUCMCADQiCIfEHoDTUCGCAEfnwhAyAAIAM+AjAgADUCNCADQiCIfEHoDTUCHCAEfnwhAyAAIAM+AjRBiBAgA0IgiD4CGEIAIQMgADUCHCACfkL/////D4MhBCAANQIcIANCIIh8QegNNQIAIAR+fCEDIAAgAz4CHCAANQIgIANCIIh8QegNNQIEIAR+fCEDIAAgAz4CICAANQIkIANCIIh8QegNNQIIIAR+fCEDIAAgAz4CJCAANQIoIANCIIh8QegNNQIMIAR+fCEDIAAgAz4CKCAANQIsIANCIIh8QegNNQIQIAR+fCEDIAAgAz4CLCAANQIwIANCIIh8QegNNQIUIAR+fCEDIAAgAz4CMCAANQI0IANCIIh8QegNNQIYIAR+fCEDIAAgAz4CNCAANQI4IANCIIh8QegNNQIcIAR+fCEDIAAgAz4COEGIECADQiCIPgIcQYgQIABBIGogARAqC74fIwF+AX4BfgF+AX4BfgF+AX4BfgF+AX4BfgF+AX4BfgF+AX4BfgF+AX4BfgF+AX4BfgF+AX4BfgF+AX4BfgF+AX4BfgF+AX5C/////w4hBSADQv////8PgyAANQIAIgYgATUCACIHfnwhAyAEIANCIIh8IQQgA0L/////D4MgBX5C/////w+DIQggA0L/////D4NBADUC6A0iCSAIfnwhAyAEIANCIIh8IQQgBEIgiCEDIARC/////w+DIAYgATUCBCILfnwhBCADIARCIIh8IQMgBEL/////D4MgADUCBCIKIAd+fCEEIAMgBEIgiHwhAyAEQv////8Pg0EANQLsDSINIAh+fCEEIAMgBEIgiHwhAyAEQv////8PgyAFfkL/////D4MhDCAEQv////8PgyAJIAx+fCEEIAMgBEIgiHwhAyADQiCIIQQgA0L/////D4MgBiABNQIIIg9+fCEDIAQgA0IgiHwhBCADQv////8PgyAKIAt+fCEDIAQgA0IgiHwhBCADQv////8PgyAANQIIIg4gB358IQMgBCADQiCIfCEEIANC/////w+DIA0gDH58IQMgBCADQiCIfCEEIANC/////w+DQQA1AvANIhEgCH58IQMgBCADQiCIfCEEIANC/////w+DIAV+Qv////8PgyEQIANC/////w+DIAkgEH58IQMgBCADQiCIfCEEIARCIIghAyAEQv////8PgyAGIAE1AgwiE358IQQgAyAEQiCIfCEDIARC/////w+DIAogD358IQQgAyAEQiCIfCEDIARC/////w+DIA4gC358IQQgAyAEQiCIfCEDIARC/////w+DIAA1AgwiEiAHfnwhBCADIARCIIh8IQMgBEL/////D4MgDSAQfnwhBCADIARCIIh8IQMgBEL/////D4MgESAMfnwhBCADIARCIIh8IQMgBEL/////D4NBADUC9A0iFSAIfnwhBCADIARCIIh8IQMgBEL/////D4MgBX5C/////w+DIRQgBEL/////D4MgCSAUfnwhBCADIARCIIh8IQMgA0IgiCEEIANC/////w+DIAYgATUCECIXfnwhAyAEIANCIIh8IQQgA0L/////D4MgCiATfnwhAyAEIANCIIh8IQQgA0L/////D4MgDiAPfnwhAyAEIANCIIh8IQQgA0L/////D4MgEiALfnwhAyAEIANCIIh8IQQgA0L/////D4MgADUCECIWIAd+fCEDIAQgA0IgiHwhBCADQv////8PgyANIBR+fCEDIAQgA0IgiHwhBCADQv////8PgyARIBB+fCEDIAQgA0IgiHwhBCADQv////8PgyAVIAx+fCEDIAQgA0IgiHwhBCADQv////8Pg0EANQL4DSIZIAh+fCEDIAQgA0IgiHwhBCADQv////8PgyAFfkL/////D4MhGCADQv////8PgyAJIBh+fCEDIAQgA0IgiHwhBCAEQiCIIQMgBEL/////D4MgBiABNQIUIht+fCEEIAMgBEIgiHwhAyAEQv////8PgyAKIBd+fCEEIAMgBEIgiHwhAyAEQv////8PgyAOIBN+fCEEIAMgBEIgiHwhAyAEQv////8PgyASIA9+fCEEIAMgBEIgiHwhAyAEQv////8PgyAWIAt+fCEEIAMgBEIgiHwhAyAEQv////8PgyAANQIUIhogB358IQQgAyAEQiCIfCEDIARC/////w+DIA0gGH58IQQgAyAEQiCIfCEDIARC/////w+DIBEgFH58IQQgAyAEQiCIfCEDIARC/////w+DIBUgEH58IQQgAyAEQiCIfCEDIARC/////w+DIBkgDH58IQQgAyAEQiCIfCEDIARC/////w+DQQA1AvwNIh0gCH58IQQgAyAEQiCIfCEDIARC/////w+DIAV+Qv////8PgyEcIARC/////w+DIAkgHH58IQQgAyAEQiCIfCEDIANCIIghBCADQv////8PgyAGIAE1AhgiH358IQMgBCADQiCIfCEEIANC/////w+DIAogG358IQMgBCADQiCIfCEEIANC/////w+DIA4gF358IQMgBCADQiCIfCEEIANC/////w+DIBIgE358IQMgBCADQiCIfCEEIANC/////w+DIBYgD358IQMgBCADQiCIfCEEIANC/////w+DIBogC358IQMgBCADQiCIfCEEIANC/////w+DIAA1AhgiHiAHfnwhAyAEIANCIIh8IQQgA0L/////D4MgDSAcfnwhAyAEIANCIIh8IQQgA0L/////D4MgESAYfnwhAyAEIANCIIh8IQQgA0L/////D4MgFSAUfnwhAyAEIANCIIh8IQQgA0L/////D4MgGSAQfnwhAyAEIANCIIh8IQQgA0L/////D4MgHSAMfnwhAyAEIANCIIh8IQQgA0L/////D4NBADUCgA4iISAIfnwhAyAEIANCIIh8IQQgA0L/////D4MgBX5C/////w+DISAgA0L/////D4MgCSAgfnwhAyAEIANCIIh8IQQgBEIgiCEDIARC/////w+DIAYgATUCHCIjfnwhBCADIARCIIh8IQMgBEL/////D4MgCiAffnwhBCADIARCIIh8IQMgBEL/////D4MgDiAbfnwhBCADIARCIIh8IQMgBEL/////D4MgEiAXfnwhBCADIARCIIh8IQMgBEL/////D4MgFiATfnwhBCADIARCIIh8IQMgBEL/////D4MgGiAPfnwhBCADIARCIIh8IQMgBEL/////D4MgHiALfnwhBCADIARCIIh8IQMgBEL/////D4MgADUCHCIiIAd+fCEEIAMgBEIgiHwhAyAEQv////8PgyANICB+fCEEIAMgBEIgiHwhAyAEQv////8PgyARIBx+fCEEIAMgBEIgiHwhAyAEQv////8PgyAVIBh+fCEEIAMgBEIgiHwhAyAEQv////8PgyAZIBR+fCEEIAMgBEIgiHwhAyAEQv////8PgyAdIBB+fCEEIAMgBEIgiHwhAyAEQv////8PgyAhIAx+fCEEIAMgBEIgiHwhAyAEQv////8Pg0EANQKEDiIlIAh+fCEEIAMgBEIgiHwhAyAEQv////8PgyAFfkL/////D4MhJCAEQv////8PgyAJICR+fCEEIAMgBEIgiHwhAyADQiCIIQQgA0L/////D4MgCiAjfnwhAyAEIANCIIh8IQQgA0L/////D4MgDiAffnwhAyAEIANCIIh8IQQgA0L/////D4MgEiAbfnwhAyAEIANCIIh8IQQgA0L/////D4MgFiAXfnwhAyAEIANCIIh8IQQgA0L/////D4MgGiATfnwhAyAEIANCIIh8IQQgA0L/////D4MgHiAPfnwhAyAEIANCIIh8IQQgA0L/////D4MgIiALfnwhAyAEIANCIIh8IQQgA0L/////D4MgDSAkfnwhAyAEIANCIIh8IQQgA0L/////D4MgESAgfnwhAyAEIANCIIh8IQQgA0L/////D4MgFSAcfnwhAyAEIANCIIh8IQQgA0L/////D4MgGSAYfnwhAyAEIANCIIh8IQQgA0L/////D4MgHSAUfnwhAyAEIANCIIh8IQQgA0L/////D4MgISAQfnwhAyAEIANCIIh8IQQgA0L/////D4MgJSAMfnwhAyAEIANCIIh8IQQgAiADPgIAIARCIIghAyAEQv////8PgyAOICN+fCEEIAMgBEIgiHwhAyAEQv////8PgyASIB9+fCEEIAMgBEIgiHwhAyAEQv////8PgyAWIBt+fCEEIAMgBEIgiHwhAyAEQv////8PgyAaIBd+fCEEIAMgBEIgiHwhAyAEQv////8PgyAeIBN+fCEEIAMgBEIgiHwhAyAEQv////8PgyAiIA9+fCEEIAMgBEIgiHwhAyAEQv////8PgyARICR+fCEEIAMgBEIgiHwhAyAEQv////8PgyAVICB+fCEEIAMgBEIgiHwhAyAEQv////8PgyAZIBx+fCEEIAMgBEIgiHwhAyAEQv////8PgyAdIBh+fCEEIAMgBEIgiHwhAyAEQv////8PgyAhIBR+fCEEIAMgBEIgiHwhAyAEQv////8PgyAlIBB+fCEEIAMgBEIgiHwhAyACIAQ+AgQgA0IgiCEEIANC/////w+DIBIgI358IQMgBCADQiCIfCEEIANC/////w+DIBYgH358IQMgBCADQiCIfCEEIANC/////w+DIBogG358IQMgBCADQiCIfCEEIANC/////w+DIB4gF358IQMgBCADQiCIfCEEIANC/////w+DICIgE358IQMgBCADQiCIfCEEIANC/////w+DIBUgJH58IQMgBCADQiCIfCEEIANC/////w+DIBkgIH58IQMgBCADQiCIfCEEIANC/////w+DIB0gHH58IQMgBCADQiCIfCEEIANC/////w+DICEgGH58IQMgBCADQiCIfCEEIANC/////w+DICUgFH58IQMgBCADQiCIfCEEIAIgAz4CCCAEQiCIIQMgBEL/////D4MgFiAjfnwhBCADIARCIIh8IQMgBEL/////D4MgGiAffnwhBCADIARCIIh8IQMgBEL/////D4MgHiAbfnwhBCADIARCIIh8IQMgBEL/////D4MgIiAXfnwhBCADIARCIIh8IQMgBEL/////D4MgGSAkfnwhBCADIARCIIh8IQMgBEL/////D4MgHSAgfnwhBCADIARCIIh8IQMgBEL/////D4MgISAcfnwhBCADIARCIIh8IQMgBEL/////D4MgJSAYfnwhBCADIARCIIh8IQMgAiAEPgIMIANCIIghBCADQv////8PgyAaICN+fCEDIAQgA0IgiHwhBCADQv////8PgyAeIB9+fCEDIAQgA0IgiHwhBCADQv////8PgyAiIBt+fCEDIAQgA0IgiHwhBCADQv////8PgyAdICR+fCEDIAQgA0IgiHwhBCADQv////8PgyAhICB+fCEDIAQgA0IgiHwhBCADQv////8PgyAlIBx+fCEDIAQgA0IgiHwhBCACIAM+AhAgBEIgiCEDIARC/////w+DIB4gI358IQQgAyAEQiCIfCEDIARC/////w+DICIgH358IQQgAyAEQiCIfCEDIARC/////w+DICEgJH58IQQgAyAEQiCIfCEDIARC/////w+DICUgIH58IQQgAyAEQiCIfCEDIAIgBD4CFCADQiCIIQQgA0L/////D4MgIiAjfnwhAyAEIANCIIh8IQQgA0L/////D4MgJSAkfnwhAyAEIANCIIh8IQQgAiADPgIYIARCIIghAyACIAQ+AhwgA6cEQCACQegNIAIQBxoFIAJB6A0QBQRAIAJB6A0gAhAHGgsLC7shHQF+AX4BfgF+AX4BfgF+AX4BfgF+AX4BfgF+AX4BfgF+AX4BfgF+AX4BfgF+AX4BfgF+AX4BfgF+AX5C/////w4hBkIAIQJCACEDIAJC/////w+DQgGGIQIgA0IBhiACQiCIfCEDIAJC/////w+DIAA1AgAiByAHfnwhAiADIAJCIIh8IQMgAkL/////D4MgBn5C/////w+DIQggAkL/////D4NBADUC6A0iCSAIfnwhAiADIAJCIIh8IQMgAyEEIARCIIghBUIAIQJCACEDIAJC/////w+DIAcgADUCBCIKfnwhAiADIAJCIIh8IQMgAkL/////D4NCAYYhAiADQgGGIAJCIIh8IQMgAkL/////D4MgBEL/////D4N8IQIgAyACQiCIfCAFfCEDIAJC/////w+DQQA1AuwNIgwgCH58IQIgAyACQiCIfCEDIAJC/////w+DIAZ+Qv////8PgyELIAJC/////w+DIAkgC358IQIgAyACQiCIfCEDIAMhBCAEQiCIIQVCACECQgAhAyACQv////8PgyAHIAA1AggiDX58IQIgAyACQiCIfCEDIAJC/////w+DQgGGIQIgA0IBhiACQiCIfCEDIAJC/////w+DIAogCn58IQIgAyACQiCIfCEDIAJC/////w+DIARC/////w+DfCECIAMgAkIgiHwgBXwhAyACQv////8PgyAMIAt+fCECIAMgAkIgiHwhAyACQv////8Pg0EANQLwDSIPIAh+fCECIAMgAkIgiHwhAyACQv////8PgyAGfkL/////D4MhDiACQv////8PgyAJIA5+fCECIAMgAkIgiHwhAyADIQQgBEIgiCEFQgAhAkIAIQMgAkL/////D4MgByAANQIMIhB+fCECIAMgAkIgiHwhAyACQv////8PgyAKIA1+fCECIAMgAkIgiHwhAyACQv////8Pg0IBhiECIANCAYYgAkIgiHwhAyACQv////8PgyAEQv////8Pg3whAiADIAJCIIh8IAV8IQMgAkL/////D4MgDCAOfnwhAiADIAJCIIh8IQMgAkL/////D4MgDyALfnwhAiADIAJCIIh8IQMgAkL/////D4NBADUC9A0iEiAIfnwhAiADIAJCIIh8IQMgAkL/////D4MgBn5C/////w+DIREgAkL/////D4MgCSARfnwhAiADIAJCIIh8IQMgAyEEIARCIIghBUIAIQJCACEDIAJC/////w+DIAcgADUCECITfnwhAiADIAJCIIh8IQMgAkL/////D4MgCiAQfnwhAiADIAJCIIh8IQMgAkL/////D4NCAYYhAiADQgGGIAJCIIh8IQMgAkL/////D4MgDSANfnwhAiADIAJCIIh8IQMgAkL/////D4MgBEL/////D4N8IQIgAyACQiCIfCAFfCEDIAJC/////w+DIAwgEX58IQIgAyACQiCIfCEDIAJC/////w+DIA8gDn58IQIgAyACQiCIfCEDIAJC/////w+DIBIgC358IQIgAyACQiCIfCEDIAJC/////w+DQQA1AvgNIhUgCH58IQIgAyACQiCIfCEDIAJC/////w+DIAZ+Qv////8PgyEUIAJC/////w+DIAkgFH58IQIgAyACQiCIfCEDIAMhBCAEQiCIIQVCACECQgAhAyACQv////8PgyAHIAA1AhQiFn58IQIgAyACQiCIfCEDIAJC/////w+DIAogE358IQIgAyACQiCIfCEDIAJC/////w+DIA0gEH58IQIgAyACQiCIfCEDIAJC/////w+DQgGGIQIgA0IBhiACQiCIfCEDIAJC/////w+DIARC/////w+DfCECIAMgAkIgiHwgBXwhAyACQv////8PgyAMIBR+fCECIAMgAkIgiHwhAyACQv////8PgyAPIBF+fCECIAMgAkIgiHwhAyACQv////8PgyASIA5+fCECIAMgAkIgiHwhAyACQv////8PgyAVIAt+fCECIAMgAkIgiHwhAyACQv////8Pg0EANQL8DSIYIAh+fCECIAMgAkIgiHwhAyACQv////8PgyAGfkL/////D4MhFyACQv////8PgyAJIBd+fCECIAMgAkIgiHwhAyADIQQgBEIgiCEFQgAhAkIAIQMgAkL/////D4MgByAANQIYIhl+fCECIAMgAkIgiHwhAyACQv////8PgyAKIBZ+fCECIAMgAkIgiHwhAyACQv////8PgyANIBN+fCECIAMgAkIgiHwhAyACQv////8Pg0IBhiECIANCAYYgAkIgiHwhAyACQv////8PgyAQIBB+fCECIAMgAkIgiHwhAyACQv////8PgyAEQv////8Pg3whAiADIAJCIIh8IAV8IQMgAkL/////D4MgDCAXfnwhAiADIAJCIIh8IQMgAkL/////D4MgDyAUfnwhAiADIAJCIIh8IQMgAkL/////D4MgEiARfnwhAiADIAJCIIh8IQMgAkL/////D4MgFSAOfnwhAiADIAJCIIh8IQMgAkL/////D4MgGCALfnwhAiADIAJCIIh8IQMgAkL/////D4NBADUCgA4iGyAIfnwhAiADIAJCIIh8IQMgAkL/////D4MgBn5C/////w+DIRogAkL/////D4MgCSAafnwhAiADIAJCIIh8IQMgAyEEIARCIIghBUIAIQJCACEDIAJC/////w+DIAcgADUCHCIcfnwhAiADIAJCIIh8IQMgAkL/////D4MgCiAZfnwhAiADIAJCIIh8IQMgAkL/////D4MgDSAWfnwhAiADIAJCIIh8IQMgAkL/////D4MgECATfnwhAiADIAJCIIh8IQMgAkL/////D4NCAYYhAiADQgGGIAJCIIh8IQMgAkL/////D4MgBEL/////D4N8IQIgAyACQiCIfCAFfCEDIAJC/////w+DIAwgGn58IQIgAyACQiCIfCEDIAJC/////w+DIA8gF358IQIgAyACQiCIfCEDIAJC/////w+DIBIgFH58IQIgAyACQiCIfCEDIAJC/////w+DIBUgEX58IQIgAyACQiCIfCEDIAJC/////w+DIBggDn58IQIgAyACQiCIfCEDIAJC/////w+DIBsgC358IQIgAyACQiCIfCEDIAJC/////w+DQQA1AoQOIh4gCH58IQIgAyACQiCIfCEDIAJC/////w+DIAZ+Qv////8PgyEdIAJC/////w+DIAkgHX58IQIgAyACQiCIfCEDIAMhBCAEQiCIIQVCACECQgAhAyACQv////8PgyAKIBx+fCECIAMgAkIgiHwhAyACQv////8PgyANIBl+fCECIAMgAkIgiHwhAyACQv////8PgyAQIBZ+fCECIAMgAkIgiHwhAyACQv////8Pg0IBhiECIANCAYYgAkIgiHwhAyACQv////8PgyATIBN+fCECIAMgAkIgiHwhAyACQv////8PgyAEQv////8Pg3whAiADIAJCIIh8IAV8IQMgAkL/////D4MgDCAdfnwhAiADIAJCIIh8IQMgAkL/////D4MgDyAafnwhAiADIAJCIIh8IQMgAkL/////D4MgEiAXfnwhAiADIAJCIIh8IQMgAkL/////D4MgFSAUfnwhAiADIAJCIIh8IQMgAkL/////D4MgGCARfnwhAiADIAJCIIh8IQMgAkL/////D4MgGyAOfnwhAiADIAJCIIh8IQMgAkL/////D4MgHiALfnwhAiADIAJCIIh8IQMgASACPgIAIAMhBCAEQiCIIQVCACECQgAhAyACQv////8PgyANIBx+fCECIAMgAkIgiHwhAyACQv////8PgyAQIBl+fCECIAMgAkIgiHwhAyACQv////8PgyATIBZ+fCECIAMgAkIgiHwhAyACQv////8Pg0IBhiECIANCAYYgAkIgiHwhAyACQv////8PgyAEQv////8Pg3whAiADIAJCIIh8IAV8IQMgAkL/////D4MgDyAdfnwhAiADIAJCIIh8IQMgAkL/////D4MgEiAafnwhAiADIAJCIIh8IQMgAkL/////D4MgFSAXfnwhAiADIAJCIIh8IQMgAkL/////D4MgGCAUfnwhAiADIAJCIIh8IQMgAkL/////D4MgGyARfnwhAiADIAJCIIh8IQMgAkL/////D4MgHiAOfnwhAiADIAJCIIh8IQMgASACPgIEIAMhBCAEQiCIIQVCACECQgAhAyACQv////8PgyAQIBx+fCECIAMgAkIgiHwhAyACQv////8PgyATIBl+fCECIAMgAkIgiHwhAyACQv////8Pg0IBhiECIANCAYYgAkIgiHwhAyACQv////8PgyAWIBZ+fCECIAMgAkIgiHwhAyACQv////8PgyAEQv////8Pg3whAiADIAJCIIh8IAV8IQMgAkL/////D4MgEiAdfnwhAiADIAJCIIh8IQMgAkL/////D4MgFSAafnwhAiADIAJCIIh8IQMgAkL/////D4MgGCAXfnwhAiADIAJCIIh8IQMgAkL/////D4MgGyAUfnwhAiADIAJCIIh8IQMgAkL/////D4MgHiARfnwhAiADIAJCIIh8IQMgASACPgIIIAMhBCAEQiCIIQVCACECQgAhAyACQv////8PgyATIBx+fCECIAMgAkIgiHwhAyACQv////8PgyAWIBl+fCECIAMgAkIgiHwhAyACQv////8Pg0IBhiECIANCAYYgAkIgiHwhAyACQv////8PgyAEQv////8Pg3whAiADIAJCIIh8IAV8IQMgAkL/////D4MgFSAdfnwhAiADIAJCIIh8IQMgAkL/////D4MgGCAafnwhAiADIAJCIIh8IQMgAkL/////D4MgGyAXfnwhAiADIAJCIIh8IQMgAkL/////D4MgHiAUfnwhAiADIAJCIIh8IQMgASACPgIMIAMhBCAEQiCIIQVCACECQgAhAyACQv////8PgyAWIBx+fCECIAMgAkIgiHwhAyACQv////8Pg0IBhiECIANCAYYgAkIgiHwhAyACQv////8PgyAZIBl+fCECIAMgAkIgiHwhAyACQv////8PgyAEQv////8Pg3whAiADIAJCIIh8IAV8IQMgAkL/////D4MgGCAdfnwhAiADIAJCIIh8IQMgAkL/////D4MgGyAafnwhAiADIAJCIIh8IQMgAkL/////D4MgHiAXfnwhAiADIAJCIIh8IQMgASACPgIQIAMhBCAEQiCIIQVCACECQgAhAyACQv////8PgyAZIBx+fCECIAMgAkIgiHwhAyACQv////8Pg0IBhiECIANCAYYgAkIgiHwhAyACQv////8PgyAEQv////8Pg3whAiADIAJCIIh8IAV8IQMgAkL/////D4MgGyAdfnwhAiADIAJCIIh8IQMgAkL/////D4MgHiAafnwhAiADIAJCIIh8IQMgASACPgIUIAMhBCAEQiCIIQVCACECQgAhAyACQv////8Pg0IBhiECIANCAYYgAkIgiHwhAyACQv////8PgyAcIBx+fCECIAMgAkIgiHwhAyACQv////8PgyAEQv////8Pg3whAiADIAJCIIh8IAV8IQMgAkL/////D4MgHiAdfnwhAiADIAJCIIh8IQMgASACPgIYIAMhBCAEQiCIIQUgASAEPgIcIAWnBEAgAUHoDSABEAcaBSABQegNEAUEQCABQegNIAEQBxoLCwsKACAAIAAgARAuCwsAIABBiA4gARAu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IAVByNoAIAcQjwELIAYgAWshBiAHIARrIQcgBUHAAGshBSAIQQFrIQgMAAsLQQAgBTYCAAvOAgIBfwF/IAJFBEAgAxCNAQ8LIABBiNsAEI4BIAMQjQEgAiEEAkADQCAEQQFrIQQgASAEai0AACEFIAMgAxCRASAFQYABTwRAIAVBgAFrIQUgA0GI2wAgAxCPAQsgAyADEJEBIAVBwABPBEAgBUHAAGshBSADQYjbACADEI8BCyADIAMQkQEgBUEgTwRAIAVBIGshBSADQYjbACADEI8BCyADIAMQkQEgBUEQTwRAIAVBEGshBSADQYjbACADEI8BCyADIAMQkQEgBUEITwRAIAVBCGshBSADQYjbACADEI8BCyADIAMQkQEgBUEETwRAIAVBBGshBSADQYjbACADEI8BCyADIAMQkQEgBUECTwRAIAVBAmshBSADQYjbACADEI8BCyADIAMQkQEgBUEBTwRAIAVBAWshBSADQYjbACADEI8BCyAERQ0BDAALCwvNAQBByN0AEI0BQcjdAEHI3QAQlAEgAEHI2wBBIEGI3AAQngFBiNwAQcjcABCRASAAQcjcAEHI3AAQjwFByNwAQYjdABCVAUGI3QBByNwAQYjdABCPAUGI3QBByN0AEJgBBEAAC0GI3AAgAEGI3gAQjwFByNwAQcjdABCYAQRAQcjdABABQejdABAcQcjdAEGI3gAgARCPAQVByN4AEI0BQcjeAEHI3ABByN4AEJIBQcjeAEHo2wBBIEHI3gAQngFByN4AQYjeACABEI8BCwtpAEHo4AAQjQFB6OAAQejgABCUASAAQYjfAEEgQajfABCeAUGo3wBB6N8AEJEBIABB6N8AQejfABCPAUHo3wBBqOAAEJUBQajgAEHo3wBBqOAAEI8BQajgAEHo4AAQmAEEQEEADwtBAQ8LEQAgABCKASAAQcAAahCKAXELCwAgAEGAAWoQigELEAAgABCMASAAQcAAahCMAQsZACAAEIwBIABBwABqEI0BIABBgAFqEIwBC6IBACABIAApAwA3AwAgASAAKQMINwMIIAEgACkDEDcDECABIAApAxg3AxggASAAKQMgNwMgIAEgACkDKDcDKCABIAApAzA3AzAgASAAKQM4NwM4IAEgACkDQDcDQCABIAApA0g3A0ggASAAKQNQNwNQIAEgACkDWDcDWCABIAApA2A3A2AgASAAKQNoNwNoIAEgACkDcDcDcCABIAApA3g3A3gLggIAIAEgACkDADcDACABIAApAwg3AwggASAAKQMQNwMQIAEgACkDGDcDGCABIAApAyA3AyAgASAAKQMoNwMoIAEgACkDMDcDMCABIAApAzg3AzggASAAKQNANwNAIAEgACkDSDcDSCABIAApA1A3A1AgASAAKQNYNwNYIAEgACkDYDcDYCABIAApA2g3A2ggASAAKQNwNwNwIAEgACkDeDcDeCABIAApA4ABNwOAASABIAApA4gBNwOIASABIAApA5ABNwOQASABIAApA5gBNwOYASABIAApA6ABNwOgASABIAApA6gBNwOoASABIAApA7ABNwOwASABIAApA7gBNwO4AQsvACAAEKEBBEAgARCkAQUgAUGAAWoQjQEgAEHAAGogAUHAAGoQjgEgACABEI4BCwscAQF/IAAgARCYASAAQcAAaiABQcAAahCYAXEPC4sBAQF/IABBgAFqIQIgABCiAQRAIAEQoQEPCyABEKEBBEBBAA8LIAIQiwEEQCAAIAEQqAEPCyACQejhABCRASABQejhAEGo4gAQjwEgAkHo4QBB6OIAEI8BIAFBwABqQejiAEGo4wAQjwEgAEGo4gAQmAEEQCAAQcAAakGo4wAQmAEEQEEBDwsLQQAPC9kBAgF/AX8gAEGAAWohAiABQYABaiEDIAAQogEEQCABEKIBDwsgARCiAQRAQQAPCyACEIsBBEAgASAAEKkBDwsgAxCLAQRAIAAgARCpAQ8LIAJB6OMAEJEBIANBqOQAEJEBIABBqOQAQejkABCPASABQejjAEGo5QAQjwEgAkHo4wBB6OUAEI8BIANBqOQAQajmABCPASAAQcAAakGo5gBB6OYAEI8BIAFBwABqQejlAEGo5wAQjwFB6OQAQajlABCYAQRAQejmAEGo5wAQmAEEQEEBDwsLQQAPC6wCACAAEKEBBEAgACABEKcBDwsgAEHo5wAQkQEgAEHAAGpBqOgAEJEBQajoAEHo6AAQkQEgAEGo6ABBqOkAEJIBQajpAEGo6QAQkQFBqOkAQejnAEGo6QAQkwFBqOkAQejoAEGo6QAQkwFBqOkAQajpAEGo6QAQkgFB6OcAQejnAEHo6QAQkgFB6OkAQejnAEHo6QAQkgEgAEHAAGogAEHAAGog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wABqEI4BIABBgAFqIAFBgAFqEI4BC+UCACAAIAFByP4BEI8BIABBwABqIAFBwABqQYj/ARCPASAAQYABaiABQYABakHI/wEQjwEgACAAQcAAakGIgAIQkgEgASABQcAAakHIgAIQkgEgACAAQYABakGIgQIQkgEgASABQYABakHIgQIQkgEgAEHAAGogAEGAAWpBiIICEJIBIAFBwABqIAFBgAFqQciCAhCSAUHI/gFBiP8BQYiDAhCSAUHI/gFByP8BQciDAhCSAUGI/wFByP8BQYiEAhCSAUGIggJByIICIAIQjwEgAkGIhAIgAhCTASACIAIQ9QFByP4BIAIgAhCSAUGIgAJByIACIAJBwABqEI8BIAJBwABqQYiDAiACQcAAahCTAUHI/wFByIQCEPUBIAJBwABqQciEAiACQcAAahCSAUGIgQJByIECIAJBgAFqEI8BIAJBgAFqQciDAiACQYABahCTASACQYABakGI/wEgAkGAAWoQkgELgQIAIABBiIUCEJEBIAAgAEHAAGpByIUCEI8BQciFAkHIhQJBiIYCEJIBIAAgAEHAAGpByIYCEJMBQciGAiAAQYABakHIhgIQkgFByIYCQciGAhCRASAAQcAAaiAAQYABakGIhwIQjwFBiIcCQYiHAkHIhwIQkgEgAEGAAWpBiIgCEJEBQciHAiABEPUBQYiFAiABIAEQkgFBiIgCIAFBwABqEPUBQYiGAiABQcAAaiABQcAAahCSAUGIhQJBiIgCIAFBgAFqEJIBQciHAiABQYABaiABQYABahCTAUHIhgIgAUGAAWogAUGAAWoQkgFBiIYCIAFBgAFqIAFBgAFqEJIBCzUAIAAgASACEJIBIABBwABqIAFBwABqIAJBwABqEJIBIABBgAFqIAFBgAFqIAJBgAFqEJIBCzUAIAAgASACEJMBIABBwABqIAFBwABqIAJBwABqEJMBIABBgAFqIAFBgAFqIAJBgAFqEJMBCycAIAAgARCUASAAQcAAaiABQcAAahCUASAAQYABaiABQYABahCUAQswAQF/IABBgAFqEJsBIQEgAQRAIAEPCyAAQcAAahCbASEBIAEEQCABDwsgABCbAQ8LJwAgACABEJYBIABBwABqIAFBwABqEJYBIABBgAFqIAFBgAFqEJYBCycAIAAgARCXASAAQcAAaiABQcAAahCX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ASAAQcACakGQiQQgAUHAAmoQjwELigIAIAAgARAAIABBIGogAUEgahASIAFB0IkEIAEQjwEgAEHAAGogAUHAAGoQACAAQeAAaiABQeAAahASIAFBwABqQZCKBCABQcAAahCPASAAQYABaiABQYABahAAIABBoAFqIAFBoAFqEBIgAUGAAWpB0IoEIAFBgAFqEI8BIABBwAFqIAFBwAFqEAAgAEHgAWogAUHgAWoQEiABQcABakGQiwQgAUHAAWoQjwEgAEGAAmogAUGAAmoQACAAQaACaiABQaACahASIAFBgAJqQdCLBCABQYACahCPASAAQcACaiABQcACahAAIABB4AJqIAFB4AJqEBIgAUHAAmpBkIwEIAFBwAJqEI8BC2wAIABB0IwEIAEQjwEgAEHAAGpBkI0EIAFBwABqEI8BIABBgAFqQdCNBCABQYABahCPASAAQcABakGQjgQgAUHAAWoQjwEgAEGAAmpB0I4EIAFBgAJqEI8BIABBwAJqQZCPBCABQcACahCPAQuKAgAgACABEAAgAEEgaiABQSBqEBIgAUHQjwQgARCPASAAQcAAaiABQcAAahAAIABB4ABqIAFB4ABqEBIgAUHAAGpBkJAEIAFBwABqEI8BIABBgAFqIAFBgAFqEAAgAEGgAWogAUGgAWoQEiABQYABakHQkAQgAUGAAWoQjwEgAEHAAWogAUHAAWoQACAAQeABaiABQeABahASIAFBwAFqQZCRBCABQcABahCPASAAQYACaiABQYACahAAIABBoAJqIAFBoAJqEBIgAUGAAmpB0JEEIAFBgAJqEI8BIABBwAJqIAFBwAJqEAAgAEHgAmogAUHgAmoQEiABQcACakGQkgQgAUHAAmoQjwELbAAgAEHQkgQgARCPASAAQcAAakGQkwQgAUHAAGoQjwEgAEGAAWpB0JMEIAFBgAFqEI8BIABBwAFqQZCUBCABQcABahCPASAAQYACakHQlAQgAUGAAmoQjwEgAEHAAmpBkJUEIAFBwAJqEI8BC4oCACAAIAEQACAAQSBqIAFBIGoQEiABQdCVBCABEI8BIABBwABqIAFBwABqEAAgAEHgAGogAUHgAGoQEiABQcAAakGQlgQgAUHAAGoQjwEgAEGAAWogAUGAAWoQACAAQaABaiABQaABahASIAFBgAFqQdCWBCABQYABahCP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', imports)}
|
|
2425
|
-
|
|
2426
|
-
/**
|
|
2427
|
-
* Cloudflare-Workers-compatible Groth16 ZK proof verifier.
|
|
2428
|
-
*
|
|
2429
|
-
* Drives the BN128 WASM module's exported pairing functions directly. Does NOT
|
|
2430
|
-
* use snarkjs/ffjavascript — those depend on runtime features (most notably
|
|
2431
|
-
* `URL.createObjectURL`) that CF Workers don't expose. See ROADMAP / CLAUDE.md.
|
|
2432
|
-
*
|
|
2433
|
-
* Two ways to initialize:
|
|
2434
|
-
* 1. `await initBn128Wasm()` — uses the bundled bn128.wasm (base64-inlined at
|
|
2435
|
-
* build time via `@rollup/plugin-wasm`). Works in any modern JS runtime
|
|
2436
|
-
* that allows runtime `WebAssembly.compile()` — Node, browsers, most
|
|
2437
|
-
* Workers configurations.
|
|
2438
|
-
* 2. `await initBn128Wasm(myWasmModule)` — accepts a pre-compiled
|
|
2439
|
-
* `WebAssembly.Module`. Lets a CF-Workers consumer keep the native pattern
|
|
2440
|
-
* of importing `.wasm` so wrangler precompiles it at deploy time and the
|
|
2441
|
-
* worker never pays for runtime compilation.
|
|
2442
|
-
*/
|
|
2443
|
-
// BN128 curve sizing
|
|
2444
|
-
const n8q = 32;
|
|
2445
|
-
const n8r = 32;
|
|
2446
|
-
const f1size = n8q;
|
|
2447
|
-
const g1size = f1size * 3;
|
|
2448
|
-
const g2size = f1size * 6;
|
|
2449
|
-
const ftsize = f1size * 12;
|
|
2450
|
-
// Field modulus q (base field) and r (scalar field)
|
|
2451
|
-
const q = BigInt("21888242871839275222246405745257275088696311157297823662689037894645226208583");
|
|
2452
|
-
const r = BigInt("21888242871839275222246405745257275088548364400416034343698204186575808495617");
|
|
2453
|
-
// Initial linear memory: 25 * 64KB = 1.6MB
|
|
2454
|
-
const MEM_SIZE = 25;
|
|
2455
|
-
/**
|
|
2456
|
-
* Initialize the BN128 WASM module.
|
|
2457
|
-
*
|
|
2458
|
-
* @param wasmModule Optional pre-compiled `WebAssembly.Module`. If omitted, the
|
|
2459
|
-
* bundled bn128.wasm (base64-inlined by `@rollup/plugin-wasm` at build time)
|
|
2460
|
-
* is used. Passing your own module is the recommended path inside CF Workers,
|
|
2461
|
-
* where wrangler can precompile a `.wasm` import at deploy time and avoid the
|
|
2462
|
-
* runtime compile cost.
|
|
2463
|
-
*/
|
|
2464
|
-
async function initBn128Wasm(wasmModule) {
|
|
2465
|
-
const memory = new WebAssembly.Memory({ initial: MEM_SIZE, maximum: 32767 });
|
|
2466
|
-
const imports = { env: { memory } };
|
|
2467
|
-
let instance;
|
|
2468
|
-
if (wasmModule) {
|
|
2469
|
-
instance = await WebAssembly.instantiate(wasmModule, imports);
|
|
2470
|
-
}
|
|
2471
|
-
else {
|
|
2472
|
-
// Bundled fallback: the rollup plugin returns a loader function that takes
|
|
2473
|
-
// an import object and resolves to an instantiated WebAssembly.Instance.
|
|
2474
|
-
instance = await loadBundledBn128(imports);
|
|
2475
|
-
}
|
|
2476
|
-
// The first u32 in linear memory is the heap free pointer (set by the module
|
|
2477
|
-
// during static initialization). Remember it so we can reset between calls.
|
|
2478
|
-
const u32 = new Uint32Array(memory.buffer);
|
|
2479
|
-
const initialPFree = u32[0];
|
|
2480
|
-
return { instance, memory, initialPFree };
|
|
2481
|
-
}
|
|
2482
|
-
/**
|
|
2483
|
-
* Verify a Groth16 proof.
|
|
2484
|
-
*
|
|
2485
|
-
* Returns `false` (rather than throwing) for any structural problem — malformed
|
|
2486
|
-
* proof, out-of-range field elements, off-curve points, or a failed pairing.
|
|
2487
|
-
* Throws only for runtime/WASM faults.
|
|
2488
|
-
*/
|
|
2489
|
-
async function verifyGroth16(wasmInstance, memory, initialPFree, verificationKey, proof, publicSignals) {
|
|
2490
|
-
try {
|
|
2491
|
-
const vk = unstringifyBigInts(verificationKey);
|
|
2492
|
-
const p = unstringifyBigInts(proof);
|
|
2493
|
-
const signals = unstringifyBigInts(publicSignals);
|
|
2494
|
-
// Validate proof coordinates are within the base field
|
|
2495
|
-
if (!validateG1InField(p.pi_a))
|
|
2496
|
-
return false;
|
|
2497
|
-
if (!validateG1InField(p.pi_c))
|
|
2498
|
-
return false;
|
|
2499
|
-
for (let i = 0; i < 2; i++) {
|
|
2500
|
-
for (let j = 0; j < 2; j++) {
|
|
2501
|
-
const val = typeof p.pi_b[i][j] === "bigint" ? p.pi_b[i][j] : BigInt(String(p.pi_b[i][j]));
|
|
2502
|
-
if (val >= q)
|
|
2503
|
-
return false;
|
|
2504
|
-
}
|
|
2505
|
-
}
|
|
2506
|
-
// Validate public signals are within the scalar field
|
|
2507
|
-
for (const signal of signals) {
|
|
2508
|
-
if (signal >= r)
|
|
2509
|
-
return false;
|
|
2510
|
-
}
|
|
2511
|
-
const exports = wasmInstance.exports;
|
|
2512
|
-
const u8 = new Uint8Array(memory.buffer);
|
|
2513
|
-
const u32 = new Uint32Array(memory.buffer);
|
|
2514
|
-
// Reset the WASM allocator's heap pointer between invocations
|
|
2515
|
-
u32[0] = initialPFree;
|
|
2516
|
-
const g1m_add = exports["g1m_add"];
|
|
2517
|
-
const g1m_timesScalar = exports["g1m_timesScalar"];
|
|
2518
|
-
const g1m_neg = exports["g1m_neg"];
|
|
2519
|
-
const g1m_inCurve = exports["g1m_inCurve"];
|
|
2520
|
-
const g2m_inCurve = exports["g2m_inCurve"];
|
|
2521
|
-
const ftm_one = exports["ftm_one"];
|
|
2522
|
-
const bn128_pairingEq4 = exports["bn128_pairingEq4"];
|
|
2523
|
-
// Compute vk_x = IC[0] + sum(IC[i+1] * signal[i])
|
|
2524
|
-
const pVkX = alloc(u32, g1size);
|
|
2525
|
-
const pIC0 = alloc(u32, g1size);
|
|
2526
|
-
writeG1Point(u8, pIC0, vk.IC[0], exports);
|
|
2527
|
-
u8.set(u8.slice(pIC0, pIC0 + g1size), pVkX);
|
|
2528
|
-
const pTemp = alloc(u32, g1size);
|
|
2529
|
-
const pScalar = alloc(u32, n8r);
|
|
2530
|
-
for (let i = 0; i < signals.length; i++) {
|
|
2531
|
-
const pICi = alloc(u32, g1size);
|
|
2532
|
-
writeG1Point(u8, pICi, vk.IC[i + 1], exports);
|
|
2533
|
-
u8.set(bigIntToLE(signals[i], n8r), pScalar);
|
|
2534
|
-
g1m_timesScalar(pICi, pScalar, n8r, pTemp);
|
|
2535
|
-
const pSum = alloc(u32, g1size);
|
|
2536
|
-
g1m_add(pVkX, pTemp, pSum);
|
|
2537
|
-
u8.set(u8.slice(pSum, pSum + g1size), pVkX);
|
|
2538
|
-
}
|
|
2539
|
-
// Allocate proof points
|
|
2540
|
-
const pA = alloc(u32, g1size);
|
|
2541
|
-
const pNegA = alloc(u32, g1size);
|
|
2542
|
-
const pB = alloc(u32, g2size);
|
|
2543
|
-
const pC = alloc(u32, g1size);
|
|
2544
|
-
writeG1Point(u8, pA, p.pi_a, exports);
|
|
2545
|
-
writeG2Point(u8, pB, p.pi_b, exports);
|
|
2546
|
-
writeG1Point(u8, pC, p.pi_c, exports);
|
|
2547
|
-
// Subgroup / on-curve checks
|
|
2548
|
-
if (!g1m_inCurve(pA))
|
|
2549
|
-
return false;
|
|
2550
|
-
if (!g2m_inCurve(pB))
|
|
2551
|
-
return false;
|
|
2552
|
-
if (!g1m_inCurve(pC))
|
|
2553
|
-
return false;
|
|
2554
|
-
g1m_neg(pA, pNegA);
|
|
2555
|
-
// Verification key points
|
|
2556
|
-
const pAlpha = alloc(u32, g1size);
|
|
2557
|
-
const pBeta = alloc(u32, g2size);
|
|
2558
|
-
const pGamma = alloc(u32, g2size);
|
|
2559
|
-
const pDelta = alloc(u32, g2size);
|
|
2560
|
-
writeG1Point(u8, pAlpha, vk.vk_alpha_1, exports);
|
|
2561
|
-
writeG2Point(u8, pBeta, vk.vk_beta_2, exports);
|
|
2562
|
-
writeG2Point(u8, pGamma, vk.vk_gamma_2, exports);
|
|
2563
|
-
writeG2Point(u8, pDelta, vk.vk_delta_2, exports);
|
|
2564
|
-
// Target: F12 element representing 1
|
|
2565
|
-
const pOne = alloc(u32, ftsize);
|
|
2566
|
-
ftm_one(pOne);
|
|
2567
|
-
// e(-A, B) * e(alpha, beta) * e(vk_x, gamma) * e(C, delta) == 1
|
|
2568
|
-
const result = bn128_pairingEq4(pNegA, pB, pAlpha, pBeta, pVkX, pGamma, pC, pDelta, pOne);
|
|
2569
|
-
return result === 1;
|
|
2570
|
-
}
|
|
2571
|
-
catch {
|
|
2572
|
-
return false;
|
|
2573
|
-
}
|
|
2574
|
-
}
|
|
2575
|
-
// ---------------------------------------------------------------------------
|
|
2576
|
-
// Internals
|
|
2577
|
-
// ---------------------------------------------------------------------------
|
|
2578
|
-
function parseToBigInt(value) {
|
|
2579
|
-
if (typeof value === "string") {
|
|
2580
|
-
if (/^0x[0-9a-fA-F]+$/.test(value))
|
|
2581
|
-
return BigInt(value);
|
|
2582
|
-
if (/^[0-9a-fA-F]+$/.test(value) && /[a-fA-F]/.test(value))
|
|
2583
|
-
return BigInt("0x" + value);
|
|
2584
|
-
if (/^[0-9]+$/.test(value))
|
|
2585
|
-
return BigInt(value);
|
|
2586
|
-
throw new Error(`Cannot convert ${value} to BigInt`);
|
|
2587
|
-
}
|
|
2588
|
-
if (typeof value === "number")
|
|
2589
|
-
return BigInt(value);
|
|
2590
|
-
return value;
|
|
2591
|
-
}
|
|
2592
|
-
function bigIntToLE(value, size) {
|
|
2593
|
-
const result = new Uint8Array(size);
|
|
2594
|
-
let v = parseToBigInt(value);
|
|
2595
|
-
for (let i = 0; i < size; i++) {
|
|
2596
|
-
result[i] = Number(v & 0xffn);
|
|
2597
|
-
v = v >> 8n;
|
|
2598
|
-
}
|
|
2599
|
-
return result;
|
|
2600
|
-
}
|
|
2601
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2602
|
-
function unstringifyBigInts(o) {
|
|
2603
|
-
if (typeof o === "string") {
|
|
2604
|
-
try {
|
|
2605
|
-
if (/^[0-9]+$/.test(o) || /^0x[0-9a-fA-F]+$/.test(o) ||
|
|
2606
|
-
(/^[0-9a-fA-F]+$/.test(o) && /[a-fA-F]/.test(o))) {
|
|
2607
|
-
return parseToBigInt(o);
|
|
2608
|
-
}
|
|
2609
|
-
}
|
|
2610
|
-
catch {
|
|
2611
|
-
// fall through
|
|
2612
|
-
}
|
|
2613
|
-
return o;
|
|
2614
|
-
}
|
|
2615
|
-
if (Array.isArray(o))
|
|
2616
|
-
return o.map(unstringifyBigInts);
|
|
2617
|
-
if (typeof o === "object" && o !== null) {
|
|
2618
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2619
|
-
const res = {};
|
|
2620
|
-
for (const k of Object.keys(o))
|
|
2621
|
-
res[k] = unstringifyBigInts(o[k]);
|
|
2622
|
-
return res;
|
|
2623
|
-
}
|
|
2624
|
-
return o;
|
|
2625
|
-
}
|
|
2626
|
-
function validateG1InField(point) {
|
|
2627
|
-
const x = typeof point[0] === "bigint" ? point[0] : BigInt(String(point[0]));
|
|
2628
|
-
const y = typeof point[1] === "bigint" ? point[1] : BigInt(String(point[1]));
|
|
2629
|
-
return x < q && y < q;
|
|
2630
|
-
}
|
|
2631
|
-
function writeG1Point(u8, offset, point, exports) {
|
|
2632
|
-
const x = point[0];
|
|
2633
|
-
const y = point[1];
|
|
2634
|
-
const z = point.length > 2 ? point[2] : 1n;
|
|
2635
|
-
u8.set(bigIntToLE(x, f1size), offset);
|
|
2636
|
-
u8.set(bigIntToLE(y, f1size), offset + f1size);
|
|
2637
|
-
u8.set(bigIntToLE(z, f1size), offset + 2 * f1size);
|
|
2638
|
-
const f1m_toMontgomery = exports["f1m_toMontgomery"];
|
|
2639
|
-
f1m_toMontgomery(offset, offset);
|
|
2640
|
-
f1m_toMontgomery(offset + f1size, offset + f1size);
|
|
2641
|
-
f1m_toMontgomery(offset + 2 * f1size, offset + 2 * f1size);
|
|
2642
|
-
}
|
|
2643
|
-
function writeG2Point(u8, offset, point, exports) {
|
|
2644
|
-
const x = point[0];
|
|
2645
|
-
const y = point[1];
|
|
2646
|
-
const z = point.length > 2 ? point[2] : [1n, 0n];
|
|
2647
|
-
u8.set(bigIntToLE(x[0], f1size), offset);
|
|
2648
|
-
u8.set(bigIntToLE(x[1], f1size), offset + f1size);
|
|
2649
|
-
u8.set(bigIntToLE(y[0], f1size), offset + 2 * f1size);
|
|
2650
|
-
u8.set(bigIntToLE(y[1], f1size), offset + 3 * f1size);
|
|
2651
|
-
u8.set(bigIntToLE(z[0], f1size), offset + 4 * f1size);
|
|
2652
|
-
u8.set(bigIntToLE(z[1], f1size), offset + 5 * f1size);
|
|
2653
|
-
const f1m_toMontgomery = exports["f1m_toMontgomery"];
|
|
2654
|
-
for (let i = 0; i < 6; i++) {
|
|
2655
|
-
f1m_toMontgomery(offset + i * f1size, offset + i * f1size);
|
|
2656
|
-
}
|
|
2657
|
-
}
|
|
2658
|
-
function alloc(u32, size) {
|
|
2659
|
-
while (u32[0] & 3)
|
|
2660
|
-
u32[0]++;
|
|
2661
|
-
const ptr = u32[0];
|
|
2662
|
-
u32[0] += size;
|
|
2663
|
-
return ptr;
|
|
2664
|
-
}
|
|
2665
|
-
|
|
2666
|
-
const appLogger$1 = new Logger("connect", 'ERROR');
|
|
2667
|
-
globalThis.appLogger = appLogger$1;
|
|
2668
|
-
|
|
2669
|
-
export { AES_GCM_IV_BYTES, AES_GCM_KEY_BYTES, AES_GCM_TAG_BYTES, BroadcastChannel, BroadcastChannelEvents, ChunkCipher, DoubleRatchet, EncryptedSession, EventCore, EventCoreEvents, KeyStore, Message, Message as MessageDefault, PBKDF2_DEFAULT_ITERATIONS, PREIMAGE_POK_VERIFICATION_KEY, Packet, SerializeMessage, WSTransport, decorators, decryptAesGcm, deriveAesKeyFromPassphrase, deriveBitsHkdf, encryptAesGcm, getSubtle, initBn128Wasm, randomBytes, unwrapWithPassphrase, verifyGroth16, wrapWithPassphrase };
|
|
2670
|
-
//# sourceMappingURL=index.js.map
|