lemon-tls 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.
package/wire.js ADDED
@@ -0,0 +1,1672 @@
1
+
2
+
3
+ var {
4
+ concatUint8Arrays,
5
+ } = require('./utils');
6
+
7
+
8
+ var TLS_VERSION = {
9
+ TLS1_0: 0x0301,
10
+ TLS1_1: 0x0302,
11
+ TLS1_2: 0x0303,
12
+ TLS1_3: 0x0304
13
+ };
14
+
15
+ var TLS_MESSAGE_TYPE = {
16
+ CLIENT_HELLO: 1,
17
+ SERVER_HELLO: 2,
18
+ NEW_SESSION_TICKET: 4,
19
+ END_OF_EARLY_DATA: 5,
20
+ ENCRYPTED_EXTENSIONS: 8,
21
+ CERTIFICATE: 11,
22
+ SERVER_KEY_EXCHANGE: 12,
23
+ CERTIFICATE_REQUEST: 13,
24
+ SERVER_HELLO_DONE: 14,
25
+ CERTIFICATE_VERIFY: 15,
26
+ CLIENT_KEY_EXCHANGE: 16,
27
+ FINISHED: 20,
28
+ KEY_UPDATE: 24,
29
+ MESSAGE_HASH: 254 // HRR flow marker
30
+ };
31
+
32
+ var TLS_EXT = {
33
+ SERVER_NAME: 0,
34
+ MAX_FRAGMENT_LENGTH: 1,
35
+ STATUS_REQUEST: 5,
36
+ SUPPORTED_GROUPS: 10,
37
+ SIGNATURE_ALGORITHMS: 13,
38
+ USE_SRTP: 14,
39
+ HEARTBEAT: 15,
40
+ ALPN: 16,
41
+ SCT: 18,
42
+ CLIENT_CERT_TYPE: 19,
43
+ SERVER_CERT_TYPE: 20,
44
+ PADDING: 21,
45
+ PRE_SHARED_KEY: 41,
46
+ EARLY_DATA: 42,
47
+ SUPPORTED_VERSIONS: 43,
48
+ COOKIE: 44,
49
+ PSK_KEY_EXCHANGE_MODES: 45,
50
+ CERTIFICATE_AUTHORITIES: 47,
51
+ OID_FILTERS: 48,
52
+ POST_HANDSHAKE_AUTH: 49,
53
+ SIGNATURE_ALGORITHMS_CERT: 50,
54
+ KEY_SHARE: 51,
55
+ RENEGOTIATION_INFO: 0xFF01
56
+ };
57
+
58
+ /* =============================== Small utils ============================== */
59
+
60
+ function toU8(x) {
61
+ if (x == null) return new Uint8Array(0);
62
+ if (x instanceof Uint8Array) return x;
63
+ if (typeof x === 'string') return (new TextEncoder()).encode(x);
64
+ return new Uint8Array(0);
65
+ }
66
+
67
+ /* ============================ Binary write helpers ============================ */
68
+ function w_u8(buf, off, v) {
69
+ buf[off++] = v & 0xFF;
70
+ return off;
71
+ }
72
+
73
+ function w_u16(buf, off, v) {
74
+ buf[off++] = (v >>> 8) & 0xFF;
75
+ buf[off++] = v & 0xFF;
76
+ return off;
77
+ }
78
+
79
+ function w_u24(buf, off, v) {
80
+ buf[off++] = (v >>> 16) & 0xFF;
81
+ buf[off++] = (v >>> 8) & 0xFF;
82
+ buf[off++] = v & 0xFF;
83
+ return off;
84
+ }
85
+
86
+ function w_bytes(buf, off, b) {
87
+ buf.set(b, off);
88
+ return off + b.length;
89
+ }
90
+
91
+ /* ============================ Binary read helpers ============================ */
92
+ function r_u8(buf, off) {
93
+ return [buf[off++] >>> 0, off];
94
+ }
95
+
96
+ function r_u16(buf, off) {
97
+ var v = ((buf[off] << 8) | buf[off + 1]) >>> 0;
98
+ return [v, off + 2];
99
+ }
100
+
101
+ function r_u24(buf, off) {
102
+ var v = ((buf[off] << 16) | (buf[off + 1] << 8) | buf[off + 2]) >>> 0;
103
+ return [v, off + 3];
104
+ }
105
+
106
+ function r_bytes(buf, off, n) {
107
+ var slice;
108
+ if (buf instanceof Uint8Array) {
109
+ // חיתוך אמיתי מתוך Uint8Array
110
+ slice = buf.slice(off, off + n);
111
+ } else if (typeof Buffer !== "undefined" && Buffer.isBuffer && Buffer.isBuffer(buf)) {
112
+ // Node Buffer → slice מחזיר view, אז נעשה copy ל־Uint8Array
113
+ var tmp = buf.slice(off, off + n);
114
+ slice = new Uint8Array(tmp);
115
+ } else if (Array.isArray(buf)) {
116
+ // מערך רגיל
117
+ var tmp = buf.slice(off, off + n);
118
+ slice = new Uint8Array(tmp);
119
+ } else {
120
+ throw new Error("r_bytes: unsupported buffer type " + (typeof buf));
121
+ }
122
+ return [slice, off + n];
123
+ }
124
+
125
+
126
+ /* ================================= Vectors ================================= */
127
+ function veclen(lenBytes, inner) {
128
+ var out, off = 0;
129
+
130
+ if (lenBytes === 1) {
131
+ out = new Uint8Array(1 + inner.length);
132
+ off = w_u8(out, off, inner.length);
133
+ off = w_bytes(out, off, inner);
134
+ return out;
135
+ }
136
+
137
+ if (lenBytes === 2) {
138
+ out = new Uint8Array(2 + inner.length);
139
+ off = w_u16(out, off, inner.length);
140
+ off = w_bytes(out, off, inner);
141
+ return out;
142
+ }
143
+
144
+ if (lenBytes === 3) {
145
+ out = new Uint8Array(3 + inner.length);
146
+ off = w_u24(out, off, inner.length);
147
+ off = w_bytes(out, off, inner);
148
+ return out;
149
+ }
150
+
151
+ throw new Error('veclen only supports 1/2/3');
152
+ }
153
+
154
+ function readVec(buf, off, lenBytes) {
155
+ var n, off2 = off;
156
+
157
+ if (lenBytes === 1) {
158
+ [n, off2] = r_u8(buf, off2);
159
+ } else if (lenBytes === 2) {
160
+ [n, off2] = r_u16(buf, off2);
161
+ } else {
162
+ [n, off2] = r_u24(buf, off2);
163
+ }
164
+
165
+ var b;
166
+ [b, off2] = r_bytes(buf, off2, n);
167
+ return [b, off2];
168
+ }
169
+
170
+ /* =========================== Extensions registry =========================== */
171
+ var exts = {};
172
+
173
+ // Predeclare wanted entries
174
+ exts.SERVER_NAME = { encode: null, decode: null };
175
+ exts.SUPPORTED_VERSIONS = { encode: null, decode: null };
176
+ exts.SUPPORTED_GROUPS = { encode: null, decode: null };
177
+ exts.SIGNATURE_ALGORITHMS = { encode: null, decode: null };
178
+ exts.PSK_KEY_EXCHANGE_MODES = { encode: null, decode: null };
179
+ exts.KEY_SHARE = { encode: null, decode: null };
180
+ exts.ALPN = { encode: null, decode: null };
181
+ exts.RENEGOTIATION_INFO = { encode: null, decode: null };
182
+
183
+ /* ------------------------------ SERVER_NAME (0) ------------------------------ */
184
+ exts.SERVER_NAME.encode = function (value) {
185
+ var host = toU8(value || "");
186
+
187
+ // one name: type(1)=0, len(2), bytes
188
+ var inner = new Uint8Array(1 + 2 + host.length);
189
+ var off = 0;
190
+
191
+ off = w_u8(inner, off, 0);
192
+ off = w_u16(inner, off, host.length);
193
+ off = w_bytes(inner, off, host);
194
+
195
+ // ServerNameList is vector<2>
196
+ return veclen(2, inner);
197
+ };
198
+
199
+ exts.SERVER_NAME.decode = function (data) {
200
+ var off = 0;
201
+ var list;
202
+ [list, off] = readVec(data, off, 2);
203
+
204
+ var off2 = 0;
205
+ var host = "";
206
+
207
+ while (off2 < list.length) {
208
+ var typ;
209
+ [typ, off2] = r_u8(list, off2);
210
+
211
+ var l;
212
+ [l, off2] = r_u16(list, off2);
213
+
214
+ var v;
215
+ [v, off2] = r_bytes(list, off2, l);
216
+
217
+ if (typ === 0) {
218
+ host = (new TextDecoder()).decode(v);
219
+ }
220
+ }
221
+
222
+ // Return just the value (string), not {host: ...}
223
+ return host;
224
+ };
225
+
226
+ /* --------------------------- SUPPORTED_VERSIONS (43) --------------------------- */
227
+ exts.SUPPORTED_VERSIONS.encode = function (value) {
228
+ // ServerHello form: selected (number)
229
+ if (typeof value === 'number') {
230
+ var out = new Uint8Array(2);
231
+ var off = 0;
232
+ off = w_u16(out, off, value);
233
+ return out;
234
+ }
235
+
236
+ // ClientHello form: array of versions
237
+ var arr = Array.isArray(value) ? value : [TLS_VERSION.TLS1_3, TLS_VERSION.TLS1_2];
238
+
239
+ var body = new Uint8Array(1 + arr.length * 2);
240
+ var off2 = 0;
241
+
242
+ off2 = w_u8(body, off2, arr.length * 2);
243
+ for (var i = 0; i < arr.length; i++) {
244
+ off2 = w_u16(body, off2, arr[i]);
245
+ }
246
+ return body;
247
+ };
248
+
249
+ exts.SUPPORTED_VERSIONS.decode = function (data) {
250
+ // ServerHello form: 2 bytes
251
+ if (data.length === 2) {
252
+ var v, off = 0;
253
+ [v, off] = r_u16(data, off);
254
+ return v; // return the selected version (number)
255
+ }
256
+
257
+ // ClientHello form: vector<1> of versions (u16 each)
258
+ var off2 = 0;
259
+ var n;
260
+ [n, off2] = r_u8(data, off2);
261
+
262
+ var out = [];
263
+ for (var i = 0; i < n; i += 2) {
264
+ var vv;
265
+ [vv, off2] = r_u16(data, off2);
266
+ out.push(vv);
267
+ }
268
+ return out; // return the array directly
269
+ };
270
+
271
+ /* ---------------------------- SUPPORTED_GROUPS (10) ---------------------------- */
272
+ exts.SUPPORTED_GROUPS.encode = function (value) {
273
+ var groups = Array.isArray(value) ? value : [23, 29]; // secp256r1, x25519
274
+
275
+ var body = new Uint8Array(2 + groups.length * 2);
276
+ var off = 0;
277
+
278
+ off = w_u16(body, off, groups.length * 2);
279
+ for (var i = 0; i < groups.length; i++) {
280
+ off = w_u16(body, off, groups[i]);
281
+ }
282
+ return body;
283
+ };
284
+
285
+ exts.SUPPORTED_GROUPS.decode = function (data) {
286
+ var off = 0;
287
+ var n;
288
+ [n, off] = r_u16(data, off);
289
+
290
+ var out = [];
291
+ for (var i = 0; i < n; i += 2) {
292
+ var g;
293
+ [g, off] = r_u16(data, off);
294
+ out.push(g);
295
+ }
296
+ return out; // array of named groups
297
+ };
298
+
299
+ /* -------------------------- SIGNATURE_ALGORITHMS (13) -------------------------- */
300
+ exts.SIGNATURE_ALGORITHMS.encode = function (value) {
301
+ var algs = Array.isArray(value) ? value : [0x0403, 0x0804, 0x0401];
302
+
303
+ var body = new Uint8Array(2 + algs.length * 2);
304
+ var off = 0;
305
+
306
+ off = w_u16(body, off, algs.length * 2);
307
+ for (var i = 0; i < algs.length; i++) {
308
+ off = w_u16(body, off, algs[i]);
309
+ }
310
+ return body;
311
+ };
312
+
313
+ exts.SIGNATURE_ALGORITHMS.decode = function (data) {
314
+ var off = 0;
315
+ var n;
316
+ [n, off] = r_u16(data, off);
317
+
318
+ var out = [];
319
+ for (var i = 0; i < n; i += 2) {
320
+ var a;
321
+ [a, off] = r_u16(data, off);
322
+ out.push(a);
323
+ }
324
+ return out; // array of sigalgs (u16)
325
+ };
326
+
327
+ /* ------------------------ PSK_KEY_EXCHANGE_MODES (45) ------------------------ */
328
+ exts.PSK_KEY_EXCHANGE_MODES.encode = function (value) {
329
+ var modes = Array.isArray(value) ? value : [1]; // 0=psk_ke, 1=psk_dhe_ke
330
+
331
+ var body = new Uint8Array(1 + modes.length);
332
+ var off = 0;
333
+
334
+ off = w_u8(body, off, modes.length);
335
+ for (var i = 0; i < modes.length; i++) {
336
+ off = w_u8(body, off, modes[i]);
337
+ }
338
+ return body;
339
+ };
340
+
341
+ exts.PSK_KEY_EXCHANGE_MODES.decode = function (data) {
342
+ var off = 0;
343
+ var n;
344
+ [n, off] = r_u8(data, off);
345
+
346
+ var out = [];
347
+ for (var i = 0; i < n; i++) {
348
+ var m;
349
+ [m, off] = r_u8(data, off);
350
+ out.push(m);
351
+ }
352
+ return out; // array of modes (u8)
353
+ };
354
+
355
+ /* --------------------------------- KEY_SHARE (51) -------------------------------- */
356
+ exts.KEY_SHARE.encode = function (value) {
357
+ // ServerHello form: { group:number, key_exchange:Uint8Array }
358
+ if (value && typeof value.group === 'number' && value.key_exchange) {
359
+ var ke = toU8(value.key_exchange);
360
+
361
+ var out = new Uint8Array(2 + 2 + ke.length);
362
+ var off = 0;
363
+
364
+ off = w_u16(out, off, value.group);
365
+ off = w_u16(out, off, ke.length);
366
+ off = w_bytes(out, off, ke);
367
+
368
+ return out;
369
+ }
370
+
371
+ // ClientHello form: [{ group:number, key_exchange:Uint8Array }, ...]
372
+ var list = Array.isArray(value) ? value : [];
373
+
374
+ var parts = [];
375
+ for (var i = 0; i < list.length; i++) {
376
+ var e = list[i];
377
+ var ke2 = toU8(e.key_exchange || new Uint8Array(0));
378
+
379
+ var ent = new Uint8Array(2 + 2 + ke2.length);
380
+ var o2 = 0;
381
+
382
+ o2 = w_u16(ent, o2, e.group >>> 0);
383
+ o2 = w_u16(ent, o2, ke2.length);
384
+ o2 = w_bytes(ent, o2, ke2);
385
+
386
+ parts.push(ent);
387
+ }
388
+
389
+ return veclen(2, concatUint8Arrays(parts));
390
+ };
391
+
392
+ exts.KEY_SHARE.decode = function (data) {
393
+ // Try ServerHello form: group(2) + len(2) + key
394
+ if (data.length >= 4) {
395
+ var g, off = 0;
396
+ [g, off] = r_u16(data, off);
397
+
398
+ var l;
399
+ [l, off] = r_u16(data, off);
400
+
401
+ if (4 + l === data.length) {
402
+ var ke;
403
+ [ke, off] = r_bytes(data, off, l);
404
+ // Two fields required → return object
405
+ return { group: g, key_exchange: ke };
406
+ }
407
+ }
408
+
409
+ // ClientHello form: vector<2> of KeyShareEntry
410
+ var off2 = 0;
411
+ var listBytes;
412
+ [listBytes, off2] = r_u16(data, off2);
413
+
414
+ var end = off2 + listBytes;
415
+ var out = [];
416
+
417
+ while (off2 < end) {
418
+ var g2;
419
+ [g2, off2] = r_u16(data, off2);
420
+
421
+ var l2;
422
+ [l2, off2] = r_u16(data, off2);
423
+
424
+ var ke2;
425
+ [ke2, off2] = r_bytes(data, off2, l2);
426
+
427
+ out.push({ group: g2, key_exchange: ke2 });
428
+ }
429
+
430
+ return out; // array of entries
431
+ };
432
+
433
+ /* ------------------------------------ ALPN (16) ----------------------------------- */
434
+ exts.ALPN.encode = function (value) {
435
+ var list = Array.isArray(value) ? value : [];
436
+
437
+ var total = 2; // vec16 length
438
+ var items = [];
439
+
440
+ for (var i = 0; i < list.length; i++) {
441
+ var p = toU8(list[i]);
442
+ items.push(p);
443
+ total += 1 + p.length;
444
+ }
445
+
446
+ var out = new Uint8Array(total);
447
+ var off = 0;
448
+
449
+ off = w_u16(out, off, total - 2);
450
+ for (var j = 0; j < items.length; j++) {
451
+ off = w_u8(out, off, items[j].length);
452
+ off = w_bytes(out, off, items[j]);
453
+ }
454
+
455
+ return out;
456
+ };
457
+
458
+ exts.ALPN.decode = function (data) {
459
+ var off = 0;
460
+ var n;
461
+ [n, off] = r_u16(data, off);
462
+
463
+ var end = off + n;
464
+ var out = [];
465
+
466
+ while (off < end) {
467
+ var l;
468
+ [l, off] = r_u8(data, off);
469
+
470
+ var v;
471
+ [v, off] = r_bytes(data, off, l);
472
+
473
+ out.push((new TextDecoder()).decode(v));
474
+ }
475
+
476
+ return out; // array of protocol strings
477
+ };
478
+
479
+ /* ----------------------------- RENEGOTIATION_INFO (FF01) ----------------------------- */
480
+ exts.RENEGOTIATION_INFO.encode = function (value) {
481
+ // value is Uint8Array of renegotiated_connection data
482
+ var rb = toU8(value || new Uint8Array(0));
483
+ return veclen(1, rb);
484
+ };
485
+
486
+ exts.RENEGOTIATION_INFO.decode = function (data) {
487
+ var off = 0;
488
+ var v;
489
+ [v, off] = readVec(data, off, 1);
490
+ return v; // return raw bytes (Uint8Array)
491
+ };
492
+
493
+ /* ============================= Extensions helpers ============================= */
494
+ function ext_name_by_code(code) {
495
+ // best-effort pretty name
496
+ for (var k in TLS_EXT) {
497
+ if ((TLS_EXT[k] >>> 0) === (code >>> 0)) return k;
498
+ }
499
+ return 'EXT_' + code;
500
+ }
501
+
502
+ function build_extensions(list) {
503
+ // list items may be {type:number|string, value:any, data?:Uint8Array}
504
+ if (!list || !list.length) {
505
+ var e = new Uint8Array(2);
506
+ w_u16(e, 0, 0);
507
+ return e;
508
+ }
509
+
510
+ var parts = [];
511
+ var total = 2; // vec16
512
+
513
+ for (var i = 0; i < list.length; i++) {
514
+ var t = list[i].type;
515
+
516
+ // allow symbolic name e.g. 'SERVER_NAME'
517
+ if (typeof t === 'string') {
518
+ t = TLS_EXT[t];
519
+ }
520
+
521
+ var payload;
522
+ if (list[i].data) {
523
+ payload = list[i].data;
524
+ } else {
525
+ // try registry
526
+ var regKey = ext_name_by_code(t);
527
+ var enc = exts[regKey] && exts[regKey].encode;
528
+ payload = enc ? enc(list[i].value) : new Uint8Array(0);
529
+ }
530
+
531
+ var rec = new Uint8Array(4 + payload.length);
532
+ var off = 0;
533
+
534
+ off = w_u16(rec, off, t >>> 0);
535
+ off = w_u16(rec, off, payload.length);
536
+ off = w_bytes(rec, off, payload);
537
+
538
+ parts.push(rec);
539
+ total += rec.length;
540
+ }
541
+
542
+ var out = new Uint8Array(total);
543
+ var off2 = 0;
544
+
545
+ off2 = w_u16(out, off2, total - 2);
546
+
547
+ for (var j = 0; j < parts.length; j++) {
548
+ off2 = w_bytes(out, off2, parts[j]);
549
+ }
550
+
551
+ return out;
552
+ }
553
+
554
+ function parse_extensions(buf) {
555
+ var off = 0;
556
+ var n;
557
+ [n, off] = r_u16(buf, off);
558
+
559
+ var end = off + n;
560
+ var out = [];
561
+
562
+ while (off < end) {
563
+ var t;
564
+ [t, off] = r_u16(buf, off);
565
+
566
+ var l;
567
+ [l, off] = r_u16(buf, off);
568
+
569
+ var d;
570
+ [d, off] = r_bytes(buf, off, l);
571
+
572
+ var name = ext_name_by_code(t);
573
+ var dec = exts[name] && exts[name].decode;
574
+ var val = dec ? dec(d) : null;
575
+
576
+ out.push({ type: t, name: name, data: d, value: val });
577
+ }
578
+
579
+ return out;
580
+ }
581
+
582
+
583
+ /* ================================ Hello I/O ================================ */
584
+ function build_hello(kind, params) {
585
+ params = params || {};
586
+
587
+ var legacy_version = TLS_VERSION.TLS1_2; // even for TLS1.3 legacy fields
588
+
589
+ var sid = toU8(params.session_id || "");
590
+ if (sid.length > 32) sid = sid.subarray(0, 32);
591
+
592
+ var extsBuf = build_extensions(params.extensions || []);
593
+
594
+ if (kind === 'client') {
595
+ var cs = params.cipher_suites || [0x1301, 0x1302, 0x1303, 0xC02F, 0xC02B];
596
+
597
+ var csBlock = new Uint8Array(2 + cs.length * 2);
598
+ var o = 0;
599
+ o = w_u16(csBlock, o, cs.length * 2);
600
+ for (var i = 0; i < cs.length; i++) {
601
+ o = w_u16(csBlock, o, cs[i]);
602
+ }
603
+
604
+ var comp = params.legacy_compression || [0]; // for TLS1.3 must be [0]
605
+ var compBlock = new Uint8Array(1 + comp.length);
606
+ var oc = 0;
607
+ oc = w_u8(compBlock, oc, comp.length);
608
+ for (var j = 0; j < comp.length; j++) {
609
+ oc = w_u8(compBlock, oc, comp[j]);
610
+ }
611
+
612
+ var out = new Uint8Array(
613
+ 2 + 32 + 1 + sid.length + csBlock.length + compBlock.length + extsBuf.length
614
+ );
615
+
616
+ var off = 0;
617
+ off = w_u16(out, off, legacy_version);
618
+ off = w_bytes(out, off, params.random);
619
+ off = w_u8(out, off, sid.length);
620
+ off = w_bytes(out, off, sid);
621
+ off = w_bytes(out, off, csBlock);
622
+ off = w_bytes(out, off, compBlock);
623
+ off = w_bytes(out, off, extsBuf);
624
+
625
+ return out;
626
+ }
627
+
628
+ if (kind === 'server') {
629
+ var cipher_suite = (typeof params.cipher_suite === 'number') ? params.cipher_suite : 0x1301;
630
+
631
+ var out2 = new Uint8Array(2 + 32 + 1 + sid.length + 2 + 1 + extsBuf.length);
632
+ var off2 = 0;
633
+
634
+ off2 = w_u16(out2, off2, legacy_version);
635
+ off2 = w_bytes(out2, off2, params.random);
636
+ off2 = w_u8(out2, off2, sid.length);
637
+ off2 = w_bytes(out2, off2, sid);
638
+ off2 = w_u16(out2, off2, cipher_suite);
639
+ off2 = w_u8(out2, off2, 0); // compression method = 0
640
+ off2 = w_bytes(out2, off2, extsBuf);
641
+
642
+ return out2;
643
+ }
644
+
645
+ throw new Error('build_hello: kind must be "client" or "server"');
646
+ }
647
+
648
+ function parse_hello(hsType, body) {
649
+ var isClient = (hsType === TLS_MESSAGE_TYPE.CLIENT_HELLO || hsType === 'client_hello');
650
+
651
+ var off = 0;
652
+
653
+ var legacy_version;
654
+ [legacy_version, off] = r_u16(body, off);
655
+
656
+ var random;
657
+ [random, off] = r_bytes(body, off, 32);
658
+
659
+ var sidLen;
660
+ [sidLen, off] = r_u8(body, off);
661
+
662
+ var session_id;
663
+ [session_id, off] = r_bytes(body, off, sidLen);
664
+
665
+ if (isClient) {
666
+ var csLen;
667
+ [csLen, off] = r_u16(body, off);
668
+
669
+ var csEnd = off + csLen;
670
+ var cipher_suites = [];
671
+
672
+ while (off < csEnd) {
673
+ var cs;
674
+ [cs, off] = r_u16(body, off);
675
+ cipher_suites.push(cs);
676
+ }
677
+
678
+ var compLen;
679
+ [compLen, off] = r_u8(body, off);
680
+
681
+ var legacy_compression = [];
682
+ for (var i = 0; i < compLen; i++) {
683
+ var c;
684
+ [c, off] = r_u8(body, off);
685
+ legacy_compression.push(c);
686
+ }
687
+
688
+ var extRaw = (body.length > off) ? body.subarray(off) : new Uint8Array(0);
689
+ var extensions = extRaw.length ? parse_extensions(extRaw) : [];
690
+
691
+ // version hint: if supported_versions includes TLS1.3, prefer it
692
+ var ver = legacy_version;
693
+ for (var k = 0; k < extensions.length; k++) {
694
+ var e = extensions[k];
695
+ if (e.type === TLS_EXT.SUPPORTED_VERSIONS && Array.isArray(e.value)) {
696
+ for (var t = 0; t < e.value.length; t++) {
697
+ if (e.value[t] === TLS_VERSION.TLS1_3) {
698
+ ver = TLS_VERSION.TLS1_3;
699
+ break;
700
+ }
701
+ }
702
+ }
703
+ }
704
+
705
+ return {
706
+ message: 'client_hello',
707
+ legacy_version: legacy_version,
708
+ version_hint: ver,
709
+ random: random,
710
+ session_id: session_id,
711
+ cipher_suites: cipher_suites,
712
+ legacy_compression: legacy_compression,
713
+ extensions: extensions
714
+ };
715
+ }
716
+
717
+ // ServerHello
718
+ var cipher_suite;
719
+ [cipher_suite, off] = r_u16(body, off);
720
+
721
+ var comp;
722
+ [comp, off] = r_u8(body, off);
723
+
724
+ var extRaw2 = (body.length > off) ? body.subarray(off) : new Uint8Array(0);
725
+ var extensions2 = extRaw2.length ? parse_extensions(extRaw2) : [];
726
+
727
+ var ver2 = legacy_version;
728
+ for (var z = 0; z < extensions2.length; z++) {
729
+ var ex = extensions2[z];
730
+ if (ex.type === TLS_EXT.SUPPORTED_VERSIONS && typeof ex.value === 'number') {
731
+ ver2 = ex.value; // selected version
732
+ }
733
+ }
734
+
735
+ return {
736
+ message: 'server_hello',
737
+ legacy_version: legacy_version,
738
+ version: ver2,
739
+ random: random,
740
+ session_id: session_id,
741
+ cipher_suite: cipher_suite,
742
+ legacy_compression: comp,
743
+ extensions: extensions2
744
+ };
745
+ }
746
+
747
+ function isVec2(u8){
748
+ if (!(u8 instanceof Uint8Array) || u8.length < 2) return false;
749
+ var len = (u8[0] << 8) | u8[1];
750
+ return u8.length === 2 + len;
751
+ }
752
+
753
+ /* ===================== Certificate / CertificateVerify / Finished ===================== */
754
+
755
+ function build_certificate(params) {
756
+ // Unified API:
757
+ // { version: TLS_VERSION.TLS1_2 | TLS_VERSION.TLS1_3,
758
+ // entries?: [ { cert: Uint8Array|string, extensions?: Uint8Array|ext-list } ],
759
+ // request_context?: Uint8Array|string,
760
+ // // backward-compat:
761
+ // certs?: [Uint8Array|string] }
762
+ //
763
+ // TLS 1.3 → מחזיר: request_context (vec<1>) || certificate_list (vec<3> של [ cert(vec<3>) || extensions(vec<2>) ]*)
764
+ // TLS 1.2 → מחזיר: certificate_list (vec<3> של cert(vec<3>)*), מתעלם מ-extensions/request_context
765
+
766
+ var v = params.version || TLS_VERSION.TLS1_2;
767
+
768
+ // Normalize to entries[] always
769
+ var entries = Array.isArray(params.entries) ? params.entries.slice() : null;
770
+ if (!entries && Array.isArray(params.certs)) {
771
+ // backward-compat: { certs:[...] } → entries:[{cert},...]
772
+ entries = params.certs.map(function (c) { return { cert: c }; });
773
+ }
774
+ if (!entries) entries = [];
775
+
776
+ if (v === TLS_VERSION.TLS1_3) {
777
+ var ctx = toU8(params.request_context || new Uint8Array(0));
778
+
779
+ var entryParts = [];
780
+ for (var i = 0; i < entries.length; i++) {
781
+ var certBytes = toU8(entries[i].cert || new Uint8Array(0));
782
+ var certVec = veclen(3, certBytes); // cert_data vec<3>
783
+
784
+ var extRaw = entries[i].extensions;
785
+ if (Array.isArray(extRaw)) {
786
+ // list of ext objects → encode to vec<2>
787
+ extRaw = build_extensions(extRaw);
788
+ } else if (extRaw instanceof Uint8Array) {
789
+ // already raw bytes; ensure it's vec<2>
790
+ extRaw = isVec2 && isVec2(extRaw) ? extRaw : veclen(2, extRaw);
791
+ } else {
792
+ // no extensions
793
+ extRaw = veclen(2, new Uint8Array(0));
794
+ }
795
+
796
+ entryParts.push(certVec, extRaw);
797
+ }
798
+
799
+ var ctxVec = veclen(1, ctx);
800
+ var listVec = veclen(3, concatUint8Arrays(entryParts));
801
+ return concatUint8Arrays([ctxVec, listVec]);
802
+
803
+ } else {
804
+ // TLS 1.2: only certificate_list (vec<3> of cert(vec<3>)*), ignore per-entry extensions & request_context
805
+ var certListParts = [];
806
+ for (var j = 0; j < entries.length; j++) {
807
+ var c = toU8(entries[j].cert || new Uint8Array(0));
808
+ certListParts.push(veclen(3, c));
809
+ }
810
+ return veclen(3, concatUint8Arrays(certListParts));
811
+ }
812
+ }
813
+
814
+ function parse_certificate(body) {
815
+ // תמיד מחזיר:
816
+ // {
817
+ // version: TLS_VERSION.TLS1_2 | TLS_VERSION.TLS1_3,
818
+ // request_context?: Uint8Array, // רק ב-1.3
819
+ // entries: [ { cert: Uint8Array, extensions?: any[] } ] // ב-1.2 אין extensions
820
+ // }
821
+
822
+ // נסה לזהות TLS 1.3: rc(vec<1>) ואז list(vec<3> של [cert(vec<3>) || exts(vec<2>)]*)
823
+ if (body && body.length >= 4) {
824
+ let off = 0;
825
+ let rcLen; [rcLen, off] = r_u8(body, off); // 1 byte
826
+ const afterCtx = off + rcLen; // off מצביע אחרי rcLen (כלומר 1)
827
+ if (afterCtx + 3 <= body.length) {
828
+ let listLen, off2 = afterCtx; // התחלת certificate_list
829
+ [listLen, off2] = r_u24(body, off2); // 3 bytes
830
+ if (afterCtx + 3 + listLen === body.length) {
831
+ // נראה כמו 1.3 תקני
832
+ const request_context = body.subarray(off, off + rcLen);
833
+ off = off2;
834
+ const end = off2 + listLen;
835
+
836
+ const entries = [];
837
+ while (off < end) {
838
+ let certLen; [certLen, off] = r_u24(body, off); // cert_data vec<3>
839
+ let cert; [cert, off] = r_bytes(body, off, certLen);
840
+
841
+ let extLen; [extLen, off] = r_u16(body, off); // extensions vec<2>
842
+ let extRaw; [extRaw, off] = r_bytes(body, off, extLen);
843
+
844
+ const extensions = extLen ? parse_extensions(extRaw) : [];
845
+ entries.push({ cert, extensions });
846
+ }
847
+
848
+ return {
849
+ version: TLS_VERSION.TLS1_3,
850
+ request_context,
851
+ entries
852
+ };
853
+ }
854
+ }
855
+ }
856
+
857
+ // אחרת: TLS 1.2 — certificate_list(vec<3>) של cert(vec<3>)* ללא הרחבות
858
+ let off3 = 0;
859
+ let listLen2; [listLen2, off3] = r_u24(body, off3);
860
+
861
+ const end2 = off3 + listLen2;
862
+ if (end2 !== body.length) {
863
+ // התאוששות קלה: אם האורך לא תואם, ננסה לפחות לא לקרוס (אפשר גם לזרוק שגיאה)
864
+ // throw new Error('Bad TLS1.2 Certificate length');
865
+ }
866
+
867
+ const entries12 = [];
868
+ while (off3 < Math.min(end2, body.length)) {
869
+ let len3; [len3, off3] = r_u24(body, off3);
870
+ let cert; [cert, off3] = r_bytes(body, off3, len3);
871
+ entries12.push({ cert }); // אין extensions ב-1.2
872
+ }
873
+
874
+ return {
875
+ version: TLS_VERSION.TLS1_2,
876
+ entries: entries12
877
+ };
878
+ }
879
+
880
+
881
+ // CertificateVerify (TLS 1.2/1.3 share the same wire framing at this level)
882
+ // struct {
883
+ // SignatureScheme algorithm; // u16
884
+ // opaque signature<0..2^16-1>;
885
+ // } CertificateVerify;
886
+ function build_certificate_verify(scheme, signature) {
887
+ var sig = toU8(signature || new Uint8Array(0));
888
+ var alg = scheme >>> 0;
889
+
890
+ var out = new Uint8Array(2 + 2 + sig.length);
891
+ var off = 0;
892
+
893
+ off = w_u16(out, off, alg);
894
+ off = w_u16(out, off, sig.length);
895
+ off = w_bytes(out, off, sig);
896
+
897
+ return out;
898
+ }
899
+
900
+ function parse_certificate_verify(body) {
901
+ var off = 0;
902
+
903
+ var alg;
904
+ [alg, off] = r_u16(body, off);
905
+
906
+ var slen;
907
+ [slen, off] = r_u16(body, off);
908
+
909
+ var sig;
910
+ [sig, off] = r_bytes(body, off, slen);
911
+
912
+ return { scheme: alg, signature: sig };
913
+ }
914
+
915
+ /* ============================ TLS 1.3 Post-Handshake ============================ */
916
+ // NewSessionTicket (TLS1.3)
917
+ // struct {
918
+ // uint32 ticket_lifetime;
919
+ // uint32 ticket_age_add;
920
+ // opaque ticket_nonce<0..2^8-1>;
921
+ // opaque ticket<1..2^16-1>;
922
+ // Extension extensions<0..2^16-1>;
923
+ // } NewSessionTicket;
924
+ function build_new_session_ticket(p) {
925
+ var lifetime = (p && p.ticket_lifetime) >>> 0;
926
+ var age_add = (p && p.ticket_age_add) >>> 0;
927
+ var nonce = toU8(p && p.ticket_nonce || new Uint8Array(0));
928
+ var ticket = toU8(p && p.ticket || new Uint8Array(0));
929
+ var extsBuf = Array.isArray(p && p.extensions) ? build_extensions(p.extensions) : (p && p.extensions) || veclen(2, new Uint8Array(0));
930
+
931
+ var out = new Uint8Array(4 + 4 + 1 + nonce.length + 2 + ticket.length + extsBuf.length);
932
+ var off = 0;
933
+
934
+ // u32 big-endian
935
+ off = w_u8(out, off, (lifetime>>>24)&0xFF);
936
+ off = w_u8(out, off, (lifetime>>>16)&0xFF);
937
+ off = w_u8(out, off, (lifetime>>>8)&0xFF);
938
+ off = w_u8(out, off, (lifetime)&0xFF);
939
+
940
+ off = w_u8(out, off, (age_add>>>24)&0xFF);
941
+ off = w_u8(out, off, (age_add>>>16)&0xFF);
942
+ off = w_u8(out, off, (age_add>>>8)&0xFF);
943
+ off = w_u8(out, off, (age_add)&0xFF);
944
+
945
+ off = w_u8(out, off, nonce.length);
946
+ off = w_bytes(out, off, nonce);
947
+
948
+ off = w_u16(out, off, ticket.length);
949
+ off = w_bytes(out, off, ticket);
950
+
951
+ off = w_bytes(out, off, extsBuf);
952
+
953
+ return out;
954
+ }
955
+
956
+ function parse_new_session_ticket(body) {
957
+ var off = 0;
958
+
959
+ var lifetime = (body[off]<<24 | body[off+1]<<16 | body[off+2]<<8 | body[off+3]) >>> 0; off+=4;
960
+ var age_add = (body[off]<<24 | body[off+1]<<16 | body[off+2]<<8 | body[off+3]) >>> 0; off+=4;
961
+
962
+ var nlen;
963
+ [nlen, off] = r_u8(body, off);
964
+ var nonce;
965
+ [nonce, off] = r_bytes(body, off, nlen);
966
+
967
+ var tlen;
968
+ [tlen, off] = r_u16(body, off);
969
+ var ticket;
970
+ [ticket, off] = r_bytes(body, off, tlen);
971
+
972
+ var extBuf = body.subarray(off);
973
+ var extensions = extBuf.length ? parse_extensions(extBuf) : [];
974
+
975
+ return {
976
+ ticket_lifetime: lifetime,
977
+ ticket_age_add: age_add,
978
+ ticket_nonce: nonce,
979
+ ticket: ticket,
980
+ extensions: extensions
981
+ };
982
+ }
983
+
984
+
985
+ /* ================================ CertificateRequest ================================ */
986
+ // TLS 1.3:
987
+ // struct {
988
+ // opaque certificate_request_context<0..2^8-1>;
989
+ // Extension extensions<0..2^16-1>;
990
+ // } CertificateRequest;
991
+ //
992
+ // TLS 1.2:
993
+ // struct {
994
+ // ClientCertificateType certificate_types<1..2^8-1>;
995
+ // SignatureAndHashAlgorithm signature_algorithms<2..2^16-2>; // optional in <1.2
996
+ // DistinguishedName certificate_authorities<0..2^16-1>; // vector of DNs, each DN is opaque<1..2^16-1>
997
+ // } CertificateRequest;
998
+
999
+ function build_certificate_request(params) {
1000
+ var v = params && params.version || TLS_VERSION.TLS1_3;
1001
+
1002
+ if (v === TLS_VERSION.TLS1_3) {
1003
+ var ctx = toU8((params && params.request_context) || new Uint8Array(0));
1004
+ var extsBuf = Array.isArray(params && params.extensions)
1005
+ ? build_extensions(params.extensions)
1006
+ : (params && params.extensions) || veclen(2, new Uint8Array(0));
1007
+
1008
+ var ctxVec = veclen(1, ctx);
1009
+ return concatUint8Arrays([ctxVec, extsBuf]);
1010
+ }
1011
+
1012
+ // TLS 1.2 / 1.0 / 1.1
1013
+ var typesArr = (params && params.certificate_types) || [1]; // rsa_sign(1) as default
1014
+ var typesBuf = new Uint8Array(typesArr.length);
1015
+ for (var i = 0; i < typesArr.length; i++) typesBuf[i] = typesArr[i] & 0xFF;
1016
+ var typesVec = veclen(1, typesBuf);
1017
+
1018
+ var sigalgs = (params && params.signature_algorithms) || [];
1019
+ var sigBuf = new Uint8Array(sigalgs.length * 2);
1020
+ var o = 0;
1021
+ for (var j = 0; j < sigalgs.length; j++) o = w_u16(sigBuf, o, sigalgs[j]);
1022
+ var sigVec = sigalgs.length ? veclen(2, sigBuf) : new Uint8Array(0);
1023
+
1024
+ var cas = (params && params.certificate_authorities) || [];
1025
+ var caParts = [];
1026
+ var caTotal = 0;
1027
+ for (var k = 0; k < cas.length; k++) {
1028
+ var dn = toU8(cas[k]);
1029
+ var ent = new Uint8Array(2 + dn.length);
1030
+ var oo = 0; oo = w_u16(ent, oo, dn.length); oo = w_bytes(ent, oo, dn);
1031
+ caParts.push(ent); caTotal += ent.length;
1032
+ }
1033
+ var caVec = veclen(2, caParts.length ? concatUint8Arrays(caParts) : new Uint8Array(0));
1034
+
1035
+ return concatUint8Arrays([typesVec, sigVec, caVec]);
1036
+ }
1037
+
1038
+ function parse_certificate_request(body) {
1039
+ // Try TLS 1.3 form first: ctx<1> + extensions<2>
1040
+ if (body.length >= 3) {
1041
+ var ctxLen = body[0];
1042
+ if (1 + ctxLen + 2 <= body.length) {
1043
+ var extLen = (body[1 + ctxLen] << 8) | body[2 + ctxLen];
1044
+ if (1 + ctxLen + 2 + extLen === body.length) {
1045
+ var ctx = body.subarray(1, 1 + ctxLen);
1046
+ var extBuf = body.subarray(1 + ctxLen + 2);
1047
+ return {
1048
+ version: TLS_VERSION.TLS1_3,
1049
+ request_context: ctx,
1050
+ extensions: parse_extensions(extBuf)
1051
+ };
1052
+ }
1053
+ }
1054
+ }
1055
+
1056
+ // Otherwise TLS 1.2/1.1/1.0
1057
+ var off = 0;
1058
+
1059
+ var typesBytes, off1;
1060
+ [typesBytes, off1] = readVec(body, off, 1);
1061
+ off = off1;
1062
+ var certificate_types = [];
1063
+ for (var i = 0; i < typesBytes.length; i++) certificate_types.push(typesBytes[i] >>> 0);
1064
+
1065
+ var signature_algorithms = [];
1066
+ if (off + 2 <= body.length) {
1067
+ var sigLen = (body[off] << 8) | body[off + 1];
1068
+ if (off + 2 + sigLen <= body.length) {
1069
+ off += 2;
1070
+ var endSig = off + sigLen;
1071
+ while (off < endSig) {
1072
+ var alg;
1073
+ [alg, off] = r_u16(body, off);
1074
+ signature_algorithms.push(alg);
1075
+ }
1076
+ }
1077
+ }
1078
+
1079
+ var cas = [];
1080
+ if (off + 2 <= body.length) {
1081
+ var caLen;
1082
+ [caLen, off] = r_u16(body, off);
1083
+ var end = off + caLen;
1084
+ while (off < end) {
1085
+ var dnLen;
1086
+ [dnLen, off] = r_u16(body, off);
1087
+ var dn;
1088
+ [dn, off] = r_bytes(body, off, dnLen);
1089
+ cas.push(dn);
1090
+ }
1091
+ }
1092
+
1093
+ return {
1094
+ version: TLS_VERSION.TLS1_2,
1095
+ certificate_types: certificate_types,
1096
+ signature_algorithms: signature_algorithms,
1097
+ certificate_authorities: cas
1098
+ };
1099
+ }
1100
+
1101
+
1102
+
1103
+ /* ============================== TLS 1.3 HelloRetryRequest ============================== */
1104
+ function build_hello_retry_request(params) {
1105
+ // params: { cipher_suite, selected_version, selected_group, cookie?: Uint8Array|string, other_exts?: list }
1106
+ var rnd = TLS13_HRR_RANDOM;
1107
+ var sid = new Uint8Array(0);
1108
+ var legacy_version = TLS_VERSION.TLS1_2;
1109
+
1110
+ var extList = [];
1111
+ // supported_versions (selected)
1112
+ extList.push({ type: 'SUPPORTED_VERSIONS', value: (params && params.selected_version) || TLS_VERSION.TLS1_3 });
1113
+ // key_share: only selected_group (no key)
1114
+ if (params && params.selected_group != null) {
1115
+ extList.push({ type: 'KEY_SHARE', value: { selected_group: params.selected_group, key_exchange: new Uint8Array(0) } });
1116
+ }
1117
+ // cookie if supplied
1118
+ if (params && params.cookie) {
1119
+ if (!exts.COOKIE) { exts.COOKIE = { encode: function(v){ return veclen(2, toU8(v||'')); }, decode: function(d){ var off=0,v; [v,off]=readVec(d,0,2); return v; } }; }
1120
+ extList.push({ type: 'COOKIE', value: params.cookie });
1121
+ }
1122
+ // other extensions passthrough
1123
+ if (params && Array.isArray(params.other_exts)) {
1124
+ for (var i=0;i<params.other_exts.length;i++) extList.push(params.other_exts[i]);
1125
+ }
1126
+
1127
+ var extsBuf = build_extensions(extList);
1128
+ var cipher_suite = (params && typeof params.cipher_suite==='number') ? params.cipher_suite : 0x1301;
1129
+
1130
+ // Wire = legacy_version + random + sid + cipher_suite + compression(0) + extensions
1131
+ var out = new Uint8Array(2 + 32 + 1 + 0 + 2 + 1 + extsBuf.length);
1132
+ var off = 0;
1133
+ off = w_u16(out, off, legacy_version);
1134
+ off = w_bytes(out, off, rnd);
1135
+ off = w_u8(out, off, 0);
1136
+ off = w_u16(out, off, cipher_suite);
1137
+ off = w_u8(out, off, 0);
1138
+ off = w_bytes(out, off, extsBuf);
1139
+ return out;
1140
+ }
1141
+
1142
+ /* ============================== TLS 1.2 ServerKeyExchange ============================== */
1143
+ // We'll implement the common ECDHE form and basic DHE form.
1144
+ // ECDHE ServerKeyExchange:
1145
+ // struct {
1146
+ // ECParameters curve; // curve_type(1)=3, named_curve(2)
1147
+ // opaque ec_point<1..2^8-1>; // server's ephemeral ECDH public key
1148
+ // digitally-signed struct { .. } // in TLS1.2: SignatureAndHashAlgorithm(2) + signature<2>
1149
+ // }
1150
+ function build_server_key_exchange_ecdhe(p) {
1151
+ // p: { group:u16, public:Uint8Array|string, sig_alg:u16, signature:Uint8Array|string }
1152
+ var pub = toU8(p.public||u8(0));
1153
+
1154
+ var head = new Uint8Array(1+2 + 1 + pub.length);
1155
+ var off = 0;
1156
+ off = w_u8(head, off, 3); // curve_type = named_curve
1157
+ off = w_u16(head, off, p.group>>>0); // named group
1158
+ off = w_u8(head, off, pub.length); // ec_point length
1159
+ off = w_bytes(head, off, pub);
1160
+
1161
+ var sig = toU8(p.signature||u8(0));
1162
+ var sigpart = new Uint8Array(2 + 2 + sig.length);
1163
+ var o2 = 0;
1164
+ o2 = w_u16(sigpart, o2, p.sig_alg>>>0);
1165
+ o2 = w_u16(sigpart, o2, sig.length);
1166
+ o2 = w_bytes(sigpart, o2, sig);
1167
+
1168
+ return concatUint8Arrays([head, sigpart]);
1169
+ }
1170
+
1171
+ function parse_server_key_exchange(body) {
1172
+ var off = 0;
1173
+ var curve_type;
1174
+ [curve_type, off] = r_u8(body, off);
1175
+
1176
+ if (curve_type === 3) { // named_curve (ECDHE)
1177
+ var group;
1178
+ [group, off] = r_u16(body, off);
1179
+
1180
+ var plen;
1181
+ [plen, off] = r_u8(body, off);
1182
+
1183
+ var pub;
1184
+ [pub, off] = r_bytes(body, off, plen);
1185
+
1186
+ var sig_alg;
1187
+ [sig_alg, off] = r_u16(body, off);
1188
+
1189
+ var slen;
1190
+ [slen, off] = r_u16(body, off);
1191
+
1192
+ var sig;
1193
+ [sig, off] = r_bytes(body, off, slen);
1194
+
1195
+ return { kex: 'ECDHE', group: group, public: pub, sig_alg: sig_alg, signature: sig };
1196
+ }
1197
+
1198
+ // Basic DHE: dh_p<2>, dh_g<2>, dh_Ys<2>, then SignatureAndHashAlgorithm + signature<2>
1199
+ var pLen;
1200
+ [pLen, off] = r_u16(body, off);
1201
+ var dh_p;
1202
+ [dh_p, off] = r_bytes(body, off, pLen);
1203
+
1204
+ var gLen;
1205
+ [gLen, off] = r_u16(body, off);
1206
+ var dh_g;
1207
+ [dh_g, off] = r_bytes(body, off, gLen);
1208
+
1209
+ var yLen;
1210
+ [yLen, off] = r_u16(body, off);
1211
+ var dh_Ys;
1212
+ [dh_Ys, off] = r_bytes(body, off, yLen);
1213
+
1214
+ var sig_alg2;
1215
+ [sig_alg2, off] = r_u16(body, off);
1216
+
1217
+ var s2len;
1218
+ [s2len, off] = r_u16(body, off);
1219
+ var sig2;
1220
+ [sig2, off] = r_bytes(body, off, s2len);
1221
+
1222
+ return { kex: 'DHE', dh_p: dh_p, dh_g: dh_g, dh_Ys: dh_Ys, sig_alg: sig_alg2, signature: sig2 };
1223
+ }
1224
+
1225
+ /* ============================== TLS 1.2 ClientKeyExchange ============================== */
1226
+ // Two common forms:
1227
+ // 1) ECDHE: opaque ec_point<1..2^8-1>
1228
+ // 2) RSA : EncryptedPreMasterSecret opaque<2>
1229
+ function build_client_key_exchange_ecdhe(pubkey) {
1230
+ var p = toU8(pubkey||u8(0));
1231
+ return veclen(1, p);
1232
+ }
1233
+ function parse_client_key_exchange_ecdhe(body) {
1234
+ var off=0; var v; [v,off]=readVec(body,0,1); return v;
1235
+ }
1236
+
1237
+ function build_client_key_exchange_rsa(enc_pms) {
1238
+ var e = toU8(enc_pms||u8(0));
1239
+ return veclen(2, e);
1240
+ }
1241
+ function parse_client_key_exchange_rsa(body) {
1242
+ var off=0; var v; [v,off]=readVec(body,0,2); return v;
1243
+ }
1244
+
1245
+ /* ============================== TLS 1.2 NewSessionTicket ============================== */
1246
+ // struct {
1247
+ // uint32 ticket_lifetime_hint;
1248
+ // opaque ticket<0..2^16-1>;
1249
+ // } NewSessionTicket;
1250
+ function build_new_session_ticket_tls12(p) {
1251
+ var hint = (p && p.ticket_lifetime_hint) >>> 0;
1252
+ var ticket = toU8(p && p.ticket || new Uint8Array(0));
1253
+ var out = new Uint8Array(4 + 2 + ticket.length);
1254
+ var off = 0;
1255
+ off = w_u8(out, off, (hint>>>24)&0xFF);
1256
+ off = w_u8(out, off, (hint>>>16)&0xFF);
1257
+ off = w_u8(out, off, (hint>>>8)&0xFF);
1258
+ off = w_u8(out, off, (hint)&0xFF);
1259
+ off = w_u16(out, off, ticket.length);
1260
+ off = w_bytes(out, off, ticket);
1261
+ return out;
1262
+ }
1263
+ function parse_new_session_ticket_tls12(body) {
1264
+ var off=0;
1265
+ var hint = (body[off]<<24 | body[off+1]<<16 | body[off+2]<<8 | body[off+3])>>>0; off+=4;
1266
+ var tlen; [tlen,off]=r_u16(body,off); var t; [t,off]=r_bytes(body,off,tlen);
1267
+ return { ticket_lifetime_hint: hint, ticket: t };
1268
+ }
1269
+
1270
+ /* ============================= Handshake message ============================= */
1271
+ function build_message(params) {
1272
+
1273
+ var type=0;
1274
+ var body=null;
1275
+
1276
+ if(params.type=='server_hello'){
1277
+ type=TLS_MESSAGE_TYPE.SERVER_HELLO;
1278
+ body=build_hello('server', params);
1279
+ }else if(params.type=='client_hello'){
1280
+ type=TLS_MESSAGE_TYPE.SERVER_HELLO;
1281
+ body=build_hello('client', params);
1282
+ }else if(params.type=='encrypted_extensions'){
1283
+ type=TLS_MESSAGE_TYPE.ENCRYPTED_EXTENSIONS;
1284
+ body=build_extensions(params.extensions);
1285
+ }else if(params.type=='certificate'){
1286
+ type=TLS_MESSAGE_TYPE.CERTIFICATE;
1287
+ body=build_certificate(params);
1288
+ }else if(params.type=='certificate_verify'){
1289
+ type=TLS_MESSAGE_TYPE.CERTIFICATE_VERIFY;
1290
+ body=build_certificate_verify(params.scheme,params.signature);
1291
+ }else if(params.type=='finished'){
1292
+ type=TLS_MESSAGE_TYPE.FINISHED;
1293
+ body=params.data;
1294
+ }
1295
+
1296
+ var out = new Uint8Array(4 + body.length);
1297
+ var off = 0;
1298
+
1299
+ off = w_u8(out, off, type);
1300
+ off = w_u24(out, off, body.length);
1301
+ off = w_bytes(out, off, body);
1302
+
1303
+ return out;
1304
+ }
1305
+
1306
+ function parse_message(buf) {
1307
+ var off = 0;
1308
+ var t;
1309
+ [t, off] = r_u8(buf, off);
1310
+
1311
+ var l;
1312
+ [l, off] = r_u24(buf, off);
1313
+
1314
+ var b;
1315
+ [b, off] = r_bytes(buf, off, l);
1316
+
1317
+ return { type: t, body: b };
1318
+ }
1319
+
1320
+
1321
+
1322
+
1323
+
1324
+ /* ================================ Exports ================================= */
1325
+ if (typeof module !== 'undefined' && module.exports) {
1326
+ module.exports = {
1327
+ TLS_VERSION,
1328
+ TLS_MESSAGE_TYPE,
1329
+ TLS_EXT,
1330
+
1331
+ w_u8,
1332
+ w_u16,
1333
+ w_u24,
1334
+ w_bytes,
1335
+ r_u8,
1336
+ r_u16,
1337
+ r_u24,
1338
+ r_bytes,
1339
+ veclen,
1340
+ readVec,
1341
+
1342
+ exts,
1343
+ build_extensions,
1344
+ parse_extensions,
1345
+
1346
+ build_message,
1347
+ parse_message,
1348
+ build_hello,
1349
+ parse_hello,
1350
+
1351
+ build_certificate,
1352
+ parse_certificate,
1353
+
1354
+ build_certificate_verify,
1355
+ parse_certificate_verify,
1356
+
1357
+ build_new_session_ticket,
1358
+ parse_new_session_ticket,
1359
+
1360
+ build_certificate_request,
1361
+ parse_certificate_request,
1362
+
1363
+
1364
+ build_hello_retry_request,
1365
+
1366
+ build_server_key_exchange_ecdhe,
1367
+ parse_server_key_exchange,
1368
+
1369
+ build_client_key_exchange_ecdhe,
1370
+ parse_client_key_exchange_ecdhe,
1371
+
1372
+ build_client_key_exchange_rsa,
1373
+ parse_client_key_exchange_rsa,
1374
+
1375
+ build_new_session_ticket_tls12,
1376
+ parse_new_session_ticket_tls12
1377
+ };
1378
+ }
1379
+
1380
+
1381
+
1382
+ /*
1383
+ function build_extensions(exts){
1384
+ // מרכיבים את רשימת ההרחבות (ללא שדה האורך הראשי)
1385
+ var list = [];
1386
+ for (var i=0; i<exts.length; i++){
1387
+ var t = exts[i].type|0;
1388
+ var d = exts[i].data instanceof Uint8Array ? exts[i].data
1389
+ : (exts[i].data && exts[i].data.buffer) ? new Uint8Array(exts[i].data)
1390
+ : new Uint8Array(0);
1391
+
1392
+ // type (2B)
1393
+ list.push((t>>>8)&0xFF, t&0xFF);
1394
+ // len (2B)
1395
+ list.push((d.length>>>8)&0xFF, d.length&0xFF);
1396
+ // data
1397
+ for (var k=0; k<d.length; k++) list.push(d[k]);
1398
+ }
1399
+
1400
+ // עוטפים באורך כולל דו־בתי
1401
+ var out = [];
1402
+ var L = list.length;
1403
+ out.push((L>>>8)&0xFF, L&0xFF);
1404
+ for (var j=0; j<list.length; j++) out.push(list[j]);
1405
+
1406
+ return new Uint8Array(out);
1407
+ }
1408
+
1409
+ function parse_extensions(data) {
1410
+ var ptr = 0;
1411
+ if (data.length < 2) return [];
1412
+
1413
+ var totalLen = (data[ptr++] << 8) | data[ptr++];
1414
+ if (ptr + totalLen > data.length) totalLen = data.length - ptr; // גידור
1415
+
1416
+ var exts = [];
1417
+ var end = ptr + totalLen;
1418
+
1419
+ while (ptr + 4 <= end) {
1420
+ var type = (data[ptr++] << 8) | data[ptr++];
1421
+ var len = (data[ptr++] << 8) | data[ptr++];
1422
+ if (ptr + len > end) break; // שבור – עוצרים
1423
+
1424
+ var extData = data.subarray(ptr, ptr + len);
1425
+ ptr += len;
1426
+
1427
+ exts.push({ type, data: extData });
1428
+ }
1429
+
1430
+ return exts;
1431
+ }
1432
+
1433
+
1434
+
1435
+
1436
+
1437
+
1438
+ function parse_client_hello(data) {
1439
+ var ptr = 0;
1440
+
1441
+ var legacy_version = (data[ptr++] << 8) | data[ptr++];
1442
+ var random = data.slice(ptr, ptr + 32); ptr += 32;
1443
+ var session_id_len = data[ptr++];
1444
+ var session_id = data.slice(ptr, ptr + session_id_len); ptr += session_id_len;
1445
+
1446
+ var cipher_suites_len = (data[ptr++] << 8) | data[ptr++];
1447
+ var cipher_suites = [];
1448
+ for (var i = 0; i < cipher_suites_len; i += 2) {
1449
+ var code = (data[ptr++] << 8) | data[ptr++];
1450
+ cipher_suites.push(code);
1451
+ }
1452
+
1453
+ var compression_methods_len = data[ptr++];
1454
+ var compression_methods = data.slice(ptr, ptr + compression_methods_len); ptr += compression_methods_len;
1455
+
1456
+ var extensions_len = (data[ptr++] << 8) | data[ptr++];
1457
+ var extensions = [];
1458
+ var ext_end = ptr + extensions_len;
1459
+ while (ptr < ext_end) {
1460
+ var ext_type = (data[ptr++] << 8) | data[ptr++];
1461
+ var ext_len = (data[ptr++] << 8) | data[ptr++];
1462
+ var ext_data = data.slice(ptr, ptr + ext_len); ptr += ext_len;
1463
+ extensions.push({ type: ext_type, data: ext_data });
1464
+ }
1465
+
1466
+ var sni = null;
1467
+ var key_shares = [];
1468
+ var supported_versions = [];
1469
+ var supported_groups = [];
1470
+ var signature_algorithms = [];
1471
+ var alpn = [];
1472
+ var max_fragment_length = null;
1473
+ var padding = null;
1474
+ var cookie = null;
1475
+ var psk_key_exchange_modes = [];
1476
+ var pre_shared_key = null;
1477
+ var renegotiation_info = null;
1478
+ var unknown_extensions = null;
1479
+
1480
+ for (var ext of extensions) {
1481
+ var ext_data = new Uint8Array(ext.data);
1482
+ if (ext.type === 0x00) { // SNI
1483
+ var list_len = (ext_data[0] << 8) | ext_data[1];
1484
+ var name_type = ext_data[2];
1485
+ var name_len = (ext_data[3] << 8) | ext_data[4];
1486
+ var name = new TextDecoder().decode(ext_data.slice(5, 5 + name_len));
1487
+ sni = name;
1488
+ }
1489
+ if (ext.type === 0x33) {
1490
+ var ptr2 = 0;
1491
+ var list_len = (ext_data[ptr2++] << 8) | ext_data[ptr2++];
1492
+ var end = ptr2 + list_len;
1493
+ while (ptr2 < end) {
1494
+ var group = (ext_data[ptr2++] << 8) | ext_data[ptr2++];
1495
+ var key_len = (ext_data[ptr2++] << 8) | ext_data[ptr2++];
1496
+ var pubkey = ext_data.slice(ptr2, ptr2 + key_len);
1497
+ ptr2 += key_len;
1498
+ key_shares.push({ group, pubkey });
1499
+ }
1500
+
1501
+ }
1502
+ if (ext.type === 0x2b) { // supported_versions
1503
+ var len = ext_data[0];
1504
+ for (var i = 1; i < 1 + len; i += 2) {
1505
+ var ver = (ext_data[i] << 8) | ext_data[i + 1];
1506
+ supported_versions.push(ver);
1507
+ }
1508
+ }
1509
+ if (ext.type === 0x0a) { // supported_groups
1510
+ var len = (ext_data[0] << 8) | ext_data[1];
1511
+ for (var i = 2; i < 2 + len; i += 2) {
1512
+ supported_groups.push((ext_data[i] << 8) | ext_data[i + 1]);
1513
+ }
1514
+ }
1515
+ if (ext.type === 0x0d) { // signature_algorithms
1516
+ var len = (ext_data[0] << 8) | ext_data[1];
1517
+ for (var i = 2; i < 2 + len; i += 2) {
1518
+ signature_algorithms.push((ext_data[i] << 8) | ext_data[i + 1]);
1519
+ }
1520
+ }
1521
+ if (ext.type === 0x10) { // ALPN
1522
+ var list_len = (ext_data[0] << 8) | ext_data[1];
1523
+ var i = 2;
1524
+ while (i < 2 + list_len) {
1525
+ var name_len = ext_data[i++];
1526
+ var proto = new TextDecoder().decode(ext_data.slice(i, i + name_len));
1527
+ alpn.push(proto);
1528
+ i += name_len;
1529
+ }
1530
+ }
1531
+ if (ext.type === 0x39) { // quic_transport_parameters
1532
+ unknown_extensions = ext.data;
1533
+ }
1534
+ if (ext.type === 0x01) { // Max Fragment Length
1535
+ max_fragment_length = ext_data[0];
1536
+ }
1537
+ if (ext.type === 0x15) { // Padding
1538
+ padding = ext_data;
1539
+ }
1540
+ if (ext.type === 0x002a) { // Cookie
1541
+ var len = (ext_data[0] << 8) | ext_data[1];
1542
+ cookie = ext_data.slice(2, 2 + len);
1543
+ }
1544
+ if (ext.type === 0x2d) { // PSK Key Exchange Modes
1545
+ var len = ext_data[0];
1546
+ for (var i = 1; i <= len; i++) {
1547
+ psk_key_exchange_modes.push(ext_data[i]);
1548
+ }
1549
+ }
1550
+ if (ext.type === 0x29) { // PreSharedKey (placeholder)
1551
+ pre_shared_key = ext_data;
1552
+ }
1553
+ if (ext.type === 0xff01) { // Renegotiation Info
1554
+ renegotiation_info = ext_data;
1555
+ }
1556
+ }
1557
+
1558
+ return {
1559
+ legacy_version,
1560
+ random,
1561
+ session_id,
1562
+ cipher_suites,
1563
+ compression_methods,
1564
+ extensions,
1565
+ sni,
1566
+ key_shares,
1567
+ supported_versions,
1568
+ supported_groups,
1569
+ signature_algorithms,
1570
+ alpn,
1571
+ max_fragment_length,
1572
+ padding,
1573
+ cookie,
1574
+ psk_key_exchange_modes,
1575
+ pre_shared_key,
1576
+ renegotiation_info,
1577
+ unknown_extensions
1578
+ };
1579
+ }
1580
+
1581
+
1582
+
1583
+
1584
+ function build_server_hello(params){
1585
+ var version = params.version|0;
1586
+ var body = [];
1587
+
1588
+ // 1) legacy_version
1589
+ var legacy_version = (version === 0x0304) ? 0x0303 : 0x0303;
1590
+ body.push((legacy_version>>>8)&0xFF, legacy_version&0xFF);
1591
+
1592
+ // 2) random
1593
+ var rnd = params.server_random;
1594
+ for (var i=0;i<rnd.length;i++) body.push(rnd[i]);
1595
+
1596
+ // 3) legacy_session_id
1597
+ var sid = params.legacy_session_id || new Uint8Array(0);
1598
+ body.push(sid.length & 0xFF);
1599
+ for (var i=0;i<sid.length;i++) body.push(sid[i]);
1600
+
1601
+ // 4) cipher_suite
1602
+ var cs = params.cipher_suite|0;
1603
+ body.push((cs>>>8)&0xFF, cs&0xFF);
1604
+
1605
+ // 5) legacy_compression_method
1606
+ body.push(params.compression_method|0);
1607
+
1608
+ // 6) extensions
1609
+ var exts = [];
1610
+
1611
+ if (version === 0x0304){
1612
+ // --- TLS 1.3 extensions ---
1613
+
1614
+ // supported_versions (0x002b)
1615
+ exts.push(0x00,0x2b); // type
1616
+ exts.push(0x00,0x02); // len=2
1617
+ exts.push(0x03,0x04); // TLS1.3
1618
+
1619
+ // key_share (0x0033)
1620
+ var group = params.selected_group|0;
1621
+ var pub = params.server_key_share;
1622
+ var ks = [];
1623
+ ks.push((group>>>8)&0xFF, group&0xFF);
1624
+ ks.push((pub.length>>>8)&0xFF, pub.length&0xFF);
1625
+ for (var j=0;j<pub.length;j++) ks.push(pub[j]);
1626
+
1627
+ exts.push(0x00,0x33); // type
1628
+ exts.push((ks.length>>>8)&0xFF, ks.length&0xFF);
1629
+ for (var j=0;j<ks.length;j++) exts.push(ks[j]);
1630
+
1631
+ } else if (version === 0x0303){
1632
+ // --- TLS 1.2 extensions (אופציונלי) ---
1633
+
1634
+ if (params.secure_renegotiation){
1635
+ // renegotiation_info (0xFF01), length=1, value=0x00
1636
+ exts.push(0xFF,0x01);
1637
+ exts.push(0x00,0x01);
1638
+ exts.push(0x00);
1639
+ }
1640
+ if (params.extended_master_secret){
1641
+ // extended_master_secret (0x0017), empty
1642
+ exts.push(0x00,0x17);
1643
+ exts.push(0x00,0x00);
1644
+ }
1645
+ }
1646
+
1647
+ if (params.extra_extensions && params.extra_extensions.length){
1648
+ for (var e=0;e<params.extra_extensions.length;e++){
1649
+ var ext = params.extra_extensions[e];
1650
+ var et = ext.type|0;
1651
+ var ed = ext.data;
1652
+ exts.push((et>>>8)&0xFF, et&0xFF);
1653
+ exts.push((ed.length>>>8)&0xFF, ed.length&0xFF);
1654
+ for (var k=0;k<ed.length;k++) exts.push(ed[k]);
1655
+ }
1656
+ }
1657
+
1658
+ body.push((exts.length>>>8)&0xFF, exts.length&0xFF);
1659
+ for (var i=0;i<exts.length;i++) body.push(exts[i]);
1660
+
1661
+ // 7) Handshake header (ServerHello=2)
1662
+ var sh = [];
1663
+ sh.push(2); // msg_type=server_hello
1664
+ var len = body.length;
1665
+ sh.push((len>>>16)&0xFF, (len>>>8)&0xFF, len&0xFF);
1666
+ for (var i=0;i<body.length;i++) sh.push(body[i]);
1667
+
1668
+ return new Uint8Array(sh);
1669
+ }
1670
+
1671
+
1672
+ */