Message.pm 17 KB

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