aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorIvan Nardi <12729895+IvanNardi@users.noreply.github.com>2020-12-08 15:47:58 +0100
committerGitHub <noreply@github.com>2020-12-08 15:47:58 +0100
commit860ef58aceed8dd8907f16d026c58345f1d84fb3 (patch)
treecc31bcac5e4d9e1754a113e1689f2f3df7083817 /src
parent1fb1848586af7b5b121a3e08ed31e607c5758aab (diff)
QUIC: sync with Wireshark latest changes (#1074)
Most of the QUIC crypto code has been "copied-and-pasted" from Wireshark; try to stay in sync with the original sources to ease backporting of fixes. Only cosmetic changes and code refactoring; no behaviour changes or bugfixes. See: https://gitlab.com/wireshark/wireshark/-/commit/5e45f770fd79ca979c41ed397fee72d2e8fb5f1e https://gitlab.com/wireshark/wireshark/-/commit/5798b91c1526747bf688b6746b33562c1b24a9e0
Diffstat (limited to 'src')
-rw-r--r--src/lib/protocols/quic.c168
1 files changed, 118 insertions, 50 deletions
diff --git a/src/lib/protocols/quic.c b/src/lib/protocols/quic.c
index 9a3acab3b..a15cdaf7a 100644
--- a/src/lib/protocols/quic.c
+++ b/src/lib/protocols/quic.c
@@ -278,11 +278,18 @@ typedef struct _StringInfo {
} StringInfo;
/* QUIC decryption context. */
-typedef struct quic_cipher {
+
+typedef struct quic_hp_cipher {
gcry_cipher_hd_t hp_cipher; /* Header protection cipher. */
+} quic_hp_cipher;
+typedef struct quic_pp_cipher {
gcry_cipher_hd_t pp_cipher; /* Packet protection cipher. */
uint8_t pp_iv[TLS13_AEAD_NONCE_LENGTH];
-} quic_cipher;
+} quic_pp_cipher;
+typedef struct quic_ciphers {
+ quic_hp_cipher hp_cipher;
+ quic_pp_cipher pp_cipher;
+} quic_ciphers;
typedef struct quic_decrypt_result {
uint8_t *data; /* Decrypted result on success (file-scoped). */
@@ -487,23 +494,45 @@ static int quic_hkdf_expand_label(int hash_algo, uint8_t *secret, uint32_t secre
}
return 0;
}
-static void quic_cipher_reset(quic_cipher *cipher)
+static void quic_hp_cipher_reset(quic_hp_cipher *hp_cipher)
+{
+ gcry_cipher_close(hp_cipher->hp_cipher);
+#if 0
+ memset(hp_cipher, 0, sizeof(*hp_cipher));
+#endif
+}
+static void quic_pp_cipher_reset(quic_pp_cipher *pp_cipher)
{
- gcry_cipher_close(cipher->hp_cipher);
- gcry_cipher_close(cipher->pp_cipher);
+ gcry_cipher_close(pp_cipher->pp_cipher);
#if 0
- memset(cipher, 0, sizeof(*cipher));
+ memset(pp_cipher, 0, sizeof(*pp_cipher));
#endif
}
+static void quic_ciphers_reset(quic_ciphers *ciphers)
+{
+ quic_hp_cipher_reset(&ciphers->hp_cipher);
+ quic_pp_cipher_reset(&ciphers->pp_cipher);
+}
/**
* Expands the secret (length MUST be the same as the "hash_algo" digest size)
* and initialize cipher with the new key.
*/
-static int quic_cipher_init(quic_cipher *cipher, int hash_algo,
- uint8_t key_length, uint8_t *secret)
+static int quic_hp_cipher_init(quic_hp_cipher *hp_cipher, int hash_algo,
+ uint8_t key_length, uint8_t *secret)
{
- uint8_t write_key[256/8]; /* Maximum key size is for AES256 cipher. */
- uint8_t hp_key[256/8];
+ uint8_t hp_key[256/8]; /* Maximum key size is for AES256 cipher. */
+ uint32_t hash_len = gcry_md_get_algo_dlen(hash_algo);
+
+ if(!quic_hkdf_expand_label(hash_algo, secret, hash_len, "quic hp", 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 write_key[256/8]; /* Maximum key size is for AES256 cipher. */
uint32_t hash_len = gcry_md_get_algo_dlen(hash_algo);
if(key_length > sizeof(write_key)) {
@@ -511,13 +540,11 @@ static int quic_cipher_init(quic_cipher *cipher, int hash_algo,
}
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", cipher->pp_iv, sizeof(cipher->pp_iv)) ||
- !quic_hkdf_expand_label(hash_algo, secret, hash_len, "quic hp", hp_key, key_length)) {
+ !quic_hkdf_expand_label(hash_algo, secret, hash_len, "quic iv", pp_cipher->pp_iv, sizeof(pp_cipher->pp_iv))) {
return 0;
}
- return gcry_cipher_setkey(cipher->hp_cipher, hp_key, key_length) == 0 &&
- gcry_cipher_setkey(cipher->pp_cipher, write_key, key_length) == 0;
+ return gcry_cipher_setkey(pp_cipher->pp_cipher, write_key, key_length) == 0;
}
/**
* Maps a Packet Protection cipher to the Packet Number protection cipher.
@@ -539,12 +566,11 @@ 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_cipher_prepare(quic_cipher *cipher, int hash_algo, int cipher_algo,
- int cipher_mode, uint8_t *secret)
+static int quic_hp_cipher_prepare(quic_hp_cipher *hp_cipher, int hash_algo, int cipher_algo, uint8_t *secret)
{
#if 0
/* Clear previous state (if any). */
- quic_cipher_reset(cipher);
+ quic_hp_cipher_reset(hp_cipher);
#endif
int hp_cipher_mode;
@@ -555,21 +581,20 @@ static int quic_cipher_prepare(quic_cipher *cipher, int hash_algo, int cipher_al
return 0;
}
- if(gcry_cipher_open(&cipher->hp_cipher, cipher_algo, hp_cipher_mode, 0) ||
- gcry_cipher_open(&cipher->pp_cipher, cipher_algo, cipher_mode, 0)) {
- quic_cipher_reset(cipher);
+ if(gcry_cipher_open(&hp_cipher->hp_cipher, cipher_algo, hp_cipher_mode, 0)) {
+ quic_hp_cipher_reset(hp_cipher);
#ifdef DEBUG_CRYPT
- printf("Failed to create ciphers\n");
+ printf("Failed to create HP cipher\n");
#endif
return 0;
}
if(secret) {
uint32_t cipher_keylen = (uint8_t)gcry_cipher_get_algo_keylen(cipher_algo);
- if(!quic_cipher_init(cipher, hash_algo, cipher_keylen, secret)) {
- quic_cipher_reset(cipher);
+ if(!quic_hp_cipher_init(hp_cipher, hash_algo, cipher_keylen, secret)) {
+ quic_hp_cipher_reset(hp_cipher);
#ifdef DEBUG_CRYPT
- printf("Failed to derive key material for cipher\n");
+ printf("Failed to derive key material for HP cipher\n");
#endif
return 0;
}
@@ -577,19 +602,55 @@ static int quic_cipher_prepare(quic_cipher *cipher, int hash_algo, int cipher_al
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)
+{
+#if 0
+ /* Clear previous state (if any). */
+ quic_pp_cipher_reset(pp_cipher);
+#endif
+
+ if(gcry_cipher_open(&pp_cipher->pp_cipher, cipher_algo, cipher_mode, 0)) {
+ quic_pp_cipher_reset(pp_cipher);
+#ifdef DEBUG_CRYPT
+ printf("Failed to create PP cipher\n");
+#endif
+ return 0;
+ }
+
+ 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)) {
+ quic_pp_cipher_reset(pp_cipher);
+#ifdef DEBUG_CRYPT
+ printf("Failed to derive key material for PP cipher\n");
+#endif
+ return 0;
+ }
+ }
+
+ return 1;
+}
+static int quic_ciphers_prepare(quic_ciphers *ciphers, int hash_algo, int cipher_algo, int cipher_mode, uint8_t *secret)
+{
+ 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);
+}
/**
* Given a header protection cipher, a buffer and the packet number offset,
* return the unmasked first byte and packet number.
+ * If the loss bits feature is enabled, the protected bits in the first byte
+ * are fewer than usual: 3 instead of 5 (on short headers only)
*/
static int quic_decrypt_header(const uint8_t *packet_payload,
- uint32_t pn_offset, gcry_cipher_hd_t hp_cipher,
- int hp_cipher_algo, uint8_t *first_byte, uint32_t *pn)
+ uint32_t pn_offset, quic_hp_cipher *hp_cipher,
+ int hp_cipher_algo, uint8_t *first_byte, uint32_t *pn,
+ int loss_bits_negotiated)
{
- gcry_cipher_hd_t h = hp_cipher;
- if(!hp_cipher) {
+ if(!hp_cipher->hp_cipher) {
/* Need to know the cipher */
return 0;
}
+ gcry_cipher_hd_t h = hp_cipher->hp_cipher;
/* Sample is always 16 bytes and starts after PKN (assuming length 4).
https://tools.ietf.org/html/draft-ietf-quic-tls-22#section-5.4.2 */
@@ -616,8 +677,14 @@ static int quic_decrypt_header(const uint8_t *packet_payload,
/* Long header: 4 bits masked */
packet0 ^= mask[0] & 0x0f;
} else {
- /* Short header: 5 bits masked */
- packet0 ^= mask[0] & 0x1f;
+ /* Short header */
+ if(loss_bits_negotiated == 0) {
+ /* Standard mask: 5 bits masked */
+ packet0 ^= mask[0] & 0x1F;
+ } else {
+ /* https://tools.ietf.org/html/draft-ferrieuxhamchaoui-quic-lossbits-03#section-5.3 */
+ packet0 ^= mask[0] & 0x07;
+ }
}
uint32_t pkn_len = (packet0 & 0x03) + 1;
/* printf("packet0 0x%x pkn_len %d\n", packet0, pkn_len); */
@@ -641,7 +708,7 @@ static int quic_decrypt_header(const uint8_t *packet_payload,
* The actual packet number must be constructed according to
* https://tools.ietf.org/html/draft-ietf-quic-transport-22#section-12.3
*/
-static void quic_decrypt_message(quic_cipher *cipher, const uint8_t *packet_payload, uint32_t packet_payload_len,
+static void quic_decrypt_message(quic_pp_cipher *pp_cipher, const uint8_t *packet_payload, uint32_t packet_payload_len,
uint32_t header_length, uint8_t first_byte, uint32_t pkn_len,
uint64_t packet_number, quic_decrypt_result_t *result)
{
@@ -655,8 +722,8 @@ static void quic_decrypt_message(quic_cipher *cipher, const uint8_t *packet_payl
char buferr[128];
#endif
- if(!(cipher != NULL) ||
- !(cipher->pp_cipher != NULL) ||
+ if(!(pp_cipher != NULL) ||
+ !(pp_cipher->pp_cipher != NULL) ||
!(pkn_len < header_length) ||
!(1 <= pkn_len && pkn_len <= 4)) {
#ifdef DEBUG_CRYPT
@@ -689,12 +756,12 @@ static void quic_decrypt_message(quic_cipher *cipher, const uint8_t *packet_payl
}
memcpy(atag, packet_payload + header_length + buffer_length, 16);
- memcpy(nonce, cipher->pp_iv, TLS13_AEAD_NONCE_LENGTH);
+ memcpy(nonce, pp_cipher->pp_iv, TLS13_AEAD_NONCE_LENGTH);
/* Packet number is left-padded with zeroes and XORed with write_iv */
phton64(nonce + sizeof(nonce) - 8, pntoh64(nonce + sizeof(nonce) - 8) ^ packet_number);
- gcry_cipher_reset(cipher->pp_cipher);
- err = gcry_cipher_setiv(cipher->pp_cipher, nonce, TLS13_AEAD_NONCE_LENGTH);
+ gcry_cipher_reset(pp_cipher->pp_cipher);
+ err = gcry_cipher_setiv(pp_cipher->pp_cipher, nonce, TLS13_AEAD_NONCE_LENGTH);
if(err) {
#ifdef DEBUG_CRYPT
printf("Decryption (setiv) failed: %s\n", __gcry_err(err, buferr, sizeof(buferr)));
@@ -705,7 +772,7 @@ static void quic_decrypt_message(quic_cipher *cipher, const uint8_t *packet_payl
}
/* associated data (A) is the contents of QUIC header */
- err = gcry_cipher_authenticate(cipher->pp_cipher, header, header_length);
+ err = gcry_cipher_authenticate(pp_cipher->pp_cipher, header, header_length);
if(err) {
#ifdef DEBUG_CRYPT
printf("Decryption (authenticate) failed: %s\n", __gcry_err(err, buferr, sizeof(buferr)));
@@ -718,7 +785,7 @@ static void quic_decrypt_message(quic_cipher *cipher, const uint8_t *packet_payl
ndpi_free(header);
/* Output ciphertext (C) */
- err = gcry_cipher_decrypt(cipher->pp_cipher, buffer, buffer_length, NULL, 0);
+ err = gcry_cipher_decrypt(pp_cipher->pp_cipher, buffer, buffer_length, NULL, 0);
if(err) {
#ifdef DEBUG_CRYPT
printf("Decryption (decrypt) failed: %s\n", __gcry_err(err, buferr, sizeof(buferr)));
@@ -727,7 +794,7 @@ static void quic_decrypt_message(quic_cipher *cipher, const uint8_t *packet_payl
return;
}
- err = gcry_cipher_checktag(cipher->pp_cipher, atag, 16);
+ err = gcry_cipher_checktag(pp_cipher->pp_cipher, atag, 16);
if(err) {
#ifdef DEBUG_CRYPT
printf("Decryption (checktag) failed: %s\n", __gcry_err(err, buferr, sizeof(buferr)));
@@ -848,10 +915,11 @@ static uint8_t *decrypt_initial_packet(struct ndpi_detection_module_struct *ndpi
struct ndpi_packet_struct *packet = &flow->packet;
uint8_t first_byte;
uint32_t pkn32, pn_offset, pkn_len, offset;
- quic_cipher cipher = {0}; /* Client initial cipher */
+ quic_ciphers ciphers; /* Client initial ciphers */
quic_decrypt_result_t decryption = {0};
uint8_t client_secret[HASH_SHA2_256_LENGTH];
+ memset(&ciphers, '\0', sizeof(ciphers));
if(quic_derive_initial_secrets(version, dest_conn_id, dest_conn_id_len,
client_secret) != 0) {
NDPI_LOG_DBG(ndpi_struct, "Error quic_derive_initial_secrets\n");
@@ -860,9 +928,9 @@ static uint8_t *decrypt_initial_packet(struct ndpi_detection_module_struct *ndpi
/* Packet numbers are protected with AES128-CTR,
Initial packets are protected with AEAD_AES_128_GCM. */
- if(!quic_cipher_prepare(&cipher, GCRY_MD_SHA256,
- GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_GCM,
- client_secret)) {
+ if(!quic_ciphers_prepare(&ciphers, GCRY_MD_SHA256,
+ GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_GCM,
+ client_secret)) {
NDPI_LOG_DBG(ndpi_struct, "Error quic_cipher_prepare\n");
return NULL;
}
@@ -873,7 +941,7 @@ static uint8_t *decrypt_initial_packet(struct ndpi_detection_module_struct *ndpi
pn_offset += token_length;
/* Checks: quic_len reads 8 bytes, at most; quic_decrypt_header reads other 20 bytes */
if(pn_offset + 8 + (4 + 16) >= packet->payload_packet_len) {
- quic_cipher_reset(&cipher);
+ quic_ciphers_reset(&ciphers);
return NULL;
}
pn_offset += quic_len(&packet->payload[pn_offset], &payload_length);
@@ -884,13 +952,13 @@ static uint8_t *decrypt_initial_packet(struct ndpi_detection_module_struct *ndpi
if (pn_offset + payload_length > packet->payload_packet_len) {
NDPI_LOG_DBG(ndpi_struct, "Too short %d %d\n", pn_offset + payload_length,
packet->payload_packet_len);
- quic_cipher_reset(&cipher);
+ quic_ciphers_reset(&ciphers);
return NULL;
}
- if(!quic_decrypt_header(&packet->payload[0], pn_offset, cipher.hp_cipher,
- GCRY_CIPHER_AES128, &first_byte, &pkn32)) {
- quic_cipher_reset(&cipher);
+ if(!quic_decrypt_header(&packet->payload[0], pn_offset, &ciphers.hp_cipher,
+ GCRY_CIPHER_AES128, &first_byte, &pkn32, 0)) {
+ quic_ciphers_reset(&ciphers);
return NULL;
}
NDPI_LOG_DBG2(ndpi_struct, "first_byte 0x%x pkn32 0x%x\n", first_byte, pkn32);
@@ -900,10 +968,10 @@ static uint8_t *decrypt_initial_packet(struct ndpi_detection_module_struct *ndpi
packet_number = pkn32;
offset = pn_offset + pkn_len;
- quic_decrypt_message(&cipher, &packet->payload[0], pn_offset + payload_length,
+ quic_decrypt_message(&ciphers.pp_cipher, &packet->payload[0], pn_offset + payload_length,
offset, first_byte, pkn_len, packet_number, &decryption);
- quic_cipher_reset(&cipher);
+ quic_ciphers_reset(&ciphers);
if(decryption.data_len) {
*clear_payload_len = decryption.data_len;