13d804f42a31bac885e2e6c41ac29f2c1cc04858
[openssl.git] / util / perl / TLSProxy / Record.pm
1 # Copyright 2016 The OpenSSL Project Authors. All Rights Reserved.
2 #
3 # Licensed under the OpenSSL license (the "License").  You may not use
4 # this file except in compliance with the License.  You can obtain a copy
5 # in the file LICENSE in the source distribution or at
6 # https://www.openssl.org/source/license.html
7
8 use strict;
9
10 use TLSProxy::Proxy;
11
12 package TLSProxy::Record;
13
14 my $server_ccs_seen = 0;
15 my $client_ccs_seen = 0;
16 my $etm = 0;
17
18 use constant TLS_RECORD_HEADER_LENGTH => 5;
19
20 #Record types
21 use constant {
22     RT_APPLICATION_DATA => 23,
23     RT_HANDSHAKE => 22,
24     RT_ALERT => 21,
25     RT_CCS => 20,
26     RT_UNKNOWN => 100
27 };
28
29 my %record_type = (
30     RT_APPLICATION_DATA, "APPLICATION DATA",
31     RT_HANDSHAKE, "HANDSHAKE",
32     RT_ALERT, "ALERT",
33     RT_CCS, "CCS",
34     RT_UNKNOWN, "UNKNOWN"
35 );
36
37 use constant {
38     VERS_TLS_1_3 => 772,
39     VERS_TLS_1_2 => 771,
40     VERS_TLS_1_1 => 770,
41     VERS_TLS_1_0 => 769,
42     VERS_SSL_3_0 => 768,
43     VERS_SSL_LT_3_0 => 767
44 };
45
46 my %tls_version = (
47     VERS_TLS_1_3, "TLS1.3",
48     VERS_TLS_1_2, "TLS1.2",
49     VERS_TLS_1_1, "TLS1.1",
50     VERS_TLS_1_0, "TLS1.0",
51     VERS_SSL_3_0, "SSL3",
52     VERS_SSL_LT_3_0, "SSL<3"
53 );
54
55 #Class method to extract records from a packet of data
56 sub get_records
57 {
58     my $class = shift;
59     my $server = shift;
60     my $flight = shift;
61     my $packet = shift;
62     my $partial = "";
63     my @record_list = ();
64     my @message_list = ();
65     my $data;
66     my $content_type;
67     my $version;
68     my $len;
69     my $len_real;
70     my $decrypt_len;
71
72     my $recnum = 1;
73     while (length ($packet) > 0) {
74         print " Record $recnum";
75         if ($server) {
76             print " (server -> client)\n";
77         } else {
78             print " (client -> server)\n";
79         }
80         #Get the record header
81         if (length($packet) < TLS_RECORD_HEADER_LENGTH
82                 || length($packet) < 5 + unpack("n", substr($packet, 3, 2))) {
83             print "Partial data : ".length($packet)." bytes\n";
84             $partial = $packet;
85             $packet = "";
86         } else {
87             ($content_type, $version, $len) = unpack('CnnC*', $packet);
88             $data = substr($packet, 5, $len);
89
90             print "  Content type: ".$record_type{$content_type}."\n";
91             print "  Version: $tls_version{$version}\n";
92             print "  Length: $len";
93             if ($len == length($data)) {
94                 print "\n";
95                 $decrypt_len = $len_real = $len;
96             } else {
97                 print " (expected), ".length($data)." (actual)\n";
98                 $decrypt_len = $len_real = length($data);
99             }
100
101             my $record = TLSProxy::Record->new(
102                 $flight,
103                 $content_type,
104                 $version,
105                 $len,
106                 0,
107                 $len_real,
108                 $decrypt_len,
109                 substr($packet, TLS_RECORD_HEADER_LENGTH, $len_real),
110                 substr($packet, TLS_RECORD_HEADER_LENGTH, $len_real)
111             );
112
113             if (($server && $server_ccs_seen)
114                      || (!$server && $client_ccs_seen)) {
115                 if ($etm) {
116                     $record->decryptETM();
117                 } else {
118                     $record->decrypt();
119                 }
120             }
121
122             push @record_list, $record;
123
124             #Now figure out what messages are contained within this record
125             my @messages = TLSProxy::Message->get_messages($server, $record);
126             push @message_list, @messages;
127
128             $packet = substr($packet, TLS_RECORD_HEADER_LENGTH + $len_real);
129             $recnum++;
130         }
131     }
132
133     return (\@record_list, \@message_list, $partial);
134 }
135
136 sub clear
137 {
138     $server_ccs_seen = 0;
139     $client_ccs_seen = 0;
140 }
141
142 #Class level accessors
143 sub server_ccs_seen
144 {
145     my $class = shift;
146     if (@_) {
147       $server_ccs_seen = shift;
148     }
149     return $server_ccs_seen;
150 }
151 sub client_ccs_seen
152 {
153     my $class = shift;
154     if (@_) {
155       $client_ccs_seen = shift;
156     }
157     return $client_ccs_seen;
158 }
159 #Enable/Disable Encrypt-then-MAC
160 sub etm
161 {
162     my $class = shift;
163     if (@_) {
164       $etm = shift;
165     }
166     return $etm;
167 }
168
169 sub new
170 {
171     my $class = shift;
172     my ($flight,
173         $content_type,
174         $version,
175         $len,
176         $sslv2,
177         $len_real,
178         $decrypt_len,
179         $data,
180         $decrypt_data) = @_;
181     
182     my $self = {
183         flight => $flight,
184         content_type => $content_type,
185         version => $version,
186         len => $len,
187         sslv2 => $sslv2,
188         len_real => $len_real,
189         decrypt_len => $decrypt_len,
190         data => $data,
191         decrypt_data => $decrypt_data,
192         orig_decrypt_data => $decrypt_data,
193         sent => 0
194     };
195
196     return bless $self, $class;
197 }
198
199 #Decrypt using encrypt-then-MAC
200 sub decryptETM
201 {
202     my ($self) = shift;
203
204     my $data = $self->data;
205
206     if($self->version >= VERS_TLS_1_1()) {
207         #TLS1.1+ has an explicit IV. Throw it away
208         $data = substr($data, 16);
209     }
210
211     #Throw away the MAC (assumes MAC is 20 bytes for now. FIXME)
212     $data = substr($data, 0, length($data) - 20);
213
214     #Find out what the padding byte is
215     my $padval = unpack("C", substr($data, length($data) - 1));
216
217     #Throw away the padding
218     $data = substr($data, 0, length($data) - ($padval + 1));
219
220     $self->decrypt_data($data);
221     $self->decrypt_len(length($data));
222
223     return $data;
224 }
225
226 #Standard decrypt
227 sub decrypt()
228 {
229     my ($self) = shift;
230
231     my $data = $self->data;
232
233     if($self->version >= VERS_TLS_1_1()) {
234         #TLS1.1+ has an explicit IV. Throw it away
235         $data = substr($data, 16);
236     }
237
238     #Find out what the padding byte is
239     my $padval = unpack("C", substr($data, length($data) - 1));
240
241     #Throw away the padding
242     $data = substr($data, 0, length($data) - ($padval + 1));
243
244     #Throw away the MAC (assumes MAC is 20 bytes for now. FIXME)
245     $data = substr($data, 0, length($data) - 20);
246
247     $self->decrypt_data($data);
248     $self->decrypt_len(length($data));
249
250     return $data;
251 }
252
253 #Reconstruct the on-the-wire record representation
254 sub reconstruct_record
255 {
256     my $self = shift;
257     my $data;
258
259     if ($self->{sent}) {
260         return "";
261     }
262     $self->{sent} = 1;
263
264     if ($self->sslv2) {
265         $data = pack('n', $self->len | 0x8000);
266     } else {
267         $data = pack('Cnn', $self->content_type, $self->version, $self->len);
268     }
269     $data .= $self->data;
270
271     return $data;
272 }
273
274 #Read only accessors
275 sub flight
276 {
277     my $self = shift;
278     return $self->{flight};
279 }
280 sub content_type
281 {
282     my $self = shift;
283     return $self->{content_type};
284 }
285 sub version
286 {
287     my $self = shift;
288     return $self->{version};
289 }
290 sub sslv2
291 {
292     my $self = shift;
293     return $self->{sslv2};
294 }
295 sub len_real
296 {
297     my $self = shift;
298     return $self->{len_real};
299 }
300 sub orig_decrypt_data
301 {
302     my $self = shift;
303     return $self->{orig_decrypt_data};
304 }
305
306 #Read/write accessors
307 sub decrypt_len
308 {
309     my $self = shift;
310     if (@_) {
311       $self->{decrypt_len} = shift;
312     }
313     return $self->{decrypt_len};
314 }
315 sub data
316 {
317     my $self = shift;
318     if (@_) {
319       $self->{data} = shift;
320     }
321     return $self->{data};
322 }
323 sub decrypt_data
324 {
325     my $self = shift;
326     if (@_) {
327       $self->{decrypt_data} = shift;
328     }
329     return $self->{decrypt_data};
330 }
331 sub len
332 {
333     my $self = shift;
334     if (@_) {
335       $self->{len} = shift;
336     }
337     return $self->{len};
338 }
339 1;