aboutsummaryrefslogblamecommitdiffstats
path: root/lib/ssl/src/dtls_connection.erl
blob: fda488501c281b5fcfa42f2352fdd0a589237944 (plain) (tree)
1
2
3
4
5
6
7
8
9
10

                   


                                                        




                                                                      
  



                                                                         
  
                 
  
                         



























































































































































































































































































































                                                                                                    





                                                                                            
%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 2013-2013. 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/.
%%
%% 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.
%%
%% %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).

%% 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).