Support DTLS in TLS::Proxy.
authorFrederik Wedel-Heinen <frederik.wedel-heinen@dencrypt.dk>
Thu, 11 Jan 2024 13:18:07 +0000 (14:18 +0100)
committerMatt Caswell <matt@openssl.org>
Fri, 9 Feb 2024 08:11:23 +0000 (08:11 +0000)
Fixes #23199

Reviewed-by: Tomas Mraz <tomas@openssl.org>
Reviewed-by: Matt Caswell <matt@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/23319)

16 files changed:
test/recipes/70-test_dtlsrecords.t [new file with mode: 0644]
test/recipes/70-test_sslcbcpadding.t
test/recipes/70-test_sslrecords.t
test/recipes/70-test_tls13hrr.t
util/perl/TLSProxy/Certificate.pm
util/perl/TLSProxy/CertificateRequest.pm
util/perl/TLSProxy/CertificateVerify.pm
util/perl/TLSProxy/ClientHello.pm
util/perl/TLSProxy/EncryptedExtensions.pm
util/perl/TLSProxy/HelloVerifyRequest.pm [new file with mode: 0644]
util/perl/TLSProxy/Message.pm
util/perl/TLSProxy/NewSessionTicket.pm
util/perl/TLSProxy/Proxy.pm
util/perl/TLSProxy/Record.pm
util/perl/TLSProxy/ServerHello.pm
util/perl/TLSProxy/ServerKeyExchange.pm

diff --git a/test/recipes/70-test_dtlsrecords.t b/test/recipes/70-test_dtlsrecords.t
new file mode 100644 (file)
index 0000000..99ce112
--- /dev/null
@@ -0,0 +1,153 @@
+#! /usr/bin/env perl
+# Copyright 2024 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the Apache License 2.0 (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+use strict;
+use feature 'state';
+
+use OpenSSL::Test qw/:DEFAULT cmdstr srctop_file bldtop_dir/;
+use OpenSSL::Test::Utils;
+use TLSProxy::Proxy;
+use TLSProxy::Message;
+
+my $test_name = "test_dtlsrecords";
+setup($test_name);
+
+plan skip_all => "TLSProxy isn't usable on $^O"
+    if $^O =~ /^(VMS)$/;
+
+plan skip_all => "$test_name needs the dynamic engine feature enabled"
+    if disabled("engine") || disabled("dynamic-engine");
+
+plan skip_all => "$test_name needs the sock feature enabled"
+    if disabled("sock");
+
+plan skip_all => "$test_name needs DTLSv1.2 enabled"
+    if disabled("dtls1_2");
+
+my $proxy = TLSProxy::Proxy->new_dtls(
+    undef,
+    cmdstr(app(["openssl"]), display => 1),
+    srctop_file("apps", "server.pem"),
+    (!$ENV{HARNESS_ACTIVE} || $ENV{HARNESS_VERBOSE})
+);
+
+plan tests => 4;
+
+my $fatal_alert = 0;        # set by filters at expected fatal alerts
+my $inject_recs_num = 0;    # used by add_empty_recs_filter
+my $proxy_start_success = 0;
+
+#Test 1: Injecting out of context empty records should succeed
+my $content_type = TLSProxy::Record::RT_APPLICATION_DATA;
+$inject_recs_num = 1;
+$proxy->serverflags("-min_protocol DTLSv1.2 -max_protocol DTLSv1.2");
+$proxy->clientflags("-max_protocol DTLSv1.2");
+$proxy->filter(\&add_empty_recs_filter);
+$proxy_start_success = $proxy->start();
+ok($proxy_start_success && TLSProxy::Message->success(), "Out of context empty records test");
+
+#Test 2: Injecting in context empty records should succeed
+$proxy->clear();
+$content_type = TLSProxy::Record::RT_HANDSHAKE;
+$inject_recs_num = 1;
+$proxy->serverflags("-min_protocol DTLSv1.2 -max_protocol DTLSv1.2");
+$proxy->clientflags("-max_protocol DTLSv1.2");
+$proxy->filter(\&add_empty_recs_filter);
+$proxy_start_success = $proxy->start();
+ok($proxy_start_success && TLSProxy::Message->success(), "In context empty records test");
+
+#Unrecognised record type tests
+
+#Test 3: Sending an unrecognised record type in DTLSv1.2 should fail
+$fatal_alert = 0;
+$proxy->clear();
+$proxy->serverflags("-min_protocol DTLSv1.2 -max_protocol DTLSv1.2");
+$proxy->clientflags("-max_protocol DTLSv1.2");
+$proxy->filter(\&add_unknown_record_type);
+ok($proxy->start() == 0, "Unrecognised record type in DTLS1.2");
+
+SKIP: {
+    skip "DTLSv1 disabled", 1 if disabled("dtls1");
+
+    #Test 4: Sending an unrecognised record type in DTLSv1 should fail
+    $fatal_alert = 0;
+    $proxy->clear();
+    $proxy->clientflags("-min_protocol DTLSv1 -max_protocol DTLSv1 -cipher DEFAULT:\@SECLEVEL=0");
+    $proxy->ciphers("AES128-SHA:\@SECLEVEL=0");
+    $proxy->filter(\&add_unknown_record_type);
+    ok($proxy->start() == 0, "Unrecognised record type in DTLSv1");
+}
+
+sub add_empty_recs_filter
+{
+    my $proxy = shift;
+    my $records = $proxy->record_list;
+
+    # We're only interested in the initial ClientHello
+    if ($proxy->flight != 0) {
+        $fatal_alert = 1 if @{$records}[-1]->is_fatal_alert(1) == TLSProxy::Message::AL_DESC_UNEXPECTED_MESSAGE;
+        return;
+    }
+
+    for (my $i = 0; $i < $inject_recs_num; $i++) {
+        my $record = TLSProxy::Record->new_dtls(
+            0,
+            $content_type,
+            TLSProxy::Record::VERS_TLS_1_2,
+            0,
+            0,
+            0,
+            0,
+            0,
+            0,
+            "",
+            ""
+        );
+        push @{$records}, $record;
+    }
+}
+
+sub add_unknown_record_type
+{
+    my $proxy = shift;
+    my $records = $proxy->record_list;
+    state $added_record;
+
+    # We'll change a record after the initial version neg has taken place
+    if ($proxy->flight == 0) {
+        $added_record = 0;
+        return;
+    } elsif ($proxy->flight != 1 || $added_record) {
+        $fatal_alert = 1 if @{$records}[-1]->is_fatal_alert(0) == TLSProxy::Message::AL_DESC_UNEXPECTED_MESSAGE;
+        return;
+    }
+
+    my $record = TLSProxy::Record->new_dtls(
+        1,
+        TLSProxy::Record::RT_UNKNOWN,
+        @{$records}[-1]->version(),
+        @{$records}[-1]->epoch(),
+        @{$records}[-1]->seq() +1,
+        1,
+        0,
+        1,
+        1,
+        "X",
+        "X"
+    );
+
+    #Find ServerHello record and insert after that
+    my $i;
+    for ($i = 0; ${$proxy->record_list}[$i]->flight() < 1; $i++) {
+        next;
+    }
+    $i++;
+
+    splice @{$proxy->record_list}, $i, 0, $record;
+    $added_record = 1;
+}
index c24f315c60024733f7e128b87387d9e1ea6bd686..e49efb28e255b0f0caca2d22b7a196b6be1ac0b5 100644 (file)
@@ -127,6 +127,6 @@ sub add_maximal_padding_filter
     } elsif ($sent_corrupted_payload) {
         # Check for bad_record_mac from client
         my $last_record = @{$proxy->record_list}[-1];
-        $fatal_alert = 1 if $last_record->is_fatal_alert(0) == 20;
+        $fatal_alert = 1 if $last_record->is_fatal_alert(0) == TLSProxy::Message::AL_DESC_BAD_RECORD_MAC;
     }
 }
