@thenewlabs/entangle-serve 2.1.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.
@@ -0,0 +1,362 @@
1
+ import { FrameType, encodeFrame, } from '@thenewlabs/entangle-protocol';
2
+ import { streamAeadEncrypt, streamAeadDecrypt, frameAad, AeadDir, } from '@thenewlabs/entangle-crypto';
3
+ import { OutputHandler, parseOutputMode, } from '@thenewlabs/entangle-utils';
4
+ import { encode, decode } from 'cborg';
5
+ import { StreamManager } from './stream-manager.js';
6
+ const output = new OutputHandler({ mode: parseOutputMode(process.env.OUTPUT_MODE || 'text') });
7
+ // Tear down a single invoker session on a protocol/counter error WITHOUT
8
+ // closing session.ws — that socket is the shared agent-to-relay control
9
+ // connection carrying every invoker. Killing it here would take down all of
10
+ // them, so instead we close just this session's streams, mark it terminated so
11
+ // further frames are ignored, and let the relay's idle timeout drop the peer.
12
+ function terminateSession(session, reason) {
13
+ if (session.terminated)
14
+ return;
15
+ session.terminated = true;
16
+ output.warn(`Terminating invoker session ${session.socketId}: ${reason}`);
17
+ if (session.streamManager) {
18
+ try {
19
+ session.streamManager.closeAllStreams(reason);
20
+ }
21
+ catch { }
22
+ }
23
+ }
24
+ // Helper to send encrypted messages with per-stream counters
25
+ async function sendEncrypted(session, frameType, message, sid) {
26
+ if (!session.keys) {
27
+ throw new Error('Session not authenticated');
28
+ }
29
+ // If we have a stream ID, update the counter
30
+ if (sid && 'ctr' in message) {
31
+ message.ctr = session.streamCounters.increment(sid, 'outgoing');
32
+ }
33
+ const plaintext = encode(message);
34
+ const aad = frameAad(frameType, AeadDir.ServerToClient);
35
+ const ciphertext = await streamAeadEncrypt(session.keys.K_enc, plaintext, aad);
36
+ const frame = encodeFrame(frameType, ciphertext);
37
+ // Relay via server: wrap in JSON envelope so the server can
38
+ // forward to the correct invoker socket.
39
+ try {
40
+ const envelope = {
41
+ type: 'RELAY_RESPONSE',
42
+ socketId: session.socketId,
43
+ frame: Buffer.from(frame).toString('base64'),
44
+ };
45
+ session.ws.send(JSON.stringify(envelope));
46
+ }
47
+ catch {
48
+ // Best-effort fallback: avoid crashing handler on send errors
49
+ }
50
+ }
51
+ // Handle stream open request
52
+ async function handleStreamOpen(session, message) {
53
+ if (!session.streamManager) {
54
+ session.streamManager = new StreamManager({
55
+ policy: session.cap.policy,
56
+ output,
57
+ onStreamData: async (sid, data, channel) => {
58
+ await sendStreamData(session, sid, data, channel);
59
+ },
60
+ onStreamExit: async (sid, code, signal, usage) => {
61
+ await sendStreamExit(session, sid, code, signal, usage);
62
+ },
63
+ onStreamError: async (sid, error) => {
64
+ await sendStreamError(session, sid, error);
65
+ },
66
+ });
67
+ }
68
+ try {
69
+ const { mode, pty, exec } = message.msg;
70
+ let actualSid;
71
+ if (mode === 'pty' && pty) {
72
+ const ptyOptions = {
73
+ cols: pty.cols,
74
+ rows: pty.rows,
75
+ };
76
+ if (pty.env) {
77
+ ptyOptions.env = pty.env;
78
+ }
79
+ if (exec?.cwd) {
80
+ ptyOptions.cwd = exec.cwd;
81
+ }
82
+ actualSid = await session.streamManager.openPtyStream(ptyOptions);
83
+ }
84
+ else if (mode === 'cmd' && exec) {
85
+ const cmdOptions = {
86
+ argv: exec.argv,
87
+ };
88
+ if (exec.cwd) {
89
+ cmdOptions.cwd = exec.cwd;
90
+ }
91
+ if (exec.env) {
92
+ cmdOptions.env = exec.env;
93
+ }
94
+ if (exec.stdin !== undefined) {
95
+ cmdOptions.stdin = exec.stdin;
96
+ }
97
+ actualSid = await session.streamManager.openCmdStream(cmdOptions);
98
+ }
99
+ else {
100
+ throw new Error(`Invalid stream configuration for mode ${mode}`);
101
+ }
102
+ // Send opened confirmation
103
+ const openedMsg = {
104
+ ctr: 0, // Will be set by sendEncrypted
105
+ msg: {
106
+ v: 1,
107
+ kind: 'opened',
108
+ sid: actualSid,
109
+ startedAt: Date.now(),
110
+ mode,
111
+ },
112
+ };
113
+ await sendEncrypted(session, FrameType.STREAM_OPEN, openedMsg, actualSid);
114
+ }
115
+ catch (error) {
116
+ await sendStreamError(session, message.msg.sid, error.message);
117
+ }
118
+ }
119
+ // Handle stream data (stdin)
120
+ async function handleStreamData(session, message) {
121
+ if (!session.streamManager) {
122
+ await sendStreamError(session, message.msg.sid, 'No active streams');
123
+ return;
124
+ }
125
+ try {
126
+ session.streamManager.writeToStream(message.msg.sid, message.msg.chunk);
127
+ }
128
+ catch (error) {
129
+ await sendStreamError(session, message.msg.sid, error.message);
130
+ }
131
+ }
132
+ // Handle stream resize (PTY only)
133
+ async function handleStreamResize(session, message) {
134
+ if (!session.streamManager) {
135
+ await sendStreamError(session, message.msg.sid, 'No active streams');
136
+ return;
137
+ }
138
+ try {
139
+ session.streamManager.resizePtyStream(message.msg.sid, message.msg.cols, message.msg.rows);
140
+ }
141
+ catch (error) {
142
+ await sendStreamError(session, message.msg.sid, error.message);
143
+ }
144
+ }
145
+ // Handle stream signal
146
+ async function handleStreamSignal(session, message) {
147
+ if (!session.streamManager) {
148
+ await sendStreamError(session, message.msg.sid, 'No active streams');
149
+ return;
150
+ }
151
+ try {
152
+ session.streamManager.signalStream(message.msg.sid, message.msg.signal);
153
+ }
154
+ catch (error) {
155
+ await sendStreamError(session, message.msg.sid, error.message);
156
+ }
157
+ }
158
+ // Handle stream close
159
+ async function handleStreamClose(session, message) {
160
+ if (!session.streamManager) {
161
+ return;
162
+ }
163
+ session.streamManager.closeStream(message.msg.sid, 'Client requested close');
164
+ // Send closed confirmation
165
+ const closedMsg = {
166
+ ctr: 0, // Will be set by sendEncrypted
167
+ msg: {
168
+ v: 1,
169
+ kind: 'closed',
170
+ sid: message.msg.sid,
171
+ },
172
+ };
173
+ await sendEncrypted(session, FrameType.STREAM_CLOSE, closedMsg, message.msg.sid);
174
+ // Clean up stream counters
175
+ session.streamCounters.removeStream(message.msg.sid);
176
+ }
177
+ // Send stream data to client
178
+ async function sendStreamData(session, sid, data, channel = 'stdout') {
179
+ const msg = {
180
+ ctr: 0, // Will be set by sendEncrypted
181
+ msg: {
182
+ v: 1,
183
+ kind: 'data',
184
+ sid,
185
+ chunk: data,
186
+ channel,
187
+ },
188
+ };
189
+ await sendEncrypted(session, FrameType.STREAM_DATA, msg, sid);
190
+ }
191
+ // Send stream exit to client
192
+ async function sendStreamExit(session, sid, code, signal, usage) {
193
+ const msg = {
194
+ ctr: 0, // Will be set by sendEncrypted
195
+ msg: {
196
+ v: 1,
197
+ kind: 'exit',
198
+ sid,
199
+ code,
200
+ signal,
201
+ usage,
202
+ },
203
+ };
204
+ await sendEncrypted(session, FrameType.STREAM_EXIT, msg, sid);
205
+ // Clean up stream counters after exit
206
+ session.streamCounters.removeStream(sid);
207
+ }
208
+ // Send stream error to client
209
+ async function sendStreamError(session, sid, error) {
210
+ const msg = {
211
+ ctr: 0, // Will be set by sendEncrypted
212
+ msg: {
213
+ v: 1,
214
+ kind: 'error',
215
+ sid,
216
+ message: error,
217
+ },
218
+ };
219
+ await sendEncrypted(session, FrameType.STREAM_ERROR, msg, sid);
220
+ }
221
+ // Main frame handler for multi-stream protocol
222
+ export async function handleMultiStreamFrame(session, frame) {
223
+ if (session.terminated)
224
+ return;
225
+ if (!session.authenticated) {
226
+ terminateSession(session, 'Frame received before authentication');
227
+ return;
228
+ }
229
+ if (!session.keys) {
230
+ throw new Error('Keys not derived');
231
+ }
232
+ try {
233
+ // Log diagnostics for STREAM frames
234
+ if (frame.type === FrameType.STREAM_OPEN ||
235
+ frame.type === FrameType.STREAM_DATA ||
236
+ frame.type === FrameType.STREAM_CLOSE ||
237
+ frame.type === FrameType.STREAM_SIGNAL ||
238
+ frame.type === FrameType.STREAM_RESIZE ||
239
+ frame.type === FrameType.STREAM_ERROR ||
240
+ frame.type === FrameType.STREAM_EXIT) {
241
+ output.info(`MultiStream frame received: type=${frame.type} payloadLen=${frame.payload.length}`);
242
+ // Debug: log first few bytes of payload
243
+ const preview = Array.from(frame.payload.slice(0, 32)).map(b => b.toString(16).padStart(2, '0')).join(' ');
244
+ output.debug(`Payload preview (first 32 bytes): ${preview}`);
245
+ }
246
+ // Single canonical decryption: session key + direction-bound AAD. Frames
247
+ // that don't authenticate under exactly this AAD are rejected (no
248
+ // downgrade/alternate-AAD fallbacks).
249
+ const aad = frameAad(frame.type, AeadDir.ClientToServer);
250
+ const plaintext = await streamAeadDecrypt(session.keys.K_enc, frame.payload, aad);
251
+ const message = decode(plaintext);
252
+ // Bounded structural validation before we trust any field. A decoded value
253
+ // that is not a well-formed envelope terminates only this invoker session.
254
+ if (!message || typeof message !== 'object' || typeof message.ctr !== 'number') {
255
+ terminateSession(session, 'Malformed message envelope');
256
+ return;
257
+ }
258
+ // For stream messages, verify per-stream counter
259
+ if (message.msg && typeof message.msg.sid === 'string') {
260
+ const sid = message.msg.sid;
261
+ const expectedCounter = session.streamCounters.getNext(sid, 'incoming');
262
+ if (message.ctr !== expectedCounter) {
263
+ terminateSession(session, `Stream counter mismatch for ${sid}: expected=${expectedCounter}, received=${message.ctr}`);
264
+ return;
265
+ }
266
+ session.streamCounters.increment(sid, 'incoming');
267
+ }
268
+ else {
269
+ // Non-stream messages use global counter
270
+ try {
271
+ session.counters.incoming.validate(message.ctr);
272
+ }
273
+ catch (err) {
274
+ terminateSession(session, `Counter mismatch: ${err.message}`);
275
+ return;
276
+ }
277
+ }
278
+ // Enforce password verification for all stream operations when required
279
+ const pwRequiredButMissing = !!session.requiresPassword && !session.passwordVerified;
280
+ // Handle frame based on type
281
+ switch (frame.type) {
282
+ case FrameType.STREAM_OPEN:
283
+ if (pwRequiredButMissing) {
284
+ // Block stream open and notify client
285
+ try {
286
+ const sid = message.msg?.sid;
287
+ if (sid)
288
+ await sendStreamError(session, sid, 'Password verification required');
289
+ }
290
+ catch { }
291
+ return;
292
+ }
293
+ await handleStreamOpen(session, message);
294
+ break;
295
+ case FrameType.STREAM_DATA:
296
+ if (pwRequiredButMissing) {
297
+ try {
298
+ const sid = message.msg?.sid;
299
+ if (sid)
300
+ await sendStreamError(session, sid, 'Password verification required');
301
+ }
302
+ catch { }
303
+ return;
304
+ }
305
+ await handleStreamData(session, message);
306
+ break;
307
+ case FrameType.STREAM_RESIZE:
308
+ if (pwRequiredButMissing) {
309
+ try {
310
+ const sid = message.msg?.sid;
311
+ if (sid)
312
+ await sendStreamError(session, sid, 'Password verification required');
313
+ }
314
+ catch { }
315
+ return;
316
+ }
317
+ await handleStreamResize(session, message);
318
+ break;
319
+ case FrameType.STREAM_SIGNAL:
320
+ if (pwRequiredButMissing) {
321
+ try {
322
+ const sid = message.msg?.sid;
323
+ if (sid)
324
+ await sendStreamError(session, sid, 'Password verification required');
325
+ }
326
+ catch { }
327
+ return;
328
+ }
329
+ await handleStreamSignal(session, message);
330
+ break;
331
+ case FrameType.STREAM_CLOSE:
332
+ if (pwRequiredButMissing) {
333
+ try {
334
+ const sid = message.msg?.sid;
335
+ if (sid)
336
+ await sendStreamError(session, sid, 'Password verification required');
337
+ }
338
+ catch { }
339
+ return;
340
+ }
341
+ await handleStreamClose(session, message);
342
+ break;
343
+ case FrameType.KEEPALIVE:
344
+ // Echo keepalive back
345
+ await sendEncrypted(session, FrameType.KEEPALIVE, message);
346
+ break;
347
+ default:
348
+ output.warn(`Unknown frame type in multi-stream mode: ${frame.type}`);
349
+ }
350
+ }
351
+ catch (error) {
352
+ output.error(`Error handling multi-stream frame type ${frame.type}`, error.message);
353
+ terminateSession(session, 'Protocol error');
354
+ }
355
+ }
356
+ // Clean up session
357
+ export function cleanupMultiSession(session) {
358
+ if (session.streamManager) {
359
+ session.streamManager.closeAllStreams('Session cleanup');
360
+ }
361
+ }
362
+ //# sourceMappingURL=multi-session.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"multi-session.js","sourceRoot":"","sources":["../src/multi-session.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ import WebSocket from 'ws';
2
+ import { type DerivedKeys } from '@thenewlabs/entangle-crypto';
3
+ import { BidirectionalCounters } from '@thenewlabs/entangle-utils';
4
+ import type { CapabilityInfo } from './capability.js';
5
+ export interface Session {
6
+ socketId: string;
7
+ ws: WebSocket;
8
+ cap: CapabilityInfo;
9
+ K_raw?: Uint8Array;
10
+ bootstrapKeys?: DerivedKeys;
11
+ keys?: DerivedKeys;
12
+ counters: BidirectionalCounters;
13
+ authenticated: boolean;
14
+ passwordVerified: boolean;
15
+ requiresPassword: boolean;
16
+ passwordHash?: string | undefined;
17
+ nonceB?: string;
18
+ nonceC?: string;
19
+ auth1Seen?: boolean;
20
+ }
21
+ export declare function sendRelayResponse(session: Session, frame: Uint8Array): void;
22
+ export declare function handleInvokerConnection(agentWs: WebSocket, socketId: string, cap: CapabilityInfo, passwordHash?: string): {
23
+ handleFrame: (data: Buffer) => Promise<void>;
24
+ cleanup: () => void;
25
+ };
26
+ //# sourceMappingURL=session.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"AAAA,OAAO,SAAS,MAAM,IAAI,CAAC;AAO3B,OAAO,EAWL,KAAK,WAAW,EACjB,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAGL,qBAAqB,EAEtB,MAAM,4BAA4B,CAAC;AAGpC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAStD,MAAM,WAAW,OAAO;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,EAAE,EAAE,SAAS,CAAC;IACd,GAAG,EAAE,cAAc,CAAC;IACpB,KAAK,CAAC,EAAE,UAAU,CAAC;IACnB,aAAa,CAAC,EAAE,WAAW,CAAC;IAC5B,IAAI,CAAC,EAAE,WAAW,CAAC;IACnB,QAAQ,EAAE,qBAAqB,CAAC;IAChC,aAAa,EAAE,OAAO,CAAC;IACvB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,gBAAgB,EAAE,OAAO,CAAC;IAC1B,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAGD,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,UAAU,GAAG,IAAI,CAM3E;AAMD,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,SAAS,EAClB,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,cAAc,EACnB,YAAY,CAAC,EAAE,MAAM,GACpB;IAAE,WAAW,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAAC,OAAO,EAAE,MAAM,IAAI,CAAA;CAAE,CAmCvE"}
@@ -0,0 +1,215 @@
1
+ import { FrameType, FrameReader, encodeFrame, ErrorCode, } from '@thenewlabs/entangle-protocol';
2
+ import { deriveKeyMaterial, deriveBootstrapKeys, deriveSessionKeys, extractSaltFromCapId, aeadEncrypt, aeadDecrypt, verifyHmac, verifyPassword, hashPolicy, AeadDir, } from '@thenewlabs/entangle-crypto';
3
+ import { OutputHandler, parseOutputMode, BidirectionalCounters, StreamCounters, } from '@thenewlabs/entangle-utils';
4
+ import { encode, decode } from 'cborg';
5
+ import { randomBytes } from 'crypto';
6
+ import { handleMultiStreamFrame, cleanupMultiSession } from './multi-session.js';
7
+ const output = new OutputHandler({ mode: parseOutputMode(process.env.OUTPUT_MODE || 'text') });
8
+ // How long an authenticated session is considered valid (advertised to the
9
+ // client in AUTH2 so it can reject stale/replayed handshakes).
10
+ const SESSION_TTL_MS = 3600_000;
11
+ // Helper to send wrapped relay responses
12
+ export function sendRelayResponse(session, frame) {
13
+ session.ws.send(JSON.stringify({
14
+ type: 'RELAY_RESPONSE',
15
+ socketId: session.socketId,
16
+ frame: Buffer.from(frame).toString('base64'),
17
+ }));
18
+ }
19
+ // NOTE: synchronous on purpose. The caller must register the returned session
20
+ // before the next relay message is processed; an `await` here would defer that
21
+ // by a microtask and drop the AUTH1 that the client sends immediately after
22
+ // connecting (breaking concurrent sessions on the same capability).
23
+ export function handleInvokerConnection(agentWs, socketId, cap, passwordHash) {
24
+ const session = {
25
+ socketId,
26
+ ws: agentWs,
27
+ cap,
28
+ counters: new BidirectionalCounters(),
29
+ authenticated: false,
30
+ passwordVerified: !passwordHash, // If no password, consider it verified
31
+ requiresPassword: !!passwordHash,
32
+ passwordHash: passwordHash || undefined,
33
+ };
34
+ const reader = new FrameReader();
35
+ const store = {};
36
+ return {
37
+ handleFrame: async (data) => {
38
+ const frames = reader.push(data);
39
+ for (const frame of frames) {
40
+ try {
41
+ await handleFrame(session, store, frame);
42
+ }
43
+ catch (error) {
44
+ output.error(`Failed to handle frame for socket ${socketId}`, error instanceof Error ? error.message : String(error));
45
+ sendError(session, null, ErrorCode.INTERNAL_ERROR, 'Internal error');
46
+ }
47
+ }
48
+ },
49
+ cleanup: () => {
50
+ if (store.multiSession) {
51
+ try {
52
+ cleanupMultiSession(store.multiSession);
53
+ }
54
+ catch { }
55
+ }
56
+ },
57
+ };
58
+ }
59
+ const STREAM_FRAME_TYPES = new Set([
60
+ FrameType.STREAM_OPEN,
61
+ FrameType.STREAM_DATA,
62
+ FrameType.STREAM_RESIZE,
63
+ FrameType.STREAM_SIGNAL,
64
+ FrameType.STREAM_CLOSE,
65
+ FrameType.STREAM_ERROR,
66
+ FrameType.STREAM_EXIT,
67
+ ]);
68
+ async function handleFrame(session, store, frame) {
69
+ if (STREAM_FRAME_TYPES.has(frame.type)) {
70
+ if (!session.authenticated || !session.keys) {
71
+ output.error(`Received stream frame before authentication: type=${frame.type}`);
72
+ return;
73
+ }
74
+ if (!store.multiSession) {
75
+ store.multiSession = {
76
+ socketId: session.socketId,
77
+ ws: session.ws,
78
+ cap: session.cap,
79
+ keys: session.keys,
80
+ counters: session.counters,
81
+ streamCounters: new StreamCounters(),
82
+ authenticated: true,
83
+ requiresPassword: session.requiresPassword,
84
+ passwordVerified: session.passwordVerified,
85
+ };
86
+ }
87
+ // Keep dynamic gating fields fresh (password may be verified after streams open attempts)
88
+ store.multiSession.passwordVerified = session.passwordVerified;
89
+ await handleMultiStreamFrame(store.multiSession, frame);
90
+ return;
91
+ }
92
+ switch (frame.type) {
93
+ case FrameType.AUTH1:
94
+ await handleAuth1(session, frame.payload);
95
+ break;
96
+ case FrameType.AUTH3:
97
+ await handleAuth3(session, frame.payload);
98
+ break;
99
+ case FrameType.AUTH_PW:
100
+ await handleAuthPw(session, frame.payload);
101
+ break;
102
+ case FrameType.KEEPALIVE:
103
+ break;
104
+ default:
105
+ output.warn(`Unexpected frame type: ${frame.type}`);
106
+ }
107
+ }
108
+ async function handleAuth1(session, payload) {
109
+ // Bound the expensive Argon2 derivation to a single attempt per connection so
110
+ // a client that only knows capId cannot queue unbounded key-derivation work.
111
+ // (Connection setup is separately rate-limited per IP at the relay.)
112
+ if (session.auth1Seen) {
113
+ output.warn(`Ignoring repeat AUTH1 for socket ${session.socketId}`);
114
+ return;
115
+ }
116
+ session.auth1Seen = true;
117
+ // Cheap structural checks BEFORE any key derivation. AUTH1 payload is
118
+ // HMAC(32 bytes) || nonceB, and nonceB is a 16-byte hex string (32 chars).
119
+ const EXPECTED_NONCE_LEN = 32;
120
+ if (payload.length !== 32 + EXPECTED_NONCE_LEN) {
121
+ output.warn(`Rejecting malformed AUTH1 for socket ${session.socketId}: len=${payload.length}`);
122
+ return;
123
+ }
124
+ try {
125
+ const saltCap = extractSaltFromCapId(session.cap.capId);
126
+ const K_raw = await deriveKeyMaterial(session.cap.S, saltCap);
127
+ session.K_raw = K_raw;
128
+ session.bootstrapKeys = deriveBootstrapKeys(K_raw);
129
+ const receivedHmac = payload.slice(0, 32);
130
+ const nonceBBytes = payload.slice(32);
131
+ session.nonceB = new TextDecoder().decode(nonceBBytes);
132
+ const auth1Data = new TextEncoder().encode('hello' + session.cap.capId + session.nonceB);
133
+ if (!verifyHmac(session.bootstrapKeys.K_auth, auth1Data, receivedHmac)) {
134
+ throw new Error('AUTH1 HMAC verification failed');
135
+ }
136
+ // Fresh session nonce and per-session keys bound to (nonceB, nonceC).
137
+ session.nonceC = randomBytes(16).toString('hex');
138
+ session.keys = deriveSessionKeys(K_raw, session.nonceB, session.nonceC);
139
+ const auth2 = {
140
+ ok: true,
141
+ nonceB: session.nonceB,
142
+ nonceC: session.nonceC,
143
+ expiryTs: Date.now() + SESSION_TTL_MS,
144
+ policyHash: hashPolicy(session.cap.policy),
145
+ requiresPassword: session.requiresPassword,
146
+ };
147
+ // AUTH2 is protected with the BOOTSTRAP key: the client cannot derive the
148
+ // session key until it has learned nonceC from this very message.
149
+ const encrypted = aeadEncrypt(session.bootstrapKeys.K_enc, FrameType.AUTH2, 0, auth2, AeadDir.ServerToClient);
150
+ const frame = encodeFrame(FrameType.AUTH2, encode(encrypted));
151
+ sendRelayResponse(session, frame);
152
+ }
153
+ catch (error) {
154
+ output.error('AUTH1 failed', error instanceof Error ? error.message : String(error));
155
+ // No session key yet on failure paths; nothing encryptable to send.
156
+ }
157
+ }
158
+ async function handleAuth3(session, payload) {
159
+ if (!session.keys || !session.nonceC) {
160
+ output.error('AUTH3 received out of sequence');
161
+ return;
162
+ }
163
+ // AUTH3 HMAC is keyed with the SESSION auth key, binding the client's
164
+ // handshake completion to the fresh per-session key material.
165
+ const expectedData = new TextEncoder().encode('ready' + session.nonceC);
166
+ if (!verifyHmac(session.keys.K_auth, expectedData, payload)) {
167
+ output.error('AUTH3 HMAC verification failed');
168
+ return;
169
+ }
170
+ session.authenticated = true;
171
+ output.info(`Session authenticated: ${session.socketId}`);
172
+ }
173
+ async function handleAuthPw(session, payload) {
174
+ if (!session.authenticated || !session.keys) {
175
+ sendError(session, null, ErrorCode.AUTH_FAILED, 'Not authenticated');
176
+ return;
177
+ }
178
+ if (!session.requiresPassword || !session.passwordHash) {
179
+ sendError(session, null, ErrorCode.AUTH_FAILED, 'Password not required');
180
+ return;
181
+ }
182
+ if (session.passwordVerified) {
183
+ sendError(session, null, ErrorCode.AUTH_FAILED, 'Already verified');
184
+ return;
185
+ }
186
+ try {
187
+ const encrypted = decode(payload);
188
+ const decrypted = aeadDecrypt(session.keys.K_enc, FrameType.AUTH_PW, encrypted.nonce, encrypted.cipher, AeadDir.ClientToServer);
189
+ session.counters.incoming.validate(decrypted.ctr);
190
+ const { password } = decrypted.msg;
191
+ const ok = typeof password === 'string' && await verifyPassword(session.passwordHash, password);
192
+ if (!ok) {
193
+ output.warn(`Invalid password attempt for session: ${session.socketId}`);
194
+ sendError(session, null, ErrorCode.AUTH_FAILED, 'Invalid password');
195
+ return;
196
+ }
197
+ session.passwordVerified = true;
198
+ output.info(`Password verified for session: ${session.socketId}`);
199
+ const ctr = session.counters.outgoing.next();
200
+ const successEncrypted = aeadEncrypt(session.keys.K_enc, FrameType.AUTH_PW, ctr, { ok: true }, AeadDir.ServerToClient);
201
+ sendRelayResponse(session, encodeFrame(FrameType.AUTH_PW, encode(successEncrypted)));
202
+ }
203
+ catch (error) {
204
+ output.error('AUTH_PW failed', error instanceof Error ? error.message : String(error));
205
+ sendError(session, null, ErrorCode.AUTH_FAILED, 'Password verification failed');
206
+ }
207
+ }
208
+ function sendError(session, commandId, code, detail) {
209
+ if (!session.keys)
210
+ return;
211
+ const ctr = session.counters.outgoing.next();
212
+ const encrypted = aeadEncrypt(session.keys.K_enc, FrameType.ERROR, ctr, { commandId, code, detail }, AeadDir.ServerToClient);
213
+ sendRelayResponse(session, encodeFrame(FrameType.ERROR, encode(encrypted)));
214
+ }
215
+ //# sourceMappingURL=session.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.js","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"AACA,OAAO,EACL,SAAS,EACT,WAAW,EACX,WAAW,EACX,SAAS,GACV,MAAM,+BAA+B,CAAC;AACvC,OAAO,EACL,iBAAiB,EACjB,mBAAmB,EACnB,iBAAiB,EACjB,oBAAoB,EACpB,WAAW,EACX,WAAW,EACX,UAAU,EACV,cAAc,EACd,UAAU,EACV,OAAO,GAER,MAAM,6BAA6B,CAAC;AACrC,OAAO,EACL,aAAa,EACb,eAAe,EACf,qBAAqB,EACrB,cAAc,GACf,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC;AACvC,OAAO,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAC;AAErC,OAAO,EAAE,sBAAsB,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAEjF,MAAM,MAAM,GAAG,IAAI,aAAa,CAAC,EAAE,IAAI,EAAE,eAAe,CAAC,OAAO,CAAC,GAAG,CAAC,WAAW,IAAI,MAAM,CAAC,EAAE,CAAC,CAAC;AAE/F,2EAA2E;AAC3E,+DAA+D;AAC/D,MAAM,cAAc,GAAG,QAAQ,CAAC;AAmBhC,yCAAyC;AACzC,MAAM,UAAU,iBAAiB,CAAC,OAAgB,EAAE,KAAiB;IACnE,OAAO,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC;QAC7B,IAAI,EAAE,gBAAgB;QACtB,QAAQ,EAAE,OAAO,CAAC,QAAQ;QAC1B,KAAK,EAAE,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC;KAC7C,CAAC,CAAC,CAAC;AACN,CAAC;AAED,8EAA8E;AAC9E,+EAA+E;AAC/E,4EAA4E;AAC5E,oEAAoE;AACpE,MAAM,UAAU,uBAAuB,CACrC,OAAkB,EAClB,QAAgB,EAChB,GAAmB,EACnB,YAAqB;IAErB,MAAM,OAAO,GAAY;QACvB,QAAQ;QACR,EAAE,EAAE,OAAO;QACX,GAAG;QACH,QAAQ,EAAE,IAAI,qBAAqB,EAAE;QACrC,aAAa,EAAE,KAAK;QACpB,gBAAgB,EAAE,CAAC,YAAY,EAAE,uCAAuC;QACxE,gBAAgB,EAAE,CAAC,CAAC,YAAY;QAChC,YAAY,EAAE,YAAY,IAAI,SAAS;KACxC,CAAC;IAEF,MAAM,MAAM,GAAG,IAAI,WAAW,EAAE,CAAC;IACjC,MAAM,KAAK,GAA2B,EAAE,CAAC;IAEzC,OAAO;QACL,WAAW,EAAE,KAAK,EAAE,IAAY,EAAE,EAAE;YAClC,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACjC,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;gBAC3B,IAAI,CAAC;oBACH,MAAM,WAAW,CAAC,OAAO,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;gBAC3C,CAAC;gBAAC,OAAO,KAAK,EAAE,CAAC;oBACf,MAAM,CAAC,KAAK,CAAC,qCAAqC,QAAQ,EAAE,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;oBACtH,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,SAAS,CAAC,cAAc,EAAE,gBAAgB,CAAC,CAAC;gBACvE,CAAC;YACH,CAAC;QACH,CAAC;QACD,OAAO,EAAE,GAAG,EAAE;YACZ,IAAI,KAAK,CAAC,YAAY,EAAE,CAAC;gBACvB,IAAI,CAAC;oBACH,mBAAmB,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;gBAC1C,CAAC;gBAAC,MAAM,CAAC,CAAA,CAAC;YACZ,CAAC;QACH,CAAC;KACF,CAAC;AACJ,CAAC;AAED,MAAM,kBAAkB,GAAG,IAAI,GAAG,CAAY;IAC5C,SAAS,CAAC,WAAW;IACrB,SAAS,CAAC,WAAW;IACrB,SAAS,CAAC,aAAa;IACvB,SAAS,CAAC,aAAa;IACvB,SAAS,CAAC,YAAY;IACtB,SAAS,CAAC,YAAY;IACtB,SAAS,CAAC,WAAW;CACtB,CAAC,CAAC;AAEH,KAAK,UAAU,WAAW,CACxB,OAAgB,EAChB,KAA6B,EAC7B,KAA+C;IAE/C,IAAI,kBAAkB,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QACvC,IAAI,CAAC,OAAO,CAAC,aAAa,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;YAC5C,MAAM,CAAC,KAAK,CAAC,qDAAqD,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;YAChF,OAAO;QACT,CAAC;QAED,IAAI,CAAC,KAAK,CAAC,YAAY,EAAE,CAAC;YACxB,KAAK,CAAC,YAAY,GAAG;gBACnB,QAAQ,EAAE,OAAO,CAAC,QAAQ;gBAC1B,EAAE,EAAE,OAAO,CAAC,EAAE;gBACd,GAAG,EAAE,OAAO,CAAC,GAAG;gBAChB,IAAI,EAAE,OAAO,CAAC,IAAI;gBAClB,QAAQ,EAAE,OAAO,CAAC,QAAQ;gBAC1B,cAAc,EAAE,IAAI,cAAc,EAAE;gBACpC,aAAa,EAAE,IAAI;gBACnB,gBAAgB,EAAE,OAAO,CAAC,gBAAgB;gBAC1C,gBAAgB,EAAE,OAAO,CAAC,gBAAgB;aAC3C,CAAC;QACJ,CAAC;QACD,0FAA0F;QAC1F,KAAK,CAAC,YAAY,CAAC,gBAAgB,GAAG,OAAO,CAAC,gBAAgB,CAAC;QAE/D,MAAM,sBAAsB,CAAC,KAAK,CAAC,YAAY,EAAE,KAAK,CAAC,CAAC;QACxD,OAAO;IACT,CAAC;IAED,QAAQ,KAAK,CAAC,IAAI,EAAE,CAAC;QACnB,KAAK,SAAS,CAAC,KAAK;YAClB,MAAM,WAAW,CAAC,OAAO,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;YAC1C,MAAM;QACR,KAAK,SAAS,CAAC,KAAK;YAClB,MAAM,WAAW,CAAC,OAAO,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;YAC1C,MAAM;QACR,KAAK,SAAS,CAAC,OAAO;YACpB,MAAM,YAAY,CAAC,OAAO,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;YAC3C,MAAM;QACR,KAAK,SAAS,CAAC,SAAS;YACtB,MAAM;QACR;YACE,MAAM,CAAC,IAAI,CAAC,0BAA0B,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;IACxD,CAAC;AACH,CAAC;AAED,KAAK,UAAU,WAAW,CAAC,OAAgB,EAAE,OAAmB;IAC9D,8EAA8E;IAC9E,6EAA6E;IAC7E,qEAAqE;IACrE,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;QACtB,MAAM,CAAC,IAAI,CAAC,oCAAoC,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC;QACpE,OAAO;IACT,CAAC;IACD,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC;IAEzB,sEAAsE;IACtE,2EAA2E;IAC3E,MAAM,kBAAkB,GAAG,EAAE,CAAC;IAC9B,IAAI,OAAO,CAAC,MAAM,KAAK,EAAE,GAAG,kBAAkB,EAAE,CAAC;QAC/C,MAAM,CAAC,IAAI,CAAC,wCAAwC,OAAO,CAAC,QAAQ,SAAS,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;QAC/F,OAAO;IACT,CAAC;IAED,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,oBAAoB,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACxD,MAAM,KAAK,GAAG,MAAM,iBAAiB,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;QAC9D,OAAO,CAAC,KAAK,GAAG,KAAK,CAAC;QACtB,OAAO,CAAC,aAAa,GAAG,mBAAmB,CAAC,KAAK,CAAC,CAAC;QAEnD,MAAM,YAAY,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAC1C,MAAM,WAAW,GAAG,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QACtC,OAAO,CAAC,MAAM,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;QAEvD,MAAM,SAAS,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,KAAK,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;QACzF,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,EAAE,SAAS,EAAE,YAAY,CAAC,EAAE,CAAC;YACvE,MAAM,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAC;QACpD,CAAC;QAED,sEAAsE;QACtE,OAAO,CAAC,MAAM,GAAG,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;QACjD,OAAO,CAAC,IAAI,GAAG,iBAAiB,CAAC,KAAK,EAAE,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;QAExE,MAAM,KAAK,GAAG;YACZ,EAAE,EAAE,IAAI;YACR,MAAM,EAAE,OAAO,CAAC,MAAM;YACtB,MAAM,EAAE,OAAO,CAAC,MAAM;YACtB,QAAQ,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,cAAc;YACrC,UAAU,EAAE,UAAU,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC;YAC1C,gBAAgB,EAAE,OAAO,CAAC,gBAAgB;SAC3C,CAAC;QAEF,0EAA0E;QAC1E,kEAAkE;QAClE,MAAM,SAAS,GAAG,WAAW,CAAC,OAAO,CAAC,aAAa,CAAC,KAAK,EAAE,SAAS,CAAC,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,cAAc,CAAC,CAAC;QAC9G,MAAM,KAAK,GAAG,WAAW,CAAC,SAAS,CAAC,KAAK,EAAE,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC;QAC9D,iBAAiB,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IACpC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,CAAC,KAAK,CAAC,cAAc,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;QACrF,oEAAoE;IACtE,CAAC;AACH,CAAC;AAED,KAAK,UAAU,WAAW,CAAC,OAAgB,EAAE,OAAmB;IAC9D,IAAI,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;QACrC,MAAM,CAAC,KAAK,CAAC,gCAAgC,CAAC,CAAC;QAC/C,OAAO;IACT,CAAC;IAED,sEAAsE;IACtE,8DAA8D;IAC9D,MAAM,YAAY,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACxE,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,YAAY,EAAE,OAAO,CAAC,EAAE,CAAC;QAC5D,MAAM,CAAC,KAAK,CAAC,gCAAgC,CAAC,CAAC;QAC/C,OAAO;IACT,CAAC;IAED,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC;IAC7B,MAAM,CAAC,IAAI,CAAC,0BAA0B,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC;AAC5D,CAAC;AAED,KAAK,UAAU,YAAY,CAAC,OAAgB,EAAE,OAAmB;IAC/D,IAAI,CAAC,OAAO,CAAC,aAAa,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;QAC5C,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,SAAS,CAAC,WAAW,EAAE,mBAAmB,CAAC,CAAC;QACrE,OAAO;IACT,CAAC;IACD,IAAI,CAAC,OAAO,CAAC,gBAAgB,IAAI,CAAC,OAAO,CAAC,YAAY,EAAE,CAAC;QACvD,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,SAAS,CAAC,WAAW,EAAE,uBAAuB,CAAC,CAAC;QACzE,OAAO;IACT,CAAC;IACD,IAAI,OAAO,CAAC,gBAAgB,EAAE,CAAC;QAC7B,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,SAAS,CAAC,WAAW,EAAE,kBAAkB,CAAC,CAAC;QACpE,OAAO;IACT,CAAC;IAED,IAAI,CAAC;QACH,MAAM,SAAS,GAAG,MAAM,CAAC,OAAO,CAAQ,CAAC;QACzC,MAAM,SAAS,GAAG,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,EAAE,SAAS,CAAC,OAAO,EAAE,SAAS,CAAC,KAAK,EAAE,SAAS,CAAC,MAAM,EAAE,OAAO,CAAC,cAAc,CAAC,CAAC;QAChI,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,QAAQ,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;QAElD,MAAM,EAAE,QAAQ,EAAE,GAAG,SAAS,CAAC,GAA4B,CAAC;QAC5D,MAAM,EAAE,GAAG,OAAO,QAAQ,KAAK,QAAQ,IAAI,MAAM,cAAc,CAAC,OAAO,CAAC,YAAY,EAAE,QAAQ,CAAC,CAAC;QAChG,IAAI,CAAC,EAAE,EAAE,CAAC;YACR,MAAM,CAAC,IAAI,CAAC,yCAAyC,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC;YACzE,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,SAAS,CAAC,WAAW,EAAE,kBAAkB,CAAC,CAAC;YACpE,OAAO;QACT,CAAC;QAED,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC;QAChC,MAAM,CAAC,IAAI,CAAC,kCAAkC,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC;QAElE,MAAM,GAAG,GAAG,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC;QAC7C,MAAM,gBAAgB,GAAG,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,EAAE,SAAS,CAAC,OAAO,EAAE,GAAG,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,OAAO,CAAC,cAAc,CAAC,CAAC;QACvH,iBAAiB,CAAC,OAAO,EAAE,WAAW,CAAC,SAAS,CAAC,OAAO,EAAE,MAAM,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC;IACvF,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,CAAC,KAAK,CAAC,gBAAgB,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;QACvF,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,SAAS,CAAC,WAAW,EAAE,8BAA8B,CAAC,CAAC;IAClF,CAAC;AACH,CAAC;AAED,SAAS,SAAS,CAAC,OAAgB,EAAE,SAAwB,EAAE,IAAY,EAAE,MAAe;IAC1F,IAAI,CAAC,OAAO,CAAC,IAAI;QAAE,OAAO;IAC1B,MAAM,GAAG,GAAG,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC;IAC7C,MAAM,SAAS,GAAG,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,EAAE,SAAS,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,OAAO,CAAC,cAAc,CAAC,CAAC;IAC7H,iBAAiB,CAAC,OAAO,EAAE,WAAW,CAAC,SAAS,CAAC,KAAK,EAAE,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;AAC9E,CAAC"}