70-test_sslrecords.t 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866
  1. #! /usr/bin/env perl
  2. # Copyright 2016-2024 The OpenSSL Project Authors. All Rights Reserved.
  3. #
  4. # Licensed under the Apache License 2.0 (the "License"). You may not use
  5. # this file except in compliance with the License. You can obtain a copy
  6. # in the file LICENSE in the source distribution or at
  7. # https://www.openssl.org/source/license.html
  8. use strict;
  9. use feature 'state';
  10. use OpenSSL::Test qw/:DEFAULT cmdstr srctop_file bldtop_dir/;
  11. use OpenSSL::Test::Utils;
  12. use TLSProxy::Proxy;
  13. use TLSProxy::Message;
  14. my $test_name = "test_sslrecords";
  15. setup($test_name);
  16. plan skip_all => "TLSProxy isn't usable on $^O"
  17. if $^O =~ /^(VMS)$/;
  18. plan skip_all => "$test_name needs the dynamic engine feature enabled"
  19. if disabled("engine") || disabled("dynamic-engine");
  20. plan skip_all => "$test_name needs the sock feature enabled"
  21. if disabled("sock");
  22. my $inject_recs_num = undef;
  23. my $content_type = undef;
  24. my $boundary_test_type = undef;
  25. my $fatal_alert = undef; # set by filters at expected fatal alerts
  26. my $sslv2testtype = undef;
  27. my $proxy_start_success = 0;
  28. plan tests => 42;
  29. SKIP: {
  30. skip "TLS 1.2 is disabled", 21 if disabled("tls1_2");
  31. # Run tests with TLS
  32. run_tests(0);
  33. }
  34. SKIP: {
  35. skip "DTLS 1.2 is disabled", 21 if disabled("dtls1_2");
  36. skip "DTLSProxy does not work on Windows", 21 if $^O =~ /^(MSWin32)$/;
  37. run_tests(1);
  38. }
  39. sub run_tests
  40. {
  41. my $run_test_as_dtls = shift;
  42. my $proxy;
  43. if ($run_test_as_dtls == 1) {
  44. $proxy = TLSProxy::Proxy->new_dtls(
  45. \&add_empty_recs_filter,
  46. cmdstr(app([ "openssl" ]), display => 1),
  47. srctop_file("apps", "server.pem"),
  48. (!$ENV{HARNESS_ACTIVE} || $ENV{HARNESS_VERBOSE})
  49. );
  50. } else {
  51. $proxy = TLSProxy::Proxy->new(
  52. \&add_empty_recs_filter,
  53. cmdstr(app([ "openssl" ]), display => 1),
  54. srctop_file("apps", "server.pem"),
  55. (!$ENV{HARNESS_ACTIVE} || $ENV{HARNESS_VERBOSE})
  56. );
  57. }
  58. $fatal_alert = 0; # set by filters at expected fatal alerts
  59. SKIP: {
  60. skip "Record tests not intended for dtls", 1 if $run_test_as_dtls == 1;
  61. #Test 1: Injecting out of context empty records should fail
  62. $proxy->clear();
  63. $content_type = TLSProxy::Record::RT_APPLICATION_DATA;
  64. $inject_recs_num = 1;
  65. $fatal_alert = 0;
  66. $proxy->serverflags("-tls1_2");
  67. $proxy->clientflags("-no_tls1_3");
  68. $proxy_start_success = $proxy->start();
  69. ok($fatal_alert, "Out of context empty records test");
  70. }
  71. skip "TLSProxy did not start correctly", 21 if $proxy_start_success == 0
  72. && $run_test_as_dtls == 0;
  73. #Test 2: Injecting in context empty records should succeed
  74. $proxy->clear();
  75. $content_type = TLSProxy::Record::RT_HANDSHAKE;
  76. if ($run_test_as_dtls == 1) {
  77. $proxy->serverflags("-min_protocol DTLSv1.2 -max_protocol DTLSv1.2");
  78. $proxy->clientflags("-max_protocol DTLSv1.2");
  79. } else {
  80. $proxy->serverflags("-tls1_2");
  81. $proxy->clientflags("-no_tls1_3");
  82. }
  83. $proxy_start_success = $proxy->start();
  84. skip "TLSProxy did not start correctly", 20 if $proxy_start_success == 0
  85. && $run_test_as_dtls == 1;
  86. ok($proxy_start_success && TLSProxy::Message->success(),
  87. "In context empty records test".($run_test_as_dtls == 1) ? " for DTLS" : " for TLS");
  88. SKIP: {
  89. skip "Record tests not intended for dtls", 7 if $run_test_as_dtls == 1;
  90. #Test 3: Injecting too many in context empty records should fail
  91. $fatal_alert = 0;
  92. $proxy->clear();
  93. #We allow 32 consecutive in context empty records
  94. $inject_recs_num = 33;
  95. $proxy->serverflags("-tls1_2");
  96. $proxy->clientflags("-no_tls1_3");
  97. $proxy->start();
  98. ok($fatal_alert, "Too many in context empty records test");
  99. #Test 4: Injecting a fragmented fatal alert should fail. We expect the server to
  100. # send back an alert of its own because it cannot handle fragmented
  101. # alerts
  102. $fatal_alert = 0;
  103. $proxy->clear();
  104. $proxy->filter(\&add_frag_alert_filter);
  105. $proxy->serverflags("-tls1_2");
  106. $proxy->clientflags("-no_tls1_3");
  107. $proxy->start();
  108. ok($fatal_alert, "Fragmented alert records test");
  109. #Run some SSLv2 ClientHello tests
  110. use constant {
  111. TLSV1_2_IN_SSLV2 => 0,
  112. SSLV2_IN_SSLV2 => 1,
  113. FRAGMENTED_IN_TLSV1_2 => 2,
  114. FRAGMENTED_IN_SSLV2 => 3,
  115. ALERT_BEFORE_SSLV2 => 4
  116. };
  117. # The TLSv1.2 in SSLv2 ClientHello need to run at security level 0
  118. # because in a SSLv2 ClientHello we can't send extensions to indicate
  119. # which signature algorithm we want to use, and the default is SHA1.
  120. #Test 5: Inject an SSLv2 style record format for a TLSv1.2 ClientHello
  121. $sslv2testtype = TLSV1_2_IN_SSLV2;
  122. $proxy->clear();
  123. $proxy->filter(\&add_sslv2_filter);
  124. $proxy->serverflags("-tls1_2");
  125. $proxy->clientflags("-no_tls1_3 -legacy_renegotiation");
  126. $proxy->ciphers("AES128-SHA:\@SECLEVEL=0");
  127. $proxy->start();
  128. ok(TLSProxy::Message->success(), "TLSv1.2 in SSLv2 ClientHello test");
  129. #Test 6: Inject an SSLv2 style record format for an SSLv2 ClientHello. We don't
  130. # support this so it should fail. We actually treat it as an unknown
  131. # protocol so we don't even send an alert in this case.
  132. $sslv2testtype = SSLV2_IN_SSLV2;
  133. $proxy->clear();
  134. $proxy->serverflags("-tls1_2");
  135. $proxy->clientflags("-no_tls1_3");
  136. $proxy->ciphers("AES128-SHA:\@SECLEVEL=0");
  137. $proxy->start();
  138. ok(TLSProxy::Message->fail(), "SSLv2 in SSLv2 ClientHello test");
  139. #Test 7: Sanity check ClientHello fragmentation. This isn't really an SSLv2 test
  140. # at all, but it gives us confidence that Test 8 fails for the right
  141. # reasons
  142. $sslv2testtype = FRAGMENTED_IN_TLSV1_2;
  143. $proxy->clear();
  144. $proxy->serverflags("-tls1_2");
  145. $proxy->clientflags("-no_tls1_3");
  146. $proxy->ciphers("AES128-SHA:\@SECLEVEL=0");
  147. $proxy->start();
  148. ok(TLSProxy::Message->success(), "Fragmented ClientHello in TLSv1.2 test");
  149. #Test 8: Fragment a TLSv1.2 ClientHello across a TLS1.2 record; an SSLv2
  150. # record; and another TLS1.2 record. This isn't allowed so should fail
  151. $sslv2testtype = FRAGMENTED_IN_SSLV2;
  152. $proxy->clear();
  153. $proxy->serverflags("-tls1_2");
  154. $proxy->clientflags("-no_tls1_3");
  155. $proxy->ciphers("AES128-SHA:\@SECLEVEL=0");
  156. $proxy->start();
  157. ok(TLSProxy::Message->fail(), "Fragmented ClientHello in TLSv1.2/SSLv2 test");
  158. #Test 9: Send a TLS warning alert before an SSLv2 ClientHello. This should
  159. # fail because an SSLv2 ClientHello must be the first record.
  160. $sslv2testtype = ALERT_BEFORE_SSLV2;
  161. $proxy->clear();
  162. $proxy->serverflags("-tls1_2");
  163. $proxy->clientflags("-no_tls1_3");
  164. $proxy->ciphers("AES128-SHA:\@SECLEVEL=0");
  165. $proxy->start();
  166. ok(TLSProxy::Message->fail(), "Alert before SSLv2 ClientHello test");
  167. }
  168. #Unrecognised record type tests
  169. #Test 10: Sending an unrecognised record type in TLS1.2 should fail
  170. $fatal_alert = 0;
  171. $proxy->clear();
  172. if ($run_test_as_dtls == 1) {
  173. $proxy->serverflags("-min_protocol DTLSv1.2 -max_protocol DTLSv1.2");
  174. $proxy->clientflags("-max_protocol DTLSv1.2");
  175. } else {
  176. $proxy->serverflags("-tls1_2");
  177. $proxy->clientflags("-no_tls1_3");
  178. }
  179. $proxy->filter(\&add_unknown_record_type);
  180. $proxy_start_success = $proxy->start();
  181. if ($run_test_as_dtls == 1) {
  182. ok($proxy_start_success == 0, "Unrecognised record type in DTLS1.2");
  183. } else {
  184. ok($fatal_alert, "Unrecognised record type in TLS1.2");
  185. }
  186. SKIP: {
  187. skip "TLSv1.1 or DTLSv1 disabled", 1 if ($run_test_as_dtls == 0 && disabled("tls1_1"))
  188. || ($run_test_as_dtls == 1 && disabled("dtls1"));
  189. #Test 11: Sending an unrecognised record type in TLS1.1 should fail
  190. $fatal_alert = 0;
  191. $proxy->clear();
  192. if ($run_test_as_dtls == 1) {
  193. $proxy->clientflags("-min_protocol DTLSv1 -max_protocol DTLSv1 -cipher DEFAULT:\@SECLEVEL=0");
  194. } else {
  195. $proxy->clientflags("-tls1_1 -cipher DEFAULT:\@SECLEVEL=0");
  196. }
  197. $proxy->ciphers("AES128-SHA:\@SECLEVEL=0");
  198. $proxy_start_success = $proxy->start();
  199. if ($run_test_as_dtls == 1) {
  200. ok($proxy_start_success == 0, "Unrecognised record type in DTLSv1");
  201. } else {
  202. ok($fatal_alert, "Unrecognised record type in TLSv1.1");
  203. }
  204. }
  205. SKIP: {
  206. skip "Record tests not intended for dtls", 10 if $run_test_as_dtls == 1;
  207. #Test 12: Sending a different record version in TLS1.2 should fail
  208. $fatal_alert = 0;
  209. $proxy->clear();
  210. $proxy->clientflags("-tls1_2");
  211. $proxy->filter(\&change_version);
  212. $proxy->start();
  213. ok($fatal_alert, "Changed record version in TLS1.2");
  214. #TLS1.3 specific tests
  215. SKIP: {
  216. skip "TLSv1.3 disabled", 9
  217. if disabled("tls1_3") || (disabled("ec") && disabled("dh"));
  218. #Test 13: Sending a different record version in TLS1.3 should fail
  219. $proxy->clear();
  220. $proxy->filter(\&change_version);
  221. $proxy->start();
  222. ok(TLSProxy::Message->fail(), "Changed record version in TLS1.3");
  223. #Test 14: Sending an unrecognised record type in TLS1.3 should fail
  224. $fatal_alert = 0;
  225. $proxy->clear();
  226. $proxy->filter(\&add_unknown_record_type);
  227. $proxy->start();
  228. ok($fatal_alert, "Unrecognised record type in TLS1.3");
  229. #Test 15: Sending an outer record type other than app data once encrypted
  230. #should fail
  231. $fatal_alert = 0;
  232. $proxy->clear();
  233. $proxy->filter(\&change_outer_record_type);
  234. $proxy->start();
  235. ok($fatal_alert, "Wrong outer record type in TLS1.3");
  236. use constant {
  237. DATA_AFTER_SERVER_HELLO => 0,
  238. DATA_AFTER_FINISHED => 1,
  239. DATA_AFTER_KEY_UPDATE => 2,
  240. DATA_BETWEEN_KEY_UPDATE => 3,
  241. NO_DATA_BETWEEN_KEY_UPDATE => 4,
  242. };
  243. #Test 16: Sending a ServerHello which doesn't end on a record boundary
  244. # should fail
  245. $fatal_alert = 0;
  246. $proxy->clear();
  247. $boundary_test_type = DATA_AFTER_SERVER_HELLO;
  248. $proxy->filter(\&not_on_record_boundary);
  249. $proxy->start();
  250. ok($fatal_alert, "Record not on boundary in TLS1.3 (ServerHello)");
  251. #Test 17: Sending a Finished which doesn't end on a record boundary
  252. # should fail
  253. $fatal_alert = 0;
  254. $proxy->clear();
  255. $boundary_test_type = DATA_AFTER_FINISHED;
  256. $proxy->start();
  257. ok($fatal_alert, "Record not on boundary in TLS1.3 (Finished)");
  258. #Test 18: Sending a KeyUpdate which doesn't end on a record boundary
  259. # should fail
  260. $fatal_alert = 0;
  261. $proxy->clear();
  262. $boundary_test_type = DATA_AFTER_KEY_UPDATE;
  263. $proxy->start();
  264. ok($fatal_alert, "Record not on boundary in TLS1.3 (KeyUpdate)");
  265. #Test 19: Sending application data in the middle of a fragmented KeyUpdate
  266. # should fail. Strictly speaking this is not a record boundary test
  267. # but we use the same filter.
  268. $fatal_alert = 0;
  269. $proxy->clear();
  270. $boundary_test_type = DATA_BETWEEN_KEY_UPDATE;
  271. $proxy->start();
  272. ok($fatal_alert, "Data between KeyUpdate");
  273. #Test 20: Fragmented KeyUpdate. This should succeed. Strictly speaking this
  274. # is not a record boundary test but we use the same filter.
  275. $proxy->clear();
  276. $boundary_test_type = NO_DATA_BETWEEN_KEY_UPDATE;
  277. $proxy->start();
  278. ok(TLSProxy::Message->success(), "No data between KeyUpdate");
  279. SKIP: {
  280. skip "EC disabled", 1 if disabled("ec");
  281. #Test 21: Force an HRR and change the "real" ServerHello to have a protocol
  282. # record version of 0x0301 (TLSv1.0). At this point we have already
  283. # decided that we are doing TLSv1.3 but are still using plaintext
  284. # records. The server should be sending a record version of 0x303
  285. # (TLSv1.2), but the RFC requires us to ignore this field so we
  286. # should tolerate the incorrect version.
  287. $proxy->clear();
  288. $proxy->filter(\&change_server_hello_version);
  289. $proxy->serverflags("-groups P-256"); # Force an HRR
  290. $proxy->start();
  291. ok(TLSProxy::Message->success(), "Bad ServerHello record version after HRR");
  292. }
  293. }
  294. }
  295. }
  296. sub add_empty_recs_filter
  297. {
  298. my $proxy = shift;
  299. my $records = $proxy->record_list;
  300. my $isdtls = $proxy->isdtls();
  301. # We're only interested in the initial ClientHello
  302. if ($proxy->flight != 0) {
  303. $fatal_alert = 1 if @{$records}[-1]->is_fatal_alert(1) == TLSProxy::Message::AL_DESC_UNEXPECTED_MESSAGE;
  304. return;
  305. }
  306. for (my $i = 0; $i < $inject_recs_num; $i++) {
  307. my $record;
  308. if ($isdtls == 1) {
  309. $record = TLSProxy::Record->new_dtls(
  310. 0,
  311. $content_type,
  312. TLSProxy::Record::VERS_DTLS_1_2,
  313. 0,
  314. 0,
  315. 0,
  316. 0,
  317. 0,
  318. 0,
  319. "",
  320. ""
  321. );
  322. } else {
  323. $record = TLSProxy::Record->new(
  324. 0,
  325. $content_type,
  326. TLSProxy::Record::VERS_TLS_1_2,
  327. 0,
  328. 0,
  329. 0,
  330. 0,
  331. "",
  332. ""
  333. );
  334. }
  335. push @{$records}, $record;
  336. }
  337. }
  338. sub add_frag_alert_filter
  339. {
  340. my $proxy = shift;
  341. my $records = $proxy->record_list;
  342. my $byte;
  343. # We're only interested in the initial ClientHello
  344. if ($proxy->flight != 0) {
  345. $fatal_alert = 1 if @{$records}[-1]->is_fatal_alert(1) == TLSProxy::Message::AL_DESC_UNEXPECTED_MESSAGE;
  346. return;
  347. }
  348. # Add a zero length fragment first
  349. #my $record = TLSProxy::Record->new(
  350. # 0,
  351. # TLSProxy::Record::RT_ALERT,
  352. # TLSProxy::Record::VERS_TLS_1_2,
  353. # 0,
  354. # 0,
  355. # 0,
  356. # "",
  357. # ""
  358. #);
  359. #push @{$proxy->record_list}, $record;
  360. # Now add the alert level (Fatal) as a separate record
  361. $byte = pack('C', TLSProxy::Message::AL_LEVEL_FATAL);
  362. my $record = TLSProxy::Record->new(
  363. 0,
  364. TLSProxy::Record::RT_ALERT,
  365. TLSProxy::Record::VERS_TLS_1_2,
  366. 1,
  367. 0,
  368. 1,
  369. 1,
  370. $byte,
  371. $byte
  372. );
  373. push @{$records}, $record;
  374. # And finally the description (Unexpected message) in a third record
  375. $byte = pack('C', TLSProxy::Message::AL_DESC_UNEXPECTED_MESSAGE);
  376. $record = TLSProxy::Record->new(
  377. 0,
  378. TLSProxy::Record::RT_ALERT,
  379. TLSProxy::Record::VERS_TLS_1_2,
  380. 1,
  381. 0,
  382. 1,
  383. 1,
  384. $byte,
  385. $byte
  386. );
  387. push @{$records}, $record;
  388. }
  389. sub add_sslv2_filter
  390. {
  391. my $proxy = shift;
  392. my $clienthello;
  393. my $record;
  394. # We're only interested in the initial ClientHello
  395. if ($proxy->flight != 0) {
  396. return;
  397. }
  398. # Ditch the real ClientHello - we're going to replace it with our own
  399. shift @{$proxy->record_list};
  400. if ($sslv2testtype == ALERT_BEFORE_SSLV2) {
  401. my $alert = pack('CC', TLSProxy::Message::AL_LEVEL_FATAL,
  402. TLSProxy::Message::AL_DESC_NO_RENEGOTIATION);
  403. my $alertlen = length $alert;
  404. $record = TLSProxy::Record->new(
  405. 0,
  406. TLSProxy::Record::RT_ALERT,
  407. TLSProxy::Record::VERS_TLS_1_2,
  408. $alertlen,
  409. 0,
  410. $alertlen,
  411. $alertlen,
  412. $alert,
  413. $alert
  414. );
  415. push @{$proxy->record_list}, $record;
  416. }
  417. if ($sslv2testtype == ALERT_BEFORE_SSLV2
  418. || $sslv2testtype == TLSV1_2_IN_SSLV2
  419. || $sslv2testtype == SSLV2_IN_SSLV2) {
  420. # This is an SSLv2 format ClientHello
  421. $clienthello =
  422. pack "C44",
  423. 0x01, # ClientHello
  424. 0x03, 0x03, #TLSv1.2
  425. 0x00, 0x03, # Ciphersuites len
  426. 0x00, 0x00, # Session id len
  427. 0x00, 0x20, # Challenge len
  428. 0x00, 0x00, 0x2f, #AES128-SHA
  429. 0x01, 0x18, 0x9F, 0x76, 0xEC, 0x57, 0xCE, 0xE5, 0xB3, 0xAB, 0x79, 0x90,
  430. 0xAD, 0xAC, 0x6E, 0xD1, 0x58, 0x35, 0x03, 0x97, 0x16, 0x10, 0x82, 0x56,
  431. 0xD8, 0x55, 0xFF, 0xE1, 0x8A, 0xA3, 0x2E, 0xF6; # Challenge
  432. if ($sslv2testtype == SSLV2_IN_SSLV2) {
  433. # Set the version to "real" SSLv2
  434. vec($clienthello, 1, 8) = 0x00;
  435. vec($clienthello, 2, 8) = 0x02;
  436. }
  437. my $chlen = length $clienthello;
  438. $record = TLSProxy::Record->new(
  439. 0,
  440. TLSProxy::Record::RT_HANDSHAKE,
  441. TLSProxy::Record::VERS_TLS_1_2,
  442. $chlen,
  443. 1, #SSLv2
  444. $chlen,
  445. $chlen,
  446. $clienthello,
  447. $clienthello
  448. );
  449. push @{$proxy->record_list}, $record;
  450. } else {
  451. # For this test we're using a real TLS ClientHello
  452. $clienthello =
  453. pack "C49",
  454. 0x01, # ClientHello
  455. 0x00, 0x00, 0x2D, # Message length
  456. 0x03, 0x03, # TLSv1.2
  457. 0x01, 0x18, 0x9F, 0x76, 0xEC, 0x57, 0xCE, 0xE5, 0xB3, 0xAB, 0x79, 0x90,
  458. 0xAD, 0xAC, 0x6E, 0xD1, 0x58, 0x35, 0x03, 0x97, 0x16, 0x10, 0x82, 0x56,
  459. 0xD8, 0x55, 0xFF, 0xE1, 0x8A, 0xA3, 0x2E, 0xF6, # Random
  460. 0x00, # Session id len
  461. 0x00, 0x04, # Ciphersuites len
  462. 0x00, 0x2f, # AES128-SHA
  463. 0x00, 0xff, # Empty reneg info SCSV
  464. 0x01, # Compression methods len
  465. 0x00, # Null compression
  466. 0x00, 0x00; # Extensions len
  467. # Split this into 3: A TLS record; a SSLv2 record and a TLS record.
  468. # We deliberately split the second record prior to the Challenge/Random
  469. # and set the first byte of the random to 1. This makes the second SSLv2
  470. # record look like an SSLv2 ClientHello
  471. my $frag1 = substr $clienthello, 0, 6;
  472. my $frag2 = substr $clienthello, 6, 32;
  473. my $frag3 = substr $clienthello, 38;
  474. my $fraglen = length $frag1;
  475. $record = TLSProxy::Record->new(
  476. 0,
  477. TLSProxy::Record::RT_HANDSHAKE,
  478. TLSProxy::Record::VERS_TLS_1_2,
  479. $fraglen,
  480. 0,
  481. $fraglen,
  482. $fraglen,
  483. $frag1,
  484. $frag1
  485. );
  486. push @{$proxy->record_list}, $record;
  487. $fraglen = length $frag2;
  488. my $recvers;
  489. if ($sslv2testtype == FRAGMENTED_IN_SSLV2) {
  490. $recvers = 1;
  491. } else {
  492. $recvers = 0;
  493. }
  494. $record = TLSProxy::Record->new(
  495. 0,
  496. TLSProxy::Record::RT_HANDSHAKE,
  497. TLSProxy::Record::VERS_TLS_1_2,
  498. $fraglen,
  499. $recvers,
  500. $fraglen,
  501. $fraglen,
  502. $frag2,
  503. $frag2
  504. );
  505. push @{$proxy->record_list}, $record;
  506. $fraglen = length $frag3;
  507. $record = TLSProxy::Record->new(
  508. 0,
  509. TLSProxy::Record::RT_HANDSHAKE,
  510. TLSProxy::Record::VERS_TLS_1_2,
  511. $fraglen,
  512. 0,
  513. $fraglen,
  514. $fraglen,
  515. $frag3,
  516. $frag3
  517. );
  518. push @{$proxy->record_list}, $record;
  519. }
  520. }
  521. sub add_unknown_record_type
  522. {
  523. my $proxy = shift;
  524. my $records = $proxy->record_list;
  525. my $isdtls = $proxy->isdtls;
  526. state $added_record;
  527. # We'll change a record after the initial version neg has taken place
  528. if ($proxy->flight == 0) {
  529. $added_record = 0;
  530. return;
  531. } elsif ($proxy->flight != 1 || $added_record) {
  532. $fatal_alert = 1 if @{$records}[-1]->is_fatal_alert(0) == TLSProxy::Message::AL_DESC_UNEXPECTED_MESSAGE;
  533. return;
  534. }
  535. my $record;
  536. if ($isdtls) {
  537. $record = TLSProxy::Record->new_dtls(
  538. 1,
  539. TLSProxy::Record::RT_UNKNOWN,
  540. @{$records}[-1]->version(),
  541. @{$records}[-1]->epoch(),
  542. @{$records}[-1]->seq() +1,
  543. 1,
  544. 0,
  545. 1,
  546. 1,
  547. "X",
  548. "X"
  549. );
  550. } else {
  551. $record = TLSProxy::Record->new(
  552. 1,
  553. TLSProxy::Record::RT_UNKNOWN,
  554. @{$records}[-1]->version(),
  555. 1,
  556. 0,
  557. 1,
  558. 1,
  559. "X",
  560. "X"
  561. );
  562. }
  563. #Find ServerHello record and insert after that
  564. my $i;
  565. for ($i = 0; ${$proxy->record_list}[$i]->flight() < 1; $i++) {
  566. next;
  567. }
  568. $i++;
  569. splice @{$proxy->record_list}, $i, 0, $record;
  570. $added_record = 1;
  571. }
  572. sub change_version
  573. {
  574. my $proxy = shift;
  575. my $records = $proxy->record_list;
  576. # We'll change a version after the initial version neg has taken place
  577. if ($proxy->flight != 1) {
  578. $fatal_alert = 1 if @{$records}[-1]->is_fatal_alert(0) == TLSProxy::Message::AL_DESC_PROTOCOL_VERSION;
  579. return;
  580. }
  581. if ($#{$records} > 1) {
  582. # ... typically in ServerHelloDone
  583. @{$records}[-1]->version(TLSProxy::Record::VERS_TLS_1_1);
  584. }
  585. }
  586. sub change_server_hello_version
  587. {
  588. my $proxy = shift;
  589. my $records = $proxy->record_list;
  590. # We're only interested in changing the ServerHello after an HRR
  591. if ($proxy->flight != 3) {
  592. return;
  593. }
  594. # The ServerHello has index 5
  595. # 0 - ClientHello
  596. # 1 - HRR
  597. # 2 - CCS
  598. # 3 - ClientHello(2)
  599. # 4 - CCS
  600. # 5 - ServerHello
  601. @{$records}[5]->version(TLSProxy::Record::VERS_TLS_1_0);
  602. }
  603. sub change_outer_record_type
  604. {
  605. my $proxy = shift;
  606. my $records = $proxy->record_list;
  607. # We'll change a record after the initial version neg has taken place
  608. if ($proxy->flight != 1) {
  609. $fatal_alert = 1 if @{$records}[-1]->is_fatal_alert(0) == TLSProxy::Message::AL_DESC_UNEXPECTED_MESSAGE;
  610. return;
  611. }
  612. # Find CCS record and change record after that
  613. my $i = 0;
  614. foreach my $record (@{$records}) {
  615. last if $record->content_type == TLSProxy::Record::RT_CCS;
  616. $i++;
  617. }
  618. if (defined(${$records}[++$i])) {
  619. ${$records}[$i]->outer_content_type(TLSProxy::Record::RT_HANDSHAKE);
  620. }
  621. }
  622. sub not_on_record_boundary
  623. {
  624. my $proxy = shift;
  625. my $records = $proxy->record_list;
  626. my $data;
  627. #Find server's first flight
  628. if ($proxy->flight != 1) {
  629. $fatal_alert = 1 if @{$records}[-1]->is_fatal_alert(0) == TLSProxy::Message::AL_DESC_UNEXPECTED_MESSAGE;
  630. return;
  631. }
  632. if ($boundary_test_type == DATA_AFTER_SERVER_HELLO) {
  633. #Merge the ServerHello and EncryptedExtensions records into one
  634. my $i = 0;
  635. foreach my $record (@{$records}) {
  636. if ($record->content_type == TLSProxy::Record::RT_HANDSHAKE) {
  637. $record->{sent} = 1; # pretend it's sent already
  638. last;
  639. }
  640. $i++;
  641. }
  642. if (defined(${$records}[$i+1])) {
  643. $data = ${$records}[$i]->data();
  644. $data .= ${$records}[$i+1]->decrypt_data();
  645. ${$records}[$i+1]->data($data);
  646. ${$records}[$i+1]->len(length $data);
  647. #Delete the old ServerHello record
  648. splice @{$records}, $i, 1;
  649. }
  650. } elsif ($boundary_test_type == DATA_AFTER_FINISHED) {
  651. return if @{$proxy->{message_list}}[-1]->{mt}
  652. != TLSProxy::Message::MT_FINISHED;
  653. my $last_record = @{$records}[-1];
  654. $data = $last_record->decrypt_data;
  655. #Add a KeyUpdate message onto the end of the Finished record
  656. my $keyupdate = pack "C5",
  657. 0x18, # KeyUpdate
  658. 0x00, 0x00, 0x01, # Message length
  659. 0x00; # Update not requested
  660. $data .= $keyupdate;
  661. #Add content type and tag
  662. $data .= pack("C", TLSProxy::Record::RT_HANDSHAKE).("\0"x16);
  663. #Update the record
  664. $last_record->data($data);
  665. $last_record->len(length $data);
  666. } elsif ($boundary_test_type == DATA_AFTER_KEY_UPDATE) {
  667. return if @{$proxy->{message_list}}[-1]->{mt}
  668. != TLSProxy::Message::MT_FINISHED;
  669. #KeyUpdates must end on a record boundary
  670. my $record = TLSProxy::Record->new(
  671. 1,
  672. TLSProxy::Record::RT_APPLICATION_DATA,
  673. TLSProxy::Record::VERS_TLS_1_2,
  674. 0,
  675. 0,
  676. 0,
  677. 0,
  678. "",
  679. ""
  680. );
  681. #Add two KeyUpdate messages into a single record
  682. my $keyupdate = pack "C5",
  683. 0x18, # KeyUpdate
  684. 0x00, 0x00, 0x01, # Message length
  685. 0x00; # Update not requested
  686. $data = $keyupdate.$keyupdate;
  687. #Add content type and tag
  688. $data .= pack("C", TLSProxy::Record::RT_HANDSHAKE).("\0"x16);
  689. $record->data($data);
  690. $record->len(length $data);
  691. push @{$records}, $record;
  692. } else {
  693. return if @{$proxy->{message_list}}[-1]->{mt}
  694. != TLSProxy::Message::MT_FINISHED;
  695. my $record = TLSProxy::Record->new(
  696. 1,
  697. TLSProxy::Record::RT_APPLICATION_DATA,
  698. TLSProxy::Record::VERS_TLS_1_2,
  699. 0,
  700. 0,
  701. 0,
  702. 0,
  703. "",
  704. ""
  705. );
  706. #Add a partial KeyUpdate message into the record
  707. $data = pack "C1",
  708. 0x18; # KeyUpdate message type. Omit the rest of the message header
  709. #Add content type and tag
  710. $data .= pack("C", TLSProxy::Record::RT_HANDSHAKE).("\0"x16);
  711. $record->data($data);
  712. $record->len(length $data);
  713. push @{$records}, $record;
  714. if ($boundary_test_type == DATA_BETWEEN_KEY_UPDATE) {
  715. #Now add an app data record
  716. $record = TLSProxy::Record->new(
  717. 1,
  718. TLSProxy::Record::RT_APPLICATION_DATA,
  719. TLSProxy::Record::VERS_TLS_1_2,
  720. 0,
  721. 0,
  722. 0,
  723. 0,
  724. "",
  725. ""
  726. );
  727. #Add an empty app data record (just content type and tag)
  728. $data = pack("C", TLSProxy::Record::RT_APPLICATION_DATA).("\0"x16);
  729. $record->data($data);
  730. $record->len(length $data);
  731. push @{$records}, $record;
  732. }
  733. #Now add the rest of the KeyUpdate message
  734. $record = TLSProxy::Record->new(
  735. 1,
  736. TLSProxy::Record::RT_APPLICATION_DATA,
  737. TLSProxy::Record::VERS_TLS_1_2,
  738. 0,
  739. 0,
  740. 0,
  741. 0,
  742. "",
  743. ""
  744. );
  745. #Add the last 4 bytes of the KeyUpdate record
  746. $data = pack "C4",
  747. 0x00, 0x00, 0x01, # Message length
  748. 0x00; # Update not requested
  749. #Add content type and tag
  750. $data .= pack("C", TLSProxy::Record::RT_HANDSHAKE).("\0"x16);
  751. $record->data($data);
  752. $record->len(length $data);
  753. push @{$records}, $record;
  754. }
  755. }