Blame util/perl/TLSProxy/Record.pm

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