Home | History | Annotate | Line # | Download | only in TLSProxy
      1 # Copyright 2016-2024 The OpenSSL Project Authors. All Rights Reserved.
      2 #
      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
      7 
      8 use strict;
      9 
     10 package TLSProxy::Certificate;
     11 
     12 use vars '@ISA';
     13 push @ISA, 'TLSProxy::Message';
     14 
     15 sub new
     16 {
     17     my $class = shift;
     18     my ($isdtls,
     19         $server,
     20         $msgseq,
     21         $msgfrag,
     22         $msgfragoffs,
     23         $data,
     24         $records,
     25         $startoffset,
     26         $message_frag_lens) = @_;
     27 
     28     my $self = $class->SUPER::new(
     29         $isdtls,
     30         $server,
     31         TLSProxy::Message::MT_CERTIFICATE,
     32         $msgseq,
     33         $msgfrag,
     34         $msgfragoffs,
     35         $data,
     36         $records,
     37         $startoffset,
     38         $message_frag_lens);
     39 
     40     $self->{first_certificate} = "";
     41     $self->{extension_data} = "";
     42     $self->{remaining_certdata} = "";
     43 
     44     return $self;
     45 }
     46 
     47 sub parse
     48 {
     49     my $self = shift;
     50 
     51     if (TLSProxy::Proxy->is_tls13()) {
     52         my $context_len = unpack('C', $self->data);
     53         my $context = substr($self->data, 1, $context_len);
     54 
     55         my $remdata = substr($self->data, 1 + $context_len);
     56 
     57         my ($hicertlistlen, $certlistlen) = unpack('Cn', $remdata);
     58         $certlistlen += ($hicertlistlen << 16);
     59 
     60         $remdata = substr($remdata, 3);
     61 
     62         die "Invalid Certificate List length"
     63             if length($remdata) != $certlistlen;
     64 
     65         my ($hicertlen, $certlen) = unpack('Cn', $remdata);
     66         $certlen += ($hicertlen << 16);
     67 
     68         die "Certificate too long" if ($certlen + 3) > $certlistlen;
     69 
     70         $remdata = substr($remdata, 3);
     71 
     72         my $certdata = substr($remdata, 0, $certlen);
     73 
     74         $remdata = substr($remdata, $certlen);
     75 
     76         my $extensions_len = unpack('n', $remdata);
     77         $remdata = substr($remdata, 2);
     78 
     79         die "Extensions too long"
     80             if ($certlen + 3 + $extensions_len + 2) > $certlistlen;
     81 
     82         my $extension_data = "";
     83         if ($extensions_len != 0) {
     84             $extension_data = substr($remdata, 0, $extensions_len);
     85 
     86             if (length($extension_data) != $extensions_len) {
     87                 die "Invalid extension length\n";
     88             }
     89         }
     90         my %extensions = ();
     91         while (length($extension_data) >= 4) {
     92             my ($type, $size) = unpack("nn", $extension_data);
     93             my $extdata = substr($extension_data, 4, $size);
     94             $extension_data = substr($extension_data, 4 + $size);
     95             $extensions{$type} = $extdata;
     96         }
     97         $remdata = substr($remdata, $extensions_len);
     98 
     99         $self->context($context);
    100         $self->first_certificate($certdata);
    101         $self->extension_data(\%extensions);
    102         $self->remaining_certdata($remdata);
    103 
    104         print "    Context:".$context."\n";
    105         print "    Certificate List Len:".$certlistlen."\n";
    106         print "    Certificate Len:".$certlen."\n";
    107         print "    Extensions Len:".$extensions_len."\n";
    108     } else {
    109         my ($hicertlistlen, $certlistlen) = unpack('Cn', $self->data);
    110         $certlistlen += ($hicertlistlen << 16);
    111 
    112         my $remdata = substr($self->data, 3);
    113 
    114         die "Invalid Certificate List length"
    115             if length($remdata) != $certlistlen;
    116 
    117         my ($hicertlen, $certlen) = unpack('Cn', $remdata);
    118         $certlen += ($hicertlen << 16);
    119 
    120         die "Certificate too long" if ($certlen + 3) > $certlistlen;
    121 
    122         $remdata = substr($remdata, 3);
    123 
    124         my $certdata = substr($remdata, 0, $certlen);
    125 
    126         $remdata = substr($remdata, $certlen);
    127 
    128         $self->first_certificate($certdata);
    129         $self->remaining_certdata($remdata);
    130 
    131         print "    Certificate List Len:".$certlistlen."\n";
    132         print "    Certificate Len:".$certlen."\n";
    133     }
    134 }
    135 
    136 #Reconstruct the on-the-wire message data following changes
    137 sub set_message_contents
    138 {
    139     my $self = shift;
    140     my $data;
    141     my $extensions = "";
    142 
    143     if (TLSProxy::Proxy->is_tls13()) {
    144         foreach my $key (keys %{$self->extension_data}) {
    145             my $extdata = ${$self->extension_data}{$key};
    146             $extensions .= pack("n", $key);
    147             $extensions .= pack("n", length($extdata));
    148             $extensions .= $extdata;
    149         }
    150         $data = pack('C', length($self->context()));
    151         $data .= $self->context;
    152         my $certlen = length($self->first_certificate);
    153         my $certlistlen = $certlen + length($extensions)
    154                           + length($self->remaining_certdata);
    155         my $hi = $certlistlen >> 16;
    156         $certlistlen = $certlistlen & 0xffff;
    157         $data .= pack('Cn', $hi, $certlistlen);
    158         $hi = $certlen >> 16;
    159         $certlen = $certlen & 0xffff;
    160         $data .= pack('Cn', $hi, $certlen);
    161         $data .= pack('n', length($extensions));
    162         $data .= $extensions;
    163         $data .= $self->remaining_certdata();
    164         $self->data($data);
    165     } else {
    166         my $certlen = length($self->first_certificate);
    167         my $certlistlen = $certlen + length($self->remaining_certdata);
    168         my $hi = $certlistlen >> 16;
    169         $certlistlen = $certlistlen & 0xffff;
    170         $data .= pack('Cn', $hi, $certlistlen);
    171         $hi = $certlen >> 16;
    172         $certlen = $certlen & 0xffff;
    173         $data .= pack('Cn', $hi, $certlen);
    174         $data .= $self->remaining_certdata();
    175         $self->data($data);
    176     }
    177 }
    178 
    179 #Read/write accessors
    180 sub context
    181 {
    182     my $self = shift;
    183     if (@_) {
    184       $self->{context} = shift;
    185     }
    186     return $self->{context};
    187 }
    188 sub first_certificate
    189 {
    190     my $self = shift;
    191     if (@_) {
    192       $self->{first_certificate} = shift;
    193     }
    194     return $self->{first_certificate};
    195 }
    196 sub remaining_certdata
    197 {
    198     my $self = shift;
    199     if (@_) {
    200       $self->{remaining_certdata} = shift;
    201     }
    202     return $self->{remaining_certdata};
    203 }
    204 sub extension_data
    205 {
    206     my $self = shift;
    207     if (@_) {
    208       $self->{extension_data} = shift;
    209     }
    210     return $self->{extension_data};
    211 }
    212 sub set_extension
    213 {
    214     my ($self, $ext_type, $ext_data) = @_;
    215     $self->{extension_data}{$ext_type} = $ext_data;
    216 }
    217 sub delete_extension
    218 {
    219     my ($self, $ext_type) = @_;
    220     delete $self->{extension_data}{$ext_type};
    221 }
    222 1;
    223