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 => 0x7f1c,
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
66 my @message_list = ();
69 while (length ($packet) > 0) {
70 print " Record $recnum ", $server ? "(server -> client)\n"
71 : "(client -> server)\n";
73 #Get the record header (unpack can't fail if $packet is too short)
74 my ($content_type, $version, $len) = unpack('Cnn', $packet);
76 if (length($packet) < TLS_RECORD_HEADER_LENGTH + ($len // 0)) {
77 print "Partial data : ".length($packet)." bytes\n";
82 my $data = substr($packet, TLS_RECORD_HEADER_LENGTH, $len);
84 print " Content type: ".$record_type{$content_type}."\n";
85 print " Version: $tls_version{$version}\n";
86 print " Length: $len\n";
88 my $record = TLSProxy::Record->new(
100 if ($content_type != RT_CCS
101 && (!TLSProxy::Proxy->is_tls13()
102 || $content_type != RT_ALERT)) {
103 if (($server && $server_encrypting)
104 || (!$server && $client_encrypting)) {
105 if (!TLSProxy::Proxy->is_tls13() && $etm) {
106 $record->decryptETM();
110 $record->encrypted(1);
112 if (TLSProxy::Proxy->is_tls13()) {
113 print " Inner content type: "
114 .$record_type{$record->content_type()}."\n";
119 push @record_list, $record;
121 #Now figure out what messages are contained within this record
122 my @messages = TLSProxy::Message->get_messages($server, $record);
123 push @message_list, @messages;
125 $packet = substr($packet, TLS_RECORD_HEADER_LENGTH + $len);
129 return (\@record_list, \@message_list, $partial);
134 $server_encrypting = 0;
135 $client_encrypting = 0;
138 #Class level accessors
139 sub server_encrypting
143 $server_encrypting = shift;
145 return $server_encrypting;
147 sub client_encrypting
151 $client_encrypting= shift;
153 return $client_encrypting;
155 #Enable/Disable Encrypt-then-MAC
180 content_type => $content_type,
184 len_real => $len_real,
185 decrypt_len => $decrypt_len,
187 decrypt_data => $decrypt_data,
188 orig_decrypt_data => $decrypt_data,
191 outer_content_type => RT_APPLICATION_DATA
194 return bless $self, $class;
197 #Decrypt using encrypt-then-MAC
202 my $data = $self->data;
204 if($self->version >= VERS_TLS_1_1()) {
205 #TLS1.1+ has an explicit IV. Throw it away
206 $data = substr($data, 16);
209 #Throw away the MAC (assumes MAC is 20 bytes for now. FIXME)
210 $data = substr($data, 0, length($data) - 20);
212 #Find out what the padding byte is
213 my $padval = unpack("C", substr($data, length($data) - 1));
215 #Throw away the padding
216 $data = substr($data, 0, length($data) - ($padval + 1));
218 $self->decrypt_data($data);
219 $self->decrypt_len(length($data));
229 my $data = $self->data;
232 if (TLSProxy::Proxy->is_tls13()) {
233 #A TLS1.3 client, when processing the server's initial flight, could
234 #respond with either an encrypted or an unencrypted alert.
235 if ($self->content_type() == RT_ALERT) {
236 #TODO(TLS1.3): Eventually it is sufficient just to check the record
237 #content type. If an alert is encrypted it will have a record
238 #content type of application data. However we haven't done the
239 #record layer changes yet, so it's a bit more complicated. For now
240 #we will additionally check if the data length is 2 (1 byte for
241 #alert level, 1 byte for alert description). If it is, then this is
242 #an unencrypted alert, so don't try to decrypt
243 return $data if (length($data) == 2);
246 } elsif ($self->version >= VERS_TLS_1_1()) {
247 #16 bytes for a standard IV
248 $data = substr($data, 16);
250 #Find out what the padding byte is
251 my $padval = unpack("C", substr($data, length($data) - 1));
253 #Throw away the padding
254 $data = substr($data, 0, length($data) - ($padval + 1));
257 #Throw away the MAC or TAG
258 $data = substr($data, 0, length($data) - $mactaglen);
260 if (TLSProxy::Proxy->is_tls13()) {
261 #Get the content type
262 my $content_type = unpack("C", substr($data, length($data) - 1));
263 $self->content_type($content_type);
264 $data = substr($data, 0, length($data) - 1);
267 $self->decrypt_data($data);
268 $self->decrypt_len(length($data));
273 #Reconstruct the on-the-wire record representation
274 sub reconstruct_record
280 #We only replay the records in the same direction
281 if ($self->{sent} || ($self->flight & 1) != $server) {
287 $data = pack('n', $self->len | 0x8000);
289 if (TLSProxy::Proxy->is_tls13() && $self->encrypted) {
290 $data = pack('Cnn', $self->outer_content_type, $self->version,
293 $data = pack('Cnn', $self->content_type, $self->version,
298 $data .= $self->data;
307 return $self->{flight};
312 return $self->{sslv2};
317 return $self->{len_real};
319 sub orig_decrypt_data
322 return $self->{orig_decrypt_data};
325 #Read/write accessors
330 $self->{decrypt_len} = shift;
332 return $self->{decrypt_len};
338 $self->{data} = shift;
340 return $self->{data};
346 $self->{decrypt_data} = shift;
348 return $self->{decrypt_data};
354 $self->{len} = shift;
362 $self->{version} = shift;
364 return $self->{version};
370 $self->{content_type} = shift;
372 return $self->{content_type};
378 $self->{encrypted} = shift;
380 return $self->{encrypted};
382 sub outer_content_type
386 $self->{outer_content_type} = shift;
388 return $self->{outer_content_type};
395 if (($self->{flight} & 1) == $server
396 && $self->{content_type} == TLSProxy::Record::RT_ALERT) {
397 my ($level, $alert) = unpack('CC', $self->decrypt_data);
398 return $alert if ($level == 2);