lemon-tls 0.1.1 → 0.2.1

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