1 # Copyright 2016-2018 The OpenSSL Project Authors. All Rights Reserved.
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
12 package TLSProxy
::Record
;
14 my $server_encrypting = 0;
15 my $client_encrypting = 0;
18 use constant TLS_RECORD_HEADER_LENGTH
=> 5;
22 RT_APPLICATION_DATA
=> 23,
30 RT_APPLICATION_DATA
, "APPLICATION DATA",
31 RT_HANDSHAKE
, "HANDSHAKE",
38 VERS_TLS_1_4
=> 0x0305,
39 VERS_TLS_1_3_DRAFT
=> 0x7f17,
40 VERS_TLS_1_3
=> 0x0304,
41 VERS_TLS_1_2
=> 0x0303,
42 VERS_TLS_1_1
=> 0x0302,
43 VERS_TLS_1_0
=> 0x0301,
44 VERS_SSL_3_0
=> 0x0300,
45 VERS_SSL_LT_3_0
=> 0x02ff
49 VERS_TLS_1_3
, "TLS1.3",
50 VERS_TLS_1_2
, "TLS1.2",
51 VERS_TLS_1_1
, "TLS1.1",
52 VERS_TLS_1_0
, "TLS1.0",
54 VERS_SSL_LT_3_0
, "SSL<3"
57 #Class method to extract records from a packet of data
65 my @message_list = ();
74 while (length ($packet) > 0) {
75 print " Record $recnum";
77 print " (server -> client)\n";
79 print " (client -> server)\n";
81 #Get the record header
82 if (length($packet) < TLS_RECORD_HEADER_LENGTH
) {
83 print "Partial data : ".length($packet)." bytes\n";
86 ($content_type, $version, $len) = unpack('CnnC*', $packet);
87 $data = substr($packet, 5, $len);
89 print " Content type: ".$record_type{$content_type}."\n";
90 print " Version: $tls_version{$version}\n";
91 print " Length: $len";
92 if ($len == length($data)) {
94 $decrypt_len = $len_real = $len;
96 print " (expected), ".length($data)." (actual)\n";
97 $decrypt_len = $len_real = length($data);
100 my $record = TLSProxy
::Record
->new(
108 substr($packet, TLS_RECORD_HEADER_LENGTH
, $len_real),
109 substr($packet, TLS_RECORD_HEADER_LENGTH
, $len_real)
112 if ($content_type != RT_CCS
) {
113 if (($server && $server_encrypting)
114 || (!$server && $client_encrypting)) {
115 if (!TLSProxy
::Proxy
->is_tls13() && $etm) {
116 $record->decryptETM();
120 $record->encrypted(1);
122 if (TLSProxy
::Proxy
->is_tls13()) {
123 print " Inner content type: "
124 .$record_type{$record->content_type()}."\n";
129 push @record_list, $record;
131 #Now figure out what messages are contained within this record
132 my @messages = TLSProxy
::Message
->get_messages($server, $record);
133 push @message_list, @messages;
135 $packet = substr($packet, TLS_RECORD_HEADER_LENGTH
+ $len_real);
140 return (\
@record_list, \
@message_list);
145 $server_encrypting = 0;
146 $client_encrypting = 0;
149 #Class level accessors
150 sub server_encrypting
154 $server_encrypting = shift;
156 return $server_encrypting;
158 sub client_encrypting
162 $client_encrypting= shift;
164 return $client_encrypting;
166 #Enable/Disable Encrypt-then-MAC
191 content_type
=> $content_type,
195 len_real
=> $len_real,
196 decrypt_len
=> $decrypt_len,
198 decrypt_data
=> $decrypt_data,
199 orig_decrypt_data
=> $decrypt_data,
201 outer_content_type
=> RT_APPLICATION_DATA
204 return bless $self, $class;
207 #Decrypt using encrypt-then-MAC
212 my $data = $self->data;
214 if($self->version >= VERS_TLS_1_1
()) {
215 #TLS1.1+ has an explicit IV. Throw it away
216 $data = substr($data, 16);
219 #Throw away the MAC (assumes MAC is 20 bytes for now. FIXME)
220 $data = substr($data, 0, length($data) - 20);
222 #Find out what the padding byte is
223 my $padval = unpack("C", substr($data, length($data) - 1));
225 #Throw away the padding
226 $data = substr($data, 0, length($data) - ($padval + 1));
228 $self->decrypt_data($data);
229 $self->decrypt_len(length($data));
239 my $data = $self->data;
242 if (TLSProxy
::Proxy
->is_tls13()) {
243 #A TLS1.3 client, when processing the server's initial flight, could
244 #respond with either an encrypted or an unencrypted alert.
245 if ($self->content_type() == RT_ALERT
) {
246 #TODO(TLS1.3): Eventually it is sufficient just to check the record
247 #content type. If an alert is encrypted it will have a record
248 #content type of application data. However we haven't done the
249 #record layer changes yet, so it's a bit more complicated. For now
250 #we will additionally check if the data length is 2 (1 byte for
251 #alert level, 1 byte for alert description). If it is, then this is
252 #an unencrypted alert, so don't try to decrypt
253 return $data if (length($data) == 2);
256 } elsif ($self->version >= VERS_TLS_1_1
()) {
257 #16 bytes for a standard IV
258 $data = substr($data, 16);
260 #Find out what the padding byte is
261 my $padval = unpack("C", substr($data, length($data) - 1));
263 #Throw away the padding
264 $data = substr($data, 0, length($data) - ($padval + 1));
267 #Throw away the MAC or TAG
268 $data = substr($data, 0, length($data) - $mactaglen);
270 if (TLSProxy
::Proxy
->is_tls13()) {
271 #Get the content type
272 my $content_type = unpack("C", substr($data, length($data) - 1));
273 $self->content_type($content_type);
274 $data = substr($data, 0, length($data) - 1);
277 $self->decrypt_data($data);
278 $self->decrypt_len(length($data));
283 #Reconstruct the on-the-wire record representation
284 sub reconstruct_record
292 $data = pack('n', $self->len | 0x8000);
294 if (TLSProxy
::Proxy
->is_tls13() && $self->encrypted) {
295 $data = pack('Cnn', $self->outer_content_type, $self->version,
299 $data = pack('Cnn', $self->content_type, $self->version,
304 $data .= $self->data;
307 $data .= pack('C', $self->content_type);
317 return $self->{flight
};
322 return $self->{sslv2
};
327 return $self->{len_real
};
329 sub orig_decrypt_data
332 return $self->{orig_decrypt_data
};
335 #Read/write accessors
340 $self->{decrypt_len
} = shift;
342 return $self->{decrypt_len
};
348 $self->{data
} = shift;
350 return $self->{data
};
356 $self->{decrypt_data
} = shift;
358 return $self->{decrypt_data
};
364 $self->{len
} = shift;
372 $self->{version
} = shift;
374 return $self->{version
};
380 $self->{content_type
} = shift;
382 return $self->{content_type
};
388 $self->{encrypted
} = shift;
390 return $self->{encrypted
};
392 sub outer_content_type
396 $self->{outer_content_type
} = shift;
398 return $self->{outer_content_type
};