quico 0.1.3 → 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.
package/libs/crypto.js DELETED
@@ -1,1841 +0,0 @@
1
- /*
2
- * quico: HTTP/3 and QUIC implementation for Node.js
3
- * Copyright 2025 colocohen
4
- *
5
- * Licensed under the Apache License, Version 2.0 (the "License");
6
- * you may not use this file except in compliance with the License.
7
- * You may obtain a copy of the License at:
8
- *
9
- * https://www.apache.org/licenses/LICENSE-2.0
10
- *
11
- * This file is part of the open-source project hosted at:
12
- * https://github.com/colocohen/quico
13
- *
14
- * Unless required by applicable law or agreed to in writing, software
15
- * distributed under the License is distributed on an "AS IS" BASIS,
16
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
- */
18
-
19
-
20
- import { hmac } from '@noble/hashes/hmac.js';
21
- import { hkdf, extract as hkdf_extract, expand as hkdf_expand } from '@noble/hashes/hkdf.js';
22
- import { sha256, sha384 } from '@noble/hashes/sha2.js';
23
-
24
- import { p256 } from '@noble/curves/nist.js';
25
- import { ed25519, x25519 } from '@noble/curves/ed25519.js';
26
-
27
- var nobleHashes = { hmac, hkdf, hkdf_extract, hkdf_expand, sha256 };
28
-
29
- import * as crypto from 'crypto';
30
-
31
- import {
32
- concatUint8Arrays,
33
- writeVarInt,
34
- readVarInt
35
- } from './utils.js';
36
-
37
-
38
-
39
- function getHashFn(hashName) {
40
- if (hashName === 'sha256') return sha256;
41
- if (hashName === 'sha384') return sha384;
42
- throw new Error('Unsupported hash: ' + hashName);
43
- }
44
-
45
- function getHashLen(hashName) {
46
- var fn = getHashFn(hashName);
47
- return fn.outputLen|0;
48
- }
49
-
50
-
51
-
52
-
53
- function build_quic_ext(params) {
54
- var out = [];
55
-
56
- function addParam(id, value) {
57
- var id_bytes = writeVarInt(id);
58
- var length_bytes, value_bytes;
59
-
60
- if (typeof value === 'number') {
61
- value_bytes = writeVarInt(value);
62
- } else if (value instanceof Uint8Array) {
63
- value_bytes = Array.from(value);
64
- } else if (value === true) {
65
- value_bytes = []; // for disable_active_migration
66
- } else {
67
- throw new Error('Unsupported value type for parameter ' + id);
68
- }
69
-
70
- length_bytes = writeVarInt(value_bytes.length);
71
- out.push(...id_bytes, ...length_bytes, ...value_bytes);
72
- }
73
-
74
- if (params.original_destination_connection_id)
75
- addParam(0x00, params.original_destination_connection_id);
76
- if (params.max_idle_timeout)
77
- addParam(0x01, params.max_idle_timeout);
78
- if (params.stateless_reset_token)
79
- addParam(0x02, params.stateless_reset_token);
80
- if (params.max_udp_payload_size)
81
- addParam(0x03, params.max_udp_payload_size);
82
- if (params.initial_max_data)
83
- addParam(0x04, params.initial_max_data);
84
- if (params.initial_max_stream_data_bidi_local)
85
- addParam(0x05, params.initial_max_stream_data_bidi_local);
86
- if (params.initial_max_stream_data_bidi_remote)
87
- addParam(0x06, params.initial_max_stream_data_bidi_remote);
88
- if (params.initial_max_stream_data_uni)
89
- addParam(0x07, params.initial_max_stream_data_uni);
90
- if (params.initial_max_streams_bidi)
91
- addParam(0x08, params.initial_max_streams_bidi);
92
- if (params.initial_max_streams_uni)
93
- addParam(0x09, params.initial_max_streams_uni);
94
- if (params.ack_delay_exponent !== undefined)
95
- addParam(0x0a, params.ack_delay_exponent);
96
- if (params.max_ack_delay !== undefined)
97
- addParam(0x0b, params.max_ack_delay);
98
- if (params.disable_active_migration)
99
- addParam(0x0c, true);
100
- if (params.active_connection_id_limit)
101
- addParam(0x0e, params.active_connection_id_limit);
102
- if (params.initial_source_connection_id)
103
- addParam(0x0f, params.initial_source_connection_id);
104
- if (params.retry_source_connection_id)
105
- addParam(0x10, params.retry_source_connection_id);
106
- if (params.max_datagram_frame_size)
107
- addParam(0x20, params.max_datagram_frame_size); // אין ערך – presence בלבד
108
- if (params.web_accepted_origins) {
109
- for (var i = 0; i < params.web_accepted_origins.length; i++) {
110
- var origin = params.web_accepted_origins[i];
111
- var origin_bytes = new TextEncoder().encode(origin);
112
- addParam(0x2b603742, origin_bytes);
113
- }
114
- }
115
-
116
- return new Uint8Array(out);
117
- }
118
-
119
-
120
-
121
-
122
-
123
- function build_alpn_ext(protocol) {
124
- var proto_bytes = new TextEncoder().encode(protocol);
125
- var ext = new Uint8Array(2 + 1 + proto_bytes.length);
126
- ext[0] = 0x00;
127
- ext[1] = proto_bytes.length + 1;
128
- ext[2] = proto_bytes.length;
129
- ext.set(proto_bytes, 3);
130
- return ext;
131
- }
132
-
133
-
134
-
135
-
136
-
137
-
138
-
139
- function parse_transport_parameters(buf, start) {
140
- if (!(buf instanceof Uint8Array)) throw new Error("Expect Uint8Array");
141
- var offset = start || 0;
142
- var end = buf.length;
143
- var out = {
144
- web_accepted_origins: []
145
- };
146
-
147
- while (offset < end) {
148
- // ---- מזהה הפרמטר ----
149
- var idVar = readVarInt(buf, offset);
150
- if (!idVar) throw new Error("Bad varint (id) at " + offset);
151
- offset += idVar.byteLength;
152
- var id = idVar.value;
153
-
154
- // ---- אורך הערך ----
155
- var lenVar = readVarInt(buf, offset);
156
- if (!lenVar) throw new Error("Bad varint (len) at " + offset);
157
- offset += lenVar.byteLength;
158
- var length = lenVar.value;
159
-
160
- if (offset + length > end) throw new Error("Truncated value for id " + id);
161
- var valueBytes = buf.slice(offset, offset + length);
162
- offset += length;
163
-
164
- // ---- פענוח לפי ID ----
165
- switch (id) {
166
- case 0x00:
167
- out.original_destination_connection_id = valueBytes;
168
- break;
169
- case 0x01:
170
- out.max_idle_timeout = readVarInt(valueBytes, 0).value;
171
- break;
172
- case 0x02:
173
- if (valueBytes.length !== 16) throw new Error("stateless_reset_token len≠16");
174
- out.stateless_reset_token = valueBytes;
175
- break;
176
- case 0x03:
177
- out.max_udp_payload_size = readVarInt(valueBytes, 0).value;
178
- break;
179
- case 0x04:
180
- out.initial_max_data = readVarInt(valueBytes, 0).value;
181
- break;
182
- case 0x05:
183
- out.initial_max_stream_data_bidi_local = readVarInt(valueBytes, 0).value;
184
- break;
185
- case 0x06:
186
- out.initial_max_stream_data_bidi_remote = readVarInt(valueBytes, 0).value;
187
- break;
188
- case 0x07:
189
- out.initial_max_stream_data_uni = readVarInt(valueBytes, 0).value;
190
- break;
191
- case 0x08:
192
- out.initial_max_streams_bidi = readVarInt(valueBytes, 0).value;
193
- break;
194
- case 0x09:
195
- out.initial_max_streams_uni = readVarInt(valueBytes, 0).value;
196
- break;
197
- case 0x0a:
198
- out.ack_delay_exponent = readVarInt(valueBytes, 0).value;
199
- break;
200
- case 0x0b:
201
- out.max_ack_delay = readVarInt(valueBytes, 0).value;
202
- break;
203
- case 0x0c:
204
- if (length !== 0) throw new Error("disable_active_migration must be zero-length");
205
- out.disable_active_migration = true;
206
- break;
207
- case 0x0e:
208
- out.active_connection_id_limit = readVarInt(valueBytes, 0).value;
209
- break;
210
- case 0x0f:
211
- out.initial_source_connection_id = valueBytes;
212
- break;
213
- case 0x10:
214
- out.retry_source_connection_id = valueBytes;
215
- break;
216
- case 0x20:
217
- out.max_datagram_frame_size = readVarInt(valueBytes, 0).value;
218
- break;
219
- case 0x11:
220
- out.server_certificate_hash = valueBytes;
221
- break;
222
- case 0x2b603742:
223
- var origin = new TextDecoder().decode(valueBytes);
224
- out.web_accepted_origins.push(origin);
225
- break;
226
- default:
227
- if (!out.unknown) out.unknown = [];
228
- out.unknown.push({ id: id, bytes: valueBytes });
229
- }
230
- }
231
-
232
- return out;
233
- }
234
-
235
-
236
-
237
-
238
- function parse_tls_client_hello2(body) {
239
- var view = new Uint8Array(body);
240
- var ptr = 0;
241
-
242
- var legacy_version = (view[ptr++] << 8) | view[ptr++];
243
- var random = view.slice(ptr, ptr + 32); ptr += 32;
244
- var session_id_len = view[ptr++];
245
- var session_id = view.slice(ptr, ptr + session_id_len); ptr += session_id_len;
246
-
247
- var cipher_suites_len = (view[ptr++] << 8) | view[ptr++];
248
- var cipher_suites = [];
249
- for (var i = 0; i < cipher_suites_len; i += 2) {
250
- var code = (view[ptr++] << 8) | view[ptr++];
251
- cipher_suites.push(code);
252
- }
253
-
254
- var compression_methods_len = view[ptr++];
255
- var compression_methods = view.slice(ptr, ptr + compression_methods_len); ptr += compression_methods_len;
256
-
257
- var extensions_len = (view[ptr++] << 8) | view[ptr++];
258
- var extensions = [];
259
- var ext_end = ptr + extensions_len;
260
- while (ptr < ext_end) {
261
- var ext_type = (view[ptr++] << 8) | view[ptr++];
262
- var ext_len = (view[ptr++] << 8) | view[ptr++];
263
- var ext_data = view.slice(ptr, ptr + ext_len); ptr += ext_len;
264
- extensions.push({ type: ext_type, data: ext_data });
265
- }
266
-
267
- var sni = null;
268
- var key_shares = [];
269
- var supported_versions = [];
270
- var supported_groups = [];
271
- var signature_algorithms = [];
272
- var alpn = [];
273
- var max_fragment_length = null;
274
- var padding = null;
275
- var cookie = null;
276
- var psk_key_exchange_modes = [];
277
- var pre_shared_key = null;
278
- var renegotiation_info = null;
279
- var quic_transport_parameters = {
280
- original: {},
281
- initial_max_stream_data_bidi_local: undefined,
282
- initial_max_data: undefined,
283
- initial_max_streams_bidi: undefined,
284
- idle_timeout: undefined,
285
- max_packet_size: undefined,
286
- ack_delay_exponent: undefined,
287
- max_datagram_frame_size: undefined,
288
- web_accepted_origins: undefined
289
- };
290
-
291
- for (var ext of extensions) {
292
- var ext_view = new Uint8Array(ext.data);
293
- if (ext.type === 0x00) {
294
- var name_len = (ext_view[3] << 8) | ext_view[4];
295
- sni = new TextDecoder().decode(ext_view.slice(5, 5 + name_len));
296
- }
297
- if (ext.type === 0x33) {
298
- var ptr2 = 0;
299
- var list_len = (ext_view[ptr2++] << 8) | ext_view[ptr2++];
300
- var end = ptr2 + list_len;
301
- while (ptr2 < end) {
302
- var group = (ext_view[ptr2++] << 8) | ext_view[ptr2++];
303
- var key_len = (ext_view[ptr2++] << 8) | ext_view[ptr2++];
304
- var pubkey = ext_view.slice(ptr2, ptr2 + key_len);
305
- ptr2 += key_len;
306
- key_shares.push({ group, pubkey });
307
- }
308
- }
309
- if (ext.type === 0x2b) {
310
- var len = ext_view[0];
311
- for (var i = 1; i < 1 + len; i += 2) {
312
- supported_versions.push((ext_view[i] << 8) | ext_view[i + 1]);
313
- }
314
- }
315
- if (ext.type === 0x0a) {
316
- var len = (ext_view[0] << 8) | ext_view[1];
317
- for (var i = 2; i < 2 + len; i += 2) {
318
- supported_groups.push((ext_view[i] << 8) | ext_view[i + 1]);
319
- }
320
- }
321
- if (ext.type === 0x0d) {
322
- var len = (ext_view[0] << 8) | ext_view[1];
323
- for (var i = 2; i < 2 + len; i += 2) {
324
- signature_algorithms.push((ext_view[i] << 8) | ext_view[i + 1]);
325
- }
326
- }
327
- if (ext.type === 0x10) {
328
- var list_len = (ext_view[0] << 8) | ext_view[1];
329
- var i = 2;
330
- while (i < 2 + list_len) {
331
- var name_len = ext_view[i++];
332
- var proto = new TextDecoder().decode(ext_view.slice(i, i + name_len));
333
- alpn.push(proto);
334
- i += name_len;
335
- }
336
- }
337
- if (ext.type === 0x39) {
338
- var ext_data = ext.data;
339
- var ptr2 = 0;
340
- while (ptr2 < ext_data.length) {
341
- var idRes = readVarInt(ext_data, ptr2);
342
- if (!idRes) break;
343
- var id = idRes.value;
344
- ptr2 += idRes.byteLength;
345
-
346
- var lenRes = readVarInt(ext_data, ptr2);
347
- if (!lenRes) break;
348
- var len = lenRes.value;
349
- ptr2 += lenRes.byteLength;
350
-
351
- var value = ext_data.slice(ptr2, ptr2 + len);
352
- ptr2 += len;
353
-
354
- quic_transport_parameters.original[id] = value;
355
-
356
- function toNumber(bytes) {
357
- var n = 0;
358
- for (var i = 0; i < bytes.length; i++) {
359
- n = (n << 8) | bytes[i];
360
- }
361
- return n;
362
- }
363
-
364
- if (id === 0x00) quic_transport_parameters.original_destination_connection_id = value;
365
- if (id === 0x01) quic_transport_parameters.max_idle_timeout = toNumber(value);
366
- if (id === 0x03) quic_transport_parameters.max_packet_size = toNumber(value);
367
- if (id === 0x04) quic_transport_parameters.initial_max_data = toNumber(value);
368
- if (id === 0x05) quic_transport_parameters.initial_max_stream_data_bidi_local = toNumber(value);
369
- if (id === 0x08) quic_transport_parameters.initial_max_streams_bidi = toNumber(value);
370
- if (id === 0x0a) quic_transport_parameters.ack_delay_exponent = toNumber(value);
371
- if (id === 0x20) quic_transport_parameters.max_datagram_frame_size = toNumber(value);
372
- if (id === 0x2b603742) {
373
- try {
374
- quic_transport_parameters.web_accepted_origins = new TextDecoder().decode(value);
375
- } catch (e) {}
376
- }
377
- }
378
- }
379
- if (ext.type === 0x01) max_fragment_length = ext_view[0];
380
- if (ext.type === 0x15) padding = ext_view;
381
- if (ext.type === 0x002a) {
382
- var len = (ext_view[0] << 8) | ext_view[1];
383
- cookie = ext_view.slice(2, 2 + len);
384
- }
385
- if (ext.type === 0x2d) {
386
- var len = ext_view[0];
387
- for (var i = 1; i <= len; i++) {
388
- psk_key_exchange_modes.push(ext_view[i]);
389
- }
390
- }
391
- if (ext.type === 0x29) pre_shared_key = ext_view;
392
- if (ext.type === 0xff01) renegotiation_info = ext_view;
393
- }
394
-
395
- return {
396
- type: 'client_hello',
397
- legacy_version,
398
- random,
399
- session_id,
400
- cipher_suites,
401
- compression_methods,
402
- extensions,
403
- sni,
404
- key_shares,
405
- supported_versions,
406
- supported_groups,
407
- signature_algorithms,
408
- alpn,
409
- max_fragment_length,
410
- padding,
411
- cookie,
412
- psk_key_exchange_modes,
413
- pre_shared_key,
414
- renegotiation_info,
415
- quic_transport_parameters
416
- };
417
- }
418
-
419
-
420
-
421
- function parse_tls_client_hello(body) {
422
- var view = new Uint8Array(body);
423
- var ptr = 0;
424
-
425
- var legacy_version = (view[ptr++] << 8) | view[ptr++];
426
- var random = view.slice(ptr, ptr + 32); ptr += 32;
427
- var session_id_len = view[ptr++];
428
- var session_id = view.slice(ptr, ptr + session_id_len); ptr += session_id_len;
429
-
430
- var cipher_suites_len = (view[ptr++] << 8) | view[ptr++];
431
- var cipher_suites = [];
432
- for (var i = 0; i < cipher_suites_len; i += 2) {
433
- var code = (view[ptr++] << 8) | view[ptr++];
434
- cipher_suites.push(code);
435
- }
436
-
437
- var compression_methods_len = view[ptr++];
438
- var compression_methods = view.slice(ptr, ptr + compression_methods_len); ptr += compression_methods_len;
439
-
440
- var extensions_len = (view[ptr++] << 8) | view[ptr++];
441
- var extensions = [];
442
- var ext_end = ptr + extensions_len;
443
- while (ptr < ext_end) {
444
- var ext_type = (view[ptr++] << 8) | view[ptr++];
445
- var ext_len = (view[ptr++] << 8) | view[ptr++];
446
- var ext_data = view.slice(ptr, ptr + ext_len); ptr += ext_len;
447
- extensions.push({ type: ext_type, data: ext_data });
448
- }
449
-
450
- var sni = null;
451
- var key_shares = [];
452
- var supported_versions = [];
453
- var supported_groups = [];
454
- var signature_algorithms = [];
455
- var alpn = [];
456
- var max_fragment_length = null;
457
- var padding = null;
458
- var cookie = null;
459
- var psk_key_exchange_modes = [];
460
- var pre_shared_key = null;
461
- var renegotiation_info = null;
462
- var quic_transport_parameters_raw = null;
463
-
464
- for (var ext of extensions) {
465
- var ext_view = new Uint8Array(ext.data);
466
- if (ext.type === 0x00) { // SNI
467
- var list_len = (ext_view[0] << 8) | ext_view[1];
468
- var name_type = ext_view[2];
469
- var name_len = (ext_view[3] << 8) | ext_view[4];
470
- var name = new TextDecoder().decode(ext_view.slice(5, 5 + name_len));
471
- sni = name;
472
- }
473
- if (ext.type === 0x33) {
474
- var ptr2 = 0;
475
- var list_len = (ext_view[ptr2++] << 8) | ext_view[ptr2++];
476
- var end = ptr2 + list_len;
477
- while (ptr2 < end) {
478
- var group = (ext_view[ptr2++] << 8) | ext_view[ptr2++];
479
- var key_len = (ext_view[ptr2++] << 8) | ext_view[ptr2++];
480
- var pubkey = ext_view.slice(ptr2, ptr2 + key_len);
481
- ptr2 += key_len;
482
- key_shares.push({ group, pubkey });
483
- }
484
-
485
- }
486
- if (ext.type === 0x2b) { // supported_versions
487
- var len = ext_view[0];
488
- for (var i = 1; i < 1 + len; i += 2) {
489
- var ver = (ext_view[i] << 8) | ext_view[i + 1];
490
- supported_versions.push(ver);
491
- }
492
- }
493
- if (ext.type === 0x0a) { // supported_groups
494
- var len = (ext_view[0] << 8) | ext_view[1];
495
- for (var i = 2; i < 2 + len; i += 2) {
496
- supported_groups.push((ext_view[i] << 8) | ext_view[i + 1]);
497
- }
498
- }
499
- if (ext.type === 0x0d) { // signature_algorithms
500
- var len = (ext_view[0] << 8) | ext_view[1];
501
- for (var i = 2; i < 2 + len; i += 2) {
502
- signature_algorithms.push((ext_view[i] << 8) | ext_view[i + 1]);
503
- }
504
- }
505
- if (ext.type === 0x10) { // ALPN
506
- var list_len = (ext_view[0] << 8) | ext_view[1];
507
- var i = 2;
508
- while (i < 2 + list_len) {
509
- var name_len = ext_view[i++];
510
- var proto = new TextDecoder().decode(ext_view.slice(i, i + name_len));
511
- alpn.push(proto);
512
- i += name_len;
513
- }
514
- }
515
- if (ext.type === 0x39) { // quic_transport_parameters
516
- quic_transport_parameters_raw = ext.data;
517
- }
518
- if (ext.type === 0x01) { // Max Fragment Length
519
- max_fragment_length = ext_view[0];
520
- }
521
- if (ext.type === 0x15) { // Padding
522
- padding = ext_view;
523
- }
524
- if (ext.type === 0x002a) { // Cookie
525
- var len = (ext_view[0] << 8) | ext_view[1];
526
- cookie = ext_view.slice(2, 2 + len);
527
- }
528
- if (ext.type === 0x2d) { // PSK Key Exchange Modes
529
- var len = ext_view[0];
530
- for (var i = 1; i <= len; i++) {
531
- psk_key_exchange_modes.push(ext_view[i]);
532
- }
533
- }
534
- if (ext.type === 0x29) { // PreSharedKey (placeholder)
535
- pre_shared_key = ext_view;
536
- }
537
- if (ext.type === 0xff01) { // Renegotiation Info
538
- renegotiation_info = ext_view;
539
- }
540
- }
541
-
542
- return {
543
- type: 'client_hello',
544
- legacy_version,
545
- random,
546
- session_id,
547
- cipher_suites,
548
- compression_methods,
549
- extensions,
550
- sni,
551
- key_shares,
552
- supported_versions,
553
- supported_groups,
554
- signature_algorithms,
555
- alpn,
556
- max_fragment_length,
557
- padding,
558
- cookie,
559
- psk_key_exchange_modes,
560
- pre_shared_key,
561
- renegotiation_info,
562
- quic_transport_parameters_raw
563
- };
564
- }
565
-
566
- ////////////////////////////////
567
-
568
- function hmac2(hash, key, data) {
569
- return new Uint8Array(crypto.createHmac(hash, key).update(data).digest());
570
- }
571
- function hkdf_extract2(salt, ikm, hash_func) {
572
- return nobleHashes.hkdf_extract(hash_func, ikm, salt);
573
- }
574
-
575
-
576
- function hkdf_expand2(prk, info, length, hash_func) {
577
- return nobleHashes.hkdf_expand(hash_func, prk, info, length);
578
- }
579
-
580
-
581
-
582
- function build_hkdf_label(label, context, length) {
583
- var prefix = "tls13 ";
584
- var full = new TextEncoder().encode(prefix + label);
585
-
586
- var info = new Uint8Array(
587
- 2 + 1 + full.length + 1 + context.length);
588
-
589
- // length (2-bytes, BE)
590
- info[0] = (length >> 8) & 0xff;
591
- info[1] = length & 0xff;
592
-
593
- // label length + bytes
594
- info[2] = full.length;
595
- info.set(full, 3);
596
-
597
- // context length + bytes
598
- var ctxOfs = 3 + full.length;
599
- info[ctxOfs] = context.length;
600
- info.set(context, ctxOfs + 1);
601
-
602
- return info;
603
- }
604
-
605
- function hkdf_expand_label(secret, label, context, length, hash_func) {
606
- var info = build_hkdf_label(label, context, length);
607
- return hkdf_expand2(secret, info, length, hash_func); // hash = sha384/sha256
608
- }
609
-
610
-
611
-
612
-
613
- function aead_decrypt(key, iv, packetNumber, ciphertextWithTag, aad, callback) {
614
- try {
615
- // יצירת nonce לפי QUIC (IV XOR packetNumber)
616
- var nonce = new Uint8Array(iv.length);
617
- for (var i = 0; i < iv.length; i++) {
618
- var pnIndex = iv.length - 1 - i;
619
- var pnByte = (packetNumber >>> (8 * i)) & 0xff;
620
- nonce[pnIndex] = iv[pnIndex] ^ pnByte;
621
- }
622
-
623
- var tag = ciphertextWithTag.slice(-16);
624
- var ciphertext = ciphertextWithTag.slice(0, -16);
625
-
626
- var algo = key.length === 32 ? 'aes-256-gcm' :
627
- key.length === 16 ? 'aes-128-gcm' :
628
- (() => { throw new Error("Unsupported key length: " + key.length); })();
629
-
630
- var decipher = crypto.createDecipheriv(algo, key, nonce);
631
- decipher.setAuthTag(tag);
632
- decipher.setAAD(aad);
633
-
634
- var decrypted = decipher.update(ciphertext);
635
- decipher.final();
636
-
637
- callback(null, decrypted);
638
- } catch (e) {
639
- callback(e);
640
- }
641
- }
642
-
643
- function aes_gcm_decrypt(ciphertext, tag, key, nonce, aad) {
644
- try {
645
- var algo = key.length === 32 ? 'aes-256-gcm' :
646
- key.length === 16 ? 'aes-128-gcm' :
647
- (() => { throw new Error("Unsupported key length: " + key.length); })();
648
-
649
- var decipher = crypto.createDecipheriv(
650
- algo,
651
- Buffer.from(key),
652
- Buffer.from(nonce)
653
- );
654
-
655
- decipher.setAuthTag(Buffer.from(tag));
656
- decipher.setAAD(Buffer.from(aad));
657
-
658
- var decrypted = Buffer.concat([
659
- decipher.update(Buffer.from(ciphertext)),
660
- decipher.final()
661
- ]);
662
-
663
- //console.log("✅ Decryption success!");
664
- return new Uint8Array(decrypted);
665
- } catch (e) {
666
- return null;
667
- }
668
- }
669
-
670
- var INITIAL_SALTS = {
671
- // QUIC v1 (RFC 9001)
672
- 0x00000001: new Uint8Array([
673
- 0x38, 0x76, 0x2c, 0xf7, 0xf5, 0x59, 0x34, 0xb3,
674
- 0x4d, 0x17, 0x9a, 0xe6, 0xa4, 0xc8, 0x0c, 0xad,
675
- 0xcc, 0xbb, 0x7f, 0x0a
676
- ]),
677
-
678
- // QUIC draft-29 (HTTP/3 version h3-29)
679
- 0xff00001d: new Uint8Array([
680
- 0xaf, 0xbf, 0xec, 0x28, 0x99, 0x93, 0xd2, 0x4c,
681
- 0x9e, 0x97, 0x86, 0xf1, 0x9c, 0x61, 0x11, 0xe0,
682
- 0x43, 0x90, 0xa8, 0x99
683
- ]),
684
-
685
- // QUIC draft-32 (h3-32)
686
- 0xff000020: new Uint8Array([
687
- 0x7f, 0xbc, 0xdb, 0x0e, 0x7c, 0x66, 0xbb, 0x77,
688
- 0x7b, 0xe3, 0x0e, 0xbd, 0x5f, 0xa5, 0x15, 0x87,
689
- 0x3d, 0x8d, 0x6e, 0x67
690
- ]),
691
-
692
- // Google QUIC v50 ("Q050") — נדיר יותר אבל נתמך בדפדפנים מסוימים
693
- 0x51303530: new Uint8Array([
694
- 0x69, 0x45, 0x6f, 0xbe, 0xf1, 0x6e, 0xd7, 0xdc,
695
- 0x48, 0x15, 0x9d, 0x98, 0xd0, 0x7f, 0x5c, 0x3c,
696
- 0x3d, 0x5a, 0xa7, 0x0a
697
- ])
698
- };
699
-
700
- function quic_derive_init_secrets(client_dcid, version, direction) {
701
- var hash_func = sha256;
702
- //console.log(version);
703
- var salt = INITIAL_SALTS[version] || null;
704
- if (!salt) throw new Error("Unsupported QUIC version: 0x" + version.toString(16));
705
-
706
- var label = direction === 'read' ? 'client in' : 'server in';
707
- var initial_secret = hkdf_extract2(salt, client_dcid, hash_func);
708
-
709
- var initial_secret2 = hkdf_expand_label(
710
- initial_secret,
711
- label,
712
- new Uint8Array(0),
713
- 32,
714
- hash_func
715
- );
716
-
717
- var key = hkdf_expand_label(initial_secret2, 'quic key', new Uint8Array(0), 16, hash_func); // AES-128-GCM
718
- var iv = hkdf_expand_label(initial_secret2, 'quic iv', new Uint8Array(0), 12, hash_func);
719
- var hp = hkdf_expand_label(initial_secret2, 'quic hp', new Uint8Array(0), 16, hash_func);
720
-
721
- return { key, iv, hp };
722
- }
723
-
724
-
725
- function quic_derive_from_tls_secrets(traffic_secret, hash_name) {
726
- var hash_func = getHashFn(hash_name);
727
-
728
- if(traffic_secret){
729
- var key = hkdf_expand_label(traffic_secret, 'quic key', new Uint8Array(0), 16, hash_func);
730
- var iv = hkdf_expand_label(traffic_secret, 'quic iv', new Uint8Array(0), 12, hash_func);
731
- var hp = hkdf_expand_label(traffic_secret, 'quic hp', new Uint8Array(0), 16, hash_func);
732
-
733
- return { key, iv, hp };
734
- }
735
- }
736
-
737
-
738
-
739
- function compute_nonce(iv, packetNumber) {
740
- var nonce = new Uint8Array(iv); // עותק של ה־IV המקורי (12 בתים)
741
- var pnBuffer = new Uint8Array(12); // 12 בתים, מיושר לימין
742
-
743
- // הכנס את packetNumber לימין של pnBuffer
744
- var n = packetNumber;
745
- for (var i = 11; n > 0 && i >= 0; i--) {
746
- pnBuffer[i] = n & 0xff;
747
- n >>= 8;
748
- }
749
-
750
- // בצע XOR בין ה־IV לבין pnBuffer
751
- for (var i = 0; i < 12; i++) {
752
- nonce[i] ^= pnBuffer[i];
753
- }
754
-
755
- return nonce;
756
- }
757
-
758
-
759
- function aes_ecb_encrypt(keyBytes, plaintext) {
760
- if (keyBytes.length !== 16 && keyBytes.length !== 24 && keyBytes.length !== 32) {
761
- throw new Error("Invalid AES key size");
762
- }
763
-
764
- if (plaintext.length % 16 !== 0) {
765
- throw new Error("Plaintext must be a multiple of 16 bytes");
766
- }
767
-
768
- var cipher = crypto.createCipheriv('aes-' + (keyBytes.length * 8) + '-ecb', keyBytes, null);
769
- cipher.setAutoPadding(false);
770
-
771
- var encrypted = Buffer.concat([cipher.update(plaintext), cipher.final()]);
772
- return new Uint8Array(encrypted);
773
- }
774
-
775
-
776
- function aead_encrypt(key, iv, packetNumber, plaintext, aad) {
777
- try {
778
- var algo = key.length === 32 ? 'aes-256-gcm' :
779
- key.length === 16 ? 'aes-128-gcm' :
780
- (() => { throw new Error("Unsupported key length: " + key.length); })();
781
-
782
- var nonce = compute_nonce(iv, packetNumber);
783
-
784
- var cipher = crypto.createCipheriv(algo, Buffer.from(key), Buffer.from(nonce));
785
- cipher.setAAD(Buffer.from(aad));
786
-
787
- var encrypted = Buffer.concat([
788
- cipher.update(Buffer.from(plaintext)),
789
- cipher.final()
790
- ]);
791
- var tag = cipher.getAuthTag();
792
-
793
- var result = new Uint8Array(encrypted.length + tag.length);
794
- result.set(encrypted, 0);
795
- result.set(tag, encrypted.length);
796
-
797
- return result;
798
-
799
- } catch (e) {
800
- return null;
801
- }
802
- }
803
-
804
-
805
-
806
-
807
- function apply_header_protection(packet, pnOffset, hpKey, pnLength) {
808
- var sample = packet.slice(pnOffset + 4, pnOffset + 4 + 16);
809
- if (sample.length < 16) throw new Error("Not enough bytes for header protection sample");
810
-
811
- var maskFull = aes_ecb_encrypt(hpKey, sample);
812
- var mask = maskFull.slice(0, 5);
813
-
814
- var firstByte = packet[0];
815
- var isLongHeader = (firstByte & 0x80) !== 0;
816
-
817
- if (isLongHeader) {
818
- packet[0] ^= (mask[0] & 0x0f); // רק 4 ביטים אחרונים
819
- } else {
820
- packet[0] ^= (mask[0] & 0x1f); // ל־Short Header
821
- }
822
-
823
- for (var i = 0; i < pnLength; i++) {
824
- packet[pnOffset + i] ^= mask[1 + i];
825
- }
826
-
827
- return packet;
828
- }
829
-
830
-
831
-
832
-
833
- function aes128ecb(sample,hpKey) {
834
- var cipher = crypto.createCipheriv('aes-128-ecb', Buffer.from(hpKey), null);
835
- cipher.setAutoPadding(false);
836
- var input = Buffer.from(sample);
837
- return new Uint8Array(Buffer.concat([cipher.update(input), cipher.final()]));
838
- }
839
-
840
- function expandPacketNumber(truncated, pnLen, largestReceived) {
841
- var pnWin = 1 << (pnLen * 8);
842
- var pnHalf = pnWin >>> 1;
843
- var expected = largestReceived + 1;
844
- return truncated + pnWin * Math.floor((expected - truncated + pnHalf) / pnWin);
845
- }
846
-
847
- function decode_packet_number(array, offset, pnLength) {
848
- var value = 0;
849
- for (var i = 0; i < pnLength; i++) {
850
- value = (value << 8) | array[offset + i];
851
- }
852
- return value;
853
- }
854
-
855
- function decode_and_expand_packet_number(array, offset, pnLength, largestReceived) {
856
- var truncated = decode_packet_number(array, offset, pnLength);
857
- return expandPacketNumber(truncated, pnLength, largestReceived);
858
- }
859
-
860
- function remove_header_protection(array, pnOffset, hpKey, isShort) {
861
- // Step 1: קח sample של 16 בתים מתוך ה־payload אחרי pnOffset + 4
862
- var sampleOffset = pnOffset + 4;
863
- var sample = array.slice(sampleOffset, sampleOffset + 16);
864
-
865
- var mask = aes128ecb(sample, hpKey).slice(0, 5); // ECB with no IV
866
-
867
- // Step 2: הסר הגנה מה־first byte
868
- var firstByte = array[0];
869
-
870
- if (isShort) {
871
- // Short Header: רק 5 הביטים הנמוכים מוצפנים
872
- array[0] ^= mask[0] & 0x1f;
873
- } else {
874
- // Long Header: רק 4 הביטים הנמוכים מוצפנים
875
- array[0] ^= mask[0] & 0x0f;
876
- }
877
-
878
- // Step 3: הסר הגנה מה־packet number (pnLength נקבע מתוך הביטים עכשיו)
879
- var pnLength = (array[0] & 0x03) + 1;
880
-
881
- for (var i = 0; i < pnLength; i++) {
882
- array[pnOffset + i] ^= mask[1 + i];
883
- }
884
-
885
- return pnLength;
886
- }
887
-
888
-
889
- function decrypt_quic_packet(array, read_key, read_iv, read_hp, dcid, largest_pn) {
890
- if (!(array instanceof Uint8Array)) throw new Error("Invalid input");
891
-
892
- var firstByte = array[0];
893
- var isShort = (firstByte & 0x80) === 0;
894
- var isLong = !isShort;
895
-
896
- var keyPhase = false;
897
- var pnOffset = 0;
898
- var pnLength = 0;
899
- var aad = null;
900
- var ciphertext = null;
901
- var tag = null;
902
- var packetNumber = null;
903
- var nonce = null;
904
-
905
- if (isLong) {
906
- // ---------- ניתוח Long Header ----------
907
- var view = new DataView(array.buffer, array.byteOffset, array.byteLength);
908
- var version = view.getUint32(1);
909
- var dcidLen = array[5];
910
-
911
- var offset = 6;
912
- var parsed_dcid = array.slice(offset, offset + dcidLen);
913
- offset += dcidLen;
914
-
915
- var scidLen = array[offset++];
916
- var scid = array.slice(offset, offset + scidLen);
917
- offset += scidLen;
918
-
919
- var typeBits = (firstByte & 0x30) >> 4;
920
- var typeMap = ['initial', '0rtt', 'handshake', 'retry'];
921
- var packetType = typeMap[typeBits];
922
-
923
- if (packetType === 'initial') {
924
- var tokenLen = readVarInt(array, offset);
925
- offset += tokenLen.byteLength + tokenLen.value;
926
- }
927
-
928
- var len = readVarInt(array, offset);
929
- offset += len.byteLength;
930
-
931
- pnOffset = offset;
932
-
933
- // הסרת הגנת כותרת
934
- pnLength = remove_header_protection(array, pnOffset, read_hp, false);
935
-
936
- if(pnLength!==null){
937
- packetNumber = decode_and_expand_packet_number(array, pnOffset, pnLength, largest_pn);
938
- nonce = compute_nonce(read_iv, packetNumber);
939
-
940
- var payloadStart = pnOffset + pnLength;
941
- var payloadLength = len.value - pnLength;
942
- var payloadEnd = payloadStart + payloadLength;
943
-
944
- if (payloadEnd > array.length) throw new Error("Truncated long header packet");
945
-
946
- var payload = array.slice(payloadStart, payloadEnd);
947
- if (payload.length < 16) throw new Error("Encrypted payload too short");
948
-
949
- ciphertext = payload.slice(0, payload.length - 16);
950
- tag = payload.slice(payload.length - 16);
951
- aad = array.slice(0, pnOffset + pnLength);
952
- }else{
953
- return null;
954
- }
955
-
956
- } else {
957
- // ---------- ניתוח Short Header ----------
958
- // פורמט: 1 byte header + DCID + Packet Number + Payload
959
-
960
- var dcidLen = dcid.length;
961
- pnOffset = 1 + dcidLen;
962
-
963
- // הסרת הגנת כותרת
964
- pnLength = remove_header_protection(array, pnOffset, read_hp, true);
965
-
966
- if(pnLength!==null){
967
- keyPhase = Boolean((array[0] & 0x04) >>> 2);
968
-
969
- packetNumber = decode_and_expand_packet_number(array, pnOffset, pnLength, largest_pn);
970
- nonce = compute_nonce(read_iv, packetNumber);
971
-
972
- var payloadStart = pnOffset + pnLength;
973
- var payload = array.slice(payloadStart);
974
- if (payload.length < 16) throw new Error("Encrypted payload too short");
975
-
976
- ciphertext = payload.slice(0, payload.length - 16);
977
- tag = payload.slice(payload.length - 16);
978
- aad = array.slice(0, pnOffset + pnLength);
979
- }else{
980
- return null;
981
- }
982
-
983
- }
984
-
985
- var plaintext = aes_gcm_decrypt(ciphertext, tag, read_key, nonce, aad);
986
-
987
- return {
988
- packet_number: packetNumber,
989
- key_phase: keyPhase,
990
- plaintext
991
- };
992
- }
993
-
994
-
995
-
996
- function extract_tls_messages_from_chunks(chunks, from_offset) {
997
- var offset = from_offset;
998
- var buffers = [];
999
-
1000
- // מאחדים רצף שלם של chunks מה־offset הנוכחי
1001
- while (chunks[offset]) {
1002
- buffers.push(chunks[offset]);
1003
- offset += chunks[offset].length;
1004
- }
1005
-
1006
- // אם לא קיבלנו שום דבר – נחזיר ריק
1007
- if (buffers.length === 0) return false;
1008
-
1009
- var combined = concatUint8Arrays(buffers);
1010
- var tls_messages = [];
1011
- var i = 0;
1012
-
1013
- while (i + 4 <= combined.length) {
1014
- var msgType = combined[i];
1015
- var length = (combined[i + 1] << 16) | (combined[i + 2] << 8) | combined[i + 3];
1016
-
1017
- if (i + 4 + length > combined.length) break; // הודעה לא שלמה – עוצרים
1018
-
1019
- var msg = combined.slice(i, i + 4 + length);
1020
- tls_messages.push(msg);
1021
- i += 4 + length;
1022
- }
1023
-
1024
- // עדכון offset רק עד איפה שעברנו בפועל
1025
- if (i > 0) {
1026
- // מוחקים את החלקים המאוחדים מתוך chunks
1027
- var cleanupOffset = from_offset;
1028
- while (cleanupOffset < from_offset + i) {
1029
- var chunk = chunks[cleanupOffset];
1030
- delete chunks[cleanupOffset];
1031
- cleanupOffset += chunk.length;
1032
- }
1033
-
1034
- // השארית – אם קיימת – נחזיר אותה כ־chunk חדש
1035
- if (i < combined.length) {
1036
- var leftover = combined.slice(i);
1037
- chunks[cleanupOffset] = leftover;
1038
- }
1039
-
1040
- // נעדכן את currentOffset
1041
- from_offset += i;
1042
- }
1043
-
1044
- return {tls_messages,new_from_offset: from_offset};
1045
- }
1046
-
1047
-
1048
-
1049
- function encode_version(version) {
1050
- return new Uint8Array([
1051
- (version >>> 24) & 0xff,
1052
- (version >>> 16) & 0xff,
1053
- (version >>> 8) & 0xff,
1054
- version & 0xff
1055
- ]);
1056
- }
1057
-
1058
- function build_quic_header(packetType, dcid, scid, token, lengthField, pnLen) {
1059
- var hdr = [];
1060
- var firstByte;
1061
-
1062
- // שלב 1: הגדרת הביט הראשון לפי סוג הפאקט
1063
- if (packetType === 'initial') {
1064
- firstByte = 0xC0 | ((pnLen - 1) & 0x03); // Long Header, Initial
1065
- } else if (packetType === 'handshake') {
1066
- firstByte = 0xE0 | ((pnLen - 1) & 0x03); // Long Header, Handshake
1067
- } else if (packetType === '0rtt') {
1068
- firstByte = 0xD0 | ((pnLen - 1) & 0x03); // Long Header, 0-RTT
1069
- } else if (packetType === '1rtt') {
1070
- firstByte = 0x40 | ((pnLen - 1) & 0x03); // Short Header
1071
- hdr.push(Uint8Array.of(firstByte));
1072
- hdr.push(dcid); // ב־short header, זהו ה־Destination CID בלבד
1073
- return {
1074
- header: concatUint8Arrays(hdr),
1075
- packetNumberOffset: hdr.reduce((sum, u8) => sum + u8.length, 0)
1076
- };
1077
- } else {
1078
- throw new Error('Unsupported packet type: ' + packetType);
1079
- }
1080
-
1081
- // שלב 2: Header בסיסי לכל long header
1082
- hdr.push(Uint8Array.of(firstByte));
1083
- hdr.push(encode_version(0x00000001)); // גרסה (4 בייטים)
1084
- hdr.push(writeVarInt(dcid.length), dcid);
1085
- hdr.push(writeVarInt(scid.length), scid);
1086
-
1087
- // שלב 3: רק ל־Initial מוסיפים טוקן
1088
- if (packetType === 'initial') {
1089
- if (!token) token = new Uint8Array(0);
1090
- hdr.push(writeVarInt(token.length), token);
1091
- }
1092
-
1093
- // שלב 4: שדה אורך (Length), חובה
1094
- hdr.push(lengthField);
1095
-
1096
- // שלב 5: חישוב נקודת התחלה של packet number (מופיע מיד לאחר header)
1097
- var header = concatUint8Arrays(hdr);
1098
- return {
1099
- header: header,
1100
- packetNumberOffset: header.length
1101
- };
1102
- }
1103
-
1104
-
1105
-
1106
- function encrypt_quic_packet(packetType, encodedFrames, writeKey, writeIv, writeHp, packetNumber, dcid, scid, token) {
1107
-
1108
- var pnLength;
1109
- if (packetNumber <= 0xff) pnLength = 1;
1110
- else if (packetNumber <= 0xffff) pnLength = 2;
1111
- else if (packetNumber <= 0xffffff) pnLength = 3;
1112
- else pnLength = 4;
1113
-
1114
- var pnFull = new Uint8Array(4);
1115
- pnFull[0] = (packetNumber >>> 24) & 0xff;
1116
- pnFull[1] = (packetNumber >>> 16) & 0xff;
1117
- pnFull[2] = (packetNumber >>> 8) & 0xff;
1118
- pnFull[3] = packetNumber & 0xff;
1119
- var packetNumberField = pnFull.slice(4 - pnLength);
1120
-
1121
- var unprotectedPayloadLength = encodedFrames.length + pnLength + 16;
1122
- var lengthField = writeVarInt(unprotectedPayloadLength);
1123
- var headerInfo = build_quic_header(packetType, dcid, scid, token, lengthField, pnLength);
1124
-
1125
- var header = headerInfo.header;
1126
- var packetNumberOffset = headerInfo.packetNumberOffset;
1127
-
1128
- // בונים AAD
1129
- var fullHeader = concatUint8Arrays([header, packetNumberField]);
1130
-
1131
- // ✨ הוספת padding אם צריך כדי לאפשר sample
1132
- var minSampleLength = 32; // או 32 ל־ChaCha20
1133
- var minTotalLength = packetNumberOffset + pnLength + minSampleLength;
1134
- var fullLength = header.length + pnLength + encodedFrames.length + 16; // 16 = GCM tag
1135
-
1136
- if (fullLength < minTotalLength) {
1137
- var extraPadding = minTotalLength - (header.length + pnLength + encodedFrames.length);
1138
- var padded = new Uint8Array(encodedFrames.length + extraPadding);
1139
- padded.set(encodedFrames, 0);
1140
- encodedFrames = padded;
1141
- // חשוב! גם unprotectedPayloadLength צריך להתעדכן
1142
- unprotectedPayloadLength = encodedFrames.length + pnLength + 16;
1143
- lengthField = writeVarInt(unprotectedPayloadLength);
1144
- headerInfo = build_quic_header(packetType, dcid, scid, token, lengthField, pnLength);
1145
- header = headerInfo.header;
1146
- packetNumberOffset = headerInfo.packetNumberOffset;
1147
- fullHeader = concatUint8Arrays([header, packetNumberField]);
1148
- }
1149
-
1150
- var ciphertext = aead_encrypt(writeKey, writeIv, packetNumber, encodedFrames, fullHeader);
1151
- if (ciphertext == null) return null;
1152
-
1153
- var fullPacket = concatUint8Arrays([
1154
- header,
1155
- packetNumberField,
1156
- ciphertext
1157
- ]);
1158
-
1159
- return apply_header_protection(fullPacket, packetNumberOffset, writeHp, pnLength);
1160
- }
1161
-
1162
- function encrypt_quic_packet2(packetType, encodedFrames, writeKey, writeIv, writeHp, packetNumber, dcid, scid, token) {
1163
-
1164
- // 2. קביעת אורך packet number
1165
- var pnLength;
1166
- if (packetNumber <= 0xff) pnLength = 1;
1167
- else if (packetNumber <= 0xffff) pnLength = 2;
1168
- else if (packetNumber <= 0xffffff) pnLength = 3;
1169
- else pnLength = 4;
1170
-
1171
- // 3. חיתוך שדה ה־packet number לבתים
1172
- var pnFull = new Uint8Array(4);
1173
- pnFull[0] = (packetNumber >>> 24) & 0xff;
1174
- pnFull[1] = (packetNumber >>> 16) & 0xff;
1175
- pnFull[2] = (packetNumber >>> 8) & 0xff;
1176
- pnFull[3] = packetNumber & 0xff;
1177
- var packetNumberField = pnFull.slice(4 - pnLength);
1178
-
1179
- // 4. נבנה header בלי packet number
1180
-
1181
- var unprotectedPayloadLength = encodedFrames.length + pnLength + 16;
1182
- var lengthField = writeVarInt(unprotectedPayloadLength);
1183
- var headerInfo = build_quic_header(packetType, dcid, scid, token, lengthField, pnLength);
1184
-
1185
- var header = headerInfo.header; // עד לפני packet number
1186
- var packetNumberOffset = headerInfo.packetNumberOffset;
1187
-
1188
- // 5. AAD כולל את header + packet number (לפני ההצפנה)
1189
- var fullHeader = concatUint8Arrays([header, packetNumberField]);
1190
-
1191
- // 6. הצפנת המטען
1192
- var ciphertext = aead_encrypt(writeKey, writeIv, packetNumber, encodedFrames, fullHeader);
1193
- if (ciphertext == null) return null;
1194
-
1195
- // 7. בניית הפקט המלא לפני header protection
1196
- var fullPacket = concatUint8Arrays([
1197
- header,
1198
- packetNumberField,
1199
- ciphertext
1200
- ]);
1201
-
1202
- // 8. החלת הגנת כותרת (XOR)
1203
- return apply_header_protection(fullPacket, packetNumberOffset, writeHp, pnLength);
1204
- }
1205
-
1206
-
1207
-
1208
-
1209
- function encode_quic_frames(frames) {
1210
- var parts = [];
1211
- var i;
1212
-
1213
- for (i = 0; i < frames.length; i++) {
1214
- var frame = frames[i];
1215
-
1216
- if (frame.type === 'padding') {
1217
- var pad = new Uint8Array(frame.length);
1218
- for (var j = 0; j < pad.length; j++) pad[j] = 0x00;
1219
- parts.push(pad);
1220
-
1221
- } else if (frame.type === 'ping') {
1222
- parts.push(new Uint8Array([0x01]));
1223
-
1224
- } else if (frame.type === 'ack') {
1225
- var hasECN = frame.ecn !== null && frame.ecn !== undefined;
1226
- var typeByte = hasECN ? 0x03 : 0x02;
1227
-
1228
- var b1 = writeVarInt(frame.largest); // Largest Acknowledged
1229
- var b2 = writeVarInt(frame.delay); // ACK Delay
1230
- var b3 = writeVarInt(frame.ranges.length); // ACK Range Count
1231
- var b4 = writeVarInt(frame.firstRange != null ? frame.firstRange : 0);
1232
-
1233
- var temp = [new Uint8Array([typeByte]), b1, b2, b3, b4];
1234
-
1235
-
1236
- for (j = 0; j < frame.ranges.length; j++) {
1237
- var gap = writeVarInt(frame.ranges[j].gap); // Gap to next range
1238
- var len = writeVarInt(frame.ranges[j].length); // Length of next range
1239
- temp.push(gap, len);
1240
- }
1241
-
1242
- if (hasECN) {
1243
- temp.push(
1244
- writeVarInt(frame.ecn.ect0),
1245
- writeVarInt(frame.ecn.ect1),
1246
- writeVarInt(frame.ecn.ce)
1247
- );
1248
- }
1249
-
1250
- parts.push(concatUint8Arrays(temp));
1251
-
1252
- } else if (frame.type === 'reset_stream') {
1253
- var id = writeVarInt(frame.id);
1254
- var err = new Uint8Array([frame.error >> 8, frame.error & 0xff]);
1255
- var size = writeVarInt(frame.finalSize);
1256
- parts.push(concatUint8Arrays([
1257
- new Uint8Array([0x04]), id, err, size
1258
- ]));
1259
-
1260
- } else if (frame.type === 'stop_sending') {
1261
- var id = writeVarInt(frame.id);
1262
- var err = new Uint8Array([frame.error >> 8, frame.error & 0xff]);
1263
- parts.push(concatUint8Arrays([
1264
- new Uint8Array([0x05]), id, err
1265
- ]));
1266
-
1267
- } else if (frame.type === 'crypto') {
1268
- var off = writeVarInt(frame.offset);
1269
- var len = writeVarInt(frame.data.length);
1270
- parts.push(concatUint8Arrays([
1271
- new Uint8Array([0x06]), off, len, frame.data
1272
- ]));
1273
-
1274
- } else if (frame.type === 'new_token') {
1275
- var len = writeVarInt(frame.token.length);
1276
- parts.push(concatUint8Arrays([
1277
- new Uint8Array([0x07]), len, frame.token
1278
- ]));
1279
-
1280
- } else if (frame.type === 'stream') {
1281
-
1282
- var typeByte = 0x08;
1283
-
1284
- var hasOffset = (frame.offset != null);
1285
- var hasLen = (frame.data && frame.data.length > 0);
1286
- var hasFin = !!frame.fin;
1287
-
1288
- if (hasOffset) typeByte |= 0x04;
1289
- if (hasLen) typeByte |= 0x02;
1290
- if (hasFin) typeByte |= 0x01;
1291
-
1292
- var id = writeVarInt(frame.id);
1293
- var off = hasOffset ? writeVarInt(frame.offset) : new Uint8Array(0);
1294
- var len = hasLen ? writeVarInt(frame.data.length) : new Uint8Array(0);
1295
-
1296
- parts.push(concatUint8Arrays([
1297
- new Uint8Array([typeByte]), id, off, len, frame.data
1298
- ]));
1299
-
1300
- } else if (frame.type === 'max_data') {
1301
- parts.push(concatUint8Arrays([
1302
- new Uint8Array([0x09]), writeVarInt(frame.max)
1303
- ]));
1304
-
1305
- } else if (frame.type === 'max_stream_data') {
1306
- parts.push(concatUint8Arrays([
1307
- new Uint8Array([0x0a]), writeVarInt(frame.id), writeVarInt(frame.max)
1308
- ]));
1309
-
1310
- } else if (frame.type === 'max_streams_bidi' || frame.type === 'max_streams_uni') {
1311
- var code = frame.type === 'max_streams_bidi' ? 0x0b : 0x0c;
1312
- parts.push(concatUint8Arrays([
1313
- new Uint8Array([code]), writeVarInt(frame.max)
1314
- ]));
1315
-
1316
- } else if (frame.type === 'data_blocked') {
1317
- parts.push(concatUint8Arrays([
1318
- new Uint8Array([0x0d]), writeVarInt(frame.limit)
1319
- ]));
1320
-
1321
- } else if (frame.type === 'stream_data_blocked') {
1322
- parts.push(concatUint8Arrays([
1323
- new Uint8Array([0x0e]), writeVarInt(frame.id), writeVarInt(frame.limit)
1324
- ]));
1325
-
1326
- } else if (frame.type === 'streams_blocked_bidi') {
1327
- parts.push(concatUint8Arrays([
1328
- new Uint8Array([0x0f]), writeVarInt(frame.limit)
1329
- ]));
1330
-
1331
- } else if (frame.type === 'streams_blocked_uni') {
1332
- parts.push(concatUint8Arrays([
1333
- new Uint8Array([0x10]), writeVarInt(frame.limit)
1334
- ]));
1335
-
1336
- } else if (frame.type === 'new_connection_id') {
1337
- parts.push(concatUint8Arrays([
1338
- new Uint8Array([0x11]),
1339
- writeVarInt(frame.seq),
1340
- writeVarInt(frame.retire),
1341
- new Uint8Array([frame.connId.length]),
1342
- frame.connId,
1343
- frame.token
1344
- ]));
1345
-
1346
- } else if (frame.type === 'retire_connection_id') {
1347
- parts.push(concatUint8Arrays([
1348
- new Uint8Array([0x12]),
1349
- writeVarInt(frame.seq)
1350
- ]));
1351
-
1352
- } else if (frame.type === 'path_challenge' || frame.type === 'path_response') {
1353
- var code = frame.type === 'path_challenge' ? 0x13 : 0x14;
1354
- parts.push(concatUint8Arrays([
1355
- new Uint8Array([code]), frame.data
1356
- ]));
1357
-
1358
- } else if (frame.type === 'connection_close') {
1359
- var code = frame.application ? 0x1d : 0x1c;
1360
- var err = new Uint8Array([frame.error >> 8, frame.error & 0xff]);
1361
- var ft = frame.application ? new Uint8Array(0) : writeVarInt(frame.frameType);
1362
- var reason = new TextEncoder().encode(frame.reason || "");
1363
- var reasonLen = writeVarInt(reason.length);
1364
- parts.push(concatUint8Arrays([
1365
- new Uint8Array([code]), err, ft, reasonLen, reason
1366
- ]));
1367
-
1368
- } else if (frame.type === 'handshake_done') {
1369
- parts.push(new Uint8Array([0x1e]));
1370
-
1371
- } else if (frame.type === 'datagram') {
1372
- var firstByte;
1373
- var prefixBytes;
1374
- var payload = frame.data;
1375
-
1376
- if (frame.contextId != null) {
1377
- // אם יש contextId — משתמשים ב־0x31 לפי תקן QUIC
1378
- firstByte = 0x31;
1379
- var contextBytes = writeVarInt(frame.contextId);
1380
- prefixBytes = concatUint8Arrays([
1381
- new Uint8Array([firstByte]),
1382
- contextBytes
1383
- ]);
1384
- } else {
1385
- // אם אין contextId — שולחים כ־0x30 לפי תקן QUIC
1386
- firstByte = 0x30;
1387
- prefixBytes = new Uint8Array([firstByte]);
1388
- // payload נשאר כמו שהוא — לא מוסיפים streamId!
1389
- }
1390
-
1391
- parts.push(concatUint8Arrays([
1392
- prefixBytes,
1393
- payload
1394
- ]));
1395
- } else {
1396
- // פריים לא ידוע – נתעלם או אפשר להתריע בלוג
1397
- //console.warn('Unsupported frame type:', frame.type);
1398
- }
1399
- }
1400
-
1401
- if(parts.length==1){
1402
- return parts[0];
1403
- }else{
1404
- return concatUint8Arrays(parts);
1405
- }
1406
-
1407
- }
1408
-
1409
-
1410
-
1411
-
1412
-
1413
- function parse_quic_frames(buf) {
1414
- var offset = 0;
1415
- var frames = [];
1416
- var textDecoder = new TextDecoder();
1417
-
1418
- function safeReadVarInt() {
1419
- if (offset >= buf.length) return null;
1420
- var res = readVarInt(buf, offset);
1421
- if (!res || typeof res.byteLength !== 'number') return null;
1422
- offset += res.byteLength;
1423
- return res;
1424
- }
1425
-
1426
- while (offset < buf.length) {
1427
- var start = offset;
1428
- var type = buf[offset++];
1429
-
1430
- if (type >= 0x80) {
1431
- offset--; // backtrack and read full varint
1432
- var t = safeReadVarInt();
1433
- if (!t) break;
1434
- type = t.value;
1435
- }
1436
-
1437
- if (type === 0x00) {
1438
- // padding
1439
-
1440
- } else if (type === 0x01) {
1441
- frames.push({ type: 'ping' });
1442
-
1443
- } else if ((type & 0xfe) === 0x02) {
1444
- var hasECN = (type & 0x01) === 0x01;
1445
- var largest = safeReadVarInt(); if (!largest) break;
1446
- var delay = safeReadVarInt(); if (!delay) break;
1447
- var rangeCount = safeReadVarInt(); if (!rangeCount) break;
1448
- var firstRange = safeReadVarInt(); if (!firstRange) break;
1449
-
1450
- var ranges = [];
1451
- for (var i = 0; i < rangeCount.value; i++) {
1452
- var gap = safeReadVarInt(); if (!gap) break;
1453
- var len = safeReadVarInt(); if (!len) break;
1454
- ranges.push({ gap: gap.value, length: len.value });
1455
- }
1456
-
1457
- var ecn = null;
1458
- if (hasECN) {
1459
- var ect0 = safeReadVarInt(); if (!ect0) break;
1460
- var ect1 = safeReadVarInt(); if (!ect1) break;
1461
- var ce = safeReadVarInt(); if (!ce) break;
1462
- ecn = { ect0: ect0.value, ect1: ect1.value, ce: ce.value };
1463
- }
1464
-
1465
- frames.push({ type: 'ack', largest: largest.value, delay: delay.value, firstRange: firstRange.value, ranges, ecn });
1466
-
1467
- } else if (type === 0x04) {
1468
- var id = safeReadVarInt(); if (!id) break;
1469
- if (offset + 2 > buf.length) break;
1470
- var error = buf[offset++] << 8 | buf[offset++];
1471
- var finalSize = safeReadVarInt(); if (!finalSize) break;
1472
- frames.push({ type: 'reset_stream', id: id.value, error, finalSize: finalSize.value });
1473
-
1474
- } else if (type === 0x05) {
1475
- var id = safeReadVarInt(); if (!id) break;
1476
- if (offset + 2 > buf.length) break;
1477
- var error = buf[offset++] << 8 | buf[offset++];
1478
- frames.push({ type: 'stop_sending', id: id.value, error });
1479
-
1480
- } else if (type === 0x06) {
1481
- var off = safeReadVarInt(); if (!off) break;
1482
- var len = safeReadVarInt(); if (!len) break;
1483
- if (offset + len.value > buf.length) break;
1484
- var data = buf.slice(offset, offset + len.value); offset += len.value;
1485
- frames.push({ type: 'crypto', offset: off.value, data });
1486
-
1487
- } else if (type === 0x07) {
1488
- var len = safeReadVarInt(); if (!len) break;
1489
- if (offset + len.value > buf.length) break;
1490
- var token = buf.slice(offset, offset + len.value); offset += len.value;
1491
- frames.push({ type: 'new_token', token });
1492
-
1493
- } else if ((type & 0xe0) === 0x00) {
1494
- var fin = !!(type & 0x01);
1495
- var lenb = !!(type & 0x02);
1496
- var offb = !!(type & 0x04);
1497
-
1498
- var stream_id = safeReadVarInt(); if (!stream_id) break;
1499
- var offset_val = offb ? safeReadVarInt() : { value: 0 }; if (!offset_val) break;
1500
- var length_val = lenb ? safeReadVarInt() : { value: buf.length - offset }; if (!length_val) break;
1501
-
1502
- if (offset + length_val.value > buf.length) break;
1503
-
1504
- var data = buf.slice(offset, offset + length_val.value); offset += length_val.value;
1505
-
1506
- frames.push({
1507
- type: 'stream',
1508
- id: stream_id.value,
1509
- offset: offset_val.value,
1510
- fin,
1511
- data
1512
- });
1513
- } else if (type === 0x09) {
1514
- var max = safeReadVarInt(); if (!max) break;
1515
- frames.push({ type: 'max_data', max: max.value });
1516
-
1517
- } else if (type === 0x0a) {
1518
- var id = safeReadVarInt(); if (!id) break;
1519
- var max = safeReadVarInt(); if (!max) break;
1520
- frames.push({ type: 'max_stream_data', id: id.value, max: max.value });
1521
-
1522
- } else if (type === 0x12 || type === 0x13) {
1523
- var max = safeReadVarInt(); if (!max) break;
1524
- frames.push({ type: type === 0x12 ? 'max_streams_bidi' : 'max_streams_uni', max: max.value });
1525
-
1526
- } else if (type === 0x14) {
1527
- var max = safeReadVarInt(); if (!max) break;
1528
- frames.push({ type: 'data_blocked', max: max.value });
1529
-
1530
- } else if (type === 0x15) {
1531
- var id = safeReadVarInt(); if (!id) break;
1532
- frames.push({ type: 'stream_data_blocked', id: id.value });
1533
-
1534
- } else if (type === 0x16 || type === 0x17) {
1535
- var max = safeReadVarInt(); if (!max) break;
1536
- frames.push({ type: type === 0x16 ? 'streams_blocked_bidi' : 'streams_blocked_uni', max: max.value });
1537
-
1538
- } else if (type === 0x18) {
1539
- var seq = safeReadVarInt(); if (!seq) break;
1540
- var retire = safeReadVarInt(); if (!retire) break;
1541
- if (offset >= buf.length) break;
1542
- var len = buf[offset++];
1543
- if (offset + len + 16 > buf.length) break;
1544
- var connId = buf.slice(offset, offset + len); offset += len;
1545
- var token = buf.slice(offset, offset + 16); offset += 16;
1546
- frames.push({ type: 'new_connection_id', seq: seq.value, retire: retire.value, connId, token });
1547
-
1548
- } else if (type === 0x19) {
1549
- var seq = safeReadVarInt(); if (!seq) break;
1550
- frames.push({ type: 'retire_connection_id', seq: seq.value });
1551
-
1552
- } else if (type === 0x1a || type === 0x1b) {
1553
- if (offset + 8 > buf.length) break;
1554
- var data = buf.slice(offset, offset + 8); offset += 8;
1555
- frames.push({ type: type === 0x1a ? 'path_challenge' : 'path_response', data });
1556
-
1557
- } else if (type === 0x1c || type === 0x1d) {
1558
- if (offset + 2 > buf.length) break;
1559
- var error = buf[offset++] << 8 | buf[offset++];
1560
- var frameType = null;
1561
- if (type === 0x1c) {
1562
- var ft = safeReadVarInt(); if (!ft) break;
1563
- frameType = ft.value;
1564
- }
1565
- var reasonLen = safeReadVarInt(); if (!reasonLen) break;
1566
- if (offset + reasonLen.value > buf.length) break;
1567
- var reason = textDecoder.decode(buf.slice(offset, offset + reasonLen.value)); offset += reasonLen.value;
1568
- frames.push({ type: 'connection_close', application: type === 0x1d, error, frameType, reason });
1569
-
1570
- } else if (type === 0x1e) {
1571
- frames.push({ type: 'handshake_done' });
1572
-
1573
- } else if (type === 0x1f) {
1574
- frames.push({ type: 'immediate_ack' });
1575
-
1576
- } else if (type === 0x30 || type === 0x31) {
1577
- var contextId = null;
1578
- var len = null;
1579
-
1580
- if (type === 0x31) {
1581
- // קורא את context ID
1582
- var cid = safeReadVarInt(buf, offset);
1583
- if (!cid) break;
1584
- contextId = cid.value;
1585
- offset = cid.nextOffset;
1586
- }
1587
-
1588
- // החישוב של len מבוסס על מה שנשאר בפאקט אחרי הקריאה של contextId
1589
- len = { value: buf.length - offset };
1590
-
1591
- if (offset + len.value > buf.length) break;
1592
-
1593
- var data = buf.slice(offset, offset + len.value);
1594
- offset += len.value;
1595
-
1596
- frames.push({
1597
- type: 'datagram',
1598
- contextId: contextId,
1599
- data: data
1600
- });
1601
-
1602
- } else if (type === 0xaf) {
1603
- var seq = safeReadVarInt(); if (!seq) break;
1604
- var packetTolerance = safeReadVarInt(); if (!packetTolerance) break;
1605
- if (offset >= buf.length) break;
1606
- var ackDelayExponent = buf[offset++];
1607
- var maxAckDelay = safeReadVarInt(); if (!maxAckDelay) break;
1608
- frames.push({
1609
- type: 'ack_frequency',
1610
- seq: seq.value,
1611
- packetTolerance: packetTolerance.value,
1612
- ackDelayExponent,
1613
- maxAckDelay: maxAckDelay.value
1614
- });
1615
-
1616
- } else if (type >= 0x15228c00 && type <= 0x15228cff) {
1617
- frames.push({ type: 'multipath_extension', frameType: type });
1618
-
1619
- } else {
1620
- frames.push({ type: 'unknown', frameType: type, offset: start });
1621
- break;
1622
- }
1623
- }
1624
-
1625
- return frames;
1626
- }
1627
-
1628
-
1629
-
1630
-
1631
-
1632
-
1633
- function parse_quic_packet(array, offset0 = 0) {
1634
- if (!(array instanceof Uint8Array)) return null;
1635
- if (offset0 >= array.length) return null;
1636
-
1637
- var firstByte = array[offset0];
1638
- var isLongHeader = (firstByte & 0x80) !== 0;
1639
-
1640
- if (isLongHeader) {
1641
- if (offset0 + 6 > array.length) return null;
1642
-
1643
- var version = ((array[offset0+1] << 24) | (array[offset0+2] << 16) | (array[offset0+3] << 8) | array[offset0+4]) >>> 0;
1644
-
1645
- var dcidLen = array[offset0+5];
1646
- var offset = offset0 + 6;
1647
-
1648
- if (offset + dcidLen + 1 > array.length) return null;
1649
- var dcid = array.slice(offset, offset + dcidLen);
1650
- offset += dcidLen;
1651
-
1652
- var scidLen = array[offset++];
1653
- if (offset + scidLen > array.length) return null;
1654
- var scid = array.slice(offset, offset + scidLen);
1655
- offset += scidLen;
1656
-
1657
- // Version negotiation
1658
- if (version === 0) {
1659
- var supportedVersions = [];
1660
- while (offset + 4 <= array.length) {
1661
- var v = (array[offset] << 24) | (array[offset+1] << 16) | (array[offset+2] << 8) | array[offset+3];
1662
- supportedVersions.push(v);
1663
- offset += 4;
1664
- }
1665
- return {
1666
- form: 'long',
1667
- type: 'version_negotiation',
1668
- version,
1669
- dcid,
1670
- scid,
1671
- supportedVersions,
1672
- totalLength: offset - offset0
1673
- };
1674
- }
1675
-
1676
- var packetTypeBits = (firstByte & 0x30) >> 4;
1677
- var typeMap = ['initial', '0rtt', 'handshake', 'retry'];
1678
- var packetType = typeMap[packetTypeBits] || 'unknown';
1679
-
1680
- if (packetType === 'retry') {
1681
- var odcid = array.slice(offset);
1682
- return {
1683
- form: 'long',
1684
- type: 'retry',
1685
- version,
1686
- dcid,
1687
- scid,
1688
- originalDestinationConnectionId: odcid,
1689
- totalLength: array.length - offset0 // כל השאר
1690
- };
1691
- }
1692
-
1693
- // === קריאה של Token אם זה Initial ===
1694
- var token = null;
1695
- if (packetType === 'initial') {
1696
- try {
1697
- var tokenLen = readVarInt(array, offset);
1698
- offset += tokenLen.byteLength;
1699
- if (offset + tokenLen.value > array.length) return null;
1700
- token = array.slice(offset, offset + tokenLen.value);
1701
- offset += tokenLen.value;
1702
- } catch (e) {
1703
- return null;
1704
- }
1705
- }
1706
-
1707
- // === כאן בא השלב הקריטי: לקרוא את Length ===
1708
- try {
1709
- var lengthInfo = readVarInt(array, offset);
1710
- offset += lengthInfo.byteLength;
1711
-
1712
- var payloadLength = lengthInfo.value;
1713
- var totalLength = offset - offset0 + payloadLength;
1714
-
1715
- if (offset0 + totalLength > array.length) return null;
1716
-
1717
- return {
1718
- form: 'long',
1719
- type: packetType,
1720
- version,
1721
- dcid,
1722
- scid,
1723
- token,
1724
- totalLength
1725
- };
1726
- } catch (e) {
1727
- return null;
1728
- }
1729
- } else {
1730
- var totalLength = array.length - offset0; // לא ניתן לדעת בדיוק, אז נניח שזה האחרון
1731
- return {
1732
- form: 'short',
1733
- type: '1rtt',
1734
- totalLength
1735
- };
1736
- }
1737
- }
1738
-
1739
- function parse_quic_datagram(array) {
1740
- var packets = [];
1741
- var offset = 0;
1742
-
1743
- while (offset < array.length) {
1744
- var pkt = parse_quic_packet(array, offset);
1745
- if (!pkt || !pkt.totalLength) break;
1746
-
1747
- var start = offset;
1748
- var end = offset + pkt.totalLength;
1749
-
1750
- // slice רק אם חייב
1751
- pkt.raw = (start === 0 && end === array.length)
1752
- ? array
1753
- : array.slice(start, end);
1754
-
1755
- packets.push(pkt);
1756
- offset = end;
1757
- }
1758
-
1759
- return packets;
1760
- }
1761
-
1762
-
1763
- function build_new_session_ticket(session_id_bytes, options) {
1764
- var ticket_lifetime = options.lifetime || 86400;
1765
- var ticket_age_add = Math.floor(Math.random() * 0xffffffff);
1766
- var ticket_nonce = crypto.getRandomValues(new Uint8Array(8));
1767
- var ticket = session_id_bytes;
1768
-
1769
- var extensions = [];
1770
- if (options.early_data_max_size != null) {
1771
- var ed = new Uint8Array(8);
1772
- ed[0] = 0x00; ed[1] = 0x2a; // early_data extension type
1773
- ed[2] = 0x00; ed[3] = 0x04; // extension length
1774
- ed[4] = (options.early_data_max_size >>> 24) & 0xff;
1775
- ed[5] = (options.early_data_max_size >>> 16) & 0xff;
1776
- ed[6] = (options.early_data_max_size >>> 8) & 0xff;
1777
- ed[7] = (options.early_data_max_size) & 0xff;
1778
- extensions.push(ed);
1779
- }
1780
-
1781
- var ext_len = 0;
1782
- for (var i = 0; i < extensions.length; i++) ext_len += extensions[i].length;
1783
- var extensions_block = new Uint8Array(ext_len);
1784
- for (var i = 0, offset = 0; i < extensions.length; i++) {
1785
- extensions_block.set(extensions[i], offset);
1786
- offset += extensions[i].length;
1787
- }
1788
-
1789
- var total_len =
1790
- 4 + // ticket_lifetime
1791
- 4 + // ticket_age_add
1792
- 1 + ticket_nonce.length +
1793
- 2 + ticket.length +
1794
- 2 + extensions_block.length;
1795
-
1796
- var result = new Uint8Array(total_len);
1797
- var p = 0;
1798
-
1799
- result[p++] = (ticket_lifetime >>> 24) & 0xff;
1800
- result[p++] = (ticket_lifetime >>> 16) & 0xff;
1801
- result[p++] = (ticket_lifetime >>> 8) & 0xff;
1802
- result[p++] = (ticket_lifetime) & 0xff;
1803
-
1804
- result[p++] = (ticket_age_add >>> 24) & 0xff;
1805
- result[p++] = (ticket_age_add >>> 16) & 0xff;
1806
- result[p++] = (ticket_age_add >>> 8) & 0xff;
1807
- result[p++] = (ticket_age_add) & 0xff;
1808
-
1809
- result[p++] = ticket_nonce.length;
1810
- result.set(ticket_nonce, p);
1811
- p += ticket_nonce.length;
1812
-
1813
- result[p++] = (ticket.length >>> 8) & 0xff;
1814
- result[p++] = (ticket.length) & 0xff;
1815
- result.set(ticket, p);
1816
- p += ticket.length;
1817
-
1818
- result[p++] = (extensions_block.length >>> 8) & 0xff;
1819
- result[p++] = (extensions_block.length) & 0xff;
1820
- result.set(extensions_block, p);
1821
-
1822
- return result;
1823
- }
1824
-
1825
-
1826
- export {
1827
- extract_tls_messages_from_chunks,
1828
- decrypt_quic_packet,
1829
- quic_derive_init_secrets,
1830
- quic_derive_from_tls_secrets,
1831
- parse_tls_client_hello,
1832
- build_quic_ext,
1833
- hkdf_expand_label,
1834
- encode_quic_frames,
1835
- encrypt_quic_packet,
1836
- parse_quic_datagram,
1837
- parse_quic_packet,
1838
- parse_quic_frames,
1839
- build_alpn_ext,
1840
- parse_transport_parameters
1841
- };