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