aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIvan Nardi <12729895+IvanNardi@users.noreply.github.com>2021-12-04 13:29:30 +0100
committerGitHub <noreply@github.com>2021-12-04 13:29:30 +0100
commit6e86e6d924286491055608850e1df2db0c2322ad (patch)
tree0bfda0098a66158fb1bcf726c0890b138987756b
parent4d60c70c8bc1439fd59ac78869767fe5f1ef109e (diff)
QUIC: add support for QUICv2 (draft 00) (#1379)
It is already time to start looking at the new QUIC version. See: https://datatracker.ietf.org/doc/html/draft-ietf-quic-v2-00
-rw-r--r--src/lib/protocols/quic.c59
-rwxr-xr-xtests/do.sh.in2
-rw-r--r--tests/pcap/quic-v2-00.pcapngbin0 -> 50416 bytes
-rw-r--r--tests/result/quic-v2-00.pcapng.out12
4 files changed, 57 insertions, 16 deletions
diff --git a/src/lib/protocols/quic.c b/src/lib/protocols/quic.c
index 6bae70524..7d218efe9 100644
--- a/src/lib/protocols/quic.c
+++ b/src/lib/protocols/quic.c
@@ -83,7 +83,8 @@ static int is_version_quic(uint32_t version)
return version == V_1 ||
((version & 0xFFFFFF00) == 0xFF000000) /* IETF Drafts*/ ||
((version & 0xFFFFF000) == 0xfaceb000) /* Facebook */ ||
- ((version & 0x0F0F0F0F) == 0x0a0a0a0a) /* Forcing Version Negotiation */;
+ ((version & 0x0F0F0F0F) == 0x0a0a0a0a) /* Forcing Version Negotiation */ ||
+ ((version & 0xFFFFFF00) == 0xFF020000) /* V2 IETF Drafts */;
}
static int is_version_valid(uint32_t version)
{
@@ -112,6 +113,12 @@ static uint8_t get_u8_quic_ver(uint32_t version)
only latest drafts... */
if ((version & 0x0F0F0F0F) == 0x0a0a0a0a)
return 29;
+
+ /* QUIC Version 2 */
+ /* For the time being use 100 + draft as a number for V2 */
+ if ((version >> 8) == 0xff0200)
+ return 100 + (uint8_t)version;
+
return 0;
}
#ifdef HAVE_LIBGCRYPT
@@ -181,6 +188,15 @@ int is_version_with_ietf_long_header(uint32_t version)
((version & 0xFFFFFF00) == 0x51303500) /* Q05X */ ||
((version & 0xFFFFFF00) == 0x54303500) /* T05X */;
}
+#ifdef HAVE_LIBGCRYPT
+int is_version_with_v1_labels(uint32_t version)
+{
+ if(((version & 0xFFFFFF00) == 0x51303500) /* Q05X */ ||
+ ((version & 0xFFFFFF00) == 0x54303500)) /* T05X */
+ return 1;
+ return is_quic_ver_less_than(version, 33);
+}
+#endif
int quic_len(const uint8_t *buf, uint64_t *value)
{
@@ -531,29 +547,34 @@ static void quic_ciphers_reset(quic_ciphers *ciphers)
* and initialize cipher with the new key.
*/
static int quic_hp_cipher_init(quic_hp_cipher *hp_cipher, int hash_algo,
- uint8_t key_length, uint8_t *secret)
+ uint8_t key_length, uint8_t *secret,
+ uint32_t version)
{
uint8_t hp_key[256/8]; /* Maximum key size is for AES256 cipher. */
uint32_t hash_len = gcry_md_get_algo_dlen(hash_algo);
+ char *label = is_version_with_v1_labels(version) ? "quic hp" : "quicv2 hp";
- if(!quic_hkdf_expand_label(hash_algo, secret, hash_len, "quic hp", hp_key, key_length)) {
+ if(!quic_hkdf_expand_label(hash_algo, secret, hash_len, label, hp_key, key_length)) {
return 0;
}
return gcry_cipher_setkey(hp_cipher->hp_cipher, hp_key, key_length) == 0;
}
static int quic_pp_cipher_init(quic_pp_cipher *pp_cipher, int hash_algo,
- uint8_t key_length, uint8_t *secret)
+ uint8_t key_length, uint8_t *secret,
+ uint32_t version)
{
uint8_t write_key[256/8]; /* Maximum key size is for AES256 cipher. */
uint32_t hash_len = gcry_md_get_algo_dlen(hash_algo);
+ char *key_label = is_version_with_v1_labels(version) ? "quic key" : "quicv2 key";
+ char *iv_label = is_version_with_v1_labels(version) ? "quic iv" : "quicv2 iv";
if(key_length > sizeof(write_key)) {
return 0;
}
- if(!quic_hkdf_expand_label(hash_algo, secret, hash_len, "quic key", write_key, key_length) ||
- !quic_hkdf_expand_label(hash_algo, secret, hash_len, "quic iv", pp_cipher->pp_iv, sizeof(pp_cipher->pp_iv))) {
+ if(!quic_hkdf_expand_label(hash_algo, secret, hash_len, key_label, write_key, key_length) ||
+ !quic_hkdf_expand_label(hash_algo, secret, hash_len, iv_label, pp_cipher->pp_iv, sizeof(pp_cipher->pp_iv))) {
return 0;
}
@@ -579,7 +600,7 @@ static int quic_get_pn_cipher_algo(int cipher_algo, int *hp_cipher_mode)
* If the optional base secret is given, then its length MUST match the hash
* algorithm output.
*/
-static int quic_hp_cipher_prepare(quic_hp_cipher *hp_cipher, int hash_algo, int cipher_algo, uint8_t *secret)
+static int quic_hp_cipher_prepare(quic_hp_cipher *hp_cipher, int hash_algo, int cipher_algo, uint8_t *secret, u_int32_t version)
{
#if 0
/* Clear previous state (if any). */
@@ -604,7 +625,7 @@ static int quic_hp_cipher_prepare(quic_hp_cipher *hp_cipher, int hash_algo, int
if(secret) {
uint32_t cipher_keylen = (uint8_t)gcry_cipher_get_algo_keylen(cipher_algo);
- if(!quic_hp_cipher_init(hp_cipher, hash_algo, cipher_keylen, secret)) {
+ if(!quic_hp_cipher_init(hp_cipher, hash_algo, cipher_keylen, secret, version)) {
quic_hp_cipher_reset(hp_cipher);
#ifdef DEBUG_CRYPT
printf("Failed to derive key material for HP cipher\n");
@@ -615,7 +636,7 @@ static int quic_hp_cipher_prepare(quic_hp_cipher *hp_cipher, int hash_algo, int
return 1;
}
-static int quic_pp_cipher_prepare(quic_pp_cipher *pp_cipher, int hash_algo, int cipher_algo, int cipher_mode, uint8_t *secret)
+static int quic_pp_cipher_prepare(quic_pp_cipher *pp_cipher, int hash_algo, int cipher_algo, int cipher_mode, uint8_t *secret, u_int32_t version)
{
#if 0
/* Clear previous state (if any). */
@@ -632,7 +653,7 @@ static int quic_pp_cipher_prepare(quic_pp_cipher *pp_cipher, int hash_algo, int
if(secret) {
uint32_t cipher_keylen = (uint8_t)gcry_cipher_get_algo_keylen(cipher_algo);
- if(!quic_pp_cipher_init(pp_cipher, hash_algo, cipher_keylen, secret)) {
+ if(!quic_pp_cipher_init(pp_cipher, hash_algo, cipher_keylen, secret, version)) {
quic_pp_cipher_reset(pp_cipher);
#ifdef DEBUG_CRYPT
printf("Failed to derive key material for PP cipher\n");
@@ -643,10 +664,10 @@ static int quic_pp_cipher_prepare(quic_pp_cipher *pp_cipher, int hash_algo, int
return 1;
}
-static int quic_ciphers_prepare(quic_ciphers *ciphers, int hash_algo, int cipher_algo, int cipher_mode, uint8_t *secret)
+static int quic_ciphers_prepare(quic_ciphers *ciphers, int hash_algo, int cipher_algo, int cipher_mode, uint8_t *secret, u_int32_t version)
{
- return quic_hp_cipher_prepare(&ciphers->hp_cipher, hash_algo, cipher_algo, secret) &&
- quic_pp_cipher_prepare(&ciphers->pp_cipher, hash_algo, cipher_algo, cipher_mode, secret);
+ return quic_hp_cipher_prepare(&ciphers->hp_cipher, hash_algo, cipher_algo, secret, version) &&
+ quic_pp_cipher_prepare(&ciphers->pp_cipher, hash_algo, cipher_algo, cipher_mode, secret, version);
}
/**
* Given a header protection cipher, a buffer and the packet number offset,
@@ -864,6 +885,10 @@ static int quic_derive_initial_secrets(uint32_t version,
0x38, 0x76, 0x2c, 0xf7, 0xf5, 0x59, 0x34, 0xb3, 0x4d, 0x17,
0x9a, 0xe6, 0xa4, 0xc8, 0x0c, 0xad, 0xcc, 0xbb, 0x7f, 0x0a
};
+ static const uint8_t handshake_salt_v2_draft_00[20] = {
+ 0xa7, 0x07, 0xc2, 0x03, 0xa5, 0x9b, 0x47, 0x18, 0x4a, 0x1d,
+ 0x62, 0xca, 0x57, 0x04, 0x06, 0xea, 0x7a, 0xe3, 0xe5, 0xd3
+ };
gcry_error_t err;
uint8_t secret[HASH_SHA2_256_LENGTH];
#ifdef DEBUG_CRYPT
@@ -894,10 +919,14 @@ static int quic_derive_initial_secrets(uint32_t version,
err = hkdf_extract(GCRY_MD_SHA256, handshake_salt_draft_29,
sizeof(handshake_salt_draft_29),
cid, cid_len, secret);
- } else {
+ } else if (is_quic_ver_less_than(version, 33)) {
err = hkdf_extract(GCRY_MD_SHA256, handshake_salt_v1,
sizeof(handshake_salt_v1),
cid, cid_len, secret);
+ } else {
+ err = hkdf_extract(GCRY_MD_SHA256, handshake_salt_v2_draft_00,
+ sizeof(handshake_salt_v2_draft_00),
+ cid, cid_len, secret);
}
if(err) {
#ifdef DEBUG_CRYPT
@@ -947,7 +976,7 @@ static uint8_t *decrypt_initial_packet(struct ndpi_detection_module_struct *ndpi
Initial packets are protected with AEAD_AES_128_GCM. */
if(!quic_ciphers_prepare(&ciphers, GCRY_MD_SHA256,
GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_GCM,
- client_secret)) {
+ client_secret, version)) {
NDPI_LOG_DBG(ndpi_struct, "Error quic_cipher_prepare\n");
return NULL;
}
diff --git a/tests/do.sh.in b/tests/do.sh.in
index d04b483b6..692f432be 100755
--- a/tests/do.sh.in
+++ b/tests/do.sh.in
@@ -16,7 +16,7 @@ fi
GCRYPT_ENABLED=@GCRYPT_ENABLED@
PCRE_ENABLED=@PCRE_ENABLED@
PCRE_PCAPS="WebattackRCE.pcap"
-GCRYPT_PCAPS="gquic.pcap quic-23.pcap quic-24.pcap quic-27.pcap quic-28.pcap quic-29.pcap quic-mvfst-22.pcap quic-mvfst-27.pcapng quic-mvfst-exp.pcap quic_q50.pcap quic_t50.pcap quic_t51.pcap quic_0RTT.pcap quic_interop_V.pcapng quic-33.pcapng doq.pcapng doq_adguard.pcapng dlt_ppp.pcap os_detected.pcapng quic_frags_ch_out_of_order_same_packet_craziness.pcapng quic_frags_ch_in_multiple_packets.pcapng"
+GCRYPT_PCAPS="gquic.pcap quic-23.pcap quic-24.pcap quic-27.pcap quic-28.pcap quic-29.pcap quic-mvfst-22.pcap quic-mvfst-27.pcapng quic-mvfst-exp.pcap quic_q50.pcap quic_t50.pcap quic_t51.pcap quic_0RTT.pcap quic_interop_V.pcapng quic-33.pcapng doq.pcapng doq_adguard.pcapng dlt_ppp.pcap os_detected.pcapng quic_frags_ch_out_of_order_same_packet_craziness.pcapng quic_frags_ch_in_multiple_packets.pcapng quic-v2-00.pcapng"
READER="$VALGRIND ../example/ndpiReader -p ../example/protos.txt -c ../example/categories.txt -r ../example/risky_domains.txt -j ../example/ja3_fingerprints.csv -S ../example/sha1_fingerprints.csv"
RC=0
diff --git a/tests/pcap/quic-v2-00.pcapng b/tests/pcap/quic-v2-00.pcapng
new file mode 100644
index 000000000..146d3c935
--- /dev/null
+++ b/tests/pcap/quic-v2-00.pcapng
Binary files differ
diff --git a/tests/result/quic-v2-00.pcapng.out b/tests/result/quic-v2-00.pcapng.out
new file mode 100644
index 000000000..18c7c03e3
--- /dev/null
+++ b/tests/result/quic-v2-00.pcapng.out
@@ -0,0 +1,12 @@
+Guessed flow protos: 0
+
+DPI Packets (UDP): 1 (1.00 pkts/flow)
+
+QUIC 30 27593 1
+
+JA3 Host Stats:
+ IP Address # JA3C
+ 1 192.168.56.1 1
+
+
+ 1 UDP 192.168.56.1:50277 <-> 192.168.56.198:4443 [proto: 188/QUIC][Encrypted][cat: Web/5][11 pkts/5450 bytes <-> 19 pkts/22143 bytes][Goodput ratio: 92/96][0.01 sec][ALPN: h3-34;hq-34;h3-33;hq-33;h3-32;hq-32;h3-31;hq-31;h3-29;hq-29;h3-30;hq-30;h3-28;hq-28;h3-27;hq-27;h3;hq-interop][TLS Supported Versions: TLSv1.3;TLSv1.3 (draft);TLSv1.3 (draft);TLSv1.3 (draft)][bytes ratio: -0.605 (Download)][IAT c2s/s2c min/avg/max/stddev: 0/0 0/0 3/2 1/1][Pkt Len c2s/s2c min/avg/max/stddev: 97/97 495/1165 1482/1482 539/528][Risk: ** Known protocol on non standard port **** SNI TLS extension was missing **][Risk Score: 100][TLSv1.3][JA3C: 0299b052ace53a14c3a04aceb5efd247][PLAIN TEXT (anezfN)][Plen Bins: 0,23,3,0,0,6,0,0,0,0,0,0,3,3,0,0,0,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,0,0,0,0,0,47,0,0]