aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorIngela Anderton Andin <[email protected]>2014-02-27 14:48:59 +0100
committerIngela Anderton Andin <[email protected]>2014-03-25 15:48:22 +0100
commitc635f15d22802f7ff18fd1ce9197b2cc760979ef (patch)
tree2f4ff0c4b570f5049d614fd076cf50cee5320f78 /lib
parent8ffbf0feccb375afc10ce676070b6b778e9bf260 (diff)
downloadotp-c635f15d22802f7ff18fd1ce9197b2cc760979ef.tar.gz
otp-c635f15d22802f7ff18fd1ce9197b2cc760979ef.tar.bz2
otp-c635f15d22802f7ff18fd1ce9197b2cc760979ef.zip
ssl: Refactor and start implementing dtls_connection.erl
Diffstat (limited to 'lib')
-rw-r--r--lib/ssl/src/dtls_connection.erl717
-rw-r--r--lib/ssl/src/dtls_connection.hrl21
-rw-r--r--lib/ssl/src/dtls_connection_sup.erl12
-rw-r--r--lib/ssl/src/dtls_handshake.erl56
-rw-r--r--lib/ssl/src/dtls_record.erl38
-rw-r--r--lib/ssl/src/dtls_record.hrl13
-rw-r--r--lib/ssl/src/ssl.erl17
-rw-r--r--lib/ssl/src/ssl_cipher.erl4
-rw-r--r--lib/ssl/src/ssl_record.erl4
9 files changed, 602 insertions, 280 deletions
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).
diff --git a/lib/ssl/src/dtls_connection.hrl b/lib/ssl/src/dtls_connection.hrl
index b8dff479d5..08707dc8de 100644
--- a/lib/ssl/src/dtls_connection.hrl
+++ b/lib/ssl/src/dtls_connection.hrl
@@ -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
@@ -28,24 +28,19 @@
-include("ssl_connection.hrl").
-record(protocol_buffers, {
- dtls_packets = [] ::[binary()], % Not yet handled decode ssl/tls packets.
- dtls_record_buffer :: binary(), % Buffer of incomplete records
- dtls_handshake_buffer :: binary(), % Buffer of incomplete handshakes
- dtls_cipher_texts :: [binary()],
- dtls_cipher_texts_next :: [binary()] % Received for Epoch not yet active
+ dtls_packets = [], %%::[binary()], % Not yet handled decode ssl/tls packets.
+ dtls_record_buffer = <<>>, %%:: binary(), % Buffer of incomplete records
+ dtls_handshake_buffer = <<>>, %%:: binary(), % Buffer of incomplete handshakes
+ dtls_cipher_texts = [], %%:: [binary()],
+ dtls_cipher_texts_next %%:: [binary()] % Received for Epoch not yet active
}).
-record(flight, {
last_retransmit,
last_read_seq,
msl_timer,
- flight_state,
- flight_buffer, % buffer of not yet ACKed TLS records
- }).
-
--record(message_sequences, {
- read = 0,
- write = 0
+ state,
+ buffer % buffer of not yet ACKed TLS records
}).
-endif. % -ifdef(dtls_connection).
diff --git a/lib/ssl/src/dtls_connection_sup.erl b/lib/ssl/src/dtls_connection_sup.erl
index 9fe545be18..0b4711cfb4 100644
--- a/lib/ssl/src/dtls_connection_sup.erl
+++ b/lib/ssl/src/dtls_connection_sup.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2007-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
@@ -26,8 +26,8 @@
-behaviour(supervisor).
%% API
--export([start_link/0]).
--export([start_child/1]).
+-export([start_link/0, start_link_dist/0]).
+-export([start_child/1, start_child_dist/1]).
%% Supervisor callback
-export([init/1]).
@@ -38,8 +38,14 @@
start_link() ->
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
+start_link_dist() ->
+ supervisor:start_link({local, dtls_connection_sup_dist}, ?MODULE, []).
+
start_child(Args) ->
supervisor:start_child(?MODULE, Args).
+
+start_child_dist(Args) ->
+ supervisor:start_child(dtls_connection_sup_dist, Args).
%%%=========================================================================
%%% Supervisor callback
diff --git a/lib/ssl/src/dtls_handshake.erl b/lib/ssl/src/dtls_handshake.erl
index 5db2434753..2ab5598dea 100644
--- a/lib/ssl/src/dtls_handshake.erl
+++ b/lib/ssl/src/dtls_handshake.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
@@ -21,10 +21,10 @@
-include("dtls_record.hrl").
-include("ssl_internal.hrl").
--export([client_hello/8, client_hello/9, hello/3,
+-export([client_hello/8, client_hello/9, hello/4,
get_dtls_handshake/2,
dtls_handshake_new_flight/1, dtls_handshake_new_epoch/1,
- encode_handshake/4]).
+ encode_handshake/2]).
%%====================================================================
%% Internal application API
@@ -32,7 +32,7 @@
%%--------------------------------------------------------------------
-spec client_hello(host(), inet:port_number(), #connection_states{},
#ssl_options{}, integer(), atom(), boolean(), der_cert()) ->
- #client_hello{}.
+ #client_hello{} | {retransmit, term()}.
%%
%% Description: Creates a client hello message.
%%--------------------------------------------------------------------
@@ -45,16 +45,16 @@ client_hello(Host, Port, ConnectionStates, SslOpts,
%%--------------------------------------------------------------------
-spec client_hello(host(), inet:port_number(), term(), #connection_states{},
#ssl_options{}, integer(), atom(), boolean(), der_cert()) ->
- #client_hello{}.
+ #client_hello{} | {retransmit, term()}.
%%
%% Description: Creates a client hello message.
%%--------------------------------------------------------------------
client_hello(Host, Port, Cookie, ConnectionStates,
- #ssl_options{versions = Versions,
+ #ssl_options{versions = _Versions,
ciphers = UserSuites
} = SslOpts,
Cache, CacheCb, Renegotiation, OwnCert) ->
- Version = dtls_record:highest_protocol_version(Versions),
+ Version = [{254, 253}], %%dtls_record:highest_protocol_version(Versions),
Pending = ssl_record:pending_connection_state(ConnectionStates, read),
SecParams = Pending#connection_state.security_parameters,
CipherSuites = ssl_handshake:available_suites(UserSuites, Version),
@@ -74,11 +74,17 @@ client_hello(Host, Port, Cookie, ConnectionStates,
}.
hello(Address, Port,
- #ssl_tls{epoch = _Epoch, record_seq = _Seq,
+ #ssl_tls{epoch = _Epoch, sequence_number = _Seq,
version = Version} = Record) ->
- {[{Hello, _}], _, _} =
- get_dtls_handshake(Record,
- dtls_handshake_new_flight(undefined)),
+ case get_dtls_handshake(Record,
+ dtls_handshake_new_flight(undefined)) of
+ {[Hello | _], _} ->
+ hello(Address, Port, Version, Hello);
+ {retransmit, HandshakeState} ->
+ {retransmit, HandshakeState}
+ end.
+
+hello(Address, Port, Version, Hello) ->
#client_hello{client_version = {Major, Minor},
random = Random,
session_id = SessionId,
@@ -94,19 +100,19 @@ hello(Address, Port,
accept;
_ ->
%% generate HelloVerifyRequest
- HelloVerifyRequest = encode_handshake(#hello_verify_request{protocol_version = Version,
+ HelloVerifyRequest = enc_hs(#hello_verify_request{protocol_version = Version,
cookie = Cookie},
Version, 0, 1400),
{reply, HelloVerifyRequest}
end.
%%--------------------------------------------------------------------
-encode_handshake(Package, Version, MsgSeq, Mss) ->
- {MsgType, Bin} = enc_hs(Package, Version),
+enc_hs(Package, Version, MsgSeq, Mss) ->
+ {MsgType, Bin} = encode_handshake(Package, Version),
Len = byte_size(Bin),
- HsHistory = [MsgType, ?uint24(Len), ?uint16(MsgSeq), ?uint24(0), ?uint24(Len), Bin],
- BinMsg = dtls_split_handshake(Mss, MsgType, Len, MsgSeq, Bin, 0, []),
- {HsHistory, BinMsg}.
+ Handshake = [MsgType, ?uint24(Len), ?uint16(MsgSeq), ?uint24(0), ?uint24(Len), Bin],
+ FragmentedHandshake = dtls_fragment(Mss, MsgType, Len, MsgSeq, Bin, 0, []),
+ {Handshake, FragmentedHandshake}.
%--------------------------------------------------------------------
-spec get_dtls_handshake(#ssl_tls{}, #dtls_hs_state{} | binary()) ->
@@ -147,19 +153,19 @@ dtls_handshake_new_flight(ExpectedReadReq) ->
%%% Internal functions
%%--------------------------------------------------------------------
-dtls_split_handshake(Mss, MsgType, Len, MsgSeq, Bin, Offset, Acc)
+dtls_fragment(Mss, MsgType, Len, MsgSeq, Bin, Offset, Acc)
when byte_size(Bin) + 12 < Mss ->
FragmentLen = byte_size(Bin),
BinMsg = [MsgType, ?uint24(Len), ?uint16(MsgSeq), ?uint24(Offset), ?uint24(FragmentLen), Bin],
lists:reverse([BinMsg|Acc]);
-dtls_split_handshake(Mss, MsgType, Len, MsgSeq, Bin, Offset, Acc) ->
+dtls_fragment(Mss, MsgType, Len, MsgSeq, Bin, Offset, Acc) ->
FragmentLen = Mss - 12,
<<Fragment:FragmentLen/bytes, Rest/binary>> = Bin,
BinMsg = [MsgType, ?uint24(Len), ?uint16(MsgSeq), ?uint24(Offset), ?uint24(FragmentLen), Fragment],
- dtls_split_handshake(Mss, MsgType, Len, MsgSeq, Rest, Offset + FragmentLen, [BinMsg|Acc]).
+ dtls_fragment(Mss, MsgType, Len, MsgSeq, Rest, Offset + FragmentLen, [BinMsg|Acc]).
get_dtls_handshake_aux(#ssl_tls{version = Version,
- record_seq = SeqNo,
+ sequence_number = SeqNo,
fragment = Data}, HsState) ->
get_dtls_handshake_aux(Version, SeqNo, Data, HsState).
@@ -370,14 +376,14 @@ merge_fragment_list([{HStart, HEnd}|Rest], _Frag = {FStart, FEnd}, Acc)
add_fragment(List, {FragmentOffset, FragmentLength}) ->
merge_fragment_list(List, {FragmentOffset, FragmentOffset + FragmentLength}, []).
-enc_hs(#hello_verify_request{protocol_version = {Major, Minor},
- cookie = Cookie}, _Version) ->
+encode_handshake(#hello_verify_request{protocol_version = {Major, Minor},
+ cookie = Cookie}, _Version) ->
CookieLength = byte_size(Cookie),
{?HELLO_VERIFY_REQUEST, <<?BYTE(Major), ?BYTE(Minor),
?BYTE(CookieLength),
Cookie/binary>>};
-enc_hs(#client_hello{client_version = {Major, Minor},
+encode_handshake(#client_hello{client_version = {Major, Minor},
random = Random,
session_id = SessionID,
cookie = Cookie,
@@ -397,7 +403,7 @@ enc_hs(#client_hello{client_version = {Major, Minor},
BinCookie/binary,
?UINT16(CsLength), BinCipherSuites/binary,
?BYTE(CmLength), BinCompMethods/binary, ExtensionsBin/binary>>};
-enc_hs(HandshakeMsg, Version) ->
+encode_handshake(HandshakeMsg, Version) ->
ssl_handshake:encode_handshake(HandshakeMsg, Version).
enc_client_hello_cookie(_, <<>>) ->
diff --git a/lib/ssl/src/dtls_record.erl b/lib/ssl/src/dtls_record.erl
index b0a7976864..531e2612a5 100644
--- a/lib/ssl/src/dtls_record.erl
+++ b/lib/ssl/src/dtls_record.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
@@ -35,7 +35,7 @@
-export([decode_cipher_text/2]).
%% Encoding
--export([encode_plain_text/4]).
+-export([encode_plain_text/4, encode_handshake/3, encode_change_cipher_spec/2]).
%% Protocol version handling
-export([protocol_version/1, lowest_protocol_version/2,
@@ -70,7 +70,7 @@ get_dtls_records_aux(<<?BYTE(?APPLICATION_DATA),?BYTE(MajVer),?BYTE(MinVer),
Acc) ->
get_dtls_records_aux(Rest, [#ssl_tls{type = ?APPLICATION_DATA,
version = {MajVer, MinVer},
- epoch = Epoch, record_seq = SequenceNumber,
+ epoch = Epoch, sequence_number = SequenceNumber,
fragment = Data} | Acc]);
get_dtls_records_aux(<<?BYTE(?HANDSHAKE),?BYTE(MajVer),?BYTE(MinVer),
?UINT16(Epoch), ?UINT48(SequenceNumber),
@@ -78,7 +78,7 @@ get_dtls_records_aux(<<?BYTE(?HANDSHAKE),?BYTE(MajVer),?BYTE(MinVer),
Data:Length/binary, Rest/binary>>, Acc) when MajVer >= 128 ->
get_dtls_records_aux(Rest, [#ssl_tls{type = ?HANDSHAKE,
version = {MajVer, MinVer},
- epoch = Epoch, record_seq = SequenceNumber,
+ epoch = Epoch, sequence_number = SequenceNumber,
fragment = Data} | Acc]);
get_dtls_records_aux(<<?BYTE(?ALERT),?BYTE(MajVer),?BYTE(MinVer),
?UINT16(Epoch), ?UINT48(SequenceNumber),
@@ -86,7 +86,7 @@ get_dtls_records_aux(<<?BYTE(?ALERT),?BYTE(MajVer),?BYTE(MinVer),
Rest/binary>>, Acc) ->
get_dtls_records_aux(Rest, [#ssl_tls{type = ?ALERT,
version = {MajVer, MinVer},
- epoch = Epoch, record_seq = SequenceNumber,
+ epoch = Epoch, sequence_number = SequenceNumber,
fragment = Data} | Acc]);
get_dtls_records_aux(<<?BYTE(?CHANGE_CIPHER_SPEC),?BYTE(MajVer),?BYTE(MinVer),
?UINT16(Epoch), ?UINT48(SequenceNumber),
@@ -94,7 +94,7 @@ get_dtls_records_aux(<<?BYTE(?CHANGE_CIPHER_SPEC),?BYTE(MajVer),?BYTE(MinVer),
Acc) ->
get_dtls_records_aux(Rest, [#ssl_tls{type = ?CHANGE_CIPHER_SPEC,
version = {MajVer, MinVer},
- epoch = Epoch, record_seq = SequenceNumber,
+ epoch = Epoch, sequence_number = SequenceNumber,
fragment = Data} | Acc]);
get_dtls_records_aux(<<0:1, _CT:7, ?BYTE(_MajVer), ?BYTE(_MinVer),
@@ -125,14 +125,15 @@ encode_plain_text(Type, Version, Data,
{Comp, CompS1} = ssl_record:compress(CompAlg, Data, CompS0),
WriteState1 = WriteState0#connection_state{compression_state = CompS1},
MacHash = calc_mac_hash(WriteState1, Type, Version, Epoch, Seq, Comp),
- {CipherFragment, WriteState} = ssl_record:cipher(Version, Comp, WriteState1, MacHash),
+ {CipherFragment, WriteState} = ssl_record:cipher(dtls_v1:corresponding_tls_version(Version),
+ Comp, WriteState1, MacHash),
CipherText = encode_tls_cipher_text(Type, Version, Epoch, Seq, CipherFragment),
{CipherText, ConnectionStates#connection_states{current_write =
WriteState#connection_state{sequence_number = Seq +1}}}.
decode_cipher_text(#ssl_tls{type = Type, version = Version,
epoch = Epoch,
- record_seq = Seq,
+ sequence_number = Seq,
fragment = CipherFragment} = CipherText,
#connection_states{current_read =
#connection_state{compression_state = CompressionS0,
@@ -141,7 +142,7 @@ decode_cipher_text(#ssl_tls{type = Type, version = Version,
CompressAlg = SecParams#security_parameters.compression_algorithm,
{PlainFragment, Mac, ReadState1} = ssl_record:decipher(dtls_v1:corresponding_tls_version(Version),
CipherFragment, ReadState0),
- MacHash = calc_mac_hash(Type, Version, Epoch, Seq, PlainFragment, ReadState1),
+ MacHash = calc_mac_hash(ReadState1, Type, Version, Epoch, Seq, PlainFragment),
case ssl_record:is_correct_mac(Mac, MacHash) of
true ->
{Plain, CompressionS1} = ssl_record:uncompress(CompressAlg,
@@ -153,6 +154,23 @@ decode_cipher_text(#ssl_tls{type = Type, version = Version,
false ->
?ALERT_REC(?FATAL, ?BAD_RECORD_MAC)
end.
+%%--------------------------------------------------------------------
+-spec encode_handshake(iolist(), tls_version(), #connection_states{}) ->
+ {iolist(), #connection_states{}}.
+%%
+%% Description: Encodes a handshake message to send on the ssl-socket.
+%%--------------------------------------------------------------------
+encode_handshake(Frag, Version, ConnectionStates) ->
+ encode_plain_text(?HANDSHAKE, Version, Frag, ConnectionStates).
+
+%%--------------------------------------------------------------------
+-spec encode_change_cipher_spec(tls_version(), #connection_states{}) ->
+ {iolist(), #connection_states{}}.
+%%
+%% Description: Encodes a change_cipher_spec-message to send on the ssl socket.
+%%--------------------------------------------------------------------
+encode_change_cipher_spec(Version, ConnectionStates) ->
+ encode_plain_text(?CHANGE_CIPHER_SPEC, Version, <<1:8>>, ConnectionStates).
%%--------------------------------------------------------------------
-spec protocol_version(tls_atom_version() | tls_version()) ->
@@ -343,5 +361,5 @@ calc_mac_hash(#connection_state{mac_secret = MacSecret,
Length, Fragment).
mac_hash(Version, MacAlg, MacSecret, SeqNo, Type, Length, Fragment) ->
- dtls_v1:mac_hash(MacAlg, MacSecret, SeqNo, Type, Version,
+ dtls_v1:mac_hash(Version, MacAlg, MacSecret, SeqNo, Type,
Length, Fragment).
diff --git a/lib/ssl/src/dtls_record.hrl b/lib/ssl/src/dtls_record.hrl
index e935d84bdf..edb77fb2b1 100644
--- a/lib/ssl/src/dtls_record.hrl
+++ b/lib/ssl/src/dtls_record.hrl
@@ -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
@@ -28,16 +28,15 @@
-include("ssl_record.hrl"). %% Common TLS and DTLS records and Constantes
-%% Used to handle tls_plain_text, tls_compressed and tls_cipher_text
+%% Used to handle dtls_plain_text, dtls_compressed and dtls_cipher_text
-record(ssl_tls, {
type,
version,
- record_seq, % used in plain_text
- epoch, % used in plain_text
- message_seq,
- fragment_offset,
- fragment_length,
+ epoch,
+ sequence_number,
+ offset,
+ length,
fragment
}).
diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl
index 9e098e12c4..e7b4c3a7bf 100644
--- a/lib/ssl/src/ssl.erl
+++ b/lib/ssl/src/ssl.erl
@@ -558,6 +558,8 @@ handle_options(Opts0, _Role) ->
Opts = proplists:expand([{binary, [{mode, binary}]},
{list, [{mode, list}]}], Opts0),
assert_proplist(Opts),
+ RecordCb = record_cb(Opts),
+
ReuseSessionFun = fun(_, _, _, _) -> true end,
DefaultVerifyNoneFun =
@@ -600,12 +602,14 @@ handle_options(Opts0, _Role) ->
end,
CertFile = handle_option(certfile, Opts, <<>>),
-
+
+ RecordCb = record_cb(Opts),
+
Versions = case handle_option(versions, Opts, []) of
[] ->
- tls_record:supported_protocol_versions();
+ RecordCb:supported_protocol_versions();
Vsns ->
- [tls_record:protocol_version(Vsn) || Vsn <- Vsns]
+ [RecordCb:protocol_version(Vsn) || Vsn <- Vsns]
end,
SSLOptions = #ssl_options{
@@ -1035,6 +1039,13 @@ connection_cb(dtls) ->
connection_cb(Opts) ->
connection_cb(proplists:get_value(protocol, Opts, tls)).
+record_cb(tls) ->
+ tls_record;
+record_cb(dtls) ->
+ dtls_record;
+record_cb(Opts) ->
+ record_cb(proplists:get_value(protocol, Opts, tls)).
+
connection_sup(tls_connection) ->
tls_connection_sup;
connection_sup(dtls_connection) ->
diff --git a/lib/ssl/src/ssl_cipher.erl b/lib/ssl/src/ssl_cipher.erl
index b2077c662a..97e8a4241f 100644
--- a/lib/ssl/src/ssl_cipher.erl
+++ b/lib/ssl/src/ssl_cipher.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2007-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
@@ -72,7 +72,7 @@ security_parameters(Version, CipherSuite, SecParams) ->
hash_size = hash_size(Hash)}.
%%--------------------------------------------------------------------
--spec cipher(cipher_enum(), #cipher_state{}, binary(), iolist(), tls_version()) ->
+-spec cipher(cipher_enum(), #cipher_state{}, binary(), iodata(), tls_version()) ->
{binary(), #cipher_state{}}.
%%
%% Description: Encrypts the data and the MAC using chipher described
diff --git a/lib/ssl/src/ssl_record.erl b/lib/ssl/src/ssl_record.erl
index 018c8befe0..4d02ef65d2 100644
--- a/lib/ssl/src/ssl_record.erl
+++ b/lib/ssl/src/ssl_record.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
@@ -356,7 +356,7 @@ compressions() ->
[?byte(?NULL)].
%%--------------------------------------------------------------------
--spec cipher(tls_version(), iolist(), #connection_state{}, MacHash::binary()) ->
+-spec cipher(tls_version(), iodata(), #connection_state{}, MacHash::binary()) ->
{CipherFragment::binary(), #connection_state{}}.
%%
%% Description: Payload encryption