1 # Copyright 2016-2024 The OpenSSL Project Authors. All Rights Reserved.
3 # Licensed under the Apache License 2.0 (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 DTLS_RECORD_HEADER_LENGTH => 13;
19 use constant TLS_RECORD_HEADER_LENGTH => 5;
23 RT_APPLICATION_DATA => 23,
31 RT_APPLICATION_DATA, "APPLICATION DATA",
32 RT_HANDSHAKE, "HANDSHAKE",
39 VERS_DTLS_1_2 => 0xfefd,
40 VERS_DTLS_1 => 0xfeff,
41 VERS_TLS_1_4 => 0x0305,
42 VERS_TLS_1_3 => 0x0304,
43 VERS_TLS_1_2 => 0x0303,
44 VERS_TLS_1_1 => 0x0302,
45 VERS_TLS_1_0 => 0x0301,
46 VERS_SSL_3_0 => 0x0300,
47 VERS_SSL_LT_3_0 => 0x02ff
51 VERS_DTLS_1_2, "DTLS1.2",
53 VERS_TLS_1_3, "TLS1.3",
54 VERS_TLS_1_2, "TLS1.2",
55 VERS_TLS_1_1, "TLS1.1",
56 VERS_TLS_1_0, "TLS1.0",
58 VERS_SSL_LT_3_0, "SSL<3"
61 #Class method to extract records from a packet of data
71 my @message_list = ();
72 my $record_hdr_len = $isdtls ? DTLS_RECORD_HEADER_LENGTH
73 : TLS_RECORD_HEADER_LENGTH;
76 while (length ($packet) > 0) {
77 print " Record $recnum ", $server ? "(server -> client)\n"
78 : "(client -> server)\n";
90 #Get the record header (unpack can't fail if $packet is too short)
91 ($content_type, $version, $epoch,
92 $seqhi, $seqmi, $seqlo, $len) = unpack('Cnnnnnn', $packet);
93 $seq = ($seqhi << 32) | ($seqmi << 16) | $seqlo
95 #Get the record header (unpack can't fail if $packet is too short)
96 ($content_type, $version, $len) = unpack('Cnn', $packet);
99 if (length($packet) < $record_hdr_len + ($len // 0)) {
100 print "Partial data : ".length($packet)." bytes\n";
105 my $data = substr($packet, $record_hdr_len, $len);
107 print " Content type: ".$record_type{$content_type}."\n";
108 print " Version: $tls_version{$version}\n";
110 print " Epoch: $epoch\n";
111 print " Sequence: $seq\n";
113 print " Length: $len\n";
117 $record = TLSProxy::Record->new_dtls(
131 $record = TLSProxy::Record->new(
144 if ($content_type != RT_CCS
145 && (!TLSProxy::Proxy->is_tls13()
146 || $content_type != RT_ALERT)) {
147 if (($server && $server_encrypting)
148 || (!$server && $client_encrypting)) {
149 if (!TLSProxy::Proxy->is_tls13() && $etm) {
150 $record->decryptETM();
154 $record->encrypted(1);
156 if (TLSProxy::Proxy->is_tls13()) {
157 print " Inner content type: "
158 .$record_type{$record->content_type()}."\n";
163 push @record_list, $record;
165 #Now figure out what messages are contained within this record
166 my @messages = TLSProxy::Message->get_messages($server, $record, $isdtls);
167 push @message_list, @messages;
169 $packet = substr($packet, $record_hdr_len + $len);
173 return (\@record_list, \@message_list, $partial);
178 $server_encrypting = 0;
179 $client_encrypting = 0;
182 #Class level accessors
183 sub server_encrypting
187 $server_encrypting = shift;
189 return $server_encrypting;
191 sub client_encrypting
195 $client_encrypting= shift;
197 return $client_encrypting;
199 #Enable/Disable Encrypt-then-MAC
223 return $class->init(1,
283 content_type => $content_type,
289 len_real => $len_real,
290 decrypt_len => $decrypt_len,
292 decrypt_data => $decrypt_data,
293 orig_decrypt_data => $decrypt_data,
296 outer_content_type => RT_APPLICATION_DATA
299 return bless $self, $class;
302 #Decrypt using encrypt-then-MAC
307 my $data = $self->data;
309 if($self->version >= VERS_TLS_1_1()) {
310 #TLS1.1+ has an explicit IV. Throw it away
311 $data = substr($data, 16);
314 #Throw away the MAC (assumes MAC is 20 bytes for now. FIXME)
315 $data = substr($data, 0, length($data) - 20);
317 #Find out what the padding byte is
318 my $padval = unpack("C", substr($data, length($data) - 1));
320 #Throw away the padding
321 $data = substr($data, 0, length($data) - ($padval + 1));
323 $self->decrypt_data($data);
324 $self->decrypt_len(length($data));
334 my $data = $self->data;
337 if (TLSProxy::Proxy->is_tls13()) {
338 #A TLS1.3 client, when processing the server's initial flight, could
339 #respond with either an encrypted or an unencrypted alert.
340 if ($self->content_type() == RT_ALERT) {
341 #TODO(TLS1.3): Eventually it is sufficient just to check the record
342 #content type. If an alert is encrypted it will have a record
343 #content type of application data. However we haven't done the
344 #record layer changes yet, so it's a bit more complicated. For now
345 #we will additionally check if the data length is 2 (1 byte for
346 #alert level, 1 byte for alert description). If it is, then this is
347 #an unencrypted alert, so don't try to decrypt
348 return $data if (length($data) == 2);
351 } elsif ($self->version >= VERS_TLS_1_1()) {
352 #16 bytes for a standard IV
353 $data = substr($data, 16);
355 #Find out what the padding byte is
356 my $padval = unpack("C", substr($data, length($data) - 1));
358 #Throw away the padding
359 $data = substr($data, 0, length($data) - ($padval + 1));
362 #Throw away the MAC or TAG
363 $data = substr($data, 0, length($data) - $mactaglen);
365 if (TLSProxy::Proxy->is_tls13()) {
366 #Get the content type
367 my $content_type = unpack("C", substr($data, length($data) - 1));
368 $self->content_type($content_type);
369 $data = substr($data, 0, length($data) - 1);
372 $self->decrypt_data($data);
373 $self->decrypt_len(length($data));
378 #Reconstruct the on-the-wire record representation
379 sub reconstruct_record
385 #We only replay the records in the same direction
386 if ($self->{sent} || ($self->flight & 1) != $server) {
392 $data = pack('n', $self->len | 0x8000);
394 if($self->{isdtls}) {
395 my $seqhi = ($self->seq >> 32) & 0xffff;
396 my $seqmi = ($self->seq >> 16) & 0xffff;
397 my $seqlo = ($self->seq >> 0) & 0xffff;
398 $data = pack('Cnnnnnn', $self->content_type, $self->version,
399 $self->epoch, $seqhi, $seqmi, $seqlo, $self->len);
401 if (TLSProxy::Proxy->is_tls13() && $self->encrypted) {
402 $data = pack('Cnn', $self->outer_content_type, $self->version,
406 $data = pack('Cnn', $self->content_type, $self->version,
412 $data .= $self->data;
421 return $self->{flight};
426 return $self->{sslv2};
431 return $self->{len_real};
433 sub orig_decrypt_data
436 return $self->{orig_decrypt_data};
439 #Read/write accessors
444 $self->{decrypt_len} = shift;
446 return $self->{decrypt_len};
452 $self->{data} = shift;
454 return $self->{data};
460 $self->{decrypt_data} = shift;
462 return $self->{decrypt_data};
468 $self->{len} = shift;
476 $self->{version} = shift;
478 return $self->{version};
484 $self->{content_type} = shift;
486 return $self->{content_type};
492 $self->{epoch} = shift;
494 return $self->{epoch};
500 $self->{seq} = shift;
508 $self->{encrypted} = shift;
510 return $self->{encrypted};
512 sub outer_content_type
516 $self->{outer_content_type} = shift;
518 return $self->{outer_content_type};
525 if (($self->{flight} & 1) == $server && $self->{content_type} == RT_ALERT) {
526 my ($level, $description) = unpack('CC', $self->decrypt_data);
527 return $description if ($level == 2);