openssl/util/perl/TLSProxy/Record.pm
Sam Roberts df4439186f Remove unnecessary trailing whitespace
Trim trailing whitespace. It doesn't match OpenSSL coding standards,
AFAICT, and it can cause problems with git tooling.

Trailing whitespace remains in test data and external source.

Reviewed-by: Kurt Roeckx <kurt@roeckx.be>
Reviewed-by: Matthias St. Pierre <Matthias.St.Pierre@ncp-e.com>
Reviewed-by: Richard Levitte <levitte@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/8092)
2019-02-05 16:25:11 +01:00

402 lines
9.3 KiB
Perl

# Copyright 2016-2018 The OpenSSL Project Authors. All Rights Reserved.
#
# Licensed under the Apache License 2.0 (the "License"). You may not use
# this file except in compliance with the License. You can obtain a copy
# in the file LICENSE in the source distribution or at
# https://www.openssl.org/source/license.html
use strict;
use TLSProxy::Proxy;
package TLSProxy::Record;
my $server_encrypting = 0;
my $client_encrypting = 0;
my $etm = 0;
use constant TLS_RECORD_HEADER_LENGTH => 5;
#Record types
use constant {
RT_APPLICATION_DATA => 23,
RT_HANDSHAKE => 22,
RT_ALERT => 21,
RT_CCS => 20,
RT_UNKNOWN => 100
};
my %record_type = (
RT_APPLICATION_DATA, "APPLICATION DATA",
RT_HANDSHAKE, "HANDSHAKE",
RT_ALERT, "ALERT",
RT_CCS, "CCS",
RT_UNKNOWN, "UNKNOWN"
);
use constant {
VERS_TLS_1_4 => 0x0305,
VERS_TLS_1_3 => 0x0304,
VERS_TLS_1_2 => 0x0303,
VERS_TLS_1_1 => 0x0302,
VERS_TLS_1_0 => 0x0301,
VERS_SSL_3_0 => 0x0300,
VERS_SSL_LT_3_0 => 0x02ff
};
my %tls_version = (
VERS_TLS_1_3, "TLS1.3",
VERS_TLS_1_2, "TLS1.2",
VERS_TLS_1_1, "TLS1.1",
VERS_TLS_1_0, "TLS1.0",
VERS_SSL_3_0, "SSL3",
VERS_SSL_LT_3_0, "SSL<3"
);
#Class method to extract records from a packet of data
sub get_records
{
my $class = shift;
my $server = shift;
my $flight = shift;
my $packet = shift;
my $partial = "";
my @record_list = ();
my @message_list = ();
my $recnum = 1;
while (length ($packet) > 0) {
print " Record $recnum ", $server ? "(server -> client)\n"
: "(client -> server)\n";
#Get the record header (unpack can't fail if $packet is too short)
my ($content_type, $version, $len) = unpack('Cnn', $packet);
if (length($packet) < TLS_RECORD_HEADER_LENGTH + ($len // 0)) {
print "Partial data : ".length($packet)." bytes\n";
$partial = $packet;
last;
}
my $data = substr($packet, TLS_RECORD_HEADER_LENGTH, $len);
print " Content type: ".$record_type{$content_type}."\n";
print " Version: $tls_version{$version}\n";
print " Length: $len\n";
my $record = TLSProxy::Record->new(
$flight,
$content_type,
$version,
$len,
0,
$len, # len_real
$len, # decrypt_len
$data, # data
$data # decrypt_data
);
if ($content_type != RT_CCS
&& (!TLSProxy::Proxy->is_tls13()
|| $content_type != RT_ALERT)) {
if (($server && $server_encrypting)
|| (!$server && $client_encrypting)) {
if (!TLSProxy::Proxy->is_tls13() && $etm) {
$record->decryptETM();
} else {
$record->decrypt();
}
$record->encrypted(1);
if (TLSProxy::Proxy->is_tls13()) {
print " Inner content type: "
.$record_type{$record->content_type()}."\n";
}
}
}
push @record_list, $record;
#Now figure out what messages are contained within this record
my @messages = TLSProxy::Message->get_messages($server, $record);
push @message_list, @messages;
$packet = substr($packet, TLS_RECORD_HEADER_LENGTH + $len);
$recnum++;
}
return (\@record_list, \@message_list, $partial);
}
sub clear
{
$server_encrypting = 0;
$client_encrypting = 0;
}
#Class level accessors
sub server_encrypting
{
my $class = shift;
if (@_) {
$server_encrypting = shift;
}
return $server_encrypting;
}
sub client_encrypting
{
my $class = shift;
if (@_) {
$client_encrypting= shift;
}
return $client_encrypting;
}
#Enable/Disable Encrypt-then-MAC
sub etm
{
my $class = shift;
if (@_) {
$etm = shift;
}
return $etm;
}
sub new
{
my $class = shift;
my ($flight,
$content_type,
$version,
$len,
$sslv2,
$len_real,
$decrypt_len,
$data,
$decrypt_data) = @_;
my $self = {
flight => $flight,
content_type => $content_type,
version => $version,
len => $len,
sslv2 => $sslv2,
len_real => $len_real,
decrypt_len => $decrypt_len,
data => $data,
decrypt_data => $decrypt_data,
orig_decrypt_data => $decrypt_data,
sent => 0,
encrypted => 0,
outer_content_type => RT_APPLICATION_DATA
};
return bless $self, $class;
}
#Decrypt using encrypt-then-MAC
sub decryptETM
{
my ($self) = shift;
my $data = $self->data;
if($self->version >= VERS_TLS_1_1()) {
#TLS1.1+ has an explicit IV. Throw it away
$data = substr($data, 16);
}
#Throw away the MAC (assumes MAC is 20 bytes for now. FIXME)
$data = substr($data, 0, length($data) - 20);
#Find out what the padding byte is
my $padval = unpack("C", substr($data, length($data) - 1));
#Throw away the padding
$data = substr($data, 0, length($data) - ($padval + 1));
$self->decrypt_data($data);
$self->decrypt_len(length($data));
return $data;
}
#Standard decrypt
sub decrypt()
{
my ($self) = shift;
my $mactaglen = 20;
my $data = $self->data;
#Throw away any IVs
if (TLSProxy::Proxy->is_tls13()) {
#A TLS1.3 client, when processing the server's initial flight, could
#respond with either an encrypted or an unencrypted alert.
if ($self->content_type() == RT_ALERT) {
#TODO(TLS1.3): Eventually it is sufficient just to check the record
#content type. If an alert is encrypted it will have a record
#content type of application data. However we haven't done the
#record layer changes yet, so it's a bit more complicated. For now
#we will additionally check if the data length is 2 (1 byte for
#alert level, 1 byte for alert description). If it is, then this is
#an unencrypted alert, so don't try to decrypt
return $data if (length($data) == 2);
}
$mactaglen = 16;
} elsif ($self->version >= VERS_TLS_1_1()) {
#16 bytes for a standard IV
$data = substr($data, 16);
#Find out what the padding byte is
my $padval = unpack("C", substr($data, length($data) - 1));
#Throw away the padding
$data = substr($data, 0, length($data) - ($padval + 1));
}
#Throw away the MAC or TAG
$data = substr($data, 0, length($data) - $mactaglen);
if (TLSProxy::Proxy->is_tls13()) {
#Get the content type
my $content_type = unpack("C", substr($data, length($data) - 1));
$self->content_type($content_type);
$data = substr($data, 0, length($data) - 1);
}
$self->decrypt_data($data);
$self->decrypt_len(length($data));
return $data;
}
#Reconstruct the on-the-wire record representation
sub reconstruct_record
{
my $self = shift;
my $server = shift;
my $data;
#We only replay the records in the same direction
if ($self->{sent} || ($self->flight & 1) != $server) {
return "";
}
$self->{sent} = 1;
if ($self->sslv2) {
$data = pack('n', $self->len | 0x8000);
} else {
if (TLSProxy::Proxy->is_tls13() && $self->encrypted) {
$data = pack('Cnn', $self->outer_content_type, $self->version,
$self->len);
} else {
$data = pack('Cnn', $self->content_type, $self->version,
$self->len);
}
}
$data .= $self->data;
return $data;
}
#Read only accessors
sub flight
{
my $self = shift;
return $self->{flight};
}
sub sslv2
{
my $self = shift;
return $self->{sslv2};
}
sub len_real
{
my $self = shift;
return $self->{len_real};
}
sub orig_decrypt_data
{
my $self = shift;
return $self->{orig_decrypt_data};
}
#Read/write accessors
sub decrypt_len
{
my $self = shift;
if (@_) {
$self->{decrypt_len} = shift;
}
return $self->{decrypt_len};
}
sub data
{
my $self = shift;
if (@_) {
$self->{data} = shift;
}
return $self->{data};
}
sub decrypt_data
{
my $self = shift;
if (@_) {
$self->{decrypt_data} = shift;
}
return $self->{decrypt_data};
}
sub len
{
my $self = shift;
if (@_) {
$self->{len} = shift;
}
return $self->{len};
}
sub version
{
my $self = shift;
if (@_) {
$self->{version} = shift;
}
return $self->{version};
}
sub content_type
{
my $self = shift;
if (@_) {
$self->{content_type} = shift;
}
return $self->{content_type};
}
sub encrypted
{
my $self = shift;
if (@_) {
$self->{encrypted} = shift;
}
return $self->{encrypted};
}
sub outer_content_type
{
my $self = shift;
if (@_) {
$self->{outer_content_type} = shift;
}
return $self->{outer_content_type};
}
sub is_fatal_alert
{
my $self = shift;
my $server = shift;
if (($self->{flight} & 1) == $server
&& $self->{content_type} == TLSProxy::Record::RT_ALERT) {
my ($level, $alert) = unpack('CC', $self->decrypt_data);
return $alert if ($level == 2);
}
return 0;
}
1;