1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 2013-2018. 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%% Purpose: Help funtions for handling the DTLS (specific parts of)
22%%% SSL/TLS/DTLS handshake protocol
23%%----------------------------------------------------------------------
24-module(dtls_handshake).
25
26-include("dtls_connection.hrl").
27-include("dtls_handshake.hrl").
28-include("dtls_record.hrl").
29-include("ssl_internal.hrl").
30-include("ssl_alert.hrl").
31
32%% Handshake handling
33-export([client_hello/8, client_hello/9, cookie/4, hello/4,
34	 hello_verify_request/2]).
35
36%% Handshake encoding
37-export([fragment_handshake/2, encode_handshake/3]).
38
39%% Handshake decodeing
40-export([get_dtls_handshake/3]).
41
42-type dtls_handshake() :: #client_hello{} | #hello_verify_request{} |
43			  ssl_handshake:ssl_handshake().
44
45%%====================================================================
46%% Handshake handling
47%%====================================================================
48%%--------------------------------------------------------------------
49-spec client_hello(ssl:host(), inet:port_number(), ssl_record:connection_states(),
50		   #ssl_options{}, integer(), atom(), boolean(), der_cert()) ->
51			  #client_hello{}.
52%%
53%% Description: Creates a client hello message.
54%%--------------------------------------------------------------------
55client_hello(Host, Port, ConnectionStates, SslOpts,
56	     Cache, CacheCb, Renegotiation, OwnCert) ->
57    %% First client hello (two sent in DTLS ) uses empty Cookie
58    client_hello(Host, Port, <<>>, ConnectionStates, SslOpts,
59		 Cache, CacheCb, Renegotiation, OwnCert).
60
61%%--------------------------------------------------------------------
62-spec client_hello(ssl:host(), inet:port_number(), term(), ssl_record:connection_states(),
63		   #ssl_options{}, integer(), atom(), boolean(), der_cert()) ->
64			  #client_hello{}.
65%%
66%% Description: Creates a client hello message.
67%%--------------------------------------------------------------------
68client_hello(Host, Port, Cookie, ConnectionStates,
69	     #ssl_options{versions = Versions,
70			  ciphers = UserSuites,
71                          fallback = Fallback
72			 } = SslOpts,
73	     Cache, CacheCb, Renegotiation, OwnCert) ->
74    Version =  dtls_record:highest_protocol_version(Versions),
75    Pending = ssl_record:pending_connection_state(ConnectionStates, read),
76    SecParams = maps:get(security_parameters, Pending),
77    TLSVersion = dtls_v1:corresponding_tls_version(Version),
78    CipherSuites = ssl_handshake:available_suites(UserSuites, TLSVersion),
79
80    Extensions = ssl_handshake:client_hello_extensions(TLSVersion, CipherSuites,
81                                                       SslOpts, ConnectionStates,
82                                                       Renegotiation),
83    Id = ssl_session:client_id({Host, Port, SslOpts}, Cache, CacheCb, OwnCert),
84
85    #client_hello{session_id = Id,
86		  client_version = Version,
87		  cipher_suites =
88                      ssl_handshake:cipher_suites(CipherSuites,
89                                                  Renegotiation, Fallback),
90		  compression_methods = ssl_record:compressions(),
91		  random = SecParams#security_parameters.client_random,
92		  cookie = Cookie,
93		  extensions = Extensions
94		 }.
95
96hello(#server_hello{server_version = Version, random = Random,
97		    cipher_suite = CipherSuite,
98		    compression_method = Compression,
99		    session_id = SessionId, extensions = HelloExt},
100      #ssl_options{versions = SupportedVersions} = SslOpt,
101      ConnectionStates0, Renegotiation) ->
102    case dtls_record:is_acceptable_version(Version, SupportedVersions) of
103	true ->
104	    handle_server_hello_extensions(Version, SessionId, Random, CipherSuite,
105					   Compression, HelloExt, SslOpt,
106                                           ConnectionStates0, Renegotiation);
107	false ->
108	    ?ALERT_REC(?FATAL, ?PROTOCOL_VERSION)
109    end;
110hello(#client_hello{client_version = ClientVersion} = Hello,
111      #ssl_options{versions = Versions} = SslOpts,
112      Info, Renegotiation) ->
113    Version = ssl_handshake:select_version(dtls_record, ClientVersion, Versions),
114    handle_client_hello(Version, Hello, SslOpts, Info, Renegotiation).
115
116cookie(Key, Address, Port, #client_hello{client_version = {Major, Minor},
117					 random = Random,
118					 session_id = SessionId,
119					 cipher_suites = CipherSuites,
120					 compression_methods = CompressionMethods}) ->
121    CookieData = [address_to_bin(Address, Port),
122		  <<?BYTE(Major), ?BYTE(Minor)>>,
123		  Random, SessionId, CipherSuites, CompressionMethods],
124    crypto:hmac(sha, Key, CookieData).
125%%--------------------------------------------------------------------
126-spec hello_verify_request(binary(),  ssl_record:ssl_version()) -> #hello_verify_request{}.
127%%
128%% Description: Creates a hello verify request message sent by server to
129%% verify client
130%%--------------------------------------------------------------------
131hello_verify_request(Cookie, Version) ->
132    #hello_verify_request{protocol_version = Version, cookie = Cookie}.
133
134%%--------------------------------------------------------------------
135%%% Handshake encoding
136%%--------------------------------------------------------------------
137
138fragment_handshake(Bin, _) when is_binary(Bin)->
139    %% This is the change_cipher_spec not a "real handshake" but part of the flight
140    Bin;
141fragment_handshake([MsgType, Len, Seq, _, Len, Bin], Size) ->
142    Bins = bin_fragments(Bin, Size),
143    handshake_fragments(MsgType, Seq, Len, Bins, []).
144encode_handshake(Handshake, Version, Seq) ->
145    {MsgType, Bin} = enc_handshake(Handshake, Version),
146    Len = byte_size(Bin),
147    [MsgType, ?uint24(Len), ?uint16(Seq), ?uint24(0), ?uint24(Len), Bin].
148
149%%--------------------------------------------------------------------
150%%% Handshake decodeing
151%%--------------------------------------------------------------------
152
153%%--------------------------------------------------------------------
154-spec get_dtls_handshake(ssl_record:ssl_version(), binary(), #protocol_buffers{}) ->
155                                {[dtls_handshake()], #protocol_buffers{}}.
156%%
157%% Description:  Given buffered and new data from dtls_record, collects
158%% and returns it as a list of handshake messages, also returns
159%% possible leftover data in the new "protocol_buffers".
160%%--------------------------------------------------------------------
161get_dtls_handshake(Version, Fragment, ProtocolBuffers) ->
162    handle_fragments(Version, Fragment, ProtocolBuffers, []).
163
164%%--------------------------------------------------------------------
165%%% Internal functions
166%%--------------------------------------------------------------------
167handle_client_hello(Version,
168                    #client_hello{session_id = SugesstedId,
169                                  cipher_suites = CipherSuites,
170                                  compression_methods = Compressions,
171                                  random = Random,
172                                  extensions =
173                                      #hello_extensions{elliptic_curves = Curves,
174                                                        signature_algs = ClientHashSigns}
175                                  = HelloExt},
176		    #ssl_options{versions = Versions,
177				 signature_algs = SupportedHashSigns,
178                                 eccs = SupportedECCs,
179				 honor_ecc_order = ECCOrder} = SslOpts,
180		    {Port, Session0, Cache, CacheCb, ConnectionStates0, Cert, _},
181                    Renegotiation) ->
182    case dtls_record:is_acceptable_version(Version, Versions) of
183	true ->
184	    TLSVersion = dtls_v1:corresponding_tls_version(Version),
185	    AvailableHashSigns = ssl_handshake:available_signature_algs(
186				   ClientHashSigns, SupportedHashSigns, Cert,TLSVersion),
187	    ECCCurve = ssl_handshake:select_curve(Curves, SupportedECCs, ECCOrder),
188	    {Type, #session{cipher_suite = CipherSuite} = Session1}
189		= ssl_handshake:select_session(SugesstedId, CipherSuites,
190                                               AvailableHashSigns, Compressions,
191					       Port, Session0#session{ecc = ECCCurve}, TLSVersion,
192					       SslOpts, Cache, CacheCb, Cert),
193	    case CipherSuite of
194		no_suite ->
195		    ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY);
196		_ ->
197		    #{key_exchange := KeyExAlg} = ssl_cipher_format:suite_definition(CipherSuite),
198		    case ssl_handshake:select_hashsign(ClientHashSigns, Cert, KeyExAlg,
199						       SupportedHashSigns, TLSVersion) of
200			#alert{} = Alert ->
201			    Alert;
202			HashSign ->
203			    handle_client_hello_extensions(Version, Type, Random, CipherSuites, HelloExt,
204							   SslOpts, Session1, ConnectionStates0,
205							   Renegotiation, HashSign)
206		    end
207	    end;
208	false ->
209	    ?ALERT_REC(?FATAL, ?PROTOCOL_VERSION)
210    end.
211
212handle_client_hello_extensions(Version, Type, Random, CipherSuites,
213			HelloExt, SslOpts, Session0, ConnectionStates0, Renegotiation, HashSign) ->
214    try ssl_handshake:handle_client_hello_extensions(dtls_record, Random, CipherSuites,
215						     HelloExt, dtls_v1:corresponding_tls_version(Version),
216						     SslOpts, Session0,
217                                                     ConnectionStates0, Renegotiation) of
218	{Session, ConnectionStates, Protocol, ServerHelloExt} ->
219	    {Version, {Type, Session}, ConnectionStates, Protocol, ServerHelloExt, HashSign}
220    catch throw:Alert ->
221	    Alert
222    end.
223
224handle_server_hello_extensions(Version, SessionId, Random, CipherSuite,
225			       Compression, HelloExt, SslOpt, ConnectionStates0, Renegotiation) ->
226    try ssl_handshake:handle_server_hello_extensions(dtls_record, Random, CipherSuite,
227                                                     Compression, HelloExt,
228                                                     dtls_v1:corresponding_tls_version(Version),
229                                                     SslOpt, ConnectionStates0, Renegotiation) of
230	{ConnectionStates, ProtoExt, Protocol} ->
231	    {Version, SessionId, ConnectionStates, ProtoExt, Protocol}
232    catch throw:Alert ->
233	    Alert
234    end.
235
236%%--------------------------------------------------------------------
237
238enc_handshake(#hello_verify_request{protocol_version = {Major, Minor},
239 				       cookie = Cookie}, _Version) ->
240    CookieLength = byte_size(Cookie),
241    {?HELLO_VERIFY_REQUEST, <<?BYTE(Major), ?BYTE(Minor),
242 			      ?BYTE(CookieLength),
243 			      Cookie:CookieLength/binary>>};
244enc_handshake(#hello_request{}, _Version) ->
245    {?HELLO_REQUEST, <<>>};
246enc_handshake(#client_hello{client_version = {Major, Minor},
247			       random = Random,
248			       session_id = SessionID,
249			       cookie = Cookie,
250			       cipher_suites = CipherSuites,
251			       compression_methods = CompMethods,
252			       extensions = HelloExtensions}, _Version) ->
253    SIDLength = byte_size(SessionID),
254    CookieLength = byte_size(Cookie),
255    BinCompMethods = list_to_binary(CompMethods),
256    CmLength = byte_size(BinCompMethods),
257    BinCipherSuites = list_to_binary(CipherSuites),
258    CsLength = byte_size(BinCipherSuites),
259    ExtensionsBin = ssl_handshake:encode_hello_extensions(HelloExtensions),
260
261    {?CLIENT_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary,
262 		      ?BYTE(SIDLength), SessionID/binary,
263		      ?BYTE(CookieLength), Cookie/binary,
264		      ?UINT16(CsLength), BinCipherSuites/binary,
265 		      ?BYTE(CmLength), BinCompMethods/binary, ExtensionsBin/binary>>};
266enc_handshake(#server_hello{} = HandshakeMsg, Version) ->
267    {Type, <<?BYTE(Major), ?BYTE(Minor), Rest/binary>>} =
268	ssl_handshake:encode_handshake(HandshakeMsg, Version),
269    {DTLSMajor, DTLSMinor} = dtls_v1:corresponding_dtls_version({Major, Minor}),
270    {Type,  <<?BYTE(DTLSMajor), ?BYTE(DTLSMinor), Rest/binary>>};
271enc_handshake(HandshakeMsg, Version) ->
272    ssl_handshake:encode_handshake(HandshakeMsg, dtls_v1:corresponding_tls_version(Version)).
273
274handshake_bin(#handshake_fragment{
275		 type = Type,
276		 length = Len,
277		 message_seq = Seq,
278		 fragment_length = Len,
279		 fragment_offset = 0,
280		 fragment = Fragment}) ->
281    handshake_bin(Type, Len, Seq, Fragment).
282handshake_bin(Type, Length, Seq, FragmentData) ->
283    <<?BYTE(Type), ?UINT24(Length),
284      ?UINT16(Seq), ?UINT24(0), ?UINT24(Length),
285      FragmentData:Length/binary>>.
286
287bin_fragments(Bin, Size) ->
288     bin_fragments(Bin, size(Bin), Size, 0, []).
289bin_fragments(Bin, BinSize,  FragSize, Offset, Fragments) ->
290    case (BinSize - Offset - FragSize)  > 0 of
291	true ->
292	    Frag = binary:part(Bin, {Offset, FragSize}),
293	    bin_fragments(Bin, BinSize, FragSize, Offset + FragSize, [{Frag, Offset} | Fragments]);
294	false ->
295	    Frag = binary:part(Bin, {Offset, BinSize-Offset}),
296	    lists:reverse([{Frag, Offset} | Fragments])
297    end.
298
299handshake_fragments(_, _, _, [], Acc) ->
300    lists:reverse(Acc);
301handshake_fragments(MsgType, Seq, Len, [{Bin, Offset} | Bins], Acc) ->
302    FragLen = size(Bin),
303    handshake_fragments(MsgType, Seq, Len, Bins,
304      [<<?BYTE(MsgType), Len/binary, Seq/binary, ?UINT24(Offset),
305	 ?UINT24(FragLen), Bin/binary>> | Acc]).
306
307address_to_bin({A,B,C,D}, Port) ->
308    <<0:80,16#ffff:16,A,B,C,D,Port:16>>;
309address_to_bin({A,B,C,D,E,F,G,H}, Port) ->
310    <<A:16,B:16,C:16,D:16,E:16,F:16,G:16,H:16,Port:16>>.
311
312%%--------------------------------------------------------------------
313
314handle_fragments(Version, FragmentData, Buffers0, Acc) ->
315    Fragments = decode_handshake_fragments(FragmentData),
316    do_handle_fragments(Version, Fragments, Buffers0, Acc).
317
318do_handle_fragments(_, [], Buffers, Acc) ->
319    {lists:reverse(Acc), Buffers};
320do_handle_fragments(Version, [Fragment | Fragments], Buffers0, Acc) ->
321    case reassemble(Version, Fragment, Buffers0) of
322	{more_data, Buffers} when Fragments == [] ->
323	    {lists:reverse(Acc), Buffers};
324	{more_data, Buffers} ->
325	    do_handle_fragments(Version, Fragments, Buffers, Acc);
326	{HsPacket, Buffers} ->
327	    do_handle_fragments(Version, Fragments, Buffers, [HsPacket | Acc])
328    end.
329
330decode_handshake(Version, <<?BYTE(Type), Bin/binary>>) ->
331    decode_handshake(Version, Type, Bin).
332
333decode_handshake(_, ?HELLO_REQUEST, <<>>) ->
334    #hello_request{};
335decode_handshake(_Version, ?CLIENT_HELLO, <<?UINT24(_), ?UINT16(_),
336					    ?UINT24(_),  ?UINT24(_),
337					    ?BYTE(Major), ?BYTE(Minor), Random:32/binary,
338					    ?BYTE(SID_length), Session_ID:SID_length/binary,
339					    ?BYTE(CookieLength), Cookie:CookieLength/binary,
340					    ?UINT16(Cs_length), CipherSuites:Cs_length/binary,
341					    ?BYTE(Cm_length), Comp_methods:Cm_length/binary,
342					    Extensions/binary>>) ->
343
344    DecodedExtensions = ssl_handshake:decode_hello_extensions({client, Extensions}),
345
346    #client_hello{
347       client_version = {Major,Minor},
348       random = Random,
349       cookie = Cookie,
350       session_id = Session_ID,
351       cipher_suites = ssl_handshake:decode_suites('2_bytes', CipherSuites),
352       compression_methods = Comp_methods,
353       extensions = DecodedExtensions
354      };
355decode_handshake(_Version, ?HELLO_VERIFY_REQUEST, <<?UINT24(_), ?UINT16(_),
356						    ?UINT24(_),  ?UINT24(_),
357						    ?BYTE(Major), ?BYTE(Minor),
358						    ?BYTE(CookieLength),
359						    Cookie:CookieLength/binary>>) ->
360    #hello_verify_request{protocol_version = {Major, Minor},
361			  cookie = Cookie};
362decode_handshake(Version, Tag,  <<?UINT24(_), ?UINT16(_),
363				  ?UINT24(_),  ?UINT24(_), Msg/binary>>) ->
364    %% DTLS specifics stripped
365    decode_tls_thandshake(Version, Tag, Msg).
366
367decode_tls_thandshake(Version, Tag, Msg) ->
368    TLSVersion = dtls_v1:corresponding_tls_version(Version),
369    ssl_handshake:decode_handshake(TLSVersion, Tag, Msg).
370
371decode_handshake_fragments(<<>>) ->
372    [<<>>];
373decode_handshake_fragments(<<?BYTE(Type), ?UINT24(Length),
374			     ?UINT16(MessageSeq),
375			     ?UINT24(FragmentOffset), ?UINT24(FragmentLength),
376			    Fragment:FragmentLength/binary, Rest/binary>>) ->
377    [#handshake_fragment{type = Type,
378			length = Length,
379			message_seq = MessageSeq,
380			fragment_offset = FragmentOffset,
381			fragment_length = FragmentLength,
382			fragment = Fragment} | decode_handshake_fragments(Rest)].
383
384reassemble(Version,  #handshake_fragment{message_seq = Seq} = Fragment,
385	   #protocol_buffers{dtls_handshake_next_seq = Seq,
386			     dtls_handshake_next_fragments = Fragments0,
387			     dtls_handshake_later_fragments = LaterFragments0} =
388	       Buffers0)->
389    case reassemble_fragments(Fragment, Fragments0) of
390	{more_data, Fragments} ->
391	    {more_data,  Buffers0#protocol_buffers{dtls_handshake_next_fragments = Fragments}};
392	{raw, RawHandshake} ->
393	    Handshake = decode_handshake(Version, RawHandshake),
394	    {NextFragments, LaterFragments} = next_fragments(LaterFragments0),
395	    {{Handshake, RawHandshake}, Buffers0#protocol_buffers{dtls_handshake_next_seq = Seq + 1,
396						  dtls_handshake_next_fragments = NextFragments,
397						  dtls_handshake_later_fragments = LaterFragments}}
398    end;
399reassemble(_,  #handshake_fragment{message_seq = FragSeq} = Fragment,
400	   #protocol_buffers{dtls_handshake_next_seq = Seq,
401			     dtls_handshake_later_fragments = LaterFragments}
402           = Buffers0) when FragSeq > Seq->
403    {more_data,
404     Buffers0#protocol_buffers{dtls_handshake_later_fragments = [Fragment | LaterFragments]}};
405reassemble(_, _, Buffers) ->
406    %% Disregard fragments FragSeq < Seq
407    {more_data, Buffers}.
408
409reassemble_fragments(Current, Fragments0) ->
410    [Frag1 | Frags] = lists:keysort(#handshake_fragment.fragment_offset, [Current | Fragments0]),
411    [Fragment | _] = Fragments = merge_fragment(Frag1, Frags),
412    case is_complete_handshake(Fragment) of
413	true ->
414	    {raw, handshake_bin(Fragment)};
415	false ->
416	    {more_data, Fragments}
417    end.
418
419merge_fragment(Frag0, []) ->
420    [Frag0];
421merge_fragment(Frag0, [Frag1 | Rest]) ->
422    case merge_fragments(Frag0, Frag1) of
423	[_|_] = Frags ->
424	    Frags ++ Rest;
425	Frag ->
426	    merge_fragment(Frag, Rest)
427    end.
428%% Duplicate
429merge_fragments(#handshake_fragment{
430		   fragment_offset = PreviousOffSet,
431		   fragment_length = PreviousLen,
432		   fragment = PreviousData
433		  } = Previous,
434		#handshake_fragment{
435		   fragment_offset = PreviousOffSet,
436		   fragment_length = PreviousLen,
437		   fragment = PreviousData}) ->
438    Previous;
439
440%% Lager fragment save new data
441merge_fragments(#handshake_fragment{
442		   fragment_offset = PreviousOffSet,
443		   fragment_length = PreviousLen,
444		   fragment = PreviousData
445		  } = Previous,
446		#handshake_fragment{
447		   fragment_offset = PreviousOffSet,
448		   fragment_length = CurrentLen,
449		   fragment = CurrentData}) when CurrentLen > PreviousLen ->
450    NewLength = CurrentLen - PreviousLen,
451    <<_:PreviousLen/binary, NewData/binary>> = CurrentData,
452    Previous#handshake_fragment{
453      fragment_length = PreviousLen + NewLength,
454      fragment = <<PreviousData/binary, NewData/binary>>
455     };
456
457%% Smaller fragment
458merge_fragments(#handshake_fragment{
459		   fragment_offset = PreviousOffSet,
460		   fragment_length = PreviousLen
461		  } = Previous,
462		#handshake_fragment{
463		   fragment_offset = PreviousOffSet,
464		   fragment_length = CurrentLen}) when CurrentLen < PreviousLen ->
465    Previous;
466%% Next fragment, might be overlapping
467merge_fragments(#handshake_fragment{
468		   fragment_offset = PreviousOffSet,
469		   fragment_length = PreviousLen,
470		   fragment = PreviousData
471		  } = Previous,
472		#handshake_fragment{
473		   fragment_offset = CurrentOffSet,
474		   fragment_length = CurrentLen,
475                  fragment = CurrentData})
476  when PreviousOffSet + PreviousLen >= CurrentOffSet andalso
477       PreviousOffSet + PreviousLen < CurrentOffSet + CurrentLen ->
478    CurrentStart = PreviousOffSet + PreviousLen - CurrentOffSet,
479    <<_:CurrentStart/bytes, Data/binary>> = CurrentData,
480    Previous#handshake_fragment{
481      fragment_length =  PreviousLen + CurrentLen - CurrentStart,
482      fragment = <<PreviousData/binary, Data/binary>>};
483%% already fully contained fragment
484merge_fragments(#handshake_fragment{
485                   fragment_offset = PreviousOffSet,
486                   fragment_length = PreviousLen
487                  } = Previous,
488                #handshake_fragment{
489                   fragment_offset = CurrentOffSet,
490                   fragment_length = CurrentLen})
491  when PreviousOffSet + PreviousLen >= CurrentOffSet andalso
492       PreviousOffSet + PreviousLen >= CurrentOffSet + CurrentLen ->
493    Previous;
494
495%% No merge there is a gap
496merge_fragments(Previous, Current) ->
497    [Previous, Current].
498
499next_fragments(LaterFragments) ->
500    case lists:keysort(#handshake_fragment.message_seq, LaterFragments) of
501	[] ->
502	    {[], []};
503	[#handshake_fragment{message_seq = Seq} | _] = Fragments ->
504	    split_frags(Fragments, Seq, [])
505    end.
506
507split_frags([#handshake_fragment{message_seq = Seq} = Frag | Rest], Seq, Acc) ->
508    split_frags(Rest, Seq, [Frag | Acc]);
509split_frags(Frags, _, Acc) ->
510    {lists:reverse(Acc), Frags}.
511
512is_complete_handshake(#handshake_fragment{length = Length, fragment_length = Length}) ->
513    true;
514is_complete_handshake(_) ->
515    false.
516
517
518
519
520
521