10a05173cSchristos# Copyright 2016-2021 The OpenSSL Project Authors. All Rights Reserved.
2162ae260Schristos#
3*66bae5e7Schristos# Licensed under the Apache License 2.0 (the "License").  You may not use
4162ae260Schristos# this file except in compliance with the License.  You can obtain a copy
5162ae260Schristos# in the file LICENSE in the source distribution or at
6162ae260Schristos# https://www.openssl.org/source/license.html
7162ae260Schristos
8162ae260Schristosuse strict;
9162ae260Schristos
10162ae260Schristospackage TLSProxy::Message;
11162ae260Schristos
1205901b04Schristosuse TLSProxy::Alert;
1305901b04Schristos
14162ae260Schristosuse constant TLS_MESSAGE_HEADER_LENGTH => 4;
15162ae260Schristos
16162ae260Schristos#Message types
17162ae260Schristosuse constant {
18162ae260Schristos    MT_HELLO_REQUEST => 0,
19162ae260Schristos    MT_CLIENT_HELLO => 1,
20162ae260Schristos    MT_SERVER_HELLO => 2,
21162ae260Schristos    MT_NEW_SESSION_TICKET => 4,
2205901b04Schristos    MT_ENCRYPTED_EXTENSIONS => 8,
23162ae260Schristos    MT_CERTIFICATE => 11,
24162ae260Schristos    MT_SERVER_KEY_EXCHANGE => 12,
25162ae260Schristos    MT_CERTIFICATE_REQUEST => 13,
26162ae260Schristos    MT_SERVER_HELLO_DONE => 14,
27162ae260Schristos    MT_CERTIFICATE_VERIFY => 15,
28162ae260Schristos    MT_CLIENT_KEY_EXCHANGE => 16,
29162ae260Schristos    MT_FINISHED => 20,
30162ae260Schristos    MT_CERTIFICATE_STATUS => 22,
31162ae260Schristos    MT_NEXT_PROTO => 67
32162ae260Schristos};
33162ae260Schristos
34162ae260Schristos#Alert levels
35162ae260Schristosuse constant {
36162ae260Schristos    AL_LEVEL_WARN => 1,
37162ae260Schristos    AL_LEVEL_FATAL => 2
38162ae260Schristos};
39162ae260Schristos
40162ae260Schristos#Alert descriptions
41162ae260Schristosuse constant {
42162ae260Schristos    AL_DESC_CLOSE_NOTIFY => 0,
43162ae260Schristos    AL_DESC_UNEXPECTED_MESSAGE => 10,
4405901b04Schristos    AL_DESC_ILLEGAL_PARAMETER => 47,
45162ae260Schristos    AL_DESC_NO_RENEGOTIATION => 100
46162ae260Schristos};
47162ae260Schristos
48162ae260Schristosmy %message_type = (
49162ae260Schristos    MT_HELLO_REQUEST, "HelloRequest",
50162ae260Schristos    MT_CLIENT_HELLO, "ClientHello",
51162ae260Schristos    MT_SERVER_HELLO, "ServerHello",
52162ae260Schristos    MT_NEW_SESSION_TICKET, "NewSessionTicket",
5305901b04Schristos    MT_ENCRYPTED_EXTENSIONS, "EncryptedExtensions",
54162ae260Schristos    MT_CERTIFICATE, "Certificate",
55162ae260Schristos    MT_SERVER_KEY_EXCHANGE, "ServerKeyExchange",
56162ae260Schristos    MT_CERTIFICATE_REQUEST, "CertificateRequest",
57162ae260Schristos    MT_SERVER_HELLO_DONE, "ServerHelloDone",
58162ae260Schristos    MT_CERTIFICATE_VERIFY, "CertificateVerify",
59162ae260Schristos    MT_CLIENT_KEY_EXCHANGE, "ClientKeyExchange",
60162ae260Schristos    MT_FINISHED, "Finished",
61162ae260Schristos    MT_CERTIFICATE_STATUS, "CertificateStatus",
62162ae260Schristos    MT_NEXT_PROTO, "NextProto"
63162ae260Schristos);
64162ae260Schristos
65162ae260Schristosuse constant {
6605901b04Schristos    EXT_SERVER_NAME => 0,
6705901b04Schristos    EXT_MAX_FRAGMENT_LENGTH => 1,
68162ae260Schristos    EXT_STATUS_REQUEST => 5,
6905901b04Schristos    EXT_SUPPORTED_GROUPS => 10,
7005901b04Schristos    EXT_EC_POINT_FORMATS => 11,
7105901b04Schristos    EXT_SRP => 12,
7205901b04Schristos    EXT_SIG_ALGS => 13,
7305901b04Schristos    EXT_USE_SRTP => 14,
7405901b04Schristos    EXT_ALPN => 16,
7505901b04Schristos    EXT_SCT => 18,
7605901b04Schristos    EXT_PADDING => 21,
77162ae260Schristos    EXT_ENCRYPT_THEN_MAC => 22,
78162ae260Schristos    EXT_EXTENDED_MASTER_SECRET => 23,
79162ae260Schristos    EXT_SESSION_TICKET => 35,
8005901b04Schristos    EXT_KEY_SHARE => 51,
8105901b04Schristos    EXT_PSK => 41,
8205901b04Schristos    EXT_SUPPORTED_VERSIONS => 43,
8305901b04Schristos    EXT_COOKIE => 44,
8405901b04Schristos    EXT_PSK_KEX_MODES => 45,
8505901b04Schristos    EXT_POST_HANDSHAKE_AUTH => 49,
8605901b04Schristos    EXT_SIG_ALGS_CERT => 50,
8705901b04Schristos    EXT_RENEGOTIATE => 65281,
8805901b04Schristos    EXT_NPN => 13172,
89fa9f2818Schristos    EXT_CRYPTOPRO_BUG_EXTENSION => 0xfde8,
9005901b04Schristos    EXT_UNKNOWN => 0xfffe,
9105901b04Schristos    #Unknown extension that should appear last
9205901b04Schristos    EXT_FORCE_LAST => 0xffff
9305901b04Schristos};
9405901b04Schristos
9505901b04Schristos# SignatureScheme of TLS 1.3 from:
9605901b04Schristos# https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-signaturescheme
9705901b04Schristos# We have to manually grab the SHA224 equivalents from the old registry
9805901b04Schristosuse constant {
9905901b04Schristos    SIG_ALG_RSA_PKCS1_SHA256 => 0x0401,
10005901b04Schristos    SIG_ALG_RSA_PKCS1_SHA384 => 0x0501,
10105901b04Schristos    SIG_ALG_RSA_PKCS1_SHA512 => 0x0601,
10205901b04Schristos    SIG_ALG_ECDSA_SECP256R1_SHA256 => 0x0403,
10305901b04Schristos    SIG_ALG_ECDSA_SECP384R1_SHA384 => 0x0503,
10405901b04Schristos    SIG_ALG_ECDSA_SECP521R1_SHA512 => 0x0603,
10505901b04Schristos    SIG_ALG_RSA_PSS_RSAE_SHA256 => 0x0804,
10605901b04Schristos    SIG_ALG_RSA_PSS_RSAE_SHA384 => 0x0805,
10705901b04Schristos    SIG_ALG_RSA_PSS_RSAE_SHA512 => 0x0806,
10805901b04Schristos    SIG_ALG_ED25519 => 0x0807,
10905901b04Schristos    SIG_ALG_ED448 => 0x0808,
11005901b04Schristos    SIG_ALG_RSA_PSS_PSS_SHA256 => 0x0809,
11105901b04Schristos    SIG_ALG_RSA_PSS_PSS_SHA384 => 0x080a,
11205901b04Schristos    SIG_ALG_RSA_PSS_PSS_SHA512 => 0x080b,
11305901b04Schristos    SIG_ALG_RSA_PKCS1_SHA1 => 0x0201,
11405901b04Schristos    SIG_ALG_ECDSA_SHA1 => 0x0203,
11505901b04Schristos    SIG_ALG_DSA_SHA1 => 0x0202,
11605901b04Schristos    SIG_ALG_DSA_SHA256 => 0x0402,
11705901b04Schristos    SIG_ALG_DSA_SHA384 => 0x0502,
11805901b04Schristos    SIG_ALG_DSA_SHA512 => 0x0602,
11905901b04Schristos    OSSL_SIG_ALG_RSA_PKCS1_SHA224 => 0x0301,
12005901b04Schristos    OSSL_SIG_ALG_DSA_SHA224 => 0x0302,
12105901b04Schristos    OSSL_SIG_ALG_ECDSA_SHA224 => 0x0303
12205901b04Schristos};
12305901b04Schristos
12405901b04Schristosuse constant {
12505901b04Schristos    CIPHER_RSA_WITH_AES_128_CBC_SHA => 0x002f,
12605901b04Schristos    CIPHER_DHE_RSA_AES_128_SHA => 0x0033,
12705901b04Schristos    CIPHER_ADH_AES_128_SHA => 0x0034,
12805901b04Schristos    CIPHER_TLS13_AES_128_GCM_SHA256 => 0x1301,
12905901b04Schristos    CIPHER_TLS13_AES_256_GCM_SHA384 => 0x1302
130162ae260Schristos};
131162ae260Schristos
132033a4089Schristosuse constant {
133033a4089Schristos    CLIENT => 0,
134033a4089Schristos    SERVER => 1
135033a4089Schristos};
136033a4089Schristos
137162ae260Schristosmy $payload = "";
138162ae260Schristosmy $messlen = -1;
139162ae260Schristosmy $mt;
140162ae260Schristosmy $startoffset = -1;
141162ae260Schristosmy $server = 0;
142162ae260Schristosmy $success = 0;
143162ae260Schristosmy $end = 0;
144162ae260Schristosmy @message_rec_list = ();
145162ae260Schristosmy @message_frag_lens = ();
146162ae260Schristosmy $ciphersuite = 0;
14705901b04Schristosmy $successondata = 0;
14805901b04Schristosmy $alert;
149162ae260Schristos
150162ae260Schristossub clear
151162ae260Schristos{
152162ae260Schristos    $payload = "";
153162ae260Schristos    $messlen = -1;
154162ae260Schristos    $startoffset = -1;
155162ae260Schristos    $server = 0;
156162ae260Schristos    $success = 0;
157162ae260Schristos    $end = 0;
15805901b04Schristos    $successondata = 0;
159162ae260Schristos    @message_rec_list = ();
160162ae260Schristos    @message_frag_lens = ();
16105901b04Schristos    $alert = undef;
162162ae260Schristos}
163162ae260Schristos
164162ae260Schristos#Class method to extract messages from a record
165162ae260Schristossub get_messages
166162ae260Schristos{
167162ae260Schristos    my $class = shift;
168162ae260Schristos    my $serverin = shift;
169162ae260Schristos    my $record = shift;
170162ae260Schristos    my @messages = ();
171162ae260Schristos    my $message;
172162ae260Schristos
173162ae260Schristos    @message_frag_lens = ();
174162ae260Schristos
175162ae260Schristos    if ($serverin != $server && length($payload) != 0) {
176162ae260Schristos        die "Changed peer, but we still have fragment data\n";
177162ae260Schristos    }
178162ae260Schristos    $server = $serverin;
179162ae260Schristos
180162ae260Schristos    if ($record->content_type == TLSProxy::Record::RT_CCS) {
181162ae260Schristos        if ($payload ne "") {
182162ae260Schristos            #We can't handle this yet
183162ae260Schristos            die "CCS received before message data complete\n";
184162ae260Schristos        }
18505901b04Schristos        if (!TLSProxy::Proxy->is_tls13()) {
186162ae260Schristos            if ($server) {
18705901b04Schristos                TLSProxy::Record->server_encrypting(1);
188162ae260Schristos            } else {
18905901b04Schristos                TLSProxy::Record->client_encrypting(1);
19005901b04Schristos            }
191162ae260Schristos        }
192162ae260Schristos    } elsif ($record->content_type == TLSProxy::Record::RT_HANDSHAKE) {
193162ae260Schristos        if ($record->len == 0 || $record->len_real == 0) {
194162ae260Schristos            print "  Message truncated\n";
195162ae260Schristos        } else {
196162ae260Schristos            my $recoffset = 0;
197162ae260Schristos
198162ae260Schristos            if (length $payload > 0) {
199162ae260Schristos                #We are continuing processing a message started in a previous
200162ae260Schristos                #record. Add this record to the list associated with this
201162ae260Schristos                #message
202162ae260Schristos                push @message_rec_list, $record;
203162ae260Schristos
204162ae260Schristos                if ($messlen <= length($payload)) {
205162ae260Schristos                    #Shouldn't happen
206162ae260Schristos                    die "Internal error: invalid messlen: ".$messlen
207162ae260Schristos                        ." payload length:".length($payload)."\n";
208162ae260Schristos                }
209162ae260Schristos                if (length($payload) + $record->decrypt_len >= $messlen) {
210162ae260Schristos                    #We can complete the message with this record
211162ae260Schristos                    $recoffset = $messlen - length($payload);
212162ae260Schristos                    $payload .= substr($record->decrypt_data, 0, $recoffset);
213162ae260Schristos                    push @message_frag_lens, $recoffset;
214162ae260Schristos                    $message = create_message($server, $mt, $payload,
215162ae260Schristos                                              $startoffset);
216162ae260Schristos                    push @messages, $message;
217162ae260Schristos
218162ae260Schristos                    $payload = "";
219162ae260Schristos                } else {
220162ae260Schristos                    #This is just part of the total message
221162ae260Schristos                    $payload .= $record->decrypt_data;
222162ae260Schristos                    $recoffset = $record->decrypt_len;
223162ae260Schristos                    push @message_frag_lens, $record->decrypt_len;
224162ae260Schristos                }
225162ae260Schristos                print "  Partial message data read: ".$recoffset." bytes\n";
226162ae260Schristos            }
227162ae260Schristos
228162ae260Schristos            while ($record->decrypt_len > $recoffset) {
229162ae260Schristos                #We are at the start of a new message
230162ae260Schristos                if ($record->decrypt_len - $recoffset < 4) {
231162ae260Schristos                    #Whilst technically probably valid we can't cope with this
232162ae260Schristos                    die "End of record in the middle of a message header\n";
233162ae260Schristos                }
234162ae260Schristos                @message_rec_list = ($record);
235162ae260Schristos                my $lenhi;
236162ae260Schristos                my $lenlo;
237162ae260Schristos                ($mt, $lenhi, $lenlo) = unpack('CnC',
238162ae260Schristos                                               substr($record->decrypt_data,
239162ae260Schristos                                                      $recoffset));
240162ae260Schristos                $messlen = ($lenhi << 8) | $lenlo;
241162ae260Schristos                print "  Message type: $message_type{$mt}\n";
242162ae260Schristos                print "  Message Length: $messlen\n";
243162ae260Schristos                $startoffset = $recoffset;
244162ae260Schristos                $recoffset += 4;
245162ae260Schristos                $payload = "";
246162ae260Schristos
247162ae260Schristos                if ($recoffset <= $record->decrypt_len) {
248162ae260Schristos                    #Some payload data is present in this record
249162ae260Schristos                    if ($record->decrypt_len - $recoffset >= $messlen) {
250162ae260Schristos                        #We can complete the message with this record
251162ae260Schristos                        $payload .= substr($record->decrypt_data, $recoffset,
252162ae260Schristos                                           $messlen);
253162ae260Schristos                        $recoffset += $messlen;
254162ae260Schristos                        push @message_frag_lens, $messlen;
255162ae260Schristos                        $message = create_message($server, $mt, $payload,
256162ae260Schristos                                                  $startoffset);
257162ae260Schristos                        push @messages, $message;
258162ae260Schristos
259162ae260Schristos                        $payload = "";
260162ae260Schristos                    } else {
261162ae260Schristos                        #This is just part of the total message
262162ae260Schristos                        $payload .= substr($record->decrypt_data, $recoffset,
263162ae260Schristos                                           $record->decrypt_len - $recoffset);
264162ae260Schristos                        $recoffset = $record->decrypt_len;
265162ae260Schristos                        push @message_frag_lens, $recoffset;
266162ae260Schristos                    }
267162ae260Schristos                }
268162ae260Schristos            }
269162ae260Schristos        }
270162ae260Schristos    } elsif ($record->content_type == TLSProxy::Record::RT_APPLICATION_DATA) {
271162ae260Schristos        print "  [ENCRYPTED APPLICATION DATA]\n";
272162ae260Schristos        print "  [".$record->decrypt_data."]\n";
27305901b04Schristos
27405901b04Schristos        if ($successondata) {
27505901b04Schristos            $success = 1;
27605901b04Schristos            $end = 1;
27705901b04Schristos        }
278162ae260Schristos    } elsif ($record->content_type == TLSProxy::Record::RT_ALERT) {
279162ae260Schristos        my ($alertlev, $alertdesc) = unpack('CC', $record->decrypt_data);
28005901b04Schristos        print "  [$alertlev, $alertdesc]\n";
281162ae260Schristos        #A CloseNotify from the client indicates we have finished successfully
282162ae260Schristos        #(we assume)
283162ae260Schristos        if (!$end && !$server && $alertlev == AL_LEVEL_WARN
284162ae260Schristos            && $alertdesc == AL_DESC_CLOSE_NOTIFY) {
285162ae260Schristos            $success = 1;
286162ae260Schristos        }
28705901b04Schristos        #Fatal or close notify alerts end the test
28805901b04Schristos        if ($alertlev == AL_LEVEL_FATAL || $alertdesc == AL_DESC_CLOSE_NOTIFY) {
289162ae260Schristos            $end = 1;
290162ae260Schristos        }
29105901b04Schristos        $alert = TLSProxy::Alert->new(
29205901b04Schristos            $server,
29305901b04Schristos            $record->encrypted,
29405901b04Schristos            $alertlev,
29505901b04Schristos            $alertdesc);
29605901b04Schristos    }
297162ae260Schristos
298162ae260Schristos    return @messages;
299162ae260Schristos}
300162ae260Schristos
301162ae260Schristos#Function to work out which sub-class we need to create and then
302162ae260Schristos#construct it
303162ae260Schristossub create_message
304162ae260Schristos{
305162ae260Schristos    my ($server, $mt, $data, $startoffset) = @_;
306162ae260Schristos    my $message;
307162ae260Schristos
308162ae260Schristos    #We only support ClientHello in this version...needs to be extended for
309162ae260Schristos    #others
310162ae260Schristos    if ($mt == MT_CLIENT_HELLO) {
311162ae260Schristos        $message = TLSProxy::ClientHello->new(
312162ae260Schristos            $server,
313162ae260Schristos            $data,
314162ae260Schristos            [@message_rec_list],
315162ae260Schristos            $startoffset,
316162ae260Schristos            [@message_frag_lens]
317162ae260Schristos        );
318162ae260Schristos        $message->parse();
319162ae260Schristos    } elsif ($mt == MT_SERVER_HELLO) {
320162ae260Schristos        $message = TLSProxy::ServerHello->new(
321162ae260Schristos            $server,
322162ae260Schristos            $data,
323162ae260Schristos            [@message_rec_list],
324162ae260Schristos            $startoffset,
325162ae260Schristos            [@message_frag_lens]
326162ae260Schristos        );
327162ae260Schristos        $message->parse();
32805901b04Schristos    } elsif ($mt == MT_ENCRYPTED_EXTENSIONS) {
32905901b04Schristos        $message = TLSProxy::EncryptedExtensions->new(
33005901b04Schristos            $server,
33105901b04Schristos            $data,
33205901b04Schristos            [@message_rec_list],
33305901b04Schristos            $startoffset,
33405901b04Schristos            [@message_frag_lens]
33505901b04Schristos        );
33605901b04Schristos        $message->parse();
33705901b04Schristos    } elsif ($mt == MT_CERTIFICATE) {
33805901b04Schristos        $message = TLSProxy::Certificate->new(
33905901b04Schristos            $server,
34005901b04Schristos            $data,
34105901b04Schristos            [@message_rec_list],
34205901b04Schristos            $startoffset,
34305901b04Schristos            [@message_frag_lens]
34405901b04Schristos        );
34505901b04Schristos        $message->parse();
346033a4089Schristos    } elsif ($mt == MT_CERTIFICATE_REQUEST) {
347033a4089Schristos        $message = TLSProxy::CertificateRequest->new(
348033a4089Schristos            $server,
349033a4089Schristos            $data,
350033a4089Schristos            [@message_rec_list],
351033a4089Schristos            $startoffset,
352033a4089Schristos            [@message_frag_lens]
353033a4089Schristos        );
354033a4089Schristos        $message->parse();
35505901b04Schristos    } elsif ($mt == MT_CERTIFICATE_VERIFY) {
35605901b04Schristos        $message = TLSProxy::CertificateVerify->new(
35705901b04Schristos            $server,
35805901b04Schristos            $data,
35905901b04Schristos            [@message_rec_list],
36005901b04Schristos            $startoffset,
36105901b04Schristos            [@message_frag_lens]
36205901b04Schristos        );
36305901b04Schristos        $message->parse();
364162ae260Schristos    } elsif ($mt == MT_SERVER_KEY_EXCHANGE) {
365162ae260Schristos        $message = TLSProxy::ServerKeyExchange->new(
366162ae260Schristos            $server,
367162ae260Schristos            $data,
368162ae260Schristos            [@message_rec_list],
369162ae260Schristos            $startoffset,
370162ae260Schristos            [@message_frag_lens]
371162ae260Schristos        );
372162ae260Schristos        $message->parse();
373162ae260Schristos    } elsif ($mt == MT_NEW_SESSION_TICKET) {
374162ae260Schristos        $message = TLSProxy::NewSessionTicket->new(
375162ae260Schristos            $server,
376162ae260Schristos            $data,
377162ae260Schristos            [@message_rec_list],
378162ae260Schristos            $startoffset,
379162ae260Schristos            [@message_frag_lens]
380162ae260Schristos        );
381162ae260Schristos        $message->parse();
382162ae260Schristos    } else {
383162ae260Schristos        #Unknown message type
384162ae260Schristos        $message = TLSProxy::Message->new(
385162ae260Schristos            $server,
386162ae260Schristos            $mt,
387162ae260Schristos            $data,
388162ae260Schristos            [@message_rec_list],
389162ae260Schristos            $startoffset,
390162ae260Schristos            [@message_frag_lens]
391162ae260Schristos        );
392162ae260Schristos    }
393162ae260Schristos
394162ae260Schristos    return $message;
395162ae260Schristos}
396162ae260Schristos
397162ae260Schristossub end
398162ae260Schristos{
399162ae260Schristos    my $class = shift;
400162ae260Schristos    return $end;
401162ae260Schristos}
402162ae260Schristossub success
403162ae260Schristos{
404162ae260Schristos    my $class = shift;
405162ae260Schristos    return $success;
406162ae260Schristos}
407162ae260Schristossub fail
408162ae260Schristos{
409162ae260Schristos    my $class = shift;
410162ae260Schristos    return !$success && $end;
411162ae260Schristos}
41205901b04Schristos
41305901b04Schristossub alert
41405901b04Schristos{
41505901b04Schristos    return $alert;
41605901b04Schristos}
41705901b04Schristos
418162ae260Schristossub new
419162ae260Schristos{
420162ae260Schristos    my $class = shift;
421162ae260Schristos    my ($server,
422162ae260Schristos        $mt,
423162ae260Schristos        $data,
424162ae260Schristos        $records,
425162ae260Schristos        $startoffset,
426162ae260Schristos        $message_frag_lens) = @_;
427162ae260Schristos
428162ae260Schristos    my $self = {
429162ae260Schristos        server => $server,
430162ae260Schristos        data => $data,
431162ae260Schristos        records => $records,
432162ae260Schristos        mt => $mt,
433162ae260Schristos        startoffset => $startoffset,
434fa9f2818Schristos        message_frag_lens => $message_frag_lens,
435fa9f2818Schristos        dupext => -1
436162ae260Schristos    };
437162ae260Schristos
438162ae260Schristos    return bless $self, $class;
439162ae260Schristos}
440162ae260Schristos
441162ae260Schristossub ciphersuite
442162ae260Schristos{
443162ae260Schristos    my $class = shift;
444162ae260Schristos    if (@_) {
445162ae260Schristos      $ciphersuite = shift;
446162ae260Schristos    }
447162ae260Schristos    return $ciphersuite;
448162ae260Schristos}
449162ae260Schristos
450162ae260Schristos#Update all the underlying records with the modified data from this message
4510a05173cSchristos#Note: Only supports TLSv1.3 and ETM encryption
452162ae260Schristossub repack
453162ae260Schristos{
454162ae260Schristos    my $self = shift;
455162ae260Schristos    my $msgdata;
456162ae260Schristos
457162ae260Schristos    my $numrecs = $#{$self->records};
458162ae260Schristos
459162ae260Schristos    $self->set_message_contents();
460162ae260Schristos
461162ae260Schristos    my $lenhi;
462162ae260Schristos    my $lenlo;
463162ae260Schristos
464162ae260Schristos    $lenlo = length($self->data) & 0xff;
465162ae260Schristos    $lenhi = length($self->data) >> 8;
466162ae260Schristos    $msgdata = pack('CnC', $self->mt, $lenhi, $lenlo).$self->data;
467162ae260Schristos
468162ae260Schristos    if ($numrecs == 0) {
469162ae260Schristos        #The message is fully contained within one record
470162ae260Schristos        my ($rec) = @{$self->records};
471162ae260Schristos        my $recdata = $rec->decrypt_data;
472162ae260Schristos
473162ae260Schristos        my $old_length;
474162ae260Schristos
475162ae260Schristos        # We use empty message_frag_lens to indicates that pre-repacking,
476162ae260Schristos        # the message wasn't present. The first fragment length doesn't include
477162ae260Schristos        # the TLS header, so we need to check and compute the right length.
478162ae260Schristos        if (@{$self->message_frag_lens}) {
479162ae260Schristos            $old_length = ${$self->message_frag_lens}[0] +
480162ae260Schristos              TLS_MESSAGE_HEADER_LENGTH;
481162ae260Schristos        } else {
482162ae260Schristos            $old_length = 0;
483162ae260Schristos        }
484162ae260Schristos
485162ae260Schristos        my $prefix = substr($recdata, 0, $self->startoffset);
486162ae260Schristos        my $suffix = substr($recdata, $self->startoffset + $old_length);
487162ae260Schristos
488162ae260Schristos        $rec->decrypt_data($prefix.($msgdata).($suffix));
489162ae260Schristos        # TODO(openssl-team): don't keep explicit lengths.
490162ae260Schristos        # (If a length override is ever needed to construct invalid packets,
491162ae260Schristos        #  use an explicit override field instead.)
492162ae260Schristos        $rec->decrypt_len(length($rec->decrypt_data));
4930a05173cSchristos        # Only support re-encryption for TLSv1.3 and ETM.
4940a05173cSchristos        if ($rec->encrypted()) {
4950a05173cSchristos            if (TLSProxy::Proxy->is_tls13()) {
49605901b04Schristos                #Add content type (1 byte) and 16 tag bytes
49705901b04Schristos                $rec->data($rec->decrypt_data
49805901b04Schristos                    .pack("C", TLSProxy::Record::RT_HANDSHAKE).("\0"x16));
4990a05173cSchristos            } elsif ($rec->etm()) {
5000a05173cSchristos                my $data = $rec->decrypt_data;
5010a05173cSchristos                #Add padding
5020a05173cSchristos                my $padval = length($data) % 16;
5030a05173cSchristos                $padval = 15 - $padval;
5040a05173cSchristos                for (0..$padval) {
5050a05173cSchristos                    $data .= pack("C", $padval);
5060a05173cSchristos                }
5070a05173cSchristos
5080a05173cSchristos                #Add MAC. Assumed to be 20 bytes
5090a05173cSchristos                foreach my $macval (0..19) {
5100a05173cSchristos                    $data .= pack("C", $macval);
5110a05173cSchristos                }
5120a05173cSchristos
5130a05173cSchristos                if ($rec->version() >= TLSProxy::Record::VERS_TLS_1_1) {
5140a05173cSchristos                    #Explicit IV
5150a05173cSchristos                    $data = ("\0"x16).$data;
5160a05173cSchristos                }
5170a05173cSchristos                $rec->data($data);
5180a05173cSchristos            } else {
5190a05173cSchristos                die "Unsupported encryption: No ETM";
5200a05173cSchristos            }
52105901b04Schristos        } else {
522162ae260Schristos            $rec->data($rec->decrypt_data);
52305901b04Schristos        }
5240a05173cSchristos        $rec->len(length($rec->data));
525162ae260Schristos
526162ae260Schristos        #Update the fragment len in case we changed it above
527162ae260Schristos        ${$self->message_frag_lens}[0] = length($msgdata)
528162ae260Schristos                                         - TLS_MESSAGE_HEADER_LENGTH;
529162ae260Schristos        return;
530162ae260Schristos    }
531162ae260Schristos
532162ae260Schristos    #Note we don't currently support changing a fragmented message length
533162ae260Schristos    my $recctr = 0;
534162ae260Schristos    my $datadone = 0;
535162ae260Schristos    foreach my $rec (@{$self->records}) {
536162ae260Schristos        my $recdata = $rec->decrypt_data;
537162ae260Schristos        if ($recctr == 0) {
538162ae260Schristos            #This is the first record
539162ae260Schristos            my $remainlen = length($recdata) - $self->startoffset;
540162ae260Schristos            $rec->data(substr($recdata, 0, $self->startoffset)
541162ae260Schristos                       .substr(($msgdata), 0, $remainlen));
542162ae260Schristos            $datadone += $remainlen;
543162ae260Schristos        } elsif ($recctr + 1 == $numrecs) {
544162ae260Schristos            #This is the last record
545162ae260Schristos            $rec->data(substr($msgdata, $datadone));
546162ae260Schristos        } else {
547162ae260Schristos            #This is a middle record
548162ae260Schristos            $rec->data(substr($msgdata, $datadone, length($rec->data)));
549162ae260Schristos            $datadone += length($rec->data);
550162ae260Schristos        }
551162ae260Schristos        $recctr++;
552162ae260Schristos    }
553162ae260Schristos}
554162ae260Schristos
555162ae260Schristos#To be overridden by sub-classes
556162ae260Schristossub set_message_contents
557162ae260Schristos{
558162ae260Schristos}
559162ae260Schristos
560162ae260Schristos#Read only accessors
561162ae260Schristossub server
562162ae260Schristos{
563162ae260Schristos    my $self = shift;
564162ae260Schristos    return $self->{server};
565162ae260Schristos}
566162ae260Schristos
567162ae260Schristos#Read/write accessors
568162ae260Schristossub mt
569162ae260Schristos{
570162ae260Schristos    my $self = shift;
571162ae260Schristos    if (@_) {
572162ae260Schristos      $self->{mt} = shift;
573162ae260Schristos    }
574162ae260Schristos    return $self->{mt};
575162ae260Schristos}
576162ae260Schristossub data
577162ae260Schristos{
578162ae260Schristos    my $self = shift;
579162ae260Schristos    if (@_) {
580162ae260Schristos      $self->{data} = shift;
581162ae260Schristos    }
582162ae260Schristos    return $self->{data};
583162ae260Schristos}
584162ae260Schristossub records
585162ae260Schristos{
586162ae260Schristos    my $self = shift;
587162ae260Schristos    if (@_) {
588162ae260Schristos      $self->{records} = shift;
589162ae260Schristos    }
590162ae260Schristos    return $self->{records};
591162ae260Schristos}
592162ae260Schristossub startoffset
593162ae260Schristos{
594162ae260Schristos    my $self = shift;
595162ae260Schristos    if (@_) {
596162ae260Schristos      $self->{startoffset} = shift;
597162ae260Schristos    }
598162ae260Schristos    return $self->{startoffset};
599162ae260Schristos}
600162ae260Schristossub message_frag_lens
601162ae260Schristos{
602162ae260Schristos    my $self = shift;
603162ae260Schristos    if (@_) {
604162ae260Schristos      $self->{message_frag_lens} = shift;
605162ae260Schristos    }
606162ae260Schristos    return $self->{message_frag_lens};
607162ae260Schristos}
608162ae260Schristossub encoded_length
609162ae260Schristos{
610162ae260Schristos    my $self = shift;
611162ae260Schristos    return TLS_MESSAGE_HEADER_LENGTH + length($self->data);
612162ae260Schristos}
613fa9f2818Schristossub dupext
614fa9f2818Schristos{
615fa9f2818Schristos    my $self = shift;
616fa9f2818Schristos    if (@_) {
617fa9f2818Schristos        $self->{dupext} = shift;
618fa9f2818Schristos    }
619fa9f2818Schristos    return $self->{dupext};
620fa9f2818Schristos}
62105901b04Schristossub successondata
62205901b04Schristos{
62305901b04Schristos    my $class = shift;
62405901b04Schristos    if (@_) {
62505901b04Schristos        $successondata = shift;
62605901b04Schristos    }
62705901b04Schristos    return $successondata;
62805901b04Schristos}
629162ae260Schristos1;
630