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