@sideband/secure-relay 0.2.2 → 0.3.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.
Files changed (46) hide show
  1. package/README.md +6 -4
  2. package/dist/.tsbuildinfo +1 -0
  3. package/dist/constants.d.ts +49 -0
  4. package/dist/constants.d.ts.map +1 -0
  5. package/dist/constants.js +51 -0
  6. package/dist/constants.js.map +1 -0
  7. package/dist/crypto.d.ts +70 -0
  8. package/dist/crypto.d.ts.map +1 -0
  9. package/dist/crypto.js +144 -0
  10. package/dist/crypto.js.map +1 -0
  11. package/dist/frame.d.ts +219 -0
  12. package/dist/frame.d.ts.map +1 -0
  13. package/dist/frame.js +554 -0
  14. package/dist/frame.js.map +1 -0
  15. package/dist/handshake.d.ts +39 -0
  16. package/dist/handshake.d.ts.map +1 -0
  17. package/dist/handshake.js +93 -0
  18. package/dist/handshake.js.map +1 -0
  19. package/dist/index.d.ts +46 -0
  20. package/dist/index.d.ts.map +1 -0
  21. package/dist/index.js +12 -0
  22. package/dist/index.js.map +1 -0
  23. package/dist/replay.d.ts +32 -0
  24. package/dist/replay.d.ts.map +1 -0
  25. package/dist/replay.js +88 -0
  26. package/dist/replay.js.map +1 -0
  27. package/dist/session.d.ts +67 -0
  28. package/dist/session.d.ts.map +1 -0
  29. package/dist/session.js +122 -0
  30. package/dist/session.js.map +1 -0
  31. package/dist/types.d.ts +120 -0
  32. package/dist/types.d.ts.map +1 -0
  33. package/dist/types.js +81 -0
  34. package/dist/types.js.map +1 -0
  35. package/package.json +1 -1
  36. package/src/constants.ts +3 -3
  37. package/src/crypto.test.ts +5 -5
  38. package/src/frame.test.ts +113 -47
  39. package/src/frame.ts +119 -86
  40. package/src/handshake.test.ts +29 -41
  41. package/src/handshake.ts +25 -27
  42. package/src/index.ts +4 -10
  43. package/src/integration.test.ts +97 -138
  44. package/src/session.test.ts +12 -10
  45. package/src/types.ts +4 -14
  46. /package/{dist/LICENSE → LICENSE} +0 -0
