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