From f3be514fd1e015f78a227d25c3471dbe2cfb3d51 Mon Sep 17 00:00:00 2001 From: Ingela Anderton Andin Date: Tue, 11 Jun 2013 20:44:04 +0200 Subject: ssl: Refactor to provide common handshake functions for TLS/DTLS Common functions will be located in ssl_handshake.erl while specific functions will be located in tls_handshake.erl and dtls_handshake.erl --- lib/ssl/src/dtls_connection.erl | 316 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 316 insertions(+) (limited to 'lib/ssl/src/dtls_connection.erl') diff --git a/lib/ssl/src/dtls_connection.erl b/lib/ssl/src/dtls_connection.erl index ac2ee0d09f..f6a1dc699e 100644 --- a/lib/ssl/src/dtls_connection.erl +++ b/lib/ssl/src/dtls_connection.erl @@ -17,3 +17,319 @@ %% %CopyrightEnd% %% -module(dtls_connection). + +%%-behaviour(gen_fsm). + +%% -include("dtls_handshake.hrl"). +%% -include("ssl_alert.hrl"). +%% -include("dtls_record.hrl"). +%% -include("ssl_cipher.hrl"). +%% -include("ssl_internal.hrl"). +%% -include("ssl_srp.hrl"). +%% -include_lib("public_key/include/public_key.hrl"). + + +%% %% Called by dtls_connection_sup +%% %%-export([start_link/7]). + +%% %% gen_fsm callbacks +%% -export([init/1, hello/2, certify/2, cipher/2, +%% abbreviated/2, connection/2, handle_event/3, +%% handle_sync_event/4, handle_info/3, terminate/3, code_change/4]). + +%% -record(message_sequences, { +%% read = 0, +%% write = 0 +%% }). + +%% -record(state, { +%% role, % client | server +%% user_application, % {MonitorRef, pid()} +%% transport_cb, % atom() - callback module +%% data_tag, % atom() - ex tcp. +%% close_tag, % atom() - ex tcp_closed +%% error_tag, % atom() - ex tcp_error +%% host, % string() | ipadress() +%% port, % integer() +%% socket, % socket() +%% ssl_options, % #ssl_options{} +%% socket_options, % #socket_options{} +%% connection_states, % #connection_states{} from ssl_record.hrl +%% message_sequences = #message_sequences{}, +%% dtls_packets = [], % Not yet handled decode ssl/tls packets. +%% dtls_record_buffer, % binary() buffer of incomplete records +%% dtls_handshake_buffer, % binary() buffer of incomplete handshakes +%% dtls_handshake_history, % tls_handshake_history() +%% dtls_cipher_texts, % list() received but not deciphered yet +%% cert_db, % +%% session, % #session{} from tls_handshake.hrl +%% session_cache, % +%% session_cache_cb, % +%% negotiated_version, % tls_version() +%% client_certificate_requested = false, +%% key_algorithm, % atom as defined by cipher_suite +%% hashsign_algorithm, % atom as defined by cipher_suite +%% public_key_info, % PKIX: {Algorithm, PublicKey, PublicKeyParams} +%% private_key, % PKIX: #'RSAPrivateKey'{} +%% diffie_hellman_params, % PKIX: #'DHParameter'{} relevant for server side +%% diffie_hellman_keys, % {PublicKey, PrivateKey} +%% psk_identity, % binary() - server psk identity hint +%% srp_params, % #srp_user{} +%% srp_keys, % {PublicKey, PrivateKey} +%% premaster_secret, % +%% file_ref_db, % ets() +%% cert_db_ref, % ref() +%% bytes_to_read, % integer(), # bytes to read in passive mode +%% user_data_buffer, % binary() +%% log_alert, % boolean() +%% renegotiation, % {boolean(), From | internal | peer} +%% start_or_recv_from, % "gen_fsm From" +%% timer, % start_or_recv_timer +%% send_queue, % queue() +%% terminated = false, % +%% allow_renegotiate = true, +%% expecting_next_protocol_negotiation = false :: boolean(), +%% next_protocol = undefined :: undefined | binary(), +%% client_ecc, % {Curves, PointFmt} +%% client_cookie = <<>> +%% }). + + + +%% %%==================================================================== +%% %% Internal application API +%% %%==================================================================== + + +%% %%==================================================================== +%% %% State functions +%% %%==================================================================== + +%% -spec hello(start | #hello_request{} | #client_hello{} | #server_hello{} | term(), +%% #state{}) -> gen_fsm_state_return(). +%% %%-------------------------------------------------------------------- +%% hello(start, #state{host = Host, port = Port, role = client, +%% ssl_options = SslOpts, +%% session = #session{own_certificate = Cert} = Session0, +%% session_cache = Cache, session_cache_cb = CacheCb, +%% connection_states = ConnectionStates0, +%% renegotiation = {Renegotiation, _}, +%% client_cookie = Cookie} = State0) -> +%% Hello = dtls_handshake:client_hello(Host, Port, Cookie, ConnectionStates0, SslOpts, +%% Cache, CacheCb, Renegotiation, Cert), + +%% Version = Hello#client_hello.client_version, +%% State1 = State0#state{negotiated_version = Version, %% Requested version +%% session = +%% Session0#session{session_id = Hello#client_hello.session_id}, +%% dtls_handshake_history = ssl_handshake:init_handshake_history()}, + +%% State2 = send_flight(Hello, waiting, State1), + +%% {Record, State} = next_record(State2), +%% next_state(hello, hello, Record, State); + +%% hello(start, #state{role = server} = State0) -> +%% {Record, State} = next_record(State0), +%% next_state(hello, hello, Record, State); + +%% hello(#hello_request{}, #state{role = client} = State0) -> +%% {Record, State} = next_record(State0), +%% next_state(hello, hello, Record, State); + +%% hello(#server_hello{cipher_suite = CipherSuite, +%% compression_method = Compression} = Hello, +%% #state{session = #session{session_id = OldId}, +%% connection_states = ConnectionStates0, +%% role = client, +%% negotiated_version = ReqVersion, +%% renegotiation = {Renegotiation, _}, +%% ssl_options = SslOptions} = State1) -> +%% State0 = flight_done(State1), +%% case ssl_handshake:hello(Hello, SslOptions, ConnectionStates0, Renegotiation) of +%% #alert{} = Alert -> +%% handle_own_alert(Alert, ReqVersion, hello, State0); +%% {Version, NewId, ConnectionStates, NextProtocol} -> +%% {KeyAlgorithm, _, _, _} = +%% ssl_cipher:suite_definition(CipherSuite), + +%% PremasterSecret = make_premaster_secret(ReqVersion, KeyAlgorithm), + +%% NewNextProtocol = case NextProtocol of +%% undefined -> +%% State0#state.next_protocol; +%% _ -> +%% NextProtocol +%% end, + +%% State = State0#state{key_algorithm = KeyAlgorithm, +%% hashsign_algorithm = default_hashsign(Version, KeyAlgorithm), +%% negotiated_version = Version, +%% connection_states = ConnectionStates, +%% premaster_secret = PremasterSecret, +%% expecting_next_protocol_negotiation = NextProtocol =/= undefined, +%% next_protocol = NewNextProtocol}, + +%% case ssl_session:is_new(OldId, NewId) of +%% true -> +%% handle_new_session(NewId, CipherSuite, Compression, +%% State#state{connection_states = ConnectionStates}); +%% false -> +%% handle_resumed_session(NewId, State#state{connection_states = ConnectionStates}) +%% end +%% end; + +%% hello(#hello_verify_request{cookie = Cookie}, +%% #state{host = Host, port = Port, +%% session = #session{own_certificate = Cert}, +%% session_cache = Cache, session_cache_cb = CacheCb, +%% ssl_options = SslOpts, +%% connection_states = ConnectionStates0, +%% renegotiation = {Renegotiation, _}} = State0) -> +%% Hello = ssl_handshake:client_hello(Host, Port, Cookie, ConnectionStates0, SslOpts, +%% Cache, CacheCb, Renegotiation, Cert), +%% State1 = State0#state{ +%% tls_handshake_history = ssl_handshake:init_handshake_history(), +%% client_cookie = Cookie}, +%% State2 = send_flight(Hello, waiting, State1), + +%% {Record, State} = next_record(State2), +%% next_state(hello, hello, Record, State); + +%% hello(Hello = #client_hello{client_version = ClientVersion}, +%% State = #state{connection_states = ConnectionStates0, +%% port = Port, session = #session{own_certificate = Cert} = Session0, +%% renegotiation = {Renegotiation, _}, +%% session_cache = Cache, +%% session_cache_cb = CacheCb, +%% ssl_options = SslOpts}) -> +%% case ssl_handshake:hello(Hello, SslOpts, {Port, Session0, Cache, CacheCb, +%% ConnectionStates0, Cert}, Renegotiation) of +%% {Version, {Type, Session}, ConnectionStates, ProtocolsToAdvertise, +%% EcPointFormats, EllipticCurves} -> +%% do_server_hello(Type, ProtocolsToAdvertise, +%% EcPointFormats, EllipticCurves, +%% State#state{connection_states = ConnectionStates, +%% negotiated_version = Version, +%% session = Session, +%% client_ecc = {EllipticCurves, EcPointFormats}}); +%% #alert{} = Alert -> +%% handle_own_alert(Alert, ClientVersion, hello, State) +%% end; + +%% hello(timeout, State) -> +%% { next_state, hello, State, hibernate }; + +%% hello(Msg, State) -> +%% handle_unexpected_message(Msg, hello, State). +%% %%-------------------------------------------------------------------- +%% -spec abbreviated(#hello_request{} | #finished{} | term(), +%% #state{}) -> gen_fsm_state_return(). +%% %%-------------------------------------------------------------------- + +%% abbreviated(timeout, State) -> +%% { next_state, abbreviated, State, hibernate }; + +%% abbreviated(Msg, State) -> +%% handle_unexpected_message(Msg, abbreviated, State). + +%% %%-------------------------------------------------------------------- +%% -spec certify(#hello_request{} | #certificate{} | #server_key_exchange{} | +%% #certificate_request{} | #server_hello_done{} | #client_key_exchange{} | term(), +%% #state{}) -> gen_fsm_state_return(). +%% %%-------------------------------------------------------------------- + + +%% certify(timeout, State) -> +%% { next_state, certify, State, hibernate }; + +%% certify(Msg, State) -> +%% handle_unexpected_message(Msg, certify, State). + + +%% %%-------------------------------------------------------------------- +%% -spec cipher(#hello_request{} | #certificate_verify{} | #finished{} | term(), +%% #state{}) -> gen_fsm_state_return(). +%% %%-------------------------------------------------------------------- + +%% cipher(timeout, State) -> +%% { next_state, cipher, State, hibernate }; + +%% cipher(Msg, State) -> +%% handle_unexpected_message(Msg, cipher, State). + +%% %%-------------------------------------------------------------------- +%% -spec connection(#hello_request{} | #client_hello{} | term(), +%% #state{}) -> gen_fsm_state_return(). +%% %%-------------------------------------------------------------------- + +%% connection(timeout, State) -> +%% {next_state, connection, State, hibernate}; + +%% connection(Msg, State) -> +%% handle_unexpected_message(Msg, connection, State). + +%% %%-------------------------------------------------------------------- +%% %%% Internal functions +%% %%-------------------------------------------------------------------- +%% handle_unexpected_message(Msg, Info, #state{negotiated_version = Version} = State) -> +%% Alert = ?ALERT_REC(?FATAL,?UNEXPECTED_MESSAGE), +%% handle_own_alert(Alert, Version, {Info, Msg}, State). + +%% send_flight(HandshakeRec, FlightState, State) -> +%% send_flight(FlightState, buffer_flight(HandshakeRec, State)). + +%% send_flight(FlightState, State = #state{negotiated_version = Version, +%% flight_buffer = Buffer}) -> + +%% State1 = do_send_flight(queue:to_list(Buffer), [], State), +%% finish_send_flight(Version, FlightState, State1). + +%% resend_flight(State = #state{negotiated_version = Version, +%% flight_state = FlightState, +%% flight_buffer = Buffer}) +%% when FlightState == finished; FlightState == waiting -> +%% State1 = do_send_flight(queue:to_list(Buffer), [], State), +%% finish_send_flight(Version, FlightState, State1); + +%% resend_flight(State) -> +%% State. + +%% flight_done(State) -> +%% cancel_dtls_retransmit_timer(State#state{flight_state = done, +%% flight_buffer = undefined}). + +%% do_send_flight([], BinMsgs, State = #state{transport_cb = Transport, socket = Socket}) -> +%% Transport:send(Socket, lists:reverse(BinMsgs)), +%% State; +%% do_send_flight([{Epoch, MsgSeq, HandshakeRec}|T], BinMsgs0, +%% State = #state{negotiated_version = Version, +%% connection_states = ConnectionStates0}) -> +%% CS0 = ssl_record:connection_state_by_epoch(ConnectionStates0, Epoch, write), +%% {BinMsgs, CS1} = encode_handshake_rec(HandshakeRec, Version, MsgSeq, BinMsgs0, CS0), +%% ConnectionStates1 = ssl_record:set_connection_state_by_epoch(ConnectionStates0, CS1, write), +%% do_send_flight(T, BinMsgs, State#state{connection_states = ConnectionStates1}). + +%% cancel_dtls_retransmit_timer(State = #state{dtls_retransmit_timer = TimerRef}) -> +%% cancel_timer(TimerRef), +%% State#state{dtls_retransmit_timer = undefined}. + +%% rearm_dtls_retransmit_timer(State = #state{dtls_retransmit_timer = undefined}) -> +%% TimerRef = erlang:start_timer(1000, self(), dtls_retransmit), +%% State#state{dtls_retransmit_timer = TimerRef}; +%% rearm_dtls_retransmit_timer(State) -> +%% State. + +%% finish_send_flight({254, _}, waiting, State) -> +%% TimerRef = erlang:start_timer(1000, self(), dtls_retransmit), +%% State#state{ +%% dtls_retransmit_timer = TimerRef, +%% last_retransmit = timestamp(), +%% flight_state = waiting}; + +%% finish_send_flight(_, FlightState, State) -> +%% State#state{flight_state = FlightState}. + +%% timestamp() -> +%% {Mega, Sec, Micro} = erlang:now(), +%% Mega * 1000000 * 1000 + Sec * 1000 + (Micro div 1000). -- cgit v1.2.3 From 754b87eb181552d67c61c9a80c31ce52e4b39f19 Mon Sep 17 00:00:00 2001 From: Ingela Anderton Andin Date: Thu, 5 Sep 2013 10:43:31 +0200 Subject: ssl: Refactor TLS/DTLS record handling --- lib/ssl/src/dtls_connection.erl | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'lib/ssl/src/dtls_connection.erl') diff --git a/lib/ssl/src/dtls_connection.erl b/lib/ssl/src/dtls_connection.erl index f6a1dc699e..fda488501c 100644 --- a/lib/ssl/src/dtls_connection.erl +++ b/lib/ssl/src/dtls_connection.erl @@ -333,3 +333,9 @@ %% timestamp() -> %% {Mega, Sec, Micro} = erlang:now(), %% Mega * 1000000 * 1000 + Sec * 1000 + (Micro div 1000). + +%% encode_handshake_rec(HandshakeRec, Version, MsgSeq, BinMsgs0, CS0) -> +%% {_, Fragments} = ssl_handshake:encode_handshake(HandshakeRec, Version, MsgSeq, 1400), +%% lists:foldl(fun(F, {Bin, C0}) -> +%% {B, C1} = ssl_record:encode_handshake(F, Version, C0), +%% {[B|Bin], C1} end, {BinMsgs0, CS0}, Fragments). -- cgit v1.2.3 From b3d5f82bd3baf5b63ea97c017cb83674ca703c66 Mon Sep 17 00:00:00 2001 From: Ingela Anderton Andin Date: Tue, 17 Sep 2013 15:28:20 +0200 Subject: ssl: Refactor connection and handshake handling --- lib/ssl/src/dtls_connection.erl | 101 ---------------------------------------- 1 file changed, 101 deletions(-) (limited to 'lib/ssl/src/dtls_connection.erl') diff --git a/lib/ssl/src/dtls_connection.erl b/lib/ssl/src/dtls_connection.erl index fda488501c..0271c4490a 100644 --- a/lib/ssl/src/dtls_connection.erl +++ b/lib/ssl/src/dtls_connection.erl @@ -18,82 +18,6 @@ %% -module(dtls_connection). -%%-behaviour(gen_fsm). - -%% -include("dtls_handshake.hrl"). -%% -include("ssl_alert.hrl"). -%% -include("dtls_record.hrl"). -%% -include("ssl_cipher.hrl"). -%% -include("ssl_internal.hrl"). -%% -include("ssl_srp.hrl"). -%% -include_lib("public_key/include/public_key.hrl"). - - -%% %% Called by dtls_connection_sup -%% %%-export([start_link/7]). - -%% %% gen_fsm callbacks -%% -export([init/1, hello/2, certify/2, cipher/2, -%% abbreviated/2, connection/2, handle_event/3, -%% handle_sync_event/4, handle_info/3, terminate/3, code_change/4]). - -%% -record(message_sequences, { -%% read = 0, -%% write = 0 -%% }). - -%% -record(state, { -%% role, % client | server -%% user_application, % {MonitorRef, pid()} -%% transport_cb, % atom() - callback module -%% data_tag, % atom() - ex tcp. -%% close_tag, % atom() - ex tcp_closed -%% error_tag, % atom() - ex tcp_error -%% host, % string() | ipadress() -%% port, % integer() -%% socket, % socket() -%% ssl_options, % #ssl_options{} -%% socket_options, % #socket_options{} -%% connection_states, % #connection_states{} from ssl_record.hrl -%% message_sequences = #message_sequences{}, -%% dtls_packets = [], % Not yet handled decode ssl/tls packets. -%% dtls_record_buffer, % binary() buffer of incomplete records -%% dtls_handshake_buffer, % binary() buffer of incomplete handshakes -%% dtls_handshake_history, % tls_handshake_history() -%% dtls_cipher_texts, % list() received but not deciphered yet -%% cert_db, % -%% session, % #session{} from tls_handshake.hrl -%% session_cache, % -%% session_cache_cb, % -%% negotiated_version, % tls_version() -%% client_certificate_requested = false, -%% key_algorithm, % atom as defined by cipher_suite -%% hashsign_algorithm, % atom as defined by cipher_suite -%% public_key_info, % PKIX: {Algorithm, PublicKey, PublicKeyParams} -%% private_key, % PKIX: #'RSAPrivateKey'{} -%% diffie_hellman_params, % PKIX: #'DHParameter'{} relevant for server side -%% diffie_hellman_keys, % {PublicKey, PrivateKey} -%% psk_identity, % binary() - server psk identity hint -%% srp_params, % #srp_user{} -%% srp_keys, % {PublicKey, PrivateKey} -%% premaster_secret, % -%% file_ref_db, % ets() -%% cert_db_ref, % ref() -%% bytes_to_read, % integer(), # bytes to read in passive mode -%% user_data_buffer, % binary() -%% log_alert, % boolean() -%% renegotiation, % {boolean(), From | internal | peer} -%% start_or_recv_from, % "gen_fsm From" -%% timer, % start_or_recv_timer -%% send_queue, % queue() -%% terminated = false, % -%% allow_renegotiate = true, -%% expecting_next_protocol_negotiation = false :: boolean(), -%% next_protocol = undefined :: undefined | binary(), -%% client_ecc, % {Curves, PointFmt} -%% client_cookie = <<>> -%% }). - %% %%==================================================================== @@ -196,32 +120,7 @@ %% {Record, State} = next_record(State2), %% next_state(hello, hello, Record, State); -%% hello(Hello = #client_hello{client_version = ClientVersion}, -%% State = #state{connection_states = ConnectionStates0, -%% port = Port, session = #session{own_certificate = Cert} = Session0, -%% renegotiation = {Renegotiation, _}, -%% session_cache = Cache, -%% session_cache_cb = CacheCb, -%% ssl_options = SslOpts}) -> -%% case ssl_handshake:hello(Hello, SslOpts, {Port, Session0, Cache, CacheCb, -%% ConnectionStates0, Cert}, Renegotiation) of -%% {Version, {Type, Session}, ConnectionStates, ProtocolsToAdvertise, -%% EcPointFormats, EllipticCurves} -> -%% do_server_hello(Type, ProtocolsToAdvertise, -%% EcPointFormats, EllipticCurves, -%% State#state{connection_states = ConnectionStates, -%% negotiated_version = Version, -%% session = Session, -%% client_ecc = {EllipticCurves, EcPointFormats}}); -%% #alert{} = Alert -> -%% handle_own_alert(Alert, ClientVersion, hello, State) -%% end; - -%% hello(timeout, State) -> -%% { next_state, hello, State, hibernate }; -%% hello(Msg, State) -> -%% handle_unexpected_message(Msg, hello, State). %% %%-------------------------------------------------------------------- %% -spec abbreviated(#hello_request{} | #finished{} | term(), %% #state{}) -> gen_fsm_state_return(). -- cgit v1.2.3 From 1b6cb772ce5d1d1b7ce280c22e6a8d2ceb8165ed Mon Sep 17 00:00:00 2001 From: Ingela Anderton Andin Date: Wed, 20 Nov 2013 15:54:47 +0100 Subject: ssl: Refactor connetion handling --- lib/ssl/src/dtls_connection.erl | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'lib/ssl/src/dtls_connection.erl') diff --git a/lib/ssl/src/dtls_connection.erl b/lib/ssl/src/dtls_connection.erl index 0271c4490a..da2e076856 100644 --- a/lib/ssl/src/dtls_connection.erl +++ b/lib/ssl/src/dtls_connection.erl @@ -18,11 +18,12 @@ %% -module(dtls_connection). +%% Internal application API +%%==================================================================== +%% Internal application API +%%==================================================================== -%% %%==================================================================== -%% %% Internal application API -%% %%==================================================================== %% %%==================================================================== -- cgit v1.2.3 From c635f15d22802f7ff18fd1ce9197b2cc760979ef Mon Sep 17 00:00:00 2001 From: Ingela Anderton Andin Date: Thu, 27 Feb 2014 14:48:59 +0100 Subject: ssl: Refactor and start implementing dtls_connection.erl --- lib/ssl/src/dtls_connection.erl | 717 ++++++++++++++++++++++++++++------------ 1 file changed, 502 insertions(+), 215 deletions(-) (limited to 'lib/ssl/src/dtls_connection.erl') diff --git a/lib/ssl/src/dtls_connection.erl b/lib/ssl/src/dtls_connection.erl index da2e076856..823bed14fd 100644 --- a/lib/ssl/src/dtls_connection.erl +++ b/lib/ssl/src/dtls_connection.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2013-2013. All Rights Reserved. +%% Copyright Ericsson AB 2013-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -20,222 +20,509 @@ %% Internal application API -%%==================================================================== +-behaviour(gen_fsm). + +-include("dtls_connection.hrl"). +-include("dtls_handshake.hrl"). +-include("ssl_alert.hrl"). +-include("dtls_record.hrl"). +-include("ssl_cipher.hrl"). +-include("ssl_api.hrl"). +-include("ssl_internal.hrl"). +-include("ssl_srp.hrl"). +-include_lib("public_key/include/public_key.hrl"). + %% Internal application API + +%% Setup +-export([start_fsm/8]). + +%% State transition handling +-export([next_record/1, next_state/4%, + %%next_state_connection/2 + ]). + +%% Handshake handling +-export([%%renegotiate/1, + send_handshake/2, send_change_cipher/2]). + +%% Alert and close handling +-export([send_alert/2, handle_own_alert/4, %%handle_close_alert/3, + handle_normal_shutdown/3 + %%handle_unexpected_message/3, + %%alert_user/5, alert_user/8 + ]). + +%% Data handling +-export([%%write_application_data/3, + read_application_data/2%%, +%% passive_receive/2, next_record_if_active/1 + ]). + +%% Called by tls_connection_sup +-export([start_link/7]). + +%% gen_fsm callbacks +-export([init/1, hello/2, certify/2, cipher/2, + abbreviated/2, connection/2, handle_event/3, + handle_sync_event/4, handle_info/3, terminate/3, code_change/4]). + %%==================================================================== +%% Internal application API +%%==================================================================== +start_fsm(Role, Host, Port, Socket, {#ssl_options{erl_dist = false},_} = Opts, + User, {CbModule, _,_, _} = CbInfo, + Timeout) -> + try + {ok, Pid} = dtls_connection_sup:start_child([Role, Host, Port, Socket, + Opts, User, CbInfo]), + {ok, SslSocket} = ssl_connection:socket_control(?MODULE, Socket, Pid, CbModule), + ok = ssl_connection:handshake(SslSocket, Timeout), + {ok, SslSocket} + catch + error:{badmatch, {error, _} = Error} -> + Error + end; + +start_fsm(Role, Host, Port, Socket, {#ssl_options{erl_dist = true},_} = Opts, + User, {CbModule, _,_, _} = CbInfo, + Timeout) -> + try + {ok, Pid} = dtls_connection_sup:start_child_dist([Role, Host, Port, Socket, + Opts, User, CbInfo]), + {ok, SslSocket} = ssl_connection:socket_control(?MODULE, Socket, Pid, CbModule), + ok = ssl_connection:handshake(SslSocket, Timeout), + {ok, SslSocket} + catch + error:{badmatch, {error, _} = Error} -> + Error + end. + +send_handshake(Handshake, #state{negotiated_version = Version, + tls_handshake_history = Hist0, + connection_states = ConnectionStates0} = State0) -> + {BinHandshake, ConnectionStates, Hist} = + encode_handshake(Handshake, Version, ConnectionStates0, Hist0), + send_flight(BinHandshake, State0#state{connection_states = ConnectionStates, + tls_handshake_history = Hist + }). + +send_alert(Alert, #state{negotiated_version = Version, + socket = Socket, + transport_cb = Transport, + connection_states = ConnectionStates0} = State0) -> + {BinMsg, ConnectionStates} = + ssl_alert:encode(Alert, Version, ConnectionStates0), + Transport:send(Socket, BinMsg), + State0#state{connection_states = ConnectionStates}. + +send_change_cipher(Msg, #state{connection_states = ConnectionStates0, + socket = Socket, + negotiated_version = Version, + transport_cb = Transport} = State0) -> + {BinChangeCipher, ConnectionStates} = + encode_change_cipher(Msg, Version, ConnectionStates0), + Transport:send(Socket, BinChangeCipher), + State0#state{connection_states = ConnectionStates}. +%%==================================================================== +%% tls_connection_sup API +%%==================================================================== +%%-------------------------------------------------------------------- +-spec start_link(atom(), host(), inet:port_number(), port(), list(), pid(), tuple()) -> + {ok, pid()} | ignore | {error, reason()}. +%% +%% Description: Creates a gen_fsm process which calls Module:init/1 to +%% initialize. To ensure a synchronized start-up procedure, this function +%% does not return until Module:init/1 has returned. +%%-------------------------------------------------------------------- +start_link(Role, Host, Port, Socket, Options, User, CbInfo) -> + {ok, proc_lib:spawn_link(?MODULE, init, [[Role, Host, Port, Socket, Options, User, CbInfo]])}. + +init([Role, Host, Port, Socket, {SSLOpts0, _} = Options, User, CbInfo]) -> + process_flag(trap_exit, true), + State0 = initial_state(Role, Host, Port, Socket, Options, User, CbInfo), + Handshake = ssl_handshake:init_handshake_history(), + TimeStamp = calendar:datetime_to_gregorian_seconds({date(), time()}), + try ssl_config:init(SSLOpts0, Role) of + {ok, Ref, CertDbHandle, FileRefHandle, CacheHandle, OwnCert, Key, DHParams} -> + Session = State0#state.session, + State = State0#state{ + tls_handshake_history = Handshake, + session = Session#session{own_certificate = OwnCert, + time_stamp = TimeStamp}, + file_ref_db = FileRefHandle, + cert_db_ref = Ref, + cert_db = CertDbHandle, + session_cache = CacheHandle, + private_key = Key, + diffie_hellman_params = DHParams}, + gen_fsm:enter_loop(?MODULE, [], hello, State, get_timeout(State)) + catch + throw:Error -> + gen_fsm:enter_loop(?MODULE, [], error, {Error,State0}, get_timeout(State0)) + end. + +%%-------------------------------------------------------------------- +%% Description:There should be one instance of this function for each +%% possible state name. Whenever a gen_fsm receives an event sent +%% using gen_fsm:send_event/2, the instance of this function with the +%% same name as the current state name StateName is called to handle +%% the event. It is also called if a timeout occurs. +%% +hello(start, #state{host = Host, port = Port, role = client, + ssl_options = SslOpts, + session = #session{own_certificate = Cert} = Session0, + session_cache = Cache, session_cache_cb = CacheCb, + transport_cb = Transport, socket = Socket, + connection_states = ConnectionStates0, + renegotiation = {Renegotiation, _}} = State0) -> + Hello = dtls_handshake:client_hello(Host, Port, ConnectionStates0, SslOpts, + Cache, CacheCb, Renegotiation, Cert), + + Version = Hello#client_hello.client_version, + Handshake0 = ssl_handshake:init_handshake_history(), + {BinMsg, ConnectionStates, Handshake} = + encode_handshake(Hello, Version, ConnectionStates0, Handshake0), + Transport:send(Socket, BinMsg), + State1 = State0#state{connection_states = ConnectionStates, + negotiated_version = Version, %% Requested version + session = + Session0#session{session_id = Hello#client_hello.session_id}, + tls_handshake_history = Handshake}, + {Record, State} = next_record(State1), + next_state(hello, hello, Record, State); + +hello(Hello = #client_hello{client_version = ClientVersion, + extensions = #hello_extensions{hash_signs = HashSigns}}, + State = #state{connection_states = ConnectionStates0, + port = Port, session = #session{own_certificate = Cert} = Session0, + renegotiation = {Renegotiation, _}, + session_cache = Cache, + session_cache_cb = CacheCb, + ssl_options = SslOpts}) -> + HashSign = ssl_handshake:select_hashsign(HashSigns, Cert), + case dtls_handshake:hello(Hello, SslOpts, {Port, Session0, Cache, CacheCb, + ConnectionStates0, Cert}, Renegotiation) of + {Version, {Type, Session}, + ConnectionStates, + #hello_extensions{ec_point_formats = EcPointFormats, + elliptic_curves = EllipticCurves} = ServerHelloExt} -> + ssl_connection:hello({common_client_hello, Type, ServerHelloExt, HashSign}, + State#state{connection_states = ConnectionStates, + negotiated_version = Version, + session = Session, + client_ecc = {EllipticCurves, EcPointFormats}}, ?MODULE); + #alert{} = Alert -> + handle_own_alert(Alert, ClientVersion, hello, State) + end; +hello(Hello, + #state{connection_states = ConnectionStates0, + negotiated_version = ReqVersion, + role = client, + renegotiation = {Renegotiation, _}, + ssl_options = SslOptions} = State) -> + case dtls_handshake:hello(Hello, SslOptions, ConnectionStates0, Renegotiation) of + #alert{} = Alert -> + handle_own_alert(Alert, ReqVersion, hello, State); + {Version, NewId, ConnectionStates, NextProtocol} -> + ssl_connection:handle_session(Hello, + Version, NewId, ConnectionStates, NextProtocol, State) + end; + +hello(Msg, State) -> + ssl_connection:hello(Msg, State, ?MODULE). + +abbreviated(Msg, State) -> + ssl_connection:abbreviated(Msg, State, ?MODULE). + +certify(Msg, State) -> + ssl_connection:certify(Msg, State, ?MODULE). + +cipher(Msg, State) -> + ssl_connection:cipher(Msg, State, ?MODULE). + +connection(#hello_request{}, #state{host = Host, port = Port, + session = #session{own_certificate = Cert} = Session0, + session_cache = Cache, session_cache_cb = CacheCb, + ssl_options = SslOpts, + connection_states = ConnectionStates0, + renegotiation = {Renegotiation, _}} = State0) -> + Hello = dtls_handshake:client_hello(Host, Port, ConnectionStates0, SslOpts, + Cache, CacheCb, Renegotiation, Cert), + %% TODO DTLS version State1 = send_handshake(Hello, State0), + State1 = State0, + {Record, State} = + next_record( + State1#state{session = Session0#session{session_id + = Hello#client_hello.session_id}}), + next_state(connection, hello, Record, State); + +connection(#client_hello{} = Hello, #state{role = server, allow_renegotiate = true} = State) -> + %% Mitigate Computational DoS attack + %% http://www.educatedguesswork.org/2011/10/ssltls_and_computational_dos.html + %% http://www.thc.org/thc-ssl-dos/ Rather than disabling client + %% initiated renegotiation we will disallow many client initiated + %% renegotiations immediately after each other. + erlang:send_after(?WAIT_TO_ALLOW_RENEGOTIATION, self(), allow_renegotiate), + hello(Hello, State#state{allow_renegotiate = false}); + +connection(#client_hello{}, #state{role = server, allow_renegotiate = false} = State0) -> + Alert = ?ALERT_REC(?WARNING, ?NO_RENEGOTIATION), + State = send_alert(Alert, State0), + next_state_connection(connection, State); + +connection(Msg, State) -> + ssl_connection:connection(Msg, State, tls_connection). + +%%-------------------------------------------------------------------- +%% Description: Whenever a gen_fsm receives an event sent using +%% gen_fsm:send_all_state_event/2, this function is called to handle +%% the event. Not currently used! +%%-------------------------------------------------------------------- +handle_event(_Event, StateName, State) -> + {next_state, StateName, State, get_timeout(State)}. + +%%-------------------------------------------------------------------- +%% Description: Whenever a gen_fsm receives an event sent using +%% gen_fsm:sync_send_all_state_event/2,3, this function is called to handle +%% the event. +%%-------------------------------------------------------------------- +handle_sync_event(Event, From, StateName, State) -> + ssl_connection:handle_sync_event(Event, From, StateName, State). + +%%-------------------------------------------------------------------- +%% Description: This function is called by a gen_fsm when it receives any +%% other message than a synchronous or asynchronous event +%% (or a system message). +%%-------------------------------------------------------------------- + +%% raw data from socket, unpack records +handle_info({Protocol, _, Data}, StateName, + #state{data_tag = Protocol} = State0) -> + %% Simplify for now to avoid dialzer warnings before implementation is compleate + %% case next_tls_record(Data, State0) of + %% {Record, State} -> + %% next_state(StateName, StateName, Record, State); + %% #alert{} = Alert -> + %% handle_normal_shutdown(Alert, StateName, State0), + %% {stop, {shutdown, own_alert}, State0} + %% end; + {Record, State} = next_tls_record(Data, State0), + next_state(StateName, StateName, Record, State); + +handle_info({CloseTag, Socket}, StateName, + #state{socket = Socket, close_tag = CloseTag, + negotiated_version = _Version} = State) -> + handle_normal_shutdown(?ALERT_REC(?FATAL, ?CLOSE_NOTIFY), StateName, State), + {stop, {shutdown, transport_closed}, State}; + +handle_info(Msg, StateName, State) -> + ssl_connection:handle_info(Msg, StateName, State). + +%%-------------------------------------------------------------------- +%% Description:This function is called by a gen_fsm when it is about +%% to terminate. It should be the opposite of Module:init/1 and do any +%% necessary cleaning up. When it returns, the gen_fsm terminates with +%% Reason. The return value is ignored. +%%-------------------------------------------------------------------- +terminate(Reason, StateName, State) -> + ssl_connection:terminate(Reason, StateName, State). + +%%-------------------------------------------------------------------- +%% code_change(OldVsn, StateName, State, Extra) -> {ok, StateName, NewState} +%% Description: Convert process state when code is changed +%%-------------------------------------------------------------------- +code_change(_OldVsn, StateName, State, _Extra) -> + {ok, StateName, State}. + +%%-------------------------------------------------------------------- +%%% Internal functions +%%-------------------------------------------------------------------- +encode_handshake(Handshake, Version, ConnectionStates0, Hist0) -> + {Handshake, FragmentedHandshake} = dtls_handshake:encode_handshake(Handshake, Version), + Hist = ssl_handshake:update_handshake_history(Hist0, Handshake), + {Encoded, ConnectionStates} = + dtls_record:encode_handshake(FragmentedHandshake, + Version, ConnectionStates0), + {Encoded, ConnectionStates, Hist}. + +next_record(#state{%%flight = #flight{state = finished}, + protocol_buffers = + #protocol_buffers{dtls_packets = [], dtls_cipher_texts = [CT | Rest]} + = Buffers, + connection_states = ConnStates0} = State) -> + case dtls_record:decode_cipher_text(CT, ConnStates0) of + {Plain, ConnStates} -> + {Plain, State#state{protocol_buffers = + Buffers#protocol_buffers{dtls_cipher_texts = Rest}, + connection_states = ConnStates}}; + #alert{} = Alert -> + {Alert, State} + end; +next_record(#state{socket = Socket, + transport_cb = Transport} = State) -> %% when FlightState =/= finished + ssl_socket:setopts(Transport, Socket, [{active,once}]), + {no_record, State}; + + +next_record(State) -> + {no_record, State}. + +next_state(Current,_, #alert{} = Alert, #state{negotiated_version = Version} = State) -> + handle_own_alert(Alert, Version, Current, State); + +next_state(_,Next, no_record, State) -> + {next_state, Next, State, get_timeout(State)}; + +%% next_state(_,Next, #ssl_tls{type = ?ALERT, fragment = EncAlerts}, State) -> +%% Alerts = decode_alerts(EncAlerts), +%% handle_alerts(Alerts, {next_state, Next, State, get_timeout(State)}); + +next_state(Current, Next, #ssl_tls{type = ?HANDSHAKE, fragment = Data}, + State0 = #state{protocol_buffers = + #protocol_buffers{dtls_handshake_buffer = Buf0} = Buffers, + negotiated_version = Version}) -> + Handle = + fun({#hello_request{} = Packet, _}, {next_state, connection = SName, State}) -> + %% This message should not be included in handshake + %% message hashes. Starts new handshake (renegotiation) + Hs0 = ssl_handshake:init_handshake_history(), + ?MODULE:SName(Packet, State#state{tls_handshake_history=Hs0, + renegotiation = {true, peer}}); + ({#hello_request{} = Packet, _}, {next_state, SName, State}) -> + %% This message should not be included in handshake + %% message hashes. Already in negotiation so it will be ignored! + ?MODULE:SName(Packet, State); + ({#client_hello{} = Packet, Raw}, {next_state, connection = SName, State}) -> + Version = Packet#client_hello.client_version, + Hs0 = ssl_handshake:init_handshake_history(), + Hs1 = ssl_handshake:update_handshake_history(Hs0, Raw), + ?MODULE:SName(Packet, State#state{tls_handshake_history=Hs1, + renegotiation = {true, peer}}); + ({Packet, Raw}, {next_state, SName, State = #state{tls_handshake_history=Hs0}}) -> + Hs1 = ssl_handshake:update_handshake_history(Hs0, Raw), + ?MODULE:SName(Packet, State#state{tls_handshake_history=Hs1}); + (_, StopState) -> StopState + end, + try + {Packets, Buf} = tls_handshake:get_tls_handshake(Version,Data,Buf0), + State = State0#state{protocol_buffers = + Buffers#protocol_buffers{dtls_packets = Packets, + dtls_handshake_buffer = Buf}}, + handle_dtls_handshake(Handle, Next, State) + catch throw:#alert{} = Alert -> + handle_own_alert(Alert, Version, Current, State0) + end; + +next_state(_, StateName, #ssl_tls{type = ?APPLICATION_DATA, fragment = Data}, State0) -> + %% Simplify for now to avoid dialzer warnings before implementation is compleate + %% case read_application_data(Data, State0) of + %% Stop = {stop,_,_} -> + %% Stop; + %% {Record, State} -> + %% next_state(StateName, StateName, Record, State) + %% end; + {Record, State} = read_application_data(Data, State0), + next_state(StateName, StateName, Record, State); + +next_state(Current, Next, #ssl_tls{type = ?CHANGE_CIPHER_SPEC, fragment = <<1>>} = + _ChangeCipher, + #state{connection_states = ConnectionStates0} = State0) -> + ConnectionStates1 = + ssl_record:activate_pending_connection_state(ConnectionStates0, read), + {Record, State} = next_record(State0#state{connection_states = ConnectionStates1}), + next_state(Current, Next, Record, State); +next_state(Current, Next, #ssl_tls{type = _Unknown}, State0) -> + %% Ignore unknown type + {Record, State} = next_record(State0), + next_state(Current, Next, Record, State). + +handle_dtls_handshake(Handle, StateName, + #state{protocol_buffers = + #protocol_buffers{dtls_packets = [Packet]} = Buffers} = State) -> + FsmReturn = {next_state, StateName, State#state{protocol_buffers = + Buffers#protocol_buffers{dtls_packets = []}}}, + Handle(Packet, FsmReturn); + +handle_dtls_handshake(Handle, StateName, + #state{protocol_buffers = + #protocol_buffers{dtls_packets = [Packet | Packets]} = Buffers} = + State0) -> + FsmReturn = {next_state, StateName, State0#state{protocol_buffers = + Buffers#protocol_buffers{dtls_packets = + Packets}}}, + case Handle(Packet, FsmReturn) of + {next_state, NextStateName, State, _Timeout} -> + handle_dtls_handshake(Handle, NextStateName, State); + {stop, _,_} = Stop -> + Stop + end. + + +send_flight(Fragments, #state{transport_cb = Transport, socket = Socket, + protocol_buffers = _PBuffers} = State) -> + Transport:send(Socket, Fragments), + %% Start retransmission + %% State#state{protocol_buffers = + %% (PBuffers#protocol_buffers){ #flight{state = waiting}}}}. + State. + +handle_own_alert(_,_,_, State) -> %% Place holder + {stop, {shutdown, own_alert}, State}. + +handle_normal_shutdown(_, _, _State) -> %% Place holder + ok. + +encode_change_cipher(#change_cipher_spec{}, Version, ConnectionStates) -> + dtls_record:encode_change_cipher_spec(Version, ConnectionStates). + +initial_state(Role, Host, Port, Socket, {SSLOptions, SocketOptions}, User, + {CbModule, DataTag, CloseTag, ErrorTag}) -> + ConnectionStates = ssl_record:init_connection_states(Role), + + SessionCacheCb = case application:get_env(ssl, session_cb) of + {ok, Cb} when is_atom(Cb) -> + Cb; + _ -> + ssl_session_cache + end, + + Monitor = erlang:monitor(process, User), + + #state{socket_options = SocketOptions, + %% We do not want to save the password in the state so that + %% could be written in the clear into error logs. + ssl_options = SSLOptions#ssl_options{password = undefined}, + session = #session{is_resumable = new}, + transport_cb = CbModule, + data_tag = DataTag, + close_tag = CloseTag, + error_tag = ErrorTag, + role = Role, + host = Host, + port = Port, + socket = Socket, + connection_states = ConnectionStates, + protocol_buffers = #protocol_buffers{}, + user_application = {Monitor, User}, + user_data_buffer = <<>>, + session_cache_cb = SessionCacheCb, + renegotiation = {false, first}, + start_or_recv_from = undefined, + send_queue = queue:new(), + protocol_cb = ?MODULE + }. +read_application_data(_,State) -> + {#ssl_tls{fragment = <<"place holder">>}, State}. + +next_tls_record(_, State) -> + {#ssl_tls{fragment = <<"place holder">>}, State}. + +get_timeout(_) -> %% Place holder + infinity. + +next_state_connection(_, State) -> %% Place holder + {next_state, connection, State, get_timeout(State)}. -%% %%==================================================================== -%% %% State functions -%% %%==================================================================== - -%% -spec hello(start | #hello_request{} | #client_hello{} | #server_hello{} | term(), -%% #state{}) -> gen_fsm_state_return(). -%% %%-------------------------------------------------------------------- -%% hello(start, #state{host = Host, port = Port, role = client, -%% ssl_options = SslOpts, -%% session = #session{own_certificate = Cert} = Session0, -%% session_cache = Cache, session_cache_cb = CacheCb, -%% connection_states = ConnectionStates0, -%% renegotiation = {Renegotiation, _}, -%% client_cookie = Cookie} = State0) -> -%% Hello = dtls_handshake:client_hello(Host, Port, Cookie, ConnectionStates0, SslOpts, -%% Cache, CacheCb, Renegotiation, Cert), - -%% Version = Hello#client_hello.client_version, -%% State1 = State0#state{negotiated_version = Version, %% Requested version -%% session = -%% Session0#session{session_id = Hello#client_hello.session_id}, -%% dtls_handshake_history = ssl_handshake:init_handshake_history()}, - -%% State2 = send_flight(Hello, waiting, State1), - -%% {Record, State} = next_record(State2), -%% next_state(hello, hello, Record, State); - -%% hello(start, #state{role = server} = State0) -> -%% {Record, State} = next_record(State0), -%% next_state(hello, hello, Record, State); - -%% hello(#hello_request{}, #state{role = client} = State0) -> -%% {Record, State} = next_record(State0), -%% next_state(hello, hello, Record, State); - -%% hello(#server_hello{cipher_suite = CipherSuite, -%% compression_method = Compression} = Hello, -%% #state{session = #session{session_id = OldId}, -%% connection_states = ConnectionStates0, -%% role = client, -%% negotiated_version = ReqVersion, -%% renegotiation = {Renegotiation, _}, -%% ssl_options = SslOptions} = State1) -> -%% State0 = flight_done(State1), -%% case ssl_handshake:hello(Hello, SslOptions, ConnectionStates0, Renegotiation) of -%% #alert{} = Alert -> -%% handle_own_alert(Alert, ReqVersion, hello, State0); -%% {Version, NewId, ConnectionStates, NextProtocol} -> -%% {KeyAlgorithm, _, _, _} = -%% ssl_cipher:suite_definition(CipherSuite), - -%% PremasterSecret = make_premaster_secret(ReqVersion, KeyAlgorithm), - -%% NewNextProtocol = case NextProtocol of -%% undefined -> -%% State0#state.next_protocol; -%% _ -> -%% NextProtocol -%% end, - -%% State = State0#state{key_algorithm = KeyAlgorithm, -%% hashsign_algorithm = default_hashsign(Version, KeyAlgorithm), -%% negotiated_version = Version, -%% connection_states = ConnectionStates, -%% premaster_secret = PremasterSecret, -%% expecting_next_protocol_negotiation = NextProtocol =/= undefined, -%% next_protocol = NewNextProtocol}, - -%% case ssl_session:is_new(OldId, NewId) of -%% true -> -%% handle_new_session(NewId, CipherSuite, Compression, -%% State#state{connection_states = ConnectionStates}); -%% false -> -%% handle_resumed_session(NewId, State#state{connection_states = ConnectionStates}) -%% end -%% end; - -%% hello(#hello_verify_request{cookie = Cookie}, -%% #state{host = Host, port = Port, -%% session = #session{own_certificate = Cert}, -%% session_cache = Cache, session_cache_cb = CacheCb, -%% ssl_options = SslOpts, -%% connection_states = ConnectionStates0, -%% renegotiation = {Renegotiation, _}} = State0) -> -%% Hello = ssl_handshake:client_hello(Host, Port, Cookie, ConnectionStates0, SslOpts, -%% Cache, CacheCb, Renegotiation, Cert), -%% State1 = State0#state{ -%% tls_handshake_history = ssl_handshake:init_handshake_history(), -%% client_cookie = Cookie}, -%% State2 = send_flight(Hello, waiting, State1), - -%% {Record, State} = next_record(State2), -%% next_state(hello, hello, Record, State); - - -%% %%-------------------------------------------------------------------- -%% -spec abbreviated(#hello_request{} | #finished{} | term(), -%% #state{}) -> gen_fsm_state_return(). -%% %%-------------------------------------------------------------------- - -%% abbreviated(timeout, State) -> -%% { next_state, abbreviated, State, hibernate }; - -%% abbreviated(Msg, State) -> -%% handle_unexpected_message(Msg, abbreviated, State). - -%% %%-------------------------------------------------------------------- -%% -spec certify(#hello_request{} | #certificate{} | #server_key_exchange{} | -%% #certificate_request{} | #server_hello_done{} | #client_key_exchange{} | term(), -%% #state{}) -> gen_fsm_state_return(). -%% %%-------------------------------------------------------------------- - - -%% certify(timeout, State) -> -%% { next_state, certify, State, hibernate }; - -%% certify(Msg, State) -> -%% handle_unexpected_message(Msg, certify, State). - - -%% %%-------------------------------------------------------------------- -%% -spec cipher(#hello_request{} | #certificate_verify{} | #finished{} | term(), -%% #state{}) -> gen_fsm_state_return(). -%% %%-------------------------------------------------------------------- - -%% cipher(timeout, State) -> -%% { next_state, cipher, State, hibernate }; - -%% cipher(Msg, State) -> -%% handle_unexpected_message(Msg, cipher, State). - -%% %%-------------------------------------------------------------------- -%% -spec connection(#hello_request{} | #client_hello{} | term(), -%% #state{}) -> gen_fsm_state_return(). -%% %%-------------------------------------------------------------------- - -%% connection(timeout, State) -> -%% {next_state, connection, State, hibernate}; - -%% connection(Msg, State) -> -%% handle_unexpected_message(Msg, connection, State). - -%% %%-------------------------------------------------------------------- -%% %%% Internal functions -%% %%-------------------------------------------------------------------- -%% handle_unexpected_message(Msg, Info, #state{negotiated_version = Version} = State) -> -%% Alert = ?ALERT_REC(?FATAL,?UNEXPECTED_MESSAGE), -%% handle_own_alert(Alert, Version, {Info, Msg}, State). - -%% send_flight(HandshakeRec, FlightState, State) -> -%% send_flight(FlightState, buffer_flight(HandshakeRec, State)). - -%% send_flight(FlightState, State = #state{negotiated_version = Version, -%% flight_buffer = Buffer}) -> - -%% State1 = do_send_flight(queue:to_list(Buffer), [], State), -%% finish_send_flight(Version, FlightState, State1). - -%% resend_flight(State = #state{negotiated_version = Version, -%% flight_state = FlightState, -%% flight_buffer = Buffer}) -%% when FlightState == finished; FlightState == waiting -> -%% State1 = do_send_flight(queue:to_list(Buffer), [], State), -%% finish_send_flight(Version, FlightState, State1); - -%% resend_flight(State) -> -%% State. - -%% flight_done(State) -> -%% cancel_dtls_retransmit_timer(State#state{flight_state = done, -%% flight_buffer = undefined}). - -%% do_send_flight([], BinMsgs, State = #state{transport_cb = Transport, socket = Socket}) -> -%% Transport:send(Socket, lists:reverse(BinMsgs)), -%% State; -%% do_send_flight([{Epoch, MsgSeq, HandshakeRec}|T], BinMsgs0, -%% State = #state{negotiated_version = Version, -%% connection_states = ConnectionStates0}) -> -%% CS0 = ssl_record:connection_state_by_epoch(ConnectionStates0, Epoch, write), -%% {BinMsgs, CS1} = encode_handshake_rec(HandshakeRec, Version, MsgSeq, BinMsgs0, CS0), -%% ConnectionStates1 = ssl_record:set_connection_state_by_epoch(ConnectionStates0, CS1, write), -%% do_send_flight(T, BinMsgs, State#state{connection_states = ConnectionStates1}). - -%% cancel_dtls_retransmit_timer(State = #state{dtls_retransmit_timer = TimerRef}) -> -%% cancel_timer(TimerRef), -%% State#state{dtls_retransmit_timer = undefined}. - -%% rearm_dtls_retransmit_timer(State = #state{dtls_retransmit_timer = undefined}) -> -%% TimerRef = erlang:start_timer(1000, self(), dtls_retransmit), -%% State#state{dtls_retransmit_timer = TimerRef}; -%% rearm_dtls_retransmit_timer(State) -> -%% State. - -%% finish_send_flight({254, _}, waiting, State) -> -%% TimerRef = erlang:start_timer(1000, self(), dtls_retransmit), -%% State#state{ -%% dtls_retransmit_timer = TimerRef, -%% last_retransmit = timestamp(), -%% flight_state = waiting}; - -%% finish_send_flight(_, FlightState, State) -> -%% State#state{flight_state = FlightState}. - -%% timestamp() -> -%% {Mega, Sec, Micro} = erlang:now(), -%% Mega * 1000000 * 1000 + Sec * 1000 + (Micro div 1000). - -%% encode_handshake_rec(HandshakeRec, Version, MsgSeq, BinMsgs0, CS0) -> -%% {_, Fragments} = ssl_handshake:encode_handshake(HandshakeRec, Version, MsgSeq, 1400), -%% lists:foldl(fun(F, {Bin, C0}) -> -%% {B, C1} = ssl_record:encode_handshake(F, Version, C0), -%% {[B|Bin], C1} end, {BinMsgs0, CS0}, Fragments). -- cgit v1.2.3 From c2aab88300a61960a38b6ca33d64c9fae39989b3 Mon Sep 17 00:00:00 2001 From: Ingela Anderton Andin Date: Mon, 24 Mar 2014 15:48:43 +0100 Subject: ssl: Avoid dialyzer warnings in dtls code Even if DTLS is not finished, e.i. not runnable yet we want to phase in the code together with refactoring of TLS code, but without introducing warnings in the release. --- lib/ssl/src/dtls_connection.erl | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'lib/ssl/src/dtls_connection.erl') diff --git a/lib/ssl/src/dtls_connection.erl b/lib/ssl/src/dtls_connection.erl index 823bed14fd..57f8dd86d3 100644 --- a/lib/ssl/src/dtls_connection.erl +++ b/lib/ssl/src/dtls_connection.erl @@ -341,8 +341,10 @@ code_change(_OldVsn, StateName, State, _Extra) -> %%% Internal functions %%-------------------------------------------------------------------- encode_handshake(Handshake, Version, ConnectionStates0, Hist0) -> - {Handshake, FragmentedHandshake} = dtls_handshake:encode_handshake(Handshake, Version), - Hist = ssl_handshake:update_handshake_history(Hist0, Handshake), + Seq = sequence(ConnectionStates0), + {EncHandshake, FragmentedHandshake} = dtls_handshake:encode_handshake(Handshake, Version, + Seq), + Hist = ssl_handshake:update_handshake_history(Hist0, EncHandshake), {Encoded, ConnectionStates} = dtls_record:encode_handshake(FragmentedHandshake, Version, ConnectionStates0), @@ -526,3 +528,6 @@ get_timeout(_) -> %% Place holder next_state_connection(_, State) -> %% Place holder {next_state, connection, State, get_timeout(State)}. +sequence(_) -> + %%TODO real imp + 1. -- cgit v1.2.3 From b4fc84117b6bd1105ca8ccb91f699564ac400dff Mon Sep 17 00:00:00 2001 From: Danil Zagoskin Date: Mon, 21 Apr 2014 22:00:09 +0400 Subject: ssl: always pass negotiated version when selecting hashsign Negotiated version is now always passed to ssl_handshake:select_hashsign because ssl_handshake:select_cert_hashsign has different rsa defaults on tlsv1.2 and older versions. --- lib/ssl/src/dtls_connection.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/ssl/src/dtls_connection.erl') diff --git a/lib/ssl/src/dtls_connection.erl b/lib/ssl/src/dtls_connection.erl index 57f8dd86d3..ec0f408f51 100644 --- a/lib/ssl/src/dtls_connection.erl +++ b/lib/ssl/src/dtls_connection.erl @@ -202,13 +202,13 @@ hello(Hello = #client_hello{client_version = ClientVersion, session_cache = Cache, session_cache_cb = CacheCb, ssl_options = SslOpts}) -> - HashSign = ssl_handshake:select_hashsign(HashSigns, Cert), case dtls_handshake:hello(Hello, SslOpts, {Port, Session0, Cache, CacheCb, ConnectionStates0, Cert}, Renegotiation) of {Version, {Type, Session}, ConnectionStates, #hello_extensions{ec_point_formats = EcPointFormats, elliptic_curves = EllipticCurves} = ServerHelloExt} -> + HashSign = ssl_handshake:select_hashsign(HashSigns, Cert, Version), ssl_connection:hello({common_client_hello, Type, ServerHelloExt, HashSign}, State#state{connection_states = ConnectionStates, negotiated_version = Version, -- cgit v1.2.3 From 081ee510f1fb1d821a524bd6b8efd20e520add3c Mon Sep 17 00:00:00 2001 From: Ingela Anderton Andin Date: Wed, 23 Apr 2014 09:35:55 +0200 Subject: ssl: Refactor so that there is only one source for the default hashsign values Also fix DTLS call to supply its corresponding TLS version --- lib/ssl/src/dtls_connection.erl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'lib/ssl/src/dtls_connection.erl') diff --git a/lib/ssl/src/dtls_connection.erl b/lib/ssl/src/dtls_connection.erl index ec0f408f51..508983ddac 100644 --- a/lib/ssl/src/dtls_connection.erl +++ b/lib/ssl/src/dtls_connection.erl @@ -208,7 +208,8 @@ hello(Hello = #client_hello{client_version = ClientVersion, ConnectionStates, #hello_extensions{ec_point_formats = EcPointFormats, elliptic_curves = EllipticCurves} = ServerHelloExt} -> - HashSign = ssl_handshake:select_hashsign(HashSigns, Cert, Version), + HashSign = ssl_handshake:select_hashsign(HashSigns, Cert, + dtls_v1:corresponding_tls_version(Version)), ssl_connection:hello({common_client_hello, Type, ServerHelloExt, HashSign}, State#state{connection_states = ConnectionStates, negotiated_version = Version, -- cgit v1.2.3 From b65d8b7ba72a42cfe5df28577433e553d2c0f4ca Mon Sep 17 00:00:00 2001 From: Ingela Anderton Andin Date: Tue, 10 Mar 2015 15:40:37 +0100 Subject: ssl: Dialyzer fixes --- lib/ssl/src/dtls_connection.erl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'lib/ssl/src/dtls_connection.erl') diff --git a/lib/ssl/src/dtls_connection.erl b/lib/ssl/src/dtls_connection.erl index 508983ddac..f177a8610d 100644 --- a/lib/ssl/src/dtls_connection.erl +++ b/lib/ssl/src/dtls_connection.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2013-2014. All Rights Reserved. +%% Copyright Ericsson AB 2013-2015. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -146,7 +146,7 @@ init([Role, Host, Port, Socket, {SSLOpts0, _} = Options, User, CbInfo]) -> Handshake = ssl_handshake:init_handshake_history(), TimeStamp = calendar:datetime_to_gregorian_seconds({date(), time()}), try ssl_config:init(SSLOpts0, Role) of - {ok, Ref, CertDbHandle, FileRefHandle, CacheHandle, OwnCert, Key, DHParams} -> + {ok, Ref, CertDbHandle, FileRefHandle, CacheHandle, CRLDbInfo, OwnCert, Key, DHParams} -> Session = State0#state.session, State = State0#state{ tls_handshake_history = Handshake, @@ -155,6 +155,7 @@ init([Role, Host, Port, Socket, {SSLOpts0, _} = Options, User, CbInfo]) -> file_ref_db = FileRefHandle, cert_db_ref = Ref, cert_db = CertDbHandle, + crl_db = CRLDbInfo, session_cache = CacheHandle, private_key = Key, diffie_hellman_params = DHParams}, -- cgit v1.2.3 From 7cf85926c11d38ce5ebd181f9f98185f359d64a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Hoguin?= Date: Thu, 26 Feb 2015 18:55:16 +0100 Subject: ssl: Add TLS-ALPN support This commit adds support for RFC7301, application-layer protocol negotiation. ALPN is the standard based approach to the NPN extension, and is required for HTTP/2. ALPN lives side by side with NPN and provides an equivalent feature but in this case it is the server that decides what protocol to use, not the client. When both ALPN and NPN are sent by a client, and the server is configured with both ALPN and NPN options, ALPN will always take precedence. This behavior can also be found in the OpenSSL implementation of ALPN. ALPN and NPN share the ssl:negotiated_protocol/1 function for retrieving the negotiated protocol. The previously existing function ssl:negotiated_next_protocol/1 still exists, but has been deprecated and removed from the documentation. The tests against OpenSSL require OpenSSL version 1.0.2+. --- lib/ssl/src/dtls_connection.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib/ssl/src/dtls_connection.erl') diff --git a/lib/ssl/src/dtls_connection.erl b/lib/ssl/src/dtls_connection.erl index f177a8610d..610e2c4e41 100644 --- a/lib/ssl/src/dtls_connection.erl +++ b/lib/ssl/src/dtls_connection.erl @@ -228,9 +228,9 @@ hello(Hello, case dtls_handshake:hello(Hello, SslOptions, ConnectionStates0, Renegotiation) of #alert{} = Alert -> handle_own_alert(Alert, ReqVersion, hello, State); - {Version, NewId, ConnectionStates, NextProtocol} -> + {Version, NewId, ConnectionStates, ProtoExt, Protocol} -> ssl_connection:handle_session(Hello, - Version, NewId, ConnectionStates, NextProtocol, State) + Version, NewId, ConnectionStates, ProtoExt, Protocol, State) end; hello(Msg, State) -> -- cgit v1.2.3 From 916226427901f40d695d0d7d91106b0188900055 Mon Sep 17 00:00:00 2001 From: Fred Hebert Date: Tue, 2 Jun 2015 10:51:22 -0400 Subject: Add disable client-initiated renegotiation option Client-initiated renegotiation is more costly for the server than the client, and this feature can be abused in denial of service attempts. Although the ssl application already takes counter-measure for these (via cooldown periods between renegotiations), it can be useful to disable the feature entirely. This patch adds the `{client_renegotiation, boolean()}' option to the server-side of the SSL application (defaulting to `true' to be compatible with the current behaviour). The option disables the ability to do any renegotiation at all in the protocol's state, reusing the existing denial code, but without opening the code path that sets up a timed message to eventually reopen it up. --- lib/ssl/src/dtls_connection.erl | 1 + 1 file changed, 1 insertion(+) (limited to 'lib/ssl/src/dtls_connection.erl') diff --git a/lib/ssl/src/dtls_connection.erl b/lib/ssl/src/dtls_connection.erl index 508983ddac..039495ef9e 100644 --- a/lib/ssl/src/dtls_connection.erl +++ b/lib/ssl/src/dtls_connection.erl @@ -513,6 +513,7 @@ initial_state(Role, Host, Port, Socket, {SSLOptions, SocketOptions}, User, user_data_buffer = <<>>, session_cache_cb = SessionCacheCb, renegotiation = {false, first}, + allow_renegotiate = SSLOptions#ssl_options.client_renegotiation, start_or_recv_from = undefined, send_queue = queue:new(), protocol_cb = ?MODULE -- cgit v1.2.3 From 738c34d4bb8f1a3811acd00af8c6c12107f8315b Mon Sep 17 00:00:00 2001 From: Bruce Yinhe Date: Thu, 18 Jun 2015 11:31:02 +0200 Subject: Change license text to APLv2 --- lib/ssl/src/dtls_connection.erl | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) (limited to 'lib/ssl/src/dtls_connection.erl') diff --git a/lib/ssl/src/dtls_connection.erl b/lib/ssl/src/dtls_connection.erl index 0c73a49a04..78662e0ea2 100644 --- a/lib/ssl/src/dtls_connection.erl +++ b/lib/ssl/src/dtls_connection.erl @@ -3,16 +3,17 @@ %% %% Copyright Ericsson AB 2013-2015. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at %% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% -- cgit v1.2.3 From 5e95fdff6589432327e38415fc9c5545231e7961 Mon Sep 17 00:00:00 2001 From: Ingela Anderton Andin Date: Fri, 13 Nov 2015 21:56:30 +0100 Subject: ssl: Measure elapsed time with erlang:monotonic_time --- lib/ssl/src/dtls_connection.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/ssl/src/dtls_connection.erl') diff --git a/lib/ssl/src/dtls_connection.erl b/lib/ssl/src/dtls_connection.erl index 78662e0ea2..153d3fef48 100644 --- a/lib/ssl/src/dtls_connection.erl +++ b/lib/ssl/src/dtls_connection.erl @@ -145,7 +145,7 @@ init([Role, Host, Port, Socket, {SSLOpts0, _} = Options, User, CbInfo]) -> process_flag(trap_exit, true), State0 = initial_state(Role, Host, Port, Socket, Options, User, CbInfo), Handshake = ssl_handshake:init_handshake_history(), - TimeStamp = calendar:datetime_to_gregorian_seconds({date(), time()}), + TimeStamp = erlang:monotonic_time(), try ssl_config:init(SSLOpts0, Role) of {ok, Ref, CertDbHandle, FileRefHandle, CacheHandle, CRLDbInfo, OwnCert, Key, DHParams} -> Session = State0#state.session, -- cgit v1.2.3 From 923ea90fb23ff65071b688fbee09c00b47afc272 Mon Sep 17 00:00:00 2001 From: Ingela Anderton Andin Date: Fri, 5 Feb 2016 17:39:04 +0100 Subject: ssl: Add option signature_algs In TLS-1.2 The signature algorithm and the hash function algorithm used to produce the digest that is used when creating the digital signature may be negotiated through the signature algorithm extension RFC 5246. We want to make these algorithm pairs configurable. In connections using lower versions of TLS these algorithms are implicit defined and can not be negotiated or configured. DTLS is updated to not cause dialyzer errors, but needs to get a real implementation later. --- lib/ssl/src/dtls_connection.erl | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'lib/ssl/src/dtls_connection.erl') diff --git a/lib/ssl/src/dtls_connection.erl b/lib/ssl/src/dtls_connection.erl index 153d3fef48..e490de7eeb 100644 --- a/lib/ssl/src/dtls_connection.erl +++ b/lib/ssl/src/dtls_connection.erl @@ -196,8 +196,7 @@ hello(start, #state{host = Host, port = Port, role = client, {Record, State} = next_record(State1), next_state(hello, hello, Record, State); -hello(Hello = #client_hello{client_version = ClientVersion, - extensions = #hello_extensions{hash_signs = HashSigns}}, +hello(Hello = #client_hello{client_version = ClientVersion}, State = #state{connection_states = ConnectionStates0, port = Port, session = #session{own_certificate = Cert} = Session0, renegotiation = {Renegotiation, _}, @@ -209,9 +208,7 @@ hello(Hello = #client_hello{client_version = ClientVersion, {Version, {Type, Session}, ConnectionStates, #hello_extensions{ec_point_formats = EcPointFormats, - elliptic_curves = EllipticCurves} = ServerHelloExt} -> - HashSign = ssl_handshake:select_hashsign(HashSigns, Cert, - dtls_v1:corresponding_tls_version(Version)), + elliptic_curves = EllipticCurves} = ServerHelloExt, HashSign} -> ssl_connection:hello({common_client_hello, Type, ServerHelloExt, HashSign}, State#state{connection_states = ConnectionStates, negotiated_version = Version, -- cgit v1.2.3 From 67de1ba82d1af910dc6ab6d41a1f22dbfb39f658 Mon Sep 17 00:00:00 2001 From: Ingela Anderton Andin Date: Thu, 24 Mar 2016 17:08:44 +0100 Subject: ssl: Use gen_statem instead of gen_fsm Also reduce timing issues in tests --- lib/ssl/src/dtls_connection.erl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'lib/ssl/src/dtls_connection.erl') diff --git a/lib/ssl/src/dtls_connection.erl b/lib/ssl/src/dtls_connection.erl index e490de7eeb..2c9fe5cfb5 100644 --- a/lib/ssl/src/dtls_connection.erl +++ b/lib/ssl/src/dtls_connection.erl @@ -21,7 +21,7 @@ %% Internal application API --behaviour(gen_fsm). +-behaviour(gen_statem). -include("dtls_connection.hrl"). -include("dtls_handshake.hrl"). @@ -514,7 +514,6 @@ initial_state(Role, Host, Port, Socket, {SSLOptions, SocketOptions}, User, renegotiation = {false, first}, allow_renegotiate = SSLOptions#ssl_options.client_renegotiation, start_or_recv_from = undefined, - send_queue = queue:new(), protocol_cb = ?MODULE }. read_application_data(_,State) -> -- cgit v1.2.3 From 0f782eb6377ddd1e5673c186b8b1a37edd610e4b Mon Sep 17 00:00:00 2001 From: Ingela Anderton Andin Date: Wed, 27 Apr 2016 11:44:37 +0200 Subject: ssl: Adapt DTLS to gen_statem DTLS is not in working mode yet, but the gen_statem rewrite should make completion easier. --- lib/ssl/src/dtls_connection.erl | 351 +++++++++++++++++----------------------- 1 file changed, 144 insertions(+), 207 deletions(-) (limited to 'lib/ssl/src/dtls_connection.erl') diff --git a/lib/ssl/src/dtls_connection.erl b/lib/ssl/src/dtls_connection.erl index 2c9fe5cfb5..82d6faee42 100644 --- a/lib/ssl/src/dtls_connection.erl +++ b/lib/ssl/src/dtls_connection.erl @@ -36,37 +36,38 @@ %% Internal application API %% Setup --export([start_fsm/8]). +-export([start_fsm/8, start_link/7, init/1]). %% State transition handling --export([next_record/1, next_state/4%, - %%next_state_connection/2 - ]). +-export([next_record/1, next_event/3]). %% Handshake handling --export([%%renegotiate/1, +-export([%%renegotiate/2, send_handshake/2, send_change_cipher/2]). + %% Alert and close handling --export([send_alert/2, handle_own_alert/4, %%handle_close_alert/3, - handle_normal_shutdown/3 - %%handle_unexpected_message/3, - %%alert_user/5, alert_user/8 +-export([%%send_alert/2, handle_own_alert/4, handle_close_alert/3, + handle_normal_shutdown/3 %%, close/5 + %%alert_user/6, alert_user/9 ]). %% Data handling -export([%%write_application_data/3, - read_application_data/2%%, -%% passive_receive/2, next_record_if_active/1 + read_application_data/2, + %%passive_receive/2, + next_record_if_active/1 %%, + %%handle_common_event/4 ]). -%% Called by tls_connection_sup --export([start_link/7]). +%% gen_statem state functions +-export([init/3, error/3, downgrade/3, %% Initiation and take down states + hello/3, certify/3, cipher/3, abbreviated/3, %% Handshake states + connection/3]). +%% gen_statem callbacks +-export([terminate/3, code_change/4, format_status/2]). -%% gen_fsm callbacks --export([init/1, hello/2, certify/2, cipher/2, - abbreviated/2, connection/2, handle_event/3, - handle_sync_event/4, handle_info/3, terminate/3, code_change/4]). +-define(GEN_STATEM_CB_MODE, state_functions). %%==================================================================== %% Internal application API @@ -141,75 +142,74 @@ send_change_cipher(Msg, #state{connection_states = ConnectionStates0, start_link(Role, Host, Port, Socket, Options, User, CbInfo) -> {ok, proc_lib:spawn_link(?MODULE, init, [[Role, Host, Port, Socket, Options, User, CbInfo]])}. -init([Role, Host, Port, Socket, {SSLOpts0, _} = Options, User, CbInfo]) -> +init([Role, Host, Port, Socket, Options, User, CbInfo]) -> process_flag(trap_exit, true), State0 = initial_state(Role, Host, Port, Socket, Options, User, CbInfo), - Handshake = ssl_handshake:init_handshake_history(), - TimeStamp = erlang:monotonic_time(), - try ssl_config:init(SSLOpts0, Role) of - {ok, Ref, CertDbHandle, FileRefHandle, CacheHandle, CRLDbInfo, OwnCert, Key, DHParams} -> - Session = State0#state.session, - State = State0#state{ - tls_handshake_history = Handshake, - session = Session#session{own_certificate = OwnCert, - time_stamp = TimeStamp}, - file_ref_db = FileRefHandle, - cert_db_ref = Ref, - cert_db = CertDbHandle, - crl_db = CRLDbInfo, - session_cache = CacheHandle, - private_key = Key, - diffie_hellman_params = DHParams}, - gen_fsm:enter_loop(?MODULE, [], hello, State, get_timeout(State)) + try + State = ssl_connection:ssl_config(State0#state.ssl_options, Role, State0), + gen_statem:enter_loop(?MODULE, [], ?GEN_STATEM_CB_MODE, init, State) catch throw:Error -> - gen_fsm:enter_loop(?MODULE, [], error, {Error,State0}, get_timeout(State0)) + gen_statem:enter_loop(?MODULE, [], ?GEN_STATEM_CB_MODE, error, {Error,State0}) end. %%-------------------------------------------------------------------- -%% Description:There should be one instance of this function for each -%% possible state name. Whenever a gen_fsm receives an event sent -%% using gen_fsm:send_event/2, the instance of this function with the -%% same name as the current state name StateName is called to handle -%% the event. It is also called if a timeout occurs. -%% -hello(start, #state{host = Host, port = Port, role = client, - ssl_options = SslOpts, - session = #session{own_certificate = Cert} = Session0, - session_cache = Cache, session_cache_cb = CacheCb, - transport_cb = Transport, socket = Socket, - connection_states = ConnectionStates0, - renegotiation = {Renegotiation, _}} = State0) -> +%% State functionsconnection/2 +%%-------------------------------------------------------------------- + +init({call, From}, {start, Timeout}, + #state{host = Host, port = Port, role = client, + ssl_options = SslOpts, + session = #session{own_certificate = Cert} = Session0, + transport_cb = Transport, socket = Socket, + connection_states = ConnectionStates0, + renegotiation = {Renegotiation, _}, + session_cache = Cache, + session_cache_cb = CacheCb + } = State0) -> + Timer = ssl_connection:start_or_recv_cancel_timer(Timeout, From), Hello = dtls_handshake:client_hello(Host, Port, ConnectionStates0, SslOpts, - Cache, CacheCb, Renegotiation, Cert), + Cache, CacheCb, Renegotiation, Cert), Version = Hello#client_hello.client_version, + HelloVersion = dtls_record:lowest_protocol_version(SslOpts#ssl_options.versions), Handshake0 = ssl_handshake:init_handshake_history(), {BinMsg, ConnectionStates, Handshake} = - encode_handshake(Hello, Version, ConnectionStates0, Handshake0), + encode_handshake(Hello, HelloVersion, ConnectionStates0, Handshake0), Transport:send(Socket, BinMsg), State1 = State0#state{connection_states = ConnectionStates, negotiated_version = Version, %% Requested version session = Session0#session{session_id = Hello#client_hello.session_id}, - tls_handshake_history = Handshake}, + tls_handshake_history = Handshake, + start_or_recv_from = From, + timer = Timer}, {Record, State} = next_record(State1), - next_state(hello, hello, Record, State); - -hello(Hello = #client_hello{client_version = ClientVersion}, - State = #state{connection_states = ConnectionStates0, - port = Port, session = #session{own_certificate = Cert} = Session0, - renegotiation = {Renegotiation, _}, - session_cache = Cache, - session_cache_cb = CacheCb, - ssl_options = SslOpts}) -> + next_event(hello, Record, State); +init(Type, Event, State) -> + ssl_connection:init(Type, Event, State, ?MODULE). + +error({call, From}, {start, _Timeout}, {Error, State}) -> + {stop_and_reply, normal, {reply, From, {error, Error}}, State}; +error({call, From}, Msg, State) -> + handle_call(Msg, From, error, State); +error(_, _, _) -> + {keep_state_and_data, [postpone]}. + +hello(internal, #client_hello{client_version = ClientVersion} = Hello, + #state{connection_states = ConnectionStates0, + port = Port, session = #session{own_certificate = Cert} = Session0, + renegotiation = {Renegotiation, _}, + session_cache = Cache, + session_cache_cb = CacheCb, + ssl_options = SslOpts} = State) -> case dtls_handshake:hello(Hello, SslOpts, {Port, Session0, Cache, CacheCb, ConnectionStates0, Cert}, Renegotiation) of {Version, {Type, Session}, ConnectionStates, #hello_extensions{ec_point_formats = EcPointFormats, elliptic_curves = EllipticCurves} = ServerHelloExt, HashSign} -> - ssl_connection:hello({common_client_hello, Type, ServerHelloExt, HashSign}, + ssl_connection:hello(internal, {common_client_hello, Type, ServerHelloExt, HashSign}, State#state{connection_states = ConnectionStates, negotiated_version = Version, session = Session, @@ -217,7 +217,7 @@ hello(Hello = #client_hello{client_version = ClientVersion}, #alert{} = Alert -> handle_own_alert(Alert, ClientVersion, hello, State) end; -hello(Hello, +hello(internal, Hello, #state{connection_states = ConnectionStates0, negotiated_version = ReqVersion, role = client, @@ -230,20 +230,30 @@ hello(Hello, ssl_connection:handle_session(Hello, Version, NewId, ConnectionStates, ProtoExt, Protocol, State) end; - -hello(Msg, State) -> - ssl_connection:hello(Msg, State, ?MODULE). - -abbreviated(Msg, State) -> - ssl_connection:abbreviated(Msg, State, ?MODULE). - -certify(Msg, State) -> - ssl_connection:certify(Msg, State, ?MODULE). - -cipher(Msg, State) -> - ssl_connection:cipher(Msg, State, ?MODULE). - -connection(#hello_request{}, #state{host = Host, port = Port, +hello(info, Event, State) -> + handle_info(Event, hello, State); + +hello(Type, Event, State) -> + ssl_connection:hello(Type, Event, State, ?MODULE). + +abbreviated(info, Event, State) -> + handle_info(Event, abbreviated, State); +abbreviated(Type, Event, State) -> + ssl_connection:abbreviated(Type, Event, State, ?MODULE). + +certify(info, Event, State) -> + handle_info(Event, certify, State); +certify(Type, Event, State) -> + ssl_connection:certify(Type, Event, State, ?MODULE). + +cipher(info, Event, State) -> + handle_info(Event, cipher, State); +cipher(Type, Event, State) -> + ssl_connection:cipher(Type, Event, State, ?MODULE). + +connection(info, Event, State) -> + handle_info(Event, connection, State); +connection(internal, #hello_request{}, #state{host = Host, port = Port, session = #session{own_certificate = Cert} = Session0, session_cache = Cache, session_cache_cb = CacheCb, ssl_options = SslOpts, @@ -257,40 +267,30 @@ connection(#hello_request{}, #state{host = Host, port = Port, next_record( State1#state{session = Session0#session{session_id = Hello#client_hello.session_id}}), - next_state(connection, hello, Record, State); + next_event(hello, Record, State); -connection(#client_hello{} = Hello, #state{role = server, allow_renegotiate = true} = State) -> +connection(internal, #client_hello{} = Hello, #state{role = server, allow_renegotiate = true} = State) -> %% Mitigate Computational DoS attack %% http://www.educatedguesswork.org/2011/10/ssltls_and_computational_dos.html %% http://www.thc.org/thc-ssl-dos/ Rather than disabling client %% initiated renegotiation we will disallow many client initiated %% renegotiations immediately after each other. erlang:send_after(?WAIT_TO_ALLOW_RENEGOTIATION, self(), allow_renegotiate), - hello(Hello, State#state{allow_renegotiate = false}); + {next_state, hello, State#state{allow_renegotiate = false}, [{next_event, internal, Hello}]}; + -connection(#client_hello{}, #state{role = server, allow_renegotiate = false} = State0) -> +connection(internal, #client_hello{}, #state{role = server, allow_renegotiate = false} = State0) -> Alert = ?ALERT_REC(?WARNING, ?NO_RENEGOTIATION), - State = send_alert(Alert, State0), - next_state_connection(connection, State); + State1 = send_alert(Alert, State0), + {Record, State} = ssl_connection:prepare_connection(State1, ?MODULE), + next_event(connection, Record, State); -connection(Msg, State) -> - ssl_connection:connection(Msg, State, tls_connection). +connection(Type, Event, State) -> + ssl_connection:connection(Type, Event, State, ?MODULE). -%%-------------------------------------------------------------------- -%% Description: Whenever a gen_fsm receives an event sent using -%% gen_fsm:send_all_state_event/2, this function is called to handle -%% the event. Not currently used! -%%-------------------------------------------------------------------- -handle_event(_Event, StateName, State) -> - {next_state, StateName, State, get_timeout(State)}. +downgrade(Type, Event, State) -> + ssl_connection:downgrade(Type, Event, State, ?MODULE). -%%-------------------------------------------------------------------- -%% Description: Whenever a gen_fsm receives an event sent using -%% gen_fsm:sync_send_all_state_event/2,3, this function is called to handle -%% the event. -%%-------------------------------------------------------------------- -handle_sync_event(Event, From, StateName, State) -> - ssl_connection:handle_sync_event(Event, From, StateName, State). %%-------------------------------------------------------------------- %% Description: This function is called by a gen_fsm when it receives any @@ -301,26 +301,25 @@ handle_sync_event(Event, From, StateName, State) -> %% raw data from socket, unpack records handle_info({Protocol, _, Data}, StateName, #state{data_tag = Protocol} = State0) -> - %% Simplify for now to avoid dialzer warnings before implementation is compleate - %% case next_tls_record(Data, State0) of - %% {Record, State} -> - %% next_state(StateName, StateName, Record, State); - %% #alert{} = Alert -> - %% handle_normal_shutdown(Alert, StateName, State0), - %% {stop, {shutdown, own_alert}, State0} - %% end; - {Record, State} = next_tls_record(Data, State0), - next_state(StateName, StateName, Record, State); - + case next_tls_record(Data, State0) of + {Record, State} -> + next_event(StateName, Record, State); + #alert{} = Alert -> + handle_normal_shutdown(Alert, StateName, State0), + {stop, {shutdown, own_alert}} + end; handle_info({CloseTag, Socket}, StateName, #state{socket = Socket, close_tag = CloseTag, negotiated_version = _Version} = State) -> handle_normal_shutdown(?ALERT_REC(?FATAL, ?CLOSE_NOTIFY), StateName, State), - {stop, {shutdown, transport_closed}, State}; + {stop, {shutdown, transport_closed}}; handle_info(Msg, StateName, State) -> ssl_connection:handle_info(Msg, StateName, State). +handle_call(Event, From, StateName, State) -> + ssl_connection:handle_call(Event, From, StateName, State, ?MODULE). + %%-------------------------------------------------------------------- %% Description:This function is called by a gen_fsm when it is about %% to terminate. It should be the opposite of Module:init/1 and do any @@ -335,7 +334,10 @@ terminate(Reason, StateName, State) -> %% Description: Convert process state when code is changed %%-------------------------------------------------------------------- code_change(_OldVsn, StateName, State, _Extra) -> - {ok, StateName, State}. + {?GEN_STATEM_CB_MODE, StateName, State}. + +format_status(Type, Data) -> + ssl_connection:format_status(Type, Data). %%-------------------------------------------------------------------- %%% Internal functions @@ -372,96 +374,28 @@ next_record(#state{socket = Socket, next_record(State) -> {no_record, State}. -next_state(Current,_, #alert{} = Alert, #state{negotiated_version = Version} = State) -> - handle_own_alert(Alert, Version, Current, State); - -next_state(_,Next, no_record, State) -> - {next_state, Next, State, get_timeout(State)}; - -%% next_state(_,Next, #ssl_tls{type = ?ALERT, fragment = EncAlerts}, State) -> -%% Alerts = decode_alerts(EncAlerts), -%% handle_alerts(Alerts, {next_state, Next, State, get_timeout(State)}); - -next_state(Current, Next, #ssl_tls{type = ?HANDSHAKE, fragment = Data}, - State0 = #state{protocol_buffers = - #protocol_buffers{dtls_handshake_buffer = Buf0} = Buffers, - negotiated_version = Version}) -> - Handle = - fun({#hello_request{} = Packet, _}, {next_state, connection = SName, State}) -> - %% This message should not be included in handshake - %% message hashes. Starts new handshake (renegotiation) - Hs0 = ssl_handshake:init_handshake_history(), - ?MODULE:SName(Packet, State#state{tls_handshake_history=Hs0, - renegotiation = {true, peer}}); - ({#hello_request{} = Packet, _}, {next_state, SName, State}) -> - %% This message should not be included in handshake - %% message hashes. Already in negotiation so it will be ignored! - ?MODULE:SName(Packet, State); - ({#client_hello{} = Packet, Raw}, {next_state, connection = SName, State}) -> - Version = Packet#client_hello.client_version, - Hs0 = ssl_handshake:init_handshake_history(), - Hs1 = ssl_handshake:update_handshake_history(Hs0, Raw), - ?MODULE:SName(Packet, State#state{tls_handshake_history=Hs1, - renegotiation = {true, peer}}); - ({Packet, Raw}, {next_state, SName, State = #state{tls_handshake_history=Hs0}}) -> - Hs1 = ssl_handshake:update_handshake_history(Hs0, Raw), - ?MODULE:SName(Packet, State#state{tls_handshake_history=Hs1}); - (_, StopState) -> StopState - end, - try - {Packets, Buf} = tls_handshake:get_tls_handshake(Version,Data,Buf0), - State = State0#state{protocol_buffers = - Buffers#protocol_buffers{dtls_packets = Packets, - dtls_handshake_buffer = Buf}}, - handle_dtls_handshake(Handle, Next, State) - catch throw:#alert{} = Alert -> - handle_own_alert(Alert, Version, Current, State0) - end; -next_state(_, StateName, #ssl_tls{type = ?APPLICATION_DATA, fragment = Data}, State0) -> - %% Simplify for now to avoid dialzer warnings before implementation is compleate - %% case read_application_data(Data, State0) of - %% Stop = {stop,_,_} -> - %% Stop; - %% {Record, State} -> - %% next_state(StateName, StateName, Record, State) - %% end; - {Record, State} = read_application_data(Data, State0), - next_state(StateName, StateName, Record, State); - -next_state(Current, Next, #ssl_tls{type = ?CHANGE_CIPHER_SPEC, fragment = <<1>>} = - _ChangeCipher, - #state{connection_states = ConnectionStates0} = State0) -> - ConnectionStates1 = - ssl_record:activate_pending_connection_state(ConnectionStates0, read), - {Record, State} = next_record(State0#state{connection_states = ConnectionStates1}), - next_state(Current, Next, Record, State); -next_state(Current, Next, #ssl_tls{type = _Unknown}, State0) -> - %% Ignore unknown type - {Record, State} = next_record(State0), - next_state(Current, Next, Record, State). - -handle_dtls_handshake(Handle, StateName, - #state{protocol_buffers = - #protocol_buffers{dtls_packets = [Packet]} = Buffers} = State) -> - FsmReturn = {next_state, StateName, State#state{protocol_buffers = - Buffers#protocol_buffers{dtls_packets = []}}}, - Handle(Packet, FsmReturn); - -handle_dtls_handshake(Handle, StateName, - #state{protocol_buffers = - #protocol_buffers{dtls_packets = [Packet | Packets]} = Buffers} = - State0) -> - FsmReturn = {next_state, StateName, State0#state{protocol_buffers = - Buffers#protocol_buffers{dtls_packets = - Packets}}}, - case Handle(Packet, FsmReturn) of - {next_state, NextStateName, State, _Timeout} -> - handle_dtls_handshake(Handle, NextStateName, State); - {stop, _,_} = Stop -> - Stop - end. +next_event(StateName, Record, State) -> + next_event(StateName, Record, State, []). +next_event(connection = StateName, no_record, State0, Actions) -> + case next_record_if_active(State0) of + {no_record, State} -> + ssl_connection:hibernate_after(StateName, State, Actions); + {#ssl_tls{} = Record, State} -> + {next_state, StateName, State, [{next_event, internal, {dtls_record, Record}} | Actions]}; + {#alert{} = Alert, State} -> + {next_state, StateName, State, [{next_event, internal, Alert} | Actions]} + end; +next_event(StateName, Record, State, Actions) -> + case Record of + no_record -> + {next_state, StateName, State, Actions}; + #ssl_tls{} = Record -> + {next_state, StateName, State, [{next_event, internal, {dtls_record, Record}} | Actions]}; + #alert{} = Alert -> + {next_state, StateName, State, [{next_event, internal, Alert} | Actions]} + end. send_flight(Fragments, #state{transport_cb = Transport, socket = Socket, protocol_buffers = _PBuffers} = State) -> @@ -518,16 +452,19 @@ initial_state(Role, Host, Port, Socket, {SSLOptions, SocketOptions}, User, }. read_application_data(_,State) -> {#ssl_tls{fragment = <<"place holder">>}, State}. - + +next_tls_record(<<>>, _State) -> + #alert{}; %% Place holder next_tls_record(_, State) -> {#ssl_tls{fragment = <<"place holder">>}, State}. -get_timeout(_) -> %% Place holder - infinity. - -next_state_connection(_, State) -> %% Place holder - {next_state, connection, State, get_timeout(State)}. - sequence(_) -> %%TODO real imp 1. +next_record_if_active(State = + #state{socket_options = + #socket_options{active = false}}) -> + {no_record ,State}; + +next_record_if_active(State) -> + next_record(State). -- cgit v1.2.3 From df0c5663dd944a3dd06936105d0696a704c20e4e Mon Sep 17 00:00:00 2001 From: Kenneth Lakin Date: Sat, 30 Apr 2016 02:31:51 -0700 Subject: ssl: Add BEAST mitigation selection option Some legacy TLS 1.0 software does not tolerate the 1/n-1 content split BEAST mitigation technique. This commit adds a beast_mitigation SSL option (defaulting to one_n_minus_one) to select or disable the BEAST mitigation technique. Valid option values are (one_n_minus_one | zero_n | disabled). --- lib/ssl/src/dtls_connection.erl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'lib/ssl/src/dtls_connection.erl') diff --git a/lib/ssl/src/dtls_connection.erl b/lib/ssl/src/dtls_connection.erl index 82d6faee42..60a61bc901 100644 --- a/lib/ssl/src/dtls_connection.erl +++ b/lib/ssl/src/dtls_connection.erl @@ -416,7 +416,8 @@ encode_change_cipher(#change_cipher_spec{}, Version, ConnectionStates) -> initial_state(Role, Host, Port, Socket, {SSLOptions, SocketOptions}, User, {CbModule, DataTag, CloseTag, ErrorTag}) -> - ConnectionStates = ssl_record:init_connection_states(Role), + #ssl_options{beast_mitigation = BeastMitigation} = SSLOptions, + ConnectionStates = ssl_record:init_connection_states(Role, BeastMitigation), SessionCacheCb = case application:get_env(ssl, session_cb) of {ok, Cb} when is_atom(Cb) -> -- cgit v1.2.3 From 8ee98df1212cf4184c5d990e8f010f52a03cd728 Mon Sep 17 00:00:00 2001 From: Andreas Schultz Date: Thu, 7 Jan 2016 11:22:47 +0100 Subject: ssl: introduce the notion of flights for dtls and tls The flight concept was introduced by DTLS (RFC 4347) to optimize the packing of DTLS records into UDP packets. This change implments the flight concept in the the generic SSL connection logic and add the queue logic to the TLS and DTLS stack. The DTLS required resend handling is not implemented yet. While the flight handling is only required for DTSL, it turns out that the same mechanism can be usefull to TCP based TLS as well. With the current scheme each TLS record will be mapped into a separate TCP frame. This causes more TCP frames to be generate that necessary. On fast network this will have no impact, but reducing the number of frames and thereby the number of round trips can result in significant speedups on slow and unreliable networks. Conflicts: lib/ssl/src/tls_connection.erl --- lib/ssl/src/dtls_connection.erl | 51 ++++++++++++++++++++++++++--------------- 1 file changed, 32 insertions(+), 19 deletions(-) (limited to 'lib/ssl/src/dtls_connection.erl') diff --git a/lib/ssl/src/dtls_connection.erl b/lib/ssl/src/dtls_connection.erl index 60a61bc901..b7436ff15a 100644 --- a/lib/ssl/src/dtls_connection.erl +++ b/lib/ssl/src/dtls_connection.erl @@ -42,9 +42,8 @@ -export([next_record/1, next_event/3]). %% Handshake handling --export([%%renegotiate/2, - send_handshake/2, send_change_cipher/2]). - +-export([%%renegotiate/2, + send_handshake/2, queue_handshake/2, queue_change_cipher/2]). %% Alert and close handling -export([%%send_alert/2, handle_own_alert/4, handle_close_alert/3, @@ -100,14 +99,37 @@ start_fsm(Role, Host, Port, Socket, {#ssl_options{erl_dist = true},_} = Opts, Error end. -send_handshake(Handshake, #state{negotiated_version = Version, - tls_handshake_history = Hist0, - connection_states = ConnectionStates0} = State0) -> - {BinHandshake, ConnectionStates, Hist} = +send_handshake(Handshake, State) -> + send_handshake_flight(queue_handshake(Handshake, State)). + +queue_flight_buffer(Msg, #state{negotiated_version = Version, + connection_states = #connection_states{ + current_write = + #connection_state{epoch = Epoch}}, + flight_buffer = Flight} = State) -> + State#state{flight_buffer = Flight ++ [{Version, Epoch, Msg}]}. + +queue_handshake(Handshake, #state{negotiated_version = Version, + tls_handshake_history = Hist0, + connection_states = ConnectionStates0} = State0) -> + {Frag, ConnectionStates, Hist} = encode_handshake(Handshake, Version, ConnectionStates0, Hist0), - send_flight(BinHandshake, State0#state{connection_states = ConnectionStates, - tls_handshake_history = Hist - }). + queue_flight_buffer(Frag, State0#state{connection_states = ConnectionStates, + tls_handshake_history = Hist}). + +send_handshake_flight(#state{socket = Socket, + transport_cb = Transport, + flight_buffer = Flight, + connection_states = ConnectionStates0} = State0) -> + + {Encoded, ConnectionStates} = + encode_handshake_flight(Flight, ConnectionStates0), + + Transport:send(Socket, Encoded), + State0#state{flight_buffer = [], connection_states = ConnectionStates}. + +queue_change_cipher(Msg, State) -> + queue_flight_buffer(Msg, State). send_alert(Alert, #state{negotiated_version = Version, socket = Socket, @@ -118,15 +140,6 @@ send_alert(Alert, #state{negotiated_version = Version, Transport:send(Socket, BinMsg), State0#state{connection_states = ConnectionStates}. -send_change_cipher(Msg, #state{connection_states = ConnectionStates0, - socket = Socket, - negotiated_version = Version, - transport_cb = Transport} = State0) -> - {BinChangeCipher, ConnectionStates} = - encode_change_cipher(Msg, Version, ConnectionStates0), - Transport:send(Socket, BinChangeCipher), - State0#state{connection_states = ConnectionStates}. - %%==================================================================== %% tls_connection_sup API %%==================================================================== -- cgit v1.2.3 From 29a54ed880d0481ecf77069442b55994e8d2fe48 Mon Sep 17 00:00:00 2001 From: Andreas Schultz Date: Thu, 7 Jan 2016 13:33:23 +0100 Subject: dtls: update start_fsm for new ssl_connection API --- lib/ssl/src/dtls_connection.erl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'lib/ssl/src/dtls_connection.erl') diff --git a/lib/ssl/src/dtls_connection.erl b/lib/ssl/src/dtls_connection.erl index b7436ff15a..67490f9f29 100644 --- a/lib/ssl/src/dtls_connection.erl +++ b/lib/ssl/src/dtls_connection.erl @@ -71,13 +71,13 @@ %%==================================================================== %% Internal application API %%==================================================================== -start_fsm(Role, Host, Port, Socket, {#ssl_options{erl_dist = false},_} = Opts, +start_fsm(Role, Host, Port, Socket, {#ssl_options{erl_dist = false},_, Tracker} = Opts, User, {CbModule, _,_, _} = CbInfo, Timeout) -> try {ok, Pid} = dtls_connection_sup:start_child([Role, Host, Port, Socket, Opts, User, CbInfo]), - {ok, SslSocket} = ssl_connection:socket_control(?MODULE, Socket, Pid, CbModule), + {ok, SslSocket} = ssl_connection:socket_control(?MODULE, Socket, Pid, CbModule, Tracker), ok = ssl_connection:handshake(SslSocket, Timeout), {ok, SslSocket} catch @@ -85,13 +85,13 @@ start_fsm(Role, Host, Port, Socket, {#ssl_options{erl_dist = false},_} = Opts, Error end; -start_fsm(Role, Host, Port, Socket, {#ssl_options{erl_dist = true},_} = Opts, +start_fsm(Role, Host, Port, Socket, {#ssl_options{erl_dist = true},_, Tracker} = Opts, User, {CbModule, _,_, _} = CbInfo, Timeout) -> try {ok, Pid} = dtls_connection_sup:start_child_dist([Role, Host, Port, Socket, Opts, User, CbInfo]), - {ok, SslSocket} = ssl_connection:socket_control(?MODULE, Socket, Pid, CbModule), + {ok, SslSocket} = ssl_connection:socket_control(?MODULE, Socket, Pid, CbModule, Tracker), ok = ssl_connection:handshake(SslSocket, Timeout), {ok, SslSocket} catch -- cgit v1.2.3 From 549a92ed4980f1e87756edea55354de4c9b10856 Mon Sep 17 00:00:00 2001 From: Andreas Schultz Date: Wed, 4 May 2016 10:00:51 +0200 Subject: dtls: sync init and initial_state with tls_connection Sync initial_state overall functionality with TLS and add a few DTLS specific initalizers. Conflicts: lib/ssl/src/dtls_connection.erl --- lib/ssl/src/dtls_connection.erl | 138 +++++++++++++++++++++------------------- 1 file changed, 73 insertions(+), 65 deletions(-) (limited to 'lib/ssl/src/dtls_connection.erl') diff --git a/lib/ssl/src/dtls_connection.erl b/lib/ssl/src/dtls_connection.erl index 67490f9f29..cb5db3f4c3 100644 --- a/lib/ssl/src/dtls_connection.erl +++ b/lib/ssl/src/dtls_connection.erl @@ -365,6 +365,55 @@ encode_handshake(Handshake, Version, ConnectionStates0, Hist0) -> Version, ConnectionStates0), {Encoded, ConnectionStates, Hist}. +encode_change_cipher(#change_cipher_spec{}, Version, ConnectionStates) -> + dtls_record:encode_change_cipher_spec(Version, ConnectionStates). + +decode_alerts(Bin) -> + ssl_alert:decode(Bin). + +initial_state(Role, Host, Port, Socket, {SSLOptions, SocketOptions}, User, + {CbModule, DataTag, CloseTag, ErrorTag}) -> + #ssl_options{beast_mitigation = BeastMitigation} = SSLOptions, + ConnectionStates = ssl_record:init_connection_states(Role, BeastMitigation), + + SessionCacheCb = case application:get_env(ssl, session_cb) of + {ok, Cb} when is_atom(Cb) -> + Cb; + _ -> + ssl_session_cache + end, + + Monitor = erlang:monitor(process, User), + + #state{socket_options = SocketOptions, + %% We do not want to save the password in the state so that + %% could be written in the clear into error logs. + ssl_options = SSLOptions#ssl_options{password = undefined}, + session = #session{is_resumable = new}, + transport_cb = CbModule, + data_tag = DataTag, + close_tag = CloseTag, + error_tag = ErrorTag, + role = Role, + host = Host, + port = Port, + socket = Socket, + connection_states = ConnectionStates, + protocol_buffers = #protocol_buffers{}, + user_application = {Monitor, User}, + user_data_buffer = <<>>, + session_cache_cb = SessionCacheCb, + renegotiation = {false, first}, + allow_renegotiate = SSLOptions#ssl_options.client_renegotiation, + start_or_recv_from = undefined, + protocol_cb = ?MODULE + }. + +next_tls_record(<<>>, _State) -> + #alert{}; %% Place holder +next_tls_record(_, State) -> + {#ssl_tls{fragment = <<"place holder">>}, State}. + next_record(#state{%%flight = #flight{state = finished}, protocol_buffers = #protocol_buffers{dtls_packets = [], dtls_cipher_texts = [CT | Rest]} @@ -382,11 +431,30 @@ next_record(#state{socket = Socket, transport_cb = Transport} = State) -> %% when FlightState =/= finished ssl_socket:setopts(Transport, Socket, [{active,once}]), {no_record, State}; - - next_record(State) -> {no_record, State}. +next_record_if_active(State = + #state{socket_options = + #socket_options{active = false}}) -> + {no_record ,State}; + +next_record_if_active(State) -> + next_record(State). + +passive_receive(State0 = #state{user_data_buffer = Buffer}, StateName) -> + case Buffer of + <<>> -> + {Record, State} = next_record(State0), + next_event(StateName, Record, State); + _ -> + case read_application_data(<<>>, State0) of + Stop = {stop, _} -> + Stop; + {Record, State} -> + next_event(StateName, Record, State) + end + end. next_event(StateName, Record, State) -> next_event(StateName, Record, State, []). @@ -410,13 +478,8 @@ next_event(StateName, Record, State, Actions) -> {next_state, StateName, State, [{next_event, internal, Alert} | Actions]} end. -send_flight(Fragments, #state{transport_cb = Transport, socket = Socket, - protocol_buffers = _PBuffers} = State) -> - Transport:send(Socket, Fragments), - %% Start retransmission - %% State#state{protocol_buffers = - %% (PBuffers#protocol_buffers){ #flight{state = waiting}}}}. - State. +read_application_data(_,State) -> + {#ssl_tls{fragment = <<"place holder">>}, State}. handle_own_alert(_,_,_, State) -> %% Place holder {stop, {shutdown, own_alert}, State}. @@ -424,61 +487,6 @@ handle_own_alert(_,_,_, State) -> %% Place holder handle_normal_shutdown(_, _, _State) -> %% Place holder ok. -encode_change_cipher(#change_cipher_spec{}, Version, ConnectionStates) -> - dtls_record:encode_change_cipher_spec(Version, ConnectionStates). - -initial_state(Role, Host, Port, Socket, {SSLOptions, SocketOptions}, User, - {CbModule, DataTag, CloseTag, ErrorTag}) -> - #ssl_options{beast_mitigation = BeastMitigation} = SSLOptions, - ConnectionStates = ssl_record:init_connection_states(Role, BeastMitigation), - - SessionCacheCb = case application:get_env(ssl, session_cb) of - {ok, Cb} when is_atom(Cb) -> - Cb; - _ -> - ssl_session_cache - end, - - Monitor = erlang:monitor(process, User), - - #state{socket_options = SocketOptions, - %% We do not want to save the password in the state so that - %% could be written in the clear into error logs. - ssl_options = SSLOptions#ssl_options{password = undefined}, - session = #session{is_resumable = new}, - transport_cb = CbModule, - data_tag = DataTag, - close_tag = CloseTag, - error_tag = ErrorTag, - role = Role, - host = Host, - port = Port, - socket = Socket, - connection_states = ConnectionStates, - protocol_buffers = #protocol_buffers{}, - user_application = {Monitor, User}, - user_data_buffer = <<>>, - session_cache_cb = SessionCacheCb, - renegotiation = {false, first}, - allow_renegotiate = SSLOptions#ssl_options.client_renegotiation, - start_or_recv_from = undefined, - protocol_cb = ?MODULE - }. -read_application_data(_,State) -> - {#ssl_tls{fragment = <<"place holder">>}, State}. - -next_tls_record(<<>>, _State) -> - #alert{}; %% Place holder -next_tls_record(_, State) -> - {#ssl_tls{fragment = <<"place holder">>}, State}. - -sequence(_) -> +sequence(_) -> %%TODO real imp 1. -next_record_if_active(State = - #state{socket_options = - #socket_options{active = false}}) -> - {no_record ,State}; - -next_record_if_active(State) -> - next_record(State). -- cgit v1.2.3 From 54b59a6270355787700d06de619735a4c4240608 Mon Sep 17 00:00:00 2001 From: Andreas Schultz Date: Wed, 4 May 2016 10:11:17 +0200 Subject: dtls: implement next_tls_record Conflicts: lib/ssl/src/dtls_connection.erl --- lib/ssl/src/dtls_connection.erl | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) (limited to 'lib/ssl/src/dtls_connection.erl') diff --git a/lib/ssl/src/dtls_connection.erl b/lib/ssl/src/dtls_connection.erl index cb5db3f4c3..014e915e12 100644 --- a/lib/ssl/src/dtls_connection.erl +++ b/lib/ssl/src/dtls_connection.erl @@ -409,10 +409,19 @@ initial_state(Role, Host, Port, Socket, {SSLOptions, SocketOptions}, User, protocol_cb = ?MODULE }. -next_tls_record(<<>>, _State) -> - #alert{}; %% Place holder -next_tls_record(_, State) -> - {#ssl_tls{fragment = <<"place holder">>}, State}. +next_tls_record(Data, #state{protocol_buffers = #protocol_buffers{ + dtls_record_buffer = Buf0, + dtls_cipher_texts = CT0} = Buffers} = State0) -> + case dtls_record:get_dtls_records(Data, Buf0) of + {Records, Buf1} -> + CT1 = CT0 ++ Records, + next_record(State0#state{protocol_buffers = + Buffers#protocol_buffers{dtls_record_buffer = Buf1, + dtls_cipher_texts = CT1}}); + + #alert{} = Alert -> + Alert + end. next_record(#state{%%flight = #flight{state = finished}, protocol_buffers = -- cgit v1.2.3 From 62bca08d3c1c380ad473796ed0d80e97bd3d9265 Mon Sep 17 00:00:00 2001 From: Andreas Schultz Date: Thu, 7 Jan 2016 14:03:24 +0100 Subject: dtls: rework handshake flight encodeing The MSS might change between sending the a flight and possible resend. We therefore have to be able to fragment the records differently for resent. Encoding and fragmenting of handshake record therefor needs to be done independently. With this change the handshake is encoded to it's full length first, then queued to a flight. The fragmentation is handled during assembly of the flights datagram. Conflicts: lib/ssl/src/dtls_connection.erl --- lib/ssl/src/dtls_connection.erl | 81 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 71 insertions(+), 10 deletions(-) (limited to 'lib/ssl/src/dtls_connection.erl') diff --git a/lib/ssl/src/dtls_connection.erl b/lib/ssl/src/dtls_connection.erl index 014e915e12..3ebc340ff8 100644 --- a/lib/ssl/src/dtls_connection.erl +++ b/lib/ssl/src/dtls_connection.erl @@ -54,7 +54,7 @@ %% Data handling -export([%%write_application_data/3, read_application_data/2, - %%passive_receive/2, + passive_receive/2, next_record_if_active/1 %%, %%handle_common_event/4 ]). @@ -356,20 +356,81 @@ format_status(Type, Data) -> %%% Internal functions %%-------------------------------------------------------------------- encode_handshake(Handshake, Version, ConnectionStates0, Hist0) -> - Seq = sequence(ConnectionStates0), - {EncHandshake, FragmentedHandshake} = dtls_handshake:encode_handshake(Handshake, Version, - Seq), + {Seq, ConnectionStates} = sequence(ConnectionStates0), + {EncHandshake, Frag} = dtls_handshake:encode_handshake(Handshake, Version, Seq), Hist = ssl_handshake:update_handshake_history(Hist0, EncHandshake), - {Encoded, ConnectionStates} = - dtls_record:encode_handshake(FragmentedHandshake, - Version, ConnectionStates0), - {Encoded, ConnectionStates, Hist}. + {Frag, ConnectionStates, Hist}. encode_change_cipher(#change_cipher_spec{}, Version, ConnectionStates) -> dtls_record:encode_change_cipher_spec(Version, ConnectionStates). -decode_alerts(Bin) -> - ssl_alert:decode(Bin). +encode_handshake_flight(Flight, ConnectionStates) -> + MSS = 1400, + encode_handshake_records(Flight, ConnectionStates, MSS, init_pack_records()). + +encode_handshake_records([], CS, _MSS, Recs) -> + {finish_pack_records(Recs), CS}; + +encode_handshake_records([{Version, _Epoch, Frag = #change_cipher_spec{}}|Tail], ConnectionStates0, MSS, Recs0) -> + {Encoded, ConnectionStates} = + encode_change_cipher(Frag, Version, ConnectionStates0), + Recs = append_pack_records([Encoded], MSS, Recs0), + encode_handshake_records(Tail, ConnectionStates, MSS, Recs); + +encode_handshake_records([{Version, Epoch, {MsgType, MsgSeq, Bin}}|Tail], CS0, MSS, Recs0 = {Buf0, _}) -> + Space = MSS - iolist_size(Buf0), + Len = byte_size(Bin), + {Encoded, CS} = + encode_handshake_record(Version, Epoch, Space, MsgType, MsgSeq, Len, Bin, 0, MSS, [], CS0), + Recs = append_pack_records(Encoded, MSS, Recs0), + encode_handshake_records(Tail, CS, MSS, Recs). + +%% TODO: move to dtls_handshake???? +encode_handshake_record(_Version, _Epoch, _Space, _MsgType, _MsgSeq, _Len, <<>>, _Offset, _MRS, Encoded, CS) + when length(Encoded) > 0 -> + %% make sure we encode at least one segment (for empty messages like Server Hello Done + {lists:reverse(Encoded), CS}; + +encode_handshake_record(Version, Epoch, Space, MsgType, MsgSeq, Len, Bin, + Offset, MRS, Encoded0, CS0) -> + MaxFragmentLen = Space - 25, + case Bin of + <> -> + ok; + _ -> + BinFragment = Bin, + Rest = <<>> + end, + FragLength = byte_size(BinFragment), + Frag = [MsgType, ?uint24(Len), ?uint16(MsgSeq), ?uint24(Offset), ?uint24(FragLength), BinFragment], + {Encoded, CS} = ssl_record:encode_handshake({Epoch, Frag}, Version, CS0), + encode_handshake_record(Version, Epoch, MRS, MsgType, MsgSeq, Len, Rest, Offset + FragLength, MRS, [Encoded|Encoded0], CS). + +init_pack_records() -> + {[], []}. + +append_pack_records([], MSS, Recs = {Buf0, Acc0}) -> + Remaining = MSS - iolist_size(Buf0), + if Remaining < 12 -> + {[], [lists:reverse(Buf0)|Acc0]}; + true -> + Recs + end; +append_pack_records([Head|Tail], MSS, {Buf0, Acc0}) -> + TotLen = iolist_size(Buf0) + iolist_size(Head), + if TotLen > MSS -> + append_pack_records(Tail, MSS, {[Head], [lists:reverse(Buf0)|Acc0]}); + true -> + append_pack_records(Tail, MSS, {[Head|Buf0], Acc0}) + end. + +finish_pack_records({[], Acc}) -> + lists:reverse(Acc); +finish_pack_records({Buf, Acc}) -> + lists:reverse([lists:reverse(Buf)|Acc]). + +%% decode_alerts(Bin) -> +%% ssl_alert:decode(Bin). initial_state(Role, Host, Port, Socket, {SSLOptions, SocketOptions}, User, {CbModule, DataTag, CloseTag, ErrorTag}) -> -- cgit v1.2.3 From 615ae720c1d123168623f411ccf5e8e528828088 Mon Sep 17 00:00:00 2001 From: Andreas Schultz Date: Wed, 4 May 2016 10:54:10 +0200 Subject: dtls: sync handling of ClientHello with TLS --- lib/ssl/src/dtls_connection.erl | 52 +++++++++++++++++++++++++++-------------- 1 file changed, 34 insertions(+), 18 deletions(-) (limited to 'lib/ssl/src/dtls_connection.erl') diff --git a/lib/ssl/src/dtls_connection.erl b/lib/ssl/src/dtls_connection.erl index 3ebc340ff8..dfed9ce8c3 100644 --- a/lib/ssl/src/dtls_connection.erl +++ b/lib/ssl/src/dtls_connection.erl @@ -209,28 +209,44 @@ error({call, From}, Msg, State) -> error(_, _, _) -> {keep_state_and_data, [postpone]}. -hello(internal, #client_hello{client_version = ClientVersion} = Hello, - #state{connection_states = ConnectionStates0, - port = Port, session = #session{own_certificate = Cert} = Session0, - renegotiation = {Renegotiation, _}, - session_cache = Cache, - session_cache_cb = CacheCb, - ssl_options = SslOpts} = State) -> +%%-------------------------------------------------------------------- +-spec hello(gen_statem:event_type(), + #hello_request{} | #client_hello{} | #server_hello{} | term(), + #state{}) -> + gen_statem:state_function_result(). +%%-------------------------------------------------------------------- +hello(internal, #client_hello{client_version = ClientVersion, + extensions = #hello_extensions{ec_point_formats = EcPointFormats, + elliptic_curves = EllipticCurves}} = Hello, + State = #state{connection_states = ConnectionStates0, + port = Port, session = #session{own_certificate = Cert} = Session0, + renegotiation = {Renegotiation, _}, + session_cache = Cache, + session_cache_cb = CacheCb, + negotiated_protocol = CurrentProtocol, + key_algorithm = KeyExAlg, + ssl_options = SslOpts}) -> + case dtls_handshake:hello(Hello, SslOpts, {Port, Session0, Cache, CacheCb, - ConnectionStates0, Cert}, Renegotiation) of - {Version, {Type, Session}, - ConnectionStates, - #hello_extensions{ec_point_formats = EcPointFormats, - elliptic_curves = EllipticCurves} = ServerHelloExt, HashSign} -> - ssl_connection:hello(internal, {common_client_hello, Type, ServerHelloExt, HashSign}, - State#state{connection_states = ConnectionStates, + ConnectionStates0, Cert, KeyExAlg}, Renegotiation) of + #alert{} = Alert -> + handle_own_alert(Alert, ClientVersion, hello, State); + {Version, {Type, Session}, + ConnectionStates, Protocol0, ServerHelloExt, HashSign} -> + Protocol = case Protocol0 of + undefined -> CurrentProtocol; + _ -> Protocol0 + end, + + ssl_connection:hello(internal, {common_client_hello, Type, ServerHelloExt}, + State#state{connection_states = ConnectionStates, negotiated_version = Version, + hashsign_algorithm = HashSign, session = Session, - client_ecc = {EllipticCurves, EcPointFormats}}, ?MODULE); - #alert{} = Alert -> - handle_own_alert(Alert, ClientVersion, hello, State) + client_ecc = {EllipticCurves, EcPointFormats}, + negotiated_protocol = Protocol}, ?MODULE) end; -hello(internal, Hello, +hello(internal, #server_hello{} = Hello, #state{connection_states = ConnectionStates0, negotiated_version = ReqVersion, role = client, -- cgit v1.2.3 From e919adc2411718514419ea91034a911a08d1605d Mon Sep 17 00:00:00 2001 From: Andreas Schultz Date: Wed, 4 May 2016 10:57:16 +0200 Subject: dtls: sync handle_info for connection close with TLS --- lib/ssl/src/dtls_connection.erl | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) (limited to 'lib/ssl/src/dtls_connection.erl') diff --git a/lib/ssl/src/dtls_connection.erl b/lib/ssl/src/dtls_connection.erl index dfed9ce8c3..8d7763c7cb 100644 --- a/lib/ssl/src/dtls_connection.erl +++ b/lib/ssl/src/dtls_connection.erl @@ -338,11 +338,24 @@ handle_info({Protocol, _, Data}, StateName, {stop, {shutdown, own_alert}} end; handle_info({CloseTag, Socket}, StateName, - #state{socket = Socket, close_tag = CloseTag, - negotiated_version = _Version} = State) -> + #state{socket = Socket, close_tag = CloseTag, + negotiated_version = Version} = State) -> + %% Note that as of DTLS 1.2 (TLS 1.1), + %% failure to properly close a connection no longer requires that a + %% session not be resumed. This is a change from DTLS 1.0 to conform + %% with widespread implementation practice. + case Version of + {254, N} when N =< 253 -> + ok; + _ -> + %% As invalidate_sessions here causes performance issues, + %% we will conform to the widespread implementation + %% practice and go aginst the spec + %%invalidate_session(Role, Host, Port, Session) + ok + end, handle_normal_shutdown(?ALERT_REC(?FATAL, ?CLOSE_NOTIFY), StateName, State), {stop, {shutdown, transport_closed}}; - handle_info(Msg, StateName, State) -> ssl_connection:handle_info(Msg, StateName, State). -- cgit v1.2.3 From 1e967711800a38575f78dd9f82373fa1b39c8f76 Mon Sep 17 00:00:00 2001 From: Andreas Schultz Date: Thu, 7 Jan 2016 15:29:22 +0100 Subject: dtls: add support first packet and HelloVerifyRequest The actual user of this API is the UDP socket multiplexer which will be added later. Conflicts: lib/ssl/src/dtls_connection.erl --- lib/ssl/src/dtls_connection.erl | 57 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 53 insertions(+), 4 deletions(-) (limited to 'lib/ssl/src/dtls_connection.erl') diff --git a/lib/ssl/src/dtls_connection.erl b/lib/ssl/src/dtls_connection.erl index 8d7763c7cb..0864b2143a 100644 --- a/lib/ssl/src/dtls_connection.erl +++ b/lib/ssl/src/dtls_connection.erl @@ -52,12 +52,12 @@ ]). %% Data handling + -export([%%write_application_data/3, read_application_data/2, - passive_receive/2, - next_record_if_active/1 %%, - %%handle_common_event/4 - ]). + passive_receive/2, next_record_if_active/1, + %%handle_common_event/4, + handle_packet/3]). %% gen_statem state functions -export([init/3, error/3, downgrade/3, %% Initiation and take down states @@ -586,6 +586,55 @@ handle_own_alert(_,_,_, State) -> %% Place holder handle_normal_shutdown(_, _, _State) -> %% Place holder ok. + sequence(_) -> %%TODO real imp 1. + +handle_packet(Address, Port, Packet) -> + try dtls_record:get_dtls_records(Packet, <<>>) of + %% expect client hello + {[#ssl_tls{type = ?HANDSHAKE, version = {254, _}} = Record], <<>>} -> + handle_dtls_client_hello(Address, Port, Record); + _Other -> + {error, not_dtls} + catch + _Class:_Error -> + {error, not_dtls} + end. + +handle_dtls_client_hello(Address, Port, + #ssl_tls{epoch = Epoch, sequence_number = Seq, + version = Version} = Record) -> + {[{Hello, _}], _} = + dtls_handshake:get_dtls_handshake(Record, + dtls_handshake:dtls_handshake_new_flight(undefined)), + #client_hello{client_version = {Major, Minor}, + random = Random, + session_id = SessionId, + cipher_suites = CipherSuites, + compression_methods = CompressionMethods} = Hello, + CookieData = [address_to_bin(Address, Port), + <>, + Random, SessionId, CipherSuites, CompressionMethods], + Cookie = crypto:hmac(sha, <<"secret">>, CookieData), + + case Hello of + #client_hello{cookie = Cookie} -> + accept; + + _ -> + %% generate HelloVerifyRequest + {RequestFragment, _} = dtls_handshake:encode_handshake( + dtls_handshake:hello_verify_request(Cookie), + Version, 0), + HelloVerifyRequest = + dtls_record:encode_tls_cipher_text(?HANDSHAKE, Version, Epoch, Seq, RequestFragment), + {reply, HelloVerifyRequest} + end. + +address_to_bin({A,B,C,D}, Port) -> + <<0:80,16#ffff:16,A,B,C,D,Port:16>>; +address_to_bin({A,B,C,D,E,F,G,H}, Port) -> + <>. + -- cgit v1.2.3 From 0c38557bbee3f5cb288e8e1df9a77f2fbf1765d1 Mon Sep 17 00:00:00 2001 From: Ingela Anderton Andin Date: Mon, 13 Jun 2016 21:41:21 +0200 Subject: dtls: Remove TODO --- lib/ssl/src/dtls_connection.erl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'lib/ssl/src/dtls_connection.erl') diff --git a/lib/ssl/src/dtls_connection.erl b/lib/ssl/src/dtls_connection.erl index 0864b2143a..e4486d5b56 100644 --- a/lib/ssl/src/dtls_connection.erl +++ b/lib/ssl/src/dtls_connection.erl @@ -290,8 +290,7 @@ connection(internal, #hello_request{}, #state{host = Host, port = Port, renegotiation = {Renegotiation, _}} = State0) -> Hello = dtls_handshake:client_hello(Host, Port, ConnectionStates0, SslOpts, Cache, CacheCb, Renegotiation, Cert), - %% TODO DTLS version State1 = send_handshake(Hello, State0), - State1 = State0, + State1 = send_handshake(Hello, State0), {Record, State} = next_record( State1#state{session = Session0#session{session_id -- cgit v1.2.3 From 4c4bec1b8ba8bb8f9af0e8bb28725ade5e24adfe Mon Sep 17 00:00:00 2001 From: Andreas Schultz Date: Thu, 7 Jan 2016 14:14:47 +0100 Subject: dtls: add implementation for msg sequence Conflicts: lib/ssl/src/dtls_connection.erl lib/ssl/src/ssl_record.erl --- lib/ssl/src/dtls_connection.erl | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'lib/ssl/src/dtls_connection.erl') diff --git a/lib/ssl/src/dtls_connection.erl b/lib/ssl/src/dtls_connection.erl index e4486d5b56..c5687ef58b 100644 --- a/lib/ssl/src/dtls_connection.erl +++ b/lib/ssl/src/dtls_connection.erl @@ -585,11 +585,6 @@ handle_own_alert(_,_,_, State) -> %% Place holder handle_normal_shutdown(_, _, _State) -> %% Place holder ok. - -sequence(_) -> - %%TODO real imp - 1. - handle_packet(Address, Port, Packet) -> try dtls_record:get_dtls_records(Packet, <<>>) of %% expect client hello @@ -637,3 +632,5 @@ address_to_bin({A,B,C,D}, Port) -> address_to_bin({A,B,C,D,E,F,G,H}, Port) -> <>. +sequence(#connection_states{dtls_write_msg_seq = Seq} = CS) -> + {Seq, CS#connection_states{dtls_write_msg_seq = Seq + 1}}. -- cgit v1.2.3 From 49b815f872d7e7ea38260ee5bd8bf470fa42c03a Mon Sep 17 00:00:00 2001 From: Ingela Anderton Andin Date: Mon, 13 Jun 2016 22:16:59 +0200 Subject: dtls: Avoid dialyzer errors Make real solution later. For now we want to move forward without dialyzer errors. --- lib/ssl/src/dtls_connection.erl | 109 ++++++++++++++++++++-------------------- 1 file changed, 54 insertions(+), 55 deletions(-) (limited to 'lib/ssl/src/dtls_connection.erl') diff --git a/lib/ssl/src/dtls_connection.erl b/lib/ssl/src/dtls_connection.erl index c5687ef58b..b8be686b99 100644 --- a/lib/ssl/src/dtls_connection.erl +++ b/lib/ssl/src/dtls_connection.erl @@ -55,9 +55,10 @@ -export([%%write_application_data/3, read_application_data/2, - passive_receive/2, next_record_if_active/1, + passive_receive/2, next_record_if_active/1%, %%handle_common_event/4, - handle_packet/3]). + %handle_packet/3 + ]). %% gen_statem state functions -export([init/3, error/3, downgrade/3, %% Initiation and take down states @@ -431,7 +432,8 @@ encode_handshake_record(Version, Epoch, Space, MsgType, MsgSeq, Len, Bin, end, FragLength = byte_size(BinFragment), Frag = [MsgType, ?uint24(Len), ?uint16(MsgSeq), ?uint24(Offset), ?uint24(FragLength), BinFragment], - {Encoded, CS} = ssl_record:encode_handshake({Epoch, Frag}, Version, CS0), + %% TODO Real solution, now avoid dialyzer error {Encoded, CS} = ssl_record:encode_handshake({Epoch, Frag}, Version, CS0), + {Encoded, CS} = ssl_record:encode_handshake(Frag, Version, CS0), encode_handshake_record(Version, Epoch, MRS, MsgType, MsgSeq, Len, Rest, Offset + FragLength, MRS, [Encoded|Encoded0], CS). init_pack_records() -> @@ -546,12 +548,8 @@ passive_receive(State0 = #state{user_data_buffer = Buffer}, StateName) -> {Record, State} = next_record(State0), next_event(StateName, Record, State); _ -> - case read_application_data(<<>>, State0) of - Stop = {stop, _} -> - Stop; - {Record, State} -> - next_event(StateName, Record, State) - end + {Record, State} = read_application_data(<<>>, State0), + next_event(StateName, Record, State) end. next_event(StateName, Record, State) -> @@ -585,52 +583,53 @@ handle_own_alert(_,_,_, State) -> %% Place holder handle_normal_shutdown(_, _, _State) -> %% Place holder ok. -handle_packet(Address, Port, Packet) -> - try dtls_record:get_dtls_records(Packet, <<>>) of - %% expect client hello - {[#ssl_tls{type = ?HANDSHAKE, version = {254, _}} = Record], <<>>} -> - handle_dtls_client_hello(Address, Port, Record); - _Other -> - {error, not_dtls} - catch - _Class:_Error -> - {error, not_dtls} - end. - -handle_dtls_client_hello(Address, Port, - #ssl_tls{epoch = Epoch, sequence_number = Seq, - version = Version} = Record) -> - {[{Hello, _}], _} = - dtls_handshake:get_dtls_handshake(Record, - dtls_handshake:dtls_handshake_new_flight(undefined)), - #client_hello{client_version = {Major, Minor}, - random = Random, - session_id = SessionId, - cipher_suites = CipherSuites, - compression_methods = CompressionMethods} = Hello, - CookieData = [address_to_bin(Address, Port), - <>, - Random, SessionId, CipherSuites, CompressionMethods], - Cookie = crypto:hmac(sha, <<"secret">>, CookieData), - - case Hello of - #client_hello{cookie = Cookie} -> - accept; - - _ -> - %% generate HelloVerifyRequest - {RequestFragment, _} = dtls_handshake:encode_handshake( - dtls_handshake:hello_verify_request(Cookie), - Version, 0), - HelloVerifyRequest = - dtls_record:encode_tls_cipher_text(?HANDSHAKE, Version, Epoch, Seq, RequestFragment), - {reply, HelloVerifyRequest} - end. - -address_to_bin({A,B,C,D}, Port) -> - <<0:80,16#ffff:16,A,B,C,D,Port:16>>; -address_to_bin({A,B,C,D,E,F,G,H}, Port) -> - <>. +%% TODO This generates dialyzer warnings, has to be handled differently. +%% handle_packet(Address, Port, Packet) -> +%% try dtls_record:get_dtls_records(Packet, <<>>) of +%% %% expect client hello +%% {[#ssl_tls{type = ?HANDSHAKE, version = {254, _}} = Record], <<>>} -> +%% handle_dtls_client_hello(Address, Port, Record); +%% _Other -> +%% {error, not_dtls} +%% catch +%% _Class:_Error -> +%% {error, not_dtls} +%% end. + +%% handle_dtls_client_hello(Address, Port, +%% #ssl_tls{epoch = Epoch, sequence_number = Seq, +%% version = Version} = Record) -> +%% {[{Hello, _}], _} = +%% dtls_handshake:get_dtls_handshake(Record, +%% dtls_handshake:dtls_handshake_new_flight(undefined)), +%% #client_hello{client_version = {Major, Minor}, +%% random = Random, +%% session_id = SessionId, +%% cipher_suites = CipherSuites, +%% compression_methods = CompressionMethods} = Hello, +%% CookieData = [address_to_bin(Address, Port), +%% <>, +%% Random, SessionId, CipherSuites, CompressionMethods], +%% Cookie = crypto:hmac(sha, <<"secret">>, CookieData), + +%% case Hello of +%% #client_hello{cookie = Cookie} -> +%% accept; + +%% _ -> +%% %% generate HelloVerifyRequest +%% {RequestFragment, _} = dtls_handshake:encode_handshake( +%% dtls_handshake:hello_verify_request(Cookie), +%% Version, 0), +%% HelloVerifyRequest = +%% dtls_record:encode_tls_cipher_text(?HANDSHAKE, Version, Epoch, Seq, RequestFragment), +%% {reply, HelloVerifyRequest} +%% end. + +%% address_to_bin({A,B,C,D}, Port) -> +%% <<0:80,16#ffff:16,A,B,C,D,Port:16>>; +%% address_to_bin({A,B,C,D,E,F,G,H}, Port) -> +%% <>. sequence(#connection_states{dtls_write_msg_seq = Seq} = CS) -> {Seq, CS#connection_states{dtls_write_msg_seq = Seq + 1}}. -- cgit v1.2.3