quic: Squashed follow-ups: quic.log, tests, various fixes, performance

This commit is contained in:
Arne Welzel 2023-08-15 16:14:42 +02:00
parent 44d7c45723
commit 359f8d2ae6
65 changed files with 1194 additions and 532 deletions

View file

@ -1 +1,2 @@
@load ./consts
@load ./main @load ./main

View file

@ -0,0 +1,7 @@
module QUIC;
export {
const version_strings: table[count] of string = {
[0x00000001] = "1",
} &default=function(version: count): string { return fmt("unknown-%x", version); };
}

View file

@ -1,2 +1,215 @@
##! Initial idea for a quic.log.
@load base/frameworks/notice/weird
@load base/protocols/conn/removal-hooks
@load ./consts
module QUIC; module QUIC;
export {
redef enum Log::ID += { LOG };
type Info: record {
## Timestamp of first QUIC packet for this entry.
ts: time &log;
## Unique ID for the connection.
uid: string &log;
## The connection's 4-tuple of endpoint addresses/ports.
id: conn_id &log;
## QUIC version as found in the first INITIAL packet from
## the client.
version: string &log;
## First Destination Connection ID used by client. This is
## random and unpredictable, but used for packet protection
## by client and server.
client_initial_dcid: string &log &optional;
## Server chosen Connection ID usually from server's first
## INITIAL packet. This is to be used by the client in
## subsequent packets.
server_scid: string &log &optional;
## Server name extracted from SNI extension in ClientHello
## packet if available.
server_name: string &log &optional;
## First protocol extracted from ALPN extension in ClientHello
## packet if available.
client_protocol: string &log &optional;
## Experimental QUIC history.
##
## Letters have the following meaning with client-sent
## letters being capitalized:
##
## ====== ====================================================
## Letter Meaning
## ====== ====================================================
## I INIT packet
## H HANDSHAKE packet
## Z 0RTT packet
## R RETRY packet
## C CONNECTION_CLOSE packet
## S SSL Client/Server Hello
## ====== ====================================================
history: string &log &default="";
# Internal state for the history field.
history_state: vector of string;
# Internal state if this record has already been logged.
logged: bool &default=F;
};
global log_quic: event(rec: Info);
global log_policy: Log::PolicyHook;
global finalize_quic: Conn::RemovalHook;
}
redef record connection += {
# XXX: We may have multiple QUIC connections with different
# Connection ID over the same UDP connection.
quic: Info &optional;
};
# Faster to modify here than re-compiling .evt files.
const quic_ports = {
443/udp, # HTTP3-over-QUIC
853/udp, # DNS-over-QUIC
784/udp, # DNS-over-QUIC early
};
function add_to_history(quic: Info, is_orig: bool, what: string)
{
if ( |quic$history_state| == 10 )
return;
quic$history_state += is_orig ? to_upper(what[0]) : to_lower(what[0]);
}
function log_record(quic: Info)
{
quic$history = join_string_vec(quic$history_state, "");
Log::write(LOG, quic);
quic$logged = T;
}
function set_conn(c: connection, is_orig: bool, version: count, dcid: string, scid: string)
{
if ( ! c?$quic )
{
c$quic = Info(
$ts=network_time(),
$uid=c$uid,
$id=c$id,
$version=version_strings[version],
);
Conn::register_removal_hook(c, finalize_quic);
}
if ( is_orig && |dcid| > 0 && ! c$quic?$client_initial_dcid )
c$quic$client_initial_dcid = bytestring_to_hexstr(dcid);
if ( ! is_orig && |scid| > 0 )
c$quic$server_scid = bytestring_to_hexstr(scid);
}
event QUIC::initial_packet(c: connection, is_orig: bool, version: count, dcid: string, scid: string)
{
set_conn(c, is_orig, version, dcid, scid);
add_to_history(c$quic, is_orig, "INIT");
}
event QUIC::handshake_packet(c: connection, is_orig: bool, version: count, dcid: string, scid: string)
{
set_conn(c, is_orig, version, dcid, scid);
add_to_history(c$quic, is_orig, "HANDSHAKE");
}
event QUIC::zero_rtt_packet(c: connection, is_orig: bool, version: count, dcid: string, scid: string)
{
set_conn(c, is_orig, version, dcid, scid);
add_to_history(c$quic, is_orig, "ZeroRTT");
}
# RETRY packets trigger a log entry and state reset.
event QUIC::retry_packet(c: connection, is_orig: bool, version: count, dcid: string, scid: string, retry_token: string, integrity_tag: string)
{
if ( ! c?$quic )
set_conn(c, is_orig, version, dcid, scid);
add_to_history(c$quic, is_orig, "RETRY");
log_record(c$quic);
delete c$quic;
}
# Upon a connection_close_frame(), if any c$quic state is pending to be logged, do so
# now and prepare for a new entry.
event QUIC::connection_close_frame(c: connection, is_orig: bool, version: count, dcid: string, scid: string, error_code: count, reason_phrase: string)
{
if ( ! c?$quic )
return;
add_to_history(c$quic, is_orig, "CONNECTION_CLOSE");
log_record(c$quic);
delete c$quic;
}
event ssl_extension_server_name(c: connection, is_client: bool, names: string_vec) &priority=5
{
if ( is_client && c?$quic && |names| > 0 )
c$quic$server_name = names[0];
}
event ssl_extension_application_layer_protocol_negotiation(c: connection, is_client: bool, protocols: string_vec)
{
if ( c?$quic && is_client )
{
c$quic$client_protocol = protocols[0];
if ( |protocols| > 1 )
# Probably not overly weird, but the quic.log only
# works with the first one in the hope to avoid
# vector or concatenation.
Reporter::conn_weird("QUIC_many_protocols", c, cat(protocols));
}
}
event ssl_client_hello(c: connection, version: count, record_version: count, possible_ts: time, client_random: string, session_id: string, ciphers: index_vec, comp_methods: index_vec)
{
if ( ! c?$quic )
return;
add_to_history(c$quic, T, "SSL");
}
event ssl_server_hello(c: connection, version: count, record_version: count, possible_ts: time, server_random: string, session_id: string, cipher: count, comp_method: count) &priority=-5
{
if ( ! c?$quic )
return;
add_to_history(c$quic, F, "SSL");
}
hook finalize_quic(c: connection)
{
if ( ! c?$quic || c$quic$logged )
return;
log_record(c$quic);
}
event zeek_init()
{
Log::create_stream(LOG, [$columns=Info, $ev=log_quic, $path="quic", $policy=log_policy]);
Analyzer::register_for_ports(Analyzer::ANALYZER_QUIC, quic_ports);
}

View file

@ -1,5 +1,23 @@
spicy_add_analyzer( spicy_add_analyzer(
NAME QUIC NAME QUIC
PACKAGE_NAME QUIC PACKAGE_NAME QUIC
SOURCES decrypt_crypto.cc QUIC.spicy QUIC.evt zeek_QUIC.spicy SOURCES QUIC.spicy QUIC.evt
SCRIPTS __load__.zeek main.zeek) SCRIPTS __load__.zeek main.zeek
CXX_LINK ${CMAKE_CURRENT_BINARY_DIR}/libdecrypt_crypto.a)
add_dependencies(QUIC decrypt_crypto)
find_program(SPICY_CONFIG name spicy-config REQUIRED)
execute_process(
COMMAND ${SPICY_CONFIG} --include-dirs
OUTPUT_VARIABLE SPICY_INCLUDE_DIRS)
string(REPLACE " " ";" SPICY_INCLUDE_DIRS ${SPICY_INCLUDE_DIRS})
find_package(OpenSSL REQUIRED)
add_library(decrypt_crypto STATIC decrypt_crypto.cc)
set_target_properties(
decrypt_crypto PROPERTIES
CXX_STANDARD 17
POSITION_INDEPENDENT_CODE ON)
target_include_directories(decrypt_crypto PRIVATE "${OPENSSL_INCLUDE_DIR}" "${SPICY_INCLUDE_DIRS}")
target_link_libraries(decrypt_crypto ${OpenSSL_LIBRARIES})

View file

@ -1,10 +1,19 @@
protocol analyzer spicy::QUIC over UDP: protocol analyzer QUIC over UDP:
parse originator with QUIC::RequestFrame, parse originator with QUIC::RequestFrame,
parse responder with QUIC::ResponseFrame, parse responder with QUIC::ResponseFrame;
ports { 443/udp };
import QUIC; import QUIC;
import Zeek_QUIC;
# TODO: Add actual events, instead of this dummy event # Make the enum available.
on QUIC::ResponseFrame -> event QUIC::example($conn); export QUIC::LongPacketType;
on QUIC::InitialPacket -> event QUIC::initial_packet($conn, $is_orig, self.header.version, self.header.dest_conn_id, self.header.src_conn_id);
on QUIC::RetryPacket -> event QUIC::retry_packet($conn, $is_orig, self.header.version, self.header.dest_conn_id, self.header.src_conn_id, self.retry_token, self.integrity_tag);
on QUIC::HandshakePacket -> event QUIC::handshake_packet($conn, $is_orig, self.header.version, self.header.dest_conn_id, self.header.src_conn_id);
on QUIC::ZeroRTTPacket -> event QUIC::zero_rtt_packet($conn, $is_orig, self.header.version, self.header.dest_conn_id, self.header.src_conn_id);
on QUIC::ConnectionClosePayload -> event QUIC::connection_close_frame($conn, $is_orig, self.header.version, self.header.dest_conn_id, self.header.src_conn_id,
self.error_code.result, self.reason_phrase);

View file

