/** * parser-quic.c * * Created on 2020-11-26 * @author: qyc * * @explain: QUIC解析 */ #include #include #include #include "parser_quic.h" #include "wsgcrypt.h" #include "utils.h" #include "pint.h" #include "gcrypt.h" // #define DEBUG_PARSER_QUIC int gcry_init() { gcry_check_version("1.8.7"); //gcry_control(GCRYCTL_SET_THREAD_CBS,&gcry_threads_pthread); return 0; } #define QUIC_LPT_INITIAL 0x0 #define QUIC_LPT_0RTT 0x1 #define QUIC_LPT_HANDSHAKE 0x2 #define QUIC_LPT_RETRY 0x3 /* Version Negotiation packets don't have any real packet type */ #define QUIC_LPT_VER_NEG 0xfe /* dummy value that is definitely not LPT */ #define QUIC_SHORT_PACKET 0xff /* * Although the QUIC SCID/DCID length field can store at most 255, v1 limits the * CID length to 20. */ #define QUIC_MAX_CID_LENGTH 20 typedef struct _quic_cid { unsigned char len; unsigned char cid[QUIC_MAX_CID_LENGTH]; } quic_cid_t; /* * PROTECTED PAYLOAD DECRYPTION (done in first pass) * * Long packet types always use a single cipher depending on packet type. * Short packet types always use 1-RTT secrets for packet protection (pp). * * Considerations: * - QUIC packets might appear out-of-order (short packets before handshake * message is captured), lost or retransmitted/duplicated. * - During live capture, keys might not be immediately be available. 1-RTT * client keys will be ready while client proceses Server Hello (Handshake). * 1-RTT server keys will be ready while server creates Handshake message in * response to Initial Handshake. * - So delay cipher creation until first short packet is received. * * Required input from TLS dissector: TLS-Exporter 0-RTT/1-RTT secrets and * cipher/hash algorithms. * * QUIC payload decryption requires proper reconstruction of the packet number * which requires proper header decryption. The different states are: * * Packet type Packet number space Secrets * Long: Initial Initial Initial secrets * Long: Handshake Handshake Handshake * Long: 0-RTT 0/1-RTT (appdata) 0-RTT * Short header 0/1-RTT (appdata) 1-RTT (KP0 / KP1) * * Important to note is that Short Header decryption requires TWO ciphers (one * for each key phase), but that header protection uses only KP0. Total state * needed for each peer (client and server): * - 3 packet number spaces: Initial, Handshake, 0/1-RTT (appdata). * - 4 header protection ciphers: initial, 0-RTT, HS, 1-RTT. * - 5 payload protection ciphers: initial, 0-RTT, HS, 1-RTT (KP0), 1-RTT (KP1). */ typedef struct _quic_decrypt_result { // Error message or NULL for success. const guchar *error; // Decrypted result on success (file-scoped). const guint8 *data; // Size of decrypted data. guint data_len; } quic_decrypt_result_t; /** QUIC decryption context. */ typedef struct _quic_hp_cipher { // Header protection cipher. gcry_cipher_hd_t hp_cipher; } quic_hp_cipher; typedef struct _quic_pp_cipher { // Packet protection cipher. gcry_cipher_hd_t pp_cipher; guint8 pp_iv[TLS13_AEAD_NONCE_LENGTH]; } quic_pp_cipher; typedef struct _quic_ciphers { quic_hp_cipher hp_cipher; quic_pp_cipher pp_cipher; } quic_ciphers; /** * State for a single QUIC connection, identified by one or more Destination * Connection IDs (DCID). */ typedef struct _quic_info_data { guint32 version; quic_ciphers client_initial_ciphers; quic_ciphers server_initial_ciphers; // Packet number spaces for Initial, Handshake and appdata. guint64 max_client_pkn[3]; guint64 max_server_pkn[3]; } quic_info_data_t; /** Per-packet information about QUIC, populated on the first pass. */ typedef struct _quic_packet_info { // Reconstructed full packet number. guint64 packet_number; quic_decrypt_result_t decryption; // Length of PKN (1/2/3/4) or unknown (0). guint8 pkn_len; // Decrypted flag byte, valid only if pkn_len is non-zero. guint8 first_byte; } quic_packet_info_t; /** * Given a QUIC message (header + non-empty payload), the actual packet number, * try to decrypt it using the PP cipher. * As the header points to the original buffer with an encrypted packet number, * the (encrypted) packet number length is also included. * * 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_pp_cipher *pp_cipher, const char *payload, guint length, guint header_length, guint8 first_byte, guint pkn_len, guint64 packet_number, quic_decrypt_result_t *result) { gcry_error_t err; guint8 *header; guint8 nonce[TLS13_AEAD_NONCE_LENGTH]; guint8 *buffer; guint8 atag[16]; guint buffer_length; const guchar **error = &result->error; g_assert(pp_cipher != NULL); g_assert(pp_cipher->pp_cipher != NULL); g_assert(pkn_len < header_length); g_assert(1 <= pkn_len && pkn_len <= 4); // copy header, but replace encrypted first byte and PKN by plaintext. header = (guint8 *)g_malloc(header_length); memcpy(header, payload, header_length); header[0] = first_byte; guint i; for (i = 0; i < pkn_len; i++) header[header_length - 1 - i] = (guint8)(packet_number >> (8 * i)); // Input is "header || ciphertext (buffer) || auth tag (16 bytes)" // buffer_length = length - (header_length + 16); // buffer_length = 297 - (2 + 16); buffer_length = length - (pkn_len + 16); if (buffer_length == 0) { *error = (const guchar *)"Decryption not possible, ciphertext is too short"; return; } buffer = (guint8 *)g_malloc(buffer_length); memcpy(buffer, payload + header_length, buffer_length); memcpy(atag, payload + header_length + buffer_length, 16); 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(pp_cipher->pp_cipher); err = gcry_cipher_setiv(pp_cipher->pp_cipher, nonce, TLS13_AEAD_NONCE_LENGTH); if (err) { //printf("Decryption (setiv) failed: %s\n", gcry_strerror(err)); *error = (const guchar *)"Decryption (setiv) failed"; return; } // associated data (A) is the contents of QUIC header err = gcry_cipher_authenticate(pp_cipher->pp_cipher, header, header_length); if (err) { //printf("Decryption (authenticate) failed: %s\n", gcry_strerror(err)); *error = (const guchar *)"Decryption (authenticate) failed"; return; } // Output ciphertext (C) err = gcry_cipher_decrypt(pp_cipher->pp_cipher, buffer, buffer_length, NULL, 0); if (err) { //printf("Decryption (decrypt) failed: %s\n", gcry_strerror(err)); *error = (const guchar *)"Decryption (decrypt) failed"; return; } err = gcry_cipher_checktag(pp_cipher->pp_cipher, atag, 16); if (err) { //printf("Decryption (checktag) failed: %s\n", gcry_strerror(err)); *error = (const guchar *)"Decryption (checktag) failed"; return; } g_free(header); result->error = NULL; result->data = buffer; result->data_len = buffer_length; } static gboolean quic_is_pp_cipher_initialized(quic_pp_cipher *pp_cipher) { return pp_cipher && pp_cipher->pp_cipher; } /** * Process (protected) payload, adding the encrypted payload to the tree. If * decryption is possible, frame dissection is also attempted. * * The given offset must correspond to the end of the QUIC header and begin of * the (protected) payload. Dissected frames are appended to "tree" and expert * info is attached to "ti" (the field with the encrypted payload). */ static void quic_process_payload(const char *payload, guint length, guint offset, quic_info_data_t *quic_info, quic_packet_info_t *quic_packet, gboolean from_server, quic_pp_cipher *pp_cipher, guint8 first_byte, guint pkn_len) { /* * If no decryption error has occurred yet, try decryption on the first * pass and store the result for later use. */ if (quic_is_pp_cipher_initialized(pp_cipher)) quic_decrypt_message(pp_cipher, payload, length, offset, first_byte, pkn_len, quic_packet->packet_number, &quic_packet->decryption); } /* Inspired from ngtcp2 */ static guint64 quic_pkt_adjust_pkt_num(guint64 max_pkt_num, guint64 pkt_num, size_t n) { guint64 k = max_pkt_num == G_MAXUINT64 ? max_pkt_num : max_pkt_num + 1; guint64 u = k & ~((G_GUINT64_CONSTANT(1) << n) - 1); guint64 a = u | pkt_num; guint64 b = (u + (G_GUINT64_CONSTANT(1) << n)) | pkt_num; guint64 a1 = k < a ? a - k : k - a; guint64 b1 = k < b ? b - k : k - b; if (a1 < b1) return a; return b; } /** * Retrieve the maximum valid packet number space for a peer. */ static guint64 *quic_max_packet_number(quic_info_data_t *quic_info, gboolean from_server, guint8 first_byte) { int pkn_space; if ((first_byte & 0x80) && (first_byte & 0x30) >> 4 == QUIC_LPT_INITIAL) // Long header, Initial pkn_space = 0; else if ((first_byte & 0x80) && (first_byte & 0x30) >> 4 == QUIC_LPT_HANDSHAKE) // Long header, Handshake pkn_space = 1; else // Long header (0-RTT) or Short Header (1-RTT appdata). pkn_space = 2; if (from_server) return &quic_info->max_server_pkn[pkn_space]; else return &quic_info->max_client_pkn[pkn_space]; } /** * Calculate the full packet number and store it for later use. */ static void quic_set_full_packet_number(quic_info_data_t *quic_info, quic_packet_info_t *quic_packet, gboolean from_server, guint8 first_byte, guint32 pkn32) { guint pkn_len = (first_byte & 3) + 1; guint64 pkn_full; guint64 max_pn = *quic_max_packet_number(quic_info, from_server, first_byte); // Sequential first pass, try to reconstruct full packet number. pkn_full = quic_pkt_adjust_pkt_num(max_pn, pkn32, 8 * pkn_len); quic_packet->pkn_len = pkn_len; quic_packet->packet_number = pkn_full; } /** * Given a header protection cipher, a buffer and the packet number offset, * return the unmasked first byte and packet number. */ static gboolean quic_decrypt_header(const char *payload, guint pn_offset, quic_hp_cipher *hp_cipher, int hp_cipher_algo, guint8 *first_byte, guint32 *pn) { if (!hp_cipher->hp_cipher) // need to know the cipher. return FALSE; 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 guint8 sample[16]; memcpy(sample, payload + pn_offset + 4, 16); guint8 mask[5] = { 0 }; switch (hp_cipher_algo) { case GCRY_CIPHER_AES128: case GCRY_CIPHER_AES256: // Encrypt in-place with AES-ECB and extract the mask. if (gcry_cipher_encrypt(h, sample, sizeof(sample), NULL, 0)) return FALSE; memcpy(mask, sample, sizeof(mask)); break; #ifdef HAVE_LIBGCRYPT_CHACHA20 case GCRY_CIPHER_CHACHA20: // If Gcrypt receives a 16 byte IV, it will assume the buffer to be // counter || nonce (in little endian), as desired. */ if (gcry_cipher_setiv(h, sample, 16)) return FALSE; // Apply ChaCha20, encrypt in-place five zero bytes. if (gcry_cipher_encrypt(h, mask, sizeof(mask), NULL, 0)) return FALSE; break; #endif // HAVE_LIBGCRYPT_CHACHA20 default: return FALSE; } // https://tools.ietf.org/html/draft-ietf-quic-tls-22#section-5.4.1 guint8 packet0 = payload[0]; if ((packet0 & 0x80) == 0x80) // Long header: 4 bits masked packet0 ^= mask[0] & 0x0f; else // Short header: 5 bits masked packet0 ^= mask[0] & 0x1f; guint pkn_len = (packet0 & 0x03) + 1; guint8 pkn_bytes[4]; memcpy(pkn_bytes, payload + pn_offset, pkn_len); guint32 pkt_pkn = 0; guint i; for (i = 0; i < pkn_len; i++) pkt_pkn |= (pkn_bytes[i] ^ mask[1 + i]) << (8 * (pkn_len - 1 - i)); *first_byte = packet0; *pn = pkt_pkn; return TRUE; } static gboolean quic_hkdf_expand_label(int hash_algo, guint8 *secret, guint secret_len, const char *label, guint8 *out, guint out_len) { const StringInfo secret_si = { secret, secret_len }; guchar *out_mem = NULL; if (tls13_hkdf_expand_label(hash_algo, &secret_si, "tls13 ", label, out_len, &out_mem)) { memcpy(out, out_mem, out_len); g_free(out_mem); return TRUE; } return FALSE; } /** * Expands the secret (length MUST be the same as the "hash_algo" digest size) * and initialize cipher with the new key. */ static gboolean quic_hp_cipher_init(quic_hp_cipher *hp_cipher, int hash_algo, guint8 key_length, guint8 *secret) { guchar hp_key[256/8]; guint 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 FALSE; return gcry_cipher_setkey(hp_cipher->hp_cipher, hp_key, key_length) == 0; } static gboolean quic_pp_cipher_init(quic_pp_cipher *pp_cipher, int hash_algo, guint8 key_length, guint8 *secret) { // Maximum key size is for AES256 cipher. guchar write_key[256/8]; guint hash_len = gcry_md_get_algo_dlen(hash_algo); if (key_length > sizeof(write_key)) return FALSE; 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))) return FALSE; return gcry_cipher_setkey(pp_cipher->pp_cipher, write_key, key_length) == 0; } static void quic_hp_cipher_reset(quic_hp_cipher *hp_cipher) { gcry_cipher_close(hp_cipher->hp_cipher); memset(hp_cipher, 0, sizeof(*hp_cipher)); } static void quic_pp_cipher_reset(quic_pp_cipher *pp_cipher) { gcry_cipher_close(pp_cipher->pp_cipher); memset(pp_cipher, 0, sizeof(*pp_cipher)); } /** * Maps a Packet Protection cipher to the Packet Number protection cipher. * See https://tools.ietf.org/html/draft-ietf-quic-tls-22#section-5.4.3 */ static gboolean quic_get_pn_cipher_algo(int cipher_algo, int *hp_cipher_mode) { switch (cipher_algo) { case GCRY_CIPHER_AES128: case GCRY_CIPHER_AES256: *hp_cipher_mode = GCRY_CIPHER_MODE_ECB; return TRUE; #ifdef HAVE_LIBGCRYPT_CHACHA20 case GCRY_CIPHER_CHACHA20: *hp_cipher_mode = GCRY_CIPHER_MODE_STREAM; return TRUE; #endif // HAVE_LIBGCRYPT_CHACHA20 default: return FALSE; } } /* * (Re)initialize the PNE/PP ciphers using the given cipher algorithm. * If the optional base secret is given, then its length MUST match the hash * algorithm output. */ static gboolean quic_hp_cipher_prepare(quic_hp_cipher *hp_cipher, int hash_algo, int cipher_algo, guint8 *secret, const char **error) { // Clear previous state (if any). quic_hp_cipher_reset(hp_cipher); int hp_cipher_mode; if (!quic_get_pn_cipher_algo(cipher_algo, &hp_cipher_mode)) { *error = "Unsupported cipher algorithm"; return FALSE; } if (gcry_cipher_open(&hp_cipher->hp_cipher, cipher_algo, hp_cipher_mode, 0)) { quic_hp_cipher_reset(hp_cipher); *error = "Failed to create HP cipher"; return FALSE; } if (secret) { guint cipher_keylen = (guint8)gcry_cipher_get_algo_keylen(cipher_algo); if (!quic_hp_cipher_init(hp_cipher, hash_algo, cipher_keylen, secret)) { quic_hp_cipher_reset(hp_cipher); *error = "Failed to derive key material for HP cipher"; return FALSE; } } return TRUE; } static gboolean quic_pp_cipher_prepare(quic_pp_cipher *pp_cipher, int hash_algo, int cipher_algo, int cipher_mode, guint8 *secret, const char **error) { // Clear previous state (if any). quic_pp_cipher_reset(pp_cipher); int hp_cipher_mode; if (!quic_get_pn_cipher_algo(cipher_algo, &hp_cipher_mode)) { *error = "Unsupported cipher algorithm"; return FALSE; } if (gcry_cipher_open(&pp_cipher->pp_cipher, cipher_algo,cipher_mode, 0)) { quic_pp_cipher_reset(pp_cipher); *error = "Failed to create PP cipher"; return FALSE; } if (secret) { guint cipher_keylen = (guint8) 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); *error = "Failed to derive key material for PP cipher"; return FALSE; } } return TRUE; } static gboolean quic_ciphers_prepare(quic_ciphers *ciphers, int hash_algo, int cipher_algo, int cipher_mode, guint8 *secret, const char **error) { return quic_hp_cipher_prepare(&ciphers->hp_cipher, hash_algo, cipher_algo, secret, error) && quic_pp_cipher_prepare(&ciphers->pp_cipher, hash_algo, cipher_algo, cipher_mode, secret, error); } /* Returns the QUIC draft version or 0 if not applicable. */ static inline guint8 quic_draft_version(guint32 version) { if ((version >> 8) == 0xff0000) return (guint8) version; // Facebook mvfst, based on draft -22. if (version == 0xfaceb001) return 22; // Facebook mvfst, based on draft -27. if (version == 0xfaceb002 || version == 0xfaceb00e) return 27; // GQUIC Q050, T050 and T051: they are not really based on any drafts, // but we must return a sensible value if (version == 0x51303530 || version == 0x54303530 || version == 0x54303531) return 27; /* * https://tools.ietf.org/html/draft-ietf-quic-transport-32#section-15 * "Versions that follow the pattern 0x?a?a?a?a are reserved for use in * forcing version negotiation to be exercised" * It is tricky to return a correct draft version: such number is primarly * used to select a proper salt (which depends on the version itself), but * we don't have a real version here! Let's hope that we need to handle * only latest drafts... */ if ((version & 0x0F0F0F0F) == 0x0a0a0a0a) return 29; return 0; } static inline gboolean is_quic_draft_max(guint32 version, guint8 max_version) { guint8 draft_version = quic_draft_version(version); return draft_version && draft_version <= max_version; } /** * Compute the client and server initial secrets given Connection ID "cid". * * On success TRUE is returned and the two initial secrets are set. * FALSE is returned on error (see "error" parameter for the reason). */ static gboolean quic_derive_initial_secrets(const quic_cid_t *cid, guint8 client_initial_secret[HASH_SHA2_256_LENGTH], guint8 server_initial_secret[HASH_SHA2_256_LENGTH], guint32 version, const gchar **error) { /* * https://tools.ietf.org/html/draft-ietf-quic-tls-29#section-5.2 * * initial_salt = 0xafbfec289993d24c9e9786f19c6111e04390a899 * initial_secret = HKDF-Extract(initial_salt, client_dst_connection_id) * * client_initial_secret = HKDF-Expand-Label(initial_secret, * "client in", "", Hash.length) * server_initial_secret = HKDF-Expand-Label(initial_secret, * "server in", "", Hash.length) * * Hash for handshake packets is SHA-256 (output size 32). */ static const guint8 handshake_salt_draft_22[20] = { 0x7f, 0xbc, 0xdb, 0x0e, 0x7c, 0x66, 0xbb, 0xe9, 0x19, 0x3a, 0x96, 0xcd, 0x21, 0x51, 0x9e, 0xbd, 0x7a, 0x02, 0x64, 0x4a }; static const guint8 handshake_salt_draft_23[20] = { 0xc3, 0xee, 0xf7, 0x12, 0xc7, 0x2e, 0xbb, 0x5a, 0x11, 0xa7, 0xd2, 0x43, 0x2b, 0xb4, 0x63, 0x65, 0xbe, 0xf9, 0xf5, 0x02, }; static const guint8 handshake_salt_draft_29[20] = { 0xaf, 0xbf, 0xec, 0x28, 0x99, 0x93, 0xd2, 0x4c, 0x9e, 0x97, 0x86, 0xf1, 0x9c, 0x61, 0x11, 0xe0, 0x43, 0x90, 0xa8, 0x99 }; static const guint8 handshake_salt_v1[20] = { 0x38, 0x76, 0x2c, 0xf7, 0xf5, 0x59, 0x34, 0xb3, 0x4d, 0x17, 0x9a, 0xe6, 0xa4, 0xc8, 0x0c, 0xad, 0xcc, 0xbb, 0x7f, 0x0a }; static const guint8 hanshake_salt_draft_q50[20] = { 0x50, 0x45, 0x74, 0xEF, 0xD0, 0x66, 0xFE, 0x2F, 0x9D, 0x94, 0x5C, 0xFC, 0xDB, 0xD3, 0xA7, 0xF0, 0xD3, 0xB5, 0x6B, 0x45 }; static const guint8 hanshake_salt_draft_t50[20] = { 0x7f, 0xf5, 0x79, 0xe5, 0xac, 0xd0, 0x72, 0x91, 0x55, 0x80, 0x30, 0x4c, 0x43, 0xa2, 0x36, 0x7c, 0x60, 0x48, 0x83, 0x10 }; static const guint8 hanshake_salt_draft_t51[20] = { 0x7a, 0x4e, 0xde, 0xf4, 0xe7, 0xcc, 0xee, 0x5f, 0xa4, 0x50, 0x6c, 0x19, 0x12, 0x4f, 0xc8, 0xcc, 0xda, 0x6e, 0x03, 0x3d }; gcry_error_t err; guint8 secret[HASH_SHA2_256_LENGTH]; if (version == 0x51303530) { err = hkdf_extract(GCRY_MD_SHA256, hanshake_salt_draft_q50, sizeof(hanshake_salt_draft_q50), cid->cid, cid->len, secret); } else if (version == 0x54303530) { err = hkdf_extract(GCRY_MD_SHA256, hanshake_salt_draft_t50, sizeof(hanshake_salt_draft_t50), cid->cid, cid->len, secret); } else if (version == 0x54303531) { err = hkdf_extract(GCRY_MD_SHA256, hanshake_salt_draft_t51, sizeof(hanshake_salt_draft_t51), cid->cid, cid->len, secret); } else if (is_quic_draft_max(version, 22)) { err = hkdf_extract(GCRY_MD_SHA256, handshake_salt_draft_22, sizeof(handshake_salt_draft_22), cid->cid, cid->len, secret); } else if (is_quic_draft_max(version, 28)) { err = hkdf_extract(GCRY_MD_SHA256, handshake_salt_draft_23, sizeof(handshake_salt_draft_23), cid->cid, cid->len, secret); } else if (is_quic_draft_max(version, 32)) { err = hkdf_extract(GCRY_MD_SHA256, handshake_salt_draft_29, sizeof(handshake_salt_draft_29), cid->cid, cid->len, secret); } else { err = hkdf_extract(GCRY_MD_SHA256, handshake_salt_v1, sizeof(handshake_salt_v1), cid->cid, cid->len, secret); } if (err) { //printf("Failed to extract secrets: %s\n", gcry_strerror(err)); *error = "Failed to extract secrets"; return FALSE; } if (!quic_hkdf_expand_label(GCRY_MD_SHA256, secret, sizeof(secret), "client in", client_initial_secret, HASH_SHA2_256_LENGTH)) { *error = "Key expansion (client) failed"; return FALSE; } if (!quic_hkdf_expand_label(GCRY_MD_SHA256, secret, sizeof(secret), "server in", server_initial_secret, HASH_SHA2_256_LENGTH)) { *error = "Key expansion (server) failed"; return FALSE; } *error = NULL; return TRUE; } static gboolean quic_create_initial_decoders(const quic_cid_t *cid, const gchar **error, quic_info_data_t *quic_info) { unsigned char client_secret[HASH_SHA2_256_LENGTH]; unsigned char server_secret[HASH_SHA2_256_LENGTH]; if (!quic_derive_initial_secrets(cid, client_secret, server_secret, quic_info->version, error)) return -1; // Packet numbers are protected with AES128-CTR, // initial packets are protected with AEAD_AES_128_GCM. if (!quic_ciphers_prepare(&quic_info->client_initial_ciphers, GCRY_MD_SHA256, GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_GCM, client_secret, error)) { return FALSE; } if(!quic_ciphers_prepare(&quic_info->server_initial_ciphers, GCRY_MD_SHA256, GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_GCM, server_secret, error)) { quic_hp_cipher_reset(&quic_info->client_initial_ciphers.hp_cipher); quic_pp_cipher_reset(&quic_info->client_initial_ciphers.pp_cipher); return FALSE; } return TRUE; } static int quic_extract_header(const char *payload, unsigned char *long_packet_type, unsigned int *version, quic_cid_t *dcid, quic_cid_t *scid) { unsigned int offset = 0; unsigned char packet_type = payload[offset]; unsigned char is_long_header = packet_type & 0x80; if (is_long_header) // long header form *long_packet_type = (packet_type & 0x30) >> 4; else // short header form, store dummy value that is not a long packet type. *long_packet_type = QUIC_SHORT_PACKET; offset++; *version = pntoh32((unsigned int *)&payload[offset]); if (is_long_header) { // VN packets don't have any real packet type field, // even if they have a long header: use a dummy value */ if (*version == 0x00000000) *long_packet_type = QUIC_LPT_VER_NEG; // skip version offset += 4; // read DCID and SCID (both are prefixed by a length byte). unsigned char dcil = payload[offset]; offset++; if (dcil && dcil <= QUIC_MAX_CID_LENGTH) { memcpy(dcid->cid, &payload[offset], dcil); dcid->len = dcil; } offset += dcil; unsigned char scil = payload[offset]; offset++; if (scil && scil <= QUIC_MAX_CID_LENGTH) { memcpy(scid->cid, &payload[offset], scil); scid->len = scil; } offset += scil; } else { // Definitely not draft -10, set version to dummy value. *version = 0; // For short headers, the DCID length is unknown and could be 0 or // anything from 1 to 20 bytes. Copy the maximum possible and let the // consumer truncate it as necessary. memcpy(dcid->cid, &payload[offset], QUIC_MAX_CID_LENGTH); dcid->len = QUIC_MAX_CID_LENGTH; offset += QUIC_MAX_CID_LENGTH; } return offset; } int dissect_quic(const char *payload, unsigned int length, unsigned char *out, unsigned int *out_length) { guint offset = 0; quic_packet_info_t quic_packet; quic_info_data_t conn; unsigned char long_packet_type; quic_cid_t dcid = {.len=0}, scid = {.len=0}; guint64 token_length, payload_length; const char *error = NULL; guint8 first_byte = 0; const gboolean from_server = FALSE; quic_ciphers *ciphers = NULL; int ret; memset(&quic_packet, 0, sizeof(quic_packet_info_t)); memset(&conn, 0, sizeof(quic_info_data_t)); ret = quic_extract_header(payload, &long_packet_type, &conn.version, &dcid, &scid); if (ret < 0) { return -1; } if (long_packet_type == QUIC_LPT_INITIAL) { // Create new decryption context based on the Client Connection ID // from the *very first* Client Initial packet. quic_create_initial_decoders(&dcid, &error, &conn); if (!error) { guint32 pkn32 = 0; // PKN is after type(1) + version(4) + DCIL+DCID + SCIL+SCID guint pn_offset = 1 + 4 + 1 + dcid.len + 1 + scid.len; pn_offset += tvb_get_varint(payload, pn_offset, 8, &token_length, ENC_VARINT_QUIC); pn_offset += (guint)token_length; // printf("%d\n", token_length); pn_offset += tvb_get_varint(payload, pn_offset, 8, &payload_length, ENC_VARINT_QUIC); // printf("%d\n", payload_length); // Assume failure unless proven otherwise. ciphers = &conn.client_initial_ciphers; error = "Header deprotection failed"; if (quic_decrypt_header(payload, pn_offset, &ciphers->hp_cipher, GCRY_CIPHER_AES128, &first_byte, &pkn32)) error = NULL; if (!error) { quic_set_full_packet_number(&conn, &quic_packet, from_server, first_byte, pkn32); quic_packet.first_byte = first_byte; } // Payload // skip type(1) + version(4) + DCIL+DCID + SCIL+SCID + len_token_length + token_length + len_payload_length + len_packet_number offset = pn_offset + quic_packet.pkn_len; //quic_process_payload(payload, length, offset, &conn, &quic_packet, from_server, &ciphers->pp_cipher, first_byte, quic_packet.pkn_len); quic_process_payload(payload, payload_length, offset, &conn, &quic_packet, from_server, &ciphers->pp_cipher, first_byte, quic_packet.pkn_len); // Out if (!quic_packet.decryption.error) { memcpy(out, quic_packet.decryption.data, quic_packet.decryption.data_len); *out_length = quic_packet.decryption.data_len; g_free((gpointer)quic_packet.decryption.data); quic_packet.decryption.data = NULL; ret=1; } else { ret=0; } quic_hp_cipher_reset(&conn.client_initial_ciphers.hp_cipher); quic_pp_cipher_reset(&conn.client_initial_ciphers.pp_cipher); quic_hp_cipher_reset(&conn.server_initial_ciphers.hp_cipher); quic_pp_cipher_reset(&conn.server_initial_ciphers.pp_cipher); return ret; } } return 0; }