aboutsummaryrefslogtreecommitdiffstats
path: root/lib/ssl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/ssl')
-rw-r--r--lib/ssl/doc/src/notes.xml64
-rw-r--r--lib/ssl/src/Makefile9
-rw-r--r--lib/ssl/src/dtls_connection.erl322
-rw-r--r--lib/ssl/src/dtls_handshake.erl410
-rw-r--r--lib/ssl/src/dtls_handshake.hrl18
-rw-r--r--lib/ssl/src/dtls_record.erl366
-rw-r--r--lib/ssl/src/dtls_v1.erl43
-rw-r--r--lib/ssl/src/ssl.app.src42
-rw-r--r--lib/ssl/src/ssl.appup.src2
-rw-r--r--lib/ssl/src/ssl_cipher.erl28
-rw-r--r--lib/ssl/src/ssl_cipher.hrl3
-rw-r--r--lib/ssl/src/ssl_handshake.erl1650
-rw-r--r--lib/ssl/src/ssl_handshake.hrl34
-rw-r--r--lib/ssl/src/ssl_internal.hrl9
-rw-r--r--lib/ssl/src/ssl_record.erl439
-rw-r--r--lib/ssl/src/ssl_record.hrl24
-rw-r--r--lib/ssl/src/ssl_v2.erl (renamed from lib/ssl/src/ssl_ssl2.erl)16
-rw-r--r--lib/ssl/src/ssl_v3.erl (renamed from lib/ssl/src/ssl_ssl3.erl)28
-rw-r--r--lib/ssl/src/tls_connection.erl249
-rw-r--r--lib/ssl/src/tls_handshake.erl1750
-rw-r--r--lib/ssl/src/tls_handshake.hrl9
-rw-r--r--lib/ssl/src/tls_record.erl536
-rw-r--r--lib/ssl/src/tls_record.hrl1
-rw-r--r--lib/ssl/src/tls_v1.erl (renamed from lib/ssl/src/ssl_tls1.erl)42
-rw-r--r--lib/ssl/test/ssl_handshake_SUITE.erl63
-rw-r--r--lib/ssl/test/ssl_npn_hello_SUITE.erl77
-rw-r--r--lib/ssl/vsn.mk2
27 files changed, 3809 insertions, 2427 deletions
diff --git a/lib/ssl/doc/src/notes.xml b/lib/ssl/doc/src/notes.xml
index 301ff21068..9f706d435e 100644
--- a/lib/ssl/doc/src/notes.xml
+++ b/lib/ssl/doc/src/notes.xml
@@ -25,7 +25,69 @@
<file>notes.xml</file>
</header>
<p>This document describes the changes made to the SSL application.</p>
- <section><title>SSL 5.3</title>
+ <section><title>SSL 5.3.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Setopts during renegotiation caused the renegotiation to
+ be unsuccessful.</p>
+ <p>
+ If calling setopts during a renegotiation the FSM state
+ might change during the handling of the setopts messages,
+ this is now handled correctly.</p>
+ <p>
+ Own Id: OTP-11228</p>
+ </item>
+ <item>
+ <p>
+ Now handles signature_algorithm field in digitally_signed
+ properly with proper defaults. Prior to this change some
+ elliptic curve cipher suites could fail reporting the
+ error "bad certificate".</p>
+ <p>
+ Own Id: OTP-11229</p>
+ </item>
+ <item>
+ <p>
+ The code emulating the inet header option was changed in
+ the belief that it made it inet compatible. However the
+ testing is a bit hairy as the inet option is actually
+ broken, now the tests are corrected and the header option
+ should work in the same broken way as inet again,
+ preferably use the bitsyntax instead.</p>
+ <p>
+ Own Id: OTP-11230</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Make the ssl manager name for erlang distribution over
+ SSL/TLS relative to the module name of the ssl_manager.</p>
+ <p>
+ This can be beneficial when making tools that rename
+ modules for internal processing in the tool.</p>
+ <p>
+ Own Id: OTP-11255</p>
+ </item>
+ <item>
+ <p>
+ Add documentation regarding log_alert option.</p>
+ <p>
+ Own Id: OTP-11271</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>SSL 5.3</title>
<section><title>Fixed Bugs and Malfunctions</title>
<list>
diff --git a/lib/ssl/src/Makefile b/lib/ssl/src/Makefile
index cf9f7d5001..6744e2f256 100644
--- a/lib/ssl/src/Makefile
+++ b/lib/ssl/src/Makefile
@@ -58,15 +58,18 @@ MODULES= \
ssl_connection_sup \
tls_handshake \
dtls_handshake\
+ ssl_handshake\
ssl_manager \
ssl_session \
ssl_session_cache \
ssl_socket \
tls_record \
dtls_record \
- ssl_ssl2 \
- ssl_ssl3 \
- ssl_tls1 \
+ ssl_record \
+ ssl_v2 \
+ ssl_v3 \
+ tls_v1 \
+ dtls_v1 \
ssl_tls_dist_proxy
INTERNAL_HRL_FILES = \
diff --git a/lib/ssl/src/dtls_connection.erl b/lib/ssl/src/dtls_connection.erl
index ac2ee0d09f..fda488501c 100644
--- a/lib/ssl/src/dtls_connection.erl
+++ b/lib/ssl/src/dtls_connection.erl
@@ -17,3 +17,325 @@
%% %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).
diff --git a/lib/ssl/src/dtls_handshake.erl b/lib/ssl/src/dtls_handshake.erl
index b25daa59d9..26e8ce7503 100644
--- a/lib/ssl/src/dtls_handshake.erl
+++ b/lib/ssl/src/dtls_handshake.erl
@@ -16,3 +16,413 @@
%%
%% %CopyrightEnd%
-module(dtls_handshake).
+
+-include("dtls_handshake.hrl").
+-include("dtls_record.hrl").
+-include("ssl_internal.hrl").
+
+-export([client_hello/9, hello/3, get_dtls_handshake/2,
+ dtls_handshake_new_flight/1, dtls_handshake_new_epoch/1,
+ encode_handshake/4]).
+
+%%====================================================================
+%% Internal application API
+%%====================================================================
+
+%%--------------------------------------------------------------------
+-spec client_hello(host(), inet:port_number(), term(), #connection_states{},
+ #ssl_options{}, integer(), atom(), boolean(), der_cert()) ->
+ #client_hello{}.
+%%
+%% Description: Creates a client hello message.
+%%--------------------------------------------------------------------
+client_hello(Host, Port, Cookie, ConnectionStates,
+ #ssl_options{versions = Versions,
+ ciphers = UserSuites
+ } = SslOpts,
+ Cache, CacheCb, Renegotiation, OwnCert) ->
+ Version = 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),
+
+ Extensions = ssl_handshake:client_hello_extensions(Version, CipherSuites,
+ SslOpts, ConnectionStates, Renegotiation),
+
+ Id = ssl_session:client_id({Host, Port, SslOpts}, Cache, CacheCb, OwnCert),
+
+ #client_hello{session_id = Id,
+ client_version = Version,
+ cipher_suites = ssl_handshake:cipher_suites(CipherSuites, Renegotiation),
+ compression_methods = ssl_record:compressions(),
+ random = SecParams#security_parameters.client_random,
+ cookie = Cookie,
+ extensions = Extensions
+ }.
+
+hello(Address, Port,
+ #ssl_tls{epoch = _Epoch, record_seq = _Seq,
+ version = Version} = Record) ->
+ {[{Hello, _}], _, _} =
+ get_dtls_handshake(Record,
+ 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),
+ <<?BYTE(Major), ?BYTE(Minor)>>,
+ Random, SessionId, CipherSuites, CompressionMethods],
+ Cookie = crypto:hmac(sha, <<"secret">>, CookieData),
+
+ case Hello of
+ #client_hello{cookie = Cookie} ->
+ accept;
+ _ ->
+ %% generate HelloVerifyRequest
+ HelloVerifyRequest = encode_handshake(#hello_verify_request{protocol_version = Version,
+ cookie = Cookie},
+ Version, 0, 1400),
+ {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) ->
+ <<A:16,B:16,C:16,D:16,E:16,F:16,G:16,H:16,Port:16>>.
+
+%%--------------------------------------------------------------------
+encode_handshake(Package, Version, MsgSeq, Mss) ->
+ {MsgType, Bin} = enc_hs(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}.
+
+%--------------------------------------------------------------------
+-spec get_dtls_handshake(#ssl_tls{}, #dtls_hs_state{} | binary()) ->
+ {[dtls_handshake()], #ssl_tls{}}.
+%
+% Description: Given a DTLS state and new data from ssl_record, collects
+% and returns it as a list of handshake messages, also returns a new
+% DTLS state
+%--------------------------------------------------------------------
+% get_dtls_handshake(Record, <<>>) ->
+% get_dtls_handshake_aux(Record, dtls_hs_state_init());
+get_dtls_handshake(Record, HsState) ->
+ get_dtls_handshake_aux(Record, HsState).
+
+%--------------------------------------------------------------------
+-spec dtls_handshake_new_epoch(#dtls_hs_state{}) -> #dtls_hs_state{}.
+%
+% Description: Reset the DTLS decoder state for a new Epoch
+%--------------------------------------------------------------------
+% dtls_handshake_new_epoch(<<>>) ->
+% dtls_hs_state_init();
+dtls_handshake_new_epoch(HsState) ->
+ HsState#dtls_hs_state{highest_record_seq = 0,
+ starting_read_seq = HsState#dtls_hs_state.current_read_seq,
+ fragments = gb_trees:empty(), completed = []}.
+
+%--------------------------------------------------------------------
+-spec dtls_handshake_new_flight(integer() | undefined) -> #dtls_hs_state{}.
+%
+% Description: Init the DTLS decoder state for a new Flight
+dtls_handshake_new_flight(ExpectedReadReq) ->
+ #dtls_hs_state{current_read_seq = ExpectedReadReq,
+ highest_record_seq = 0,
+ starting_read_seq = 0,
+ fragments = gb_trees:empty(), completed = []}.
+
+%%--------------------------------------------------------------------
+%%% Internal functions
+%%--------------------------------------------------------------------
+
+dtls_split_handshake(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) ->
+ 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]).
+
+get_dtls_handshake_aux(#ssl_tls{version = Version,
+ record_seq = SeqNo,
+ fragment = Data}, HsState) ->
+ get_dtls_handshake_aux(Version, SeqNo, Data, HsState).
+
+get_dtls_handshake_aux(Version, SeqNo,
+ <<?BYTE(Type), ?UINT24(Length),
+ ?UINT16(MessageSeq),
+ ?UINT24(FragmentOffset), ?UINT24(FragmentLength),
+ Body:FragmentLength/binary, Rest/binary>>,
+ HsState0) ->
+ case reassemble_dtls_fragment(SeqNo, Type, Length, MessageSeq,
+ FragmentOffset, FragmentLength,
+ Body, HsState0) of
+ {retransmit, HsState1} ->
+ case Rest of
+ <<>> ->
+ {retransmit, HsState1};
+ _ ->
+ get_dtls_handshake_aux(Version, SeqNo, Rest, HsState1)
+ end;
+ {HsState1, HighestSeqNo, MsgBody} ->
+ HsState2 = dec_dtls_fragment(Version, HighestSeqNo, Type, Length, MessageSeq, MsgBody, HsState1),
+ HsState3 = process_dtls_fragments(Version, HsState2),
+ get_dtls_handshake_aux(Version, SeqNo, Rest, HsState3);
+ HsState2 ->
+ HsState3 = process_dtls_fragments(Version, HsState2),
+ get_dtls_handshake_aux(Version, SeqNo, Rest, HsState3)
+ end;
+
+get_dtls_handshake_aux(_Version, _SeqNo, <<>>, HsState) ->
+ {lists:reverse(HsState#dtls_hs_state.completed),
+ HsState#dtls_hs_state.highest_record_seq,
+ HsState#dtls_hs_state{completed = []}}.
+
+dec_dtls_fragment(Version, SeqNo, Type, Length, MessageSeq, MsgBody,
+ HsState = #dtls_hs_state{highest_record_seq = HighestSeqNo, completed = Acc}) ->
+ Raw = <<?BYTE(Type), ?UINT24(Length), ?UINT16(MessageSeq), ?UINT24(0), ?UINT24(Length), MsgBody/binary>>,
+ H = decode_handshake(Version, Type, MsgBody),
+ HsState#dtls_hs_state{completed = [{H,Raw}|Acc], highest_record_seq = erlang:max(HighestSeqNo, SeqNo)}.
+
+process_dtls_fragments(Version,
+ HsState0 = #dtls_hs_state{current_read_seq = CurrentReadSeq,
+ fragments = Fragments0}) ->
+ case gb_trees:is_empty(Fragments0) of
+ true ->
+ HsState0;
+ _ ->
+ case gb_trees:smallest(Fragments0) of
+ {CurrentReadSeq, {SeqNo, Type, Length, CurrentReadSeq, {Length, [{0, Length}], MsgBody}}} ->
+ HsState1 = dtls_hs_state_process_seq(HsState0),
+ HsState2 = dec_dtls_fragment(Version, SeqNo, Type, Length, CurrentReadSeq, MsgBody, HsState1),
+ process_dtls_fragments(Version, HsState2);
+ _ ->
+ HsState0
+ end
+ end.
+
+dtls_hs_state_process_seq(HsState0 = #dtls_hs_state{current_read_seq = CurrentReadSeq,
+ fragments = Fragments0}) ->
+ Fragments1 = gb_trees:delete_any(CurrentReadSeq, Fragments0),
+ HsState0#dtls_hs_state{current_read_seq = CurrentReadSeq + 1,
+ fragments = Fragments1}.
+
+dtls_hs_state_add_fragment(MessageSeq, Fragment, HsState0 = #dtls_hs_state{fragments = Fragments0}) ->
+ Fragments1 = gb_trees:enter(MessageSeq, Fragment, Fragments0),
+ HsState0#dtls_hs_state{fragments = Fragments1}.
+
+reassemble_dtls_fragment(SeqNo, Type, Length, MessageSeq, 0, Length,
+ Body, HsState0 = #dtls_hs_state{current_read_seq = undefined})
+ when Type == ?CLIENT_HELLO;
+ Type == ?SERVER_HELLO;
+ Type == ?HELLO_VERIFY_REQUEST ->
+ %% First message, should be client hello
+ %% return the current message and set the next expected Sequence
+ %%
+ %% Note: this could (should?) be restricted further, ClientHello and
+ %% HelloVerifyRequest have to have message_seq = 0, ServerHello
+ %% can have a message_seq of 0 or 1
+ %%
+ {HsState0#dtls_hs_state{current_read_seq = MessageSeq + 1}, SeqNo, Body};
+
+reassemble_dtls_fragment(_SeqNo, _Type, Length, _MessageSeq, _, Length,
+ _Body, HsState = #dtls_hs_state{current_read_seq = undefined}) ->
+ %% not what we expected, drop it
+ HsState;
+
+reassemble_dtls_fragment(SeqNo, _Type, Length, MessageSeq, 0, Length,
+ Body, HsState0 =
+ #dtls_hs_state{starting_read_seq = StartingReadSeq})
+ when MessageSeq < StartingReadSeq ->
+ %% this has to be the start of a new flight, let it through
+ %%
+ %% Note: this could (should?) be restricted further, the first message of a
+ %% new flight has to have message_seq = 0
+ %%
+ HsState = dtls_hs_state_process_seq(HsState0),
+ {HsState, SeqNo, Body};
+
+reassemble_dtls_fragment(_SeqNo, _Type, Length, MessageSeq, 0, Length,
+ _Body, HsState =
+ #dtls_hs_state{current_read_seq = CurrentReadSeq})
+ when MessageSeq < CurrentReadSeq ->
+ {retransmit, HsState};
+
+reassemble_dtls_fragment(_SeqNo, _Type, Length, MessageSeq, 0, Length,
+ _Body, HsState = #dtls_hs_state{current_read_seq = CurrentReadSeq})
+ when MessageSeq < CurrentReadSeq ->
+ HsState;
+
+reassemble_dtls_fragment(SeqNo, _Type, Length, MessageSeq, 0, Length,
+ Body, HsState0 = #dtls_hs_state{current_read_seq = MessageSeq}) ->
+ %% Message fully contained and it's the current seq
+ HsState1 = dtls_hs_state_process_seq(HsState0),
+ {HsState1, SeqNo, Body};
+
+reassemble_dtls_fragment(SeqNo, Type, Length, MessageSeq, 0, Length,
+ Body, HsState) ->
+ %% Message fully contained and it's the NOT the current seq -> buffer
+ Fragment = {SeqNo, Type, Length, MessageSeq,
+ dtls_fragment_init(Length, 0, Length, Body)},
+ dtls_hs_state_add_fragment(MessageSeq, Fragment, HsState);
+
+reassemble_dtls_fragment(_SeqNo, _Type, Length, MessageSeq, FragmentOffset, FragmentLength,
+ _Body,
+ HsState = #dtls_hs_state{current_read_seq = CurrentReadSeq})
+ when FragmentOffset + FragmentLength == Length andalso MessageSeq == (CurrentReadSeq - 1) ->
+ {retransmit, HsState};
+
+reassemble_dtls_fragment(_SeqNo, _Type, _Length, MessageSeq, _FragmentOffset, _FragmentLength,
+ _Body,
+ HsState = #dtls_hs_state{current_read_seq = CurrentReadSeq})
+ when MessageSeq < CurrentReadSeq ->
+ HsState;
+
+reassemble_dtls_fragment(SeqNo, Type, Length, MessageSeq,
+ FragmentOffset, FragmentLength,
+ Body,
+ HsState = #dtls_hs_state{fragments = Fragments0}) ->
+ case gb_trees:lookup(MessageSeq, Fragments0) of
+ {value, Fragment} ->
+ dtls_fragment_reassemble(SeqNo, Type, Length, MessageSeq,
+ FragmentOffset, FragmentLength,
+ Body, Fragment, HsState);
+ none ->
+ dtls_fragment_start(SeqNo, Type, Length, MessageSeq,
+ FragmentOffset, FragmentLength,
+ Body, HsState)
+ end.
+
+dtls_fragment_start(SeqNo, Type, Length, MessageSeq,
+ FragmentOffset, FragmentLength,
+ Body, HsState = #dtls_hs_state{fragments = Fragments0}) ->
+ Fragment = {SeqNo, Type, Length, MessageSeq,
+ dtls_fragment_init(Length, FragmentOffset, FragmentLength, Body)},
+ Fragments1 = gb_trees:insert(MessageSeq, Fragment, Fragments0),
+ HsState#dtls_hs_state{fragments = Fragments1}.
+
+dtls_fragment_reassemble(SeqNo, Type, Length, MessageSeq,
+ FragmentOffset, FragmentLength,
+ Body,
+ {LastSeqNo, Type, Length, MessageSeq, FragBuffer0},
+ HsState = #dtls_hs_state{fragments = Fragments0}) ->
+ FragBuffer1 = dtls_fragment_add(FragBuffer0, FragmentOffset, FragmentLength, Body),
+ Fragment = {erlang:max(SeqNo, LastSeqNo), Type, Length, MessageSeq, FragBuffer1},
+ Fragments1 = gb_trees:enter(MessageSeq, Fragment, Fragments0),
+ HsState#dtls_hs_state{fragments = Fragments1};
+
+%% Type, Length or Seq mismatch, drop everything...
+%% Note: the RFC is not clear on how to handle this...
+dtls_fragment_reassemble(_SeqNo, _Type, _Length, MessageSeq,
+ _FragmentOffset, _FragmentLength, _Body, _Fragment,
+ HsState = #dtls_hs_state{fragments = Fragments0}) ->
+ Fragments1 = gb_trees:delete_any(MessageSeq, Fragments0),
+ HsState#dtls_hs_state{fragments = Fragments1}.
+
+dtls_fragment_add({Length, FragmentList0, Bin0}, FragmentOffset, FragmentLength, Body) ->
+ Bin1 = dtls_fragment_bin_add(FragmentOffset, FragmentLength, Body, Bin0),
+ FragmentList1 = add_fragment(FragmentList0, {FragmentOffset, FragmentLength}),
+ {Length, FragmentList1, Bin1}.
+
+dtls_fragment_init(Length, 0, Length, Body) ->
+ {Length, [{0, Length}], Body};
+dtls_fragment_init(Length, FragmentOffset, FragmentLength, Body) ->
+ Bin = dtls_fragment_bin_add(FragmentOffset, FragmentLength, Body, <<0:(Length*8)>>),
+ {Length, [{FragmentOffset, FragmentLength}], Bin}.
+
+dtls_fragment_bin_add(FragmentOffset, FragmentLength, Add, Buffer) ->
+ <<First:FragmentOffset/bytes, _:FragmentLength/bytes, Rest/binary>> = Buffer,
+ <<First/binary, Add/binary, Rest/binary>>.
+
+merge_fragment_list([], Fragment, Acc) ->
+ lists:reverse([Fragment|Acc]);
+
+merge_fragment_list([H = {_, HEnd}|Rest], Frag = {FStart, _}, Acc)
+ when FStart > HEnd ->
+ merge_fragment_list(Rest, Frag, [H|Acc]);
+
+merge_fragment_list(Rest = [{HStart, _HEnd}|_], Frag = {_FStart, FEnd}, Acc)
+ when FEnd < HStart ->
+ lists:reverse(Acc) ++ [Frag|Rest];
+
+merge_fragment_list([{HStart, HEnd}|Rest], _Frag = {FStart, FEnd}, Acc)
+ when
+ FStart =< HEnd orelse FEnd >= HStart ->
+ Start = erlang:min(HStart, FStart),
+ End = erlang:max(HEnd, FEnd),
+ NewFrag = {Start, End},
+ merge_fragment_list(Rest, NewFrag, 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) ->
+ CookieLength = byte_size(Cookie),
+ {?HELLO_VERIFY_REQUEST, <<?BYTE(Major), ?BYTE(Minor),
+ ?BYTE(CookieLength),
+ Cookie/binary>>};
+
+enc_hs(#client_hello{client_version = {Major, Minor},
+ random = Random,
+ session_id = SessionID,
+ cookie = Cookie,
+ cipher_suites = CipherSuites,
+ compression_methods = CompMethods,
+ extensions = HelloExtensions}, Version) ->
+ SIDLength = byte_size(SessionID),
+ BinCookie = enc_client_hello_cookie(Version, Cookie),
+ BinCompMethods = list_to_binary(CompMethods),
+ CmLength = byte_size(BinCompMethods),
+ BinCipherSuites = list_to_binary(CipherSuites),
+ CsLength = byte_size(BinCipherSuites),
+ ExtensionsBin = ssl_handshake:encode_hello_extensions(HelloExtensions),
+
+ {?CLIENT_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary,
+ ?BYTE(SIDLength), SessionID/binary,
+ BinCookie/binary,
+ ?UINT16(CsLength), BinCipherSuites/binary,
+ ?BYTE(CmLength), BinCompMethods/binary, ExtensionsBin/binary>>};
+enc_hs(HandshakeMsg, Version) ->
+ ssl_handshake:encode_handshake(HandshakeMsg, Version).
+
+enc_client_hello_cookie(_, <<>>) ->
+ <<>>;
+enc_client_hello_cookie(_, Cookie) ->
+ CookieLength = byte_size(Cookie),
+ <<?BYTE(CookieLength), Cookie/binary>>.
+
+decode_handshake(_Version, ?CLIENT_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary,
+ ?BYTE(SID_length), Session_ID:SID_length/binary,
+ ?BYTE(Cookie_length), Cookie:Cookie_length/binary,
+ ?UINT16(Cs_length), CipherSuites:Cs_length/binary,
+ ?BYTE(Cm_length), Comp_methods:Cm_length/binary,
+ Extensions/binary>>) ->
+
+ DecodedExtensions = ssl_handshake:decode_hello_extensions(Extensions),
+
+ #client_hello{
+ client_version = {Major,Minor},
+ random = Random,
+ session_id = Session_ID,
+ cookie = Cookie,
+ cipher_suites = ssl_handshake:decode_suites('2_bytes', CipherSuites),
+ compression_methods = Comp_methods,
+ extensions = DecodedExtensions
+ };
+
+decode_handshake(_Version, ?HELLO_VERIFY_REQUEST, <<?BYTE(Major), ?BYTE(Minor),
+ ?BYTE(CookieLength), Cookie:CookieLength/binary>>) ->
+
+ #hello_verify_request{
+ protocol_version = {Major,Minor},
+ cookie = Cookie};
+decode_handshake(Version, Tag, Msg) ->
+ ssl_handshake:decode_handshake(Version, Tag, Msg).
diff --git a/lib/ssl/src/dtls_handshake.hrl b/lib/ssl/src/dtls_handshake.hrl
index db7b8596ae..5bdf45f627 100644
--- a/lib/ssl/src/dtls_handshake.hrl
+++ b/lib/ssl/src/dtls_handshake.hrl
@@ -27,6 +27,8 @@
-include("ssl_handshake.hrl"). %% Common TLS and DTLS records and Constantes
+-define(HELLO_VERIFY_REQUEST, 3).
+
-record(client_hello, {
client_version,
random,
@@ -35,16 +37,22 @@
cipher_suites, % cipher_suites<2..2^16-1>
compression_methods, % compression_methods<1..2^8-1>,
%% Extensions
- renegotiation_info,
- hash_signs, % supported combinations of hashes/signature algos
- next_protocol_negotiation = undefined % [binary()]
+ extensions
}).
--record(hello_verify_request {
+-record(hello_verify_request, {
protocol_version,
cookie
}).
--define(HELLO_VERIFY_REQUEST, 3).
+-record(dtls_hs_state,
+ {current_read_seq,
+ starting_read_seq,
+ highest_record_seq,
+ fragments,
+ completed
+ }).
+
+-type dtls_handshake() :: #client_hello{} | #hello_verify_request{} | ssl_handshake().
-endif. % -ifdef(dtls_handshake).
diff --git a/lib/ssl/src/dtls_record.erl b/lib/ssl/src/dtls_record.erl
index 2469a7d26c..f667458a10 100644
--- a/lib/ssl/src/dtls_record.erl
+++ b/lib/ssl/src/dtls_record.erl
@@ -15,4 +15,370 @@
%% under the License.
%%
%% %CopyrightEnd%
+
+%%
+%%----------------------------------------------------------------------
+%% Purpose: Handle DTLS record protocol. (Parts that are not shared with SSL/TLS)
+%%----------------------------------------------------------------------
-module(dtls_record).
+
+-include("dtls_record.hrl").
+-include("ssl_internal.hrl").
+-include("ssl_alert.hrl").
+-include("dtls_handshake.hrl").
+-include("ssl_cipher.hrl").
+
+%% Handling of incoming data
+-export([get_dtls_records/2]).
+
+%% Decoding
+-export([decode_cipher_text/2]).
+
+%% Encoding
+-export([encode_plain_text/4]).
+
+%% Protocol version handling
+-export([protocol_version/1, lowest_protocol_version/2,
+ highest_protocol_version/1, supported_protocol_versions/0,
+ is_acceptable_version/2, cipher/4, decipher/2]).
+
+-export([init_connection_state_seq/2, current_connection_state_epoch/2,
+ set_connection_state_by_epoch/3, connection_state_by_epoch/3]).
+
+-compile(inline).
+
+%%====================================================================
+%% Internal application API
+%%====================================================================
+
+%%--------------------------------------------------------------------
+-spec get_dtls_records(binary(), binary()) -> {[binary()], binary()} | #alert{}.
+%%
+%% Description: Given old buffer and new data from UDP/SCTP, packs up a records
+%% and returns it as a list of tls_compressed binaries also returns leftover
+%% data
+%%--------------------------------------------------------------------
+get_dtls_records(Data, <<>>) ->
+ get_dtls_records_aux(Data, []);
+get_dtls_records(Data, Buffer) ->
+ get_dtls_records_aux(list_to_binary([Buffer, Data]), []).
+
+get_dtls_records_aux(<<?BYTE(?APPLICATION_DATA),?BYTE(MajVer),?BYTE(MinVer),
+ ?UINT16(Epoch), ?UINT48(SequenceNumber),
+ ?UINT16(Length), Data:Length/binary, Rest/binary>>,
+ Acc) ->
+ get_dtls_records_aux(Rest, [#ssl_tls{type = ?APPLICATION_DATA,
+ version = {MajVer, MinVer},
+ epoch = Epoch, record_seq = SequenceNumber,
+ fragment = Data} | Acc]);
+get_dtls_records_aux(<<?BYTE(?HANDSHAKE),?BYTE(MajVer),?BYTE(MinVer),
+ ?UINT16(Epoch), ?UINT48(SequenceNumber),
+ ?UINT16(Length),
+ 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,
+ fragment = Data} | Acc]);
+get_dtls_records_aux(<<?BYTE(?ALERT),?BYTE(MajVer),?BYTE(MinVer),
+ ?UINT16(Epoch), ?UINT48(SequenceNumber),
+ ?UINT16(Length), Data:Length/binary,
+ Rest/binary>>, Acc) ->
+ get_dtls_records_aux(Rest, [#ssl_tls{type = ?ALERT,
+ version = {MajVer, MinVer},
+ epoch = Epoch, record_seq = SequenceNumber,
+ fragment = Data} | Acc]);
+get_dtls_records_aux(<<?BYTE(?CHANGE_CIPHER_SPEC),?BYTE(MajVer),?BYTE(MinVer),
+ ?UINT16(Epoch), ?UINT48(SequenceNumber),
+ ?UINT16(Length), Data:Length/binary, Rest/binary>>,
+ Acc) ->
+ get_dtls_records_aux(Rest, [#ssl_tls{type = ?CHANGE_CIPHER_SPEC,
+ version = {MajVer, MinVer},
+ epoch = Epoch, record_seq = SequenceNumber,
+ fragment = Data} | Acc]);
+
+get_dtls_records_aux(<<0:1, _CT:7, ?BYTE(_MajVer), ?BYTE(_MinVer),
+ ?UINT16(Length), _/binary>>,
+ _Acc) when Length > ?MAX_CIPHER_TEXT_LENGTH ->
+ ?ALERT_REC(?FATAL, ?RECORD_OVERFLOW);
+
+get_dtls_records_aux(<<1:1, Length0:15, _/binary>>,_Acc)
+ when Length0 > ?MAX_CIPHER_TEXT_LENGTH ->
+ ?ALERT_REC(?FATAL, ?RECORD_OVERFLOW);
+
+get_dtls_records_aux(Data, Acc) ->
+ case size(Data) =< ?MAX_CIPHER_TEXT_LENGTH + ?INITIAL_BYTES of
+ true ->
+ {lists:reverse(Acc), Data};
+ false ->
+ ?ALERT_REC(?FATAL, ?UNEXPECTED_MESSAGE)
+ end.
+
+encode_plain_text(Type, Version, Data,
+ #connection_state{
+ compression_state = CompS0,
+ epoch = Epoch,
+ sequence_number = Seq,
+ security_parameters=
+ #security_parameters{compression_algorithm = CompAlg}
+ }= CS0) ->
+ {Comp, CompS1} = ssl_record:compress(CompAlg, Data, CompS0),
+ CS1 = CS0#connection_state{compression_state = CompS1},
+ {CipherText, CS2} = cipher(Type, Version, Comp, CS1),
+ CTBin = encode_tls_cipher_text(Type, Version, Epoch, Seq, CipherText),
+ {CTBin, CS2}.
+
+decode_cipher_text(CipherText, ConnnectionStates0) ->
+ ReadState0 = ConnnectionStates0#connection_states.current_read,
+ #connection_state{compression_state = CompressionS0,
+ security_parameters = SecParams} = ReadState0,
+ CompressAlg = SecParams#security_parameters.compression_algorithm,
+ case decipher(CipherText, ReadState0) of
+ {Compressed, ReadState1} ->
+ {Plain, CompressionS1} = ssl_record:uncompress(CompressAlg,
+ Compressed, CompressionS0),
+ ConnnectionStates = ConnnectionStates0#connection_states{
+ current_read = ReadState1#connection_state{
+ compression_state = CompressionS1}},
+ {Plain, ConnnectionStates};
+ #alert{} = Alert ->
+ Alert
+ end.
+
+%%--------------------------------------------------------------------
+-spec protocol_version(tls_atom_version() | tls_version()) ->
+ tls_version() | tls_atom_version().
+%%
+%% Description: Creates a protocol version record from a version atom
+%% or vice versa.
+%%--------------------------------------------------------------------
+protocol_version('dtlsv1.2') ->
+ {254, 253};
+protocol_version(dtlsv1) ->
+ {254, 255};
+protocol_version({254, 253}) ->
+ 'dtlsv1.2';
+protocol_version({254, 255}) ->
+ dtlsv1.
+%%--------------------------------------------------------------------
+-spec lowest_protocol_version(tls_version(), tls_version()) -> tls_version().
+%%
+%% Description: Lowes protocol version of two given versions
+%%--------------------------------------------------------------------
+lowest_protocol_version(Version = {M, N}, {M, O}) when N > O ->
+ Version;
+lowest_protocol_version({M, _}, Version = {M, _}) ->
+ Version;
+lowest_protocol_version(Version = {M,_}, {N, _}) when M > N ->
+ Version;
+lowest_protocol_version(_,Version) ->
+ Version.
+%%--------------------------------------------------------------------
+-spec highest_protocol_version([tls_version()]) -> tls_version().
+%%
+%% Description: Highest protocol version present in a list
+%%--------------------------------------------------------------------
+highest_protocol_version([Ver | Vers]) ->
+ highest_protocol_version(Ver, Vers).
+
+highest_protocol_version(Version, []) ->
+ Version;
+highest_protocol_version(Version = {N, M}, [{N, O} | Rest]) when M < O ->
+ highest_protocol_version(Version, Rest);
+highest_protocol_version({M, _}, [Version = {M, _} | Rest]) ->
+ highest_protocol_version(Version, Rest);
+highest_protocol_version(Version = {M,_}, [{N,_} | Rest]) when M < N ->
+ highest_protocol_version(Version, Rest);
+highest_protocol_version(_, [Version | Rest]) ->
+ highest_protocol_version(Version, Rest).
+
+
+%%--------------------------------------------------------------------
+-spec supported_protocol_versions() -> [tls_version()].
+%%
+%% Description: Protocol versions supported
+%%--------------------------------------------------------------------
+supported_protocol_versions() ->
+ Fun = fun(Version) ->
+ protocol_version(Version)
+ end,
+ case application:get_env(ssl, dtls_protocol_version) of
+ undefined ->
+ lists:map(Fun, supported_protocol_versions([]));
+ {ok, []} ->
+ lists:map(Fun, supported_protocol_versions([]));
+ {ok, Vsns} when is_list(Vsns) ->
+ supported_protocol_versions(Vsns);
+ {ok, Vsn} ->
+ supported_protocol_versions([Vsn])
+ end.
+
+supported_protocol_versions([]) ->
+ Vsns = supported_connection_protocol_versions([]),
+ application:set_env(ssl, dtls_protocol_version, Vsns),
+ Vsns;
+
+supported_protocol_versions([_|_] = Vsns) ->
+ Vsns.
+
+supported_connection_protocol_versions([]) ->
+ ?ALL_DATAGRAM_SUPPORTED_VERSIONS.
+
+%%--------------------------------------------------------------------
+-spec is_acceptable_version(tls_version(), Supported :: [tls_version()]) -> boolean().
+%%
+%% Description: ssl version 2 is not acceptable security risks are too big.
+%%
+%%--------------------------------------------------------------------
+is_acceptable_version(Version, Versions) ->
+ lists:member(Version, Versions).
+
+
+%%--------------------------------------------------------------------
+-spec init_connection_state_seq(tls_version(), #connection_states{}) ->
+ #connection_state{}.
+%%
+%% Description: Copy the read sequence number to the write sequence number
+%% This is only valid for DTLS in the first client_hello
+%%--------------------------------------------------------------------
+init_connection_state_seq({254, _},
+ #connection_states{
+ current_read = Read = #connection_state{epoch = 0},
+ current_write = Write = #connection_state{epoch = 0}} = CS0) ->
+ CS0#connection_states{current_write =
+ Write#connection_state{
+ sequence_number = Read#connection_state.sequence_number}};
+init_connection_state_seq(_, CS) ->
+ CS.
+
+%%--------------------------------------------------------
+-spec current_connection_state_epoch(#connection_states{}, read | write) ->
+ integer().
+%%
+%% Description: Returns the epoch the connection_state record
+%% that is currently defined as the current conection state.
+%%--------------------------------------------------------------------
+current_connection_state_epoch(#connection_states{current_read = Current},
+ read) ->
+ Current#connection_state.epoch;
+current_connection_state_epoch(#connection_states{current_write = Current},
+ write) ->
+ Current#connection_state.epoch.
+
+%%--------------------------------------------------------------------
+
+-spec connection_state_by_epoch(#connection_states{}, integer(), read | write) ->
+ #connection_state{}.
+%%
+%% Description: Returns the instance of the connection_state record
+%% that is defined by the Epoch.
+%%--------------------------------------------------------------------
+connection_state_by_epoch(#connection_states{current_read = CS}, Epoch, read)
+ when CS#connection_state.epoch == Epoch ->
+ CS;
+connection_state_by_epoch(#connection_states{pending_read = CS}, Epoch, read)
+ when CS#connection_state.epoch == Epoch ->
+ CS;
+connection_state_by_epoch(#connection_states{current_write = CS}, Epoch, write)
+ when CS#connection_state.epoch == Epoch ->
+ CS;
+connection_state_by_epoch(#connection_states{pending_write = CS}, Epoch, write)
+ when CS#connection_state.epoch == Epoch ->
+ CS.
+%%--------------------------------------------------------------------
+-spec set_connection_state_by_epoch(#connection_states{},
+ #connection_state{}, read | write) -> ok.
+%%
+%% Description: Returns the instance of the connection_state record
+%% that is defined by the Epoch.
+%%--------------------------------------------------------------------
+set_connection_state_by_epoch(ConnectionStates0 =
+ #connection_states{current_read = CS},
+ NewCS = #connection_state{epoch = Epoch}, read)
+ when CS#connection_state.epoch == Epoch ->
+ ConnectionStates0#connection_states{current_read = NewCS};
+
+set_connection_state_by_epoch(ConnectionStates0 =
+ #connection_states{pending_read = CS},
+ NewCS = #connection_state{epoch = Epoch}, read)
+ when CS#connection_state.epoch == Epoch ->
+ ConnectionStates0#connection_states{pending_read = NewCS};
+
+set_connection_state_by_epoch(ConnectionStates0 =
+ #connection_states{current_write = CS},
+ NewCS = #connection_state{epoch = Epoch}, write)
+ when CS#connection_state.epoch == Epoch ->
+ ConnectionStates0#connection_states{current_write = NewCS};
+
+set_connection_state_by_epoch(ConnectionStates0 =
+ #connection_states{pending_write = CS},
+ NewCS = #connection_state{epoch = Epoch}, write)
+ when CS#connection_state.epoch == Epoch ->
+ ConnectionStates0#connection_states{pending_write = NewCS}.
+
+%%--------------------------------------------------------------------
+%%% Internal functions
+%%--------------------------------------------------------------------
+encode_tls_cipher_text(Type, {MajVer, MinVer}, Epoch, Seq, Fragment) ->
+ Length = erlang:iolist_size(Fragment),
+ [<<?BYTE(Type), ?BYTE(MajVer), ?BYTE(MinVer), ?UINT16(Epoch),
+ ?UINT48(Seq), ?UINT16(Length)>>, Fragment].
+
+cipher(Type, Version, Fragment, CS0) ->
+ Length = erlang:iolist_size(Fragment),
+ {MacHash, CS1=#connection_state{cipher_state = CipherS0,
+ security_parameters=
+ #security_parameters{bulk_cipher_algorithm =
+ BCA}
+ }} =
+ hash_and_bump_seqno(CS0, Type, Version, Length, Fragment),
+ {Ciphered, CipherS1} = ssl_cipher:cipher(BCA, CipherS0, MacHash, Fragment, Version),
+ CS2 = CS1#connection_state{cipher_state=CipherS1},
+ {Ciphered, CS2}.
+
+decipher(TLS=#ssl_tls{type=Type, version=Version={254, _},
+ epoch = Epoch, record_seq = SeqNo,
+ fragment=Fragment}, CS0) ->
+ SP = CS0#connection_state.security_parameters,
+ BCA = SP#security_parameters.bulk_cipher_algorithm,
+ HashSz = SP#security_parameters.hash_size,
+ CipherS0 = CS0#connection_state.cipher_state,
+ case ssl_cipher:decipher(BCA, HashSz, CipherS0, Fragment, Version) of
+ {T, Mac, CipherS1} ->
+ CS1 = CS0#connection_state{cipher_state = CipherS1},
+ TLength = size(T),
+ MacHash = hash_with_seqno(CS1, Type, Version, Epoch, SeqNo, TLength, T),
+ case ssl_record:is_correct_mac(Mac, MacHash) of
+ true ->
+ {TLS#ssl_tls{fragment = T}, CS1};
+ false ->
+ ?ALERT_REC(?FATAL, ?BAD_RECORD_MAC)
+ end;
+ #alert{} = Alert ->
+ Alert
+ end.
+
+hash_with_seqno(#connection_state{mac_secret = MacSecret,
+ security_parameters =
+ SecPars},
+ Type, Version = {254, _},
+ Epoch, SeqNo, Length, Fragment) ->
+ mac_hash(Version,
+ SecPars#security_parameters.mac_algorithm,
+ MacSecret, (Epoch bsl 48) + SeqNo, Type,
+ Length, Fragment).
+
+hash_and_bump_seqno(#connection_state{epoch = Epoch,
+ sequence_number = SeqNo,
+ mac_secret = MacSecret,
+ security_parameters =
+ SecPars} = CS0,
+ Type, Version = {254, _}, Length, Fragment) ->
+ Hash = mac_hash(Version,
+ SecPars#security_parameters.mac_algorithm,
+ MacSecret, (Epoch bsl 48) + SeqNo, Type,
+ Length, Fragment),
+ {Hash, CS0#connection_state{sequence_number = SeqNo+1}}.
+
+mac_hash(Version, MacAlg, MacSecret, SeqNo, Type, Length, Fragment) ->
+ dtls_v1:mac_hash(MacAlg, MacSecret, SeqNo, Type, Version,
+ Length, Fragment).
diff --git a/lib/ssl/src/dtls_v1.erl b/lib/ssl/src/dtls_v1.erl
new file mode 100644
index 0000000000..c12e12e424
--- /dev/null
+++ b/lib/ssl/src/dtls_v1.erl
@@ -0,0 +1,43 @@
+%%
+%% %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_v1).
+
+-include("ssl_cipher.hrl").
+
+-export([suites/1, mac_hash/7, ecc_curves/1, corresponding_tls_version/1]).
+
+-spec suites(Minor:: 253|255) -> [cipher_suite()].
+
+suites(Minor) ->
+ tls_v1:suites(corresponding_minor_tls_version(Minor)).
+
+mac_hash(Version, MacAlg, MacSecret, SeqNo, Type, Length, Fragment) ->
+ tls_v1:mac_hash(MacAlg, MacSecret, SeqNo, Type, Version,
+ Length, Fragment).
+
+ecc_curves({_Major, Minor}) ->
+ tls_v1:ecc_curves(corresponding_minor_tls_version(Minor)).
+
+corresponding_tls_version({254, Minor}) ->
+ {3, corresponding_minor_tls_version(Minor)}.
+
+corresponding_minor_tls_version(255) ->
+ 2;
+corresponding_minor_tls_version(253) ->
+ 3.
diff --git a/lib/ssl/src/ssl.app.src b/lib/ssl/src/ssl.app.src
index 582a60635f..44798f8c12 100644
--- a/lib/ssl/src/ssl.app.src
+++ b/lib/ssl/src/ssl.app.src
@@ -3,40 +3,44 @@
{vsn, "%VSN%"},
{modules, [
%% TLS/SSL
- tls,
tls_connection,
tls_handshake,
tls_record,
+ tls_v1,
+ ssl_v3,
+ ssl_v2,
%% DTLS
- dtls_record,
- dtls_handshake,
dtls_connection,
- dtls,
- %% Backwards compatibility
+ dtls_handshake,
+ dtls_record,
+ dtls_v1,
+ %% API
+ tls, %% Future API module
+ dtls, %% Future API module
ssl,
+ ssl_session_cache_api,
%% Both TLS/SSL and DTLS
- ssl_app,
- ssl_sup,
+ ssl_handshake,
+ ssl_record,
+ ssl_cipher,
+ ssl_srp_primes,
+ ssl_alert,
+ ssl_socket,
+ %%ssl_connection,
+ %% Erlang Distribution over SSL/TLS
inet_tls_dist,
ssl_tls_dist_proxy,
ssl_dist_sup,
- ssl_tls1,
- ssl_ssl3,
- ssl_ssl2,
+ %% SSL/TLS session handling
ssl_session,
- ssl_session_cache_api,
ssl_session_cache,
- ssl_socket,
- %%ssl_record,
ssl_manager,
- %%ssl_handshake,
- ssl_connection_sup,
- %%ssl_connection,
- ssl_cipher,
- ssl_srp_primes,
ssl_pkix_db,
ssl_certificate,
- ssl_alert
+ %% App structure
+ ssl_app,
+ ssl_sup,
+ ssl_connection_sup
]},
{registered, [ssl_sup, ssl_manager]},
{applications, [crypto, public_key, kernel, stdlib]},
diff --git a/lib/ssl/src/ssl.appup.src b/lib/ssl/src/ssl.appup.src
index 9e5bec26f1..c090b6ebfb 100644
--- a/lib/ssl/src/ssl.appup.src
+++ b/lib/ssl/src/ssl.appup.src
@@ -1,6 +1,7 @@
%% -*- erlang -*-
{"%VSN%",
[
+ {<<"5.3\\*">>, [{restart_application, ssl}]},
{<<"5.2\\*">>, [{restart_application, ssl}]},
{<<"5.1\\*">>, [{restart_application, ssl}]},
{<<"5.0\\*">>, [{restart_application, ssl}]},
@@ -8,6 +9,7 @@
{<<"3\\.*">>, [{restart_application, ssl}]}
],
[
+ {<<"5.3\\*">>, [{restart_application, ssl}]},
{<<"5.2\\*">>, [{restart_application, ssl}]},
{<<"5.1\\*">>, [{restart_application, ssl}]},
{<<"5.0\\*">>, [{restart_application, ssl}]},
diff --git a/lib/ssl/src/ssl_cipher.erl b/lib/ssl/src/ssl_cipher.erl
index 09aad8e414..6513042e98 100644
--- a/lib/ssl/src/ssl_cipher.erl
+++ b/lib/ssl/src/ssl_cipher.erl
@@ -32,7 +32,7 @@
-include("ssl_alert.hrl").
-include_lib("public_key/include/public_key.hrl").
--export([security_parameters/3, suite_definition/1,
+-export([security_parameters/2, security_parameters/3, suite_definition/1,
decipher/5, cipher/5,
suite/1, suites/1, anonymous_suites/0, psk_suites/1, srp_suites/0,
openssl_suite/1, openssl_suite_name/1, filter/2, filter_suites/1,
@@ -41,7 +41,17 @@
-compile(inline).
%%--------------------------------------------------------------------
--spec security_parameters(tls_version(), cipher_suite(), #security_parameters{}) ->
+-spec security_parameters(cipher_suite(), #security_parameters{}) ->
+ #security_parameters{}.
+%% Only security_parameters/2 should call security_parameters/3 with undefined as
+%% first argument.
+%%--------------------------------------------------------------------
+
+security_parameters(?TLS_NULL_WITH_NULL_NULL = CipherSuite, SecParams) ->
+ security_parameters(undefined, CipherSuite, SecParams).
+
+%%--------------------------------------------------------------------
+-spec security_parameters(tls_version() | undefined, cipher_suite(), #security_parameters{}) ->
#security_parameters{}.
%%
%% Description: Returns a security parameters record where the
@@ -195,9 +205,9 @@ block_decipher(Fun, #cipher_state{key=Key, iv=IV} = CipherState0,
%% Description: Returns a list of supported cipher suites.
%%--------------------------------------------------------------------
suites({3, 0}) ->
- ssl_ssl3:suites();
+ ssl_v3:suites();
suites({3, N}) ->
- ssl_tls1:suites(N).
+ tls_v1:suites(N).
%%--------------------------------------------------------------------
-spec anonymous_suites() -> [cipher_suite()].
@@ -1192,15 +1202,15 @@ hash_size(md5) ->
hash_size(sha) ->
20;
%% Uncomment when adding cipher suite that needs it
-hash_size(sha224) ->
- 28;
+%hash_size(sha224) ->
+% 28;
hash_size(sha256) ->
32;
hash_size(sha384) ->
- 48;
+ 48.
%% Uncomment when adding cipher suite that needs it
-hash_size(sha512) ->
- 64.
+%hash_size(sha512) ->
+% 64.
%% RFC 5246: 6.2.3.2. CBC Block Cipher
%%
diff --git a/lib/ssl/src/ssl_cipher.hrl b/lib/ssl/src/ssl_cipher.hrl
index c7c71ee1a7..62a5269def 100644
--- a/lib/ssl/src/ssl_cipher.hrl
+++ b/lib/ssl/src/ssl_cipher.hrl
@@ -28,7 +28,8 @@
-type cipher() :: null |rc4_128 | idea_cbc | des40_cbc | des_cbc | '3des_ede_cbc'
| aes_128_cbc | aes_256_cbc.
--type hash() :: null | sha | md5 | ssh224 | sha256 | sha384 | sha512.
+-type hash() :: null | sha | md5 | sha224 | sha256 | sha384 | sha512.
+-type key_algo() :: null | rsa | dhe_rsa | dhe_dss | ecdhe_ecdsa| ecdh_ecdsa | ecdh_rsa| srp_rsa| srp_dss | psk | dhe_psk | rsa_psk | dh_anon | ecdh_anon | srp_anon.
-type erl_cipher_suite() :: {key_algo(), cipher(), hash()}.
-type int_cipher_suite() :: {key_algo(), cipher(), hash(), hash() | default_prf}.
-type cipher_suite() :: binary().
diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl
new file mode 100644
index 0000000000..29a8996bd6
--- /dev/null
+++ b/lib/ssl/src/ssl_handshake.erl
@@ -0,0 +1,1650 @@
+%%
+%% %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%
+
+%----------------------------------------------------------------------
+%% Purpose: Help funtions for handling the SSL-handshake protocol (common
+%% to SSL/TLS and DTLS
+%%----------------------------------------------------------------------
+
+-module(ssl_handshake).
+
+-include("ssl_handshake.hrl").
+-include("ssl_record.hrl").
+-include("ssl_cipher.hrl").
+-include("ssl_alert.hrl").
+-include("ssl_internal.hrl").
+-include("ssl_srp.hrl").
+-include_lib("public_key/include/public_key.hrl").
+
+%% Handshake messages
+-export([hello_request/0, server_hello_done/0,
+ certificate/4, certificate_request/4, key_exchange/3,
+ finished/5, next_protocol/1]).
+
+%% Handle handshake messages
+-export([certify/7, client_certificate_verify/6, certificate_verify/6, verify_signature/5,
+ master_secret/5, server_key_exchange_hash/2, verify_connection/6]).
+
+%% Encode/Decode
+-export([encode_handshake/2, encode_hello_extensions/1,
+ encode_client_protocol_negotiation/2, encode_protocols_advertised_on_server/1,
+ decode_handshake/3, decode_hello_extensions/1,
+ decode_server_key/3, decode_client_key/3,
+ decode_suites/2
+ ]).
+
+%% Cipher suites handling
+-export([available_suites/2, available_suites/3, cipher_suites/2,
+ select_session/10]).
+
+%% Extensions handling
+-export([client_hello_extensions/5,
+ handle_client_hello_extensions/8, %% Returns server hello extensions
+ handle_server_hello_extensions/9
+ ]).
+
+%% MISC
+-export([select_version/3, prf/5, select_hashsign/2, select_cert_hashsign/3,
+ decrypt_premaster_secret/2]).
+
+%%====================================================================
+%% Internal application API
+%%====================================================================
+
+%% ---------- Create handshake messages ----------
+
+%%--------------------------------------------------------------------
+-spec hello_request() -> #hello_request{}.
+%%
+%% Description: Creates a hello request message sent by server to
+%% trigger renegotiation.
+%%--------------------------------------------------------------------
+hello_request() ->
+ #hello_request{}.
+
+%%--------------------------------------------------------------------
+-spec server_hello_done() -> #server_hello_done{}.
+%%
+%% Description: Creates a server hello done message.
+%%--------------------------------------------------------------------
+server_hello_done() ->
+ #server_hello_done{}.
+
+client_hello_extensions(Version, CipherSuites, SslOpts, ConnectionStates, Renegotiation) ->
+ {EcPointFormats, EllipticCurves} =
+ case advertises_ec_ciphers(lists:map(fun ssl_cipher:suite_definition/1, CipherSuites)) of
+ true ->
+ ecc_extensions(tls_v1, Version);
+ false ->
+ {undefined, undefined}
+ end,
+ SRP = srp_user(SslOpts),
+
+ #hello_extensions{
+ renegotiation_info = renegotiation_info(tls_record, client,
+ ConnectionStates, Renegotiation),
+ srp = SRP,
+ hash_signs = advertised_hash_signs(Version),
+ ec_point_formats = EcPointFormats,
+ elliptic_curves = EllipticCurves,
+ next_protocol_negotiation =
+ encode_client_protocol_negotiation(SslOpts#ssl_options.next_protocol_selector,
+ Renegotiation)}.
+
+%%--------------------------------------------------------------------
+-spec certificate(der_cert(), db_handle(), certdb_ref(), client | server) -> #certificate{} | #alert{}.
+%%
+%% Description: Creates a certificate message.
+%%--------------------------------------------------------------------
+certificate(OwnCert, CertDbHandle, CertDbRef, client) ->
+ Chain =
+ case ssl_certificate:certificate_chain(OwnCert, CertDbHandle, CertDbRef) of
+ {ok, CertChain} ->
+ CertChain;
+ {error, _} ->
+ %% If no suitable certificate is available, the client
+ %% SHOULD send a certificate message containing no
+ %% certificates. (chapter 7.4.6. RFC 4346)
+ []
+ end,
+ #certificate{asn1_certificates = Chain};
+
+certificate(OwnCert, CertDbHandle, CertDbRef, server) ->
+ case ssl_certificate:certificate_chain(OwnCert, CertDbHandle, CertDbRef) of
+ {ok, Chain} ->
+ #certificate{asn1_certificates = Chain};
+ {error, _} ->
+ ?ALERT_REC(?FATAL, ?INTERNAL_ERROR)
+ end.
+
+%%--------------------------------------------------------------------
+-spec next_protocol(binary()) -> #next_protocol{}.
+%%
+%% Description: Creates a next protocol message
+%%-------------------------------------------------------------------
+next_protocol(SelectedProtocol) ->
+ #next_protocol{selected_protocol = SelectedProtocol}.
+
+%%--------------------------------------------------------------------
+-spec client_certificate_verify(undefined | der_cert(), binary(),
+ tls_version(), term(), private_key(),
+ tls_handshake_history()) ->
+ #certificate_verify{} | ignore | #alert{}.
+%%
+%% Description: Creates a certificate_verify message, called by the client.
+%%--------------------------------------------------------------------
+client_certificate_verify(undefined, _, _, _, _, _) ->
+ ignore;
+client_certificate_verify(_, _, _, _, undefined, _) ->
+ ignore;
+client_certificate_verify(OwnCert, MasterSecret, Version,
+ {HashAlgo, SignAlgo},
+ PrivateKey, {Handshake, _}) ->
+ case public_key:pkix_is_fixed_dh_cert(OwnCert) of
+ true ->
+ ?ALERT_REC(?FATAL, ?UNSUPPORTED_CERTIFICATE);
+ false ->
+ Hashes =
+ calc_certificate_verify(Version, HashAlgo, MasterSecret, Handshake),
+ Signed = digitally_signed(Version, Hashes, HashAlgo, PrivateKey),
+ #certificate_verify{signature = Signed, hashsign_algorithm = {HashAlgo, SignAlgo}}
+ end.
+
+%%--------------------------------------------------------------------
+-spec certificate_request(erl_cipher_suite(), db_handle(), certdb_ref(), tls_version()) ->
+ #certificate_request{}.
+%%
+%% Description: Creates a certificate_request message, called by the server.
+%%--------------------------------------------------------------------
+certificate_request(CipherSuite, CertDbHandle, CertDbRef, Version) ->
+ Types = certificate_types(CipherSuite),
+ HashSigns = advertised_hash_signs(Version),
+ Authorities = certificate_authorities(CertDbHandle, CertDbRef),
+ #certificate_request{
+ certificate_types = Types,
+ hashsign_algorithms = HashSigns,
+ certificate_authorities = Authorities
+ }.
+%%--------------------------------------------------------------------
+-spec key_exchange(client | server, tls_version(),
+ {premaster_secret, binary(), public_key_info()} |
+ {dh, binary()} |
+ {dh, {binary(), binary()}, #'DHParameter'{}, {HashAlgo::atom(), SignAlgo::atom()},
+ binary(), binary(), private_key()} |
+ {ecdh, #'ECPrivateKey'{}} |
+ {psk, binary()} |
+ {dhe_psk, binary(), binary()} |
+ {srp, {binary(), binary()}, #srp_user{}, {HashAlgo::atom(), SignAlgo::atom()},
+ binary(), binary(), private_key()}) ->
+ #client_key_exchange{} | #server_key_exchange{}.
+
+%%
+%% Description: Creates a keyexchange message.
+%%--------------------------------------------------------------------
+key_exchange(client, _Version, {premaster_secret, Secret, {_, PublicKey, _}}) ->
+ EncPremasterSecret =
+ encrypted_premaster_secret(Secret, PublicKey),
+ #client_key_exchange{exchange_keys = EncPremasterSecret};
+
+key_exchange(client, _Version, {dh, PublicKey}) ->
+ #client_key_exchange{
+ exchange_keys = #client_diffie_hellman_public{
+ dh_public = PublicKey}
+ };
+
+key_exchange(client, _Version, {ecdh, #'ECPrivateKey'{publicKey = {0, ECPublicKey}}}) ->
+ #client_key_exchange{
+ exchange_keys = #client_ec_diffie_hellman_public{
+ dh_public = ECPublicKey}
+ };
+
+key_exchange(client, _Version, {psk, Identity}) ->
+ #client_key_exchange{
+ exchange_keys = #client_psk_identity{
+ identity = Identity}
+ };
+
+key_exchange(client, _Version, {dhe_psk, Identity, PublicKey}) ->
+ #client_key_exchange{
+ exchange_keys = #client_dhe_psk_identity{
+ identity = Identity,
+ dh_public = PublicKey}
+ };
+
+key_exchange(client, _Version, {psk_premaster_secret, PskIdentity, Secret, {_, PublicKey, _}}) ->
+ EncPremasterSecret =
+ encrypted_premaster_secret(Secret, PublicKey),
+ #client_key_exchange{
+ exchange_keys = #client_rsa_psk_identity{
+ identity = PskIdentity,
+ exchange_keys = EncPremasterSecret}};
+
+key_exchange(client, _Version, {srp, PublicKey}) ->
+ #client_key_exchange{
+ exchange_keys = #client_srp_public{
+ srp_a = PublicKey}
+ };
+
+key_exchange(server, Version, {dh, {PublicKey, _},
+ #'DHParameter'{prime = P, base = G},
+ HashSign, ClientRandom, ServerRandom, PrivateKey}) ->
+ ServerDHParams = #server_dh_params{dh_p = int_to_bin(P),
+ dh_g = int_to_bin(G), dh_y = PublicKey},
+ enc_server_key_exchange(Version, ServerDHParams, HashSign,
+ ClientRandom, ServerRandom, PrivateKey);
+
+key_exchange(server, Version, {ecdh, #'ECPrivateKey'{publicKey = {0, ECPublicKey},
+ parameters = ECCurve}, HashSign,
+ ClientRandom, ServerRandom, PrivateKey}) ->
+ ServerECParams = #server_ecdh_params{curve = ECCurve, public = ECPublicKey},
+ enc_server_key_exchange(Version, ServerECParams, HashSign,
+ ClientRandom, ServerRandom, PrivateKey);
+
+key_exchange(server, Version, {psk, PskIdentityHint,
+ HashSign, ClientRandom, ServerRandom, PrivateKey}) ->
+ ServerPSKParams = #server_psk_params{hint = PskIdentityHint},
+ enc_server_key_exchange(Version, ServerPSKParams, HashSign,
+ ClientRandom, ServerRandom, PrivateKey);
+
+key_exchange(server, Version, {dhe_psk, PskIdentityHint, {PublicKey, _},
+ #'DHParameter'{prime = P, base = G},
+ HashSign, ClientRandom, ServerRandom, PrivateKey}) ->
+ ServerEDHPSKParams = #server_dhe_psk_params{
+ hint = PskIdentityHint,
+ dh_params = #server_dh_params{dh_p = int_to_bin(P),
+ dh_g = int_to_bin(G), dh_y = PublicKey}
+ },
+ enc_server_key_exchange(Version, ServerEDHPSKParams,
+ HashSign, ClientRandom, ServerRandom, PrivateKey);
+
+key_exchange(server, Version, {srp, {PublicKey, _},
+ #srp_user{generator = Generator, prime = Prime,
+ salt = Salt},
+ HashSign, ClientRandom, ServerRandom, PrivateKey}) ->
+ ServerSRPParams = #server_srp_params{srp_n = Prime, srp_g = Generator,
+ srp_s = Salt, srp_b = PublicKey},
+ enc_server_key_exchange(Version, ServerSRPParams, HashSign,
+ ClientRandom, ServerRandom, PrivateKey).
+
+%%--------------------------------------------------------------------
+-spec finished(tls_version(), client | server, integer(), binary(), tls_handshake_history()) ->
+ #finished{}.
+%%
+%% Description: Creates a handshake finished message
+%%-------------------------------------------------------------------
+finished(Version, Role, PrfAlgo, MasterSecret, {Handshake, _}) -> % use the current handshake
+ #finished{verify_data =
+ calc_finished(Version, Role, PrfAlgo, MasterSecret, Handshake)}.
+
+%% ---------- Handle handshake messages ----------
+
+%%--------------------------------------------------------------------
+-spec certificate_verify(binary(), public_key_info(), tls_version(), term(),
+ binary(), tls_handshake_history()) -> valid | #alert{}.
+%%
+%% Description: Checks that the certificate_verify message is valid.
+%%--------------------------------------------------------------------
+certificate_verify(Signature, PublicKeyInfo, Version,
+ HashSign = {HashAlgo, _}, MasterSecret, {_, Handshake}) ->
+ Hash = calc_certificate_verify(Version, HashAlgo, MasterSecret, Handshake),
+ case verify_signature(Version, Hash, HashSign, Signature, PublicKeyInfo) of
+ true ->
+ valid;
+ _ ->
+ ?ALERT_REC(?FATAL, ?BAD_CERTIFICATE)
+ end.
+%%--------------------------------------------------------------------
+-spec verify_signature(tls_version(), binary(), {term(), term()}, binary(),
+ public_key_info()) -> true | false.
+%%
+%% Description: Checks that a public_key signature is valid.
+%%--------------------------------------------------------------------
+verify_signature(_Version, _Hash, {_HashAlgo, anon}, _Signature, _) ->
+ true;
+verify_signature({3, Minor}, Hash, {HashAlgo, rsa}, Signature, {?rsaEncryption, PubKey, _PubKeyParams})
+ when Minor >= 3 ->
+ public_key:verify({digest, Hash}, HashAlgo, Signature, PubKey);
+verify_signature(_Version, Hash, _HashAlgo, Signature, {?rsaEncryption, PubKey, _PubKeyParams}) ->
+ case public_key:decrypt_public(Signature, PubKey,
+ [{rsa_pad, rsa_pkcs1_padding}]) of
+ Hash -> true;
+ _ -> false
+ end;
+verify_signature(_Version, Hash, {HashAlgo, dsa}, Signature, {?'id-dsa', PublicKey, PublicKeyParams}) ->
+ public_key:verify({digest, Hash}, HashAlgo, Signature, {PublicKey, PublicKeyParams});
+verify_signature(_Version, Hash, {HashAlgo, ecdsa}, Signature,
+ {?'id-ecPublicKey', PublicKey, PublicKeyParams}) ->
+ public_key:verify({digest, Hash}, HashAlgo, Signature, {PublicKey, PublicKeyParams}).
+
+%%--------------------------------------------------------------------
+-spec certify(#certificate{}, db_handle(), certdb_ref(), integer() | nolimit,
+ verify_peer | verify_none, {fun(), term},
+ client | server) -> {der_cert(), public_key_info()} | #alert{}.
+%%
+%% Description: Handles a certificate handshake message
+%%--------------------------------------------------------------------
+certify(#certificate{asn1_certificates = ASN1Certs}, CertDbHandle, CertDbRef,
+ MaxPathLen, _Verify, VerifyFunAndState, Role) ->
+ [PeerCert | _] = ASN1Certs,
+
+ ValidationFunAndState =
+ case VerifyFunAndState of
+ undefined ->
+ {fun(OtpCert, ExtensionOrVerifyResult, SslState) ->
+ ssl_certificate:validate_extension(OtpCert,
+ ExtensionOrVerifyResult, SslState)
+ end, Role};
+ {Fun, UserState0} ->
+ {fun(OtpCert, {extension, _} = Extension, {SslState, UserState}) ->
+ case ssl_certificate:validate_extension(OtpCert,
+ Extension,
+ SslState) of
+ {valid, NewSslState} ->
+ {valid, {NewSslState, UserState}};
+ {fail, Reason} ->
+ apply_user_fun(Fun, OtpCert, Reason, UserState,
+ SslState);
+ {unknown, _} ->
+ apply_user_fun(Fun, OtpCert,
+ Extension, UserState, SslState)
+ end;
+ (OtpCert, VerifyResult, {SslState, UserState}) ->
+ apply_user_fun(Fun, OtpCert, VerifyResult, UserState,
+ SslState)
+ end, {Role, UserState0}}
+ end,
+
+ try
+ {TrustedErlCert, CertPath} =
+ ssl_certificate:trusted_cert_and_path(ASN1Certs, CertDbHandle, CertDbRef),
+ case public_key:pkix_path_validation(TrustedErlCert,
+ CertPath,
+ [{max_path_length,
+ MaxPathLen},
+ {verify_fun, ValidationFunAndState}]) of
+ {ok, {PublicKeyInfo,_}} ->
+ {PeerCert, PublicKeyInfo};
+ {error, Reason} ->
+ path_validation_alert(Reason)
+ end
+ catch
+ error:_ ->
+ %% ASN-1 decode of certificate somehow failed
+ ?ALERT_REC(?FATAL, ?CERTIFICATE_UNKNOWN)
+ end.
+
+%%--------------------------------------------------------------------
+-spec verify_connection(tls_version(), #finished{}, client | server, integer(), binary(),
+ tls_handshake_history()) -> verified | #alert{}.
+%%
+%% Description: Checks the ssl handshake finished message to verify
+%% the connection.
+%%-------------------------------------------------------------------
+verify_connection(Version, #finished{verify_data = Data},
+ Role, PrfAlgo, MasterSecret, {_, Handshake}) ->
+ %% use the previous hashes
+ case calc_finished(Version, Role, PrfAlgo, MasterSecret, Handshake) of
+ Data ->
+ verified;
+ _ ->
+ ?ALERT_REC(?FATAL, ?DECRYPT_ERROR)
+ end.
+%%--------------------------------------------------------------------
+-spec decrypt_premaster_secret(binary(), #'RSAPrivateKey'{}) -> binary().
+
+%%
+%% Description: Public key decryption using the private key.
+%%--------------------------------------------------------------------
+decrypt_premaster_secret(Secret, RSAPrivateKey) ->
+ try public_key:decrypt_private(Secret, RSAPrivateKey,
+ [{rsa_pad, rsa_pkcs1_padding}])
+ catch
+ _:_ ->
+ throw(?ALERT_REC(?FATAL, ?DECRYPT_ERROR))
+ end.
+%%--------------------------------------------------------------------
+-spec server_key_exchange_hash(md5sha | md5 | sha | sha224 |sha256 | sha384 | sha512, binary()) -> binary().
+%%
+%% Description: Calculate server key exchange hash
+%%--------------------------------------------------------------------
+server_key_exchange_hash(md5sha, Value) ->
+ MD5 = crypto:hash(md5, Value),
+ SHA = crypto:hash(sha, Value),
+ <<MD5/binary, SHA/binary>>;
+
+server_key_exchange_hash(Hash, Value) ->
+ crypto:hash(Hash, Value).
+%%--------------------------------------------------------------------
+-spec prf(tls_version(), binary(), binary(), [binary()], non_neg_integer()) ->
+ {ok, binary()} | {error, undefined}.
+%%
+%% Description: use the TLS PRF to generate key material
+%%--------------------------------------------------------------------
+prf({3,0}, _, _, _, _) ->
+ {error, undefined};
+prf({3,1}, Secret, Label, Seed, WantedLength) ->
+ {ok, tls_v1:prf(?MD5SHA, Secret, Label, Seed, WantedLength)};
+prf({3,_N}, Secret, Label, Seed, WantedLength) ->
+ {ok, tls_v1:prf(?SHA256, Secret, Label, Seed, WantedLength)}.
+%%--------------------------------------------------------------------
+-spec select_hashsign(#hash_sign_algos{}| undefined, undefined | binary()) ->
+ [{atom(), atom()}] | undefined.
+
+%%
+%% Description:
+%%--------------------------------------------------------------------
+select_hashsign(_, undefined) ->
+ {null, anon};
+select_hashsign(undefined, Cert) ->
+ #'OTPCertificate'{tbsCertificate = TBSCert} = public_key:pkix_decode_cert(Cert, otp),
+ #'OTPSubjectPublicKeyInfo'{algorithm = {_,Algo, _}} = TBSCert#'OTPTBSCertificate'.subjectPublicKeyInfo,
+ select_cert_hashsign(undefined, Algo, {undefined, undefined});
+select_hashsign(#hash_sign_algos{hash_sign_algos = HashSigns}, Cert) ->
+ #'OTPCertificate'{tbsCertificate = TBSCert} =public_key:pkix_decode_cert(Cert, otp),
+ #'OTPSubjectPublicKeyInfo'{algorithm = {_,Algo, _}} = TBSCert#'OTPTBSCertificate'.subjectPublicKeyInfo,
+ DefaultHashSign = {_, Sign} = select_cert_hashsign(undefined, Algo, {undefined, undefined}),
+ case lists:filter(fun({sha, dsa}) ->
+ true;
+ ({_, dsa}) ->
+ false;
+ ({Hash, S}) when S == Sign ->
+ ssl_cipher:is_acceptable_hash(Hash, proplists:get_value(hashs, crypto:supports()));
+ (_) ->
+ false
+ end, HashSigns) of
+ [] ->
+ DefaultHashSign;
+ [HashSign| _] ->
+ HashSign
+ end.
+%%--------------------------------------------------------------------
+-spec select_cert_hashsign(#hash_sign_algos{}| undefined, oid(), tls_version() | {undefined, undefined}) ->
+ {atom(), atom()}.
+
+%%
+%% Description: For TLS 1.2 selected cert_hash_sign will be recived
+%% in the handshake message, for previous versions use appropriate defaults.
+%% This function is also used by select_hashsign to extract
+%% the alogrithm of the server cert key.
+%%--------------------------------------------------------------------
+select_cert_hashsign(HashSign, _, {Major, Minor}) when HashSign =/= undefined andalso Major >= 3 andalso Minor >= 3 ->
+ HashSign;
+select_cert_hashsign(undefined,?'id-ecPublicKey', _) ->
+ {sha, ecdsa};
+select_cert_hashsign(undefined, ?rsaEncryption, _) ->
+ {md5sha, rsa};
+select_cert_hashsign(undefined, ?'id-dsa', _) ->
+ {sha, dsa}.
+
+%%--------------------------------------------------------------------
+-spec master_secret(atom(), tls_version(), #session{} | binary(), #connection_states{},
+ client | server) -> {binary(), #connection_states{}} | #alert{}.
+%%
+%% Description: Sets or calculates the master secret and calculate keys,
+%% updating the pending connection states. The Mastersecret and the update
+%% connection states are returned or an alert if the calculation fails.
+%%-------------------------------------------------------------------
+master_secret(RecordCB, Version, #session{master_secret = Mastersecret},
+ ConnectionStates, Role) ->
+ ConnectionState =
+ ssl_record:pending_connection_state(ConnectionStates, read),
+ SecParams = ConnectionState#connection_state.security_parameters,
+ try master_secret(RecordCB, Version, Mastersecret, SecParams,
+ ConnectionStates, Role)
+ catch
+ exit:Reason ->
+ Report = io_lib:format("Key calculation failed due to ~p",
+ [Reason]),
+ error_logger:error_report(Report),
+ ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE)
+ end;
+
+master_secret(RecordCB, Version, PremasterSecret, ConnectionStates, Role) ->
+ ConnectionState =
+ ssl_record:pending_connection_state(ConnectionStates, read),
+ SecParams = ConnectionState#connection_state.security_parameters,
+ #security_parameters{prf_algorithm = PrfAlgo,
+ client_random = ClientRandom,
+ server_random = ServerRandom} = SecParams,
+ try master_secret(RecordCB, Version,
+ calc_master_secret(Version,PrfAlgo,PremasterSecret,
+ ClientRandom, ServerRandom),
+ SecParams, ConnectionStates, Role)
+ catch
+ exit:Reason ->
+ Report = io_lib:format("Master secret calculation failed"
+ " due to ~p", [Reason]),
+ error_logger:error_report(Report),
+ ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE)
+ end.
+
+%%-------------Encode/Decode --------------------------------
+encode_handshake(#next_protocol{selected_protocol = SelectedProtocol}, _Version) ->
+ PaddingLength = 32 - ((byte_size(SelectedProtocol) + 2) rem 32),
+ {?NEXT_PROTOCOL, <<?BYTE((byte_size(SelectedProtocol))), SelectedProtocol/binary,
+ ?BYTE(PaddingLength), 0:(PaddingLength * 8)>>};
+
+encode_handshake(#server_hello{server_version = {Major, Minor},
+ random = Random,
+ session_id = Session_ID,
+ cipher_suite = CipherSuite,
+ compression_method = Comp_method,
+ extensions = #hello_extensions{} = Extensions}, _Version) ->
+ SID_length = byte_size(Session_ID),
+ ExtensionsBin = encode_hello_extensions(Extensions),
+ {?SERVER_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary,
+ ?BYTE(SID_length), Session_ID/binary,
+ CipherSuite/binary, ?BYTE(Comp_method), ExtensionsBin/binary>>};
+encode_handshake(#certificate{asn1_certificates = ASN1CertList}, _Version) ->
+ ASN1Certs = certs_from_list(ASN1CertList),
+ ACLen = erlang:iolist_size(ASN1Certs),
+ {?CERTIFICATE, <<?UINT24(ACLen), ASN1Certs:ACLen/binary>>};
+encode_handshake(#server_key_exchange{exchange_keys = Keys}, _Version) ->
+ {?SERVER_KEY_EXCHANGE, Keys};
+encode_handshake(#server_key_params{params_bin = Keys, hashsign = HashSign,
+ signature = Signature}, Version) ->
+ EncSign = enc_sign(HashSign, Signature, Version),
+ {?SERVER_KEY_EXCHANGE, <<Keys/binary, EncSign/binary>>};
+encode_handshake(#certificate_request{certificate_types = CertTypes,
+ hashsign_algorithms = #hash_sign_algos{hash_sign_algos = HashSignAlgos},
+ certificate_authorities = CertAuths},
+ {Major, Minor})
+ when Major == 3, Minor >= 3 ->
+ HashSigns= << <<(ssl_cipher:hash_algorithm(Hash)):8, (ssl_cipher:sign_algorithm(Sign)):8>> ||
+ {Hash, Sign} <- HashSignAlgos >>,
+ CertTypesLen = byte_size(CertTypes),
+ HashSignsLen = byte_size(HashSigns),
+ CertAuthsLen = byte_size(CertAuths),
+ {?CERTIFICATE_REQUEST,
+ <<?BYTE(CertTypesLen), CertTypes/binary,
+ ?UINT16(HashSignsLen), HashSigns/binary,
+ ?UINT16(CertAuthsLen), CertAuths/binary>>
+ };
+encode_handshake(#certificate_request{certificate_types = CertTypes,
+ certificate_authorities = CertAuths},
+ _Version) ->
+ CertTypesLen = byte_size(CertTypes),
+ CertAuthsLen = byte_size(CertAuths),
+ {?CERTIFICATE_REQUEST,
+ <<?BYTE(CertTypesLen), CertTypes/binary,
+ ?UINT16(CertAuthsLen), CertAuths/binary>>
+ };
+encode_handshake(#server_hello_done{}, _Version) ->
+ {?SERVER_HELLO_DONE, <<>>};
+encode_handshake(#client_key_exchange{exchange_keys = ExchangeKeys}, Version) ->
+ {?CLIENT_KEY_EXCHANGE, encode_client_key(ExchangeKeys, Version)};
+encode_handshake(#certificate_verify{signature = BinSig, hashsign_algorithm = HashSign}, Version) ->
+ EncSig = enc_sign(HashSign, BinSig, Version),
+ {?CERTIFICATE_VERIFY, EncSig};
+encode_handshake(#finished{verify_data = VerifyData}, _Version) ->
+ {?FINISHED, VerifyData}.
+
+encode_hello_extensions(#hello_extensions{} = Extensions) ->
+ encode_hello_extensions(hello_extensions_list(Extensions), <<>>).
+encode_hello_extensions([], <<>>) ->
+ <<>>;
+encode_hello_extensions([], Acc) ->
+ Size = byte_size(Acc),
+ <<?UINT16(Size), Acc/binary>>;
+
+encode_hello_extensions([#next_protocol_negotiation{extension_data = ExtensionData} | Rest], Acc) ->
+ Len = byte_size(ExtensionData),
+ encode_hello_extensions(Rest, <<?UINT16(?NEXTPROTONEG_EXT), ?UINT16(Len),
+ ExtensionData/binary, Acc/binary>>);
+encode_hello_extensions([#renegotiation_info{renegotiated_connection = undefined} | Rest], Acc) ->
+ encode_hello_extensions(Rest, Acc);
+encode_hello_extensions([#renegotiation_info{renegotiated_connection = ?byte(0) = Info} | Rest], Acc) ->
+ Len = byte_size(Info),
+ encode_hello_extensions(Rest, <<?UINT16(?RENEGOTIATION_EXT), ?UINT16(Len), Info/binary, Acc/binary>>);
+
+encode_hello_extensions([#renegotiation_info{renegotiated_connection = Info} | Rest], Acc) ->
+ InfoLen = byte_size(Info),
+ Len = InfoLen +1,
+ encode_hello_extensions(Rest, <<?UINT16(?RENEGOTIATION_EXT), ?UINT16(Len), ?BYTE(InfoLen),
+ Info/binary, Acc/binary>>);
+encode_hello_extensions([#elliptic_curves{elliptic_curve_list = EllipticCurves} | Rest], Acc) ->
+
+ EllipticCurveList = << <<(tls_v1:oid_to_enum(X)):16>> || X <- EllipticCurves>>,
+ ListLen = byte_size(EllipticCurveList),
+ Len = ListLen + 2,
+ encode_hello_extensions(Rest, <<?UINT16(?ELLIPTIC_CURVES_EXT),
+ ?UINT16(Len), ?UINT16(ListLen), EllipticCurveList/binary, Acc/binary>>);
+encode_hello_extensions([#ec_point_formats{ec_point_format_list = ECPointFormats} | Rest], Acc) ->
+ ECPointFormatList = list_to_binary(ECPointFormats),
+ ListLen = byte_size(ECPointFormatList),
+ Len = ListLen + 1,
+ encode_hello_extensions(Rest, <<?UINT16(?EC_POINT_FORMATS_EXT),
+ ?UINT16(Len), ?BYTE(ListLen), ECPointFormatList/binary, Acc/binary>>);
+encode_hello_extensions([#srp{username = UserName} | Rest], Acc) ->
+ SRPLen = byte_size(UserName),
+ Len = SRPLen + 2,
+ encode_hello_extensions(Rest, <<?UINT16(?SRP_EXT), ?UINT16(Len), ?BYTE(SRPLen),
+ UserName/binary, Acc/binary>>);
+encode_hello_extensions([#hash_sign_algos{hash_sign_algos = HashSignAlgos} | Rest], Acc) ->
+ SignAlgoList = << <<(ssl_cipher:hash_algorithm(Hash)):8, (ssl_cipher:sign_algorithm(Sign)):8>> ||
+ {Hash, Sign} <- HashSignAlgos >>,
+ ListLen = byte_size(SignAlgoList),
+ Len = ListLen + 2,
+ encode_hello_extensions(Rest, <<?UINT16(?SIGNATURE_ALGORITHMS_EXT),
+ ?UINT16(Len), ?UINT16(ListLen), SignAlgoList/binary, Acc/binary>>).
+
+enc_server_key_exchange(Version, Params, {HashAlgo, SignAlgo},
+ ClientRandom, ServerRandom, PrivateKey) ->
+ EncParams = encode_server_key(Params),
+ case HashAlgo of
+ null ->
+ #server_key_params{params = Params,
+ params_bin = EncParams,
+ hashsign = {null, anon},
+ signature = <<>>};
+ _ ->
+ Hash =
+ server_key_exchange_hash(HashAlgo, <<ClientRandom/binary,
+ ServerRandom/binary,
+ EncParams/binary>>),
+ Signature = digitally_signed(Version, Hash, HashAlgo, PrivateKey),
+ #server_key_params{params = Params,
+ params_bin = EncParams,
+ hashsign = {HashAlgo, SignAlgo},
+ signature = Signature}
+ end.
+
+%%--------------------------------------------------------------------
+-spec decode_client_key(binary(), key_algo(), tls_version()) ->
+ #encrypted_premaster_secret{}
+ | #client_diffie_hellman_public{}
+ | #client_ec_diffie_hellman_public{}
+ | #client_psk_identity{}
+ | #client_dhe_psk_identity{}
+ | #client_rsa_psk_identity{}
+ | #client_srp_public{}.
+%%
+%% Description: Decode client_key data and return appropriate type
+%%--------------------------------------------------------------------
+decode_client_key(ClientKey, Type, Version) ->
+ dec_client_key(ClientKey, key_exchange_alg(Type), Version).
+
+%%--------------------------------------------------------------------
+-spec decode_server_key(binary(), key_algo(), tls_version()) ->
+ #server_key_params{}.
+%%
+%% Description: Decode server_key data and return appropriate type
+%%--------------------------------------------------------------------
+decode_server_key(ServerKey, Type, Version) ->
+ dec_server_key(ServerKey, key_exchange_alg(Type), Version).
+
+encode_client_protocol_negotiation(undefined, _) ->
+ undefined;
+encode_client_protocol_negotiation(_, false) ->
+ #next_protocol_negotiation{extension_data = <<>>};
+encode_client_protocol_negotiation(_, _) ->
+ undefined.
+
+encode_protocols_advertised_on_server(undefined) ->
+ undefined;
+
+encode_protocols_advertised_on_server(Protocols) ->
+ #next_protocol_negotiation{extension_data = lists:foldl(fun encode_protocol/2, <<>>, Protocols)}.
+
+decode_handshake(_, ?HELLO_REQUEST, <<>>) ->
+ #hello_request{};
+decode_handshake(_, ?NEXT_PROTOCOL, <<?BYTE(SelectedProtocolLength),
+ SelectedProtocol:SelectedProtocolLength/binary,
+ ?BYTE(PaddingLength), _Padding:PaddingLength/binary>>) ->
+ #next_protocol{selected_protocol = SelectedProtocol};
+
+decode_handshake(_Version, ?SERVER_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary,
+ ?BYTE(SID_length), Session_ID:SID_length/binary,
+ Cipher_suite:2/binary, ?BYTE(Comp_method)>>) ->
+ #server_hello{
+ server_version = {Major,Minor},
+ random = Random,
+ session_id = Session_ID,
+ cipher_suite = Cipher_suite,
+ compression_method = Comp_method,
+ extensions = #hello_extensions{}};
+
+decode_handshake(_Version, ?SERVER_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary,
+ ?BYTE(SID_length), Session_ID:SID_length/binary,
+ Cipher_suite:2/binary, ?BYTE(Comp_method),
+ ?UINT16(ExtLen), Extensions:ExtLen/binary>>) ->
+
+ HelloExtensions = decode_hello_extensions(Extensions),
+
+ #server_hello{
+ server_version = {Major,Minor},
+ random = Random,
+ session_id = Session_ID,
+ cipher_suite = Cipher_suite,
+ compression_method = Comp_method,
+ extensions = HelloExtensions};
+
+decode_handshake(_Version, ?CERTIFICATE, <<?UINT24(ACLen), ASN1Certs:ACLen/binary>>) ->
+ #certificate{asn1_certificates = certs_to_list(ASN1Certs)};
+decode_handshake(_Version, ?SERVER_KEY_EXCHANGE, Keys) ->
+ #server_key_exchange{exchange_keys = Keys};
+decode_handshake({Major, Minor}, ?CERTIFICATE_REQUEST,
+ <<?BYTE(CertTypesLen), CertTypes:CertTypesLen/binary,
+ ?UINT16(HashSignsLen), HashSigns:HashSignsLen/binary,
+ ?UINT16(CertAuthsLen), CertAuths:CertAuthsLen/binary>>)
+ when Major >= 3, Minor >= 3 ->
+ HashSignAlgos = [{ssl_cipher:hash_algorithm(Hash), ssl_cipher:sign_algorithm(Sign)} ||
+ <<?BYTE(Hash), ?BYTE(Sign)>> <= HashSigns],
+ #certificate_request{certificate_types = CertTypes,
+ hashsign_algorithms = #hash_sign_algos{hash_sign_algos = HashSignAlgos},
+ certificate_authorities = CertAuths};
+decode_handshake(_Version, ?CERTIFICATE_REQUEST,
+ <<?BYTE(CertTypesLen), CertTypes:CertTypesLen/binary,
+ ?UINT16(CertAuthsLen), CertAuths:CertAuthsLen/binary>>) ->
+ #certificate_request{certificate_types = CertTypes,
+ certificate_authorities = CertAuths};
+decode_handshake(_Version, ?SERVER_HELLO_DONE, <<>>) ->
+ #server_hello_done{};
+decode_handshake({Major, Minor}, ?CERTIFICATE_VERIFY,<<HashSign:2/binary, ?UINT16(SignLen),
+ Signature:SignLen/binary>>)
+ when Major == 3, Minor >= 3 ->
+ #certificate_verify{hashsign_algorithm = dec_hashsign(HashSign), signature = Signature};
+decode_handshake(_Version, ?CERTIFICATE_VERIFY,<<?UINT16(SignLen), Signature:SignLen/binary>>)->
+ #certificate_verify{signature = Signature};
+decode_handshake(_Version, ?CLIENT_KEY_EXCHANGE, PKEPMS) ->
+ #client_key_exchange{exchange_keys = PKEPMS};
+decode_handshake(_Version, ?FINISHED, VerifyData) ->
+ #finished{verify_data = VerifyData};
+decode_handshake(_, _, _) ->
+ throw(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE)).
+
+%%--------------------------------------------------------------------
+-spec decode_hello_extensions({client, binary()} | binary()) -> #hello_extensions{}.
+%%
+%% Description: Decodes TLS hello extensions
+%%--------------------------------------------------------------------
+decode_hello_extensions({client, <<>>}) ->
+ #hello_extensions{};
+decode_hello_extensions({client, <<?UINT16(ExtLen), Extensions:ExtLen/binary>>}) ->
+ decode_hello_extensions(Extensions);
+decode_hello_extensions(Extensions) ->
+ dec_hello_extensions(Extensions, #hello_extensions{}).
+
+dec_server_key(<<?UINT16(PLen), P:PLen/binary,
+ ?UINT16(GLen), G:GLen/binary,
+ ?UINT16(YLen), Y:YLen/binary, _/binary>> = KeyStruct,
+ ?KEY_EXCHANGE_DIFFIE_HELLMAN, Version) ->
+ Params = #server_dh_params{dh_p = P, dh_g = G, dh_y = Y},
+ {BinMsg, HashSign, Signature} = dec_server_key_params(PLen + GLen + YLen + 6, KeyStruct, Version),
+ #server_key_params{params = Params,
+ params_bin = BinMsg,
+ hashsign = HashSign,
+ signature = Signature};
+%% ECParameters with named_curve
+%% TODO: explicit curve
+dec_server_key(<<?BYTE(?NAMED_CURVE), ?UINT16(CurveID),
+ ?BYTE(PointLen), ECPoint:PointLen/binary,
+ _/binary>> = KeyStruct,
+ ?KEY_EXCHANGE_EC_DIFFIE_HELLMAN, Version) ->
+ Params = #server_ecdh_params{curve = {namedCurve, tls_v1:enum_to_oid(CurveID)},
+ public = ECPoint},
+ {BinMsg, HashSign, Signature} = dec_server_key_params(PointLen + 4, KeyStruct, Version),
+ #server_key_params{params = Params,
+ params_bin = BinMsg,
+ hashsign = HashSign,
+ signature = Signature};
+dec_server_key(<<?UINT16(Len), PskIdentityHint:Len/binary, _/binary>> = KeyStruct,
+ KeyExchange, Version)
+ when KeyExchange == ?KEY_EXCHANGE_PSK; KeyExchange == ?KEY_EXCHANGE_RSA_PSK ->
+ Params = #server_psk_params{
+ hint = PskIdentityHint},
+ {BinMsg, HashSign, Signature} = dec_server_key_params(Len + 2, KeyStruct, Version),
+ #server_key_params{params = Params,
+ params_bin = BinMsg,
+ hashsign = HashSign,
+ signature = Signature};
+dec_server_key(<<?UINT16(Len), IdentityHint:Len/binary,
+ ?UINT16(PLen), P:PLen/binary,
+ ?UINT16(GLen), G:GLen/binary,
+ ?UINT16(YLen), Y:YLen/binary, _/binary>> = KeyStruct,
+ ?KEY_EXCHANGE_DHE_PSK, Version) ->
+ DHParams = #server_dh_params{dh_p = P, dh_g = G, dh_y = Y},
+ Params = #server_dhe_psk_params{
+ hint = IdentityHint,
+ dh_params = DHParams},
+ {BinMsg, HashSign, Signature} = dec_server_key_params(Len + PLen + GLen + YLen + 8, KeyStruct, Version),
+ #server_key_params{params = Params,
+ params_bin = BinMsg,
+ hashsign = HashSign,
+ signature = Signature};
+dec_server_key(<<?UINT16(NLen), N:NLen/binary,
+ ?UINT16(GLen), G:GLen/binary,
+ ?BYTE(SLen), S:SLen/binary,
+ ?UINT16(BLen), B:BLen/binary, _/binary>> = KeyStruct,
+ ?KEY_EXCHANGE_SRP, Version) ->
+ Params = #server_srp_params{srp_n = N, srp_g = G, srp_s = S, srp_b = B},
+ {BinMsg, HashSign, Signature} = dec_server_key_params(NLen + GLen + SLen + BLen + 7, KeyStruct, Version),
+ #server_key_params{params = Params,
+ params_bin = BinMsg,
+ hashsign = HashSign,
+ signature = Signature};
+dec_server_key(_, _, _) ->
+ throw(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE)).
+
+%%--------------------------------------------------------------------
+-spec decode_suites('2_bytes'|'3_bytes', binary()) -> list().
+%%
+%% Description:
+%%--------------------------------------------------------------------
+decode_suites('2_bytes', Dec) ->
+ from_2bytes(Dec);
+decode_suites('3_bytes', Dec) ->
+ from_3bytes(Dec).
+
+%%-------------Cipeher suite handling --------------------------------
+
+available_suites(UserSuites, Version) ->
+ case UserSuites of
+ [] ->
+ ssl_cipher:suites(Version);
+ _ ->
+ UserSuites
+ end.
+
+available_suites(ServerCert, UserSuites, Version) ->
+ ssl_cipher:filter(ServerCert, available_suites(UserSuites, Version)).
+
+cipher_suites(Suites, false) ->
+ [?TLS_EMPTY_RENEGOTIATION_INFO_SCSV | Suites];
+cipher_suites(Suites, true) ->
+ Suites.
+
+select_session(SuggestedSessionId, CipherSuites, Compressions, Port, Session, Version,
+ #ssl_options{ciphers = UserSuites} = SslOpts, Cache, CacheCb, Cert) ->
+ {SessionId, Resumed} = ssl_session:server_id(Port, SuggestedSessionId,
+ SslOpts, Cert,
+ Cache, CacheCb),
+ Suites = ssl_handshake:available_suites(Cert, UserSuites, Version),
+ case Resumed of
+ undefined ->
+ CipherSuite = select_cipher_suite(CipherSuites, Suites),
+ Compression = select_compression(Compressions),
+ {new, Session#session{session_id = SessionId,
+ cipher_suite = CipherSuite,
+ compression_method = Compression}};
+ _ ->
+ {resumed, Resumed}
+ end.
+
+%%-------------certificate handling --------------------------------
+
+certificate_types({KeyExchange, _, _, _})
+ when KeyExchange == rsa;
+ KeyExchange == dhe_dss;
+ KeyExchange == dhe_rsa;
+ KeyExchange == ecdhe_rsa ->
+ <<?BYTE(?RSA_SIGN), ?BYTE(?DSS_SIGN)>>;
+
+certificate_types({KeyExchange, _, _, _})
+ when KeyExchange == dh_ecdsa;
+ KeyExchange == dhe_ecdsa ->
+ <<?BYTE(?ECDSA_SIGN)>>;
+
+certificate_types(_) ->
+ <<?BYTE(?RSA_SIGN)>>.
+
+certificate_authorities(CertDbHandle, CertDbRef) ->
+ Authorities = certificate_authorities_from_db(CertDbHandle, CertDbRef),
+ Enc = fun(#'OTPCertificate'{tbsCertificate=TBSCert}) ->
+ OTPSubj = TBSCert#'OTPTBSCertificate'.subject,
+ DNEncodedBin = public_key:pkix_encode('Name', OTPSubj, otp),
+ DNEncodedLen = byte_size(DNEncodedBin),
+ <<?UINT16(DNEncodedLen), DNEncodedBin/binary>>
+ end,
+ list_to_binary([Enc(Cert) || {_, Cert} <- Authorities]).
+
+certificate_authorities_from_db(CertDbHandle, CertDbRef) ->
+ ConnectionCerts = fun({{Ref, _, _}, Cert}, Acc) when Ref == CertDbRef ->
+ [Cert | Acc];
+ (_, Acc) ->
+ Acc
+ end,
+ ssl_pkix_db:foldl(ConnectionCerts, [], CertDbHandle).
+
+%%-------------Extension handling --------------------------------
+
+handle_client_hello_extensions(RecordCB, Random,
+ #hello_extensions{renegotiation_info = Info,
+ srp = SRP,
+ next_protocol_negotiation = NextProtocolNegotiation,
+ ec_point_formats = EcPointFormats0,
+ elliptic_curves = EllipticCurves0}, Version,
+ #ssl_options{secure_renegotiate = SecureRenegotation} = Opts,
+ #session{cipher_suite = CipherSuite, compression_method = Compression} = Session0,
+ ConnectionStates0, Renegotiation) ->
+ Session = handle_srp_extension(SRP, Session0),
+ ConnectionStates = handle_renegotiation_extension(server, RecordCB, Version, Info,
+ Random, CipherSuite, Compression,
+ ConnectionStates0, Renegotiation, SecureRenegotation),
+ ProtocolsToAdvertise = handle_next_protocol_extension(NextProtocolNegotiation, Renegotiation, Opts),
+ {EcPointFormats, EllipticCurves} = handle_ecc_extensions(Version, EcPointFormats0, EllipticCurves0),
+ ServerHelloExtensions = #hello_extensions{
+ renegotiation_info = renegotiation_info(RecordCB, server,
+ ConnectionStates, Renegotiation),
+ ec_point_formats = EcPointFormats,
+ elliptic_curves = EllipticCurves,
+ next_protocol_negotiation =
+ encode_protocols_advertised_on_server(ProtocolsToAdvertise)
+ },
+ {Session, ConnectionStates, ServerHelloExtensions}.
+
+handle_server_hello_extensions(RecordCB, Random, CipherSuite, Compression,
+ #hello_extensions{renegotiation_info = Info,
+ next_protocol_negotiation = NextProtocolNegotiation}, Version,
+ #ssl_options{secure_renegotiate = SecureRenegotation,
+ next_protocol_selector = NextProtoSelector},
+ ConnectionStates0, Renegotiation) ->
+ ConnectionStates = handle_renegotiation_extension(client, RecordCB, Version, Info, Random, CipherSuite,
+ Compression, ConnectionStates0,
+ Renegotiation, SecureRenegotation),
+ case handle_next_protocol(NextProtocolNegotiation, NextProtoSelector, Renegotiation) of
+ #alert{} = Alert ->
+ Alert;
+ Protocol ->
+ {ConnectionStates, Protocol}
+ end.
+
+select_version(RecordCB, ClientVersion, Versions) ->
+ ServerVersion = RecordCB:highest_protocol_version(Versions),
+ RecordCB:lowest_protocol_version(ClientVersion, ServerVersion).
+
+renegotiation_info(_, client, _, false) ->
+ #renegotiation_info{renegotiated_connection = undefined};
+renegotiation_info(_RecordCB, server, ConnectionStates, false) ->
+ CS = ssl_record:current_connection_state(ConnectionStates, read),
+ case CS#connection_state.secure_renegotiation of
+ true ->
+ #renegotiation_info{renegotiated_connection = ?byte(0)};
+ false ->
+ #renegotiation_info{renegotiated_connection = undefined}
+ end;
+renegotiation_info(_RecordCB, client, ConnectionStates, true) ->
+ CS = ssl_record:current_connection_state(ConnectionStates, read),
+ case CS#connection_state.secure_renegotiation of
+ true ->
+ Data = CS#connection_state.client_verify_data,
+ #renegotiation_info{renegotiated_connection = Data};
+ false ->
+ #renegotiation_info{renegotiated_connection = undefined}
+ end;
+
+renegotiation_info(_RecordCB, server, ConnectionStates, true) ->
+ CS = ssl_record:current_connection_state(ConnectionStates, read),
+ case CS#connection_state.secure_renegotiation of
+ true ->
+ CData = CS#connection_state.client_verify_data,
+ SData =CS#connection_state.server_verify_data,
+ #renegotiation_info{renegotiated_connection = <<CData/binary, SData/binary>>};
+ false ->
+ #renegotiation_info{renegotiated_connection = undefined}
+ end.
+
+handle_renegotiation_info(_RecordCB, _, #renegotiation_info{renegotiated_connection = ?byte(0)},
+ ConnectionStates, false, _, _) ->
+ {ok, ssl_record:set_renegotiation_flag(true, ConnectionStates)};
+
+handle_renegotiation_info(_RecordCB, server, undefined, ConnectionStates, _, _, CipherSuites) ->
+ case is_member(?TLS_EMPTY_RENEGOTIATION_INFO_SCSV, CipherSuites) of
+ true ->
+ {ok, ssl_record:set_renegotiation_flag(true, ConnectionStates)};
+ false ->
+ {ok, ssl_record:set_renegotiation_flag(false, ConnectionStates)}
+ end;
+
+handle_renegotiation_info(_RecordCB, _, undefined, ConnectionStates, false, _, _) ->
+ {ok, ssl_record:set_renegotiation_flag(false, ConnectionStates)};
+
+handle_renegotiation_info(_RecordCB, client, #renegotiation_info{renegotiated_connection = ClientServerVerify},
+ ConnectionStates, true, _, _) ->
+ CS = ssl_record:current_connection_state(ConnectionStates, read),
+ CData = CS#connection_state.client_verify_data,
+ SData = CS#connection_state.server_verify_data,
+ case <<CData/binary, SData/binary>> == ClientServerVerify of
+ true ->
+ {ok, ConnectionStates};
+ false ->
+ ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE)
+ end;
+handle_renegotiation_info(_RecordCB, server, #renegotiation_info{renegotiated_connection = ClientVerify},
+ ConnectionStates, true, _, CipherSuites) ->
+
+ case is_member(?TLS_EMPTY_RENEGOTIATION_INFO_SCSV, CipherSuites) of
+ true ->
+ ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE);
+ false ->
+ CS = ssl_record:current_connection_state(ConnectionStates, read),
+ Data = CS#connection_state.client_verify_data,
+ case Data == ClientVerify of
+ true ->
+ {ok, ConnectionStates};
+ false ->
+ ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE)
+ end
+ end;
+
+handle_renegotiation_info(RecordCB, client, undefined, ConnectionStates, true, SecureRenegotation, _) ->
+ handle_renegotiation_info(RecordCB, ConnectionStates, SecureRenegotation);
+
+handle_renegotiation_info(RecordCB, server, undefined, ConnectionStates, true, SecureRenegotation, CipherSuites) ->
+ case is_member(?TLS_EMPTY_RENEGOTIATION_INFO_SCSV, CipherSuites) of
+ true ->
+ ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE);
+ false ->
+ handle_renegotiation_info(RecordCB, ConnectionStates, SecureRenegotation)
+ end.
+
+handle_renegotiation_info(_RecordCB, ConnectionStates, SecureRenegotation) ->
+ CS = ssl_record:current_connection_state(ConnectionStates, read),
+ case {SecureRenegotation, CS#connection_state.secure_renegotiation} of
+ {_, true} ->
+ ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE);
+ {true, false} ->
+ ?ALERT_REC(?FATAL, ?NO_RENEGOTIATION);
+ {false, false} ->
+ {ok, ConnectionStates}
+ end.
+
+hello_extensions_list(#hello_extensions{renegotiation_info = RenegotiationInfo,
+ srp = SRP,
+ hash_signs = HashSigns,
+ ec_point_formats = EcPointFormats,
+ elliptic_curves = EllipticCurves,
+ next_protocol_negotiation = NextProtocolNegotiation}) ->
+ [Ext || Ext <- [RenegotiationInfo, SRP, HashSigns,
+ EcPointFormats,EllipticCurves, NextProtocolNegotiation], Ext =/= undefined].
+
+srp_user(#ssl_options{srp_identity = {UserName, _}}) ->
+ #srp{username = UserName};
+srp_user(_) ->
+ undefined.
+
+ecc_extensions(Module, Version) ->
+ CryptoSupport = proplists:get_value(public_keys, crypto:supports()),
+ case proplists:get_bool(ecdh, CryptoSupport) of
+ true ->
+ EcPointFormats = #ec_point_formats{ec_point_format_list = [?ECPOINT_UNCOMPRESSED]},
+ EllipticCurves = #elliptic_curves{elliptic_curve_list = Module:ecc_curves(Version)},
+ {EcPointFormats, EllipticCurves};
+ _ ->
+ {undefined, undefined}
+ end.
+
+handle_ecc_extensions(Version, EcPointFormats0, EllipticCurves0) ->
+ CryptoSupport = proplists:get_value(public_keys, crypto:supports()),
+ case proplists:get_bool(ecdh, CryptoSupport) of
+ true ->
+ EcPointFormats1 = handle_ecc_point_fmt_extension(EcPointFormats0),
+ EllipticCurves1 = handle_ecc_curves_extension(Version, EllipticCurves0),
+ {EcPointFormats1, EllipticCurves1};
+ _ ->
+ {undefined, undefined}
+ end.
+
+handle_ecc_point_fmt_extension(undefined) ->
+ undefined;
+handle_ecc_point_fmt_extension(_) ->
+ #ec_point_formats{ec_point_format_list = [?ECPOINT_UNCOMPRESSED]}.
+
+handle_ecc_curves_extension(_Version, undefined) ->
+ undefined;
+handle_ecc_curves_extension(Version, _) ->
+ #elliptic_curves{elliptic_curve_list = tls_v1:ecc_curves(Version)}.
+
+advertises_ec_ciphers([]) ->
+ false;
+advertises_ec_ciphers([{ecdh_ecdsa, _,_,_} | _]) ->
+ true;
+advertises_ec_ciphers([{ecdhe_ecdsa, _,_,_} | _]) ->
+ true;
+advertises_ec_ciphers([{ecdh_rsa, _,_,_} | _]) ->
+ true;
+advertises_ec_ciphers([{ecdhe_rsa, _,_,_} | _]) ->
+ true;
+advertises_ec_ciphers([{ecdh_anon, _,_,_} | _]) ->
+ true;
+advertises_ec_ciphers([_| Rest]) ->
+ advertises_ec_ciphers(Rest).
+
+%%--------------------------------------------------------------------
+%%% Internal functions
+%%--------------------------------------------------------------------
+apply_user_fun(Fun, OtpCert, ExtensionOrError, UserState0, SslState) ->
+ case Fun(OtpCert, ExtensionOrError, UserState0) of
+ {valid, UserState} ->
+ {valid, {SslState, UserState}};
+ {fail, _} = Fail ->
+ Fail;
+ {unknown, UserState} ->
+ {unknown, {SslState, UserState}}
+ end.
+path_validation_alert({bad_cert, cert_expired}) ->
+ ?ALERT_REC(?FATAL, ?CERTIFICATE_EXPIRED);
+path_validation_alert({bad_cert, invalid_issuer}) ->
+ ?ALERT_REC(?FATAL, ?BAD_CERTIFICATE);
+path_validation_alert({bad_cert, invalid_signature}) ->
+ ?ALERT_REC(?FATAL, ?BAD_CERTIFICATE);
+path_validation_alert({bad_cert, name_not_permitted}) ->
+ ?ALERT_REC(?FATAL, ?BAD_CERTIFICATE);
+path_validation_alert({bad_cert, unknown_critical_extension}) ->
+ ?ALERT_REC(?FATAL, ?UNSUPPORTED_CERTIFICATE);
+path_validation_alert({bad_cert, cert_revoked}) ->
+ ?ALERT_REC(?FATAL, ?CERTIFICATE_REVOKED);
+path_validation_alert({bad_cert, selfsigned_peer}) ->
+ ?ALERT_REC(?FATAL, ?BAD_CERTIFICATE);
+path_validation_alert({bad_cert, unknown_ca}) ->
+ ?ALERT_REC(?FATAL, ?UNKNOWN_CA);
+path_validation_alert(_) ->
+ ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE).
+
+encrypted_premaster_secret(Secret, RSAPublicKey) ->
+ try
+ PreMasterSecret = public_key:encrypt_public(Secret, RSAPublicKey,
+ [{rsa_pad,
+ rsa_pkcs1_padding}]),
+ #encrypted_premaster_secret{premaster_secret = PreMasterSecret}
+ catch
+ _:_->
+ throw(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE))
+ end.
+
+digitally_signed({3, Minor}, Hash, HashAlgo, Key) when Minor >= 3 ->
+ public_key:sign({digest, Hash}, HashAlgo, Key);
+digitally_signed(_Version, Hash, HashAlgo, #'DSAPrivateKey'{} = Key) ->
+ public_key:sign({digest, Hash}, HashAlgo, Key);
+digitally_signed(_Version, Hash, _HashAlgo, #'RSAPrivateKey'{} = Key) ->
+ public_key:encrypt_private(Hash, Key,
+ [{rsa_pad, rsa_pkcs1_padding}]);
+digitally_signed(_Version, Hash, HashAlgo, Key) ->
+ public_key:sign({digest, Hash}, HashAlgo, Key).
+
+calc_certificate_verify({3, 0}, HashAlgo, MasterSecret, Handshake) ->
+ ssl_v3:certificate_verify(HashAlgo, MasterSecret, lists:reverse(Handshake));
+calc_certificate_verify({3, N}, HashAlgo, _MasterSecret, Handshake) ->
+ tls_v1:certificate_verify(HashAlgo, N, lists:reverse(Handshake)).
+
+calc_finished({3, 0}, Role, _PrfAlgo, MasterSecret, Handshake) ->
+ ssl_v3:finished(Role, MasterSecret, lists:reverse(Handshake));
+calc_finished({3, N}, Role, PrfAlgo, MasterSecret, Handshake) ->
+ tls_v1:finished(Role, N, PrfAlgo, MasterSecret, lists:reverse(Handshake)).
+
+master_secret(_RecordCB, Version, MasterSecret,
+ #security_parameters{
+ client_random = ClientRandom,
+ server_random = ServerRandom,
+ hash_size = HashSize,
+ prf_algorithm = PrfAlgo,
+ key_material_length = KML,
+ expanded_key_material_length = EKML,
+ iv_size = IVS},
+ ConnectionStates, Role) ->
+ {ClientWriteMacSecret, ServerWriteMacSecret, ClientWriteKey,
+ ServerWriteKey, ClientIV, ServerIV} =
+ setup_keys(Version, PrfAlgo, MasterSecret, ServerRandom,
+ ClientRandom, HashSize, KML, EKML, IVS),
+
+ ConnStates1 = ssl_record:set_master_secret(MasterSecret, ConnectionStates),
+ ConnStates2 =
+ ssl_record:set_mac_secret(ClientWriteMacSecret, ServerWriteMacSecret,
+ Role, ConnStates1),
+
+ ClientCipherState = #cipher_state{iv = ClientIV, key = ClientWriteKey},
+ ServerCipherState = #cipher_state{iv = ServerIV, key = ServerWriteKey},
+ {MasterSecret,
+ ssl_record:set_pending_cipher_state(ConnStates2, ClientCipherState,
+ ServerCipherState, Role)}.
+
+setup_keys({3,0}, _PrfAlgo, MasterSecret,
+ ServerRandom, ClientRandom, HashSize, KML, EKML, IVS) ->
+ ssl_v3:setup_keys(MasterSecret, ServerRandom,
+ ClientRandom, HashSize, KML, EKML, IVS);
+
+setup_keys({3,N}, PrfAlgo, MasterSecret,
+ ServerRandom, ClientRandom, HashSize, KML, _EKML, IVS) ->
+ tls_v1:setup_keys(N, PrfAlgo, MasterSecret, ServerRandom, ClientRandom, HashSize,
+ KML, IVS).
+
+calc_master_secret({3,0}, _PrfAlgo, PremasterSecret, ClientRandom, ServerRandom) ->
+ ssl_v3:master_secret(PremasterSecret, ClientRandom, ServerRandom);
+
+calc_master_secret({3,_}, PrfAlgo, PremasterSecret, ClientRandom, ServerRandom) ->
+ tls_v1:master_secret(PrfAlgo, PremasterSecret, ClientRandom, ServerRandom).
+
+handle_renegotiation_extension(Role, RecordCB, Version, Info, Random, CipherSuite, Compression,
+ ConnectionStates0, Renegotiation, SecureRenegotation) ->
+ case handle_renegotiation_info(RecordCB, Role, Info, ConnectionStates0,
+ Renegotiation, SecureRenegotation,
+ [CipherSuite]) of
+ {ok, ConnectionStates} ->
+ hello_pending_connection_states(RecordCB, Role,
+ Version,
+ CipherSuite,
+ Random,
+ Compression,
+ ConnectionStates);
+ #alert{} = Alert ->
+ throw(Alert)
+ end.
+
+%% Update pending connection states with parameters exchanged via
+%% hello messages
+%% NOTE : Role is the role of the receiver of the hello message
+%% currently being processed.
+hello_pending_connection_states(_RecordCB, Role, Version, CipherSuite, Random, Compression,
+ ConnectionStates) ->
+ ReadState =
+ ssl_record:pending_connection_state(ConnectionStates, read),
+ WriteState =
+ ssl_record:pending_connection_state(ConnectionStates, write),
+
+ NewReadSecParams =
+ hello_security_parameters(Role, Version, ReadState, CipherSuite,
+ Random, Compression),
+
+ NewWriteSecParams =
+ hello_security_parameters(Role, Version, WriteState, CipherSuite,
+ Random, Compression),
+
+ ssl_record:set_security_params(NewReadSecParams,
+ NewWriteSecParams,
+ ConnectionStates).
+
+hello_security_parameters(client, Version, ConnectionState, CipherSuite, Random,
+ Compression) ->
+ SecParams = ConnectionState#connection_state.security_parameters,
+ NewSecParams = ssl_cipher:security_parameters(Version, CipherSuite, SecParams),
+ NewSecParams#security_parameters{
+ server_random = Random,
+ compression_algorithm = Compression
+ };
+
+hello_security_parameters(server, Version, ConnectionState, CipherSuite, Random,
+ Compression) ->
+ SecParams = ConnectionState#connection_state.security_parameters,
+ NewSecParams = ssl_cipher:security_parameters(Version, CipherSuite, SecParams),
+ NewSecParams#security_parameters{
+ client_random = Random,
+ compression_algorithm = Compression
+ }.
+
+%%-------------Encode/Decode --------------------------------
+
+encode_server_key(#server_dh_params{dh_p = P, dh_g = G, dh_y = Y}) ->
+ PLen = byte_size(P),
+ GLen = byte_size(G),
+ YLen = byte_size(Y),
+ <<?UINT16(PLen), P/binary, ?UINT16(GLen), G/binary, ?UINT16(YLen), Y/binary>>;
+encode_server_key(#server_ecdh_params{curve = {namedCurve, ECCurve}, public = ECPubKey}) ->
+ %%TODO: support arbitrary keys
+ KLen = size(ECPubKey),
+ <<?BYTE(?NAMED_CURVE), ?UINT16((tls_v1:oid_to_enum(ECCurve))),
+ ?BYTE(KLen), ECPubKey/binary>>;
+encode_server_key(#server_psk_params{hint = PskIdentityHint}) ->
+ Len = byte_size(PskIdentityHint),
+ <<?UINT16(Len), PskIdentityHint/binary>>;
+encode_server_key(Params = #server_dhe_psk_params{hint = undefined}) ->
+ encode_server_key(Params#server_dhe_psk_params{hint = <<>>});
+encode_server_key(#server_dhe_psk_params{
+ hint = PskIdentityHint,
+ dh_params = #server_dh_params{dh_p = P, dh_g = G, dh_y = Y}}) ->
+ Len = byte_size(PskIdentityHint),
+ PLen = byte_size(P),
+ GLen = byte_size(G),
+ YLen = byte_size(Y),
+ <<?UINT16(Len), PskIdentityHint/binary,
+ ?UINT16(PLen), P/binary, ?UINT16(GLen), G/binary, ?UINT16(YLen), Y/binary>>;
+encode_server_key(#server_srp_params{srp_n = N, srp_g = G, srp_s = S, srp_b = B}) ->
+ NLen = byte_size(N),
+ GLen = byte_size(G),
+ SLen = byte_size(S),
+ BLen = byte_size(B),
+ <<?UINT16(NLen), N/binary, ?UINT16(GLen), G/binary,
+ ?BYTE(SLen), S/binary, ?UINT16(BLen), B/binary>>.
+
+encode_client_key(#encrypted_premaster_secret{premaster_secret = PKEPMS},{3, 0}) ->
+ PKEPMS;
+encode_client_key(#encrypted_premaster_secret{premaster_secret = PKEPMS}, _) ->
+ PKEPMSLen = byte_size(PKEPMS),
+ <<?UINT16(PKEPMSLen), PKEPMS/binary>>;
+encode_client_key(#client_diffie_hellman_public{dh_public = DHPublic}, _) ->
+ Len = byte_size(DHPublic),
+ <<?UINT16(Len), DHPublic/binary>>;
+encode_client_key(#client_ec_diffie_hellman_public{dh_public = DHPublic}, _) ->
+ Len = byte_size(DHPublic),
+ <<?BYTE(Len), DHPublic/binary>>;
+encode_client_key(#client_psk_identity{identity = undefined}, _) ->
+ Id = <<"psk_identity">>,
+ Len = byte_size(Id),
+ <<?UINT16(Len), Id/binary>>;
+encode_client_key(#client_psk_identity{identity = Id}, _) ->
+ Len = byte_size(Id),
+ <<?UINT16(Len), Id/binary>>;
+encode_client_key(Identity = #client_dhe_psk_identity{identity = undefined}, Version) ->
+ encode_client_key(Identity#client_dhe_psk_identity{identity = <<"psk_identity">>}, Version);
+encode_client_key(#client_dhe_psk_identity{identity = Id, dh_public = DHPublic}, _) ->
+ Len = byte_size(Id),
+ DHLen = byte_size(DHPublic),
+ <<?UINT16(Len), Id/binary, ?UINT16(DHLen), DHPublic/binary>>;
+encode_client_key(Identity = #client_rsa_psk_identity{identity = undefined}, Version) ->
+ encode_client_key(Identity#client_rsa_psk_identity{identity = <<"psk_identity">>}, Version);
+encode_client_key(#client_rsa_psk_identity{identity = Id, exchange_keys = ExchangeKeys}, Version) ->
+ EncPMS = encode_client_key(ExchangeKeys, Version),
+ Len = byte_size(Id),
+ <<?UINT16(Len), Id/binary, EncPMS/binary>>;
+encode_client_key(#client_srp_public{srp_a = A}, _) ->
+ Len = byte_size(A),
+ <<?UINT16(Len), A/binary>>.
+
+enc_sign({_, anon}, _Sign, _Version) ->
+ <<>>;
+enc_sign({HashAlg, SignAlg}, Signature, _Version = {Major, Minor})
+ when Major == 3, Minor >= 3->
+ SignLen = byte_size(Signature),
+ HashSign = enc_hashsign(HashAlg, SignAlg),
+ <<HashSign/binary, ?UINT16(SignLen), Signature/binary>>;
+enc_sign(_HashSign, Sign, _Version) ->
+ SignLen = byte_size(Sign),
+ <<?UINT16(SignLen), Sign/binary>>.
+
+enc_hashsign(HashAlgo, SignAlgo) ->
+ Hash = ssl_cipher:hash_algorithm(HashAlgo),
+ Sign = ssl_cipher:sign_algorithm(SignAlgo),
+ <<?BYTE(Hash), ?BYTE(Sign)>>.
+
+encode_protocol(Protocol, Acc) ->
+ Len = byte_size(Protocol),
+ <<Acc/binary, ?BYTE(Len), Protocol/binary>>.
+
+dec_client_key(PKEPMS, ?KEY_EXCHANGE_RSA, {3, 0}) ->
+ #encrypted_premaster_secret{premaster_secret = PKEPMS};
+dec_client_key(<<?UINT16(_), PKEPMS/binary>>, ?KEY_EXCHANGE_RSA, _) ->
+ #encrypted_premaster_secret{premaster_secret = PKEPMS};
+dec_client_key(<<>>, ?KEY_EXCHANGE_DIFFIE_HELLMAN, _) ->
+ throw(?ALERT_REC(?FATAL, ?UNSUPPORTED_CERTIFICATE));
+dec_client_key(<<?UINT16(DH_YLen), DH_Y:DH_YLen/binary>>,
+ ?KEY_EXCHANGE_DIFFIE_HELLMAN, _) ->
+ #client_diffie_hellman_public{dh_public = DH_Y};
+dec_client_key(<<>>, ?KEY_EXCHANGE_EC_DIFFIE_HELLMAN, _) ->
+ throw(?ALERT_REC(?FATAL, ?UNSUPPORTED_CERTIFICATE));
+dec_client_key(<<?BYTE(DH_YLen), DH_Y:DH_YLen/binary>>,
+ ?KEY_EXCHANGE_EC_DIFFIE_HELLMAN, _) ->
+ #client_ec_diffie_hellman_public{dh_public = DH_Y};
+dec_client_key(<<?UINT16(Len), Id:Len/binary>>,
+ ?KEY_EXCHANGE_PSK, _) ->
+ #client_psk_identity{identity = Id};
+dec_client_key(<<?UINT16(Len), Id:Len/binary,
+ ?UINT16(DH_YLen), DH_Y:DH_YLen/binary>>,
+ ?KEY_EXCHANGE_DHE_PSK, _) ->
+ #client_dhe_psk_identity{identity = Id, dh_public = DH_Y};
+dec_client_key(<<?UINT16(Len), Id:Len/binary, PKEPMS/binary>>,
+ ?KEY_EXCHANGE_RSA_PSK, {3, 0}) ->
+ #client_rsa_psk_identity{identity = Id,
+ exchange_keys = #encrypted_premaster_secret{premaster_secret = PKEPMS}};
+dec_client_key(<<?UINT16(Len), Id:Len/binary, ?UINT16(_), PKEPMS/binary>>,
+ ?KEY_EXCHANGE_RSA_PSK, _) ->
+ #client_rsa_psk_identity{identity = Id,
+ exchange_keys = #encrypted_premaster_secret{premaster_secret = PKEPMS}};
+dec_client_key(<<?UINT16(ALen), A:ALen/binary>>,
+ ?KEY_EXCHANGE_SRP, _) ->
+ #client_srp_public{srp_a = A}.
+
+dec_server_key_params(Len, Keys, Version) ->
+ <<Params:Len/bytes, Signature/binary>> = Keys,
+ dec_server_key_signature(Params, Signature, Version).
+
+dec_server_key_signature(Params, <<?BYTE(HashAlgo), ?BYTE(SignAlgo),
+ ?UINT16(0)>>, {Major, Minor})
+ when Major == 3, Minor >= 3 ->
+ HashSign = {ssl_cipher:hash_algorithm(HashAlgo), ssl_cipher:sign_algorithm(SignAlgo)},
+ {Params, HashSign, <<>>};
+dec_server_key_signature(Params, <<?BYTE(HashAlgo), ?BYTE(SignAlgo),
+ ?UINT16(Len), Signature:Len/binary>>, {Major, Minor})
+ when Major == 3, Minor >= 3 ->
+ HashSign = {ssl_cipher:hash_algorithm(HashAlgo), ssl_cipher:sign_algorithm(SignAlgo)},
+ {Params, HashSign, Signature};
+dec_server_key_signature(Params, <<>>, _) ->
+ {Params, {null, anon}, <<>>};
+dec_server_key_signature(Params, <<?UINT16(0)>>, _) ->
+ {Params, {null, anon}, <<>>};
+dec_server_key_signature(Params, <<?UINT16(Len), Signature:Len/binary>>, _) ->
+ {Params, undefined, Signature};
+dec_server_key_signature(_, _, _) ->
+ throw(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE)).
+
+dec_hello_extensions(<<>>, Acc) ->
+ Acc;
+dec_hello_extensions(<<?UINT16(?NEXTPROTONEG_EXT), ?UINT16(Len), ExtensionData:Len/binary, Rest/binary>>, Acc) ->
+ NextP = #next_protocol_negotiation{extension_data = ExtensionData},
+ dec_hello_extensions(Rest, Acc#hello_extensions{next_protocol_negotiation = NextP});
+dec_hello_extensions(<<?UINT16(?RENEGOTIATION_EXT), ?UINT16(Len), Info:Len/binary, Rest/binary>>, Acc) ->
+ RenegotiateInfo = case Len of
+ 1 -> % Initial handshake
+ Info; % should be <<0>> will be matched in handle_renegotiation_info
+ _ ->
+ VerifyLen = Len - 1,
+ <<?BYTE(VerifyLen), VerifyInfo/binary>> = Info,
+ VerifyInfo
+ end,
+ dec_hello_extensions(Rest, Acc#hello_extensions{renegotiation_info =
+ #renegotiation_info{renegotiated_connection =
+ RenegotiateInfo}});
+
+dec_hello_extensions(<<?UINT16(?SRP_EXT), ?UINT16(Len), ?BYTE(SRPLen), SRP:SRPLen/binary, Rest/binary>>, Acc)
+ when Len == SRPLen + 2 ->
+ dec_hello_extensions(Rest, Acc#hello_extensions{srp = #srp{username = SRP}});
+
+dec_hello_extensions(<<?UINT16(?SIGNATURE_ALGORITHMS_EXT), ?UINT16(Len),
+ ExtData:Len/binary, Rest/binary>>, Acc) ->
+ SignAlgoListLen = Len - 2,
+ <<?UINT16(SignAlgoListLen), SignAlgoList/binary>> = ExtData,
+ HashSignAlgos = [{ssl_cipher:hash_algorithm(Hash), ssl_cipher:sign_algorithm(Sign)} ||
+ <<?BYTE(Hash), ?BYTE(Sign)>> <= SignAlgoList],
+ dec_hello_extensions(Rest, Acc#hello_extensions{hash_signs =
+ #hash_sign_algos{hash_sign_algos = HashSignAlgos}});
+
+dec_hello_extensions(<<?UINT16(?ELLIPTIC_CURVES_EXT), ?UINT16(Len),
+ ExtData:Len/binary, Rest/binary>>, Acc) ->
+ <<?UINT16(_), EllipticCurveList/binary>> = ExtData,
+ EllipticCurves = [tls_v1:enum_to_oid(X) || <<X:16>> <= EllipticCurveList],
+ dec_hello_extensions(Rest, Acc#hello_extensions{elliptic_curves =
+ #elliptic_curves{elliptic_curve_list =
+ EllipticCurves}});
+dec_hello_extensions(<<?UINT16(?EC_POINT_FORMATS_EXT), ?UINT16(Len),
+ ExtData:Len/binary, Rest/binary>>, Acc) ->
+ <<?BYTE(_), ECPointFormatList/binary>> = ExtData,
+ ECPointFormats = binary_to_list(ECPointFormatList),
+ dec_hello_extensions(Rest, Acc#hello_extensions{ec_point_formats =
+ #ec_point_formats{ec_point_format_list =
+ ECPointFormats}});
+%% Ignore data following the ClientHello (i.e.,
+%% extensions) if not understood.
+
+dec_hello_extensions(<<?UINT16(_), ?UINT16(Len), _Unknown:Len/binary, Rest/binary>>, Acc) ->
+ dec_hello_extensions(Rest, Acc);
+%% This theoretically should not happen if the protocol is followed, but if it does it is ignored.
+dec_hello_extensions(_, Acc) ->
+ Acc.
+
+dec_hashsign(<<?BYTE(HashAlgo), ?BYTE(SignAlgo)>>) ->
+ {ssl_cipher:hash_algorithm(HashAlgo), ssl_cipher:sign_algorithm(SignAlgo)}.
+
+decode_next_protocols({next_protocol_negotiation, Protocols}) ->
+ decode_next_protocols(Protocols, []).
+decode_next_protocols(<<>>, Acc) ->
+ lists:reverse(Acc);
+decode_next_protocols(<<?BYTE(Len), Protocol:Len/binary, Rest/binary>>, Acc) ->
+ case Len of
+ 0 ->
+ {error, invalid_next_protocols};
+ _ ->
+ decode_next_protocols(Rest, [Protocol|Acc])
+ end;
+decode_next_protocols(_Bytes, _Acc) ->
+ {error, invalid_next_protocols}.
+
+%% encode/decode stream of certificate data to/from list of certificate data
+certs_to_list(ASN1Certs) ->
+ certs_to_list(ASN1Certs, []).
+
+certs_to_list(<<?UINT24(CertLen), Cert:CertLen/binary, Rest/binary>>, Acc) ->
+ certs_to_list(Rest, [Cert | Acc]);
+certs_to_list(<<>>, Acc) ->
+ lists:reverse(Acc, []).
+
+certs_from_list(ACList) ->
+ list_to_binary([begin
+ CertLen = byte_size(Cert),
+ <<?UINT24(CertLen), Cert/binary>>
+ end || Cert <- ACList]).
+from_3bytes(Bin3) ->
+ from_3bytes(Bin3, []).
+
+from_3bytes(<<>>, Acc) ->
+ lists:reverse(Acc);
+from_3bytes(<<?UINT24(N), Rest/binary>>, Acc) ->
+ from_3bytes(Rest, [?uint16(N) | Acc]).
+
+from_2bytes(Bin2) ->
+ from_2bytes(Bin2, []).
+
+from_2bytes(<<>>, Acc) ->
+ lists:reverse(Acc);
+from_2bytes(<<?UINT16(N), Rest/binary>>, Acc) ->
+ from_2bytes(Rest, [?uint16(N) | Acc]).
+key_exchange_alg(rsa) ->
+ ?KEY_EXCHANGE_RSA;
+key_exchange_alg(Alg) when Alg == dhe_rsa; Alg == dhe_dss;
+ Alg == dh_dss; Alg == dh_rsa; Alg == dh_anon ->
+ ?KEY_EXCHANGE_DIFFIE_HELLMAN;
+key_exchange_alg(Alg) when Alg == ecdhe_rsa; Alg == ecdh_rsa;
+ Alg == ecdhe_ecdsa; Alg == ecdh_ecdsa;
+ Alg == ecdh_anon ->
+ ?KEY_EXCHANGE_EC_DIFFIE_HELLMAN;
+key_exchange_alg(psk) ->
+ ?KEY_EXCHANGE_PSK;
+key_exchange_alg(dhe_psk) ->
+ ?KEY_EXCHANGE_DHE_PSK;
+key_exchange_alg(rsa_psk) ->
+ ?KEY_EXCHANGE_RSA_PSK;
+key_exchange_alg(Alg)
+ when Alg == srp_rsa; Alg == srp_dss; Alg == srp_anon ->
+ ?KEY_EXCHANGE_SRP;
+key_exchange_alg(_) ->
+ ?NULL.
+
+%%-------------Extension handling --------------------------------
+
+handle_next_protocol(undefined,
+ _NextProtocolSelector, _Renegotiating) ->
+ undefined;
+
+handle_next_protocol(#next_protocol_negotiation{} = NextProtocols,
+ NextProtocolSelector, Renegotiating) ->
+
+ case next_protocol_extension_allowed(NextProtocolSelector, Renegotiating) of
+ true ->
+ select_next_protocol(decode_next_protocols(NextProtocols), NextProtocolSelector);
+ false ->
+ ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE) % unexpected next protocol extension
+ end.
+
+
+handle_next_protocol_extension(NextProtocolNegotiation, Renegotiation, SslOpts)->
+ case handle_next_protocol_on_server(NextProtocolNegotiation, Renegotiation, SslOpts) of
+ #alert{} = Alert ->
+ Alert;
+ ProtocolsToAdvertise ->
+ ProtocolsToAdvertise
+ end.
+
+handle_next_protocol_on_server(undefined, _Renegotiation, _SslOpts) ->
+ undefined;
+
+handle_next_protocol_on_server(#next_protocol_negotiation{extension_data = <<>>},
+ false, #ssl_options{next_protocols_advertised = Protocols}) ->
+ Protocols;
+
+handle_next_protocol_on_server(_Hello, _Renegotiation, _SSLOpts) ->
+ ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE). % unexpected next protocol extension
+
+next_protocol_extension_allowed(NextProtocolSelector, Renegotiating) ->
+ NextProtocolSelector =/= undefined andalso not Renegotiating.
+
+select_next_protocol({error, _Reason}, _NextProtocolSelector) ->
+ ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE);
+select_next_protocol(Protocols, NextProtocolSelector) ->
+ case NextProtocolSelector(Protocols) of
+ ?NO_PROTOCOL ->
+ ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE);
+ Protocol when is_binary(Protocol) ->
+ Protocol
+ end.
+
+handle_srp_extension(undefined, Session) ->
+ Session;
+handle_srp_extension(#srp{username = Username}, Session) ->
+ Session#session{srp_username = Username}.
+
+%%-------------Misc --------------------------------
+
+select_cipher_suite([], _) ->
+ no_suite;
+select_cipher_suite([Suite | ClientSuites], SupportedSuites) ->
+ case is_member(Suite, SupportedSuites) of
+ true ->
+ Suite;
+ false ->
+ select_cipher_suite(ClientSuites, SupportedSuites)
+ end.
+
+int_to_bin(I) ->
+ L = (length(integer_to_list(I, 16)) + 1) div 2,
+ <<I:(L*8)>>.
+
+is_member(Suite, SupportedSuites) ->
+ lists:member(Suite, SupportedSuites).
+
+select_compression(_CompressionMetodes) ->
+ ?NULL.
+
+-define(TLSEXT_SIGALG_RSA(MD), {MD, rsa}).
+-define(TLSEXT_SIGALG_DSA(MD), {MD, dsa}).
+-define(TLSEXT_SIGALG_ECDSA(MD), {MD, ecdsa}).
+
+-define(TLSEXT_SIGALG(MD), ?TLSEXT_SIGALG_ECDSA(MD), ?TLSEXT_SIGALG_RSA(MD)).
+
+advertised_hash_signs({Major, Minor}) when Major >= 3 andalso Minor >= 3 ->
+ HashSigns = [?TLSEXT_SIGALG(sha512),
+ ?TLSEXT_SIGALG(sha384),
+ ?TLSEXT_SIGALG(sha256),
+ ?TLSEXT_SIGALG(sha224),
+ ?TLSEXT_SIGALG(sha),
+ ?TLSEXT_SIGALG_DSA(sha),
+ ?TLSEXT_SIGALG_RSA(md5)],
+ CryptoSupport = crypto:supports(),
+ HasECC = proplists:get_bool(ecdsa, proplists:get_value(public_keys, CryptoSupport)),
+ Hashs = proplists:get_value(hashs, CryptoSupport),
+ #hash_sign_algos{hash_sign_algos =
+ lists:filter(fun({Hash, ecdsa}) -> HasECC andalso proplists:get_bool(Hash, Hashs);
+ ({Hash, _}) -> proplists:get_bool(Hash, Hashs) end, HashSigns)};
+advertised_hash_signs(_) ->
+ undefined.
diff --git a/lib/ssl/src/ssl_handshake.hrl b/lib/ssl/src/ssl_handshake.hrl
index eb1a1dbf62..3a3ad8cf35 100644
--- a/lib/ssl/src/ssl_handshake.hrl
+++ b/lib/ssl/src/ssl_handshake.hrl
@@ -28,11 +28,6 @@
-include_lib("public_key/include/public_key.hrl").
--type oid() :: tuple().
--type public_key_params() :: #'Dss-Parms'{} | {namedCurve, oid()} | #'ECParameters'{} | term().
--type public_key_info() :: {oid(), #'RSAPublicKey'{} | integer() | #'ECPoint'{}, public_key_params()}.
--type tls_handshake_history() :: {[binary()], [binary()]}.
-
-define(NO_PROTOCOL, <<>>).
%% Signature algorithms
@@ -96,17 +91,22 @@
%% client_hello defined in tls_handshake.hrl and dtls_handshake.hrl
+-record(hello_extensions, {
+ renegotiation_info,
+ hash_signs, % supported combinations of hashes/signature algos
+ next_protocol_negotiation = undefined, % [binary()]
+ srp,
+ ec_point_formats,
+ elliptic_curves
+ }).
+
-record(server_hello, {
server_version,
random,
session_id, % opaque SessionID<0..32>
cipher_suite, % cipher_suites
compression_method, % compression_method
- renegotiation_info,
- hash_signs, % supported combinations of hashes/signature algos
- ec_point_formats, % supported ec point formats
- elliptic_curves, % supported elliptic curver
- next_protocol_negotiation = undefined % [binary()]
+ extensions
}).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -337,6 +337,20 @@
-define(EXPLICIT_CHAR2, 2).
-define(NAMED_CURVE, 3).
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Dialyzer types
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+-type oid() :: tuple().
+-type public_key_params() :: #'Dss-Parms'{} | {namedCurve, oid()} | #'ECParameters'{} | term().
+-type public_key_info() :: {oid(), #'RSAPublicKey'{} | integer() | #'ECPoint'{}, public_key_params()}.
+-type tls_handshake_history() :: {[binary()], [binary()]}.
+
+-type ssl_handshake() :: #server_hello{} | #server_hello_done{} | #certificate{} | #certificate_request{} |
+ #client_key_exchange{} | #finished{} | #certificate_verify{} |
+ #hello_request{} | #next_protocol{}.
+
+
-endif. % -ifdef(ssl_handshake).
diff --git a/lib/ssl/src/ssl_internal.hrl b/lib/ssl/src/ssl_internal.hrl
index de8d20d399..96e3280fb5 100644
--- a/lib/ssl/src/ssl_internal.hrl
+++ b/lib/ssl/src/ssl_internal.hrl
@@ -37,7 +37,6 @@
-type tls_atom_version() :: sslv3 | tlsv1 | 'tlsv1.1' | 'tlsv1.2'.
-type certdb_ref() :: reference().
-type db_handle() :: term().
--type key_algo() :: null | rsa | dhe_rsa | dhe_dss | ecdhe_ecdsa| ecdh_ecdsa | ecdh_rsa| srp_rsa| srp_dss | psk | dhe_psk | rsa_psk | dh_anon | ecdh_anon | srp_anon.
-type der_cert() :: binary().
-type private_key() :: #'RSAPrivateKey'{} | #'DSAPrivateKey'{} | #'ECPrivateKey'{}.
-type issuer() :: tuple().
@@ -50,6 +49,7 @@
-define(UINT16(X), X:16/unsigned-big-integer).
-define(UINT24(X), X:24/unsigned-big-integer).
-define(UINT32(X), X:32/unsigned-big-integer).
+-define(UINT48(X), X:48/unsigned-big-integer).
-define(UINT64(X), X:64/unsigned-big-integer).
-define(STRING(X), ?UINT32((size(X))), (X)/binary).
@@ -57,6 +57,7 @@
-define(uint16(X), << ?UINT16(X) >> ).
-define(uint24(X), << ?UINT24(X) >> ).
-define(uint32(X), << ?UINT32(X) >> ).
+-define(uint48(X), << ?UINT48(X) >> ).
-define(uint64(X), << ?UINT64(X) >> ).
-define(CDR_MAGIC, "GIOP").
@@ -71,6 +72,8 @@
-define(ALL_SUPPORTED_VERSIONS, ['tlsv1.2', 'tlsv1.1', tlsv1, sslv3]).
-define(MIN_SUPPORTED_VERSIONS, ['tlsv1.1', tlsv1, sslv3]).
+-define(ALL_DATAGRAM_SUPPORTED_VERSIONS, ['dtlsv1.2', dtlsv1]).
+-define(MIN_DATAGRAM_SUPPORTED_VERSIONS, ['dtlsv1.2', dtlsv1]).
-record(ssl_options, {
versions, % 'tlsv1.2' | 'tlsv1.1' | tlsv1 | sslv3
@@ -124,6 +127,10 @@
active = true
}).
+-type state_name() :: hello | abbreviated | certify | cipher | connection.
+-type gen_fsm_state_return() :: {next_state, state_name(), term()} |
+ {next_state, state_name(), term(), timeout()} |
+ {stop, term(), term()}.
-endif. % -ifdef(ssl_internal).
diff --git a/lib/ssl/src/ssl_record.erl b/lib/ssl/src/ssl_record.erl
new file mode 100644
index 0000000000..50a45dc16b
--- /dev/null
+++ b/lib/ssl/src/ssl_record.erl
@@ -0,0 +1,439 @@
+%%
+%% %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%
+
+%%----------------------------------------------------------------------
+%% Purpose: Handle TLS/SSL/DTLS record protocol. Note epoch is only
+%% used by DTLS but handled here so we can avoid code duplication.
+%%----------------------------------------------------------------------
+
+-module(ssl_record).
+
+-include("ssl_record.hrl").
+-include("ssl_internal.hrl").
+-include("ssl_cipher.hrl").
+-include("ssl_alert.hrl").
+
+%% Connection state handling
+-export([init_connection_states/1,
+ current_connection_state/2, pending_connection_state/2,
+ activate_pending_connection_state/2,
+ set_security_params/3,
+ set_mac_secret/4,
+ set_master_secret/2,
+ set_pending_cipher_state/4,
+ set_renegotiation_flag/2,
+ set_client_verify_data/3,
+ set_server_verify_data/3]).
+
+%% Encoding records
+-export([encode_handshake/3, encode_alert_record/3,
+ encode_change_cipher_spec/2, encode_data/3]).
+
+%% Compression
+-export([compress/3, uncompress/3, compressions/0]).
+
+-export([is_correct_mac/2]).
+
+%%====================================================================
+%% Internal application API
+%%====================================================================
+
+%%--------------------------------------------------------------------
+-spec init_connection_states(client | server) -> #connection_states{}.
+%%
+%% Description: Creates a connection_states record with appropriate
+%% values for the initial SSL connection setup.
+%%--------------------------------------------------------------------
+init_connection_states(Role) ->
+ ConnectionEnd = record_protocol_role(Role),
+ Current = initial_connection_state(ConnectionEnd),
+ Pending = empty_connection_state(ConnectionEnd),
+ #connection_states{current_read = Current,
+ pending_read = Pending,
+ current_write = Current,
+ pending_write = Pending
+ }.
+
+%%--------------------------------------------------------------------
+-spec current_connection_state(#connection_states{}, read | write) ->
+ #connection_state{}.
+%%
+%% Description: Returns the instance of the connection_state record
+%% that is currently defined as the current conection state.
+%%--------------------------------------------------------------------
+current_connection_state(#connection_states{current_read = Current},
+ read) ->
+ Current;
+current_connection_state(#connection_states{current_write = Current},
+ write) ->
+ Current.
+
+%%--------------------------------------------------------------------
+-spec pending_connection_state(#connection_states{}, read | write) ->
+ term().
+%%
+%% Description: Returns the instance of the connection_state record
+%% that is currently defined as the pending conection state.
+%%--------------------------------------------------------------------
+pending_connection_state(#connection_states{pending_read = Pending},
+ read) ->
+ Pending;
+pending_connection_state(#connection_states{pending_write = Pending},
+ write) ->
+ Pending.
+
+
+%%--------------------------------------------------------------------
+-spec activate_pending_connection_state(#connection_states{}, read | write) ->
+ #connection_states{}.
+%%
+%% Description: Creates a new instance of the connection_states record
+%% where the pending state of <Type> has been activated.
+%%--------------------------------------------------------------------
+activate_pending_connection_state(States =
+ #connection_states{current_read = Current,
+ pending_read = Pending},
+ read) ->
+ NewCurrent = Pending#connection_state{epoch = dtls_next_epoch(Current),
+ sequence_number = 0},
+ SecParams = Pending#connection_state.security_parameters,
+ ConnectionEnd = SecParams#security_parameters.connection_end,
+ EmptyPending = empty_connection_state(ConnectionEnd),
+ SecureRenegotation = NewCurrent#connection_state.secure_renegotiation,
+ NewPending = EmptyPending#connection_state{secure_renegotiation = SecureRenegotation},
+ States#connection_states{current_read = NewCurrent,
+ pending_read = NewPending
+ };
+
+activate_pending_connection_state(States =
+ #connection_states{current_write = Current,
+ pending_write = Pending},
+ write) ->
+ NewCurrent = Pending#connection_state{epoch = dtls_next_epoch(Current),
+ sequence_number = 0},
+ SecParams = Pending#connection_state.security_parameters,
+ ConnectionEnd = SecParams#security_parameters.connection_end,
+ EmptyPending = empty_connection_state(ConnectionEnd),
+ SecureRenegotation = NewCurrent#connection_state.secure_renegotiation,
+ NewPending = EmptyPending#connection_state{secure_renegotiation = SecureRenegotation},
+ States#connection_states{current_write = NewCurrent,
+ pending_write = NewPending
+ }.
+
+
+%%--------------------------------------------------------------------
+-spec set_security_params(#security_parameters{}, #security_parameters{},
+ #connection_states{}) -> #connection_states{}.
+%%
+%% Description: Creates a new instance of the connection_states record
+%% where the pending states gets its security parameters updated.
+%%--------------------------------------------------------------------
+set_security_params(ReadParams, WriteParams, States =
+ #connection_states{pending_read = Read,
+ pending_write = Write}) ->
+ States#connection_states{pending_read =
+ Read#connection_state{security_parameters =
+ ReadParams},
+ pending_write =
+ Write#connection_state{security_parameters =
+ WriteParams}
+ }.
+%%--------------------------------------------------------------------
+-spec set_mac_secret(binary(), binary(), client | server,
+ #connection_states{}) -> #connection_states{}.
+%%
+%% Description: update the mac_secret field in pending connection states
+%%--------------------------------------------------------------------
+set_mac_secret(ClientWriteMacSecret, ServerWriteMacSecret, client, States) ->
+ set_mac_secret(ServerWriteMacSecret, ClientWriteMacSecret, States);
+set_mac_secret(ClientWriteMacSecret, ServerWriteMacSecret, server, States) ->
+ set_mac_secret(ClientWriteMacSecret, ServerWriteMacSecret, States).
+
+set_mac_secret(ReadMacSecret, WriteMacSecret,
+ States = #connection_states{pending_read = Read,
+ pending_write = Write}) ->
+ States#connection_states{
+ pending_read = Read#connection_state{mac_secret = ReadMacSecret},
+ pending_write = Write#connection_state{mac_secret = WriteMacSecret}
+ }.
+
+
+%%--------------------------------------------------------------------
+-spec set_master_secret(binary(), #connection_states{}) -> #connection_states{}.
+%%
+%% Description: Set master_secret in pending connection states
+%%--------------------------------------------------------------------
+set_master_secret(MasterSecret,
+ States = #connection_states{pending_read = Read,
+ pending_write = Write}) ->
+ ReadSecPar = Read#connection_state.security_parameters,
+ Read1 = Read#connection_state{
+ security_parameters = ReadSecPar#security_parameters{
+ master_secret = MasterSecret}},
+ WriteSecPar = Write#connection_state.security_parameters,
+ Write1 = Write#connection_state{
+ security_parameters = WriteSecPar#security_parameters{
+ master_secret = MasterSecret}},
+ States#connection_states{pending_read = Read1, pending_write = Write1}.
+
+%%--------------------------------------------------------------------
+-spec set_renegotiation_flag(boolean(), #connection_states{}) -> #connection_states{}.
+%%
+%% Description: Set secure_renegotiation in pending connection states
+%%--------------------------------------------------------------------
+set_renegotiation_flag(Flag, #connection_states{
+ current_read = CurrentRead0,
+ current_write = CurrentWrite0,
+ pending_read = PendingRead0,
+ pending_write = PendingWrite0}
+ = ConnectionStates) ->
+ CurrentRead = CurrentRead0#connection_state{secure_renegotiation = Flag},
+ CurrentWrite = CurrentWrite0#connection_state{secure_renegotiation = Flag},
+ PendingRead = PendingRead0#connection_state{secure_renegotiation = Flag},
+ PendingWrite = PendingWrite0#connection_state{secure_renegotiation = Flag},
+ ConnectionStates#connection_states{current_read = CurrentRead,
+ current_write = CurrentWrite,
+ pending_read = PendingRead,
+ pending_write = PendingWrite}.
+
+%%--------------------------------------------------------------------
+-spec set_client_verify_data(current_read | current_write | current_both,
+ binary(), #connection_states{})->
+ #connection_states{}.
+%%
+%% Description: Set verify data in connection states.
+%%--------------------------------------------------------------------
+set_client_verify_data(current_read, Data,
+ #connection_states{current_read = CurrentRead0,
+ pending_write = PendingWrite0}
+ = ConnectionStates) ->
+ CurrentRead = CurrentRead0#connection_state{client_verify_data = Data},
+ PendingWrite = PendingWrite0#connection_state{client_verify_data = Data},
+ ConnectionStates#connection_states{current_read = CurrentRead,
+ pending_write = PendingWrite};
+set_client_verify_data(current_write, Data,
+ #connection_states{pending_read = PendingRead0,
+ current_write = CurrentWrite0}
+ = ConnectionStates) ->
+ PendingRead = PendingRead0#connection_state{client_verify_data = Data},
+ CurrentWrite = CurrentWrite0#connection_state{client_verify_data = Data},
+ ConnectionStates#connection_states{pending_read = PendingRead,
+ current_write = CurrentWrite};
+set_client_verify_data(current_both, Data,
+ #connection_states{current_read = CurrentRead0,
+ current_write = CurrentWrite0}
+ = ConnectionStates) ->
+ CurrentRead = CurrentRead0#connection_state{client_verify_data = Data},
+ CurrentWrite = CurrentWrite0#connection_state{client_verify_data = Data},
+ ConnectionStates#connection_states{current_read = CurrentRead,
+ current_write = CurrentWrite}.
+%%--------------------------------------------------------------------
+-spec set_server_verify_data(current_read | current_write | current_both,
+ binary(), #connection_states{})->
+ #connection_states{}.
+%%
+%% Description: Set verify data in pending connection states.
+%%--------------------------------------------------------------------
+set_server_verify_data(current_write, Data,
+ #connection_states{pending_read = PendingRead0,
+ current_write = CurrentWrite0}
+ = ConnectionStates) ->
+ PendingRead = PendingRead0#connection_state{server_verify_data = Data},
+ CurrentWrite = CurrentWrite0#connection_state{server_verify_data = Data},
+ ConnectionStates#connection_states{pending_read = PendingRead,
+ current_write = CurrentWrite};
+
+set_server_verify_data(current_read, Data,
+ #connection_states{current_read = CurrentRead0,
+ pending_write = PendingWrite0}
+ = ConnectionStates) ->
+ CurrentRead = CurrentRead0#connection_state{server_verify_data = Data},
+ PendingWrite = PendingWrite0#connection_state{server_verify_data = Data},
+ ConnectionStates#connection_states{current_read = CurrentRead,
+ pending_write = PendingWrite};
+
+set_server_verify_data(current_both, Data,
+ #connection_states{current_read = CurrentRead0,
+ current_write = CurrentWrite0}
+ = ConnectionStates) ->
+ CurrentRead = CurrentRead0#connection_state{server_verify_data = Data},
+ CurrentWrite = CurrentWrite0#connection_state{server_verify_data = Data},
+ ConnectionStates#connection_states{current_read = CurrentRead,
+ current_write = CurrentWrite}.
+%%--------------------------------------------------------------------
+-spec set_pending_cipher_state(#connection_states{}, #cipher_state{},
+ #cipher_state{}, client | server) ->
+ #connection_states{}.
+%%
+%% Description: Set the cipher state in the specified pending connection state.
+%%--------------------------------------------------------------------
+set_pending_cipher_state(#connection_states{pending_read = Read,
+ pending_write = Write} = States,
+ ClientState, ServerState, server) ->
+ States#connection_states{
+ pending_read = Read#connection_state{cipher_state = ClientState},
+ pending_write = Write#connection_state{cipher_state = ServerState}};
+
+set_pending_cipher_state(#connection_states{pending_read = Read,
+ pending_write = Write} = States,
+ ClientState, ServerState, client) ->
+ States#connection_states{
+ pending_read = Read#connection_state{cipher_state = ServerState},
+ pending_write = Write#connection_state{cipher_state = ClientState}}.
+
+
+%%--------------------------------------------------------------------
+-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_alert_record(#alert{}, tls_version(), #connection_states{}) ->
+ {iolist(), #connection_states{}}.
+%%
+%% Description: Encodes an alert message to send on the ssl-socket.
+%%--------------------------------------------------------------------
+encode_alert_record(#alert{level = Level, description = Description},
+ Version, ConnectionStates) ->
+ encode_plain_text(?ALERT, Version, <<?BYTE(Level), ?BYTE(Description)>>,
+ 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 encode_data(binary(), tls_version(), #connection_states{}) ->
+ {iolist(), #connection_states{}}.
+%%
+%% Description: Encodes data to send on the ssl-socket.
+%%--------------------------------------------------------------------
+encode_data(Frag, Version,
+ #connection_states{current_write = #connection_state{
+ security_parameters =
+ #security_parameters{bulk_cipher_algorithm = BCA}}} =
+ ConnectionStates) ->
+ Data = split_bin(Frag, ?MAX_PLAIN_TEXT_LENGTH, Version, BCA),
+ encode_iolist(?APPLICATION_DATA, Data, Version, ConnectionStates).
+
+uncompress(?NULL, Data, CS) ->
+ {Data, CS}.
+
+compress(?NULL, Data, CS) ->
+ {Data, CS}.
+
+%%--------------------------------------------------------------------
+-spec compressions() -> [binary()].
+%%
+%% Description: return a list of compressions supported (currently none)
+%%--------------------------------------------------------------------
+compressions() ->
+ [?byte(?NULL)].
+
+%%--------------------------------------------------------------------
+%%% Internal functions
+%%--------------------------------------------------------------------
+empty_connection_state(ConnectionEnd) ->
+ SecParams = empty_security_params(ConnectionEnd),
+ #connection_state{security_parameters = SecParams}.
+
+empty_security_params(ConnectionEnd = ?CLIENT) ->
+ #security_parameters{connection_end = ConnectionEnd,
+ client_random = random()};
+empty_security_params(ConnectionEnd = ?SERVER) ->
+ #security_parameters{connection_end = ConnectionEnd,
+ server_random = random()}.
+random() ->
+ Secs_since_1970 = calendar:datetime_to_gregorian_seconds(
+ calendar:universal_time()) - 62167219200,
+ Random_28_bytes = crypto:rand_bytes(28),
+ <<?UINT32(Secs_since_1970), Random_28_bytes/binary>>.
+
+dtls_next_epoch(#connection_state{epoch = undefined}) -> %% SSL/TLS
+ undefined;
+dtls_next_epoch(#connection_state{epoch = Epoch}) -> %% DTLS
+ Epoch + 1.
+
+is_correct_mac(Mac, Mac) ->
+ true;
+is_correct_mac(_M,_H) ->
+ false.
+
+record_protocol_role(client) ->
+ ?CLIENT;
+record_protocol_role(server) ->
+ ?SERVER.
+
+initial_connection_state(ConnectionEnd) ->
+ #connection_state{security_parameters =
+ initial_security_params(ConnectionEnd),
+ sequence_number = 0
+ }.
+
+initial_security_params(ConnectionEnd) ->
+ SecParams = #security_parameters{connection_end = ConnectionEnd,
+ compression_algorithm = ?NULL},
+ ssl_cipher:security_parameters(?TLS_NULL_WITH_NULL_NULL, SecParams).
+
+
+encode_plain_text(Type, Version, Data, ConnectionStates) ->
+ RecordCB = protocol_module(Version),
+ RecordCB:encode_plain_text(Type, Version, Data, ConnectionStates).
+
+encode_iolist(Type, Data, Version, ConnectionStates0) ->
+ RecordCB = protocol_module(Version),
+ {ConnectionStates, EncodedMsg} =
+ lists:foldl(fun(Text, {CS0, Encoded}) ->
+ {Enc, CS1} =
+ RecordCB:encode_plain_text(Type, Version, Text, CS0),
+ {CS1, [Enc | Encoded]}
+ end, {ConnectionStates0, []}, Data),
+ {lists:reverse(EncodedMsg), ConnectionStates}.
+
+%% 1/n-1 splitting countermeasure Rizzo/Duong-Beast, RC4 chiphers are
+%% not vulnerable to this attack.
+split_bin(<<FirstByte:8, Rest/binary>>, ChunkSize, Version, BCA) when
+ BCA =/= ?RC4 andalso ({3, 1} == Version orelse
+ {3, 0} == Version) ->
+ do_split_bin(Rest, ChunkSize, [[FirstByte]]);
+split_bin(Bin, ChunkSize, _, _) ->
+ do_split_bin(Bin, ChunkSize, []).
+
+do_split_bin(<<>>, _, Acc) ->
+ lists:reverse(Acc);
+do_split_bin(Bin, ChunkSize, Acc) ->
+ case Bin of
+ <<Chunk:ChunkSize/binary, Rest/binary>> ->
+ do_split_bin(Rest, ChunkSize, [Chunk | Acc]);
+ _ ->
+ lists:reverse(Acc, [Bin])
+ end.
+
+protocol_module({3, _}) ->
+ tls_record;
+protocol_module({254, _}) ->
+ dtls_record.
diff --git a/lib/ssl/src/ssl_record.hrl b/lib/ssl/src/ssl_record.hrl
index 2fd17f9c35..c17fa53a62 100644
--- a/lib/ssl/src/ssl_record.hrl
+++ b/lib/ssl/src/ssl_record.hrl
@@ -29,6 +29,18 @@
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% Connection states - RFC 4346 section 6.1
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+-record(connection_state, {
+ security_parameters,
+ compression_state,
+ cipher_state,
+ mac_secret,
+ epoch, %% Only used by DTLS
+ sequence_number,
+ %% RFC 5746
+ secure_renegotiation,
+ client_verify_data,
+ server_verify_data
+ }).
-record(connection_states, {
current_read,
@@ -56,17 +68,7 @@
exportable % boolean
}).
--record(connection_state, {
- security_parameters,
- compression_state,
- cipher_state,
- mac_secret,
- sequence_number,
- %% RFC 5746
- secure_renegotiation,
- client_verify_data,
- server_verify_data
- }).
+-define(INITIAL_BYTES, 5).
-define(MAX_SEQENCE_NUMBER, 18446744073709552000). %% math:pow(2, 64) - 1 = 1.8446744073709552e19
%% Sequence numbers can not wrap so when max is about to be reached we should renegotiate.
diff --git a/lib/ssl/src/ssl_ssl2.erl b/lib/ssl/src/ssl_v2.erl
index a9ab6a2678..07876366f1 100644
--- a/lib/ssl/src/ssl_ssl2.erl
+++ b/lib/ssl/src/ssl_v2.erl
@@ -1,30 +1,30 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2007-2011. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2007-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%
%%
%%
%%----------------------------------------------------------------------
-%% Purpose: Handles sslv2 hello as clients supporting sslv2 and higher
+%% Purpose: Handles sslv2 hello as clients supporting sslv2 and higher
%% will send an sslv2 hello.
%%----------------------------------------------------------------------
--module(ssl_ssl2).
-
+-module(ssl_v2).
+
-export([client_random/2]).
client_random(ChallengeData, 32) ->
diff --git a/lib/ssl/src/ssl_ssl3.erl b/lib/ssl/src/ssl_v3.erl
index 013c27ebb5..d477b3df81 100644
--- a/lib/ssl/src/ssl_ssl3.erl
+++ b/lib/ssl/src/ssl_v3.erl
@@ -22,14 +22,14 @@
%% Purpose: Handles sslv3 encryption.
%%----------------------------------------------------------------------
--module(ssl_ssl3).
+-module(ssl_v3).
-include("ssl_cipher.hrl").
-include("ssl_internal.hrl").
-include("ssl_record.hrl"). % MD5 and SHA
-export([master_secret/3, finished/3, certificate_verify/3,
- mac_hash/6, setup_keys/7,
+ mac_hash/6, setup_keys/7,
suites/0]).
-compile(inline).
@@ -40,7 +40,7 @@
-spec master_secret(binary(), binary(), binary()) -> binary().
master_secret(PremasterSecret, ClientRandom, ServerRandom) ->
- %% draft-ietf-tls-ssl-version3-00 - 6.2.2
+ %% draft-ietf-tls-ssl-version3-00 - 6.2.2
%% key_block =
%% MD5(master_secret + SHA(`A' + master_secret +
%% ServerHello.random +
@@ -62,7 +62,7 @@ finished(Role, MasterSecret, Handshake) ->
%% opaque md5_hash[16];
%% opaque sha_hash[20];
%% } Finished;
- %%
+ %%
%% md5_hash MD5(master_secret + pad2 +
%% MD5(handshake_messages + Sender +
%% master_secret + pad1));
@@ -95,23 +95,23 @@ certificate_verify(sha, MasterSecret, Handshake) ->
handshake_hash(?SHA, MasterSecret, undefined, Handshake).
--spec mac_hash(integer(), binary(), integer(), integer(), integer(), binary()) -> binary().
+-spec mac_hash(integer(), binary(), integer(), integer(), integer(), binary()) -> binary().
mac_hash(Method, Mac_write_secret, Seq_num, Type, Length, Fragment) ->
- %% draft-ietf-tls-ssl-version3-00 - 5.2.3.1
+ %% draft-ietf-tls-ssl-version3-00 - 5.2.3.1
%% hash(MAC_write_secret + pad_2 +
%% hash(MAC_write_secret + pad_1 + seq_num +
%% SSLCompressed.type + SSLCompressed.length +
%% SSLCompressed.fragment));
- Mac = mac_hash(Method, Mac_write_secret,
- [<<?UINT64(Seq_num), ?BYTE(Type),
+ Mac = mac_hash(Method, Mac_write_secret,
+ [<<?UINT64(Seq_num), ?BYTE(Type),
?UINT16(Length)>>, Fragment]),
Mac.
--spec setup_keys(binary(), binary(), binary(),
- integer(), integer(), term(), integer()) ->
- {binary(), binary(), binary(),
- binary(), binary(), binary()}.
+-spec setup_keys(binary(), binary(), binary(),
+ integer(), integer(), term(), integer()) ->
+ {binary(), binary(), binary(),
+ binary(), binary(), binary()}.
setup_keys(MasterSecret, ServerRandom, ClientRandom, HS, KML, _EKML, IVS) ->
KeyBlock = generate_keyblock(MasterSecret, ServerRandom, ClientRandom,
@@ -133,7 +133,7 @@ setup_keys(MasterSecret, ServerRandom, ClientRandom, HS, KML, _EKML, IVS) ->
-spec suites() -> [cipher_suite()].
suites() ->
- [
+ [
?TLS_DHE_RSA_WITH_AES_256_CBC_SHA,
?TLS_DHE_DSS_WITH_AES_256_CBC_SHA,
?TLS_RSA_WITH_AES_256_CBC_SHA,
@@ -143,7 +143,7 @@ suites() ->
?TLS_DHE_RSA_WITH_AES_128_CBC_SHA,
?TLS_DHE_DSS_WITH_AES_128_CBC_SHA,
?TLS_RSA_WITH_AES_128_CBC_SHA,
- %%?TLS_RSA_WITH_IDEA_CBC_SHA,
+ %%?TLS_RSA_WITH_IDEA_CBC_SHA,
?TLS_RSA_WITH_RC4_128_SHA,
?TLS_RSA_WITH_RC4_128_MD5,
?TLS_RSA_WITH_DES_CBC_SHA
diff --git a/lib/ssl/src/tls_connection.erl b/lib/ssl/src/tls_connection.erl
index 0415ea6ecc..5618837506 100644
--- a/lib/ssl/src/tls_connection.erl
+++ b/lib/ssl/src/tls_connection.erl
@@ -106,10 +106,6 @@
base = ?DEFAULT_DIFFIE_HELLMAN_GENERATOR}).
-define(WAIT_TO_ALLOW_RENEGOTIATION, 12000).
--type state_name() :: hello | abbreviated | certify | cipher | connection.
--type gen_fsm_state_return() :: {next_state, state_name(), #state{}} |
- {next_state, state_name(), #state{}, timeout()} |
- {stop, term(), #state{}}.
%%====================================================================
%% Internal application API
@@ -367,7 +363,6 @@ hello(#hello_request{}, #state{role = client} = State0) ->
next_state(hello, hello, Record, State);
hello(#server_hello{cipher_suite = CipherSuite,
- hash_signs = HashSign,
compression_method = Compression} = Hello,
#state{session = #session{session_id = OldId},
connection_states = ConnectionStates0,
@@ -392,8 +387,6 @@ hello(#server_hello{cipher_suite = CipherSuite,
end,
State = State0#state{key_algorithm = KeyAlgorithm,
- hashsign_algorithm =
- negotiated_hashsign(HashSign, KeyAlgorithm, Version),
negotiated_version = Version,
connection_states = ConnectionStates,
premaster_secret = PremasterSecret,
@@ -410,27 +403,27 @@ hello(#server_hello{cipher_suite = CipherSuite,
end;
hello(Hello = #client_hello{client_version = ClientVersion,
- hash_signs = HashSigns},
+ 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 = Cache,
session_cache_cb = CacheCb,
ssl_options = SslOpts}) ->
-
- HashSign = tls_handshake:select_hashsign(HashSigns, Cert),
+ HashSign = ssl_handshake:select_hashsign(HashSigns, Cert),
case tls_handshake:hello(Hello, SslOpts, {Port, Session0, Cache, CacheCb,
ConnectionStates0, Cert}, Renegotiation) of
- {Version, {Type, #session{cipher_suite = CipherSuite} = Session}, ConnectionStates, ProtocolsToAdvertise,
- EcPointFormats, EllipticCurves} ->
- {KeyAlgorithm, _, _, _} = ssl_cipher:suite_definition(CipherSuite),
- NH = negotiated_hashsign(HashSign, KeyAlgorithm, Version),
- do_server_hello(Type, ProtocolsToAdvertise,
- EcPointFormats, EllipticCurves,
+ {Version, {Type, #session{cipher_suite = CipherSuite} = Session},
+ ConnectionStates,
+ #hello_extensions{ec_point_formats = EcPointFormats,
+ elliptic_curves = EllipticCurves} = ServerHelloExt} ->
+ {KeyAlg, _, _, _} = ssl_cipher:suite_definition(CipherSuite),
+ NegotiatedHashSign = negotiated_hashsign(HashSign, KeyAlg, Version),
+ do_server_hello(Type, ServerHelloExt,
State#state{connection_states = ConnectionStates,
negotiated_version = Version,
session = Session,
- hashsign_algorithm = NH,
+ hashsign_algorithm = NegotiatedHashSign,
client_ecc = {EllipticCurves, EcPointFormats}});
#alert{} = Alert ->
handle_own_alert(Alert, ClientVersion, hello, State)
@@ -456,11 +449,11 @@ abbreviated(#finished{verify_data = Data} = Finished,
session = #session{master_secret = MasterSecret},
connection_states = ConnectionStates0} =
State) ->
- case tls_handshake:verify_connection(Version, Finished, client,
+ case ssl_handshake:verify_connection(Version, Finished, client,
get_current_connection_state_prf(ConnectionStates0, write),
MasterSecret, Handshake) of
verified ->
- ConnectionStates = tls_record:set_client_verify_data(current_both, Data, ConnectionStates0),
+ ConnectionStates = ssl_record:set_client_verify_data(current_both, Data, ConnectionStates0),
next_state_connection(abbreviated,
ack_connection(State#state{connection_states = ConnectionStates}));
#alert{} = Alert ->
@@ -472,11 +465,11 @@ abbreviated(#finished{verify_data = Data} = Finished,
session = #session{master_secret = MasterSecret},
negotiated_version = Version,
connection_states = ConnectionStates0} = State) ->
- case tls_handshake:verify_connection(Version, Finished, server,
+ case ssl_handshake:verify_connection(Version, Finished, server,
get_pending_connection_state_prf(ConnectionStates0, write),
MasterSecret, Handshake0) of
verified ->
- ConnectionStates1 = tls_record:set_server_verify_data(current_read, Data, ConnectionStates0),
+ ConnectionStates1 = ssl_record:set_server_verify_data(current_read, Data, ConnectionStates0),
{ConnectionStates, Handshake} =
finalize_handshake(State#state{connection_states = ConnectionStates1}, abbreviated),
next_state_connection(abbreviated,
@@ -531,7 +524,7 @@ certify(#certificate{} = Cert,
cert_db = CertDbHandle,
cert_db_ref = CertDbRef,
ssl_options = Opts} = State) ->
- case tls_handshake:certify(Cert, CertDbHandle, CertDbRef, Opts#ssl_options.depth,
+ case ssl_handshake:certify(Cert, CertDbHandle, CertDbRef, Opts#ssl_options.depth,
Opts#ssl_options.verify,
Opts#ssl_options.verify_fun, Role) of
{PeerCert, PublicKeyInfo} ->
@@ -563,7 +556,7 @@ certify(#server_key_exchange{} = Msg,
certify(#certificate_request{hashsign_algorithms = HashSigns},
#state{session = #session{own_certificate = Cert}} = State0) ->
- HashSign = tls_handshake:select_hashsign(HashSigns, Cert),
+ HashSign = ssl_handshake:select_hashsign(HashSigns, Cert),
{Record, State} = next_record(State0#state{client_certificate_requested = true}),
next_state(certify, certify, Record, State#state{cert_hashsign_algorithm = HashSign});
@@ -613,7 +606,7 @@ certify(#server_hello_done{},
negotiated_version = Version,
premaster_secret = undefined,
role = client} = State0) ->
- case tls_handshake:master_secret(Version, Session,
+ case ssl_handshake:master_secret(tls_record, Version, Session,
ConnectionStates0, client) of
{MasterSecret, ConnectionStates} ->
State = State0#state{connection_states = ConnectionStates},
@@ -629,7 +622,7 @@ certify(#server_hello_done{},
negotiated_version = Version,
premaster_secret = PremasterSecret,
role = client} = State0) ->
- case tls_handshake:master_secret(Version, PremasterSecret,
+ case ssl_handshake:master_secret(tls_record, Version, PremasterSecret,
ConnectionStates0, client) of
{MasterSecret, ConnectionStates} ->
Session = Session0#session{master_secret = MasterSecret},
@@ -650,7 +643,7 @@ certify(#client_key_exchange{} = Msg,
certify(#client_key_exchange{exchange_keys = Keys},
State = #state{key_algorithm = KeyAlg, negotiated_version = Version}) ->
try
- certify_client_key_exchange(tls_handshake:decode_client_key(Keys, KeyAlg, Version), State)
+ certify_client_key_exchange(ssl_handshake:decode_client_key(Keys, KeyAlg, Version), State)
catch
#alert{} = Alert ->
handle_own_alert(Alert, Version, certify, State)
@@ -668,8 +661,8 @@ certify_client_key_exchange(#encrypted_premaster_secret{premaster_secret= EncPMS
connection_states = ConnectionStates0,
session = Session0,
private_key = Key} = State0) ->
- PremasterSecret = tls_handshake:decrypt_premaster_secret(EncPMS, Key),
- case tls_handshake:master_secret(Version, PremasterSecret,
+ PremasterSecret = ssl_handshake:decrypt_premaster_secret(EncPMS, Key),
+ case ssl_handshake:master_secret(tls_record, Version, PremasterSecret,
ConnectionStates0, server) of
{MasterSecret, ConnectionStates} ->
Session = Session0#session{master_secret = MasterSecret},
@@ -735,7 +728,7 @@ certify_client_key_exchange(#client_rsa_psk_identity{
#encrypted_premaster_secret{premaster_secret= EncPMS}},
#state{negotiated_version = Version,
private_key = Key} = State0) ->
- PremasterSecret = tls_handshake:decrypt_premaster_secret(EncPMS, Key),
+ PremasterSecret = ssl_handshake:decrypt_premaster_secret(EncPMS, Key),
case server_rsa_psk_master_secret(PskIdentity, PremasterSecret, State0) of
#state{} = State1 ->
{Record, State} = next_record(State1),
@@ -774,8 +767,8 @@ cipher(#certificate_verify{signature = Signature, hashsign_algorithm = CertHashS
tls_handshake_history = Handshake
} = State0) ->
- HashSign = tls_handshake:select_cert_hashsign(CertHashSign, Algo, Version),
- case tls_handshake:certificate_verify(Signature, PublicKeyInfo,
+ HashSign = ssl_handshake:select_cert_hashsign(CertHashSign, Algo, Version),
+ case ssl_handshake:certificate_verify(Signature, PublicKeyInfo,
Version, HashSign, MasterSecret, Handshake) of
valid ->
{Record, State} = next_record(State0),
@@ -798,7 +791,7 @@ cipher(#finished{verify_data = Data} = Finished,
= Session0,
connection_states = ConnectionStates0,
tls_handshake_history = Handshake0} = State) ->
- case tls_handshake:verify_connection(Version, Finished,
+ case ssl_handshake:verify_connection(Version, Finished,
opposite_role(Role),
get_current_connection_state_prf(ConnectionStates0, read),
MasterSecret, Handshake0) of
@@ -1033,7 +1026,7 @@ handle_sync_event({prf, Secret, Label, Seed, WantedLength}, _, StateName,
#state{connection_states = ConnectionStates,
negotiated_version = Version} = State) ->
ConnectionState =
- tls_record:current_connection_state(ConnectionStates, read),
+ ssl_record:current_connection_state(ConnectionStates, read),
SecParams = ConnectionState#connection_state.security_parameters,
#security_parameters{master_secret = MasterSecret,
client_random = ClientRandom,
@@ -1048,7 +1041,7 @@ handle_sync_event({prf, Secret, Label, Seed, WantedLength}, _, StateName,
(client_random, Acc) -> [ClientRandom|Acc];
(server_random, Acc) -> [ServerRandom|Acc]
end, [], Seed)),
- tls_handshake:prf(Version, SecretToUse, Label, SeedToUse, WantedLength)
+ ssl_handshake:prf(Version, SecretToUse, Label, SeedToUse, WantedLength)
catch
exit:_ -> {error, badarg};
error:Reason -> {error, Reason}
@@ -1414,7 +1407,7 @@ certify_client(#state{client_certificate_requested = true, role = client,
session = #session{own_certificate = OwnCert},
socket = Socket,
tls_handshake_history = Handshake0} = State) ->
- Certificate = tls_handshake:certificate(OwnCert, CertDbHandle, CertDbRef, client),
+ Certificate = ssl_handshake:certificate(OwnCert, CertDbHandle, CertDbRef, client),
{BinCert, ConnectionStates, Handshake} =
encode_handshake(Certificate, Version, ConnectionStates0, Handshake0),
Transport:send(Socket, BinCert),
@@ -1434,7 +1427,7 @@ verify_client_cert(#state{client_certificate_requested = true, role = client,
cert_hashsign_algorithm = HashSign,
tls_handshake_history = Handshake0} = State) ->
- case tls_handshake:client_certificate_verify(OwnCert, MasterSecret,
+ case ssl_handshake:client_certificate_verify(OwnCert, MasterSecret,
Version, HashSign, PrivateKey, Handshake0) of
#certificate_verify{} = Verified ->
{BinVerified, ConnectionStates, Handshake} =
@@ -1451,21 +1444,17 @@ verify_client_cert(#state{client_certificate_requested = true, role = client,
verify_client_cert(#state{client_certificate_requested = false} = State) ->
State.
-do_server_hello(Type, NextProtocolsToSend,
- EcPointFormats, EllipticCurves,
+do_server_hello(Type, #hello_extensions{next_protocol_negotiation = NextProtocols} = ServerHelloExt,
#state{negotiated_version = Version,
session = #session{session_id = SessId},
- connection_states = ConnectionStates0,
- renegotiation = {Renegotiation, _}}
+ connection_states = ConnectionStates0}
= State0) when is_atom(Type) ->
ServerHello =
- tls_handshake:server_hello(SessId, Version,
- ConnectionStates0, Renegotiation,
- NextProtocolsToSend, EcPointFormats, EllipticCurves),
+ tls_handshake:server_hello(SessId, Version, ConnectionStates0, ServerHelloExt),
State = server_hello(ServerHello,
State0#state{expecting_next_protocol_negotiation =
- NextProtocolsToSend =/= undefined}),
+ NextProtocols =/= undefined}),
case Type of
new ->
new_server_hello(ServerHello, State);
@@ -1496,7 +1485,7 @@ resumed_server_hello(#state{session = Session,
connection_states = ConnectionStates0,
negotiated_version = Version} = State0) ->
- case tls_handshake:master_secret(Version, Session,
+ case ssl_handshake:master_secret(tls_record, Version, Session,
ConnectionStates0, server) of
{_, ConnectionStates1} ->
State1 = State0#state{connection_states = ConnectionStates1,
@@ -1525,7 +1514,7 @@ handle_resumed_session(SessId, #state{connection_states = ConnectionStates0,
session_cache = Cache,
session_cache_cb = CacheCb} = State0) ->
Session = CacheCb:lookup(Cache, {{Host, Port}, SessId}),
- case tls_handshake:master_secret(Version, Session,
+ case ssl_handshake:master_secret(tls_record, Version, Session,
ConnectionStates0, client) of
{_, ConnectionStates} ->
{Record, State} =
@@ -1584,7 +1573,7 @@ server_hello_done(#state{transport_cb = Transport,
connection_states = ConnectionStates0,
tls_handshake_history = Handshake0} = State) ->
- HelloDone = tls_handshake:server_hello_done(),
+ HelloDone = ssl_handshake:server_hello_done(),
{BinHelloDone, ConnectionStates, Handshake} =
encode_handshake(HelloDone, Version, ConnectionStates0, Handshake0),
@@ -1604,7 +1593,7 @@ certify_server(#state{transport_cb = Transport,
cert_db = CertDbHandle,
cert_db_ref = CertDbRef,
session = #session{own_certificate = OwnCert}} = State) ->
- case tls_handshake:certificate(OwnCert, CertDbHandle, CertDbRef, server) of
+ case ssl_handshake:certificate(OwnCert, CertDbHandle, CertDbRef, server) of
CertMsg = #certificate{} ->
{BinCertMsg, ConnectionStates, Handshake} =
encode_handshake(CertMsg, Version, ConnectionStates0, Handshake0),
@@ -1633,11 +1622,11 @@ key_exchange(#state{role = server, key_algorithm = Algo,
Algo == dh_anon ->
DHKeys = public_key:generate_key(Params),
ConnectionState =
- tls_record:pending_connection_state(ConnectionStates0, read),
+ ssl_record:pending_connection_state(ConnectionStates0, read),
SecParams = ConnectionState#connection_state.security_parameters,
#security_parameters{client_random = ClientRandom,
server_random = ServerRandom} = SecParams,
- Msg = tls_handshake:key_exchange(server, Version, {dh, DHKeys, Params,
+ Msg = ssl_handshake:key_exchange(server, Version, {dh, DHKeys, Params,
HashSignAlgo, ClientRandom,
ServerRandom,
PrivateKey}),
@@ -1665,11 +1654,11 @@ key_exchange(#state{role = server, key_algorithm = Algo,
ECDHKeys = public_key:generate_key(select_curve(State)),
ConnectionState =
- tls_record:pending_connection_state(ConnectionStates0, read),
+ ssl_record:pending_connection_state(ConnectionStates0, read),
SecParams = ConnectionState#connection_state.security_parameters,
#security_parameters{client_random = ClientRandom,
server_random = ServerRandom} = SecParams,
- Msg = tls_handshake:key_exchange(server, Version, {ecdh, ECDHKeys,
+ Msg = ssl_handshake:key_exchange(server, Version, {ecdh, ECDHKeys,
HashSignAlgo, ClientRandom,
ServerRandom,
PrivateKey}),
@@ -1694,11 +1683,11 @@ key_exchange(#state{role = server, key_algorithm = psk,
transport_cb = Transport
} = State) ->
ConnectionState =
- tls_record:pending_connection_state(ConnectionStates0, read),
+ ssl_record:pending_connection_state(ConnectionStates0, read),
SecParams = ConnectionState#connection_state.security_parameters,
#security_parameters{client_random = ClientRandom,
server_random = ServerRandom} = SecParams,
- Msg = tls_handshake:key_exchange(server, Version, {psk, PskIdentityHint,
+ Msg = ssl_handshake:key_exchange(server, Version, {psk, PskIdentityHint,
HashSignAlgo, ClientRandom,
ServerRandom,
PrivateKey}),
@@ -1721,11 +1710,11 @@ key_exchange(#state{role = server, key_algorithm = dhe_psk,
} = State) ->
DHKeys = public_key:generate_key(Params),
ConnectionState =
- tls_record:pending_connection_state(ConnectionStates0, read),
+ ssl_record:pending_connection_state(ConnectionStates0, read),
SecParams = ConnectionState#connection_state.security_parameters,
#security_parameters{client_random = ClientRandom,
server_random = ServerRandom} = SecParams,
- Msg = tls_handshake:key_exchange(server, Version, {dhe_psk, PskIdentityHint, DHKeys, Params,
+ Msg = ssl_handshake:key_exchange(server, Version, {dhe_psk, PskIdentityHint, DHKeys, Params,
HashSignAlgo, ClientRandom,
ServerRandom,
PrivateKey}),
@@ -1750,11 +1739,11 @@ key_exchange(#state{role = server, key_algorithm = rsa_psk,
transport_cb = Transport
} = State) ->
ConnectionState =
- tls_record:pending_connection_state(ConnectionStates0, read),
+ ssl_record:pending_connection_state(ConnectionStates0, read),
SecParams = ConnectionState#connection_state.security_parameters,
#security_parameters{client_random = ClientRandom,
server_random = ServerRandom} = SecParams,
- Msg = tls_handshake:key_exchange(server, Version, {psk, PskIdentityHint,
+ Msg = ssl_handshake:key_exchange(server, Version, {psk, PskIdentityHint,
HashSignAlgo, ClientRandom,
ServerRandom,
PrivateKey}),
@@ -1786,11 +1775,11 @@ key_exchange(#state{role = server, key_algorithm = Algo,
Keys0
end,
ConnectionState =
- tls_record:pending_connection_state(ConnectionStates0, read),
+ ssl_record:pending_connection_state(ConnectionStates0, read),
SecParams = ConnectionState#connection_state.security_parameters,
#security_parameters{client_random = ClientRandom,
server_random = ServerRandom} = SecParams,
- Msg = tls_handshake:key_exchange(server, Version, {srp, Keys, SrpParams,
+ Msg = ssl_handshake:key_exchange(server, Version, {srp, Keys, SrpParams,
HashSignAlgo, ClientRandom,
ServerRandom,
PrivateKey}),
@@ -1826,7 +1815,7 @@ key_exchange(#state{role = client,
when Algorithm == dhe_dss;
Algorithm == dhe_rsa;
Algorithm == dh_anon ->
- Msg = tls_handshake:key_exchange(client, Version, {dh, DhPubKey}),
+ Msg = ssl_handshake:key_exchange(client, Version, {dh, DhPubKey}),
{BinMsg, ConnectionStates, Handshake} =
encode_handshake(Msg, Version, ConnectionStates0, Handshake0),
Transport:send(Socket, BinMsg),
@@ -1843,7 +1832,7 @@ key_exchange(#state{role = client,
when Algorithm == ecdhe_ecdsa; Algorithm == ecdhe_rsa;
Algorithm == ecdh_ecdsa; Algorithm == ecdh_rsa;
Algorithm == ecdh_anon ->
- Msg = tls_handshake:key_exchange(client, Version, {ecdh, Keys}),
+ Msg = ssl_handshake:key_exchange(client, Version, {ecdh, Keys}),
{BinMsg, ConnectionStates, Handshake} =
encode_handshake(Msg, Version, ConnectionStates0, Handshake0),
Transport:send(Socket, BinMsg),
@@ -1857,7 +1846,7 @@ key_exchange(#state{role = client,
negotiated_version = Version,
socket = Socket, transport_cb = Transport,
tls_handshake_history = Handshake0} = State) ->
- Msg = tls_handshake:key_exchange(client, Version, {psk, SslOpts#ssl_options.psk_identity}),
+ Msg = ssl_handshake:key_exchange(client, Version, {psk, SslOpts#ssl_options.psk_identity}),
{BinMsg, ConnectionStates, Handshake} =
encode_handshake(Msg, Version, ConnectionStates0, Handshake0),
Transport:send(Socket, BinMsg),
@@ -1872,7 +1861,7 @@ key_exchange(#state{role = client,
diffie_hellman_keys = {DhPubKey, _},
socket = Socket, transport_cb = Transport,
tls_handshake_history = Handshake0} = State) ->
- Msg = tls_handshake:key_exchange(client, Version, {dhe_psk, SslOpts#ssl_options.psk_identity, DhPubKey}),
+ Msg = ssl_handshake:key_exchange(client, Version, {dhe_psk, SslOpts#ssl_options.psk_identity, DhPubKey}),
{BinMsg, ConnectionStates, Handshake} =
encode_handshake(Msg, Version, ConnectionStates0, Handshake0),
Transport:send(Socket, BinMsg),
@@ -1905,7 +1894,7 @@ key_exchange(#state{role = client,
when Algorithm == srp_dss;
Algorithm == srp_rsa;
Algorithm == srp_anon ->
- Msg = tls_handshake:key_exchange(client, Version, {srp, ClientPubKey}),
+ Msg = ssl_handshake:key_exchange(client, Version, {srp, ClientPubKey}),
{BinMsg, ConnectionStates, Handshake} =
encode_handshake(Msg, Version, ConnectionStates0, Handshake0),
Transport:send(Socket, BinMsg),
@@ -1922,7 +1911,7 @@ rsa_key_exchange(Version, PremasterSecret, PublicKeyInfo = {Algorithm, _, _})
Algorithm == ?sha384WithRSAEncryption;
Algorithm == ?sha512WithRSAEncryption
->
- tls_handshake:key_exchange(client, Version,
+ ssl_handshake:key_exchange(client, Version,
{premaster_secret, PremasterSecret,
PublicKeyInfo});
rsa_key_exchange(_, _, _) ->
@@ -1938,7 +1927,7 @@ rsa_psk_key_exchange(Version, PskIdentity, PremasterSecret, PublicKeyInfo = {Alg
Algorithm == ?sha384WithRSAEncryption;
Algorithm == ?sha512WithRSAEncryption
->
- tls_handshake:key_exchange(client, Version,
+ ssl_handshake:key_exchange(client, Version,
{psk_premaster_secret, PskIdentity, PremasterSecret,
PublicKeyInfo});
rsa_psk_key_exchange(_, _, _, _) ->
@@ -1952,7 +1941,11 @@ request_client_cert(#state{ssl_options = #ssl_options{verify = verify_peer},
negotiated_version = Version,
socket = Socket,
transport_cb = Transport} = State) ->
- Msg = tls_handshake:certificate_request(ConnectionStates0, CertDbHandle, CertDbRef, Version),
+ #connection_state{security_parameters =
+ #security_parameters{cipher_suite = CipherSuite}} =
+ ssl_record:pending_connection_state(ConnectionStates0, read),
+ Msg = ssl_handshake:certificate_request(CipherSuite, CertDbHandle, CertDbRef, Version),
+
{BinMsg, ConnectionStates, Handshake} =
encode_handshake(Msg, Version, ConnectionStates0, Handshake0),
Transport:send(Socket, BinMsg),
@@ -1967,7 +1960,7 @@ finalize_handshake(State, StateName) ->
ConnectionStates0 = cipher_protocol(State),
ConnectionStates =
- tls_record:activate_pending_connection_state(ConnectionStates0,
+ ssl_record:activate_pending_connection_state(ConnectionStates0,
write),
State1 = State#state{connection_states = ConnectionStates},
@@ -1985,7 +1978,7 @@ next_protocol(#state{transport_cb = Transport, socket = Socket,
next_protocol = NextProtocol,
connection_states = ConnectionStates0,
tls_handshake_history = Handshake0} = State) ->
- NextProtocolMessage = tls_handshake:next_protocol(NextProtocol),
+ NextProtocolMessage = ssl_handshake:next_protocol(NextProtocol),
{BinMsg, ConnectionStates, Handshake} = encode_handshake(NextProtocolMessage, Version, ConnectionStates0, Handshake0),
Transport:send(Socket, BinMsg),
State#state{connection_states = ConnectionStates,
@@ -2007,7 +2000,7 @@ finished(#state{role = Role, socket = Socket, negotiated_version = Version,
connection_states = ConnectionStates0,
tls_handshake_history = Handshake0}, StateName) ->
MasterSecret = Session#session.master_secret,
- Finished = tls_handshake:finished(Version, Role,
+ Finished = ssl_handshake:finished(Version, Role,
get_current_connection_state_prf(ConnectionStates0, write),
MasterSecret, Handshake0),
ConnectionStates1 = save_verify_data(Role, Finished, ConnectionStates0, StateName),
@@ -2017,18 +2010,19 @@ finished(#state{role = Role, socket = Socket, negotiated_version = Version,
{ConnectionStates, Handshake}.
save_verify_data(client, #finished{verify_data = Data}, ConnectionStates, certify) ->
- tls_record:set_client_verify_data(current_write, Data, ConnectionStates);
+ ssl_record:set_client_verify_data(current_write, Data, ConnectionStates);
save_verify_data(server, #finished{verify_data = Data}, ConnectionStates, cipher) ->
- tls_record:set_server_verify_data(current_both, Data, ConnectionStates);
+ ssl_record:set_server_verify_data(current_both, Data, ConnectionStates);
save_verify_data(client, #finished{verify_data = Data}, ConnectionStates, abbreviated) ->
- tls_record:set_client_verify_data(current_both, Data, ConnectionStates);
+ ssl_record:set_client_verify_data(current_both, Data, ConnectionStates);
save_verify_data(server, #finished{verify_data = Data}, ConnectionStates, abbreviated) ->
- tls_record:set_server_verify_data(current_write, Data, ConnectionStates).
+ ssl_record:set_server_verify_data(current_write, Data, ConnectionStates).
handle_server_key(#server_key_exchange{exchange_keys = Keys},
#state{key_algorithm = KeyAlg,
negotiated_version = Version} = State) ->
- Params = tls_handshake:decode_server_key(Keys, KeyAlg, Version),
+
+ Params = ssl_handshake:decode_server_key(Keys, KeyAlg, Version),
HashSign = negotiated_hashsign(Params#server_key_params.hashsign, KeyAlg, Version),
case is_anonymous(KeyAlg) of
true ->
@@ -2046,15 +2040,15 @@ verify_server_key(#server_key_params{params = Params,
public_key_info = PubKeyInfo,
connection_states = ConnectionStates} = State) ->
ConnectionState =
- tls_record:pending_connection_state(ConnectionStates, read),
+ ssl_record:pending_connection_state(ConnectionStates, read),
SecParams = ConnectionState#connection_state.security_parameters,
#security_parameters{client_random = ClientRandom,
server_random = ServerRandom} = SecParams,
- Hash = tls_handshake:server_key_exchange_hash(HashAlgo,
+ Hash = ssl_handshake:server_key_exchange_hash(HashAlgo,
<<ClientRandom/binary,
ServerRandom/binary,
EncParams/binary>>),
- case tls_handshake:verify_signature(Version, Hash, HashSign, Signature, PubKeyInfo) of
+ case ssl_handshake:verify_signature(Version, Hash, HashSign, Signature, PubKeyInfo) of
true ->
server_master_secret(Params, State);
false ->
@@ -2090,7 +2084,7 @@ master_from_premaster_secret(PremasterSecret,
#state{session = Session,
negotiated_version = Version, role = Role,
connection_states = ConnectionStates0} = State) ->
- case tls_handshake:master_secret(Version, PremasterSecret,
+ case ssl_handshake:master_secret(tls_record, Version, PremasterSecret,
ConnectionStates0, Role) of
{MasterSecret, ConnectionStates} ->
State#state{
@@ -2243,12 +2237,12 @@ client_srp_master_secret(Generator, Prime, Salt, ServerPub, ClientKeys,
end.
cipher_role(client, Data, Session, #state{connection_states = ConnectionStates0} = State) ->
- ConnectionStates = tls_record:set_server_verify_data(current_both, Data, ConnectionStates0),
+ ConnectionStates = ssl_record:set_server_verify_data(current_both, Data, ConnectionStates0),
next_state_connection(cipher, ack_connection(State#state{session = Session,
connection_states = ConnectionStates}));
cipher_role(server, Data, Session, #state{connection_states = ConnectionStates0} = State) ->
- ConnectionStates1 = tls_record:set_client_verify_data(current_read, Data, ConnectionStates0),
+ ConnectionStates1 = ssl_record:set_client_verify_data(current_read, Data, ConnectionStates0),
{ConnectionStates, Handshake} =
finalize_handshake(State#state{connection_states = ConnectionStates1,
session = Session}, cipher),
@@ -2258,16 +2252,16 @@ cipher_role(server, Data, Session, #state{connection_states = ConnectionStates0
tls_handshake_history =
Handshake})).
encode_alert(#alert{} = Alert, Version, ConnectionStates) ->
- tls_record:encode_alert_record(Alert, Version, ConnectionStates).
+ ssl_record:encode_alert_record(Alert, Version, ConnectionStates).
encode_change_cipher(#change_cipher_spec{}, Version, ConnectionStates) ->
- tls_record:encode_change_cipher_spec(Version, ConnectionStates).
+ ssl_record:encode_change_cipher_spec(Version, ConnectionStates).
encode_handshake(HandshakeRec, Version, ConnectionStates0, Handshake0) ->
Frag = tls_handshake:encode_handshake(HandshakeRec, Version),
Handshake1 = tls_handshake:update_handshake_history(Handshake0, Frag),
{E, ConnectionStates1} =
- tls_record:encode_handshake(Frag, Version, ConnectionStates0),
+ ssl_record:encode_handshake(Frag, Version, ConnectionStates0),
{E, ConnectionStates1, Handshake1}.
encode_packet(Data, #socket_options{packet=Packet}) ->
@@ -2362,7 +2356,7 @@ write_application_data(Data0, From, #state{socket = Socket,
renegotiate(State#state{send_queue = queue:in_r({From, Data}, SendQueue),
renegotiation = {true, internal}});
false ->
- {Msgs, ConnectionStates} = tls_record:encode_data(Data, Version, ConnectionStates0),
+ {Msgs, ConnectionStates} = ssl_record:encode_data(Data, Version, ConnectionStates0),
Result = Transport:send(Socket, Msgs),
{reply, Result,
connection, State#state{connection_states = ConnectionStates}, get_timeout(State)}
@@ -2567,7 +2561,7 @@ next_state(Current, Next, #ssl_tls{type = ?CHANGE_CIPHER_SPEC, fragment = <<1>>}
_ChangeCipher,
#state{connection_states = ConnectionStates0} = State0) ->
ConnectionStates1 =
- tls_record:activate_pending_connection_state(ConnectionStates0, read),
+ 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) ->
@@ -2619,7 +2613,7 @@ next_state_connection(StateName, #state{send_queue = Queue0,
case queue:out(Queue0) of
{{value, {From, Data}}, Queue} ->
{Msgs, ConnectionStates} =
- tls_record:encode_data(Data, Version, ConnectionStates0),
+ ssl_record:encode_data(Data, Version, ConnectionStates0),
Result = Transport:send(Socket, Msgs),
gen_fsm:reply(From, Result),
next_state_connection(StateName,
@@ -2664,7 +2658,7 @@ invalidate_session(server, _, Port, Session) ->
initial_state(Role, Host, Port, Socket, {SSLOptions, SocketOptions}, User,
{CbModule, DataTag, CloseTag, ErrorTag}) ->
- ConnectionStates = tls_record:init_connection_states(Role),
+ ConnectionStates = ssl_record:init_connection_states(Role),
SessionCacheCb = case application:get_env(ssl, session_cb) of
{ok, Cb} when is_atom(Cb) ->
@@ -2926,11 +2920,11 @@ renegotiate(#state{role = server,
transport_cb = Transport,
negotiated_version = Version,
connection_states = ConnectionStates0} = State0) ->
- HelloRequest = tls_handshake:hello_request(),
+ HelloRequest = ssl_handshake:hello_request(),
Frag = tls_handshake:encode_handshake(HelloRequest, Version),
Hs0 = tls_handshake:init_handshake_history(),
{BinMsg, ConnectionStates} =
- tls_record:encode_handshake(Frag, Version, ConnectionStates0),
+ ssl_record:encode_handshake(Frag, Version, ConnectionStates0),
Transport:send(Socket, BinMsg),
{Record, State} = next_record(State0#state{connection_states =
ConnectionStates,
@@ -3005,12 +2999,45 @@ handle_trusted_certs_db(#state{cert_db_ref = Ref,
end.
get_current_connection_state_prf(CStates, Direction) ->
- CS = tls_record:current_connection_state(CStates, Direction),
+ CS = ssl_record:current_connection_state(CStates, Direction),
CS#connection_state.security_parameters#security_parameters.prf_algorithm.
get_pending_connection_state_prf(CStates, Direction) ->
- CS = tls_record:pending_connection_state(CStates, Direction),
+ CS = ssl_record:pending_connection_state(CStates, Direction),
CS#connection_state.security_parameters#security_parameters.prf_algorithm.
+start_or_recv_cancel_timer(infinity, _RecvFrom) ->
+ undefined;
+start_or_recv_cancel_timer(Timeout, RecvFrom) ->
+ erlang:send_after(Timeout, self(), {cancel_start_or_recv, RecvFrom}).
+
+cancel_timer(undefined) ->
+ ok;
+cancel_timer(Timer) ->
+ erlang:cancel_timer(Timer),
+ ok.
+
+handle_unrecv_data(StateName, #state{socket = Socket, transport_cb = Transport} = State) ->
+ ssl_socket:setopts(Transport, Socket, [{active, false}]),
+ case Transport:recv(Socket, 0, 0) of
+ {error, closed} ->
+ ok;
+ {ok, Data} ->
+ handle_close_alert(Data, StateName, State)
+ end.
+
+handle_close_alert(Data, StateName, State0) ->
+ case next_tls_record(Data, State0) of
+ {#ssl_tls{type = ?ALERT, fragment = EncAlerts}, State} ->
+ [Alert|_] = decode_alerts(EncAlerts),
+ handle_normal_shutdown(Alert, StateName, State);
+ _ ->
+ ok
+ end.
+negotiated_hashsign(undefined, Algo, Version) ->
+ default_hashsign(Version, Algo);
+negotiated_hashsign(HashSign = {_, _}, _, _) ->
+ HashSign.
+
%% RFC 5246, Sect. 7.4.1.4.1. Signature Algorithms
%% If the client does not send the signature_algorithms extension, the
%% server MUST do the following:
@@ -3025,11 +3052,6 @@ get_pending_connection_state_prf(CStates, Direction) ->
%% - If the negotiated key exchange algorithm is one of (ECDH_ECDSA,
%% ECDHE_ECDSA), behave as if the client had sent value {sha1,ecdsa}.
-negotiated_hashsign(undefined, Algo, Version) ->
- default_hashsign(Version, Algo);
-negotiated_hashsign(HashSign = {_, _}, _, _) ->
- HashSign.
-
default_hashsign(_Version = {Major, Minor}, KeyExchange)
when Major >= 3 andalso Minor >= 3 andalso
(KeyExchange == rsa orelse
@@ -3065,35 +3087,6 @@ default_hashsign(_Version, KeyExchange)
KeyExchange == srp_anon ->
{null, anon}.
-start_or_recv_cancel_timer(infinity, _RecvFrom) ->
- undefined;
-start_or_recv_cancel_timer(Timeout, RecvFrom) ->
- erlang:send_after(Timeout, self(), {cancel_start_or_recv, RecvFrom}).
-
-cancel_timer(undefined) ->
- ok;
-cancel_timer(Timer) ->
- erlang:cancel_timer(Timer),
- ok.
-
-handle_unrecv_data(StateName, #state{socket = Socket, transport_cb = Transport} = State) ->
- ssl_socket:setopts(Transport, Socket, [{active, false}]),
- case Transport:recv(Socket, 0, 0) of
- {error, closed} ->
- ok;
- {ok, Data} ->
- handle_close_alert(Data, StateName, State)
- end.
-
-handle_close_alert(Data, StateName, State0) ->
- case next_tls_record(Data, State0) of
- {#ssl_tls{type = ?ALERT, fragment = EncAlerts}, State} ->
- [Alert|_] = decode_alerts(EncAlerts),
- handle_normal_shutdown(Alert, StateName, State);
- _ ->
- ok
- end.
-
select_curve(#state{client_ecc = {[Curve|_], _}}) ->
{namedCurve, Curve};
select_curve(_) ->
diff --git a/lib/ssl/src/tls_handshake.erl b/lib/ssl/src/tls_handshake.erl
index 6cc6e9e885..02bfa69fc5 100644
--- a/lib/ssl/src/tls_handshake.erl
+++ b/lib/ssl/src/tls_handshake.erl
@@ -18,7 +18,8 @@
%%
%%----------------------------------------------------------------------
-%% Purpose: Help funtions for handling the SSL-handshake protocol
+%% Purpose: Help funtions for handling the TLS (specific parts of)
+%%% SSL/TLS/DTLS handshake protocol
%%----------------------------------------------------------------------
-module(tls_handshake).
@@ -31,24 +32,9 @@
-include("ssl_srp.hrl").
-include_lib("public_key/include/public_key.hrl").
--export([master_secret/4, client_hello/8, server_hello/7, hello/4,
- hello_request/0, certify/7, certificate/4,
- client_certificate_verify/6, certificate_verify/6, verify_signature/5,
- certificate_request/4, key_exchange/3, server_key_exchange_hash/2,
- finished/5, verify_connection/6, get_tls_handshake/3,
- decode_client_key/3, decode_server_key/3, server_hello_done/0,
- encode_handshake/2, init_handshake_history/0, update_handshake_history/2,
- decrypt_premaster_secret/2, prf/5, next_protocol/1, select_hashsign/2,
- select_cert_hashsign/3]).
-
--export([dec_hello_extensions/2]).
-
--type tls_handshake() :: #client_hello{} | #server_hello{} |
- #server_hello_done{} | #certificate{} | #certificate_request{} |
- #client_key_exchange{} | #finished{} | #certificate_verify{} |
- #hello_request{} | #next_protocol{}.
-
--define(NAMED_CURVE_TYPE, 3).
+-export([client_hello/8, server_hello/4, hello/4,
+ get_tls_handshake/3, encode_handshake/2, decode_handshake/3,
+ init_handshake_history/0, update_handshake_history/2]).
%%====================================================================
%% Internal application API
@@ -66,515 +52,105 @@ client_hello(Host, Port, ConnectionStates,
} = SslOpts,
Cache, CacheCb, Renegotiation, OwnCert) ->
Version = tls_record:highest_protocol_version(Versions),
- Pending = tls_record:pending_connection_state(ConnectionStates, read),
+ Pending = ssl_record:pending_connection_state(ConnectionStates, read),
SecParams = Pending#connection_state.security_parameters,
- Ciphers = available_suites(UserSuites, Version),
- SRP = srp_user(SslOpts),
- {EcPointFormats, EllipticCurves} = default_ecc_extensions(Version),
+ CipherSuites = ssl_handshake:available_suites(UserSuites, Version),
+
+ Extensions = ssl_handshake:client_hello_extensions(Version, CipherSuites,
+ SslOpts, ConnectionStates, Renegotiation),
Id = ssl_session:client_id({Host, Port, SslOpts}, Cache, CacheCb, OwnCert),
#client_hello{session_id = Id,
client_version = Version,
- cipher_suites = cipher_suites(Ciphers, Renegotiation),
- compression_methods = tls_record:compressions(),
+ cipher_suites = ssl_handshake:cipher_suites(CipherSuites, Renegotiation),
+ compression_methods = ssl_record:compressions(),
random = SecParams#security_parameters.client_random,
-
- renegotiation_info =
- renegotiation_info(client, ConnectionStates, Renegotiation),
- srp = SRP,
- hash_signs = advertised_hash_signs(Version),
- ec_point_formats = EcPointFormats,
- elliptic_curves = EllipticCurves,
- next_protocol_negotiation =
- encode_client_protocol_negotiation(SslOpts#ssl_options.next_protocol_selector, Renegotiation)
+ extensions = Extensions
}.
-encode_protocol(Protocol, Acc) ->
- Len = byte_size(Protocol),
- <<Acc/binary, ?BYTE(Len), Protocol/binary>>.
-
-encode_protocols_advertised_on_server(undefined) ->
- undefined;
-
-encode_protocols_advertised_on_server(Protocols) ->
- #next_protocol_negotiation{extension_data = lists:foldl(fun encode_protocol/2, <<>>, Protocols)}.
-
%%--------------------------------------------------------------------
--spec server_hello(session_id(), tls_version(), #connection_states{},
- boolean(), [binary()] | undefined,
- #ec_point_formats{} | undefined,
- #elliptic_curves{} | undefined) -> #server_hello{}.
+-spec server_hello(#session{}, tls_version(), #connection_states{},
+ #hello_extensions{}) -> #server_hello{}.
%%
%% Description: Creates a server hello message.
%%--------------------------------------------------------------------
-server_hello(SessionId, Version, ConnectionStates, Renegotiation,
- ProtocolsAdvertisedOnServer, EcPointFormats, EllipticCurves) ->
- Pending = tls_record:pending_connection_state(ConnectionStates, read),
+server_hello(SessionId, Version, ConnectionStates, Extensions) ->
+ Pending = ssl_record:pending_connection_state(ConnectionStates, read),
SecParams = Pending#connection_state.security_parameters,
+
#server_hello{server_version = Version,
cipher_suite = SecParams#security_parameters.cipher_suite,
compression_method =
SecParams#security_parameters.compression_algorithm,
random = SecParams#security_parameters.server_random,
session_id = SessionId,
- renegotiation_info =
- renegotiation_info(server, ConnectionStates, Renegotiation),
- ec_point_formats = EcPointFormats,
- elliptic_curves = EllipticCurves,
- next_protocol_negotiation = encode_protocols_advertised_on_server(ProtocolsAdvertisedOnServer)
+ extensions = Extensions
}.
%%--------------------------------------------------------------------
--spec hello_request() -> #hello_request{}.
-%%
-%% Description: Creates a hello request message sent by server to
-%% trigger renegotiation.
-%%--------------------------------------------------------------------
-hello_request() ->
- #hello_request{}.
-
-%%--------------------------------------------------------------------
-spec hello(#server_hello{} | #client_hello{}, #ssl_options{},
#connection_states{} | {inet:port_number(), #session{}, db_handle(),
- atom(), #connection_states{}, binary()},
+ atom(), #connection_states{}, binary() | undefined},
boolean()) ->
{tls_version(), session_id(), #connection_states{}, binary() | undefined}|
- {tls_version(), {resumed | new, #session{}}, #connection_states{}, [binary()] | undefined,
+ {tls_version(), {resumed | new, #session{}}, #connection_states{},
+ [binary()] | undefined,
[oid()] | undefined, [oid()] | undefined} |
#alert{}.
%%
%% Description: Handles a recieved hello message
%%--------------------------------------------------------------------
-hello(#server_hello{cipher_suite = CipherSuite, server_version = Version,
- compression_method = Compression, random = Random,
- session_id = SessionId, renegotiation_info = Info,
- hash_signs = _HashSigns} = Hello,
- #ssl_options{secure_renegotiate = SecureRenegotation, next_protocol_selector = NextProtocolSelector,
- versions = SupportedVersions},
+hello(#server_hello{server_version = Version, random = Random,
+ cipher_suite = CipherSuite,
+ compression_method = Compression,
+ session_id = SessionId, extensions = HelloExt},
+ #ssl_options{versions = SupportedVersions} = SslOpt,
ConnectionStates0, Renegotiation) ->
case tls_record:is_acceptable_version(Version, SupportedVersions) of
true ->
- case handle_renegotiation_info(client, Info, ConnectionStates0,
- Renegotiation, SecureRenegotation, []) of
- {ok, ConnectionStates1} ->
- ConnectionStates =
- hello_pending_connection_states(client, Version, CipherSuite, Random,
- Compression, ConnectionStates1),
- case handle_next_protocol(Hello, NextProtocolSelector, Renegotiation) of
- #alert{} = Alert ->
- Alert;
- Protocol ->
- {Version, SessionId, ConnectionStates, Protocol}
- end;
- #alert{} = Alert ->
- Alert
- end;
+ handle_hello_extensions(Version, SessionId, Random, CipherSuite,
+ Compression, HelloExt, SslOpt, ConnectionStates0, Renegotiation);
false ->
?ALERT_REC(?FATAL, ?PROTOCOL_VERSION)
end;
-hello(#client_hello{client_version = ClientVersion} = Hello,
+hello(#client_hello{client_version = ClientVersion,
+ session_id = SugesstedId,
+ cipher_suites = CipherSuites,
+ compression_methods = Compressions,
+ random = Random,
+ extensions = HelloExt},
#ssl_options{versions = Versions} = SslOpts,
{Port, Session0, Cache, CacheCb, ConnectionStates0, Cert}, Renegotiation) ->
- Version = select_version(ClientVersion, Versions),
+ Version = ssl_handshake:select_version(tls_record, ClientVersion, Versions),
case tls_record:is_acceptable_version(Version, Versions) of
true ->
%% TODO: need to take supported Curves into Account when selecting the CipherSuite....
%% if whe have an ECDSA cert with an unsupported curve, we need to drop ECDSA ciphers
{Type, #session{cipher_suite = CipherSuite} = Session1}
- = select_session(Hello, Port, Session0, Version,
- SslOpts, Cache, CacheCb, Cert),
+ = ssl_handshake:select_session(SugesstedId, CipherSuites, Compressions,
+ Port, Session0, Version,
+ SslOpts, Cache, CacheCb, Cert),
case CipherSuite of
no_suite ->
?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY);
_ ->
- try handle_hello_extensions(Hello, Version, SslOpts, Session1, ConnectionStates0, Renegotiation) of
- {Session, ConnectionStates, ProtocolsToAdvertise, ECPointFormats, EllipticCurves} ->
- {Version, {Type, Session}, ConnectionStates,
- ProtocolsToAdvertise, ECPointFormats, EllipticCurves}
- catch throw:Alert ->
- Alert
- end
+ handle_hello_extensions(Version, Type, Random, HelloExt,
+ SslOpts, Session1, ConnectionStates0,
+ Renegotiation)
end;
false ->
?ALERT_REC(?FATAL, ?PROTOCOL_VERSION)
end.
%%--------------------------------------------------------------------
--spec certify(#certificate{}, db_handle(), certdb_ref(), integer() | nolimit,
- verify_peer | verify_none, {fun(), term},
- client | server) -> {der_cert(), public_key_info()} | #alert{}.
-%%
-%% Description: Handles a certificate handshake message
-%%--------------------------------------------------------------------
-certify(#certificate{asn1_certificates = ASN1Certs}, CertDbHandle, CertDbRef,
- MaxPathLen, _Verify, VerifyFunAndState, Role) ->
- [PeerCert | _] = ASN1Certs,
-
- ValidationFunAndState =
- case VerifyFunAndState of
- undefined ->
- {fun(OtpCert, ExtensionOrVerifyResult, SslState) ->
- ssl_certificate:validate_extension(OtpCert,
- ExtensionOrVerifyResult, SslState)
- end, Role};
- {Fun, UserState0} ->
- {fun(OtpCert, {extension, _} = Extension, {SslState, UserState}) ->
- case ssl_certificate:validate_extension(OtpCert,
- Extension,
- SslState) of
- {valid, NewSslState} ->
- {valid, {NewSslState, UserState}};
- {fail, Reason} ->
- apply_user_fun(Fun, OtpCert, Reason, UserState,
- SslState);
- {unknown, _} ->
- apply_user_fun(Fun, OtpCert,
- Extension, UserState, SslState)
- end;
- (OtpCert, VerifyResult, {SslState, UserState}) ->
- apply_user_fun(Fun, OtpCert, VerifyResult, UserState,
- SslState)
- end, {Role, UserState0}}
- end,
-
- try
- {TrustedErlCert, CertPath} =
- ssl_certificate:trusted_cert_and_path(ASN1Certs, CertDbHandle, CertDbRef),
- case public_key:pkix_path_validation(TrustedErlCert,
- CertPath,
- [{max_path_length,
- MaxPathLen},
- {verify_fun, ValidationFunAndState}]) of
- {ok, {PublicKeyInfo,_}} ->
- {PeerCert, PublicKeyInfo};
- {error, Reason} ->
- path_validation_alert(Reason)
- end
- catch
- error:_ ->
- %% ASN-1 decode of certificate somehow failed
- ?ALERT_REC(?FATAL, ?CERTIFICATE_UNKNOWN)
- end.
-
-%%--------------------------------------------------------------------
--spec certificate(der_cert(), db_handle(), certdb_ref(), client | server) -> #certificate{} | #alert{}.
-%%
-%% Description: Creates a certificate message.
-%%--------------------------------------------------------------------
-certificate(OwnCert, CertDbHandle, CertDbRef, client) ->
- Chain =
- case ssl_certificate:certificate_chain(OwnCert, CertDbHandle, CertDbRef) of
- {ok, CertChain} ->
- CertChain;
- {error, _} ->
- %% If no suitable certificate is available, the client
- %% SHOULD send a certificate message containing no
- %% certificates. (chapter 7.4.6. RFC 4346)
- []
- end,
- #certificate{asn1_certificates = Chain};
-
-certificate(OwnCert, CertDbHandle, CertDbRef, server) ->
- case ssl_certificate:certificate_chain(OwnCert, CertDbHandle, CertDbRef) of
- {ok, Chain} ->
- #certificate{asn1_certificates = Chain};
- {error, _} ->
- ?ALERT_REC(?FATAL, ?INTERNAL_ERROR)
- end.
-
-%%--------------------------------------------------------------------
--spec client_certificate_verify(undefined | der_cert(), binary(),
- tls_version(), term(), private_key(),
- tls_handshake_history()) ->
- #certificate_verify{} | ignore | #alert{}.
-%%
-%% Description: Creates a certificate_verify message, called by the client.
-%%--------------------------------------------------------------------
-client_certificate_verify(undefined, _, _, _, _, _) ->
- ignore;
-client_certificate_verify(_, _, _, _, undefined, _) ->
- ignore;
-client_certificate_verify(OwnCert, MasterSecret, Version,
- {HashAlgo, _} = HashSign,
- PrivateKey, {Handshake, _}) ->
- case public_key:pkix_is_fixed_dh_cert(OwnCert) of
- true ->
- ?ALERT_REC(?FATAL, ?UNSUPPORTED_CERTIFICATE);
- false ->
- Hashes =
- calc_certificate_verify(Version, HashAlgo, MasterSecret, Handshake),
- Signed = digitally_signed(Version, Hashes, HashAlgo, PrivateKey),
- #certificate_verify{signature = Signed, hashsign_algorithm = HashSign}
- end.
-
-%%--------------------------------------------------------------------
--spec certificate_verify(binary(), public_key_info(), tls_version(), term(),
- binary(), tls_handshake_history()) -> valid | #alert{}.
-%%
-%% Description: Checks that the certificate_verify message is valid.
-%%--------------------------------------------------------------------
-certificate_verify(Signature, PublicKeyInfo, Version,
- HashSign = {HashAlgo, _}, MasterSecret, {_, Handshake}) ->
- Hash = calc_certificate_verify(Version, HashAlgo, MasterSecret, Handshake),
- case verify_signature(Version, Hash, HashSign, Signature, PublicKeyInfo) of
- true ->
- valid;
- _ ->
- ?ALERT_REC(?FATAL, ?BAD_CERTIFICATE)
- end.
-
-%%--------------------------------------------------------------------
--spec verify_signature(tls_version(), binary(), {term(), term()}, binary(),
- public_key_info()) -> true | false.
-%%
-%% Description: Checks that a public_key signature is valid.
-%%--------------------------------------------------------------------
-verify_signature(_Version, _Hash, {_HashAlgo, anon}, _Signature, _) ->
- true;
-verify_signature({3, Minor}, Hash, {HashAlgo, rsa}, Signature, {?rsaEncryption, PubKey, _PubKeyParams})
- when Minor >= 3 ->
- public_key:verify({digest, Hash}, HashAlgo, Signature, PubKey);
-verify_signature(_Version, Hash, _HashAlgo, Signature, {?rsaEncryption, PubKey, _PubKeyParams}) ->
- case public_key:decrypt_public(Signature, PubKey,
- [{rsa_pad, rsa_pkcs1_padding}]) of
- Hash -> true;
- _ -> false
- end;
-verify_signature(_Version, Hash, {HashAlgo, dsa}, Signature, {?'id-dsa', PublicKey, PublicKeyParams}) ->
- public_key:verify({digest, Hash}, HashAlgo, Signature, {PublicKey, PublicKeyParams});
-verify_signature(_Version, Hash, {HashAlgo, ecdsa}, Signature, {?'id-ecPublicKey', PublicKey, PublicKeyParams}) ->
- public_key:verify({digest, Hash}, HashAlgo, Signature, {PublicKey, PublicKeyParams}).
-
-%%--------------------------------------------------------------------
--spec certificate_request(#connection_states{}, db_handle(), certdb_ref(), tls_version()) ->
- #certificate_request{}.
-%%
-%% Description: Creates a certificate_request message, called by the server.
-%%--------------------------------------------------------------------
-certificate_request(ConnectionStates, CertDbHandle, CertDbRef, Version) ->
- #connection_state{security_parameters =
- #security_parameters{cipher_suite = CipherSuite}} =
- tls_record:pending_connection_state(ConnectionStates, read),
- Types = certificate_types(CipherSuite),
- HashSigns = advertised_hash_signs(Version),
- Authorities = certificate_authorities(CertDbHandle, CertDbRef),
- #certificate_request{
- certificate_types = Types,
- hashsign_algorithms = HashSigns,
- certificate_authorities = Authorities
- }.
-
-%%--------------------------------------------------------------------
--spec key_exchange(client | server, tls_version(),
- {premaster_secret, binary(), public_key_info()} |
- {dh, binary()} |
- {dh, {binary(), binary()}, #'DHParameter'{}, {HashAlgo::atom(), SignAlgo::atom()},
- binary(), binary(), private_key()} |
- {ecdh, #'ECPrivateKey'{}} |
- {psk, binary()} |
- {dhe_psk, binary(), binary()} |
- {srp, {binary(), binary()}, #srp_user{}, {HashAlgo::atom(), SignAlgo::atom()},
- binary(), binary(), private_key()}) ->
- #client_key_exchange{} | #server_key_exchange{}.
-%%
-%% Description: Creates a keyexchange message.
-%%--------------------------------------------------------------------
-key_exchange(client, _Version, {premaster_secret, Secret, {_, PublicKey, _}}) ->
- EncPremasterSecret =
- encrypted_premaster_secret(Secret, PublicKey),
- #client_key_exchange{exchange_keys = EncPremasterSecret};
-
-key_exchange(client, _Version, {dh, PublicKey}) ->
- #client_key_exchange{
- exchange_keys = #client_diffie_hellman_public{
- dh_public = PublicKey}
- };
-
-key_exchange(client, _Version, {ecdh, #'ECPrivateKey'{publicKey = {0, ECPublicKey}}}) ->
- #client_key_exchange{
- exchange_keys = #client_ec_diffie_hellman_public{
- dh_public = ECPublicKey}
- };
-
-key_exchange(client, _Version, {psk, Identity}) ->
- #client_key_exchange{
- exchange_keys = #client_psk_identity{
- identity = Identity}
- };
-
-key_exchange(client, _Version, {dhe_psk, Identity, PublicKey}) ->
- #client_key_exchange{
- exchange_keys = #client_dhe_psk_identity{
- identity = Identity,
- dh_public = PublicKey}
- };
-
-key_exchange(client, _Version, {psk_premaster_secret, PskIdentity, Secret, {_, PublicKey, _}}) ->
- EncPremasterSecret =
- encrypted_premaster_secret(Secret, PublicKey),
- #client_key_exchange{
- exchange_keys = #client_rsa_psk_identity{
- identity = PskIdentity,
- exchange_keys = EncPremasterSecret}};
-
-key_exchange(client, _Version, {srp, PublicKey}) ->
- #client_key_exchange{
- exchange_keys = #client_srp_public{
- srp_a = PublicKey}
- };
-
-key_exchange(server, Version, {dh, {PublicKey, _},
- #'DHParameter'{prime = P, base = G},
- HashSign, ClientRandom, ServerRandom, PrivateKey}) ->
- ServerDHParams = #server_dh_params{dh_p = int_to_bin(P),
- dh_g = int_to_bin(G), dh_y = PublicKey},
- enc_server_key_exchange(Version, ServerDHParams, HashSign,
- ClientRandom, ServerRandom, PrivateKey);
-
-key_exchange(server, Version, {ecdh, #'ECPrivateKey'{publicKey = {0, ECPublicKey},
- parameters = ECCurve}, HashSign, ClientRandom, ServerRandom,
- PrivateKey}) ->
- ServerECParams = #server_ecdh_params{curve = ECCurve, public = ECPublicKey},
- enc_server_key_exchange(Version, ServerECParams, HashSign,
- ClientRandom, ServerRandom, PrivateKey);
-
-key_exchange(server, Version, {psk, PskIdentityHint,
- HashSign, ClientRandom, ServerRandom, PrivateKey}) ->
- ServerPSKParams = #server_psk_params{hint = PskIdentityHint},
- enc_server_key_exchange(Version, ServerPSKParams, HashSign,
- ClientRandom, ServerRandom, PrivateKey);
-
-key_exchange(server, Version, {dhe_psk, PskIdentityHint, {PublicKey, _},
- #'DHParameter'{prime = P, base = G},
- HashSign, ClientRandom, ServerRandom, PrivateKey}) ->
- ServerEDHPSKParams = #server_dhe_psk_params{
- hint = PskIdentityHint,
- dh_params = #server_dh_params{dh_p = int_to_bin(P),
- dh_g = int_to_bin(G), dh_y = PublicKey}
- },
- enc_server_key_exchange(Version, ServerEDHPSKParams,
- HashSign, ClientRandom, ServerRandom, PrivateKey);
-
-key_exchange(server, Version, {srp, {PublicKey, _},
- #srp_user{generator = Generator, prime = Prime,
- salt = Salt},
- HashSign, ClientRandom, ServerRandom, PrivateKey}) ->
- ServerSRPParams = #server_srp_params{srp_n = Prime, srp_g = Generator,
- srp_s = Salt, srp_b = PublicKey},
- enc_server_key_exchange(Version, ServerSRPParams, HashSign,
- ClientRandom, ServerRandom, PrivateKey).
-
-enc_server_key_exchange(Version, Params, {HashAlgo, SignAlgo},
- ClientRandom, ServerRandom, PrivateKey) ->
- EncParams = enc_server_key(Params),
- case HashAlgo of
- null ->
- #server_key_params{params = Params,
- params_bin = EncParams,
- hashsign = {null, anon},
- signature = <<>>};
- _ ->
- Hash =
- server_key_exchange_hash(HashAlgo, <<ClientRandom/binary,
- ServerRandom/binary,
- EncParams/binary>>),
- Signature = digitally_signed(Version, Hash, HashAlgo, PrivateKey),
- #server_key_params{params = Params,
- params_bin = EncParams,
- hashsign = {HashAlgo, SignAlgo},
- signature = Signature}
- end.
-
-%%--------------------------------------------------------------------
--spec master_secret(tls_version(), #session{} | binary(), #connection_states{},
- client | server) -> {binary(), #connection_states{}} | #alert{}.
-%%
-%% Description: Sets or calculates the master secret and calculate keys,
-%% updating the pending connection states. The Mastersecret and the update
-%% connection states are returned or an alert if the calculation fails.
-%%-------------------------------------------------------------------
-master_secret(Version, #session{master_secret = Mastersecret},
- ConnectionStates, Role) ->
- ConnectionState =
- tls_record:pending_connection_state(ConnectionStates, read),
- SecParams = ConnectionState#connection_state.security_parameters,
- try master_secret(Version, Mastersecret, SecParams,
- ConnectionStates, Role)
- catch
- exit:Reason ->
- Report = io_lib:format("Key calculation failed due to ~p",
- [Reason]),
- error_logger:error_report(Report),
- ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE)
- end;
-
-master_secret(Version, PremasterSecret, ConnectionStates, Role) ->
- ConnectionState =
- tls_record:pending_connection_state(ConnectionStates, read),
- SecParams = ConnectionState#connection_state.security_parameters,
- #security_parameters{prf_algorithm = PrfAlgo,
- client_random = ClientRandom,
- server_random = ServerRandom} = SecParams,
- try master_secret(Version,
- calc_master_secret(Version,PrfAlgo,PremasterSecret,
- ClientRandom, ServerRandom),
- SecParams, ConnectionStates, Role)
- catch
- exit:Reason ->
- Report = io_lib:format("Master secret calculation failed"
- " due to ~p", [Reason]),
- error_logger:error_report(Report),
- ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE)
- end.
-
--spec next_protocol(binary()) -> #next_protocol{}.
-
-next_protocol(SelectedProtocol) ->
- #next_protocol{selected_protocol = SelectedProtocol}.
-
-%%--------------------------------------------------------------------
--spec finished(tls_version(), client | server, integer(), binary(), tls_handshake_history()) ->
- #finished{}.
-%%
-%% Description: Creates a handshake finished message
-%%-------------------------------------------------------------------
-finished(Version, Role, PrfAlgo, MasterSecret, {Handshake, _}) -> % use the current handshake
- #finished{verify_data =
- calc_finished(Version, Role, PrfAlgo, MasterSecret, Handshake)}.
-
-%%--------------------------------------------------------------------
--spec verify_connection(tls_version(), #finished{}, client | server, integer(), binary(),
- tls_handshake_history()) -> verified | #alert{}.
-%%
-%% Description: Checks the ssl handshake finished message to verify
-%% the connection.
-%%-------------------------------------------------------------------
-verify_connection(Version, #finished{verify_data = Data},
- Role, PrfAlgo, MasterSecret, {_, Handshake}) ->
- %% use the previous hashes
- case calc_finished(Version, Role, PrfAlgo, MasterSecret, Handshake) of
- Data ->
- verified;
- _ ->
- ?ALERT_REC(?FATAL, ?DECRYPT_ERROR)
- end.
-%%--------------------------------------------------------------------
--spec server_hello_done() -> #server_hello_done{}.
-%%
-%% Description: Creates a server hello done message.
-%%--------------------------------------------------------------------
-server_hello_done() ->
- #server_hello_done{}.
-
-%%--------------------------------------------------------------------
-spec encode_handshake(tls_handshake(), tls_version()) -> iolist().
%%
-%% Description: Encode a handshake packet to binary
+%% Description: Encode a handshake packet
%%--------------------------------------------------------------------x
encode_handshake(Package, Version) ->
- {MsgType, Bin} = enc_hs(Package, Version),
+ {MsgType, Bin} = enc_handshake(Package, Version),
Len = byte_size(Bin),
[MsgType, ?uint24(Len), Bin].
@@ -592,30 +168,6 @@ get_tls_handshake(Version, Data, Buffer) ->
get_tls_handshake_aux(Version, list_to_binary([Buffer, Data]), []).
%%--------------------------------------------------------------------
--spec decode_client_key(binary(), key_algo(), tls_version()) ->
- #encrypted_premaster_secret{}
- | #client_diffie_hellman_public{}
- | #client_ec_diffie_hellman_public{}
- | #client_psk_identity{}
- | #client_dhe_psk_identity{}
- | #client_rsa_psk_identity{}
- | #client_srp_public{}.
-%%
-%% Description: Decode client_key data and return appropriate type
-%%--------------------------------------------------------------------
-decode_client_key(ClientKey, Type, Version) ->
- dec_client_key(ClientKey, key_exchange_alg(Type), Version).
-
-%%--------------------------------------------------------------------
--spec decode_server_key(binary(), key_algo(), tls_version()) ->
- #server_key_params{}.
-%%
-%% Description: Decode server_key data and return appropriate type
-%%--------------------------------------------------------------------
-decode_server_key(ServerKey, Type, Version) ->
- dec_server_key(ServerKey, key_exchange_alg(Type), Version).
-
-%%--------------------------------------------------------------------
-spec init_handshake_history() -> tls_handshake_history().
%%
@@ -645,1231 +197,99 @@ update_handshake_history(Handshake, % special-case SSL2 client hello
update_handshake_history({Handshake0, _Prev}, Data) ->
{[Data|Handshake0], Handshake0}.
-%%--------------------------------------------------------------------
--spec decrypt_premaster_secret(binary(), #'RSAPrivateKey'{}) -> binary().
-
-%%
-%% Description: Public key decryption using the private key.
-%%--------------------------------------------------------------------
-decrypt_premaster_secret(Secret, RSAPrivateKey) ->
- try public_key:decrypt_private(Secret, RSAPrivateKey,
- [{rsa_pad, rsa_pkcs1_padding}])
- catch
- _:_ ->
- io:format("decrypt_premaster_secret error"),
- throw(?ALERT_REC(?FATAL, ?DECRYPT_ERROR))
- end.
-%%--------------------------------------------------------------------
--spec server_key_exchange_hash(md5sha | md5 | sha | sha224 |sha256 | sha384 | sha512, binary()) -> binary().
-%%
-%% Description: Calculate server key exchange hash
-%%--------------------------------------------------------------------
-server_key_exchange_hash(md5sha, Value) ->
- MD5 = crypto:hash(md5, Value),
- SHA = crypto:hash(sha, Value),
- <<MD5/binary, SHA/binary>>;
-
-server_key_exchange_hash(Hash, Value) ->
- crypto:hash(Hash, Value).
-
-%%--------------------------------------------------------------------
--spec prf(tls_version(), binary(), binary(), [binary()], non_neg_integer()) ->
- {ok, binary()} | {error, undefined}.
-%%
-%% Description: use the TLS PRF to generate key material
-%%--------------------------------------------------------------------
-prf({3,0}, _, _, _, _) ->
- {error, undefined};
-prf({3,1}, Secret, Label, Seed, WantedLength) ->
- {ok, ssl_tls1:prf(?MD5SHA, Secret, Label, Seed, WantedLength)};
-prf({3,_N}, Secret, Label, Seed, WantedLength) ->
- {ok, ssl_tls1:prf(?SHA256, Secret, Label, Seed, WantedLength)}.
-
-
-%%--------------------------------------------------------------------
--spec select_hashsign(#hash_sign_algos{}| undefined, undefined | term()) ->
- [{atom(), atom()}] | undefined.
-
-%%
-%% Description:
-%%--------------------------------------------------------------------
-select_hashsign(_, undefined) ->
- {null, anon};
-select_hashsign(undefined, Cert) ->
- #'OTPCertificate'{tbsCertificate = TBSCert} = public_key:pkix_decode_cert(Cert, otp),
- #'OTPSubjectPublicKeyInfo'{algorithm = {_,Algo, _}} = TBSCert#'OTPTBSCertificate'.subjectPublicKeyInfo,
- select_cert_hashsign(undefined, Algo, {undefined, undefined});
-select_hashsign(#hash_sign_algos{hash_sign_algos = HashSigns}, Cert) ->
- #'OTPCertificate'{tbsCertificate = TBSCert} =public_key:pkix_decode_cert(Cert, otp),
- #'OTPSubjectPublicKeyInfo'{algorithm = {_,Algo, _}} = TBSCert#'OTPTBSCertificate'.subjectPublicKeyInfo,
- DefaultHashSign = {_, Sign} = select_cert_hashsign(undefined, Algo, {undefined, undefined}),
- case lists:filter(fun({sha, dsa}) ->
- true;
- ({_, dsa}) ->
- false;
- ({Hash, S}) when S == Sign ->
- ssl_cipher:is_acceptable_hash(Hash, proplists:get_value(hashs, crypto:supports()));
- (_) ->
- false
- end, HashSigns) of
- [] ->
- DefaultHashSign;
- [HashSign| _] ->
- HashSign
- end.
-%%--------------------------------------------------------------------
--spec select_cert_hashsign(#hash_sign_algos{}| undefined, oid(), tls_version()) ->
- [{atom(), atom()}].
-
-%%
-%% Description:
-%%--------------------------------------------------------------------
-select_cert_hashsign(HashSign, _, {Major, Minor}) when HashSign =/= undefined andalso Major >= 3 andalso Minor >= 3 ->
- HashSign;
-select_cert_hashsign(undefined,?'id-ecPublicKey', _) ->
- {sha, ecdsa};
-select_cert_hashsign(undefined, ?rsaEncryption, _) ->
- {md5sha, rsa};
-select_cert_hashsign(undefined, ?'id-dsa', _) ->
- {sha, dsa}.
-
-%%--------------------------------------------------------------------
-%%% Internal functions
-%%--------------------------------------------------------------------
get_tls_handshake_aux(Version, <<?BYTE(Type), ?UINT24(Length),
Body:Length/binary,Rest/binary>>, Acc) ->
Raw = <<?BYTE(Type), ?UINT24(Length), Body/binary>>,
- H = dec_hs(Version, Type, Body),
- get_tls_handshake_aux(Version, Rest, [{H,Raw} | Acc]);
+ Handshake = decode_handshake(Version, Type, Body),
+ get_tls_handshake_aux(Version, Rest, [{Handshake,Raw} | Acc]);
get_tls_handshake_aux(_Version, Data, Acc) ->
{lists:reverse(Acc), Data}.
-path_validation_alert({bad_cert, cert_expired}) ->
- ?ALERT_REC(?FATAL, ?CERTIFICATE_EXPIRED);
-path_validation_alert({bad_cert, invalid_issuer}) ->
- ?ALERT_REC(?FATAL, ?BAD_CERTIFICATE);
-path_validation_alert({bad_cert, invalid_signature}) ->
- ?ALERT_REC(?FATAL, ?BAD_CERTIFICATE);
-path_validation_alert({bad_cert, name_not_permitted}) ->
- ?ALERT_REC(?FATAL, ?BAD_CERTIFICATE);
-path_validation_alert({bad_cert, unknown_critical_extension}) ->
- ?ALERT_REC(?FATAL, ?UNSUPPORTED_CERTIFICATE);
-path_validation_alert({bad_cert, cert_revoked}) ->
- ?ALERT_REC(?FATAL, ?CERTIFICATE_REVOKED);
-path_validation_alert({bad_cert, selfsigned_peer}) ->
- ?ALERT_REC(?FATAL, ?BAD_CERTIFICATE);
-path_validation_alert({bad_cert, unknown_ca}) ->
- ?ALERT_REC(?FATAL, ?UNKNOWN_CA);
-path_validation_alert(_) ->
- ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE).
-
-select_session(Hello, Port, Session, Version,
- #ssl_options{ciphers = UserSuites} = SslOpts, Cache, CacheCb, Cert) ->
- SuggestedSessionId = Hello#client_hello.session_id,
- {SessionId, Resumed} = ssl_session:server_id(Port, SuggestedSessionId,
- SslOpts, Cert,
- Cache, CacheCb),
- Suites = available_suites(Cert, UserSuites, Version),
- case Resumed of
- undefined ->
- CipherSuite = select_cipher_suite(Hello#client_hello.cipher_suites, Suites),
- Compressions = Hello#client_hello.compression_methods,
- Compression = select_compression(Compressions),
- {new, Session#session{session_id = SessionId,
- cipher_suite = CipherSuite,
- compression_method = Compression}};
- _ ->
- {resumed, Resumed}
- end.
-
-available_suites(UserSuites, Version) ->
- case UserSuites of
- [] ->
- ssl_cipher:suites(Version);
- _ ->
- UserSuites
- end.
-
-available_suites(ServerCert, UserSuites, Version) ->
- ssl_cipher:filter(ServerCert, available_suites(UserSuites, Version)).
-
-cipher_suites(Suites, false) ->
- [?TLS_EMPTY_RENEGOTIATION_INFO_SCSV | Suites];
-cipher_suites(Suites, true) ->
- Suites.
-
-srp_user(#ssl_options{srp_identity = {UserName, _}}) ->
- #srp{username = UserName};
-srp_user(_) ->
- undefined.
-
-renegotiation_info(client, _, false) ->
- #renegotiation_info{renegotiated_connection = undefined};
-renegotiation_info(server, ConnectionStates, false) ->
- CS = tls_record:current_connection_state(ConnectionStates, read),
- case CS#connection_state.secure_renegotiation of
- true ->
- #renegotiation_info{renegotiated_connection = ?byte(0)};
- false ->
- #renegotiation_info{renegotiated_connection = undefined}
- end;
-renegotiation_info(client, ConnectionStates, true) ->
- CS = tls_record:current_connection_state(ConnectionStates, read),
- case CS#connection_state.secure_renegotiation of
- true ->
- Data = CS#connection_state.client_verify_data,
- #renegotiation_info{renegotiated_connection = Data};
- false ->
- #renegotiation_info{renegotiated_connection = undefined}
- end;
-
-renegotiation_info(server, ConnectionStates, true) ->
- CS = tls_record:current_connection_state(ConnectionStates, read),
- case CS#connection_state.secure_renegotiation of
- true ->
- CData = CS#connection_state.client_verify_data,
- SData =CS#connection_state.server_verify_data,
- #renegotiation_info{renegotiated_connection = <<CData/binary, SData/binary>>};
- false ->
- #renegotiation_info{renegotiated_connection = undefined}
- end.
-
-decode_next_protocols({next_protocol_negotiation, Protocols}) ->
- decode_next_protocols(Protocols, []).
-decode_next_protocols(<<>>, Acc) ->
- lists:reverse(Acc);
-decode_next_protocols(<<?BYTE(Len), Protocol:Len/binary, Rest/binary>>, Acc) ->
- case Len of
- 0 ->
- {error, invalid_next_protocols};
- _ ->
- decode_next_protocols(Rest, [Protocol|Acc])
- end;
-decode_next_protocols(_Bytes, _Acc) ->
- {error, invalid_next_protocols}.
-
-next_protocol_extension_allowed(NextProtocolSelector, Renegotiating) ->
- NextProtocolSelector =/= undefined andalso not Renegotiating.
-
-handle_next_protocol_on_server(#client_hello{next_protocol_negotiation = undefined}, _Renegotiation, _SslOpts) ->
- undefined;
-
-handle_next_protocol_on_server(#client_hello{next_protocol_negotiation = {next_protocol_negotiation, <<>>}},
- false, #ssl_options{next_protocols_advertised = Protocols}) ->
- Protocols;
-
-handle_next_protocol_on_server(_Hello, _Renegotiation, _SSLOpts) ->
- ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE). % unexpected next protocol extension
-
-handle_next_protocol(#server_hello{next_protocol_negotiation = undefined},
- _NextProtocolSelector, _Renegotiating) ->
- undefined;
-
-handle_next_protocol(#server_hello{next_protocol_negotiation = Protocols},
- NextProtocolSelector, Renegotiating) ->
-
- case next_protocol_extension_allowed(NextProtocolSelector, Renegotiating) of
- true ->
- select_next_protocol(decode_next_protocols(Protocols), NextProtocolSelector);
- false ->
- ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE) % unexpected next protocol extension
- end.
-
-select_next_protocol({error, _Reason}, _NextProtocolSelector) ->
- ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE);
-select_next_protocol(Protocols, NextProtocolSelector) ->
- case NextProtocolSelector(Protocols) of
- ?NO_PROTOCOL ->
- ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE);
- Protocol when is_binary(Protocol) ->
- Protocol
- end.
-
-default_ecc_extensions(Version) ->
- CryptoSupport = proplists:get_value(public_keys, crypto:supports()),
- case proplists:get_bool(ecdh, CryptoSupport) of
- true ->
- EcPointFormats = #ec_point_formats{ec_point_format_list = [?ECPOINT_UNCOMPRESSED]},
- EllipticCurves = #elliptic_curves{elliptic_curve_list = ssl_tls1:ecc_curves(Version)},
- {EcPointFormats, EllipticCurves};
- _ ->
- {undefined, undefined}
- end.
-
-handle_ecc_extensions(Version, EcPointFormats0, EllipticCurves0) ->
- CryptoSupport = proplists:get_value(public_keys, crypto:supports()),
- case proplists:get_bool(ecdh, CryptoSupport) of
- true ->
- EcPointFormats1 = handle_ecc_point_fmt_extension(EcPointFormats0),
- EllipticCurves1 = handle_ecc_curves_extension(Version, EllipticCurves0),
- {EcPointFormats1, EllipticCurves1};
- _ ->
- {undefined, undefined}
- end.
-
-handle_ecc_point_fmt_extension(undefined) ->
- undefined;
-handle_ecc_point_fmt_extension(_) ->
- #ec_point_formats{ec_point_format_list = [?ECPOINT_UNCOMPRESSED]}.
-
-handle_ecc_curves_extension(_Version, undefined) ->
- undefined;
-handle_ecc_curves_extension(Version, _) ->
- #elliptic_curves{elliptic_curve_list = ssl_tls1:ecc_curves(Version)}.
-
-handle_renegotiation_info(_, #renegotiation_info{renegotiated_connection = ?byte(0)},
- ConnectionStates, false, _, _) ->
- {ok, tls_record:set_renegotiation_flag(true, ConnectionStates)};
-
-handle_renegotiation_info(server, undefined, ConnectionStates, _, _, CipherSuites) ->
- case is_member(?TLS_EMPTY_RENEGOTIATION_INFO_SCSV, CipherSuites) of
- true ->
- {ok, tls_record:set_renegotiation_flag(true, ConnectionStates)};
- false ->
- {ok, tls_record:set_renegotiation_flag(false, ConnectionStates)}
- end;
-
-handle_renegotiation_info(_, undefined, ConnectionStates, false, _, _) ->
- {ok, tls_record:set_renegotiation_flag(false, ConnectionStates)};
-
-handle_renegotiation_info(client, #renegotiation_info{renegotiated_connection = ClientServerVerify},
- ConnectionStates, true, _, _) ->
- CS = tls_record:current_connection_state(ConnectionStates, read),
- CData = CS#connection_state.client_verify_data,
- SData = CS#connection_state.server_verify_data,
- case <<CData/binary, SData/binary>> == ClientServerVerify of
- true ->
- {ok, ConnectionStates};
- false ->
- ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE)
- end;
-handle_renegotiation_info(server, #renegotiation_info{renegotiated_connection = ClientVerify},
- ConnectionStates, true, _, CipherSuites) ->
-
- case is_member(?TLS_EMPTY_RENEGOTIATION_INFO_SCSV, CipherSuites) of
- true ->
- ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE);
- false ->
- CS = tls_record:current_connection_state(ConnectionStates, read),
- Data = CS#connection_state.client_verify_data,
- case Data == ClientVerify of
- true ->
- {ok, ConnectionStates};
- false ->
- ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE)
- end
- end;
-
-handle_renegotiation_info(client, undefined, ConnectionStates, true, SecureRenegotation, _) ->
- handle_renegotiation_info(ConnectionStates, SecureRenegotation);
-
-handle_renegotiation_info(server, undefined, ConnectionStates, true, SecureRenegotation, CipherSuites) ->
- case is_member(?TLS_EMPTY_RENEGOTIATION_INFO_SCSV, CipherSuites) of
- true ->
- ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE);
- false ->
- handle_renegotiation_info(ConnectionStates, SecureRenegotation)
- end.
-
-handle_renegotiation_info(ConnectionStates, SecureRenegotation) ->
- CS = tls_record:current_connection_state(ConnectionStates, read),
- case {SecureRenegotation, CS#connection_state.secure_renegotiation} of
- {_, true} ->
- ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE);
- {true, false} ->
- ?ALERT_REC(?FATAL, ?NO_RENEGOTIATION);
- {false, false} ->
- {ok, ConnectionStates}
- end.
-
-%% Update pending connection states with parameters exchanged via
-%% hello messages
-%% NOTE : Role is the role of the receiver of the hello message
-%% currently being processed.
-hello_pending_connection_states(Role, Version, CipherSuite, Random, Compression,
- ConnectionStates) ->
- ReadState =
- tls_record:pending_connection_state(ConnectionStates, read),
- WriteState =
- tls_record:pending_connection_state(ConnectionStates, write),
-
- NewReadSecParams =
- hello_security_parameters(Role, Version, ReadState, CipherSuite,
- Random, Compression),
-
- NewWriteSecParams =
- hello_security_parameters(Role, Version, WriteState, CipherSuite,
- Random, Compression),
-
- tls_record:update_security_params(NewReadSecParams,
- NewWriteSecParams,
- ConnectionStates).
-
-hello_security_parameters(client, Version, ConnectionState, CipherSuite, Random,
- Compression) ->
- SecParams = ConnectionState#connection_state.security_parameters,
- NewSecParams = ssl_cipher:security_parameters(Version, CipherSuite, SecParams),
- NewSecParams#security_parameters{
- server_random = Random,
- compression_algorithm = Compression
- };
-
-hello_security_parameters(server, Version, ConnectionState, CipherSuite, Random,
- Compression) ->
- SecParams = ConnectionState#connection_state.security_parameters,
- NewSecParams = ssl_cipher:security_parameters(Version, CipherSuite, SecParams),
- NewSecParams#security_parameters{
- client_random = Random,
- compression_algorithm = Compression
- }.
-
-select_version(ClientVersion, Versions) ->
- ServerVersion = tls_record:highest_protocol_version(Versions),
- tls_record:lowest_protocol_version(ClientVersion, ServerVersion).
-
-select_cipher_suite([], _) ->
- no_suite;
-select_cipher_suite([Suite | ClientSuites], SupportedSuites) ->
- case is_member(Suite, SupportedSuites) of
- true ->
- Suite;
- false ->
- select_cipher_suite(ClientSuites, SupportedSuites)
- end.
-
-is_member(Suite, SupportedSuites) ->
- lists:member(Suite, SupportedSuites).
-
-select_compression(_CompressionMetodes) ->
- ?NULL.
-
-master_secret(Version, MasterSecret, #security_parameters{
- client_random = ClientRandom,
- server_random = ServerRandom,
- hash_size = HashSize,
- prf_algorithm = PrfAlgo,
- key_material_length = KML,
- expanded_key_material_length = EKML,
- iv_size = IVS},
- ConnectionStates, Role) ->
- {ClientWriteMacSecret, ServerWriteMacSecret, ClientWriteKey,
- ServerWriteKey, ClientIV, ServerIV} =
- setup_keys(Version, PrfAlgo, MasterSecret, ServerRandom,
- ClientRandom, HashSize, KML, EKML, IVS),
-
- ConnStates1 = tls_record:set_master_secret(MasterSecret, ConnectionStates),
- ConnStates2 =
- tls_record:set_mac_secret(ClientWriteMacSecret, ServerWriteMacSecret,
- Role, ConnStates1),
-
- ClientCipherState = #cipher_state{iv = ClientIV, key = ClientWriteKey},
- ServerCipherState = #cipher_state{iv = ServerIV, key = ServerWriteKey},
- {MasterSecret,
- tls_record:set_pending_cipher_state(ConnStates2, ClientCipherState,
- ServerCipherState, Role)}.
-
-
-dec_hs(_, ?NEXT_PROTOCOL, <<?BYTE(SelectedProtocolLength), SelectedProtocol:SelectedProtocolLength/binary,
- ?BYTE(PaddingLength), _Padding:PaddingLength/binary>>) ->
- #next_protocol{selected_protocol = SelectedProtocol};
+%%--------------------------------------------------------------------
+%%% Internal functions
+%%--------------------------------------------------------------------
-dec_hs(_, ?HELLO_REQUEST, <<>>) ->
+decode_handshake(_, ?HELLO_REQUEST, <<>>) ->
#hello_request{};
%% Client hello v2.
%% The server must be able to receive such messages, from clients that
%% are willing to use ssl v3 or higher, but have ssl v2 compatibility.
-dec_hs(_Version, ?CLIENT_HELLO, <<?BYTE(Major), ?BYTE(Minor),
+decode_handshake(_Version, ?CLIENT_HELLO, <<?BYTE(Major), ?BYTE(Minor),
?UINT16(CSLength), ?UINT16(0),
?UINT16(CDLength),
CipherSuites:CSLength/binary,
ChallengeData:CDLength/binary>>) ->
#client_hello{client_version = {Major, Minor},
- random = ssl_ssl2:client_random(ChallengeData, CDLength),
+ random = ssl_v2:client_random(ChallengeData, CDLength),
session_id = 0,
- cipher_suites = from_3bytes(CipherSuites),
+ cipher_suites = ssl_handshake:decode_suites('3_bytes', CipherSuites),
compression_methods = [?NULL],
- renegotiation_info = undefined
+ extensions = #hello_extensions{}
};
-dec_hs(_Version, ?CLIENT_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary,
+decode_handshake(_Version, ?CLIENT_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary,
?BYTE(SID_length), Session_ID:SID_length/binary,
?UINT16(Cs_length), CipherSuites:Cs_length/binary,
?BYTE(Cm_length), Comp_methods:Cm_length/binary,
Extensions/binary>>) ->
- DecodedExtensions = dec_hello_extensions(Extensions),
- RenegotiationInfo = proplists:get_value(renegotiation_info, DecodedExtensions, undefined),
- SRP = proplists:get_value(srp, DecodedExtensions, undefined),
- HashSigns = proplists:get_value(hash_signs, DecodedExtensions, undefined),
- EllipticCurves = proplists:get_value(elliptic_curves, DecodedExtensions,
- undefined),
- NextProtocolNegotiation = proplists:get_value(next_protocol_negotiation, DecodedExtensions, undefined),
+ DecodedExtensions = ssl_handshake:decode_hello_extensions({client, Extensions}),
#client_hello{
client_version = {Major,Minor},
random = Random,
session_id = Session_ID,
- cipher_suites = from_2bytes(CipherSuites),
+ cipher_suites = ssl_handshake:decode_suites('2_bytes', CipherSuites),
compression_methods = Comp_methods,
- renegotiation_info = RenegotiationInfo,
- srp = SRP,
- hash_signs = HashSigns,
- elliptic_curves = EllipticCurves,
- next_protocol_negotiation = NextProtocolNegotiation
+ extensions = DecodedExtensions
};
-dec_hs(_Version, ?SERVER_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary,
- ?BYTE(SID_length), Session_ID:SID_length/binary,
- Cipher_suite:2/binary, ?BYTE(Comp_method)>>) ->
- #server_hello{
- server_version = {Major,Minor},
- random = Random,
- session_id = Session_ID,
- cipher_suite = Cipher_suite,
- compression_method = Comp_method,
- renegotiation_info = undefined,
- hash_signs = undefined,
- elliptic_curves = undefined};
-
-dec_hs(_Version, ?SERVER_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary,
- ?BYTE(SID_length), Session_ID:SID_length/binary,
- Cipher_suite:2/binary, ?BYTE(Comp_method),
- ?UINT16(ExtLen), Extensions:ExtLen/binary>>) ->
-
- HelloExtensions = dec_hello_extensions(Extensions, []),
- RenegotiationInfo = proplists:get_value(renegotiation_info, HelloExtensions,
- undefined),
- HashSigns = proplists:get_value(hash_signs, HelloExtensions,
- undefined),
- EllipticCurves = proplists:get_value(elliptic_curves, HelloExtensions,
- undefined),
- NextProtocolNegotiation = proplists:get_value(next_protocol_negotiation, HelloExtensions, undefined),
-
- #server_hello{
- server_version = {Major,Minor},
- random = Random,
- session_id = Session_ID,
- cipher_suite = Cipher_suite,
- compression_method = Comp_method,
- renegotiation_info = RenegotiationInfo,
- hash_signs = HashSigns,
- elliptic_curves = EllipticCurves,
- next_protocol_negotiation = NextProtocolNegotiation};
-dec_hs(_Version, ?CERTIFICATE, <<?UINT24(ACLen), ASN1Certs:ACLen/binary>>) ->
- #certificate{asn1_certificates = certs_to_list(ASN1Certs)};
-dec_hs(_Version, ?SERVER_KEY_EXCHANGE, Keys) ->
- #server_key_exchange{exchange_keys = Keys};
-dec_hs({Major, Minor}, ?CERTIFICATE_REQUEST,
- <<?BYTE(CertTypesLen), CertTypes:CertTypesLen/binary,
- ?UINT16(HashSignsLen), HashSigns:HashSignsLen/binary,
- ?UINT16(CertAuthsLen), CertAuths:CertAuthsLen/binary>>)
- when Major == 3, Minor >= 3 ->
- HashSignAlgos = [{ssl_cipher:hash_algorithm(Hash), ssl_cipher:sign_algorithm(Sign)} ||
- <<?BYTE(Hash), ?BYTE(Sign)>> <= HashSigns],
- #certificate_request{certificate_types = CertTypes,
- hashsign_algorithms = #hash_sign_algos{hash_sign_algos = HashSignAlgos},
- certificate_authorities = CertAuths};
-dec_hs(_Version, ?CERTIFICATE_REQUEST,
- <<?BYTE(CertTypesLen), CertTypes:CertTypesLen/binary,
- ?UINT16(CertAuthsLen), CertAuths:CertAuthsLen/binary>>) ->
- #certificate_request{certificate_types = CertTypes,
- certificate_authorities = CertAuths};
-dec_hs(_Version, ?SERVER_HELLO_DONE, <<>>) ->
- #server_hello_done{};
-dec_hs({Major, Minor}, ?CERTIFICATE_VERIFY,<<HashSign:2/binary, ?UINT16(SignLen), Signature:SignLen/binary>>)
- when Major == 3, Minor >= 3 ->
- #certificate_verify{hashsign_algorithm = hashsign_dec(HashSign), signature = Signature};
-dec_hs(_Version, ?CERTIFICATE_VERIFY,<<?UINT16(SignLen), Signature:SignLen/binary>>)->
- #certificate_verify{signature = Signature};
-dec_hs(_Version, ?CLIENT_KEY_EXCHANGE, PKEPMS) ->
- #client_key_exchange{exchange_keys = PKEPMS};
-dec_hs(_Version, ?FINISHED, VerifyData) ->
- #finished{verify_data = VerifyData};
-dec_hs(_, _, _) ->
- throw(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE)).
-
-dec_client_key(PKEPMS, ?KEY_EXCHANGE_RSA, {3, 0}) ->
- #encrypted_premaster_secret{premaster_secret = PKEPMS};
-dec_client_key(<<?UINT16(_), PKEPMS/binary>>, ?KEY_EXCHANGE_RSA, _) ->
- #encrypted_premaster_secret{premaster_secret = PKEPMS};
-dec_client_key(<<>>, ?KEY_EXCHANGE_DIFFIE_HELLMAN, _) ->
- throw(?ALERT_REC(?FATAL, ?UNSUPPORTED_CERTIFICATE));
-dec_client_key(<<?UINT16(DH_YLen), DH_Y:DH_YLen/binary>>,
- ?KEY_EXCHANGE_DIFFIE_HELLMAN, _) ->
- #client_diffie_hellman_public{dh_public = DH_Y};
-dec_client_key(<<>>, ?KEY_EXCHANGE_EC_DIFFIE_HELLMAN, _) ->
- throw(?ALERT_REC(?FATAL, ?UNSUPPORTED_CERTIFICATE));
-dec_client_key(<<?BYTE(DH_YLen), DH_Y:DH_YLen/binary>>,
- ?KEY_EXCHANGE_EC_DIFFIE_HELLMAN, _) ->
- #client_ec_diffie_hellman_public{dh_public = DH_Y};
-dec_client_key(<<?UINT16(Len), Id:Len/binary>>,
- ?KEY_EXCHANGE_PSK, _) ->
- #client_psk_identity{identity = Id};
-dec_client_key(<<?UINT16(Len), Id:Len/binary,
- ?UINT16(DH_YLen), DH_Y:DH_YLen/binary>>,
- ?KEY_EXCHANGE_DHE_PSK, _) ->
- #client_dhe_psk_identity{identity = Id, dh_public = DH_Y};
-dec_client_key(<<?UINT16(Len), Id:Len/binary, PKEPMS/binary>>,
- ?KEY_EXCHANGE_RSA_PSK, {3, 0}) ->
- #client_rsa_psk_identity{identity = Id, exchange_keys = #encrypted_premaster_secret{premaster_secret = PKEPMS}};
-dec_client_key(<<?UINT16(Len), Id:Len/binary, ?UINT16(_), PKEPMS/binary>>,
- ?KEY_EXCHANGE_RSA_PSK, _) ->
- #client_rsa_psk_identity{identity = Id, exchange_keys = #encrypted_premaster_secret{premaster_secret = PKEPMS}};
-dec_client_key(<<?UINT16(ALen), A:ALen/binary>>,
- ?KEY_EXCHANGE_SRP, _) ->
- #client_srp_public{srp_a = A}.
-
-dec_ske_params(Len, Keys, Version) ->
- <<Params:Len/bytes, Signature/binary>> = Keys,
- dec_ske_signature(Params, Signature, Version).
-
-dec_ske_signature(Params, <<?BYTE(HashAlgo), ?BYTE(SignAlgo),
- ?UINT16(0)>>, {Major, Minor})
- when Major >= 3, Minor >= 3 ->
- HashSign = {ssl_cipher:hash_algorithm(HashAlgo), ssl_cipher:sign_algorithm(SignAlgo)},
- {Params, HashSign, <<>>};
-dec_ske_signature(Params, <<?BYTE(HashAlgo), ?BYTE(SignAlgo),
- ?UINT16(Len), Signature:Len/binary>>, {Major, Minor})
- when Major >= 3, Minor >= 3 ->
- HashSign = {ssl_cipher:hash_algorithm(HashAlgo), ssl_cipher:sign_algorithm(SignAlgo)},
- {Params, HashSign, Signature};
-dec_ske_signature(Params, <<>>, _) ->
- {Params, {null, anon}, <<>>};
-dec_ske_signature(Params, <<?UINT16(0)>>, _) ->
- {Params, {null, anon}, <<>>};
-dec_ske_signature(Params, <<?UINT16(Len), Signature:Len/binary>>, _) ->
- {Params, undefined, Signature};
-dec_ske_signature(_, _, _) ->
- throw(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE)).
+decode_handshake(Version, Tag, Msg) ->
+ ssl_handshake:decode_handshake(Version, Tag, Msg).
-dec_server_key(<<?UINT16(PLen), P:PLen/binary,
- ?UINT16(GLen), G:GLen/binary,
- ?UINT16(YLen), Y:YLen/binary, _/binary>> = KeyStruct,
- ?KEY_EXCHANGE_DIFFIE_HELLMAN, Version) ->
- Params = #server_dh_params{dh_p = P, dh_g = G, dh_y = Y},
- {BinMsg, HashSign, Signature} = dec_ske_params(PLen + GLen + YLen + 6, KeyStruct, Version),
- #server_key_params{params = Params,
- params_bin = BinMsg,
- hashsign = HashSign,
- signature = Signature};
-%% ECParameters with named_curve
-%% TODO: explicit curve
-dec_server_key(<<?BYTE(?NAMED_CURVE), ?UINT16(CurveID),
- ?BYTE(PointLen), ECPoint:PointLen/binary,
- _/binary>> = KeyStruct,
- ?KEY_EXCHANGE_EC_DIFFIE_HELLMAN, Version) ->
- Params = #server_ecdh_params{curve = {namedCurve, ssl_tls1:enum_to_oid(CurveID)},
- public = ECPoint},
- {BinMsg, HashSign, Signature} = dec_ske_params(PointLen + 4, KeyStruct, Version),
- #server_key_params{params = Params,
- params_bin = BinMsg,
- hashsign = HashSign,
- signature = Signature};
-dec_server_key(<<?UINT16(Len), PskIdentityHint:Len/binary, _/binary>> = KeyStruct,
- KeyExchange, Version)
- when KeyExchange == ?KEY_EXCHANGE_PSK; KeyExchange == ?KEY_EXCHANGE_RSA_PSK ->
- Params = #server_psk_params{
- hint = PskIdentityHint},
- {BinMsg, HashSign, Signature} = dec_ske_params(Len + 2, KeyStruct, Version),
- #server_key_params{params = Params,
- params_bin = BinMsg,
- hashsign = HashSign,
- signature = Signature};
-dec_server_key(<<?UINT16(Len), IdentityHint:Len/binary,
- ?UINT16(PLen), P:PLen/binary,
- ?UINT16(GLen), G:GLen/binary,
- ?UINT16(YLen), Y:YLen/binary, _/binary>> = KeyStruct,
- ?KEY_EXCHANGE_DHE_PSK, Version) ->
- DHParams = #server_dh_params{dh_p = P, dh_g = G, dh_y = Y},
- Params = #server_dhe_psk_params{
- hint = IdentityHint,
- dh_params = DHParams},
- {BinMsg, HashSign, Signature} = dec_ske_params(Len + PLen + GLen + YLen + 8, KeyStruct, Version),
- #server_key_params{params = Params,
- params_bin = BinMsg,
- hashsign = HashSign,
- signature = Signature};
-dec_server_key(<<?UINT16(NLen), N:NLen/binary,
- ?UINT16(GLen), G:GLen/binary,
- ?BYTE(SLen), S:SLen/binary,
- ?UINT16(BLen), B:BLen/binary, _/binary>> = KeyStruct,
- ?KEY_EXCHANGE_SRP, Version) ->
- Params = #server_srp_params{srp_n = N, srp_g = G, srp_s = S, srp_b = B},
- {BinMsg, HashSign, Signature} = dec_ske_params(NLen + GLen + SLen + BLen + 7, KeyStruct, Version),
- #server_key_params{params = Params,
- params_bin = BinMsg,
- hashsign = HashSign,
- signature = Signature};
-dec_server_key(_, _, _) ->
- throw(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE)).
-
-dec_hello_extensions(<<>>) ->
- [];
-dec_hello_extensions(<<?UINT16(ExtLen), Extensions:ExtLen/binary>>) ->
- dec_hello_extensions(Extensions, []);
-dec_hello_extensions(_) ->
- [].
-
-dec_hello_extensions(<<>>, Acc) ->
- Acc;
-dec_hello_extensions(<<?UINT16(?NEXTPROTONEG_EXT), ?UINT16(Len), ExtensionData:Len/binary, Rest/binary>>, Acc) ->
- Prop = {next_protocol_negotiation, #next_protocol_negotiation{extension_data = ExtensionData}},
- dec_hello_extensions(Rest, [Prop | Acc]);
-dec_hello_extensions(<<?UINT16(?RENEGOTIATION_EXT), ?UINT16(Len), Info:Len/binary, Rest/binary>>, Acc) ->
- RenegotiateInfo = case Len of
- 1 -> % Initial handshake
- Info; % should be <<0>> will be matched in handle_renegotiation_info
- _ ->
- VerifyLen = Len - 1,
- <<?BYTE(VerifyLen), VerifyInfo/binary>> = Info,
- VerifyInfo
- end,
- dec_hello_extensions(Rest, [{renegotiation_info,
- #renegotiation_info{renegotiated_connection = RenegotiateInfo}} | Acc]);
-
-dec_hello_extensions(<<?UINT16(?SRP_EXT), ?UINT16(Len), ?BYTE(SRPLen), SRP:SRPLen/binary, Rest/binary>>, Acc)
- when Len == SRPLen + 2 ->
- dec_hello_extensions(Rest, [{srp,
- #srp{username = SRP}} | Acc]);
-
-dec_hello_extensions(<<?UINT16(?SIGNATURE_ALGORITHMS_EXT), ?UINT16(Len),
- ExtData:Len/binary, Rest/binary>>, Acc) ->
- SignAlgoListLen = Len - 2,
- <<?UINT16(SignAlgoListLen), SignAlgoList/binary>> = ExtData,
- HashSignAlgos = [{ssl_cipher:hash_algorithm(Hash), ssl_cipher:sign_algorithm(Sign)} ||
- <<?BYTE(Hash), ?BYTE(Sign)>> <= SignAlgoList],
- dec_hello_extensions(Rest, [{hash_signs,
- #hash_sign_algos{hash_sign_algos = HashSignAlgos}} | Acc]);
-
-dec_hello_extensions(<<?UINT16(?ELLIPTIC_CURVES_EXT), ?UINT16(Len),
- ExtData:Len/binary, Rest/binary>>, Acc) ->
- <<?UINT16(_), EllipticCurveList/binary>> = ExtData,
- EllipticCurves = [ssl_tls1:enum_to_oid(X) || <<X:16>> <= EllipticCurveList],
- dec_hello_extensions(Rest, [{elliptic_curves,
- #elliptic_curves{elliptic_curve_list = EllipticCurves}} | Acc]);
-
-dec_hello_extensions(<<?UINT16(?EC_POINT_FORMATS_EXT), ?UINT16(Len),
- ExtData:Len/binary, Rest/binary>>, Acc) ->
- <<?BYTE(_), ECPointFormatList/binary>> = ExtData,
- ECPointFormats = binary_to_list(ECPointFormatList),
- dec_hello_extensions(Rest, [{ec_point_formats,
- #ec_point_formats{ec_point_format_list = ECPointFormats}} | Acc]);
-
-%% Ignore data following the ClientHello (i.e.,
-%% extensions) if not understood.
-
-dec_hello_extensions(<<?UINT16(_), ?UINT16(Len), _Unknown:Len/binary, Rest/binary>>, Acc) ->
- dec_hello_extensions(Rest, Acc);
-%% This theoretically should not happen if the protocol is followed, but if it does it is ignored.
-dec_hello_extensions(_, Acc) ->
- Acc.
-
-encrypted_premaster_secret(Secret, RSAPublicKey) ->
- try
- PreMasterSecret = public_key:encrypt_public(Secret, RSAPublicKey,
- [{rsa_pad,
- rsa_pkcs1_padding}]),
- #encrypted_premaster_secret{premaster_secret = PreMasterSecret}
- catch
- _:_->
- throw(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE))
- end.
-
-%% encode/decode stream of certificate data to/from list of certificate data
-certs_to_list(ASN1Certs) ->
- certs_to_list(ASN1Certs, []).
-
-certs_to_list(<<?UINT24(CertLen), Cert:CertLen/binary, Rest/binary>>, Acc) ->
- certs_to_list(Rest, [Cert | Acc]);
-certs_to_list(<<>>, Acc) ->
- lists:reverse(Acc, []).
-
-certs_from_list(ACList) ->
- list_to_binary([begin
- CertLen = byte_size(Cert),
- <<?UINT24(CertLen), Cert/binary>>
- end || Cert <- ACList]).
-
-enc_hs(#next_protocol{selected_protocol = SelectedProtocol}, _Version) ->
- PaddingLength = 32 - ((byte_size(SelectedProtocol) + 2) rem 32),
-
- {?NEXT_PROTOCOL, <<?BYTE((byte_size(SelectedProtocol))), SelectedProtocol/binary,
- ?BYTE(PaddingLength), 0:(PaddingLength * 8)>>};
-enc_hs(#hello_request{}, _Version) ->
+enc_handshake(#hello_request{}, _Version) ->
{?HELLO_REQUEST, <<>>};
-enc_hs(#client_hello{client_version = {Major, Minor},
+enc_handshake(#client_hello{client_version = {Major, Minor},
random = Random,
session_id = SessionID,
cipher_suites = CipherSuites,
compression_methods = CompMethods,
- renegotiation_info = RenegotiationInfo,
- srp = SRP,
- hash_signs = HashSigns,
- ec_point_formats = EcPointFormats,
- elliptic_curves = EllipticCurves,
- next_protocol_negotiation = NextProtocolNegotiation}, _Version) ->
+ extensions = HelloExtensions}, _Version) ->
SIDLength = byte_size(SessionID),
BinCompMethods = list_to_binary(CompMethods),
CmLength = byte_size(BinCompMethods),
BinCipherSuites = list_to_binary(CipherSuites),
CsLength = byte_size(BinCipherSuites),
- Extensions0 = hello_extensions(RenegotiationInfo, SRP, NextProtocolNegotiation)
- ++ ec_hello_extensions(lists:map(fun ssl_cipher:suite_definition/1, CipherSuites), EcPointFormats)
- ++ ec_hello_extensions(lists:map(fun ssl_cipher:suite_definition/1, CipherSuites), EllipticCurves),
- Extensions1 = if
- Major == 3, Minor >=3 -> Extensions0 ++ hello_extensions(HashSigns);
- true -> Extensions0
- end,
- ExtensionsBin = enc_hello_extensions(Extensions1),
-
- {?CLIENT_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary,
- ?BYTE(SIDLength), SessionID/binary,
- ?UINT16(CsLength), BinCipherSuites/binary,
- ?BYTE(CmLength), BinCompMethods/binary, ExtensionsBin/binary>>};
-
-enc_hs(#server_hello{server_version = {Major, Minor},
- random = Random,
- session_id = Session_ID,
- cipher_suite = CipherSuite,
- compression_method = Comp_method,
- renegotiation_info = RenegotiationInfo,
- ec_point_formats = EcPointFormats,
- elliptic_curves = EllipticCurves,
- next_protocol_negotiation = NextProtocolNegotiation}, _Version) ->
- SID_length = byte_size(Session_ID),
- CipherSuites = [ssl_cipher:suite_definition(CipherSuite)],
- Extensions = hello_extensions(RenegotiationInfo, NextProtocolNegotiation)
- ++ ec_hello_extensions(CipherSuites, EcPointFormats)
- ++ ec_hello_extensions(CipherSuites, EllipticCurves),
- ExtensionsBin = enc_hello_extensions(Extensions),
- {?SERVER_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary,
- ?BYTE(SID_length), Session_ID/binary,
- CipherSuite/binary, ?BYTE(Comp_method), ExtensionsBin/binary>>};
-enc_hs(#certificate{asn1_certificates = ASN1CertList}, _Version) ->
- ASN1Certs = certs_from_list(ASN1CertList),
- ACLen = erlang:iolist_size(ASN1Certs),
- {?CERTIFICATE, <<?UINT24(ACLen), ASN1Certs:ACLen/binary>>};
-enc_hs(#server_key_exchange{exchange_keys = Keys}, _Version) ->
- {?SERVER_KEY_EXCHANGE, Keys};
-enc_hs(#server_key_params{params_bin = Keys, hashsign = HashSign,
- signature = Signature}, Version) ->
- EncSign = enc_sign(HashSign, Signature, Version),
- {?SERVER_KEY_EXCHANGE, <<Keys/binary, EncSign/binary>>};
-enc_hs(#certificate_request{certificate_types = CertTypes,
- hashsign_algorithms = #hash_sign_algos{hash_sign_algos = HashSignAlgos},
- certificate_authorities = CertAuths},
- {Major, Minor})
- when Major == 3, Minor >= 3 ->
- HashSigns= << <<(ssl_cipher:hash_algorithm(Hash)):8, (ssl_cipher:sign_algorithm(Sign)):8>> ||
- {Hash, Sign} <- HashSignAlgos >>,
- CertTypesLen = byte_size(CertTypes),
- HashSignsLen = byte_size(HashSigns),
- CertAuthsLen = byte_size(CertAuths),
- {?CERTIFICATE_REQUEST,
- <<?BYTE(CertTypesLen), CertTypes/binary,
- ?UINT16(HashSignsLen), HashSigns/binary,
- ?UINT16(CertAuthsLen), CertAuths/binary>>
- };
-enc_hs(#certificate_request{certificate_types = CertTypes,
- certificate_authorities = CertAuths},
- _Version) ->
- CertTypesLen = byte_size(CertTypes),
- CertAuthsLen = byte_size(CertAuths),
- {?CERTIFICATE_REQUEST,
- <<?BYTE(CertTypesLen), CertTypes/binary,
- ?UINT16(CertAuthsLen), CertAuths/binary>>
- };
-enc_hs(#server_hello_done{}, _Version) ->
- {?SERVER_HELLO_DONE, <<>>};
-enc_hs(#client_key_exchange{exchange_keys = ExchangeKeys}, Version) ->
- {?CLIENT_KEY_EXCHANGE, enc_cke(ExchangeKeys, Version)};
-enc_hs(#certificate_verify{signature = BinSig, hashsign_algorithm = HashSign}, Version) ->
- EncSig = enc_sign(HashSign, BinSig, Version),
- {?CERTIFICATE_VERIFY, EncSig};
-enc_hs(#finished{verify_data = VerifyData}, _Version) ->
- {?FINISHED, VerifyData}.
-
-enc_cke(#encrypted_premaster_secret{premaster_secret = PKEPMS},{3, 0}) ->
- PKEPMS;
-enc_cke(#encrypted_premaster_secret{premaster_secret = PKEPMS}, _) ->
- PKEPMSLen = byte_size(PKEPMS),
- <<?UINT16(PKEPMSLen), PKEPMS/binary>>;
-enc_cke(#client_diffie_hellman_public{dh_public = DHPublic}, _) ->
- Len = byte_size(DHPublic),
- <<?UINT16(Len), DHPublic/binary>>;
-enc_cke(#client_ec_diffie_hellman_public{dh_public = DHPublic}, _) ->
- Len = byte_size(DHPublic),
- <<?BYTE(Len), DHPublic/binary>>;
-enc_cke(#client_psk_identity{identity = undefined}, _) ->
- Id = <<"psk_identity">>,
- Len = byte_size(Id),
- <<?UINT16(Len), Id/binary>>;
-enc_cke(#client_psk_identity{identity = Id}, _) ->
- Len = byte_size(Id),
- <<?UINT16(Len), Id/binary>>;
-enc_cke(Identity = #client_dhe_psk_identity{identity = undefined}, Version) ->
- enc_cke(Identity#client_dhe_psk_identity{identity = <<"psk_identity">>}, Version);
-enc_cke(#client_dhe_psk_identity{identity = Id, dh_public = DHPublic}, _) ->
- Len = byte_size(Id),
- DHLen = byte_size(DHPublic),
- <<?UINT16(Len), Id/binary, ?UINT16(DHLen), DHPublic/binary>>;
-enc_cke(Identity = #client_rsa_psk_identity{identity = undefined}, Version) ->
- enc_cke(Identity#client_rsa_psk_identity{identity = <<"psk_identity">>}, Version);
-enc_cke(#client_rsa_psk_identity{identity = Id, exchange_keys = ExchangeKeys}, Version) ->
- EncPMS = enc_cke(ExchangeKeys, Version),
- Len = byte_size(Id),
- <<?UINT16(Len), Id/binary, EncPMS/binary>>;
-enc_cke(#client_srp_public{srp_a = A}, _) ->
- Len = byte_size(A),
- <<?UINT16(Len), A/binary>>.
-
-enc_server_key(#server_dh_params{dh_p = P, dh_g = G, dh_y = Y}) ->
- PLen = byte_size(P),
- GLen = byte_size(G),
- YLen = byte_size(Y),
- <<?UINT16(PLen), P/binary, ?UINT16(GLen), G/binary, ?UINT16(YLen), Y/binary>>;
-enc_server_key(#server_ecdh_params{curve = {namedCurve, ECCurve}, public = ECPubKey}) ->
- %%TODO: support arbitrary keys
- KLen = size(ECPubKey),
- <<?BYTE(?NAMED_CURVE_TYPE), ?UINT16((ssl_tls1:oid_to_enum(ECCurve))),
- ?BYTE(KLen), ECPubKey/binary>>;
-enc_server_key(#server_psk_params{hint = PskIdentityHint}) ->
- Len = byte_size(PskIdentityHint),
- <<?UINT16(Len), PskIdentityHint/binary>>;
-enc_server_key(Params = #server_dhe_psk_params{hint = undefined}) ->
- enc_server_key(Params#server_dhe_psk_params{hint = <<>>});
-enc_server_key(#server_dhe_psk_params{
- hint = PskIdentityHint,
- dh_params = #server_dh_params{dh_p = P, dh_g = G, dh_y = Y}}) ->
- Len = byte_size(PskIdentityHint),
- PLen = byte_size(P),
- GLen = byte_size(G),
- YLen = byte_size(Y),
- <<?UINT16(Len), PskIdentityHint/binary,
- ?UINT16(PLen), P/binary, ?UINT16(GLen), G/binary, ?UINT16(YLen), Y/binary>>;
-enc_server_key(#server_srp_params{srp_n = N, srp_g = G, srp_s = S, srp_b = B}) ->
- NLen = byte_size(N),
- GLen = byte_size(G),
- SLen = byte_size(S),
- BLen = byte_size(B),
- <<?UINT16(NLen), N/binary, ?UINT16(GLen), G/binary,
- ?BYTE(SLen), S/binary, ?UINT16(BLen), B/binary>>.
-
-enc_sign({_, anon}, _Sign, _Version) ->
- <<>>;
-enc_sign({HashAlg, SignAlg}, Signature, _Version = {Major, Minor})
- when Major == 3, Minor >= 3->
- SignLen = byte_size(Signature),
- HashSign = hashsign_enc(HashAlg, SignAlg),
- <<HashSign/binary, ?UINT16(SignLen), Signature/binary>>;
-enc_sign(_HashSign, Sign, _Version) ->
- SignLen = byte_size(Sign),
- <<?UINT16(SignLen), Sign/binary>>.
-
-
-ec_hello_extensions(CipherSuites, #elliptic_curves{} = Info) ->
- case advertises_ec_ciphers(CipherSuites) of
- true ->
- [Info];
- false ->
- []
- end;
-ec_hello_extensions(CipherSuites, #ec_point_formats{} = Info) ->
- case advertises_ec_ciphers(CipherSuites) of
- true ->
- [Info];
- false ->
- []
- end;
-ec_hello_extensions(_, undefined) ->
- [].
-
-hello_extensions(RenegotiationInfo, NextProtocolNegotiation) ->
- hello_extensions(RenegotiationInfo) ++ next_protocol_extension(NextProtocolNegotiation).
-
-hello_extensions(RenegotiationInfo, SRP, NextProtocolNegotiation) ->
- hello_extensions(RenegotiationInfo)
- ++ hello_extensions(SRP)
- ++ next_protocol_extension(NextProtocolNegotiation).
-
-advertises_ec_ciphers([]) ->
- false;
-advertises_ec_ciphers([{ecdh_ecdsa, _,_,_} | _]) ->
- true;
-advertises_ec_ciphers([{ecdhe_ecdsa, _,_,_} | _]) ->
- true;
-advertises_ec_ciphers([{ecdh_rsa, _,_,_} | _]) ->
- true;
-advertises_ec_ciphers([{ecdhe_rsa, _,_,_} | _]) ->
- true;
-advertises_ec_ciphers([{ecdh_anon, _,_,_} | _]) ->
- true;
-advertises_ec_ciphers([_| Rest]) ->
- advertises_ec_ciphers(Rest).
+ ExtensionsBin = ssl_handshake:encode_hello_extensions(HelloExtensions),
-%% Renegotiation info
-hello_extensions(#renegotiation_info{renegotiated_connection = undefined}) ->
- [];
-hello_extensions(#renegotiation_info{} = Info) ->
- [Info];
-hello_extensions(#srp{} = Info) ->
- [Info];
-hello_extensions(#hash_sign_algos{} = Info) ->
- [Info];
-hello_extensions(undefined) ->
- [].
+ {?CLIENT_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary,
+ ?BYTE(SIDLength), SessionID/binary,
+ ?UINT16(CsLength), BinCipherSuites/binary,
+ ?BYTE(CmLength), BinCompMethods/binary, ExtensionsBin/binary>>};
-next_protocol_extension(undefined) ->
- [];
-next_protocol_extension(#next_protocol_negotiation{} = Info) ->
- [Info].
+enc_handshake(HandshakeMsg, Version) ->
+ ssl_handshake:encode_handshake(HandshakeMsg, Version).
-enc_hello_extensions(Extensions) ->
- enc_hello_extensions(Extensions, <<>>).
-enc_hello_extensions([], <<>>) ->
- <<>>;
-enc_hello_extensions([], Acc) ->
- Size = byte_size(Acc),
- <<?UINT16(Size), Acc/binary>>;
-enc_hello_extensions([#next_protocol_negotiation{extension_data = ExtensionData} | Rest], Acc) ->
- Len = byte_size(ExtensionData),
- enc_hello_extensions(Rest, <<?UINT16(?NEXTPROTONEG_EXT), ?UINT16(Len), ExtensionData/binary, Acc/binary>>);
-enc_hello_extensions([#renegotiation_info{renegotiated_connection = ?byte(0) = Info} | Rest], Acc) ->
- Len = byte_size(Info),
- enc_hello_extensions(Rest, <<?UINT16(?RENEGOTIATION_EXT), ?UINT16(Len), Info/binary, Acc/binary>>);
-
-enc_hello_extensions([#renegotiation_info{renegotiated_connection = Info} | Rest], Acc) ->
- InfoLen = byte_size(Info),
- Len = InfoLen +1,
- enc_hello_extensions(Rest, <<?UINT16(?RENEGOTIATION_EXT), ?UINT16(Len), ?BYTE(InfoLen), Info/binary, Acc/binary>>);
-enc_hello_extensions([#elliptic_curves{elliptic_curve_list = EllipticCurves} | Rest], Acc) ->
- EllipticCurveList = << <<(ssl_tls1:oid_to_enum(X)):16>> || X <- EllipticCurves>>,
- ListLen = byte_size(EllipticCurveList),
- Len = ListLen + 2,
- enc_hello_extensions(Rest, <<?UINT16(?ELLIPTIC_CURVES_EXT),
- ?UINT16(Len), ?UINT16(ListLen), EllipticCurveList/binary, Acc/binary>>);
-enc_hello_extensions([#ec_point_formats{ec_point_format_list = ECPointFormats} | Rest], Acc) ->
- ECPointFormatList = list_to_binary(ECPointFormats),
- ListLen = byte_size(ECPointFormatList),
- Len = ListLen + 1,
- enc_hello_extensions(Rest, <<?UINT16(?EC_POINT_FORMATS_EXT),
- ?UINT16(Len), ?BYTE(ListLen), ECPointFormatList/binary, Acc/binary>>);
-enc_hello_extensions([#srp{username = UserName} | Rest], Acc) ->
- SRPLen = byte_size(UserName),
- Len = SRPLen + 2,
- enc_hello_extensions(Rest, <<?UINT16(?SRP_EXT), ?UINT16(Len), ?BYTE(SRPLen), UserName/binary, Acc/binary>>);
-enc_hello_extensions([#hash_sign_algos{hash_sign_algos = HashSignAlgos} | Rest], Acc) ->
- SignAlgoList = << <<(ssl_cipher:hash_algorithm(Hash)):8, (ssl_cipher:sign_algorithm(Sign)):8>> ||
- {Hash, Sign} <- HashSignAlgos >>,
- ListLen = byte_size(SignAlgoList),
- Len = ListLen + 2,
- enc_hello_extensions(Rest, <<?UINT16(?SIGNATURE_ALGORITHMS_EXT),
- ?UINT16(Len), ?UINT16(ListLen), SignAlgoList/binary, Acc/binary>>).
-
-encode_client_protocol_negotiation(undefined, _) ->
- undefined;
-encode_client_protocol_negotiation(_, false) ->
- #next_protocol_negotiation{extension_data = <<>>};
-encode_client_protocol_negotiation(_, _) ->
- undefined.
-
-from_3bytes(Bin3) ->
- from_3bytes(Bin3, []).
-
-from_3bytes(<<>>, Acc) ->
- lists:reverse(Acc);
-from_3bytes(<<?UINT24(N), Rest/binary>>, Acc) ->
- from_3bytes(Rest, [?uint16(N) | Acc]).
-
-from_2bytes(Bin2) ->
- from_2bytes(Bin2, []).
-
-from_2bytes(<<>>, Acc) ->
- lists:reverse(Acc);
-from_2bytes(<<?UINT16(N), Rest/binary>>, Acc) ->
- from_2bytes(Rest, [?uint16(N) | Acc]).
-
-certificate_types({KeyExchange, _, _, _})
- when KeyExchange == rsa;
- KeyExchange == dhe_dss;
- KeyExchange == dhe_rsa;
- KeyExchange == ecdhe_rsa ->
- <<?BYTE(?RSA_SIGN), ?BYTE(?DSS_SIGN)>>;
-
-certificate_types({KeyExchange, _, _, _})
- when KeyExchange == dh_ecdsa;
- KeyExchange == dhe_ecdsa ->
- <<?BYTE(?ECDSA_SIGN)>>;
-
-certificate_types(_) ->
- <<?BYTE(?RSA_SIGN)>>.
-
-hashsign_dec(<<?BYTE(HashAlgo), ?BYTE(SignAlgo)>>) ->
- {ssl_cipher:hash_algorithm(HashAlgo), ssl_cipher:sign_algorithm(SignAlgo)}.
-
-hashsign_enc(HashAlgo, SignAlgo) ->
- Hash = ssl_cipher:hash_algorithm(HashAlgo),
- Sign = ssl_cipher:sign_algorithm(SignAlgo),
- <<?BYTE(Hash), ?BYTE(Sign)>>.
-
-certificate_authorities(CertDbHandle, CertDbRef) ->
- Authorities = certificate_authorities_from_db(CertDbHandle, CertDbRef),
- Enc = fun(#'OTPCertificate'{tbsCertificate=TBSCert}) ->
- OTPSubj = TBSCert#'OTPTBSCertificate'.subject,
- DNEncodedBin = public_key:pkix_encode('Name', OTPSubj, otp),
- DNEncodedLen = byte_size(DNEncodedBin),
- <<?UINT16(DNEncodedLen), DNEncodedBin/binary>>
- end,
- list_to_binary([Enc(Cert) || {_, Cert} <- Authorities]).
-
-certificate_authorities_from_db(CertDbHandle, CertDbRef) ->
- ConnectionCerts = fun({{Ref, _, _}, Cert}, Acc) when Ref == CertDbRef ->
- [Cert | Acc];
- (_, Acc) ->
- Acc
- end,
- ssl_pkix_db:foldl(ConnectionCerts, [], CertDbHandle).
-
-
-digitally_signed({3, Minor}, Hash, HashAlgo, Key) when Minor >= 3 ->
- public_key:sign({digest, Hash}, HashAlgo, Key);
-digitally_signed(_Version, Hash, HashAlgo, #'DSAPrivateKey'{} = Key) ->
- public_key:sign({digest, Hash}, HashAlgo, Key);
-digitally_signed(_Version, Hash, _HashAlgo, #'RSAPrivateKey'{} = Key) ->
- public_key:encrypt_private(Hash, Key,
- [{rsa_pad, rsa_pkcs1_padding}]);
-digitally_signed(_Version, Hash, HashAlgo, Key) ->
- public_key:sign({digest, Hash}, HashAlgo, Key).
-
-calc_master_secret({3,0}, _PrfAlgo, PremasterSecret, ClientRandom, ServerRandom) ->
- ssl_ssl3:master_secret(PremasterSecret, ClientRandom, ServerRandom);
-
-calc_master_secret({3,_}, PrfAlgo, PremasterSecret, ClientRandom, ServerRandom) ->
- ssl_tls1:master_secret(PrfAlgo, PremasterSecret, ClientRandom, ServerRandom).
-
-setup_keys({3,0}, _PrfAlgo, MasterSecret,
- ServerRandom, ClientRandom, HashSize, KML, EKML, IVS) ->
- ssl_ssl3:setup_keys(MasterSecret, ServerRandom,
- ClientRandom, HashSize, KML, EKML, IVS);
-
-setup_keys({3,N}, PrfAlgo, MasterSecret,
- ServerRandom, ClientRandom, HashSize, KML, _EKML, IVS) ->
- ssl_tls1:setup_keys(N, PrfAlgo, MasterSecret, ServerRandom, ClientRandom, HashSize,
- KML, IVS).
-
-calc_finished({3, 0}, Role, _PrfAlgo, MasterSecret, Handshake) ->
- ssl_ssl3:finished(Role, MasterSecret, lists:reverse(Handshake));
-calc_finished({3, N}, Role, PrfAlgo, MasterSecret, Handshake) ->
- ssl_tls1:finished(Role, N, PrfAlgo, MasterSecret, lists:reverse(Handshake)).
-
-calc_certificate_verify({3, 0}, HashAlgo, MasterSecret, Handshake) ->
- ssl_ssl3:certificate_verify(HashAlgo, MasterSecret, lists:reverse(Handshake));
-calc_certificate_verify({3, N}, HashAlgo, _MasterSecret, Handshake) ->
- ssl_tls1:certificate_verify(HashAlgo, N, lists:reverse(Handshake)).
-
-key_exchange_alg(rsa) ->
- ?KEY_EXCHANGE_RSA;
-key_exchange_alg(Alg) when Alg == dhe_rsa; Alg == dhe_dss;
- Alg == dh_dss; Alg == dh_rsa; Alg == dh_anon ->
- ?KEY_EXCHANGE_DIFFIE_HELLMAN;
-key_exchange_alg(Alg) when Alg == ecdhe_rsa; Alg == ecdh_rsa;
- Alg == ecdhe_ecdsa; Alg == ecdh_ecdsa;
- Alg == ecdh_anon ->
- ?KEY_EXCHANGE_EC_DIFFIE_HELLMAN;
-key_exchange_alg(psk) ->
- ?KEY_EXCHANGE_PSK;
-key_exchange_alg(dhe_psk) ->
- ?KEY_EXCHANGE_DHE_PSK;
-key_exchange_alg(rsa_psk) ->
- ?KEY_EXCHANGE_RSA_PSK;
-key_exchange_alg(Alg)
- when Alg == srp_rsa; Alg == srp_dss; Alg == srp_anon ->
- ?KEY_EXCHANGE_SRP;
-key_exchange_alg(_) ->
- ?NULL.
-
-apply_user_fun(Fun, OtpCert, ExtensionOrError, UserState0, SslState) ->
- case Fun(OtpCert, ExtensionOrError, UserState0) of
- {valid, UserState} ->
- {valid, {SslState, UserState}};
- {fail, _} = Fail ->
- Fail;
- {unknown, UserState} ->
- {unknown, {SslState, UserState}}
+handle_hello_extensions(Version, Type, Random, HelloExt, SslOpts, Session0, ConnectionStates0, Renegotiation) ->
+ try ssl_handshake:handle_client_hello_extensions(tls_record, Random, HelloExt, Version, SslOpts,
+ Session0, ConnectionStates0, Renegotiation) of
+ {Session, ConnectionStates, ServerHelloExt} ->
+ {Version, {Type, Session}, ConnectionStates, ServerHelloExt}
+ catch throw:Alert ->
+ Alert
end.
-handle_hello_extensions(#client_hello{random = Random,
- cipher_suites = CipherSuites,
- renegotiation_info = Info,
- srp = SRP,
- ec_point_formats = EcPointFormats0,
- elliptic_curves = EllipticCurves0} = Hello, Version,
- #ssl_options{secure_renegotiate = SecureRenegotation} = Opts,
- Session0, ConnectionStates0, Renegotiation) ->
- Session = handle_srp_extension(SRP, Session0),
- ConnectionStates = handle_renegotiation_extension(Version, Info, Random, Session, ConnectionStates0,
- Renegotiation, SecureRenegotation, CipherSuites),
- ProtocolsToAdvertise = handle_next_protocol_extension(Hello, Renegotiation, Opts),
- {EcPointFormats, EllipticCurves} = handle_ecc_extensions(Version, EcPointFormats0, EllipticCurves0),
- %%TODO make extensions compund data structure
- {Session, ConnectionStates, ProtocolsToAdvertise, EcPointFormats, EllipticCurves}.
-
-handle_renegotiation_extension(Version, Info, Random, #session{cipher_suite = CipherSuite,
- compression_method = Compression},
- ConnectionStates0, Renegotiation, SecureRenegotation, CipherSuites) ->
- case handle_renegotiation_info(server, Info, ConnectionStates0,
- Renegotiation, SecureRenegotation,
- CipherSuites) of
- {ok, ConnectionStates1} ->
- hello_pending_connection_states(server,
- Version,
- CipherSuite,
- Random,
- Compression,
- ConnectionStates1);
+handle_hello_extensions(Version, SessionId, Random, CipherSuite,
+ Compression, HelloExt, SslOpt, ConnectionStates0, Renegotiation) ->
+ case ssl_handshake:handle_server_hello_extensions(tls_record, Random, CipherSuite,
+ Compression, HelloExt, Version,
+ SslOpt, ConnectionStates0, Renegotiation) of
#alert{} = Alert ->
- throw(Alert)
+ Alert;
+ {ConnectionStates, Protocol} ->
+ {Version, SessionId, ConnectionStates, Protocol}
end.
-handle_next_protocol_extension(Hello, Renegotiation, SslOpts)->
- case handle_next_protocol_on_server(Hello, Renegotiation, SslOpts) of
- #alert{} = Alert ->
- throw(Alert);
- ProtocolsToAdvertise ->
- ProtocolsToAdvertise
- end.
-
-handle_srp_extension(undefined, Session) ->
- Session;
-handle_srp_extension(#srp{username = Username}, Session) ->
- Session#session{srp_username = Username}.
-
-int_to_bin(I) ->
- L = (length(integer_to_list(I, 16)) + 1) div 2,
- <<I:(L*8)>>.
-
--define(TLSEXT_SIGALG_RSA(MD), {MD, rsa}).
--define(TLSEXT_SIGALG_DSA(MD), {MD, dsa}).
--define(TLSEXT_SIGALG_ECDSA(MD), {MD, ecdsa}).
-
--define(TLSEXT_SIGALG(MD), ?TLSEXT_SIGALG_ECDSA(MD), ?TLSEXT_SIGALG_RSA(MD)).
-
-advertised_hash_signs({Major, Minor}) when Major >= 3 andalso Minor >= 3 ->
- HashSigns = [?TLSEXT_SIGALG(sha512),
- ?TLSEXT_SIGALG(sha384),
- ?TLSEXT_SIGALG(sha256),
- ?TLSEXT_SIGALG(sha224),
- ?TLSEXT_SIGALG(sha),
- ?TLSEXT_SIGALG_DSA(sha),
- ?TLSEXT_SIGALG_RSA(md5)],
- CryptoSupport = crypto:supports(),
- HasECC = proplists:get_bool(ecdsa, proplists:get_value(public_keys, CryptoSupport)),
- Hashs = proplists:get_value(hashs, CryptoSupport),
- #hash_sign_algos{hash_sign_algos =
- lists:filter(fun({Hash, ecdsa}) -> HasECC andalso proplists:get_bool(Hash, Hashs);
- ({Hash, _}) -> proplists:get_bool(Hash, Hashs) end, HashSigns)};
-advertised_hash_signs(_) ->
- undefined.
diff --git a/lib/ssl/src/tls_handshake.hrl b/lib/ssl/src/tls_handshake.hrl
index abf1b5abb6..dbe930cb90 100644
--- a/lib/ssl/src/tls_handshake.hrl
+++ b/lib/ssl/src/tls_handshake.hrl
@@ -34,12 +34,9 @@
cipher_suites, % cipher_suites<2..2^16-1>
compression_methods, % compression_methods<1..2^8-1>,
%% Extensions
- renegotiation_info,
- hash_signs, % supported combinations of hashes/signature algos
- next_protocol_negotiation = undefined, % [binary()]
- srp,
- ec_point_formats,
- elliptic_curves
+ extensions
}).
+-type tls_handshake() :: #client_hello{} | ssl_handshake().
+
-endif. % -ifdef(tls_handshake).
diff --git a/lib/ssl/src/tls_record.erl b/lib/ssl/src/tls_record.erl
index 1409a04763..54cf8d0b80 100644
--- a/lib/ssl/src/tls_record.erl
+++ b/lib/ssl/src/tls_record.erl
@@ -19,8 +19,7 @@
%%
%%----------------------------------------------------------------------
-%% Purpose: Help functions for handling the SSL-Record protocol
-%%
+%% Purpose: Handle TLS/SSL record protocol. (Parts that are not shared with DTLS)
%%----------------------------------------------------------------------
-module(tls_record).
@@ -31,282 +30,27 @@
-include("tls_handshake.hrl").
-include("ssl_cipher.hrl").
-%% Connection state handling
--export([init_connection_states/1,
- current_connection_state/2, pending_connection_state/2,
- update_security_params/3,
- set_mac_secret/4,
- set_master_secret/2,
- activate_pending_connection_state/2,
- set_pending_cipher_state/4,
- set_renegotiation_flag/2,
- set_client_verify_data/3,
- set_server_verify_data/3]).
-
%% Handling of incoming data
-export([get_tls_records/2]).
-%% Encoding records
--export([encode_handshake/3, encode_alert_record/3,
- encode_change_cipher_spec/2, encode_data/3]).
-
%% Decoding
-export([decode_cipher_text/2]).
-%% Misc.
+%% Encoding
+-export([encode_plain_text/4]).
+
+%% Protocol version handling
-export([protocol_version/1, lowest_protocol_version/2,
highest_protocol_version/1, supported_protocol_versions/0,
is_acceptable_version/1, is_acceptable_version/2]).
--export([compressions/0]).
-
-compile(inline).
--define(INITIAL_BYTES, 5).
-
%%====================================================================
%% Internal application API
%%====================================================================
%%--------------------------------------------------------------------
--spec init_connection_states(client | server) -> #connection_states{}.
-%%
-%% Description: Creates a connection_states record with appropriate
-%% values for the initial SSL connection setup.
-%%--------------------------------------------------------------------
-init_connection_states(Role) ->
- ConnectionEnd = record_protocol_role(Role),
- Current = initial_connection_state(ConnectionEnd),
- Pending = empty_connection_state(ConnectionEnd),
- #connection_states{current_read = Current,
- pending_read = Pending,
- current_write = Current,
- pending_write = Pending
- }.
-
-%%--------------------------------------------------------------------
--spec current_connection_state(#connection_states{}, read | write) ->
- #connection_state{}.
-%%
-%% Description: Returns the instance of the connection_state record
-%% that is currently defined as the current conection state.
-%%--------------------------------------------------------------------
-current_connection_state(#connection_states{current_read = Current},
- read) ->
- Current;
-current_connection_state(#connection_states{current_write = Current},
- write) ->
- Current.
-
-%%--------------------------------------------------------------------
--spec pending_connection_state(#connection_states{}, read | write) ->
- #connection_state{}.
-%%
-%% Description: Returns the instance of the connection_state record
-%% that is currently defined as the pending conection state.
-%%--------------------------------------------------------------------
-pending_connection_state(#connection_states{pending_read = Pending},
- read) ->
- Pending;
-pending_connection_state(#connection_states{pending_write = Pending},
- write) ->
- Pending.
-
-%%--------------------------------------------------------------------
--spec update_security_params(#security_parameters{}, #security_parameters{},
- #connection_states{}) -> #connection_states{}.
-%%
-%% Description: Creates a new instance of the connection_states record
-%% where the pending states gets its security parameters updated.
-%%--------------------------------------------------------------------
-update_security_params(ReadParams, WriteParams, States =
- #connection_states{pending_read = Read,
- pending_write = Write}) ->
- States#connection_states{pending_read =
- Read#connection_state{security_parameters =
- ReadParams},
- pending_write =
- Write#connection_state{security_parameters =
- WriteParams}
- }.
-%%--------------------------------------------------------------------
--spec set_mac_secret(binary(), binary(), client | server,
- #connection_states{}) -> #connection_states{}.
-%%
-%% Description: update the mac_secret field in pending connection states
-%%--------------------------------------------------------------------
-set_mac_secret(ClientWriteMacSecret, ServerWriteMacSecret, client, States) ->
- set_mac_secret(ServerWriteMacSecret, ClientWriteMacSecret, States);
-set_mac_secret(ClientWriteMacSecret, ServerWriteMacSecret, server, States) ->
- set_mac_secret(ClientWriteMacSecret, ServerWriteMacSecret, States).
-
-set_mac_secret(ReadMacSecret, WriteMacSecret,
- States = #connection_states{pending_read = Read,
- pending_write = Write}) ->
- States#connection_states{
- pending_read = Read#connection_state{mac_secret = ReadMacSecret},
- pending_write = Write#connection_state{mac_secret = WriteMacSecret}
- }.
-
-
-%%--------------------------------------------------------------------
--spec set_master_secret(binary(), #connection_states{}) -> #connection_states{}.
-%%
-%% Description: Set master_secret in pending connection states
-%%--------------------------------------------------------------------
-set_master_secret(MasterSecret,
- States = #connection_states{pending_read = Read,
- pending_write = Write}) ->
- ReadSecPar = Read#connection_state.security_parameters,
- Read1 = Read#connection_state{
- security_parameters = ReadSecPar#security_parameters{
- master_secret = MasterSecret}},
- WriteSecPar = Write#connection_state.security_parameters,
- Write1 = Write#connection_state{
- security_parameters = WriteSecPar#security_parameters{
- master_secret = MasterSecret}},
- States#connection_states{pending_read = Read1, pending_write = Write1}.
-
-%%--------------------------------------------------------------------
--spec set_renegotiation_flag(boolean(), #connection_states{}) -> #connection_states{}.
-%%
-%% Description: Set secure_renegotiation in pending connection states
-%%--------------------------------------------------------------------
-set_renegotiation_flag(Flag, #connection_states{
- current_read = CurrentRead0,
- current_write = CurrentWrite0,
- pending_read = PendingRead0,
- pending_write = PendingWrite0}
- = ConnectionStates) ->
- CurrentRead = CurrentRead0#connection_state{secure_renegotiation = Flag},
- CurrentWrite = CurrentWrite0#connection_state{secure_renegotiation = Flag},
- PendingRead = PendingRead0#connection_state{secure_renegotiation = Flag},
- PendingWrite = PendingWrite0#connection_state{secure_renegotiation = Flag},
- ConnectionStates#connection_states{current_read = CurrentRead,
- current_write = CurrentWrite,
- pending_read = PendingRead,
- pending_write = PendingWrite}.
-
-%%--------------------------------------------------------------------
--spec set_client_verify_data(current_read | current_write | current_both,
- binary(), #connection_states{})->
- #connection_states{}.
-%%
-%% Description: Set verify data in connection states.
-%%--------------------------------------------------------------------
-set_client_verify_data(current_read, Data,
- #connection_states{current_read = CurrentRead0,
- pending_write = PendingWrite0}
- = ConnectionStates) ->
- CurrentRead = CurrentRead0#connection_state{client_verify_data = Data},
- PendingWrite = PendingWrite0#connection_state{client_verify_data = Data},
- ConnectionStates#connection_states{current_read = CurrentRead,
- pending_write = PendingWrite};
-set_client_verify_data(current_write, Data,
- #connection_states{pending_read = PendingRead0,
- current_write = CurrentWrite0}
- = ConnectionStates) ->
- PendingRead = PendingRead0#connection_state{client_verify_data = Data},
- CurrentWrite = CurrentWrite0#connection_state{client_verify_data = Data},
- ConnectionStates#connection_states{pending_read = PendingRead,
- current_write = CurrentWrite};
-set_client_verify_data(current_both, Data,
- #connection_states{current_read = CurrentRead0,
- current_write = CurrentWrite0}
- = ConnectionStates) ->
- CurrentRead = CurrentRead0#connection_state{client_verify_data = Data},
- CurrentWrite = CurrentWrite0#connection_state{client_verify_data = Data},
- ConnectionStates#connection_states{current_read = CurrentRead,
- current_write = CurrentWrite}.
-%%--------------------------------------------------------------------
--spec set_server_verify_data(current_read | current_write | current_both,
- binary(), #connection_states{})->
- #connection_states{}.
-%%
-%% Description: Set verify data in pending connection states.
-%%--------------------------------------------------------------------
-set_server_verify_data(current_write, Data,
- #connection_states{pending_read = PendingRead0,
- current_write = CurrentWrite0}
- = ConnectionStates) ->
- PendingRead = PendingRead0#connection_state{server_verify_data = Data},
- CurrentWrite = CurrentWrite0#connection_state{server_verify_data = Data},
- ConnectionStates#connection_states{pending_read = PendingRead,
- current_write = CurrentWrite};
-
-set_server_verify_data(current_read, Data,
- #connection_states{current_read = CurrentRead0,
- pending_write = PendingWrite0}
- = ConnectionStates) ->
- CurrentRead = CurrentRead0#connection_state{server_verify_data = Data},
- PendingWrite = PendingWrite0#connection_state{server_verify_data = Data},
- ConnectionStates#connection_states{current_read = CurrentRead,
- pending_write = PendingWrite};
-
-set_server_verify_data(current_both, Data,
- #connection_states{current_read = CurrentRead0,
- current_write = CurrentWrite0}
- = ConnectionStates) ->
- CurrentRead = CurrentRead0#connection_state{server_verify_data = Data},
- CurrentWrite = CurrentWrite0#connection_state{server_verify_data = Data},
- ConnectionStates#connection_states{current_read = CurrentRead,
- current_write = CurrentWrite}.
-
-%%--------------------------------------------------------------------
--spec activate_pending_connection_state(#connection_states{}, read | write) ->
- #connection_states{}.
-%%
-%% Description: Creates a new instance of the connection_states record
-%% where the pending state of <Type> has been activated.
-%%--------------------------------------------------------------------
-activate_pending_connection_state(States =
- #connection_states{pending_read = Pending},
- read) ->
- NewCurrent = Pending#connection_state{sequence_number = 0},
- SecParams = Pending#connection_state.security_parameters,
- ConnectionEnd = SecParams#security_parameters.connection_end,
- EmptyPending = empty_connection_state(ConnectionEnd),
- SecureRenegotation = NewCurrent#connection_state.secure_renegotiation,
- NewPending = EmptyPending#connection_state{secure_renegotiation = SecureRenegotation},
- States#connection_states{current_read = NewCurrent,
- pending_read = NewPending
- };
-
-activate_pending_connection_state(States =
- #connection_states{pending_write = Pending},
- write) ->
- NewCurrent = Pending#connection_state{sequence_number = 0},
- SecParams = Pending#connection_state.security_parameters,
- ConnectionEnd = SecParams#security_parameters.connection_end,
- EmptyPending = empty_connection_state(ConnectionEnd),
- SecureRenegotation = NewCurrent#connection_state.secure_renegotiation,
- NewPending = EmptyPending#connection_state{secure_renegotiation = SecureRenegotation},
- States#connection_states{current_write = NewCurrent,
- pending_write = NewPending
- }.
-
-%%--------------------------------------------------------------------
--spec set_pending_cipher_state(#connection_states{}, #cipher_state{},
- #cipher_state{}, client | server) ->
- #connection_states{}.
-%%
-%% Description: Set the cipher state in the specified pending connection state.
-%%--------------------------------------------------------------------
-set_pending_cipher_state(#connection_states{pending_read = Read,
- pending_write = Write} = States,
- ClientState, ServerState, server) ->
- States#connection_states{
- pending_read = Read#connection_state{cipher_state = ClientState},
- pending_write = Write#connection_state{cipher_state = ServerState}};
-
-set_pending_cipher_state(#connection_states{pending_read = Read,
- pending_write = Write} = States,
- ClientState, ServerState, client) ->
- States#connection_states{
- pending_read = Read#connection_state{cipher_state = ServerState},
- pending_write = Write#connection_state{cipher_state = ClientState}}.
-
-%%--------------------------------------------------------------------
-spec get_tls_records(binary(), binary()) -> {[binary()], binary()} | #alert{}.
%%
%% Description: Given old buffer and new data from TCP, packs up a records
@@ -376,6 +120,43 @@ get_tls_records_aux(Data, Acc) ->
false ->
?ALERT_REC(?FATAL, ?UNEXPECTED_MESSAGE)
end.
+
+encode_plain_text(Type, Version, Data, ConnectionStates) ->
+ #connection_states{current_write=#connection_state{
+ compression_state=CompS0,
+ security_parameters=
+ #security_parameters{compression_algorithm=CompAlg}
+ }=CS0} = ConnectionStates,
+ {Comp, CompS1} = ssl_record:compress(CompAlg, Data, CompS0),
+ CS1 = CS0#connection_state{compression_state = CompS1},
+ {CipherFragment, CS2} = cipher(Type, Version, Comp, CS1),
+ CTBin = encode_tls_cipher_text(Type, Version, CipherFragment),
+ {CTBin, ConnectionStates#connection_states{current_write = CS2}}.
+
+%%--------------------------------------------------------------------
+-spec decode_cipher_text(#ssl_tls{}, #connection_states{}) ->
+ {#ssl_tls{}, #connection_states{}}| #alert{}.
+%%
+%% Description: Decode cipher text
+%%--------------------------------------------------------------------
+decode_cipher_text(#ssl_tls{type = Type, version = Version,
+ fragment = CipherFragment} = CipherText, ConnnectionStates0) ->
+ ReadState0 = ConnnectionStates0#connection_states.current_read,
+ #connection_state{compression_state = CompressionS0,
+ security_parameters = SecParams} = ReadState0,
+ CompressAlg = SecParams#security_parameters.compression_algorithm,
+ case decipher(Type, Version, CipherFragment, ReadState0) of
+ {PlainFragment, ReadState1} ->
+ {Plain, CompressionS1} = ssl_record:uncompress(CompressAlg,
+ PlainFragment, CompressionS0),
+ ConnnectionStates = ConnnectionStates0#connection_states{
+ current_read = ReadState1#connection_state{
+ compression_state = CompressionS1}},
+ {CipherText#ssl_tls{fragment = Plain}, ConnnectionStates};
+ #alert{} = Alert ->
+ Alert
+ end.
+
%%--------------------------------------------------------------------
-spec protocol_version(tls_atom_version() | tls_version()) ->
tls_version() | tls_atom_version().
@@ -493,183 +274,39 @@ is_acceptable_version(_,_) ->
false.
%%--------------------------------------------------------------------
--spec compressions() -> [binary()].
-%%
-%% Description: return a list of compressions supported (currently none)
-%%--------------------------------------------------------------------
-compressions() ->
- [?byte(?NULL)].
-
-%%--------------------------------------------------------------------
--spec decode_cipher_text(#ssl_tls{}, #connection_states{}) ->
- {#ssl_tls{}, #connection_states{}}| #alert{}.
-%%
-%% Description: Decode cipher text
-%%--------------------------------------------------------------------
-decode_cipher_text(CipherText, ConnnectionStates0) ->
- ReadState0 = ConnnectionStates0#connection_states.current_read,
- #connection_state{compression_state = CompressionS0,
- security_parameters = SecParams} = ReadState0,
- CompressAlg = SecParams#security_parameters.compression_algorithm,
- case decipher(CipherText, ReadState0) of
- {Compressed, ReadState1} ->
- {Plain, CompressionS1} = uncompress(CompressAlg,
- Compressed, CompressionS0),
- ConnnectionStates = ConnnectionStates0#connection_states{
- current_read = ReadState1#connection_state{
- compression_state = CompressionS1}},
- {Plain, ConnnectionStates};
- #alert{} = Alert ->
- Alert
- end.
-%%--------------------------------------------------------------------
--spec encode_data(binary(), tls_version(), #connection_states{}) ->
- {iolist(), #connection_states{}}.
-%%
-%% Description: Encodes data to send on the ssl-socket.
-%%--------------------------------------------------------------------
-encode_data(Frag, Version,
- #connection_states{current_write = #connection_state{
- security_parameters =
- #security_parameters{bulk_cipher_algorithm = BCA}}} =
- ConnectionStates) ->
- Data = split_bin(Frag, ?MAX_PLAIN_TEXT_LENGTH, Version, BCA),
- encode_iolist(?APPLICATION_DATA, Data, Version, ConnectionStates).
-
-%%--------------------------------------------------------------------
--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_alert_record(#alert{}, tls_version(), #connection_states{}) ->
- {iolist(), #connection_states{}}.
-%%
-%% Description: Encodes an alert message to send on the ssl-socket.
-%%--------------------------------------------------------------------
-encode_alert_record(#alert{level = Level, description = Description},
- Version, ConnectionStates) ->
- encode_plain_text(?ALERT, Version, <<?BYTE(Level), ?BYTE(Description)>>,
- 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).
-
-%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
-encode_iolist(Type, Data, Version, ConnectionStates0) ->
- {ConnectionStates, EncodedMsg} =
- lists:foldl(fun(Text, {CS0, Encoded}) ->
- {Enc, CS1} = encode_plain_text(Type, Version, Text, CS0),
- {CS1, [Enc | Encoded]}
- end, {ConnectionStates0, []}, Data),
- {lists:reverse(EncodedMsg), ConnectionStates}.
-
-highest_protocol_version() ->
- highest_protocol_version(supported_protocol_versions()).
-
-initial_connection_state(ConnectionEnd) ->
- #connection_state{security_parameters =
- initial_security_params(ConnectionEnd),
- sequence_number = 0
- }.
-
-initial_security_params(ConnectionEnd) ->
- SecParams = #security_parameters{connection_end = ConnectionEnd,
- compression_algorithm = ?NULL},
- ssl_cipher:security_parameters(highest_protocol_version(), ?TLS_NULL_WITH_NULL_NULL,
- SecParams).
-
-empty_connection_state(ConnectionEnd) ->
- SecParams = empty_security_params(ConnectionEnd),
- #connection_state{security_parameters = SecParams}.
-
-empty_security_params(ConnectionEnd = ?CLIENT) ->
- #security_parameters{connection_end = ConnectionEnd,
- client_random = random()};
-empty_security_params(ConnectionEnd = ?SERVER) ->
- #security_parameters{connection_end = ConnectionEnd,
- server_random = random()}.
-random() ->
- Secs_since_1970 = calendar:datetime_to_gregorian_seconds(
- calendar:universal_time()) - 62167219200,
- Random_28_bytes = crypto:rand_bytes(28),
- <<?UINT32(Secs_since_1970), Random_28_bytes/binary>>.
-
-record_protocol_role(client) ->
- ?CLIENT;
-record_protocol_role(server) ->
- ?SERVER.
-
-%% 1/n-1 splitting countermeasure Rizzo/Duong-Beast, RC4 chiphers are not vulnerable to this attack.
-split_bin(<<FirstByte:8, Rest/binary>>, ChunkSize, Version, BCA) when BCA =/= ?RC4 andalso ({3, 1} == Version orelse
- {3, 0} == Version) ->
- do_split_bin(Rest, ChunkSize, [[FirstByte]]);
-split_bin(Bin, ChunkSize, _, _) ->
- do_split_bin(Bin, ChunkSize, []).
-
-do_split_bin(<<>>, _, Acc) ->
- lists:reverse(Acc);
-do_split_bin(Bin, ChunkSize, Acc) ->
- case Bin of
- <<Chunk:ChunkSize/binary, Rest/binary>> ->
- do_split_bin(Rest, ChunkSize, [Chunk | Acc]);
- _ ->
- lists:reverse(Acc, [Bin])
- end.
-
-encode_plain_text(Type, Version, Data, ConnectionStates) ->
- #connection_states{current_write=#connection_state{
- compression_state=CompS0,
- security_parameters=
- #security_parameters{compression_algorithm=CompAlg}
- }=CS0} = ConnectionStates,
- {Comp, CompS1} = compress(CompAlg, Data, CompS0),
- CS1 = CS0#connection_state{compression_state = CompS1},
- {CipherText, CS2} = cipher(Type, Version, Comp, CS1),
- CTBin = encode_tls_cipher_text(Type, Version, CipherText),
- {CTBin, ConnectionStates#connection_states{current_write = CS2}}.
-
encode_tls_cipher_text(Type, {MajVer, MinVer}, Fragment) ->
Length = erlang:iolist_size(Fragment),
[<<?BYTE(Type), ?BYTE(MajVer), ?BYTE(MinVer), ?UINT16(Length)>>, Fragment].
-cipher(Type, Version, Fragment, CS0) ->
- Length = erlang:iolist_size(Fragment),
- {MacHash, CS1=#connection_state{cipher_state = CipherS0,
- security_parameters=
- #security_parameters{bulk_cipher_algorithm =
+cipher(Type, Version, Fragment,
+ #connection_state{cipher_state = CipherS0,
+ sequence_number = SeqNo,
+ security_parameters=
+ #security_parameters{bulk_cipher_algorithm =
BCA}
- }} =
- hash_and_bump_seqno(CS0, Type, Version, Length, Fragment),
- {Ciphered, CipherS1} = ssl_cipher:cipher(BCA, CipherS0, MacHash, Fragment, Version),
- CS2 = CS1#connection_state{cipher_state=CipherS1},
- {Ciphered, CS2}.
-
-decipher(TLS=#ssl_tls{type=Type, version=Version, fragment=Fragment}, CS0) ->
- SP = CS0#connection_state.security_parameters,
+ } = WriteState0) ->
+ MacHash = calc_mac_hash(Type, Version, Fragment, WriteState0),
+ {CipherFragment, CipherS1} =
+ ssl_cipher:cipher(BCA, CipherS0, MacHash, Fragment, Version),
+ WriteState = WriteState0#connection_state{cipher_state=CipherS1},
+ {CipherFragment, WriteState#connection_state{sequence_number = SeqNo+1}}.
+
+decipher(Type, Version, CipherFragment,
+ #connection_state{sequence_number = SeqNo} = ReadState) ->
+ SP = ReadState#connection_state.security_parameters,
BCA = SP#security_parameters.bulk_cipher_algorithm,
HashSz = SP#security_parameters.hash_size,
- CipherS0 = CS0#connection_state.cipher_state,
- case ssl_cipher:decipher(BCA, HashSz, CipherS0, Fragment, Version) of
- {T, Mac, CipherS1} ->
- CS1 = CS0#connection_state{cipher_state = CipherS1},
- TLength = size(T),
- {MacHash, CS2} = hash_and_bump_seqno(CS1, Type, Version, TLength, T),
- case is_correct_mac(Mac, MacHash) of
- true ->
- {TLS#ssl_tls{fragment = T}, CS2};
+ CipherS0 = ReadState#connection_state.cipher_state,
+ case ssl_cipher:decipher(BCA, HashSz, CipherS0, CipherFragment, Version) of
+ {PlainFragment, Mac, CipherS1} ->
+ CS1 = ReadState#connection_state{cipher_state = CipherS1},
+ MacHash = calc_mac_hash(Type, Version, PlainFragment, ReadState),
+ case ssl_record:is_correct_mac(Mac, MacHash) of
+ true ->
+ {PlainFragment,
+ CS1#connection_state{sequence_number = SeqNo+1}};
false ->
?ALERT_REC(?FATAL, ?BAD_RECORD_MAC)
end;
@@ -677,40 +314,29 @@ decipher(TLS=#ssl_tls{type=Type, version=Version, fragment=Fragment}, CS0) ->
Alert
end.
-uncompress(?NULL, Data = #ssl_tls{type = _Type,
- version = _Version,
- fragment = _Fragment}, CS) ->
- {Data, CS}.
-
-compress(?NULL, Data, CS) ->
- {Data, CS}.
-
-hash_and_bump_seqno(#connection_state{sequence_number = SeqNo,
- mac_secret = MacSecret,
- security_parameters =
- SecPars} = CS0,
- Type, Version, Length, Fragment) ->
- Hash = mac_hash(Version,
- SecPars#security_parameters.mac_algorithm,
- MacSecret, SeqNo, Type,
- Length, Fragment),
- {Hash, CS0#connection_state{sequence_number = SeqNo+1}}.
-
-is_correct_mac(Mac, Mac) ->
- true;
-is_correct_mac(_M,_H) ->
- false.
-
mac_hash({_,_}, ?NULL, _MacSecret, _SeqNo, _Type,
_Length, _Fragment) ->
<<>>;
mac_hash({3, 0}, MacAlg, MacSecret, SeqNo, Type, Length, Fragment) ->
- ssl_ssl3:mac_hash(MacAlg, MacSecret, SeqNo, Type, Length, Fragment);
+ ssl_v3:mac_hash(MacAlg, MacSecret, SeqNo, Type, Length, Fragment);
mac_hash({3, N} = Version, MacAlg, MacSecret, SeqNo, Type, Length, Fragment)
when N =:= 1; N =:= 2; N =:= 3 ->
- ssl_tls1:mac_hash(MacAlg, MacSecret, SeqNo, Type, Version,
+ tls_v1:mac_hash(MacAlg, MacSecret, SeqNo, Type, Version,
Length, Fragment).
+highest_protocol_version() ->
+ highest_protocol_version(supported_protocol_versions()).
+
sufficient_tlsv1_2_crypto_support() ->
CryptoSupport = crypto:supports(),
proplists:get_bool(sha256, proplists:get_value(hashs, CryptoSupport)).
+
+calc_mac_hash(Type, Version,
+ PlainFragment, #connection_state{sequence_number = SeqNo,
+ mac_secret = MacSecret,
+ security_parameters =
+ SecPars}) ->
+ Length = erlang:iolist_size(PlainFragment),
+ mac_hash(Version, SecPars#security_parameters.mac_algorithm,
+ MacSecret, SeqNo, Type,
+ Length, PlainFragment).
diff --git a/lib/ssl/src/tls_record.hrl b/lib/ssl/src/tls_record.hrl
index c9350fa137..30d7343074 100644
--- a/lib/ssl/src/tls_record.hrl
+++ b/lib/ssl/src/tls_record.hrl
@@ -29,7 +29,6 @@
-include("ssl_record.hrl"). %% Common TLS and DTLS records and Constantes
%% Used to handle tls_plain_text, tls_compressed and tls_cipher_text
-
-record(ssl_tls, {
type,
version,
diff --git a/lib/ssl/src/ssl_tls1.erl b/lib/ssl/src/tls_v1.erl
index 8ab66d0627..2395e98642 100644
--- a/lib/ssl/src/ssl_tls1.erl
+++ b/lib/ssl/src/tls_v1.erl
@@ -22,7 +22,7 @@
%% Purpose: Handles tls1 encryption.
%%----------------------------------------------------------------------
--module(ssl_tls1).
+-module(tls_v1).
-include("ssl_cipher.hrl").
-include("ssl_internal.hrl").
@@ -86,7 +86,7 @@ certificate_verify(HashAlgo, _Version, Handshake) ->
-spec setup_keys(integer(), integer(), binary(), binary(), binary(), integer(),
integer(), integer()) -> {binary(), binary(), binary(),
- binary(), binary(), binary()}.
+ binary(), binary(), binary()}.
setup_keys(Version, _PrfAlgo, MasterSecret, ServerRandom, ClientRandom, HashSize,
KeyMatLen, IVSize)
@@ -106,7 +106,7 @@ setup_keys(Version, _PrfAlgo, MasterSecret, ServerRandom, ClientRandom, HashSize
WantedLength = 2 * (HashSize + KeyMatLen + IVSize),
KeyBlock = prf(?MD5SHA, MasterSecret, "key expansion",
[ServerRandom, ClientRandom], WantedLength),
- <<ClientWriteMacSecret:HashSize/binary,
+ <<ClientWriteMacSecret:HashSize/binary,
ServerWriteMacSecret:HashSize/binary,
ClientWriteKey:KeyMatLen/binary, ServerWriteKey:KeyMatLen/binary,
ClientIV:IVSize/binary, ServerIV:IVSize/binary>> = KeyBlock,
@@ -167,22 +167,22 @@ setup_keys(Version, PrfAlgo, MasterSecret, ServerRandom, ClientRandom, HashSize,
ServerWriteKey, ClientIV, ServerIV}.
-spec mac_hash(integer(), binary(), integer(), integer(), tls_version(),
- integer(), binary()) -> binary().
+ integer(), binary()) -> binary().
-mac_hash(Method, Mac_write_secret, Seq_num, Type, {Major, Minor},
+mac_hash(Method, Mac_write_secret, Seq_num, Type, {Major, Minor},
Length, Fragment) ->
%% RFC 2246 & 4346 - 6.2.3.1.
%% HMAC_hash(MAC_write_secret, seq_num + TLSCompressed.type +
%% TLSCompressed.version + TLSCompressed.length +
%% TLSCompressed.fragment));
- Mac = hmac_hash(Method, Mac_write_secret,
- [<<?UINT64(Seq_num), ?BYTE(Type),
- ?BYTE(Major), ?BYTE(Minor), ?UINT16(Length)>>,
+ Mac = hmac_hash(Method, Mac_write_secret,
+ [<<?UINT64(Seq_num), ?BYTE(Type),
+ ?BYTE(Major), ?BYTE(Minor), ?UINT16(Length)>>,
Fragment]),
Mac.
-spec suites(1|2|3) -> [cipher_suite()].
-
+
suites(Minor) when Minor == 1; Minor == 2->
case sufficent_ec_support() of
true ->
@@ -199,8 +199,8 @@ suites(Minor) when Minor == 3 ->
no_ec_suites(3) ++ no_ec_suites(2)
end.
-all_suites(Minor) when Minor == 1; Minor == 2->
- [
+all_suites(Minor) when Minor == 1; Minor == 2->
+ [
?TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
?TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
?TLS_DHE_RSA_WITH_AES_256_CBC_SHA,
@@ -224,7 +224,7 @@ all_suites(Minor) when Minor == 1; Minor == 2->
?TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA,
?TLS_ECDH_RSA_WITH_AES_128_CBC_SHA,
?TLS_RSA_WITH_AES_128_CBC_SHA,
-
+
?TLS_ECDHE_ECDSA_WITH_RC4_128_SHA,
?TLS_ECDHE_RSA_WITH_RC4_128_SHA,
?TLS_RSA_WITH_RC4_128_SHA,
@@ -232,32 +232,32 @@ all_suites(Minor) when Minor == 1; Minor == 2->
?TLS_DHE_RSA_WITH_DES_CBC_SHA,
?TLS_ECDH_ECDSA_WITH_RC4_128_SHA,
?TLS_ECDH_RSA_WITH_RC4_128_SHA,
-
+
?TLS_RSA_WITH_DES_CBC_SHA
];
-all_suites(3) ->
+all_suites(3) ->
[
?TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384,
?TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384,
?TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384,
?TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384,
-
+
?TLS_DHE_RSA_WITH_AES_256_CBC_SHA256,
?TLS_DHE_DSS_WITH_AES_256_CBC_SHA256,
?TLS_RSA_WITH_AES_256_CBC_SHA256,
-
+
?TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
?TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
?TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256,
?TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256,
-
+
?TLS_DHE_RSA_WITH_AES_128_CBC_SHA256,
?TLS_DHE_DSS_WITH_AES_128_CBC_SHA256,
?TLS_RSA_WITH_AES_128_CBC_SHA256
].
-no_ec_suites(Minor) when Minor == 1; Minor == 2->
- [
+no_ec_suites(Minor) when Minor == 1; Minor == 2->
+ [
?TLS_DHE_RSA_WITH_AES_256_CBC_SHA,
?TLS_DHE_DSS_WITH_AES_256_CBC_SHA,
?TLS_RSA_WITH_AES_256_CBC_SHA,
@@ -272,7 +272,7 @@ no_ec_suites(Minor) when Minor == 1; Minor == 2->
?TLS_DHE_RSA_WITH_DES_CBC_SHA,
?TLS_RSA_WITH_DES_CBC_SHA
];
-no_ec_suites(3) ->
+no_ec_suites(3) ->
[
?TLS_DHE_RSA_WITH_AES_256_CBC_SHA256,
?TLS_DHE_DSS_WITH_AES_256_CBC_SHA256,
@@ -323,7 +323,7 @@ p_hash(Secret, Seed, WantedLength, Method, N, Acc) ->
%% ... Where A(0) = seed
%% A(i) = HMAC_hash(secret, A(i-1))
-%% a(0, _Secret, Seed, _Method) ->
+%% a(0, _Secret, Seed, _Method) ->
%% Seed.
%% a(N, Secret, Seed, Method) ->
%% hmac_hash(Method, Secret, a(N-1, Secret, Seed, Method)).
diff --git a/lib/ssl/test/ssl_handshake_SUITE.erl b/lib/ssl/test/ssl_handshake_SUITE.erl
index a40f07fd07..9695710230 100644
--- a/lib/ssl/test/ssl_handshake_SUITE.erl
+++ b/lib/ssl/test/ssl_handshake_SUITE.erl
@@ -32,43 +32,44 @@
%%--------------------------------------------------------------------
suite() -> [{ct_hooks,[ts_install_cth]}].
-all() -> [
- decode_hello_handshake,
- decode_single_hello_extension_correctly,
- decode_unknown_hello_extension_correctly].
+all() -> [decode_hello_handshake,
+ decode_single_hello_extension_correctly,
+ decode_unknown_hello_extension_correctly].
%%--------------------------------------------------------------------
%% Test Cases --------------------------------------------------------
%%--------------------------------------------------------------------
decode_hello_handshake(_Config) ->
- HelloPacket = <<16#02, 16#00, 16#00,
- 16#44, 16#03, 16#03, 16#4e, 16#7f, 16#c1, 16#03, 16#35,
- 16#c2, 16#07, 16#b9, 16#4a, 16#58, 16#af, 16#34, 16#07,
- 16#a6, 16#7e, 16#ef, 16#52, 16#cb, 16#e0, 16#ea, 16#b7,
- 16#aa, 16#47, 16#c8, 16#c2, 16#2c, 16#66, 16#fa, 16#f8,
- 16#09, 16#42, 16#cf, 16#00, 16#c0, 16#30, 16#00, 16#00,
- 16#1c,
- 16#00, 16#0b, 16#00, 16#04, 16#03, 16#00, 16#01, 16#02, % ec_point_formats
- 16#ff, 16#01, 16#00, 16#01, 16#00, %% renegotiate
- 16#00, 16#23,
- 16#00, 16#00, 16#33, 16#74, 16#00, 16#07, 16#06, 16#73,
- 16#70, 16#64, 16#79, 16#2f, 16#32>>,
-
- Version = {3, 0},
- {Records, _Buffer} = tls_handshake:get_tls_handshake(Version, HelloPacket, <<>>),
-
- {Hello, _Data} = hd(Records),
- #renegotiation_info{renegotiated_connection = <<0>>} = Hello#server_hello.renegotiation_info.
+ HelloPacket = <<16#02, 16#00, 16#00,
+ 16#44, 16#03, 16#03, 16#4e, 16#7f, 16#c1, 16#03, 16#35,
+ 16#c2, 16#07, 16#b9, 16#4a, 16#58, 16#af, 16#34, 16#07,
+ 16#a6, 16#7e, 16#ef, 16#52, 16#cb, 16#e0, 16#ea, 16#b7,
+ 16#aa, 16#47, 16#c8, 16#c2, 16#2c, 16#66, 16#fa, 16#f8,
+ 16#09, 16#42, 16#cf, 16#00, 16#c0, 16#30, 16#00, 16#00,
+ 16#1c,
+ 16#00, 16#0b, 16#00, 16#04, 16#03, 16#00, 16#01, 16#02, % ec_point_formats
+ 16#ff, 16#01, 16#00, 16#01, 16#00, %% renegotiate
+ 16#00, 16#23,
+ 16#00, 16#00, 16#33, 16#74, 16#00, 16#07, 16#06, 16#73,
+ 16#70, 16#64, 16#79, 16#2f, 16#32>>,
+ Version = {3, 0},
+ {Records, _Buffer} = tls_handshake:get_tls_handshake(Version, HelloPacket, <<>>),
+
+ {Hello, _Data} = hd(Records),
+ #renegotiation_info{renegotiated_connection = <<0>>}
+ = (Hello#server_hello.extensions)#hello_extensions.renegotiation_info.
+
decode_single_hello_extension_correctly(_Config) ->
- Renegotiation = <<?UINT16(?RENEGOTIATION_EXT), ?UINT16(1), 0>>,
- Extensions = tls_handshake:dec_hello_extensions(Renegotiation, []),
- [{renegotiation_info,#renegotiation_info{renegotiated_connection = <<0>>}}] = Extensions.
-
+ Renegotiation = <<?UINT16(?RENEGOTIATION_EXT), ?UINT16(1), 0>>,
+ Extensions = ssl_handshake:decode_hello_extensions(Renegotiation),
+ #renegotiation_info{renegotiated_connection = <<0>>}
+ = Extensions#hello_extensions.renegotiation_info.
+
decode_unknown_hello_extension_correctly(_Config) ->
- FourByteUnknown = <<16#CA,16#FE, ?UINT16(4), 3, 0, 1, 2>>,
- Renegotiation = <<?UINT16(?RENEGOTIATION_EXT), ?UINT16(1), 0>>,
- Extensions = tls_handshake:dec_hello_extensions(<<FourByteUnknown/binary, Renegotiation/binary>>, []),
- [{renegotiation_info,#renegotiation_info{renegotiated_connection = <<0>>}}] = Extensions.
-
+ FourByteUnknown = <<16#CA,16#FE, ?UINT16(4), 3, 0, 1, 2>>,
+ Renegotiation = <<?UINT16(?RENEGOTIATION_EXT), ?UINT16(1), 0>>,
+ Extensions = ssl_handshake:decode_hello_extensions(<<FourByteUnknown/binary, Renegotiation/binary>>),
+ #renegotiation_info{renegotiated_connection = <<0>>}
+ = Extensions#hello_extensions.renegotiation_info.
diff --git a/lib/ssl/test/ssl_npn_hello_SUITE.erl b/lib/ssl/test/ssl_npn_hello_SUITE.erl
index ef5a02abef..27e1090114 100644
--- a/lib/ssl/test/ssl_npn_hello_SUITE.erl
+++ b/lib/ssl/test/ssl_npn_hello_SUITE.erl
@@ -52,7 +52,7 @@ encode_and_decode_client_hello_test(_Config) ->
Version = tls_record:protocol_version(tls_record:highest_protocol_version([])),
{[{DecodedHandshakeMessage, _Raw}], _} =
tls_handshake:get_tls_handshake(Version, list_to_binary(HandShakeData), <<>>),
- NextProtocolNegotiation = DecodedHandshakeMessage#client_hello.next_protocol_negotiation,
+ NextProtocolNegotiation = (DecodedHandshakeMessage#client_hello.extensions)#hello_extensions.next_protocol_negotiation,
NextProtocolNegotiation = undefined.
%%--------------------------------------------------------------------
encode_and_decode_npn_client_hello_test(_Config) ->
@@ -60,7 +60,7 @@ encode_and_decode_npn_client_hello_test(_Config) ->
Version = tls_record:protocol_version(tls_record:highest_protocol_version([])),
{[{DecodedHandshakeMessage, _Raw}], _} =
tls_handshake:get_tls_handshake(Version, list_to_binary(HandShakeData), <<>>),
- NextProtocolNegotiation = DecodedHandshakeMessage#client_hello.next_protocol_negotiation,
+ NextProtocolNegotiation = (DecodedHandshakeMessage#client_hello.extensions)#hello_extensions.next_protocol_negotiation,
NextProtocolNegotiation = #next_protocol_negotiation{extension_data = <<>>}.
%%--------------------------------------------------------------------
encode_and_decode_server_hello_test(_Config) ->
@@ -68,7 +68,7 @@ encode_and_decode_server_hello_test(_Config) ->
Version = tls_record:protocol_version(tls_record:highest_protocol_version([])),
{[{DecodedHandshakeMessage, _Raw}], _} =
tls_handshake:get_tls_handshake(Version, list_to_binary(HandShakeData), <<>>),
- NextProtocolNegotiation = DecodedHandshakeMessage#server_hello.next_protocol_negotiation,
+ NextProtocolNegotiation = (DecodedHandshakeMessage#server_hello.extensions)#hello_extensions.next_protocol_negotiation,
NextProtocolNegotiation = undefined.
%%--------------------------------------------------------------------
encode_and_decode_npn_server_hello_test(_Config) ->
@@ -76,56 +76,59 @@ encode_and_decode_npn_server_hello_test(_Config) ->
Version = tls_record:protocol_version(tls_record:highest_protocol_version([])),
{[{DecodedHandshakeMessage, _Raw}], _} =
tls_handshake:get_tls_handshake(Version, list_to_binary(HandShakeData), <<>>),
- NextProtocolNegotiation = DecodedHandshakeMessage#server_hello.next_protocol_negotiation,
+ NextProtocolNegotiation = (DecodedHandshakeMessage#server_hello.extensions)#hello_extensions.next_protocol_negotiation,
ct:log("~p ~n", [NextProtocolNegotiation]),
NextProtocolNegotiation = #next_protocol_negotiation{extension_data = <<6, "spdy/2">>}.
%%--------------------------------------------------------------------
create_server_hello_with_no_advertised_protocols_test(_Config) ->
- Hello = tls_handshake:server_hello(<<>>, {3, 0}, create_connection_states(), false, undefined, undefined, undefined),
- undefined = Hello#server_hello.next_protocol_negotiation.
+ Hello = tls_handshake:server_hello(<<>>, {3, 0}, create_connection_states(), #hello_extensions{}),
+ undefined = (Hello#server_hello.extensions)#hello_extensions.next_protocol_negotiation.
%%--------------------------------------------------------------------
create_server_hello_with_advertised_protocols_test(_Config) ->
Hello = tls_handshake:server_hello(<<>>, {3, 0}, create_connection_states(),
- false, [<<"spdy/1">>, <<"http/1.0">>, <<"http/1.1">>], undefined, undefined),
- #next_protocol_negotiation{extension_data = <<6, "spdy/1", 8, "http/1.0", 8, "http/1.1">>} =
- Hello#server_hello.next_protocol_negotiation.
+ #hello_extensions{next_protocol_negotiation = [<<"spdy/1">>, <<"http/1.0">>, <<"http/1.1">>]}),
+ [<<"spdy/1">>, <<"http/1.0">>, <<"http/1.1">>] =
+ (Hello#server_hello.extensions)#hello_extensions.next_protocol_negotiation.
%%--------------------------------------------------------------------
%% Internal functions ------------------------------------------------
%%--------------------------------------------------------------------
create_client_handshake(Npn) ->
+ Vsn = {1, 2},
tls_handshake:encode_handshake(#client_hello{
- client_version = {1, 2},
- random = <<1:256>>,
- session_id = <<>>,
- cipher_suites = [?TLS_DHE_DSS_WITH_DES_CBC_SHA],
- compression_methods = "",
- next_protocol_negotiation = Npn,
- renegotiation_info = #renegotiation_info{}
- }, vsn).
+ client_version = Vsn,
+ random = <<1:256>>,
+ session_id = <<>>,
+ cipher_suites = [?TLS_DHE_DSS_WITH_DES_CBC_SHA],
+ compression_methods = "",
+ extensions = #hello_extensions{
+ next_protocol_negotiation = Npn,
+ renegotiation_info = #renegotiation_info{}}
+ }, Vsn).
create_server_handshake(Npn) ->
+ Vsn = {1, 2},
tls_handshake:encode_handshake(#server_hello{
- server_version = {1, 2},
- random = <<1:256>>,
- session_id = <<>>,
- cipher_suite = ?TLS_DHE_DSS_WITH_DES_CBC_SHA,
- compression_method = 1,
- next_protocol_negotiation = Npn,
- renegotiation_info = #renegotiation_info{}
- }, vsn).
+ server_version = Vsn,
+ random = <<1:256>>,
+ session_id = <<>>,
+ cipher_suite = ?TLS_DHE_DSS_WITH_DES_CBC_SHA,
+ compression_method = 1,
+ extensions = #hello_extensions{
+ next_protocol_negotiation = Npn,
+ renegotiation_info = #renegotiation_info{}}
+ }, Vsn).
create_connection_states() ->
#connection_states{
- pending_read = #connection_state{
- security_parameters = #security_parameters{
- server_random = <<1:256>>,
- compression_algorithm = 1,
- cipher_suite = ?TLS_DHE_DSS_WITH_DES_CBC_SHA
- }
- },
-
- current_read = #connection_state {
- secure_renegotiation = false
- }
- }.
+ pending_read = #connection_state{
+ security_parameters = #security_parameters{
+ server_random = <<1:256>>,
+ compression_algorithm = 1,
+ cipher_suite = ?TLS_DHE_DSS_WITH_DES_CBC_SHA
+ }
+ },
+ current_read = #connection_state {
+ secure_renegotiation = false
+ }
+ }.
diff --git a/lib/ssl/vsn.mk b/lib/ssl/vsn.mk
index 9dd151553c..78424ee578 100644
--- a/lib/ssl/vsn.mk
+++ b/lib/ssl/vsn.mk
@@ -1 +1 @@
-SSL_VSN = 5.3
+SSL_VSN = 5.3.1