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