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