Add basic DTLSv1.3 support

DTLSv1.3 changes the DTLS record format, introducing a completely new
header - which is a first for DTLS.

We don't currently completely parse this header, as this requires a bit
more statekeeping. This will be added in a future revision. This also
also has little practical implications.
This commit is contained in:
Johanna Amann 2023-05-03 16:15:47 +01:00
parent e7c9fa1f6e
commit d6c4c510ea
7 changed files with 88 additions and 5 deletions

View file

@ -11,6 +11,7 @@ export {
const DTLSv10 = 0xFEFF; const DTLSv10 = 0xFEFF;
# DTLSv11 does not exist # DTLSv11 does not exist
const DTLSv12 = 0xFEFD; const DTLSv12 = 0xFEFD;
const DTLSv13 = 0xFEFC;
## Mapping between the constants and string values for SSL/TLS versions. ## Mapping between the constants and string values for SSL/TLS versions.
const version_strings: table[count] of string = { const version_strings: table[count] of string = {
@ -21,7 +22,8 @@ export {
[TLSv12] = "TLSv12", [TLSv12] = "TLSv12",
[TLSv13] = "TLSv13", [TLSv13] = "TLSv13",
[DTLSv10] = "DTLSv10", [DTLSv10] = "DTLSv10",
[DTLSv12] = "DTLSv12" [DTLSv12] = "DTLSv12",
[DTLSv13] = "DTLSv13"
} &default=function(i: count):string } &default=function(i: count):string
{ {
if ( i/0xFF == 0x7F ) # TLS 1.3 draft if ( i/0xFF == 0x7F ) # TLS 1.3 draft

View file

@ -138,6 +138,31 @@ refine connection SSL_Conn += {
return true; return true;
%} %}
function proc_unified_record(is_orig: bool, ur: UnifiedRecord) : bool
%{
// we don't have a CCS packet anymore - so let's just assume the connection is established once we have seen a packet from each direction.
if ( is_orig )
client_state_ = STATE_ENCRYPTED;
else
server_state_ = STATE_ENCRYPTED;
if ( client_state_ == STATE_ENCRYPTED && server_state_ == STATE_ENCRYPTED && established_ == false )
{
established_ = true;
if ( ssl_established )
zeek::BifEvent::enqueue_ssl_established(zeek_analyzer(), zeek_analyzer()->Conn());
}
if ( ssl_encrypted_data )
{
// FIXME: swallow is not quite the correct length, because we are not parsing the entire header
zeek::BifEvent::enqueue_ssl_encrypted_data(zeek_analyzer(),
zeek_analyzer()->Conn(), is_orig ^ zeek_analyzer()->GetFlipped(), DTLSv13, APPLICATION_DATA, ur->swallow().length());
}
return true;
%}
}; };
refine typeattr SSLRecord += &let { refine typeattr SSLRecord += &let {
@ -147,3 +172,7 @@ refine typeattr SSLRecord += &let {
refine typeattr Handshake += &let { refine typeattr Handshake += &let {
proc: bool = $context.connection.proc_handshake(rec, this); proc: bool = $context.connection.proc_handshake(rec, this);
}; };
refine typeattr UnifiedRecord += &let {
proc: bool = $context.connection.proc_unified_record(is_orig, this);
};

View file

@ -4,11 +4,37 @@
###################################################################### ######################################################################
type DTLSPDU(is_orig: bool) = record { type DTLSPDU(is_orig: bool) = record {
records: SSLRecord(is_orig)[] &transient; records: SSLRecordSwitch(is_orig)[] &transient;
}; };
type SSLRecord(is_orig: bool) = record { # This feels like (another) really dirty hack. DTLS 1.3 introduces a new way in which ciphertext records
content_type: uint8; # can be encoded, using a new unified header, which is completely different from the earlier DTLS headers.
# It only is used after the client & server hello - which essentially are the same as in DTLS 1.2 (including
# using the same record-layer versions - which is why `dtls_version_ok` underneath does not refer to DTLS 1.3)
# The DTLS 1.3 unified header is signaled by the first 3 bits of the first byte being set to `001`, but only
# after DTLS 1.3 has been negotiated.
type SSLRecordSwitch(is_orig: bool) = record {
firstbyte: uint8;
cont: case $context.connection.choose_record_type(firstbyte) of {
false -> rec: SSLRecord(firstbyte, is_orig);
true -> unified: UnifiedRecord(firstbyte, is_orig);
};
};
type UnifiedRecord(firstbyte: uint8, is_orig: bool) = record {
# sequence_number: bytestring &length=(sequence_number_length?2:1);
# lets just ignore eveything for now. We have very limited example
# data, and it is hard to parse the CID due to variable length.
swallow: bytestring &restofdata;
} &let {
with_cid: bool = ((firstbyte&0x10)==0x10);
sequence_number_length: bool = ((firstbyte&0x08)==0x08);
lengh_present: bool = ((firstbyte&0x04)==0x04);
epoch_low_bits: uint8 = (firstbyte&0x03);
};
type SSLRecord(content_type: uint8, is_orig: bool) = record {
version: uint16; version: uint16;
# the epoch signalizes that a changecipherspec message has been received. Hence, everything with # the epoch signalizes that a changecipherspec message has been received. Hence, everything with
# an epoch > 0 should be encrypted # an epoch > 0 should be encrypted
@ -83,4 +109,11 @@ refine connection SSL_Conn += {
} }
%} %}
function choose_record_type(firstbyte: uint8): bool
%{
uint16_t negotiated_version = zeek_analyzer()->GetNegotiatedVersion();
if ( negotiated_version == DTLSv13 && ( (firstbyte & 0x20) == 0x20 ) )
return true;
return false;
%}
}; };

View file

@ -77,6 +77,7 @@ function version_ok(vers : uint16) : bool
case TLSv13: case TLSv13:
case DTLSv10: case DTLSv10:
case DTLSv12: case DTLSv12:
case DTLSv13:
return true; return true;
default: default:
@ -121,7 +122,8 @@ enum SSLVersions {
DTLSv10 = 0xFEFF, DTLSv10 = 0xFEFF,
# DTLSv11 does not exist. # DTLSv11 does not exist.
DTLSv12 = 0xFEFD DTLSv12 = 0xFEFD,
DTLSv13 = 0xFEFC
}; };
enum SSLExtensions { enum SSLExtensions {

View file

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

Binary file not shown.

View file

@ -0,0 +1,6 @@
# This tests a normal SSL connection and the log it outputs.
# @TEST-EXEC: zeek -C -r $TRACES/tls/dtls13-wolfssl.pcap %INPUT
# @TEST-EXEC: btest-diff ssl.log
# @TEST-EXEC: test ! -f dpd.log