quico 0.1.0 → 0.1.3
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/CHANGELOG.md +6 -0
- package/README.md +216 -143
- package/examples/webtransport_datagram_echo_server/server.js +12 -18
- package/h3_server.js +416 -0
- package/h3_socket.js +473 -0
- package/index.js +3 -2611
- package/libs/crypto.js +182 -448
- package/libs/h3.js +312 -65
- package/libs/utils.js +12 -12
- package/package.json +60 -28
- package/quic_socket.js +1550 -0
- package/quico.svg +1 -1
package/libs/crypto.js
CHANGED
|
@@ -16,110 +16,35 @@
|
|
|
16
16
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
17
17
|
*/
|
|
18
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
19
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
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';
|
|
30
23
|
|
|
31
|
-
|
|
24
|
+
import { p256 } from '@noble/curves/nist.js';
|
|
25
|
+
import { ed25519, x25519 } from '@noble/curves/ed25519.js';
|
|
32
26
|
|
|
33
|
-
var {
|
|
34
|
-
var { GCM } = require('@stablelib/gcm');
|
|
27
|
+
var nobleHashes = { hmac, hkdf, hkdf_extract, hkdf_expand, sha256 };
|
|
35
28
|
|
|
36
|
-
|
|
29
|
+
import * as crypto from 'crypto';
|
|
37
30
|
|
|
38
|
-
|
|
31
|
+
import {
|
|
39
32
|
concatUint8Arrays,
|
|
40
33
|
writeVarInt,
|
|
41
34
|
readVarInt
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
|
|
35
|
+
} from './utils.js';
|
|
45
36
|
|
|
46
37
|
|
|
47
38
|
|
|
48
|
-
function
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
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
|
-
}
|
|
39
|
+
function getHashFn(hashName) {
|
|
40
|
+
if (hashName === 'sha256') return sha256;
|
|
41
|
+
if (hashName === 'sha384') return sha384;
|
|
42
|
+
throw new Error('Unsupported hash: ' + hashName);
|
|
59
43
|
}
|
|
60
44
|
|
|
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
|
|
45
|
+
function getHashLen(hashName) {
|
|
46
|
+
var fn = getHashFn(hashName);
|
|
47
|
+
return fn.outputLen|0;
|
|
123
48
|
}
|
|
124
49
|
|
|
125
50
|
|
|
@@ -205,130 +130,7 @@ function build_alpn_ext(protocol) {
|
|
|
205
130
|
return ext;
|
|
206
131
|
}
|
|
207
132
|
|
|
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
133
|
|
|
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
134
|
|
|
333
135
|
|
|
334
136
|
|
|
@@ -433,13 +235,6 @@ function parse_transport_parameters(buf, start) {
|
|
|
433
235
|
|
|
434
236
|
|
|
435
237
|
|
|
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
238
|
function parse_tls_client_hello2(body) {
|
|
444
239
|
var view = new Uint8Array(body);
|
|
445
240
|
var ptr = 0;
|
|
@@ -770,25 +565,25 @@ function parse_tls_client_hello(body) {
|
|
|
770
565
|
|
|
771
566
|
////////////////////////////////
|
|
772
567
|
|
|
773
|
-
function
|
|
568
|
+
function hmac2(hash, key, data) {
|
|
774
569
|
return new Uint8Array(crypto.createHmac(hash, key).update(data).digest());
|
|
775
570
|
}
|
|
776
|
-
function
|
|
571
|
+
function hkdf_extract2(salt, ikm, hash_func) {
|
|
777
572
|
return nobleHashes.hkdf_extract(hash_func, ikm, salt);
|
|
778
573
|
}
|
|
779
574
|
|
|
780
575
|
|
|
781
|
-
function
|
|
576
|
+
function hkdf_expand2(prk, info, length, hash_func) {
|
|
782
577
|
return nobleHashes.hkdf_expand(hash_func, prk, info, length);
|
|
783
578
|
}
|
|
784
579
|
|
|
785
580
|
|
|
786
581
|
|
|
787
582
|
function build_hkdf_label(label, context, length) {
|
|
788
|
-
|
|
789
|
-
|
|
583
|
+
var prefix = "tls13 ";
|
|
584
|
+
var full = new TextEncoder().encode(prefix + label);
|
|
790
585
|
|
|
791
|
-
|
|
586
|
+
var info = new Uint8Array(
|
|
792
587
|
2 + 1 + full.length + 1 + context.length);
|
|
793
588
|
|
|
794
589
|
// length (2-bytes, BE)
|
|
@@ -800,7 +595,7 @@ function build_hkdf_label(label, context, length) {
|
|
|
800
595
|
info.set(full, 3);
|
|
801
596
|
|
|
802
597
|
// context length + bytes
|
|
803
|
-
|
|
598
|
+
var ctxOfs = 3 + full.length;
|
|
804
599
|
info[ctxOfs] = context.length;
|
|
805
600
|
info.set(context, ctxOfs + 1);
|
|
806
601
|
|
|
@@ -808,64 +603,12 @@ function build_hkdf_label(label, context, length) {
|
|
|
808
603
|
}
|
|
809
604
|
|
|
810
605
|
function hkdf_expand_label(secret, label, context, length, hash_func) {
|
|
811
|
-
|
|
812
|
-
return
|
|
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
|
-
};
|
|
606
|
+
var info = build_hkdf_label(label, context, length);
|
|
607
|
+
return hkdf_expand2(secret, info, length, hash_func); // hash = sha384/sha256
|
|
846
608
|
}
|
|
847
609
|
|
|
848
610
|
|
|
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
611
|
|
|
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
612
|
|
|
870
613
|
function aead_decrypt(key, iv, packetNumber, ciphertextWithTag, aad, callback) {
|
|
871
614
|
try {
|
|
@@ -884,11 +627,11 @@ function aead_decrypt(key, iv, packetNumber, ciphertextWithTag, aad, callback) {
|
|
|
884
627
|
key.length === 16 ? 'aes-128-gcm' :
|
|
885
628
|
(() => { throw new Error("Unsupported key length: " + key.length); })();
|
|
886
629
|
|
|
887
|
-
|
|
630
|
+
var decipher = crypto.createDecipheriv(algo, key, nonce);
|
|
888
631
|
decipher.setAuthTag(tag);
|
|
889
632
|
decipher.setAAD(aad);
|
|
890
633
|
|
|
891
|
-
|
|
634
|
+
var decrypted = decipher.update(ciphertext);
|
|
892
635
|
decipher.final();
|
|
893
636
|
|
|
894
637
|
callback(null, decrypted);
|
|
@@ -924,7 +667,7 @@ function aes_gcm_decrypt(ciphertext, tag, key, nonce, aad) {
|
|
|
924
667
|
}
|
|
925
668
|
}
|
|
926
669
|
|
|
927
|
-
|
|
670
|
+
var INITIAL_SALTS = {
|
|
928
671
|
// QUIC v1 (RFC 9001)
|
|
929
672
|
0x00000001: new Uint8Array([
|
|
930
673
|
0x38, 0x76, 0x2c, 0xf7, 0xf5, 0x59, 0x34, 0xb3,
|
|
@@ -955,15 +698,15 @@ const INITIAL_SALTS = {
|
|
|
955
698
|
};
|
|
956
699
|
|
|
957
700
|
function quic_derive_init_secrets(client_dcid, version, direction) {
|
|
958
|
-
|
|
701
|
+
var hash_func = sha256;
|
|
959
702
|
//console.log(version);
|
|
960
|
-
|
|
703
|
+
var salt = INITIAL_SALTS[version] || null;
|
|
961
704
|
if (!salt) throw new Error("Unsupported QUIC version: 0x" + version.toString(16));
|
|
962
705
|
|
|
963
|
-
|
|
964
|
-
|
|
706
|
+
var label = direction === 'read' ? 'client in' : 'server in';
|
|
707
|
+
var initial_secret = hkdf_extract2(salt, client_dcid, hash_func);
|
|
965
708
|
|
|
966
|
-
|
|
709
|
+
var initial_secret2 = hkdf_expand_label(
|
|
967
710
|
initial_secret,
|
|
968
711
|
label,
|
|
969
712
|
new Uint8Array(0),
|
|
@@ -971,39 +714,41 @@ function quic_derive_init_secrets(client_dcid, version, direction) {
|
|
|
971
714
|
hash_func
|
|
972
715
|
);
|
|
973
716
|
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
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);
|
|
977
720
|
|
|
978
721
|
return { key, iv, hp };
|
|
979
722
|
}
|
|
980
723
|
|
|
981
724
|
|
|
982
|
-
function quic_derive_from_tls_secrets(traffic_secret,
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
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);
|
|
987
732
|
|
|
988
733
|
return { key, iv, hp };
|
|
989
|
-
|
|
734
|
+
}
|
|
990
735
|
}
|
|
991
736
|
|
|
992
737
|
|
|
993
738
|
|
|
994
739
|
function compute_nonce(iv, packetNumber) {
|
|
995
|
-
|
|
996
|
-
|
|
740
|
+
var nonce = new Uint8Array(iv); // עותק של ה־IV המקורי (12 בתים)
|
|
741
|
+
var pnBuffer = new Uint8Array(12); // 12 בתים, מיושר לימין
|
|
997
742
|
|
|
998
743
|
// הכנס את packetNumber לימין של pnBuffer
|
|
999
|
-
|
|
1000
|
-
for (
|
|
744
|
+
var n = packetNumber;
|
|
745
|
+
for (var i = 11; n > 0 && i >= 0; i--) {
|
|
1001
746
|
pnBuffer[i] = n & 0xff;
|
|
1002
747
|
n >>= 8;
|
|
1003
748
|
}
|
|
1004
749
|
|
|
1005
750
|
// בצע XOR בין ה־IV לבין pnBuffer
|
|
1006
|
-
for (
|
|
751
|
+
for (var i = 0; i < 12; i++) {
|
|
1007
752
|
nonce[i] ^= pnBuffer[i];
|
|
1008
753
|
}
|
|
1009
754
|
|
|
@@ -1020,32 +765,32 @@ function aes_ecb_encrypt(keyBytes, plaintext) {
|
|
|
1020
765
|
throw new Error("Plaintext must be a multiple of 16 bytes");
|
|
1021
766
|
}
|
|
1022
767
|
|
|
1023
|
-
|
|
768
|
+
var cipher = crypto.createCipheriv('aes-' + (keyBytes.length * 8) + '-ecb', keyBytes, null);
|
|
1024
769
|
cipher.setAutoPadding(false);
|
|
1025
770
|
|
|
1026
|
-
|
|
771
|
+
var encrypted = Buffer.concat([cipher.update(plaintext), cipher.final()]);
|
|
1027
772
|
return new Uint8Array(encrypted);
|
|
1028
773
|
}
|
|
1029
774
|
|
|
1030
775
|
|
|
1031
776
|
function aead_encrypt(key, iv, packetNumber, plaintext, aad) {
|
|
1032
777
|
try {
|
|
1033
|
-
|
|
778
|
+
var algo = key.length === 32 ? 'aes-256-gcm' :
|
|
1034
779
|
key.length === 16 ? 'aes-128-gcm' :
|
|
1035
780
|
(() => { throw new Error("Unsupported key length: " + key.length); })();
|
|
1036
781
|
|
|
1037
|
-
|
|
782
|
+
var nonce = compute_nonce(iv, packetNumber);
|
|
1038
783
|
|
|
1039
|
-
|
|
784
|
+
var cipher = crypto.createCipheriv(algo, Buffer.from(key), Buffer.from(nonce));
|
|
1040
785
|
cipher.setAAD(Buffer.from(aad));
|
|
1041
786
|
|
|
1042
|
-
|
|
787
|
+
var encrypted = Buffer.concat([
|
|
1043
788
|
cipher.update(Buffer.from(plaintext)),
|
|
1044
789
|
cipher.final()
|
|
1045
790
|
]);
|
|
1046
|
-
|
|
791
|
+
var tag = cipher.getAuthTag();
|
|
1047
792
|
|
|
1048
|
-
|
|
793
|
+
var result = new Uint8Array(encrypted.length + tag.length);
|
|
1049
794
|
result.set(encrypted, 0);
|
|
1050
795
|
result.set(tag, encrypted.length);
|
|
1051
796
|
|
|
@@ -1086,9 +831,9 @@ function apply_header_protection(packet, pnOffset, hpKey, pnLength) {
|
|
|
1086
831
|
|
|
1087
832
|
|
|
1088
833
|
function aes128ecb(sample,hpKey) {
|
|
1089
|
-
|
|
834
|
+
var cipher = crypto.createCipheriv('aes-128-ecb', Buffer.from(hpKey), null);
|
|
1090
835
|
cipher.setAutoPadding(false);
|
|
1091
|
-
|
|
836
|
+
var input = Buffer.from(sample);
|
|
1092
837
|
return new Uint8Array(Buffer.concat([cipher.update(input), cipher.final()]));
|
|
1093
838
|
}
|
|
1094
839
|
|
|
@@ -1100,8 +845,8 @@ function expandPacketNumber(truncated, pnLen, largestReceived) {
|
|
|
1100
845
|
}
|
|
1101
846
|
|
|
1102
847
|
function decode_packet_number(array, offset, pnLength) {
|
|
1103
|
-
|
|
1104
|
-
for (
|
|
848
|
+
var value = 0;
|
|
849
|
+
for (var i = 0; i < pnLength; i++) {
|
|
1105
850
|
value = (value << 8) | array[offset + i];
|
|
1106
851
|
}
|
|
1107
852
|
return value;
|
|
@@ -1144,43 +889,43 @@ function remove_header_protection(array, pnOffset, hpKey, isShort) {
|
|
|
1144
889
|
function decrypt_quic_packet(array, read_key, read_iv, read_hp, dcid, largest_pn) {
|
|
1145
890
|
if (!(array instanceof Uint8Array)) throw new Error("Invalid input");
|
|
1146
891
|
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
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;
|
|
1159
904
|
|
|
1160
905
|
if (isLong) {
|
|
1161
906
|
// ---------- ניתוח Long Header ----------
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
907
|
+
var view = new DataView(array.buffer, array.byteOffset, array.byteLength);
|
|
908
|
+
var version = view.getUint32(1);
|
|
909
|
+
var dcidLen = array[5];
|
|
1165
910
|
|
|
1166
|
-
|
|
1167
|
-
|
|
911
|
+
var offset = 6;
|
|
912
|
+
var parsed_dcid = array.slice(offset, offset + dcidLen);
|
|
1168
913
|
offset += dcidLen;
|
|
1169
914
|
|
|
1170
|
-
|
|
1171
|
-
|
|
915
|
+
var scidLen = array[offset++];
|
|
916
|
+
var scid = array.slice(offset, offset + scidLen);
|
|
1172
917
|
offset += scidLen;
|
|
1173
918
|
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
919
|
+
var typeBits = (firstByte & 0x30) >> 4;
|
|
920
|
+
var typeMap = ['initial', '0rtt', 'handshake', 'retry'];
|
|
921
|
+
var packetType = typeMap[typeBits];
|
|
1177
922
|
|
|
1178
923
|
if (packetType === 'initial') {
|
|
1179
|
-
|
|
924
|
+
var tokenLen = readVarInt(array, offset);
|
|
1180
925
|
offset += tokenLen.byteLength + tokenLen.value;
|
|
1181
926
|
}
|
|
1182
927
|
|
|
1183
|
-
|
|
928
|
+
var len = readVarInt(array, offset);
|
|
1184
929
|
offset += len.byteLength;
|
|
1185
930
|
|
|
1186
931
|
pnOffset = offset;
|
|
@@ -1192,13 +937,13 @@ function decrypt_quic_packet(array, read_key, read_iv, read_hp, dcid, largest_pn
|
|
|
1192
937
|
packetNumber = decode_and_expand_packet_number(array, pnOffset, pnLength, largest_pn);
|
|
1193
938
|
nonce = compute_nonce(read_iv, packetNumber);
|
|
1194
939
|
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
940
|
+
var payloadStart = pnOffset + pnLength;
|
|
941
|
+
var payloadLength = len.value - pnLength;
|
|
942
|
+
var payloadEnd = payloadStart + payloadLength;
|
|
1198
943
|
|
|
1199
944
|
if (payloadEnd > array.length) throw new Error("Truncated long header packet");
|
|
1200
945
|
|
|
1201
|
-
|
|
946
|
+
var payload = array.slice(payloadStart, payloadEnd);
|
|
1202
947
|
if (payload.length < 16) throw new Error("Encrypted payload too short");
|
|
1203
948
|
|
|
1204
949
|
ciphertext = payload.slice(0, payload.length - 16);
|
|
@@ -1212,7 +957,7 @@ function decrypt_quic_packet(array, read_key, read_iv, read_hp, dcid, largest_pn
|
|
|
1212
957
|
// ---------- ניתוח Short Header ----------
|
|
1213
958
|
// פורמט: 1 byte header + DCID + Packet Number + Payload
|
|
1214
959
|
|
|
1215
|
-
|
|
960
|
+
var dcidLen = dcid.length;
|
|
1216
961
|
pnOffset = 1 + dcidLen;
|
|
1217
962
|
|
|
1218
963
|
// הסרת הגנת כותרת
|
|
@@ -1224,8 +969,8 @@ function decrypt_quic_packet(array, read_key, read_iv, read_hp, dcid, largest_pn
|
|
|
1224
969
|
packetNumber = decode_and_expand_packet_number(array, pnOffset, pnLength, largest_pn);
|
|
1225
970
|
nonce = compute_nonce(read_iv, packetNumber);
|
|
1226
971
|
|
|
1227
|
-
|
|
1228
|
-
|
|
972
|
+
var payloadStart = pnOffset + pnLength;
|
|
973
|
+
var payload = array.slice(payloadStart);
|
|
1229
974
|
if (payload.length < 16) throw new Error("Encrypted payload too short");
|
|
1230
975
|
|
|
1231
976
|
ciphertext = payload.slice(0, payload.length - 16);
|
|
@@ -1237,7 +982,7 @@ function decrypt_quic_packet(array, read_key, read_iv, read_hp, dcid, largest_pn
|
|
|
1237
982
|
|
|
1238
983
|
}
|
|
1239
984
|
|
|
1240
|
-
|
|
985
|
+
var plaintext = aes_gcm_decrypt(ciphertext, tag, read_key, nonce, aad);
|
|
1241
986
|
|
|
1242
987
|
return {
|
|
1243
988
|
packet_number: packetNumber,
|
|
@@ -1259,7 +1004,7 @@ function extract_tls_messages_from_chunks(chunks, from_offset) {
|
|
|
1259
1004
|
}
|
|
1260
1005
|
|
|
1261
1006
|
// אם לא קיבלנו שום דבר – נחזיר ריק
|
|
1262
|
-
if (buffers.length === 0) return
|
|
1007
|
+
if (buffers.length === 0) return false;
|
|
1263
1008
|
|
|
1264
1009
|
var combined = concatUint8Arrays(buffers);
|
|
1265
1010
|
var tls_messages = [];
|
|
@@ -1359,6 +1104,7 @@ function build_quic_header(packetType, dcid, scid, token, lengthField, pnLen) {
|
|
|
1359
1104
|
|
|
1360
1105
|
|
|
1361
1106
|
function encrypt_quic_packet(packetType, encodedFrames, writeKey, writeIv, writeHp, packetNumber, dcid, scid, token) {
|
|
1107
|
+
|
|
1362
1108
|
var pnLength;
|
|
1363
1109
|
if (packetNumber <= 0xff) pnLength = 1;
|
|
1364
1110
|
else if (packetNumber <= 0xffff) pnLength = 2;
|
|
@@ -1665,25 +1411,25 @@ function encode_quic_frames(frames) {
|
|
|
1665
1411
|
|
|
1666
1412
|
|
|
1667
1413
|
function parse_quic_frames(buf) {
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1414
|
+
var offset = 0;
|
|
1415
|
+
var frames = [];
|
|
1416
|
+
var textDecoder = new TextDecoder();
|
|
1671
1417
|
|
|
1672
1418
|
function safeReadVarInt() {
|
|
1673
1419
|
if (offset >= buf.length) return null;
|
|
1674
|
-
|
|
1420
|
+
var res = readVarInt(buf, offset);
|
|
1675
1421
|
if (!res || typeof res.byteLength !== 'number') return null;
|
|
1676
1422
|
offset += res.byteLength;
|
|
1677
1423
|
return res;
|
|
1678
1424
|
}
|
|
1679
1425
|
|
|
1680
1426
|
while (offset < buf.length) {
|
|
1681
|
-
|
|
1682
|
-
|
|
1427
|
+
var start = offset;
|
|
1428
|
+
var type = buf[offset++];
|
|
1683
1429
|
|
|
1684
1430
|
if (type >= 0x80) {
|
|
1685
1431
|
offset--; // backtrack and read full varint
|
|
1686
|
-
|
|
1432
|
+
var t = safeReadVarInt();
|
|
1687
1433
|
if (!t) break;
|
|
1688
1434
|
type = t.value;
|
|
1689
1435
|
}
|
|
@@ -1695,67 +1441,67 @@ function parse_quic_frames(buf) {
|
|
|
1695
1441
|
frames.push({ type: 'ping' });
|
|
1696
1442
|
|
|
1697
1443
|
} else if ((type & 0xfe) === 0x02) {
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
for (
|
|
1706
|
-
|
|
1707
|
-
|
|
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;
|
|
1708
1454
|
ranges.push({ gap: gap.value, length: len.value });
|
|
1709
1455
|
}
|
|
1710
1456
|
|
|
1711
|
-
|
|
1457
|
+
var ecn = null;
|
|
1712
1458
|
if (hasECN) {
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1459
|
+
var ect0 = safeReadVarInt(); if (!ect0) break;
|
|
1460
|
+
var ect1 = safeReadVarInt(); if (!ect1) break;
|
|
1461
|
+
var ce = safeReadVarInt(); if (!ce) break;
|
|
1716
1462
|
ecn = { ect0: ect0.value, ect1: ect1.value, ce: ce.value };
|
|
1717
1463
|
}
|
|
1718
1464
|
|
|
1719
1465
|
frames.push({ type: 'ack', largest: largest.value, delay: delay.value, firstRange: firstRange.value, ranges, ecn });
|
|
1720
1466
|
|
|
1721
1467
|
} else if (type === 0x04) {
|
|
1722
|
-
|
|
1468
|
+
var id = safeReadVarInt(); if (!id) break;
|
|
1723
1469
|
if (offset + 2 > buf.length) break;
|
|
1724
|
-
|
|
1725
|
-
|
|
1470
|
+
var error = buf[offset++] << 8 | buf[offset++];
|
|
1471
|
+
var finalSize = safeReadVarInt(); if (!finalSize) break;
|
|
1726
1472
|
frames.push({ type: 'reset_stream', id: id.value, error, finalSize: finalSize.value });
|
|
1727
1473
|
|
|
1728
1474
|
} else if (type === 0x05) {
|
|
1729
|
-
|
|
1475
|
+
var id = safeReadVarInt(); if (!id) break;
|
|
1730
1476
|
if (offset + 2 > buf.length) break;
|
|
1731
|
-
|
|
1477
|
+
var error = buf[offset++] << 8 | buf[offset++];
|
|
1732
1478
|
frames.push({ type: 'stop_sending', id: id.value, error });
|
|
1733
1479
|
|
|
1734
1480
|
} else if (type === 0x06) {
|
|
1735
|
-
|
|
1736
|
-
|
|
1481
|
+
var off = safeReadVarInt(); if (!off) break;
|
|
1482
|
+
var len = safeReadVarInt(); if (!len) break;
|
|
1737
1483
|
if (offset + len.value > buf.length) break;
|
|
1738
|
-
|
|
1484
|
+
var data = buf.slice(offset, offset + len.value); offset += len.value;
|
|
1739
1485
|
frames.push({ type: 'crypto', offset: off.value, data });
|
|
1740
1486
|
|
|
1741
1487
|
} else if (type === 0x07) {
|
|
1742
|
-
|
|
1488
|
+
var len = safeReadVarInt(); if (!len) break;
|
|
1743
1489
|
if (offset + len.value > buf.length) break;
|
|
1744
|
-
|
|
1490
|
+
var token = buf.slice(offset, offset + len.value); offset += len.value;
|
|
1745
1491
|
frames.push({ type: 'new_token', token });
|
|
1746
1492
|
|
|
1747
1493
|
} else if ((type & 0xe0) === 0x00) {
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1494
|
+
var fin = !!(type & 0x01);
|
|
1495
|
+
var lenb = !!(type & 0x02);
|
|
1496
|
+
var offb = !!(type & 0x04);
|
|
1751
1497
|
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
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;
|
|
1755
1501
|
|
|
1756
1502
|
if (offset + length_val.value > buf.length) break;
|
|
1757
1503
|
|
|
1758
|
-
|
|
1504
|
+
var data = buf.slice(offset, offset + length_val.value); offset += length_val.value;
|
|
1759
1505
|
|
|
1760
1506
|
frames.push({
|
|
1761
1507
|
type: 'stream',
|
|
@@ -1765,60 +1511,60 @@ function parse_quic_frames(buf) {
|
|
|
1765
1511
|
data
|
|
1766
1512
|
});
|
|
1767
1513
|
} else if (type === 0x09) {
|
|
1768
|
-
|
|
1514
|
+
var max = safeReadVarInt(); if (!max) break;
|
|
1769
1515
|
frames.push({ type: 'max_data', max: max.value });
|
|
1770
1516
|
|
|
1771
1517
|
} else if (type === 0x0a) {
|
|
1772
|
-
|
|
1773
|
-
|
|
1518
|
+
var id = safeReadVarInt(); if (!id) break;
|
|
1519
|
+
var max = safeReadVarInt(); if (!max) break;
|
|
1774
1520
|
frames.push({ type: 'max_stream_data', id: id.value, max: max.value });
|
|
1775
1521
|
|
|
1776
1522
|
} else if (type === 0x12 || type === 0x13) {
|
|
1777
|
-
|
|
1523
|
+
var max = safeReadVarInt(); if (!max) break;
|
|
1778
1524
|
frames.push({ type: type === 0x12 ? 'max_streams_bidi' : 'max_streams_uni', max: max.value });
|
|
1779
1525
|
|
|
1780
1526
|
} else if (type === 0x14) {
|
|
1781
|
-
|
|
1527
|
+
var max = safeReadVarInt(); if (!max) break;
|
|
1782
1528
|
frames.push({ type: 'data_blocked', max: max.value });
|
|
1783
1529
|
|
|
1784
1530
|
} else if (type === 0x15) {
|
|
1785
|
-
|
|
1531
|
+
var id = safeReadVarInt(); if (!id) break;
|
|
1786
1532
|
frames.push({ type: 'stream_data_blocked', id: id.value });
|
|
1787
1533
|
|
|
1788
1534
|
} else if (type === 0x16 || type === 0x17) {
|
|
1789
|
-
|
|
1535
|
+
var max = safeReadVarInt(); if (!max) break;
|
|
1790
1536
|
frames.push({ type: type === 0x16 ? 'streams_blocked_bidi' : 'streams_blocked_uni', max: max.value });
|
|
1791
1537
|
|
|
1792
1538
|
} else if (type === 0x18) {
|
|
1793
|
-
|
|
1794
|
-
|
|
1539
|
+
var seq = safeReadVarInt(); if (!seq) break;
|
|
1540
|
+
var retire = safeReadVarInt(); if (!retire) break;
|
|
1795
1541
|
if (offset >= buf.length) break;
|
|
1796
|
-
|
|
1542
|
+
var len = buf[offset++];
|
|
1797
1543
|
if (offset + len + 16 > buf.length) break;
|
|
1798
|
-
|
|
1799
|
-
|
|
1544
|
+
var connId = buf.slice(offset, offset + len); offset += len;
|
|
1545
|
+
var token = buf.slice(offset, offset + 16); offset += 16;
|
|
1800
1546
|
frames.push({ type: 'new_connection_id', seq: seq.value, retire: retire.value, connId, token });
|
|
1801
1547
|
|
|
1802
1548
|
} else if (type === 0x19) {
|
|
1803
|
-
|
|
1549
|
+
var seq = safeReadVarInt(); if (!seq) break;
|
|
1804
1550
|
frames.push({ type: 'retire_connection_id', seq: seq.value });
|
|
1805
1551
|
|
|
1806
1552
|
} else if (type === 0x1a || type === 0x1b) {
|
|
1807
1553
|
if (offset + 8 > buf.length) break;
|
|
1808
|
-
|
|
1554
|
+
var data = buf.slice(offset, offset + 8); offset += 8;
|
|
1809
1555
|
frames.push({ type: type === 0x1a ? 'path_challenge' : 'path_response', data });
|
|
1810
1556
|
|
|
1811
1557
|
} else if (type === 0x1c || type === 0x1d) {
|
|
1812
1558
|
if (offset + 2 > buf.length) break;
|
|
1813
|
-
|
|
1814
|
-
|
|
1559
|
+
var error = buf[offset++] << 8 | buf[offset++];
|
|
1560
|
+
var frameType = null;
|
|
1815
1561
|
if (type === 0x1c) {
|
|
1816
|
-
|
|
1562
|
+
var ft = safeReadVarInt(); if (!ft) break;
|
|
1817
1563
|
frameType = ft.value;
|
|
1818
1564
|
}
|
|
1819
|
-
|
|
1565
|
+
var reasonLen = safeReadVarInt(); if (!reasonLen) break;
|
|
1820
1566
|
if (offset + reasonLen.value > buf.length) break;
|
|
1821
|
-
|
|
1567
|
+
var reason = textDecoder.decode(buf.slice(offset, offset + reasonLen.value)); offset += reasonLen.value;
|
|
1822
1568
|
frames.push({ type: 'connection_close', application: type === 0x1d, error, frameType, reason });
|
|
1823
1569
|
|
|
1824
1570
|
} else if (type === 0x1e) {
|
|
@@ -1828,8 +1574,8 @@ function parse_quic_frames(buf) {
|
|
|
1828
1574
|
frames.push({ type: 'immediate_ack' });
|
|
1829
1575
|
|
|
1830
1576
|
} else if (type === 0x30 || type === 0x31) {
|
|
1831
|
-
|
|
1832
|
-
|
|
1577
|
+
var contextId = null;
|
|
1578
|
+
var len = null;
|
|
1833
1579
|
|
|
1834
1580
|
if (type === 0x31) {
|
|
1835
1581
|
// קורא את context ID
|
|
@@ -1844,7 +1590,7 @@ function parse_quic_frames(buf) {
|
|
|
1844
1590
|
|
|
1845
1591
|
if (offset + len.value > buf.length) break;
|
|
1846
1592
|
|
|
1847
|
-
|
|
1593
|
+
var data = buf.slice(offset, offset + len.value);
|
|
1848
1594
|
offset += len.value;
|
|
1849
1595
|
|
|
1850
1596
|
frames.push({
|
|
@@ -1854,11 +1600,11 @@ function parse_quic_frames(buf) {
|
|
|
1854
1600
|
});
|
|
1855
1601
|
|
|
1856
1602
|
} else if (type === 0xaf) {
|
|
1857
|
-
|
|
1858
|
-
|
|
1603
|
+
var seq = safeReadVarInt(); if (!seq) break;
|
|
1604
|
+
var packetTolerance = safeReadVarInt(); if (!packetTolerance) break;
|
|
1859
1605
|
if (offset >= buf.length) break;
|
|
1860
|
-
|
|
1861
|
-
|
|
1606
|
+
var ackDelayExponent = buf[offset++];
|
|
1607
|
+
var maxAckDelay = safeReadVarInt(); if (!maxAckDelay) break;
|
|
1862
1608
|
frames.push({
|
|
1863
1609
|
type: 'ack_frequency',
|
|
1864
1610
|
seq: seq.value,
|
|
@@ -1888,31 +1634,31 @@ function parse_quic_packet(array, offset0 = 0) {
|
|
|
1888
1634
|
if (!(array instanceof Uint8Array)) return null;
|
|
1889
1635
|
if (offset0 >= array.length) return null;
|
|
1890
1636
|
|
|
1891
|
-
|
|
1892
|
-
|
|
1637
|
+
var firstByte = array[offset0];
|
|
1638
|
+
var isLongHeader = (firstByte & 0x80) !== 0;
|
|
1893
1639
|
|
|
1894
1640
|
if (isLongHeader) {
|
|
1895
1641
|
if (offset0 + 6 > array.length) return null;
|
|
1896
1642
|
|
|
1897
|
-
|
|
1643
|
+
var version = ((array[offset0+1] << 24) | (array[offset0+2] << 16) | (array[offset0+3] << 8) | array[offset0+4]) >>> 0;
|
|
1898
1644
|
|
|
1899
|
-
|
|
1900
|
-
|
|
1645
|
+
var dcidLen = array[offset0+5];
|
|
1646
|
+
var offset = offset0 + 6;
|
|
1901
1647
|
|
|
1902
1648
|
if (offset + dcidLen + 1 > array.length) return null;
|
|
1903
|
-
|
|
1649
|
+
var dcid = array.slice(offset, offset + dcidLen);
|
|
1904
1650
|
offset += dcidLen;
|
|
1905
1651
|
|
|
1906
|
-
|
|
1652
|
+
var scidLen = array[offset++];
|
|
1907
1653
|
if (offset + scidLen > array.length) return null;
|
|
1908
|
-
|
|
1654
|
+
var scid = array.slice(offset, offset + scidLen);
|
|
1909
1655
|
offset += scidLen;
|
|
1910
1656
|
|
|
1911
1657
|
// Version negotiation
|
|
1912
1658
|
if (version === 0) {
|
|
1913
|
-
|
|
1659
|
+
var supportedVersions = [];
|
|
1914
1660
|
while (offset + 4 <= array.length) {
|
|
1915
|
-
|
|
1661
|
+
var v = (array[offset] << 24) | (array[offset+1] << 16) | (array[offset+2] << 8) | array[offset+3];
|
|
1916
1662
|
supportedVersions.push(v);
|
|
1917
1663
|
offset += 4;
|
|
1918
1664
|
}
|
|
@@ -1927,12 +1673,12 @@ function parse_quic_packet(array, offset0 = 0) {
|
|
|
1927
1673
|
};
|
|
1928
1674
|
}
|
|
1929
1675
|
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1676
|
+
var packetTypeBits = (firstByte & 0x30) >> 4;
|
|
1677
|
+
var typeMap = ['initial', '0rtt', 'handshake', 'retry'];
|
|
1678
|
+
var packetType = typeMap[packetTypeBits] || 'unknown';
|
|
1933
1679
|
|
|
1934
1680
|
if (packetType === 'retry') {
|
|
1935
|
-
|
|
1681
|
+
var odcid = array.slice(offset);
|
|
1936
1682
|
return {
|
|
1937
1683
|
form: 'long',
|
|
1938
1684
|
type: 'retry',
|
|
@@ -1945,10 +1691,10 @@ function parse_quic_packet(array, offset0 = 0) {
|
|
|
1945
1691
|
}
|
|
1946
1692
|
|
|
1947
1693
|
// === קריאה של Token אם זה Initial ===
|
|
1948
|
-
|
|
1694
|
+
var token = null;
|
|
1949
1695
|
if (packetType === 'initial') {
|
|
1950
1696
|
try {
|
|
1951
|
-
|
|
1697
|
+
var tokenLen = readVarInt(array, offset);
|
|
1952
1698
|
offset += tokenLen.byteLength;
|
|
1953
1699
|
if (offset + tokenLen.value > array.length) return null;
|
|
1954
1700
|
token = array.slice(offset, offset + tokenLen.value);
|
|
@@ -1960,11 +1706,11 @@ function parse_quic_packet(array, offset0 = 0) {
|
|
|
1960
1706
|
|
|
1961
1707
|
// === כאן בא השלב הקריטי: לקרוא את Length ===
|
|
1962
1708
|
try {
|
|
1963
|
-
|
|
1709
|
+
var lengthInfo = readVarInt(array, offset);
|
|
1964
1710
|
offset += lengthInfo.byteLength;
|
|
1965
1711
|
|
|
1966
|
-
|
|
1967
|
-
|
|
1712
|
+
var payloadLength = lengthInfo.value;
|
|
1713
|
+
var totalLength = offset - offset0 + payloadLength;
|
|
1968
1714
|
|
|
1969
1715
|
if (offset0 + totalLength > array.length) return null;
|
|
1970
1716
|
|
|
@@ -1981,7 +1727,7 @@ function parse_quic_packet(array, offset0 = 0) {
|
|
|
1981
1727
|
return null;
|
|
1982
1728
|
}
|
|
1983
1729
|
} else {
|
|
1984
|
-
|
|
1730
|
+
var totalLength = array.length - offset0; // לא ניתן לדעת בדיוק, אז נניח שזה האחרון
|
|
1985
1731
|
return {
|
|
1986
1732
|
form: 'short',
|
|
1987
1733
|
type: '1rtt',
|
|
@@ -1998,8 +1744,8 @@ function parse_quic_datagram(array) {
|
|
|
1998
1744
|
var pkt = parse_quic_packet(array, offset);
|
|
1999
1745
|
if (!pkt || !pkt.totalLength) break;
|
|
2000
1746
|
|
|
2001
|
-
|
|
2002
|
-
|
|
1747
|
+
var start = offset;
|
|
1748
|
+
var end = offset + pkt.totalLength;
|
|
2003
1749
|
|
|
2004
1750
|
// slice רק אם חייב
|
|
2005
1751
|
pkt.raw = (start === 0 && end === array.length)
|
|
@@ -2077,31 +1823,19 @@ function build_new_session_ticket(session_id_bytes, options) {
|
|
|
2077
1823
|
}
|
|
2078
1824
|
|
|
2079
1825
|
|
|
2080
|
-
|
|
1826
|
+
export {
|
|
2081
1827
|
extract_tls_messages_from_chunks,
|
|
2082
|
-
get_cipher_info,
|
|
2083
|
-
build_certificate,
|
|
2084
1828
|
decrypt_quic_packet,
|
|
2085
1829
|
quic_derive_init_secrets,
|
|
2086
1830
|
quic_derive_from_tls_secrets,
|
|
2087
|
-
parse_tls_message,
|
|
2088
1831
|
parse_tls_client_hello,
|
|
2089
|
-
build_server_hello,
|
|
2090
|
-
tls_derive_handshake_secrets,
|
|
2091
1832
|
build_quic_ext,
|
|
2092
|
-
build_encrypted_extensions,
|
|
2093
1833
|
hkdf_expand_label,
|
|
2094
|
-
hmac,
|
|
2095
|
-
hash_transcript,
|
|
2096
|
-
handle_client_hello,
|
|
2097
|
-
build_certificate_verify,
|
|
2098
1834
|
encode_quic_frames,
|
|
2099
1835
|
encrypt_quic_packet,
|
|
2100
1836
|
parse_quic_datagram,
|
|
2101
1837
|
parse_quic_packet,
|
|
2102
1838
|
parse_quic_frames,
|
|
2103
1839
|
build_alpn_ext,
|
|
2104
|
-
build_finished,
|
|
2105
|
-
tls_derive_app_secrets,
|
|
2106
1840
|
parse_transport_parameters
|
|
2107
1841
|
};
|