index 9a7e3d8c06839263174375d0064be66b69e37916..43e288b63e5648413d52150de0881ab047161bce 100644 (file)
@@ -12,6 +12,7 @@ use feature 'state';
 use OpenSSL::Test qw/:DEFAULT cmdstr srctop_file bldtop_dir/;
 use OpenSSL::Test::Utils;
 use TLSProxy::Proxy;
+use TLSProxy::Message;
 
 my $test_name = "test_sslrecords";
 setup($test_name);
@@ -273,7 +274,7 @@ sub add_empty_recs_filter
 
     # We're only interested in the initial ClientHello
     if ($proxy->flight != 0) {
-        $fatal_alert = 1 if @{$records}[-1]->is_fatal_alert(1) == 10;
+        $fatal_alert = 1 if @{$records}[-1]->is_fatal_alert(1) == TLSProxy::Message::AL_DESC_UNEXPECTED_MESSAGE;
         return;
     }
 
@@ -301,7 +302,7 @@ sub add_frag_alert_filter
 
     # We're only interested in the initial ClientHello
     if ($proxy->flight != 0) {
-        $fatal_alert = 1 if @{$records}[-1]->is_fatal_alert(1) == 10;
+        $fatal_alert = 1 if @{$records}[-1]->is_fatal_alert(1) == TLSProxy::Message::AL_DESC_UNEXPECTED_MESSAGE;
         return;
     }
 
@@ -507,7 +508,7 @@ sub add_unknown_record_type
         $added_record = 0;
         return;
     } elsif ($proxy->flight != 1 || $added_record) {
-        $fatal_alert = 1 if @{$records}[-1]->is_fatal_alert(0) == 10;
+        $fatal_alert = 1 if @{$records}[-1]->is_fatal_alert(0) == TLSProxy::Message::AL_DESC_UNEXPECTED_MESSAGE;
         return;
     }
 
@@ -541,7 +542,7 @@ sub change_version
 
     # We'll change a version after the initial version neg has taken place
     if ($proxy->flight != 1) {
-        $fatal_alert = 1 if @{$records}[-1]->is_fatal_alert(0) == 70;
+        $fatal_alert = 1 if @{$records}[-1]->is_fatal_alert(0) == TLSProxy::Message::AL_DESC_PROTOCOL_VERSION;
         return;
     }
 
@@ -578,7 +579,7 @@ sub change_outer_record_type
 
     # We'll change a record after the initial version neg has taken place
     if ($proxy->flight != 1) {
-        $fatal_alert = 1 if @{$records}[-1]->is_fatal_alert(0) == 10;
+        $fatal_alert = 1 if @{$records}[-1]->is_fatal_alert(0) == TLSProxy::Message::AL_DESC_UNEXPECTED_MESSAGE;
         return;
     }
 
@@ -601,7 +602,7 @@ sub not_on_record_boundary
 
     #Find server's first flight
     if ($proxy->flight != 1) {
-        $fatal_alert = 1 if @{$records}[-1]->is_fatal_alert(0) == 10;
+        $fatal_alert = 1 if @{$records}[-1]->is_fatal_alert(0) == TLSProxy::Message::AL_DESC_UNEXPECTED_MESSAGE;
         return;
     }
 
index 3feabef060ce70668209eb79a108d982d4bac3a5..c49a6be88bd9469cfe02dd33ad00b033eaa8718d 100644 (file)
@@ -10,6 +10,7 @@ use strict;
 use OpenSSL::Test qw/:DEFAULT cmdstr srctop_file bldtop_dir/;
 use OpenSSL::Test::Utils;
 use TLSProxy::Proxy;
+use TLSProxy::Message;
 
 my $test_name = "test_tls13hrr";
 setup($test_name);
