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