1d1050867c416c19101ec5810dd48110722849d8
[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 };
88
89 my %tls_version = (
90     VERS_TLS_1_3, "TLS1.3",
91     VERS_TLS_1_2, "TLS1.2",
92     VERS_TLS_1_1, "TLS1.1",
93     VERS_TLS_1_0, "TLS1.0",
94     VERS_SSL_3_0, "SSL3"
95 );
96
97 #Class method to extract records from a packet of data
98 sub get_records
99 {
100     my $class = shift;
101     my $server = shift;
102     my $flight = shift;
103     my $packet = shift;
104     my @record_list = ();
105     my @message_list = ();
106     my $data;
107     my $content_type;
108     my $version;
109     my $len;
110     my $len_real;
111     my $decrypt_len;
112
113     my $recnum = 1;
114     while (length ($packet) > 0) {
115         print " Record $recnum";
116         if ($server) {
117             print " (server -> client)\n";
118         } else {
119             print " (client -> server)\n";
120         }
121         #Get the record header
122         if (length($packet) < TLS_RECORD_HEADER_LENGTH) {
123             print "Partial data : ".length($packet)." bytes\n";
124             $packet = "";
125         } else {
126             ($content_type, $version, $len) = unpack('CnnC*', $packet);
127             $data = substr($packet, 5, $len);
128
129             print "  Content type: ".$record_type{$content_type}."\n";
130             print "  Version: $tls_version{$version}\n";
131             print "  Length: $len";
132             if ($len == length($data)) {
133                 print "\n";
134                 $decrypt_len = $len_real = $len;
135             } else {
136                 print " (expected), ".length($data)." (actual)\n";
137                 $decrypt_len = $len_real = length($data);
138             }
139
140             my $record = TLSProxy::Record->new(
141                 $flight,
142                 $content_type,
143                 $version,
144                 $len,
145                 $len_real,
146                 $decrypt_len,
147                 substr($packet, TLS_RECORD_HEADER_LENGTH, $len_real),
148                 substr($packet, TLS_RECORD_HEADER_LENGTH, $len_real)
149             );
150
151             if (($server && $server_ccs_seen)
152                      || (!$server && $client_ccs_seen)) {
153                 if ($etm) {
154                     $record->decryptETM();
155                 } else {
156                     $record->decrypt();
157                 }
158             }
159
160             push @record_list, $record;
161
162             #Now figure out what messages are contained within this record
163             my @messages = TLSProxy::Message->get_messages($server, $record);
164             push @message_list, @messages;
165
166             $packet = substr($packet, TLS_RECORD_HEADER_LENGTH + $len_real);
167             $recnum++;
168         }
169     }
170
171     return (\@record_list, \@message_list);
172 }
173
174 sub clear
175 {
176     $server_ccs_seen = 0;
177     $client_ccs_seen = 0;
178 }
179
180 #Class level accessors
181 sub server_ccs_seen
182 {
183     my $class = shift;
184     if (@_) {
185       $server_ccs_seen = shift;
186     }
187     return $server_ccs_seen;
188 }
189 sub client_ccs_seen
190 {
191     my $class = shift;
192     if (@_) {
193       $client_ccs_seen = shift;
194     }
195     return $client_ccs_seen;
196 }
197 #Enable/Disable Encrypt-then-MAC
198 sub etm
199 {
200     my $class = shift;
201     if (@_) {
202       $etm = shift;
203     }
204     return $etm;
205 }
206
207 sub new
208 {
209     my $class = shift;
210     my ($flight,
211         $content_type,
212         $version,
213         $len,
214         $len_real,
215         $decrypt_len,
216         $data,
217         $decrypt_data) = @_;
218     
219     my $self = {
220         flight => $flight,
221         content_type => $content_type,
222         version => $version,
223         len => $len,
224         len_real => $len_real,
225         decrypt_len => $decrypt_len,
226         data => $data,
227         decrypt_data => $decrypt_data,
228         orig_decrypt_data => $decrypt_data
229     };
230
231     return bless $self, $class;
232 }
233
234 #Decrypt using encrypt-then-MAC
235 sub decryptETM
236 {
237     my ($self) = shift;
238
239     my $data = $self->data;
240
241     if($self->version >= VERS_TLS_1_1()) {
242         #TLS1.1+ has an explicit IV. Throw it away
243         $data = substr($data, 16);
244     }
245
246     #Throw away the MAC (assumes MAC is 20 bytes for now. FIXME)
247     $data = substr($data, 0, length($data) - 20);
248
249     #Find out what the padding byte is
250     my $padval = unpack("C", substr($data, length($data) - 1));
251
252     #Throw away the padding
253     $data = substr($data, 0, length($data) - ($padval + 1));
254
255     $self->decrypt_data($data);
256     $self->decrypt_len(length($data));
257
258     return $data;
259 }
260
261 #Standard decrypt
262 sub decrypt()
263 {
264     my ($self) = shift;
265
266     my $data = $self->data;
267
268     if($self->version >= VERS_TLS_1_1()) {
269         #TLS1.1+ has an explicit IV. Throw it away
270         $data = substr($data, 16);
271     }
272
273     #Find out what the padding byte is
274     my $padval = unpack("C", substr($data, length($data) - 1));
275
276     #Throw away the padding
277     $data = substr($data, 0, length($data) - ($padval + 1));
278
279     #Throw away the MAC (assumes MAC is 20 bytes for now. FIXME)
280     $data = substr($data, 0, length($data) - 20);
281
282     $self->decrypt_data($data);
283     $self->decrypt_len(length($data));
284
285     return $data;
286 }
287
288 #Reconstruct the on-the-wire record representation
289 sub reconstruct_record
290 {
291     my $self = shift;
292     my $data;
293
294     $data = pack('Cnn', $self->content_type, $self->version, $self->len);
295     $data .= $self->data;
296
297     return $data;
298 }
299
300 #Read only accessors
301 sub flight
302 {
303     my $self = shift;
304     return $self->{flight};
305 }
306 sub content_type
307 {
308     my $self = shift;
309     return $self->{content_type};
310 }
311 sub version
312 {
313     my $self = shift;
314     return $self->{version};
315 }
316 sub len_real
317 {
318     my $self = shift;
319     return $self->{len_real};
320 }
321 sub orig_decrypt_data
322 {
323     my $self = shift;
324     return $self->{orig_decrypt_data};
325 }
326
327 #Read/write accessors
328 sub decrypt_len
329 {
330     my $self = shift;
331     if (@_) {
332       $self->{decrypt_len} = shift;
333     }
334     return $self->{decrypt_len};
335 }
336 sub data
337 {
338     my $self = shift;
339     if (@_) {
340       $self->{data} = shift;
341     }
342     return $self->{data};
343 }
344 sub decrypt_data
345 {
346     my $self = shift;
347     if (@_) {
348       $self->{decrypt_data} = shift;
349     }
350     return $self->{decrypt_data};
351 }
352 sub len
353 {
354     my $self = shift;
355     if (@_) {
356       $self->{len} = shift;
357     }
358     return $self->{len};
359 }
360 1;