@@ -122,7 +123,7 @@ sub hrr_filter
         # and the unexpected_message alert from client
         if ($proxy->flight == 4) {
             $fatal_alert = 1
-                if @{$proxy->record_list}[-1]->is_fatal_alert(0) == 10;
+                if @{$proxy->record_list}[-1]->is_fatal_alert(0) == TLSProxy::Message::AL_DESC_UNEXPECTED_MESSAGE;
             return;
         }
         if ($proxy->flight != 3) {
index 03f661995482ed0a004bacfb1d9939a26b81ac1a..a32bc2c97b7dae029d1a07b1173d1c39b09bf180 100644 (file)
@@ -15,15 +15,23 @@ push @ISA, 'TLSProxy::Message';
 sub new
 {
     my $class = shift;
-    my ($server,
+    my ($isdtls,
+        $server,
+        $msgseq,
+        $msgfrag,
+        $msgfragoffs,
         $data,
         $records,
         $startoffset,
         $message_frag_lens) = @_;
 
     my $self = $class->SUPER::new(
+        $isdtls,
         $server,
         TLSProxy::Message::MT_CERTIFICATE,
+        $msgseq,
+        $msgfrag,
+        $msgfragoffs,
         $data,
         $records,
         $startoffset,
index 193bea168a53260ebe0bbb8d3c35d458f4f56e89..0191df68f9fbf8938576752a9a436785a2bd8a2c 100644 (file)
@@ -15,15 +15,23 @@ push @ISA, 'TLSProxy::Message';
 sub new
 {
     my $class = shift;
-    my ($server,
+    my ($isdtls,
+        $server,
+        $msgseq,
+        $msgfrag,
+        $msgfragoffs,
         $data,
         $records,
         $startoffset,
         $message_frag_lens) = @_;
 
     my $self = $class->SUPER::new(
+        $isdtls,
         $server,
         TLSProxy::Message::MT_CERTIFICATE_REQUEST,
+        $msgseq,
+        $msgfrag,
+        $msgfragoffs,
         $data,
         $records,
         $startoffset,
index fe45001405028417c270eebade8d3d5ff5166756..95922729918c5b95a5f01b3a26a24d9479d5f9f5 100644 (file)
@@ -15,15 +15,23 @@ push @ISA, 'TLSProxy::Message';
 sub new
 {
     my $class = shift;
-    my ($server,
+    my ($isdtls,
+        $server,
+        $msgseq,
+        $msgfrag,
+        $msgfragoffs,
         $data,
         $records,
         $startoffset,
         $message_frag_lens) = @_;
 
     my $self = $class->SUPER::new(
+        $isdtls,
         $server,
         TLSProxy::Message::MT_CERTIFICATE_VERIFY,
+        $msgseq,
+        $msgfrag,
+        $msgfragoffs,
         $data,
         $records,
         $startoffset,
index c49bc23671ffd00fe972f12d433556ee7d86f263..5a5f5fd34da60586265a82b3e2dbcecd62ecbce9 100644 (file)
@@ -9,30 +9,43 @@ use strict;
 
 package TLSProxy::ClientHello;
 
+use TLSProxy::Record;
+
 use vars '@ISA';
 push @ISA, 'TLSProxy::Message';
 
 sub new
 {
     my $class = shift;
-    my ($server,
+    my ($isdtls,
+        $server,
+        $msgseq,
+        $msgfrag,
+        $msgfragoffs,
         $data,
         $records,
         $startoffset,
         $message_frag_lens) = @_;
 
     my $self = $class->SUPER::new(
+        $isdtls,
         $server,
-        1,
+        TLSProxy::Message::MT_CLIENT_HELLO,
+        $msgseq,
+        $msgfrag,
+        $msgfragoffs,
         $data,
         $records,
         $startoffset,
         $message_frag_lens);
 
+    $self->{isdtls} = $isdtls;
     $self->{client_version} = 0;
     $self->{random} = [];
     $self->{session_id_len} = 0;
     $self->{session} = "";
+    $self->{legacy_cookie_len} = 0; #DTLS only
+    $self->{legacy_cookie} = ""; #DTLS only
     $self->{ciphersuite_len} = 0;
     $self->{ciphersuites} = [];
     $self->{comp_meth_len} = 0;
@@ -54,6 +67,14 @@ sub parse
     $ptr++;
     my $session = substr($self->data, $ptr, $session_id_len);
     $ptr += $session_id_len;
+    my $legacy_cookie_len = 0;
+    my $legacy_cookie = "";
+    if($self->{isdtls}) {
+        $legacy_cookie_len = unpack('C', substr($self->data, $ptr));
+        $ptr++;
+        $legacy_cookie = substr($self->data, $ptr, $legacy_cookie_len);
+        $ptr += $legacy_cookie_len;
+    }
     my $ciphersuite_len = unpack('n', substr($self->data, $ptr));
     $ptr += 2;
     my @ciphersuites = unpack('n*', substr($self->data, $ptr,
@@ -84,6 +105,8 @@ sub parse
     $self->random($random);
     $self->session_id_len($session_id_len);
     $self->session($session);
+    $self->legacy_cookie_len($legacy_cookie_len);
+    $self->legacy_cookie($legacy_cookie);
     $self->ciphersuite_len($ciphersuite_len);
     $self->ciphersuites(\@ciphersuites);
     $self->comp_meth_len($comp_meth_len);
@@ -93,8 +116,11 @@ sub parse
 
     $self->process_extensions();
 
-    print "    Client Version:".$client_version."\n";
+    print "    Client Version:".$TLSProxy::Record::tls_version{$client_version}."\n";
     print "    Session ID Len:".$session_id_len."\n";
+    if($self->{isdtls}) {
+        print "    Legacy Cookie Len:".$legacy_cookie_len."\n";
+    }
     print "    Ciphersuite len:".$ciphersuite_len."\n";
     print "    Compression Method Len:".$comp_meth_len."\n";
     print "    Extensions Len:".$extensions_len."\n";
@@ -138,6 +164,12 @@ sub set_message_contents
     $data .= $self->random;
     $data .= pack('C', $self->session_id_len);
     $data .= $self->session;
+    if($self->{isdtls}){
+        $data .= pack('C', $self->legacy_cookie_len);
+        if($self->legacy_cookie_len > 0) {
+            $data .= $self->legacy_cookie;
+        }
+    }
     $data .= pack('n', $self->ciphersuite_len);
     $data .= pack("n*", @{$self->ciphersuites});
     $data .= pack('C', $self->comp_meth_len);
@@ -197,6 +229,22 @@ sub session
     }
     return $self->{session};
 }
+sub legacy_cookie_len
+{
+    my $self = shift;
+    if (@_) {
+        $self->{legacy_cookie_len} = shift;
+    }
+    return $self->{legacy_cookie_len};
+}
+sub legacy_cookie
+{
+    my $self = shift;
+    if (@_) {
+        $self->{legacy_cookie} = shift;
+    }
+    return $self->{legacy_cookie};
+}
 sub ciphersuite_len
 {
     my $self = shift;
index 4fd445b41e08cc5e00b03be20df5403fa13bd015..5f867101d9f495ccba6794f2f72ef2a41817e858 100644 (file)
@@ -15,15 +15,23 @@ push @ISA, 'TLSProxy::Message';
 sub new
 {
     my $class = shift;
-    my ($server,
+    my ($isdtls,
+        $server,
+        $msgseq,
+        $msgfrag,
+        $msgfragoffs,
         $data,
         $records,
         $startoffset,
         $message_frag_lens) = @_;
 
     my $self = $class->SUPER::new(
+        $isdtls,
         $server,
         TLSProxy::Message::MT_ENCRYPTED_EXTENSIONS,
+        $msgseq,
+        $msgfrag,
+        $msgfragoffs,
         $data,
         $records,
         $startoffset,
diff --git a/util/perl/TLSProxy/HelloVerifyRequest.pm b/util/perl/TLSProxy/HelloVerifyRequest.pm
new file mode 100644 (file)
index 0000000..40162d0
--- /dev/null
@@ -0,0 +1,115 @@
+# Copyright 2016-2018 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the Apache License 2.0 (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+use strict;
+
+package TLSProxy::HelloVerifyRequest;
+
+use TLSProxy::Record;
+
+use vars '@ISA';
+push @ISA, 'TLSProxy::Message';
+
+
+sub new
+{
+    my $class = shift;
+    my ($isdtls,
+        $server,
+        $msgseq,
+        $msgfrag,
+        $msgfragoffs,
+        $data,
+        $records,
+        $startoffset,
+        $message_frag_lens) = @_;
+
+    my $self = $class->SUPER::new(
+        $isdtls,
+        $server,
+        TLSProxy::Message::MT_HELLO_VERIFY_REQUEST,
+        $msgseq,
+        $msgfrag,
+        $msgfragoffs,
+        $data,
+        $records,
+        $startoffset,
+        $message_frag_lens);
+
+    $self->{server_version} = 0;
+    $self->{cookie_len} = 0;
+    $self->{cookie} = "";
+
+    return $self;
+}
+
+sub parse
+{
+    my $self = shift;
+
+    my ($server_version) = unpack('n', $self->data);
+    my $ptr = 2;
+    my $cookie_len = unpack('C', substr($self->data, $ptr));
+    $ptr++;
+    my $cookie = substr($self->data, $ptr, $cookie_len);
+
+    $self->server_version($server_version);
+    $self->cookie_len($cookie_len);
+    $self->cookie($cookie);
+
+    $self->process_data();
+
+    print "    Server Version:".$TLSProxy::Record::tls_version{$server_version}."\n";
+    print "    Cookie Len:".$cookie_len."\n";
+}
+
+#Perform any actions necessary based on the data we've seen
+sub process_data
+{
+    my $self = shift;
+    #Intentional no-op
+}
+
+#Reconstruct the on-the-wire message data following changes
+sub set_message_contents
+{
+    my $self = shift;
+    my $data;
+
+    $data = pack('n', $self->server_version);
+    $data .= pack('C', $self->cookie_len);
+    $data .= $self->cookie;
+
+    $self->data($data);
+}
+
+#Read/write accessors
+sub server_version
+{
+    my $self = shift;
+    if (@_) {
+      $self->{server_version} = shift;
+    }
+    return $self->{server_version};
+}
+sub cookie_len
+{
+    my $self = shift;
+    if (@_) {
+      $self->{cookie_len} = shift;
+    }
+    return $self->{cookie_len};
+}
+sub cookie
+{
+    my $self = shift;
+    if (@_) {
+      $self->{cookie} = shift;
+    }
+    return $self->{cookie};
+}
+1;
index ce221875697f1f257aa52d4ecc476cee005c5277..c5e822e90d3d8d31f599c97cd52d98f56039a7f1 100644 (file)
@@ -11,6 +11,7 @@ package TLSProxy::Message;
 
 use TLSProxy::Alert;
 
+use constant DTLS_MESSAGE_HEADER_LENGTH => 12;
 use constant TLS_MESSAGE_HEADER_LENGTH => 4;
 
 #Message types
@@ -18,6 +19,7 @@ use constant {
     MT_HELLO_REQUEST => 0,
     MT_CLIENT_HELLO => 1,
     MT_SERVER_HELLO => 2,
+    MT_HELLO_VERIFY_REQUEST => 3,
     MT_NEW_SESSION_TICKET => 4,
     MT_ENCRYPTED_EXTENSIONS => 8,
     MT_CERTIFICATE => 11,
@@ -42,7 +44,9 @@ use constant {
 use constant {
     AL_DESC_CLOSE_NOTIFY => 0,
     AL_DESC_UNEXPECTED_MESSAGE => 10,
+    AL_DESC_BAD_RECORD_MAC => 20,
     AL_DESC_ILLEGAL_PARAMETER => 47,
+    AL_DESC_PROTOCOL_VERSION => 70,
     AL_DESC_NO_RENEGOTIATION => 100
 };
 
@@ -50,6 +54,7 @@ my %message_type = (
     MT_HELLO_REQUEST, "HelloRequest",
     MT_CLIENT_HELLO, "ClientHello",
     MT_SERVER_HELLO, "ServerHello",
+    MT_HELLO_VERIFY_REQUEST, "HelloVerifyRequest",
     MT_NEW_SESSION_TICKET, "NewSessionTicket",
     MT_ENCRYPTED_EXTENSIONS, "EncryptedExtensions",
     MT_CERTIFICATE, "Certificate",
@@ -172,6 +177,7 @@ sub get_messages
     my $class = shift;
     my $serverin = shift;
     my $record = shift;
+    my $isdtls = shift;
     my @messages = ();
     my $message;
 
@@ -216,8 +222,14 @@ sub get_messages
                     $recoffset = $messlen - length($payload);
                     $payload .= substr($record->decrypt_data, 0, $recoffset);
                     push @message_frag_lens, $recoffset;
-                    $message = create_message($server, $mt, $payload,
-                                              $startoffset);
+                    if ($isdtls) {
+                        # We must set $msgseq, $msgfrag, $msgfragoffs
+                        die "Internal error: cannot handle partial dtls messages\n"
+                    }
+                    $message = create_message($server, $mt,
+                        #$msgseq, $msgfrag, $msgfragoffs,
+                        0, 0, 0,
+                        $payload, $startoffset, $isdtls);
                     push @messages, $message;
 
                     $payload = "";
@@ -232,21 +244,36 @@ sub get_messages
 
             while ($record->decrypt_len > $recoffset) {
                 #We are at the start of a new message
-                if ($record->decrypt_len - $recoffset < 4) {
+                my $msgheaderlen = $isdtls ? DTLS_MESSAGE_HEADER_LENGTH
+                                           : TLS_MESSAGE_HEADER_LENGTH;
+                if ($record->decrypt_len - $recoffset < $msgheaderlen) {
                     #Whilst technically probably valid we can't cope with this
                     die "End of record in the middle of a message header\n";
                 }
                 @message_rec_list = ($record);
                 my $lenhi;
                 my $lenlo;
-                ($mt, $lenhi, $lenlo) = unpack('CnC',
-                                               substr($record->decrypt_data,
-                                                      $recoffset));
+                my $msgseq;
+                my $msgfrag;
+                my $msgfragoffs;
+                if ($isdtls) {
+                    my $msgfraghi;
+                    my $msgfraglo;
+                    my $msgfragoffshi;
+                    my $msgfragoffslo;
+                    ($mt, $lenhi, $lenlo, $msgseq, $msgfraghi, $msgfraglo, $msgfragoffshi, $msgfragoffslo) =
+                        unpack('CnCnnCnC', substr($record->decrypt_data, $recoffset));
+                    $msgfrag = ($msgfraghi << 8) | $msgfraglo;
+                    $msgfragoffs = ($msgfragoffshi << 8) | $msgfragoffslo;
+                } else {
+                    ($mt, $lenhi, $lenlo) =
+                        unpack('CnC', substr($record->decrypt_data, $recoffset));
+                }
                 $messlen = ($lenhi << 8) | $lenlo;
-                print "  Message type: $message_type{$mt}\n";
+                print "  Message type: $message_type{$mt}($mt)\n";
                 print "  Message Length: $messlen\n";
                 $startoffset = $recoffset;
-                $recoffset += 4;
+                $recoffset += $msgheaderlen;
                 $payload = "";
 
                 if ($recoffset <= $record->decrypt_len) {
@@ -257,8 +284,9 @@ sub get_messages
                                            $messlen);
                         $recoffset += $messlen;
                         push @message_frag_lens, $messlen;
-                        $message = create_message($server, $mt, $payload,
-                                                  $startoffset);
+                        $message = create_message($server, $mt, $msgseq,
+                                                  $msgfrag, $msgfragoffs,
+                                                  $payload, $startoffset, $isdtls);
                         push @messages, $message;
 
                         $payload = "";
@@ -307,14 +335,18 @@ sub get_messages
 #construct it
 sub create_message
 {
-    my ($server, $mt, $data, $startoffset) = @_;
+    my ($server, $mt, $msgseq, $msgfrag, $msgfragoffs, $data, $startoffset, $isdtls) = @_;
     my $message;
 
     #We only support ClientHello in this version...needs to be extended for
     #others
     if ($mt == MT_CLIENT_HELLO) {
         $message = TLSProxy::ClientHello->new(
+            $isdtls,
             $server,
+            $msgseq,
+            $msgfrag,
+            $msgfragoffs,
             $data,
             [@message_rec_list],
             $startoffset,
@@ -323,7 +355,24 @@ sub create_message
         $message->parse();
     } elsif ($mt == MT_SERVER_HELLO) {
         $message = TLSProxy::ServerHello->new(
+            $isdtls,
+            $server,
+            $msgseq,
+            $msgfrag,
+            $msgfragoffs,
+            $data,
+            [@message_rec_list],
+            $startoffset,
+            [@message_frag_lens]
+        );
+        $message->parse();
+    } elsif ($mt == MT_HELLO_VERIFY_REQUEST) {
+        $message = TLSProxy::HelloVerifyRequest->new(
+            $isdtls,
             $server,
+            $msgseq,
+            $msgfrag,
+            $msgfragoffs,
             $data,
             [@message_rec_list],
             $startoffset,
@@ -332,7 +381,11 @@ sub create_message
         $message->parse();
     } elsif ($mt == MT_ENCRYPTED_EXTENSIONS) {
         $message = TLSProxy::EncryptedExtensions->new(
+            $isdtls,
             $server,
+            $msgseq,
+            $msgfrag,
+            $msgfragoffs,
             $data,
             [@message_rec_list],
             $startoffset,
@@ -341,7 +394,11 @@ sub create_message
         $message->parse();
     } elsif ($mt == MT_CERTIFICATE) {
         $message = TLSProxy::Certificate->new(
+            $isdtls,
             $server,
+            $msgseq,
+            $msgfrag,
+            $msgfragoffs,
             $data,
             [@message_rec_list],
             $startoffset,
@@ -350,7 +407,11 @@ sub create_message
         $message->parse();
     } elsif ($mt == MT_CERTIFICATE_REQUEST) {
         $message = TLSProxy::CertificateRequest->new(
+            $isdtls,
             $server,
+            $msgseq,
+            $msgfrag,
+            $msgfragoffs,
             $data,
             [@message_rec_list],
             $startoffset,
@@ -359,7 +420,11 @@ sub create_message
         $message->parse();
     } elsif ($mt == MT_CERTIFICATE_VERIFY) {
         $message = TLSProxy::CertificateVerify->new(
+            $isdtls,
             $server,
+            $msgseq,
+            $msgfrag,
+            $msgfragoffs,
             $data,
             [@message_rec_list],
             $startoffset,
@@ -368,7 +433,11 @@ sub create_message
         $message->parse();
     } elsif ($mt == MT_SERVER_KEY_EXCHANGE) {
         $message = TLSProxy::ServerKeyExchange->new(
+            $isdtls,
             $server,
+            $msgseq,
+            $msgfrag,
+            $msgfragoffs,
             $data,
             [@message_rec_list],
             $startoffset,
@@ -376,19 +445,36 @@ sub create_message
         );
         $message->parse();
     } elsif ($mt == MT_NEW_SESSION_TICKET) {
-        $message = TLSProxy::NewSessionTicket->new(
-            $server,
-            $data,
-            [@message_rec_list],
-            $startoffset,
-            [@message_frag_lens]
-        );
+        if ($isdtls) {
+            $message = TLSProxy::NewSessionTicket->new_dtls(
+                $server,
+                $msgseq,
+                $msgfrag,
+                $msgfragoffs,
+                $data,
+                [@message_rec_list],
+                $startoffset,
+                [@message_frag_lens]
+            );
+        } else {
+            $message = TLSProxy::NewSessionTicket->new(
+                $server,
+                $data,
+                [@message_rec_list],
+                $startoffset,
+                [@message_frag_lens]
+            );
+        }
         $message->parse();
     } else {
         #Unknown message type
         $message = TLSProxy::Message->new(
+            $isdtls,
             $server,
             $mt,
+            $msgseq,
+            $msgfrag,
+            $msgfragoffs,
             $data,
             [@message_rec_list],
             $startoffset,
@@ -423,18 +509,26 @@ sub alert
 sub new
 {
     my $class = shift;
-    my ($server,
+    my ($isdtls,
+        $server,
         $mt,
+        $msgseq,
+        $msgfrag,
+        $msgfragoffs,
         $data,
         $records,
         $startoffset,
         $message_frag_lens) = @_;
 
     my $self = {
+        isdtls => $isdtls,
         server => $server,
         data => $data,
         records => $records,
         mt => $mt,
+        msgseq => $msgseq,
+        msgfrag => $msgfrag,
+        msgfragoffs => $msgfragoffs,
         startoffset => $startoffset,
         message_frag_lens => $message_frag_lens,
         dupext => -1
@@ -463,12 +557,21 @@ sub repack
 
     $self->set_message_contents();
 
-    my $lenhi;
-    my $lenlo;
+    my $lenlo = length($self->data) & 0xff;
+    my $lenhi = length($self->data) >> 8;
 
-    $lenlo = length($self->data) & 0xff;
-    $lenhi = length($self->data) >> 8;
-    $msgdata = pack('CnC', $self->mt, $lenhi, $lenlo).$self->data;
+    if ($self->{isdtls}) {
+        my $msgfraghi = $self->msgfrag >> 8;
+        my $msgfraglo = $self->msgfrag & 0xff;
+        my $msgfragoffshi = $self->msgfragoffs >> 8;
+        my $msgfragoffslo = $self->msgfragoffs & 0xff;
+
+        $msgdata = pack('CnCnnCnC', $self->mt, $lenhi, $lenlo, $self->msgseq,
+                                    $msgfraghi, $msgfraglo,
+                                    $msgfragoffshi, $msgfragoffslo).$self->data;
+    } else {
+        $msgdata = pack('CnC', $self->mt, $lenhi, $lenlo).$self->data;
+    }
 
     if ($numrecs == 0) {
         #The message is fully contained within one record
@@ -476,13 +579,14 @@ sub repack
         my $recdata = $rec->decrypt_data;
 
         my $old_length;
+        my $msg_header_len = $self->{isdtls} ? DTLS_MESSAGE_HEADER_LENGTH
+                                             : TLS_MESSAGE_HEADER_LENGTH;
 
         # We use empty message_frag_lens to indicates that pre-repacking,
         # the message wasn't present. The first fragment length doesn't include
         # the TLS header, so we need to check and compute the right length.
         if (@{$self->message_frag_lens}) {
-            $old_length = ${$self->message_frag_lens}[0] +
-              TLS_MESSAGE_HEADER_LENGTH;
+            $old_length = ${$self->message_frag_lens}[0] + $msg_header_len;
         } else {
             $old_length = 0;
         }
@@ -529,8 +633,7 @@ sub repack
         $rec->len(length($rec->data));
 
         #Update the fragment len in case we changed it above
-        ${$self->message_frag_lens}[0] = length($msgdata)
-                                         - TLS_MESSAGE_HEADER_LENGTH;
+        ${$self->message_frag_lens}[0] = length($msgdata) - $msg_header_len;
         return;
     }
 
@@ -578,6 +681,30 @@ sub mt
     }
     return $self->{mt};
 }
+sub msgseq
+{
+    my $self = shift;
+    if (@_) {
+        $self->{msgseq} = shift;
+    }
+    return $self->{msgseq};
+}
+sub msgfrag
+{
+    my $self = shift;
+    if (@_) {
+        $self->{msgfrag} = shift;
+    }
+    return $self->{msgfrag};
+}
+sub msgfragoffs
+{
+    my $self = shift;
+    if (@_) {
+        $self->{msgfragoffs} = shift;
+    }
+    return $self->{msgfragoffs};
+}
 sub data
 {
     my $self = shift;
@@ -613,7 +740,9 @@ sub message_frag_lens
 sub encoded_length
 {
     my $self = shift;
-    return TLS_MESSAGE_HEADER_LENGTH + length($self->data);
+    my $msg_header_len = $self->{isdtls} ? DTLS_MESSAGE_HEADER_LENGTH
+                                         : TLS_MESSAGE_HEADER_LENGTH;
+    return $msg_header_len + length($self->data);
 }
 sub dupext
 {
index 1c532ff7beef55eea206ea7772842f71a51d79aa..748efb8aa8f564eaa3804471b8b72a712c41e19f 100644 (file)
@@ -12,18 +12,74 @@ package TLSProxy::NewSessionTicket;
 use vars '@ISA';
 push @ISA, 'TLSProxy::Message';
 
+sub new_dtls
+{
+    my $class = shift;
+
+    my ($server,
+        $msgseq,
+        $msgfrag,
+        $msgfragoffs,
+        $data,
+        $records,
+        $startoffset,
+        $message_frag_lens) = @_;
+
+    return $class->init(
+        1,
+        $server,
+        $msgseq,
+        $msgfrag,
+        $msgfragoffs,
+        $data,
+        $records,
+        $startoffset,
+        $message_frag_lens
+    )
+}
+
 sub new
 {
     my $class = shift;
+
     my ($server,
         $data,
         $records,
         $startoffset,
         $message_frag_lens) = @_;
 
+    return $class->init(
+        0,
+        $server,
+        0, # msgseq
+        0, # msgfrag
+        0, # $msgfragoffs
+        $data,
+        $records,
+        $startoffset,
+        $message_frag_lens
+    )
+}
+
+sub init{
+    my $class = shift;
+    my ($isdtls,
+        $server,
+        $msgseq,
+        $msgfrag,
+        $msgfragoffs,
+        $data,
+        $records,
+        $startoffset,
+        $message_frag_lens) = @_;
+
     my $self = $class->SUPER::new(
+        $isdtls,
         $server,
         TLSProxy::Message::MT_NEW_SESSION_TICKET,
+        $msgseq,
+        $msgfrag,
+        $msgfragoffs,
         $data,
         $records,
         $startoffset,
index 3de10eccb94eff1f8f9f6fb336efe9b5365c36dc..0084328a5f17a6b338e7cd70833688a5e2d7beec 100644 (file)
@@ -17,6 +17,7 @@ use TLSProxy::Record;
 use TLSProxy::Message;
 use TLSProxy::ClientHello;
 use TLSProxy::ServerHello;
+use TLSProxy::HelloVerifyRequest;
 use TLSProxy::EncryptedExtensions;
 use TLSProxy::Certificate;
 use TLSProxy::CertificateRequest;
@@ -71,17 +72,37 @@ BEGIN
 my $is_tls13 = 0;
 my $ciphersuite = undef;
 
-sub new
-{
+sub new {
+    my $class = shift;
+    my ($filter,
+        $execute,
+        $cert,
+        $debug) = @_;
+    return init($class, $filter, $execute, $cert, $debug, 0);
+}
+
+sub new_dtls {
     my $class = shift;
     my ($filter,
         $execute,
         $cert,
         $debug) = @_;
+    return init($class, $filter, $execute, $cert, $debug, 1);
+}
+
+sub init
+{
+    my $class = shift;
+    my ($filter,
+        $execute,
+        $cert,
+        $debug,
+        $isdtls) = @_;
 
     my $self = {
         #Public read/write
         proxy_addr => $have_IPv6 ? "[::1]" : "127.0.0.1",
+        client_addr => $have_IPv6 ? "[::1]" : "127.0.0.1",
         filter => $filter,
         serverflags => "",
         clientflags => "",
@@ -90,7 +111,9 @@ sub new
         sessionfile => undef,
 
         #Public read
+        isdtls => $isdtls,
         proxy_port => 0,
+        client_port => 49152 + int(rand(65535 - 49152)),
         server_port => 0,
         serverpid => 0,
         clientpid => 0,
@@ -108,29 +131,6 @@ sub new
         message_list => [],
     };
 
-    # Create the Proxy socket
-    my $proxaddr = $self->{proxy_addr};
-    $proxaddr =~ s/[\[\]]//g; # Remove [ and ]
-    my @proxyargs = (
-        LocalHost   => $proxaddr,
-        LocalPort   => 0,
-        Proto       => "tcp",
-        Listen      => SOMAXCONN,
-       );
-
-    if (my $sock = $IP_factory->(@proxyargs)) {
-        $self->{proxy_sock} = $sock;
-        $self->{proxy_port} = $sock->sockport();
-        $self->{proxy_addr} = $sock->sockhost();
-        $self->{proxy_addr} =~ s/(.*:.*)/[$1]/;
-        print "Proxy started on port ",
-              "$self->{proxy_addr}:$self->{proxy_port}\n";
-        # use same address for s_server
-        $self->{server_addr} = $self->{proxy_addr};
-    } else {
-        warn "Failed creating proxy socket (".$proxaddr.",0): $!\n";
-    }
-
     return bless $self, $class;
 }
 
@@ -200,7 +200,7 @@ sub connect_to_server
 
     my $sock = $IP_factory->(PeerAddr => $servaddr,
                              PeerPort => $self->{server_port},
-                             Proto => 'tcp');
+                             Proto => $self->{isdtls} ? 'udp' : 'tcp');
     if (!defined($sock)) {
         my $err = $!;
         kill(3, $self->{real_serverpid});
@@ -215,12 +215,51 @@ sub start
     my ($self) = shift;
     my $pid;
 
+
+    # Create the Proxy socket
+    my $proxaddr = $self->{proxy_addr};
+    $proxaddr =~ s/[\[\]]//g; # Remove [ and ]
+    my $clientaddr = $self->{client_addr};
+    $clientaddr =~ s/[\[\]]//g; # Remove [ and ]
+
+    my @proxyargs;
+
+    if ($self->{isdtls}) {
+        @proxyargs = (
+            LocalHost   => $proxaddr,
+            LocalPort   => 0,
+            PeerHost   => $clientaddr,
+            PeerPort   => $self->{client_port},
+            Proto       => "udp",
+        );
+    } else {
+        @proxyargs = (
+            LocalHost   => $proxaddr,
+            LocalPort   => 0,
+            Proto       => "tcp",
+            Listen      => SOMAXCONN,
+        );
+    }
+
+    if (my $sock = $IP_factory->(@proxyargs)) {
+        $self->{proxy_sock} = $sock;
+        $self->{proxy_port} = $sock->sockport();
+        $self->{proxy_addr} = $sock->sockhost();
+        $self->{proxy_addr} =~ s/(.*:.*)/[$1]/;
+        print "Proxy started on port ",
+            "$self->{proxy_addr}:$self->{proxy_port}\n";
+        # use same address for s_server
+        $self->{server_addr} = $self->{proxy_addr};
+    } else {
+        warn "Failed creating proxy socket (".$proxaddr.",0): $!\n";
+    }
+
     if ($self->{proxy_sock} == 0) {
         return 0;
     }
 
     my $execcmd = $self->execute
-        ." s_server -max_protocol TLSv1.3 -no_comp -rev -engine ossltest"
+        ." s_server -no_comp -engine ossltest -state"
         #In TLSv1.3 we issue two session tickets. The default session id
         #callback gets confused because the ossltest engine causes the same
         #session id to be created twice due to the changed random number
@@ -230,6 +269,14 @@ sub start
         ." -accept $self->{server_addr}:0"
         ." -cert ".$self->cert." -cert2 ".$self->cert
         ." -naccept ".$self->serverconnects;
+    if ($self->{isdtls}) {
+        $execcmd .= " -dtls -max_protocol DTLSv1.2"
+                    # TLSProxy does not support message fragmentation. So
+                    # set a high mtu and fingers crossed.
+                    ." -mtu 1500";
+    } else {
+        $execcmd .= " -rev -max_protocol TLSv1.3";
+    }
     if ($self->ciphers ne "") {
         $execcmd .= " -cipher ".$self->ciphers;
     }
@@ -311,11 +358,24 @@ sub clientstart
 {
     my ($self) = shift;
 
+    my $succes = 1;
+
     if ($self->execute) {
         my $pid;
         my $execcmd = $self->execute
-             ." s_client -max_protocol TLSv1.3 -engine ossltest"
+             ." s_client -engine ossltest"
              ." -connect $self->{proxy_addr}:$self->{proxy_port}";
+        if ($self->{isdtls}) {
+            $execcmd .= " -dtls -max_protocol DTLSv1.2"
+                        # TLSProxy does not support message fragmentation. So
+                        # set a high mtu and fingers crossed.
+                        ." -mtu 1500"
+                        # UDP has no "accept" for sockets which means we need to
+                        # know were to send data back to.
+                        ." -bind $self->{client_addr}:$self->{client_port}";
+        } else {
+            $execcmd .= " -max_protocol TLSv1.3";
+        }
         if ($self->cipherc ne "") {
             $execcmd .= " -cipher ".$self->cipherc;
         }
@@ -362,7 +422,9 @@ sub clientstart
     }
 
     my $client_sock;
-    if(!($client_sock = $self->{proxy_sock}->accept())) {
+    if($self->{isdtls}) {
+        $client_sock = $self->{proxy_sock}
+    } elsif (!($client_sock = $self->{proxy_sock}->accept())) {
         warn "Failed accepting incoming connection: $!\n";
         return 0;
     }
@@ -386,6 +448,9 @@ sub clientstart
                     && $self->{saw_session_ticket};
         }
         if (!(@ready = $fdset->can_read(1))) {
+            last if TLSProxy::Message->success()
+                && $self->{saw_session_ticket};
+
             $ctr++;
             next;
         }
@@ -419,7 +484,8 @@ sub clientstart
 
     if ($ctr >= 10) {
         kill(3, $self->{real_serverpid});
-        die "No progress made";
+        print "No progress made\n";
+        $succes = 0;
     }
 
     END:
@@ -460,7 +526,7 @@ sub clientstart
     print "Waiting for s_client process to close: $pid...\n";
     waitpid($pid, 0);
 
-    return 1;
+    return $succes;
 }
 
 sub process_packet
@@ -488,7 +554,9 @@ sub process_packet
     #Return contains the list of record found in the packet followed by the
     #list of messages in those records and any partial message
     my @ret = TLSProxy::Record->get_records($server, $self->flight,
-                                            $self->{partial}[$server].$packet);
+                                            $self->{partial}[$server].$packet,
+                                            $self->{isdtls});
+
     $self->{partial}[$server] = $ret[2];
     push @{$self->{record_list}}, @{$ret[0]};
     push @{$self->{message_list}}, @{$ret[1]};
index 183aa0569798d4cccde1be8e90c980082bd1b86a..c309bc2f9fb6da8563a767202eff9b37896c8d32 100644 (file)
@@ -15,6 +15,7 @@ my $server_encrypting = 0;
 my $client_encrypting = 0;
 my $etm = 0;
 
+use constant DTLS_RECORD_HEADER_LENGTH => 13;
 use constant TLS_RECORD_HEADER_LENGTH => 5;
 
 #Record types
@@ -35,6 +36,8 @@ my %record_type = (
 );
 
 use constant {
+    VERS_DTLS_1_2 => 0xfefd,
+    VERS_DTLS_1 => 0xfeff,
     VERS_TLS_1_4 => 0x0305,
     VERS_TLS_1_3 => 0x0304,
     VERS_TLS_1_2 => 0x0303,
@@ -44,7 +47,9 @@ use constant {
     VERS_SSL_LT_3_0 => 0x02ff
 };
 
-my %tls_version = (
+our %tls_version = (
+    VERS_DTLS_1_2, "DTLS1.2",
+    VERS_DTLS_1, "DTLS1",
     VERS_TLS_1_3, "TLS1.3",
     VERS_TLS_1_2, "TLS1.2",
     VERS_TLS_1_1, "TLS1.1",
@@ -60,41 +65,81 @@ sub get_records
     my $server = shift;
     my $flight = shift;
     my $packet = shift;
+    my $isdtls = shift;
     my $partial = "";
     my @record_list = ();
     my @message_list = ();
+    my $record_hdr_len = $isdtls ? DTLS_RECORD_HEADER_LENGTH
+                                 : TLS_RECORD_HEADER_LENGTH;
 
     my $recnum = 1;
     while (length ($packet) > 0) {
         print " Record $recnum ", $server ? "(server -> client)\n"
                                           : "(client -> server)\n";
 
-        #Get the record header (unpack can't fail if $packet is too short)
-        my ($content_type, $version, $len) = unpack('Cnn', $packet);
+        my $content_type;
+        my $version;
+        my $len;
+        my $epoch;
+        my $seq;
+
+        if ($isdtls) {
+            my $seqhi;
+            my $seqmi;
+            my $seqlo;
+            #Get the record header (unpack can't fail if $packet is too short)
+            ($content_type, $version, $epoch,
+                $seqhi, $seqmi, $seqlo, $len) = unpack('Cnnnnnn', $packet);
+            $seq = ($seqhi << 32) | ($seqmi << 16) | $seqlo
+        } else {
+            #Get the record header (unpack can't fail if $packet is too short)
+            ($content_type, $version, $len) = unpack('Cnn', $packet);
+        }
 
-        if (length($packet) < TLS_RECORD_HEADER_LENGTH + ($len // 0)) {
+        if (length($packet) < $record_hdr_len + ($len // 0)) {
             print "Partial data : ".length($packet)." bytes\n";
             $partial = $packet;
             last;
         }
 
-        my $data = substr($packet, TLS_RECORD_HEADER_LENGTH, $len);
+        my $data = substr($packet, $record_hdr_len, $len);
 
         print "  Content type: ".$record_type{$content_type}."\n";
         print "  Version: $tls_version{$version}\n";
+        if($isdtls) {
+            print "  Epoch: $epoch\n";
+            print "  Sequence: $seq\n";
+        }
         print "  Length: $len\n";
 
-        my $record = TLSProxy::Record->new(
-            $flight,
-            $content_type,
-            $version,
-            $len,
-            0,
-            $len,       # len_real
-            $len,       # decrypt_len
-            $data,      # data
-            $data       # decrypt_data
-        );
+        my $record;
+        if ($isdtls) {
+            $record = TLSProxy::Record->new_dtls(
+                $flight,
+                $content_type,
+                $version,
+                $epoch,
+                $seq,
+                $len,
+                0,
+                $len,       # len_real
+                $len,       # decrypt_len
+                $data,      # data
+                $data       # decrypt_data
+            );
+        } else {
+            $record = TLSProxy::Record->new(
+                $flight,
+                $content_type,
+                $version,
+                $len,
+                0,
+                $len,  # len_real
+                $len,  # decrypt_len
+                $data, # data
+                $data  # decrypt_data
+            );
+        }
 
         if ($content_type != RT_CCS
                 && (!TLSProxy::Proxy->is_tls13()
@@ -118,10 +163,10 @@ sub get_records
         push @record_list, $record;
 
         #Now figure out what messages are contained within this record
-        my @messages = TLSProxy::Message->get_messages($server, $record);
+        my @messages = TLSProxy::Message->get_messages($server, $record, $isdtls);
         push @message_list, @messages;
 
-        $packet = substr($packet, TLS_RECORD_HEADER_LENGTH + $len);
+        $packet = substr($packet, $record_hdr_len + $len);
         $recnum++;
     }
 
@@ -161,6 +206,34 @@ sub etm
     return $etm;
 }
 
+sub new_dtls
+{
+    my $class = shift;
+    my ($flight,
+        $content_type,
+        $version,
+        $epoch,
+        $seq,
+        $len,
+        $sslv2,
+        $len_real,
+        $decrypt_len,
+        $data,
+        $decrypt_data) = @_;
+    return $class->init(1,
+        $flight,
+        $content_type,
+        $version,
+        $epoch,
+        $seq,
+        $len,
+        $sslv2,
+        $len_real,
+        $decrypt_len,
+        $data,
+        $decrypt_data);
+}
+
 sub new
 {
     my $class = shift;
@@ -173,11 +246,44 @@ sub new
         $decrypt_len,
         $data,
         $decrypt_data) = @_;
+    return $class->init(
+        0,
+        $flight,
+        $content_type,
+        $version,
+        0, #epoch
+        0, #seq
+        $len,
+        $sslv2,
+        $len_real,
+        $decrypt_len,
+        $data,
+        $decrypt_data);
+}
+
+sub init
+{
+    my $class = shift;
+    my ($isdtls,
+        $flight,
+        $content_type,
+        $version,
+        $epoch,
+        $seq,
+        $len,
+        $sslv2,
+        $len_real,
+        $decrypt_len,
+        $data,
+        $decrypt_data) = @_;
 
     my $self = {
+        isdtls => $isdtls,
         flight => $flight,
         content_type => $content_type,
         version => $version,
+        epoch => $epoch,
+        seq => $seq,
         len => $len,
         sslv2 => $sslv2,
         len_real => $len_real,
@@ -285,12 +391,21 @@ sub reconstruct_record
     if ($self->sslv2) {
         $data = pack('n', $self->len | 0x8000);
     } else {
-        if (TLSProxy::Proxy->is_tls13() && $self->encrypted) {
-            $data = pack('Cnn', $self->outer_content_type, $self->version,
-                         $self->len);
+        if($self->{isdtls}) {
+            my $seqhi = ($self->seq >> 32) & 0xffff;
+            my $seqmi = ($self->seq >> 16) & 0xffff;
+            my $seqlo = ($self->seq >> 0) & 0xffff;
+            $data = pack('Cnnnnnn', $self->content_type, $self->version,
+                         $self->epoch, $seqhi, $seqmi, $seqlo, $self->len);
         } else {
-            $data = pack('Cnn', $self->content_type, $self->version,
-                         $self->len);
+            if (TLSProxy::Proxy->is_tls13() && $self->encrypted) {
+                $data = pack('Cnn', $self->outer_content_type, $self->version,
+                             $self->len);
+            }
+            else {
+                $data = pack('Cnn', $self->content_type, $self->version,
+                             $self->len);
+            }
         }
 
     }
@@ -370,6 +485,22 @@ sub content_type
     }
     return $self->{content_type};
 }
+sub epoch
+{
+    my $self = shift;
+    if (@_) {
+        $self->{epoch} = shift;
+    }
+    return $self->{epoch};
+}
+sub seq
+{
+    my $self = shift;
+    if (@_) {
+        $self->{seq} = shift;
+    }
+    return $self->{seq};
+}
 sub encrypted
 {
     my $self = shift;
@@ -391,10 +522,9 @@ sub is_fatal_alert
     my $self = shift;
     my $server = shift;
 
-    if (($self->{flight} & 1) == $server
-        && $self->{content_type} == TLSProxy::Record::RT_ALERT) {
-        my ($level, $alert) = unpack('CC', $self->decrypt_data);
-        return $alert if ($level == 2);
+    if (($self->{flight} & 1) == $server && $self->{content_type} == RT_ALERT) {
+        my ($level, $description) = unpack('CC', $self->decrypt_data);
+        return $description if ($level == 2);
     }
     return 0;
 }
index ab7f2c8f8ab0e6750cbc96b324929bc572b53706..ca1486e0411deed7f396a11dcddc11243aebbb45 100644 (file)
@@ -9,6 +9,8 @@ use strict;
 
 package TLSProxy::ServerHello;
 
+use TLSProxy::Record;
+
 use vars '@ISA';
 push @ISA, 'TLSProxy::Message';
 
@@ -20,15 +22,23 @@ my $hrrrandom = pack("C*", 0xCF, 0x21, 0xAD, 0x74, 0xE5, 0x9A, 0x61, 0x11, 0xBE,
 sub new
 {
     my $class = shift;
-    my ($server,
+    my ($isdtls,
+        $server,
+        $msgseq,
+        $msgfrag,
+        $msgfragoffs,
         $data,
         $records,
         $startoffset,
         $message_frag_lens) = @_;
 
     my $self = $class->SUPER::new(
+        $isdtls,
         $server,
         TLSProxy::Message::MT_SERVER_HELLO,
+        $msgseq,
+        $msgfrag,
+        $msgfragoffs,
         $data,
         $records,
         $startoffset,
@@ -120,7 +130,7 @@ sub parse
     $self->process_data();
 
 
-    print "    Server Version:".$server_version."\n";
+    print "    Server Version:".$TLSProxy::Record::tls_version{$server_version}."\n";
     print "    Session ID Len:".$session_id_len."\n";
     print "    Ciphersuite:".$ciphersuite."\n";
     print "    Compression Method:".$comp_meth."\n";
index e694c15158c2713b22dea0da6b2fb05100d7fbd7..c570d1eb3037a3f668869f878afd1825d3129bde 100644 (file)
@@ -15,15 +15,23 @@ push @ISA, 'TLSProxy::Message';
 sub new
 {
     my $class = shift;
-    my ($server,
+    my ($isdtls,
+        $server,
+        $msgseq,
+        $msgfrag,
+        $msgfragoffs,
         $data,
         $records,
         $startoffset,
         $message_frag_lens) = @_;
 
     my $self = $class->SUPER::new(
+        $isdtls,
         $server,
         TLSProxy::Message::MT_SERVER_KEY_EXCHANGE,
+        $msgseq,
+        $msgfrag,
+        $msgfragoffs,
         $data,
         $records,
         $startoffset,