@acpantenna/relay-protocol 0.0.0-stage → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +54 -2
- package/dist/bip39-english.d.ts +28 -0
- package/dist/bip39-english.d.ts.map +1 -0
- package/dist/bip39-english.js +28 -0
- package/dist/bip39-english.js.map +1 -0
- package/dist/bytes.d.ts +21 -0
- package/dist/bytes.d.ts.map +1 -0
- package/dist/bytes.js +114 -0
- package/dist/bytes.js.map +1 -0
- package/dist/frames.d.ts +90 -0
- package/dist/frames.d.ts.map +1 -0
- package/dist/frames.js +234 -0
- package/dist/frames.js.map +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -0
- package/dist/link.d.ts +44 -0
- package/dist/link.d.ts.map +1 -0
- package/dist/link.js +120 -0
- package/dist/link.js.map +1 -0
- package/dist/noise.d.ts +181 -0
- package/dist/noise.d.ts.map +1 -0
- package/dist/noise.js +490 -0
- package/dist/noise.js.map +1 -0
- package/dist/relay.d.ts +37 -0
- package/dist/relay.d.ts.map +1 -0
- package/dist/relay.js +70 -0
- package/dist/relay.js.map +1 -0
- package/dist/sas.d.ts +11 -0
- package/dist/sas.d.ts.map +1 -0
- package/dist/sas.js +26 -0
- package/dist/sas.js.map +1 -0
- package/dist/session.d.ts +298 -0
- package/dist/session.d.ts.map +1 -0
- package/dist/session.js +608 -0
- package/dist/session.js.map +1 -0
- package/package.json +56 -5
- package/src/bip39-english.ts +30 -0
- package/src/bytes.ts +125 -0
- package/src/frames.ts +278 -0
- package/src/index.ts +14 -0
- package/src/link.ts +160 -0
- package/src/noise.ts +655 -0
- package/src/relay.ts +106 -0
- package/src/sas.ts +28 -0
- package/src/session.ts +848 -0
- package/test-vectors/frames.json +353 -0
- package/test-vectors/noise-kk.json +43 -0
- package/test-vectors/noise-nkpsk0.json +47 -0
- package/test-vectors/sas.json +21 -0
- package/test-vectors/session.json +58 -0
package/dist/session.js
ADDED
|
@@ -0,0 +1,608 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One end of an encrypted channel over a relay: the Noise handshake, then
|
|
3
|
+
* framed ACP messages. Both sides use it — the browser as the
|
|
4
|
+
* {@link initiator}, the agent side as the {@link responder} — to pair with
|
|
5
|
+
* a link, and a remembered browser and the agent side to reconnect
|
|
6
|
+
* ({@link reconnectInitiator}, {@link reconnectResponder}).
|
|
7
|
+
*
|
|
8
|
+
* The session does no I/O of its own. Give it `send`, which puts one binary
|
|
9
|
+
* message on the relay socket, and hand every binary message from the socket
|
|
10
|
+
* to `receive`. When the socket ends, call `transportClosed`.
|
|
11
|
+
*/
|
|
12
|
+
import { base64urlDecode, base64urlEncode, equalBytes, utf8Decode, utf8Encode, } from "./bytes.js";
|
|
13
|
+
import { chunkMessage, controlFrame, DEFAULT_MAX_MESSAGE_BYTES, decodeFrame, encodeFrame, FrameError, FrameType, Reassembler, } from "./frames.js";
|
|
14
|
+
import { prologue } from "./link.js";
|
|
15
|
+
import { DHLEN, HandshakeState, KKHandshakeState, MAX_MESSAGE_LENGTH, NoiseError, staticKey, } from "./noise.js";
|
|
16
|
+
import { sasWords } from "./sas.js";
|
|
17
|
+
/** Each side rekeys its sending CipherState after this many messages, and its receiving one likewise. */
|
|
18
|
+
export const REKEY_INTERVAL = 10_000;
|
|
19
|
+
/** How much of what it sent each side keeps by default, to resend after a reconnect. */
|
|
20
|
+
export const DEFAULT_RESUME_BYTES = 8 * 1024 * 1024;
|
|
21
|
+
/**
|
|
22
|
+
* What one side keeps across the transports of a connection, so a reconnect
|
|
23
|
+
* loses nothing: ACP stream ids go on counting from one transport to the
|
|
24
|
+
* next, and the messages most recently sent are kept to send again.
|
|
25
|
+
*/
|
|
26
|
+
export class ResumeBuffer {
|
|
27
|
+
maxBytes;
|
|
28
|
+
/** The last stream received whole from the other side. */
|
|
29
|
+
received = 0;
|
|
30
|
+
next = 1;
|
|
31
|
+
kept = [];
|
|
32
|
+
keptBytes = 0;
|
|
33
|
+
constructor(maxBytes = DEFAULT_RESUME_BYTES) {
|
|
34
|
+
this.maxBytes = maxBytes;
|
|
35
|
+
}
|
|
36
|
+
/** The stream id of the next message sent. */
|
|
37
|
+
get nextStream() {
|
|
38
|
+
return this.next;
|
|
39
|
+
}
|
|
40
|
+
/** Numbers a message to send and keeps it, dropping the oldest past the limit. */
|
|
41
|
+
add(message) {
|
|
42
|
+
const stream = this.next++;
|
|
43
|
+
this.kept.push({ stream, message });
|
|
44
|
+
this.keptBytes += message.length;
|
|
45
|
+
while (this.keptBytes > this.maxBytes && this.kept.length > 0) {
|
|
46
|
+
this.keptBytes -= this.kept.shift().message.length;
|
|
47
|
+
}
|
|
48
|
+
return stream;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* The messages sent after stream `stream`, which the other side says it
|
|
52
|
+
* received last; undefined if some are no longer kept (or never were).
|
|
53
|
+
*/
|
|
54
|
+
after(stream) {
|
|
55
|
+
if (!Number.isInteger(stream) || stream < 0 || stream >= this.next) {
|
|
56
|
+
return undefined;
|
|
57
|
+
}
|
|
58
|
+
if (stream === this.next - 1) {
|
|
59
|
+
return [];
|
|
60
|
+
}
|
|
61
|
+
const first = this.kept[0];
|
|
62
|
+
if (!first || first.stream > stream + 1) {
|
|
63
|
+
return undefined;
|
|
64
|
+
}
|
|
65
|
+
return this.kept.filter((entry) => entry.stream > stream);
|
|
66
|
+
}
|
|
67
|
+
/** Starts again from stream 1 both ways: nothing before is resent. */
|
|
68
|
+
reset() {
|
|
69
|
+
this.received = 0;
|
|
70
|
+
this.next = 1;
|
|
71
|
+
this.kept = [];
|
|
72
|
+
this.keptBytes = 0;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
export class SessionError extends Error {
|
|
76
|
+
constructor(message) {
|
|
77
|
+
super(message);
|
|
78
|
+
this.name = "SessionError";
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
const EMPTY = new Uint8Array(0);
|
|
82
|
+
/** Calls `next` with `value` now if it is ready, else once it resolves. */
|
|
83
|
+
function then(value, next) {
|
|
84
|
+
return value instanceof Promise ? value.then(next) : next(value);
|
|
85
|
+
}
|
|
86
|
+
/** One end of the channel. Use {@link initiator} or {@link responder} to make one. */
|
|
87
|
+
export class RelaySession {
|
|
88
|
+
role;
|
|
89
|
+
/** A first pairing with a link, or a remembered browser coming back. */
|
|
90
|
+
kind;
|
|
91
|
+
/** What this side keeps across the connection's transports. */
|
|
92
|
+
buffer;
|
|
93
|
+
onHandshake;
|
|
94
|
+
onKeyConfirmed;
|
|
95
|
+
onConfirmed;
|
|
96
|
+
onMessage;
|
|
97
|
+
onPong;
|
|
98
|
+
onRemember;
|
|
99
|
+
onClose;
|
|
100
|
+
stateValue;
|
|
101
|
+
handshake;
|
|
102
|
+
sendRaw;
|
|
103
|
+
acceptEphemeral;
|
|
104
|
+
reassembler;
|
|
105
|
+
maxMessageBytes;
|
|
106
|
+
askResume;
|
|
107
|
+
sendCipher;
|
|
108
|
+
receiveCipher;
|
|
109
|
+
keyConfirmed = false;
|
|
110
|
+
resumedValue = false;
|
|
111
|
+
info;
|
|
112
|
+
/** REMEMBER has been sent or received. */
|
|
113
|
+
remembered = false;
|
|
114
|
+
/** An ACP message has gone, or come, since CONFIRMED: too late for REMEMBER. */
|
|
115
|
+
acpSeen = false;
|
|
116
|
+
/** A handshake step is waiting on an asynchronous DH. */
|
|
117
|
+
busy = false;
|
|
118
|
+
/** What arrived meanwhile, in order. */
|
|
119
|
+
backlog = [];
|
|
120
|
+
constructor(role, kind, handshake, options) {
|
|
121
|
+
this.role = role;
|
|
122
|
+
this.kind = kind;
|
|
123
|
+
this.buffer = options.buffer ?? new ResumeBuffer();
|
|
124
|
+
this.askResume = options.resume ?? false;
|
|
125
|
+
if (kind === "pairing" || (role === "initiator" && !this.askResume)) {
|
|
126
|
+
this.buffer.reset();
|
|
127
|
+
}
|
|
128
|
+
this.handshake = handshake;
|
|
129
|
+
this.sendRaw = options.send;
|
|
130
|
+
this.acceptEphemeral = options.acceptEphemeral;
|
|
131
|
+
this.maxMessageBytes = options.maxMessageBytes ?? DEFAULT_MAX_MESSAGE_BYTES;
|
|
132
|
+
this.reassembler = new Reassembler(this.maxMessageBytes, this.buffer.received);
|
|
133
|
+
this.stateValue = role === "initiator" ? "idle" : "handshake";
|
|
134
|
+
this.onHandshake = options.onHandshake;
|
|
135
|
+
this.onKeyConfirmed = options.onKeyConfirmed;
|
|
136
|
+
this.onConfirmed = options.onConfirmed;
|
|
137
|
+
this.onMessage = options.onMessage;
|
|
138
|
+
this.onPong = options.onPong;
|
|
139
|
+
this.onRemember = options.onRemember;
|
|
140
|
+
this.onClose = options.onClose;
|
|
141
|
+
}
|
|
142
|
+
get state() {
|
|
143
|
+
return this.stateValue;
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* A reconnect that carried on from the last transport: nothing was lost.
|
|
147
|
+
* False after a pairing, or a reconnect that started again from stream 1,
|
|
148
|
+
* when the browser must load the session afresh. Set before
|
|
149
|
+
* `onConfirmed` and `onKeyConfirmed` run.
|
|
150
|
+
*/
|
|
151
|
+
get resumed() {
|
|
152
|
+
return this.resumedValue;
|
|
153
|
+
}
|
|
154
|
+
/** The SAS words and handshake hash, once the handshake is done (and, on the agent side, the keys confirmed). */
|
|
155
|
+
get handshakeInfo() {
|
|
156
|
+
return this.stateValue === "handshake" ? undefined : this.info;
|
|
157
|
+
}
|
|
158
|
+
/** Initiator: sends message 1. */
|
|
159
|
+
start() {
|
|
160
|
+
if (this.role !== "initiator" || this.stateValue !== "idle") {
|
|
161
|
+
throw new SessionError("Only an idle initiator can start");
|
|
162
|
+
}
|
|
163
|
+
this.stateValue = "handshake";
|
|
164
|
+
this.run(() => then(this.handshake.writeMessage(EMPTY), (message) => {
|
|
165
|
+
if (this.stateValue === "handshake") {
|
|
166
|
+
this.sendRaw(message);
|
|
167
|
+
}
|
|
168
|
+
}));
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Responder: the user confirmed the SAS words. Sends CONFIRMED, and lets
|
|
172
|
+
* ACP messages through from now on.
|
|
173
|
+
*/
|
|
174
|
+
confirm() {
|
|
175
|
+
if (this.role !== "responder" ||
|
|
176
|
+
this.kind !== "pairing" ||
|
|
177
|
+
this.stateValue !== "pairing") {
|
|
178
|
+
throw new SessionError("Only a responder that is pairing can confirm");
|
|
179
|
+
}
|
|
180
|
+
this.sendFrame(controlFrame(FrameType.CONFIRMED));
|
|
181
|
+
this.stateValue = "open";
|
|
182
|
+
}
|
|
183
|
+
/** Sends one ACP message (a JSON-RPC 2.0 message, as text). */
|
|
184
|
+
send(message) {
|
|
185
|
+
if (this.stateValue !== "open") {
|
|
186
|
+
throw new SessionError(`Cannot send while ${this.stateValue}`);
|
|
187
|
+
}
|
|
188
|
+
this.acpSeen = true;
|
|
189
|
+
this.sendStream(this.buffer.add(message), message);
|
|
190
|
+
}
|
|
191
|
+
sendStream(stream, message) {
|
|
192
|
+
for (const frame of chunkMessage(stream, utf8Encode(message))) {
|
|
193
|
+
this.sendFrame(frame);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Initiator, after a pairing: sends REMEMBER with the public half of the
|
|
198
|
+
* static key it will reconnect with. Once, as soon as CONFIRMED has come,
|
|
199
|
+
* before any ACP message.
|
|
200
|
+
*/
|
|
201
|
+
remember(publicKey) {
|
|
202
|
+
if (this.role !== "initiator" ||
|
|
203
|
+
this.kind !== "pairing" ||
|
|
204
|
+
this.stateValue !== "open" ||
|
|
205
|
+
this.remembered ||
|
|
206
|
+
this.acpSeen) {
|
|
207
|
+
throw new SessionError("REMEMBER goes once, right after CONFIRMED of a pairing");
|
|
208
|
+
}
|
|
209
|
+
if (publicKey.length !== DHLEN) {
|
|
210
|
+
throw new SessionError("A static key is 32 bytes");
|
|
211
|
+
}
|
|
212
|
+
this.remembered = true;
|
|
213
|
+
this.sendFrame(controlFrame(FrameType.REMEMBER, utf8Encode(base64urlEncode(publicKey))));
|
|
214
|
+
}
|
|
215
|
+
ping(payload = EMPTY) {
|
|
216
|
+
if (this.stateValue !== "pairing" && this.stateValue !== "open") {
|
|
217
|
+
throw new SessionError(`Cannot ping while ${this.stateValue}`);
|
|
218
|
+
}
|
|
219
|
+
this.sendFrame(controlFrame(FrameType.PING, payload));
|
|
220
|
+
}
|
|
221
|
+
/** Ends the session with an authenticated CLOSE. */
|
|
222
|
+
close(reason = "") {
|
|
223
|
+
if (this.stateValue === "closed") {
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
if (this.sendCipher) {
|
|
227
|
+
this.sendFrame(controlFrame(FrameType.CLOSE, utf8Encode(reason)));
|
|
228
|
+
}
|
|
229
|
+
this.finish({ kind: "ended", reason, byPeer: false });
|
|
230
|
+
}
|
|
231
|
+
/** The socket ended. Without an authenticated CLOSE first, the connection was lost. */
|
|
232
|
+
transportClosed(reason = "connection lost") {
|
|
233
|
+
if (this.stateValue !== "closed") {
|
|
234
|
+
this.finish({ kind: "lost", reason });
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
/** Takes one binary message from the relay socket. */
|
|
238
|
+
receive(data) {
|
|
239
|
+
if (this.stateValue === "closed") {
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
if (this.busy) {
|
|
243
|
+
this.backlog.push(data);
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
this.run(() => this.receiveUnsafe(data));
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Runs one step. A step that waits on a DH holds back what arrives
|
|
250
|
+
* meanwhile, which then runs in order.
|
|
251
|
+
*/
|
|
252
|
+
run(step) {
|
|
253
|
+
let pending;
|
|
254
|
+
try {
|
|
255
|
+
pending = step();
|
|
256
|
+
}
|
|
257
|
+
catch (error) {
|
|
258
|
+
this.failOn(error, true);
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
if (!pending) {
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
this.busy = true;
|
|
265
|
+
pending.then(() => this.resume(), (error) => {
|
|
266
|
+
this.failOn(error, false);
|
|
267
|
+
this.resume();
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
resume() {
|
|
271
|
+
this.busy = false;
|
|
272
|
+
while (!this.busy && this.backlog.length > 0) {
|
|
273
|
+
const next = this.backlog.shift();
|
|
274
|
+
this.receive(next);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
/** Protocol errors end the session; others are bugs, thrown when they can be. */
|
|
278
|
+
failOn(error, rethrow) {
|
|
279
|
+
if (error instanceof NoiseError ||
|
|
280
|
+
error instanceof FrameError ||
|
|
281
|
+
error instanceof SessionError ||
|
|
282
|
+
!rethrow) {
|
|
283
|
+
if (this.stateValue !== "closed") {
|
|
284
|
+
this.fail(error instanceof Error ? error : new SessionError(String(error)));
|
|
285
|
+
}
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
throw error;
|
|
289
|
+
}
|
|
290
|
+
receiveUnsafe(data) {
|
|
291
|
+
if (data.length > MAX_MESSAGE_LENGTH) {
|
|
292
|
+
throw new SessionError("Message longer than a Noise message");
|
|
293
|
+
}
|
|
294
|
+
switch (this.stateValue) {
|
|
295
|
+
case "idle":
|
|
296
|
+
throw new SessionError("Message before the handshake started");
|
|
297
|
+
case "handshake":
|
|
298
|
+
if (this.receiveCipher) {
|
|
299
|
+
// Responder: message 2 is out; this is the browser's first frame.
|
|
300
|
+
this.receiveTransport(data);
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
return this.receiveHandshake(data);
|
|
304
|
+
default:
|
|
305
|
+
this.receiveTransport(data);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
receiveHandshake(data) {
|
|
309
|
+
return then(this.handshake.readMessage(data), (payload) => {
|
|
310
|
+
if (payload.length > 0) {
|
|
311
|
+
throw new SessionError("Handshake payloads are empty");
|
|
312
|
+
}
|
|
313
|
+
if (this.role !== "responder") {
|
|
314
|
+
return this.handshakeDone(undefined);
|
|
315
|
+
}
|
|
316
|
+
const ephemeral = data.slice(0, 32);
|
|
317
|
+
if (this.acceptEphemeral && !this.acceptEphemeral(ephemeral)) {
|
|
318
|
+
throw new SessionError("Replayed handshake message");
|
|
319
|
+
}
|
|
320
|
+
return then(this.handshake.writeMessage(EMPTY), (reply) => this.handshakeDone(reply));
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
/** Message 2 is written (responder, `reply`) or read (initiator). */
|
|
324
|
+
handshakeDone(reply) {
|
|
325
|
+
if (this.stateValue !== "handshake") {
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
328
|
+
const result = this.handshake.result;
|
|
329
|
+
if (!result) {
|
|
330
|
+
throw new SessionError("Handshake did not finish");
|
|
331
|
+
}
|
|
332
|
+
// Keys first: the peer may answer before `send` returns.
|
|
333
|
+
this.sendCipher = result.send;
|
|
334
|
+
this.receiveCipher = result.receive;
|
|
335
|
+
this.info = {
|
|
336
|
+
sas: sasWords(result.handshakeHash),
|
|
337
|
+
handshakeHash: result.handshakeHash,
|
|
338
|
+
};
|
|
339
|
+
if (reply) {
|
|
340
|
+
// The words wait for key confirmation: a replayed message 1 must not
|
|
341
|
+
// put them in front of the user.
|
|
342
|
+
this.sendRaw(reply);
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
345
|
+
this.stateValue = "pairing";
|
|
346
|
+
// The first transport frame is a PING, so the agent side can confirm
|
|
347
|
+
// the keys before it shows the words. Reconnecting, it carries the
|
|
348
|
+
// last stream received, to carry on from.
|
|
349
|
+
this.sendFrame(controlFrame(FrameType.PING, this.askResume ? u32(this.buffer.received) : EMPTY));
|
|
350
|
+
if (this.stateValue === "pairing" && this.kind === "pairing") {
|
|
351
|
+
this.onHandshake?.(this.info);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
receiveTransport(data) {
|
|
355
|
+
const cipher = this.receiveCipher;
|
|
356
|
+
const plaintext = cipher.decryptWithAd(EMPTY, data);
|
|
357
|
+
if (cipher.messages % REKEY_INTERVAL === 0) {
|
|
358
|
+
cipher.rekey();
|
|
359
|
+
}
|
|
360
|
+
const frame = decodeFrame(plaintext);
|
|
361
|
+
if (this.role === "responder" && !this.keyConfirmed) {
|
|
362
|
+
if (frame.type !== FrameType.PING) {
|
|
363
|
+
throw new SessionError("The first frame from the browser is a PING");
|
|
364
|
+
}
|
|
365
|
+
this.keyConfirmed = true;
|
|
366
|
+
if (this.kind === "reconnect") {
|
|
367
|
+
// Both static keys are known: no words to confirm.
|
|
368
|
+
this.openReconnect(frame.payload);
|
|
369
|
+
if (this.stateValue === "open") {
|
|
370
|
+
this.onKeyConfirmed?.();
|
|
371
|
+
}
|
|
372
|
+
if (this.stateValue === "open") {
|
|
373
|
+
this.handleFrame(frame);
|
|
374
|
+
}
|
|
375
|
+
return;
|
|
376
|
+
}
|
|
377
|
+
this.stateValue = "pairing";
|
|
378
|
+
this.onKeyConfirmed?.();
|
|
379
|
+
if (this.stateValue !== "pairing") {
|
|
380
|
+
return;
|
|
381
|
+
}
|
|
382
|
+
this.onHandshake?.(this.info);
|
|
383
|
+
if (this.stateValue !== "pairing") {
|
|
384
|
+
return;
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
this.handleFrame(frame);
|
|
388
|
+
}
|
|
389
|
+
handleFrame(frame) {
|
|
390
|
+
switch (frame.type) {
|
|
391
|
+
case FrameType.ACP: {
|
|
392
|
+
if (this.stateValue !== "open") {
|
|
393
|
+
throw new SessionError("ACP message before the SAS was confirmed");
|
|
394
|
+
}
|
|
395
|
+
this.acpSeen = true;
|
|
396
|
+
const message = this.reassembler.push(frame);
|
|
397
|
+
if (message) {
|
|
398
|
+
this.buffer.received = frame.stream;
|
|
399
|
+
this.onMessage?.(decodeText(message));
|
|
400
|
+
}
|
|
401
|
+
return;
|
|
402
|
+
}
|
|
403
|
+
case FrameType.CLOSE:
|
|
404
|
+
this.reassembler.end();
|
|
405
|
+
this.finish({
|
|
406
|
+
kind: "ended",
|
|
407
|
+
reason: decodeText(frame.payload),
|
|
408
|
+
byPeer: true,
|
|
409
|
+
});
|
|
410
|
+
return;
|
|
411
|
+
case FrameType.PING:
|
|
412
|
+
this.sendFrame(controlFrame(FrameType.PONG, frame.payload));
|
|
413
|
+
return;
|
|
414
|
+
case FrameType.PONG:
|
|
415
|
+
this.onPong?.(frame.payload);
|
|
416
|
+
return;
|
|
417
|
+
case FrameType.CONFIRMED:
|
|
418
|
+
if (this.role !== "initiator" || this.stateValue !== "pairing") {
|
|
419
|
+
throw new SessionError("Unexpected CONFIRMED");
|
|
420
|
+
}
|
|
421
|
+
if (this.kind === "reconnect") {
|
|
422
|
+
this.confirmReconnect(frame.payload);
|
|
423
|
+
}
|
|
424
|
+
else if (frame.payload.length > 0) {
|
|
425
|
+
throw new SessionError("CONFIRMED of a pairing is empty");
|
|
426
|
+
}
|
|
427
|
+
else {
|
|
428
|
+
this.stateValue = "open";
|
|
429
|
+
}
|
|
430
|
+
this.onConfirmed?.();
|
|
431
|
+
return;
|
|
432
|
+
case FrameType.REMEMBER: {
|
|
433
|
+
if (this.role !== "responder" ||
|
|
434
|
+
this.kind !== "pairing" ||
|
|
435
|
+
this.stateValue !== "open" ||
|
|
436
|
+
this.remembered ||
|
|
437
|
+
this.acpSeen) {
|
|
438
|
+
throw new SessionError("Unexpected REMEMBER");
|
|
439
|
+
}
|
|
440
|
+
let key;
|
|
441
|
+
try {
|
|
442
|
+
key = base64urlDecode(decodeText(frame.payload));
|
|
443
|
+
}
|
|
444
|
+
catch {
|
|
445
|
+
throw new FrameError("REMEMBER carries a 32-byte key in base64url");
|
|
446
|
+
}
|
|
447
|
+
if (key.length !== DHLEN) {
|
|
448
|
+
throw new FrameError("REMEMBER carries a 32-byte key in base64url");
|
|
449
|
+
}
|
|
450
|
+
this.remembered = true;
|
|
451
|
+
this.onRemember?.(key);
|
|
452
|
+
return;
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
/**
|
|
457
|
+
* Responder, reconnecting: the browser's first PING has decrypted. Its
|
|
458
|
+
* payload, if any, is the last stream it received. Answers CONFIRMED with
|
|
459
|
+
* the last stream received here, then sends again what the browser
|
|
460
|
+
* missed; or, if that can't be done, an empty CONFIRMED, and both sides
|
|
461
|
+
* start again from stream 1.
|
|
462
|
+
*/
|
|
463
|
+
openReconnect(payload) {
|
|
464
|
+
if (payload.length !== 0 && payload.length !== 4) {
|
|
465
|
+
throw new SessionError("A reconnect's PING is empty, or a u32");
|
|
466
|
+
}
|
|
467
|
+
const missed = payload.length === 4 ? this.buffer.after(readU32(payload)) : undefined;
|
|
468
|
+
if (!missed) {
|
|
469
|
+
this.restart();
|
|
470
|
+
this.sendFrame(controlFrame(FrameType.CONFIRMED));
|
|
471
|
+
this.stateValue = "open";
|
|
472
|
+
return;
|
|
473
|
+
}
|
|
474
|
+
this.sendFrame(controlFrame(FrameType.CONFIRMED, u32(this.buffer.received)));
|
|
475
|
+
this.resumedValue = true;
|
|
476
|
+
this.stateValue = "open";
|
|
477
|
+
for (const { stream, message } of missed) {
|
|
478
|
+
this.sendStream(stream, message);
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
/** Initiator, reconnecting: the agent side's CONFIRMED. */
|
|
482
|
+
confirmReconnect(payload) {
|
|
483
|
+
if (payload.length === 0) {
|
|
484
|
+
this.restart();
|
|
485
|
+
this.stateValue = "open";
|
|
486
|
+
return;
|
|
487
|
+
}
|
|
488
|
+
if (!this.askResume) {
|
|
489
|
+
throw new SessionError("CONFIRMED resumes what was not asked for");
|
|
490
|
+
}
|
|
491
|
+
const missed = this.buffer.after(readU32(payload));
|
|
492
|
+
if (!missed) {
|
|
493
|
+
throw new SessionError("Cannot resume: messages the agent side missed are gone");
|
|
494
|
+
}
|
|
495
|
+
this.resumedValue = true;
|
|
496
|
+
this.stateValue = "open";
|
|
497
|
+
for (const { stream, message } of missed) {
|
|
498
|
+
this.sendStream(stream, message);
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
restart() {
|
|
502
|
+
this.buffer.reset();
|
|
503
|
+
this.reassembler = new Reassembler(this.maxMessageBytes, 0);
|
|
504
|
+
}
|
|
505
|
+
sendFrame(frame) {
|
|
506
|
+
const cipher = this.sendCipher;
|
|
507
|
+
if (!cipher) {
|
|
508
|
+
throw new SessionError("No transport keys yet");
|
|
509
|
+
}
|
|
510
|
+
const ciphertext = cipher.encryptWithAd(EMPTY, encodeFrame(frame));
|
|
511
|
+
if (cipher.messages % REKEY_INTERVAL === 0) {
|
|
512
|
+
cipher.rekey();
|
|
513
|
+
}
|
|
514
|
+
this.sendRaw(ciphertext);
|
|
515
|
+
}
|
|
516
|
+
fail(error) {
|
|
517
|
+
this.finish({ kind: "error", reason: error.message, error });
|
|
518
|
+
}
|
|
519
|
+
finish(event) {
|
|
520
|
+
this.stateValue = "closed";
|
|
521
|
+
this.sendCipher = undefined;
|
|
522
|
+
this.receiveCipher = undefined;
|
|
523
|
+
this.onClose?.(event);
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
function u32(value) {
|
|
527
|
+
const out = new Uint8Array(4);
|
|
528
|
+
new DataView(out.buffer).setUint32(0, value);
|
|
529
|
+
return out;
|
|
530
|
+
}
|
|
531
|
+
function readU32(bytes) {
|
|
532
|
+
return new DataView(bytes.buffer, bytes.byteOffset, 4).getUint32(0);
|
|
533
|
+
}
|
|
534
|
+
function decodeText(bytes) {
|
|
535
|
+
try {
|
|
536
|
+
return utf8Decode(bytes);
|
|
537
|
+
}
|
|
538
|
+
catch {
|
|
539
|
+
throw new FrameError("Invalid UTF-8");
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
/**
|
|
543
|
+
* The browser's end: no static key, the agent side's key and the psk from
|
|
544
|
+
* the link. Call `start` once the relay says `ready`.
|
|
545
|
+
*/
|
|
546
|
+
export function initiator(options) {
|
|
547
|
+
const handshake = HandshakeState.initiator({
|
|
548
|
+
prologue: prologue(options.link.room, options.link.webOrigin),
|
|
549
|
+
psk: options.link.psk,
|
|
550
|
+
remoteStaticPublicKey: options.link.staticPublicKey,
|
|
551
|
+
ephemeralPrivateKey: options.ephemeralPrivateKey,
|
|
552
|
+
});
|
|
553
|
+
return new RelaySession("initiator", "pairing", handshake, options);
|
|
554
|
+
}
|
|
555
|
+
/**
|
|
556
|
+
* The agent side's end, with its static key and the link's psk. It answers
|
|
557
|
+
* message 1 when it arrives.
|
|
558
|
+
*/
|
|
559
|
+
export function responder(options) {
|
|
560
|
+
const handshake = HandshakeState.responder({
|
|
561
|
+
prologue: prologue(options.room, options.webOrigin),
|
|
562
|
+
psk: options.psk,
|
|
563
|
+
staticKeyPair: options.staticKeyPair,
|
|
564
|
+
ephemeralPrivateKey: options.ephemeralPrivateKey,
|
|
565
|
+
});
|
|
566
|
+
return new RelaySession("responder", "pairing", handshake, options);
|
|
567
|
+
}
|
|
568
|
+
/**
|
|
569
|
+
* A remembered browser's end of a reconnect (Noise KK): its own static key,
|
|
570
|
+
* and the agent side's from the link it paired with. Call `start` once the
|
|
571
|
+
* relay says `ready`.
|
|
572
|
+
*/
|
|
573
|
+
export function reconnectInitiator(options) {
|
|
574
|
+
const handshake = KKHandshakeState.initiator({
|
|
575
|
+
prologue: prologue(options.room, options.webOrigin),
|
|
576
|
+
staticKey: options.staticKey,
|
|
577
|
+
remoteStaticPublicKey: options.remoteStaticPublicKey,
|
|
578
|
+
ephemeralPrivateKey: options.ephemeralPrivateKey,
|
|
579
|
+
});
|
|
580
|
+
return new RelaySession("initiator", "reconnect", handshake, options);
|
|
581
|
+
}
|
|
582
|
+
/**
|
|
583
|
+
* The agent side's end of a reconnect, with the key the browser sent in
|
|
584
|
+
* REMEMBER. It answers message 1 when it arrives, and opens by itself once
|
|
585
|
+
* the keys are confirmed.
|
|
586
|
+
*/
|
|
587
|
+
export function reconnectResponder(options) {
|
|
588
|
+
const handshake = KKHandshakeState.responder({
|
|
589
|
+
prologue: prologue(options.room, options.webOrigin),
|
|
590
|
+
staticKey: staticKey(options.staticKeyPair),
|
|
591
|
+
remoteStaticPublicKey: options.remoteStaticPublicKey,
|
|
592
|
+
ephemeralPrivateKey: options.ephemeralPrivateKey,
|
|
593
|
+
});
|
|
594
|
+
return new RelaySession("responder", "reconnect", handshake, options);
|
|
595
|
+
}
|
|
596
|
+
/** Remembers the ephemeral keys seen while a link is valid, to refuse replays of message 1. */
|
|
597
|
+
export class EphemeralCache {
|
|
598
|
+
seen = [];
|
|
599
|
+
/** Returns false if `key` was seen before; otherwise remembers it. */
|
|
600
|
+
accept = (key) => {
|
|
601
|
+
if (this.seen.some((seen) => equalBytes(seen, key))) {
|
|
602
|
+
return false;
|
|
603
|
+
}
|
|
604
|
+
this.seen.push(key.slice());
|
|
605
|
+
return true;
|
|
606
|
+
};
|
|
607
|
+
}
|
|
608
|
+
//# sourceMappingURL=session.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session.js","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"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"}
|