package/dist/frame.js ADDED
@@ -0,0 +1,554 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ /**
3
+ * Binary wire format for Sideband Relay Protocol (SBRP).
4
+ *
5
+ * Frame structure (§13):
6
+ * ```
7
+ * ┌───────────┬──────────────┬────────────────┬─────────────────────┐
8
+ * │ Type (1B) │ Length (4B) │ SessionID (8B) │ Payload (0..64KB) │
9
+ * └───────────┴──────────────┴────────────────┴─────────────────────┘
10
+ * ```
11
+ *
12
+ * All multi-byte integers are big-endian.
13
+ */
14
+ import { ED25519_PUBLIC_KEY_LENGTH, ED25519_SIGNATURE_LENGTH, FRAME_HEADER_SIZE, HANDSHAKE_ACCEPT_PAYLOAD_SIZE, HANDSHAKE_INIT_PAYLOAD_SIZE, MAX_PAYLOAD_SIZE, MAX_PING_PAYLOAD_SIZE, MIN_CONTROL_PAYLOAD_SIZE, MIN_ENCRYPTED_PAYLOAD_SIZE, SIGNAL_PAYLOAD_SIZE, X25519_PUBLIC_KEY_LENGTH, } from "./constants.js";
15
+ import { extractSequence } from "./crypto.js";
16
+ import { SbrpError, SbrpErrorCode, SignalCode, SignalReason } from "./types.js";
17
+ const textEncoder = new TextEncoder();
18
+ const textDecoder = new TextDecoder("utf-8", { fatal: false });
19
+ /**
20
+ * Frame type discriminant (wire byte).
21
+ *
22
+ * Frame types are organized by authority:
23
+ * - Endpoint frames (0x01-0x03): Forwarded by relay, E2EE
24
+ * - Signal frame (0x04): Daemon → Relay only
25
+ * - Keepalive frames (0x10-0x11): Connection-scoped, never forwarded
26
+ * - Control frame (0x20): Relay → Endpoint only
27
+ */
28
+ export const FrameType = {
29
+ // Endpoint frames (forwarded, E2EE)
30
+ HandshakeInit: 0x01,
31
+ HandshakeAccept: 0x02,
32
+ Data: 0x03, // Renamed from Encrypted for clarity
33
+ // Signal frame (daemon → relay)
34
+ Signal: 0x04,
35
+ // Keepalive frames (connection-scoped, never forwarded)
36
+ Ping: 0x10,
37
+ Pong: 0x11,
38
+ // Control frame (relay → endpoint)
39
+ Control: 0x20,
40
+ };
41
+ /**
42
+ * Wire control codes (uint16, §14).
43
+ *
44
+ * Codes use ranges for categorization:
45
+ * - 0x01xx: Authentication (terminal)
46
+ * - 0x02xx: Routing (terminal)
47
+ * - 0x03xx: Session (terminal)
48
+ * - 0x04xx: Wire format (terminal)
49
+ * - 0x09xx: Throttling (varies: rate_limited=N, backpressure=T)
50
+ * - 0x10xx: Session state (non-terminal)
51
+ */
52
+ export const WireControlCode = {
53
+ // Authentication (0x01xx) - Terminal
54
+ Unauthorized: 0x0101,
55
+ Forbidden: 0x0102,
56
+ // Routing (0x02xx) - Terminal
57
+ DaemonNotFound: 0x0201,
58
+ DaemonOffline: 0x0202, // Terminal
59
+ // Session (0x03xx) - Terminal
60
+ SessionNotFound: 0x0301,
61
+ SessionExpired: 0x0302,
62
+ // Wire Format (0x04xx) - Terminal
63
+ MalformedFrame: 0x0401,
64
+ PayloadTooLarge: 0x0402,
65
+ InvalidFrameType: 0x0403,
66
+ InvalidSessionId: 0x0404,
67
+ DisallowedSender: 0x0405,
68
+ // Internal (0x06xx) - Terminal
69
+ InternalError: 0x0601,
70
+ // Throttling (0x09xx) - Varies (RateLimited=N, Backpressure=T)
71
+ RateLimited: 0x0901,
72
+ Backpressure: 0x0902,
73
+ // Session State (0x10xx) - Non-terminal
74
+ SessionPaused: 0x1001,
75
+ SessionResumed: 0x1002,
76
+ SessionEnded: 0x1003,
77
+ SessionPending: 0x1004,
78
+ };
79
+ /**
80
+ * Check if a control code is terminal (relay closes WebSocket after sending).
81
+ *
82
+ * Fail-safe pattern: only enumerate non-terminal exceptions; unknown/new codes
83
+ * default to terminal so they never silently keep a session alive.
84
+ */
85
+ export function isTerminalCode(code) {
86
+ switch (code) {
87
+ case WireControlCode.RateLimited:
88
+ case WireControlCode.SessionPaused:
89
+ case WireControlCode.SessionResumed:
90
+ case WireControlCode.SessionEnded:
91
+ case WireControlCode.SessionPending:
92
+ return false;
93
+ default:
94
+ return true;
95
+ }
96
+ }
97
+ // ============================================================================
98
+ // Control code mapping
99
+ // ============================================================================
100
+ const sbrpToWire = {
101
+ // Authentication
102
+ [SbrpErrorCode.Unauthorized]: WireControlCode.Unauthorized,
103
+ [SbrpErrorCode.Forbidden]: WireControlCode.Forbidden,
104
+ // Routing
105
+ [SbrpErrorCode.DaemonNotFound]: WireControlCode.DaemonNotFound,
106
+ [SbrpErrorCode.DaemonOffline]: WireControlCode.DaemonOffline,
107
+ // Session
108
+ [SbrpErrorCode.SessionNotFound]: WireControlCode.SessionNotFound,
109
+ [SbrpErrorCode.SessionExpired]: WireControlCode.SessionExpired,
110
+ // Wire Format
111
+ [SbrpErrorCode.MalformedFrame]: WireControlCode.MalformedFrame,
112
+ [SbrpErrorCode.PayloadTooLarge]: WireControlCode.PayloadTooLarge,
113
+ [SbrpErrorCode.InvalidFrameType]: WireControlCode.InvalidFrameType,
114
+ [SbrpErrorCode.InvalidSessionId]: WireControlCode.InvalidSessionId,
115
+ [SbrpErrorCode.DisallowedSender]: WireControlCode.DisallowedSender,
116
+ // Internal
117
+ [SbrpErrorCode.InternalError]: WireControlCode.InternalError,
118
+ // Rate Limiting / Backpressure
119
+ [SbrpErrorCode.RateLimited]: WireControlCode.RateLimited,
120
+ [SbrpErrorCode.Backpressure]: WireControlCode.Backpressure,
121
+ // Session State
122
+ [SbrpErrorCode.SessionPaused]: WireControlCode.SessionPaused,
123
+ [SbrpErrorCode.SessionResumed]: WireControlCode.SessionResumed,
124
+ [SbrpErrorCode.SessionEnded]: WireControlCode.SessionEnded,
125
+ [SbrpErrorCode.SessionPending]: WireControlCode.SessionPending,
126
+ };
127
+ const wireToSbrp = {
128
+ // Authentication
129
+ [WireControlCode.Unauthorized]: SbrpErrorCode.Unauthorized,
130
+ [WireControlCode.Forbidden]: SbrpErrorCode.Forbidden,
131
+ // Routing
132
+ [WireControlCode.DaemonNotFound]: SbrpErrorCode.DaemonNotFound,
133
+ [WireControlCode.DaemonOffline]: SbrpErrorCode.DaemonOffline,
134
+ // Session
135
+ [WireControlCode.SessionNotFound]: SbrpErrorCode.SessionNotFound,
136
+ [WireControlCode.SessionExpired]: SbrpErrorCode.SessionExpired,
137
+ // Wire Format
138
+ [WireControlCode.MalformedFrame]: SbrpErrorCode.MalformedFrame,
139
+ [WireControlCode.PayloadTooLarge]: SbrpErrorCode.PayloadTooLarge,
140
+ [WireControlCode.InvalidFrameType]: SbrpErrorCode.InvalidFrameType,
141
+ [WireControlCode.InvalidSessionId]: SbrpErrorCode.InvalidSessionId,
142
+ [WireControlCode.DisallowedSender]: SbrpErrorCode.DisallowedSender,
143
+ // Internal
144
+ [WireControlCode.InternalError]: SbrpErrorCode.InternalError,
145
+ // Rate Limiting / Backpressure
146
+ [WireControlCode.RateLimited]: SbrpErrorCode.RateLimited,
147
+ [WireControlCode.Backpressure]: SbrpErrorCode.Backpressure,
148
+ // Session State
149
+ [WireControlCode.SessionPaused]: SbrpErrorCode.SessionPaused,
150
+ [WireControlCode.SessionResumed]: SbrpErrorCode.SessionResumed,
151
+ [WireControlCode.SessionEnded]: SbrpErrorCode.SessionEnded,
152
+ [WireControlCode.SessionPending]: SbrpErrorCode.SessionPending,
153
+ };
154
+ /** Convert SbrpErrorCode to wire format (for relay-transmittable codes only) */
155
+ export function toWireControlCode(code) {
156
+ const wire = sbrpToWire[code];
157
+ if (wire === undefined) {
158
+ throw new Error(`Unknown or non-wire SbrpErrorCode: ${code}`);
159
+ }
160
+ return wire;
161
+ }
162
+ /** Convert wire control code to SbrpErrorCode */
163
+ export function fromWireControlCode(code) {
164
+ const sbrp = wireToSbrp[code];
165
+ if (sbrp === undefined) {
166
+ throw new Error(`Unknown WireControlCode: 0x${code.toString(16).padStart(4, "0")}`);
167
+ }
168
+ return sbrp;
169
+ }
170
+ // ============================================================================
171
+ // Validation helpers
172
+ // ============================================================================
173
+ const MAX_UINT64 = 0xffffffffffffffffn;
174
+ /**
175
+ * Check if frame type requires non-zero sessionId (§13.2).
176
+ *
177
+ * Session-bound: HandshakeInit, HandshakeAccept, Data, Signal
178
+ * Connection-scoped (sessionId must be 0): Ping, Pong
179
+ * Variable (depends on content): Control
180
+ */
181
+ function isSessionBound(type) {
182
+ return (type === FrameType.HandshakeInit ||
183
+ type === FrameType.HandshakeAccept ||
184
+ type === FrameType.Data ||
185
+ type === FrameType.Signal);
186
+ }
187
+ /** Check if frame type requires sessionId = 0 (connection-scoped) */
188
+ function isConnectionScoped(type) {
189
+ return type === FrameType.Ping || type === FrameType.Pong;
190
+ }
191
+ function validateSessionId(sessionId, type) {
192
+ if (sessionId < 0n || sessionId > MAX_UINT64) {
193
+ throw new SbrpError(SbrpErrorCode.MalformedFrame, `SessionId out of uint64 range: ${sessionId}`);
194
+ }
195
+ if (isSessionBound(type) && sessionId === 0n) {
196
+ throw new SbrpError(SbrpErrorCode.MalformedFrame, `Session-bound frame type 0x${type.toString(16).padStart(2, "0")} requires non-zero sessionId`);
197
+ }
198
+ if (isConnectionScoped(type) && sessionId !== 0n) {
199
+ throw new SbrpError(SbrpErrorCode.MalformedFrame, `Connection-scoped frame type 0x${type.toString(16).padStart(2, "0")} requires sessionId = 0`);
200
+ }
201
+ }
202
+ // ============================================================================
203
+ // Low-level frame encoding/decoding
204
+ // ============================================================================
205
+ /**
206
+ * Encode a frame to binary wire format.
207
+ *
208
+ * @throws {SbrpError} if payload exceeds MAX_PAYLOAD_SIZE or sessionId is invalid
209
+ */
210
+ export function encodeFrame(type, sessionId, payload) {
211
+ validateSessionId(sessionId, type);
212
+ if (payload.length > MAX_PAYLOAD_SIZE) {
213
+ throw new SbrpError(SbrpErrorCode.PayloadTooLarge, `Payload size ${payload.length} exceeds maximum ${MAX_PAYLOAD_SIZE}`);
214
+ }
215
+ const frame = new Uint8Array(FRAME_HEADER_SIZE + payload.length);
216
+ const view = new DataView(frame.buffer);
217
+ frame[0] = type;
218
+ view.setUint32(1, payload.length, false);
219
+ view.setBigUint64(5, sessionId, false);
220
+ frame.set(payload, FRAME_HEADER_SIZE);
221
+ return frame;
222
+ }
223
+ /**
224
+ * Read frame header without decoding payload.
225
+ * Useful for routing decisions before full decode.
226
+ *
227
+ * Validates wire format constraints including non-zero sessionId
228
+ * for session-bound frames (§13.2).
229
+ *
230
+ * @throws {SbrpError} if buffer is too short, length exceeds max, or sessionId invalid
231
+ */
232
+ export function readFrameHeader(data) {
233
+ if (data.length < FRAME_HEADER_SIZE) {
234
+ throw new SbrpError(SbrpErrorCode.MalformedFrame, `Frame too short: ${data.length} < ${FRAME_HEADER_SIZE}`);
235
+ }
236
+ const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
237
+ const type = data[0];
238
+ switch (type) {
239
+ case FrameType.HandshakeInit:
240
+ case FrameType.HandshakeAccept:
241
+ case FrameType.Data:
242
+ case FrameType.Signal:
243
+ case FrameType.Ping:
244
+ case FrameType.Pong:
245
+ case FrameType.Control:
246
+ break;
247
+ default:
248
+ throw new SbrpError(SbrpErrorCode.InvalidFrameType, `Unknown frame type: 0x${type.toString(16).padStart(2, "0")}`);
249
+ }
250
+ const length = view.getUint32(1, false);
251
+ const sessionId = view.getBigUint64(5, false);
252
+ if (length > MAX_PAYLOAD_SIZE) {
253
+ throw new SbrpError(SbrpErrorCode.PayloadTooLarge, `Payload length ${length} exceeds maximum ${MAX_PAYLOAD_SIZE}`);
254
+ }
255
+ // Wire format constraint: session-bound frames require non-zero sessionId
256
+ if (isSessionBound(type) && sessionId === 0n) {
257
+ throw new SbrpError(SbrpErrorCode.MalformedFrame, `Session-bound frame type 0x${type.toString(16).padStart(2, "0")} requires non-zero sessionId`);
258
+ }
259
+ // Wire format constraint: connection-scoped frames require sessionId = 0
260
+ if (isConnectionScoped(type) && sessionId !== 0n) {
261
+ throw new SbrpError(SbrpErrorCode.MalformedFrame, `Connection-scoped frame type 0x${type.toString(16).padStart(2, "0")} requires sessionId = 0`);
262
+ }
263
+ return { type, length, sessionId };
264
+ }
265
+ /**
266
+ * Decode a complete frame from binary data.
267
+ *
268
+ * Rejects trailing bytes to catch framing mistakes. Use `FrameDecoder`
269
+ * for streaming scenarios with multiple frames per buffer.
270
+ *
271
+ * @throws {SbrpError} if frame is malformed or has trailing bytes
272
+ */
273
+ export function decodeFrame(data) {
274
+ const header = readFrameHeader(data);
275
+ const expectedSize = FRAME_HEADER_SIZE + header.length;
276
+ if (data.length < expectedSize) {
277
+ throw new SbrpError(SbrpErrorCode.MalformedFrame, `Frame truncated: got ${data.length}, expected ${expectedSize}`);
278
+ }
279
+ if (data.length > expectedSize) {
280
+ throw new SbrpError(SbrpErrorCode.MalformedFrame, `Frame has ${data.length - expectedSize} trailing bytes`);
281
+ }
282
+ const payload = data.subarray(FRAME_HEADER_SIZE, expectedSize);
283
+ return { ...header, payload };
284
+ }
285
+ // ============================================================================
286
+ // High-level frame encoding (typed message → binary)
287
+ // ============================================================================
288
+ /**
289
+ * Encode HandshakeInit to wire frame.
290
+ *
291
+ * @throws {SbrpError} if initPublicKey is not exactly 32 bytes or sessionId is invalid
292
+ */
293
+ export function encodeHandshakeInit(sessionId, init) {
294
+ if (init.initPublicKey.length !== HANDSHAKE_INIT_PAYLOAD_SIZE) {
295
+ throw new SbrpError(SbrpErrorCode.MalformedFrame, `initPublicKey must be ${HANDSHAKE_INIT_PAYLOAD_SIZE} bytes, got ${init.initPublicKey.length}`);
296
+ }
297
+ return encodeFrame(FrameType.HandshakeInit, sessionId, init.initPublicKey);
298
+ }
299
+ /**
300
+ * Encode HandshakeAccept to wire frame.
301
+ *
302
+ * Wire layout (128 bytes): identityPublicKey(32) + acceptPublicKey(32) + signature(64)
303
+ *
304
+ * @throws {SbrpError} if field sizes are wrong or sessionId is invalid
305
+ */
306
+ export function encodeHandshakeAccept(sessionId, accept) {
307
+ if (accept.identityPublicKey.length !== ED25519_PUBLIC_KEY_LENGTH) {
308
+ throw new SbrpError(SbrpErrorCode.MalformedFrame, `identityPublicKey must be ${ED25519_PUBLIC_KEY_LENGTH} bytes, got ${accept.identityPublicKey.length}`);
309
+ }
310
+ if (accept.acceptPublicKey.length !== X25519_PUBLIC_KEY_LENGTH) {
311
+ throw new SbrpError(SbrpErrorCode.MalformedFrame, `acceptPublicKey must be ${X25519_PUBLIC_KEY_LENGTH} bytes, got ${accept.acceptPublicKey.length}`);
312
+ }
313
+ if (accept.signature.length !== ED25519_SIGNATURE_LENGTH) {
314
+ throw new SbrpError(SbrpErrorCode.MalformedFrame, `signature must be ${ED25519_SIGNATURE_LENGTH} bytes, got ${accept.signature.length}`);
315
+ }
316
+ const payload = new Uint8Array(HANDSHAKE_ACCEPT_PAYLOAD_SIZE);
317
+ payload.set(accept.identityPublicKey, 0);
318
+ payload.set(accept.acceptPublicKey, ED25519_PUBLIC_KEY_LENGTH);
319
+ payload.set(accept.signature, ED25519_PUBLIC_KEY_LENGTH + X25519_PUBLIC_KEY_LENGTH);
320
+ return encodeFrame(FrameType.HandshakeAccept, sessionId, payload);
321
+ }
322
+ /**
323
+ * Encode Data frame (encrypted message).
324
+ *
325
+ * @throws {SbrpError} if data is too short (< nonce + authTag) or sessionId is invalid
326
+ */
327
+ export function encodeData(sessionId, message) {
328
+ if (message.data.length < MIN_ENCRYPTED_PAYLOAD_SIZE) {
329
+ throw new SbrpError(SbrpErrorCode.MalformedFrame, `Data payload must be at least ${MIN_ENCRYPTED_PAYLOAD_SIZE} bytes, got ${message.data.length}`);
330
+ }
331
+ return encodeFrame(FrameType.Data, sessionId, message.data);
332
+ }
333
+ /**
334
+ * Encode Signal frame (daemon → relay).
335
+ *
336
+ * @param sessionId Session being signaled
337
+ * @param signal Signal code (ready or close)
338
+ * @param reason Reason code (for close signal)
339
+ */
340
+ export function encodeSignal(sessionId, signal, reason = SignalReason.None) {
341
+ const payload = new Uint8Array(SIGNAL_PAYLOAD_SIZE);
342
+ payload[0] = signal;
343
+ payload[1] = reason;
344
+ return encodeFrame(FrameType.Signal, sessionId, payload);
345
+ }
346
+ /**
347
+ * Encode Ping frame (connection-scoped keepalive).
348
+ *
349
+ * @param payload Optional 0-8 byte payload for RTT measurement
350
+ */
351
+ export function encodePing(payload = new Uint8Array(0)) {
352
+ if (payload.length > MAX_PING_PAYLOAD_SIZE) {
353
+ throw new SbrpError(SbrpErrorCode.PayloadTooLarge, `Ping payload must be 0-${MAX_PING_PAYLOAD_SIZE} bytes, got ${payload.length}`);
354
+ }
355
+ return encodeFrame(FrameType.Ping, 0n, payload);
356
+ }
357
+ /**
358
+ * Encode Pong frame (connection-scoped keepalive response).
359
+ *
360
+ * @param payload Payload from corresponding Ping (must be copied)
361
+ */
362
+ export function encodePong(payload = new Uint8Array(0)) {
363
+ if (payload.length > MAX_PING_PAYLOAD_SIZE) {
364
+ throw new SbrpError(SbrpErrorCode.PayloadTooLarge, `Pong payload must be 0-${MAX_PING_PAYLOAD_SIZE} bytes, got ${payload.length}`);
365
+ }
366
+ return encodeFrame(FrameType.Pong, 0n, payload);
367
+ }
368
+ /**
369
+ * Encode Control frame (relay → endpoint).
370
+ *
371
+ * @param sessionId Session ID (non-zero for session events, 0 for connection errors)
372
+ * @param code Control code from WireControlCode
373
+ * @param message Optional diagnostic message (for errors only)
374
+ */
375
+ export function encodeControl(sessionId, code, message) {
376
+ const msgBytes = message ? textEncoder.encode(message) : null;
377
+ const payload = new Uint8Array(2 + (msgBytes?.length ?? 0));
378
+ new DataView(payload.buffer).setUint16(0, code, false);
379
+ if (msgBytes)
380
+ payload.set(msgBytes, 2);
381
+ return encodeFrame(FrameType.Control, sessionId, payload);
382
+ }
383
+ // ============================================================================
384
+ // High-level frame decoding (Frame → typed message)
385
+ // ============================================================================
386
+ /**
387
+ * Decode HandshakeInit from frame.
388
+ *
389
+ * @throws {SbrpError} if frame type or payload size is invalid
390
+ */
391
+ export function decodeHandshakeInit(frame) {
392
+ if (frame.type !== FrameType.HandshakeInit) {
393
+ throw new SbrpError(SbrpErrorCode.InvalidFrameType, `Expected HandshakeInit (0x01), got 0x${frame.type.toString(16).padStart(2, "0")}`);
394
+ }
395
+ if (frame.payload.length !== HANDSHAKE_INIT_PAYLOAD_SIZE) {
396
+ throw new SbrpError(SbrpErrorCode.MalformedFrame, `HandshakeInit payload must be ${HANDSHAKE_INIT_PAYLOAD_SIZE} bytes, got ${frame.payload.length}`);
397
+ }
398
+ return {
399
+ type: "handshake.init",
400
+ initPublicKey: frame.payload.slice(),
401
+ };
402
+ }
403
+ /**
404
+ * Decode HandshakeAccept from frame.
405
+ *
406
+ * Wire layout (128 bytes): identityPublicKey(32) + acceptPublicKey(32) + signature(64)
407
+ *
408
+ * @throws {SbrpError} if frame type or payload size is invalid
409
+ */
410
+ export function decodeHandshakeAccept(frame) {
411
+ if (frame.type !== FrameType.HandshakeAccept) {
412
+ throw new SbrpError(SbrpErrorCode.InvalidFrameType, `Expected HandshakeAccept (0x02), got 0x${frame.type.toString(16).padStart(2, "0")}`);
413
+ }
414
+ if (frame.payload.length !== HANDSHAKE_ACCEPT_PAYLOAD_SIZE) {
415
+ throw new SbrpError(SbrpErrorCode.MalformedFrame, `HandshakeAccept payload must be ${HANDSHAKE_ACCEPT_PAYLOAD_SIZE} bytes, got ${frame.payload.length}`);
416
+ }
417
+ const keyEnd = ED25519_PUBLIC_KEY_LENGTH + X25519_PUBLIC_KEY_LENGTH;
418
+ return {
419
+ type: "handshake.accept",
420
+ identityPublicKey: frame.payload.slice(0, ED25519_PUBLIC_KEY_LENGTH),
421
+ acceptPublicKey: frame.payload.slice(ED25519_PUBLIC_KEY_LENGTH, keyEnd),
422
+ signature: frame.payload.slice(keyEnd, keyEnd + ED25519_SIGNATURE_LENGTH),
423
+ };
424
+ }
425
+ /**
426
+ * Decode Data frame (encrypted message).
427
+ *
428
+ * @throws {SbrpError} if frame type or payload is invalid
429
+ */
430
+ export function decodeData(frame) {
431
+ if (frame.type !== FrameType.Data) {
432
+ throw new SbrpError(SbrpErrorCode.InvalidFrameType, `Expected Data (0x03), got 0x${frame.type.toString(16).padStart(2, "0")}`);
433
+ }
434
+ if (frame.payload.length < MIN_ENCRYPTED_PAYLOAD_SIZE) {
435
+ throw new SbrpError(SbrpErrorCode.MalformedFrame, `Data payload must be at least ${MIN_ENCRYPTED_PAYLOAD_SIZE} bytes, got ${frame.payload.length}`);
436
+ }
437
+ const seq = extractSequence(frame.payload);
438
+ return {
439
+ type: "encrypted",
440
+ seq,
441
+ data: frame.payload.slice(),
442
+ };
443
+ }
444
+ /**
445
+ * Decode Signal frame (daemon → relay).
446
+ *
447
+ * @throws {SbrpError} if frame type, payload size, or signal values are invalid
448
+ */
449
+ export function decodeSignal(frame) {
450
+ if (frame.type !== FrameType.Signal) {
451
+ throw new SbrpError(SbrpErrorCode.InvalidFrameType, `Expected Signal (0x04), got 0x${frame.type.toString(16).padStart(2, "0")}`);
452
+ }
453
+ if (frame.payload.length !== SIGNAL_PAYLOAD_SIZE) {
454
+ throw new SbrpError(SbrpErrorCode.MalformedFrame, `Signal payload must be ${SIGNAL_PAYLOAD_SIZE} bytes, got ${frame.payload.length}`);
455
+ }
456
+ // Length validated above (exactly SIGNAL_PAYLOAD_SIZE = 2 bytes)
457
+ const signal = frame.payload[0];
458
+ const reason = frame.payload[1];
459
+ if (signal !== SignalCode.Ready && signal !== SignalCode.Close) {
460
+ throw new SbrpError(SbrpErrorCode.MalformedFrame, `Unknown signal code: 0x${signal.toString(16).padStart(2, "0")}`);
461
+ }
462
+ switch (reason) {
463
+ case SignalReason.None:
464
+ case SignalReason.StateLost:
465
+ case SignalReason.Shutdown:
466
+ case SignalReason.Policy:
467
+ case SignalReason.Error:
468
+ break;
469
+ default:
470
+ throw new SbrpError(SbrpErrorCode.MalformedFrame, `Unknown signal reason: 0x${reason.toString(16).padStart(2, "0")}`);
471
+ }
472
+ return { signal: signal, reason: reason };
473
+ }
474
+ /**
475
+ * Decode Control frame (relay → endpoint).
476
+ *
477
+ * Invalid UTF-8 sequences in message are replaced with U+FFFD.
478
+ *
479
+ * @throws {SbrpError} if frame type or payload is invalid
480
+ */
481
+ export function decodeControl(frame) {
482
+ if (frame.type !== FrameType.Control) {
483
+ throw new SbrpError(SbrpErrorCode.InvalidFrameType, `Expected Control (0x20), got 0x${frame.type.toString(16).padStart(2, "0")}`);
484
+ }
485
+ if (frame.payload.length < MIN_CONTROL_PAYLOAD_SIZE) {
486
+ throw new SbrpError(SbrpErrorCode.MalformedFrame, `Control payload must be at least ${MIN_CONTROL_PAYLOAD_SIZE} bytes, got ${frame.payload.length}`);
487
+ }
488
+ const view = new DataView(frame.payload.buffer, frame.payload.byteOffset, frame.payload.byteLength);
489
+ const rawCode = view.getUint16(0, false);
490
+ if (wireToSbrp[rawCode] === undefined) {
491
+ throw new SbrpError(SbrpErrorCode.MalformedFrame, `Unknown control code: 0x${rawCode.toString(16).padStart(4, "0")}`);
492
+ }
493
+ const code = rawCode;
494
+ // TextDecoder with fatal:false replaces invalid UTF-8 with U+FFFD
495
+ const message = textDecoder.decode(frame.payload.subarray(2));
496
+ return { code, message };
497
+ }
498
+ // ============================================================================
499
+ // Streaming frame decoder
500
+ // ============================================================================
501
+ /**
502
+ * Streaming frame decoder for incremental parsing.
503
+ *
504
+ * Accumulates bytes and yields complete frames. Useful when frames
505
+ * may be fragmented across WebSocket messages or TCP reads.
506
+ *
507
+ * @example
508
+ * ```typescript
509
+ * const decoder = new FrameDecoder();
510
+ * ws.on("message", (data) => {
511
+ * for (const frame of decoder.push(data)) {
512
+ * handleFrame(frame);
513
+ * }
514
+ * });
515
+ * ```
516
+ */
517
+ export class FrameDecoder {
518
+ buffer = new Uint8Array(0);
519
+ /**
520
+ * Push data and yield any complete frames.
521
+ */
522
+ *push(data) {
523
+ // Append to buffer
524
+ if (this.buffer.length === 0) {
525
+ this.buffer = data;
526
+ }
527
+ else {
528
+ const combined = new Uint8Array(this.buffer.length + data.length);
529
+ combined.set(this.buffer, 0);
530
+ combined.set(data, this.buffer.length);
531
+ this.buffer = combined;
532
+ }
533
+ // Yield complete frames
534
+ while (this.buffer.length >= FRAME_HEADER_SIZE) {
535
+ const header = readFrameHeader(this.buffer);
536
+ const frameSize = FRAME_HEADER_SIZE + header.length;
537
+ if (this.buffer.length < frameSize) {
538
+ break; // Incomplete frame, wait for more data
539
+ }
540
+ const payload = this.buffer.subarray(FRAME_HEADER_SIZE, frameSize);
541
+ yield { ...header, payload };
542
+ this.buffer = this.buffer.subarray(frameSize);
543
+ }
544
+ }
545
+ /** Reset decoder state, discarding any buffered data */
546
+ reset() {
547
+ this.buffer = new Uint8Array(0);
548
+ }
549
+ /** Number of bytes currently buffered */
550
+ get bufferedBytes() {
551
+ return this.buffer.length;
552
+ }
553
+ }
554
+ //# sourceMappingURL=frame.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"frame.js","sourceRoot":"","sources":["../src/frame.ts"],"names":[],"mappings":"AAAA,sCAAsC;AAEtC;;;;;;;;;;;GAWG;AAEH,OAAO,EACL,yBAAyB,EACzB,wBAAwB,EACxB,iBAAiB,EACjB,6BAA6B,EAC7B,2BAA2B,EAC3B,gBAAgB,EAChB,qBAAqB,EACrB,wBAAwB,EACxB,0BAA0B,EAC1B,mBAAmB,EACnB,wBAAwB,GACzB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAO9C,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAEhF,MAAM,WAAW,GAAG,IAAI,WAAW,EAAE,CAAC;AACtC,MAAM,WAAW,GAAG,IAAI,WAAW,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;AAE/D;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG;IACvB,oCAAoC;IACpC,aAAa,EAAE,IAAI;IACnB,eAAe,EAAE,IAAI;IACrB,IAAI,EAAE,IAAI,EAAE,qCAAqC;IAEjD,gCAAgC;IAChC,MAAM,EAAE,IAAI;IAEZ,wDAAwD;IACxD,IAAI,EAAE,IAAI;IACV,IAAI,EAAE,IAAI;IAEV,mCAAmC;IACnC,OAAO,EAAE,IAAI;CACL,CAAC;AAIX;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG;IAC7B,qCAAqC;IACrC,YAAY,EAAE,MAAM;IACpB,SAAS,EAAE,MAAM;IAEjB,8BAA8B;IAC9B,cAAc,EAAE,MAAM;IACtB,aAAa,EAAE,MAAM,EAAE,WAAW;IAElC,8BAA8B;IAC9B,eAAe,EAAE,MAAM;IACvB,cAAc,EAAE,MAAM;IAEtB,kCAAkC;IAClC,cAAc,EAAE,MAAM;IACtB,eAAe,EAAE,MAAM;IACvB,gBAAgB,EAAE,MAAM;IACxB,gBAAgB,EAAE,MAAM;IACxB,gBAAgB,EAAE,MAAM;IAExB,+BAA+B;IAC/B,aAAa,EAAE,MAAM;IAErB,+DAA+D;IAC/D,WAAW,EAAE,MAAM;IACnB,YAAY,EAAE,MAAM;IAEpB,wCAAwC;IACxC,aAAa,EAAE,MAAM;IACrB,cAAc,EAAE,MAAM;IACtB,YAAY,EAAE,MAAM;IACpB,cAAc,EAAE,MAAM;CACd,CAAC;AAKX;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAAC,IAAqB;IAClD,QAAQ,IAAI,EAAE,CAAC;QACb,KAAK,eAAe,CAAC,WAAW,CAAC;QACjC,KAAK,eAAe,CAAC,aAAa,CAAC;QACnC,KAAK,eAAe,CAAC,cAAc,CAAC;QACpC,KAAK,eAAe,CAAC,YAAY,CAAC;QAClC,KAAK,eAAe,CAAC,cAAc;YACjC,OAAO,KAAK,CAAC;QACf;YACE,OAAO,IAAI,CAAC;IAChB,CAAC;AACH,CAAC;AA0BD,+EAA+E;AAC/E,uBAAuB;AACvB,+EAA+E;AAE/E,MAAM,UAAU,GAAoC;IAClD,iBAAiB;IACjB,CAAC,aAAa,CAAC,YAAY,CAAC,EAAE,eAAe,CAAC,YAAY;IAC1D,CAAC,aAAa,CAAC,SAAS,CAAC,EAAE,eAAe,CAAC,SAAS;IAEpD,UAAU;IACV,CAAC,aAAa,CAAC,cAAc,CAAC,EAAE,eAAe,CAAC,cAAc;IAC9D,CAAC,aAAa,CAAC,aAAa,CAAC,EAAE,eAAe,CAAC,aAAa;IAE5D,UAAU;IACV,CAAC,aAAa,CAAC,eAAe,CAAC,EAAE,eAAe,CAAC,eAAe;IAChE,CAAC,aAAa,CAAC,cAAc,CAAC,EAAE,eAAe,CAAC,cAAc;IAE9D,cAAc;IACd,CAAC,aAAa,CAAC,cAAc,CAAC,EAAE,eAAe,CAAC,cAAc;IAC9D,CAAC,aAAa,CAAC,eAAe,CAAC,EAAE,eAAe,CAAC,eAAe;IAChE,CAAC,aAAa,CAAC,gBAAgB,CAAC,EAAE,eAAe,CAAC,gBAAgB;IAClE,CAAC,aAAa,CAAC,gBAAgB,CAAC,EAAE,eAAe,CAAC,gBAAgB;IAClE,CAAC,aAAa,CAAC,gBAAgB,CAAC,EAAE,eAAe,CAAC,gBAAgB;IAElE,WAAW;IACX,CAAC,aAAa,CAAC,aAAa,CAAC,EAAE,eAAe,CAAC,aAAa;IAE5D,+BAA+B;IAC/B,CAAC,aAAa,CAAC,WAAW,CAAC,EAAE,eAAe,CAAC,WAAW;IACxD,CAAC,aAAa,CAAC,YAAY,CAAC,EAAE,eAAe,CAAC,YAAY;IAE1D,gBAAgB;IAChB,CAAC,aAAa,CAAC,aAAa,CAAC,EAAE,eAAe,CAAC,aAAa;IAC5D,CAAC,aAAa,CAAC,cAAc,CAAC,EAAE,eAAe,CAAC,cAAc;IAC9D,CAAC,aAAa,CAAC,YAAY,CAAC,EAAE,eAAe,CAAC,YAAY;IAC1D,CAAC,aAAa,CAAC,cAAc,CAAC,EAAE,eAAe,CAAC,cAAc;CAC/D,CAAC;AAEF,MAAM,UAAU,GAAkC;IAChD,iBAAiB;IACjB,CAAC,eAAe,CAAC,YAAY,CAAC,EAAE,aAAa,CAAC,YAAY;IAC1D,CAAC,eAAe,CAAC,SAAS,CAAC,EAAE,aAAa,CAAC,SAAS;IAEpD,UAAU;IACV,CAAC,eAAe,CAAC,cAAc,CAAC,EAAE,aAAa,CAAC,cAAc;IAC9D,CAAC,eAAe,CAAC,aAAa,CAAC,EAAE,aAAa,CAAC,aAAa;IAE5D,UAAU;IACV,CAAC,eAAe,CAAC,eAAe,CAAC,EAAE,aAAa,CAAC,eAAe;IAChE,CAAC,eAAe,CAAC,cAAc,CAAC,EAAE,aAAa,CAAC,cAAc;IAE9D,cAAc;IACd,CAAC,eAAe,CAAC,cAAc,CAAC,EAAE,aAAa,CAAC,cAAc;IAC9D,CAAC,eAAe,CAAC,eAAe,CAAC,EAAE,aAAa,CAAC,eAAe;IAChE,CAAC,eAAe,CAAC,gBAAgB,CAAC,EAAE,aAAa,CAAC,gBAAgB;IAClE,CAAC,eAAe,CAAC,gBAAgB,CAAC,EAAE,aAAa,CAAC,gBAAgB;IAClE,CAAC,eAAe,CAAC,gBAAgB,CAAC,EAAE,aAAa,CAAC,gBAAgB;IAElE,WAAW;IACX,CAAC,eAAe,CAAC,aAAa,CAAC,EAAE,aAAa,CAAC,aAAa;IAE5D,+BAA+B;IAC/B,CAAC,eAAe,CAAC,WAAW,CAAC,EAAE,aAAa,CAAC,WAAW;IACxD,CAAC,eAAe,CAAC,YAAY,CAAC,EAAE,aAAa,CAAC,YAAY;IAE1D,gBAAgB;IAChB,CAAC,eAAe,CAAC,aAAa,CAAC,EAAE,aAAa,CAAC,aAAa;IAC5D,CAAC,eAAe,CAAC,cAAc,CAAC,EAAE,aAAa,CAAC,cAAc;IAC9D,CAAC,eAAe,CAAC,YAAY,CAAC,EAAE,aAAa,CAAC,YAAY;IAC1D,CAAC,eAAe,CAAC,cAAc,CAAC,EAAE,aAAa,CAAC,cAAc;CAC/D,CAAC;AAEF,gFAAgF;AAChF,MAAM,UAAU,iBAAiB,CAAC,IAAmB;IACnD,MAAM,IAAI,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC;IAC9B,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;QACvB,MAAM,IAAI,KAAK,CAAC,sCAAsC,IAAI,EAAE,CAAC,CAAC;IAChE,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,iDAAiD;AACjD,MAAM,UAAU,mBAAmB,CAAC,IAAqB;IACvD,MAAM,IAAI,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC;IAC9B,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;QACvB,MAAM,IAAI,KAAK,CACb,8BAA8B,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CACnE,CAAC;IACJ,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,+EAA+E;AAC/E,qBAAqB;AACrB,+EAA+E;AAE/E,MAAM,UAAU,GAAG,mBAAsB,CAAC;AAE1C;;;;;;GAMG;AACH,SAAS,cAAc,CAAC,IAAe;IACrC,OAAO,CACL,IAAI,KAAK,SAAS,CAAC,aAAa;QAChC,IAAI,KAAK,SAAS,CAAC,eAAe;QAClC,IAAI,KAAK,SAAS,CAAC,IAAI;QACvB,IAAI,KAAK,SAAS,CAAC,MAAM,CAC1B,CAAC;AACJ,CAAC;AAED,qEAAqE;AACrE,SAAS,kBAAkB,CAAC,IAAe;IACzC,OAAO,IAAI,KAAK,SAAS,CAAC,IAAI,IAAI,IAAI,KAAK,SAAS,CAAC,IAAI,CAAC;AAC5D,CAAC;AAED,SAAS,iBAAiB,CAAC,SAAoB,EAAE,IAAe;IAC9D,IAAI,SAAS,GAAG,EAAE,IAAI,SAAS,GAAG,UAAU,EAAE,CAAC;QAC7C,MAAM,IAAI,SAAS,CACjB,aAAa,CAAC,cAAc,EAC5B,kCAAkC,SAAS,EAAE,CAC9C,CAAC;IACJ,CAAC;IACD,IAAI,cAAc,CAAC,IAAI,CAAC,IAAI,SAAS,KAAK,EAAE,EAAE,CAAC;QAC7C,MAAM,IAAI,SAAS,CACjB,aAAa,CAAC,cAAc,EAC5B,8BAA8B,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,8BAA8B,CAC/F,CAAC;IACJ,CAAC;IACD,IAAI,kBAAkB,CAAC,IAAI,CAAC,IAAI,SAAS,KAAK,EAAE,EAAE,CAAC;QACjD,MAAM,IAAI,SAAS,CACjB,aAAa,CAAC,cAAc,EAC5B,kCAAkC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,yBAAyB,CAC9F,CAAC;IACJ,CAAC;AACH,CAAC;AAED,+EAA+E;AAC/E,oCAAoC;AACpC,+EAA+E;AAE/E;;;;GAIG;AACH,MAAM,UAAU,WAAW,CACzB,IAAe,EACf,SAAoB,EACpB,OAAmB;IAEnB,iBAAiB,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;IAEnC,IAAI,OAAO,CAAC,MAAM,GAAG,gBAAgB,EAAE,CAAC;QACtC,MAAM,IAAI,SAAS,CACjB,aAAa,CAAC,eAAe,EAC7B,gBAAgB,OAAO,CAAC,MAAM,oBAAoB,gBAAgB,EAAE,CACrE,CAAC;IACJ,CAAC;IAED,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,iBAAiB,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACjE,MAAM,IAAI,GAAG,IAAI,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAExC,KAAK,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IAChB,IAAI,CAAC,SAAS,CAAC,CAAC,EAAE,OAAO,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IACzC,IAAI,CAAC,YAAY,CAAC,CAAC,EAAE,SAAS,EAAE,KAAK,CAAC,CAAC;IACvC,KAAK,CAAC,GAAG,CAAC,OAAO,EAAE,iBAAiB,CAAC,CAAC;IAEtC,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,eAAe,CAAC,IAAgB;IAC9C,IAAI,IAAI,CAAC,MAAM,GAAG,iBAAiB,EAAE,CAAC;QACpC,MAAM,IAAI,SAAS,CACjB,aAAa,CAAC,cAAc,EAC5B,oBAAoB,IAAI,CAAC,MAAM,MAAM,iBAAiB,EAAE,CACzD,CAAC;IACJ,CAAC;IAED,MAAM,IAAI,GAAG,IAAI,QAAQ,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;IACzE,MAAM,IAAI,GAAG,IAAI,CAAC,CAAC,CAAc,CAAC;IAElC,QAAQ,IAAI,EAAE,CAAC;QACb,KAAK,SAAS,CAAC,aAAa,CAAC;QAC7B,KAAK,SAAS,CAAC,eAAe,CAAC;QAC/B,KAAK,SAAS,CAAC,IAAI,CAAC;QACpB,KAAK,SAAS,CAAC,MAAM,CAAC;QACtB,KAAK,SAAS,CAAC,IAAI,CAAC;QACpB,KAAK,SAAS,CAAC,IAAI,CAAC;QACpB,KAAK,SAAS,CAAC,OAAO;YACpB,MAAM;QACR;YACE,MAAM,IAAI,SAAS,CACjB,aAAa,CAAC,gBAAgB,EAC9B,yBAA0B,IAAe,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAC1E,CAAC;IACN,CAAC;IAED,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;IACxC,MAAM,SAAS,GAAG,IAAI,CAAC,YAAY,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;IAE9C,IAAI,MAAM,GAAG,gBAAgB,EAAE,CAAC;QAC9B,MAAM,IAAI,SAAS,CACjB,aAAa,CAAC,eAAe,EAC7B,kBAAkB,MAAM,oBAAoB,gBAAgB,EAAE,CAC/D,CAAC;IACJ,CAAC;IAED,0EAA0E;IAC1E,IAAI,cAAc,CAAC,IAAI,CAAC,IAAI,SAAS,KAAK,EAAE,EAAE,CAAC;QAC7C,MAAM,IAAI,SAAS,CACjB,aAAa,CAAC,cAAc,EAC5B,8BAA8B,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,8BAA8B,CAC/F,CAAC;IACJ,CAAC;IAED,yEAAyE;IACzE,IAAI,kBAAkB,CAAC,IAAI,CAAC,IAAI,SAAS,KAAK,EAAE,EAAE,CAAC;QACjD,MAAM,IAAI,SAAS,CACjB,aAAa,CAAC,cAAc,EAC5B,kCAAkC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,yBAAyB,CAC9F,CAAC;IACJ,CAAC;IAED,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;AACrC,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,WAAW,CAAC,IAAgB;IAC1C,MAAM,MAAM,GAAG,eAAe,CAAC,IAAI,CAAC,CAAC;IACrC,MAAM,YAAY,GAAG,iBAAiB,GAAG,MAAM,CAAC,MAAM,CAAC;IAEvD,IAAI,IAAI,CAAC,MAAM,GAAG,YAAY,EAAE,CAAC;QAC/B,MAAM,IAAI,SAAS,CACjB,aAAa,CAAC,cAAc,EAC5B,wBAAwB,IAAI,CAAC,MAAM,cAAc,YAAY,EAAE,CAChE,CAAC;IACJ,CAAC;IAED,IAAI,IAAI,CAAC,MAAM,GAAG,YAAY,EAAE,CAAC;QAC/B,MAAM,IAAI,SAAS,CACjB,aAAa,CAAC,cAAc,EAC5B,aAAa,IAAI,CAAC,MAAM,GAAG,YAAY,iBAAiB,CACzD,CAAC;IACJ,CAAC;IAED,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,iBAAiB,EAAE,YAAY,CAAC,CAAC;IAC/D,OAAO,EAAE,GAAG,MAAM,EAAE,OAAO,EAAE,CAAC;AAChC,CAAC;AAED,+EAA+E;AAC/E,qDAAqD;AACrD,+EAA+E;AAE/E;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CACjC,SAAoB,EACpB,IAAmB;IAEnB,IAAI,IAAI,CAAC,aAAa,CAAC,MAAM,KAAK,2BAA2B,EAAE,CAAC;QAC9D,MAAM,IAAI,SAAS,CACjB,aAAa,CAAC,cAAc,EAC5B,yBAAyB,2BAA2B,eAAe,IAAI,CAAC,aAAa,CAAC,MAAM,EAAE,CAC/F,CAAC;IACJ,CAAC;IACD,OAAO,WAAW,CAAC,SAAS,CAAC,aAAa,EAAE,SAAS,EAAE,IAAI,CAAC,aAAa,CAAC,CAAC;AAC7E,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,qBAAqB,CACnC,SAAoB,EACpB,MAAuB;IAEvB,IAAI,MAAM,CAAC,iBAAiB,CAAC,MAAM,KAAK,yBAAyB,EAAE,CAAC;QAClE,MAAM,IAAI,SAAS,CACjB,aAAa,CAAC,cAAc,EAC5B,6BAA6B,yBAAyB,eAAe,MAAM,CAAC,iBAAiB,CAAC,MAAM,EAAE,CACvG,CAAC;IACJ,CAAC;IACD,IAAI,MAAM,CAAC,eAAe,CAAC,MAAM,KAAK,wBAAwB,EAAE,CAAC;QAC/D,MAAM,IAAI,SAAS,CACjB,aAAa,CAAC,cAAc,EAC5B,2BAA2B,wBAAwB,eAAe,MAAM,CAAC,eAAe,CAAC,MAAM,EAAE,CAClG,CAAC;IACJ,CAAC;IACD,IAAI,MAAM,CAAC,SAAS,CAAC,MAAM,KAAK,wBAAwB,EAAE,CAAC;QACzD,MAAM,IAAI,SAAS,CACjB,aAAa,CAAC,cAAc,EAC5B,qBAAqB,wBAAwB,eAAe,MAAM,CAAC,SAAS,CAAC,MAAM,EAAE,CACtF,CAAC;IACJ,CAAC;IACD,MAAM,OAAO,GAAG,IAAI,UAAU,CAAC,6BAA6B,CAAC,CAAC;IAC9D,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,iBAAiB,EAAE,CAAC,CAAC,CAAC;IACzC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,eAAe,EAAE,yBAAyB,CAAC,CAAC;IAC/D,OAAO,CAAC,GAAG,CACT,MAAM,CAAC,SAAS,EAChB,yBAAyB,GAAG,wBAAwB,CACrD,CAAC;IACF,OAAO,WAAW,CAAC,SAAS,CAAC,eAAe,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;AACpE,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,UAAU,CACxB,SAAoB,EACpB,OAAyB;IAEzB,IAAI,OAAO,CAAC,IAAI,CAAC,MAAM,GAAG,0BAA0B,EAAE,CAAC;QACrD,MAAM,IAAI,SAAS,CACjB,aAAa,CAAC,cAAc,EAC5B,iCAAiC,0BAA0B,eAAe,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,CAChG,CAAC;IACJ,CAAC;IACD,OAAO,WAAW,CAAC,SAAS,CAAC,IAAI,EAAE,SAAS,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;AAC9D,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,YAAY,CAC1B,SAAoB,EACpB,MAAkB,EAClB,SAAuB,YAAY,CAAC,IAAI;IAExC,MAAM,OAAO,GAAG,IAAI,UAAU,CAAC,mBAAmB,CAAC,CAAC;IACpD,OAAO,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC;IACpB,OAAO,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC;IACpB,OAAO,WAAW,CAAC,SAAS,CAAC,MAAM,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;AAC3D,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,UAAU,CACxB,UAAsB,IAAI,UAAU,CAAC,CAAC,CAAC;IAEvC,IAAI,OAAO,CAAC,MAAM,GAAG,qBAAqB,EAAE,CAAC;QAC3C,MAAM,IAAI,SAAS,CACjB,aAAa,CAAC,eAAe,EAC7B,0BAA0B,qBAAqB,eAAe,OAAO,CAAC,MAAM,EAAE,CAC/E,CAAC;IACJ,CAAC;IACD,OAAO,WAAW,CAAC,SAAS,CAAC,IAAI,EAAE,EAAE,EAAE,OAAO,CAAC,CAAC;AAClD,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,UAAU,CACxB,UAAsB,IAAI,UAAU,CAAC,CAAC,CAAC;IAEvC,IAAI,OAAO,CAAC,MAAM,GAAG,qBAAqB,EAAE,CAAC;QAC3C,MAAM,IAAI,SAAS,CACjB,aAAa,CAAC,eAAe,EAC7B,0BAA0B,qBAAqB,eAAe,OAAO,CAAC,MAAM,EAAE,CAC/E,CAAC;IACJ,CAAC;IACD,OAAO,WAAW,CAAC,SAAS,CAAC,IAAI,EAAE,EAAE,EAAE,OAAO,CAAC,CAAC;AAClD,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,aAAa,CAC3B,SAAoB,EACpB,IAAqB,EACrB,OAAgB;IAEhB,MAAM,QAAQ,GAAG,OAAO,CAAC,CAAC,CAAC,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAC9D,MAAM,OAAO,GAAG,IAAI,UAAU,CAAC,CAAC,GAAG,CAAC,QAAQ,EAAE,MAAM,IAAI,CAAC,CAAC,CAAC,CAAC;IAC5D,IAAI,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC;IACvD,IAAI,QAAQ;QAAE,OAAO,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;IACvC,OAAO,WAAW,CAAC,SAAS,CAAC,OAAO,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;AAC5D,CAAC;AAED,+EAA+E;AAC/E,oDAAoD;AACpD,+EAA+E;AAE/E;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CAAC,KAAY;IAC9C,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS,CAAC,aAAa,EAAE,CAAC;QAC3C,MAAM,IAAI,SAAS,CACjB,aAAa,CAAC,gBAAgB,EAC9B,wCAAwC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CACnF,CAAC;IACJ,CAAC;IACD,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,KAAK,2BAA2B,EAAE,CAAC;QACzD,MAAM,IAAI,SAAS,CACjB,aAAa,CAAC,cAAc,EAC5B,iCAAiC,2BAA2B,eAAe,KAAK,CAAC,OAAO,CAAC,MAAM,EAAE,CAClG,CAAC;IACJ,CAAC;IACD,OAAO;QACL,IAAI,EAAE,gBAAgB;QACtB,aAAa,EAAE,KAAK,CAAC,OAAO,CAAC,KAAK,EAAE;KACrC,CAAC;AACJ,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,qBAAqB,CAAC,KAAY;IAChD,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS,CAAC,eAAe,EAAE,CAAC;QAC7C,MAAM,IAAI,SAAS,CACjB,aAAa,CAAC,gBAAgB,EAC9B,0CAA0C,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CACrF,CAAC;IACJ,CAAC;IACD,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,KAAK,6BAA6B,EAAE,CAAC;QAC3D,MAAM,IAAI,SAAS,CACjB,aAAa,CAAC,cAAc,EAC5B,mCAAmC,6BAA6B,eAAe,KAAK,CAAC,OAAO,CAAC,MAAM,EAAE,CACtG,CAAC;IACJ,CAAC;IACD,MAAM,MAAM,GAAG,yBAAyB,GAAG,wBAAwB,CAAC;IACpE,OAAO;QACL,IAAI,EAAE,kBAAkB;QACxB,iBAAiB,EAAE,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,yBAAyB,CAAC;QACpE,eAAe,EAAE,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,yBAAyB,EAAE,MAAM,CAAC;QACvE,SAAS,EAAE,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,EAAE,MAAM,GAAG,wBAAwB,CAAC;KAC1E,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,UAAU,CAAC,KAAY;IACrC,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS,CAAC,IAAI,EAAE,CAAC;QAClC,MAAM,IAAI,SAAS,CACjB,aAAa,CAAC,gBAAgB,EAC9B,+BAA+B,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAC1E,CAAC;IACJ,CAAC;IACD,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,GAAG,0BAA0B,EAAE,CAAC;QACtD,MAAM,IAAI,SAAS,CACjB,aAAa,CAAC,cAAc,EAC5B,iCAAiC,0BAA0B,eAAe,KAAK,CAAC,OAAO,CAAC,MAAM,EAAE,CACjG,CAAC;IACJ,CAAC;IACD,MAAM,GAAG,GAAG,eAAe,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAC3C,OAAO;QACL,IAAI,EAAE,WAAW;QACjB,GAAG;QACH,IAAI,EAAE,KAAK,CAAC,OAAO,CAAC,KAAK,EAAE;KAC5B,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,YAAY,CAAC,KAAY;IACvC,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS,CAAC,MAAM,EAAE,CAAC;QACpC,MAAM,IAAI,SAAS,CACjB,aAAa,CAAC,gBAAgB,EAC9B,iCAAiC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAC5E,CAAC;IACJ,CAAC;IACD,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,KAAK,mBAAmB,EAAE,CAAC;QACjD,MAAM,IAAI,SAAS,CACjB,aAAa,CAAC,cAAc,EAC5B,0BAA0B,mBAAmB,eAAe,KAAK,CAAC,OAAO,CAAC,MAAM,EAAE,CACnF,CAAC;IACJ,CAAC;IACD,iEAAiE;IACjE,MAAM,MAAM,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC,CAAE,CAAC;IACjC,MAAM,MAAM,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC,CAAE,CAAC;IACjC,IAAI,MAAM,KAAK,UAAU,CAAC,KAAK,IAAI,MAAM,KAAK,UAAU,CAAC,KAAK,EAAE,CAAC;QAC/D,MAAM,IAAI,SAAS,CACjB,aAAa,CAAC,cAAc,EAC5B,0BAA0B,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CACjE,CAAC;IACJ,CAAC;IACD,QAAQ,MAAM,EAAE,CAAC;QACf,KAAK,YAAY,CAAC,IAAI,CAAC;QACvB,KAAK,YAAY,CAAC,SAAS,CAAC;QAC5B,KAAK,YAAY,CAAC,QAAQ,CAAC;QAC3B,KAAK,YAAY,CAAC,MAAM,CAAC;QACzB,KAAK,YAAY,CAAC,KAAK;YACrB,MAAM;QACR;YACE,MAAM,IAAI,SAAS,CACjB,aAAa,CAAC,cAAc,EAC5B,4BAA4B,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CACnE,CAAC;IACN,CAAC;IACD,OAAO,EAAE,MAAM,EAAE,MAAoB,EAAE,MAAM,EAAE,MAAsB,EAAE,CAAC;AAC1E,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,aAAa,CAAC,KAAY;IACxC,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS,CAAC,OAAO,EAAE,CAAC;QACrC,MAAM,IAAI,SAAS,CACjB,aAAa,CAAC,gBAAgB,EAC9B,kCAAkC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAC7E,CAAC;IACJ,CAAC;IACD,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,GAAG,wBAAwB,EAAE,CAAC;QACpD,MAAM,IAAI,SAAS,CACjB,aAAa,CAAC,cAAc,EAC5B,oCAAoC,wBAAwB,eAAe,KAAK,CAAC,OAAO,CAAC,MAAM,EAAE,CAClG,CAAC;IACJ,CAAC;IACD,MAAM,IAAI,GAAG,IAAI,QAAQ,CACvB,KAAK,CAAC,OAAO,CAAC,MAAM,EACpB,KAAK,CAAC,OAAO,CAAC,UAAU,EACxB,KAAK,CAAC,OAAO,CAAC,UAAU,CACzB,CAAC;IACF,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;IACzC,IAAI,UAAU,CAAC,OAAO,CAAC,KAAK,SAAS,EAAE,CAAC;QACtC,MAAM,IAAI,SAAS,CACjB,aAAa,CAAC,cAAc,EAC5B,2BAA2B,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CACnE,CAAC;IACJ,CAAC;IACD,MAAM,IAAI,GAAG,OAA0B,CAAC;IACxC,kEAAkE;IAClE,MAAM,OAAO,GAAG,WAAW,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;IAC9D,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;AAC3B,CAAC;AAED,+EAA+E;AAC/E,0BAA0B;AAC1B,+EAA+E;AAE/E;;;;;;;;;;;;;;;GAeG;AACH,MAAM,OAAO,YAAY;IACf,MAAM,GAAe,IAAI,UAAU,CAAC,CAAC,CAAC,CAAC;IAE/C;;OAEG;IACH,CAAC,IAAI,CAAC,IAAgB;QACpB,mBAAmB;QACnB,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC7B,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;QACrB,CAAC;aAAM,CAAC;YACN,MAAM,QAAQ,GAAG,IAAI,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC;YAClE,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;YAC7B,QAAQ,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;YACvC,IAAI,CAAC,MAAM,GAAG,QAAQ,CAAC;QACzB,CAAC;QAED,wBAAwB;QACxB,OAAO,IAAI,CAAC,MAAM,CAAC,MAAM,IAAI,iBAAiB,EAAE,CAAC;YAC/C,MAAM,MAAM,GAAG,eAAe,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YAC5C,MAAM,SAAS,GAAG,iBAAiB,GAAG,MAAM,CAAC,MAAM,CAAC;YAEpD,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,GAAG,SAAS,EAAE,CAAC;gBACnC,MAAM,CAAC,uCAAuC;YAChD,CAAC;YAED,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,iBAAiB,EAAE,SAAS,CAAC,CAAC;YACnE,MAAM,EAAE,GAAG,MAAM,EAAE,OAAO,EAAE,CAAC;YAC7B,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;QAChD,CAAC;IACH,CAAC;IAED,wDAAwD;IACxD,KAAK;QACH,IAAI,CAAC,MAAM,GAAG,IAAI,UAAU,CAAC,CAAC,CAAC,CAAC;IAClC,CAAC;IAED,yCAAyC;IACzC,IAAI,aAAa;QACf,OAAO,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC;IAC5B,CAAC;CACF"}
@@ -0,0 +1,39 @@
1
+ import type { DaemonId, EphemeralKeyPair, HandshakeAccept, HandshakeInit, IdentityKeyPair, SessionKeys } from "./types.js";
2
+ /**
3
+ * Create a handshake init message (client side).
4
+ *
5
+ * Generates an ephemeral X25519 keypair for this session.
6
+ */
7
+ export declare function createHandshakeInit(): {
8
+ message: HandshakeInit;
9
+ ephemeralKeyPair: EphemeralKeyPair;
10
+ };
11
+ /**
12
+ * Process handshake init and create accept message (daemon side).
13
+ *
14
+ * 1. Generate ephemeral X25519 keypair
15
+ * 2. Sign ephemeral public key with identity key (context-bound)
16
+ * 3. Derive session keys
17
+ *
18
+ * NOTE: Callers MUST enforce a 30-second handshake timeout per SBRP §1.4.
19
+ * This function does not track time; timeout enforcement is a transport concern.
20
+ */
21
+ export declare function processHandshakeInit(init: HandshakeInit, daemonId: DaemonId, identityKeyPair: IdentityKeyPair): {
22
+ message: HandshakeAccept;
23
+ sessionKeys: SessionKeys;
24
+ };
25
+ /**
26
+ * Process handshake accept message (client side).
27
+ *
28
+ * 1. Verify signature using PINNED identity key (TOFU)
29
+ * 2. Derive session keys using same transcript hash as daemon
30
+ *
31
+ * NOTE: Callers MUST enforce a 30-second handshake timeout per SBRP §1.4.
32
+ * This function does not track time; timeout enforcement is a transport concern.
33
+ *
34
+ * @param ephemeralKeyPair The privateKey is zeroized in-place after key derivation.
35
+ * @throws {SbrpError} IdentityKeyChanged if advertised key doesn't match pinned key
36
+ * @throws {SbrpError} HandshakeFailed if signature verification fails
37
+ */
38
+ export declare function processHandshakeAccept(accept: HandshakeAccept, daemonId: DaemonId, pinnedIdentityPublicKey: Uint8Array, ephemeralKeyPair: EphemeralKeyPair): SessionKeys;
39
+ //# sourceMappingURL=handshake.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handshake.d.ts","sourceRoot":"","sources":["../src/handshake.ts"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EACV,QAAQ,EACR,gBAAgB,EAChB,eAAe,EACf,aAAa,EACb,eAAe,EACf,WAAW,EACZ,MAAM,YAAY,CAAC;AAGpB;;;;GAIG;AACH,wBAAgB,mBAAmB,IAAI;IACrC,OAAO,EAAE,aAAa,CAAC;IACvB,gBAAgB,EAAE,gBAAgB,CAAC;CACpC,CASA;AAED;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,aAAa,EACnB,QAAQ,EAAE,QAAQ,EAClB,eAAe,EAAE,eAAe,GAC/B;IAAE,OAAO,EAAE,eAAe,CAAC;IAAC,WAAW,EAAE,WAAW,CAAA;CAAE,CAqCxD;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,eAAe,EACvB,QAAQ,EAAE,QAAQ,EAClB,uBAAuB,EAAE,UAAU,EACnC,gBAAgB,EAAE,gBAAgB,GACjC,WAAW,CAoDb"}