summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
author刘学利 <[email protected]>2021-11-12 08:39:24 +0000
committer刘学利 <[email protected]>2021-11-12 08:39:24 +0000
commit36054106156a9c7363df0ab16630c0cf32782cc6 (patch)
tree703633d118b9c5afd2f9048f9bb82b656017e153 /src
parent6e3830983ce5a7d47fceb418b41fcc411eb14b7e (diff)
TSG-8455 基于Nginx-QUIC和OpenSSL重构QUIC解析层
Diffstat (limited to 'src')
-rw-r--r--src/Makefile55
-rw-r--r--src/pint.h213
-rw-r--r--src/quic_deprotection.cpp1833
-rw-r--r--src/quic_deprotection.h128
-rw-r--r--src/quic_deprotection_utils.cpp136
-rw-r--r--src/quic_deprotection_utils.h43
-rw-r--r--src/quic_deprotection_wsgcrypt.cpp69
-rw-r--r--src/quic_deprotection_wsgcrypt.h78
-rw-r--r--src/quic_entry.cpp2
-rw-r--r--src/quic_process.cpp14
-rw-r--r--src/version.map9
11 files changed, 1169 insertions, 1411 deletions
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 <[email protected]>
- * Copyright 1998 Gerald Combs
- *
- * SPDX-License-Identifier: GPL-2.0-or-later
- */
-
-#ifndef __PINT_H__
-#define __PINT_H__
-
-#include <glib.h>
-
-/* 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,878 +1,1129 @@
-/**
- * parser-quic.c
- *
- * Created on 2020-11-26
- * @author: qyc
- *
- * @explain: QUIC解析
- */
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#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 <inttypes.h>
+#include <openssl/evp.h>
+#include <openssl/kdf.h>
+#include <openssl/sha.h>
+
+#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
-int gcry_init()
+/*
+ * 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;
}
+// Returns the QUIC draft version or 0 if not applicable.
+static inline uint8_t quic_draft_version(uint32_t version)
+{
+ if ((version >> 8) == 0xff0000)
+ return (uint8_t)version;
-#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
+ // Facebook mvfst, based on draft -22.
+ if (version == 0xfaceb001)
+ return 22;
+ // Facebook mvfst, based on draft -27.
+ if (version == 0xfaceb002 || version == 0xfaceb00e)
+ return 27;
-/*
- * 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;
+ // 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;
-/*
- * 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).
- */
+ /*
+ * 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;
-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)
+ return 0;
+}
+
+static inline uint8_t quic_draft_is_max(uint32_t version, uint8_t max_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;
-
- 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);
-
- 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)
- {
- g_free(header);
- header=NULL;
- g_free(buffer);
- buffer=NULL;
- *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)
- {
- 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;
+ uint8_t draft_version = quic_draft_version(version);
+ return draft_version && draft_version <= max_version;
}
-static gboolean quic_is_pp_cipher_initialized(quic_pp_cipher *pp_cipher)
+///////////////////////////////////////////////////////////////////////////////
+// quic_parse_packet_header()
+///////////////////////////////////////////////////////////////////////////////
+
+static inline u_char *quic_parse_uint8(const u_char *pos, const u_char *end, uint8_t *out)
{
- return pp_cipher && pp_cipher->pp_cipher;
+ if ((size_t)(end - pos) < 1)
+ {
+ return NULL;
+ }
+
+ *out = *pos;
+ return (u_char *)pos + 1;
}
-/**
- * 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)
+static inline u_char *quic_parse_int(const u_char *pos, const u_char *end, uint64_t *out)
{
- /*
- * 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);
+ 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;
}
-/* Inspired from ngtcp2 */
-static guint64 quic_pkt_adjust_pkt_num(guint64 max_pkt_num, guint64 pkt_num, size_t n)
+static inline u_char *quic_parse_uint32(const u_char *pos, const u_char *end, uint32_t *out)
{
- 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;
+ 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);
}
-/**
- * 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)
+static inline u_char *quic_parse_nbytes(const u_char *pos, const u_char *end, size_t len, u_char **out)
{
- 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];
+ if ((size_t)(end - pos) < len)
+ {
+ return NULL;
+ }
+
+ *out = (u_char *)pos;
+ return (u_char *)pos + len;
}
-/**
- * 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)
+static int quic_parse_short_header(quic_dpt_t *dpt, size_t dcid_len)
{
- 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;
+ 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;
}
-/**
- * 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)
+static int quic_parse_long_header(quic_dpt_t *dpt)
{
- 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;
+ 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 gboolean quic_hkdf_expand_label(int hash_algo, guint8 *secret, guint secret_len, const char *label, guint8 *out, guint out_len)
+static int quic_parse_long_header_v1(quic_dpt_t *dpt)
{
- const StringInfo secret_si = { secret, secret_len };
- guchar *out_mem = NULL;
+ 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;
- 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 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 FALSE;
+ return 0;
}
-/**
- * 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)
+///////////////////////////////////////////////////////////////////////////////
+// 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)
{
- guchar hp_key[256/8];
- guint hash_len = gcry_md_get_algo_dlen(hash_algo);
+ 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;
- if (!quic_hkdf_expand_label(hash_algo, secret, hash_len, "quic hp", hp_key, key_length))
- return FALSE;
+failed:
- return gcry_cipher_setkey(hp_cipher->hp_cipher, hp_key, key_length) == 0;
+ EVP_PKEY_CTX_free(pctx);
+ return -1;
}
-static gboolean quic_pp_cipher_init(quic_pp_cipher *pp_cipher, int hash_algo, guint8 key_length, guint8 *secret)
+static int quic_hkdf_expand(const EVP_MD *digest, quic_str_t *out, const quic_str_t *label, const quic_str_t *prk)
{
- // Maximum key size is for AES256 cipher.
- guchar write_key[256/8];
- guint hash_len = gcry_md_get_algo_dlen(hash_algo);
+ uint8_t info_buf[20];
+ info_buf[0] = 0;
+ info_buf[1] = out->len;
+ info_buf[2] = label->len;
- if (key_length > sizeof(write_key))
- return FALSE;
+ uint8_t *p = (u_char *)memcpy(&info_buf[3], label->data, label->len) + label->len;
+ *p = '\0';
- if (!quic_hkdf_expand_label(hash_algo, secret, hash_len, "quic key", write_key, key_length) ||
- !quic_hkdf_expand_label(hash_algo, secret, hash_len, "quic iv", pp_cipher->pp_iv, sizeof(pp_cipher->pp_iv)))
- return FALSE;
+ quic_str_t info;
+ info.len = 2 + 1 + label->len + 1;
+ info.data = info_buf;
- return gcry_cipher_setkey(pp_cipher->pp_cipher, write_key, key_length) == 0;
-}
+ if (hkdf_expand(out, digest, prk, &info) != 0)
+ {
+ return -1;
+ }
-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));
+ return 0;
}
-static void quic_pp_cipher_reset(quic_pp_cipher *pp_cipher)
+static int hkdf_extract(quic_str_t *out, const EVP_MD *digest, const quic_str_t *secret, const quic_str_t *initial_salt)
{
- gcry_cipher_close(pp_cipher->pp_cipher);
- memset(pp_cipher, 0, sizeof(*pp_cipher));
-}
+ 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;
-/**
- * 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;
- }
+failed:
+
+ EVP_PKEY_CTX_free(pctx);
+ return -1;
}
-/*
- * (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)
+static int quic_keys_set_initial_secret(quic_secret_t *client_secret, const quic_str_t *dcid, uint32_t version)
{
- // 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;
+ 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},
+ };
+
+ 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;
+ }
+ }
+
+ return 0;
}
-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)
+///////////////////////////////////////////////////////////////////////////////
+// quic_deprotection_packet()
+///////////////////////////////////////////////////////////////////////////////
+
+static int quic_deprotection_header(const EVP_CIPHER *cipher, const quic_secret_t *secret, u_char *out, const u_char *in)
{
- // 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;
+ int outlen;
+ u_char zero[QUIC_HP_LEN] = {0};
+
+ EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
+ if (ctx == NULL)
+ {
+ return -1;
+ }
+
+ if (EVP_EncryptInit_ex(ctx, cipher, NULL, secret->hp.data, in) != 1)
+ {
+ LOG_ERROR("EVP_EncryptInit_ex() failed");
+ goto failed;
+ }
+
+ 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 gboolean quic_ciphers_prepare(quic_ciphers *ciphers, int hash_algo, int cipher_algo, int cipher_mode, guint8 *secret, const char **error)
+static uint64_t quic_deprotection_pktnum(u_char **pos, int len, const u_char *mask, uint64_t *largest_pkt_num)
{
- 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);
-}
+ u_char *p;
+ uint64_t truncated_pn, expected_pn, candidate_pn;
+ uint64_t pn_nbits, pn_win, pn_hwin, pn_mask;
-/* 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;
+ pn_nbits = MIN(len * 8, 62);
- // Facebook mvfst, based on draft -22.
- if (version == 0xfaceb001)
- return 22;
+ p = *pos;
+ truncated_pn = *p++ ^ *mask++;
- // Facebook mvfst, based on draft -27.
- if (version == 0xfaceb002 || version == 0xfaceb00e)
- return 27;
+ while (--len)
+ {
+ truncated_pn = (truncated_pn << 8) + (*p++ ^ *mask++);
+ }
- // 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;
+ *pos = p;
- /*
- * 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;
+ 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;
+ }
- return 0;
+ *largest_pkt_num = MAX((int64_t)*largest_pkt_num, (int64_t)candidate_pn);
+
+ return candidate_pn;
}
-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;
+static void quic_deprotection_nonce(u_char *nonce, size_t len, uint64_t pkt_num)
+{
+ 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);
}
-/**
- * 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 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)
{
- /*
- * 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;
+ 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;
+ }
+
+ if (EVP_DecryptInit_ex(ctx, cipher, NULL, NULL, NULL) != 1)
+ {
+ EVP_CIPHER_CTX_free(ctx);
+ LOG_ERROR("EVP_DecryptInit_ex() failed");
+ return -1;
+ }
+
+ 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;
+ }
+
+ 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;
+ }
+
+ if (EVP_DecryptUpdate(ctx, NULL, &len, ad->data, ad->len) != 1)
+ {
+ EVP_CIPHER_CTX_free(ctx);
+ LOG_ERROR("EVP_DecryptUpdate() failed");
+ return -1;
+ }
+
+ 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;
+ }
+
+ out->len = len;
+ tag = in->data + in->len - EVP_GCM_TLS_TAG_LEN;
+
+ 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;
+ }
+
+ if (EVP_DecryptFinal_ex(ctx, out->data + len, &len) <= 0)
+ {
+ EVP_CIPHER_CTX_free(ctx);
+ LOG_ERROR("EVP_DecryptFinal_ex failed");
+ return -1;
+ }
+
+ out->len += len;
+ EVP_CIPHER_CTX_free(ctx);
+
+ return 0;
}
-static gboolean quic_create_initial_decoders(const quic_cid_t *cid, const gchar **error, quic_info_data_t *quic_info)
+static int quic_deprotection_packet(quic_dpt_t *dpt)
{
- 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;
+ 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;
}
-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)
+///////////////////////////////////////////////////////////////////////////////
+// debug
+///////////////////////////////////////////////////////////////////////////////
+
+static void quic_str_to_hex(u_char *data, size_t len)
{
- 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;
-
- 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;
+ for (unsigned int i = 0; i < len; i++)
+ {
+ printf("%02x", data[i]);
+ }
+ printf("\n");
}
-static void free_quic_cipher(quic_ciphers *initial_ciphers)
+static u_char *quic_version_to_str(uint32_t version)
{
- if(initial_ciphers!=NULL)
- {
- quic_hp_cipher_reset(&(initial_ciphers->hp_cipher));
- quic_pp_cipher_reset(&(initial_ciphers->pp_cipher));
- }
+ 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;
}
-int quic_deprotection(const char *payload, unsigned int length, unsigned char *out, unsigned int *out_length)
+void quic_deprotection_dump(quic_dpt_t *dpt)
{
- 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;
-
- memset(&quic_packet, 0, sizeof(quic_packet_info_t));
- memset(&conn, 0, sizeof(quic_info_data_t));
-
- 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;
- }
-
- 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;
- }
-
- // 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;
-
- g_free((gpointer)quic_packet.decryption.data);
- quic_packet.decryption.data = NULL;
-
- ret=1;
- }
- else
- {
- ret=0;
- }
-
- free_quic_cipher(&conn.client_initial_ciphers);
- free_quic_cipher(&conn.server_initial_ciphers);
-
- return ret;
- }
- }
-
- return 0;
+ 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
-
-#ifdef __cplusplus
-extern "C" {
+#ifndef _QUIC_DEPROTECTION_H
+#define _QUIC_DEPROTECTION_H
+
+#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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <sys/stat.h>
+#include <arpa/inet.h>
+
+#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 \
+ }
-int gcry_init();
-#ifdef __cplusplus
+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 <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#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 <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#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: *;
+};