lemon-tls 0.1.0 → 0.2.0

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