diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index f65fbb1..e6ec762 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -3,7 +3,7 @@ variables: GIT_STRATEGY: "clone" BUILD_PADDING_PREFIX: /tmp/padding_for_CPACK_RPM_BUILD_SOURCE_DIRS_PREFIX_PREFIX_PREFIX_PREFIX_PREFIX_PREFIX/ INSTALL_PREFIX: "/opt/MESA/lib/" - INSTALL_DEPENDENCY_LIBRARY: libMESA_handle_logger-devel libcjson-devel libMESA_field_stat2-devel sapp-devel framework_env libMESA_prof_load-devel sapp-devel openssl-devel glib2-devel libasan libbreakpad_mini-devel libMESA_htable-devel systemd-devel + INSTALL_DEPENDENCY_LIBRARY: libMESA_handle_logger-devel libcjson-devel libMESA_field_stat2-devel sapp-devel framework_env libMESA_prof_load-devel sapp-devel glib2-devel libasan libbreakpad_mini-devel libMESA_htable-devel systemd-devel stages: - build diff --git a/CMakeLists.txt b/CMakeLists.txt index 82f8f62..374ba8b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -32,21 +32,19 @@ set(CMAKE_INSTALL_PREFIX /home/mesasoft/sapp_run) include_directories(include) include_directories(/opt/MESA/include/MESA/) -include_directories(/usr/include/glib-2.0/) -#include_directories(/usr/include/glib-2.0/include/) -include_directories(/usr/lib64/glib-2.0/include) add_subdirectory(support) file(GLOB SRC "src/*.cpp" ) -set(DEPEND_DYN_LIB ssl crypto MESA_handle_logger) +set(DEPEND_DYN_LIB MESA_handle_logger) # Shared Library Output add_library(quic SHARED ${SRC}) +set_target_properties(quic PROPERTIES LINK_FLAGS "-Wl,--version-script=${PROJECT_SOURCE_DIR}/src/version.map") set_target_properties(quic PROPERTIES PREFIX "") -target_link_libraries(quic ${DNS_DEPEND_DYN_LIB} glib-2.0 pthread -Wl,--whole-archive libgpg-error-static -Wl,--no-whole-archive libgcrypt-static) +target_link_libraries(quic ${DNS_DEPEND_DYN_LIB} pthread -Wl,--no-whole-archive openssl-crypto-static -Wl,--no-whole-archive openssl-ssl-static) set_target_properties(quic PROPERTIES OUTPUT_NAME ${lib_name}) enable_testing() diff --git a/src/Makefile b/src/Makefile deleted file mode 100644 index 351a835..0000000 --- a/src/Makefile +++ /dev/null @@ -1,55 +0,0 @@ -CC = gcc -CCC = g++ -INCLUDES = -I/opt/MESA/include/ -I/home/sjzn/workspace/iquic_ngtcp2/openssl/build/include -LIB = -L./opt/MESA/lib/ -L/home/sjzn/workspace/iquic_ngtcp2/openssl/build/lib -lssl -lcrypto -#CFLAGS = -g3 -Wall -fPIC $(INCLUDES) -#CCCFLAGS = -std=c++11 -g3 -Wall -fPIC $(INCLUDES) -CFLAGS = -g3 -Wall -fPIC -CCCFLAGS = -std=c++11 -g3 -Wall -fPIC -TARGET = quic.so -INF = quic.inf -INSTALL_TARGET=$(TARGET) -LIB_FILE = $(wildcard ../lib/*.a) -SOURCES = $(wildcard *.c) $(wildcard gquic/*.c) -OBJECTS = $(SOURCES:.c=.o) -DEPS = $(SOURCES:.c=.d) - - -all:$(TARGET) -$(TARGET):$(OBJECTS) $(LIB_FILE) - $(CCC) -shared $(CFLAGS) $(OBJECTS) $(LIB) -o $@ - cp $(TARGET) ../bin/ - -%.o:%.c - $(CC) -c -o $@ $(CFLAGS) $< $(INCLUDES) - -%.o:%.cpp - $(CCC) -c -o $@ $(CCCFLAGS) $< $(INCLUDES) - --include $(DEPS) - -clean : - rm -f $(OBJECTS) $(DEPS) $(TARGET) - -help: - @echo "-------OBJECTS--------" $(OBJECTS) - -PLUGIN_PATH=./plug/protocol -CONFLIST_NAME=conflist_protocol.inf -PLUGIN_DIR_NAME=quic -PLUGIN_INF_NAME=quic.inf -PAPP_PATH=/home/sjzn/gitFile/ceiec/sapp - -TARGET_DIR=$(PAPP_PATH)/$(PLUGIN_PATH)/$(PLUGIN_DIR_NAME)/ -INSERT_FILE=$(PAPP_PATH)/$(PLUGIN_PATH)/$(CONFLIST_NAME) -INSERT_CONTENT=$(PLUGIN_PATH)/$(PLUGIN_DIR_NAME)/$(PLUGIN_INF_NAME) -install: - mkdir -p $(TARGET_DIR) - cp -r ../bin/*.inf $(TARGET_DIR) - cp -r ../bin/*.so $(TARGET_DIR) - @ret=`cat $(INSERT_FILE)|grep $(INSERT_CONTENT)|wc -l`;if [ $$ret -eq 0 ];then echo $(INSERT_CONTENT) >>$(INSERT_FILE);fi - -CONF_DIR=$(PAPP_PATH)/conf/ -conf: - mkdir -p $(CONF_DIR) - cp -r ../bin/quic $(CONF_DIR) diff --git a/src/pint.h b/src/pint.h deleted file mode 100644 index 11ff8c9..0000000 --- a/src/pint.h +++ /dev/null @@ -1,213 +0,0 @@ -/* pint.h - * Definitions for extracting and translating integers safely and portably - * via pointers. - * - * Wireshark - Network traffic analyzer - * By Gerald Combs - * Copyright 1998 Gerald Combs - * - * SPDX-License-Identifier: GPL-2.0-or-later - */ - -#ifndef __PINT_H__ -#define __PINT_H__ - -#include - -/* Routines that take a possibly-unaligned pointer to a 16-bit, 24-bit, - * 32-bit, 40-bit, ... 64-bit integral quantity, in a particular byte - * order, and fetch the value and return it in host byte order. - * - * The pntohN() routines fetch big-endian values; the pletohN() routines - * fetch little-endian values. - */ - -static inline guint16 pntoh16(const void *p) -{ - return (guint16)*((const guint8 *)(p)+0)<<8| - (guint16)*((const guint8 *)(p)+1)<<0; -} - -static inline guint32 pntoh24(const void *p) -{ - return (guint32)*((const guint8 *)(p)+0)<<16| - (guint32)*((const guint8 *)(p)+1)<<8| - (guint32)*((const guint8 *)(p)+2)<<0; -} - -static inline guint32 pntoh32(const void *p) -{ - return (guint32)*((const guint8 *)(p)+0)<<24| - (guint32)*((const guint8 *)(p)+1)<<16| - (guint32)*((const guint8 *)(p)+2)<<8| - (guint32)*((const guint8 *)(p)+3)<<0; -} - -static inline guint64 pntoh40(const void *p) -{ - return (guint64)*((const guint8 *)(p)+0)<<32| - (guint64)*((const guint8 *)(p)+1)<<24| - (guint64)*((const guint8 *)(p)+2)<<16| - (guint64)*((const guint8 *)(p)+3)<<8| - (guint64)*((const guint8 *)(p)+4)<<0; -} - -static inline guint64 pntoh48(const void *p) -{ - return (guint64)*((const guint8 *)(p)+0)<<40| - (guint64)*((const guint8 *)(p)+1)<<32| - (guint64)*((const guint8 *)(p)+2)<<24| - (guint64)*((const guint8 *)(p)+3)<<16| - (guint64)*((const guint8 *)(p)+4)<<8| - (guint64)*((const guint8 *)(p)+5)<<0; -} - -static inline guint64 pntoh56(const void *p) -{ - return (guint64)*((const guint8 *)(p)+0)<<48| - (guint64)*((const guint8 *)(p)+1)<<40| - (guint64)*((const guint8 *)(p)+2)<<32| - (guint64)*((const guint8 *)(p)+3)<<24| - (guint64)*((const guint8 *)(p)+4)<<16| - (guint64)*((const guint8 *)(p)+5)<<8| - (guint64)*((const guint8 *)(p)+6)<<0; -} - -static inline guint64 pntoh64(const void *p) -{ - return (guint64)*((const guint8 *)(p)+0)<<56| - (guint64)*((const guint8 *)(p)+1)<<48| - (guint64)*((const guint8 *)(p)+2)<<40| - (guint64)*((const guint8 *)(p)+3)<<32| - (guint64)*((const guint8 *)(p)+4)<<24| - (guint64)*((const guint8 *)(p)+5)<<16| - (guint64)*((const guint8 *)(p)+6)<<8| - (guint64)*((const guint8 *)(p)+7)<<0; -} - -static inline guint16 pletoh16(const void *p) -{ - return (guint16)*((const guint8 *)(p)+1)<<8| - (guint16)*((const guint8 *)(p)+0)<<0; -} - -static inline guint32 pletoh24(const void *p) -{ - return (guint32)*((const guint8 *)(p)+2)<<16| - (guint32)*((const guint8 *)(p)+1)<<8| - (guint32)*((const guint8 *)(p)+0)<<0; -} - -static inline guint32 pletoh32(const void *p) -{ - return (guint32)*((const guint8 *)(p)+3)<<24| - (guint32)*((const guint8 *)(p)+2)<<16| - (guint32)*((const guint8 *)(p)+1)<<8| - (guint32)*((const guint8 *)(p)+0)<<0; -} - -static inline guint64 pletoh40(const void *p) -{ - return (guint64)*((const guint8 *)(p)+4)<<32| - (guint64)*((const guint8 *)(p)+3)<<24| - (guint64)*((const guint8 *)(p)+2)<<16| - (guint64)*((const guint8 *)(p)+1)<<8| - (guint64)*((const guint8 *)(p)+0)<<0; -} - -static inline guint64 pletoh48(const void *p) -{ - return (guint64)*((const guint8 *)(p)+5)<<40| - (guint64)*((const guint8 *)(p)+4)<<32| - (guint64)*((const guint8 *)(p)+3)<<24| - (guint64)*((const guint8 *)(p)+2)<<16| - (guint64)*((const guint8 *)(p)+1)<<8| - (guint64)*((const guint8 *)(p)+0)<<0; -} - -static inline guint64 pletoh56(const void *p) -{ - return (guint64)*((const guint8 *)(p)+6)<<48| - (guint64)*((const guint8 *)(p)+5)<<40| - (guint64)*((const guint8 *)(p)+4)<<32| - (guint64)*((const guint8 *)(p)+3)<<24| - (guint64)*((const guint8 *)(p)+2)<<16| - (guint64)*((const guint8 *)(p)+1)<<8| - (guint64)*((const guint8 *)(p)+0)<<0; -} - -static inline guint64 pletoh64(const void *p) -{ - return (guint64)*((const guint8 *)(p)+7)<<56| - (guint64)*((const guint8 *)(p)+6)<<48| - (guint64)*((const guint8 *)(p)+5)<<40| - (guint64)*((const guint8 *)(p)+4)<<32| - (guint64)*((const guint8 *)(p)+3)<<24| - (guint64)*((const guint8 *)(p)+2)<<16| - (guint64)*((const guint8 *)(p)+1)<<8| - (guint64)*((const guint8 *)(p)+0)<<0; -} -/* Pointer routines to put items out in a particular byte order. - * These will work regardless of the byte alignment of the pointer. - */ - -static inline void phton16(guint8 *p, guint16 v) -{ - p[0] = (guint8)(v >> 8); - p[1] = (guint8)(v >> 0); -} - -static inline void phton32(guint8 *p, guint32 v) -{ - p[0] = (guint8)(v >> 24); - p[1] = (guint8)(v >> 16); - p[2] = (guint8)(v >> 8); - p[3] = (guint8)(v >> 0); -} - -static inline void phton64(guint8 *p, guint64 v) { - p[0] = (guint8)(v >> 56); - p[1] = (guint8)(v >> 48); - p[2] = (guint8)(v >> 40); - p[3] = (guint8)(v >> 32); - p[4] = (guint8)(v >> 24); - p[5] = (guint8)(v >> 16); - p[6] = (guint8)(v >> 8); - p[7] = (guint8)(v >> 0); -} - -static inline void phtole32(guint8 *p, guint32 v) { - p[0] = (guint8)(v >> 0); - p[1] = (guint8)(v >> 8); - p[2] = (guint8)(v >> 16); - p[3] = (guint8)(v >> 24); -} - -static inline void phtole64(guint8 *p, guint64 v) { - p[0] = (guint8)(v >> 0); - p[1] = (guint8)(v >> 8); - p[2] = (guint8)(v >> 16); - p[3] = (guint8)(v >> 24); - p[4] = (guint8)(v >> 32); - p[5] = (guint8)(v >> 40); - p[6] = (guint8)(v >> 48); - p[7] = (guint8)(v >> 56); -} - -/* Subtract two guint32s with respect to wraparound */ -#define guint32_wraparound_diff(higher, lower) ((higher>lower)?(higher-lower):(higher+0xffffffff-lower+1)) - -#endif /* PINT_H */ - -/* - * Editor modelines - https://www.wireshark.org/tools/modelines.html - * - * Local Variables: - * c-basic-offset: 4 - * tab-width: 8 - * indent-tabs-mode: nil - * End: - * - * ex: set shiftwidth=4 tabstop=8 expandtab: - * :indentSize=4:tabSize=8:noTabs=true: - */ diff --git a/src/quic_deprotection.cpp b/src/quic_deprotection.cpp index 861d947..923a395 100644 --- a/src/quic_deprotection.cpp +++ b/src/quic_deprotection.cpp @@ -1,549 +1,141 @@ -/** - * parser-quic.c - * - * Created on 2020-11-26 - * @author: qyc - * - * @explain: QUIC解析 - */ -#include -#include -#include - -#include "pint.h" -#include "gcrypt.h" #include "quic_deprotection.h" -#include "quic_deprotection_wsgcrypt.h" -#include "quic_deprotection_utils.h" -// #define DEBUG_PARSER_QUIC +#include +#include +#include +#include -int gcry_init() +#define QUIC_IV_LEN 12 /* RFC 5116, 5.1 and RFC 8439, 2.3 for all supported ciphers */ +#define QUIC_HP_LEN 5 /* RFC 9001, 5.4.1. Header Protection Application: 5-byte mask */ + +#define QUIC_MAX_CID_LEN 20 +#define QUIC_AES_128_KEY_LEN 16 +#define QUIC_MIN_INITIAL_SIZE 1200 +#define QUIC_UNSET_PN (uint64_t) - 1 + +/* + * RFC 9000, 17.2. Long Header Packets + * 17.3. Short Header Packets + * QUIC flags in first byte + */ +#define QUIC_PKT_LONG 0x80 /* header form */ +#define QUIC_PKT_FIXED_BIT 0x40 +#define QUIC_PKT_TYPE 0x30 /* in long packet */ +#define QUIC_PKT_KPHASE 0x04 /* in short packet */ + +#define quic_pkt_header_is_long(flags) ((flags)&QUIC_PKT_LONG) +#define quic_pkt_header_is_short(flags) (((flags)&QUIC_PKT_LONG) == 0) +#define quic_pkt_hp_mask(flags) (quic_pkt_header_is_long(flags) ? 0x0F : 0x1F) +#define quic_pkt_rb_mask(flags) (quic_pkt_header_is_long(flags) ? 0x0C : 0x18) + +/* Long packet types */ +#define quic_pkt_level_is_initial(flags) (((flags)&QUIC_PKT_TYPE) == 0x00) +#define quic_pkt_level_is_zrtt(flags) (((flags)&QUIC_PKT_TYPE) == 0x10) +#define quic_pkt_level_is_handshake(flags) (((flags)&QUIC_PKT_TYPE) == 0x20) + +/* MAX MIN */ +#define MAX(val1, val2) ((val1 < val2) ? (val2) : (val1)) +#define MIN(val1, val2) ((val1 > val2) ? (val2) : (val1)) + +#define quic_pkt_level_to_name(lvl) \ + (lvl == ssl_encryption_application) ? "application" \ + : (lvl == ssl_encryption_initial) ? "initial" \ + : (lvl == ssl_encryption_handshake) ? "handshake" \ + : "early" + +typedef struct { - gcry_check_version("1.8.7"); - //gcry_control(GCRYCTL_SET_THREAD_CBS,&gcry_threads_pthread); + const EVP_CIPHER *c; + const EVP_CIPHER *hp; + const EVP_MD *d; +} quic_ciphers_t; + +/////////////////////////////////////////////////////////////////////////////// +// QUIC version +/////////////////////////////////////////////////////////////////////////////// + +#define QUIC_NVERSIONS (sizeof(quic_version_vals) / sizeof(quic_version_vals[0])) + +const quic_str_t quic_version_vals[] = { + {0x00000000, (u_char *)"Version Negotiation"}, + {0x00000001, (u_char *)"1"}, + /* Versions QXXX < Q050 are dissected by Wireshark as GQUIC and not as QUIC. + Nonetheless, some implementations report these values in "Version Negotiation" + packets, so decode these fields */ + {0x51303433, (u_char *)"Google Q043"}, + {0x51303434, (u_char *)"Google Q044"}, + {0x51303436, (u_char *)"Google Q046"}, + {0x51303530, (u_char *)"Google Q050"}, + {0x54303530, (u_char *)"Google T050"}, + {0x54303531, (u_char *)"Google T051"}, + {0xfaceb001, (u_char *)"Facebook mvfst (draft-22)"}, + {0xfaceb002, (u_char *)"Facebook mvfst (draft-27)"}, + {0xfaceb00e, (u_char *)"Facebook mvfst (Experimental)"}, + {0xff000004, (u_char *)"draft-04"}, + {0xff000005, (u_char *)"draft-05"}, + {0xff000006, (u_char *)"draft-06"}, + {0xff000007, (u_char *)"draft-07"}, + {0xff000008, (u_char *)"draft-08"}, + {0xff000009, (u_char *)"draft-09"}, + {0xff00000a, (u_char *)"draft-10"}, + {0xff00000b, (u_char *)"draft-11"}, + {0xff00000c, (u_char *)"draft-12"}, + {0xff00000d, (u_char *)"draft-13"}, + {0xff00000e, (u_char *)"draft-14"}, + {0xff00000f, (u_char *)"draft-15"}, + {0xff000010, (u_char *)"draft-16"}, + {0xff000011, (u_char *)"draft-17"}, + {0xff000012, (u_char *)"draft-18"}, + {0xff000013, (u_char *)"draft-19"}, + {0xff000014, (u_char *)"draft-20"}, + {0xff000015, (u_char *)"draft-21"}, + {0xff000016, (u_char *)"draft-22"}, + {0xff000017, (u_char *)"draft-23"}, + {0xff000018, (u_char *)"draft-24"}, + {0xff000019, (u_char *)"draft-25"}, + {0xff00001a, (u_char *)"draft-26"}, + {0xff00001b, (u_char *)"draft-27"}, + {0xff00001c, (u_char *)"draft-28"}, + {0xff00001d, (u_char *)"draft-29"}, + {0xff00001e, (u_char *)"draft-30"}, + {0xff00001f, (u_char *)"draft-31"}, + {0xff000020, (u_char *)"draft-32"}, + {0, NULL}}; + +static int quic_version_is_supported(uint32_t version) +{ + unsigned int i = 0; + for (i = 0; i < QUIC_NVERSIONS; i++) + { + if (quic_version_vals[i].len == version) + { + return 1; + } + } + 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) +// Returns the QUIC draft version or 0 if not applicable. +static inline uint8_t quic_draft_version(uint32_t version) { - 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; + if ((version >> 8) == 0xff0000) + return (uint8_t)version; - 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 || buffer_length >1500) - { - g_free(header); - header=NULL; - *error = (const guchar *)"Decryption not possible, ciphertext is too short or too long"; - return; - } - buffer = (guint8 *)g_malloc(buffer_length); - memcpy(buffer, payload + header_length, buffer_length); - memcpy(atag, payload + header_length + buffer_length, 16); + // Facebook mvfst, based on draft -22. + if (version == 0xfaceb001) + return 22; - 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); + // Facebook mvfst, based on draft -27. + if (version == 0xfaceb002 || version == 0xfaceb00e) + return 27; - gcry_cipher_reset(pp_cipher->pp_cipher); - err = gcry_cipher_setiv(pp_cipher->pp_cipher, nonce, TLS13_AEAD_NONCE_LENGTH); - if (err) - { - g_free(header); - header=NULL; - g_free(buffer); - buffer=NULL; - *error = (const guchar *)"Decryption (setiv) failed"; - return; - } + // 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; - // associated data (A) is the contents of QUIC header - err = gcry_cipher_authenticate(pp_cipher->pp_cipher, header, header_length); - if (err) - { - g_free(header); - header=NULL; - g_free(buffer); - buffer=NULL; - *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) - { - g_free(header); - header=NULL; - g_free(buffer); - buffer=NULL; - //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) - { - g_free(header); - header=NULL; - g_free(buffer); - buffer=NULL; - //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" @@ -552,327 +144,986 @@ static inline guint8 quic_draft_version(guint32 version) { * 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; + if ((version & 0x0F0F0F0F) == 0x0a0a0a0a) + return 29; - return 0; + 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) +static inline uint8_t quic_draft_is_max(uint32_t version, uint8_t max_version) { - /* - * 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 + uint8_t draft_version = quic_draft_version(version); + return draft_version && draft_version <= max_version; +} + +/////////////////////////////////////////////////////////////////////////////// +// quic_parse_packet_header() +/////////////////////////////////////////////////////////////////////////////// + +static inline u_char *quic_parse_uint8(const u_char *pos, const u_char *end, uint8_t *out) +{ + if ((size_t)(end - pos) < 1) + { + return NULL; + } + + *out = *pos; + return (u_char *)pos + 1; +} + +static inline u_char *quic_parse_int(const u_char *pos, const u_char *end, uint64_t *out) +{ + u_char *p; + uint64_t value; + int len; + + if (pos >= end) + { + return NULL; + } + + p = (u_char *)pos; + len = 1 << (*p >> 6); + + value = *p++ & 0x3f; + + if ((size_t)(end - p) < (size_t)(len - 1)) + { + return NULL; + } + + while (--len) + { + value = (value << 8) + *p++; + } + + *out = value; + return p; +} + +static inline u_char *quic_parse_uint32(const u_char *pos, const u_char *end, uint32_t *out) +{ + if ((size_t)(end - pos) < sizeof(uint32_t)) + { + return NULL; + } + + *out = ((uint32_t)(pos)[0] << 24 | (pos)[1] << 16 | (pos)[2] << 8 | (pos)[3]); + return (u_char *)pos + sizeof(uint32_t); +} + +static inline u_char *quic_parse_nbytes(const u_char *pos, const u_char *end, size_t len, u_char **out) +{ + if ((size_t)(end - pos) < len) + { + return NULL; + } + + *out = (u_char *)pos; + return (u_char *)pos + len; +} + +static int quic_parse_short_header(quic_dpt_t *dpt, size_t dcid_len) +{ + u_char *p = dpt->pos; + u_char *end = dpt->data + dpt->len; + + if (!(dpt->flags & QUIC_PKT_FIXED_BIT)) + { + LOG_WARN("QUIC fixed bit is not set"); + return -1; + } + + p = quic_parse_nbytes(p, end, dcid_len, &dpt->dcid.data); + if (p == NULL) + { + LOG_WARN("QUIC packet is too small to read dcid"); + return -1; + } + dpt->dcid.len = dcid_len; + + dpt->pos = p; + return 0; +} + +static int quic_parse_long_header(quic_dpt_t *dpt) +{ + uint8_t idlen; + + u_char *p = dpt->pos; + u_char *end = dpt->data + dpt->len; + + // Parser Version + p = quic_parse_uint32(p, end, &dpt->version); + if (p == NULL) + { + LOG_WARN("QUIC packet is too small to read version"); + return -1; + } + + if (!(dpt->flags & QUIC_PKT_FIXED_BIT)) + { + LOG_WARN("QUIC fixed bit is not set"); + return -1; + } + + // Parser DCID + p = quic_parse_uint8(p, end, &idlen); + if (p == NULL) + { + LOG_WARN("QUIC packet is too small to read dcid len"); + return -1; + } + if (idlen > QUIC_MAX_CID_LEN) + { + LOG_WARN("QUIC packet dcid is too long"); + return -1; + } + dpt->dcid.len = idlen; + + p = quic_parse_nbytes(p, end, idlen, &dpt->dcid.data); + if (p == NULL) + { + LOG_WARN("QUIC packet is too small to read dcid"); + return -1; + } + + // Parser SCID + p = quic_parse_uint8(p, end, &idlen); + if (p == NULL) + { + LOG_WARN("QUIC packet is too small to read scid len"); + return -1; + } + if (idlen > QUIC_MAX_CID_LEN) + { + LOG_WARN("QUIC packet scid is too long"); + return -1; + } + + dpt->scid.len = idlen; + + p = quic_parse_nbytes(p, end, idlen, &dpt->scid.data); + if (p == NULL) + { + LOG_WARN("QUIC packet is too small to read scid"); + return -1; + } + + dpt->pos = p; + return 0; +} + +static int quic_parse_long_header_v1(quic_dpt_t *dpt) +{ + uint64_t varint; + + u_char *p = dpt->pos; + u_char *end = (u_char *)dpt->data + dpt->len; + + // ssl_encryption_initial + if (quic_pkt_level_is_initial(dpt->flags)) + { + dpt->level = ssl_encryption_initial; + if (dpt->len < QUIC_MIN_INITIAL_SIZE) + { + LOG_WARN("QUIC UDP datagram is too small for initial packet"); + return -1; + } + + // Parse Token + p = quic_parse_int(p, end, &varint); + if (p == NULL) + { + LOG_WARN("QUIC failed to parse token length"); + return -1; + } + dpt->token.len = varint; + + p = quic_parse_nbytes(p, end, dpt->token.len, &dpt->token.data); + if (p == NULL) + { + LOG_WARN("QUIC packet too small to read token data"); + return -1; + } + } + // ssl_encryption_early_data + else if (quic_pkt_level_is_zrtt(dpt->flags)) + { + dpt->level = ssl_encryption_early_data; + } + // ssl_encryption_handshake + else if (quic_pkt_level_is_handshake(dpt->flags)) + { + dpt->level = ssl_encryption_handshake; + } + else + { + LOG_WARN("QUIC bad packet type"); + return -1; + } + + // Parse Packet Length + p = quic_parse_int(p, end, &varint); + if (p == NULL) + { + LOG_WARN("QUIC bad packet length"); + return -1; + } + dpt->pkt_len = varint; + + if (varint > (uint64_t)((dpt->data + dpt->len) - p)) + { + LOG_WARN("QUIC truncated packet with level %s", quic_pkt_level_to_name(dpt->level)); + return -1; + } + + dpt->pos = p; + dpt->len = p + varint - dpt->data; + + return 0; +} + +static int quic_parse_packet_header(quic_dpt_t *dpt) +{ + if (quic_pkt_header_is_short(dpt->flags)) + { + dpt->header_type = SHORT; + dpt->level = ssl_encryption_application; + + // Parser DCID + if (quic_parse_short_header(dpt, QUIC_MAX_CID_LEN) != 0) + { + return -1; + } + + return 0; + } + + dpt->header_type = LONG; + + // Parser Version, DCID, SCID + if (quic_parse_long_header(dpt) != 0) + { + return -1; + } + + // Check QUIC Version + if (!quic_version_is_supported(dpt->version)) + { + LOG_WARN("QUIC version %08" PRIx32 "unsupport", dpt->version); + return -1; + } + + // Parser Level, Token, Packet Length + if (quic_parse_long_header_v1(dpt) != 0) + { + return -1; + } + + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// +// quic_keys_set_initial_secret() +/////////////////////////////////////////////////////////////////////////////// + +static int hkdf_expand(quic_str_t *out, const EVP_MD *digest, const quic_str_t *prk, const quic_str_t *info) +{ + EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL); + if (pctx == NULL) + { + LOG_ERROR("EVP_PKEY_CTX_new_id() failed"); + return -1; + } + + if (EVP_PKEY_derive_init(pctx) <= 0) + { + LOG_ERROR("EVP_PKEY_derive_init() failed"); + goto failed; + } + + if (EVP_PKEY_CTX_hkdf_mode(pctx, EVP_PKEY_HKDEF_MODE_EXPAND_ONLY) <= 0) + { + LOG_ERROR("EVP_PKEY_CTX_hkdf_mode() failed"); + goto failed; + } + + if (EVP_PKEY_CTX_set_hkdf_md(pctx, digest) <= 0) + { + LOG_ERROR("EVP_PKEY_CTX_set_hkdf_md() failed"); + goto failed; + } + + if (EVP_PKEY_CTX_set1_hkdf_key(pctx, prk->data, prk->len) <= 0) + { + LOG_ERROR("EVP_PKEY_CTX_set1_hkdf_key() failed"); + goto failed; + } + + if (EVP_PKEY_CTX_add1_hkdf_info(pctx, info->data, info->len) <= 0) + { + LOG_ERROR("EVP_PKEY_CTX_add1_hkdf_info() failed"); + goto failed; + } + + if (EVP_PKEY_derive(pctx, out->data, &(out->len)) <= 0) + { + LOG_ERROR("EVP_PKEY_derive() failed"); + goto failed; + } + + EVP_PKEY_CTX_free(pctx); + return 0; + +failed: + + EVP_PKEY_CTX_free(pctx); + return -1; +} + +static int quic_hkdf_expand(const EVP_MD *digest, quic_str_t *out, const quic_str_t *label, const quic_str_t *prk) +{ + uint8_t info_buf[20]; + info_buf[0] = 0; + info_buf[1] = out->len; + info_buf[2] = label->len; + + uint8_t *p = (u_char *)memcpy(&info_buf[3], label->data, label->len) + label->len; + *p = '\0'; + + quic_str_t info; + info.len = 2 + 1 + label->len + 1; + info.data = info_buf; + + if (hkdf_expand(out, digest, prk, &info) != 0) + { + return -1; + } + + return 0; +} + +static int hkdf_extract(quic_str_t *out, const EVP_MD *digest, const quic_str_t *secret, const quic_str_t *initial_salt) +{ + EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL); + if (pctx == NULL) + { + LOG_ERROR("EVP_PKEY_CTX_new_id() failed"); + return -1; + } + + if (EVP_PKEY_derive_init(pctx) <= 0) + { + LOG_ERROR("EVP_PKEY_derive_init() failed"); + goto failed; + } + + if (EVP_PKEY_CTX_hkdf_mode(pctx, EVP_PKEY_HKDEF_MODE_EXTRACT_ONLY) <= 0) + { + LOG_ERROR("EVP_PKEY_CTX_hkdf_mode() failed"); + goto failed; + } + + if (EVP_PKEY_CTX_set_hkdf_md(pctx, digest) <= 0) + { + LOG_ERROR("EVP_PKEY_CTX_set_hkdf_md() failed"); + goto failed; + } + + if (EVP_PKEY_CTX_set1_hkdf_key(pctx, secret->data, secret->len) <= 0) + { + LOG_ERROR("EVP_PKEY_CTX_set1_hkdf_key() failed"); + goto failed; + } + + if (EVP_PKEY_CTX_set1_hkdf_salt(pctx, initial_salt->data, initial_salt->len) <= 0) + { + LOG_ERROR("EVP_PKEY_CTX_set1_hkdf_salt() failed"); + goto failed; + } + + if (EVP_PKEY_derive(pctx, out->data, &(out->len)) <= 0) + { + LOG_ERROR("EVP_PKEY_derive() failed"); + goto failed; + } + + EVP_PKEY_CTX_free(pctx); + return 0; + +failed: + + EVP_PKEY_CTX_free(pctx); + return -1; +} + +static int quic_keys_set_initial_secret(quic_secret_t *client_secret, const quic_str_t *dcid, uint32_t version) +{ + unsigned int i; + const quic_str_t initial_salt_v1 = quic_string( + "\x38\x76\x2c\xf7\xf5\x59\x34\xb3\x4d\x17\x9a\xe6\xa4\xc8\x0c\xad\xcc\xbb\x7f\x0a"); + const quic_str_t initial_salt_draft_22 = quic_string( + "\x7f\xbc\xdb\x0e\x7c\x66\xbb\xe9\x19\x3a\x96\xcd\x21\x51\x9e\xbd\x7a\x02\x64\x4a"); + const quic_str_t initial_salt_draft_23 = quic_string( + "\xc3\xee\xf7\x12\xc7\x2e\xbb\x5a\x11\xa7\xd2\x43\x2b\xb4\x63\x65\xbe\xf9\xf5\x02"); + const quic_str_t initial_salt_draft_29 = quic_string( + "\xaf\xbf\xec\x28\x99\x93\xd2\x4c\x9e\x97\x86\xf1\x9c\x61\x11\xe0\x43\x90\xa8\x99"); + const quic_str_t initial_salt_draft_q50 = quic_string( + "\x50\x45\x74\xEF\xD0\x66\xFE\x2F\x9D\x94\x5C\xFC\xDB\xD3\xA7\xF0\xD3\xB5\x6B\x45"); + const quic_str_t initial_salt_draft_t50 = quic_string( + "\x7f\xf5\x79\xe5\xac\xd0\x72\x91\x55\x80\x30\x4c\x43\xa2\x36\x7c\x60\x48\x83\x10"); + const quic_str_t initial_salt_draft_t51 = quic_string( + "\x7a\x4e\xde\xf4\xe7\xcc\xee\x5f\xa4\x50\x6c\x19\x12\x4f\xc8\xcc\xda\x6e\x03\x3d"); + + const quic_str_t *initial_salt; + if (version == 0x51303530) + { + initial_salt = &initial_salt_draft_q50; + } + else if (version == 0x54303530) + { + initial_salt = &initial_salt_draft_t50; + } + else if (version == 0x54303531) + { + initial_salt = &initial_salt_draft_t51; + } + else if (quic_draft_is_max(version, 22)) + { + initial_salt = &initial_salt_draft_22; + } + else if (quic_draft_is_max(version, 28)) + { + initial_salt = &initial_salt_draft_23; + } + else if (quic_draft_is_max(version, 32)) + { + initial_salt = &initial_salt_draft_29; + } + else + { + initial_salt = &initial_salt_v1; + } + + /* + * RFC 9001, section 5. Packet Protection + * + * Initial packets use AEAD_AES_128_GCM. The hash function + * for HKDF when deriving initial secrets and keys is SHA-256. + */ + const EVP_MD *digest = EVP_sha256(); + + uint8_t is[SHA256_DIGEST_LENGTH] = {0}; + quic_str_t initial_secret; + initial_secret.data = is; + initial_secret.len = SHA256_DIGEST_LENGTH; + + // Use dcid and initial_salt get initial_secret + if (hkdf_extract(&initial_secret, digest, dcid, initial_salt) != 0) + { + return -1; + } + + struct + { + quic_str_t label; + quic_str_t *key; + quic_str_t *prk; + } seq[] = { + /* labels per RFC 9001, 5.1. Packet Protection Keys */ + {quic_string("tls13 client in"), &client_secret->secret, &initial_secret}, + {quic_string("tls13 quic key"), &client_secret->key, &client_secret->secret}, + {quic_string("tls13 quic iv"), &client_secret->iv, &client_secret->secret}, + {quic_string("tls13 quic hp"), &client_secret->hp, &client_secret->secret}, }; - 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]; + for (i = 0; i < (sizeof(seq) / sizeof(seq[0])); i++) + { + if (quic_hkdf_expand(digest, seq[i].key, &seq[i].label, seq[i].prk) != 0) + { + return -1; + } + } - 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; + return 0; } -static gboolean quic_create_initial_decoders(const quic_cid_t *cid, const gchar **error, quic_info_data_t *quic_info) +/////////////////////////////////////////////////////////////////////////////// +// quic_deprotection_packet() +/////////////////////////////////////////////////////////////////////////////// + +static int quic_deprotection_header(const EVP_CIPHER *cipher, const quic_secret_t *secret, u_char *out, const u_char *in) { - unsigned char client_secret[HASH_SHA2_256_LENGTH]; - unsigned char server_secret[HASH_SHA2_256_LENGTH]; + int outlen; + u_char zero[QUIC_HP_LEN] = {0}; - if (!quic_derive_initial_secrets(cid, client_secret, server_secret, quic_info->version, error)) - return -1; + EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new(); + if (ctx == NULL) + { + 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); + if (EVP_EncryptInit_ex(ctx, cipher, NULL, secret->hp.data, in) != 1) + { + LOG_ERROR("EVP_EncryptInit_ex() failed"); + goto failed; + } - return FALSE; - } - - return TRUE; + if (!EVP_EncryptUpdate(ctx, out, &outlen, zero, QUIC_HP_LEN)) + { + LOG_ERROR("EVP_EncryptUpdate() failed"); + goto failed; + } + + if (!EVP_EncryptFinal_ex(ctx, out + QUIC_HP_LEN, &outlen)) + { + LOG_ERROR("EVP_EncryptFinal_ex() failed"); + goto failed; + } + + EVP_CIPHER_CTX_free(ctx); + return 0; + +failed: + + EVP_CIPHER_CTX_free(ctx); + return -1; } -static int quic_extract_header(const char *payload, unsigned int payload_len, unsigned char *long_packet_type, unsigned int *version, quic_cid_t *dcid, quic_cid_t *scid) +static uint64_t quic_deprotection_pktnum(u_char **pos, int len, const u_char *mask, uint64_t *largest_pkt_num) { - 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++; - if(offset+6>=(int)payload_len) //verion_lenght: 4 ,scid_length_flag: 1 ,dcid_length_flag: 1 - { - return -1; - } - *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(offset+dcil+1>=(int)payload_len) //scid_length_flag: +1 - { - return -1; - } - - if (dcil && dcil <= QUIC_MAX_CID_LENGTH) { - memcpy(dcid->cid, &payload[offset], dcil); - dcid->len = dcil; - } - offset += dcil; + u_char *p; + uint64_t truncated_pn, expected_pn, candidate_pn; + uint64_t pn_nbits, pn_win, pn_hwin, pn_mask; - unsigned char scil = payload[offset]; - offset++; - if(offset+scil>=(int)payload_len) - { - return -1; - } - 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. - if(offset+QUIC_MAX_CID_LENGTH>=(int)payload_len) - { - return -1; - } - memcpy(dcid->cid, &payload[offset], QUIC_MAX_CID_LENGTH); - dcid->len = QUIC_MAX_CID_LENGTH; - offset += QUIC_MAX_CID_LENGTH; - } - - return offset; + pn_nbits = MIN(len * 8, 62); + + p = *pos; + truncated_pn = *p++ ^ *mask++; + + while (--len) + { + truncated_pn = (truncated_pn << 8) + (*p++ ^ *mask++); + } + + *pos = p; + + expected_pn = *largest_pkt_num + 1; + pn_win = 1ULL << pn_nbits; + pn_hwin = pn_win / 2; + pn_mask = pn_win - 1; + + candidate_pn = (expected_pn & ~pn_mask) | truncated_pn; + + if ((int64_t)candidate_pn <= (int64_t)(expected_pn - pn_hwin) && candidate_pn < (1ULL << 62) - pn_win) + { + candidate_pn += pn_win; + } + else if (candidate_pn > expected_pn + pn_hwin && candidate_pn >= pn_win) + { + candidate_pn -= pn_win; + } + + *largest_pkt_num = MAX((int64_t)*largest_pkt_num, (int64_t)candidate_pn); + + return candidate_pn; } -static void free_quic_cipher(quic_ciphers *initial_ciphers) +static void quic_deprotection_nonce(u_char *nonce, size_t len, uint64_t pkt_num) { - if(initial_ciphers!=NULL) - { - quic_hp_cipher_reset(&(initial_ciphers->hp_cipher)); - quic_pp_cipher_reset(&(initial_ciphers->pp_cipher)); - } - + nonce[len - 4] ^= (pkt_num & 0xff000000) >> 24; + nonce[len - 3] ^= (pkt_num & 0x00ff0000) >> 16; + nonce[len - 2] ^= (pkt_num & 0x0000ff00) >> 8; + nonce[len - 1] ^= (pkt_num & 0x000000ff); } -int quic_deprotection(const char *payload, unsigned int length, unsigned char *out, unsigned int *out_length) +static int quic_deprotection_payload(const EVP_CIPHER *cipher, const quic_secret_t *secret, quic_str_t *out, const u_char *nonce, const quic_str_t *in, const quic_str_t *ad) { - 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, out_len; + int len; + u_char *tag; + EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new(); + if (ctx == NULL) + { + LOG_ERROR("EVP_CIPHER_CTX_new() failed"); + return -1; + } - memset(&quic_packet, 0, sizeof(quic_packet_info_t)); - memset(&conn, 0, sizeof(quic_info_data_t)); + if (EVP_DecryptInit_ex(ctx, cipher, NULL, NULL, NULL) != 1) + { + EVP_CIPHER_CTX_free(ctx); + LOG_ERROR("EVP_DecryptInit_ex() failed"); + return -1; + } - ret = quic_extract_header(payload, length, &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; - if(pn_offset+8>=length) //tvb_get_varint max: 8 - { - free_quic_cipher(&conn.client_initial_ciphers); - free_quic_cipher(&conn.server_initial_ciphers); - return 0; - } - pn_offset += tvb_get_varint(payload, pn_offset, 8, &token_length, ENC_VARINT_QUIC); - pn_offset += (guint)token_length; - if(pn_offset+8>=length) //tvb_get_varint max: 8 - { - free_quic_cipher(&conn.client_initial_ciphers); - free_quic_cipher(&conn.server_initial_ciphers); - return 0; - } + if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, secret->iv.len, NULL) == 0) + { + EVP_CIPHER_CTX_free(ctx); + LOG_ERROR("EVP_CIPHER_CTX_ctrl(EVP_CTRL_GCM_SET_IVLEN) failed"); + return -1; + } - pn_offset += tvb_get_varint(payload, pn_offset, 8, &payload_length, ENC_VARINT_QUIC); - if(payload_length==0 || payload_length >length || pn_offset>=length) - { - quic_packet.decryption.error = (const guchar*)"Payload length is too small or too long"; - } - else - { - // 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; - } + if (EVP_DecryptInit_ex(ctx, NULL, NULL, secret->key.data, nonce) != 1) + { + EVP_CIPHER_CTX_free(ctx); + LOG_ERROR("EVP_DecryptInit_ex() failed"); + return -1; + } - // 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) - { - out_len=MIN(quic_packet.decryption.data_len, *out_length); - memcpy(out, quic_packet.decryption.data, out_len); - *out_length = out_len; + if (EVP_DecryptUpdate(ctx, NULL, &len, ad->data, ad->len) != 1) + { + EVP_CIPHER_CTX_free(ctx); + LOG_ERROR("EVP_DecryptUpdate() failed"); + return -1; + } - g_free((gpointer)quic_packet.decryption.data); - quic_packet.decryption.data = NULL; + if (EVP_DecryptUpdate(ctx, out->data, &len, in->data, in->len - EVP_GCM_TLS_TAG_LEN) != 1) + { + EVP_CIPHER_CTX_free(ctx); + LOG_ERROR("EVP_DecryptUpdate() failed"); + return -1; + } - ret=1; - } - else - { - ret=0; - } + out->len = len; + tag = in->data + in->len - EVP_GCM_TLS_TAG_LEN; - free_quic_cipher(&conn.client_initial_ciphers); - free_quic_cipher(&conn.server_initial_ciphers); + if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, EVP_GCM_TLS_TAG_LEN, tag) == 0) + { + EVP_CIPHER_CTX_free(ctx); + LOG_ERROR("EVP_CIPHER_CTX_ctrl(EVP_CTRL_GCM_SET_TAG) failed"); + return -1; + } - return ret; - } - } + if (EVP_DecryptFinal_ex(ctx, out->data + len, &len) <= 0) + { + EVP_CIPHER_CTX_free(ctx); + LOG_ERROR("EVP_DecryptFinal_ex failed"); + return -1; + } - return 0; + out->len += len; + EVP_CIPHER_CTX_free(ctx); + + return 0; } +static int quic_deprotection_packet(quic_dpt_t *dpt) +{ + u_char *p, *sample; + size_t len; + uint64_t pkt_num, lpn; + int pnl, rc, key_phase; + quic_str_t in, ad; + uint8_t nonce[QUIC_IV_LEN] = {0}, mask[QUIC_HP_LEN] = {0}; + + // Init ciphers, only process the quic package whose level is init, so only use AES_128_GCM_SHA256 + quic_ciphers_t ciphers; + ciphers.c = EVP_aes_128_gcm(); + ciphers.hp = EVP_aes_128_ctr(); + ciphers.d = EVP_sha256(); + + // 使用 client_secret secrets + quic_secret_t *secret = &dpt->client_secret; + + p = dpt->pos; + len = dpt->data + dpt->len - p; + + /* + * RFC 9001, 5.4.2. Header Protection Sample + * 5.4.3. AES-Based Header Protection + * 5.4.4. ChaCha20-Based Header Protection + * + * the Packet Number field is assumed to be 4 bytes long + * AES and ChaCha20 algorithms sample 16 bytes + */ + + if (len < EVP_GCM_TLS_TAG_LEN + 4) + { + LOG_WARN("QUIC payload length %zu too small", len); + return -1; + } + + sample = p + 4; + + /****************************************************** + * header protection + ******************************************************/ + + // Use the ciphers.hp algorithm and the secret.HP to encrypt the data in the sample and store it in the mask + if (quic_deprotection_header(ciphers.hp, secret, mask, sample) != 0) + { + return -1; + } + + // Parse Flags After Decode + dpt->flags ^= mask[0] & quic_pkt_hp_mask(dpt->flags); + + if (quic_pkt_header_is_short(dpt->flags)) + { + key_phase = (dpt->flags & QUIC_PKT_KPHASE) != 0; + + if (key_phase != dpt->key_phase) + { + // TODO + LOG_WARN("key need update !!!!"); + } + } + + lpn = dpt->largest_pkt_num; + + // RFC pn_length = (packet[0] & 0x03) + 1 + // Parser Packet Number length + pnl = (dpt->flags & 0x03) + 1; + + // Parser Packet number + pkt_num = quic_deprotection_pktnum(&p, pnl, &mask[1], &lpn); + dpt->pkt_num = pkt_num; + + /****************************************************** + * packet protection + ******************************************************/ + + in.data = p; + in.len = len - pnl; + + ad.len = p - dpt->data; + ad.data = dpt->plaintext; + + memcpy(ad.data, dpt->data, ad.len); + ad.data[0] = dpt->flags; + + do + { + ad.data[ad.len - pnl] = pkt_num >> (8 * (pnl - 1)) % 256; + } while (--pnl); + + memcpy(nonce, secret->iv.data, secret->iv.len); + quic_deprotection_nonce(nonce, sizeof(nonce), pkt_num); + + dpt->payload.len = in.len - EVP_GCM_TLS_TAG_LEN; + dpt->payload.data = dpt->plaintext + ad.len; + + // Parser payload + rc = quic_deprotection_payload(ciphers.c, secret, &dpt->payload, nonce, &in, &ad); + if (rc != 0) + { + return -1; + } + + if (dpt->payload.len == 0) + { + /* + * RFC 9000, 12.4. Frames and Frame Types + * + * An endpoint MUST treat receipt of a packet containing no + * frames as a connection error of type PROTOCOL_VIOLATION. + */ + LOG_WARN("QUIC zero-length packet"); + return -1; + } + + if (dpt->flags & quic_pkt_rb_mask(dpt->flags)) + { + /* + * RFC 9000, Reserved Bits + * + * An endpoint MUST treat receipt of a packet that has + * a non-zero value for these bits, after removing both + * packet and header protection, as a connection error + * of type PROTOCOL_VIOLATION. + */ + LOG_WARN("QUIC reserved bit set in packet"); + return -1; + } + + // Set Largest Packet Number + dpt->largest_pkt_num = lpn; + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// +// debug +/////////////////////////////////////////////////////////////////////////////// + +static void quic_str_to_hex(u_char *data, size_t len) +{ + for (unsigned int i = 0; i < len; i++) + { + printf("%02x", data[i]); + } + printf("\n"); +} + +static u_char *quic_version_to_str(uint32_t version) +{ + unsigned int i = 0; + for (i = 0; i < QUIC_NVERSIONS; i++) + { + if (quic_version_vals[i].len == version) + { + return quic_version_vals[i].data; + } + } + return NULL; +} + +/////////////////////////////////////////////////////////////////////////////// +// PUB API +/////////////////////////////////////////////////////////////////////////////// + +quic_dpt_t *quic_deprotection_new(void) +{ + quic_dpt_t *dpt = (quic_dpt_t *)calloc(1, sizeof(quic_dpt_t)); + + // Allocate the space of length len + 1, instead of allocating the space of length len, mainly for the convenience of debugging printf + dpt->client_secret.secret.len = SHA256_DIGEST_LENGTH; + dpt->client_secret.secret.data = (u_char *)calloc(SHA256_DIGEST_LENGTH + 1, sizeof(u_char)); + + dpt->client_secret.key.len = QUIC_AES_128_KEY_LEN; + dpt->client_secret.key.data = (u_char *)calloc(QUIC_AES_128_KEY_LEN + 1, sizeof(u_char)); + + dpt->client_secret.hp.len = QUIC_AES_128_KEY_LEN; + dpt->client_secret.hp.data = (u_char *)calloc(QUIC_AES_128_KEY_LEN + 1, sizeof(u_char)); + + dpt->client_secret.iv.len = QUIC_IV_LEN; + dpt->client_secret.iv.data = (u_char *)calloc(QUIC_IV_LEN + 1, sizeof(u_char)); + + // NOTE: QUIC_MAX_UDP_PAYLOAD_SIZE is 65527(form Nginx-quice offical) + dpt->plaintext = (u_char *)calloc(QUIC_MAX_UDP_PAYLOAD_SIZE + 1, sizeof(u_char)); + + return dpt; +} + +void quic_deprotection_free(quic_dpt_t *dpt) +{ + if (dpt == NULL) + { + return; + } + + if (dpt->client_secret.secret.data) + { + free(dpt->client_secret.secret.data); + dpt->client_secret.secret.data = NULL; + dpt->client_secret.secret.len = 0; + } + + if (dpt->client_secret.key.data) + { + free(dpt->client_secret.key.data); + dpt->client_secret.key.data = NULL; + dpt->client_secret.key.len = 0; + } + + if (dpt->client_secret.hp.data) + { + free(dpt->client_secret.hp.data); + dpt->client_secret.hp.data = NULL; + dpt->client_secret.hp.len = 0; + } + + if (dpt->client_secret.iv.data) + { + free(dpt->client_secret.iv.data); + dpt->client_secret.iv.data = NULL; + dpt->client_secret.iv.len = 0; + } + + if (dpt->plaintext) + { + free(dpt->plaintext); + dpt->plaintext = NULL; + } + + free(dpt); + dpt = NULL; +} + +void quic_deprotection_dump(quic_dpt_t *dpt) +{ + printf("QUIC version : %08" PRIx32 " %s\n", dpt->version, quic_version_to_str(dpt->version)); + printf("QUIC type : %s\n", dpt->header_type == LONG ? "long" : "short"); + printf("QUIC level : %s\n", quic_pkt_level_to_name(dpt->level)); + printf("QUIC flags : %x\n", dpt->flags); + printf("QUIC num : %" PRIu64 "\n", dpt->pkt_num); + printf("QUIC len : %" PRIu64 "\n", dpt->pkt_len); + printf("QUIC larg_num : %" PRIu64 "\n", dpt->largest_pkt_num); + + printf("QUIC dcid : %zu, ", dpt->dcid.len); + quic_str_to_hex(dpt->dcid.data, dpt->dcid.len); + + printf("QUIC scid : %zu, ", dpt->scid.len); + quic_str_to_hex(dpt->scid.data, dpt->scid.len); + + printf("QUIC token : %zu, ", dpt->token.len); + quic_str_to_hex(dpt->token.data, dpt->token.len); + + printf("QUIC hp : %zu, ", dpt->client_secret.hp.len); + quic_str_to_hex(dpt->client_secret.hp.data, dpt->client_secret.hp.len); + + printf("QUIC iv : %zu, ", dpt->client_secret.iv.len); + quic_str_to_hex(dpt->client_secret.iv.data, dpt->client_secret.iv.len); + + printf("QUIC key : %zu, ", dpt->client_secret.key.len); + quic_str_to_hex(dpt->client_secret.key.data, dpt->client_secret.key.len); + + printf("QUIC secretn : %zu, ", dpt->client_secret.secret.len); + quic_str_to_hex(dpt->client_secret.secret.data, dpt->client_secret.secret.len); + + printf("QUIC plaintex : %zu, ", strlen((char *)dpt->plaintext)); + quic_str_to_hex(dpt->plaintext, strlen((char *)dpt->plaintext)); + + printf("QUIC payload : %zu, ", dpt->payload.len); + quic_str_to_hex(dpt->payload.data, dpt->payload.len); +} + +int quic_deprotection(quic_dpt_t *dpt, const u_char *payload, size_t payload_len) +{ + dpt->data = (u_char *)payload; + dpt->len = payload_len; + dpt->pos = (u_char *)dpt->data; + dpt->flags = payload[0]; + dpt->pos++; + dpt->largest_pkt_num = QUIC_UNSET_PN; + + // Parser Level, Version, DCID, SCID, Token, Packet Length + if (quic_parse_packet_header(dpt) == -1) + { + return -1; + } + + if (dpt->level != ssl_encryption_initial) + { + LOG_WARN("QUIC level %s ignoring", quic_pkt_level_to_name(dpt->level)) + return -1; + } + + /* + * 1.Get the initial_salt to be used through pkt_version + * 2.DCID and initial_salt generate initial_secret through SHA-256 + * 3.IN/Key/IV/HP lable of initial_secret and client_secret generates HP/Key/IV key of client_secret + */ + if (quic_keys_set_initial_secret(&dpt->client_secret, &dpt->dcid, dpt->version) == -1) + { + goto failed; + } + + if (dpt->client_secret.key.len == 0) + { + LOG_WARN("QUIC packet no client_secret, ignoring"); + goto failed; + } + + if (quic_deprotection_packet(dpt) == -1) + { + goto failed; + } + + // deprotection data: dpt->payload.data + + return 0; + +failed: + + return -1; +} \ No newline at end of file diff --git a/src/quic_deprotection.h b/src/quic_deprotection.h index d7d5b67..cb6e814 100644 --- a/src/quic_deprotection.h +++ b/src/quic_deprotection.h @@ -1,24 +1,116 @@ -/** - * parser-quic.h - * - * Created on 2020-11-26 - * @author: qyc - * - * - */ -#ifndef PARSER_QUIC_H -#define PARSER_QUIC_H +#ifndef _QUIC_DEPROTECTION_H +#define _QUIC_DEPROTECTION_H -#ifdef __cplusplus -extern "C" { +#ifdef __cpluscplus +extern "C" +{ #endif -/*ret: 1 sucess*/ -int quic_deprotection(const char *payload, unsigned int length, unsigned char *out, unsigned int *out_length); +#include +#include +#include +#include +#include +#include +#include +#include +#include -int gcry_init(); -#ifdef __cplusplus +#ifdef DEBUG_SWITCH + +#define LOG_DEBUG(format, ...) \ + { \ + fprintf(stdout, format "\n", ##__VA_ARGS__); \ + fflush(stdout); \ + } + +#define LOG_WARN(format, ...) \ + { \ + fprintf(stderr, format "\n", ##__VA_ARGS__); \ + fflush(stderr); \ + } + +#define LOG_ERROR(format, ...) \ + { \ + fprintf(stderr, format "\n", ##__VA_ARGS__); \ + fflush(stderr); \ + } + +#else + +#define LOG_DEBUG(format, ...) +#define LOG_WARN(format, ...) +#define LOG_ERROR(format, ...) + +#endif + +#define QUIC_MAX_UDP_PAYLOAD_SIZE 65527 + +#define quic_string(str) \ + { \ + sizeof(str) - 1, (u_char *)str \ + } + +typedef struct +{ + size_t len; + u_char *data; +} quic_str_t; + +typedef struct quic_secret_s +{ + quic_str_t secret; + quic_str_t key; + quic_str_t iv; + quic_str_t hp; +} quic_secret_t; + +typedef enum +{ + ssl_encryption_initial = 0, + ssl_encryption_early_data = 1, + ssl_encryption_handshake = 2, + ssl_encryption_application = 3, +} ssl_encryption_level_t; + +typedef enum +{ + LONG = 0, + SHORT = 1, +} quic_header_type; + +typedef struct +{ + quic_secret_t client_secret; + ssl_encryption_level_t level; // QUIC Packet Process Level + quic_header_type header_type; // QUIC Packet Header Type + + uint32_t version; // QUIC Version + uint8_t flags; // QUIC Flags + u_char *data; // QUIC Packet Data + size_t len; // QUIC Packet Length + u_char *pos; // Process Ptr + uint64_t largest_pkt_num; + + quic_str_t dcid; // QUIC DCID + quic_str_t scid; // QUIC SCID + quic_str_t token; // QUIC TOKEN + + size_t pkt_len; + uint64_t pkt_num; // QUIC Packet Number + u_char *plaintext; + quic_str_t payload; // Decrypted data + + unsigned key_phase : 1; +} quic_dpt_t; + +quic_dpt_t *quic_deprotection_new(void); +void quic_deprotection_free(quic_dpt_t *dpt); +void quic_deprotection_dump(quic_dpt_t *dpt); +int quic_deprotection(quic_dpt_t *dpt, const u_char *payload, size_t payload_len); + +#ifdef __cpluscplus } #endif -#endif //PARSER_QUIC_H +#endif diff --git a/src/quic_deprotection_utils.cpp b/src/quic_deprotection_utils.cpp deleted file mode 100644 index f032303..0000000 --- a/src/quic_deprotection_utils.cpp +++ /dev/null @@ -1,136 +0,0 @@ -/** - * utils.c - * - * Created on 2020-11-27 - * @author: qyc - * - * @explain: - */ -#include -#include -#include - -#include "pint.h" -#include "quic_deprotection_utils.h" -#include "quic_deprotection_wsgcrypt.h" - -/* - * Computes HKDF-Expand-Label(Secret, Label, Hash(context_value), Length) with a - * custom label prefix. If "context_hash" is NULL, then an empty context is - * used. Otherwise it must have the same length as the hash algorithm output. - */ -static gboolean tls13_hkdf_expand_label_context(int md, const StringInfo *secret, const char *label_prefix, const char *label, const guint8 *context_hash, guint8 context_length, guint16 out_len, guchar **out) -{ - /* RFC 8446 Section 7.1: - * HKDF-Expand-Label(Secret, Label, Context, Length) = - * HKDF-Expand(Secret, HkdfLabel, Length) - * struct { - * uint16 length = Length; - * opaque label<7..255> = "tls13 " + Label; // "tls13 " is label prefix. - * opaque context<0..255> = Context; - * } HkdfLabel; - * - * RFC 5869 HMAC-based Extract-and-Expand Key Derivation Function (HKDF): - * HKDF-Expand(PRK, info, L) -> OKM - */ - gcry_error_t err; - const guint label_prefix_length = (guint)strlen(label_prefix); - const guint label_length = (guint)strlen(label); - - // Some sanity checks - g_assert(label_length > 0 && label_prefix_length + label_length <= 255); - - // info = HkdfLabel { length, label, context } - GByteArray *info = g_byte_array_new(); - const guint16 length = g_htons(out_len); - g_byte_array_append(info, (const guint8 *)&length, sizeof(length)); - - const guint8 label_vector_length = label_prefix_length + label_length; - g_byte_array_append(info, &label_vector_length, 1); - g_byte_array_append(info, (const guint8 *)label_prefix, label_prefix_length); - g_byte_array_append(info, (const guint8 *)label, label_length); - - g_byte_array_append(info, &context_length, 1); - if (context_length) - g_byte_array_append(info, context_hash, context_length); - - *out = (guchar *)g_malloc(out_len); - err = hkdf_expand(md, secret->data, secret->data_len, info->data, info->len, *out, out_len); - g_byte_array_free(info, TRUE); - - if (err) { - printf("%s failed %d: %s\n", G_STRFUNC, md, gcry_strerror(err)); - g_free(*out); - *out = NULL; - return FALSE; - } - - return TRUE; -} - -gboolean tls13_hkdf_expand_label(int md, const StringInfo *secret, const char *label_prefix, const char *label, guint16 out_len, guchar **out) -{ - return tls13_hkdf_expand_label_context(md, secret, label_prefix, label, NULL, 0, out_len, out); -} - -static guint8 tvb_get_guint8(const char *tvb, const gint offset) -{ - const guint8 *ptr; - - ptr = (guint8 *)tvb + offset; - return *ptr; -} - -static guint16 tvb_get_ntohs(const char *tvb, const gint offset) -{ - const guint8 *ptr; - - ptr = (guint8 *)tvb + offset; - return pntoh16(ptr); -} - -static guint32 tvb_get_ntohl(const char *tvb, const gint offset) -{ - const guint8 *ptr; - - ptr = (guint8 *)tvb + offset; - return pntoh32(ptr); -} - -static guint64 tvb_get_ntoh64(const char *tvb, const gint offset) -{ - const guint8 *ptr; - - ptr = (guint8 *)tvb + offset; - return pntoh64(ptr); -} - -guint tvb_get_varint(const char *tvb, guint offset, guint maxlen, guint64 *value, const guint encoding) -{ - *value = 0; - - if (encoding & ENC_VARINT_QUIC) { - // calculate variable length - *value = tvb_get_guint8(tvb, offset); - switch((*value) >> 6) { - case 0: /* 0b00 => 1 byte length (6 bits Usable) */ - (*value) &= 0x3F; - return 1; - case 1: /* 0b01 => 2 bytes length (14 bits Usable) */ - *value = tvb_get_ntohs(tvb, offset) & 0x3FFF; - return 2; - case 2: /* 0b10 => 4 bytes length (30 bits Usable) */ - *value = tvb_get_ntohl(tvb, offset) & 0x3FFFFFFF; - return 4; - case 3: /* 0b11 => 8 bytes length (62 bits Usable) */ - *value = tvb_get_ntoh64(tvb, offset) & G_GUINT64_CONSTANT(0x3FFFFFFFFFFFFFFF); - return 8; - default: /* No Possible */ - g_assert_not_reached(); - break; - } - } - - // 10 bytes scanned, but no bytes' msb is zero - return 0; -} diff --git a/src/quic_deprotection_utils.h b/src/quic_deprotection_utils.h deleted file mode 100644 index 58c8955..0000000 --- a/src/quic_deprotection_utils.h +++ /dev/null @@ -1,43 +0,0 @@ -/** - * utils.h - * - * Created on 2020-11-27 - * @author: qyc - * - * @explain: - */ -#ifndef UTILS_H -#define UTILS_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include "glib.h" - -/* - * Decodes a variable-length integer used in QUIC protocol - * See https://tools.ietf.org/html/draft-ietf-quic-transport-08#section-8.1 - */ -#define ENC_VARINT_QUIC 0x00000004 - -/* Explicit and implicit nonce length (RFC 5116 - Section 3.2.1) */ -#define TLS13_AEAD_NONCE_LENGTH 12 - - -/* XXX Should we use GByteArray instead? */ -typedef struct _StringInfo { - // Backing storage which may be larger than data_len - guchar *data; - // Length of the meaningful part of data - guint data_len; -} StringInfo; - -gboolean tls13_hkdf_expand_label(int md, const StringInfo *secret, const char *label_prefix, const char *label, guint16 out_len, guchar **out); -guint tvb_get_varint(const char *tvb, guint offset, guint maxlen, guint64 *value, const guint encoding); - -#ifdef __cplusplus -} -#endif - -#endif //UTILS_H diff --git a/src/quic_deprotection_wsgcrypt.cpp b/src/quic_deprotection_wsgcrypt.cpp deleted file mode 100644 index 369f7f5..0000000 --- a/src/quic_deprotection_wsgcrypt.cpp +++ /dev/null @@ -1,69 +0,0 @@ -/** - * wsgcrypt.c - * - * Created on 2020-11-26 - * @author: qyc - * - * @explain: - */ -#include -#include -#include - -#include "quic_deprotection_wsgcrypt.h" - -gcry_error_t ws_hmac_buffer(int algo, void *digest, const void *buffer, size_t length, const void *key, size_t keylen) -{ - gcry_md_hd_t hmac_handle; - gcry_error_t result = gcry_md_open(&hmac_handle, algo, GCRY_MD_FLAG_HMAC); - if (result) { - return result; - } - result = gcry_md_setkey(hmac_handle, key, keylen); - if (result) { - gcry_md_close(hmac_handle); - return result; - } - gcry_md_write(hmac_handle, buffer, length); - memcpy(digest, gcry_md_read(hmac_handle, 0), gcry_md_get_algo_dlen(algo)); - gcry_md_close(hmac_handle); - return GPG_ERR_NO_ERROR; -} - -gcry_error_t hkdf_expand(int hashalgo, const guint8 *prk, guint prk_len, const guint8 *info, guint info_len, guint8 *out, guint out_len) -{ - // Current maximum hash output size: 48 bytes for SHA-384. - guchar lastoutput[48]; - gcry_md_hd_t h; - gcry_error_t err; - const guint hash_len = gcry_md_get_algo_dlen(hashalgo); - - // Some sanity checks - if (!(out_len > 0 && out_len <= 255 * hash_len) || !(hash_len > 0 && hash_len <= sizeof(lastoutput))) - return GPG_ERR_INV_ARG; - - err = gcry_md_open(&h, hashalgo, GCRY_MD_FLAG_HMAC); - if (err) - return err; - - guint offset; - for (offset = 0; offset < out_len; offset += hash_len) { - gcry_md_reset(h); - // Set PRK - gcry_md_setkey(h, prk, prk_len); - if (offset > 0) - // T(1..N) - gcry_md_write(h, lastoutput, hash_len); - // info - gcry_md_write(h, info, info_len); - // constant 0x01..N - gcry_md_putc(h, (guint8)(offset / hash_len + 1)); - - memcpy(lastoutput, gcry_md_read(h, hashalgo), hash_len); - memcpy(out + offset, lastoutput, MIN(hash_len, out_len - offset)); - } - - gcry_md_close(h); - - return 0; -} diff --git a/src/quic_deprotection_wsgcrypt.h b/src/quic_deprotection_wsgcrypt.h deleted file mode 100644 index 1dc7416..0000000 --- a/src/quic_deprotection_wsgcrypt.h +++ /dev/null @@ -1,78 +0,0 @@ -/** - * wsgcrypt.h - * - * Created on 2020-11-26 - * @author: qyc - * - * @explain: - */ -#ifndef WSGCRYPT_H -#define WSGCRYPT_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include "gcrypt.h" -#include "glib.h" - -/* - * Define HAVE_LIBGCRYPT_AEAD here, because it's used in several source - * files. - */ -#if GCRYPT_VERSION_NUMBER >= 0x010600 /* 1.6.0 */ -/* Whether to provide support for authentication in addition to decryption. */ -#define HAVE_LIBGCRYPT_AEAD -#endif - -/* - * Define some other "do we have?" items as well. - */ -#if GCRYPT_VERSION_NUMBER >= 0x010700 /* 1.7.0 */ -/* Whether ChaCh20 PNE can be supported. */ -#define HAVE_LIBGCRYPT_CHACHA20 -/* Whether AEAD_CHACHA20_POLY1305 can be supported. */ -#define HAVE_LIBGCRYPT_CHACHA20_POLY1305 -#endif - -#define HASH_SHA2_256_LENGTH 32 - -/* Convenience function to calculate the HMAC from the data in BUFFER - of size LENGTH with key KEY of size KEYLEN using the algorithm ALGO avoiding the creating of a - hash object. The hash is returned in the caller provided buffer - DIGEST which must be large enough to hold the digest of the given - algorithm. */ -gcry_error_t ws_hmac_buffer(int algo, void *digest, const void *buffer, size_t length, const void *key, size_t keylen); - -/** - * RFC 5869 HMAC-based Extract-and-Expand Key Derivation Function (HKDF): - * HKDF-Expand(PRK, info, L) -> OKM - * - * @param hashalgo [in] Libgcrypt hash algorithm identifier. - * @param prk [in] Pseudo-random key. - * @param prk_len [in] Length of prk. - * @param info [in] Optional context (can be NULL if info_len is zero). - * @param info_len [in] Length of info. - * @param out [out] Output keying material. - * @param out_len [in] Size of output keying material. - * @return 0 on success and an error code otherwise. - */ -gcry_error_t hkdf_expand(int hashalgo, const guint8 *prk, guint prk_len, const guint8 *info, guint info_len, guint8 *out, guint out_len); - -/* - * Calculate HKDF-Extract(salt, IKM) -> PRK according to RFC 5869. - * Caller MUST ensure that 'prk' is large enough to store the digest from hash - * algorithm 'hashalgo' (e.g. 32 bytes for SHA-256). - */ -static inline gcry_error_t hkdf_extract(int hashalgo, const guint8 *salt, size_t salt_len, const guint8 *ikm, size_t ikm_len, guint8 *prk) -{ - /* PRK = HMAC-Hash(salt, IKM) where salt is key, and IKM is input. */ - return ws_hmac_buffer(hashalgo, prk, ikm, ikm_len, salt, salt_len); -} - - -#ifdef __cplusplus -} -#endif - -#endif //WSGCRYPT_H diff --git a/src/quic_entry.cpp b/src/quic_entry.cpp index 4e9f6b1..6c7a7c2 100644 --- a/src/quic_entry.cpp +++ b/src/quic_entry.cpp @@ -246,8 +246,6 @@ extern "C" int QUIC_INIT(void) return -1; } - gcry_init(); - return 0; } diff --git a/src/quic_process.cpp b/src/quic_process.cpp index 3284ab2..fa8c242 100644 --- a/src/quic_process.cpp +++ b/src/quic_process.cpp @@ -426,7 +426,7 @@ int parse_tls_client_hello(struct quic_client_hello **client_hello, const char * int parse_result=PARSE_RESULT_VERSION; unsigned short one_ext_type=0, one_ext_len=0, extension_total_len=0; - if(payload_len-payload_offset<=sizeof(struct quic_client_hello_msg_hdr)) + if(payload_len-payload_offset<=(int)sizeof(struct quic_client_hello_msg_hdr)) { return PARSE_RESULT_VERSION; } @@ -756,8 +756,6 @@ enum QUIC_VERSION is_quic_protocol(const char *payload, int payload_len, int *pa unsigned char parse_quic_all_version(struct quic_info *quic_info, const char *payload, int payload_len, int thread_seq) { int ret=0, payload_offset=0; - unsigned char decrypt_payload[2048]={0}; - unsigned int decrypt_payload_len=sizeof(decrypt_payload); enum QUIC_VERSION quic_version=QUIC_VERSION_UNKNOWN; if(payload==NULL || payload_len<=0) @@ -786,16 +784,20 @@ unsigned char parse_quic_all_version(struct quic_info *quic_info, const char *pa && g_quic_param.decrypted_switch>0 ) { - ret=quic_deprotection(payload, payload_len, decrypt_payload, &decrypt_payload_len); - if(ret!=1 || decrypt_payload_len<=0) + quic_dpt_t *dpt = quic_deprotection_new(); + if (quic_deprotection(dpt, (const u_char *)payload, payload_len) != 0) { + quic_deprotection_free(dpt); return PARSE_RESULT_VERSION; } if(g_quic_param.decrypted_switch==2) { - return parse_quic_decrypted_payload(quic_info, (const char *)decrypt_payload, decrypt_payload_len, thread_seq); + ret = parse_quic_decrypted_payload(quic_info, (const char *)dpt->payload.data, dpt->payload.len, thread_seq); + quic_deprotection_free(dpt); + return ret; } + quic_deprotection_free(dpt); } else { diff --git a/src/version.map b/src/version.map new file mode 100644 index 0000000..1906265 --- /dev/null +++ b/src/version.map @@ -0,0 +1,9 @@ +VERS_2.4{ +global: + extern "C++" { + QUIC_*; + *quic_version_int2string*; + *quic_protocol_identify*; + }; + local: *; +}; diff --git a/support/CMakeLists.txt b/support/CMakeLists.txt index 460ca9b..6c08350 100644 --- a/support/CMakeLists.txt +++ b/support/CMakeLists.txt @@ -1,34 +1,28 @@ +# CMakeFiles for 3rd vendor library + include(ExternalProject) -### libgpg-error -ExternalProject_Add(libgpg-error PREFIX libgpg-error - URL ${CMAKE_CURRENT_SOURCE_DIR}/libgpg-error-1.42.tar.bz2 - URL_MD5 133fed221ba8f63f5842858a1ff67cb3 - BUILD_COMMAND "" - CONFIGURE_COMMAND CPPFLAGS=-fPIC ./configure --enable-static --prefix= CFLAGS=-fPIC CXXFLAGS=-fPIC LDFLAGS=-fPIC +### OpenSSL 1.1.1 +ExternalProject_Add(OpenSSL PREFIX openssl + URL ${CMAKE_CURRENT_SOURCE_DIR}/openssl-1.1.1l.tar.gz + URL_MD5 ac0d4387f3ba0ad741b0580dd45f6ff3 + CONFIGURE_COMMAND ./Configure linux-x86_64 --prefix= --openssldir=/lib/ssl enable-ec_nistp_64_gcc_128 no-shared + BUILD_COMMAND ${MAKE_COMMAND} + INSTALL_COMMAND make install_sw BUILD_IN_SOURCE 1) -ExternalProject_Get_Property(libgpg-error INSTALL_DIR) +ExternalProject_Get_Property(OpenSSL INSTALL_DIR) +set(OPENSSL_INCLUDE_DIRECTORIES ${INSTALL_DIR}/include) +set(OPENSSL_LINK_DIRECTORIES ${INSTALL_DIR}/lib) +set(OPENSSL_PKGCONFIG_PATH ${INSTALL_DIR}/lib/pkgconfig/) file(MAKE_DIRECTORY ${INSTALL_DIR}/include) -add_library(libgpg-error-static STATIC IMPORTED GLOBAL) -add_dependencies(libgpg-error-static libgpg-error) -set_property(TARGET libgpg-error-static PROPERTY IMPORTED_LOCATION ${INSTALL_DIR}/lib/libgpg-error.a) -set_property(TARGET libgpg-error-static PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${INSTALL_DIR}/include/) +add_library(openssl-crypto-static STATIC IMPORTED GLOBAL) +add_dependencies(openssl-crypto-static OpenSSL) +set_property(TARGET openssl-crypto-static PROPERTY IMPORTED_LOCATION ${INSTALL_DIR}/lib/libcrypto.a) +set_property(TARGET openssl-crypto-static PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${INSTALL_DIR}/include) -### libgcrypt -ExternalProject_Add(libgcrypt PREFIX libgcrypt - URL ${CMAKE_CURRENT_SOURCE_DIR}/libgcrypt-1.9.4.tar.bz2 - URL_MD5 edc7becfe09c75d8f95ff7623e40c52e - BUILD_COMMAND "" - DEPENDS libgpg-error-static - CONFIGURE_COMMAND CPPFLAGS=-fPIC ./configure --enable-static --disable-doc --prefix= --with-libgpg-error-prefix=${CMAKE_CURRENT_BINARY_DIR}/libgpg-error/ CFLAGS=-fPIC CXXFLAGS=-fPIC LDFLAGS=-fPIC - BUILD_IN_SOURCE 1) - -ExternalProject_Get_Property(libgcrypt INSTALL_DIR) -file(MAKE_DIRECTORY ${INSTALL_DIR}/include) - -add_library(libgcrypt-static STATIC IMPORTED GLOBAL) -add_dependencies(libgcrypt-static libgcrypt) -set_property(TARGET libgcrypt-static PROPERTY IMPORTED_LOCATION ${INSTALL_DIR}/lib/libgcrypt.a) -set_property(TARGET libgcrypt-static PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${INSTALL_DIR}/include/) +add_library(openssl-ssl-static STATIC IMPORTED GLOBAL) +add_dependencies(openssl-ssl-static OpenSSL) +set_property(TARGET openssl-ssl-static PROPERTY IMPORTED_LOCATION ${INSTALL_DIR}/lib/libssl.a) +set_property(TARGET openssl-ssl-static PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${INSTALL_DIR}/include) \ No newline at end of file diff --git a/support/libgcrypt-1.9.4.tar.bz2 b/support/libgcrypt-1.9.4.tar.bz2 deleted file mode 100644 index 0eb0863..0000000 Binary files a/support/libgcrypt-1.9.4.tar.bz2 and /dev/null differ diff --git a/support/libgpg-error-1.42.tar.bz2 b/support/libgpg-error-1.42.tar.bz2 deleted file mode 100644 index e464892..0000000 Binary files a/support/libgpg-error-1.42.tar.bz2 and /dev/null differ diff --git a/support/openssl-1.1.1l.tar.gz b/support/openssl-1.1.1l.tar.gz new file mode 100644 index 0000000..81be7f9 Binary files /dev/null and b/support/openssl-1.1.1l.tar.gz differ