diff --git a/scripts/base/protocols/quic/__load__.zeek b/scripts/base/protocols/quic/__load__.zeek index a10fe855df..1d47f6e0cd 100644 --- a/scripts/base/protocols/quic/__load__.zeek +++ b/scripts/base/protocols/quic/__load__.zeek @@ -1 +1,2 @@ +@load ./consts @load ./main diff --git a/scripts/base/protocols/quic/consts.zeek b/scripts/base/protocols/quic/consts.zeek new file mode 100644 index 0000000000..b502b04be1 --- /dev/null +++ b/scripts/base/protocols/quic/consts.zeek @@ -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); }; +} diff --git a/scripts/base/protocols/quic/main.zeek b/scripts/base/protocols/quic/main.zeek index 0d54e52618..ddd34a51e6 100644 --- a/scripts/base/protocols/quic/main.zeek +++ b/scripts/base/protocols/quic/main.zeek @@ -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; +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); + } diff --git a/src/analyzer/protocol/quic/CMakeLists.txt b/src/analyzer/protocol/quic/CMakeLists.txt index 4d9e0f6964..8cbdea0228 100644 --- a/src/analyzer/protocol/quic/CMakeLists.txt +++ b/src/analyzer/protocol/quic/CMakeLists.txt @@ -1,5 +1,23 @@ spicy_add_analyzer( NAME QUIC PACKAGE_NAME QUIC - SOURCES decrypt_crypto.cc QUIC.spicy QUIC.evt zeek_QUIC.spicy - SCRIPTS __load__.zeek main.zeek) + SOURCES QUIC.spicy QUIC.evt + 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}) diff --git a/src/analyzer/protocol/quic/QUIC.evt b/src/analyzer/protocol/quic/QUIC.evt index 7a1650bf31..f447c0373f 100644 --- a/src/analyzer/protocol/quic/QUIC.evt +++ b/src/analyzer/protocol/quic/QUIC.evt @@ -1,10 +1,19 @@ -protocol analyzer spicy::QUIC over UDP: +protocol analyzer QUIC over UDP: parse originator with QUIC::RequestFrame, - parse responder with QUIC::ResponseFrame, - ports { 443/udp }; + parse responder with QUIC::ResponseFrame; import QUIC; -import Zeek_QUIC; -# TODO: Add actual events, instead of this dummy event -on QUIC::ResponseFrame -> event QUIC::example($conn); +# Make the enum available. +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); diff --git a/src/analyzer/protocol/quic/QUIC.spicy b/src/analyzer/protocol/quic/QUIC.spicy index 7781a3875c..bd040ae46a 100644 --- a/src/analyzer/protocol/quic/QUIC.spicy +++ b/src/analyzer/protocol/quic/QUIC.spicy @@ -4,26 +4,74 @@ import spicy; import zeek; # 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 ############## -type ConnectionIDInfo = unit { - var client_cid_len: uint8; - var server_cid_len: uint8; - var initial_destination_conn_id: bytes; - var initial_packets_exchanged: bool; - var initialized: bool; +# Can we decrypt? +function can_decrypt(long_header: LongHeaderPacket, context: ConnectionIDInfo, is_client: bool): bool { - on %init { - self.client_cid_len = 0; - self.server_cid_len = 0; - self.initial_packets_exchanged = False; - self.initialized = False; - } + if ( long_header.first_byte.packet_type != LongPacketType::INITIAL ) + return False; + + # decrypt_crypto_payload() has known secrets for version 1, nothing else. + 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 ############## -# 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(cast($$)); - }; - 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($$) &byte-order=spicy::ByteOrder::Big; - }; - - on %done { - self.length = self.a.length; - self.backtrack(); - } -}; - type VariableLengthInteger = unit { var bytes_to_parse: uint64; var result: uint64; - var result_bytes: bytes; - : VariableLengthIntegerLength &try { - switch ( $$.length ) { - case 0: - self.bytes_to_parse = 1; - case 1: - self.bytes_to_parse = 2; - case 2: - self.bytes_to_parse = 4; - case 3: - self.bytes_to_parse = 8; - } + # Value of the two most significant bits indicates number of bytes + # to parse for the variable length integer. + # + # https://datatracker.ietf.org/doc/rfc9000/ + # Section 16 and Appendix A + # + first_byte: bytes &size=1 { + local uint8_val = uint8($$.to_uint(spicy::ByteOrder::Big)); + self.bytes_to_parse = 2**((0xC0 & uint8_val) >> 6); + # 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 - # first two bits, leaving the actual length - 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; - } + on %done { + self.result = (self.first_byte + self.remaining_bytes).to_uint(spicy::ByteOrder::Big); } }; @@ -156,22 +156,7 @@ type VariableLengthInteger = unit { # Generic units ############## -# Used to capture all data form the entire frame. May be inefficient, but works for now. -# 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 { +public type LongHeaderPacket = unit { var encrypted_offset: uint64; var payload_length: uint64; var client_conn_id_length: uint8; @@ -179,7 +164,7 @@ public type LongHeader = unit { first_byte: bitfield(8) { header_form: 7 &convert=cast(cast($$)); - fixed_bit: 6; + fixed_bit: 6; packet_type: 4..5 &convert=cast(cast($$)); type_specific_bits: 0..3 &convert=cast($$); }; @@ -190,84 +175,68 @@ public type LongHeader = unit { src_conn_id_len: uint8 { 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 ) { - LongPacketType::INITIAL -> initial_hdr : InitialLongPacketHeader(self.first_byte.type_specific_bits) { - self.encrypted_offset = self.offset() + - self.initial_hdr.payload_length.bytes_to_parse + - self.initial_hdr.token_length.bytes_to_parse + + LongPacketType::INITIAL -> initial_hdr : InitialPacket(self) { + self.encrypted_offset = self.offset() + + self.initial_hdr.length.bytes_to_parse + + self.initial_hdr.token_length.bytes_to_parse + 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::RETRY -> retry_hdr : RetryLongPacketHeader(self.first_byte.type_specific_bits); + + LongPacketType::ZERO_RTT -> zerortt_hdr : ZeroRTTPacket(self); + LongPacketType::HANDSHAKE -> handshake_hdr : HandshakePacket(self); + LongPacketType::RETRY -> retry_hdr : RetryPacket(self); }; }; -# Decrypted long packet payload that can actually be parsed -public type DecryptedLongPacketPayload = unit(packet_type: LongPacketType, from_client: bool) { +# A QUIC Frame. +public type Frame = unit(header: LongHeaderPacket, from_client: bool, crypto_sink: sink) { frame_type : uint8 &convert=cast($$); # TODO: add other FrameTypes as well switch ( self.frame_type ) { FrameType::ACK1 -> a: ACKPayload; FrameType::ACK2 -> b: ACKPayload; - FrameType::CRYPTO -> c: CRYPTOPayload(from_client); - FrameType::PADDING -> d: PADDINGPayload; + FrameType::CRYPTO -> c: CRYPTOPayload(from_client) { + # 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) { - var length_in_byte1: bytes; - var length_in_byte2: bytes; - - offset: uint8; + offset: VariableLengthInteger; length: VariableLengthInteger; 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((self.length.result >> 8) & 0xff)).encode(); - self.length_in_byte2 = ("%c" % cast(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 { - latest_ack: uint8; - ack_delay: uint8; - ack_range_count: uint8; - first_ack_range: uint8; + latest_ack: VariableLengthInteger; + ack_delay: VariableLengthInteger; + ack_range_count: VariableLengthInteger; + first_ack_range: VariableLengthInteger; }; -public type NullBytes = unit { - : (b"\x00")[]; - x: InitialUint8 &try; -}; - -type PADDINGPayload = unit { - var padding_length: uint64 = 0; - - # Simply consume all next nullbytes - : NullBytes; +type ConnectionClosePayload = unit(header: LongHeaderPacket) { + var header: LongHeaderPacket = header; + error_code: VariableLengthInteger; + switch { + -> unknown_frame_type: b"\x00"; + -> frame_type: VariableLengthInteger; + }; + reason_phrase_length: VariableLengthInteger; + reason_phrase: bytes &size=self.reason_phrase_length.result; }; @@ -276,35 +245,78 @@ type PADDINGPayload = unit { # Specific long packet type units ############## -type InitialLongPacketHeader = unit(type_specific_bits: uint8) { - var packet_number_length_full: uint8; - +# Remainder of an Initial packet +type InitialPacket = unit(header: LongHeaderPacket) { + var header: LongHeaderPacket = header; token_length: VariableLengthInteger; 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 { - # 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. - self.packet_number_length_full = (type_specific_bits & 0x03) + 1; - } + # 5.4.2. Header Protection Sample + # + # That is, in sampling packet ciphertext for header + # 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 ZeroRTTLongPacketHeader = unit(type_specific_bits: uint8) {}; -type HandshakeLongPacketHeader = unit(type_specific_bits: uint8) {}; -type RetryLongPacketHeader = unit(type_specific_bits: uint8) {}; +type ZeroRTTPacket = 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 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 ############## # TODO: implement -public type ShortHeader = unit(dest_conn_id_length: uint8) { +public type ShortHeader = unit(dest_conn_id_length: uint8) { first_byte: bitfield(8) { header_form: 7 &convert=cast(cast($$)); - fixed_bit: 6; + fixed_bit: 6; spin_bit: 5; todo: 0..4; }; @@ -313,86 +325,193 @@ public type ShortHeader = unit(dest_conn_id_length: uint8) { # TODO: investigate whether we can parse something useful out of this public type ShortPacketPayload = unit { +@if SPICY_VERSION >= 10800 + payload: skip bytes &eod; +@else 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&) { - var hdr_form: HeaderForm; +type Packet = unit(from_client: bool, context: ConnectionIDInfo&) { var decrypted_data: bytes; var full_packet: bytes; + var start: iterator; - # Peek into the header to check if it's a SHORT or LONG header - : InitialByte &try { - self.hdr_form = $$.initialbyte.header_form; + sink crypto_sink; + var crypto_buffer: CryptoBuffer&; + + # 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 - switch ( self.hdr_form ) { + switch ( self.first_byte.header_form ) { 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. - # TODO: allow this for Retry packets - if ( self.long_header.first_byte.packet_type == LongPacketType::INITIAL - && context.initial_packets_exchanged == False ) { + # If we see a retry packet from the responder, reset the decryption + # 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""; - if ( from_client ) { - context.server_cid_len = self.long_header.dest_conn_id_len; - context.client_cid_len = self.long_header.src_conn_id_len; - - # This means that here, we can try to decrypt the initial packet! - # All data is accessible via the `long_header` unit - - self.decrypted_data = decrypt_crypto_payload(self.fpack.data, - self.long_header.dest_conn_id, - self.long_header.encrypted_offset, - self.long_header.payload_length, - from_client); - - # Set this to be the seed for the decryption - if ( ! context.initial_packets_exchanged ) { - context.initial_destination_conn_id = self.long_header.dest_conn_id; - } - - } else { - context.server_cid_len = self.long_header.src_conn_id_len; - context.client_cid_len = self.long_header.dest_conn_id_len; - - # 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 - self.decrypted_data = decrypt_crypto_payload(self.fpack.data, - context.initial_destination_conn_id, - self.long_header.encrypted_offset, - self.long_header.payload_length, - from_client); - } + # 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 } + } + }; - # 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 - # TODO: verify if this is actually correct per RFC - if (self.long_header.first_byte.packet_type != LongPacketType::RETRY && ! from_client) { - context.initial_packets_exchanged = True; - } + # 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 ) { + context.server_cid_len = self.long_header.dest_conn_id_len; + context.client_cid_len = self.long_header.src_conn_id_len; + + # This means that here, we can try to decrypt the initial packet! + # All data is accessible via the `long_header` unit + self.decrypted_data = decrypt_crypto_payload( + self.all_data, + self.long_header.dest_conn_id, + self.long_header.encrypted_offset, + self.long_header.payload_length, + from_client + ); + + # Set this to be the seed for the decryption + if ( |context.initial_destination_conn_id| == 0 ) { + context.initial_destination_conn_id = self.long_header.dest_conn_id; + } + + } else { + context.server_cid_len = self.long_header.src_conn_id_len; + context.client_cid_len = self.long_header.dest_conn_id_len; + + # 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 + self.decrypted_data = decrypt_crypto_payload( + self.all_data, + context.initial_destination_conn_id, + self.long_header.encrypted_offset, + self.long_header.payload_length, + from_client + ); } - }; - # Depending on the type of header, we parse the remaining payload. - switch ( self.hdr_form ) { - HeaderForm::SHORT -> remaining_short_payload: ShortPacketPayload; - HeaderForm::LONG -> remaining_long_payload : LongPacketPayload(self.long_header.first_byte.packet_type, from_client, context.initial_packets_exchanged)[] &parse-from=self.decrypted_data; - }; + # 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"; - on %init { - # Make sure to only attach the SSL analyzer once per QUIC connection - if ( ! context.initialized ) { - context.initialized = True; - zeek::protocol_begin("SSL"); + # 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 + if ( self.long_header.first_byte.packet_type != LongPacketType::RETRY && ! from_client ) { + context.server_initial_processed = True; + context.client_initial_processed = True; + } + } + + # Depending on the type of header and whether we were able to decrypt + # some of it, parse the remaining payload. + : ShortPacketPayload if (self.first_byte.header_form == HeaderForm::SHORT); + : EncryptedLongPacketPayload if (self.first_byte.header_form == HeaderForm::LONG && |self.decrypted_data| == 0); + + # If this was packet with a long header and decrypted data exists, attempt + # to parse the plain QUIC frames from it. + 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); + + # 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(|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 { %context = ConnectionIDInfo; - : Frame(True, self.context()); + : Packet(True, self.context()); }; public type ResponseFrame = unit { %context = ConnectionIDInfo; - : Frame(False, self.context()); + : Packet(False, self.context()); }; diff --git a/src/analyzer/protocol/quic/decrypt_crypto.cc b/src/analyzer/protocol/quic/decrypt_crypto.cc index 94282dc67d..e0f034bdfa 100644 --- a/src/analyzer/protocol/quic/decrypt_crypto.cc +++ b/src/analyzer/protocol/quic/decrypt_crypto.cc @@ -8,29 +8,40 @@ refactors as C++ development is not our main profession. */ // Default imports -#include +#include +#include +#include #include -#include -#include #include +#include // OpenSSL imports -#include #include +#include #include // Import HILTI #include +namespace + { + // Struct to store decryption info for this specific connection struct DecryptionInformation -{ - std::vector unprotected_header; - std::vector protected_header; - uint64_t packet_number; - std::vector nonce; - uint8_t packet_number_length; -}; + { + std::vector unprotected_header; + uint64_t packet_number; + std::vector nonce; + 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(b.data()); + } /* Constants used in the HKDF functions. HKDF-Expand-Label uses labels @@ -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. */ -std::vector INITIAL_SALT_V1 = { - 0x38, 0x76, 0x2c, 0xf7, 0xf5, - 0x59, 0x34, 0xb3, 0x4d, 0x17, - 0x9a, 0xe6, 0xa4, 0xc8, 0x0c, - 0xad, 0xcc, 0xbb, 0x7f, 0x0a}; +std::vector INITIAL_SALT_V1 = {0x38, 0x76, 0x2c, 0xf7, 0xf5, 0x59, 0x34, 0xb3, 0x4d, 0x17, + 0x9a, 0xe6, 0xa4, 0xc8, 0x0c, 0xad, 0xcc, 0xbb, 0x7f, 0x0a}; -std::vector CLIENT_INITIAL_INFO = { - 0x00, 0x20, 0x0f, 0x74, 0x6c, - 0x73, 0x31, 0x33, 0x20, 0x63, - 0x6c, 0x69, 0x65, 0x6e, 0x74, - 0x20, 0x69, 0x6e, 0x00}; +std::vector CLIENT_INITIAL_INFO = {0x00, 0x20, 0x0f, 0x74, 0x6c, 0x73, 0x31, + 0x33, 0x20, 0x63, 0x6c, 0x69, 0x65, 0x6e, + 0x74, 0x20, 0x69, 0x6e, 0x00}; -std::vector SERVER_INITIAL_INFO = { - 0x00, 0x20, 0x0f, 0x74, 0x6c, - 0x73, 0x31, 0x33, 0x20, 0x73, - 0x65, 0x72, 0x76, 0x65, 0x72, - 0x20, 0x69, 0x6e, 0x00}; +std::vector SERVER_INITIAL_INFO = {0x00, 0x20, 0x0f, 0x74, 0x6c, 0x73, 0x31, + 0x33, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, + 0x72, 0x20, 0x69, 0x6e, 0x00}; -std::vector KEY_INFO = { - 0x00, 0x10, 0x0e, 0x74, 0x6c, - 0x73, 0x31, 0x33, 0x20, 0x71, - 0x75, 0x69, 0x63, 0x20, 0x6b, - 0x65, 0x79, 0x00}; +std::vector KEY_INFO = {0x00, 0x10, 0x0e, 0x74, 0x6c, 0x73, 0x31, 0x33, 0x20, + 0x71, 0x75, 0x69, 0x63, 0x20, 0x6b, 0x65, 0x79, 0x00}; -std::vector IV_INFO = { - 0x00, 0x0c, 0x0d, 0x74, 0x6c, - 0x73, 0x31, 0x33, 0x20, 0x71, - 0x75, 0x69, 0x63, 0x20, 0x69, - 0x76, 0x00}; +std::vector IV_INFO = {0x00, 0x0c, 0x0d, 0x74, 0x6c, 0x73, 0x31, 0x33, 0x20, + 0x71, 0x75, 0x69, 0x63, 0x20, 0x69, 0x76, 0x00}; -std::vector HP_INFO = { - 0x00, 0x10, 0x0d, 0x74, 0x6c, - 0x73, 0x31, 0x33, 0x20, 0x71, - 0x75, 0x69, 0x63, 0x20, 0x68, - 0x70, 0x00}; +std::vector HP_INFO = {0x00, 0x10, 0x0d, 0x74, 0x6c, 0x73, 0x31, 0x33, 0x20, + 0x71, 0x75, 0x69, 0x63, 0x20, 0x68, 0x70, 0x00}; /* Constants used by the different functions @@ -88,293 +83,233 @@ const size_t MAXIMUM_PACKET_LENGTH = 1500; 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 hkdf_extract(std::vector connection_id) -{ - std::vector out_temp(INITIAL_SECRET_LEN); - size_t initial_secret_len = out_temp.size(); - const EVP_MD *digest = EVP_sha256(); - EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL); - EVP_PKEY_derive_init(pctx); - EVP_PKEY_CTX_hkdf_mode(pctx, EVP_PKEY_HKDEF_MODE_EXTRACT_ONLY); - EVP_PKEY_CTX_set_hkdf_md(pctx, digest); - EVP_PKEY_CTX_set1_hkdf_key(pctx, - connection_id.data(), - connection_id.size()); - EVP_PKEY_CTX_set1_hkdf_salt(pctx, - INITIAL_SALT_V1.data(), - INITIAL_SALT_V1.size()); - EVP_PKEY_derive(pctx, - out_temp.data(), - reinterpret_cast(&initial_secret_len)); - EVP_PKEY_CTX_free(pctx); - return out_temp; -} +std::vector hkdf_extract(const hilti::rt::Bytes& connection_id) + { + std::vector out_temp(INITIAL_SECRET_LEN); + size_t initial_secret_len = out_temp.size(); + const EVP_MD* digest = EVP_sha256(); + EVP_PKEY_CTX* pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL); + EVP_PKEY_derive_init(pctx); + EVP_PKEY_CTX_hkdf_mode(pctx, EVP_PKEY_HKDEF_MODE_EXTRACT_ONLY); + EVP_PKEY_CTX_set_hkdf_md(pctx, digest); + EVP_PKEY_CTX_set1_hkdf_key(pctx, data_as_uint8(connection_id), connection_id.size()); + EVP_PKEY_CTX_set1_hkdf_salt(pctx, INITIAL_SALT_V1.data(), INITIAL_SALT_V1.size()); + EVP_PKEY_derive(pctx, out_temp.data(), &initial_secret_len); + EVP_PKEY_CTX_free(pctx); + 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'. */ -std::vector hkdf_expand(size_t out_len, - std::vector key, - std::vector info) -{ - std::vector out_temp(out_len); - const EVP_MD *digest = EVP_sha256(); - EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL); - EVP_PKEY_derive_init(pctx); - EVP_PKEY_CTX_hkdf_mode(pctx, EVP_PKEY_HKDEF_MODE_EXPAND_ONLY); - EVP_PKEY_CTX_set_hkdf_md(pctx, digest); - EVP_PKEY_CTX_set1_hkdf_key(pctx, key.data(), key.size()); - EVP_PKEY_CTX_add1_hkdf_info(pctx, info.data(), info.size()); - EVP_PKEY_derive(pctx, out_temp.data(), &out_len); - EVP_PKEY_CTX_free(pctx); - return out_temp; -} +std::vector hkdf_expand(size_t out_len, const std::vector& key, + const std::vector& info) + { + std::vector out_temp(out_len); + const EVP_MD* digest = EVP_sha256(); + EVP_PKEY_CTX* pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL); + EVP_PKEY_derive_init(pctx); + EVP_PKEY_CTX_hkdf_mode(pctx, EVP_PKEY_HKDEF_MODE_EXPAND_ONLY); + EVP_PKEY_CTX_set_hkdf_md(pctx, digest); + EVP_PKEY_CTX_set1_hkdf_key(pctx, key.data(), key.size()); + EVP_PKEY_CTX_add1_hkdf_info(pctx, info.data(), info.size()); + EVP_PKEY_derive(pctx, out_temp.data(), &out_len); + EVP_PKEY_CTX_free(pctx); + return out_temp; + } /* -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 client_hp, uint8_t encrypted_offset, std::vector encrypted_packet) -{ - DecryptionInformation decryptInfo; - int outlen; - auto cipher = EVP_aes_128_ecb(); - auto ctx = EVP_CIPHER_CTX_new(); - EVP_CipherInit_ex(ctx, cipher, NULL, NULL, NULL, 1); - EVP_CIPHER_CTX_set_key_length(ctx, client_hp.size()); - // Passing an 1 means ENCRYPT - EVP_CipherInit_ex(ctx, NULL, NULL, client_hp.data(), NULL, 1); +DecryptionInformation remove_header_protection(const std::vector& client_hp, + uint64_t encrypted_offset, + const hilti::rt::Bytes& all_data) + { + DecryptionInformation decryptInfo; + int outlen; + auto cipher = EVP_aes_128_ecb(); + auto ctx = EVP_CIPHER_CTX_new(); + EVP_CipherInit_ex(ctx, cipher, NULL, NULL, NULL, 1); + EVP_CIPHER_CTX_set_key_length(ctx, client_hp.size()); + // Passing an 1 means ENCRYPT + EVP_CipherInit_ex(ctx, NULL, NULL, client_hp.data(), NULL, 1); - std::vector sample(encrypted_packet.begin() + - encrypted_offset + - MAXIMUM_PACKET_NUMBER_LENGTH, + static_assert(AEAD_SAMPLE_LENGTH > 0); + assert(all_data.size() >= encrypted_offset + MAXIMUM_PACKET_NUMBER_LENGTH + AEAD_SAMPLE_LENGTH); - encrypted_packet.begin() + - encrypted_offset + - MAXIMUM_PACKET_NUMBER_LENGTH + - AEAD_SAMPLE_LENGTH); - std::vector mask(sample.size()); - EVP_CipherUpdate(ctx, mask.data(), &outlen, sample.data(), AEAD_SAMPLE_LENGTH); + const uint8_t* sample = data_as_uint8(all_data) + encrypted_offset + + MAXIMUM_PACKET_NUMBER_LENGTH; - // To determine the actual packet number length, - // we have to remove the mask from the first byte - uint8_t first_byte = encrypted_packet[0]; + std::array mask; + EVP_CipherUpdate(ctx, mask.data(), &outlen, sample, AEAD_SAMPLE_LENGTH); + EVP_CIPHER_CTX_free(ctx); - if (first_byte & 0x80) - { - first_byte ^= mask[0] & 0x0F; - } - else - { - first_byte ^= first_byte & 0x1F; - } + // To determine the actual packet number length, + // we have to remove the mask from the first byte + uint8_t first_byte = data_as_uint8(all_data)[0]; - // And now we can fully recover the correct packet number length... - int recovered_packet_number_length = (first_byte & 0x03) + 1; + if ( first_byte & 0x80 ) + { + first_byte ^= mask[0] & 0x0F; + } + else + { + first_byte ^= first_byte & 0x1F; + } - // .. and use this to reconstruct the (partially) unprotected header - std::vector unprotected_header( - encrypted_packet.begin(), + // And now we can fully recover the correct packet number length... + int recovered_packet_number_length = (first_byte & 0x03) + 1; - encrypted_packet.begin() + - encrypted_offset + - recovered_packet_number_length); + // .. and use this to reconstruct the (partially) unprotected header + std::vector unprotected_header(data_as_uint8(all_data), + data_as_uint8(all_data) + encrypted_offset + + recovered_packet_number_length); - uint32_t decoded_packet_number = 0; + uint32_t decoded_packet_number = 0; - unprotected_header[0] = first_byte; - for (int i = 0; i < recovered_packet_number_length; ++i) - { - unprotected_header[encrypted_offset + i] ^= mask[1 + i]; - decoded_packet_number = - unprotected_header[encrypted_offset + i] | - (decoded_packet_number << 8); - } - std::vector protected_header(encrypted_packet.begin(), - encrypted_packet.begin() + - encrypted_offset + - recovered_packet_number_length); + unprotected_header[0] = first_byte; + for ( int i = 0; i < recovered_packet_number_length; ++i ) + { + unprotected_header[encrypted_offset + i] ^= mask[1 + i]; + decoded_packet_number = unprotected_header[encrypted_offset + i] | + (decoded_packet_number << 8); + } - // Store the information back in the struct - decryptInfo.packet_number = decoded_packet_number; - decryptInfo.packet_number_length = recovered_packet_number_length; - decryptInfo.protected_header = protected_header; - decryptInfo.unprotected_header = unprotected_header; - return decryptInfo; -} + // Store the information back in the struct + decryptInfo.packet_number = decoded_packet_number; + decryptInfo.packet_number_length = recovered_packet_number_length; + decryptInfo.unprotected_header = std::move(unprotected_header); + return decryptInfo; + } /* Calculate the nonce for the AEAD by XOR'ing the CLIENT_IV and the decoded packet number, and returns the nonce */ std::vector calculate_nonce(std::vector client_iv, uint64_t packet_number) -{ - std::vector nonce = client_iv; + { + for ( int i = 0; i < 8; ++i ) + client_iv[AEAD_IV_LEN - 1 - i] ^= (uint8_t)(packet_number >> 8 * i); - for (int i = 0; i < 8; ++i) - { - nonce[AEAD_IV_LEN - 1 - i] ^= - (uint8_t)(packet_number >> 8 * i); - } - - // Return the nonce - return nonce; -} + return client_iv; + } /* Function that calls the AEAD decryption routine, and returns the decrypted data */ -std::vector decrypt(std::vector client_key, - std::vector encrypted_packet, - uint64_t payload_offset, - DecryptionInformation decryptInfo) -{ - int out, out2, res; - std::vector encrypted_payload( - encrypted_packet.begin() + - decryptInfo.protected_header.size(), - encrypted_packet.begin() + - decryptInfo.protected_header.size() + - payload_offset - - decryptInfo.packet_number_length - - AEAD_TAG_LENGTH); +hilti::rt::Bytes decrypt(const std::vector& client_key, const hilti::rt::Bytes& all_data, + uint64_t payload_length, const DecryptionInformation& decryptInfo) + { + int out, out2, res; - std::vector tag_to_check( - encrypted_packet.begin() + - decryptInfo.protected_header.size() + - payload_offset - - decryptInfo.packet_number_length - - AEAD_TAG_LENGTH, + if ( payload_length < decryptInfo.packet_number_length + AEAD_TAG_LENGTH ) + throw hilti::rt::RuntimeError( + hilti::rt::fmt("payload too small %ld < %ld", payload_length, + decryptInfo.packet_number_length + AEAD_TAG_LENGTH)); - encrypted_packet.begin() + - decryptInfo.protected_header.size() + - payload_offset - - decryptInfo.packet_number_length); + const uint8_t* encrypted_payload = data_as_uint8(all_data) + + decryptInfo.unprotected_header.size(); - unsigned char decrypt_buffer[MAXIMUM_PACKET_LENGTH]; + int encrypted_payload_size = payload_length - decryptInfo.packet_number_length - + AEAD_TAG_LENGTH; - // Setup context - auto cipher = EVP_aes_128_gcm(); - auto ctx = EVP_CIPHER_CTX_new(); + if ( encrypted_payload_size < 0 ) + throw hilti::rt::RuntimeError( + hilti::rt::fmt("encrypted_payload_size underflow %ld", encrypted_payload_size)); - EVP_CipherInit_ex(ctx, - cipher, - NULL, - NULL, - NULL, - 0); + 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)); - // Set the sizes for the IV and KEY - EVP_CIPHER_CTX_ctrl(ctx, - EVP_CTRL_CCM_SET_IVLEN, - decryptInfo.nonce.size(), - NULL); + const void* tag_to_check = all_data.data() + decryptInfo.unprotected_header.size() + + encrypted_payload_size; + int tag_to_check_length = AEAD_TAG_LENGTH; - EVP_CIPHER_CTX_set_key_length(ctx, - client_key.size()); + std::array decrypt_buffer; - // Set the KEY and IV - EVP_CipherInit_ex(ctx, - NULL, - NULL, - client_key.data(), - decryptInfo.nonce.data(), - 0); + // Setup context + auto cipher = EVP_aes_128_gcm(); + auto ctx = EVP_CIPHER_CTX_new(); - // Set the tag to be validated after decryption - EVP_CIPHER_CTX_ctrl(ctx, - EVP_CTRL_CCM_SET_TAG, - tag_to_check.size(), - tag_to_check.data()); + EVP_CipherInit_ex(ctx, cipher, NULL, NULL, NULL, 0); - // Setting the second parameter to NULL will pass it as Associated Data - EVP_CipherUpdate(ctx, - NULL, - &out, - decryptInfo.unprotected_header.data(), - decryptInfo.unprotected_header.size()); + // Set the sizes for the IV and KEY + EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_SET_IVLEN, decryptInfo.nonce.size(), NULL); - // Set the actual data to decrypt data into the decrypt_buffer. The amount of - // byte decrypted is stored into `out` - EVP_CipherUpdate(ctx, - decrypt_buffer, - &out, - encrypted_payload.data(), - encrypted_payload.size()); + EVP_CIPHER_CTX_set_key_length(ctx, client_key.size()); - // Validate whether the decryption was successful or not - EVP_CipherFinal_ex(ctx, NULL, &out2); + // Set the KEY and IV + EVP_CipherInit_ex(ctx, NULL, NULL, client_key.data(), decryptInfo.nonce.data(), 0); - // 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 decrypted_data(decrypt_buffer, decrypt_buffer + out); - return decrypted_data; -} + // Set the tag to be validated after decryption + EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_SET_TAG, tag_to_check_length, + const_cast(tag_to_check)); + + // Setting the second parameter to NULL will pass it as Associated Data + EVP_CipherUpdate(ctx, NULL, &out, decryptInfo.unprotected_header.data(), + decryptInfo.unprotected_header.size()); + + // Set the actual data to decrypt data into the decrypt_buffer. The amount of + // byte decrypted is stored into `out` + EVP_CipherUpdate(ctx, decrypt_buffer.data(), &out, encrypted_payload, encrypted_payload_size); + + // Validate whether the decryption was successful or not + 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); + } + + } /* 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`, which will eventually return the decrypted data and pass it back to Spicy. */ -hilti::rt::Bytes decrypt_crypto_payload( - const hilti::rt::Bytes &entire_packet, - const hilti::rt::Bytes &connection_id, - const hilti::rt::integer::safe &encrypted_offset, - const hilti::rt::integer::safe &payload_offset, - const hilti::rt::Bool &from_client) -{ +hilti::rt::Bytes +QUIC_decrypt_crypto_payload(const hilti::rt::Bytes& all_data, const hilti::rt::Bytes& connection_id, + const hilti::rt::integer::safe& encrypted_offset, + const hilti::rt::integer::safe& payload_length, + const hilti::rt::Bool& from_client) + { - // Fill in the entire packet bytes - std::vector e_pkt; - for (const auto &singlebyte : entire_packet) - { - e_pkt.push_back(singlebyte); - } + if ( payload_length < 20 ) + throw hilti::rt::RuntimeError(hilti::rt::fmt("payload too small %ld < 20", payload_length)); - std::vector cnnid; - for (const auto &singlebyte : connection_id) - { - cnnid.push_back(singlebyte); - } + if ( (all_data.size() < encrypted_offset + payload_length) ) + throw hilti::rt::RuntimeError(hilti::rt::fmt("packet too small %ld %ld", all_data.size(), + encrypted_offset + payload_length)); - std::vector initial_secret = hkdf_extract(cnnid); + std::vector initial_secret = hkdf_extract(connection_id); - std::vector server_client_secret; - if (from_client) - { - server_client_secret = hkdf_expand(INITIAL_SECRET_LEN, - initial_secret, - CLIENT_INITIAL_INFO); - } - else - { - server_client_secret = hkdf_expand(INITIAL_SECRET_LEN, - initial_secret, - SERVER_INITIAL_INFO); - } + std::vector server_client_secret; + if ( from_client ) + { + server_client_secret = hkdf_expand(INITIAL_SECRET_LEN, initial_secret, CLIENT_INITIAL_INFO); + } + else + { + server_client_secret = hkdf_expand(INITIAL_SECRET_LEN, initial_secret, SERVER_INITIAL_INFO); + } - std::vector key = hkdf_expand(AEAD_KEY_LEN, - server_client_secret, - KEY_INFO); - std::vector iv = hkdf_expand(AEAD_IV_LEN, - server_client_secret, - IV_INFO); - std::vector hp = hkdf_expand(AEAD_HP_LEN, - server_client_secret, - HP_INFO); + std::vector key = hkdf_expand(AEAD_KEY_LEN, server_client_secret, KEY_INFO); + std::vector iv = hkdf_expand(AEAD_IV_LEN, server_client_secret, IV_INFO); + std::vector 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 - decryptInfo.nonce = calculate_nonce(iv, decryptInfo.packet_number); + // Calculate the correct nonce for the decryption + decryptInfo.nonce = calculate_nonce(iv, decryptInfo.packet_number); - std::vector decrypted_data = decrypt(key, e_pkt, payload_offset, decryptInfo); - - // Return it as hilti Bytes again - hilti::rt::Bytes decr(decrypted_data.begin(), decrypted_data.end()); - return decr; -} + return decrypt(key, all_data, payload_length, decryptInfo); + } diff --git a/src/analyzer/protocol/quic/zeek_QUIC.spicy b/src/analyzer/protocol/quic/zeek_QUIC.spicy deleted file mode 100644 index c2013335d3..0000000000 --- a/src/analyzer/protocol/quic/zeek_QUIC.spicy +++ /dev/null @@ -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"); -} diff --git a/testing/btest/Baseline/scripts.base.protocols.quic.chromium/conn.log.cut b/testing/btest/Baseline/scripts.base.protocols.quic.chromium/conn.log.cut new file mode 100644 index 0000000000..46d72b1541 --- /dev/null +++ b/testing/btest/Baseline/scripts.base.protocols.quic.chromium/conn.log.cut @@ -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 diff --git a/testing/btest/Baseline/scripts.base.protocols.quic.chromium/quic.log b/testing/btest/Baseline/scripts.base.protocols.quic.chromium/quic.log new file mode 100644 index 0000000000..bd6779533d --- /dev/null +++ b/testing/btest/Baseline/scripts.base.protocols.quic.chromium/quic.log @@ -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 diff --git a/testing/btest/Baseline/scripts.base.protocols.quic.chromium/ssl.log b/testing/btest/Baseline/scripts.base.protocols.quic.chromium/ssl.log new file mode 100644 index 0000000000..03e4777402 --- /dev/null +++ b/testing/btest/Baseline/scripts.base.protocols.quic.chromium/ssl.log @@ -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 diff --git a/testing/btest/Baseline/scripts.base.protocols.quic.curl-http3/conn.log.cut b/testing/btest/Baseline/scripts.base.protocols.quic.curl-http3/conn.log.cut new file mode 100644 index 0000000000..46d72b1541 --- /dev/null +++ b/testing/btest/Baseline/scripts.base.protocols.quic.curl-http3/conn.log.cut @@ -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 diff --git a/testing/btest/Baseline/scripts.base.protocols.quic.curl-http3/quic.log b/testing/btest/Baseline/scripts.base.protocols.quic.curl-http3/quic.log new file mode 100644 index 0000000000..e5bfa31fe1 --- /dev/null +++ b/testing/btest/Baseline/scripts.base.protocols.quic.curl-http3/quic.log @@ -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 diff --git a/testing/btest/Baseline/scripts.base.protocols.quic.curl-http3/ssl.log b/testing/btest/Baseline/scripts.base.protocols.quic.curl-http3/ssl.log new file mode 100644 index 0000000000..0dfdef9290 --- /dev/null +++ b/testing/btest/Baseline/scripts.base.protocols.quic.curl-http3/ssl.log @@ -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 diff --git a/testing/btest/Baseline/scripts.base.protocols.quic.events/.stderr b/testing/btest/Baseline/scripts.base.protocols.quic.events/.stderr new file mode 100644 index 0000000000..49d861c74c --- /dev/null +++ b/testing/btest/Baseline/scripts.base.protocols.quic.events/.stderr @@ -0,0 +1 @@ +### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. diff --git a/testing/btest/Baseline/scripts.base.protocols.quic.events/out b/testing/btest/Baseline/scripts.base.protocols.quic.events/out new file mode 100644 index 0000000000..7b074d32a9 --- /dev/null +++ b/testing/btest/Baseline/scripts.base.protocols.quic.events/out @@ -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, diff --git a/testing/btest/Baseline/scripts.base.protocols.quic.firefox/conn.log.cut b/testing/btest/Baseline/scripts.base.protocols.quic.firefox/conn.log.cut new file mode 100644 index 0000000000..46d72b1541 --- /dev/null +++ b/testing/btest/Baseline/scripts.base.protocols.quic.firefox/conn.log.cut @@ -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 diff --git a/testing/btest/Baseline/scripts.base.protocols.quic.firefox/quic.log b/testing/btest/Baseline/scripts.base.protocols.quic.firefox/quic.log new file mode 100644 index 0000000000..c4f0a37a2c --- /dev/null +++ b/testing/btest/Baseline/scripts.base.protocols.quic.firefox/quic.log @@ -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 diff --git a/testing/btest/Baseline/scripts.base.protocols.quic.firefox/ssl.log b/testing/btest/Baseline/scripts.base.protocols.quic.firefox/ssl.log new file mode 100644 index 0000000000..fec40bbf3a --- /dev/null +++ b/testing/btest/Baseline/scripts.base.protocols.quic.firefox/ssl.log @@ -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 diff --git a/testing/btest/Baseline/scripts.base.protocols.quic.fragmented-crypto/conn.log.cut b/testing/btest/Baseline/scripts.base.protocols.quic.fragmented-crypto/conn.log.cut new file mode 100644 index 0000000000..46d72b1541 --- /dev/null +++ b/testing/btest/Baseline/scripts.base.protocols.quic.fragmented-crypto/conn.log.cut @@ -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 diff --git a/testing/btest/Baseline/scripts.base.protocols.quic.fragmented-crypto/ssl.log.cut b/testing/btest/Baseline/scripts.base.protocols.quic.fragmented-crypto/ssl.log.cut new file mode 100644 index 0000000000..aa1b155354 --- /dev/null +++ b/testing/btest/Baseline/scripts.base.protocols.quic.fragmented-crypto/ssl.log.cut @@ -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 diff --git a/testing/btest/Baseline/scripts.base.protocols.quic.interop.quic-go_quic-go.handshake/.stderr b/testing/btest/Baseline/scripts.base.protocols.quic.interop.quic-go_quic-go.handshake/.stderr new file mode 100644 index 0000000000..49d861c74c --- /dev/null +++ b/testing/btest/Baseline/scripts.base.protocols.quic.interop.quic-go_quic-go.handshake/.stderr @@ -0,0 +1 @@ +### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. diff --git a/testing/btest/Baseline/scripts.base.protocols.quic.interop.quic-go_quic-go.handshake/conn.log.cut b/testing/btest/Baseline/scripts.base.protocols.quic.interop.quic-go_quic-go.handshake/conn.log.cut new file mode 100644 index 0000000000..6eadcd2f9d --- /dev/null +++ b/testing/btest/Baseline/scripts.base.protocols.quic.interop.quic-go_quic-go.handshake/conn.log.cut @@ -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 diff --git a/testing/btest/Baseline/scripts.base.protocols.quic.interop.quic-go_quic-go.handshake/quic.log b/testing/btest/Baseline/scripts.base.protocols.quic.interop.quic-go_quic-go.handshake/quic.log new file mode 100644 index 0000000000..621bb65d9e --- /dev/null +++ b/testing/btest/Baseline/scripts.base.protocols.quic.interop.quic-go_quic-go.handshake/quic.log @@ -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 diff --git a/testing/btest/Baseline/scripts.base.protocols.quic.interop.quic-go_quic-go.handshake/ssl.log b/testing/btest/Baseline/scripts.base.protocols.quic.interop.quic-go_quic-go.handshake/ssl.log new file mode 100644 index 0000000000..ec635dc9fb --- /dev/null +++ b/testing/btest/Baseline/scripts.base.protocols.quic.interop.quic-go_quic-go.handshake/ssl.log @@ -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 diff --git a/testing/btest/Baseline/scripts.base.protocols.quic.interop.quic-go_quic-go.retry/.stderr b/testing/btest/Baseline/scripts.base.protocols.quic.interop.quic-go_quic-go.retry/.stderr new file mode 100644 index 0000000000..49d861c74c --- /dev/null +++ b/testing/btest/Baseline/scripts.base.protocols.quic.interop.quic-go_quic-go.retry/.stderr @@ -0,0 +1 @@ +### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. diff --git a/testing/btest/Baseline/scripts.base.protocols.quic.interop.quic-go_quic-go.retry/conn.log.cut b/testing/btest/Baseline/scripts.base.protocols.quic.interop.quic-go_quic-go.retry/conn.log.cut new file mode 100644 index 0000000000..bb59162bc7 --- /dev/null +++ b/testing/btest/Baseline/scripts.base.protocols.quic.interop.quic-go_quic-go.retry/conn.log.cut @@ -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 diff --git a/testing/btest/Baseline/scripts.base.protocols.quic.interop.quic-go_quic-go.retry/quic.log b/testing/btest/Baseline/scripts.base.protocols.quic.interop.quic-go_quic-go.retry/quic.log new file mode 100644 index 0000000000..69bd69a8a4 --- /dev/null +++ b/testing/btest/Baseline/scripts.base.protocols.quic.interop.quic-go_quic-go.retry/quic.log @@ -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 diff --git a/testing/btest/Baseline/scripts.base.protocols.quic.interop.quic-go_quic-go.retry/ssl.log b/testing/btest/Baseline/scripts.base.protocols.quic.interop.quic-go_quic-go.retry/ssl.log new file mode 100644 index 0000000000..fcdb1fe6e5 --- /dev/null +++ b/testing/btest/Baseline/scripts.base.protocols.quic.interop.quic-go_quic-go.retry/ssl.log @@ -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 diff --git a/testing/btest/Baseline/scripts.base.protocols.quic.interop.quic-go_quic-go.zerortt/.stderr b/testing/btest/Baseline/scripts.base.protocols.quic.interop.quic-go_quic-go.zerortt/.stderr new file mode 100644 index 0000000000..49d861c74c --- /dev/null +++ b/testing/btest/Baseline/scripts.base.protocols.quic.interop.quic-go_quic-go.zerortt/.stderr @@ -0,0 +1 @@ +### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. diff --git a/testing/btest/Baseline/scripts.base.protocols.quic.interop.quic-go_quic-go.zerortt/conn.log.cut b/testing/btest/Baseline/scripts.base.protocols.quic.interop.quic-go_quic-go.zerortt/conn.log.cut new file mode 100644 index 0000000000..01d1a432a4 --- /dev/null +++ b/testing/btest/Baseline/scripts.base.protocols.quic.interop.quic-go_quic-go.zerortt/conn.log.cut @@ -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 diff --git a/testing/btest/Baseline/scripts.base.protocols.quic.interop.quic-go_quic-go.zerortt/quic.log b/testing/btest/Baseline/scripts.base.protocols.quic.interop.quic-go_quic-go.zerortt/quic.log new file mode 100644 index 0000000000..7ed65c4c11 --- /dev/null +++ b/testing/btest/Baseline/scripts.base.protocols.quic.interop.quic-go_quic-go.zerortt/quic.log @@ -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 diff --git a/testing/btest/Baseline/scripts.base.protocols.quic.interop.quic-go_quic-go.zerortt/ssl.log b/testing/btest/Baseline/scripts.base.protocols.quic.interop.quic-go_quic-go.zerortt/ssl.log new file mode 100644 index 0000000000..89e25b0914 --- /dev/null +++ b/testing/btest/Baseline/scripts.base.protocols.quic.interop.quic-go_quic-go.zerortt/ssl.log @@ -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 diff --git a/testing/btest/Baseline/scripts.base.protocols.quic.quic-log/.stderr b/testing/btest/Baseline/scripts.base.protocols.quic.quic-log/.stderr new file mode 100644 index 0000000000..49d861c74c --- /dev/null +++ b/testing/btest/Baseline/scripts.base.protocols.quic.quic-log/.stderr @@ -0,0 +1 @@ +### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. diff --git a/testing/btest/Baseline/scripts.base.protocols.quic.quic-log/quic.log b/testing/btest/Baseline/scripts.base.protocols.quic.quic-log/quic.log new file mode 100644 index 0000000000..dfc958bd04 --- /dev/null +++ b/testing/btest/Baseline/scripts.base.protocols.quic.quic-log/quic.log @@ -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 diff --git a/testing/btest/Baseline/scripts.base.protocols.quic.quicdoq/conn.log.cut b/testing/btest/Baseline/scripts.base.protocols.quic.quicdoq/conn.log.cut new file mode 100644 index 0000000000..46d72b1541 --- /dev/null +++ b/testing/btest/Baseline/scripts.base.protocols.quic.quicdoq/conn.log.cut @@ -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 diff --git a/testing/btest/Baseline/scripts.base.protocols.quic.quicdoq/quic.log b/testing/btest/Baseline/scripts.base.protocols.quic.quicdoq/quic.log new file mode 100644 index 0000000000..3cc5f1b42e --- /dev/null +++ b/testing/btest/Baseline/scripts.base.protocols.quic.quicdoq/quic.log @@ -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 diff --git a/testing/btest/Baseline/scripts.base.protocols.quic.quicdoq/ssl.log b/testing/btest/Baseline/scripts.base.protocols.quic.quicdoq/ssl.log new file mode 100644 index 0000000000..5897dd238a --- /dev/null +++ b/testing/btest/Baseline/scripts.base.protocols.quic.quicdoq/ssl.log @@ -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 diff --git a/testing/btest/Baseline/scripts.base.protocols.quic.run-pcap/conn.log b/testing/btest/Baseline/scripts.base.protocols.quic.run-pcap/conn.log deleted file mode 100644 index 7e2c337793..0000000000 --- a/testing/btest/Baseline/scripts.base.protocols.quic.run-pcap/conn.log +++ /dev/null @@ -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 diff --git a/testing/btest/Baseline/scripts.base.protocols.quic.run-pcap/conn.log.cut b/testing/btest/Baseline/scripts.base.protocols.quic.run-pcap/conn.log.cut new file mode 100644 index 0000000000..46d72b1541 --- /dev/null +++ b/testing/btest/Baseline/scripts.base.protocols.quic.run-pcap/conn.log.cut @@ -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 diff --git a/testing/btest/Baseline/scripts.base.protocols.quic.run-pcap/ssl.log b/testing/btest/Baseline/scripts.base.protocols.quic.run-pcap/ssl.log index 496f6a47f8..c58f04e799 100644 --- a/testing/btest/Baseline/scripts.base.protocols.quic.run-pcap/ssl.log +++ b/testing/btest/Baseline/scripts.base.protocols.quic.run-pcap/ssl.log @@ -7,5 +7,5 @@ #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 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 diff --git a/testing/btest/Baseline/scripts.base.protocols.quic.vector-max-size-crash/analyzer.log.cut b/testing/btest/Baseline/scripts.base.protocols.quic.vector-max-size-crash/analyzer.log.cut new file mode 100644 index 0000000000..2b3de832a7 --- /dev/null +++ b/testing/btest/Baseline/scripts.base.protocols.quic.vector-max-size-crash/analyzer.log.cut @@ -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::) diff --git a/testing/btest/Baseline/scripts.base.protocols.quic.vector-max-size-crash/conn.log.cut b/testing/btest/Baseline/scripts.base.protocols.quic.vector-max-size-crash/conn.log.cut new file mode 100644 index 0000000000..240a5abdd2 --- /dev/null +++ b/testing/btest/Baseline/scripts.base.protocols.quic.vector-max-size-crash/conn.log.cut @@ -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 - diff --git a/testing/btest/Traces/quic/chromium-115.0.5790.110-api-cirrus-com.pcap b/testing/btest/Traces/quic/chromium-115.0.5790.110-api-cirrus-com.pcap new file mode 100644 index 0000000000..cc203de8b9 Binary files /dev/null and b/testing/btest/Traces/quic/chromium-115.0.5790.110-api-cirrus-com.pcap differ diff --git a/testing/btest/Traces/quic/chromium-115.0.5790.110-google-de-fragmented.pcap b/testing/btest/Traces/quic/chromium-115.0.5790.110-google-de-fragmented.pcap new file mode 100644 index 0000000000..96c3ed2af8 Binary files /dev/null and b/testing/btest/Traces/quic/chromium-115.0.5790.110-google-de-fragmented.pcap differ diff --git a/testing/btest/Traces/quic/curl-8.1.2-dev-http3-www-google-de.pcap b/testing/btest/Traces/quic/curl-8.1.2-dev-http3-www-google-de.pcap new file mode 100644 index 0000000000..6cf69b82de Binary files /dev/null and b/testing/btest/Traces/quic/curl-8.1.2-dev-http3-www-google-de.pcap differ diff --git a/testing/btest/Traces/quic/firefox-102.13.0esr-blog-cloudflare-com.pcap b/testing/btest/Traces/quic/firefox-102.13.0esr-blog-cloudflare-com.pcap new file mode 100644 index 0000000000..0ab3758319 Binary files /dev/null and b/testing/btest/Traces/quic/firefox-102.13.0esr-blog-cloudflare-com.pcap differ diff --git a/testing/btest/Traces/quic/interop/attribution.txt b/testing/btest/Traces/quic/interop/attribution.txt new file mode 100644 index 0000000000..776030ae66 --- /dev/null +++ b/testing/btest/Traces/quic/interop/attribution.txt @@ -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 diff --git a/testing/btest/Traces/quic/interop/quic-go_quic-go/handshake.pcap b/testing/btest/Traces/quic/interop/quic-go_quic-go/handshake.pcap new file mode 100644 index 0000000000..ea4fccc2c7 Binary files /dev/null and b/testing/btest/Traces/quic/interop/quic-go_quic-go/handshake.pcap differ diff --git a/testing/btest/Traces/quic/interop/quic-go_quic-go/retry.pcap b/testing/btest/Traces/quic/interop/quic-go_quic-go/retry.pcap new file mode 100644 index 0000000000..f929b83621 Binary files /dev/null and b/testing/btest/Traces/quic/interop/quic-go_quic-go/retry.pcap differ diff --git a/testing/btest/Traces/quic/interop/quic-go_quic-go/zerortt.pcap b/testing/btest/Traces/quic/interop/quic-go_quic-go/zerortt.pcap new file mode 100644 index 0000000000..72796e59a5 Binary files /dev/null and b/testing/btest/Traces/quic/interop/quic-go_quic-go/zerortt.pcap differ diff --git a/testing/btest/Traces/quic/quicdoq.pcap b/testing/btest/Traces/quic/quicdoq.pcap new file mode 100644 index 0000000000..2d89536ed5 Binary files /dev/null and b/testing/btest/Traces/quic/quicdoq.pcap differ diff --git a/testing/btest/Traces/quic/vector-max-size-crash.pcap b/testing/btest/Traces/quic/vector-max-size-crash.pcap new file mode 100644 index 0000000000..f1c4ffbac4 Binary files /dev/null and b/testing/btest/Traces/quic/vector-max-size-crash.pcap differ diff --git a/testing/btest/scripts/base/protocols/quic/chromium.zeek b/testing/btest/scripts/base/protocols/quic/chromium.zeek new file mode 100644 index 0000000000..32b57ba157 --- /dev/null +++ b/testing/btest/scripts/base/protocols/quic/chromium.zeek @@ -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 diff --git a/testing/btest/scripts/base/protocols/quic/curl-http3.zeek b/testing/btest/scripts/base/protocols/quic/curl-http3.zeek new file mode 100644 index 0000000000..96d12eac6d --- /dev/null +++ b/testing/btest/scripts/base/protocols/quic/curl-http3.zeek @@ -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 diff --git a/testing/btest/scripts/base/protocols/quic/events.zeek b/testing/btest/scripts/base/protocols/quic/events.zeek new file mode 100644 index 0000000000..79759fc219 --- /dev/null +++ b/testing/btest/scripts/base/protocols/quic/events.zeek @@ -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); + } diff --git a/testing/btest/scripts/base/protocols/quic/firefox.zeek b/testing/btest/scripts/base/protocols/quic/firefox.zeek new file mode 100644 index 0000000000..b59094d02d --- /dev/null +++ b/testing/btest/scripts/base/protocols/quic/firefox.zeek @@ -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 diff --git a/testing/btest/scripts/base/protocols/quic/fragmented-crypto.zeek b/testing/btest/scripts/base/protocols/quic/fragmented-crypto.zeek new file mode 100644 index 0000000000..98e5efb438 --- /dev/null +++ b/testing/btest/scripts/base/protocols/quic/fragmented-crypto.zeek @@ -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 diff --git a/testing/btest/scripts/base/protocols/quic/interop/quic-go_quic-go/handshake.zeek b/testing/btest/scripts/base/protocols/quic/interop/quic-go_quic-go/handshake.zeek new file mode 100644 index 0000000000..00b646ff73 --- /dev/null +++ b/testing/btest/scripts/base/protocols/quic/interop/quic-go_quic-go/handshake.zeek @@ -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 diff --git a/testing/btest/scripts/base/protocols/quic/interop/quic-go_quic-go/retry.zeek b/testing/btest/scripts/base/protocols/quic/interop/quic-go_quic-go/retry.zeek new file mode 100644 index 0000000000..78b35b075f --- /dev/null +++ b/testing/btest/scripts/base/protocols/quic/interop/quic-go_quic-go/retry.zeek @@ -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 diff --git a/testing/btest/scripts/base/protocols/quic/interop/quic-go_quic-go/zerortt.zeek b/testing/btest/scripts/base/protocols/quic/interop/quic-go_quic-go/zerortt.zeek new file mode 100644 index 0000000000..213b926100 --- /dev/null +++ b/testing/btest/scripts/base/protocols/quic/interop/quic-go_quic-go/zerortt.zeek @@ -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 diff --git a/testing/btest/scripts/base/protocols/quic/quic-log.zeek b/testing/btest/scripts/base/protocols/quic/quic-log.zeek new file mode 100644 index 0000000000..4e720ed71a --- /dev/null +++ b/testing/btest/scripts/base/protocols/quic/quic-log.zeek @@ -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 diff --git a/testing/btest/scripts/base/protocols/quic/quicdoq.zeek b/testing/btest/scripts/base/protocols/quic/quicdoq.zeek new file mode 100644 index 0000000000..c60cbf78e1 --- /dev/null +++ b/testing/btest/scripts/base/protocols/quic/quicdoq.zeek @@ -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 diff --git a/testing/btest/scripts/base/protocols/quic/run-pcap.zeek b/testing/btest/scripts/base/protocols/quic/run-pcap.zeek index 763993316c..46d00d88b2 100644 --- a/testing/btest/scripts/base/protocols/quic/run-pcap.zeek +++ b/testing/btest/scripts/base/protocols/quic/run-pcap.zeek @@ -1,4 +1,5 @@ # @TEST-DOC: Test that runs the pcap -# @TEST-EXEC: zeek -Cr $TRACES/quic/quic_win11_firefox_google.pcap base/protocols/quic >output -# @TEST-EXEC: btest-diff conn.log -# @TEST-EXEC: btest-diff ssl.log \ No newline at end of file +# @TEST-EXEC: zeek -Cr $TRACES/quic/quic_win11_firefox_google.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 diff --git a/testing/btest/scripts/base/protocols/quic/vector-max-size-crash.zeek b/testing/btest/scripts/base/protocols/quic/vector-max-size-crash.zeek new file mode 100644 index 0000000000..24153e5db2 --- /dev/null +++ b/testing/btest/scripts/base/protocols/quic/vector-max-size-crash.zeek @@ -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::)/g" | $SCRIPTS/diff-remove-abspath' btest-diff analyzer.log.cut