1*e0c4386eSCy Schubert# Copyright 2016-2018 The OpenSSL Project Authors. All Rights Reserved.
2*e0c4386eSCy Schubert#
3*e0c4386eSCy Schubert# Licensed under the Apache License 2.0 (the "License").  You may not use
4*e0c4386eSCy Schubert# this file except in compliance with the License.  You can obtain a copy
5*e0c4386eSCy Schubert# in the file LICENSE in the source distribution or at
6*e0c4386eSCy Schubert# https://www.openssl.org/source/license.html
7*e0c4386eSCy Schubert
8*e0c4386eSCy Schubertuse strict;
9*e0c4386eSCy Schubert
10*e0c4386eSCy Schubertuse TLSProxy::Proxy;
11*e0c4386eSCy Schubert
12*e0c4386eSCy Schubertpackage TLSProxy::Record;
13*e0c4386eSCy Schubert
14*e0c4386eSCy Schubertmy $server_encrypting = 0;
15*e0c4386eSCy Schubertmy $client_encrypting = 0;
16*e0c4386eSCy Schubertmy $etm = 0;
17*e0c4386eSCy Schubert
18*e0c4386eSCy Schubertuse constant TLS_RECORD_HEADER_LENGTH => 5;
19*e0c4386eSCy Schubert
20*e0c4386eSCy Schubert#Record types
21*e0c4386eSCy Schubertuse constant {
22*e0c4386eSCy Schubert    RT_APPLICATION_DATA => 23,
23*e0c4386eSCy Schubert    RT_HANDSHAKE => 22,
24*e0c4386eSCy Schubert    RT_ALERT => 21,
25*e0c4386eSCy Schubert    RT_CCS => 20,
26*e0c4386eSCy Schubert    RT_UNKNOWN => 100
27*e0c4386eSCy Schubert};
28*e0c4386eSCy Schubert
29*e0c4386eSCy Schubertmy %record_type = (
30*e0c4386eSCy Schubert    RT_APPLICATION_DATA, "APPLICATION DATA",
31*e0c4386eSCy Schubert    RT_HANDSHAKE, "HANDSHAKE",
32*e0c4386eSCy Schubert    RT_ALERT, "ALERT",
33*e0c4386eSCy Schubert    RT_CCS, "CCS",
34*e0c4386eSCy Schubert    RT_UNKNOWN, "UNKNOWN"
35*e0c4386eSCy Schubert);
36*e0c4386eSCy Schubert
37*e0c4386eSCy Schubertuse constant {
38*e0c4386eSCy Schubert    VERS_TLS_1_4 => 0x0305,
39*e0c4386eSCy Schubert    VERS_TLS_1_3 => 0x0304,
40*e0c4386eSCy Schubert    VERS_TLS_1_2 => 0x0303,
41*e0c4386eSCy Schubert    VERS_TLS_1_1 => 0x0302,
42*e0c4386eSCy Schubert    VERS_TLS_1_0 => 0x0301,
43*e0c4386eSCy Schubert    VERS_SSL_3_0 => 0x0300,
44*e0c4386eSCy Schubert    VERS_SSL_LT_3_0 => 0x02ff
45*e0c4386eSCy Schubert};
46*e0c4386eSCy Schubert
47*e0c4386eSCy Schubertmy %tls_version = (
48*e0c4386eSCy Schubert    VERS_TLS_1_3, "TLS1.3",
49*e0c4386eSCy Schubert    VERS_TLS_1_2, "TLS1.2",
50*e0c4386eSCy Schubert    VERS_TLS_1_1, "TLS1.1",
51*e0c4386eSCy Schubert    VERS_TLS_1_0, "TLS1.0",
52*e0c4386eSCy Schubert    VERS_SSL_3_0, "SSL3",
53*e0c4386eSCy Schubert    VERS_SSL_LT_3_0, "SSL<3"
54*e0c4386eSCy Schubert);
55*e0c4386eSCy Schubert
56*e0c4386eSCy Schubert#Class method to extract records from a packet of data
57*e0c4386eSCy Schubertsub get_records
58*e0c4386eSCy Schubert{
59*e0c4386eSCy Schubert    my $class = shift;
60*e0c4386eSCy Schubert    my $server = shift;
61*e0c4386eSCy Schubert    my $flight = shift;
62*e0c4386eSCy Schubert    my $packet = shift;
63*e0c4386eSCy Schubert    my $partial = "";
64*e0c4386eSCy Schubert    my @record_list = ();
65*e0c4386eSCy Schubert    my @message_list = ();
66*e0c4386eSCy Schubert
67*e0c4386eSCy Schubert    my $recnum = 1;
68*e0c4386eSCy Schubert    while (length ($packet) > 0) {
69*e0c4386eSCy Schubert        print " Record $recnum ", $server ? "(server -> client)\n"
70*e0c4386eSCy Schubert                                          : "(client -> server)\n";
71*e0c4386eSCy Schubert
72*e0c4386eSCy Schubert        #Get the record header (unpack can't fail if $packet is too short)
73*e0c4386eSCy Schubert        my ($content_type, $version, $len) = unpack('Cnn', $packet);
74*e0c4386eSCy Schubert
75*e0c4386eSCy Schubert        if (length($packet) < TLS_RECORD_HEADER_LENGTH + ($len // 0)) {
76*e0c4386eSCy Schubert            print "Partial data : ".length($packet)." bytes\n";
77*e0c4386eSCy Schubert            $partial = $packet;
78*e0c4386eSCy Schubert            last;
79*e0c4386eSCy Schubert        }
80*e0c4386eSCy Schubert
81*e0c4386eSCy Schubert        my $data = substr($packet, TLS_RECORD_HEADER_LENGTH, $len);
82*e0c4386eSCy Schubert
83*e0c4386eSCy Schubert        print "  Content type: ".$record_type{$content_type}."\n";
84*e0c4386eSCy Schubert        print "  Version: $tls_version{$version}\n";
85*e0c4386eSCy Schubert        print "  Length: $len\n";
86*e0c4386eSCy Schubert
87*e0c4386eSCy Schubert        my $record = TLSProxy::Record->new(
88*e0c4386eSCy Schubert            $flight,
89*e0c4386eSCy Schubert            $content_type,
90*e0c4386eSCy Schubert            $version,
91*e0c4386eSCy Schubert            $len,
92*e0c4386eSCy Schubert            0,
93*e0c4386eSCy Schubert            $len,       # len_real
94*e0c4386eSCy Schubert            $len,       # decrypt_len
95*e0c4386eSCy Schubert            $data,      # data
96*e0c4386eSCy Schubert            $data       # decrypt_data
97*e0c4386eSCy Schubert        );
98*e0c4386eSCy Schubert
99*e0c4386eSCy Schubert        if ($content_type != RT_CCS
100*e0c4386eSCy Schubert                && (!TLSProxy::Proxy->is_tls13()
101*e0c4386eSCy Schubert                    || $content_type != RT_ALERT)) {
102*e0c4386eSCy Schubert            if (($server && $server_encrypting)
103*e0c4386eSCy Schubert                     || (!$server && $client_encrypting)) {
104*e0c4386eSCy Schubert                if (!TLSProxy::Proxy->is_tls13() && $etm) {
105*e0c4386eSCy Schubert                    $record->decryptETM();
106*e0c4386eSCy Schubert                } else {
107*e0c4386eSCy Schubert                    $record->decrypt();
108*e0c4386eSCy Schubert                }
109*e0c4386eSCy Schubert                $record->encrypted(1);
110*e0c4386eSCy Schubert
111*e0c4386eSCy Schubert                if (TLSProxy::Proxy->is_tls13()) {
112*e0c4386eSCy Schubert                    print "  Inner content type: "
113*e0c4386eSCy Schubert                          .$record_type{$record->content_type()}."\n";
114*e0c4386eSCy Schubert                }
115*e0c4386eSCy Schubert            }
116*e0c4386eSCy Schubert        }
117*e0c4386eSCy Schubert
118*e0c4386eSCy Schubert        push @record_list, $record;
119*e0c4386eSCy Schubert
120*e0c4386eSCy Schubert        #Now figure out what messages are contained within this record
121*e0c4386eSCy Schubert        my @messages = TLSProxy::Message->get_messages($server, $record);
122*e0c4386eSCy Schubert        push @message_list, @messages;
123*e0c4386eSCy Schubert
124*e0c4386eSCy Schubert        $packet = substr($packet, TLS_RECORD_HEADER_LENGTH + $len);
125*e0c4386eSCy Schubert        $recnum++;
126*e0c4386eSCy Schubert    }
127*e0c4386eSCy Schubert
128*e0c4386eSCy Schubert    return (\@record_list, \@message_list, $partial);
129*e0c4386eSCy Schubert}
130*e0c4386eSCy Schubert
131*e0c4386eSCy Schubertsub clear
132*e0c4386eSCy Schubert{
133*e0c4386eSCy Schubert    $server_encrypting = 0;
134*e0c4386eSCy Schubert    $client_encrypting = 0;
135*e0c4386eSCy Schubert}
136*e0c4386eSCy Schubert
137*e0c4386eSCy Schubert#Class level accessors
138*e0c4386eSCy Schubertsub server_encrypting
139*e0c4386eSCy Schubert{
140*e0c4386eSCy Schubert    my $class = shift;
141*e0c4386eSCy Schubert    if (@_) {
142*e0c4386eSCy Schubert      $server_encrypting = shift;
143*e0c4386eSCy Schubert    }
144*e0c4386eSCy Schubert    return $server_encrypting;
145*e0c4386eSCy Schubert}
146*e0c4386eSCy Schubertsub client_encrypting
147*e0c4386eSCy Schubert{
148*e0c4386eSCy Schubert    my $class = shift;
149*e0c4386eSCy Schubert    if (@_) {
150*e0c4386eSCy Schubert      $client_encrypting= shift;
151*e0c4386eSCy Schubert    }
152*e0c4386eSCy Schubert    return $client_encrypting;
153*e0c4386eSCy Schubert}
154*e0c4386eSCy Schubert#Enable/Disable Encrypt-then-MAC
155*e0c4386eSCy Schubertsub etm
156*e0c4386eSCy Schubert{
157*e0c4386eSCy Schubert    my $class = shift;
158*e0c4386eSCy Schubert    if (@_) {
159*e0c4386eSCy Schubert      $etm = shift;
160*e0c4386eSCy Schubert    }
161*e0c4386eSCy Schubert    return $etm;
162*e0c4386eSCy Schubert}
163*e0c4386eSCy Schubert
164*e0c4386eSCy Schubertsub new
165*e0c4386eSCy Schubert{
166*e0c4386eSCy Schubert    my $class = shift;
167*e0c4386eSCy Schubert    my ($flight,
168*e0c4386eSCy Schubert        $content_type,
169*e0c4386eSCy Schubert        $version,
170*e0c4386eSCy Schubert        $len,
171*e0c4386eSCy Schubert        $sslv2,
172*e0c4386eSCy Schubert        $len_real,
173*e0c4386eSCy Schubert        $decrypt_len,
174*e0c4386eSCy Schubert        $data,
175*e0c4386eSCy Schubert        $decrypt_data) = @_;
176*e0c4386eSCy Schubert
177*e0c4386eSCy Schubert    my $self = {
178*e0c4386eSCy Schubert        flight => $flight,
179*e0c4386eSCy Schubert        content_type => $content_type,
180*e0c4386eSCy Schubert        version => $version,
181*e0c4386eSCy Schubert        len => $len,
182*e0c4386eSCy Schubert        sslv2 => $sslv2,
183*e0c4386eSCy Schubert        len_real => $len_real,
184*e0c4386eSCy Schubert        decrypt_len => $decrypt_len,
185*e0c4386eSCy Schubert        data => $data,
186*e0c4386eSCy Schubert        decrypt_data => $decrypt_data,
187*e0c4386eSCy Schubert        orig_decrypt_data => $decrypt_data,
188*e0c4386eSCy Schubert        sent => 0,
189*e0c4386eSCy Schubert        encrypted => 0,
190*e0c4386eSCy Schubert        outer_content_type => RT_APPLICATION_DATA
191*e0c4386eSCy Schubert    };
192*e0c4386eSCy Schubert
193*e0c4386eSCy Schubert    return bless $self, $class;
194*e0c4386eSCy Schubert}
195*e0c4386eSCy Schubert
196*e0c4386eSCy Schubert#Decrypt using encrypt-then-MAC
197*e0c4386eSCy Schubertsub decryptETM
198*e0c4386eSCy Schubert{
199*e0c4386eSCy Schubert    my ($self) = shift;
200*e0c4386eSCy Schubert
201*e0c4386eSCy Schubert    my $data = $self->data;
202*e0c4386eSCy Schubert
203*e0c4386eSCy Schubert    if($self->version >= VERS_TLS_1_1()) {
204*e0c4386eSCy Schubert        #TLS1.1+ has an explicit IV. Throw it away
205*e0c4386eSCy Schubert        $data = substr($data, 16);
206*e0c4386eSCy Schubert    }
207*e0c4386eSCy Schubert
208*e0c4386eSCy Schubert    #Throw away the MAC (assumes MAC is 20 bytes for now. FIXME)
209*e0c4386eSCy Schubert    $data = substr($data, 0, length($data) - 20);
210*e0c4386eSCy Schubert
211*e0c4386eSCy Schubert    #Find out what the padding byte is
212*e0c4386eSCy Schubert    my $padval = unpack("C", substr($data, length($data) - 1));
213*e0c4386eSCy Schubert
214*e0c4386eSCy Schubert    #Throw away the padding
215*e0c4386eSCy Schubert    $data = substr($data, 0, length($data) - ($padval + 1));
216*e0c4386eSCy Schubert
217*e0c4386eSCy Schubert    $self->decrypt_data($data);
218*e0c4386eSCy Schubert    $self->decrypt_len(length($data));
219*e0c4386eSCy Schubert
220*e0c4386eSCy Schubert    return $data;
221*e0c4386eSCy Schubert}
222*e0c4386eSCy Schubert
223*e0c4386eSCy Schubert#Standard decrypt
224*e0c4386eSCy Schubertsub decrypt()
225*e0c4386eSCy Schubert{
226*e0c4386eSCy Schubert    my ($self) = shift;
227*e0c4386eSCy Schubert    my $mactaglen = 20;
228*e0c4386eSCy Schubert    my $data = $self->data;
229*e0c4386eSCy Schubert
230*e0c4386eSCy Schubert    #Throw away any IVs
231*e0c4386eSCy Schubert    if (TLSProxy::Proxy->is_tls13()) {
232*e0c4386eSCy Schubert        #A TLS1.3 client, when processing the server's initial flight, could
233*e0c4386eSCy Schubert        #respond with either an encrypted or an unencrypted alert.
234*e0c4386eSCy Schubert        if ($self->content_type() == RT_ALERT) {
235*e0c4386eSCy Schubert            #TODO(TLS1.3): Eventually it is sufficient just to check the record
236*e0c4386eSCy Schubert            #content type. If an alert is encrypted it will have a record
237*e0c4386eSCy Schubert            #content type of application data. However we haven't done the
238*e0c4386eSCy Schubert            #record layer changes yet, so it's a bit more complicated. For now
239*e0c4386eSCy Schubert            #we will additionally check if the data length is 2 (1 byte for
240*e0c4386eSCy Schubert            #alert level, 1 byte for alert description). If it is, then this is
241*e0c4386eSCy Schubert            #an unencrypted alert, so don't try to decrypt
242*e0c4386eSCy Schubert            return $data if (length($data) == 2);
243*e0c4386eSCy Schubert        }
244*e0c4386eSCy Schubert        $mactaglen = 16;
245*e0c4386eSCy Schubert    } elsif ($self->version >= VERS_TLS_1_1()) {
246*e0c4386eSCy Schubert        #16 bytes for a standard IV
247*e0c4386eSCy Schubert        $data = substr($data, 16);
248*e0c4386eSCy Schubert
249*e0c4386eSCy Schubert        #Find out what the padding byte is
250*e0c4386eSCy Schubert        my $padval = unpack("C", substr($data, length($data) - 1));
251*e0c4386eSCy Schubert
252*e0c4386eSCy Schubert        #Throw away the padding
253*e0c4386eSCy Schubert        $data = substr($data, 0, length($data) - ($padval + 1));
254*e0c4386eSCy Schubert    }
255*e0c4386eSCy Schubert
256*e0c4386eSCy Schubert    #Throw away the MAC or TAG
257*e0c4386eSCy Schubert    $data = substr($data, 0, length($data) - $mactaglen);
258*e0c4386eSCy Schubert
259*e0c4386eSCy Schubert    if (TLSProxy::Proxy->is_tls13()) {
260*e0c4386eSCy Schubert        #Get the content type
261*e0c4386eSCy Schubert        my $content_type = unpack("C", substr($data, length($data) - 1));
262*e0c4386eSCy Schubert        $self->content_type($content_type);
263*e0c4386eSCy Schubert        $data = substr($data, 0, length($data) - 1);
264*e0c4386eSCy Schubert    }
265*e0c4386eSCy Schubert
266*e0c4386eSCy Schubert    $self->decrypt_data($data);
267*e0c4386eSCy Schubert    $self->decrypt_len(length($data));
268*e0c4386eSCy Schubert
269*e0c4386eSCy Schubert    return $data;
270*e0c4386eSCy Schubert}
271*e0c4386eSCy Schubert
272*e0c4386eSCy Schubert#Reconstruct the on-the-wire record representation
273*e0c4386eSCy Schubertsub reconstruct_record
274*e0c4386eSCy Schubert{
275*e0c4386eSCy Schubert    my $self = shift;
276*e0c4386eSCy Schubert    my $server = shift;
277*e0c4386eSCy Schubert    my $data;
278*e0c4386eSCy Schubert
279*e0c4386eSCy Schubert    #We only replay the records in the same direction
280*e0c4386eSCy Schubert    if ($self->{sent} || ($self->flight & 1) != $server) {
281*e0c4386eSCy Schubert        return "";
282*e0c4386eSCy Schubert    }
283*e0c4386eSCy Schubert    $self->{sent} = 1;
284*e0c4386eSCy Schubert
285*e0c4386eSCy Schubert    if ($self->sslv2) {
286*e0c4386eSCy Schubert        $data = pack('n', $self->len | 0x8000);
287*e0c4386eSCy Schubert    } else {
288*e0c4386eSCy Schubert        if (TLSProxy::Proxy->is_tls13() && $self->encrypted) {
289*e0c4386eSCy Schubert            $data = pack('Cnn', $self->outer_content_type, $self->version,
290*e0c4386eSCy Schubert                         $self->len);
291*e0c4386eSCy Schubert        } else {
292*e0c4386eSCy Schubert            $data = pack('Cnn', $self->content_type, $self->version,
293*e0c4386eSCy Schubert                         $self->len);
294*e0c4386eSCy Schubert        }
295*e0c4386eSCy Schubert
296*e0c4386eSCy Schubert    }
297*e0c4386eSCy Schubert    $data .= $self->data;
298*e0c4386eSCy Schubert
299*e0c4386eSCy Schubert    return $data;
300*e0c4386eSCy Schubert}
301*e0c4386eSCy Schubert
302*e0c4386eSCy Schubert#Read only accessors
303*e0c4386eSCy Schubertsub flight
304*e0c4386eSCy Schubert{
305*e0c4386eSCy Schubert    my $self = shift;
306*e0c4386eSCy Schubert    return $self->{flight};
307*e0c4386eSCy Schubert}
308*e0c4386eSCy Schubertsub sslv2
309*e0c4386eSCy Schubert{
310*e0c4386eSCy Schubert    my $self = shift;
311*e0c4386eSCy Schubert    return $self->{sslv2};
312*e0c4386eSCy Schubert}
313*e0c4386eSCy Schubertsub len_real
314*e0c4386eSCy Schubert{
315*e0c4386eSCy Schubert    my $self = shift;
316*e0c4386eSCy Schubert    return $self->{len_real};
317*e0c4386eSCy Schubert}
318*e0c4386eSCy Schubertsub orig_decrypt_data
319*e0c4386eSCy Schubert{
320*e0c4386eSCy Schubert    my $self = shift;
321*e0c4386eSCy Schubert    return $self->{orig_decrypt_data};
322*e0c4386eSCy Schubert}
323*e0c4386eSCy Schubert
324*e0c4386eSCy Schubert#Read/write accessors
325*e0c4386eSCy Schubertsub decrypt_len
326*e0c4386eSCy Schubert{
327*e0c4386eSCy Schubert    my $self = shift;
328*e0c4386eSCy Schubert    if (@_) {
329*e0c4386eSCy Schubert      $self->{decrypt_len} = shift;
330*e0c4386eSCy Schubert    }
331*e0c4386eSCy Schubert    return $self->{decrypt_len};
332*e0c4386eSCy Schubert}
333*e0c4386eSCy Schubertsub data
334*e0c4386eSCy Schubert{
335*e0c4386eSCy Schubert    my $self = shift;
336*e0c4386eSCy Schubert    if (@_) {
337*e0c4386eSCy Schubert      $self->{data} = shift;
338*e0c4386eSCy Schubert    }
339*e0c4386eSCy Schubert    return $self->{data};
340*e0c4386eSCy Schubert}
341*e0c4386eSCy Schubertsub decrypt_data
342*e0c4386eSCy Schubert{
343*e0c4386eSCy Schubert    my $self = shift;
344*e0c4386eSCy Schubert    if (@_) {
345*e0c4386eSCy Schubert      $self->{decrypt_data} = shift;
346*e0c4386eSCy Schubert    }
347*e0c4386eSCy Schubert    return $self->{decrypt_data};
348*e0c4386eSCy Schubert}
349*e0c4386eSCy Schubertsub len
350*e0c4386eSCy Schubert{
351*e0c4386eSCy Schubert    my $self = shift;
352*e0c4386eSCy Schubert    if (@_) {
353*e0c4386eSCy Schubert      $self->{len} = shift;
354*e0c4386eSCy Schubert    }
355*e0c4386eSCy Schubert    return $self->{len};
356*e0c4386eSCy Schubert}
357*e0c4386eSCy Schubertsub version
358*e0c4386eSCy Schubert{
359*e0c4386eSCy Schubert    my $self = shift;
360*e0c4386eSCy Schubert    if (@_) {
361*e0c4386eSCy Schubert      $self->{version} = shift;
362*e0c4386eSCy Schubert    }
363*e0c4386eSCy Schubert    return $self->{version};
364*e0c4386eSCy Schubert}
365*e0c4386eSCy Schubertsub content_type
366*e0c4386eSCy Schubert{
367*e0c4386eSCy Schubert    my $self = shift;
368*e0c4386eSCy Schubert    if (@_) {
369*e0c4386eSCy Schubert      $self->{content_type} = shift;
370*e0c4386eSCy Schubert    }
371*e0c4386eSCy Schubert    return $self->{content_type};
372*e0c4386eSCy Schubert}
373*e0c4386eSCy Schubertsub encrypted
374*e0c4386eSCy Schubert{
375*e0c4386eSCy Schubert    my $self = shift;
376*e0c4386eSCy Schubert    if (@_) {
377*e0c4386eSCy Schubert      $self->{encrypted} = shift;
378*e0c4386eSCy Schubert    }
379*e0c4386eSCy Schubert    return $self->{encrypted};
380*e0c4386eSCy Schubert}
381*e0c4386eSCy Schubertsub outer_content_type
382*e0c4386eSCy Schubert{
383*e0c4386eSCy Schubert    my $self = shift;
384*e0c4386eSCy Schubert    if (@_) {
385*e0c4386eSCy Schubert      $self->{outer_content_type} = shift;
386*e0c4386eSCy Schubert    }
387*e0c4386eSCy Schubert    return $self->{outer_content_type};
388*e0c4386eSCy Schubert}
389*e0c4386eSCy Schubertsub is_fatal_alert
390*e0c4386eSCy Schubert{
391*e0c4386eSCy Schubert    my $self = shift;
392*e0c4386eSCy Schubert    my $server = shift;
393*e0c4386eSCy Schubert
394*e0c4386eSCy Schubert    if (($self->{flight} & 1) == $server
395*e0c4386eSCy Schubert        && $self->{content_type} == TLSProxy::Record::RT_ALERT) {
396*e0c4386eSCy Schubert        my ($level, $alert) = unpack('CC', $self->decrypt_data);
397*e0c4386eSCy Schubert        return $alert if ($level == 2);
398*e0c4386eSCy Schubert    }
399*e0c4386eSCy Schubert    return 0;
400*e0c4386eSCy Schubert}
401*e0c4386eSCy Schubert1;
402