TSG-7298: QUIC解析层支持解析quic-ietf加密SNI
This commit is contained in:
4
.gitignore
vendored
4
.gitignore
vendored
@@ -6,3 +6,7 @@ Debug
|
||||
.project
|
||||
.settings/
|
||||
SI
|
||||
build/
|
||||
src/inc
|
||||
src/lib64
|
||||
cmake-build-*/
|
||||
|
||||
@@ -3,10 +3,13 @@ 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 libasan
|
||||
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
|
||||
|
||||
stages:
|
||||
- build
|
||||
- test
|
||||
- package
|
||||
|
||||
|
||||
.build_by_travis:
|
||||
before_script:
|
||||
@@ -21,6 +24,15 @@ stages:
|
||||
tags:
|
||||
- share
|
||||
|
||||
run_test:
|
||||
stage: test
|
||||
extends: .build_by_travis
|
||||
script:
|
||||
- yum makecache
|
||||
- ./ci/travis.sh
|
||||
- cd build
|
||||
- ctest --verbose
|
||||
|
||||
branch_build_debug:
|
||||
stage: build
|
||||
extends: .build_by_travis
|
||||
@@ -42,7 +54,7 @@ branch_build_release:
|
||||
- tags
|
||||
|
||||
develop_build_debug:
|
||||
stage: build
|
||||
stage: package
|
||||
extends: .build_by_travis
|
||||
variables:
|
||||
BUILD_TYPE: Debug
|
||||
@@ -61,7 +73,7 @@ develop_build_debug:
|
||||
- /^master.*$/i
|
||||
|
||||
develop_build_release:
|
||||
stage: build
|
||||
stage: package
|
||||
extends: .build_by_travis
|
||||
variables:
|
||||
BUILD_TYPE: RelWithDebInfo
|
||||
@@ -79,7 +91,7 @@ develop_build_release:
|
||||
- /^master.*$/i
|
||||
|
||||
release_build_debug:
|
||||
stage: build
|
||||
stage: package
|
||||
variables:
|
||||
BUILD_TYPE: Debug
|
||||
PACKAGE: 1
|
||||
@@ -95,7 +107,7 @@ release_build_debug:
|
||||
- tags
|
||||
|
||||
release_build_release:
|
||||
stage: build
|
||||
stage: package
|
||||
variables:
|
||||
BUILD_TYPE: RelWithDebInfo
|
||||
PACKAGE: 1
|
||||
|
||||
@@ -7,7 +7,6 @@ project (${lib_name})
|
||||
set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)
|
||||
include(Version)
|
||||
|
||||
SET(CMAKE_C_COMPILER "/usr/bin/g++")
|
||||
set(CMAKE_MACOSX_RPATH 0)
|
||||
set(CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} -Wall)
|
||||
|
||||
@@ -31,27 +30,34 @@ endif()
|
||||
|
||||
set(CMAKE_INSTALL_PREFIX /home/mesasoft/sapp_run)
|
||||
|
||||
include_directories(inc)
|
||||
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/*.c"
|
||||
"src/*.cpp"
|
||||
)
|
||||
|
||||
set(DNS_DEPEND_DYN_LIB ssl crypto MESA_handle_logger)
|
||||
set(DEPEND_DYN_LIB ssl crypto MESA_handle_logger)
|
||||
|
||||
# Shared Library Output
|
||||
add_library(quic SHARED ${SRC})
|
||||
set_target_properties(quic PROPERTIES PREFIX "")
|
||||
target_link_libraries(quic ${DNS_DEPEND_DYN_LIB})
|
||||
target_link_libraries(quic ${DNS_DEPEND_DYN_LIB} glib-2.0 pthread -Wl,--whole-archive libgpg-error-static -Wl,--no-whole-archive libgcrypt-static)
|
||||
set_target_properties(quic PROPERTIES OUTPUT_NAME ${lib_name})
|
||||
|
||||
enable_testing()
|
||||
add_subdirectory(test)
|
||||
|
||||
set(CPACK_RPM_USER_FILELIST "%config(noreplace) ${CMAKE_INSTALL_PREFIX}/plug/protocol/quic/quic.inf"
|
||||
"%config(noreplace) ${CMAKE_INSTALL_PREFIX}/conf/quic/quic.conf")
|
||||
|
||||
install(TARGETS quic LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/plug/protocol/quic COMPONENT LIBRARIES)
|
||||
install(FILES bin/quic.inf DESTINATION ${CMAKE_INSTALL_PREFIX}/plug/protocol/quic COMPONENT PROFILE)
|
||||
install(FILES bin/quic.conf DESTINATION ${CMAKE_INSTALL_PREFIX}/conf/quic COMPONENT PROFILE)
|
||||
install(FILES src/gquic.h DESTINATION /opt/MESA/include/MESA COMPONENT HEADER)
|
||||
install(FILES inc/gquic.h DESTINATION /opt/MESA/include/MESA COMPONENT HEADER)
|
||||
|
||||
include(Package)
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
set(__SOURCE_AUTORESIVISION ${CMAKE_SOURCE_DIR}/autorevision.sh)
|
||||
set(__AUTORESIVISION ${CMAKE_BINARY_DIR}/autorevision.sh)
|
||||
set(__VERSION_CACHE ${CMAKE_SOURCE_DIR}/version.txt)
|
||||
set(__VERSION_CACHE ${CMAKE_BINARY_DIR}/version.txt)
|
||||
set(__VERSION_CONFIG ${CMAKE_BINARY_DIR}/version.cmake)
|
||||
|
||||
file(COPY ${__SOURCE_AUTORESIVISION} DESTINATION ${CMAKE_BINARY_DIR}
|
||||
|
||||
@@ -93,5 +93,4 @@ int quic_version_int2string(unsigned int version, char *buff, int buff_len);
|
||||
//ret: 0: not quic, >0: quic
|
||||
int quic_protocol_identify(struct streaminfo *a_stream, void *a_packet, char *out_sni, int out_sni_len);
|
||||
|
||||
|
||||
#endif /* SRC_GQUIC_H_ */
|
||||
@@ -13,11 +13,8 @@
|
||||
|
||||
#include "gquic_process.h"
|
||||
#include "quic_analysis.h"
|
||||
#include "parser-quic.h"
|
||||
|
||||
int is_quant()
|
||||
{
|
||||
|
||||
}
|
||||
int is_iquic(enum _QUIC_VERSION quic_version)
|
||||
{
|
||||
switch(quic_version)
|
||||
@@ -63,6 +60,38 @@ int is_iquic(enum _QUIC_VERSION quic_version)
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static int get_value(unsigned char *payload, int *offset, int len)
|
||||
{
|
||||
switch(len)
|
||||
{
|
||||
case 1:
|
||||
return (int)(payload[(*offset)++]);
|
||||
break;
|
||||
case 2:
|
||||
(*offset)+=len;
|
||||
return (int)ntohs(*(unsigned short *)(payload+*offset-len));
|
||||
break;
|
||||
case 3:
|
||||
(*offset)+=len;
|
||||
return ((int)*(payload-2+*offset)<<16|
|
||||
(int)*(payload-1+*offset)<<8|
|
||||
(int)*(payload+*offset)<<0);
|
||||
break;
|
||||
case 4:
|
||||
(*offset)+=len;
|
||||
return (int)ntohl(*(unsigned int *)(payload+*offset-len));
|
||||
break;
|
||||
case 32:
|
||||
(*offset)+=len;
|
||||
return 0;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int quic_getLinkState(struct _quic_context *_context)
|
||||
{
|
||||
UCHAR state = 0;
|
||||
@@ -154,8 +183,8 @@ unsigned long long get_variable_length(char *p, int offset, int v_len)
|
||||
(unsigned long long)*((unsigned char *)(p)+2+offset)<<32|
|
||||
(unsigned long long)*((unsigned char *)(p)+3+offset)<<24|
|
||||
(unsigned long long)*((unsigned char *)(p)+4+offset)<<16|
|
||||
(unsigned long long)*((unsigned char *)(p)+5+offset)<<8;
|
||||
(unsigned long long)*((unsigned char *)(p)+6+offset)<<0;
|
||||
(unsigned long long)*((unsigned char *)(p)+5+offset)<<8|
|
||||
(unsigned long long)*((unsigned char *)(p)+6+offset)<<0;
|
||||
break;
|
||||
case 8:
|
||||
return (unsigned long long)*((unsigned char *)(p)+0+offset)<<56|
|
||||
@@ -270,7 +299,6 @@ unsigned long long get_packet_number(char* data, int offset, char pkn_len)
|
||||
// GQUIC version from 0 to 43
|
||||
static enum _QUIC_VERSION parse_q0to43_header(struct streaminfo *pstream, struct _quic_context* _context, char *payload, int payload_len, int *used_len)
|
||||
{
|
||||
int i=0,len=0;
|
||||
char public_flags=0;
|
||||
|
||||
struct _quic_public_header *gquic_hdr=&(_context->quic_info.quic_hdr);
|
||||
@@ -288,18 +316,6 @@ static enum _QUIC_VERSION parse_q0to43_header(struct streaminfo *pstream, struct
|
||||
|
||||
if(pstream->curdir==DIR_S2C && gquic_hdr->public_flags&GQUIC_PUBLIC_FLAG_VERSION)
|
||||
{
|
||||
#if 0
|
||||
gquic_hdr->is_version_negotiation=TRUE; // Version Negotiation Packet
|
||||
|
||||
gquic_hdr->negotiation_version_num=(payload_len-*used_len)/sizeof(int);
|
||||
gquic_hdr->negotiation_version_list=(unsigned int *)dictator_malloc(pstream->threadnum, payload_len-*used_len);
|
||||
|
||||
for(i=0; i<gquic_hdr->negotiation_version_num; i++)
|
||||
{
|
||||
gquic_hdr->negotiation_version_list[i]=*(unsigned int *)(payload+*used_len);
|
||||
*used_len+=sizeof(unsigned int);
|
||||
}
|
||||
#endif
|
||||
return QUIC_VERSION_UNKNOWN;
|
||||
}
|
||||
|
||||
@@ -446,11 +462,6 @@ enum _QUIC_VERSION parse_quic_header(struct streaminfo *pstream, struct _quic_co
|
||||
|
||||
enum _QUIC_VERSION is_quic_protocol(struct streaminfo *pstream, struct _quic_context* _context, char *payload, int payload_len, int *used_len)
|
||||
{
|
||||
int i=0,len=0;
|
||||
size_t s_id_len=0,d_id_len=0;
|
||||
char d_CID[128]={0}, s_CID[128]={0};
|
||||
unsigned char s_connection_id[MAX_CONNECT_ID_LEN]={0};
|
||||
unsigned char d_connection_id[MAX_CONNECT_ID_LEN]={0};
|
||||
enum _QUIC_VERSION quic_version=QUIC_VERSION_UNKNOWN;
|
||||
|
||||
if(_context->quic_info.quic_hdr.quic_version!=QUIC_VERSION_UNKNOWN)
|
||||
@@ -516,7 +527,7 @@ enum _QUIC_VERSION is_quic_protocol(struct streaminfo *pstream, struct _quic_con
|
||||
int parse_extension_tag(struct streaminfo *pstream, struct _quic_stream **quic_stream, void *a_packet, char *payload, int payload_len, int *used_len, int tag_num)
|
||||
{
|
||||
int tag_used_num=0;
|
||||
int tag_type,skip_tsg=0;
|
||||
int tag_type=0;
|
||||
int total_tag_len=0,tag_len=0;
|
||||
int tag_offset_end=0,pre_tag_offset_end=0;
|
||||
|
||||
@@ -612,7 +623,7 @@ int parse_extension_tag(struct streaminfo *pstream, struct _quic_stream **quic_s
|
||||
int gquic_frame_type_ack(struct streaminfo *pstream, struct _quic_context* _context, char *payload, int payload_len, int *used_len, char frame_type, void *a_packet)
|
||||
{
|
||||
unsigned char num_timestamp;
|
||||
unsigned char num_blocks=0,gap_to_next_block=0;;
|
||||
unsigned char num_blocks=0;;
|
||||
unsigned short largest_acked_delta_time=0;
|
||||
unsigned long long ack_block_length=0;
|
||||
unsigned long long largest_observed_ack=0;
|
||||
@@ -629,8 +640,7 @@ int gquic_frame_type_ack(struct streaminfo *pstream, struct _quic_context* _cont
|
||||
|
||||
if(num_blocks>0)
|
||||
{
|
||||
gap_to_next_block=*(unsigned char *)(payload+*used_len);
|
||||
*used_len+=1;
|
||||
*used_len+=1; //gap_to_next_block
|
||||
|
||||
*used_len+=(num_blocks*sizeof(unsigned int)); //Ack block length
|
||||
}
|
||||
@@ -659,14 +669,12 @@ int gquic_frame_type_ack(struct streaminfo *pstream, struct _quic_context* _cont
|
||||
return 0;
|
||||
}
|
||||
|
||||
int gquic_frame_type_stream(struct streaminfo *pstream, struct _quic_context* _context, char *payload, int payload_len, int *used_len, char frame_type, void *a_packet)
|
||||
int gquic_frame_type_stream(struct streaminfo *pstream, struct _quic_context* _context, char *payload, int payload_len, int *used_len, void *a_packet)
|
||||
{
|
||||
int ret=0;
|
||||
char state=APP_STATE_GIVEME;
|
||||
unsigned short tag_num = 0;
|
||||
unsigned int stream_id, message_tag;
|
||||
|
||||
stream_id=get_stream_id(pstream, _context, payload, frame_type, used_len);
|
||||
unsigned int message_tag;
|
||||
|
||||
message_tag=(unsigned int)ntohl(*(unsigned int *)(payload+*used_len));
|
||||
*used_len+=4;
|
||||
@@ -830,12 +838,14 @@ int gquic_proc_unencrypt(struct streaminfo *pstream, struct _quic_context* _cont
|
||||
break;
|
||||
default: //Regular Frame Types
|
||||
if(frame_type&GQUIC_SPECIAL_FRAME_STREAM)
|
||||
{
|
||||
ret=gquic_frame_type_stream(pstream, _context, payload, payload_len, used_len, frame_type, a_packet);
|
||||
{
|
||||
stream_id=get_stream_id(pstream, _context, payload, frame_type, used_len);
|
||||
ret=gquic_frame_type_stream(pstream, _context, payload, payload_len, used_len, a_packet);
|
||||
}
|
||||
else if((frame_type&0xC0)==GQUIC_SPECIAL_FRAME_ACK) // high bit set 0; (frame_type: 01nullmmB)
|
||||
{
|
||||
ret=gquic_frame_type_stream(pstream, _context, payload, payload_len, used_len, frame_type, a_packet);
|
||||
{
|
||||
stream_id=get_stream_id(pstream, _context, payload, frame_type, used_len);
|
||||
ret=gquic_frame_type_stream(pstream, _context, payload, payload_len, used_len, a_packet);
|
||||
}
|
||||
else if((frame_type&0xE0)==GQUIC_SPECIAL_FRAME_CONGEST_FB) // high two bits set 0; (frame_type: 01nullmmB)
|
||||
{
|
||||
@@ -863,8 +873,6 @@ int parse_gquic_Q046(struct streaminfo *pstream, struct _quic_context* _context,
|
||||
{
|
||||
int ret=APP_STATE_GIVEME;
|
||||
unsigned char frame_type;
|
||||
unsigned short tag_num=0;
|
||||
unsigned int stream_id, message_tag;
|
||||
|
||||
while(*used_len < payload_len)
|
||||
{
|
||||
@@ -872,8 +880,9 @@ int parse_gquic_Q046(struct streaminfo *pstream, struct _quic_context* _context,
|
||||
*used_len+=1; //skip frame_type
|
||||
|
||||
if(frame_type&IQUIC_FRAME_STREAM_HEX08)
|
||||
{
|
||||
ret=gquic_frame_type_stream(pstream, _context, payload, payload_len, used_len, frame_type, a_packet);
|
||||
{
|
||||
get_stream_id(pstream, _context, payload, frame_type, used_len);
|
||||
ret=gquic_frame_type_stream(pstream, _context, payload, payload_len, used_len, a_packet);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -889,11 +898,176 @@ int parse_gquic_Q046(struct streaminfo *pstream, struct _quic_context* _context,
|
||||
return APP_STATE_GIVEME;
|
||||
}
|
||||
|
||||
int parse_encrypt_parameter(struct _quic_stream *quic_stream, unsigned char *payload, int payload_len, int thread_seq)
|
||||
{
|
||||
int used_len=0,length=0;
|
||||
while(payload_len>used_len)
|
||||
{
|
||||
if(payload[used_len]> 0x00 && payload[used_len]<=0x20)
|
||||
{
|
||||
get_value(payload, &used_len, 1); //type=1
|
||||
length=get_value(payload, &used_len, 1); // length=1
|
||||
used_len+=length;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if((*(unsigned short *)(payload+used_len)) == htons(EXT_QUIC_PARAM_USER_AGENT))
|
||||
{
|
||||
quic_stream->ua_idx=quic_stream->ext_tag_num++;
|
||||
get_value(payload, &used_len, 2); //type=2
|
||||
length=get_value(payload, &used_len, 1); // length=1
|
||||
get_quic_tlv((char *)payload+used_len, &(quic_stream->ext_tags[quic_stream->ua_idx]), length, EXT_QUIC_PARAM_USER_AGENT, thread_seq);
|
||||
used_len+=length;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if(*(unsigned int *)(payload+used_len) == htonl(EXT_QUIC_PARAM_QUIC_VERSION))
|
||||
{
|
||||
quic_stream->ver_idx=quic_stream->ext_tag_num++;
|
||||
get_value(payload, &used_len, 4); //type=4
|
||||
length=get_value(payload, &used_len, 1); // length=1
|
||||
get_quic_tlv((char *)payload+used_len, &(quic_stream->ext_tags[quic_stream->ver_idx]), length, EXT_QUIC_PARAM_QUIC_VERSION, thread_seq);
|
||||
*(unsigned int *)quic_stream->ext_tags[quic_stream->ver_idx].value=(unsigned int)htonl(*(unsigned int *)quic_stream->ext_tags[quic_stream->ver_idx].value);
|
||||
used_len+=length;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if((*(unsigned int *)(payload+used_len))== htonl(EXT_QUIC_PARAM_GREASE_HIGH4) && (*(unsigned int *)(payload+used_len+4))== htonl(EXT_QUIC_PARAM_GREASE_LOW4))
|
||||
{
|
||||
used_len+=8; //type=8
|
||||
length=get_value(payload, &used_len, 1); // length=1
|
||||
used_len+=length;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
int parse_encrypt_server_name(struct _quic_stream *quic_stream, unsigned char *payload, int payload_len, int thread_seq)
|
||||
{
|
||||
int ext_len=0,used_len=0;
|
||||
|
||||
quic_stream->sni_idx=quic_stream->ext_tag_num++;
|
||||
get_value(payload, &used_len, 2); //Server Name List length
|
||||
if(get_value(payload, &used_len, 1)==0) //Server Name type
|
||||
{
|
||||
ext_len=get_value(payload, &used_len, 2); //Server Name length
|
||||
get_quic_tlv((char *)payload+used_len, &(quic_stream->ext_tags[quic_stream->sni_idx]), ext_len, EXTENSION_SERVER_NAME, thread_seq);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int parse_encrypt_client_hello(struct streaminfo *pstream, struct _quic_stream *quic_stream, void *a_packet, unsigned char *payload, int payload_len)
|
||||
{
|
||||
int skip_len=0;
|
||||
int used_len=0;
|
||||
int flags=0;
|
||||
int ext_type=0, extension_total_len=0;
|
||||
|
||||
get_value(payload, &used_len, 1); //handshake type
|
||||
get_value(payload, &used_len, 3); //client hello length
|
||||
get_value(payload, &used_len, 2); //ssl_version
|
||||
|
||||
get_value(payload, &used_len, 32); //Random
|
||||
|
||||
skip_len=(int)get_value(payload, &used_len, 1); //Session ID length
|
||||
used_len+=skip_len;
|
||||
|
||||
skip_len=(int)get_value(payload, &used_len, 2); //Ciper Suites length
|
||||
used_len+=skip_len;
|
||||
|
||||
skip_len=(int)get_value(payload, &used_len, 1); //Compression Methods
|
||||
used_len+=skip_len;
|
||||
|
||||
extension_total_len=(int)get_value(payload, &used_len, 2); //Extension length
|
||||
|
||||
quic_stream->ext_tags=(quic_tlv_t *)dictator_malloc(pstream->threadnum, sizeof(quic_tlv_t)*3);
|
||||
memset(quic_stream->ext_tags, 0, sizeof(quic_tlv_t)*3);
|
||||
|
||||
while(extension_total_len>used_len)
|
||||
{
|
||||
ext_type=get_value(payload, &used_len, 2); //Extension type
|
||||
skip_len=get_value(payload, &used_len, 2); //length
|
||||
switch(ext_type)
|
||||
{
|
||||
case EXTENSION_SERVER_NAME:
|
||||
parse_encrypt_server_name(quic_stream, payload+used_len, skip_len, pstream->threadnum);
|
||||
flags=1;
|
||||
break;
|
||||
case EXTENSION_QUIC_PARAM:
|
||||
parse_encrypt_parameter(quic_stream, payload+used_len, skip_len, pstream->threadnum);
|
||||
break;
|
||||
case EXTENSION_SUPPORT_GROUP:
|
||||
case EXTENSION_APP_PROT_NEGO:
|
||||
case EXTENSION_SIG_ALGORITHM:
|
||||
case EXTENSION_KEY_SHARE:
|
||||
case EXTENSION_PSK_EXCHANGE:
|
||||
case EXTENSION_SUPP_SSL_VER:
|
||||
case EXTENSION_COMPRESS_CERT:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
used_len+=skip_len;
|
||||
}
|
||||
|
||||
return flags;
|
||||
}
|
||||
|
||||
int parse_decrypt_quic(struct streaminfo *pstream, struct _quic_context* _context, void *a_packet, unsigned char * payload, int payload_len, int *used_len)
|
||||
{
|
||||
int ret=0,state=APP_STATE_GIVEME;
|
||||
unsigned int quic_version=_context->quic_info.quic_hdr.quic_version;
|
||||
|
||||
get_value(payload, used_len, 4); //Frame Type=1, offset=1, length=2
|
||||
|
||||
if( (quic_version>=MVFST_VERSION_00 && quic_version<=MVFST_VERSION_0F) ||
|
||||
(quic_version>=GQUIC_VERSION_T050 && quic_version<=GQUIC_VERSION_T059) ||
|
||||
(quic_version>=IQUIC_VERSION_I022 && quic_version<=IQUIC_VERSION_I029)
|
||||
)
|
||||
{
|
||||
if(payload[*used_len] == 0x01)
|
||||
{
|
||||
if(_context->quic_info.client_hello==NULL)
|
||||
{
|
||||
_context->quic_info.client_hello=(struct _quic_stream *)dictator_malloc(pstream->threadnum, sizeof(struct _quic_stream));
|
||||
memset(_context->quic_info.client_hello, 0, sizeof(struct _quic_stream));
|
||||
}
|
||||
ret=parse_encrypt_client_hello(pstream, _context->quic_info.client_hello, a_packet, payload+*used_len, payload_len); //Frame Type=1, offset=1, length=2
|
||||
if(ret>0 && _context->call_business)
|
||||
{
|
||||
state=quic_callPlugins(pstream, _context, (void *)(_context->quic_info.client_hello), sizeof(void *), QUIC_CLIENT_HELLO_MASK, a_packet);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if( (quic_version>=GQUIC_VERSION_Q047 && quic_version<=GQUIC_VERSION_Q059))
|
||||
{
|
||||
state=gquic_frame_type_stream(pstream, _context, (char *)payload, payload_len, used_len, a_packet);
|
||||
}
|
||||
else
|
||||
{
|
||||
state=APP_STATE_DROPME;
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
//cid->version->nounce->pkt num->ahn hash(12)
|
||||
int quic_process(struct streaminfo *pstream, struct _quic_context* _context, int thread_seq, void* a_packet)
|
||||
{
|
||||
int used_len=0;
|
||||
int ret=APP_STATE_GIVEME;
|
||||
unsigned char decrypt_payload[1500]={0};
|
||||
unsigned int decrypt_payload_len=sizeof(decrypt_payload);
|
||||
|
||||
enum _QUIC_VERSION is_gquic=QUIC_VERSION_UNKNOWN;
|
||||
struct udpdetail *udp_detail=pstream->pudpdetail;
|
||||
|
||||
@@ -914,7 +1088,7 @@ int quic_process(struct streaminfo *pstream, struct _quic_context* _context, int
|
||||
{
|
||||
_context->cb_version=1;
|
||||
ret=quic_callPlugins(pstream, _context, &(_context->quic_info.quic_hdr.quic_version), sizeof(_context->quic_info.quic_hdr.quic_version), QUIC_USEING_VERSION_MASK, a_packet);
|
||||
if(ret&APP_STATE_DROPME | ret&APP_STATE_DROPPKT)
|
||||
if((ret&APP_STATE_DROPME) || (ret&APP_STATE_DROPPKT))
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
@@ -929,15 +1103,34 @@ int quic_process(struct streaminfo *pstream, struct _quic_context* _context, int
|
||||
ret=parse_gquic_Q046(pstream, _context, a_packet, (char *)udp_detail->pdata, udp_detail->datalen, &used_len);
|
||||
break;
|
||||
default:
|
||||
if( ((is_gquic>=MVFST_VERSION_00 && is_gquic<=MVFST_VERSION_0F) ||
|
||||
(is_gquic>=GQUIC_VERSION_Q047 && is_gquic<=GQUIC_VERSION_Q059) ||
|
||||
(is_gquic>=GQUIC_VERSION_T050 && is_gquic<=GQUIC_VERSION_T059) ||
|
||||
(is_gquic>=GQUIC_VERSION_T050 && is_gquic<=GQUIC_VERSION_T059) ||
|
||||
(is_gquic>=IQUIC_VERSION_I022 && is_gquic<=IQUIC_VERSION_I029)
|
||||
)
|
||||
&& _context->is_decrypt==0
|
||||
)
|
||||
{
|
||||
_context->is_decrypt=1;
|
||||
ret=dissect_quic((char *)udp_detail->pdata, udp_detail->datalen, decrypt_payload, &decrypt_payload_len);
|
||||
if(ret!=1)
|
||||
{
|
||||
return APP_STATE_DROPME;
|
||||
}
|
||||
ret=parse_decrypt_quic(pstream, _context, a_packet, decrypt_payload, decrypt_payload_len, &used_len);
|
||||
break;
|
||||
}
|
||||
|
||||
ret=quic_callPlugins(pstream, _context, (char *)udp_detail->pdata, udp_detail->datalen, QUIC_APPLICATION_DATA_MASK, a_packet);
|
||||
if(ret&APP_STATE_DROPME | ret&APP_STATE_DROPPKT)
|
||||
if((ret&APP_STATE_DROPME) || (ret&APP_STATE_DROPPKT))
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if(ret&APP_STATE_DROPME | ret&APP_STATE_DROPPKT)
|
||||
if((ret&APP_STATE_DROPME) || (ret&APP_STATE_DROPPKT))
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
@@ -152,6 +152,36 @@
|
||||
#define TAG_RSEQ 0x52534551
|
||||
#define TAG_CADR 0x43414452
|
||||
|
||||
#define EXTENSION_SERVER_NAME 0x0000
|
||||
#define EXTENSION_SUPPORT_GROUP 0x000A
|
||||
#define EXTENSION_APP_PROT_NEGO 0x0010 //application layer protocol negotiation
|
||||
#define EXTENSION_SIG_ALGORITHM 0x000D
|
||||
#define EXTENSION_KEY_SHARE 0x0033
|
||||
#define EXTENSION_PSK_EXCHANGE 0x002D
|
||||
#define EXTENSION_SUPP_SSL_VER 0x002B
|
||||
#define EXTENSION_QUIC_PARAM 0xFFA5
|
||||
#define EXTENSION_COMPRESS_CERT 0x001B
|
||||
|
||||
#define EXT_QUIC_PARAM_MAX_IDLE_TIMEOUT 0x01
|
||||
#define EXT_QUIC_PARAM_MAX_UDP_PAYLOAD 0x03
|
||||
#define EXT_QUIC_PARAM_MAX_INIT_DATA 0x04
|
||||
#define EXT_QUIC_PARAM_MAX_STREAM_BIDI_LOCAL 0x05
|
||||
#define EXT_QUIC_PARAM_MAX_STREAM_BIDI_REMOTE 0x06
|
||||
#define EXT_QUIC_PARAM_MAX_STREAM_UNI 0x07
|
||||
#define EXT_QUIC_PARAM_MAX_STREAMS_BIDI 0x08
|
||||
#define EXT_QUIC_PARAM_MAX_STREAMS_UNI 0x09
|
||||
#define EXT_QUIC_PARAM_MAX_FRAME_SIZE 0x20
|
||||
#define EXT_QUIC_PARAM_INIT_SRC_CONN_ID 0x0F
|
||||
#define EXT_QUIC_PARAM_USER_AGENT 0x7129
|
||||
#define EXT_QUIC_PARAM_NOT_YET_SUPPORTED 0x712B
|
||||
#define EXT_QUIC_PARAM_QUIC_VERSION 0x80004752
|
||||
#define EXT_QUIC_PARAM_GREASE_LOW4 0x91D24E9B
|
||||
#define EXT_QUIC_PARAM_GREASE_HIGH4 0xEA666DE7
|
||||
|
||||
#define EXTENSION_QUIC_PARAM_UA 0x7129
|
||||
#define EXTENSION_QUIC_PARAM_VERSION 0x4752
|
||||
|
||||
|
||||
//https://github.com/quicwg/base-drafts/wiki/QUIC-Versions
|
||||
enum _QUIC_VERSION
|
||||
{
|
||||
@@ -237,6 +267,14 @@ enum _QUIC_VERSION
|
||||
|
||||
//Google QUIC with TLS 50 - 59 (T050 - T059)
|
||||
GQUIC_VERSION_T050=0x54303530,
|
||||
GQUIC_VERSION_T051=0x54303531,
|
||||
GQUIC_VERSION_T052=0x54303532,
|
||||
GQUIC_VERSION_T053=0x54303533,
|
||||
GQUIC_VERSION_T054=0x54303534,
|
||||
GQUIC_VERSION_T055=0x54303535,
|
||||
GQUIC_VERSION_T056=0x54303536,
|
||||
GQUIC_VERSION_T057=0x54303537,
|
||||
GQUIC_VERSION_T058=0x54303538,
|
||||
GQUIC_VERSION_T059=0x54303539,
|
||||
|
||||
//Google QUIC with TLS 99 (T099)
|
||||
@@ -263,6 +301,20 @@ enum _QUIC_VERSION
|
||||
|
||||
//Facebook
|
||||
MVFST_VERSION_00=0xfaceb000,
|
||||
MVFST_VERSION_01=0xfaceb001,
|
||||
MVFST_VERSION_02=0xfaceb002,
|
||||
MVFST_VERSION_03=0xfaceb003,
|
||||
MVFST_VERSION_04=0xfaceb004,
|
||||
MVFST_VERSION_05=0xfaceb005,
|
||||
MVFST_VERSION_06=0xfaceb006,
|
||||
MVFST_VERSION_07=0xfaceb007,
|
||||
MVFST_VERSION_08=0xfaceb008,
|
||||
MVFST_VERSION_09=0xfaceb009,
|
||||
MVFST_VERSION_0A=0xfaceb00A,
|
||||
MVFST_VERSION_0B=0xfaceb00B,
|
||||
MVFST_VERSION_0C=0xfaceb00C,
|
||||
MVFST_VERSION_0D=0xfaceb00D,
|
||||
MVFST_VERSION_0E=0xfaceb00E,
|
||||
MVFST_VERSION_0F=0xfaceb00F,
|
||||
|
||||
//IETF
|
||||
@@ -303,6 +355,7 @@ enum _QUIC_VERSION
|
||||
struct _quic_context
|
||||
{
|
||||
int is_quic;
|
||||
int is_decrypt;
|
||||
int cb_version;
|
||||
int link_state;
|
||||
int call_business;
|
||||
|
||||
792
src/parser-quic.cpp
Normal file
792
src/parser-quic.cpp
Normal file
@@ -0,0 +1,792 @@
|
||||
/**
|
||||
* parser-quic.c
|
||||
*
|
||||
* Created on 2020-11-26
|
||||
* @author: qyc
|
||||
*
|
||||
* @explain: QUIC解析
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "parser-quic.h"
|
||||
#include "wsgcrypt.h"
|
||||
#include "utils.h"
|
||||
#include "pint.h"
|
||||
#include "gcrypt.h"
|
||||
|
||||
// #define DEBUG_PARSER_QUIC
|
||||
|
||||
int gcry_init()
|
||||
{
|
||||
//const char * tmp = gcry_check_version("1.8.7");
|
||||
//gcry_control(GCRYCTL_SET_THREAD_CBS,&gcry_threads_pthread);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#define QUIC_LPT_INITIAL 0x0
|
||||
#define QUIC_LPT_0RTT 0x1
|
||||
#define QUIC_LPT_HANDSHAKE 0x2
|
||||
#define QUIC_LPT_RETRY 0x3
|
||||
/* Version Negotiation packets don't have any real packet type */
|
||||
#define QUIC_LPT_VER_NEG 0xfe
|
||||
/* dummy value that is definitely not LPT */
|
||||
#define QUIC_SHORT_PACKET 0xff
|
||||
|
||||
|
||||
/*
|
||||
* Although the QUIC SCID/DCID length field can store at most 255, v1 limits the
|
||||
* CID length to 20.
|
||||
*/
|
||||
#define QUIC_MAX_CID_LENGTH 20
|
||||
typedef struct _quic_cid {
|
||||
unsigned char len;
|
||||
unsigned char cid[QUIC_MAX_CID_LENGTH];
|
||||
} quic_cid_t;
|
||||
|
||||
/*
|
||||
* PROTECTED PAYLOAD DECRYPTION (done in first pass)
|
||||
*
|
||||
* Long packet types always use a single cipher depending on packet type.
|
||||
* Short packet types always use 1-RTT secrets for packet protection (pp).
|
||||
*
|
||||
* Considerations:
|
||||
* - QUIC packets might appear out-of-order (short packets before handshake
|
||||
* message is captured), lost or retransmitted/duplicated.
|
||||
* - During live capture, keys might not be immediately be available. 1-RTT
|
||||
* client keys will be ready while client proceses Server Hello (Handshake).
|
||||
* 1-RTT server keys will be ready while server creates Handshake message in
|
||||
* response to Initial Handshake.
|
||||
* - So delay cipher creation until first short packet is received.
|
||||
*
|
||||
* Required input from TLS dissector: TLS-Exporter 0-RTT/1-RTT secrets and
|
||||
* cipher/hash algorithms.
|
||||
*
|
||||
* QUIC payload decryption requires proper reconstruction of the packet number
|
||||
* which requires proper header decryption. The different states are:
|
||||
*
|
||||
* Packet type Packet number space Secrets
|
||||
* Long: Initial Initial Initial secrets
|
||||
* Long: Handshake Handshake Handshake
|
||||
* Long: 0-RTT 0/1-RTT (appdata) 0-RTT
|
||||
* Short header 0/1-RTT (appdata) 1-RTT (KP0 / KP1)
|
||||
*
|
||||
* Important to note is that Short Header decryption requires TWO ciphers (one
|
||||
* for each key phase), but that header protection uses only KP0. Total state
|
||||
* needed for each peer (client and server):
|
||||
* - 3 packet number spaces: Initial, Handshake, 0/1-RTT (appdata).
|
||||
* - 4 header protection ciphers: initial, 0-RTT, HS, 1-RTT.
|
||||
* - 5 payload protection ciphers: initial, 0-RTT, HS, 1-RTT (KP0), 1-RTT (KP1).
|
||||
*/
|
||||
|
||||
typedef struct _quic_decrypt_result {
|
||||
// Error message or NULL for success.
|
||||
const guchar *error;
|
||||
// Decrypted result on success (file-scoped).
|
||||
const guint8 *data;
|
||||
// Size of decrypted data.
|
||||
guint data_len;
|
||||
} quic_decrypt_result_t;
|
||||
|
||||
/** QUIC decryption context. */
|
||||
|
||||
typedef struct _quic_hp_cipher {
|
||||
// Header protection cipher.
|
||||
gcry_cipher_hd_t hp_cipher;
|
||||
} quic_hp_cipher;
|
||||
|
||||
typedef struct _quic_pp_cipher {
|
||||
// Packet protection cipher.
|
||||
gcry_cipher_hd_t pp_cipher;
|
||||
guint8 pp_iv[TLS13_AEAD_NONCE_LENGTH];
|
||||
} quic_pp_cipher;
|
||||
|
||||
typedef struct _quic_ciphers {
|
||||
quic_hp_cipher hp_cipher;
|
||||
quic_pp_cipher pp_cipher;
|
||||
} quic_ciphers;
|
||||
|
||||
/**
|
||||
* State for a single QUIC connection, identified by one or more Destination
|
||||
* Connection IDs (DCID).
|
||||
*/
|
||||
typedef struct _quic_info_data {
|
||||
guint32 version;
|
||||
quic_ciphers client_initial_ciphers;
|
||||
quic_ciphers server_initial_ciphers;
|
||||
// Packet number spaces for Initial, Handshake and appdata.
|
||||
guint64 max_client_pkn[3];
|
||||
guint64 max_server_pkn[3];
|
||||
} quic_info_data_t;
|
||||
|
||||
/** Per-packet information about QUIC, populated on the first pass. */
|
||||
typedef struct _quic_packet_info {
|
||||
// Reconstructed full packet number.
|
||||
guint64 packet_number;
|
||||
quic_decrypt_result_t decryption;
|
||||
// Length of PKN (1/2/3/4) or unknown (0).
|
||||
guint8 pkn_len;
|
||||
// Decrypted flag byte, valid only if pkn_len is non-zero.
|
||||
guint8 first_byte;
|
||||
} quic_packet_info_t;
|
||||
|
||||
/**
|
||||
* Given a QUIC message (header + non-empty payload), the actual packet number,
|
||||
* try to decrypt it using the PP cipher.
|
||||
* As the header points to the original buffer with an encrypted packet number,
|
||||
* the (encrypted) packet number length is also included.
|
||||
*
|
||||
* The actual packet number must be constructed according to
|
||||
* https://tools.ietf.org/html/draft-ietf-quic-transport-22#section-12.3
|
||||
*/
|
||||
static void quic_decrypt_message(quic_pp_cipher *pp_cipher, const char *payload, guint length, guint header_length,
|
||||
guint8 first_byte, guint pkn_len, guint64 packet_number, quic_decrypt_result_t *result)
|
||||
{
|
||||
gcry_error_t err;
|
||||
guint8 *header;
|
||||
guint8 nonce[TLS13_AEAD_NONCE_LENGTH];
|
||||
guint8 *buffer;
|
||||
guint8 atag[16];
|
||||
guint buffer_length;
|
||||
const guchar **error = &result->error;
|
||||
|
||||
g_assert(pp_cipher != NULL);
|
||||
g_assert(pp_cipher->pp_cipher != NULL);
|
||||
g_assert(pkn_len < header_length);
|
||||
g_assert(1 <= pkn_len && pkn_len <= 4);
|
||||
// copy header, but replace encrypted first byte and PKN by plaintext.
|
||||
header = (guint8 *)g_malloc(header_length);
|
||||
memcpy(header, payload, header_length);
|
||||
header[0] = first_byte;
|
||||
guint i;
|
||||
for (i = 0; i < pkn_len; i++)
|
||||
header[header_length - 1 - i] = (guint8)(packet_number >> (8 * i));
|
||||
|
||||
// Input is "header || ciphertext (buffer) || auth tag (16 bytes)"
|
||||
// buffer_length = length - (header_length + 16);
|
||||
// buffer_length = 297 - (2 + 16);
|
||||
buffer_length = length - (pkn_len + 16);
|
||||
if (buffer_length == 0) {
|
||||
*error = (const guchar *)"Decryption not possible, ciphertext is too short";
|
||||
return;
|
||||
}
|
||||
buffer = (guint8 *)g_malloc(buffer_length);
|
||||
memcpy(buffer, payload + header_length, buffer_length);
|
||||
memcpy(atag, payload + header_length + buffer_length, 16);
|
||||
|
||||
memcpy(nonce, pp_cipher->pp_iv, TLS13_AEAD_NONCE_LENGTH);
|
||||
// Packet number is left-padded with zeroes and XORed with write_iv
|
||||
phton64(nonce + sizeof(nonce) - 8, pntoh64(nonce + sizeof(nonce) - 8) ^ packet_number);
|
||||
|
||||
gcry_cipher_reset(pp_cipher->pp_cipher);
|
||||
err = gcry_cipher_setiv(pp_cipher->pp_cipher, nonce, TLS13_AEAD_NONCE_LENGTH);
|
||||
if (err) {
|
||||
//printf("Decryption (setiv) failed: %s\n", gcry_strerror(err));
|
||||
*error = (const guchar *)"Decryption (setiv) failed";
|
||||
return;
|
||||
}
|
||||
|
||||
// associated data (A) is the contents of QUIC header
|
||||
err = gcry_cipher_authenticate(pp_cipher->pp_cipher, header, header_length);
|
||||
if (err) {
|
||||
//printf("Decryption (authenticate) failed: %s\n", gcry_strerror(err));
|
||||
*error = (const guchar *)"Decryption (authenticate) failed";
|
||||
return;
|
||||
}
|
||||
|
||||
// Output ciphertext (C)
|
||||
err = gcry_cipher_decrypt(pp_cipher->pp_cipher, buffer, buffer_length, NULL, 0);
|
||||
if (err) {
|
||||
//printf("Decryption (decrypt) failed: %s\n", gcry_strerror(err));
|
||||
*error = (const guchar *)"Decryption (decrypt) failed";
|
||||
return;
|
||||
}
|
||||
|
||||
err = gcry_cipher_checktag(pp_cipher->pp_cipher, atag, 16);
|
||||
if (err) {
|
||||
//printf("Decryption (checktag) failed: %s\n", gcry_strerror(err));
|
||||
*error = (const guchar *)"Decryption (checktag) failed";
|
||||
return;
|
||||
}
|
||||
|
||||
g_free(header);
|
||||
|
||||
result->error = NULL;
|
||||
result->data = buffer;
|
||||
result->data_len = buffer_length;
|
||||
}
|
||||
|
||||
static gboolean quic_is_pp_cipher_initialized(quic_pp_cipher *pp_cipher)
|
||||
{
|
||||
return pp_cipher && pp_cipher->pp_cipher;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process (protected) payload, adding the encrypted payload to the tree. If
|
||||
* decryption is possible, frame dissection is also attempted.
|
||||
*
|
||||
* The given offset must correspond to the end of the QUIC header and begin of
|
||||
* the (protected) payload. Dissected frames are appended to "tree" and expert
|
||||
* info is attached to "ti" (the field with the encrypted payload).
|
||||
*/
|
||||
static void quic_process_payload(const char *payload, guint length, guint offset, quic_info_data_t *quic_info,
|
||||
quic_packet_info_t *quic_packet, gboolean from_server, quic_pp_cipher *pp_cipher, guint8 first_byte, guint pkn_len)
|
||||
{
|
||||
/*
|
||||
* If no decryption error has occurred yet, try decryption on the first
|
||||
* pass and store the result for later use.
|
||||
*/
|
||||
if (quic_is_pp_cipher_initialized(pp_cipher))
|
||||
quic_decrypt_message(pp_cipher, payload, length, offset, first_byte, pkn_len, quic_packet->packet_number, &quic_packet->decryption);
|
||||
}
|
||||
|
||||
/* Inspired from ngtcp2 */
|
||||
static guint64 quic_pkt_adjust_pkt_num(guint64 max_pkt_num, guint64 pkt_num, size_t n)
|
||||
{
|
||||
guint64 k = max_pkt_num == G_MAXUINT64 ? max_pkt_num : max_pkt_num + 1;
|
||||
guint64 u = k & ~((G_GUINT64_CONSTANT(1) << n) - 1);
|
||||
guint64 a = u | pkt_num;
|
||||
guint64 b = (u + (G_GUINT64_CONSTANT(1) << n)) | pkt_num;
|
||||
guint64 a1 = k < a ? a - k : k - a;
|
||||
guint64 b1 = k < b ? b - k : k - b;
|
||||
|
||||
if (a1 < b1)
|
||||
return a;
|
||||
return b;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the maximum valid packet number space for a peer.
|
||||
*/
|
||||
static guint64 *quic_max_packet_number(quic_info_data_t *quic_info, gboolean from_server, guint8 first_byte)
|
||||
{
|
||||
int pkn_space;
|
||||
if ((first_byte & 0x80) && (first_byte & 0x30) >> 4 == QUIC_LPT_INITIAL)
|
||||
// Long header, Initial
|
||||
pkn_space = 0;
|
||||
else if ((first_byte & 0x80) && (first_byte & 0x30) >> 4 == QUIC_LPT_HANDSHAKE)
|
||||
// Long header, Handshake
|
||||
pkn_space = 1;
|
||||
else
|
||||
// Long header (0-RTT) or Short Header (1-RTT appdata).
|
||||
pkn_space = 2;
|
||||
if (from_server)
|
||||
return &quic_info->max_server_pkn[pkn_space];
|
||||
else
|
||||
return &quic_info->max_client_pkn[pkn_space];
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the full packet number and store it for later use.
|
||||
*/
|
||||
static void quic_set_full_packet_number(quic_info_data_t *quic_info, quic_packet_info_t *quic_packet, gboolean from_server, guint8 first_byte, guint32 pkn32)
|
||||
{
|
||||
guint pkn_len = (first_byte & 3) + 1;
|
||||
guint64 pkn_full;
|
||||
guint64 max_pn = *quic_max_packet_number(quic_info, from_server, first_byte);
|
||||
|
||||
// Sequential first pass, try to reconstruct full packet number.
|
||||
pkn_full = quic_pkt_adjust_pkt_num(max_pn, pkn32, 8 * pkn_len);
|
||||
quic_packet->pkn_len = pkn_len;
|
||||
quic_packet->packet_number = pkn_full;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a header protection cipher, a buffer and the packet number offset,
|
||||
* return the unmasked first byte and packet number.
|
||||
*/
|
||||
static gboolean quic_decrypt_header(const char *payload, guint pn_offset, quic_hp_cipher *hp_cipher, int hp_cipher_algo, guint8 *first_byte, guint32 *pn)
|
||||
{
|
||||
if (!hp_cipher->hp_cipher)
|
||||
// need to know the cipher.
|
||||
return FALSE;
|
||||
gcry_cipher_hd_t h = hp_cipher->hp_cipher;
|
||||
|
||||
// Sample is always 16 bytes and starts after PKN (assuming length 4).
|
||||
// https://tools.ietf.org/html/draft-ietf-quic-tls-22#section-5.4.2
|
||||
guint8 sample[16];
|
||||
memcpy(sample, payload + pn_offset + 4, 16);
|
||||
|
||||
guint8 mask[5] = { 0 };
|
||||
switch (hp_cipher_algo) {
|
||||
case GCRY_CIPHER_AES128:
|
||||
case GCRY_CIPHER_AES256:
|
||||
// Encrypt in-place with AES-ECB and extract the mask.
|
||||
if (gcry_cipher_encrypt(h, sample, sizeof(sample), NULL, 0))
|
||||
return FALSE;
|
||||
memcpy(mask, sample, sizeof(mask));
|
||||
break;
|
||||
#ifdef HAVE_LIBGCRYPT_CHACHA20
|
||||
case GCRY_CIPHER_CHACHA20:
|
||||
// If Gcrypt receives a 16 byte IV, it will assume the buffer to be
|
||||
// counter || nonce (in little endian), as desired. */
|
||||
if (gcry_cipher_setiv(h, sample, 16))
|
||||
return FALSE;
|
||||
// Apply ChaCha20, encrypt in-place five zero bytes.
|
||||
if (gcry_cipher_encrypt(h, mask, sizeof(mask), NULL, 0))
|
||||
return FALSE;
|
||||
break;
|
||||
#endif // HAVE_LIBGCRYPT_CHACHA20
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// https://tools.ietf.org/html/draft-ietf-quic-tls-22#section-5.4.1
|
||||
guint8 packet0 = payload[0];
|
||||
if ((packet0 & 0x80) == 0x80)
|
||||
// Long header: 4 bits masked
|
||||
packet0 ^= mask[0] & 0x0f;
|
||||
else
|
||||
// Short header: 5 bits masked
|
||||
packet0 ^= mask[0] & 0x1f;
|
||||
guint pkn_len = (packet0 & 0x03) + 1;
|
||||
|
||||
guint8 pkn_bytes[4];
|
||||
memcpy(pkn_bytes, payload + pn_offset, pkn_len);
|
||||
guint32 pkt_pkn = 0;
|
||||
guint i;
|
||||
for (i = 0; i < pkn_len; i++)
|
||||
pkt_pkn |= (pkn_bytes[i] ^ mask[1 + i]) << (8 * (pkn_len - 1 - i));
|
||||
*first_byte = packet0;
|
||||
*pn = pkt_pkn;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean quic_hkdf_expand_label(int hash_algo, guint8 *secret, guint secret_len, const char *label, guint8 *out, guint out_len)
|
||||
{
|
||||
const StringInfo secret_si = { secret, secret_len };
|
||||
guchar *out_mem = NULL;
|
||||
|
||||
if (tls13_hkdf_expand_label(hash_algo, &secret_si, "tls13 ", label, out_len, &out_mem)) {
|
||||
memcpy(out, out_mem, out_len);
|
||||
g_free(out_mem);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Expands the secret (length MUST be the same as the "hash_algo" digest size)
|
||||
* and initialize cipher with the new key.
|
||||
*/
|
||||
static gboolean quic_hp_cipher_init(quic_hp_cipher *hp_cipher, int hash_algo, guint8 key_length, guint8 *secret)
|
||||
{
|
||||
guchar hp_key[256/8];
|
||||
guint hash_len = gcry_md_get_algo_dlen(hash_algo);
|
||||
|
||||
if (!quic_hkdf_expand_label(hash_algo, secret, hash_len, "quic hp", hp_key, key_length))
|
||||
return FALSE;
|
||||
|
||||
return gcry_cipher_setkey(hp_cipher->hp_cipher, hp_key, key_length) == 0;
|
||||
}
|
||||
|
||||
static gboolean quic_pp_cipher_init(quic_pp_cipher *pp_cipher, int hash_algo, guint8 key_length, guint8 *secret)
|
||||
{
|
||||
// Maximum key size is for AES256 cipher.
|
||||
guchar write_key[256/8];
|
||||
guint hash_len = gcry_md_get_algo_dlen(hash_algo);
|
||||
|
||||
if (key_length > sizeof(write_key))
|
||||
return FALSE;
|
||||
|
||||
if (!quic_hkdf_expand_label(hash_algo, secret, hash_len, "quic key", write_key, key_length) ||
|
||||
!quic_hkdf_expand_label(hash_algo, secret, hash_len, "quic iv", pp_cipher->pp_iv, sizeof(pp_cipher->pp_iv)))
|
||||
return FALSE;
|
||||
|
||||
return gcry_cipher_setkey(pp_cipher->pp_cipher, write_key, key_length) == 0;
|
||||
}
|
||||
|
||||
static void quic_hp_cipher_reset(quic_hp_cipher *hp_cipher)
|
||||
{
|
||||
gcry_cipher_close(hp_cipher->hp_cipher);
|
||||
memset(hp_cipher, 0, sizeof(*hp_cipher));
|
||||
}
|
||||
|
||||
static void quic_pp_cipher_reset(quic_pp_cipher *pp_cipher)
|
||||
{
|
||||
gcry_cipher_close(pp_cipher->pp_cipher);
|
||||
memset(pp_cipher, 0, sizeof(*pp_cipher));
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps a Packet Protection cipher to the Packet Number protection cipher.
|
||||
* See https://tools.ietf.org/html/draft-ietf-quic-tls-22#section-5.4.3
|
||||
*/
|
||||
static gboolean quic_get_pn_cipher_algo(int cipher_algo, int *hp_cipher_mode)
|
||||
{
|
||||
switch (cipher_algo) {
|
||||
case GCRY_CIPHER_AES128:
|
||||
case GCRY_CIPHER_AES256:
|
||||
*hp_cipher_mode = GCRY_CIPHER_MODE_ECB;
|
||||
return TRUE;
|
||||
#ifdef HAVE_LIBGCRYPT_CHACHA20
|
||||
case GCRY_CIPHER_CHACHA20:
|
||||
*hp_cipher_mode = GCRY_CIPHER_MODE_STREAM;
|
||||
return TRUE;
|
||||
#endif // HAVE_LIBGCRYPT_CHACHA20
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* (Re)initialize the PNE/PP ciphers using the given cipher algorithm.
|
||||
* If the optional base secret is given, then its length MUST match the hash
|
||||
* algorithm output.
|
||||
*/
|
||||
static gboolean quic_hp_cipher_prepare(quic_hp_cipher *hp_cipher, int hash_algo, int cipher_algo, guint8 *secret, const char **error)
|
||||
{
|
||||
// Clear previous state (if any).
|
||||
quic_hp_cipher_reset(hp_cipher);
|
||||
|
||||
int hp_cipher_mode;
|
||||
if (!quic_get_pn_cipher_algo(cipher_algo, &hp_cipher_mode)) {
|
||||
*error = "Unsupported cipher algorithm";
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (gcry_cipher_open(&hp_cipher->hp_cipher, cipher_algo, hp_cipher_mode, 0)) {
|
||||
quic_hp_cipher_reset(hp_cipher);
|
||||
*error = "Failed to create HP cipher";
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (secret) {
|
||||
guint cipher_keylen = (guint8)gcry_cipher_get_algo_keylen(cipher_algo);
|
||||
if (!quic_hp_cipher_init(hp_cipher, hash_algo, cipher_keylen, secret)) {
|
||||
quic_hp_cipher_reset(hp_cipher);
|
||||
*error = "Failed to derive key material for HP cipher";
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean quic_pp_cipher_prepare(quic_pp_cipher *pp_cipher, int hash_algo, int cipher_algo, int cipher_mode, guint8 *secret, const char **error)
|
||||
{
|
||||
// Clear previous state (if any).
|
||||
quic_pp_cipher_reset(pp_cipher);
|
||||
|
||||
int hp_cipher_mode;
|
||||
if (!quic_get_pn_cipher_algo(cipher_algo, &hp_cipher_mode)) {
|
||||
*error = "Unsupported cipher algorithm";
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (gcry_cipher_open(&pp_cipher->pp_cipher, cipher_algo,cipher_mode, 0)) {
|
||||
quic_pp_cipher_reset(pp_cipher);
|
||||
*error = "Failed to create PP cipher";
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (secret) {
|
||||
guint cipher_keylen = (guint8) gcry_cipher_get_algo_keylen(cipher_algo);
|
||||
if (!quic_pp_cipher_init(pp_cipher, hash_algo, cipher_keylen, secret)) {
|
||||
quic_pp_cipher_reset(pp_cipher);
|
||||
*error = "Failed to derive key material for PP cipher";
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean quic_ciphers_prepare(quic_ciphers *ciphers, int hash_algo, int cipher_algo, int cipher_mode, guint8 *secret, const char **error)
|
||||
{
|
||||
return quic_hp_cipher_prepare(&ciphers->hp_cipher, hash_algo, cipher_algo, secret, error) &&
|
||||
quic_pp_cipher_prepare(&ciphers->pp_cipher, hash_algo, cipher_algo, cipher_mode, secret, error);
|
||||
}
|
||||
|
||||
/* Returns the QUIC draft version or 0 if not applicable. */
|
||||
static inline guint8 quic_draft_version(guint32 version) {
|
||||
if ((version >> 8) == 0xff0000)
|
||||
return (guint8) version;
|
||||
|
||||
// Facebook mvfst, based on draft -22.
|
||||
if (version == 0xfaceb001)
|
||||
return 22;
|
||||
|
||||
// Facebook mvfst, based on draft -27.
|
||||
if (version == 0xfaceb002 || version == 0xfaceb00e)
|
||||
return 27;
|
||||
|
||||
// GQUIC Q050, T050 and T051: they are not really based on any drafts,
|
||||
// but we must return a sensible value
|
||||
if (version == 0x51303530 || version == 0x54303530 || version == 0x54303531)
|
||||
return 27;
|
||||
|
||||
/*
|
||||
* https://tools.ietf.org/html/draft-ietf-quic-transport-32#section-15
|
||||
* "Versions that follow the pattern 0x?a?a?a?a are reserved for use in
|
||||
* forcing version negotiation to be exercised"
|
||||
* It is tricky to return a correct draft version: such number is primarly
|
||||
* used to select a proper salt (which depends on the version itself), but
|
||||
* we don't have a real version here! Let's hope that we need to handle
|
||||
* only latest drafts...
|
||||
*/
|
||||
if ((version & 0x0F0F0F0F) == 0x0a0a0a0a)
|
||||
return 29;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline gboolean is_quic_draft_max(guint32 version, guint8 max_version) {
|
||||
guint8 draft_version = quic_draft_version(version);
|
||||
return draft_version && draft_version <= max_version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the client and server initial secrets given Connection ID "cid".
|
||||
*
|
||||
* On success TRUE is returned and the two initial secrets are set.
|
||||
* FALSE is returned on error (see "error" parameter for the reason).
|
||||
*/
|
||||
static gboolean quic_derive_initial_secrets(const quic_cid_t *cid, guint8 client_initial_secret[HASH_SHA2_256_LENGTH], guint8 server_initial_secret[HASH_SHA2_256_LENGTH], guint32 version, const gchar **error)
|
||||
{
|
||||
/*
|
||||
* https://tools.ietf.org/html/draft-ietf-quic-tls-29#section-5.2
|
||||
*
|
||||
* initial_salt = 0xafbfec289993d24c9e9786f19c6111e04390a899
|
||||
* initial_secret = HKDF-Extract(initial_salt, client_dst_connection_id)
|
||||
*
|
||||
* client_initial_secret = HKDF-Expand-Label(initial_secret,
|
||||
* "client in", "", Hash.length)
|
||||
* server_initial_secret = HKDF-Expand-Label(initial_secret,
|
||||
* "server in", "", Hash.length)
|
||||
*
|
||||
* Hash for handshake packets is SHA-256 (output size 32).
|
||||
*/
|
||||
static const guint8 handshake_salt_draft_22[20] = {
|
||||
0x7f, 0xbc, 0xdb, 0x0e, 0x7c, 0x66, 0xbb, 0xe9, 0x19, 0x3a,
|
||||
0x96, 0xcd, 0x21, 0x51, 0x9e, 0xbd, 0x7a, 0x02, 0x64, 0x4a
|
||||
};
|
||||
static const guint8 handshake_salt_draft_23[20] = {
|
||||
0xc3, 0xee, 0xf7, 0x12, 0xc7, 0x2e, 0xbb, 0x5a, 0x11, 0xa7,
|
||||
0xd2, 0x43, 0x2b, 0xb4, 0x63, 0x65, 0xbe, 0xf9, 0xf5, 0x02,
|
||||
};
|
||||
static const guint8 handshake_salt_draft_29[20] = {
|
||||
0xaf, 0xbf, 0xec, 0x28, 0x99, 0x93, 0xd2, 0x4c, 0x9e, 0x97,
|
||||
0x86, 0xf1, 0x9c, 0x61, 0x11, 0xe0, 0x43, 0x90, 0xa8, 0x99
|
||||
};
|
||||
static const guint8 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
|
||||
err = hkdf_extract(GCRY_MD_SHA256, handshake_salt_draft_29, sizeof(handshake_salt_draft_29), cid->cid, cid->len, secret);
|
||||
if (err) {
|
||||
//printf("Failed to extract secrets: %s\n", gcry_strerror(err));
|
||||
*error = "Failed to extract secrets";
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!quic_hkdf_expand_label(GCRY_MD_SHA256, secret, sizeof(secret), "client in", client_initial_secret, HASH_SHA2_256_LENGTH)) {
|
||||
*error = "Key expansion (client) failed";
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!quic_hkdf_expand_label(GCRY_MD_SHA256, secret, sizeof(secret), "server in", server_initial_secret, HASH_SHA2_256_LENGTH)) {
|
||||
*error = "Key expansion (server) failed";
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
*error = NULL;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean quic_create_initial_decoders(const quic_cid_t *cid, const gchar **error, quic_info_data_t *quic_info)
|
||||
{
|
||||
unsigned char client_secret[HASH_SHA2_256_LENGTH];
|
||||
unsigned char server_secret[HASH_SHA2_256_LENGTH];
|
||||
|
||||
if (!quic_derive_initial_secrets(cid, client_secret, server_secret, quic_info->version, error))
|
||||
return -1;
|
||||
|
||||
// Packet numbers are protected with AES128-CTR,
|
||||
// initial packets are protected with AEAD_AES_128_GCM.
|
||||
if (!quic_ciphers_prepare(&quic_info->client_initial_ciphers, GCRY_MD_SHA256, GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_GCM, client_secret, error))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if(!quic_ciphers_prepare(&quic_info->server_initial_ciphers, GCRY_MD_SHA256, GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_GCM, server_secret, error))
|
||||
{
|
||||
quic_hp_cipher_reset(&quic_info->client_initial_ciphers.hp_cipher);
|
||||
quic_pp_cipher_reset(&quic_info->client_initial_ciphers.pp_cipher);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static int quic_extract_header(const char *payload, unsigned char *long_packet_type, unsigned int *version, quic_cid_t *dcid, quic_cid_t *scid)
|
||||
{
|
||||
unsigned int offset = 0;
|
||||
|
||||
unsigned char packet_type = payload[offset];
|
||||
unsigned char is_long_header = packet_type & 0x80;
|
||||
if (is_long_header)
|
||||
// long header form
|
||||
*long_packet_type = (packet_type & 0x30) >> 4;
|
||||
else
|
||||
// short header form, store dummy value that is not a long packet type.
|
||||
*long_packet_type = QUIC_SHORT_PACKET;
|
||||
offset++;
|
||||
|
||||
*version = pntoh32((unsigned int *)&payload[offset]);
|
||||
|
||||
if (is_long_header) {
|
||||
// VN packets don't have any real packet type field,
|
||||
// even if they have a long header: use a dummy value */
|
||||
if (*version == 0x00000000)
|
||||
*long_packet_type = QUIC_LPT_VER_NEG;
|
||||
|
||||
// skip version
|
||||
offset += 4;
|
||||
|
||||
// read DCID and SCID (both are prefixed by a length byte).
|
||||
unsigned char dcil = payload[offset];
|
||||
offset++;
|
||||
|
||||
if (dcil && dcil <= QUIC_MAX_CID_LENGTH) {
|
||||
memcpy(dcid->cid, &payload[offset], dcil);
|
||||
dcid->len = dcil;
|
||||
}
|
||||
offset += dcil;
|
||||
|
||||
unsigned char scil = payload[offset];
|
||||
offset++;
|
||||
|
||||
if (scil && scil <= QUIC_MAX_CID_LENGTH) {
|
||||
memcpy(scid->cid, &payload[offset], scil);
|
||||
scid->len = scil;
|
||||
}
|
||||
offset += scil;
|
||||
}
|
||||
else {
|
||||
// Definitely not draft -10, set version to dummy value.
|
||||
*version = 0;
|
||||
// For short headers, the DCID length is unknown and could be 0 or
|
||||
// anything from 1 to 20 bytes. Copy the maximum possible and let the
|
||||
// consumer truncate it as necessary.
|
||||
memcpy(dcid->cid, &payload[offset], QUIC_MAX_CID_LENGTH);
|
||||
dcid->len = QUIC_MAX_CID_LENGTH;
|
||||
offset += QUIC_MAX_CID_LENGTH;
|
||||
}
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
int dissect_quic(const char *payload, unsigned int length, unsigned char *out, unsigned int *out_length)
|
||||
{
|
||||
guint offset = 0;
|
||||
quic_packet_info_t quic_packet;
|
||||
quic_info_data_t conn;
|
||||
unsigned char long_packet_type;
|
||||
quic_cid_t dcid = {.len=0}, scid = {.len=0};
|
||||
guint64 token_length, payload_length;
|
||||
const char *error = NULL;
|
||||
guint8 first_byte = 0;
|
||||
const gboolean from_server = FALSE;
|
||||
quic_ciphers *ciphers = NULL;
|
||||
int ret;
|
||||
|
||||
memset(&quic_packet, 0, sizeof(quic_packet_info_t));
|
||||
memset(&conn, 0, sizeof(quic_info_data_t));
|
||||
|
||||
ret = quic_extract_header(payload, &long_packet_type, &conn.version, &dcid, &scid);
|
||||
if (ret < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (long_packet_type == QUIC_LPT_INITIAL)
|
||||
{
|
||||
// Create new decryption context based on the Client Connection ID
|
||||
// from the *very first* Client Initial packet.
|
||||
quic_create_initial_decoders(&dcid, &error, &conn);
|
||||
if (!error)
|
||||
{
|
||||
guint32 pkn32 = 0;
|
||||
// PKN is after type(1) + version(4) + DCIL+DCID + SCIL+SCID
|
||||
guint pn_offset = 1 + 4 + 1 + dcid.len + 1 + scid.len;
|
||||
pn_offset += tvb_get_varint(payload, pn_offset, 8, &token_length, ENC_VARINT_QUIC);
|
||||
pn_offset += (guint)token_length;
|
||||
// printf("%d\n", token_length);
|
||||
|
||||
pn_offset += tvb_get_varint(payload, pn_offset, 8, &payload_length, ENC_VARINT_QUIC);
|
||||
// printf("%d\n", payload_length);
|
||||
|
||||
// Assume failure unless proven otherwise.
|
||||
ciphers = &conn.client_initial_ciphers;
|
||||
error = "Header deprotection failed";
|
||||
if (quic_decrypt_header(payload, pn_offset, &ciphers->hp_cipher, GCRY_CIPHER_AES128, &first_byte, &pkn32))
|
||||
error = NULL;
|
||||
if (!error) {
|
||||
quic_set_full_packet_number(&conn, &quic_packet, from_server, first_byte, pkn32);
|
||||
quic_packet.first_byte = first_byte;
|
||||
}
|
||||
|
||||
// Payload
|
||||
// skip type(1) + version(4) + DCIL+DCID + SCIL+SCID + len_token_length + token_length + len_payload_length + len_packet_number
|
||||
offset = pn_offset + quic_packet.pkn_len;
|
||||
//quic_process_payload(payload, length, offset, &conn, &quic_packet, from_server, &ciphers->pp_cipher, first_byte, quic_packet.pkn_len);
|
||||
quic_process_payload(payload, payload_length, offset, &conn, &quic_packet, from_server, &ciphers->pp_cipher, first_byte, quic_packet.pkn_len);
|
||||
|
||||
// Out
|
||||
if (!quic_packet.decryption.error)
|
||||
{
|
||||
memcpy(out, quic_packet.decryption.data, quic_packet.decryption.data_len);
|
||||
*out_length = quic_packet.decryption.data_len;
|
||||
|
||||
g_free((gpointer)quic_packet.decryption.data);
|
||||
quic_packet.decryption.data = NULL;
|
||||
|
||||
ret=1;
|
||||
}
|
||||
else
|
||||
{
|
||||
ret=0;
|
||||
}
|
||||
|
||||
quic_hp_cipher_reset(&conn.client_initial_ciphers.hp_cipher);
|
||||
quic_pp_cipher_reset(&conn.client_initial_ciphers.pp_cipher);
|
||||
quic_hp_cipher_reset(&conn.server_initial_ciphers.hp_cipher);
|
||||
quic_pp_cipher_reset(&conn.server_initial_ciphers.pp_cipher);
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
24
src/parser-quic.h
Normal file
24
src/parser-quic.h
Normal file
@@ -0,0 +1,24 @@
|
||||
/**
|
||||
* parser-quic.h
|
||||
*
|
||||
* Created on 2020-11-26
|
||||
* @author: qyc
|
||||
*
|
||||
*
|
||||
*/
|
||||
#ifndef PARSER_QUIC_H
|
||||
#define PARSER_QUIC_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*ret: 1 sucess*/
|
||||
int dissect_quic(const char *payload, unsigned int length, unsigned char *out, unsigned int *out_length);
|
||||
|
||||
int gcry_init();
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif //PARSER_QUIC_H
|
||||
213
src/pint.h
Normal file
213
src/pint.h
Normal file
@@ -0,0 +1,213 @@
|
||||
/* pint.h
|
||||
* Definitions for extracting and translating integers safely and portably
|
||||
* via pointers.
|
||||
*
|
||||
* Wireshark - Network traffic analyzer
|
||||
* By Gerald Combs <gerald@wireshark.org>
|
||||
* 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:
|
||||
*/
|
||||
136
src/utils.cpp
Normal file
136
src/utils.cpp
Normal file
@@ -0,0 +1,136 @@
|
||||
/**
|
||||
* utils.c
|
||||
*
|
||||
* Created on 2020-11-27
|
||||
* @author: qyc
|
||||
*
|
||||
* @explain:
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "utils.h"
|
||||
#include "wsgcrypt.h"
|
||||
#include "pint.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;
|
||||
}
|
||||
43
src/utils.h
Normal file
43
src/utils.h
Normal file
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* 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
|
||||
69
src/wsgcrypt.cpp
Normal file
69
src/wsgcrypt.cpp
Normal file
@@ -0,0 +1,69 @@
|
||||
/**
|
||||
* wsgcrypt.c
|
||||
*
|
||||
* Created on 2020-11-26
|
||||
* @author: qyc
|
||||
*
|
||||
* @explain:
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "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;
|
||||
}
|
||||
78
src/wsgcrypt.h
Normal file
78
src/wsgcrypt.h
Normal file
@@ -0,0 +1,78 @@
|
||||
/**
|
||||
* 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
|
||||
34
support/CMakeLists.txt
Normal file
34
support/CMakeLists.txt
Normal file
@@ -0,0 +1,34 @@
|
||||
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=<INSTALL_DIR> CFLAGS=-fPIC CXXFLAGS=-fPIC LDFLAGS=-fPIC
|
||||
BUILD_IN_SOURCE 1)
|
||||
|
||||
ExternalProject_Get_Property(libgpg-error INSTALL_DIR)
|
||||
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/)
|
||||
|
||||
### 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=<INSTALL_DIR> --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/)
|
||||
BIN
support/libgcrypt-1.9.4.tar.bz2
Normal file
BIN
support/libgcrypt-1.9.4.tar.bz2
Normal file
Binary file not shown.
BIN
support/libgpg-error-1.42.tar.bz2
Normal file
BIN
support/libgpg-error-1.42.tar.bz2
Normal file
Binary file not shown.
41
test/CMakeLists.txt
Normal file
41
test/CMakeLists.txt
Normal file
@@ -0,0 +1,41 @@
|
||||
cmake_minimum_required (VERSION 2.8)
|
||||
|
||||
set(lib_name quic)
|
||||
project(${lib_name}_test)
|
||||
|
||||
include(ExternalProject)
|
||||
#### Protoco_test_run
|
||||
|
||||
|
||||
ExternalProject_Add(ProtoTest PREFIX ProtoTest
|
||||
URL ${CMAKE_CURRENT_SOURCE_DIR}/test_protocol_run.zip
|
||||
URL_MD5 71d8284b59af0286b5f31f0a3160bc44
|
||||
CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=<INSTALL_DIR> -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND ""
|
||||
INSTALL_COMMAND ""
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory <SOURCE_DIR>/conf/${lib_name}/
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_SOURCE_DIR}/bin/${lib_name}.conf <SOURCE_DIR>/conf/${lib_name}/
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_SOURCE_DIR}/conflist.inf <SOURCE_DIR>/plug/
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory <SOURCE_DIR>/plug/protocol/${lib_name}/
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_SOURCE_DIR}/bin/${lib_name}.inf <SOURCE_DIR>/plug/protocol/${lib_name}/
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory <SOURCE_DIR>/plug/business/${lib_name}_test_plug/
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_SOURCE_DIR}/test/${lib_name}_test_plug.inf <SOURCE_DIR>/plug/business/${lib_name}_test_plug/)
|
||||
|
||||
ExternalProject_Get_Property(ProtoTest INSTALL_DIR)
|
||||
ExternalProject_Get_Property(ProtoTest SOURCE_DIR)
|
||||
set(PROTO_TEST_RUN_DIR ${SOURCE_DIR})
|
||||
|
||||
add_executable(proto_test_main IMPORTED GLOBAL)
|
||||
add_dependencies(proto_test_main ProtoTest)
|
||||
set_property(TARGET proto_test_main PROPERTY IMPORTED_LOCATION ${SOURCE_DIR}/test_protocol_plug_main)
|
||||
|
||||
|
||||
add_library(${lib_name}_test_plug SHARED ${lib_name}_test_plug.cpp)
|
||||
target_link_libraries(${lib_name}_test_plug MESA_prof_load cjson)
|
||||
set_target_properties(${lib_name}_test_plug PROPERTIES PREFIX "")
|
||||
|
||||
add_test(NAME COPY_FTP_SO COMMAND sh -c "cp ${CMAKE_BINARY_DIR}/${lib_name}.so ${PROTO_TEST_RUN_DIR}/plug/protocol/${lib_name}/${lib_name}.so")
|
||||
add_test(NAME COPY_TEST_SO COMMAND sh -c "cp ${CMAKE_CURRENT_BINARY_DIR}/${lib_name}_test_plug.so ${PROTO_TEST_RUN_DIR}/plug/business/${lib_name}_test_plug/${lib_name}_test_plug.so")
|
||||
add_test(NAME QUIC_TEST COMMAND proto_test_main ${CMAKE_CURRENT_SOURCE_DIR}/${lib_name}_result.json -f "find ${CMAKE_CURRENT_SOURCE_DIR}/pcap/ -name *.pcap|sort -V" WORKING_DIRECTORY ${PROTO_TEST_RUN_DIR})
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
CC = gcc
|
||||
CCC = g++
|
||||
|
||||
INCLUDES += -I/opt/MESA/include/
|
||||
LIB = -L./opt/MESA/lib/ -lpthread
|
||||
CFLAGS = -g3 -Wall -fPIC
|
||||
CFLAGS += $(INCLUDES)
|
||||
|
||||
TARGET = dpkt_plug_gquic.so
|
||||
INF = dpkt_plug_gquic.inf
|
||||
INSTALL_TARGET=$(TARGET)
|
||||
LIB_FILE = $(wildcard ../lib/*.a)
|
||||
SOURCES = $(wildcard *.cpp)
|
||||
OBJECTS = $(SOURCES:.cpp=.o)
|
||||
DEPS = $(SOURCES:.cpp=.d)
|
||||
|
||||
INF=dpkt_plug_gquic.inf
|
||||
INSTALL_TARGET=dpkt_plug_gquic.so
|
||||
# $(CONF)
|
||||
INSTALL_DIR=/home/mesasoft/sapp/plug/business/dpkt_plug_gquic/
|
||||
|
||||
all:$(TARGET)
|
||||
$(TARGET):$(OBJECTS) $(LIB_FILE)
|
||||
$(CCC) -shared $(CFLAGS) $(OBJECTS) $(LIB) -o $@
|
||||
mkdir -p $(INSTALL_DIR)
|
||||
cp -r $(INSTALL_TARGET) $(INF) $(INSTALL_DIR) -f
|
||||
# cp $(TARGET) ../bin/
|
||||
|
||||
.c.o:
|
||||
%.d:%.c
|
||||
$(CCC) $< -MM $(INCLUDES) > $@
|
||||
|
||||
%.o:%.cpp
|
||||
$(CCC) -c -o $@ $(CFLAGS) $< $(INCLUDES)
|
||||
|
||||
-include $(DEPS)
|
||||
|
||||
clean :
|
||||
rm -f $(OBJECTS) $(DEPS) $(TARGET)
|
||||
|
||||
PLUGIN_PATH=./plug/business
|
||||
CONFLIST_NAME=conflist_business.inf
|
||||
PLUGIN_DIR_NAME=dpkt_plug_gquic
|
||||
PLUGIN_INF_NAME=dpkt_plug_gquic.inf
|
||||
PAPP_PATH=/home/dk/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)
|
||||
cp -r ../bin/*.conf $(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)
|
||||
|
||||
8
test/conflist.inf
Normal file
8
test/conflist.inf
Normal file
@@ -0,0 +1,8 @@
|
||||
[platform]
|
||||
|
||||
[protocol]
|
||||
./plug/protocol/quic/quic.inf
|
||||
|
||||
|
||||
[business]
|
||||
./plug/business/quic_test_plug/quic_test_plug.inf
|
||||
@@ -1,118 +0,0 @@
|
||||
|
||||
#include "dpkt_plug_gquic.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <dlfcn.h>
|
||||
#include "gquic.h"
|
||||
|
||||
|
||||
void a_ntoa( unsigned int in, char *buffer)
|
||||
{
|
||||
unsigned char *bytes = (unsigned char *) ∈
|
||||
int i = snprintf( buffer, 15, "%d.%d.%d.%d", bytes[0], bytes[1], bytes[2], bytes[3] );
|
||||
}
|
||||
|
||||
int DPKT_GQUIC_INIT()
|
||||
{
|
||||
int plugid = DK_PLUGID_GQUIC;
|
||||
return plugid;
|
||||
}
|
||||
|
||||
void DPKT_GQUIC_DESTROY()
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
char DPKT_GQUIC_ENTRY(stSessionInfo* session_info, void **pme, int _thread_num, struct streaminfo *pstream, void *a_packet)
|
||||
{
|
||||
int thread_num = pstream->threadnum;
|
||||
|
||||
if(session_info->session_state & SESSION_STATE_CLOSE)
|
||||
{
|
||||
return PROT_STATE_GIVEME;
|
||||
}
|
||||
|
||||
if(session_info->app_info ==NULL)
|
||||
{
|
||||
return PROT_STATE_GIVEME;
|
||||
}
|
||||
|
||||
if(session_info->prot_flag == QUIC_CLIENT_HELLO){
|
||||
printf("DPKT_QUIC_ENTRY\tQUIC_CLIENT_HELLO\n");
|
||||
struct quic_stream *quic = (struct quic_stream*)session_info->app_info;
|
||||
if(quic){
|
||||
struct quic_client_hello client_hello = quic->st_client_hello;
|
||||
|
||||
printf("BUSINESS PLUG:QUIC_CLIENT_HELLO ext_tag_num=%d--------------------\n",client_hello.ext_tag_num);
|
||||
if(quic->version){
|
||||
printf("BUSINESS PLUG:QUIC_CLIENT_HELLO version=%d--------------------\n",quic->version);
|
||||
|
||||
}
|
||||
if(client_hello.server_name){
|
||||
printf("BUSINESS PLUG:QUIC_CLIENT_HELLO server_name=%s--------------------\n",client_hello.server_name);
|
||||
|
||||
}
|
||||
if(client_hello.user_agent){
|
||||
printf("BUSINESS PLUG:QUIC_CLIENT_HELLO user_agent=%s--------------------\n",client_hello.user_agent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int i = 0, j = 0;
|
||||
if(session_info->prot_flag == QUIC_VERSION){
|
||||
printf("DPKT_QUIC_ENTRY\tQUIC_VERSION\n");
|
||||
struct quic_stream *quic = (struct quic_stream*)session_info->app_info;
|
||||
if(quic){
|
||||
printf("version:%d\n",quic->version);
|
||||
}
|
||||
}
|
||||
|
||||
if(session_info->prot_flag == QUIC_SERVER_HELLO){
|
||||
printf("DPKT_QUIC_ENTRY\tQUIC_SERVER_HELLO\n");
|
||||
struct quic_stream *quic = (struct quic_stream*)session_info->app_info;
|
||||
struct quic_server_hello server_hello = quic->st_server_hello;
|
||||
printf("BUSINESS PLUG:QUIC_SERVER_HELLO ext_tag_num=%d--------------------\n",server_hello.ext_tag_num);
|
||||
}
|
||||
|
||||
if(session_info->prot_flag == QUIC_CACHED_CERT){
|
||||
printf("DPKT_QUIC_ENTRY\tQUIC_CACHED_CERT\n");
|
||||
struct quic_stream *quic = (struct quic_stream*)session_info->app_info;
|
||||
quic_tlv_t cached_cert = quic->cached_cert;
|
||||
printf("--------------------BUSINESS PLUG:QUIC_CACHED_CERT cached_cert_length=%d--------------------\n",cached_cert.length);
|
||||
for(i = 0; i < cached_cert.length; i++){
|
||||
printf("%02X",((unsigned char*)cached_cert.ptr_value)[i]);
|
||||
}
|
||||
printf("----------------------------------------\n");
|
||||
}
|
||||
|
||||
if(session_info->prot_flag == QUIC_COMM_CERT){
|
||||
printf("DPKT_QUIC_ENTRY\tQUIC_COMM_CERT\n");
|
||||
struct quic_stream *quic = (struct quic_stream*)session_info->app_info;
|
||||
quic_tlv_t comm_cert = quic->common_cert;
|
||||
printf("--------------------BUSINESS PLUG:QUIC_COMM_CERT common_cert_length=%d--------------------\n",comm_cert.length);
|
||||
for(i = 0; i < comm_cert.length; i++){
|
||||
printf("%02X",((unsigned char*)comm_cert.ptr_value)[i]);
|
||||
}
|
||||
printf("--------------------T--------------------\n");
|
||||
}
|
||||
|
||||
if(session_info->prot_flag == QUIC_CERT_CHAIN){
|
||||
printf("DPKT_QUIC_ENTRY\tQUIC_CERT_CHAIN\n");
|
||||
struct quic_stream *quic = (struct quic_stream*)session_info->app_info;
|
||||
quic_tlv_t cert_chain = quic->cert_chain;
|
||||
printf("--------------------BUSINESS PLUG:QUIC_CERT_CHAIN cert_chain_length=%d--------------------\n",cert_chain.length);
|
||||
for(i = 0; i < cert_chain.length; i++){
|
||||
printf("%02X",((unsigned char*)cert_chain.ptr_value)[i]);
|
||||
}
|
||||
printf("----------------------------------------\n");
|
||||
}
|
||||
|
||||
|
||||
return PROT_STATE_GIVEME;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
/*
|
||||
* dk_plug_quic.h
|
||||
*
|
||||
* Created on:
|
||||
* Author: root
|
||||
*/
|
||||
|
||||
#ifndef SRC_DPKT_PLUG_QUIC_H_
|
||||
#define SRC_DPKT_PLUG_QUIC_H_
|
||||
|
||||
#include "stream.h"
|
||||
|
||||
#define DK_PLUGID_GQUIC 1003
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
int DPKT_GQUIC_INIT();
|
||||
void DPKT_GQUIC_DESTROY();
|
||||
char DPKT_GQUIC_ENTRY(stSessionInfo* session_info, void **pme, int _thread_num, struct streaminfo *pstream, void *a_packet);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif /* SRC_DPKT_PLUG_QUIC_H_ */
|
||||
@@ -1,13 +0,0 @@
|
||||
[PLUGINFO]
|
||||
PLUGNAME=dpkt_plug_gquic
|
||||
SO_PATH=./plug/business/dpkt_plug_gquic/dpkt_plug_gquic.so
|
||||
INIT_FUNC=DPKT_GQUIC_INIT
|
||||
DESTROY_FUNC=DPKT_GQUIC_DESTROY
|
||||
|
||||
[QUIC]
|
||||
#FUNC_FLAG=QUIC_CLIENT_HELLO,QUIC_SERVER_HELLO,QUIC_CACHED_CERT,QUIC_COMM_CERT,QUIC_CERT_CHAIN,QUIC_VERSION,QUIC_APPLICATION_DATA
|
||||
|
||||
FUNC_FLAG=QUIC_CLIENT_HELLO
|
||||
FUNC_NAME=DPKT_GQUIC_ENTRY
|
||||
|
||||
|
||||
1
test/empty_array.json
Normal file
1
test/empty_array.json
Normal file
@@ -0,0 +1 @@
|
||||
[]
|
||||
BIN
test/pcap/1-iquic.pcap
Executable file
BIN
test/pcap/1-iquic.pcap
Executable file
Binary file not shown.
6
test/quic_result.json
Normal file
6
test/quic_result.json
Normal file
@@ -0,0 +1,6 @@
|
||||
[{
|
||||
"Tuple4": "192.168.50.29.61891>31.13.77.35.443",
|
||||
"VERSION": "IETF QUIC 29",
|
||||
"SNI": "www.facebook.com",
|
||||
"name": "QUIC_RESULT_1"
|
||||
}]
|
||||
91
test/quic_test_plug.cpp
Normal file
91
test/quic_test_plug.cpp
Normal file
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* author:yangwei
|
||||
* create time:2021-8-21
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "cJSON.h"
|
||||
#include "gquic.h"
|
||||
#include "MESA_prof_load.h"
|
||||
#include <MESA/stream.h>
|
||||
|
||||
extern "C" int commit_test_result_json(cJSON *node, const char *name);
|
||||
|
||||
static int g_result_count = 1;
|
||||
|
||||
extern "C" unsigned char QUIC_TEST_PLUG_ENTRY(stSessionInfo *session_info, void **pme,
|
||||
int thread_seq, struct streaminfo *a_tcp, void *a_packet)
|
||||
{
|
||||
assert(NULL != session_info || pme != NULL);
|
||||
|
||||
cJSON *ctx = (cJSON *)*pme;
|
||||
struct _quic_info *quic_info=NULL;
|
||||
char version_str[128]={0};
|
||||
unsigned int version = 0;
|
||||
|
||||
if (session_info->session_state & SESSION_STATE_PENDING)
|
||||
{
|
||||
if (*pme == NULL)
|
||||
{
|
||||
ctx = cJSON_CreateObject();
|
||||
*pme = (void *)ctx;
|
||||
cJSON_AddStringToObject(ctx, "Tuple4", printaddr(&a_tcp->addr, a_tcp->threadnum));
|
||||
}
|
||||
}
|
||||
|
||||
switch (session_info->prot_flag)
|
||||
{
|
||||
case QUIC_CLIENT_HELLO:
|
||||
if (session_info == NULL || session_info->app_info == NULL)
|
||||
{
|
||||
break;
|
||||
}
|
||||
quic_info = (struct _quic_info *)session_info->app_info;
|
||||
cJSON_AddStringToObject(ctx, "SNI", (char *)(quic_info->client_hello->ext_tags[quic_info->client_hello->sni_idx].value));
|
||||
break;
|
||||
case QUIC_USEING_VERSION:
|
||||
version = *(unsigned int *)(session_info->buf);
|
||||
quic_version_int2string(version, version_str, sizeof(version_str));
|
||||
cJSON_AddStringToObject(ctx, "VERSION", version_str);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if(session_info->session_state&SESSION_STATE_CLOSE)
|
||||
{
|
||||
if(ctx)
|
||||
{
|
||||
char result_name[16]="";
|
||||
sprintf(result_name,"QUIC_RESULT_%d", g_result_count);
|
||||
commit_test_result_json(ctx, result_name);
|
||||
g_result_count+=1;
|
||||
}
|
||||
*pme = NULL;
|
||||
return PROT_STATE_DROPME;
|
||||
|
||||
}
|
||||
|
||||
return PROT_STATE_GIVEME;
|
||||
|
||||
}
|
||||
|
||||
extern "C" int QUIC_TEST_PLUG_INIT()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern "C" void QUIC_TEST_PLUG_DESTROY(void)
|
||||
{
|
||||
return ;
|
||||
}/*CHAR_DESTRORY*/
|
||||
|
||||
|
||||
|
||||
11
test/quic_test_plug.inf
Normal file
11
test/quic_test_plug.inf
Normal file
@@ -0,0 +1,11 @@
|
||||
[PLUGINFO]
|
||||
PLUGNAME=QUIC_TEST_PLUG
|
||||
SO_PATH=./plug/business/quic_test_plug/quic_test_plug.so
|
||||
INIT_FUNC=QUIC_TEST_PLUG_INIT
|
||||
DESTROY_FUNC=QUIC_TEST_PLUG_DESTROY
|
||||
|
||||
[QUIC]
|
||||
FUNC_FLAG=QUIC_CLIENT_HELLO,QUIC_SERVER_HELLO,QUIC_CACHED_CERT,QUIC_COMM_CERT,QUIC_CERT_CHAIN,QUIC_VERSION,QUIC_APPLICATION_DATA
|
||||
FUNC_NAME=QUIC_TEST_PLUG_ENTRY
|
||||
|
||||
|
||||
BIN
test/test_protocol_run.zip
Normal file
BIN
test/test_protocol_run.zip
Normal file
Binary file not shown.
Reference in New Issue
Block a user