Update the key_share tests for HelloRetryRequest
[openssl.git] / util / TLSProxy / Message.pm
1 # Copyright 2016 The OpenSSL Project Authors. All Rights Reserved.
2 #
3 # Licensed under the OpenSSL license (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::Message;
11
12 use constant TLS_MESSAGE_HEADER_LENGTH => 4;
13
14 #Message types
15 use constant {
16     MT_HELLO_REQUEST => 0,
17     MT_CLIENT_HELLO => 1,
18     MT_SERVER_HELLO => 2,
19     MT_NEW_SESSION_TICKET => 4,
20     MT_HELLO_RETRY_REQUEST => 6,
21     MT_ENCRYPTED_EXTENSIONS => 8,
22     MT_CERTIFICATE => 11,
23     MT_SERVER_KEY_EXCHANGE => 12,
24     MT_CERTIFICATE_REQUEST => 13,
25     MT_SERVER_HELLO_DONE => 14,
26     MT_CERTIFICATE_VERIFY => 15,
27     MT_CLIENT_KEY_EXCHANGE => 16,
28     MT_FINISHED => 20,
29     MT_CERTIFICATE_STATUS => 22,
30     MT_NEXT_PROTO => 67
31 };
32
33 #Alert levels
34 use constant {
35     AL_LEVEL_WARN => 1,
36     AL_LEVEL_FATAL => 2
37 };
38
39 #Alert descriptions
40 use constant {
41     AL_DESC_CLOSE_NOTIFY => 0,
42     AL_DESC_UNEXPECTED_MESSAGE => 10,
43     AL_DESC_NO_RENEGOTIATION => 100
44 };
45
46 my %message_type = (
47     MT_HELLO_REQUEST, "HelloRequest",
48     MT_CLIENT_HELLO, "ClientHello",
49     MT_SERVER_HELLO, "ServerHello",
50     MT_NEW_SESSION_TICKET, "NewSessionTicket",
51     MT_HELLO_RETRY_REQUEST, "HelloRetryRequest",
52     MT_ENCRYPTED_EXTENSIONS, "EncryptedExtensions",
53     MT_CERTIFICATE, "Certificate",
54     MT_SERVER_KEY_EXCHANGE, "ServerKeyExchange",
55     MT_CERTIFICATE_REQUEST, "CertificateRequest",
56     MT_SERVER_HELLO_DONE, "ServerHelloDone",
57     MT_CERTIFICATE_VERIFY, "CertificateVerify",
58     MT_CLIENT_KEY_EXCHANGE, "ClientKeyExchange",
59     MT_FINISHED, "Finished",
60     MT_CERTIFICATE_STATUS, "CertificateStatus",
61     MT_NEXT_PROTO, "NextProto"
62 );
63
64 use constant {
65     EXT_SERVER_NAME => 0,
66     EXT_STATUS_REQUEST => 5,
67     EXT_SUPPORTED_GROUPS => 10,
68     EXT_EC_POINT_FORMATS => 11,
69     EXT_SRP => 12,
70     EXT_SIG_ALGS => 13,
71     EXT_USE_SRTP => 14,
72     EXT_ALPN => 16,
73     EXT_SCT => 18,
74     EXT_PADDING => 21,
75     EXT_ENCRYPT_THEN_MAC => 22,
76     EXT_EXTENDED_MASTER_SECRET => 23,
77     EXT_SESSION_TICKET => 35,
78     EXT_KEY_SHARE => 40,
79     EXT_PSK => 41,
80     EXT_SUPPORTED_VERSIONS => 43,
81     EXT_PSK_KEX_MODES => 45,
82     EXT_RENEGOTIATE => 65281,
83     EXT_NPN => 13172,
84     # This extension is an unofficial extension only ever written by OpenSSL
85     # (i.e. not read), and even then only when enabled. We use it to test
86     # handling of duplicate extensions.
87     EXT_DUPLICATE_EXTENSION => 0xfde8
88 };
89
90 use constant {
91     CIPHER_DHE_RSA_AES_128_SHA => 0x0033,
92     CIPHER_ADH_AES_128_SHA => 0x0034
93 };
94
95 my $payload = "";
96 my $messlen = -1;
97 my $mt;
98 my $startoffset = -1;
99 my $server = 0;
100 my $success = 0;
101 my $end = 0;
102 my @message_rec_list = ();
103 my @message_frag_lens = ();
104 my $ciphersuite = 0;
105 my $successondata = 0;
106
107 sub clear
108 {
109     $payload = "";
110     $messlen = -1;
111     $startoffset = -1;
112     $server = 0;
113     $success = 0;
114     $end = 0;
115     $successondata = 0;
116     @message_rec_list = ();
117     @message_frag_lens = ();
118 }
119
120 #Class method to extract messages from a record
121 sub get_messages
122 {
123     my $class = shift;
124     my $serverin = shift;
125     my $record = shift;
126     my @messages = ();
127     my $message;
128
129     @message_frag_lens = ();
130
131     if ($serverin != $server && length($payload) != 0) {
132         die "Changed peer, but we still have fragment data\n";
133     }
134     $server = $serverin;
135
136     if ($record->content_type == TLSProxy::Record::RT_CCS) {
137         if ($payload ne "") {
138             #We can't handle this yet
139             die "CCS received before message data complete\n";
140         }
141         if ($server) {
142             TLSProxy::Record->server_encrypting(1);
143         } else {
144             TLSProxy::Record->client_encrypting(1);
145         }
146     } elsif ($record->content_type == TLSProxy::Record::RT_HANDSHAKE) {
147         if ($record->len == 0 || $record->len_real == 0) {
148             print "  Message truncated\n";
149         } else {
150             my $recoffset = 0;
151
152             if (length $payload > 0) {
153                 #We are continuing processing a message started in a previous
154                 #record. Add this record to the list associated with this
155                 #message
156                 push @message_rec_list, $record;
157
158                 if ($messlen <= length($payload)) {
159                     #Shouldn't happen
160                     die "Internal error: invalid messlen: ".$messlen
161                         ." payload length:".length($payload)."\n";
162                 }
163                 if (length($payload) + $record->decrypt_len >= $messlen) {
164                     #We can complete the message with this record
165                     $recoffset = $messlen - length($payload);
166                     $payload .= substr($record->decrypt_data, 0, $recoffset);
167                     push @message_frag_lens, $recoffset;
168                     $message = create_message($server, $mt, $payload,
169                                               $startoffset);
170                     push @messages, $message;
171
172                     $payload = "";
173                 } else {
174                     #This is just part of the total message
175                     $payload .= $record->decrypt_data;
176                     $recoffset = $record->decrypt_len;
177                     push @message_frag_lens, $record->decrypt_len;
178                 }
179                 print "  Partial message data read: ".$recoffset." bytes\n";
180             }
181
182             while ($record->decrypt_len > $recoffset) {
183                 #We are at the start of a new message
184                 if ($record->decrypt_len - $recoffset < 4) {
185                     #Whilst technically probably valid we can't cope with this
186                     die "End of record in the middle of a message header\n";
187                 }
188                 @message_rec_list = ($record);
189                 my $lenhi;
190                 my $lenlo;
191                 ($mt, $lenhi, $lenlo) = unpack('CnC',
192                                                substr($record->decrypt_data,
193                                                       $recoffset));
194                 $messlen = ($lenhi << 8) | $lenlo;
195                 print "  Message type: $message_type{$mt}\n";
196                 print "  Message Length: $messlen\n";
197                 $startoffset = $recoffset;
198                 $recoffset += 4;
199                 $payload = "";
200                 
201                 if ($recoffset <= $record->decrypt_len) {
202                     #Some payload data is present in this record
203                     if ($record->decrypt_len - $recoffset >= $messlen) {
204                         #We can complete the message with this record
205                         $payload .= substr($record->decrypt_data, $recoffset,
206                                            $messlen);
207                         $recoffset += $messlen;
208                         push @message_frag_lens, $messlen;
209                         $message = create_message($server, $mt, $payload,
210                                                   $startoffset);
211                         push @messages, $message;
212
213                         $payload = "";
214                     } else {
215                         #This is just part of the total message
216                         $payload .= substr($record->decrypt_data, $recoffset,
217                                            $record->decrypt_len - $recoffset);
218                         $recoffset = $record->decrypt_len;
219                         push @message_frag_lens, $recoffset;
220                     }
221                 }
222             }
223         }
224     } elsif ($record->content_type == TLSProxy::Record::RT_APPLICATION_DATA) {
225         print "  [ENCRYPTED APPLICATION DATA]\n";
226         print "  [".$record->decrypt_data."]\n";
227
228         if ($successondata) {
229             $success = 1;
230             $end = 1;
231         }
232     } elsif ($record->content_type == TLSProxy::Record::RT_ALERT) {
233         my ($alertlev, $alertdesc) = unpack('CC', $record->decrypt_data);
234         #A CloseNotify from the client indicates we have finished successfully
235         #(we assume)
236         if (!$end && !$server && $alertlev == AL_LEVEL_WARN
237             && $alertdesc == AL_DESC_CLOSE_NOTIFY) {
238             $success = 1;
239         }
240         #All alerts end the test
241         $end = 1;
242     }
243
244     return @messages;
245 }
246
247 #Function to work out which sub-class we need to create and then
248 #construct it
249 sub create_message
250 {
251     my ($server, $mt, $data, $startoffset) = @_;
252     my $message;
253
254     #We only support ClientHello in this version...needs to be extended for
255     #others
256     if ($mt == MT_CLIENT_HELLO) {
257         $message = TLSProxy::ClientHello->new(
258             $server,
259             $data,
260             [@message_rec_list],
261             $startoffset,
262             [@message_frag_lens]
263         );
264         $message->parse();
265     } elsif ($mt == MT_SERVER_HELLO) {
266         $message = TLSProxy::ServerHello->new(
267             $server,
268             $data,
269             [@message_rec_list],
270             $startoffset,
271             [@message_frag_lens]
272         );
273         $message->parse();
274     } elsif ($mt == MT_ENCRYPTED_EXTENSIONS) {
275         $message = TLSProxy::EncryptedExtensions->new(
276             $server,
277             $data,
278             [@message_rec_list],
279             $startoffset,
280             [@message_frag_lens]
281         );
282         $message->parse();
283     } elsif ($mt == MT_CERTIFICATE) {
284         $message = TLSProxy::Certificate->new(
285             $server,
286             $data,
287             [@message_rec_list],
288             $startoffset,
289             [@message_frag_lens]
290         );
291         $message->parse();
292     } elsif ($mt == MT_CERTIFICATE_VERIFY) {
293         $message = TLSProxy::CertificateVerify->new(
294             $server,
295             $data,
296             [@message_rec_list],
297             $startoffset,
298             [@message_frag_lens]
299         );
300         $message->parse();
301     } elsif ($mt == MT_SERVER_KEY_EXCHANGE) {
302         $message = TLSProxy::ServerKeyExchange->new(
303             $server,
304             $data,
305             [@message_rec_list],
306             $startoffset,
307             [@message_frag_lens]
308         );
309         $message->parse();
310     } elsif ($mt == MT_NEW_SESSION_TICKET) {
311         $message = TLSProxy::NewSessionTicket->new(
312             $server,
313             $data,
314             [@message_rec_list],
315             $startoffset,
316             [@message_frag_lens]
317         );
318         $message->parse();
319     } else {
320         #Unknown message type
321         $message = TLSProxy::Message->new(
322             $server,
323             $mt,
324             $data,
325             [@message_rec_list],
326             $startoffset,
327             [@message_frag_lens]
328         );
329     }
330
331     return $message;
332 }
333
334 sub end
335 {
336     my $class = shift;
337     return $end;
338 }
339 sub success
340 {
341     my $class = shift;
342     return $success;
343 }
344 sub fail
345 {
346     my $class = shift;
347     return !$success && $end;
348 }
349 sub new
350 {
351     my $class = shift;
352     my ($server,
353         $mt,
354         $data,
355         $records,
356         $startoffset,
357         $message_frag_lens) = @_;
358     
359     my $self = {
360         server => $server,
361         data => $data,
362         records => $records,
363         mt => $mt,
364         startoffset => $startoffset,
365         message_frag_lens => $message_frag_lens
366     };
367
368     return bless $self, $class;
369 }
370
371 sub ciphersuite
372 {
373     my $class = shift;
374     if (@_) {
375       $ciphersuite = shift;
376     }
377     return $ciphersuite;
378 }
379
380 #Update all the underlying records with the modified data from this message
381 #Note: Only supports re-encrypting for TLSv1.3
382 sub repack
383 {
384     my $self = shift;
385     my $msgdata;
386
387     my $numrecs = $#{$self->records};
388
389     $self->set_message_contents();
390
391     my $lenhi;
392     my $lenlo;
393
394     $lenlo = length($self->data) & 0xff;
395     $lenhi = length($self->data) >> 8;
396     $msgdata = pack('CnC', $self->mt, $lenhi, $lenlo).$self->data;
397
398     if ($numrecs == 0) {
399         #The message is fully contained within one record
400         my ($rec) = @{$self->records};
401         my $recdata = $rec->decrypt_data;
402
403         my $old_length;
404
405         # We use empty message_frag_lens to indicates that pre-repacking,
406         # the message wasn't present. The first fragment length doesn't include
407         # the TLS header, so we need to check and compute the right length.
408         if (@{$self->message_frag_lens}) {
409             $old_length = ${$self->message_frag_lens}[0] +
410               TLS_MESSAGE_HEADER_LENGTH;
411         } else {
412             $old_length = 0;
413         }
414
415         my $prefix = substr($recdata, 0, $self->startoffset);
416         my $suffix = substr($recdata, $self->startoffset + $old_length);
417
418         $rec->decrypt_data($prefix.($msgdata).($suffix));
419         # TODO(openssl-team): don't keep explicit lengths.
420         # (If a length override is ever needed to construct invalid packets,
421         #  use an explicit override field instead.)
422         $rec->decrypt_len(length($rec->decrypt_data));
423         $rec->len($rec->len + length($msgdata) - $old_length);
424         # Only support re-encryption for TLSv1.3.
425         if (TLSProxy::Proxy->is_tls13() && $rec->encrypted()) {
426             #Add content type (1 byte) and 16 tag bytes
427             $rec->data($rec->decrypt_data
428                 .pack("C", TLSProxy::Record::RT_HANDSHAKE).("\0"x16));
429         } else {
430             $rec->data($rec->decrypt_data);
431         }
432
433         #Update the fragment len in case we changed it above
434         ${$self->message_frag_lens}[0] = length($msgdata)
435                                          - TLS_MESSAGE_HEADER_LENGTH;
436         return;
437     }
438
439     #Note we don't currently support changing a fragmented message length
440     my $recctr = 0;
441     my $datadone = 0;
442     foreach my $rec (@{$self->records}) {
443         my $recdata = $rec->decrypt_data;
444         if ($recctr == 0) {
445             #This is the first record
446             my $remainlen = length($recdata) - $self->startoffset;
447             $rec->data(substr($recdata, 0, $self->startoffset)
448                        .substr(($msgdata), 0, $remainlen));
449             $datadone += $remainlen;
450         } elsif ($recctr + 1 == $numrecs) {
451             #This is the last record
452             $rec->data(substr($msgdata, $datadone));
453         } else {
454             #This is a middle record
455             $rec->data(substr($msgdata, $datadone, length($rec->data)));
456             $datadone += length($rec->data);
457         }
458         $recctr++;
459     }
460 }
461
462 #To be overridden by sub-classes
463 sub set_message_contents
464 {
465 }
466
467 #Read only accessors
468 sub server
469 {
470     my $self = shift;
471     return $self->{server};
472 }
473
474 #Read/write accessors
475 sub mt
476 {
477     my $self = shift;
478     if (@_) {
479       $self->{mt} = shift;
480     }
481     return $self->{mt};
482 }
483 sub data
484 {
485     my $self = shift;
486     if (@_) {
487       $self->{data} = shift;
488     }
489     return $self->{data};
490 }
491 sub records
492 {
493     my $self = shift;
494     if (@_) {
495       $self->{records} = shift;
496     }
497     return $self->{records};
498 }
499 sub startoffset
500 {
501     my $self = shift;
502     if (@_) {
503       $self->{startoffset} = shift;
504     }
505     return $self->{startoffset};
506 }
507 sub message_frag_lens
508 {
509     my $self = shift;
510     if (@_) {
511       $self->{message_frag_lens} = shift;
512     }
513     return $self->{message_frag_lens};
514 }
515 sub encoded_length
516 {
517     my $self = shift;
518     return TLS_MESSAGE_HEADER_LENGTH + length($self->data);
519 }
520 sub successondata
521 {
522     my $class = shift;
523     if (@_) {
524         $successondata = shift;
525     }
526     return $successondata;
527 }
528 1;