@ -4,26 +4,74 @@ import spicy;
import zeek; import zeek;
# The interface to the C++ code that handles the decryption of the INITIAL packet payload using well-known keys # The interface to the C++ code that handles the decryption of the INITIAL packet payload using well-known keys
public function decrypt_crypto_payload(entire_packet: bytes, connection_id: bytes, encrypted_offset: uint64, payload_offset: uint64, from_client: bool): bytes &cxxname="decrypt_crypto_payload"; public function decrypt_crypto_payload(
all_data: bytes,
connection_id: bytes,
encrypted_offset: uint64,
payload_offset: uint64,
from_client: bool
): bytes &cxxname="QUIC_decrypt_crypto_payload";
############## ##############
## Context - tracked in one connection ## Context - tracked in one connection
############## ##############
type ConnectionIDInfo = unit { # Can we decrypt?
var client_cid_len: uint8; function can_decrypt(long_header: LongHeaderPacket, context: ConnectionIDInfo, is_client: bool): bool {
var server_cid_len: uint8;
var initial_destination_conn_id: bytes;
var initial_packets_exchanged: bool;
var initialized: bool;
on %init { if ( long_header.first_byte.packet_type != LongPacketType::INITIAL )
self.client_cid_len = 0; return False;
self.server_cid_len = 0;
self.initial_packets_exchanged = False; # decrypt_crypto_payload() has known secrets for version 1, nothing else.
self.initialized = False; if ( long_header.version != 0x00000001 )
return False;
if ( is_client )
return ! context.client_initial_processed;
# This is the responder, can only decrypt if we have an initial
# destination_id from the client
return context.client_initial_processed
&& |context.initial_destination_conn_id| > 0
&& ! context.server_initial_processed;
} }
type ConnectionIDInfo = struct {
client_cid_len: uint8;
server_cid_len: uint8;
# The DCID used by the client is employed by client and
# server for packet protection. Packet re-ordering
# will make life miserable.
#
# https://quicwg.org/base-drafts/rfc9001.html#appendix-A
initial_destination_conn_id: bytes;
# Currently, this analyzer assumes that ClientHello
# and ServerHello fit into the first INITIAL packet (and
# that there is only one that we're interested in.
#
# But minimally the following section sounds like this might not
# hold in general and the Wireshark has samples showing
# the handshake spanning across more than two INITIAL packets.
# (quic-fragmented-handshakes.pcapng.gz)
#
# https://datatracker.ietf.org/doc/html/rfc9001#section-4.3
#
# Possible fix is to buffer up all CRYPTO frames across multiple
# INITIAL packets until we see a non-INITIAL frame.
#
# We also rely heavily on getting originator and responder right.
#
client_initial_processed: bool;
server_initial_processed: bool;
@if SPICY_VERSION >= 10800
ssl_handle: zeek::ProtocolHandle &optional;
@else
did_ssl_begin: bool;
@endif
}; };
############## ##############
@ -80,74 +128,26 @@ type FrameType = enum {
# Helper units # Helper units
############## ##############
# Used to peek into the next byte and determine if it's a long or short packet
public type InitialByte = unit {
initialbyte: bitfield(8) {
header_form: 7 &convert=cast<HeaderForm>(cast<uint8>($$));
};
on %done{
self.backtrack();
}
};
# Used to peek into the next byte and check it's value
type InitialUint8 = unit {
var bt: uint8;
: uint8 {
self.bt = $$;
}
on %done{
self.backtrack();
}
};
# https://datatracker.ietf.org/doc/rfc9000/
# Section 16 and Appendix A
type VariableLengthIntegerLength = unit {
var length: uint8;
a: bitfield(8) {
length: 6..7 &convert=cast<uint8>($$) &byte-order=spicy::ByteOrder::Big;
};
on %done {
self.length = self.a.length;
self.backtrack();
}
};
type VariableLengthInteger = unit { type VariableLengthInteger = unit {
var bytes_to_parse: uint64; var bytes_to_parse: uint64;
var result: uint64; var result: uint64;
var result_bytes: bytes;
: VariableLengthIntegerLength &try { # Value of the two most significant bits indicates number of bytes
switch ( $$.length ) { # to parse for the variable length integer.
case 0: #
self.bytes_to_parse = 1; # https://datatracker.ietf.org/doc/rfc9000/
case 1: # Section 16 and Appendix A
self.bytes_to_parse = 2; #
case 2: first_byte: bytes &size=1 {
self.bytes_to_parse = 4; local uint8_val = uint8($$.to_uint(spicy::ByteOrder::Big));
case 3: self.bytes_to_parse = 2**((0xC0 & uint8_val) >> 6);
self.bytes_to_parse = 8; # Re-pack without most significant two bits for later use.
} self.first_byte = pack(0x3f & uint8_val, spicy::ByteOrder::Big);
} }
remaining_bytes: bytes &size=self.bytes_to_parse - 1;
# Parse the required amount of bytes and apply a mask to clear the on %done {
# first two bits, leaving the actual length self.result = (self.first_byte + self.remaining_bytes).to_uint(spicy::ByteOrder::Big);
remainder: bytes &size=self.bytes_to_parse {
switch ( self.bytes_to_parse ) {
case 1:
self.result = $$.to_uint(spicy::ByteOrder::Big) & 0x3f;
case 2:
self.result = $$.to_uint(spicy::ByteOrder::Big) & 0x3fff;
case 4:
self.result = $$.to_uint(spicy::ByteOrder::Big) & 0x3fffffff;
case 8:
self.result = $$.to_uint(spicy::ByteOrder::Big) & 0x3fffffffffffffff;
}
} }
}; };
@ -156,22 +156,7 @@ type VariableLengthInteger = unit {
# Generic units # Generic units
############## ##############
# Used to capture all data form the entire frame. May be inefficient, but works for now. public type LongHeaderPacket = unit {
# This is passed to the decryption function, as this function needs both the header and the payload
# Performs a backtrack() at the end
type AllData = unit {
var data: bytes;
: bytes &eod {
self.data = $$;
}
on %done {
self.backtrack();
}
};
public type LongHeader = unit {
var encrypted_offset: uint64; var encrypted_offset: uint64;
var payload_length: uint64; var payload_length: uint64;
var client_conn_id_length: uint8; var client_conn_id_length: uint8;
@ -190,84 +175,68 @@ public type LongHeader = unit {
src_conn_id_len: uint8 { self.client_conn_id_length = $$; } src_conn_id_len: uint8 { self.client_conn_id_length = $$; }
src_conn_id: bytes &size=self.client_conn_id_length; src_conn_id: bytes &size=self.client_conn_id_length;
# We pass the type specific 4 bits too and don't parse them again
switch ( self.first_byte.packet_type ) { switch ( self.first_byte.packet_type ) {
LongPacketType::INITIAL -> initial_hdr : InitialLongPacketHeader(self.first_byte.type_specific_bits) { LongPacketType::INITIAL -> initial_hdr : InitialPacket(self) {
self.encrypted_offset = self.offset() + self.encrypted_offset = self.offset() +
self.initial_hdr.payload_length.bytes_to_parse + self.initial_hdr.length.bytes_to_parse +
self.initial_hdr.token_length.bytes_to_parse + self.initial_hdr.token_length.bytes_to_parse +
self.initial_hdr.token_length.result; self.initial_hdr.token_length.result;
self.payload_length = self.initial_hdr.payload_length.result; self.payload_length = self.initial_hdr.length.result;
} }
LongPacketType::ZERO_RTT -> zerortt_hdr : ZeroRTTLongPacketHeader(self.first_byte.type_specific_bits);
LongPacketType::HANDSHAKE -> handshake_hdr : HandshakeLongPacketHeader(self.first_byte.type_specific_bits); LongPacketType::ZERO_RTT -> zerortt_hdr : ZeroRTTPacket(self);
LongPacketType::RETRY -> retry_hdr : RetryLongPacketHeader(self.first_byte.type_specific_bits); LongPacketType::HANDSHAKE -> handshake_hdr : HandshakePacket(self);
LongPacketType::RETRY -> retry_hdr : RetryPacket(self);
}; };
}; };
# Decrypted long packet payload that can actually be parsed # A QUIC Frame.
public type DecryptedLongPacketPayload = unit(packet_type: LongPacketType, from_client: bool) { public type Frame = unit(header: LongHeaderPacket, from_client: bool, crypto_sink: sink) {
frame_type : uint8 &convert=cast<FrameType>($$); frame_type : uint8 &convert=cast<FrameType>($$);
# TODO: add other FrameTypes as well # TODO: add other FrameTypes as well
switch ( self.frame_type ) { switch ( self.frame_type ) {
FrameType::ACK1 -> a: ACKPayload; FrameType::ACK1 -> a: ACKPayload;
FrameType::ACK2 -> b: ACKPayload; FrameType::ACK2 -> b: ACKPayload;
FrameType::CRYPTO -> c: CRYPTOPayload(from_client); FrameType::CRYPTO -> c: CRYPTOPayload(from_client) {
FrameType::PADDING -> d: PADDINGPayload; # Have the sink re-assemble potentially out-of-order cryptodata
crypto_sink.write(self.c.cryptodata, self.c.offset.result);
}
FrameType::CONNECTION_CLOSE1 -> : ConnectionClosePayload(header);
@if SPICY_VERSION >= 10800
FrameType::PADDING -> : skip /\x00*/; # eat the padding
@else
FrameType::PADDING -> : /\x00*/; # eat the padding
@endif
FrameType::PING -> : void;
* -> : void {
throw "unhandled frame type %s in %s" % (self.frame_type, header.first_byte.packet_type);
}
}; };
}; };
# TODO: investigate whether we can do something useful with this
public type EncryptedLongPacketPayload = unit {
payload: bytes &eod;
};
# Determines how to parse the long packet payload, depending on whether is was decrypted or not
public type LongPacketPayload = unit(packet_type: LongPacketType, from_client: bool, encrypted: bool) {
: DecryptedLongPacketPayload(packet_type, from_client) if (encrypted == False);
: EncryptedLongPacketPayload if (encrypted == True);
};
type CRYPTOPayload = unit(from_client: bool) { type CRYPTOPayload = unit(from_client: bool) {
var length_in_byte1: bytes; offset: VariableLengthInteger;
var length_in_byte2: bytes;
offset: uint8;
length: VariableLengthInteger; length: VariableLengthInteger;
cryptodata: bytes &size=self.length.result; cryptodata: bytes &size=self.length.result;
on %done {
# As of 5 Sept. 2022 there is no function to convert a unsigned integer back to bytes.
# Therefore, the following (quite dirty) method is used. Should be fixed/improved whenever
# a better alternative is available.
# It converts a uint16 to its two-byte representation.
self.length_in_byte1 = ("%c" % cast<uint8>((self.length.result >> 8) & 0xff)).encode();
self.length_in_byte2 = ("%c" % cast<uint8>(self.length.result & 0xff)).encode();
# The data is passed to the SSL analyzer as part of a HANDSHAKE (0x16) message with TLS1.3 (\x03\x03).
# The 2 length bytes are also passed, followed by the actual CRYPTO blob which contains a CLIENT HELLO or SERVER HELLO
zeek::protocol_data_in(from_client, b"\x16\x03\x03" + self.length_in_byte1 + self.length_in_byte2 + self.cryptodata);
}
}; };
type ACKPayload = unit { type ACKPayload = unit {
latest_ack: uint8; latest_ack: VariableLengthInteger;
ack_delay: uint8; ack_delay: VariableLengthInteger;
ack_range_count: uint8; ack_range_count: VariableLengthInteger;
first_ack_range: uint8; first_ack_range: VariableLengthInteger;
}; };
public type NullBytes = unit { type ConnectionClosePayload = unit(header: LongHeaderPacket) {
: (b"\x00")[]; var header: LongHeaderPacket = header;
x: InitialUint8 &try; error_code: VariableLengthInteger;
switch {
-> unknown_frame_type: b"\x00";
-> frame_type: VariableLengthInteger;
}; };
reason_phrase_length: VariableLengthInteger;
type PADDINGPayload = unit { reason_phrase: bytes &size=self.reason_phrase_length.result;
var padding_length: uint64 = 0;
# Simply consume all next nullbytes
: NullBytes;
}; };
@ -276,25 +245,68 @@ type PADDINGPayload = unit {
# Specific long packet type units # Specific long packet type units
############## ##############
type InitialLongPacketHeader = unit(type_specific_bits: uint8) { # Remainder of an Initial packet
var packet_number_length_full: uint8; type InitialPacket = unit(header: LongHeaderPacket) {
var header: LongHeaderPacket = header;
token_length: VariableLengthInteger; token_length: VariableLengthInteger;
token: bytes &size=self.token_length.result; token: bytes &size=self.token_length.result;
payload_length: VariableLengthInteger;
packet_number: bytes &size=self.packet_number_length_full &convert=$$.to_uint(spicy::ByteOrder::Big);
on %init { # 5.4.2. Header Protection Sample
# Calculate the packet number length while the initial byte is still encoded. #
# Will result in 0, 1, 2 or 3. So we need to read n+1 bytes to properly parse the header. # That is, in sampling packet ciphertext for header
self.packet_number_length_full = (type_specific_bits & 0x03) + 1; # protection, the Packet Number field is assumed to
} # be 4 bytes long (its maximum possible encoded length).
#
# Enforce 4 bytes Packet Number length + 16 bytes sample
# ciphertext available.
length: VariableLengthInteger &requires=self.length.result >= 20;
# Consume the remainder of payload. This
# includes the packet number field, but we
# do not know its length yet. We need the
# payload for sampling, however.
@if SPICY_VERSION >= 10800
payload: skip bytes &size=self.length.result;
@else
payload: bytes &size=self.length.result;
@endif
}; };
# TODO: implement type ZeroRTTPacket = unit(header: LongHeaderPacket) {
type ZeroRTTLongPacketHeader = unit(type_specific_bits: uint8) {}; var header: LongHeaderPacket = header;
type HandshakeLongPacketHeader = unit(type_specific_bits: uint8) {}; length: VariableLengthInteger;
type RetryLongPacketHeader = unit(type_specific_bits: uint8) {}; @if SPICY_VERSION >= 10800
payload: skip bytes &size=self.length.result;
@else
payload: bytes &size=self.length.result;
@endif
};
type HandshakePacket = unit(header: LongHeaderPacket) {
var header: LongHeaderPacket = header;
length: VariableLengthInteger;
@if SPICY_VERSION >= 10800
payload: skip bytes &size=self.length.result;
@else
payload: bytes &size=self.length.result;
@endif
};
type RetryPacket = unit(header: LongHeaderPacket) {
var header: LongHeaderPacket = header;
var retry_token: bytes;
var integrity_tag: bytes;
# A retry packet ends with a 128bit / 16 byte integrity
# tag, but otherwise we do not know anything about the
# size of the retry_token. Slurp the whole datagram and
# post split it into the distinct parts.
data: bytes &eod {
self.retry_token = self.data.sub(0, |self.data| - 16);
self.integrity_tag = self.data.sub(|self.data| - 16, |self.data|);
}
};
############## ##############
# Short packets # Short packets
@ -313,34 +325,104 @@ public type ShortHeader = unit(dest_conn_id_length: uint8) {
# TODO: investigate whether we can parse something useful out of this # TODO: investigate whether we can parse something useful out of this
public type ShortPacketPayload = unit { public type ShortPacketPayload = unit {
@if SPICY_VERSION >= 10800
payload: skip bytes &eod;
@else
payload: bytes &eod; payload: bytes &eod;
@endif
};
# TODO: investigate whether we can do something useful with this
public type EncryptedLongPacketPayload = unit {
@if SPICY_VERSION >= 10800
payload: skip bytes &eod;
@else
payload: bytes &eod;
@endif
};
# Buffer all crypto messages (which might be fragmented and unordered)
# into the following unit.
type CryptoBuffer = unit() {
var buffered: bytes;
: bytes &chunked &eod {
self.buffered += $$;
# print "crypto_buffer got data", |$$|, |self.buffered|;
}
}; };
############## ##############
# QUIC frame parsing # QUIC packet parsing
#
# A UDP datagram contains one or more QUIC packets.
############## ##############
type Frame = unit(from_client: bool, context: ConnectionIDInfo&) { type Packet = unit(from_client: bool, context: ConnectionIDInfo&) {
var hdr_form: HeaderForm;
var decrypted_data: bytes; var decrypted_data: bytes;
var full_packet: bytes; var full_packet: bytes;
var start: iterator<stream>;
# Peek into the header to check if it's a SHORT or LONG header sink crypto_sink;
: InitialByte &try { var crypto_buffer: CryptoBuffer&;
self.hdr_form = $$.initialbyte.header_form;
# Attach an SSL analyzer to this connection once.
on %init {
@if SPICY_VERSION >= 10800
if ( ! context?.ssl_handle ) {
context.ssl_handle = zeek::protocol_handle_get_or_create("SSL");
}
@else
if ( ! context.did_ssl_begin ) {
zeek::protocol_begin("SSL");
context.did_ssl_begin = True;
}
@endif
self.start = self.input();
}
# Peek into the first byte and determine the header type.
first_byte: bitfield(8) {
header_form: 7 &convert=HeaderForm($$);
};
# TODO: Consider bitfield based look-ahead-parsing in the switch below
# to avoid this rewinding here. It's a hack.
: void {
self.set_input(self.start); # rewind
} }
# Capture all the packet bytes if we're still have a chance of decrypting the INITIAL PACKETS
fpack: AllData &try if (context.initial_packets_exchanged == False);
# Depending on the header, parse it and update the src/dest ConnectionID's # Depending on the header, parse it and update the src/dest ConnectionID's
switch ( self.hdr_form ) { switch ( self.first_byte.header_form ) {
HeaderForm::SHORT -> short_header: ShortHeader(context.client_cid_len); HeaderForm::SHORT -> short_header: ShortHeader(context.client_cid_len);
HeaderForm::LONG -> long_header: LongHeader { HeaderForm::LONG -> long_header: LongHeaderPacket {
# For now, only allow a change of src/dest ConnectionID's for INITIAL packets. # For now, only allow a change of src/dest ConnectionID's for INITIAL packets.
# TODO: allow this for Retry packets
if ( self.long_header.first_byte.packet_type == LongPacketType::INITIAL # If we see a retry packet from the responder, reset the decryption
&& context.initial_packets_exchanged == False ) { # context such that the next DCID from the client is used for decryption.
if ( self.long_header.first_byte.packet_type == LongPacketType::RETRY ) {
context.client_initial_processed = False;
context.server_initial_processed = False;
context.initial_destination_conn_id = b"";
# Allow re-opening the SSL analyzer the next time around.
@if SPICY_VERSION >= 10800
zeek::protocol_handle_close(context.ssl_handle);
unset context.ssl_handle;
@else
zeek::protocol_end();
context.did_ssl_begin = False;
@endif
}
}
};
# Slurp in the whole packet if we determined we have a chance to decrypt.
all_data: bytes &parse-at=self.start &eod if ( self?.long_header && can_decrypt(self.long_header, context, from_client) ) {
self.crypto_buffer = new CryptoBuffer();
self.crypto_sink.connect(self.crypto_buffer);
if ( from_client ) { if ( from_client ) {
context.server_cid_len = self.long_header.dest_conn_id_len; context.server_cid_len = self.long_header.dest_conn_id_len;
@ -348,15 +430,16 @@ type Frame = unit(from_client: bool, context: ConnectionIDInfo&) {
# This means that here, we can try to decrypt the initial packet! # This means that here, we can try to decrypt the initial packet!
# All data is accessible via the `long_header` unit # All data is accessible via the `long_header` unit
self.decrypted_data = decrypt_crypto_payload(
self.decrypted_data = decrypt_crypto_payload(self.fpack.data, self.all_data,
self.long_header.dest_conn_id, self.long_header.dest_conn_id,
self.long_header.encrypted_offset, self.long_header.encrypted_offset,
self.long_header.payload_length, self.long_header.payload_length,
from_client); from_client
);
# Set this to be the seed for the decryption # Set this to be the seed for the decryption
if ( ! context.initial_packets_exchanged ) { if ( |context.initial_destination_conn_id| == 0 ) {
context.initial_destination_conn_id = self.long_header.dest_conn_id; context.initial_destination_conn_id = self.long_header.dest_conn_id;
} }
@ -366,33 +449,69 @@ type Frame = unit(from_client: bool, context: ConnectionIDInfo&) {
# Assuming that the client set up the connection, this can be considered the first # Assuming that the client set up the connection, this can be considered the first
# received Initial from the client. So disable change of ConnectionID's afterwards # received Initial from the client. So disable change of ConnectionID's afterwards
self.decrypted_data = decrypt_crypto_payload(self.fpack.data, self.decrypted_data = decrypt_crypto_payload(
self.all_data,
context.initial_destination_conn_id, context.initial_destination_conn_id,
self.long_header.encrypted_offset, self.long_header.encrypted_offset,
self.long_header.payload_length, self.long_header.payload_length,
from_client); from_client
} );
} }
# If it's a reply from the server and it's not a REPLY, we assume the keys are restablished and decryption is no longer possible # We attempted decryption, but it failed. Just reject the
# input and assume Zeek will disable the analyzer for this
# connection.
if ( |self.decrypted_data| == 0 )
throw "decryption failed";
# If this was a reply from the server and it's not a RETRY, we assume the keys
# are restablished and decryption is no longer possible
#
# TODO: verify if this is actually correct per RFC # TODO: verify if this is actually correct per RFC
if ( self.long_header.first_byte.packet_type != LongPacketType::RETRY && ! from_client ) { if ( self.long_header.first_byte.packet_type != LongPacketType::RETRY && ! from_client ) {
context.initial_packets_exchanged = True; context.server_initial_processed = True;
context.client_initial_processed = True;
} }
} }
};
# Depending on the type of header, we parse the remaining payload. # Depending on the type of header and whether we were able to decrypt
switch ( self.hdr_form ) { # some of it, parse the remaining payload.
HeaderForm::SHORT -> remaining_short_payload: ShortPacketPayload; : ShortPacketPayload if (self.first_byte.header_form == HeaderForm::SHORT);
HeaderForm::LONG -> remaining_long_payload : LongPacketPayload(self.long_header.first_byte.packet_type, from_client, context.initial_packets_exchanged)[] &parse-from=self.decrypted_data; : EncryptedLongPacketPayload if (self.first_byte.header_form == HeaderForm::LONG && |self.decrypted_data| == 0);
};
on %init { # If this was packet with a long header and decrypted data exists, attempt
# Make sure to only attach the SSL analyzer once per QUIC connection # to parse the plain QUIC frames from it.
if ( ! context.initialized ) { frames: Frame(self.long_header, from_client, self.crypto_sink)[] &parse-from=self.decrypted_data if (self.first_byte.header_form == HeaderForm::LONG && |self.decrypted_data| > 0);
context.initialized = True;
zeek::protocol_begin("SSL"); # Once the Packet is fully parsed, pass the accumulated CRYPTO frames
# to the SSL analyzer as handshake data.
on %done {
# print "packet done", zeek::is_orig(), self.first_byte.header_form, |self.decrypted_data|;
if ( self.crypto_buffer != Null && |self.crypto_buffer.buffered| > 0 ) {
local handshake_data = self.crypto_buffer.buffered;
# The data is passed to the SSL analyzer as part of a HANDSHAKE (0x16) message with TLS1.3 (\x03\x03).
# The 2 length bytes are also passed, followed by the actual CRYPTO blob which contains a CLIENT HELLO or SERVER HELLO
local length_bytes = pack(cast<uint16>(|handshake_data|), spicy::ByteOrder::Big);
zeek::protocol_data_in(
from_client
, b"\x16\x03\x03" + length_bytes + handshake_data
# With Spicy 1.8.0, can use the SSL handle directly.
@if SPICY_VERSION >= 10800
, context.ssl_handle
@endif
);
# Stop decryption attempts after processing the very first
# INITIAL packet.
if ( from_client )
context.client_initial_processed = True;
else
context.server_initial_processed = True;
# Take buffered crypto data as confirmation signal.
spicy::accept_input();
} }
} }
}; };
@ -402,10 +521,10 @@ type Frame = unit(from_client: bool, context: ConnectionIDInfo&) {
############## ##############
public type RequestFrame = unit { public type RequestFrame = unit {
%context = ConnectionIDInfo; %context = ConnectionIDInfo;
: Frame(True, self.context()); : Packet(True, self.context());
}; };
public type ResponseFrame = unit { public type ResponseFrame = unit {
%context = ConnectionIDInfo; %context = ConnectionIDInfo;
: Frame(False, self.context()); : Packet(False, self.context());
}; };

View file

@ -8,30 +8,41 @@ refactors as C++ development is not our main profession.
*/ */
// Default imports // Default imports
#include <stdlib.h> #include <array>
#include <cstdint>
#include <cstdlib>
#include <cstring> #include <cstring>
#include <vector>
#include <iostream>
#include <string> #include <string>
#include <vector>
// OpenSSL imports // OpenSSL imports
#include <openssl/kdf.h>
#include <openssl/evp.h> #include <openssl/evp.h>
#include <openssl/kdf.h>
#include <openssl/sha.h> #include <openssl/sha.h>
// Import HILTI // Import HILTI
#include <hilti/rt/libhilti.h> #include <hilti/rt/libhilti.h>
namespace
{
// Struct to store decryption info for this specific connection // Struct to store decryption info for this specific connection
struct DecryptionInformation struct DecryptionInformation
{ {
std::vector<uint8_t> unprotected_header; std::vector<uint8_t> unprotected_header;
std::vector<uint8_t> protected_header;
uint64_t packet_number; uint64_t packet_number;
std::vector<uint8_t> nonce; std::vector<uint8_t> nonce;
uint8_t packet_number_length; uint8_t packet_number_length;
}; };
// Return rt::hilti::Bytes::data() value as const uint8_t*
//
// This should be alright: https://stackoverflow.com/a/15172304
inline const uint8_t* data_as_uint8(const hilti::rt::Bytes& b)
{
return reinterpret_cast<const uint8_t*>(b.data());
}
/* /*
Constants used in the HKDF functions. HKDF-Expand-Label uses labels Constants used in the HKDF functions. HKDF-Expand-Label uses labels
such as 'quic key' and 'quic hp'. These labels can obviously be such as 'quic key' and 'quic hp'. These labels can obviously be
@ -39,41 +50,25 @@ calculated dynamically, but are incluced statically for now, as the
goal of this analyser is only to analyze the INITIAL packets. goal of this analyser is only to analyze the INITIAL packets.
*/ */
std::vector<uint8_t> INITIAL_SALT_V1 = { std::vector<uint8_t> INITIAL_SALT_V1 = {0x38, 0x76, 0x2c, 0xf7, 0xf5, 0x59, 0x34, 0xb3, 0x4d, 0x17,
0x38, 0x76, 0x2c, 0xf7, 0xf5, 0x9a, 0xe6, 0xa4, 0xc8, 0x0c, 0xad, 0xcc, 0xbb, 0x7f, 0x0a};
0x59, 0x34, 0xb3, 0x4d, 0x17,
0x9a, 0xe6, 0xa4, 0xc8, 0x0c,
0xad, 0xcc, 0xbb, 0x7f, 0x0a};
std::vector<uint8_t> CLIENT_INITIAL_INFO = { std::vector<uint8_t> CLIENT_INITIAL_INFO = {0x00, 0x20, 0x0f, 0x74, 0x6c, 0x73, 0x31,
0x00, 0x20, 0x0f, 0x74, 0x6c, 0x33, 0x20, 0x63, 0x6c, 0x69, 0x65, 0x6e,
0x73, 0x31, 0x33, 0x20, 0x63, 0x74, 0x20, 0x69, 0x6e, 0x00};
0x6c, 0x69, 0x65, 0x6e, 0x74,
0x20, 0x69, 0x6e, 0x00};
std::vector<uint8_t> SERVER_INITIAL_INFO = { std::vector<uint8_t> SERVER_INITIAL_INFO = {0x00, 0x20, 0x0f, 0x74, 0x6c, 0x73, 0x31,
0x00, 0x20, 0x0f, 0x74, 0x6c, 0x33, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65,
0x73, 0x31, 0x33, 0x20, 0x73, 0x72, 0x20, 0x69, 0x6e, 0x00};
0x65, 0x72, 0x76, 0x65, 0x72,
0x20, 0x69, 0x6e, 0x00};
std::vector<uint8_t> KEY_INFO = { std::vector<uint8_t> KEY_INFO = {0x00, 0x10, 0x0e, 0x74, 0x6c, 0x73, 0x31, 0x33, 0x20,
0x00, 0x10, 0x0e, 0x74, 0x6c, 0x71, 0x75, 0x69, 0x63, 0x20, 0x6b, 0x65, 0x79, 0x00};
0x73, 0x31, 0x33, 0x20, 0x71,
0x75, 0x69, 0x63, 0x20, 0x6b,
0x65, 0x79, 0x00};
std::vector<uint8_t> IV_INFO = { std::vector<uint8_t> IV_INFO = {0x00, 0x0c, 0x0d, 0x74, 0x6c, 0x73, 0x31, 0x33, 0x20,
0x00, 0x0c, 0x0d, 0x74, 0x6c, 0x71, 0x75, 0x69, 0x63, 0x20, 0x69, 0x76, 0x00};
0x73, 0x31, 0x33, 0x20, 0x71,
0x75, 0x69, 0x63, 0x20, 0x69,
0x76, 0x00};
std::vector<uint8_t> HP_INFO = { std::vector<uint8_t> HP_INFO = {0x00, 0x10, 0x0d, 0x74, 0x6c, 0x73, 0x31, 0x33, 0x20,
0x00, 0x10, 0x0d, 0x74, 0x6c, 0x71, 0x75, 0x69, 0x63, 0x20, 0x68, 0x70, 0x00};
0x73, 0x31, 0x33, 0x20, 0x71,
0x75, 0x69, 0x63, 0x20, 0x68,
0x70, 0x00};
/* /*
Constants used by the different functions Constants used by the different functions
@ -88,9 +83,9 @@ const size_t MAXIMUM_PACKET_LENGTH = 1500;
const size_t MAXIMUM_PACKET_NUMBER_LENGTH = 4; const size_t MAXIMUM_PACKET_NUMBER_LENGTH = 4;
/* /*
HKDF-Extract as decribed in https://www.rfc-editor.org/rfc/rfc8446.html#section-7.1 HKDF-Extract as described in https://www.rfc-editor.org/rfc/rfc8446.html#section-7.1
*/ */
std::vector<uint8_t> hkdf_extract(std::vector<uint8_t> connection_id) std::vector<uint8_t> hkdf_extract(const hilti::rt::Bytes& connection_id)
{ {
std::vector<uint8_t> out_temp(INITIAL_SECRET_LEN); std::vector<uint8_t> out_temp(INITIAL_SECRET_LEN);
size_t initial_secret_len = out_temp.size(); size_t initial_secret_len = out_temp.size();
@ -99,26 +94,19 @@ std::vector<uint8_t> hkdf_extract(std::vector<uint8_t> connection_id)
EVP_PKEY_derive_init(pctx); EVP_PKEY_derive_init(pctx);
EVP_PKEY_CTX_hkdf_mode(pctx, EVP_PKEY_HKDEF_MODE_EXTRACT_ONLY); EVP_PKEY_CTX_hkdf_mode(pctx, EVP_PKEY_HKDEF_MODE_EXTRACT_ONLY);
EVP_PKEY_CTX_set_hkdf_md(pctx, digest); EVP_PKEY_CTX_set_hkdf_md(pctx, digest);
EVP_PKEY_CTX_set1_hkdf_key(pctx, EVP_PKEY_CTX_set1_hkdf_key(pctx, data_as_uint8(connection_id), connection_id.size());
connection_id.data(), EVP_PKEY_CTX_set1_hkdf_salt(pctx, INITIAL_SALT_V1.data(), INITIAL_SALT_V1.size());
connection_id.size()); EVP_PKEY_derive(pctx, out_temp.data(), &initial_secret_len);
EVP_PKEY_CTX_set1_hkdf_salt(pctx,
INITIAL_SALT_V1.data(),
INITIAL_SALT_V1.size());
EVP_PKEY_derive(pctx,
out_temp.data(),
reinterpret_cast<size_t *>(&initial_secret_len));
EVP_PKEY_CTX_free(pctx); EVP_PKEY_CTX_free(pctx);
return out_temp; return out_temp;
} }
/* /*
HKDF-Expand-Label as decribed in https://www.rfc-editor.org/rfc/rfc8446.html#section-7.1 HKDF-Expand-Label as described in https://www.rfc-editor.org/rfc/rfc8446.html#section-7.1
that uses the global constant labels such as 'quic hp'. that uses the global constant labels such as 'quic hp'.
*/ */
std::vector<uint8_t> hkdf_expand(size_t out_len, std::vector<uint8_t> hkdf_expand(size_t out_len, const std::vector<uint8_t>& key,
std::vector<uint8_t> key, const std::vector<uint8_t>& info)
std::vector<uint8_t> info)
{ {
std::vector<uint8_t> out_temp(out_len); std::vector<uint8_t> out_temp(out_len);
const EVP_MD* digest = EVP_sha256(); const EVP_MD* digest = EVP_sha256();
@ -134,9 +122,12 @@ std::vector<uint8_t> hkdf_expand(size_t out_len,
} }
/* /*
Removes the header protection from the INITIAL packet and returns a DecryptionInformation struct that is partially filled Removes the header protection from the INITIAL packet and returns a DecryptionInformation struct
that is partially filled
*/ */
DecryptionInformation remove_header_protection(std::vector<uint8_t> client_hp, uint8_t encrypted_offset, std::vector<uint8_t> encrypted_packet) DecryptionInformation remove_header_protection(const std::vector<uint8_t>& client_hp,
uint64_t encrypted_offset,
const hilti::rt::Bytes& all_data)
{ {
DecryptionInformation decryptInfo; DecryptionInformation decryptInfo;
int outlen; int outlen;
@ -147,20 +138,19 @@ DecryptionInformation remove_header_protection(std::vector<uint8_t> client_hp, u
// Passing an 1 means ENCRYPT // Passing an 1 means ENCRYPT
EVP_CipherInit_ex(ctx, NULL, NULL, client_hp.data(), NULL, 1); EVP_CipherInit_ex(ctx, NULL, NULL, client_hp.data(), NULL, 1);
std::vector<uint8_t> sample(encrypted_packet.begin() + static_assert(AEAD_SAMPLE_LENGTH > 0);
encrypted_offset + assert(all_data.size() >= encrypted_offset + MAXIMUM_PACKET_NUMBER_LENGTH + AEAD_SAMPLE_LENGTH);
MAXIMUM_PACKET_NUMBER_LENGTH,
encrypted_packet.begin() + const uint8_t* sample = data_as_uint8(all_data) + encrypted_offset +
encrypted_offset + MAXIMUM_PACKET_NUMBER_LENGTH;
MAXIMUM_PACKET_NUMBER_LENGTH +
AEAD_SAMPLE_LENGTH); std::array<uint8_t, AEAD_SAMPLE_LENGTH> mask;
std::vector<uint8_t> mask(sample.size()); EVP_CipherUpdate(ctx, mask.data(), &outlen, sample, AEAD_SAMPLE_LENGTH);
EVP_CipherUpdate(ctx, mask.data(), &outlen, sample.data(), AEAD_SAMPLE_LENGTH); EVP_CIPHER_CTX_free(ctx);
// To determine the actual packet number length, // To determine the actual packet number length,
// we have to remove the mask from the first byte // we have to remove the mask from the first byte
uint8_t first_byte = encrypted_packet[0]; uint8_t first_byte = data_as_uint8(all_data)[0];
if ( first_byte & 0x80 ) if ( first_byte & 0x80 )
{ {
@ -175,11 +165,8 @@ DecryptionInformation remove_header_protection(std::vector<uint8_t> client_hp, u
int recovered_packet_number_length = (first_byte & 0x03) + 1; int recovered_packet_number_length = (first_byte & 0x03) + 1;
// .. and use this to reconstruct the (partially) unprotected header // .. and use this to reconstruct the (partially) unprotected header
std::vector<uint8_t> unprotected_header( std::vector<uint8_t> unprotected_header(data_as_uint8(all_data),
encrypted_packet.begin(), data_as_uint8(all_data) + encrypted_offset +
encrypted_packet.begin() +
encrypted_offset +
recovered_packet_number_length); recovered_packet_number_length);
uint32_t decoded_packet_number = 0; uint32_t decoded_packet_number = 0;
@ -188,20 +175,14 @@ DecryptionInformation remove_header_protection(std::vector<uint8_t> client_hp, u
for ( int i = 0; i < recovered_packet_number_length; ++i ) for ( int i = 0; i < recovered_packet_number_length; ++i )
{ {
unprotected_header[encrypted_offset + i] ^= mask[1 + i]; unprotected_header[encrypted_offset + i] ^= mask[1 + i];
decoded_packet_number = decoded_packet_number = unprotected_header[encrypted_offset + i] |
unprotected_header[encrypted_offset + i] |
(decoded_packet_number << 8); (decoded_packet_number << 8);
} }
std::vector<uint8_t> protected_header(encrypted_packet.begin(),
encrypted_packet.begin() +
encrypted_offset +
recovered_packet_number_length);
// Store the information back in the struct // Store the information back in the struct
decryptInfo.packet_number = decoded_packet_number; decryptInfo.packet_number = decoded_packet_number;
decryptInfo.packet_number_length = recovered_packet_number_length; decryptInfo.packet_number_length = recovered_packet_number_length;
decryptInfo.protected_header = protected_header; decryptInfo.unprotected_header = std::move(unprotected_header);
decryptInfo.unprotected_header = unprotected_header;
return decryptInfo; return decryptInfo;
} }
@ -211,108 +192,83 @@ decoded packet number, and returns the nonce
*/ */
std::vector<uint8_t> calculate_nonce(std::vector<uint8_t> client_iv, uint64_t packet_number) std::vector<uint8_t> calculate_nonce(std::vector<uint8_t> client_iv, uint64_t packet_number)
{ {
std::vector<uint8_t> nonce = client_iv;
for ( int i = 0; i < 8; ++i ) for ( int i = 0; i < 8; ++i )
{ client_iv[AEAD_IV_LEN - 1 - i] ^= (uint8_t)(packet_number >> 8 * i);
nonce[AEAD_IV_LEN - 1 - i] ^=
(uint8_t)(packet_number >> 8 * i);
}
// Return the nonce return client_iv;
return nonce;
} }
/* /*
Function that calls the AEAD decryption routine, and returns the Function that calls the AEAD decryption routine, and returns the
decrypted data decrypted data
*/ */
std::vector<uint8_t> decrypt(std::vector<uint8_t> client_key,
std::vector<uint8_t> encrypted_packet, hilti::rt::Bytes decrypt(const std::vector<uint8_t>& client_key, const hilti::rt::Bytes& all_data,
uint64_t payload_offset, uint64_t payload_length, const DecryptionInformation& decryptInfo)
DecryptionInformation decryptInfo)
{ {
int out, out2, res; int out, out2, res;
std::vector<uint8_t> encrypted_payload(
encrypted_packet.begin() +
decryptInfo.protected_header.size(),
encrypted_packet.begin() + if ( payload_length < decryptInfo.packet_number_length + AEAD_TAG_LENGTH )
decryptInfo.protected_header.size() + throw hilti::rt::RuntimeError(
payload_offset - hilti::rt::fmt("payload too small %ld < %ld", payload_length,
decryptInfo.packet_number_length - decryptInfo.packet_number_length + AEAD_TAG_LENGTH));
AEAD_TAG_LENGTH);
std::vector<uint8_t> tag_to_check( const uint8_t* encrypted_payload = data_as_uint8(all_data) +
encrypted_packet.begin() + decryptInfo.unprotected_header.size();
decryptInfo.protected_header.size() +
payload_offset -
decryptInfo.packet_number_length -
AEAD_TAG_LENGTH,
encrypted_packet.begin() + int encrypted_payload_size = payload_length - decryptInfo.packet_number_length -
decryptInfo.protected_header.size() + AEAD_TAG_LENGTH;
payload_offset -
decryptInfo.packet_number_length);
unsigned char decrypt_buffer[MAXIMUM_PACKET_LENGTH]; if ( encrypted_payload_size < 0 )
throw hilti::rt::RuntimeError(
hilti::rt::fmt("encrypted_payload_size underflow %ld", encrypted_payload_size));
if ( all_data.size() <
decryptInfo.unprotected_header.size() + encrypted_payload_size + AEAD_TAG_LENGTH )
throw hilti::rt::RuntimeError(
hilti::rt::fmt("all_data too short %ld < %ld", all_data.size(),
decryptInfo.unprotected_header.size() + encrypted_payload_size));
const void* tag_to_check = all_data.data() + decryptInfo.unprotected_header.size() +
encrypted_payload_size;
int tag_to_check_length = AEAD_TAG_LENGTH;
std::array<uint8_t, MAXIMUM_PACKET_LENGTH> decrypt_buffer;
// Setup context // Setup context
auto cipher = EVP_aes_128_gcm(); auto cipher = EVP_aes_128_gcm();
auto ctx = EVP_CIPHER_CTX_new(); auto ctx = EVP_CIPHER_CTX_new();
EVP_CipherInit_ex(ctx, EVP_CipherInit_ex(ctx, cipher, NULL, NULL, NULL, 0);
cipher,
NULL,
NULL,
NULL,
0);
// Set the sizes for the IV and KEY // Set the sizes for the IV and KEY
EVP_CIPHER_CTX_ctrl(ctx, EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_SET_IVLEN, decryptInfo.nonce.size(), NULL);
EVP_CTRL_CCM_SET_IVLEN,
decryptInfo.nonce.size(),
NULL);
EVP_CIPHER_CTX_set_key_length(ctx, EVP_CIPHER_CTX_set_key_length(ctx, client_key.size());
client_key.size());
// Set the KEY and IV // Set the KEY and IV
EVP_CipherInit_ex(ctx, EVP_CipherInit_ex(ctx, NULL, NULL, client_key.data(), decryptInfo.nonce.data(), 0);
NULL,
NULL,
client_key.data(),
decryptInfo.nonce.data(),
0);
// Set the tag to be validated after decryption // Set the tag to be validated after decryption
EVP_CIPHER_CTX_ctrl(ctx, EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_SET_TAG, tag_to_check_length,
EVP_CTRL_CCM_SET_TAG, const_cast<void*>(tag_to_check));
tag_to_check.size(),
tag_to_check.data());
// Setting the second parameter to NULL will pass it as Associated Data // Setting the second parameter to NULL will pass it as Associated Data
EVP_CipherUpdate(ctx, EVP_CipherUpdate(ctx, NULL, &out, decryptInfo.unprotected_header.data(),
NULL,
&out,
decryptInfo.unprotected_header.data(),
decryptInfo.unprotected_header.size()); decryptInfo.unprotected_header.size());
// Set the actual data to decrypt data into the decrypt_buffer. The amount of // Set the actual data to decrypt data into the decrypt_buffer. The amount of
// byte decrypted is stored into `out` // byte decrypted is stored into `out`
EVP_CipherUpdate(ctx, EVP_CipherUpdate(ctx, decrypt_buffer.data(), &out, encrypted_payload, encrypted_payload_size);
decrypt_buffer,
&out,
encrypted_payload.data(),
encrypted_payload.size());
// Validate whether the decryption was successful or not // Validate whether the decryption was successful or not
EVP_CipherFinal_ex(ctx, NULL, &out2); EVP_CipherFinal_ex(ctx, NULL, &out2);
EVP_CIPHER_CTX_free(ctx);
// Copy the decrypted data from the decrypted buffer into a Bytes instance.
return hilti::rt::Bytes(decrypt_buffer.data(), decrypt_buffer.data() + out);
}
// Copy the decrypted data from the decrypted buffer into a new vector and return this
// Use the `out` variable to only include relevant bytes
std::vector<uint8_t> decrypted_data(decrypt_buffer, decrypt_buffer + out);
return decrypted_data;
} }
/* /*
@ -320,61 +276,40 @@ Function that is called from Spicy. It's a wrapper around `process_data`;
it stores all the passed data in a global struct and then calls `process_data`, it stores all the passed data in a global struct and then calls `process_data`,
which will eventually return the decrypted data and pass it back to Spicy. which will eventually return the decrypted data and pass it back to Spicy.
*/ */
hilti::rt::Bytes decrypt_crypto_payload( hilti::rt::Bytes
const hilti::rt::Bytes &entire_packet, QUIC_decrypt_crypto_payload(const hilti::rt::Bytes& all_data, const hilti::rt::Bytes& connection_id,
const hilti::rt::Bytes &connection_id,
const hilti::rt::integer::safe<uint64_t>& encrypted_offset, const hilti::rt::integer::safe<uint64_t>& encrypted_offset,
const hilti::rt::integer::safe<uint64_t> &payload_offset, const hilti::rt::integer::safe<uint64_t>& payload_length,
const hilti::rt::Bool& from_client) const hilti::rt::Bool& from_client)
{ {
// Fill in the entire packet bytes if ( payload_length < 20 )
std::vector<uint8_t> e_pkt; throw hilti::rt::RuntimeError(hilti::rt::fmt("payload too small %ld < 20", payload_length));
for (const auto &singlebyte : entire_packet)
{
e_pkt.push_back(singlebyte);
}
std::vector<uint8_t> cnnid; if ( (all_data.size() < encrypted_offset + payload_length) )
for (const auto &singlebyte : connection_id) throw hilti::rt::RuntimeError(hilti::rt::fmt("packet too small %ld %ld", all_data.size(),
{ encrypted_offset + payload_length));
cnnid.push_back(singlebyte);
}
std::vector<uint8_t> initial_secret = hkdf_extract(cnnid); std::vector<uint8_t> initial_secret = hkdf_extract(connection_id);
std::vector<uint8_t> server_client_secret; std::vector<uint8_t> server_client_secret;
if ( from_client ) if ( from_client )
{ {
server_client_secret = hkdf_expand(INITIAL_SECRET_LEN, server_client_secret = hkdf_expand(INITIAL_SECRET_LEN, initial_secret, CLIENT_INITIAL_INFO);
initial_secret,
CLIENT_INITIAL_INFO);
} }
else else
{ {
server_client_secret = hkdf_expand(INITIAL_SECRET_LEN, server_client_secret = hkdf_expand(INITIAL_SECRET_LEN, initial_secret, SERVER_INITIAL_INFO);
initial_secret,
SERVER_INITIAL_INFO);
} }
std::vector<uint8_t> key = hkdf_expand(AEAD_KEY_LEN, std::vector<uint8_t> key = hkdf_expand(AEAD_KEY_LEN, server_client_secret, KEY_INFO);
server_client_secret, std::vector<uint8_t> iv = hkdf_expand(AEAD_IV_LEN, server_client_secret, IV_INFO);
KEY_INFO); std::vector<uint8_t> hp = hkdf_expand(AEAD_HP_LEN, server_client_secret, HP_INFO);
std::vector<uint8_t> iv = hkdf_expand(AEAD_IV_LEN,
server_client_secret,
IV_INFO);
std::vector<uint8_t> hp = hkdf_expand(AEAD_HP_LEN,
server_client_secret,
HP_INFO);
DecryptionInformation decryptInfo = remove_header_protection(hp, (uint8_t)encrypted_offset, e_pkt); DecryptionInformation decryptInfo = remove_header_protection(hp, encrypted_offset, all_data);
// Calculate the correct nonce for the decryption // Calculate the correct nonce for the decryption
decryptInfo.nonce = calculate_nonce(iv, decryptInfo.packet_number); decryptInfo.nonce = calculate_nonce(iv, decryptInfo.packet_number);
std::vector<uint8_t> decrypted_data = decrypt(key, e_pkt, payload_offset, decryptInfo); return decrypt(key, all_data, payload_length, decryptInfo);
// Return it as hilti Bytes again
hilti::rt::Bytes decr(decrypted_data.begin(), decrypted_data.end());
return decr;
} }

View file

@ -1,12 +0,0 @@
module Zeek_QUIC;
import zeek;
import QUIC;
on QUIC::ResponseFrame::%done {
zeek::confirm_protocol();
}
on QUIC::ResponseFrame::%error {
zeek::reject_protocol("error while parsing QUIC message");
}

View file

@ -0,0 +1,3 @@
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.
ts uid history service
XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 Dd quic,ssl

View file

@ -0,0 +1,11 @@
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.
#separator \x09
#set_separator ,
#empty_field (empty)
#unset_field -
#path quic
#open XXXX-XX-XX-XX-XX-XX
#fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p version client_initial_dcid server_scid server_name client_protocol history
#types time string addr port addr port string string string string string string
XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 82.239.54.117 53727 110.213.53.115 443 1 95412c47018cdfe8 d5412c47018cdfe8 api.cirrus-ci.com h3 ISisH
#close XXXX-XX-XX-XX-XX-XX

View file

@ -0,0 +1,11 @@
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.
#separator \x09
#set_separator ,
#empty_field (empty)
#unset_field -
#path ssl
#open XXXX-XX-XX-XX-XX-XX
#fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p version cipher curve server_name resumed last_alert next_protocol established ssl_history cert_chain_fps client_cert_chain_fps sni_matches_cert
#types time string addr port addr port string string string string bool string string bool string vector[string] vector[string] bool
XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 82.239.54.117 53727 110.213.53.115 443 TLSv13 TLS_AES_128_GCM_SHA256 x25519 api.cirrus-ci.com T - - F Cs - - -
#close XXXX-XX-XX-XX-XX-XX

View file

@ -0,0 +1,3 @@
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.
ts uid history service
XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 Dd quic,ssl

View file

@ -0,0 +1,11 @@
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.
#separator \x09
#set_separator ,
#empty_field (empty)
#unset_field -
#path quic
#open XXXX-XX-XX-XX-XX-XX
#fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p version client_initial_dcid server_scid server_name client_protocol history
#types time string addr port addr port string string string string string string
XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 172.17.0.2 34347 64.233.166.94 443 1 815d62c70884f4b51e8ccadd5beed372 c15d62c70884f4b5 www.google.de h3 ISishIhHhh
#close XXXX-XX-XX-XX-XX-XX

View file

@ -0,0 +1,11 @@
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.
#separator \x09
#set_separator ,
#empty_field (empty)
#unset_field -
#path ssl
#open XXXX-XX-XX-XX-XX-XX
#fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p version cipher curve server_name resumed last_alert next_protocol established ssl_history cert_chain_fps client_cert_chain_fps sni_matches_cert
#types time string addr port addr port string string string string bool string string bool string vector[string] vector[string] bool
XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 172.17.0.2 34347 64.233.166.94 443 TLSv13 TLS_AES_128_GCM_SHA256 x25519 www.google.de F - - F Cs - - -
#close XXXX-XX-XX-XX-XX-XX

View file

@ -0,0 +1 @@
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.

View file

@ -0,0 +1,46 @@
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.
1.0, initial_packet, C4J4Th3PJpwUYZZ6gc, T, 1, 4a8294bf9201d6cf,
1.0, retry_packet, C4J4Th3PJpwUYZZ6gc, F, 1, , 1b036a11, 98, 9b3ac827ccae092e3f8fdf84ec8ee526
1.0, initial_packet, C4J4Th3PJpwUYZZ6gc, T, 1, 1b036a11,
1.0, initial_packet, C4J4Th3PJpwUYZZ6gc, F, 1, , fc674735
1.0, handshake_packet, F, C4J4Th3PJpwUYZZ6gc, 1, , fc674735
1.0, initial_packet, C4J4Th3PJpwUYZZ6gc, T, 1, fc674735,
1.0, handshake_packet, T, C4J4Th3PJpwUYZZ6gc, 1, ef3a4e06,
zerortt.pcap
1.0, initial_packet, C4J4Th3PJpwUYZZ6gc, T, 1, b7c7841c64883e3261d840,
1.0, initial_packet, C4J4Th3PJpwUYZZ6gc, F, 1, , 8d2041ac
1.0, handshake_packet, F, C4J4Th3PJpwUYZZ6gc, 1, , 8d2041ac
1.0, initial_packet, C4J4Th3PJpwUYZZ6gc, T, 1, 8d2041ac,
1.0, handshake_packet, T, C4J4Th3PJpwUYZZ6gc, 1, 5b7bc400,
1.0, initial_packet, CtPZjS20MLrsMUOJi2, T, 1, 15ae5e5e4962163f410b5529fc125bbc,
1.0, zero_rtt_packet, T, CtPZjS20MLrsMUOJi2, 1, 15ae5e5e4962163f410b5529fc125bbc,
1.0, initial_packet, CtPZjS20MLrsMUOJi2, F, 1, , e483a751
1.0, zero_rtt_packet, T, CtPZjS20MLrsMUOJi2, 1, 15ae5e5e4962163f410b5529fc125bbc,
1.0, zero_rtt_packet, T, CtPZjS20MLrsMUOJi2, 1, 15ae5e5e4962163f410b5529fc125bbc,
1.0, zero_rtt_packet, T, CtPZjS20MLrsMUOJi2, 1, 15ae5e5e4962163f410b5529fc125bbc,
1.0, zero_rtt_packet, T, CtPZjS20MLrsMUOJi2, 1, 15ae5e5e4962163f410b5529fc125bbc,
1.0, zero_rtt_packet, T, CtPZjS20MLrsMUOJi2, 1, 15ae5e5e4962163f410b5529fc125bbc,
1.0, zero_rtt_packet, T, CtPZjS20MLrsMUOJi2, 1, 15ae5e5e4962163f410b5529fc125bbc,
1.0, zero_rtt_packet, T, CtPZjS20MLrsMUOJi2, 1, 15ae5e5e4962163f410b5529fc125bbc,
1.0, zero_rtt_packet, T, CtPZjS20MLrsMUOJi2, 1, 15ae5e5e4962163f410b5529fc125bbc,
1.0, zero_rtt_packet, T, CtPZjS20MLrsMUOJi2, 1, 15ae5e5e4962163f410b5529fc125bbc,
1.0, zero_rtt_packet, T, CtPZjS20MLrsMUOJi2, 1, 15ae5e5e4962163f410b5529fc125bbc,
1.0, zero_rtt_packet, T, CtPZjS20MLrsMUOJi2, 1, 15ae5e5e4962163f410b5529fc125bbc,
1.0, zero_rtt_packet, T, CtPZjS20MLrsMUOJi2, 1, 15ae5e5e4962163f410b5529fc125bbc,
1.0, zero_rtt_packet, T, CtPZjS20MLrsMUOJi2, 1, 15ae5e5e4962163f410b5529fc125bbc,
1.0, zero_rtt_packet, T, CtPZjS20MLrsMUOJi2, 1, 15ae5e5e4962163f410b5529fc125bbc,
1.0, zero_rtt_packet, T, CtPZjS20MLrsMUOJi2, 1, 15ae5e5e4962163f410b5529fc125bbc,
1.0, zero_rtt_packet, T, CtPZjS20MLrsMUOJi2, 1, 15ae5e5e4962163f410b5529fc125bbc,
1.0, zero_rtt_packet, T, CtPZjS20MLrsMUOJi2, 1, 15ae5e5e4962163f410b5529fc125bbc,
1.0, zero_rtt_packet, T, CtPZjS20MLrsMUOJi2, 1, 15ae5e5e4962163f410b5529fc125bbc,
1.0, zero_rtt_packet, T, CtPZjS20MLrsMUOJi2, 1, 15ae5e5e4962163f410b5529fc125bbc,
1.0, zero_rtt_packet, T, CtPZjS20MLrsMUOJi2, 1, 15ae5e5e4962163f410b5529fc125bbc,
1.0, zero_rtt_packet, T, CtPZjS20MLrsMUOJi2, 1, 15ae5e5e4962163f410b5529fc125bbc,
1.0, zero_rtt_packet, T, CtPZjS20MLrsMUOJi2, 1, 15ae5e5e4962163f410b5529fc125bbc,
1.0, zero_rtt_packet, T, CtPZjS20MLrsMUOJi2, 1, 15ae5e5e4962163f410b5529fc125bbc,
1.0, zero_rtt_packet, T, CtPZjS20MLrsMUOJi2, 1, 15ae5e5e4962163f410b5529fc125bbc,
1.0, zero_rtt_packet, T, CtPZjS20MLrsMUOJi2, 1, 15ae5e5e4962163f410b5529fc125bbc,
1.0, zero_rtt_packet, T, CtPZjS20MLrsMUOJi2, 1, 15ae5e5e4962163f410b5529fc125bbc,
1.0, zero_rtt_packet, T, CtPZjS20MLrsMUOJi2, 1, 15ae5e5e4962163f410b5529fc125bbc,
1.0, initial_packet, CtPZjS20MLrsMUOJi2, T, 1, 3ec82f67,
1.0, handshake_packet, T, CtPZjS20MLrsMUOJi2, 1, 3ec82f67,

View file

@ -0,0 +1,3 @@
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.
ts uid history service
XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 Dd quic,ssl

View file

@ -0,0 +1,11 @@
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.
#separator \x09
#set_separator ,
#empty_field (empty)
#unset_field -
#path quic
#open XXXX-XX-XX-XX-XX-XX
#fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p version client_initial_dcid server_scid server_name client_protocol history
#types time string addr port addr port string string string string string string
XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 82.239.54.117 44174 250.58.23.113 443 1 c5a5015ae8f479784a 01275b138ee6aca8a6276b132ae6b3547cf7773f blog.cloudflare.com h3 ISiihIhhhH
#close XXXX-XX-XX-XX-XX-XX

View file

@ -0,0 +1,11 @@
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.
#separator \x09
#set_separator ,
#empty_field (empty)
#unset_field -
#path ssl
#open XXXX-XX-XX-XX-XX-XX
#fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p version cipher curve server_name resumed last_alert next_protocol established ssl_history cert_chain_fps client_cert_chain_fps sni_matches_cert
#types time string addr port addr port string string string string bool string string bool string vector[string] vector[string] bool
XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 82.239.54.117 44174 250.58.23.113 443 - - - blog.cloudflare.com F - - F C - - -
#close XXXX-XX-XX-XX-XX-XX

View file

@ -0,0 +1,3 @@
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.
ts uid history service
XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 Dd quic,ssl

View file

@ -0,0 +1,3 @@
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.
ts uid version cipher curve server_name resumed last_alert next_protocol established ssl_history
XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 TLSv13 TLS_AES_128_GCM_SHA256 x25519 www.google.de F - - F Cs

View file

@ -0,0 +1 @@
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.

View file

@ -0,0 +1,5 @@
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.
ts uid history service
0.015059 ClEkJM2Vm5giqnMf4h - -
0.001000 CHhAvVGS1DHFjwGM9 - -
0.648580 C4J4Th3PJpwUYZZ6gc Dd quic,ssl

View file

@ -0,0 +1,11 @@
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.
#separator \x09
#set_separator ,
#empty_field (empty)
#unset_field -
#path quic
#open XXXX-XX-XX-XX-XX-XX
#fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p version client_initial_dcid server_scid server_name client_protocol history
#types time string addr port addr port string string string string string string
1.000000 C4J4Th3PJpwUYZZ6gc 193.167.0.100 40084 193.167.100.100 443 1 a771f6161a4072c0bf10 5911deff server4:443 hq-interop ISishIH
#close XXXX-XX-XX-XX-XX-XX

View file

@ -0,0 +1,11 @@
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.
#separator \x09
#set_separator ,
#empty_field (empty)
#unset_field -
#path ssl
#open XXXX-XX-XX-XX-XX-XX
#fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p version cipher curve server_name resumed last_alert next_protocol established ssl_history cert_chain_fps client_cert_chain_fps sni_matches_cert
#types time string addr port addr port string string string string bool string string bool string vector[string] vector[string] bool
1.000000 C4J4Th3PJpwUYZZ6gc 193.167.0.100 40084 193.167.100.100 443 TLSv13 TLS_AES_128_GCM_SHA256 x25519 server4:443 F - - F Cs - - -
#close XXXX-XX-XX-XX-XX-XX

View file

@ -0,0 +1 @@
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.

View file

@ -0,0 +1,5 @@
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.
ts uid history service
XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 - -
0.016059 ClEkJM2Vm5giqnMf4h - -
0.669020 C4J4Th3PJpwUYZZ6gc Dd quic,ssl

View file

@ -0,0 +1,12 @@
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.
#separator \x09
#set_separator ,
#empty_field (empty)
#unset_field -
#path quic
#open XXXX-XX-XX-XX-XX-XX
#fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p version client_initial_dcid server_scid server_name client_protocol history
#types time string addr port addr port string string string string string string
1.000000 C4J4Th3PJpwUYZZ6gc 193.167.0.100 42834 193.167.100.100 443 1 4a8294bf9201d6cf - server4:443 hq-interop ISr
1.000000 C4J4Th3PJpwUYZZ6gc 193.167.0.100 42834 193.167.100.100 443 1 1b036a11 fc674735 server4:443 hq-interop ISishIH
#close XXXX-XX-XX-XX-XX-XX

View file

@ -0,0 +1,11 @@
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.
#separator \x09
#set_separator ,
#empty_field (empty)
#unset_field -
#path ssl
#open XXXX-XX-XX-XX-XX-XX
#fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p version cipher curve server_name resumed last_alert next_protocol established ssl_history cert_chain_fps client_cert_chain_fps sni_matches_cert
#types time string addr port addr port string string string string bool string string bool string vector[string] vector[string] bool
1.000000 C4J4Th3PJpwUYZZ6gc 193.167.0.100 42834 193.167.100.100 443 TLSv13 TLS_AES_128_GCM_SHA256 x25519 server4:443 F - - F CCs - - -
#close XXXX-XX-XX-XX-XX-XX

View file

@ -0,0 +1 @@
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.

View file

@ -0,0 +1,6 @@
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.
ts uid history service
0.015059 ClEkJM2Vm5giqnMf4h - -
0.001000 CHhAvVGS1DHFjwGM9 - -
0.790739 CtPZjS20MLrsMUOJi2 Dd quic,ssl
0.718160 C4J4Th3PJpwUYZZ6gc Dd quic,ssl

View file

@ -0,0 +1,12 @@
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.
#separator \x09
#set_separator ,
#empty_field (empty)
#unset_field -
#path quic
#open XXXX-XX-XX-XX-XX-XX
#fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p version client_initial_dcid server_scid server_name client_protocol history
#types time string addr port addr port string string string string string string
1.000000 CtPZjS20MLrsMUOJi2 193.167.0.100 49394 193.167.100.100 443 1 15ae5e5e4962163f410b5529fc125bbc e483a751 server4:443 hq-interop ISZisZZZZZ
1.000000 C4J4Th3PJpwUYZZ6gc 193.167.0.100 60492 193.167.100.100 443 1 b7c7841c64883e3261d840 8d2041ac server4:443 hq-interop ISishIH
#close XXXX-XX-XX-XX-XX-XX

View file

@ -0,0 +1,12 @@
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.
#separator \x09
#set_separator ,
#empty_field (empty)
#unset_field -
#path ssl
#open XXXX-XX-XX-XX-XX-XX
#fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p version cipher curve server_name resumed last_alert next_protocol established ssl_history cert_chain_fps client_cert_chain_fps sni_matches_cert
#types time string addr port addr port string string string string bool string string bool string vector[string] vector[string] bool
1.000000 CtPZjS20MLrsMUOJi2 193.167.0.100 49394 193.167.100.100 443 TLSv13 TLS_AES_128_GCM_SHA256 x25519 server4:443 T - - F Cs - - -
1.000000 C4J4Th3PJpwUYZZ6gc 193.167.0.100 60492 193.167.100.100 443 TLSv13 TLS_AES_128_GCM_SHA256 x25519 server4:443 F - - F Cs - - -
#close XXXX-XX-XX-XX-XX-XX

View file

@ -0,0 +1 @@
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.

View file

@ -0,0 +1,11 @@
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.
#separator \x09
#set_separator ,
#empty_field (empty)
#unset_field -
#path quic
#open XXXX-XX-XX-XX-XX-XX
#fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p version client_initial_dcid server_scid server_name client_protocol history
#types time string addr port addr port string string string string string string
XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 669b:cb7a:de99:6a13:4a9b:46ef:3bed:cb6c 57538 6699:ded3:da8c:be73:5a99:ca73:5a99:cadb 443 1 5a37463b0eb7cc5d da37463b0eb7cc5d www.google.de h3 ISishIhHhh
#close XXXX-XX-XX-XX-XX-XX

View file

@ -0,0 +1,3 @@
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.
ts uid history service
XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 Dd quic,ssl

View file

@ -0,0 +1,11 @@
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.
#separator \x09
#set_separator ,
#empty_field (empty)
#unset_field -
#path quic
#open XXXX-XX-XX-XX-XX-XX
#fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p version client_initial_dcid server_scid server_name client_protocol history
#types time string addr port addr port string string string string string string
XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 46907 127.0.0.1 853 1 fda05288ab9ff546 a31f4933d8727231 - doq ISishH
#close XXXX-XX-XX-XX-XX-XX

View file

@ -0,0 +1,11 @@
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.
#separator \x09
#set_separator ,
#empty_field (empty)
#unset_field -
#path ssl
#open XXXX-XX-XX-XX-XX-XX
#fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p version cipher curve server_name resumed last_alert next_protocol established ssl_history cert_chain_fps client_cert_chain_fps sni_matches_cert
#types time string addr port addr port string string string string bool string string bool string vector[string] vector[string] bool
XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 46907 127.0.0.1 853 TLSv13 TLS_AES_128_GCM_SHA256 secp256r1 - F - - F Cs - - -
#close XXXX-XX-XX-XX-XX-XX

View file

@ -1,11 +0,0 @@
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.
#separator \x09
#set_separator ,
#empty_field (empty)
#unset_field -
#path conn
#open XXXX-XX-XX-XX-XX-XX
#fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p proto service duration orig_bytes resp_bytes conn_state local_orig local_resp missed_bytes history orig_pkts orig_ip_bytes resp_pkts resp_ip_bytes tunnel_parents
#types time string addr port addr port enum string interval count count string bool bool count string count count count count set[string]
XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 1.2.3.4 49369 4.3.2.1 443 udp spicy_quic,ssl 18.071102 14371 394242 SF - - 0 Dd 96 17059 345 403902 -
#close XXXX-XX-XX-XX-XX-XX

View file

@ -0,0 +1,3 @@
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.
ts uid history service
XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 Dd quic,ssl

View file

@ -7,5 +7,5 @@
#open XXXX-XX-XX-XX-XX-XX #open XXXX-XX-XX-XX-XX-XX
#fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p version cipher curve server_name resumed last_alert next_protocol established ssl_history cert_chain_fps client_cert_chain_fps sni_matches_cert #fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p version cipher curve server_name resumed last_alert next_protocol established ssl_history cert_chain_fps client_cert_chain_fps sni_matches_cert
#types time string addr port addr port string string string string bool string string bool string vector[string] vector[string] bool #types time string addr port addr port string string string string bool string string bool string vector[string] vector[string] bool
XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 1.2.3.4 49369 4.3.2.1 443 - - - www.google.com F - - F C - - - XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 1.2.3.4 49369 4.3.2.1 443 TLSv13 TLS_AES_128_GCM_SHA256 x25519 www.google.com F - - F Cs - - -
#close XXXX-XX-XX-XX-XX-XX #close XXXX-XX-XX-XX-XX-XX

View file

@ -0,0 +1,3 @@
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.
ts uid cause analyzer_kind analyzer_name failure_reason
1693925959.000001 CHhAvVGS1DHFjwGM9 violation protocol QUIC &requires failed: self.length.result >= 20 (<...>/QUIC.spicy:<line>:<column>)

View file

@ -0,0 +1,3 @@
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.
ts uid history service
XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 D -

View file

@ -0,0 +1,10 @@
PCAP files in this directory were gathered from the QUIC interop project webpage.
Marten Seemann didn't have any concerns or reservations using these as testing
material. They require DLT_PPP support in Zeek, unfortunately.
Some references:
https://interop.seemann.io/
https://github.com/marten-seemann/quic-network-simulator
https://github.com/marten-seemann/quic-interop-runner

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1,6 @@
# @TEST-DOC: Test that runs the pcap
# @TEST-EXEC: zeek -Cr $TRACES/quic/chromium-115.0.5790.110-api-cirrus-com.pcap base/protocols/quic
# @TEST-EXEC: zeek-cut -m ts uid history service < conn.log > conn.log.cut
# @TEST-EXEC: btest-diff conn.log.cut
# @TEST-EXEC: btest-diff ssl.log
# @TEST-EXEC: btest-diff quic.log

View file

@ -0,0 +1,6 @@
# @TEST-DOC: Test that runs the pcap
# @TEST-EXEC: zeek -Cr $TRACES/quic/curl-8.1.2-dev-http3-www-google-de.pcap base/protocols/quic
# @TEST-EXEC: zeek-cut -m ts uid history service < conn.log > conn.log.cut
# @TEST-EXEC: btest-diff conn.log.cut
# @TEST-EXEC: btest-diff ssl.log
# @TEST-EXEC: btest-diff quic.log

View file

@ -0,0 +1,29 @@
# @TEST-DOC: Supported events so far.
# @TEST-REQUIRES: zeek -b -e 'print PacketAnalyzer::ANALYZER_PPP == PacketAnalyzer::ANALYZER_PPP'
# @TEST-EXEC: zeek -Cr $TRACES/quic/interop/quic-go_quic-go/retry.pcap base/protocols/quic %INPUT >out
# @TEST-EXEC: echo "zerortt.pcap" >>out
# @TEST-EXEC: zeek -Cr $TRACES/quic/interop/quic-go_quic-go/zerortt.pcap base/protocols/quic %INPUT >>out
# @TEST-EXEC: btest-diff out
# @TEST-EXEC: btest-diff .stderr
#
function b2hex(s: string):string { return bytestring_to_hexstr(s); }
event QUIC::initial_packet(c: connection, is_orig: bool, version: count, dcid: string, scid: string)
{
print network_time(), "initial_packet", c$uid, is_orig, version, b2hex(dcid), b2hex(scid);
}
event QUIC::retry_packet(c: connection, is_orig: bool, version: count, dcid: string, scid: string, retry_token: string, integrity_tag: string)
{
print network_time(), "retry_packet", c$uid, is_orig, version, b2hex(dcid), b2hex(scid), |retry_token|, b2hex(integrity_tag);
}
event QUIC::handshake_packet(c: connection, is_orig: bool, version: count, dcid: string, scid: string)
{
print network_time(), "handshake_packet", is_orig, c$uid, version, b2hex(dcid), b2hex(scid);
}
event QUIC::zero_rtt_packet(c: connection, is_orig: bool, version: count, dcid: string, scid: string)
{
print network_time(), "zero_rtt_packet", is_orig, c$uid, version, b2hex(dcid), b2hex(scid);
}

View file

@ -0,0 +1,6 @@
# @TEST-DOC: Test that runs the pcap
# @TEST-EXEC: zeek -Cr $TRACES/quic/firefox-102.13.0esr-blog-cloudflare-com.pcap base/protocols/quic
# @TEST-EXEC: zeek-cut -m ts uid history service < conn.log > conn.log.cut
# @TEST-EXEC: btest-diff conn.log.cut
# @TEST-EXEC: btest-diff ssl.log
# @TEST-EXEC: btest-diff quic.log

View file

@ -0,0 +1,7 @@
# @TEST-DOC: Pcap with fragmented and unordered CRYPTO frames.
#
# @TEST-EXEC: zeek -Cr $TRACES/quic/chromium-115.0.5790.110-google-de-fragmented.pcap base/protocols/quic
# @TEST-EXEC: zeek-cut -m ts uid history service < conn.log > conn.log.cut
# @TEST-EXEC: btest-diff conn.log.cut
# @TEST-EXEC: zeek-cut -m ts uid version cipher curve server_name resumed last_alert next_protocol established ssl_history < ssl.log > ssl.log.cut
# @TEST-EXEC: btest-diff ssl.log.cut

View file

@ -0,0 +1,12 @@
# @TEST-DOC: Test interop pcap containing RETRY packet from server side.
#
# interop pcaps have link type DLT_PPP, test for its availability. Available in Zeek 6.1 or later only.
# @TEST-REQUIRES: zeek -b -e 'print PacketAnalyzer::ANALYZER_PPP == PacketAnalyzer::ANALYZER_PPP'
#
# @TEST-EXEC: zeek -Cr $TRACES/quic/interop/quic-go_quic-go/handshake.pcap base/protocols/quic
# @TEST-EXEC: zeek-cut -m ts uid history service < conn.log > conn.log.cut
# @TEST-EXEC: btest-diff conn.log.cut
# @TEST-EXEC: btest-diff ssl.log
# @TEST-EXEC: btest-diff quic.log
# @TEST-EXEC: btest-diff .stderr
# @TEST-EXEC: test ! -f analyzer.log

View file

@ -0,0 +1,12 @@
# @TEST-DOC: Test interop pcap containing RETRY packet from server side.
#
# interop pcaps have link type DLT_PPP, test for its availability. Available in Zeek 6.1 or later only.
# @TEST-REQUIRES: zeek -b -e 'print PacketAnalyzer::ANALYZER_PPP == PacketAnalyzer::ANALYZER_PPP'
#
# @TEST-EXEC: zeek -Cr $TRACES/quic/interop/quic-go_quic-go/retry.pcap base/protocols/quic
# @TEST-EXEC: zeek-cut -m ts uid history service < conn.log > conn.log.cut
# @TEST-EXEC: btest-diff conn.log.cut
# @TEST-EXEC: btest-diff ssl.log
# @TEST-EXEC: btest-diff quic.log
# @TEST-EXEC: btest-diff .stderr
# @TEST-EXEC: test ! -f analyzer.log

View file

@ -0,0 +1,12 @@
# @TEST-DOC: Test that client initiating connection using 0RTT packet doesn't cause analyzer errors trying to decrypt server side.
#
# interop pcaps have link type DLT_PPP, test for its availability. Available in Zeek 6.1 or later only.
# @TEST-REQUIRES: zeek -b -e 'print PacketAnalyzer::ANALYZER_PPP == PacketAnalyzer::ANALYZER_PPP'
#
# @TEST-EXEC: zeek -Cr $TRACES/quic/interop/quic-go_quic-go/zerortt.pcap base/protocols/quic
# @TEST-EXEC: zeek-cut -m ts uid history service < conn.log > conn.log.cut
# @TEST-EXEC: btest-diff conn.log.cut
# @TEST-EXEC: btest-diff ssl.log
# @TEST-EXEC: btest-diff quic.log
# @TEST-EXEC: btest-diff .stderr
# @TEST-EXEC: test ! -f analyzer.log

View file

@ -0,0 +1,5 @@
# @TEST-DOC: Smoke test the quic.log production
#
# @TEST-EXEC: zeek -Cr $TRACES/quic/chromium-115.0.5790.110-google-de-fragmented.pcap base/protocols/quic
# @TEST-EXEC: btest-diff quic.log
# @TEST-EXEC: btest-diff .stderr

View file

@ -0,0 +1,6 @@
# @TEST-DOC: Pcap with dns-over-quic lookup using https://github.com/private-octopus/quicdoq
# @TEST-EXEC: zeek -Cr $TRACES/quic/quicdoq.pcap base/protocols/quic
# @TEST-EXEC: zeek-cut -m ts uid history service < conn.log > conn.log.cut
# @TEST-EXEC: btest-diff conn.log.cut
# @TEST-EXEC: btest-diff ssl.log
# @TEST-EXEC: btest-diff quic.log

View file

@ -1,4 +1,5 @@
# @TEST-DOC: Test that runs the pcap # @TEST-DOC: Test that runs the pcap
# @TEST-EXEC: zeek -Cr $TRACES/quic/quic_win11_firefox_google.pcap base/protocols/quic >output # @TEST-EXEC: zeek -Cr $TRACES/quic/quic_win11_firefox_google.pcap base/protocols/quic
# @TEST-EXEC: btest-diff conn.log # @TEST-EXEC: zeek-cut -m ts uid history service < conn.log > conn.log.cut
# @TEST-EXEC: btest-diff conn.log.cut
# @TEST-EXEC: btest-diff ssl.log # @TEST-EXEC: btest-diff ssl.log

View file

@ -0,0 +1,9 @@
# @TEST-DOC: Test that runs the pcap
# @TEST-EXEC: zeek -Cr $TRACES/quic/vector-max-size-crash.pcap base/protocols/quic
# @TEST-EXEC: zeek-cut -m ts uid history service < conn.log > conn.log.cut
# @TEST-EXEC: zeek-cut -m ts uid cause analyzer_kind analyzer_name failure_reason < analyzer.log > analyzer.log.cut
# @TEST-EXEC: btest-diff conn.log.cut
# Only run btest-ddiff on analyzer.log with 6.1-dev or later. The violation
# reporting has more detail in later versions.
# @TEST-EXEC: zeek -b -e 'exit(Version::info$version_number < 60100 ? 0 : 1)' || TEST_DIFF_CANONIFIER='sed -r "s/\((.+)\.spicy:[0-9]+:[0-9]+\)/(\1.spicy:<line>:<column>)/g" | $SCRIPTS/diff-remove-abspath' btest-diff analyzer.log.cut