1%% 2%% %CopyrightBegin% 3%% 4%% Copyright Ericsson AB 2007-2020. All Rights Reserved. 5%% 6%% Licensed under the Apache License, Version 2.0 (the "License"); 7%% you may not use this file except in compliance with the License. 8%% You may obtain a copy of the License at 9%% 10%% http://www.apache.org/licenses/LICENSE-2.0 11%% 12%% Unless required by applicable law or agreed to in writing, software 13%% distributed under the License is distributed on an "AS IS" BASIS, 14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15%% See the License for the specific language governing permissions and 16%% limitations under the License. 17%% 18%% %CopyrightEnd% 19 20-module(tls_record_1_3). 21 22-include("tls_record.hrl"). 23-include("tls_record_1_3.hrl"). 24-include("ssl_internal.hrl"). 25-include("ssl_alert.hrl"). 26-include("ssl_cipher.hrl"). 27 28%% Encoding 29-export([encode_handshake/2, encode_alert_record/2, 30 encode_data/2]). 31-export([encode_plain_text/3]). 32 33%% Decoding 34-export([decode_cipher_text/2]). 35 36%%==================================================================== 37%% Encoding 38%%==================================================================== 39 40%%-------------------------------------------------------------------- 41-spec encode_handshake(iolist(), ssl_record:connection_states()) -> 42 {iolist(), ssl_record:connection_states()}. 43% 44%% Description: Encodes a handshake message to send on the tls-1.3-socket. 45%%-------------------------------------------------------------------- 46encode_handshake(Frag, ConnectionStates) -> 47 case iolist_size(Frag) of 48 N when N > ?MAX_PLAIN_TEXT_LENGTH -> 49 %% TODO: Consider padding here 50 Data = tls_record:split_iovec(Frag), 51 encode_iolist(?HANDSHAKE, Data, ConnectionStates); 52 _ -> 53 encode_plain_text(?HANDSHAKE, Frag, ConnectionStates) 54 end. 55 56%%-------------------------------------------------------------------- 57-spec encode_alert_record(#alert{}, ssl_record:connection_states()) -> 58 {iolist(), ssl_record:connection_states()}. 59%% 60%% Description: Encodes an alert message to send on the ssl-socket. 61%%-------------------------------------------------------------------- 62encode_alert_record(#alert{level = Level, description = Description}, 63 ConnectionStates) -> 64 encode_plain_text(?ALERT, <<?BYTE(Level), ?BYTE(Description)>>, 65 ConnectionStates). 66%%-------------------------------------------------------------------- 67-spec encode_data(iolist(), ssl_record:connection_states()) -> 68 {iolist(), ssl_record:connection_states()}. 69%% 70%% Description: Encodes data to send on the ssl-socket. 71%%-------------------------------------------------------------------- 72encode_data(Frag, ConnectionStates) -> 73 Data = tls_record:split_iovec(Frag), 74 encode_iolist(?APPLICATION_DATA, Data, ConnectionStates). 75 76encode_plain_text(Type, Data0, #{current_write := Write0} = ConnectionStates) -> 77 PadLen = 0, %% TODO where to specify PadLen? 78 Data = inner_plaintext(Type, Data0, PadLen), 79 CipherFragment = encode_plain_text(Data, Write0), 80 {CipherText, Write} = encode_tls_cipher_text(CipherFragment, Write0), 81 {CipherText, ConnectionStates#{current_write => Write}}. 82 83encode_iolist(Type, Data, ConnectionStates0) -> 84 {ConnectionStates, EncodedMsg} = 85 lists:foldl(fun(Text, {CS0, Encoded}) -> 86 {Enc, CS1} = 87 encode_plain_text(Type, Text, CS0), 88 {CS1, [Enc | Encoded]} 89 end, {ConnectionStates0, []}, Data), 90 {lists:reverse(EncodedMsg), ConnectionStates}. 91 92%%==================================================================== 93%% Decoding 94%%==================================================================== 95 96%%-------------------------------------------------------------------- 97-spec decode_cipher_text(#ssl_tls{}, ssl_record:connection_states()) -> 98 {#ssl_tls{}, ssl_record:connection_states()}| #alert{}. 99%% 100%% Description: Decode cipher text, use legacy type ssl_tls instead of tls_cipher_text 101%% in decoding context so that we can reuse the code from erlier versions. 102%%-------------------------------------------------------------------- 103decode_cipher_text(#ssl_tls{type = ?OPAQUE_TYPE, 104 version = ?LEGACY_VERSION, 105 fragment = CipherFragment}, 106 #{current_read := 107 #{sequence_number := Seq, 108 cipher_state := #cipher_state{key = Key, 109 iv = IV, 110 tag_len = TagLen}, 111 security_parameters := 112 #security_parameters{ 113 cipher_type = ?AEAD, 114 bulk_cipher_algorithm = 115 BulkCipherAlgo} 116 } = ReadState0} = ConnectionStates0) -> 117 case decipher_aead(CipherFragment, BulkCipherAlgo, Key, Seq, IV, TagLen) of 118 #alert{} = Alert -> 119 Alert; 120 PlainFragment -> 121 ConnectionStates = 122 ConnectionStates0#{current_read => 123 ReadState0#{sequence_number => Seq + 1}}, 124 {decode_inner_plaintext(PlainFragment), ConnectionStates} 125 end; 126 127 128%% RFC8446 - TLS 1.3 (OpenSSL compatibility) 129%% Handle unencrypted Alerts from openssl s_client when server's 130%% connection states are already stepped into traffic encryption. 131%% (E.g. openssl s_client receives a CertificateRequest with 132%% a signature_algorithms_cert extension that does not contain 133%% the signature algorithm of the client's certificate.) 134decode_cipher_text(#ssl_tls{type = ?ALERT, 135 version = ?LEGACY_VERSION, 136 fragment = <<2,47>>}, 137 ConnectionStates0) -> 138 {#ssl_tls{type = ?ALERT, 139 version = {3,4}, %% Internally use real version 140 fragment = <<2,47>>}, ConnectionStates0}; 141%% TLS 1.3 server can receive a User Cancelled Alert when handshake is 142%% paused and then cancelled on the client side. 143decode_cipher_text(#ssl_tls{type = ?ALERT, 144 version = ?LEGACY_VERSION, 145 fragment = <<2,90>>}, 146 ConnectionStates0) -> 147 {#ssl_tls{type = ?ALERT, 148 version = {3,4}, %% Internally use real version 149 fragment = <<2,90>>}, ConnectionStates0}; 150%% RFC8446 - TLS 1.3 151%% D.4. Middlebox Compatibility Mode 152%% - If not offering early data, the client sends a dummy 153%% change_cipher_spec record (see the third paragraph of Section 5) 154%% immediately before its second flight. This may either be before 155%% its second ClientHello or before its encrypted handshake flight. 156%% If offering early data, the record is placed immediately after the 157%% first ClientHello. 158decode_cipher_text(#ssl_tls{type = ?CHANGE_CIPHER_SPEC, 159 version = ?LEGACY_VERSION, 160 fragment = <<1>>}, 161 ConnectionStates0) -> 162 {#ssl_tls{type = ?CHANGE_CIPHER_SPEC, 163 version = {3,4}, %% Internally use real version 164 fragment = <<1>>}, ConnectionStates0}; 165decode_cipher_text(#ssl_tls{type = Type, 166 version = ?LEGACY_VERSION, 167 fragment = CipherFragment}, 168 #{current_read := 169 #{security_parameters := 170 #security_parameters{ 171 cipher_suite = ?TLS_NULL_WITH_NULL_NULL} 172 }} = ConnnectionStates0) -> 173 {#ssl_tls{type = Type, 174 version = {3,4}, %% Internally use real version 175 fragment = CipherFragment}, ConnnectionStates0}; 176decode_cipher_text(#ssl_tls{type = Type}, _) -> 177 %% Version mismatch is already asserted 178 ?ALERT_REC(?FATAL, ?BAD_RECORD_MAC, {record_type_mismatch, Type}). 179 180%%-------------------------------------------------------------------- 181%%% Internal functions 182%%-------------------------------------------------------------------- 183inner_plaintext(Type, Data, Length) -> 184 #inner_plaintext{ 185 content = Data, 186 type = Type, 187 zeros = zero_padding(Length) 188 }. 189zero_padding(Length)-> 190 binary:copy(<<?BYTE(0)>>, Length). 191 192encode_plain_text(#inner_plaintext{ 193 content = Data, 194 type = Type, 195 zeros = Zeros 196 }, #{cipher_state := #cipher_state{key= Key, 197 iv = IV, 198 tag_len = TagLen}, 199 sequence_number := Seq, 200 security_parameters := 201 #security_parameters{ 202 cipher_type = ?AEAD, 203 bulk_cipher_algorithm = BulkCipherAlgo} 204 }) -> 205 PlainText = [Data, Type, Zeros], 206 Encoded = cipher_aead(PlainText, BulkCipherAlgo, Key, Seq, IV, TagLen), 207 #tls_cipher_text{opaque_type = 23, %% 23 (application_data) for outward compatibility 208 legacy_version = {3,3}, 209 encoded_record = Encoded}; 210encode_plain_text(#inner_plaintext{ 211 content = Data, 212 type = Type 213 }, #{security_parameters := 214 #security_parameters{ 215 cipher_suite = ?TLS_NULL_WITH_NULL_NULL} 216 }) -> 217 %% RFC8446 - 5.1. Record Layer 218 %% When record protection has not yet been engaged, TLSPlaintext 219 %% structures are written directly onto the wire. 220 #tls_cipher_text{opaque_type = Type, 221 legacy_version = {3,3}, 222 encoded_record = Data}; 223 224encode_plain_text(_, CS) -> 225 exit({cs, CS}). 226 227additional_data(Length) -> 228 <<?BYTE(?OPAQUE_TYPE), ?BYTE(3), ?BYTE(3),?UINT16(Length)>>. 229 230%% The per-record nonce for the AEAD construction is formed as 231%% follows: 232%% 233%% 1. The 64-bit record sequence number is encoded in network byte 234%% order and padded to the left with zeros to iv_length. 235%% 236%% 2. The padded sequence number is XORed with either the static 237%% client_write_iv or server_write_iv (depending on the role). 238%% 239%% The resulting quantity (of length iv_length) is used as the 240%% per-record nonce. 241nonce(Seq, IV) -> 242 Padding = binary:copy(<<0>>, byte_size(IV) - 8), 243 crypto:exor(<<Padding/binary,?UINT64(Seq)>>, IV). 244 245cipher_aead(Fragment, BulkCipherAlgo, Key, Seq, IV, TagLen) -> 246 AAD = additional_data(erlang:iolist_size(Fragment) + TagLen), 247 Nonce = nonce(Seq, IV), 248 {Content, CipherTag} = 249 ssl_cipher:aead_encrypt(BulkCipherAlgo, Key, Nonce, Fragment, AAD, TagLen), 250 <<Content/binary, CipherTag/binary>>. 251 252encode_tls_cipher_text(#tls_cipher_text{opaque_type = Type, 253 legacy_version = {MajVer, MinVer}, 254 encoded_record = Encoded}, #{sequence_number := Seq} = Write) -> 255 Length = erlang:iolist_size(Encoded), 256 {[<<?BYTE(Type), ?BYTE(MajVer), ?BYTE(MinVer), ?UINT16(Length)>>, Encoded], 257 Write#{sequence_number => Seq +1}}. 258 259decipher_aead(CipherFragment, BulkCipherAlgo, Key, Seq, IV, TagLen) -> 260 try 261 AAD = additional_data(erlang:iolist_size(CipherFragment)), 262 Nonce = nonce(Seq, IV), 263 {CipherText, CipherTag} = aead_ciphertext_split(CipherFragment, TagLen), 264 case ssl_cipher:aead_decrypt(BulkCipherAlgo, Key, Nonce, CipherText, CipherTag, AAD) of 265 Content when is_binary(Content) -> 266 Content; 267 _ -> 268 ?ALERT_REC(?FATAL, ?BAD_RECORD_MAC, decryption_failed) 269 end 270 catch 271 _:_ -> 272 ?ALERT_REC(?FATAL, ?BAD_RECORD_MAC, decryption_failed) 273 end. 274 275 276aead_ciphertext_split(CipherTextFragment, TagLen) 277 when is_binary(CipherTextFragment) -> 278 CipherLen = erlang:byte_size(CipherTextFragment) - TagLen, 279 <<CipherText:CipherLen/bytes, CipherTag:TagLen/bytes>> = CipherTextFragment, 280 {CipherText, CipherTag}; 281aead_ciphertext_split(CipherTextFragment, TagLen) 282 when is_list(CipherTextFragment) -> 283 CipherLen = erlang:iolist_size(CipherTextFragment) - TagLen, 284 <<CipherText:CipherLen/bytes, CipherTag:TagLen/bytes>> = 285 erlang:iolist_to_binary(CipherTextFragment), 286 {CipherText, CipherTag}. 287 288decode_inner_plaintext(PlainText) -> 289 case binary:last(PlainText) of 290 0 -> 291 decode_inner_plaintext(init_binary(PlainText)); 292 Type when Type =:= ?APPLICATION_DATA orelse 293 Type =:= ?HANDSHAKE orelse 294 Type =:= ?ALERT -> 295 #ssl_tls{type = Type, 296 version = {3,4}, %% Internally use real version 297 fragment = init_binary(PlainText)}; 298 _Else -> 299 ?ALERT_REC(?FATAL, ?UNEXPECTED_MESSAGE, empty_alert) 300 end. 301 302init_binary(B) -> 303 {Init, _} = 304 split_binary(B, byte_size(B) - 1), 305 Init. 306