Record.pm 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531
  1. # Copyright 2016-2024 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. use TLSProxy::Proxy;
  9. package TLSProxy::Record;
  10. my $server_encrypting = 0;
  11. my $client_encrypting = 0;
  12. my $etm = 0;
  13. use constant DTLS_RECORD_HEADER_LENGTH => 13;
  14. use constant TLS_RECORD_HEADER_LENGTH => 5;
  15. #Record types
  16. use constant {
  17. RT_APPLICATION_DATA => 23,
  18. RT_HANDSHAKE => 22,
  19. RT_ALERT => 21,
  20. RT_CCS => 20,
  21. RT_UNKNOWN => 100
  22. };
  23. my %record_type = (
  24. RT_APPLICATION_DATA, "APPLICATION DATA",
  25. RT_HANDSHAKE, "HANDSHAKE",
  26. RT_ALERT, "ALERT",
  27. RT_CCS, "CCS",
  28. RT_UNKNOWN, "UNKNOWN"
  29. );
  30. use constant {
  31. VERS_DTLS_1_2 => 0xfefd,
  32. VERS_DTLS_1 => 0xfeff,
  33. VERS_TLS_1_4 => 0x0305,
  34. VERS_TLS_1_3 => 0x0304,
  35. VERS_TLS_1_2 => 0x0303,
  36. VERS_TLS_1_1 => 0x0302,
  37. VERS_TLS_1_0 => 0x0301,
  38. VERS_SSL_3_0 => 0x0300,
  39. VERS_SSL_LT_3_0 => 0x02ff
  40. };
  41. our %tls_version = (
  42. VERS_DTLS_1_2, "DTLS1.2",
  43. VERS_DTLS_1, "DTLS1",
  44. VERS_TLS_1_3, "TLS1.3",
  45. VERS_TLS_1_2, "TLS1.2",
  46. VERS_TLS_1_1, "TLS1.1",
  47. VERS_TLS_1_0, "TLS1.0",
  48. VERS_SSL_3_0, "SSL3",
  49. VERS_SSL_LT_3_0, "SSL<3"
  50. );
  51. #Class method to extract records from a packet of data
  52. sub get_records
  53. {
  54. my $class = shift;
  55. my $server = shift;
  56. my $flight = shift;
  57. my $packet = shift;
  58. my $isdtls = shift;
  59. my $partial = "";
  60. my @record_list = ();
  61. my @message_list = ();
  62. my $record_hdr_len = $isdtls ? DTLS_RECORD_HEADER_LENGTH
  63. : TLS_RECORD_HEADER_LENGTH;
  64. my $recnum = 1;
  65. while (length ($packet) > 0) {
  66. print " Record $recnum ", $server ? "(server -> client)\n"
  67. : "(client -> server)\n";
  68. my $content_type;
  69. my $version;
  70. my $len;
  71. my $epoch;
  72. my $seq;
  73. if ($isdtls) {
  74. my $seqhi;
  75. my $seqmi;
  76. my $seqlo;
  77. #Get the record header (unpack can't fail if $packet is too short)
  78. ($content_type, $version, $epoch,
  79. $seqhi, $seqmi, $seqlo, $len) = unpack('Cnnnnnn', $packet);
  80. $seq = ($seqhi << 32) | ($seqmi << 16) | $seqlo
  81. } else {
  82. #Get the record header (unpack can't fail if $packet is too short)
  83. ($content_type, $version, $len) = unpack('Cnn', $packet);
  84. }
  85. if (length($packet) < $record_hdr_len + ($len // 0)) {
  86. print "Partial data : ".length($packet)." bytes\n";
  87. $partial = $packet;
  88. last;
  89. }
  90. my $data = substr($packet, $record_hdr_len, $len);
  91. print " Content type: ".$record_type{$content_type}."\n";
  92. print " Version: $tls_version{$version}\n";
  93. if($isdtls) {
  94. print " Epoch: $epoch\n";
  95. print " Sequence: $seq\n";
  96. }
  97. print " Length: $len\n";
  98. my $record;
  99. if ($isdtls) {
  100. $record = TLSProxy::Record->new_dtls(
  101. $flight,
  102. $content_type,
  103. $version,
  104. $epoch,
  105. $seq,
  106. $len,
  107. 0,
  108. $len, # len_real
  109. $len, # decrypt_len
  110. $data, # data
  111. $data # decrypt_data
  112. );
  113. } else {
  114. $record = TLSProxy::Record->new(
  115. $flight,
  116. $content_type,
  117. $version,
  118. $len,
  119. 0,
  120. $len, # len_real
  121. $len, # decrypt_len
  122. $data, # data
  123. $data # decrypt_data
  124. );
  125. }
  126. if ($content_type != RT_CCS
  127. && (!TLSProxy::Proxy->is_tls13()
  128. || $content_type != RT_ALERT)) {
  129. if (($server && $server_encrypting)
  130. || (!$server && $client_encrypting)) {
  131. if (!TLSProxy::Proxy->is_tls13() && $etm) {
  132. $record->decryptETM();
  133. } else {
  134. $record->decrypt();
  135. }
  136. $record->encrypted(1);
  137. if (TLSProxy::Proxy->is_tls13()) {
  138. print " Inner content type: "
  139. .$record_type{$record->content_type()}."\n";
  140. }
  141. }
  142. }
  143. push @record_list, $record;
  144. #Now figure out what messages are contained within this record
  145. my @messages = TLSProxy::Message->get_messages($server, $record, $isdtls);
  146. push @message_list, @messages;
  147. $packet = substr($packet, $record_hdr_len + $len);
  148. $recnum++;
  149. }
  150. return (\@record_list, \@message_list, $partial);
  151. }
  152. sub clear
  153. {
  154. $server_encrypting = 0;
  155. $client_encrypting = 0;
  156. }
  157. #Class level accessors
  158. sub server_encrypting
  159. {
  160. my $class = shift;
  161. if (@_) {
  162. $server_encrypting = shift;
  163. }
  164. return $server_encrypting;
  165. }
  166. sub client_encrypting
  167. {
  168. my $class = shift;
  169. if (@_) {
  170. $client_encrypting= shift;
  171. }
  172. return $client_encrypting;
  173. }
  174. #Enable/Disable Encrypt-then-MAC
  175. sub etm
  176. {
  177. my $class = shift;
  178. if (@_) {
  179. $etm = shift;
  180. }
  181. return $etm;
  182. }
  183. sub new_dtls
  184. {
  185. my $class = shift;
  186. my ($flight,
  187. $content_type,
  188. $version,
  189. $epoch,
  190. $seq,
  191. $len,
  192. $sslv2,
  193. $len_real,
  194. $decrypt_len,
  195. $data,
  196. $decrypt_data) = @_;
  197. return $class->init(1,
  198. $flight,
  199. $content_type,
  200. $version,
  201. $epoch,
  202. $seq,
  203. $len,
  204. $sslv2,
  205. $len_real,
  206. $decrypt_len,
  207. $data,
  208. $decrypt_data);
  209. }
  210. sub new
  211. {
  212. my $class = shift;
  213. my ($flight,
  214. $content_type,
  215. $version,
  216. $len,
  217. $sslv2,
  218. $len_real,
  219. $decrypt_len,
  220. $data,
  221. $decrypt_data) = @_;
  222. return $class->init(
  223. 0,
  224. $flight,
  225. $content_type,
  226. $version,
  227. 0, #epoch
  228. 0, #seq
  229. $len,
  230. $sslv2,
  231. $len_real,
  232. $decrypt_len,
  233. $data,
  234. $decrypt_data);
  235. }
  236. sub init
  237. {
  238. my $class = shift;
  239. my ($isdtls,
  240. $flight,
  241. $content_type,
  242. $version,
  243. $epoch,
  244. $seq,
  245. $len,
  246. $sslv2,
  247. $len_real,
  248. $decrypt_len,
  249. $data,
  250. $decrypt_data) = @_;
  251. my $self = {
  252. isdtls => $isdtls,
  253. flight => $flight,
  254. content_type => $content_type,
  255. version => $version,
  256. epoch => $epoch,
  257. seq => $seq,
  258. len => $len,
  259. sslv2 => $sslv2,
  260. len_real => $len_real,
  261. decrypt_len => $decrypt_len,
  262. data => $data,
  263. decrypt_data => $decrypt_data,
  264. orig_decrypt_data => $decrypt_data,
  265. sent => 0,
  266. encrypted => 0,
  267. outer_content_type => RT_APPLICATION_DATA
  268. };
  269. return bless $self, $class;
  270. }
  271. #Decrypt using encrypt-then-MAC
  272. sub decryptETM
  273. {
  274. my ($self) = shift;
  275. my $data = $self->data;
  276. if($self->version >= VERS_TLS_1_1()) {
  277. #TLS1.1+ has an explicit IV. Throw it away
  278. $data = substr($data, 16);
  279. }
  280. #Throw away the MAC (assumes MAC is 20 bytes for now. FIXME)
  281. $data = substr($data, 0, length($data) - 20);
  282. #Find out what the padding byte is
  283. my $padval = unpack("C", substr($data, length($data) - 1));
  284. #Throw away the padding
  285. $data = substr($data, 0, length($data) - ($padval + 1));
  286. $self->decrypt_data($data);
  287. $self->decrypt_len(length($data));
  288. return $data;
  289. }
  290. #Standard decrypt
  291. sub decrypt()
  292. {
  293. my ($self) = shift;
  294. my $mactaglen = 20;
  295. my $data = $self->data;
  296. #Throw away any IVs
  297. if (TLSProxy::Proxy->is_tls13()) {
  298. #A TLS1.3 client, when processing the server's initial flight, could
  299. #respond with either an encrypted or an unencrypted alert.
  300. if ($self->content_type() == RT_ALERT) {
  301. #TODO(TLS1.3): Eventually it is sufficient just to check the record
  302. #content type. If an alert is encrypted it will have a record
  303. #content type of application data. However we haven't done the
  304. #record layer changes yet, so it's a bit more complicated. For now
  305. #we will additionally check if the data length is 2 (1 byte for
  306. #alert level, 1 byte for alert description). If it is, then this is
  307. #an unencrypted alert, so don't try to decrypt
  308. return $data if (length($data) == 2);
  309. }
  310. $mactaglen = 16;
  311. } elsif ($self->version >= VERS_TLS_1_1()) {
  312. #16 bytes for a standard IV
  313. $data = substr($data, 16);
  314. #Find out what the padding byte is
  315. my $padval = unpack("C", substr($data, length($data) - 1));
  316. #Throw away the padding
  317. $data = substr($data, 0, length($data) - ($padval + 1));
  318. }
  319. #Throw away the MAC or TAG
  320. $data = substr($data, 0, length($data) - $mactaglen);
  321. if (TLSProxy::Proxy->is_tls13()) {
  322. #Get the content type
  323. my $content_type = unpack("C", substr($data, length($data) - 1));
  324. $self->content_type($content_type);
  325. $data = substr($data, 0, length($data) - 1);
  326. }
  327. $self->decrypt_data($data);
  328. $self->decrypt_len(length($data));
  329. return $data;
  330. }
  331. #Reconstruct the on-the-wire record representation
  332. sub reconstruct_record
  333. {
  334. my $self = shift;
  335. my $server = shift;
  336. my $data;
  337. #We only replay the records in the same direction
  338. if ($self->{sent} || ($self->flight & 1) != $server) {
  339. return "";
  340. }
  341. $self->{sent} = 1;
  342. if ($self->sslv2) {
  343. $data = pack('n', $self->len | 0x8000);
  344. } else {
  345. if($self->{isdtls}) {
  346. my $seqhi = ($self->seq >> 32) & 0xffff;
  347. my $seqmi = ($self->seq >> 16) & 0xffff;
  348. my $seqlo = ($self->seq >> 0) & 0xffff;
  349. $data = pack('Cnnnnnn', $self->content_type, $self->version,
  350. $self->epoch, $seqhi, $seqmi, $seqlo, $self->len);
  351. } else {
  352. if (TLSProxy::Proxy->is_tls13() && $self->encrypted) {
  353. $data = pack('Cnn', $self->outer_content_type, $self->version,
  354. $self->len);
  355. }
  356. else {
  357. $data = pack('Cnn', $self->content_type, $self->version,
  358. $self->len);
  359. }
  360. }
  361. }
  362. $data .= $self->data;
  363. return $data;
  364. }
  365. #Read only accessors
  366. sub flight
  367. {
  368. my $self = shift;
  369. return $self->{flight};
  370. }
  371. sub sslv2
  372. {
  373. my $self = shift;
  374. return $self->{sslv2};
  375. }
  376. sub len_real
  377. {
  378. my $self = shift;
  379. return $self->{len_real};
  380. }
  381. sub orig_decrypt_data
  382. {
  383. my $self = shift;
  384. return $self->{orig_decrypt_data};
  385. }
  386. #Read/write accessors
  387. sub decrypt_len
  388. {
  389. my $self = shift;
  390. if (@_) {
  391. $self->{decrypt_len} = shift;
  392. }
  393. return $self->{decrypt_len};
  394. }
  395. sub data
  396. {
  397. my $self = shift;
  398. if (@_) {
  399. $self->{data} = shift;
  400. }
  401. return $self->{data};
  402. }
  403. sub decrypt_data
  404. {
  405. my $self = shift;
  406. if (@_) {
  407. $self->{decrypt_data} = shift;
  408. }
  409. return $self->{decrypt_data};
  410. }
  411. sub len
  412. {
  413. my $self = shift;
  414. if (@_) {
  415. $self->{len} = shift;
  416. }
  417. return $self->{len};
  418. }
  419. sub version
  420. {
  421. my $self = shift;
  422. if (@_) {
  423. $self->{version} = shift;
  424. }
  425. return $self->{version};
  426. }
  427. sub content_type
  428. {
  429. my $self = shift;
  430. if (@_) {
  431. $self->{content_type} = shift;
  432. }
  433. return $self->{content_type};
  434. }
  435. sub epoch
  436. {
  437. my $self = shift;
  438. if (@_) {
  439. $self->{epoch} = shift;
  440. }
  441. return $self->{epoch};
  442. }
  443. sub seq
  444. {
  445. my $self = shift;
  446. if (@_) {
  447. $self->{seq} = shift;
  448. }
  449. return $self->{seq};
  450. }
  451. sub encrypted
  452. {
  453. my $self = shift;
  454. if (@_) {
  455. $self->{encrypted} = shift;
  456. }
  457. return $self->{encrypted};
  458. }
  459. sub outer_content_type
  460. {
  461. my $self = shift;
  462. if (@_) {
  463. $self->{outer_content_type} = shift;
  464. }
  465. return $self->{outer_content_type};
  466. }
  467. sub is_fatal_alert
  468. {
  469. my $self = shift;
  470. my $server = shift;
  471. if (($self->{flight} & 1) == $server && $self->{content_type} == RT_ALERT) {
  472. my ($level, $description) = unpack('CC', $self->decrypt_data);
  473. return $description if ($level == 2);
  474. }
  475. return 0;
  476. }
  477. 1;