124f924979222ba29bb5732c5a50082b36b09c8c
[openssl.git] / util / TLSProxy / Record.pm
1 # Written by Matt Caswell for the OpenSSL project.
2 # ====================================================================
3 # Copyright (c) 1998-2015 The OpenSSL Project.  All rights reserved.
4 #
5 # Redistribution and use in source and binary forms, with or without
6 # modification, are permitted provided that the following conditions
7 # are met:
8 #
9 # 1. Redistributions of source code must retain the above copyright
10 #    notice, this list of conditions and the following disclaimer.
11 #
12 # 2. Redistributions in binary form must reproduce the above copyright
13 #    notice, this list of conditions and the following disclaimer in
14 #    the documentation and/or other materials provided with the
15 #    distribution.
16 #
17 # 3. All advertising materials mentioning features or use of this
18 #    software must display the following acknowledgment:
19 #    "This product includes software developed by the OpenSSL Project
20 #    for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
21 #
22 # 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
23 #    endorse or promote products derived from this software without
24 #    prior written permission. For written permission, please contact
25 #    openssl-core@openssl.org.
26 #
27 # 5. Products derived from this software may not be called "OpenSSL"
28 #    nor may "OpenSSL" appear in their names without prior written
29 #    permission of the OpenSSL Project.
30 #
31 # 6. Redistributions of any form whatsoever must retain the following
32 #    acknowledgment:
33 #    "This product includes software developed by the OpenSSL Project
34 #    for use in the OpenSSL Toolkit (http://www.openssl.org/)"
35 #
36 # THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
37 # EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
38 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
39 # PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
40 # ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
41 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
42 # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
43 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
44 # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
45 # STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
46 # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
47 # OF THE POSSIBILITY OF SUCH DAMAGE.
48 # ====================================================================
49 #
50 # This product includes cryptographic software written by Eric Young
51 # (eay@cryptsoft.com).  This product includes software written by Tim
52 # Hudson (tjh@cryptsoft.com).
53
54 use strict;
55
56 use TLSProxy::Proxy;
57
58 package TLSProxy::Record;
59
60 my $server_ccs_seen = 0;
61 my $client_ccs_seen = 0;
62 my $etm = 0;
63
64 use constant TLS_RECORD_HEADER_LENGTH => 5;
65
66 #Record types
67 use constant {
68     RT_APPLICATION_DATA => 23,
69     RT_HANDSHAKE => 22,
70     RT_ALERT => 21,
71     RT_CCS => 20
72 };
73
74 my %record_type = (
75     RT_APPLICATION_DATA, "APPLICATION DATA",
76     RT_HANDSHAKE, "HANDSHAKE",
77     RT_ALERT, "ALERT",
78     RT_CCS, "CCS"
79 );
80
81 use constant {
82     VERS_TLS_1_3 => 772,
83     VERS_TLS_1_2 => 771,
84     VERS_TLS_1_1 => 770,
85     VERS_TLS_1_0 => 769,
86     VERS_SSL_3_0 => 768,
87     VERS_SSL_LT_3_0 => 767
88 };
89
90 my %tls_version = (
91     VERS_TLS_1_3, "TLS1.3",
92     VERS_TLS_1_2, "TLS1.2",
93     VERS_TLS_1_1, "TLS1.1",
94     VERS_TLS_1_0, "TLS1.0",
95     VERS_SSL_3_0, "SSL3",
96     VERS_SSL_LT_3_0, "SSL<3"
97 );
98
99 #Class method to extract records from a packet of data
100 sub get_records
101 {
102     my $class = shift;
103     my $server = shift;
104     my $flight = shift;
105     my $packet = shift;
106     my @record_list = ();
107     my @message_list = ();
108     my $data;
109     my $content_type;
110     my $version;
111     my $len;
112     my $len_real;
113     my $decrypt_len;
114
115     my $recnum = 1;
116     while (length ($packet) > 0) {
117         print " Record $recnum";
118         if ($server) {
119             print " (server -> client)\n";
120         } else {
121             print " (client -> server)\n";
122         }
123         #Get the record header
124         if (length($packet) < TLS_RECORD_HEADER_LENGTH) {
125             print "Partial data : ".length($packet)." bytes\n";
126             $packet = "";
127         } else {
128             ($content_type, $version, $len) = unpack('CnnC*', $packet);
129             $data = substr($packet, 5, $len);
130
131             print "  Content type: ".$record_type{$content_type}."\n";
132             print "  Version: $tls_version{$version}\n";
133             print "  Length: $len";
134             if ($len == length($data)) {
135                 print "\n";
136                 $decrypt_len = $len_real = $len;
137             } else {
138                 print " (expected), ".length($data)." (actual)\n";
139                 $decrypt_len = $len_real = length($data);
140             }
141
142             my $record = TLSProxy::Record->new(
143                 $flight,
144                 $content_type,
145                 $version,
146                 $len,
147                 $len_real,
148                 $decrypt_len,
149                 substr($packet, TLS_RECORD_HEADER_LENGTH, $len_real),
150                 substr($packet, TLS_RECORD_HEADER_LENGTH, $len_real)
151             );
152
153             if (($server && $server_ccs_seen)
154                      || (!$server && $client_ccs_seen)) {
155                 if ($etm) {
156                     $record->decryptETM();
157                 } else {
158                     $record->decrypt();
159                 }
160             }
161
162             push @record_list, $record;
163
164             #Now figure out what messages are contained within this record
165             my @messages = TLSProxy::Message->get_messages($server, $record);
166             push @message_list, @messages;
167
168             $packet = substr($packet, TLS_RECORD_HEADER_LENGTH + $len_real);
169             $recnum++;
170         }
171     }
172
173     return (\@record_list, \@message_list);
174 }
175
176 sub clear
177 {
178     $server_ccs_seen = 0;
179     $client_ccs_seen = 0;
180 }
181
182 #Class level accessors
183 sub server_ccs_seen
184 {
185     my $class = shift;
186     if (@_) {
187       $server_ccs_seen = shift;
188     }
189     return $server_ccs_seen;
190 }
191 sub client_ccs_seen
192 {
193     my $class = shift;
194     if (@_) {
195       $client_ccs_seen = shift;
196     }
197     return $client_ccs_seen;
198 }
199 #Enable/Disable Encrypt-then-MAC
200 sub etm
201 {
202     my $class = shift;
203     if (@_) {
204       $etm = shift;
205     }
206     return $etm;
207 }
208
209 sub new
210 {
211     my $class = shift;
212     my ($flight,
213         $content_type,
214         $version,
215         $len,
216         $len_real,
217         $decrypt_len,
218         $data,
219         $decrypt_data) = @_;
220     
221     my $self = {
222         flight => $flight,
223         content_type => $content_type,
224         version => $version,
225         len => $len,
226         len_real => $len_real,
227         decrypt_len => $decrypt_len,
228         data => $data,
229         decrypt_data => $decrypt_data,
230         orig_decrypt_data => $decrypt_data
231     };
232
233     return bless $self, $class;
234 }
235
236 #Decrypt using encrypt-then-MAC
237 sub decryptETM
238 {
239     my ($self) = shift;
240
241     my $data = $self->data;
242
243     if($self->version >= VERS_TLS_1_1()) {
244         #TLS1.1+ has an explicit IV. Throw it away
245         $data = substr($data, 16);
246     }
247
248     #Throw away the MAC (assumes MAC is 20 bytes for now. FIXME)
249     $data = substr($data, 0, length($data) - 20);
250
251     #Find out what the padding byte is
252     my $padval = unpack("C", substr($data, length($data) - 1));
253
254     #Throw away the padding
255     $data = substr($data, 0, length($data) - ($padval + 1));
256
257     $self->decrypt_data($data);
258     $self->decrypt_len(length($data));
259
260     return $data;
261 }
262
263 #Standard decrypt
264 sub decrypt()
265 {
266     my ($self) = shift;
267
268     my $data = $self->data;
269
270     if($self->version >= VERS_TLS_1_1()) {
271         #TLS1.1+ has an explicit IV. Throw it away
272         $data = substr($data, 16);
273     }
274
275     #Find out what the padding byte is
276     my $padval = unpack("C", substr($data, length($data) - 1));
277
278     #Throw away the padding
279     $data = substr($data, 0, length($data) - ($padval + 1));
280
281     #Throw away the MAC (assumes MAC is 20 bytes for now. FIXME)
282     $data = substr($data, 0, length($data) - 20);
283
284     $self->decrypt_data($data);
285     $self->decrypt_len(length($data));
286
287     return $data;
288 }
289
290 #Reconstruct the on-the-wire record representation
291 sub reconstruct_record
292 {
293     my $self = shift;
294     my $data;
295
296     $data = pack('Cnn', $self->content_type, $self->version, $self->len);
297     $data .= $self->data;
298
299     return $data;
300 }
301
302 #Read only accessors
303 sub flight
304 {
305     my $self = shift;
306     return $self->{flight};
307 }
308 sub content_type
309 {
310     my $self = shift;
311     return $self->{content_type};
312 }
313 sub version
314 {
315     my $self = shift;
316     return $self->{version};
317 }
318 sub len_real
319 {
320     my $self = shift;
321     return $self->{len_real};
322 }
323 sub orig_decrypt_data
324 {
325     my $self = shift;
326     return $self->{orig_decrypt_data};
327 }
328
329 #Read/write accessors
330 sub decrypt_len
331 {
332     my $self = shift;
333     if (@_) {
334       $self->{decrypt_len} = shift;
335     }
336     return $self->{decrypt_len};
337 }
338 sub data
339 {
340     my $self = shift;
341     if (@_) {
342       $self->{data} = shift;
343     }
344     return $self->{data};
345 }
346 sub decrypt_data
347 {
348     my $self = shift;
349     if (@_) {
350       $self->{decrypt_data} = shift;
351     }
352     return $self->{decrypt_data};
353 }
354 sub len
355 {
356     my $self = shift;
357     if (@_) {
358       $self->{len} = shift;
359     }
360     return $self->{len};
361 }
362 1;