diff options
author | Ivan Nardi <12729895+IvanNardi@users.noreply.github.com> | 2020-12-08 15:47:58 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-12-08 15:47:58 +0100 |
commit | 860ef58aceed8dd8907f16d026c58345f1d84fb3 (patch) | |
tree | cc31bcac5e4d9e1754a113e1689f2f3df7083817 /src | |
parent | 1fb1848586af7b5b121a3e08ed31e607c5758aab (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.c | 168 |
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; |