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 21%%---------------------------------------------------------------------- 22%% Purpose: Help funtions for handling the TLS (specific parts of) 23%%% SSL/TLS/DTLS handshake protocol 24%%---------------------------------------------------------------------- 25 26-module(tls_handshake). 27 28-include("tls_handshake.hrl"). 29-include("tls_handshake_1_3.hrl"). 30-include("tls_record.hrl"). 31-include("ssl_alert.hrl"). 32-include("ssl_internal.hrl"). 33-include("ssl_cipher.hrl"). 34-include("ssl_api.hrl"). 35-include_lib("public_key/include/public_key.hrl"). 36-include_lib("kernel/include/logger.hrl"). 37 38%% Handshake handling 39-export([client_hello/9, hello/4]). 40 41%% Handshake encoding 42-export([encode_handshake/2]). 43 44%% Handshake decodeing 45-export([get_tls_handshake/4, decode_handshake/3]). 46 47-type tls_handshake() :: #client_hello{} | ssl_handshake:ssl_handshake(). 48 49%%==================================================================== 50%% Handshake handling 51%%==================================================================== 52%%-------------------------------------------------------------------- 53-spec client_hello(ssl:host(), inet:port_number(), ssl_record:connection_states(), 54 ssl_options(), binary(), boolean(), der_cert(), 55 #key_share_client_hello{} | undefined, tuple() | undefined) -> 56 #client_hello{}. 57%% 58%% Description: Creates a client hello message. 59%%-------------------------------------------------------------------- 60client_hello(_Host, _Port, ConnectionStates, 61 #{versions := Versions, 62 ciphers := UserSuites, 63 fallback := Fallback 64 } = SslOpts, 65 Id, Renegotiation, _OwnCert, KeyShare, TicketData) -> 66 Version = tls_record:highest_protocol_version(Versions), 67 68 %% In TLS 1.3, the client indicates its version preferences in the 69 %% "supported_versions" extension (Section 4.2.1) and the 70 %% legacy_version field MUST be set to 0x0303, which is the version 71 %% number for TLS 1.2. 72 LegacyVersion = 73 case tls_record:is_higher(Version, {3,2}) of 74 true -> 75 {3,3}; 76 false -> 77 Version 78 end, 79 #{security_parameters := SecParams} = 80 ssl_record:pending_connection_state(ConnectionStates, read), 81 AvailableCipherSuites = ssl_handshake:available_suites(UserSuites, Version), 82 Extensions = ssl_handshake:client_hello_extensions(Version, 83 AvailableCipherSuites, 84 SslOpts, ConnectionStates, 85 Renegotiation, 86 KeyShare, 87 TicketData), 88 CipherSuites = ssl_handshake:cipher_suites(AvailableCipherSuites, Renegotiation, Fallback), 89 #client_hello{session_id = Id, 90 client_version = LegacyVersion, 91 cipher_suites = CipherSuites, 92 compression_methods = ssl_record:compressions(), 93 random = SecParams#security_parameters.client_random, 94 extensions = Extensions 95 }. 96 97%%-------------------------------------------------------------------- 98-spec hello(#server_hello{} | #client_hello{}, ssl_options(), 99 ssl_record:connection_states() | {inet:port_number(), #session{}, db_handle(), 100 atom(), ssl_record:connection_states(), 101 binary() | undefined, ssl:kex_algo()}, 102 boolean()) -> 103 {tls_record:tls_version(), ssl:session_id(), 104 ssl_record:connection_states(), alpn | npn, binary() | undefined}| 105 {tls_record:tls_version(), {resumed | new, #session{}}, 106 ssl_record:connection_states(), binary() | undefined, 107 HelloExt::map(), {ssl:hash(), ssl:sign_algo()} | 108 undefined} | {atom(), atom()} | {atom(), atom(), tuple()} | #alert{}. 109%% 110%% Description: Handles a received hello message 111%%-------------------------------------------------------------------- 112 113 114%% TLS 1.3 - Section 4.1.3 115%% TLS 1.3 clients receiving a ServerHello indicating TLS 1.2 or below 116%% MUST check that the last eight bytes are not equal to either of these 117%% values. 118hello(#server_hello{server_version = {Major, Minor}, 119 random = <<_:24/binary,Down:8/binary>>}, 120 #{versions := [{M,N}|_]}, _, _) 121 when (M > 3 orelse M =:= 3 andalso N >= 4) andalso %% TLS 1.3 client 122 (Major =:= 3 andalso Minor =:= 3 andalso %% Negotiating TLS 1.2 123 Down =:= ?RANDOM_OVERRIDE_TLS12) orelse 124 125 (M > 3 orelse M =:= 3 andalso N >= 4) andalso %% TLS 1.3 client 126 (Major =:= 3 andalso Minor < 3 andalso %% Negotiating TLS 1.1 or prior 127 Down =:= ?RANDOM_OVERRIDE_TLS11) -> 128 ?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER); 129 130%% TLS 1.2 clients SHOULD also check that the last eight bytes are not 131%% equal to the second value if the ServerHello indicates TLS 1.1 or below. 132hello(#server_hello{server_version = {Major, Minor}, 133 random = <<_:24/binary,Down:8/binary>>}, 134 #{versions := [{M,N}|_]}, _, _) 135 when (M =:= 3 andalso N =:= 3) andalso %% TLS 1.2 client 136 (Major =:= 3 andalso Minor < 3 andalso %% Negotiating TLS 1.1 or prior 137 Down =:= ?RANDOM_OVERRIDE_TLS11) -> 138 ?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER); 139 140 141%% TLS 1.3 - 4.2.1. Supported Versions 142%% If the "supported_versions" extension in the ServerHello contains a 143%% version not offered by the client or contains a version prior to TLS 144%% 1.3, the client MUST abort the handshake with an "illegal_parameter" 145%% alert. 146%%-------------------------------------------------------------------- 147%% TLS 1.2 Client 148%% 149%% - If "supported_version" is present (ServerHello): 150%% - Abort handshake with an "illegal_parameter" alert 151hello(#server_hello{server_version = LegacyVersion, 152 random = Random, 153 cipher_suite = CipherSuite, 154 compression_method = Compression, 155 session_id = SessionId, 156 extensions = #{server_hello_selected_version := 157 #server_hello_selected_version{selected_version = Version} = HelloExt} 158 }, 159 #{versions := SupportedVersions} = SslOpt, 160 ConnectionStates0, Renegotiation) -> 161 %% In TLS 1.3, the TLS server indicates its version using the "supported_versions" extension 162 %% (Section 4.2.1), and the legacy_version field MUST be set to 0x0303, which is the version 163 %% number for TLS 1.2. 164 %% The "supported_versions" extension is supported from TLS 1.2. 165 case LegacyVersion > {3,3} orelse 166 LegacyVersion =:= {3,3} andalso Version < {3,3} of 167 true -> 168 ?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER); 169 false -> 170 case tls_record:is_acceptable_version(Version, SupportedVersions) of 171 true -> 172 case Version of 173 {3,3} -> 174 %% TLS 1.2 ServerHello with "supported_versions" (special case) 175 handle_server_hello_extensions(Version, SessionId, Random, CipherSuite, 176 Compression, HelloExt, SslOpt, 177 ConnectionStates0, Renegotiation); 178 SelectedVersion -> 179 %% TLS 1.3 180 {next_state, wait_sh, SelectedVersion} 181 end; 182 false -> 183 ?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER) 184 end 185 end; 186 187hello(#server_hello{server_version = Version, 188 random = Random, 189 cipher_suite = CipherSuite, 190 compression_method = Compression, 191 session_id = SessionId, 192 extensions = HelloExt}, 193 #{versions := SupportedVersions} = SslOpt, 194 ConnectionStates0, Renegotiation) -> 195 case tls_record:is_acceptable_version(Version, SupportedVersions) of 196 true -> 197 handle_server_hello_extensions(Version, SessionId, Random, CipherSuite, 198 Compression, HelloExt, SslOpt, 199 ConnectionStates0, Renegotiation); 200 false -> 201 ?ALERT_REC(?FATAL, ?PROTOCOL_VERSION) 202 end; 203 204 205%% TLS 1.2 Server 206%% - If "supported_versions" is present (ClientHello): 207%% - Select version from "supported_versions" (ignore ClientHello.legacy_version) 208%% - If server only supports versions greater than "supported_versions": 209%% - Abort handshake with a "protocol_version" alert (*) 210%% - If "supported_versions" is absent (ClientHello): 211%% - Negotiate the minimum of ClientHello.legacy_version and TLS 1.2 (**) 212%% - If server only supports versions greater than ClientHello.legacy_version: 213%% - Abort handshake with a "protocol_version" alert 214%% 215%% (*) Sends alert even if there is a gap in supported versions 216%% e.g. Server 1.0,1.2 Client 1.1,1.3 217%% (**) Current implementation can negotiate a version not supported by the client 218%% e.g. Server 1.0,1.2 Client 1.1 -> ServerHello 1.0 219hello(#client_hello{client_version = _ClientVersion, 220 cipher_suites = CipherSuites, 221 extensions = #{client_hello_versions := 222 #client_hello_versions{versions = ClientVersions} 223 }} = Hello, 224 #{versions := Versions} = SslOpts, 225 Info, Renegotiation) -> 226 try 227 Version = ssl_handshake:select_supported_version(ClientVersions, Versions), 228 do_hello(Version, Versions, CipherSuites, Hello, SslOpts, Info, Renegotiation) 229 catch 230 _:_ -> 231 ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, malformed_handshake_data) 232 end; 233 234hello(#client_hello{client_version = ClientVersion, 235 cipher_suites = CipherSuites} = Hello, 236 #{versions := Versions} = SslOpts, 237 Info, Renegotiation) -> 238 try 239 Version = ssl_handshake:select_version(tls_record, ClientVersion, Versions), 240 do_hello(Version, Versions, CipherSuites, Hello, SslOpts, Info, Renegotiation) 241 catch 242 error:{case_clause,{asn1, Asn1Reason}} -> 243 %% ASN-1 decode of certificate somehow failed 244 ?ALERT_REC(?FATAL, ?INTERNAL_ERROR, {failed_to_decode_own_certificate, Asn1Reason}); 245 _:_ -> 246 ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, malformed_handshake_data) 247 end. 248 249 250%%-------------------------------------------------------------------- 251%%% Handshake encodeing 252%%-------------------------------------------------------------------- 253 254%%-------------------------------------------------------------------- 255-spec encode_handshake(tls_handshake() | tls_handshake_1_3:tls_handshake_1_3(), 256 tls_record:tls_version()) -> iolist(). 257%% 258%% Description: Encode a handshake packet 259%%-------------------------------------------------------------------- 260encode_handshake(Package, Version) -> 261 {MsgType, Bin} = enc_handshake(Package, Version), 262 Len = byte_size(Bin), 263 [MsgType, ?uint24(Len), Bin]. 264 265 266%%-------------------------------------------------------------------- 267%%% Handshake decodeing 268%%-------------------------------------------------------------------- 269 270%%-------------------------------------------------------------------- 271-spec get_tls_handshake(tls_record:tls_version(), binary(), binary() | iolist(), 272 ssl_options()) -> 273 {[{tls_handshake(), binary()}], binary()}. 274%% 275%% Description: Given buffered and new data from ssl_record, collects 276%% and returns it as a list of handshake messages, also returns leftover 277%% data. 278%%-------------------------------------------------------------------- 279get_tls_handshake(Version, Data, <<>>, Options) -> 280 get_tls_handshake_aux(Version, Data, Options, []); 281get_tls_handshake(Version, Data, Buffer, Options) -> 282 get_tls_handshake_aux(Version, list_to_binary([Buffer, Data]), Options, []). 283 284%%-------------------------------------------------------------------- 285%%% Internal functions 286%%-------------------------------------------------------------------- 287handle_client_hello(Version, 288 #client_hello{session_id = SugesstedId, 289 cipher_suites = CipherSuites, 290 compression_methods = Compressions, 291 random = Random, 292 extensions = HelloExt}, 293 #{versions := Versions, 294 signature_algs := SupportedHashSigns, 295 eccs := SupportedECCs, 296 honor_ecc_order := ECCOrder} = SslOpts, 297 {Port, Session0, Cache, CacheCb, ConnectionStates0, Cert, _}, 298 Renegotiation) -> 299 case tls_record:is_acceptable_version(Version, Versions) of 300 true -> 301 Curves = maps:get(elliptic_curves, HelloExt, undefined), 302 ClientHashSigns = maps:get(signature_algs, HelloExt, undefined), 303 ClientSignatureSchemes = maps:get(signature_algs_cert, HelloExt, undefined), 304 AvailableHashSigns = ssl_handshake:available_signature_algs( 305 ClientHashSigns, SupportedHashSigns, Cert, Version), 306 ECCCurve = ssl_handshake:select_curve(Curves, SupportedECCs, ECCOrder), 307 {Type, #session{cipher_suite = CipherSuite} = Session1} 308 = ssl_handshake:select_session(SugesstedId, CipherSuites, 309 AvailableHashSigns, Compressions, 310 Port, Session0#session{ecc = ECCCurve}, 311 Version, SslOpts, Cache, CacheCb, Cert), 312 case CipherSuite of 313 no_suite -> 314 ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, no_suitable_ciphers); 315 _ -> 316 #{key_exchange := KeyExAlg} = ssl_cipher_format:suite_bin_to_map(CipherSuite), 317 case ssl_handshake:select_hashsign({ClientHashSigns, ClientSignatureSchemes}, 318 Cert, KeyExAlg, 319 SupportedHashSigns, 320 Version) of 321 #alert{} = Alert -> 322 Alert; 323 HashSign -> 324 handle_client_hello_extensions(Version, Type, Random, 325 CipherSuites, HelloExt, 326 SslOpts, Session1, 327 ConnectionStates0, 328 Renegotiation, HashSign) 329 end 330 end; 331 false -> 332 ?ALERT_REC(?FATAL, ?PROTOCOL_VERSION) 333 end. 334 335handle_client_hello_extensions(Version, Type, Random, CipherSuites, 336 HelloExt, SslOpts, Session0, ConnectionStates0, 337 Renegotiation, HashSign) -> 338 try ssl_handshake:handle_client_hello_extensions(tls_record, Random, CipherSuites, 339 HelloExt, Version, SslOpts, 340 Session0, ConnectionStates0, 341 Renegotiation) of 342 {Session, ConnectionStates, Protocol, ServerHelloExt} -> 343 {Version, {Type, Session}, ConnectionStates, Protocol, 344 ServerHelloExt, HashSign} 345 catch throw:Alert -> 346 Alert 347 end. 348 349 350handle_server_hello_extensions(Version, SessionId, Random, CipherSuite, 351 Compression, HelloExt, SslOpt, ConnectionStates0, Renegotiation) -> 352 try ssl_handshake:handle_server_hello_extensions(tls_record, Random, CipherSuite, 353 Compression, HelloExt, Version, 354 SslOpt, ConnectionStates0, 355 Renegotiation) of 356 {ConnectionStates, ProtoExt, Protocol} -> 357 {Version, SessionId, ConnectionStates, ProtoExt, Protocol} 358 catch throw:Alert -> 359 Alert 360 end. 361 362 363do_hello(undefined, _Versions, _CipherSuites, _Hello, _SslOpts, _Info, _Renegotiation) -> 364 ?ALERT_REC(?FATAL, ?PROTOCOL_VERSION); 365do_hello(Version, Versions, CipherSuites, Hello, SslOpts, Info, Renegotiation) -> 366 case ssl_cipher:is_fallback(CipherSuites) of 367 true -> 368 Highest = tls_record:highest_protocol_version(Versions), 369 case tls_record:is_higher(Highest, Version) of 370 true -> 371 ?ALERT_REC(?FATAL, ?INAPPROPRIATE_FALLBACK); 372 false -> 373 handle_client_hello(Version, Hello, SslOpts, Info, Renegotiation) 374 end; 375 false -> 376 handle_client_hello(Version, Hello, SslOpts, Info, Renegotiation) 377 end. 378 379%%-------------------------------------------------------------------- 380enc_handshake(#hello_request{}, {3, N}) when N < 4 -> 381 {?HELLO_REQUEST, <<>>}; 382enc_handshake(#client_hello{client_version = {Major, Minor} = Version, 383 random = Random, 384 session_id = SessionID, 385 cipher_suites = CipherSuites, 386 compression_methods = CompMethods, 387 extensions = HelloExtensions}, _Version) -> 388 SIDLength = byte_size(SessionID), 389 BinCompMethods = list_to_binary(CompMethods), 390 CmLength = byte_size(BinCompMethods), 391 BinCipherSuites = list_to_binary(CipherSuites), 392 CsLength = byte_size(BinCipherSuites), 393 ExtensionsBin = ssl_handshake:encode_hello_extensions(HelloExtensions, Version), 394 395 {?CLIENT_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary, 396 ?BYTE(SIDLength), SessionID/binary, 397 ?UINT16(CsLength), BinCipherSuites/binary, 398 ?BYTE(CmLength), BinCompMethods/binary, ExtensionsBin/binary>>}; 399enc_handshake(HandshakeMsg, {3, 4}) -> 400 tls_handshake_1_3:encode_handshake(HandshakeMsg); 401enc_handshake(HandshakeMsg, Version) -> 402 ssl_handshake:encode_handshake(HandshakeMsg, Version). 403 404%%-------------------------------------------------------------------- 405get_tls_handshake_aux(Version, <<?BYTE(Type), ?UINT24(Length), 406 Body:Length/binary,Rest/binary>>, 407 #{log_level := LogLevel} = Opts, Acc) -> 408 Raw = <<?BYTE(Type), ?UINT24(Length), Body/binary>>, 409 try decode_handshake(Version, Type, Body) of 410 Handshake -> 411 ssl_logger:debug(LogLevel, inbound, 'handshake', Handshake), 412 get_tls_handshake_aux(Version, Rest, Opts, [{Handshake,Raw} | Acc]) 413 catch 414 throw:#alert{} = Alert -> 415 throw(Alert); 416 _:_ -> 417 throw(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, handshake_decode_error)) 418 end; 419get_tls_handshake_aux(_Version, Data, _, Acc) -> 420 {lists:reverse(Acc), Data}. 421 422decode_handshake({3, N}, ?HELLO_REQUEST, <<>>) when N < 4 -> 423 #hello_request{}; 424decode_handshake(Version, ?CLIENT_HELLO, 425 <<?BYTE(Major), ?BYTE(Minor), Random:32/binary, 426 ?BYTE(SID_length), Session_ID:SID_length/binary, 427 ?UINT16(Cs_length), CipherSuites:Cs_length/binary, 428 ?BYTE(Cm_length), Comp_methods:Cm_length/binary, 429 Extensions/binary>>) -> 430 Exts = ssl_handshake:decode_vector(Extensions), 431 DecodedExtensions = ssl_handshake:decode_hello_extensions(Exts, Version, {Major, Minor}, 432 client_hello), 433 #client_hello{ 434 client_version = {Major,Minor}, 435 random = Random, 436 session_id = Session_ID, 437 cipher_suites = ssl_handshake:decode_suites('2_bytes', CipherSuites), 438 compression_methods = erlang:binary_to_list(Comp_methods), 439 extensions = DecodedExtensions 440 }; 441decode_handshake({3, 4}, Tag, Msg) -> 442 tls_handshake_1_3:decode_handshake(Tag, Msg); 443decode_handshake(Version, Tag, Msg) -> 444 ssl_handshake:decode_handshake(Version, Tag, Msg). 445