@getpaseo/relay 0.2.2 → 0.2.4
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/dist/crypto.d.ts +3 -3
- package/dist/crypto.js +3 -9
- package/dist/e2ee.d.ts +1 -1
- package/dist/encrypted-channel.d.ts +10 -2
- package/dist/encrypted-channel.js +133 -29
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
package/dist/crypto.d.ts
CHANGED
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
* Bundle format (binary):
|
|
8
8
|
* [nonce (24 bytes)] [ciphertext...]
|
|
9
9
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
10
|
+
* The encrypted channel chooses the WebSocket representation. Crypto remains
|
|
11
|
+
* byte-oriented so frame kind is never inferred from plaintext contents.
|
|
12
12
|
*/
|
|
13
13
|
export interface KeyPair {
|
|
14
14
|
publicKey: Uint8Array;
|
|
@@ -26,5 +26,5 @@ export declare function deriveSharedKey(ourSecretKey: Uint8Array, peerPublicKey:
|
|
|
26
26
|
* [nonce (24)] [ciphertext...]
|
|
27
27
|
*/
|
|
28
28
|
export declare function encrypt(sharedKey: SharedKey, data: string | ArrayBuffer): ArrayBuffer;
|
|
29
|
-
export declare function decrypt(sharedKey: SharedKey, data: ArrayBuffer):
|
|
29
|
+
export declare function decrypt(sharedKey: SharedKey, data: ArrayBuffer): ArrayBuffer;
|
|
30
30
|
//# sourceMappingURL=crypto.d.ts.map
|
package/dist/crypto.js
CHANGED
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
* Bundle format (binary):
|
|
9
9
|
* [nonce (24 bytes)] [ciphertext...]
|
|
10
10
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
11
|
+
* The encrypted channel chooses the WebSocket representation. Crypto remains
|
|
12
|
+
* byte-oriented so frame kind is never inferred from plaintext contents.
|
|
13
13
|
*/
|
|
14
14
|
import nacl from "tweetnacl";
|
|
15
15
|
import { fromByteArray, toByteArray } from "base64-js";
|
|
@@ -121,12 +121,6 @@ export function decrypt(sharedKey, data) {
|
|
|
121
121
|
if (!opened) {
|
|
122
122
|
throw new Error("Decryption failed");
|
|
123
123
|
}
|
|
124
|
-
|
|
125
|
-
try {
|
|
126
|
-
return new TextDecoder("utf-8", { fatal: true }).decode(plaintext);
|
|
127
|
-
}
|
|
128
|
-
catch {
|
|
129
|
-
return plaintext;
|
|
130
|
-
}
|
|
124
|
+
return toArrayBuffer(opened);
|
|
131
125
|
}
|
|
132
126
|
//# sourceMappingURL=crypto.js.map
|
package/dist/e2ee.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { createClientChannel, createDaemonChannel, EncryptedChannel } from "./encrypted-channel.js";
|
|
2
|
-
export type { Transport, EncryptedChannelEvents } from "./encrypted-channel.js";
|
|
2
|
+
export type { Transport, TransportMessage, EncryptedChannelEvents } from "./encrypted-channel.js";
|
|
3
3
|
export { generateKeyPair, exportPublicKey, importPublicKey, exportSecretKey, importSecretKey, } from "./crypto.js";
|
|
4
4
|
export type { KeyPair, SharedKey } from "./crypto.js";
|
|
5
5
|
//# sourceMappingURL=e2ee.d.ts.map
|
|
@@ -6,12 +6,16 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { type KeyPair, type SharedKey } from "./crypto.js";
|
|
8
8
|
export interface Transport {
|
|
9
|
-
send(data: string | ArrayBuffer): void
|
|
9
|
+
send(data: string | ArrayBuffer): void | Promise<void>;
|
|
10
10
|
close(code?: number, reason?: string): void;
|
|
11
|
-
onmessage: ((
|
|
11
|
+
onmessage: ((message: TransportMessage) => void) | null;
|
|
12
12
|
onclose: ((code: number, reason: string) => void) | null;
|
|
13
13
|
onerror: ((error: Error) => void) | null;
|
|
14
14
|
}
|
|
15
|
+
export interface TransportMessage {
|
|
16
|
+
data: string | ArrayBuffer;
|
|
17
|
+
isBinary: boolean;
|
|
18
|
+
}
|
|
15
19
|
export interface EncryptedChannelEvents {
|
|
16
20
|
onopen?: () => void;
|
|
17
21
|
onmessage?: (data: string | ArrayBuffer) => void;
|
|
@@ -29,7 +33,10 @@ interface EncryptedChannelOptions {
|
|
|
29
33
|
* the daemon should re-send `{type:"e2ee_ready"}` without changing keys.
|
|
30
34
|
*/
|
|
31
35
|
daemonKeyPair?: KeyPair;
|
|
36
|
+
binaryCiphertext?: boolean;
|
|
32
37
|
}
|
|
38
|
+
export declare function base64EncryptedWireByteLength(plaintextBytes: number): number;
|
|
39
|
+
export declare function maxBase64EncryptedPlaintextByteLength(wireBytes: number): number;
|
|
33
40
|
/**
|
|
34
41
|
* Creates an encrypted channel as the initiator (client).
|
|
35
42
|
*
|
|
@@ -65,6 +72,7 @@ export declare class EncryptedChannel {
|
|
|
65
72
|
setState(state: ChannelState): void;
|
|
66
73
|
private handleMessage;
|
|
67
74
|
send(data: string | ArrayBuffer): Promise<void>;
|
|
75
|
+
outboundWireByteLength(data: string | ArrayBuffer): number;
|
|
68
76
|
private flushPendingSends;
|
|
69
77
|
private handleDaemonRehello;
|
|
70
78
|
close(code?: number, reason?: string): void;
|
|
@@ -10,14 +10,23 @@ import { arrayBufferToBase64, base64ToArrayBuffer } from "./base64.js";
|
|
|
10
10
|
function isRecord(value) {
|
|
11
11
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
12
12
|
}
|
|
13
|
+
function isE2EECapabilities(value) {
|
|
14
|
+
return (value === undefined ||
|
|
15
|
+
(isRecord(value) &&
|
|
16
|
+
(value.binaryCiphertext === undefined || typeof value.binaryCiphertext === "boolean")));
|
|
17
|
+
}
|
|
13
18
|
function isE2EEHelloMessage(value) {
|
|
14
19
|
return (isRecord(value) &&
|
|
15
20
|
value.type === "e2ee_hello" &&
|
|
16
21
|
typeof value.key === "string" &&
|
|
17
|
-
value.key.trim().length > 0
|
|
22
|
+
value.key.trim().length > 0 &&
|
|
23
|
+
isE2EECapabilities(value.capabilities));
|
|
18
24
|
}
|
|
19
25
|
function isE2EEReadyMessage(value) {
|
|
20
|
-
return isRecord(value) && value.type === "e2ee_ready";
|
|
26
|
+
return isRecord(value) && value.type === "e2ee_ready" && isE2EECapabilities(value.capabilities);
|
|
27
|
+
}
|
|
28
|
+
function supportsBinaryCiphertext(message) {
|
|
29
|
+
return message.capabilities?.binaryCiphertext === true;
|
|
21
30
|
}
|
|
22
31
|
function buildInvalidHelloError(rawText, parsed) {
|
|
23
32
|
const parsedRecord = isRecord(parsed) ? parsed : null;
|
|
@@ -38,6 +47,13 @@ function buildInvalidHelloError(rawText, parsed) {
|
|
|
38
47
|
const HANDSHAKE_RETRY_MS = 1000;
|
|
39
48
|
const MAX_PENDING_SENDS = 200;
|
|
40
49
|
const REHANDSHAKE_KEY_MISMATCH_CLOSE_CODE = 1008;
|
|
50
|
+
const ENCRYPTED_PAYLOAD_OVERHEAD_BYTES = 40;
|
|
51
|
+
export function base64EncryptedWireByteLength(plaintextBytes) {
|
|
52
|
+
return 4 * Math.ceil((plaintextBytes + ENCRYPTED_PAYLOAD_OVERHEAD_BYTES) / 3);
|
|
53
|
+
}
|
|
54
|
+
export function maxBase64EncryptedPlaintextByteLength(wireBytes) {
|
|
55
|
+
return Math.floor(wireBytes / 4) * 3 - ENCRYPTED_PAYLOAD_OVERHEAD_BYTES;
|
|
56
|
+
}
|
|
41
57
|
const REHANDSHAKE_KEY_MISMATCH_CLOSE_REASON = "E2EE re-handshake key mismatch";
|
|
42
58
|
function hasUnref(timeout) {
|
|
43
59
|
return (typeof timeout === "object" &&
|
|
@@ -61,7 +77,11 @@ export async function createClientChannel(transport, daemonPublicKeyB64, events
|
|
|
61
77
|
const channel = new EncryptedChannel(transport, sharedKey, events);
|
|
62
78
|
// Send e2ee_hello with our public key
|
|
63
79
|
const ourPublicKeyB64 = exportPublicKey(keyPair.publicKey);
|
|
64
|
-
const hello = {
|
|
80
|
+
const hello = {
|
|
81
|
+
type: "e2ee_hello",
|
|
82
|
+
key: ourPublicKeyB64,
|
|
83
|
+
capabilities: { binaryCiphertext: true },
|
|
84
|
+
};
|
|
65
85
|
const helloText = JSON.stringify(hello);
|
|
66
86
|
let retry = null;
|
|
67
87
|
const emitSendError = (error) => {
|
|
@@ -70,7 +90,10 @@ export async function createClientChannel(transport, daemonPublicKeyB64, events
|
|
|
70
90
|
};
|
|
71
91
|
const sendHello = () => {
|
|
72
92
|
try {
|
|
73
|
-
transport.send(helloText);
|
|
93
|
+
const result = transport.send(helloText);
|
|
94
|
+
if (result) {
|
|
95
|
+
void result.catch(emitSendError);
|
|
96
|
+
}
|
|
74
97
|
return true;
|
|
75
98
|
}
|
|
76
99
|
catch (error) {
|
|
@@ -113,9 +136,11 @@ export async function createClientChannel(transport, daemonPublicKeyB64, events
|
|
|
113
136
|
export async function createDaemonChannel(transport, daemonKeyPair, events = {}) {
|
|
114
137
|
return new Promise((resolve, reject) => {
|
|
115
138
|
const bufferedMessages = [];
|
|
116
|
-
const shouldIgnorePostHelloPlaintext = (
|
|
139
|
+
const shouldIgnorePostHelloPlaintext = (message) => {
|
|
117
140
|
try {
|
|
118
|
-
|
|
141
|
+
if (message.isBinary)
|
|
142
|
+
return false;
|
|
143
|
+
const text = decodeTransportText(message.data);
|
|
119
144
|
const parsed = JSON.parse(text);
|
|
120
145
|
return isE2EEHelloMessage(parsed) || isE2EEReadyMessage(parsed);
|
|
121
146
|
}
|
|
@@ -123,9 +148,12 @@ export async function createDaemonChannel(transport, daemonKeyPair, events = {})
|
|
|
123
148
|
return false;
|
|
124
149
|
}
|
|
125
150
|
};
|
|
126
|
-
const handleHello = async (
|
|
151
|
+
const handleHello = async (message) => {
|
|
127
152
|
try {
|
|
128
|
-
|
|
153
|
+
if (message.isBinary) {
|
|
154
|
+
throw buildInvalidHelloError("<binary frame>");
|
|
155
|
+
}
|
|
156
|
+
const helloText = decodeTransportText(message.data);
|
|
129
157
|
let parsed;
|
|
130
158
|
try {
|
|
131
159
|
parsed = JSON.parse(helloText);
|
|
@@ -147,8 +175,17 @@ export async function createDaemonChannel(transport, daemonKeyPair, events = {})
|
|
|
147
175
|
Object.assign(transport, { onmessage: bufferNext });
|
|
148
176
|
const clientPublicKey = importPublicKey(msg.key);
|
|
149
177
|
const sharedKey = deriveSharedKey(daemonKeyPair.secretKey, clientPublicKey);
|
|
150
|
-
const
|
|
151
|
-
transport.send(JSON.stringify({
|
|
178
|
+
const binaryCiphertext = supportsBinaryCiphertext(msg);
|
|
179
|
+
await transport.send(JSON.stringify({
|
|
180
|
+
type: "e2ee_ready",
|
|
181
|
+
...(binaryCiphertext
|
|
182
|
+
? { capabilities: { binaryCiphertext: true } }
|
|
183
|
+
: {}),
|
|
184
|
+
}));
|
|
185
|
+
const channel = new EncryptedChannel(transport, sharedKey, events, {
|
|
186
|
+
daemonKeyPair,
|
|
187
|
+
binaryCiphertext,
|
|
188
|
+
});
|
|
152
189
|
channel.setState("open");
|
|
153
190
|
events.onopen?.();
|
|
154
191
|
for (const buffered of bufferedMessages) {
|
|
@@ -187,7 +224,7 @@ export class EncryptedChannel {
|
|
|
187
224
|
this.events = events;
|
|
188
225
|
this.options = options;
|
|
189
226
|
Object.assign(transport, {
|
|
190
|
-
onmessage: (
|
|
227
|
+
onmessage: (message) => this.handleMessage(message),
|
|
191
228
|
onclose: (code, reason) => {
|
|
192
229
|
this.state = "closed";
|
|
193
230
|
this.events.onclose?.(code, reason);
|
|
@@ -202,17 +239,28 @@ export class EncryptedChannel {
|
|
|
202
239
|
setState(state) {
|
|
203
240
|
this.state = state;
|
|
204
241
|
}
|
|
205
|
-
async handleMessage(
|
|
242
|
+
async handleMessage(message) {
|
|
206
243
|
if (this.state === "handshaking") {
|
|
207
244
|
try {
|
|
208
|
-
|
|
245
|
+
if (message.isBinary)
|
|
246
|
+
return;
|
|
247
|
+
const text = decodeTransportText(message.data);
|
|
209
248
|
const parsed = JSON.parse(text);
|
|
210
249
|
if (isE2EEReadyMessage(parsed)) {
|
|
250
|
+
this.options.binaryCiphertext = supportsBinaryCiphertext(parsed);
|
|
211
251
|
this.state = "open";
|
|
212
252
|
this.events.onopen?.();
|
|
213
253
|
for (const cb of this.onOpenCallbacks)
|
|
214
254
|
cb();
|
|
215
|
-
|
|
255
|
+
try {
|
|
256
|
+
await this.flushPendingSends();
|
|
257
|
+
}
|
|
258
|
+
catch (error) {
|
|
259
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
260
|
+
this.events.onerror?.(err);
|
|
261
|
+
this.state = "closed";
|
|
262
|
+
this.transport.close(1011, err.message);
|
|
263
|
+
}
|
|
216
264
|
}
|
|
217
265
|
}
|
|
218
266
|
catch {
|
|
@@ -226,12 +274,14 @@ export class EncryptedChannel {
|
|
|
226
274
|
const ciphertext = await (async () => {
|
|
227
275
|
// Handle (or ignore) any stray plaintext handshake traffic.
|
|
228
276
|
try {
|
|
229
|
-
|
|
277
|
+
if (message.isBinary)
|
|
278
|
+
throw new Error("not plaintext handshake traffic");
|
|
279
|
+
const text = decodeTransportText(message.data);
|
|
230
280
|
if (text.trim().startsWith("{")) {
|
|
231
281
|
const parsed = JSON.parse(text);
|
|
232
282
|
if (isE2EEHelloMessage(parsed)) {
|
|
233
283
|
if (this.options.daemonKeyPair) {
|
|
234
|
-
await this.handleDaemonRehello(parsed
|
|
284
|
+
await this.handleDaemonRehello(parsed);
|
|
235
285
|
}
|
|
236
286
|
return null;
|
|
237
287
|
}
|
|
@@ -251,21 +301,31 @@ export class EncryptedChannel {
|
|
|
251
301
|
// Otherwise ignore JSON parse/TextDecoder failures and fall back to
|
|
252
302
|
// decoding ciphertext below.
|
|
253
303
|
}
|
|
254
|
-
if (
|
|
255
|
-
return
|
|
304
|
+
if (this.options.binaryCiphertext) {
|
|
305
|
+
return message.isBinary
|
|
306
|
+
? { data: requireArrayBuffer(message.data), isBinary: true }
|
|
307
|
+
: {
|
|
308
|
+
data: base64ToArrayBuffer(decodeTransportText(message.data)),
|
|
309
|
+
isBinary: false,
|
|
310
|
+
};
|
|
256
311
|
}
|
|
257
|
-
//
|
|
258
|
-
//
|
|
312
|
+
// COMPAT(binaryCiphertext): added in v0.2.3, remove legacy base64-only
|
|
313
|
+
// receive mode after 2027-01-27.
|
|
314
|
+
if (!message.isBinary) {
|
|
315
|
+
return { data: base64ToArrayBuffer(decodeTransportText(message.data)), isBinary: null };
|
|
316
|
+
}
|
|
317
|
+
// Older transport adapters could lose the opcode. Retain the former
|
|
318
|
+
// base64-first behavior only in the legacy path.
|
|
259
319
|
try {
|
|
260
|
-
|
|
261
|
-
return base64ToArrayBuffer(decoded);
|
|
320
|
+
return { data: base64ToArrayBuffer(decodeTransportText(message.data)), isBinary: null };
|
|
262
321
|
}
|
|
263
322
|
catch {
|
|
264
|
-
return data;
|
|
323
|
+
return { data: requireArrayBuffer(message.data), isBinary: null };
|
|
265
324
|
}
|
|
266
325
|
})();
|
|
267
326
|
if (ciphertext) {
|
|
268
|
-
const
|
|
327
|
+
const plaintextBytes = decrypt(this.sharedKey, ciphertext.data);
|
|
328
|
+
const plaintext = decodePlaintext(plaintextBytes, ciphertext.isBinary);
|
|
269
329
|
this.events.onmessage?.(plaintext);
|
|
270
330
|
}
|
|
271
331
|
}
|
|
@@ -294,8 +354,21 @@ export class EncryptedChannel {
|
|
|
294
354
|
throw new Error("Channel not open");
|
|
295
355
|
}
|
|
296
356
|
const ciphertext = encrypt(this.sharedKey, data);
|
|
297
|
-
|
|
298
|
-
|
|
357
|
+
if (this.options.binaryCiphertext && data instanceof ArrayBuffer) {
|
|
358
|
+
await this.transport.send(ciphertext);
|
|
359
|
+
return;
|
|
360
|
+
}
|
|
361
|
+
// COMPAT(binaryCiphertext): added in v0.2.3, remove base64 binary sends
|
|
362
|
+
// after 2027-01-27 once the supported peer floor includes negotiation.
|
|
363
|
+
await this.transport.send(arrayBufferToBase64(ciphertext));
|
|
364
|
+
}
|
|
365
|
+
outboundWireByteLength(data) {
|
|
366
|
+
const plaintextBytes = utf8ByteLength(data);
|
|
367
|
+
const encryptedBytes = plaintextBytes + ENCRYPTED_PAYLOAD_OVERHEAD_BYTES;
|
|
368
|
+
if (this.options.binaryCiphertext && data instanceof ArrayBuffer) {
|
|
369
|
+
return encryptedBytes;
|
|
370
|
+
}
|
|
371
|
+
return base64EncryptedWireByteLength(plaintextBytes);
|
|
299
372
|
}
|
|
300
373
|
async flushPendingSends() {
|
|
301
374
|
if (this.state !== "open")
|
|
@@ -306,16 +379,21 @@ export class EncryptedChannel {
|
|
|
306
379
|
await this.send(item);
|
|
307
380
|
}
|
|
308
381
|
}
|
|
309
|
-
async handleDaemonRehello(
|
|
382
|
+
async handleDaemonRehello(message) {
|
|
310
383
|
if (!this.options.daemonKeyPair)
|
|
311
384
|
return;
|
|
312
|
-
const clientPublicKey = importPublicKey(
|
|
385
|
+
const clientPublicKey = importPublicKey(message.key);
|
|
313
386
|
const nextSharedKey = deriveSharedKey(this.options.daemonKeyPair.secretKey, clientPublicKey);
|
|
314
387
|
// If it's the same client key (handshake retry), re-send
|
|
315
388
|
// "ready" but do not re-key. Re-keying here would desync
|
|
316
389
|
// the channel and cause decrypt failures.
|
|
317
390
|
if (keysEqual(nextSharedKey, this.sharedKey)) {
|
|
318
|
-
this.transport.send(JSON.stringify({
|
|
391
|
+
await this.transport.send(JSON.stringify({
|
|
392
|
+
type: "e2ee_ready",
|
|
393
|
+
...(this.options.binaryCiphertext
|
|
394
|
+
? { capabilities: { binaryCiphertext: true } }
|
|
395
|
+
: {}),
|
|
396
|
+
}));
|
|
319
397
|
return;
|
|
320
398
|
}
|
|
321
399
|
// A different key on an already-open encrypted channel is not an
|
|
@@ -338,6 +416,32 @@ export class EncryptedChannel {
|
|
|
338
416
|
this.onCloseCallbacks.push(cb);
|
|
339
417
|
}
|
|
340
418
|
}
|
|
419
|
+
function decodeTransportText(data) {
|
|
420
|
+
return typeof data === "string" ? data : new TextDecoder().decode(data);
|
|
421
|
+
}
|
|
422
|
+
function requireArrayBuffer(data) {
|
|
423
|
+
if (data instanceof ArrayBuffer)
|
|
424
|
+
return data;
|
|
425
|
+
throw new Error("Binary WebSocket frame did not contain bytes");
|
|
426
|
+
}
|
|
427
|
+
function decodeLegacyPlaintext(data) {
|
|
428
|
+
try {
|
|
429
|
+
return new TextDecoder("utf-8", { fatal: true }).decode(data);
|
|
430
|
+
}
|
|
431
|
+
catch {
|
|
432
|
+
return data;
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
function decodePlaintext(data, isBinary) {
|
|
436
|
+
if (isBinary === true)
|
|
437
|
+
return data;
|
|
438
|
+
if (isBinary === false)
|
|
439
|
+
return new TextDecoder("utf-8", { fatal: true }).decode(data);
|
|
440
|
+
return decodeLegacyPlaintext(data);
|
|
441
|
+
}
|
|
442
|
+
function utf8ByteLength(data) {
|
|
443
|
+
return typeof data === "string" ? new TextEncoder().encode(data).byteLength : data.byteLength;
|
|
444
|
+
}
|
|
341
445
|
function keysEqual(a, b) {
|
|
342
446
|
if (a.byteLength !== b.byteLength)
|
|
343
447
|
return false;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export type { ConnectionRole, RelaySessionAttachment } from "./types.js";
|
|
2
2
|
export { generateKeyPair, exportPublicKey, importPublicKey, deriveSharedKey, encrypt, decrypt, } from "./crypto.js";
|
|
3
|
-
export { createClientChannel, createDaemonChannel, EncryptedChannel } from "./encrypted-channel.js";
|
|
3
|
+
export { base64EncryptedWireByteLength, createClientChannel, createDaemonChannel, EncryptedChannel, maxBase64EncryptedPlaintextByteLength, } from "./encrypted-channel.js";
|
|
4
4
|
export type { Transport, EncryptedChannelEvents } from "./encrypted-channel.js";
|
|
5
5
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
export { generateKeyPair, exportPublicKey, importPublicKey, deriveSharedKey, encrypt, decrypt, } from "./crypto.js";
|
|
2
|
-
export { createClientChannel, createDaemonChannel, EncryptedChannel } from "./encrypted-channel.js";
|
|
2
|
+
export { base64EncryptedWireByteLength, createClientChannel, createDaemonChannel, EncryptedChannel, maxBase64EncryptedPlaintextByteLength, } from "./encrypted-channel.js";
|
|
3
3
|
//# sourceMappingURL=index.js.map
|