/src/ndpi/src/lib/protocols/quic.c
Line | Count | Source |
1 | | /* |
2 | | * quic.c |
3 | | * |
4 | | * Copyright (C) 2012-22 - ntop.org |
5 | | * |
6 | | * This module is free software: you can redistribute it and/or modify |
7 | | * it under the terms of the GNU Lesser General Public License as published by |
8 | | * the Free Software Foundation, either version 3 of the License, or |
9 | | * (at your option) any later version. |
10 | | * |
11 | | * This module is distributed in the hope that it will be useful, |
12 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 | | * GNU Lesser General Public License for more details. |
15 | | * |
16 | | * You should have received a copy of the GNU Lesser General Public License. |
17 | | * If not, see <http://www.gnu.org/licenses/>. |
18 | | * |
19 | | */ |
20 | | |
21 | | #if defined __FreeBSD__ || defined __NetBSD__ || defined __OpenBSD__ |
22 | | #include <sys/endian.h> |
23 | | #endif |
24 | | |
25 | | #include "ndpi_protocol_ids.h" |
26 | | #define NDPI_CURRENT_PROTO NDPI_PROTOCOL_QUIC |
27 | | #include "ndpi_api.h" |
28 | | #include "ndpi_private.h" |
29 | | |
30 | | |
31 | | #ifdef USE_HOST_LIBGCRYPT |
32 | | #include <gcrypt.h> |
33 | | #else |
34 | | #include <gcrypt_light.h> |
35 | | #endif |
36 | | |
37 | | /* This dissector handles GQUIC and IETF-QUIC both. |
38 | | Main references: |
39 | | * https://groups.google.com/a/chromium.org/g/proto-quic/c/wVHBir-uRU0?pli=1 |
40 | | * https://groups.google.com/a/chromium.org/g/proto-quic/c/OAVgFqw2fko/m/jCbjP0AVAAAJ |
41 | | * https://groups.google.com/a/chromium.org/g/proto-quic/c/OAVgFqw2fko/m/-NYxlh88AgAJ |
42 | | * https://docs.google.com/document/d/1FcpCJGTDEMblAs-Bm5TYuqhHyUqeWpqrItw2vkMFsdY/edit |
43 | | * https://www.rfc-editor.org/rfc/rfc9001.txt [Using TLS over QUIC] |
44 | | * https://www.rfc-editor.org/rfc/rfc9000.txt [v1] |
45 | | * https://www.rfc-editor.org/rfc/rfc9369.txt [v2] |
46 | | */ |
47 | | |
48 | | /* Versions */ |
49 | 0 | #define V_2 0x6b3343cf |
50 | 0 | #define V_1 0x00000001 |
51 | 0 | #define V_Q024 0x51303234 |
52 | 0 | #define V_Q025 0x51303235 |
53 | 0 | #define V_Q030 0x51303330 |
54 | 0 | #define V_Q033 0x51303333 |
55 | 0 | #define V_Q034 0x51303334 |
56 | 0 | #define V_Q035 0x51303335 |
57 | 0 | #define V_Q037 0x51303337 |
58 | 0 | #define V_Q039 0x51303339 |
59 | 0 | #define V_Q043 0x51303433 |
60 | 0 | #define V_Q046 0x51303436 |
61 | 0 | #define V_Q050 0x51303530 |
62 | 0 | #define V_T050 0x54303530 |
63 | 0 | #define V_T051 0x54303531 |
64 | 0 | #define V_MVFST_22 0xfaceb001 |
65 | 0 | #define V_MVFST_27 0xfaceb002 |
66 | 0 | #define V_MVFST_EXP 0xfaceb00e |
67 | | |
68 | 0 | #define QUIC_MAX_CID_LENGTH 20 |
69 | | |
70 | | static int is_version_forcing_vn(uint32_t version) |
71 | 0 | { |
72 | 0 | return (version & 0x0F0F0F0F) == 0x0a0a0a0a; /* Forcing Version Negotiation */ |
73 | 0 | } |
74 | | static int is_version_gquic(uint32_t version) |
75 | 0 | { |
76 | 0 | return ((version & 0xFFFFFF00) == 0x54303500) /* T05X */ || |
77 | 0 | ((version & 0xFFFFFF00) == 0x51303500) /* Q05X */ || |
78 | 0 | ((version & 0xFFFFFF00) == 0x51303400) /* Q04X */ || |
79 | 0 | ((version & 0xFFFFFF00) == 0x51303300) /* Q03X */ || |
80 | 0 | ((version & 0xFFFFFF00) == 0x51303200) /* Q02X */; |
81 | 0 | } |
82 | | static int is_version_quic(uint32_t version) |
83 | 0 | { |
84 | 0 | return version == V_1 || |
85 | 0 | ((version & 0xFFFFFF00) == 0xFF000000) /* IETF Drafts*/ || |
86 | 0 | ((version & 0xFFFFF000) == 0xfaceb000) /* Facebook */ || |
87 | 0 | is_version_forcing_vn(version) || |
88 | 0 | (version == V_2); |
89 | 0 | } |
90 | | static int is_version_valid(uint32_t version) |
91 | 0 | { |
92 | 0 | return is_version_gquic(version) || is_version_quic(version); |
93 | 0 | } |
94 | | static uint8_t get_u8_quic_ver(uint32_t version) |
95 | 0 | { |
96 | | /* IETF Draft versions */ |
97 | 0 | if((version >> 8) == 0xff0000) |
98 | 0 | return (uint8_t)version; |
99 | | /* QUIC (final?) constants for v1 are defined in draft-33, but latest |
100 | | draft version is -34 */ |
101 | 0 | if (version == 0x00000001) { |
102 | 0 | return 34; |
103 | 0 | } |
104 | | |
105 | 0 | if (version == V_MVFST_22) |
106 | 0 | return 22; |
107 | 0 | if (version == V_MVFST_27 || version == V_MVFST_EXP) |
108 | 0 | return 27; |
109 | | |
110 | | /* "Versions that follow the pattern 0x?a?a?a?a are reserved for use in |
111 | | forcing version negotiation to be exercised". |
112 | | We can't return a correct draft version because we don't have a real |
113 | | version here! That means that we can't decode any data and we can dissect |
114 | | only the cleartext header. |
115 | | Let's return v1 (any other numbers should be fine, anyway) to only allow |
116 | | the dissection of the (expected) long header */ |
117 | 0 | if(is_version_forcing_vn(version)) |
118 | 0 | return 34; |
119 | | |
120 | | /* QUIC Version 2 */ |
121 | 0 | if (version == V_2) |
122 | 0 | return 100; |
123 | | |
124 | 0 | return 0; |
125 | 0 | } |
126 | | |
127 | | static int is_quic_ver_less_than(uint32_t version, uint8_t max_version) |
128 | 0 | { |
129 | 0 | uint8_t u8_ver = get_u8_quic_ver(version); |
130 | 0 | return u8_ver && u8_ver <= max_version; |
131 | 0 | } |
132 | | int is_quic_ver_greater_than(uint32_t version, uint8_t min_version) |
133 | 0 | { |
134 | 0 | return get_u8_quic_ver(version) >= min_version; |
135 | 0 | } |
136 | | static uint8_t get_u8_gquic_ver(uint32_t version) |
137 | 0 | { |
138 | 0 | if(is_version_gquic(version)) { |
139 | 0 | version = ntohl(((uint16_t)version) << 16); |
140 | 0 | return atoi((char *)&version); |
141 | 0 | } |
142 | 0 | return 0; |
143 | 0 | } |
144 | | static int is_gquic_ver_less_than(uint32_t version, uint8_t max_version) |
145 | 0 | { |
146 | 0 | uint8_t u8_ver = get_u8_gquic_ver(version); |
147 | 0 | return u8_ver && u8_ver <= max_version; |
148 | 0 | } |
149 | | static int is_version_supported(uint32_t version) |
150 | 0 | { |
151 | 0 | return (version == V_Q024 || |
152 | 0 | version == V_Q025 || |
153 | 0 | version == V_Q030 || |
154 | 0 | version == V_Q033 || |
155 | 0 | version == V_Q034 || |
156 | 0 | version == V_Q035 || |
157 | 0 | version == V_Q037 || |
158 | 0 | version == V_Q039 || |
159 | 0 | version == V_Q043 || |
160 | 0 | version == V_Q046 || |
161 | 0 | version == V_Q050 || |
162 | 0 | version == V_T050 || |
163 | 0 | version == V_T051 || |
164 | 0 | version == V_MVFST_22 || |
165 | 0 | version == V_MVFST_27 || |
166 | 0 | version == V_MVFST_EXP || |
167 | 0 | is_quic_ver_greater_than(version, 23)); |
168 | 0 | } |
169 | | static int is_version_with_encrypted_header(uint32_t version) |
170 | 0 | { |
171 | 0 | return is_version_quic(version) || |
172 | 0 | ((version & 0xFFFFFF00) == 0x51303500) /* Q05X */ || |
173 | 0 | ((version & 0xFFFFFF00) == 0x54303500) /* T05X */; |
174 | 0 | } |
175 | | int is_version_with_tls(uint32_t version) |
176 | 0 | { |
177 | 0 | return is_version_quic(version) || |
178 | 0 | ((version & 0xFFFFFF00) == 0x54303500) /* T05X */; |
179 | 0 | } |
180 | | int is_version_with_var_int_transport_params(uint32_t version) |
181 | 0 | { |
182 | 0 | return (is_version_quic(version) && is_quic_ver_greater_than(version, 27)) || |
183 | 0 | (version == V_T051); |
184 | 0 | } |
185 | | int is_version_with_ietf_long_header(uint32_t version) |
186 | 0 | { |
187 | | /* At least draft-ietf-quic-invariants-06, or newer*/ |
188 | 0 | return is_version_quic(version) || |
189 | 0 | ((version & 0xFFFFFF00) == 0x51303500) /* Q05X */ || |
190 | 0 | ((version & 0xFFFFFF00) == 0x54303500) /* T05X */; |
191 | 0 | } |
192 | | static int is_version_with_v1_labels(uint32_t version) |
193 | 0 | { |
194 | 0 | if(((version & 0xFFFFFF00) == 0x51303500) /* Q05X */ || |
195 | 0 | ((version & 0xFFFFFF00) == 0x54303500)) /* T05X */ |
196 | 0 | return 1; |
197 | 0 | return is_quic_ver_less_than(version, 34); |
198 | 0 | } |
199 | | static int is_version_quic_v2(uint32_t version) |
200 | 0 | { |
201 | 0 | return version == V_2; |
202 | 0 | } |
203 | | |
204 | 0 | char *ndpi_quic_version2str(char *buf, int buf_len, u_int32_t version) { |
205 | |
|
206 | 0 | if(buf == NULL || buf_len <= 1) |
207 | 0 | return NULL; |
208 | | |
209 | 0 | switch(version) { |
210 | 0 | case V_2: strncpy(buf, "V-2", buf_len); buf[buf_len - 1] = '\0'; return buf; |
211 | 0 | case V_1: strncpy(buf, "V-1", buf_len); buf[buf_len - 1] = '\0'; return buf; |
212 | 0 | case V_Q024: strncpy(buf, "Q024", buf_len); buf[buf_len - 1] = '\0'; return buf; |
213 | 0 | case V_Q025: strncpy(buf, "Q025", buf_len); buf[buf_len - 1] = '\0'; return buf; |
214 | 0 | case V_Q030: strncpy(buf, "Q030", buf_len); buf[buf_len - 1] = '\0'; return buf; |
215 | 0 | case V_Q033: strncpy(buf, "Q033", buf_len); buf[buf_len - 1] = '\0'; return buf; |
216 | 0 | case V_Q034: strncpy(buf, "Q034", buf_len); buf[buf_len - 1] = '\0'; return buf; |
217 | 0 | case V_Q035: strncpy(buf, "Q035", buf_len); buf[buf_len - 1] = '\0'; return buf; |
218 | 0 | case V_Q037: strncpy(buf, "Q037", buf_len); buf[buf_len - 1] = '\0'; return buf; |
219 | 0 | case V_Q039: strncpy(buf, "Q039", buf_len); buf[buf_len - 1] = '\0'; return buf; |
220 | 0 | case V_Q043: strncpy(buf, "Q043", buf_len); buf[buf_len - 1] = '\0'; return buf; |
221 | 0 | case V_Q046: strncpy(buf, "Q046", buf_len); buf[buf_len - 1] = '\0'; return buf; |
222 | 0 | case V_Q050: strncpy(buf, "Q050", buf_len); buf[buf_len - 1] = '\0'; return buf; |
223 | 0 | case V_T050: strncpy(buf, "T050", buf_len); buf[buf_len - 1] = '\0'; return buf; |
224 | 0 | case V_T051: strncpy(buf, "T051", buf_len); buf[buf_len - 1] = '\0'; return buf; |
225 | 0 | case V_MVFST_22: strncpy(buf, "MVFST-22", buf_len); buf[buf_len - 1] = '\0'; return buf; |
226 | 0 | case V_MVFST_27: strncpy(buf, "MVFST-27", buf_len); buf[buf_len - 1] = '\0'; return buf; |
227 | 0 | case V_MVFST_EXP: strncpy(buf, "MVFST-EXP", buf_len); buf[buf_len - 1] = '\0'; return buf; |
228 | 0 | } |
229 | | |
230 | 0 | if(is_version_forcing_vn(version)) { |
231 | 0 | strncpy(buf, "Ver-Negotiation", buf_len); |
232 | 0 | buf[buf_len - 1] = '\0'; |
233 | 0 | return buf; |
234 | 0 | } |
235 | 0 | if(((version & 0xFFFFFF00) == 0xFF000000)) { |
236 | 0 | snprintf(buf, buf_len, "Draft-%d", version & 0x000000FF); |
237 | 0 | buf[buf_len - 1] = '\0'; |
238 | 0 | return buf; |
239 | 0 | } |
240 | | |
241 | 0 | ndpi_snprintf(buf, buf_len, "Unknown (%04X)", version); |
242 | 0 | return buf; |
243 | 0 | } |
244 | | |
245 | | int quic_len(const uint8_t *buf, uint64_t *value) |
246 | 0 | { |
247 | 0 | *value = buf[0]; |
248 | 0 | switch((*value) >> 6) { |
249 | 0 | case 0: |
250 | 0 | (*value) &= 0x3F; |
251 | 0 | return 1; |
252 | 0 | case 1: |
253 | 0 | *value = ntohs(*(uint16_t *)buf) & 0x3FFF; |
254 | 0 | return 2; |
255 | 0 | case 2: |
256 | 0 | *value = ntohl(*(uint32_t *)buf) & 0x3FFFFFFF; |
257 | 0 | return 4; |
258 | 0 | case 3: |
259 | 0 | *value = ndpi_ntohll(get_u_int64_t(buf, 0)) & 0x3FFFFFFFFFFFFFFF; |
260 | 0 | return 8; |
261 | 0 | default: /* No Possible */ |
262 | 0 | return 0; |
263 | 0 | } |
264 | 0 | } |
265 | | int quic_len_buffer_still_required(uint8_t value) |
266 | 0 | { |
267 | 0 | switch(value >> 6) { |
268 | 0 | case 0: |
269 | 0 | return 0; |
270 | 0 | case 1: |
271 | 0 | return 1; |
272 | 0 | case 2: |
273 | 0 | return 3; |
274 | 0 | case 3: |
275 | 0 | return 7; |
276 | 0 | default: /* No Possible */ |
277 | 0 | return 0; |
278 | 0 | } |
279 | 0 | } |
280 | | |
281 | | |
282 | | static uint16_t gquic_get_u16(const uint8_t *buf, uint32_t version) |
283 | 0 | { |
284 | 0 | if(version >= V_Q039) |
285 | 0 | return ntohs(*(uint16_t *)buf); |
286 | 0 | return le16toh(*(uint16_t *)buf); |
287 | 0 | } |
288 | | |
289 | | |
290 | | #ifdef NDPI_ENABLE_DEBUG_MESSAGES |
291 | | static char *__gcry_err(gpg_error_t err, char *buf, size_t buflen) |
292 | | { |
293 | | gpg_strerror_r(err, buf, buflen); |
294 | | /* I am not sure if the string will be always null-terminated... |
295 | | Better safe than sorry */ |
296 | | if(buflen > 0) |
297 | | buf[buflen - 1] = '\0'; |
298 | | return buf; |
299 | | } |
300 | | #endif |
301 | | |
302 | | static uint64_t pntoh64(const void *p) |
303 | 0 | { |
304 | 0 | return (uint64_t)*((const uint8_t *)(p)+0)<<56| |
305 | 0 | (uint64_t)*((const uint8_t *)(p)+1)<<48| |
306 | 0 | (uint64_t)*((const uint8_t *)(p)+2)<<40| |
307 | 0 | (uint64_t)*((const uint8_t *)(p)+3)<<32| |
308 | 0 | (uint64_t)*((const uint8_t *)(p)+4)<<24| |
309 | 0 | (uint64_t)*((const uint8_t *)(p)+5)<<16| |
310 | 0 | (uint64_t)*((const uint8_t *)(p)+6)<<8| |
311 | 0 | (uint64_t)*((const uint8_t *)(p)+7)<<0; |
312 | 0 | } |
313 | | static void phton64(uint8_t *p, uint64_t v) |
314 | 0 | { |
315 | 0 | p[0] = (uint8_t)(v >> 56); |
316 | 0 | p[1] = (uint8_t)(v >> 48); |
317 | 0 | p[2] = (uint8_t)(v >> 40); |
318 | 0 | p[3] = (uint8_t)(v >> 32); |
319 | 0 | p[4] = (uint8_t)(v >> 24); |
320 | 0 | p[5] = (uint8_t)(v >> 16); |
321 | 0 | p[6] = (uint8_t)(v >> 8); |
322 | 0 | p[7] = (uint8_t)(v >> 0); |
323 | 0 | } |
324 | | |
325 | | static void *memdup(const uint8_t *orig, size_t len) |
326 | 0 | { |
327 | 0 | void *dest = ndpi_malloc(len); |
328 | 0 | if(dest) |
329 | 0 | memcpy(dest, orig, len); |
330 | 0 | return dest; |
331 | 0 | } |
332 | | |
333 | | |
334 | | /* |
335 | | * Generic Wireshark definitions |
336 | | */ |
337 | | |
338 | 0 | #define HASH_SHA2_256_LENGTH 32 |
339 | 0 | #define TLS13_AEAD_NONCE_LENGTH 12 |
340 | | |
341 | | typedef struct _StringInfo { |
342 | | unsigned char *data; /* Backing storage which may be larger than data_len */ |
343 | | unsigned int data_len; /* Length of the meaningful part of data */ |
344 | | } StringInfo; |
345 | | |
346 | | /* QUIC decryption context. */ |
347 | | |
348 | | typedef struct quic_hp_cipher { |
349 | | gcry_cipher_hd_t hp_cipher; /* Header protection cipher. */ |
350 | | } quic_hp_cipher; |
351 | | typedef struct quic_pp_cipher { |
352 | | gcry_cipher_hd_t pp_cipher; /* Packet protection cipher. */ |
353 | | uint8_t pp_iv[TLS13_AEAD_NONCE_LENGTH]; |
354 | | } quic_pp_cipher; |
355 | | typedef struct quic_ciphers { |
356 | | quic_hp_cipher hp_cipher; |
357 | | quic_pp_cipher pp_cipher; |
358 | | } quic_ciphers; |
359 | | |
360 | | typedef struct quic_decrypt_result { |
361 | | uint8_t *data; /* Decrypted result on success (file-scoped). */ |
362 | | uint32_t data_len; /* Size of decrypted data. */ |
363 | | } quic_decrypt_result_t; |
364 | | |
365 | | |
366 | | /* |
367 | | * From wsutil/wsgcrypt.{c,h} |
368 | | */ |
369 | | |
370 | | static gcry_error_t ws_hmac_buffer(int algo, void *digest, const void *buffer, |
371 | | size_t length, const void *key, size_t keylen) |
372 | 0 | { |
373 | 0 | gcry_md_hd_t hmac_handle; |
374 | 0 | gcry_error_t result = gcry_md_open(&hmac_handle, algo, GCRY_MD_FLAG_HMAC); |
375 | 0 | if(result) { |
376 | 0 | return result; |
377 | 0 | } |
378 | 0 | result = gcry_md_setkey(hmac_handle, key, keylen); |
379 | 0 | if(result) { |
380 | 0 | gcry_md_close(hmac_handle); |
381 | 0 | return result; |
382 | 0 | } |
383 | 0 | gcry_md_write(hmac_handle, buffer, length); |
384 | 0 | memcpy(digest, gcry_md_read(hmac_handle, 0), gcry_md_get_algo_dlen(algo)); |
385 | 0 | gcry_md_close(hmac_handle); |
386 | 0 | return GPG_ERR_NO_ERROR; |
387 | 0 | } |
388 | | static gcry_error_t hkdf_expand(int hashalgo, const uint8_t *prk, uint32_t prk_len, |
389 | | const uint8_t *info, uint32_t info_len, |
390 | | uint8_t *out, uint32_t out_len) |
391 | 0 | { |
392 | | /* Current maximum hash output size: 48 bytes for SHA-384. */ |
393 | 0 | uint8_t lastoutput[48]; |
394 | 0 | gcry_md_hd_t h; |
395 | 0 | gcry_error_t err; |
396 | 0 | const unsigned int hash_len = gcry_md_get_algo_dlen(hashalgo); |
397 | 0 | uint32_t offset; |
398 | | |
399 | | /* Some sanity checks */ |
400 | 0 | if(!(out_len > 0 && out_len <= 255 * hash_len) || |
401 | 0 | !(hash_len > 0 && hash_len <= sizeof(lastoutput))) { |
402 | 0 | return GPG_ERR_INV_ARG; |
403 | 0 | } |
404 | | |
405 | 0 | err = gcry_md_open(&h, hashalgo, GCRY_MD_FLAG_HMAC); |
406 | 0 | if(err) { |
407 | 0 | return err; |
408 | 0 | } |
409 | | |
410 | 0 | for(offset = 0; offset < out_len; offset += hash_len) { |
411 | 0 | gcry_md_reset(h); |
412 | 0 | gcry_md_setkey(h, prk, prk_len); /* Set PRK */ |
413 | 0 | if(offset > 0) { |
414 | 0 | gcry_md_write(h, lastoutput, hash_len); /* T(1..N) */ |
415 | 0 | } |
416 | 0 | gcry_md_write(h, info, info_len); /* info */ |
417 | |
|
418 | 0 | uint8_t c = offset / hash_len + 1; |
419 | 0 | gcry_md_write(h, &c, sizeof(c)); /* constant 0x01..N */ |
420 | |
|
421 | 0 | memcpy(lastoutput, gcry_md_read(h, hashalgo), hash_len); |
422 | 0 | memcpy(out + offset, lastoutput, ndpi_min(hash_len, out_len - offset)); |
423 | 0 | } |
424 | |
|
425 | 0 | gcry_md_close(h); |
426 | 0 | return 0; |
427 | 0 | } |
428 | | /* |
429 | | * Calculate HKDF-Extract(salt, IKM) -> PRK according to RFC 5869. |
430 | | * Caller MUST ensure that 'prk' is large enough to store the digest from hash |
431 | | * algorithm 'hashalgo' (e.g. 32 bytes for SHA-256). |
432 | | */ |
433 | | static gcry_error_t hkdf_extract(int hashalgo, const uint8_t *salt, size_t salt_len, |
434 | | const uint8_t *ikm, size_t ikm_len, uint8_t *prk) |
435 | 0 | { |
436 | | /* PRK = HMAC-Hash(salt, IKM) where salt is key, and IKM is input. */ |
437 | 0 | return ws_hmac_buffer(hashalgo, prk, ikm, ikm_len, salt, salt_len); |
438 | 0 | } |
439 | | |
440 | | |
441 | | /* |
442 | | * From epan/dissectors/packet-tls-utils.c |
443 | | */ |
444 | | |
445 | | /* |
446 | | * Computes HKDF-Expand-Label(Secret, Label, Hash(context_value), Length) with a |
447 | | * custom label prefix. If "context_hash" is NULL, then an empty context is |
448 | | * used. Otherwise it must have the same length as the hash algorithm output. |
449 | | */ |
450 | | static int tls13_hkdf_expand_label_context(struct ndpi_detection_module_struct *ndpi_struct, |
451 | | int md, const StringInfo *secret, |
452 | | const char *label_prefix, const char *label, |
453 | | const uint8_t *context_hash, uint8_t context_length, |
454 | | uint16_t out_len, uint8_t **out) |
455 | 0 | { |
456 | | /* RFC 8446 Section 7.1: |
457 | | * HKDF-Expand-Label(Secret, Label, Context, Length) = |
458 | | * HKDF-Expand(Secret, HkdfLabel, Length) |
459 | | * struct { |
460 | | * uint16 length = Length; |
461 | | * opaque label<7..255> = "tls13 " + Label; // "tls13 " is label prefix. |
462 | | * opaque context<0..255> = Context; |
463 | | * } HkdfLabel; |
464 | | * |
465 | | * RFC 5869 HMAC-based Extract-and-Expand Key Derivation Function (HKDF): |
466 | | * HKDF-Expand(PRK, info, L) -> OKM |
467 | | */ |
468 | 0 | gcry_error_t err; |
469 | 0 | const unsigned int label_prefix_length = (unsigned int)strlen(label_prefix); |
470 | 0 | const unsigned label_length = (unsigned int)strlen(label); |
471 | | #ifdef NDPI_ENABLE_DEBUG_MESSAGES |
472 | | char buferr[128]; |
473 | | #endif |
474 | | |
475 | | /* Some sanity checks */ |
476 | 0 | if(!(label_length > 0 && label_prefix_length + label_length <= 255)) { |
477 | 0 | NDPI_LOG_DBG(ndpi_struct, "Failed sanity checks\n"); |
478 | 0 | return 0; |
479 | 0 | } |
480 | | |
481 | | /* info = HkdfLabel { length, label, context } */ |
482 | | /* Keep original Wireshark code as reference */ |
483 | | #if 0 |
484 | | GByteArray *info = g_byte_array_new(); |
485 | | const uint16_t length = htons(out_len); |
486 | | g_byte_array_append(info, (const guint8 *)&length, sizeof(length)); |
487 | | |
488 | | const uint8_t label_vector_length = label_prefix_length + label_length; |
489 | | g_byte_array_append(info, &label_vector_length, 1); |
490 | | g_byte_array_append(info, (const uint8_t *)label_prefix, label_prefix_length); |
491 | | g_byte_array_append(info, (const uint8_t *)label, label_length); |
492 | | |
493 | | g_byte_array_append(info, &context_length, 1); |
494 | | if (context_length) { |
495 | | g_byte_array_append(info, context_hash, context_length); |
496 | | } |
497 | | #else |
498 | 0 | uint32_t info_len = 0; |
499 | 0 | uint8_t *info_data = (uint8_t *)ndpi_malloc(1024); |
500 | 0 | if(!info_data) |
501 | 0 | return 0; |
502 | 0 | const uint16_t length = htons(out_len); |
503 | 0 | memcpy(&info_data[info_len], &length, sizeof(length)); |
504 | 0 | info_len += sizeof(length); |
505 | |
|
506 | 0 | const uint8_t label_vector_length = label_prefix_length + label_length; |
507 | 0 | memcpy(&info_data[info_len], &label_vector_length, 1); |
508 | 0 | info_len += 1; |
509 | 0 | memcpy(&info_data[info_len], (const uint8_t *)label_prefix, label_prefix_length); |
510 | 0 | info_len += label_prefix_length; |
511 | 0 | memcpy(&info_data[info_len], (const uint8_t *)label, label_length); |
512 | 0 | info_len += label_length; |
513 | |
|
514 | 0 | memcpy(&info_data[info_len], &context_length, 1); |
515 | 0 | info_len += 1; |
516 | 0 | if(context_length && context_hash != NULL) { |
517 | 0 | memcpy(&info_data[info_len], context_hash, context_length); |
518 | 0 | info_len += context_length; |
519 | 0 | } |
520 | 0 | #endif |
521 | |
|
522 | 0 | *out = (uint8_t *)ndpi_malloc(out_len); |
523 | 0 | if(!*out) { |
524 | 0 | ndpi_free(info_data); |
525 | 0 | return 0; |
526 | 0 | } |
527 | 0 | err = hkdf_expand(md, secret->data, secret->data_len, info_data, info_len, *out, out_len); |
528 | 0 | ndpi_free(info_data); |
529 | |
|
530 | 0 | if(err) { |
531 | 0 | NDPI_LOG_DBG(ndpi_struct, "Failed hkdf_expand: %s\n", __gcry_err(err, buferr, sizeof(buferr))); |
532 | 0 | ndpi_free(*out); |
533 | 0 | *out = NULL; |
534 | 0 | return 0; |
535 | 0 | } |
536 | | |
537 | 0 | return 1; |
538 | 0 | } |
539 | | static int tls13_hkdf_expand_label(struct ndpi_detection_module_struct *ndpi_struct, |
540 | | int md, const StringInfo *secret, |
541 | | const char *label_prefix, const char *label, |
542 | | uint16_t out_len, unsigned char **out) |
543 | 0 | { |
544 | 0 | return tls13_hkdf_expand_label_context(ndpi_struct, md, secret, label_prefix, label, NULL, 0, out_len, out); |
545 | 0 | } |
546 | | |
547 | | |
548 | | /* |
549 | | * From epan/dissectors/packet-quic.c |
550 | | */ |
551 | | |
552 | | static int quic_hkdf_expand_label(struct ndpi_detection_module_struct *ndpi_struct, |
553 | | int hash_algo, uint8_t *secret, uint32_t secret_len, |
554 | | const char *label, uint8_t *out, uint32_t out_len) |
555 | 0 | { |
556 | 0 | const StringInfo secret_si = { secret, secret_len }; |
557 | 0 | uint8_t *out_mem = NULL; |
558 | 0 | if(tls13_hkdf_expand_label(ndpi_struct, hash_algo, &secret_si, "tls13 ", label, out_len, &out_mem)) { |
559 | 0 | memcpy(out, out_mem, out_len); |
560 | 0 | ndpi_free(out_mem); |
561 | 0 | return 1; |
562 | 0 | } |
563 | 0 | return 0; |
564 | 0 | } |
565 | | static void quic_hp_cipher_reset(quic_hp_cipher *hp_cipher) |
566 | 0 | { |
567 | 0 | gcry_cipher_close(hp_cipher->hp_cipher); |
568 | | #if 0 |
569 | | memset(hp_cipher, 0, sizeof(*hp_cipher)); |
570 | | #endif |
571 | 0 | } |
572 | | static void quic_pp_cipher_reset(quic_pp_cipher *pp_cipher) |
573 | 0 | { |
574 | 0 | gcry_cipher_close(pp_cipher->pp_cipher); |
575 | | #if 0 |
576 | | memset(pp_cipher, 0, sizeof(*pp_cipher)); |
577 | | #endif |
578 | 0 | } |
579 | | static void quic_ciphers_reset(quic_ciphers *ciphers) |
580 | 0 | { |
581 | 0 | quic_hp_cipher_reset(&ciphers->hp_cipher); |
582 | 0 | quic_pp_cipher_reset(&ciphers->pp_cipher); |
583 | 0 | } |
584 | | /** |
585 | | * Expands the secret (length MUST be the same as the "hash_algo" digest size) |
586 | | * and initialize cipher with the new key. |
587 | | */ |
588 | | static int quic_hp_cipher_init(struct ndpi_detection_module_struct *ndpi_struct, |
589 | | quic_hp_cipher *hp_cipher, int hash_algo, |
590 | | uint8_t key_length, uint8_t *secret, |
591 | | uint32_t version) |
592 | 0 | { |
593 | 0 | uint8_t hp_key[256/8]; /* Maximum key size is for AES256 cipher. */ |
594 | 0 | uint32_t hash_len = gcry_md_get_algo_dlen(hash_algo); |
595 | 0 | char const * const label = is_version_with_v1_labels(version) ? "quic hp" : "quicv2 hp"; |
596 | |
|
597 | 0 | if(!quic_hkdf_expand_label(ndpi_struct, hash_algo, secret, hash_len, label, hp_key, key_length)) { |
598 | 0 | return 0; |
599 | 0 | } |
600 | | |
601 | 0 | return gcry_cipher_setkey(hp_cipher->hp_cipher, hp_key, key_length) == 0; |
602 | 0 | } |
603 | | static int quic_pp_cipher_init(struct ndpi_detection_module_struct *ndpi_struct, |
604 | | quic_pp_cipher *pp_cipher, int hash_algo, |
605 | | uint8_t key_length, uint8_t *secret, |
606 | | uint32_t version) |
607 | 0 | { |
608 | 0 | uint8_t write_key[256/8]; /* Maximum key size is for AES256 cipher. */ |
609 | 0 | uint32_t hash_len = gcry_md_get_algo_dlen(hash_algo); |
610 | 0 | char const * const key_label = is_version_with_v1_labels(version) ? "quic key" : "quicv2 key"; |
611 | 0 | char const * const iv_label = is_version_with_v1_labels(version) ? "quic iv" : "quicv2 iv"; |
612 | |
|
613 | 0 | if(key_length > sizeof(write_key)) { |
614 | 0 | return 0; |
615 | 0 | } |
616 | | |
617 | 0 | if(!quic_hkdf_expand_label(ndpi_struct, hash_algo, secret, hash_len, key_label, write_key, key_length) || |
618 | 0 | !quic_hkdf_expand_label(ndpi_struct, hash_algo, secret, hash_len, iv_label, pp_cipher->pp_iv, sizeof(pp_cipher->pp_iv))) { |
619 | 0 | return 0; |
620 | 0 | } |
621 | | |
622 | 0 | return gcry_cipher_setkey(pp_cipher->pp_cipher, write_key, key_length) == 0; |
623 | 0 | } |
624 | | /** |
625 | | * Maps a Packet Protection cipher to the Packet Number protection cipher. |
626 | | * See https://tools.ietf.org/html/draft-ietf-quic-tls-22#section-5.4.3 |
627 | | */ |
628 | | static int quic_get_pn_cipher_algo(int cipher_algo, int *hp_cipher_mode) |
629 | 0 | { |
630 | 0 | switch (cipher_algo) { |
631 | 0 | case GCRY_CIPHER_AES128: |
632 | 0 | case GCRY_CIPHER_AES256: |
633 | 0 | *hp_cipher_mode = GCRY_CIPHER_MODE_ECB; |
634 | 0 | return 1; |
635 | 0 | default: |
636 | 0 | return 0; |
637 | 0 | } |
638 | 0 | } |
639 | | /* |
640 | | * (Re)initialize the PNE/PP ciphers using the given cipher algorithm. |
641 | | * If the optional base secret is given, then its length MUST match the hash |
642 | | * algorithm output. |
643 | | */ |
644 | | static int quic_hp_cipher_prepare(struct ndpi_detection_module_struct *ndpi_struct, |
645 | | quic_hp_cipher *hp_cipher, int hash_algo, int cipher_algo, |
646 | | uint8_t *secret, u_int32_t version) |
647 | 0 | { |
648 | | #if 0 |
649 | | /* Clear previous state (if any). */ |
650 | | quic_hp_cipher_reset(hp_cipher); |
651 | | #endif |
652 | |
|
653 | 0 | int hp_cipher_mode; |
654 | 0 | if(!quic_get_pn_cipher_algo(cipher_algo, &hp_cipher_mode)) { |
655 | 0 | NDPI_LOG_DBG(ndpi_struct, "Unsupported cipher algorithm\n"); |
656 | 0 | return 0; |
657 | 0 | } |
658 | | |
659 | 0 | if(gcry_cipher_open(&hp_cipher->hp_cipher, cipher_algo, hp_cipher_mode, 0)) { |
660 | 0 | quic_hp_cipher_reset(hp_cipher); |
661 | 0 | NDPI_LOG_DBG(ndpi_struct, "Failed to create HP cipher\n"); |
662 | 0 | return 0; |
663 | 0 | } |
664 | | |
665 | 0 | if(secret) { |
666 | 0 | uint32_t cipher_keylen = (uint8_t)gcry_cipher_get_algo_keylen(cipher_algo); |
667 | 0 | if(!quic_hp_cipher_init(ndpi_struct, hp_cipher, hash_algo, cipher_keylen, secret, version)) { |
668 | 0 | quic_hp_cipher_reset(hp_cipher); |
669 | 0 | NDPI_LOG_DBG(ndpi_struct, "Failed to derive key material for HP cipher\n"); |
670 | 0 | return 0; |
671 | 0 | } |
672 | 0 | } |
673 | | |
674 | 0 | return 1; |
675 | 0 | } |
676 | | static int quic_pp_cipher_prepare(struct ndpi_detection_module_struct *ndpi_struct, |
677 | | quic_pp_cipher *pp_cipher, int hash_algo, int cipher_algo, |
678 | | int cipher_mode, uint8_t *secret, u_int32_t version) |
679 | 0 | { |
680 | | #if 0 |
681 | | /* Clear previous state (if any). */ |
682 | | quic_pp_cipher_reset(pp_cipher); |
683 | | #endif |
684 | |
|
685 | 0 | if(gcry_cipher_open(&pp_cipher->pp_cipher, cipher_algo, cipher_mode, 0)) { |
686 | 0 | quic_pp_cipher_reset(pp_cipher); |
687 | 0 | NDPI_LOG_DBG(ndpi_struct, "Failed to create PP cipher\n"); |
688 | 0 | return 0; |
689 | 0 | } |
690 | | |
691 | 0 | if(secret) { |
692 | 0 | uint32_t cipher_keylen = (uint8_t)gcry_cipher_get_algo_keylen(cipher_algo); |
693 | 0 | if(!quic_pp_cipher_init(ndpi_struct, pp_cipher, hash_algo, cipher_keylen, secret, version)) { |
694 | 0 | quic_pp_cipher_reset(pp_cipher); |
695 | 0 | NDPI_LOG_DBG(ndpi_struct, "Failed to derive key material for PP cipher\n"); |
696 | 0 | return 0; |
697 | 0 | } |
698 | 0 | } |
699 | | |
700 | 0 | return 1; |
701 | 0 | } |
702 | | static int quic_ciphers_prepare(struct ndpi_detection_module_struct *ndpi_struct, |
703 | | quic_ciphers *ciphers, int hash_algo, int cipher_algo, |
704 | | int cipher_mode, uint8_t *secret, u_int32_t version) |
705 | 0 | { |
706 | 0 | int ret; |
707 | |
|
708 | 0 | ret = quic_hp_cipher_prepare(ndpi_struct, &ciphers->hp_cipher, hash_algo, cipher_algo, secret, version); |
709 | 0 | if(ret != 1) |
710 | 0 | return ret; |
711 | 0 | ret = quic_pp_cipher_prepare(ndpi_struct, &ciphers->pp_cipher, hash_algo, cipher_algo, cipher_mode, secret, version); |
712 | 0 | if(ret != 1) |
713 | 0 | quic_hp_cipher_reset(&ciphers->hp_cipher); |
714 | 0 | return ret; |
715 | 0 | } |
716 | | /** |
717 | | * Given a header protection cipher, a buffer and the packet number offset, |
718 | | * return the unmasked first byte and packet number. |
719 | | * If the loss bits feature is enabled, the protected bits in the first byte |
720 | | * are fewer than usual: 3 instead of 5 (on short headers only) |
721 | | */ |
722 | | static int quic_decrypt_header(const uint8_t *packet_payload, |
723 | | uint32_t pn_offset, quic_hp_cipher *hp_cipher, |
724 | | int hp_cipher_algo, uint8_t *first_byte, uint32_t *pn, |
725 | | int loss_bits_negotiated) |
726 | 0 | { |
727 | 0 | if(!hp_cipher->hp_cipher) { |
728 | | /* Need to know the cipher */ |
729 | 0 | return 0; |
730 | 0 | } |
731 | 0 | gcry_cipher_hd_t h = hp_cipher->hp_cipher; |
732 | | |
733 | | /* Sample is always 16 bytes and starts after PKN (assuming length 4). |
734 | | https://tools.ietf.org/html/draft-ietf-quic-tls-22#section-5.4.2 */ |
735 | 0 | uint8_t sample[16]; |
736 | 0 | memcpy(sample, packet_payload + pn_offset + 4, 16); |
737 | |
|
738 | 0 | uint8_t mask[5] = { 0 }; |
739 | 0 | switch (hp_cipher_algo) { |
740 | 0 | case GCRY_CIPHER_AES128: |
741 | 0 | case GCRY_CIPHER_AES256: |
742 | | /* Encrypt in-place with AES-ECB and extract the mask. */ |
743 | 0 | if(gcry_cipher_encrypt(h, sample, sizeof(sample), NULL, 0)) { |
744 | 0 | return 0; |
745 | 0 | } |
746 | 0 | memcpy(mask, sample, sizeof(mask)); |
747 | 0 | break; |
748 | 0 | default: |
749 | 0 | return 0; |
750 | 0 | } |
751 | | |
752 | | /* https://tools.ietf.org/html/draft-ietf-quic-tls-22#section-5.4.1 */ |
753 | 0 | uint8_t packet0 = packet_payload[0]; |
754 | 0 | if((packet0 & 0x80) == 0x80) { |
755 | | /* Long header: 4 bits masked */ |
756 | 0 | packet0 ^= mask[0] & 0x0f; |
757 | 0 | } else { |
758 | | /* Short header */ |
759 | 0 | if(loss_bits_negotiated == 0) { |
760 | | /* Standard mask: 5 bits masked */ |
761 | 0 | packet0 ^= mask[0] & 0x1F; |
762 | 0 | } else { |
763 | | /* https://tools.ietf.org/html/draft-ferrieuxhamchaoui-quic-lossbits-03#section-5.3 */ |
764 | 0 | packet0 ^= mask[0] & 0x07; |
765 | 0 | } |
766 | 0 | } |
767 | 0 | uint32_t pkn_len = (packet0 & 0x03) + 1; |
768 | | /* printf("packet0 0x%x pkn_len %d\n", packet0, pkn_len); */ |
769 | |
|
770 | 0 | uint8_t pkn_bytes[4]; |
771 | 0 | memcpy(pkn_bytes, packet_payload + pn_offset, pkn_len); |
772 | 0 | uint32_t pkt_pkn = 0, i; |
773 | 0 | for(i = 0; i < pkn_len; i++) { |
774 | 0 | pkt_pkn |= (uint32_t)(pkn_bytes[i] ^ mask[1 + i]) << (8 * (pkn_len - 1 - i)); |
775 | 0 | } |
776 | 0 | *first_byte = packet0; |
777 | 0 | *pn = pkt_pkn; |
778 | 0 | return 1; |
779 | 0 | } |
780 | | /** |
781 | | * Given a QUIC message (header + non-empty payload), the actual packet number, |
782 | | * try to decrypt it using the cipher. |
783 | | * As the header points to the original buffer with an encrypted packet number, |
784 | | * the (encrypted) packet number length is also included. |
785 | | * |
786 | | * The actual packet number must be constructed according to |
787 | | * https://tools.ietf.org/html/draft-ietf-quic-transport-22#section-12.3 |
788 | | */ |
789 | | static void quic_decrypt_message(struct ndpi_detection_module_struct *ndpi_struct, |
790 | | quic_pp_cipher *pp_cipher, const uint8_t *packet_payload, uint32_t packet_payload_len, |
791 | | uint32_t header_length, uint8_t first_byte, uint32_t pkn_len, |
792 | | uint64_t packet_number, quic_decrypt_result_t *result) |
793 | 0 | { |
794 | 0 | gcry_error_t err; |
795 | 0 | uint8_t *header; |
796 | 0 | uint8_t nonce[TLS13_AEAD_NONCE_LENGTH]; |
797 | 0 | uint8_t *buffer; |
798 | 0 | uint8_t atag[16]; |
799 | 0 | uint32_t buffer_length, i; |
800 | | #ifdef NDPI_ENABLE_DEBUG_MESSAGES |
801 | | char buferr[128]; |
802 | | #endif |
803 | |
|
804 | 0 | if(!(pp_cipher != NULL) || |
805 | 0 | !(pp_cipher->pp_cipher != NULL) || |
806 | 0 | !(pkn_len < header_length) || |
807 | 0 | !(1 <= pkn_len && pkn_len <= 4)) { |
808 | 0 | NDPI_LOG_DBG(ndpi_struct, "Failed sanity checks\n"); |
809 | 0 | return; |
810 | 0 | } |
811 | | /* Copy header, but replace encrypted first byte and PKN by plaintext. */ |
812 | 0 | header = (uint8_t *)memdup(packet_payload, header_length); |
813 | 0 | if(!header) |
814 | 0 | return; |
815 | 0 | header[0] = first_byte; |
816 | 0 | for( i = 0; i < pkn_len; i++) { |
817 | 0 | header[header_length - 1 - i] = (uint8_t)(packet_number >> (8 * i)); |
818 | 0 | } |
819 | | |
820 | | /* Input is "header || ciphertext (buffer) || auth tag (16 bytes)" */ |
821 | 0 | buffer_length = packet_payload_len - (header_length + 16); |
822 | 0 | if(buffer_length == 0) { |
823 | 0 | NDPI_LOG_DBG(ndpi_struct, "Decryption not possible, ciphertext is too short\n"); |
824 | 0 | ndpi_free(header); |
825 | 0 | return; |
826 | 0 | } |
827 | 0 | buffer = (uint8_t *)memdup(packet_payload + header_length, buffer_length); |
828 | 0 | if(!buffer) { |
829 | 0 | ndpi_free(header); |
830 | 0 | return; |
831 | 0 | } |
832 | 0 | memcpy(atag, packet_payload + header_length + buffer_length, 16); |
833 | |
|
834 | 0 | memcpy(nonce, pp_cipher->pp_iv, TLS13_AEAD_NONCE_LENGTH); |
835 | | /* Packet number is left-padded with zeroes and XORed with write_iv */ |
836 | 0 | phton64(nonce + sizeof(nonce) - 8, pntoh64(nonce + sizeof(nonce) - 8) ^ packet_number); |
837 | |
|
838 | 0 | gcry_cipher_reset(pp_cipher->pp_cipher); |
839 | 0 | err = gcry_cipher_setiv(pp_cipher->pp_cipher, nonce, TLS13_AEAD_NONCE_LENGTH); |
840 | 0 | if(err) { |
841 | 0 | NDPI_LOG_DBG(ndpi_struct, "Decryption (setiv) failed: %s\n", __gcry_err(err, buferr, sizeof(buferr))); |
842 | 0 | ndpi_free(header); |
843 | 0 | ndpi_free(buffer); |
844 | 0 | return; |
845 | 0 | } |
846 | | |
847 | | /* associated data (A) is the contents of QUIC header */ |
848 | 0 | err = gcry_cipher_authenticate(pp_cipher->pp_cipher, header, header_length); |
849 | 0 | if(err) { |
850 | 0 | NDPI_LOG_DBG(ndpi_struct, "Decryption (authenticate) failed: %s\n", __gcry_err(err, buferr, sizeof(buferr))); |
851 | 0 | ndpi_free(header); |
852 | 0 | ndpi_free(buffer); |
853 | 0 | return; |
854 | 0 | } |
855 | | |
856 | 0 | ndpi_free(header); |
857 | | |
858 | | /* Output ciphertext (C) */ |
859 | 0 | err = gcry_cipher_decrypt(pp_cipher->pp_cipher, buffer, buffer_length, NULL, 0); |
860 | 0 | if(err) { |
861 | 0 | NDPI_LOG_DBG(ndpi_struct, "Decryption (decrypt) failed: %s\n", __gcry_err(err, buferr, sizeof(buferr))); |
862 | 0 | ndpi_free(buffer); |
863 | 0 | return; |
864 | 0 | } |
865 | | |
866 | 0 | err = gcry_cipher_checktag(pp_cipher->pp_cipher, atag, 16); |
867 | 0 | if(err) { |
868 | 0 | NDPI_LOG_DBG(ndpi_struct, "Decryption (checktag) failed: %s\n", __gcry_err(err, buferr, sizeof(buferr))); |
869 | 0 | ndpi_free(buffer); |
870 | 0 | return; |
871 | 0 | } |
872 | | |
873 | 0 | result->data = buffer; |
874 | 0 | result->data_len = buffer_length; |
875 | 0 | } |
876 | | /** |
877 | | * Compute the client and server initial secrets given Connection ID "cid". |
878 | | */ |
879 | | static int quic_derive_initial_secrets(struct ndpi_detection_module_struct *ndpi_struct, |
880 | | uint32_t version, |
881 | | const uint8_t *cid, uint8_t cid_len, |
882 | | uint8_t client_initial_secret[HASH_SHA2_256_LENGTH]) |
883 | 0 | { |
884 | | /* |
885 | | * https://tools.ietf.org/html/draft-ietf-quic-tls-29#section-5.2 |
886 | | * |
887 | | * initial_secret = HKDF-Extract(initial_salt, client_dst_connection_id) |
888 | | * |
889 | | * client_initial_secret = HKDF-Expand-Label(initial_secret, |
890 | | * "client in", "", Hash.length) |
891 | | * |
892 | | * Hash for handshake packets is SHA-256 (output size 32). |
893 | | */ |
894 | 0 | static const uint8_t handshake_salt_draft_22[20] = { |
895 | 0 | 0x7f, 0xbc, 0xdb, 0x0e, 0x7c, 0x66, 0xbb, 0xe9, 0x19, 0x3a, |
896 | 0 | 0x96, 0xcd, 0x21, 0x51, 0x9e, 0xbd, 0x7a, 0x02, 0x64, 0x4a |
897 | 0 | }; |
898 | 0 | static const uint8_t handshake_salt_draft_23[20] = { |
899 | 0 | 0xc3, 0xee, 0xf7, 0x12, 0xc7, 0x2e, 0xbb, 0x5a, 0x11, 0xa7, |
900 | 0 | 0xd2, 0x43, 0x2b, 0xb4, 0x63, 0x65, 0xbe, 0xf9, 0xf5, 0x02, |
901 | 0 | }; |
902 | 0 | static const uint8_t handshake_salt_draft_29[20] = { |
903 | 0 | 0xaf, 0xbf, 0xec, 0x28, 0x99, 0x93, 0xd2, 0x4c, 0x9e, 0x97, |
904 | 0 | 0x86, 0xf1, 0x9c, 0x61, 0x11, 0xe0, 0x43, 0x90, 0xa8, 0x99 |
905 | 0 | }; |
906 | 0 | static const uint8_t hanshake_salt_draft_q50[20] = { |
907 | 0 | 0x50, 0x45, 0x74, 0xEF, 0xD0, 0x66, 0xFE, 0x2F, 0x9D, 0x94, |
908 | 0 | 0x5C, 0xFC, 0xDB, 0xD3, 0xA7, 0xF0, 0xD3, 0xB5, 0x6B, 0x45 |
909 | 0 | }; |
910 | 0 | static const uint8_t hanshake_salt_draft_t50[20] = { |
911 | 0 | 0x7f, 0xf5, 0x79, 0xe5, 0xac, 0xd0, 0x72, 0x91, 0x55, 0x80, |
912 | 0 | 0x30, 0x4c, 0x43, 0xa2, 0x36, 0x7c, 0x60, 0x48, 0x83, 0x10 |
913 | 0 | }; |
914 | 0 | static const uint8_t hanshake_salt_draft_t51[20] = { |
915 | 0 | 0x7a, 0x4e, 0xde, 0xf4, 0xe7, 0xcc, 0xee, 0x5f, 0xa4, 0x50, |
916 | 0 | 0x6c, 0x19, 0x12, 0x4f, 0xc8, 0xcc, 0xda, 0x6e, 0x03, 0x3d |
917 | 0 | }; |
918 | 0 | static const uint8_t handshake_salt_v1[20] = { |
919 | 0 | 0x38, 0x76, 0x2c, 0xf7, 0xf5, 0x59, 0x34, 0xb3, 0x4d, 0x17, |
920 | 0 | 0x9a, 0xe6, 0xa4, 0xc8, 0x0c, 0xad, 0xcc, 0xbb, 0x7f, 0x0a |
921 | 0 | }; |
922 | 0 | static const uint8_t handshake_salt_v2_draft_00[20] = { |
923 | 0 | 0x0d, 0xed, 0xe3, 0xde, 0xf7, 0x00, 0xa6, 0xdb, 0x81, 0x93, |
924 | 0 | 0x81, 0xbe, 0x6e, 0x26, 0x9d, 0xcb, 0xf9, 0xbd, 0x2e, 0xd9 |
925 | 0 | }; |
926 | 0 | gcry_error_t err; |
927 | 0 | uint8_t secret[HASH_SHA2_256_LENGTH]; |
928 | | #ifdef NDPI_ENABLE_DEBUG_MESSAGES |
929 | | char buferr[128]; |
930 | | #endif |
931 | |
|
932 | 0 | if(version == V_Q050) { |
933 | 0 | err = hkdf_extract(GCRY_MD_SHA256, hanshake_salt_draft_q50, |
934 | 0 | sizeof(hanshake_salt_draft_q50), |
935 | 0 | cid, cid_len, secret); |
936 | 0 | } else if(version == V_T050) { |
937 | 0 | err = hkdf_extract(GCRY_MD_SHA256, hanshake_salt_draft_t50, |
938 | 0 | sizeof(hanshake_salt_draft_t50), |
939 | 0 | cid, cid_len, secret); |
940 | 0 | } else if(version == V_T051) { |
941 | 0 | err = hkdf_extract(GCRY_MD_SHA256, hanshake_salt_draft_t51, |
942 | 0 | sizeof(hanshake_salt_draft_t51), |
943 | 0 | cid, cid_len, secret); |
944 | 0 | } else if(is_quic_ver_less_than(version, 22)) { |
945 | 0 | err = hkdf_extract(GCRY_MD_SHA256, handshake_salt_draft_22, |
946 | 0 | sizeof(handshake_salt_draft_22), |
947 | 0 | cid, cid_len, secret); |
948 | 0 | } else if(is_quic_ver_less_than(version, 28)) { |
949 | 0 | err = hkdf_extract(GCRY_MD_SHA256, handshake_salt_draft_23, |
950 | 0 | sizeof(handshake_salt_draft_23), |
951 | 0 | cid, cid_len, secret); |
952 | 0 | } else if(is_quic_ver_less_than(version, 32)) { |
953 | 0 | err = hkdf_extract(GCRY_MD_SHA256, handshake_salt_draft_29, |
954 | 0 | sizeof(handshake_salt_draft_29), |
955 | 0 | cid, cid_len, secret); |
956 | 0 | } else if (is_quic_ver_less_than(version, 34)) { |
957 | 0 | err = hkdf_extract(GCRY_MD_SHA256, handshake_salt_v1, |
958 | 0 | sizeof(handshake_salt_v1), |
959 | 0 | cid, cid_len, secret); |
960 | 0 | } else { |
961 | 0 | err = hkdf_extract(GCRY_MD_SHA256, handshake_salt_v2_draft_00, |
962 | 0 | sizeof(handshake_salt_v2_draft_00), |
963 | 0 | cid, cid_len, secret); |
964 | 0 | } |
965 | 0 | if(err) { |
966 | 0 | NDPI_LOG_DBG(ndpi_struct, "Failed to extract secrets: %s\n", __gcry_err(err, buferr, sizeof(buferr))); |
967 | 0 | return -1; |
968 | 0 | } |
969 | | |
970 | 0 | if(!quic_hkdf_expand_label(ndpi_struct, GCRY_MD_SHA256, secret, sizeof(secret), "client in", |
971 | 0 | client_initial_secret, HASH_SHA2_256_LENGTH)) { |
972 | 0 | NDPI_LOG_DBG(ndpi_struct, "Key expansion (client) failed: %s\n", __gcry_err(err, buferr, sizeof(buferr))); |
973 | 0 | return -1; |
974 | 0 | } |
975 | | |
976 | 0 | return 0; |
977 | 0 | } |
978 | | |
979 | | /* |
980 | | * End Wireshark code |
981 | | */ |
982 | | |
983 | | |
984 | | static uint8_t *decrypt_initial_packet(struct ndpi_detection_module_struct *ndpi_struct, |
985 | | const uint8_t *orig_dest_conn_id, uint8_t orig_dest_conn_id_len, |
986 | | uint8_t dest_conn_id_len, |
987 | | uint8_t source_conn_id_len, uint32_t version, |
988 | | uint32_t *clear_payload_len) |
989 | 0 | { |
990 | 0 | uint64_t token_length, payload_length, packet_number; |
991 | 0 | struct ndpi_packet_struct *packet = &ndpi_struct->packet; |
992 | 0 | uint8_t first_byte; |
993 | 0 | uint32_t pkn32, pn_offset, pkn_len, offset; |
994 | 0 | quic_ciphers ciphers; /* Client initial ciphers */ |
995 | 0 | quic_decrypt_result_t decryption = { 0, 0}; |
996 | 0 | uint8_t client_secret[HASH_SHA2_256_LENGTH]; |
997 | |
|
998 | 0 | memset(&ciphers, '\0', sizeof(ciphers)); |
999 | 0 | if(quic_derive_initial_secrets(ndpi_struct, version, orig_dest_conn_id, orig_dest_conn_id_len, |
1000 | 0 | client_secret) != 0) { |
1001 | 0 | NDPI_LOG_DBG(ndpi_struct, "Error quic_derive_initial_secrets\n"); |
1002 | 0 | return NULL; |
1003 | 0 | } |
1004 | | |
1005 | | /* Packet numbers are protected with AES128-CTR, |
1006 | | Initial packets are protected with AEAD_AES_128_GCM. */ |
1007 | 0 | if(!quic_ciphers_prepare(ndpi_struct, &ciphers, GCRY_MD_SHA256, |
1008 | 0 | GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_GCM, |
1009 | 0 | client_secret, version)) { |
1010 | 0 | NDPI_LOG_DBG(ndpi_struct, "Error quic_cipher_prepare\n"); |
1011 | 0 | return NULL; |
1012 | 0 | } |
1013 | | |
1014 | | /* Type(1) + version(4) + DCIL + DCID + SCIL + SCID */ |
1015 | 0 | pn_offset = 1 + 4 + 1 + dest_conn_id_len + 1 + source_conn_id_len; |
1016 | 0 | pn_offset += quic_len(&packet->payload[pn_offset], &token_length); |
1017 | 0 | pn_offset += token_length; |
1018 | | /* Checks: quic_len reads 8 bytes, at most; quic_decrypt_header reads other 20 bytes. |
1019 | | Promote to uint64_t to avoid unsigned wrapping */ |
1020 | 0 | if((uint64_t)pn_offset + 8 + (4 + 16) >= (uint64_t)packet->payload_packet_len) { |
1021 | 0 | quic_ciphers_reset(&ciphers); |
1022 | 0 | return NULL; |
1023 | 0 | } |
1024 | 0 | pn_offset += quic_len(&packet->payload[pn_offset], &payload_length); |
1025 | |
|
1026 | 0 | NDPI_LOG_DBG2(ndpi_struct, "pn_offset %d token_length %d payload_length %d\n", |
1027 | 0 | pn_offset, token_length, payload_length); |
1028 | |
|
1029 | 0 | if (pn_offset + payload_length > packet->payload_packet_len) { |
1030 | 0 | NDPI_LOG_DBG(ndpi_struct, "Too short %d %d\n", pn_offset + payload_length, |
1031 | 0 | packet->payload_packet_len); |
1032 | 0 | quic_ciphers_reset(&ciphers); |
1033 | 0 | return NULL; |
1034 | 0 | } |
1035 | | |
1036 | 0 | if(!quic_decrypt_header(&packet->payload[0], pn_offset, &ciphers.hp_cipher, |
1037 | 0 | GCRY_CIPHER_AES128, &first_byte, &pkn32, 0)) { |
1038 | 0 | quic_ciphers_reset(&ciphers); |
1039 | 0 | return NULL; |
1040 | 0 | } |
1041 | 0 | NDPI_LOG_DBG2(ndpi_struct, "first_byte 0x%x pkn32 0x%x\n", first_byte, pkn32); |
1042 | |
|
1043 | 0 | pkn_len = (first_byte & 3) + 1; |
1044 | | /* TODO: is it always true in Initial Packets? */ |
1045 | 0 | packet_number = pkn32; |
1046 | |
|
1047 | 0 | offset = pn_offset + pkn_len; |
1048 | 0 | if (!(pn_offset + payload_length >= offset + 16)) { |
1049 | 0 | NDPI_LOG_DBG(ndpi_struct, "No room for Auth Tag %d %d", |
1050 | 0 | pn_offset + payload_length, offset); |
1051 | 0 | quic_ciphers_reset(&ciphers); |
1052 | 0 | return NULL; |
1053 | 0 | } |
1054 | 0 | quic_decrypt_message(ndpi_struct, &ciphers.pp_cipher, &packet->payload[0], pn_offset + payload_length, |
1055 | 0 | offset, first_byte, pkn_len, packet_number, &decryption); |
1056 | |
|
1057 | 0 | quic_ciphers_reset(&ciphers); |
1058 | |
|
1059 | 0 | if(decryption.data_len) { |
1060 | 0 | *clear_payload_len = decryption.data_len; |
1061 | 0 | return decryption.data; |
1062 | 0 | } |
1063 | 0 | return NULL; |
1064 | 0 | } |
1065 | | |
1066 | | static void update_reasm_buf_bitmap(u_int8_t *buffer_bitmap, |
1067 | | const u_int32_t buffer_bitmap_size, |
1068 | | const u_int32_t recv_pos, |
1069 | | const u_int32_t recv_len) |
1070 | 0 | { |
1071 | 0 | if (!recv_len || !buffer_bitmap_size || !buffer_bitmap || recv_pos + recv_len > buffer_bitmap_size * 8) |
1072 | 0 | return; |
1073 | 0 | const u_int32_t start_byte = recv_pos / 8; |
1074 | 0 | const u_int32_t end_byte = (recv_pos + recv_len - 1) / 8; |
1075 | 0 | const u_int32_t start_bit = recv_pos % 8; |
1076 | 0 | const u_int32_t end_bit = (start_bit + recv_len - 1) % 8; |
1077 | 0 | if (start_byte == end_byte) |
1078 | 0 | buffer_bitmap[start_byte] |= (((1U << recv_len) - 1U) << start_bit); // fill from bit 'start_bit' until bit 'end_bit', both inclusive |
1079 | 0 | else{ |
1080 | 0 | u_int32_t i; |
1081 | |
|
1082 | 0 | for (i = start_byte + 1; i <= end_byte - 1; i++) |
1083 | 0 | buffer_bitmap[i] = 0xff; // completely received byte |
1084 | 0 | buffer_bitmap[start_byte] |= ~((1U << start_bit) - 1U); // fill from bit 'start_bit' until bit 7, both inclusive |
1085 | 0 | buffer_bitmap[end_byte] |= (1U << (end_bit + 1U)) - 1U; // fill from bit 0 until bit 'end_bit', both inclusive |
1086 | 0 | } |
1087 | 0 | } |
1088 | | |
1089 | | static int is_reasm_buf_complete(const u_int8_t *buffer_bitmap, |
1090 | | const u_int32_t buffer_len) |
1091 | 0 | { |
1092 | 0 | const u_int32_t complete_bytes = buffer_len / 8; |
1093 | 0 | const u_int32_t remaining_bits = buffer_len % 8; |
1094 | 0 | u_int32_t i; |
1095 | |
|
1096 | 0 | if (!buffer_bitmap) |
1097 | 0 | return 0; |
1098 | | |
1099 | 0 | for(i = 0; i < complete_bytes; i++) |
1100 | 0 | if (buffer_bitmap[i] != 0xff) |
1101 | 0 | return 0; |
1102 | | |
1103 | 0 | if (remaining_bits && buffer_bitmap[complete_bytes] != (1U << (remaining_bits)) - 1) |
1104 | 0 | return 0; |
1105 | | |
1106 | 0 | return 1; |
1107 | 0 | } |
1108 | | |
1109 | | static int __reassemble(struct ndpi_flow_struct *flow, const u_int8_t *frag, |
1110 | | uint64_t frag_len, uint64_t frag_offset, |
1111 | | const u_int8_t **buf, u_int64_t *buf_len) |
1112 | 0 | { |
1113 | 0 | const uint64_t max_quic_reasm_buffer_len = 4096; /* Let's say a couple of full-MTU packets... Must be multiple of 8*/ |
1114 | 0 | const uint64_t quic_reasm_buffer_bitmap_len = max_quic_reasm_buffer_len / 8; |
1115 | 0 | const uint64_t last_pos = frag_offset + frag_len; |
1116 | |
|
1117 | 0 | if(!flow->l4.udp.quic_reasm_buf) { |
1118 | 0 | flow->l4.udp.quic_reasm_buf = (uint8_t *)ndpi_malloc(max_quic_reasm_buffer_len); |
1119 | 0 | if(!flow->l4.udp.quic_reasm_buf_bitmap) |
1120 | 0 | flow->l4.udp.quic_reasm_buf_bitmap = (uint8_t *)ndpi_calloc(quic_reasm_buffer_bitmap_len, sizeof(uint8_t)); |
1121 | 0 | if(!flow->l4.udp.quic_reasm_buf || !flow->l4.udp.quic_reasm_buf_bitmap) |
1122 | 0 | return -1; /* Memory error */ |
1123 | 0 | flow->l4.udp.quic_reasm_buf_last_pos = 0; |
1124 | 0 | } |
1125 | 0 | if(last_pos > max_quic_reasm_buffer_len) |
1126 | 0 | return -3; /* Buffer too small */ |
1127 | | |
1128 | 0 | memcpy(&flow->l4.udp.quic_reasm_buf[frag_offset], frag, frag_len); |
1129 | 0 | flow->l4.udp.quic_reasm_buf_last_pos = last_pos > flow->l4.udp.quic_reasm_buf_last_pos ? last_pos : flow->l4.udp.quic_reasm_buf_last_pos; |
1130 | 0 | update_reasm_buf_bitmap(flow->l4.udp.quic_reasm_buf_bitmap, quic_reasm_buffer_bitmap_len, frag_offset, frag_len); |
1131 | |
|
1132 | 0 | *buf = flow->l4.udp.quic_reasm_buf; |
1133 | 0 | *buf_len = flow->l4.udp.quic_reasm_buf_last_pos; |
1134 | 0 | return 0; |
1135 | 0 | } |
1136 | | static int is_ch_complete(const u_int8_t *buf, uint64_t buf_len) |
1137 | 0 | { |
1138 | 0 | uint32_t msg_len; |
1139 | |
|
1140 | 0 | if(buf_len >= 4) { |
1141 | 0 | msg_len = (buf[1] << 16) + (buf[2] << 8) + buf[3]; |
1142 | 0 | if (4 + msg_len == buf_len) { |
1143 | 0 | return 1; |
1144 | 0 | } |
1145 | 0 | } |
1146 | 0 | return 0; |
1147 | 0 | } |
1148 | | static int is_ch_reassembler_pending(struct ndpi_flow_struct *flow) |
1149 | 0 | { |
1150 | 0 | return flow->l4.udp.quic_reasm_buf != NULL && |
1151 | 0 | !(is_reasm_buf_complete(flow->l4.udp.quic_reasm_buf_bitmap, flow->l4.udp.quic_reasm_buf_last_pos) |
1152 | 0 | && is_ch_complete(flow->l4.udp.quic_reasm_buf, flow->l4.udp.quic_reasm_buf_last_pos)); |
1153 | 0 | } |
1154 | | static const uint8_t *get_reassembled_crypto_data(struct ndpi_detection_module_struct *ndpi_struct, |
1155 | | struct ndpi_flow_struct *flow, |
1156 | | const u_int8_t *frag, |
1157 | | uint64_t frag_offset, uint64_t frag_len, |
1158 | | uint64_t *crypto_data_len) |
1159 | 0 | { |
1160 | 0 | const u_int8_t *crypto_data; |
1161 | 0 | int rc; |
1162 | |
|
1163 | 0 | NDPI_LOG_DBG2(ndpi_struct, "frag %d/%d\n", frag_offset, frag_len); |
1164 | | |
1165 | | /* Fast path: no need of reassembler stuff */ |
1166 | 0 | if(frag_offset == 0 && |
1167 | 0 | is_ch_complete(frag, frag_len)) { |
1168 | 0 | NDPI_LOG_DBG2(ndpi_struct, "Complete CH (fast path)\n"); |
1169 | 0 | *crypto_data_len = frag_len; |
1170 | 0 | return frag; |
1171 | 0 | } |
1172 | | |
1173 | 0 | rc = __reassemble(flow, frag, frag_len, frag_offset, |
1174 | 0 | &crypto_data, crypto_data_len); |
1175 | 0 | if(rc == 0) { |
1176 | 0 | if(is_reasm_buf_complete(flow->l4.udp.quic_reasm_buf_bitmap, *crypto_data_len) && |
1177 | 0 | is_ch_complete(crypto_data, *crypto_data_len)) { |
1178 | 0 | NDPI_LOG_DBG2(ndpi_struct, "Reassembler completed!\n"); |
1179 | 0 | return crypto_data; |
1180 | 0 | } |
1181 | 0 | NDPI_LOG_DBG2(ndpi_struct, "CH not yet completed\n"); |
1182 | 0 | } else { |
1183 | 0 | NDPI_LOG_DBG(ndpi_struct, "Reassembler error: %d\n", rc); |
1184 | 0 | } |
1185 | 0 | return NULL; |
1186 | 0 | } |
1187 | | |
1188 | | const uint8_t *get_crypto_data(struct ndpi_detection_module_struct *ndpi_struct, |
1189 | | struct ndpi_flow_struct *flow, |
1190 | | u_int8_t *clear_payload, uint32_t clear_payload_len, |
1191 | | uint64_t *crypto_data_len) |
1192 | 0 | { |
1193 | 0 | const u_int8_t *crypto_data = NULL; |
1194 | 0 | uint32_t counter; |
1195 | 0 | uint8_t first_nonzero_payload_byte, offset_len; |
1196 | 0 | uint64_t unused, frag_offset, frag_len; |
1197 | 0 | u_int32_t version = flow->protos.tls_quic.quic_version; |
1198 | |
|
1199 | 0 | counter = 0; |
1200 | 0 | while(counter < clear_payload_len && clear_payload[counter] == 0) |
1201 | 0 | counter += 1; |
1202 | 0 | if(counter >= clear_payload_len) |
1203 | 0 | return NULL; |
1204 | 0 | first_nonzero_payload_byte = clear_payload[counter]; |
1205 | 0 | NDPI_LOG_DBG2(ndpi_struct, "first_nonzero_payload_byte 0x%x\n", first_nonzero_payload_byte); |
1206 | 0 | if(is_gquic_ver_less_than(version, 46)) { |
1207 | 0 | if(first_nonzero_payload_byte == 0x40 || |
1208 | 0 | first_nonzero_payload_byte == 0x60) { |
1209 | | /* Probably an ACK/NACK frame: this CHLO is not the first one but try |
1210 | | decoding it nonetheless */ |
1211 | 0 | counter += (first_nonzero_payload_byte == 0x40) ? 6 : 9; |
1212 | 0 | if(counter >= clear_payload_len) |
1213 | 0 | return NULL; |
1214 | 0 | first_nonzero_payload_byte = clear_payload[counter]; |
1215 | 0 | } |
1216 | 0 | if((first_nonzero_payload_byte != 0xA0) && |
1217 | 0 | (first_nonzero_payload_byte != 0xA4)) { |
1218 | 0 | NDPI_LOG_DBG(ndpi_struct, "Unexpected frame 0x%x version 0x%x\n",\ |
1219 | 0 | first_nonzero_payload_byte, version); |
1220 | 0 | return NULL; |
1221 | 0 | } |
1222 | 0 | offset_len = (first_nonzero_payload_byte & 0x1C) >> 2; |
1223 | 0 | if(offset_len > 0) |
1224 | 0 | offset_len += 1; |
1225 | 0 | if(counter + 2 + offset_len + 2 /*gquic_get_u16 reads 2 bytes */ > clear_payload_len) |
1226 | 0 | return NULL; |
1227 | 0 | if(clear_payload[counter + 1] != 0x01) { |
1228 | 0 | NDPI_LOG_DBG(ndpi_struct, "Unexpected stream ID version 0x%x\n", version); |
1229 | 0 | return NULL; |
1230 | 0 | } |
1231 | 0 | counter += 2 + offset_len; |
1232 | 0 | *crypto_data_len = gquic_get_u16(&clear_payload[counter], version); |
1233 | 0 | counter += 2; |
1234 | 0 | if(*crypto_data_len + counter > clear_payload_len) { |
1235 | 0 | NDPI_LOG_DBG(ndpi_struct, "Invalid length %lu + %d > %d version 0x%x\n", |
1236 | 0 | (unsigned long)*crypto_data_len, counter, clear_payload_len, version); |
1237 | 0 | return NULL; |
1238 | 0 | } |
1239 | 0 | crypto_data = &clear_payload[counter]; |
1240 | |
|
1241 | 0 | } else if(version == V_Q050 || version == V_T050 || version == V_T051) { |
1242 | 0 | if(first_nonzero_payload_byte == 0x40 || |
1243 | 0 | first_nonzero_payload_byte == 0x60) { |
1244 | | /* Probably an ACK/NACK frame: this CHLO is not the first one but try |
1245 | | decoding it nonetheless */ |
1246 | 0 | counter += (first_nonzero_payload_byte == 0x40) ? 6 : 9; |
1247 | 0 | if(counter >= clear_payload_len) |
1248 | 0 | return NULL; |
1249 | 0 | first_nonzero_payload_byte = clear_payload[counter]; |
1250 | 0 | } |
1251 | 0 | if(first_nonzero_payload_byte != 0x08) { |
1252 | 0 | NDPI_LOG_DBG(ndpi_struct, "Unexpected frame 0x%x\n", first_nonzero_payload_byte); |
1253 | 0 | return NULL; |
1254 | 0 | } |
1255 | 0 | counter += 1; |
1256 | 0 | if(counter + 8 + 8 >= clear_payload_len) /* quic_len reads 8 bytes, at most */ |
1257 | 0 | return NULL; |
1258 | 0 | counter += quic_len(&clear_payload[counter], &unused); |
1259 | 0 | counter += quic_len(&clear_payload[counter], crypto_data_len); |
1260 | 0 | if(*crypto_data_len + counter > clear_payload_len) { |
1261 | 0 | NDPI_LOG_DBG(ndpi_struct, "Invalid length %lu + %d > %d version 0x%x\n", |
1262 | 0 | (unsigned long)*crypto_data_len, counter, clear_payload_len, version); |
1263 | 0 | return NULL; |
1264 | 0 | } |
1265 | 0 | crypto_data = &clear_payload[counter]; |
1266 | |
|
1267 | 0 | } else { /* All other versions */ |
1268 | 0 | while(counter < clear_payload_len) { |
1269 | 0 | uint8_t frame_type = clear_payload[counter]; |
1270 | 0 | switch(frame_type) { |
1271 | 0 | case 0x00: |
1272 | 0 | NDPI_LOG_DBG2(ndpi_struct, "PADDING frame\n"); |
1273 | 0 | while(counter < clear_payload_len && |
1274 | 0 | clear_payload[counter] == 0) |
1275 | 0 | counter += 1; |
1276 | 0 | break; |
1277 | 0 | case 0x01: |
1278 | 0 | NDPI_LOG_DBG2(ndpi_struct, "PING frame\n"); |
1279 | 0 | counter += 1; |
1280 | 0 | break; |
1281 | 0 | case 0x06: |
1282 | 0 | NDPI_LOG_DBG2(ndpi_struct, "CRYPTO frame\n"); |
1283 | 0 | counter += 1; |
1284 | 0 | if(counter >= clear_payload_len || |
1285 | 0 | counter + quic_len_buffer_still_required(clear_payload[counter]) >= clear_payload_len) |
1286 | 0 | return NULL; |
1287 | 0 | counter += quic_len(&clear_payload[counter], &frag_offset); |
1288 | 0 | if(counter >= clear_payload_len || |
1289 | 0 | counter + quic_len_buffer_still_required(clear_payload[counter]) >= clear_payload_len) |
1290 | 0 | return NULL; |
1291 | 0 | counter += quic_len(&clear_payload[counter], &frag_len); |
1292 | 0 | if(frag_len + counter > clear_payload_len) { |
1293 | 0 | NDPI_LOG_DBG(ndpi_struct, "Invalid crypto frag length %lu + %d > %d version 0x%x\n", |
1294 | 0 | (unsigned long)frag_len, counter, clear_payload_len, version); |
1295 | 0 | return NULL; |
1296 | 0 | } |
1297 | 0 | crypto_data = get_reassembled_crypto_data(ndpi_struct, flow, |
1298 | 0 | &clear_payload[counter], |
1299 | 0 | frag_offset, frag_len, |
1300 | 0 | crypto_data_len); |
1301 | 0 | if(crypto_data) { |
1302 | 0 | return crypto_data; |
1303 | 0 | } |
1304 | 0 | NDPI_LOG_DBG(ndpi_struct, "Crypto reassembler pending\n"); |
1305 | 0 | counter += frag_len; |
1306 | 0 | break; |
1307 | 0 | case 0x1C: /* CC */ |
1308 | 0 | case 0x02: /* ACK */ |
1309 | 0 | NDPI_LOG_DBG2(ndpi_struct, "Unexpected CC/ACK frame\n"); |
1310 | 0 | return NULL; |
1311 | 0 | default: |
1312 | 0 | NDPI_LOG_DBG(ndpi_struct, "Unexpected frame 0x%x\n", frame_type); |
1313 | 0 | return NULL; |
1314 | 0 | } |
1315 | 0 | } |
1316 | 0 | if(counter > clear_payload_len) { |
1317 | 0 | NDPI_LOG_DBG(ndpi_struct, "Error parsing frames %d %d\n", counter, clear_payload_len); |
1318 | 0 | return NULL; |
1319 | 0 | } |
1320 | 0 | } |
1321 | 0 | return crypto_data; |
1322 | 0 | } |
1323 | | |
1324 | | static uint8_t *get_clear_payload(struct ndpi_detection_module_struct *ndpi_struct, |
1325 | | struct ndpi_flow_struct *flow, |
1326 | | uint32_t version, uint32_t *clear_payload_len) |
1327 | 0 | { |
1328 | 0 | struct ndpi_packet_struct *packet = &ndpi_struct->packet; |
1329 | 0 | u_int8_t *clear_payload; |
1330 | 0 | u_int8_t dest_conn_id_len; |
1331 | 0 | u_int8_t source_conn_id_len; |
1332 | |
|
1333 | 0 | if(is_gquic_ver_less_than(version, 43)) { |
1334 | 0 | clear_payload = (uint8_t *)&packet->payload[26]; |
1335 | 0 | *clear_payload_len = packet->payload_packet_len - 26; |
1336 | | /* Skip Private-flag field for version for < Q34 */ |
1337 | 0 | if(is_gquic_ver_less_than(version, 33)) { |
1338 | 0 | clear_payload += 1; |
1339 | 0 | (*clear_payload_len) -= 1; |
1340 | 0 | } |
1341 | 0 | } else if(version == V_Q046) { |
1342 | 0 | if(packet->payload[5] != 0x50) { |
1343 | 0 | NDPI_LOG_DBG(ndpi_struct, "Q46 invalid conn id len 0x%x\n", |
1344 | 0 | packet->payload[5]); |
1345 | 0 | return NULL; |
1346 | 0 | } |
1347 | 0 | clear_payload = (uint8_t *)&packet->payload[30]; |
1348 | 0 | *clear_payload_len = packet->payload_packet_len - 30; |
1349 | 0 | } else { |
1350 | | /* Upper limit of CIDs length has been already validated. If dest_conn_id_len is 0, |
1351 | | this is probably the Initial Packet from the server */ |
1352 | 0 | dest_conn_id_len = packet->payload[5]; |
1353 | 0 | if(dest_conn_id_len == 0) { |
1354 | 0 | NDPI_LOG_DBG(ndpi_struct, "Packet 0x%x with dest_conn_id_len %d\n", |
1355 | 0 | version, dest_conn_id_len); |
1356 | 0 | return NULL; |
1357 | 0 | } |
1358 | | |
1359 | 0 | source_conn_id_len = packet->payload[6 + dest_conn_id_len]; |
1360 | 0 | const u_int8_t *dest_conn_id = &packet->payload[6]; |
1361 | | |
1362 | | /* For initializing the ciphers we need the DCID of the very first Initial |
1363 | | sent by the client. This is quite important when CH is fragmented into multiple |
1364 | | packets and these packets have different DCID */ |
1365 | 0 | if(flow->l4.udp.quic_orig_dest_conn_id_len == 0) { |
1366 | 0 | memcpy(flow->l4.udp.quic_orig_dest_conn_id, |
1367 | 0 | dest_conn_id, dest_conn_id_len); |
1368 | 0 | flow->l4.udp.quic_orig_dest_conn_id_len = dest_conn_id_len; |
1369 | 0 | } |
1370 | |
|
1371 | 0 | clear_payload = decrypt_initial_packet(ndpi_struct, |
1372 | 0 | flow->l4.udp.quic_orig_dest_conn_id, |
1373 | 0 | flow->l4.udp.quic_orig_dest_conn_id_len, |
1374 | 0 | dest_conn_id_len, |
1375 | 0 | source_conn_id_len, version, |
1376 | 0 | clear_payload_len); |
1377 | 0 | } |
1378 | | |
1379 | 0 | return clear_payload; |
1380 | 0 | } |
1381 | | |
1382 | | void process_tls(struct ndpi_detection_module_struct *ndpi_struct, |
1383 | | struct ndpi_flow_struct *flow, |
1384 | | const u_int8_t *crypto_data, uint32_t crypto_data_len) |
1385 | 0 | { |
1386 | 0 | struct ndpi_packet_struct *packet = &ndpi_struct->packet; |
1387 | | |
1388 | | /* Overwriting packet payload */ |
1389 | 0 | u_int16_t p_len; |
1390 | 0 | const u_int8_t *p; |
1391 | 0 | p = packet->payload; |
1392 | 0 | p_len = packet->payload_packet_len; |
1393 | 0 | packet->payload = crypto_data; |
1394 | 0 | packet->payload_packet_len = crypto_data_len; |
1395 | |
|
1396 | 0 | processClientServerHello(ndpi_struct, flow, flow->protos.tls_quic.quic_version); |
1397 | 0 | flow->protos.tls_quic.client_hello_processed = 1; /* Allow matching of custom categories */ |
1398 | | |
1399 | | /* Restore */ |
1400 | 0 | packet->payload = p; |
1401 | 0 | packet->payload_packet_len = p_len; |
1402 | | |
1403 | | /* ServerHello is not needed to sub-classified QUIC, so we ignore it: |
1404 | | this way we lose JA3S and negotiated ciphers... |
1405 | | Negotiated version is only present in the ServerHello message too, but |
1406 | | fortunately, QUIC always uses TLS version 1.3 */ |
1407 | 0 | flow->protos.tls_quic.ssl_version = 0x0304; |
1408 | | |
1409 | | /* DNS-over-QUIC: ALPN is "doq" or "doq-XXX" (for drafts versions) */ |
1410 | 0 | if(flow->protos.tls_quic.advertised_alpns && |
1411 | 0 | strncmp(flow->protos.tls_quic.advertised_alpns, "doq", 3) == 0) { |
1412 | 0 | NDPI_LOG_DBG(ndpi_struct, "Found DOQ (ALPN: [%s])\n", flow->protos.tls_quic.advertised_alpns); |
1413 | 0 | ndpi_set_detected_protocol(ndpi_struct, flow, NDPI_PROTOCOL_DOH_DOT, NDPI_PROTOCOL_QUIC, NDPI_CONFIDENCE_DPI); |
1414 | 0 | } |
1415 | 0 | } |
1416 | | void process_chlo(struct ndpi_detection_module_struct *ndpi_struct, |
1417 | | struct ndpi_flow_struct *flow, |
1418 | | const u_int8_t *crypto_data, uint32_t crypto_data_len) |
1419 | 0 | { |
1420 | 0 | const uint8_t *tag; |
1421 | 0 | uint32_t i; |
1422 | 0 | uint16_t num_tags; |
1423 | 0 | uint32_t prev_offset; |
1424 | 0 | uint32_t tag_offset_start, offset, len; |
1425 | 0 | ndpi_protocol_match_result ret_match; |
1426 | 0 | int sni_found = 0, icsl_found = 0; |
1427 | |
|
1428 | 0 | if(crypto_data_len < 6) |
1429 | 0 | return; |
1430 | 0 | if(memcmp(crypto_data, "CHLO", 4) != 0) { |
1431 | 0 | NDPI_LOG_DBG(ndpi_struct, "Unexpected handshake message"); |
1432 | 0 | return; |
1433 | 0 | } |
1434 | 0 | num_tags = le16toh(*(uint16_t *)&crypto_data[4]); |
1435 | |
|
1436 | 0 | tag_offset_start = 8 + 8 * num_tags; |
1437 | 0 | prev_offset = 0; |
1438 | 0 | for(i = 0; i < num_tags; i++) { |
1439 | 0 | if(8 + 8 * i + 8 >= crypto_data_len) |
1440 | 0 | break; |
1441 | 0 | tag = &crypto_data[8 + 8 * i]; |
1442 | 0 | offset = le32toh(*((u_int32_t *)&crypto_data[8 + 8 * i + 4])); |
1443 | 0 | if(prev_offset > offset) |
1444 | 0 | break; |
1445 | 0 | len = offset - prev_offset; |
1446 | | /* Promote to uint64_t to avoid unsigned wrapping */ |
1447 | 0 | if((uint64_t)tag_offset_start + prev_offset + len > (uint64_t)crypto_data_len) |
1448 | 0 | break; |
1449 | | #if 0 |
1450 | | printf("crypto_data_len %u tag_offset_start %u prev_offset %u offset %u len %u\n", |
1451 | | crypto_data_len, tag_offset_start, prev_offset, offset, len); |
1452 | | #endif |
1453 | 0 | if(memcmp(tag, "SNI\0", 4) == 0) { |
1454 | |
|
1455 | 0 | ndpi_hostname_sni_set(flow, &crypto_data[tag_offset_start + prev_offset], len, NDPI_HOSTNAME_NORM_ALL); |
1456 | |
|
1457 | 0 | NDPI_LOG_DBG2(ndpi_struct, "SNI: [%s]\n", |
1458 | 0 | flow->host_server_name); |
1459 | |
|
1460 | 0 | ndpi_match_host_subprotocol(ndpi_struct, flow, |
1461 | 0 | flow->host_server_name, |
1462 | 0 | strlen(flow->host_server_name), |
1463 | 0 | &ret_match, NDPI_PROTOCOL_QUIC, 1); |
1464 | 0 | flow->protos.tls_quic.client_hello_processed = 1; /* Allow matching of custom categories */ |
1465 | |
|
1466 | 0 | ndpi_check_dga_name(ndpi_struct, flow, |
1467 | 0 | flow->host_server_name, 1, 0, 0); |
1468 | |
|
1469 | 0 | if(ndpi_is_valid_hostname((char *)&crypto_data[tag_offset_start + prev_offset], |
1470 | 0 | len) == 0) { |
1471 | 0 | if(is_flowrisk_info_enabled(ndpi_struct, NDPI_INVALID_CHARACTERS)) { |
1472 | 0 | char str[128]; |
1473 | |
|
1474 | 0 | snprintf(str, sizeof(str), "Invalid host %s", flow->host_server_name); |
1475 | 0 | ndpi_set_risk(ndpi_struct, flow, NDPI_INVALID_CHARACTERS, str); |
1476 | 0 | } else { |
1477 | 0 | ndpi_set_risk(ndpi_struct, flow, NDPI_INVALID_CHARACTERS, NULL); |
1478 | 0 | } |
1479 | | |
1480 | | /* This looks like an attack */ |
1481 | 0 | ndpi_set_risk(ndpi_struct, flow, NDPI_POSSIBLE_EXPLOIT, "Suspicious hostname: attack ?"); |
1482 | 0 | } |
1483 | | |
1484 | 0 | sni_found = 1; |
1485 | 0 | if(icsl_found) |
1486 | 0 | return; |
1487 | 0 | } |
1488 | | |
1489 | 0 | if(memcmp(tag, "ICSL", 4) == 0 && len >= 4) { |
1490 | 0 | u_int icsl_offset = tag_offset_start + prev_offset; |
1491 | |
|
1492 | 0 | flow->protos.tls_quic.quic_idle_timeout_sec = le32toh((*(uint32_t *)&crypto_data[icsl_offset])); |
1493 | 0 | NDPI_LOG_DBG2(ndpi_struct, "ICSL: %d\n", flow->protos.tls_quic.quic_idle_timeout_sec); |
1494 | 0 | icsl_found = 1; |
1495 | |
|
1496 | 0 | if(sni_found) |
1497 | 0 | return; |
1498 | 0 | } |
1499 | | |
1500 | 0 | prev_offset = offset; |
1501 | 0 | } |
1502 | 0 | if(i != num_tags) |
1503 | 0 | NDPI_LOG_DBG(ndpi_struct, "Something went wrong in tags iteration\n"); |
1504 | | |
1505 | | /* Add check for missing SNI */ |
1506 | 0 | if(flow->host_server_name[0] == '\0') { |
1507 | | /* This is a bit suspicious */ |
1508 | 0 | ndpi_set_risk(ndpi_struct, flow, NDPI_TLS_MISSING_SNI, "SNI should be present all time: attack ?"); |
1509 | 0 | } |
1510 | 0 | } |
1511 | | |
1512 | | static int may_be_gquic_rej(struct ndpi_detection_module_struct *ndpi_struct) |
1513 | 0 | { |
1514 | 0 | struct ndpi_packet_struct *packet = &ndpi_struct->packet; |
1515 | 0 | void *ptr; |
1516 | | |
1517 | | /* Common case: msg from server default port */ |
1518 | 0 | if(packet->udp->source != ntohs(443)) |
1519 | 0 | return 0; |
1520 | | /* GQUIC. Common case: cid length 8, no version, packet number length 1 */ |
1521 | 0 | if(packet->payload[0] != 0x08) |
1522 | 0 | return 0; |
1523 | 0 | if(packet->payload_packet_len < 1 + 8 + 1 + 12 /* Message auth hash */ + 16 /* Arbitrary length */) |
1524 | 0 | return 0; |
1525 | | /* Search for "REJ" tag in the first 16 bytes after the hash */ |
1526 | 0 | ptr = memchr(&packet->payload[1 + 8 + 1 + 12], 'R', 16 - 3); |
1527 | 0 | if(ptr && memcmp(ptr, "REJ", 3) == 0) |
1528 | 0 | return 1; |
1529 | 0 | return 0; |
1530 | 0 | } |
1531 | | |
1532 | | static int may_be_sh(struct ndpi_detection_module_struct *ndpi_struct, |
1533 | | struct ndpi_flow_struct *flow) |
1534 | 0 | { |
1535 | 0 | struct ndpi_packet_struct *packet = &ndpi_struct->packet; |
1536 | 0 | u_int8_t last_byte; |
1537 | |
|
1538 | 0 | if((packet->payload[0] & 0x40) == 0) |
1539 | 0 | return 0; |
1540 | 0 | if(packet->udp->dest != ntohs(443)) { |
1541 | 0 | if(packet->udp->source == ntohs(443)) { |
1542 | 0 | return -1; /* Keep looking for packets sent by the client */ |
1543 | 0 | } |
1544 | 0 | return 0; |
1545 | 0 | } |
1546 | | |
1547 | | /* SH packet sent by the client */ |
1548 | | |
1549 | | /* QUIC never retransmits packet, but we should also somehow check that |
1550 | | * these 3 packets from the client are really different from each other |
1551 | | * to avoid matching retransmissions on some other protocols. |
1552 | | * To avoid saving too much state, simply check the last byte of each packet |
1553 | | * (the idea is that being QUIC fully encrypted, the bytes are somehow always |
1554 | | * different; a weak assumption, but it allow us to save only 1 byte in |
1555 | | * flow structure and it seems to work) |
1556 | | * TODO: do we need something better? |
1557 | | */ |
1558 | | |
1559 | 0 | if(packet->payload_packet_len < 1 + QUIC_SERVER_CID_HEURISTIC_LENGTH) |
1560 | 0 | return 0; |
1561 | 0 | last_byte = packet->payload[packet->payload_packet_len - 1]; |
1562 | 0 | if(flow->l4.udp.quic_server_cid_stage > 0) { |
1563 | 0 | if(memcmp(flow->l4.udp.quic_server_cid, &packet->payload[1], |
1564 | 0 | QUIC_SERVER_CID_HEURISTIC_LENGTH) != 0 || |
1565 | 0 | flow->l4.udp.quic_client_last_byte == last_byte) |
1566 | 0 | return 0; |
1567 | 0 | flow->l4.udp.quic_server_cid_stage++; |
1568 | 0 | if(flow->l4.udp.quic_server_cid_stage == 3) { |
1569 | | /* Found QUIC via 3 SHs by client */ |
1570 | 0 | return 1; |
1571 | 0 | } |
1572 | 0 | } else { |
1573 | 0 | memcpy(flow->l4.udp.quic_server_cid, &packet->payload[1], QUIC_SERVER_CID_HEURISTIC_LENGTH); |
1574 | 0 | flow->l4.udp.quic_server_cid_stage = 1; |
1575 | 0 | } |
1576 | 0 | flow->l4.udp.quic_client_last_byte = last_byte; |
1577 | 0 | return -1; /* Keep looking for other packets sent by client */ |
1578 | 0 | } |
1579 | | |
1580 | | static int may_be_0rtt(struct ndpi_detection_module_struct *ndpi_struct, |
1581 | | uint32_t *version) |
1582 | 0 | { |
1583 | 0 | struct ndpi_packet_struct *packet = &ndpi_struct->packet; |
1584 | 0 | u_int8_t first_byte; |
1585 | 0 | u_int8_t pub_bit1, pub_bit2, pub_bit3, pub_bit4; |
1586 | 0 | u_int8_t dest_conn_id_len, source_conn_id_len; |
1587 | | |
1588 | | /* First byte + version + dest_conn_id_len */ |
1589 | 0 | if(packet->payload_packet_len < 5 + 1) { |
1590 | 0 | NDPI_LOG_DBG2(ndpi_struct, "Pkt too short\n"); |
1591 | 0 | return 0; |
1592 | 0 | } |
1593 | | |
1594 | 0 | first_byte = packet->payload[0]; |
1595 | 0 | pub_bit1 = ((first_byte & 0x80) != 0); |
1596 | 0 | pub_bit2 = ((first_byte & 0x40) != 0); |
1597 | 0 | pub_bit3 = ((first_byte & 0x20) != 0); |
1598 | 0 | pub_bit4 = ((first_byte & 0x10) != 0); |
1599 | |
|
1600 | 0 | *version = ntohl(*((u_int32_t *)&packet->payload[1])); |
1601 | | |
1602 | | /* IETF versions, Long header, fixed bit (ignore QUIC-bit-greased case), 0RTT */ |
1603 | |
|
1604 | 0 | if(!(is_version_quic(*version) && |
1605 | 0 | pub_bit1 && pub_bit2)) { |
1606 | 0 | NDPI_LOG_DBG2(ndpi_struct, "Invalid header or version\n"); |
1607 | 0 | return 0; |
1608 | 0 | } |
1609 | 0 | if(!is_version_quic_v2(*version) && |
1610 | 0 | (pub_bit3 != 0 || pub_bit4 != 1)) { |
1611 | 0 | NDPI_LOG_DBG2(ndpi_struct, "Version 0x%x not 0-RTT Packet\n", *version); |
1612 | 0 | return 0; |
1613 | 0 | } |
1614 | 0 | if(is_version_quic_v2(*version) && |
1615 | 0 | (pub_bit3 != 1 || pub_bit4 != 0)) { |
1616 | 0 | NDPI_LOG_DBG2(ndpi_struct, "Version 0x%x not 0-RTT Packet\n", *version); |
1617 | 0 | return 0; |
1618 | 0 | } |
1619 | | |
1620 | | /* Check that CIDs lengths are valid */ |
1621 | 0 | dest_conn_id_len = packet->payload[5]; |
1622 | 0 | if(packet->payload_packet_len <= 5 + 1 + dest_conn_id_len) { |
1623 | 0 | NDPI_LOG_DBG2(ndpi_struct, "Dcid too short\n"); |
1624 | 0 | return 0; |
1625 | 0 | } |
1626 | 0 | source_conn_id_len = packet->payload[5 + 1 + dest_conn_id_len]; |
1627 | 0 | if(packet->payload_packet_len <= 5 + 1 + dest_conn_id_len + 1 + source_conn_id_len) { |
1628 | 0 | NDPI_LOG_DBG2(ndpi_struct, "Scid too short\n"); |
1629 | 0 | return 0; |
1630 | 0 | } |
1631 | 0 | if(dest_conn_id_len > QUIC_MAX_CID_LENGTH || |
1632 | 0 | source_conn_id_len > QUIC_MAX_CID_LENGTH) { |
1633 | 0 | NDPI_LOG_DBG2(ndpi_struct, "Version 0x%x invalid CIDs length %u %u\n", |
1634 | 0 | *version, dest_conn_id_len, source_conn_id_len); |
1635 | 0 | return 0; |
1636 | 0 | } |
1637 | | |
1638 | 0 | return 1; |
1639 | 0 | } |
1640 | | |
1641 | | static int may_be_initial_pkt(struct ndpi_detection_module_struct *ndpi_struct, |
1642 | | uint32_t *version) |
1643 | 0 | { |
1644 | 0 | struct ndpi_packet_struct *packet = &ndpi_struct->packet; |
1645 | 0 | u_int8_t first_byte; |
1646 | 0 | u_int8_t pub_bit1, pub_bit2, pub_bit3, pub_bit4, pub_bit5, pub_bit7, pub_bit8; |
1647 | 0 | u_int8_t dest_conn_id_len, source_conn_id_len; |
1648 | | |
1649 | | /* According to draft-ietf-quic-transport-29: "Clients MUST ensure that UDP |
1650 | | datagrams containing Initial packets have UDP payloads of at least 1200 |
1651 | | bytes". Similar limit exists for previous versions */ |
1652 | 0 | if(packet->payload_packet_len < 1200) { |
1653 | 0 | return 0; |
1654 | 0 | } |
1655 | | |
1656 | 0 | first_byte = packet->payload[0]; |
1657 | 0 | pub_bit1 = ((first_byte & 0x80) != 0); |
1658 | 0 | pub_bit2 = ((first_byte & 0x40) != 0); |
1659 | 0 | pub_bit3 = ((first_byte & 0x20) != 0); |
1660 | 0 | pub_bit4 = ((first_byte & 0x10) != 0); |
1661 | 0 | pub_bit5 = ((first_byte & 0x08) != 0); |
1662 | 0 | pub_bit7 = ((first_byte & 0x02) != 0); |
1663 | 0 | pub_bit8 = ((first_byte & 0x01) != 0); |
1664 | |
|
1665 | 0 | *version = 0; |
1666 | 0 | if(pub_bit1) { |
1667 | 0 | *version = ntohl(*((u_int32_t *)&packet->payload[1])); |
1668 | 0 | } else if(pub_bit5 && !pub_bit2) { |
1669 | 0 | if(!pub_bit8) { |
1670 | 0 | NDPI_LOG_DBG2(ndpi_struct, "Packet without version\n") |
1671 | 0 | } else { |
1672 | 0 | *version = ntohl(*((u_int32_t *)&packet->payload[9])); |
1673 | 0 | } |
1674 | 0 | } |
1675 | 0 | if(!is_version_valid(*version)) { |
1676 | 0 | NDPI_LOG_DBG2(ndpi_struct, "Invalid version 0x%x\n", *version); |
1677 | 0 | return 0; |
1678 | 0 | } |
1679 | | |
1680 | 0 | if(is_gquic_ver_less_than(*version, 43) && |
1681 | 0 | (!pub_bit5 || pub_bit3 != 0 || pub_bit4 != 0)) { |
1682 | 0 | NDPI_LOG_DBG(ndpi_struct, "Version 0x%x invalid flags 0x%x\n", *version, first_byte); |
1683 | 0 | return 0; |
1684 | 0 | } |
1685 | 0 | if((*version == V_Q046) && |
1686 | 0 | (pub_bit7 != 1 || pub_bit8 != 1)) { |
1687 | 0 | NDPI_LOG_DBG(ndpi_struct, "Q46 invalid flag 0x%x\n", first_byte); |
1688 | 0 | return 0; |
1689 | 0 | } |
1690 | 0 | if(((is_version_quic(*version) && !is_version_quic_v2(*version)) || |
1691 | 0 | (*version == V_Q046) || (*version == V_Q050)) && |
1692 | 0 | (pub_bit3 != 0 || pub_bit4 != 0)) { |
1693 | 0 | NDPI_LOG_DBG2(ndpi_struct, "Version 0x%x not Initial Packet\n", *version); |
1694 | 0 | return 0; |
1695 | 0 | } |
1696 | 0 | if(is_version_quic_v2(*version) && |
1697 | 0 | (pub_bit3 != 0 || pub_bit4 != 1)) { |
1698 | 0 | NDPI_LOG_DBG2(ndpi_struct, "Version 0x%x not Initial Packet\n", *version); |
1699 | 0 | return 0; |
1700 | 0 | } |
1701 | | |
1702 | | /* Forcing Version Negotiation packets are QUIC Initial Packets (i.e. |
1703 | | Long Header). It should also be quite rare that a client sends this kind |
1704 | | of traffic with the QUIC bit greased i.e. having a server token. |
1705 | | Accordind to https://tools.ietf.org/html/draft-thomson-quic-bit-grease-00#section-3.1 |
1706 | | "A client MAY also clear the QUIC Bit in Initial packets that are sent |
1707 | | to establish a new connection. A client can only clear the QUIC Bit |
1708 | | if the packet includes a token provided by the server in a NEW_TOKEN |
1709 | | frame on a connection where the server also included the |
1710 | | grease_quic_bit transport parameter." */ |
1711 | 0 | if(is_version_forcing_vn(*version) && |
1712 | 0 | !(pub_bit1 == 1 && pub_bit2 == 1)) { |
1713 | 0 | NDPI_LOG_DBG2(ndpi_struct, "Version 0x%x with first byte 0x%x\n", *version, first_byte); |
1714 | 0 | return 0; |
1715 | 0 | } |
1716 | | |
1717 | | /* Check that CIDs lengths are valid: QUIC limits the CID length to 20 */ |
1718 | 0 | if(is_version_with_ietf_long_header(*version)) { |
1719 | 0 | dest_conn_id_len = packet->payload[5]; |
1720 | 0 | source_conn_id_len = packet->payload[5 + 1 + dest_conn_id_len]; |
1721 | 0 | if (dest_conn_id_len > QUIC_MAX_CID_LENGTH || |
1722 | 0 | source_conn_id_len > QUIC_MAX_CID_LENGTH) { |
1723 | 0 | NDPI_LOG_DBG2(ndpi_struct, "Version 0x%x invalid CIDs length %u %u", |
1724 | 0 | *version, dest_conn_id_len, source_conn_id_len); |
1725 | 0 | return 0; |
1726 | 0 | } |
1727 | 0 | } |
1728 | | |
1729 | | /* TODO: add some other checks to avoid false positives */ |
1730 | | |
1731 | 0 | return 1; |
1732 | 0 | } |
1733 | | |
1734 | | /* ***************************************************************** */ |
1735 | | |
1736 | | static int eval_extra_processing(struct ndpi_detection_module_struct *ndpi_struct, |
1737 | | struct ndpi_flow_struct *flow) |
1738 | 0 | { |
1739 | 0 | u_int32_t version = flow->protos.tls_quic.quic_version; |
1740 | | |
1741 | | /* For the time being we need extra processing in two cases only: |
1742 | | 1) to detect Snapchat calls, i.e. RTP/RTCP multiplxed with QUIC. |
1743 | | Two cases: |
1744 | | a) [old] Q046, without any SNI |
1745 | | b) v1 with SNI *.addlive.io |
1746 | | 2) to reassemble CH fragments on multiple UDP packets. |
1747 | | These two cases are mutually exclusive |
1748 | | */ |
1749 | |
|
1750 | 0 | if(version == V_Q046 && flow->host_server_name[0] == '\0') { |
1751 | 0 | NDPI_LOG_DBG2(ndpi_struct, "We have further work to do (old snapchat call?)\n"); |
1752 | 0 | return 1; |
1753 | 0 | } |
1754 | | |
1755 | 0 | if(version == V_1 && |
1756 | 0 | flow->detected_protocol_stack[0] == NDPI_PROTOCOL_SNAPCHAT) { |
1757 | 0 | size_t sni_len = strlen(flow->host_server_name); |
1758 | 0 | if(sni_len > 11 && |
1759 | 0 | strcmp(flow->host_server_name + sni_len - 11, ".addlive.io") == 0) { |
1760 | 0 | NDPI_LOG_DBG2(ndpi_struct, "We have further work to do (new snapchat call?)\n"); |
1761 | 0 | return 1; |
1762 | 0 | } |
1763 | 0 | } |
1764 | | |
1765 | 0 | if(is_ch_reassembler_pending(flow)) { |
1766 | 0 | NDPI_LOG_DBG2(ndpi_struct, "We have further work to do (reasm)\n"); |
1767 | 0 | return 1; |
1768 | 0 | } |
1769 | | |
1770 | 0 | return 0; |
1771 | 0 | } |
1772 | | |
1773 | | static void ndpi_search_quic(struct ndpi_detection_module_struct *ndpi_struct, |
1774 | | struct ndpi_flow_struct *flow); |
1775 | | static int ndpi_search_quic_extra(struct ndpi_detection_module_struct *ndpi_struct, |
1776 | | struct ndpi_flow_struct *flow) |
1777 | 0 | { |
1778 | 0 | struct ndpi_packet_struct *packet = &ndpi_struct->packet; |
1779 | | |
1780 | | /* We are elaborating a packet following the initial CHLO/ClientHello. |
1781 | | Two cases: |
1782 | | 1) Mutiplexing QUIC with RTP/RTCP. It should be quite generic, but |
1783 | | for the time being, we known only NDPI_PROTOCOL_SNAPCHAT_CALL having |
1784 | | such behaviour |
1785 | | 2) CH reasssembling is going on */ |
1786 | | /* TODO: could we unify ndpi_search_quic() and ndpi_search_quic_extra() somehow? */ |
1787 | |
|
1788 | 0 | NDPI_LOG_DBG(ndpi_struct, "search QUIC extra func\n"); |
1789 | |
|
1790 | 0 | if(packet->payload_packet_len == 0) |
1791 | 0 | return 1; |
1792 | | |
1793 | 0 | if (is_ch_reassembler_pending(flow)) { |
1794 | 0 | ndpi_search_quic(ndpi_struct, flow); |
1795 | 0 | if(is_ch_reassembler_pending(flow)) |
1796 | 0 | return 1; |
1797 | 0 | flow->extra_packets_func = NULL; |
1798 | 0 | return 0; |
1799 | 0 | } |
1800 | | |
1801 | | /* RTP/RTCP stuff */ |
1802 | | |
1803 | | /* If this packet is still a Q046 one we need to keep going */ |
1804 | 0 | if(packet->payload[0] & 0x40) { |
1805 | 0 | NDPI_LOG_DBG(ndpi_struct, "Still QUIC\n"); |
1806 | 0 | return 1; /* Keep going */ |
1807 | 0 | } |
1808 | | |
1809 | 0 | NDPI_LOG_DBG2(ndpi_struct, "No more QUIC: nothing to do on QUIC side\n"); |
1810 | 0 | flow->extra_packets_func = NULL; |
1811 | | |
1812 | | /* This might be a RTP/RTCP stream: let's check it */ |
1813 | | /* TODO: the cleanest solution should be triggering the rtp/rtcp dissector, but |
1814 | | I have not been able to that that so I reimplemented a basic RTP/RTCP detection.*/ |
1815 | 0 | if((packet->payload[0] >> 6) == 2 && /* Version 2 */ |
1816 | 0 | packet->payload_packet_len > 1 && |
1817 | 0 | (packet->payload[1] == 201 || /* RTCP, Receiver Report */ |
1818 | 0 | packet->payload[1] == 200 || /* RTCP, Sender Report */ |
1819 | 0 | is_valid_rtp_payload_type(packet->payload[1] & 0x7F)) /* RTP */) { |
1820 | 0 | ndpi_master_app_protocol proto; |
1821 | |
|
1822 | 0 | NDPI_LOG_DBG(ndpi_struct, "Found RTP/RTCP over QUIC\n"); |
1823 | 0 | ndpi_set_detected_protocol(ndpi_struct, flow, NDPI_PROTOCOL_SNAPCHAT_CALL, NDPI_PROTOCOL_QUIC, NDPI_CONFIDENCE_DPI); |
1824 | | /* In "extra_eval" data path, if we change the classification, we need to update the category, too */ |
1825 | 0 | proto.master_protocol = NDPI_PROTOCOL_QUIC; |
1826 | 0 | proto.app_protocol = NDPI_PROTOCOL_SNAPCHAT_CALL; |
1827 | 0 | flow->category = get_proto_category(ndpi_struct, proto); |
1828 | 0 | flow->breed = get_proto_breed(ndpi_struct, proto); |
1829 | 0 | } else { |
1830 | | /* Unexpected traffic pattern: we should investigate it... */ |
1831 | 0 | NDPI_LOG_INFO(ndpi_struct, "To investigate...\n"); |
1832 | 0 | } |
1833 | |
|
1834 | 0 | return 0; |
1835 | 0 | } |
1836 | | |
1837 | | static int is_vn(struct ndpi_detection_module_struct *ndpi_struct) |
1838 | 0 | { |
1839 | 0 | struct ndpi_packet_struct *packet = &ndpi_struct->packet; |
1840 | 0 | u_int32_t version; |
1841 | 0 | u_int8_t first_byte; |
1842 | 0 | u_int8_t pub_bit1; |
1843 | 0 | u_int8_t dest_conn_id_len, source_conn_id_len; |
1844 | | |
1845 | | /* RFC 8999 6 */ |
1846 | | |
1847 | | /* First byte + version (4) + 2 CID lengths (set to 0) + at least one supported version */ |
1848 | 0 | if(packet->payload_packet_len < 11) { |
1849 | 0 | return 0; |
1850 | 0 | } |
1851 | | |
1852 | 0 | first_byte = packet->payload[0]; |
1853 | 0 | pub_bit1 = ((first_byte & 0x80) != 0); |
1854 | 0 | if(!pub_bit1) { |
1855 | 0 | NDPI_LOG_DBG2(ndpi_struct, "Not a long header\n"); |
1856 | 0 | return 0; |
1857 | 0 | } |
1858 | | |
1859 | 0 | version = ntohl(*((u_int32_t *)&packet->payload[1])); |
1860 | 0 | if(version != 0) { |
1861 | 0 | NDPI_LOG_DBG2(ndpi_struct, "Invalid version 0x%x\n", version); |
1862 | 0 | return 0; |
1863 | 0 | } |
1864 | | |
1865 | | /* Check that CIDs lengths are valid: QUIC limits the CID length to 20 */ |
1866 | 0 | dest_conn_id_len = packet->payload[5]; |
1867 | 0 | if(5 + 1 + dest_conn_id_len >= packet->payload_packet_len) { |
1868 | 0 | NDPI_LOG_DBG2(ndpi_struct, "Invalid Length %d\n", packet->payload_packet_len); |
1869 | 0 | return 0; |
1870 | 0 | } |
1871 | 0 | source_conn_id_len = packet->payload[5 + 1 + dest_conn_id_len]; |
1872 | 0 | if (dest_conn_id_len > QUIC_MAX_CID_LENGTH || |
1873 | 0 | source_conn_id_len > QUIC_MAX_CID_LENGTH) { |
1874 | 0 | NDPI_LOG_DBG2(ndpi_struct, "Invalid CIDs length %u %u", |
1875 | 0 | dest_conn_id_len, source_conn_id_len); |
1876 | 0 | return 0; |
1877 | 0 | } |
1878 | | |
1879 | 0 | return 1; |
1880 | 0 | } |
1881 | | |
1882 | | static int ndpi_search_quic_extra_vn(struct ndpi_detection_module_struct *ndpi_struct, |
1883 | | struct ndpi_flow_struct *flow) |
1884 | 0 | { |
1885 | 0 | struct ndpi_packet_struct *packet = &ndpi_struct->packet; |
1886 | | |
1887 | | /* We are elaborating a packet following the Forcing VN, i.e. we are expecting: |
1888 | | 1) first a VN packet (from the server) |
1889 | | 2) then a "standard" Initial from the client */ |
1890 | | /* TODO: could we unify ndpi_search_quic() and ndpi_search_quic_extra_vn() somehow? */ |
1891 | |
|
1892 | 0 | NDPI_LOG_DBG(ndpi_struct, "search QUIC extra func VN\n"); |
1893 | |
|
1894 | 0 | if(packet->payload_packet_len == 0) |
1895 | 0 | return 1; /* Keep going */ |
1896 | | |
1897 | 0 | if(flow->l4.udp.quic_vn_pair == 0) { |
1898 | 0 | if(is_vn(ndpi_struct)) { |
1899 | 0 | NDPI_LOG_DBG(ndpi_struct, "Valid VN\n"); |
1900 | 0 | flow->l4.udp.quic_vn_pair = 1; |
1901 | 0 | return 1; |
1902 | 0 | } else { |
1903 | 0 | NDPI_LOG_DBG(ndpi_struct, "Invalid reply to a Force VN. Stop\n"); |
1904 | 0 | flow->extra_packets_func = NULL; |
1905 | 0 | return 0; /* Stop */ |
1906 | 0 | } |
1907 | 0 | } else { |
1908 | 0 | flow->extra_packets_func = NULL; |
1909 | 0 | ndpi_search_quic(ndpi_struct, flow); |
1910 | 0 | return 0; |
1911 | 0 | } |
1912 | 0 | } |
1913 | | |
1914 | | static void ndpi_search_quic(struct ndpi_detection_module_struct *ndpi_struct, |
1915 | | struct ndpi_flow_struct *flow) |
1916 | 0 | { |
1917 | 0 | u_int32_t version; |
1918 | 0 | u_int8_t *clear_payload; |
1919 | 0 | uint32_t clear_payload_len = 0; |
1920 | 0 | const u_int8_t *crypto_data; |
1921 | 0 | uint64_t crypto_data_len; |
1922 | 0 | int is_initial_quic, ret; |
1923 | |
|
1924 | 0 | NDPI_LOG_DBG2(ndpi_struct, "search QUIC\n"); |
1925 | | |
1926 | | /* Buffers: packet->payload ---> clear_payload ---> crypto_data */ |
1927 | | |
1928 | | /* |
1929 | | * 1) (Very) basic heuristic to check if it is a QUIC packet. |
1930 | | * The first packet of each QUIC session should contain a valid |
1931 | | * CHLO/ClientHello message and we need (only) it to sub-classify |
1932 | | * the flow. |
1933 | | * Detecting QUIC sessions where the first captured packet is not a |
1934 | | * CHLO/CH is VERY hard. Let try only some easy cases: |
1935 | | * * out-of-order 0-RTT, i.e 0-RTT packets received before the Initial; |
1936 | | * in that case, keep looking for the Initial |
1937 | | * * if we have only SH pkts, focus on standard case where server |
1938 | | * port is 443 and default length of Server CID is >=8 (as it happens |
1939 | | * with most common broswer and apps). Look for 3 consecutive SH |
1940 | | * pkts send by the client and check their CIDs (note that |
1941 | | * some QUIC implementations have Client CID length set to 0, so |
1942 | | * checking pkts sent by server is useless). Since we don't know the |
1943 | | * real CID length, use the min value 8, i.e. QUIC_SERVER_CID_HEURISTIC_LENGTH |
1944 | | * * with only GQUIC packets from server (usefull with unidirectional |
1945 | | * captures) look for Rejection packet |
1946 | | * Avoid the generic cases and let's see if anyone complains... |
1947 | | */ |
1948 | |
|
1949 | 0 | is_initial_quic = may_be_initial_pkt(ndpi_struct, &version); |
1950 | 0 | if(!is_initial_quic) { |
1951 | 0 | if(!is_ch_reassembler_pending(flow)) { /* Better safe than sorry */ |
1952 | 0 | ret = may_be_0rtt(ndpi_struct, &version); |
1953 | 0 | if(ret == 1) { |
1954 | 0 | NDPI_LOG_DBG(ndpi_struct, "Found 0-RTT, keep looking for Initial\n"); |
1955 | 0 | flow->l4.udp.quic_0rtt_found = 1; |
1956 | 0 | if(flow->packet_counter >= 3) { |
1957 | | /* We haven't still found an Initial.. give up */ |
1958 | 0 | NDPI_LOG_INFO(ndpi_struct, "QUIC 0RTT\n"); |
1959 | 0 | ndpi_set_detected_protocol(ndpi_struct, flow, NDPI_PROTOCOL_QUIC, NDPI_PROTOCOL_UNKNOWN, NDPI_CONFIDENCE_DPI); |
1960 | 0 | flow->protos.tls_quic.quic_version = version; |
1961 | 0 | } |
1962 | 0 | return; |
1963 | 0 | } else if(flow->l4.udp.quic_0rtt_found == 1) { |
1964 | | /* Unknown packet (probably an Handshake one) after a 0-RTT */ |
1965 | 0 | NDPI_LOG_INFO(ndpi_struct, "QUIC 0RTT (without Initial)\n"); |
1966 | 0 | ndpi_set_detected_protocol(ndpi_struct, flow, NDPI_PROTOCOL_QUIC, NDPI_PROTOCOL_UNKNOWN, NDPI_CONFIDENCE_DPI); |
1967 | 0 | flow->protos.tls_quic.quic_version = 0; /* unknown */ |
1968 | 0 | return; |
1969 | 0 | } |
1970 | 0 | ret = may_be_sh(ndpi_struct, flow); |
1971 | 0 | if(ret == 1) { |
1972 | 0 | NDPI_LOG_INFO(ndpi_struct, "SH Quic\n"); |
1973 | 0 | ndpi_set_detected_protocol(ndpi_struct, flow, NDPI_PROTOCOL_QUIC, NDPI_PROTOCOL_UNKNOWN, NDPI_CONFIDENCE_DPI); |
1974 | 0 | flow->protos.tls_quic.quic_version = 0; /* unknown */ |
1975 | 0 | return; |
1976 | 0 | } |
1977 | 0 | if(ret == -1) { |
1978 | 0 | NDPI_LOG_DBG2(ndpi_struct, "Keep looking for SH by client\n"); |
1979 | 0 | if(flow->packet_counter > 10 /* TODO */) |
1980 | 0 | NDPI_EXCLUDE_DISSECTOR(ndpi_struct, flow); |
1981 | 0 | return; |
1982 | 0 | } |
1983 | 0 | ret = may_be_gquic_rej(ndpi_struct); |
1984 | 0 | if(ret == 1) { |
1985 | 0 | NDPI_LOG_INFO(ndpi_struct, "GQUIC REJ\n"); |
1986 | 0 | ndpi_set_detected_protocol(ndpi_struct, flow, NDPI_PROTOCOL_QUIC, NDPI_PROTOCOL_UNKNOWN, NDPI_CONFIDENCE_DPI); |
1987 | 0 | flow->protos.tls_quic.quic_version = 0; /* unknown */ |
1988 | 0 | return; |
1989 | 0 | } |
1990 | 0 | } |
1991 | 0 | NDPI_EXCLUDE_DISSECTOR(ndpi_struct, flow); |
1992 | 0 | return; |
1993 | 0 | } |
1994 | | |
1995 | | /* |
1996 | | * 2) Ok, this packet seems to be QUIC |
1997 | | */ |
1998 | | |
1999 | 0 | NDPI_LOG_INFO(ndpi_struct, "found QUIC\n"); |
2000 | 0 | ndpi_set_detected_protocol(ndpi_struct, flow, NDPI_PROTOCOL_QUIC, NDPI_PROTOCOL_UNKNOWN, NDPI_CONFIDENCE_DPI); |
2001 | 0 | flow->protos.tls_quic.quic_version = version; |
2002 | | |
2003 | | /* |
2004 | | * 3) Skip not supported versions |
2005 | | */ |
2006 | |
|
2007 | 0 | if(!is_version_supported(version)) { |
2008 | 0 | NDPI_LOG_DBG(ndpi_struct, "Unsupported version 0x%x\n", version); |
2009 | 0 | NDPI_EXCLUDE_DISSECTOR(ndpi_struct, flow); |
2010 | 0 | return; |
2011 | 0 | } |
2012 | | |
2013 | | /* |
2014 | | * 3a) Forcing VN. There is no payload to analyze yet. |
2015 | | * Expecteed flow: |
2016 | | * *) C->S: Forcing VN |
2017 | | * *) S->C: VN |
2018 | | * *) C->S: "Standard" Initial with crypto data |
2019 | | */ |
2020 | 0 | if(is_version_forcing_vn(version)) { |
2021 | 0 | NDPI_LOG_DBG(ndpi_struct, "Forcing VN\n"); |
2022 | 0 | flow->max_extra_packets_to_check = 4; /* TODO */ |
2023 | 0 | flow->extra_packets_func = ndpi_search_quic_extra_vn; |
2024 | 0 | return; |
2025 | 0 | } |
2026 | | |
2027 | | /* |
2028 | | * 4) Extract the Payload from Initial Packets |
2029 | | */ |
2030 | 0 | clear_payload = get_clear_payload(ndpi_struct, flow, version, &clear_payload_len); |
2031 | 0 | if(!clear_payload) { |
2032 | 0 | NDPI_EXCLUDE_DISSECTOR(ndpi_struct, flow); |
2033 | 0 | return; |
2034 | 0 | } |
2035 | | |
2036 | | /* |
2037 | | * 5) Extract Crypto Data from the Payload |
2038 | | */ |
2039 | 0 | crypto_data = get_crypto_data(ndpi_struct, flow, |
2040 | 0 | clear_payload, clear_payload_len, |
2041 | 0 | &crypto_data_len); |
2042 | | |
2043 | | /* |
2044 | | * 6) Process ClientHello/CHLO from the Crypto Data (if any) |
2045 | | */ |
2046 | 0 | if(crypto_data) { |
2047 | 0 | if(!is_version_with_tls(version)) { |
2048 | 0 | process_chlo(ndpi_struct, flow, crypto_data, crypto_data_len); |
2049 | 0 | } else { |
2050 | 0 | process_tls(ndpi_struct, flow, crypto_data, crypto_data_len); |
2051 | 0 | } |
2052 | 0 | } |
2053 | 0 | if(is_version_with_encrypted_header(version)) { |
2054 | 0 | ndpi_free(clear_payload); |
2055 | 0 | } |
2056 | | |
2057 | | /* |
2058 | | * 7) We need to process other packets than (the first) ClientHello/CHLO? |
2059 | | */ |
2060 | 0 | if(eval_extra_processing(ndpi_struct, flow)) { |
2061 | 0 | flow->max_extra_packets_to_check = 24; /* TODO */ |
2062 | 0 | flow->extra_packets_func = ndpi_search_quic_extra; |
2063 | 0 | } else if(!crypto_data) { |
2064 | 0 | NDPI_EXCLUDE_DISSECTOR(ndpi_struct, flow); |
2065 | 0 | } |
2066 | 0 | } |
2067 | | |
2068 | | /* ***************************************************************** */ |
2069 | | |
2070 | | void init_quic_dissector(struct ndpi_detection_module_struct *ndpi_struct) |
2071 | 1 | { |
2072 | 1 | ndpi_register_dissector("QUIC", ndpi_struct, |
2073 | 1 | ndpi_search_quic, |
2074 | 1 | NDPI_SELECTION_BITMASK_PROTOCOL_V4_V6_UDP_WITH_PAYLOAD, |
2075 | 1 | 1, NDPI_PROTOCOL_QUIC); |
2076 | 1 | } |