@xiotbox/e2e-channel 0.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,319 @@
1
+ /**
2
+ * XiotBox E2E Secure Channel — generic end-to-end encrypted channel.
3
+ *
4
+ * Sits between an application protocol and the XiotBox Gateway relay: any
5
+ * channel that relays opaque JSON frames (DSH web tunnel, agent RPC, PTY
6
+ * streams, …) can wrap them with this layer and get:
7
+ *
8
+ * - confidentiality — the Gateway only ever sees ciphertext frames
9
+ * - integrity — AES-256-GCM AEAD rejects any modified frame
10
+ * - anti-replay — per-direction sequence counters bound in the AAD
11
+ * - forward secrecy — fresh ephemeral X25519 key pair per channel
12
+ * - peer authentication — Ed25519 identity signatures over the handshake
13
+ * transcript, pinned TOFU by the application via `peerIdentityPolicy`
14
+ *
15
+ * Wire format (all frames are JSON, base64-encoded by the relay):
16
+ * handshake: {t:'hello', v:1, pub, kid, nonce}
17
+ * {t:'hello_ack', v:1, pub, kid, nonce}
18
+ * {t:'ident', v:1, id, fp, sig} (identity claim, signed)
19
+ * {t:'ident_ack', v:1, id, fp, sig}
20
+ * data: {t:'enc', v:1, iv, tag, ct} (ct = encrypted app frame)
21
+ *
22
+ * Application keepalives (e.g. the tunnel's ping/pong) stay outside this
23
+ * layer and are relayed plaintext; they carry no sensitive content.
24
+ *
25
+ * See `docs/e2e-secure-channel.md` for the full protocol specification.
26
+ */
27
+ import { randomBytes } from 'node:crypto';
28
+ import { b64d, b64e, buildFrameAad, buildHandshakeTranscript, computeIdentityFingerprint, deriveSessionKey, normalizeEd25519Pub, openFrame, sealFrame, signMessage, verifyMessage, x25519Keypair, x25519Shared, } from './crypto.js';
29
+ const DEFAULT_HANDSHAKE_TIMEOUT_MS = 10_000;
30
+ /**
31
+ * One E2E session bound to one relay channel. Created per channel; the
32
+ * session key lives only in memory and is dropped on close.
33
+ */
34
+ export class SecureChannel {
35
+ opts;
36
+ state = 'init';
37
+ eph;
38
+ myNonce;
39
+ myKid;
40
+ peerEphPubB64 = '';
41
+ peerKid = '';
42
+ peerNonce = '';
43
+ peerIdentityFp = '';
44
+ sessionKey;
45
+ sendSeq = 0;
46
+ recvSeq = 0;
47
+ timeout;
48
+ closedReason = '';
49
+ constructor(options) {
50
+ this.opts = options;
51
+ this.eph = x25519Keypair();
52
+ this.myNonce = randomBytes(16).toString('hex');
53
+ this.myKid = options.keypair.keyId;
54
+ if (options.role === 'initiator')
55
+ this.state = 'hello_sent';
56
+ }
57
+ get ready() {
58
+ return this.state === 'ready';
59
+ }
60
+ get channelId() {
61
+ return this.opts.channelId;
62
+ }
63
+ get direction() {
64
+ return this.opts.role === 'initiator' ? 'c2p' : 'p2c';
65
+ }
66
+ /** Initiate (initiator) or arm the handshake timeout (responder). */
67
+ start() {
68
+ if (this.opts.role === 'initiator' && this.state === 'hello_sent') {
69
+ const ok = this.opts.send({
70
+ t: 'hello',
71
+ v: 1,
72
+ pub: b64e(this.eph.pub),
73
+ kid: this.myKid,
74
+ nonce: this.myNonce,
75
+ });
76
+ if (!ok) {
77
+ this.close('send_failed');
78
+ return;
79
+ }
80
+ }
81
+ this.armTimeout();
82
+ }
83
+ /**
84
+ * Feed an inbound relay frame. Returns:
85
+ * { status: 'ok', app?: Record } — app frame decrypted (when ready)
86
+ * { status: 'ok' } — handshake/control frame consumed
87
+ * { status: 'closed', reason } — protocol/security failure
88
+ */
89
+ receive(frame) {
90
+ if (this.state === 'closed')
91
+ return { status: 'closed', reason: this.closedReason || 'closed' };
92
+ const t = String(frame.t ?? '');
93
+ if (t === 'enc') {
94
+ if (!this.ready)
95
+ return { status: 'closed', reason: 'enc_before_handshake' };
96
+ return this.handleEnc(frame);
97
+ }
98
+ switch (t) {
99
+ case 'hello':
100
+ return this.handleHello(frame);
101
+ case 'hello_ack':
102
+ return this.handleHelloAck(frame);
103
+ case 'ident':
104
+ return this.handleIdent(frame);
105
+ case 'ident_ack':
106
+ return this.handleIdentAck(frame);
107
+ default:
108
+ return { status: 'closed', reason: 'unexpected_frame' };
109
+ }
110
+ }
111
+ /** Wrap an application frame into an encrypted relay frame. */
112
+ wrap(app) {
113
+ if (!this.ready || this.sessionKey === undefined) {
114
+ throw new Error('secure_channel_not_ready');
115
+ }
116
+ this.sendSeq += 1;
117
+ const aad = buildFrameAad(this.channelId, this.direction, this.sendSeq);
118
+ const sealed = sealFrame(this.sessionKey, Buffer.from(JSON.stringify(app), 'utf8'), aad);
119
+ return { t: 'enc', v: 1, ...sealed };
120
+ }
121
+ /** Close the channel; clears the session key and timers. */
122
+ close(reason = 'closed') {
123
+ if (this.state === 'closed')
124
+ return;
125
+ this.state = 'closed';
126
+ this.closedReason = reason;
127
+ this.clearTimeout();
128
+ this.sessionKey = undefined;
129
+ this.opts.onClose?.(reason);
130
+ }
131
+ // -------------------------------------------------------------------------
132
+ // Handshake
133
+ // -------------------------------------------------------------------------
134
+ handleHello(frame) {
135
+ if (this.opts.role !== 'responder' || this.state !== 'init') {
136
+ return { status: 'closed', reason: 'protocol_error' };
137
+ }
138
+ const pub = b64d(frame.pub);
139
+ const kid = String(frame.kid ?? '');
140
+ const nonce = String(frame.nonce ?? '');
141
+ if (pub === undefined || pub.length !== 32 || kid.length === 0 || nonce.length === 0) {
142
+ return { status: 'closed', reason: 'invalid_hello' };
143
+ }
144
+ if (this.opts.peerKid && kid !== this.opts.peerKid) {
145
+ this.opts.log?.('warn', `[E2E] peer kid mismatch channel=${this.channelId} got=${kid.slice(0, 8)}… expected=${this.opts.peerKid.slice(0, 8)}…`);
146
+ return { status: 'closed', reason: 'e2e_peer_kid_mismatch' };
147
+ }
148
+ this.peerEphPubB64 = frame.pub;
149
+ this.peerKid = kid;
150
+ this.peerNonce = nonce;
151
+ this.sessionKey = deriveSessionKey(x25519Shared(this.eph.priv, pub), this.channelId);
152
+ // Advance the state BEFORE sending: the synchronous send() re-enters the
153
+ // peer, which may immediately reply with its ident frame.
154
+ this.state = 'hello_received';
155
+ const ok = this.opts.send({
156
+ t: 'hello_ack',
157
+ v: 1,
158
+ pub: b64e(this.eph.pub),
159
+ kid: this.myKid,
160
+ nonce: this.myNonce,
161
+ });
162
+ if (!ok)
163
+ return { status: 'closed', reason: 'send_failed' };
164
+ return { status: 'ok' };
165
+ }
166
+ handleHelloAck(frame) {
167
+ if (this.opts.role !== 'initiator' || this.state !== 'hello_sent') {
168
+ return { status: 'closed', reason: 'protocol_error' };
169
+ }
170
+ const pub = b64d(frame.pub);
171
+ const kid = String(frame.kid ?? '');
172
+ const nonce = String(frame.nonce ?? '');
173
+ if (pub === undefined || pub.length !== 32 || kid.length === 0 || nonce.length === 0) {
174
+ return { status: 'closed', reason: 'invalid_hello_ack' };
175
+ }
176
+ if (this.opts.peerKid && kid !== this.opts.peerKid) {
177
+ this.opts.log?.('warn', `[E2E] peer kid mismatch channel=${this.channelId} got=${kid.slice(0, 8)}… expected=${this.opts.peerKid.slice(0, 8)}…`);
178
+ return { status: 'closed', reason: 'e2e_peer_kid_mismatch' };
179
+ }
180
+ this.peerEphPubB64 = frame.pub;
181
+ this.peerKid = kid;
182
+ this.peerNonce = nonce;
183
+ this.sessionKey = deriveSessionKey(x25519Shared(this.eph.priv, pub), this.channelId);
184
+ this.state = 'ident_sent';
185
+ // Identity claim over the full handshake transcript.
186
+ const transcript = this.buildTranscript('c2p');
187
+ const sig = signMessage(this.opts.identity.privKey, transcript);
188
+ const ok = this.opts.send({
189
+ t: 'ident',
190
+ v: 1,
191
+ id: b64e(this.opts.identity.pubRaw),
192
+ fp: this.opts.identity.fingerprint,
193
+ sig: b64e(sig),
194
+ });
195
+ if (!ok)
196
+ return { status: 'closed', reason: 'send_failed' };
197
+ return { status: 'ok' };
198
+ }
199
+ handleIdent(frame) {
200
+ if (this.opts.role !== 'responder' || this.state !== 'hello_received') {
201
+ return { status: 'closed', reason: 'protocol_error' };
202
+ }
203
+ const result = this.verifyIdentityClaim(frame, 'c2p');
204
+ if (!result.ok) {
205
+ this.opts.log?.('warn', `[E2E] peer identity rejected channel=${this.channelId} reason=${result.reason ?? 'invalid'}`);
206
+ return { status: 'closed', reason: result.reason ?? 'e2e_peer_identity_rejected' };
207
+ }
208
+ this.peerIdentityFp = String(frame.fp ?? '');
209
+ // Reply with our identity claim.
210
+ const transcript = this.buildTranscript('p2c');
211
+ const sig = signMessage(this.opts.identity.privKey, transcript);
212
+ const ok = this.opts.send({
213
+ t: 'ident_ack',
214
+ v: 1,
215
+ id: b64e(this.opts.identity.pubRaw),
216
+ fp: this.opts.identity.fingerprint,
217
+ sig: b64e(sig),
218
+ });
219
+ if (!ok)
220
+ return { status: 'closed', reason: 'send_failed' };
221
+ this.clearTimeout();
222
+ this.state = 'ready';
223
+ this.opts.log?.('info', `[E2E] channel ready channel=${this.channelId} peerFp=${this.peerIdentityFp.slice(0, 12)}…`);
224
+ return { status: 'ok' };
225
+ }
226
+ handleIdentAck(frame) {
227
+ if (this.opts.role !== 'initiator' || this.state !== 'ident_sent') {
228
+ return { status: 'closed', reason: 'protocol_error' };
229
+ }
230
+ const result = this.verifyIdentityClaim(frame, 'p2c');
231
+ if (!result.ok) {
232
+ this.opts.log?.('warn', `[E2E] peer identity rejected channel=${this.channelId} reason=${result.reason ?? 'invalid'}`);
233
+ return { status: 'closed', reason: result.reason ?? 'e2e_peer_identity_rejected' };
234
+ }
235
+ this.peerIdentityFp = String(frame.fp ?? '');
236
+ this.clearTimeout();
237
+ this.state = 'ready';
238
+ this.opts.log?.('info', `[E2E] channel ready channel=${this.channelId} peerFp=${this.peerIdentityFp.slice(0, 12)}…`);
239
+ return { status: 'ok' };
240
+ }
241
+ verifyIdentityClaim(frame, dir) {
242
+ const idDecoded = b64d(frame.id);
243
+ const sig = b64d(frame.sig);
244
+ if (idDecoded === undefined || sig === undefined) {
245
+ return { ok: false, reason: 'invalid_identity_claim' };
246
+ }
247
+ const idRaw = normalizeEd25519Pub(idDecoded);
248
+ if (idRaw === undefined) {
249
+ return { ok: false, reason: 'invalid_identity_claim' };
250
+ }
251
+ const transcript = this.buildTranscript(dir);
252
+ if (!verifyMessage(idRaw, transcript, sig)) {
253
+ return { ok: false, reason: 'e2e_identity_sig_invalid' };
254
+ }
255
+ // Advisory fingerprint check: SHA256 of the SPKI DER encoding (idRaw is
256
+ // always the raw 32 bytes here; canonical Ed25519 SPKI DER = prefix + raw).
257
+ const der = Buffer.concat([Buffer.from('302a300506032b6570032100', 'hex'), idRaw]);
258
+ const computedFp = computeIdentityFingerprint(der);
259
+ const claimedFp = String(frame.fp ?? '').trim().toLowerCase();
260
+ if (claimedFp && claimedFp !== computedFp) {
261
+ return { ok: false, reason: 'e2e_identity_fp_mismatch' };
262
+ }
263
+ if (this.opts.peerIdentityPolicy) {
264
+ return this.opts.peerIdentityPolicy(idRaw, claimedFp || computedFp, transcript, sig);
265
+ }
266
+ return { ok: true };
267
+ }
268
+ buildTranscript(dir) {
269
+ // Canonical c_/p_ fields: c_ = client/initiator side, p_ = plugin/responder
270
+ // side. Both peers must produce the identical string for the same dir.
271
+ const isInitiator = this.opts.role === 'initiator';
272
+ return buildHandshakeTranscript({
273
+ channelId: this.channelId,
274
+ dir,
275
+ cEphPubB64: isInitiator ? b64e(this.eph.pub) : this.peerEphPubB64,
276
+ pEphPubB64: isInitiator ? this.peerEphPubB64 : b64e(this.eph.pub),
277
+ cKid: isInitiator ? this.myKid : this.peerKid,
278
+ pKid: isInitiator ? this.peerKid : this.myKid,
279
+ cNonce: isInitiator ? this.myNonce : this.peerNonce,
280
+ pNonce: isInitiator ? this.peerNonce : this.myNonce,
281
+ });
282
+ }
283
+ handleEnc(frame) {
284
+ if (this.sessionKey === undefined)
285
+ return { status: 'closed', reason: 'no_session_key' };
286
+ this.recvSeq += 1;
287
+ const inboundDir = this.direction === 'c2p' ? 'p2c' : 'c2p';
288
+ const aad = buildFrameAad(this.channelId, inboundDir, this.recvSeq);
289
+ const plaintext = openFrame(this.sessionKey, frame, aad);
290
+ if (plaintext === undefined) {
291
+ this.opts.log?.('warn', `[E2E] frame auth failed channel=${this.channelId} seq=${this.recvSeq}`);
292
+ return { status: 'closed', reason: 'e2e_frame_auth_failed' };
293
+ }
294
+ try {
295
+ const app = JSON.parse(plaintext.toString('utf8'));
296
+ return { status: 'ok', app };
297
+ }
298
+ catch {
299
+ return { status: 'closed', reason: 'invalid_app_frame' };
300
+ }
301
+ }
302
+ armTimeout() {
303
+ this.clearTimeout();
304
+ const timeoutMs = this.opts.handshakeTimeoutMs ?? DEFAULT_HANDSHAKE_TIMEOUT_MS;
305
+ this.timeout = setTimeout(() => {
306
+ if (this.state !== 'ready' && this.state !== 'closed') {
307
+ this.opts.log?.('warn', `[E2E] handshake timeout channel=${this.channelId}`);
308
+ this.close('e2e_handshake_timeout');
309
+ }
310
+ }, timeoutMs);
311
+ }
312
+ clearTimeout() {
313
+ if (this.timeout !== undefined) {
314
+ clearTimeout(this.timeout);
315
+ this.timeout = undefined;
316
+ }
317
+ }
318
+ }
319
+ //# sourceMappingURL=secure-channel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"secure-channel.js","sourceRoot":"","sources":["../src/secure-channel.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "@xiotbox/e2e-channel",
3
+ "version": "0.1.0",
4
+ "description": "XiotBox E2E Secure Channel — generic end-to-end encrypted relay protocol (TypeScript/Node implementation). Zero runtime dependencies (node:crypto only).",
5
+ "type": "module",
6
+ "main": "lib/index.js",
7
+ "types": "lib/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./lib/index.d.ts",
11
+ "default": "./lib/index.js"
12
+ },
13
+ "./package.json": "./package.json"
14
+ },
15
+ "files": [
16
+ "lib",
17
+ "docs"
18
+ ],
19
+ "license": "MIT",
20
+ "scripts": {
21
+ "build": "tsc -p tsconfig.json",
22
+ "typecheck": "tsc -p tsconfig.json --noEmit",
23
+ "test": "npm run build && node --test test/*.test.js",
24
+ "prepare": "npm run build"
25
+ },
26
+ "devDependencies": {
27
+ "@types/node": "^22.0.0",
28
+ "typescript": "^5.6.0"
29
+ }
30
+ }