aboutsummaryrefslogtreecommitdiffstats
path: root/lib/ssl/src
diff options
context:
space:
mode:
Diffstat (limited to 'lib/ssl/src')
-rw-r--r--lib/ssl/src/dtls_connection.erl5
-rw-r--r--lib/ssl/src/ssl.erl56
-rw-r--r--lib/ssl/src/ssl_cipher.erl97
-rw-r--r--lib/ssl/src/ssl_connection.erl28
-rw-r--r--lib/ssl/src/ssl_connection.hrl2
-rw-r--r--lib/ssl/src/ssl_handshake.erl45
-rw-r--r--lib/ssl/src/ssl_internal.hrl2
-rw-r--r--lib/ssl/src/tls_connection.erl18
-rw-r--r--lib/ssl/src/tls_handshake.erl6
-rw-r--r--lib/ssl/src/tls_v1.erl33
10 files changed, 215 insertions, 77 deletions
diff --git a/lib/ssl/src/dtls_connection.erl b/lib/ssl/src/dtls_connection.erl
index 479f68f4bb..4f1f050e4b 100644
--- a/lib/ssl/src/dtls_connection.erl
+++ b/lib/ssl/src/dtls_connection.erl
@@ -232,9 +232,7 @@ error(_, _, _) ->
#state{}) ->
gen_statem:state_function_result().
%%--------------------------------------------------------------------
-hello(internal, #client_hello{client_version = ClientVersion,
- extensions = #hello_extensions{ec_point_formats = EcPointFormats,
- elliptic_curves = EllipticCurves}} = Hello,
+hello(internal, #client_hello{client_version = ClientVersion} = Hello,
State = #state{connection_states = ConnectionStates0,
port = Port, session = #session{own_certificate = Cert} = Session0,
renegotiation = {Renegotiation, _},
@@ -260,7 +258,6 @@ hello(internal, #client_hello{client_version = ClientVersion,
negotiated_version = Version,
hashsign_algorithm = HashSign,
session = Session,
- client_ecc = {EllipticCurves, EcPointFormats},
negotiated_protocol = Protocol}, ?MODULE)
end;
hello(internal, #server_hello{} = Hello,
diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl
index 27b753af2e..aa62ab8865 100644
--- a/lib/ssl/src/ssl.erl
+++ b/lib/ssl/src/ssl.erl
@@ -38,7 +38,7 @@
getopts/2, setopts/2, getstat/1, getstat/2
]).
%% SSL/TLS protocol handling
--export([cipher_suites/0, cipher_suites/1,
+-export([cipher_suites/0, cipher_suites/1, eccs/0, eccs/1,
connection_info/1, versions/0, session_info/1, format_error/1,
renegotiate/1, prf/5, negotiated_protocol/1, negotiated_next_protocol/1,
connection_information/1, connection_information/2]).
@@ -420,6 +420,33 @@ cipher_suites(all) ->
[ssl_cipher:erl_suite_definition(Suite) || Suite <- available_suites(all)].
%%--------------------------------------------------------------------
+-spec eccs() -> tls_v1:curves().
+%% Description: returns all supported curves across all versions
+%%--------------------------------------------------------------------
+eccs() ->
+ Curves = tls_v1:ecc_curves(all), % only tls_v1 has named curves right now
+ eccs_filter_supported(Curves).
+
+%%--------------------------------------------------------------------
+-spec eccs(tls_record:tls_version() | tls_record:tls_atom_version()) ->
+ tls_v1:curves().
+%% Description: returns the curves supported for a given version of
+%% ssl/tls.
+%%--------------------------------------------------------------------
+eccs({3,0}) ->
+ [];
+eccs({3,_}) ->
+ Curves = tls_v1:ecc_curves(all),
+ eccs_filter_supported(Curves);
+eccs(AtomVersion) when is_atom(AtomVersion) ->
+ eccs(tls_record:protocol_version(AtomVersion)).
+
+eccs_filter_supported(Curves) ->
+ CryptoCurves = crypto:ec_curves(),
+ lists:filter(fun(Curve) -> proplists:get_bool(Curve, CryptoCurves) end,
+ Curves).
+
+%%--------------------------------------------------------------------
-spec getopts(#sslsocket{}, [gen_tcp:option_name()]) ->
{ok, [gen_tcp:option()]} | {error, reason()}.
%%
@@ -647,6 +674,8 @@ do_connect(Address, Port,
end.
%% Handle extra ssl options given to ssl_accept
+-spec handle_options([any()], #ssl_options{}) -> #ssl_options{}
+ ; ([any()], client | server) -> {ok, #config{}}.
handle_options(Opts0, #ssl_options{protocol = Protocol, cacerts = CaCerts0,
cacertfile = CaCertFile0} = InheritedSslOpts) ->
RecordCB = record_cb(Protocol),
@@ -725,6 +754,8 @@ handle_options(Opts0, Role) ->
srp_identity = handle_option(srp_identity, Opts, undefined),
ciphers = handle_cipher_option(proplists:get_value(ciphers, Opts, []),
RecordCb:highest_protocol_version(Versions)),
+ eccs = handle_eccs_option(proplists:get_value(eccs, Opts, eccs()),
+ RecordCb:highest_protocol_version(Versions)),
signature_algs = handle_hashsigns_option(proplists:get_value(signature_algs, Opts,
default_option_role(server,
tls_v1:default_signature_algs(Versions), Role)),
@@ -755,6 +786,9 @@ handle_options(Opts0, Role) ->
honor_cipher_order = handle_option(honor_cipher_order, Opts,
default_option_role(server, false, Role),
server, Role),
+ honor_ecc_order = handle_option(honor_ecc_order, Opts,
+ default_option_role(server, false, Role),
+ server, Role),
protocol = proplists:get_value(protocol, Opts, tls),
padding_check = proplists:get_value(padding_check, Opts, true),
beast_mitigation = handle_option(beast_mitigation, Opts, one_n_minus_one),
@@ -780,7 +814,7 @@ handle_options(Opts0, Role) ->
alpn_preferred_protocols, next_protocols_advertised,
client_preferred_next_protocols, log_alert,
server_name_indication, honor_cipher_order, padding_check, crl_check, crl_cache,
- fallback, signature_algs, beast_mitigation, v2_hello_compatible],
+ fallback, signature_algs, eccs, honor_ecc_order, beast_mitigation, v2_hello_compatible],
SockOpts = lists:foldl(fun(Key, PropList) ->
proplists:delete(Key, PropList)
@@ -1010,6 +1044,8 @@ validate_option(sni_fun, Fun) when is_function(Fun) ->
Fun;
validate_option(honor_cipher_order, Value) when is_boolean(Value) ->
Value;
+validate_option(honor_ecc_order, Value) when is_boolean(Value) ->
+ Value;
validate_option(padding_check, Value) when is_boolean(Value) ->
Value;
validate_option(fallback, Value) when is_boolean(Value) ->
@@ -1164,6 +1200,14 @@ binary_cipher_suites(Version, Ciphers0) ->
Ciphers = [ssl_cipher:openssl_suite(C) || C <- string:tokens(Ciphers0, ":")],
binary_cipher_suites(Version, Ciphers).
+handle_eccs_option(Value, {_Major, Minor}) when is_list(Value) ->
+ try tls_v1:ecc_curves(Minor, Value) of
+ Curves -> #elliptic_curves{elliptic_curve_list = Curves}
+ catch
+ exit:_ -> throw({error, {options, {eccs, Value}}});
+ error:_ -> throw({error, {options, {eccs, Value}}})
+ end.
+
unexpected_format(Error) ->
lists:flatten(io_lib:format("Unexpected error: ~p", [Error])).
@@ -1334,6 +1378,14 @@ new_ssl_options([{server_name_indication, Value} | Rest], #ssl_options{} = Opts,
new_ssl_options(Rest, Opts#ssl_options{server_name_indication = validate_option(server_name_indication, Value)}, RecordCB);
new_ssl_options([{honor_cipher_order, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
new_ssl_options(Rest, Opts#ssl_options{honor_cipher_order = validate_option(honor_cipher_order, Value)}, RecordCB);
+new_ssl_options([{honor_ecc_order, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
+ new_ssl_options(Rest, Opts#ssl_options{honor_ecc_order = validate_option(honor_ecc_order, Value)}, RecordCB);
+new_ssl_options([{eccs, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
+ new_ssl_options(Rest,
+ Opts#ssl_options{eccs =
+ handle_eccs_option(Value, RecordCB:highest_protocol_version())
+ },
+ RecordCB);
new_ssl_options([{signature_algs, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
new_ssl_options(Rest,
Opts#ssl_options{signature_algs =
diff --git a/lib/ssl/src/ssl_cipher.erl b/lib/ssl/src/ssl_cipher.erl
index e935c033c7..605bbd859a 100644
--- a/lib/ssl/src/ssl_cipher.erl
+++ b/lib/ssl/src/ssl_cipher.erl
@@ -46,9 +46,9 @@
erl_cipher_suite/0, openssl_cipher_suite/0,
hash/0, key_algo/0, sign_algo/0]).
--type cipher() :: null |rc4_128 | idea_cbc | des40_cbc | des_cbc | '3des_ede_cbc'
+-type cipher() :: null |rc4_128 | des_cbc | '3des_ede_cbc'
| aes_128_cbc | aes_256_cbc | aes_128_gcm | aes_256_gcm | chacha20_poly1305.
--type hash() :: null | sha | md5 | sha224 | sha256 | sha384 | sha512.
+-type hash() :: null | md5 | sha | sha224 | sha256 | sha384 | sha512.
-type sign_algo() :: rsa | dsa | ecdsa.
-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.
@@ -333,21 +333,27 @@ anonymous_suites({3, N}) ->
anonymous_suites(N)
when N >= 3 ->
[?TLS_DH_anon_WITH_AES_128_GCM_SHA256,
- ?TLS_DH_anon_WITH_AES_256_GCM_SHA384
- ] ++ anonymous_suites(0);
-
-anonymous_suites(_) ->
- [?TLS_DH_anon_WITH_RC4_128_MD5,
- ?TLS_DH_anon_WITH_DES_CBC_SHA,
- ?TLS_DH_anon_WITH_3DES_EDE_CBC_SHA,
- ?TLS_DH_anon_WITH_AES_128_CBC_SHA,
- ?TLS_DH_anon_WITH_AES_256_CBC_SHA,
+ ?TLS_DH_anon_WITH_AES_256_GCM_SHA384,
?TLS_DH_anon_WITH_AES_128_CBC_SHA256,
?TLS_DH_anon_WITH_AES_256_CBC_SHA256,
- ?TLS_ECDH_anon_WITH_RC4_128_SHA,
- ?TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA,
?TLS_ECDH_anon_WITH_AES_128_CBC_SHA,
- ?TLS_ECDH_anon_WITH_AES_256_CBC_SHA].
+ ?TLS_ECDH_anon_WITH_AES_256_CBC_SHA,
+ ?TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA,
+ ?TLS_DH_anon_WITH_RC4_128_MD5];
+
+anonymous_suites(2) ->
+ [?TLS_ECDH_anon_WITH_AES_128_CBC_SHA,
+ ?TLS_ECDH_anon_WITH_AES_256_CBC_SHA,
+ ?TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA,
+ ?TLS_DH_anon_WITH_DES_CBC_SHA,
+ ?TLS_DH_anon_WITH_RC4_128_MD5];
+
+anonymous_suites(N) when N == 0;
+ N == 1 ->
+ [?TLS_DH_anon_WITH_RC4_128_MD5,
+ ?TLS_DH_anon_WITH_3DES_EDE_CBC_SHA,
+ ?TLS_DH_anon_WITH_DES_CBC_SHA
+ ].
%%--------------------------------------------------------------------
-spec psk_suites(ssl_record:ssl_version() | integer()) -> [cipher_suite()].
@@ -1441,25 +1447,60 @@ filter_suites(Suites) ->
is_acceptable_prf(Prf, Hashs)
end, Suites).
-is_acceptable_keyexchange(KeyExchange, Algos)
- when KeyExchange == ecdh_ecdsa;
- KeyExchange == ecdhe_ecdsa;
- KeyExchange == ecdh_rsa;
- KeyExchange == ecdhe_rsa;
- KeyExchange == ecdh_anon ->
+is_acceptable_keyexchange(KeyExchange, _Algos) when KeyExchange == psk;
+ KeyExchange == null ->
+ true;
+is_acceptable_keyexchange(KeyExchange, Algos) when KeyExchange == dh_anon;
+ KeyExchange == dhe_psk ->
+ proplists:get_bool(dh, Algos);
+is_acceptable_keyexchange(dhe_dss, Algos) ->
+ proplists:get_bool(dh, Algos) andalso
+ proplists:get_bool(dss, Algos);
+is_acceptable_keyexchange(dhe_rsa, Algos) ->
+ proplists:get_bool(dh, Algos) andalso
+ proplists:get_bool(rsa, Algos);
+is_acceptable_keyexchange(ecdh_anon, Algos) ->
proplists:get_bool(ecdh, Algos);
-is_acceptable_keyexchange(_, _) ->
- true.
-
+is_acceptable_keyexchange(KeyExchange, Algos) when KeyExchange == ecdh_ecdsa;
+ KeyExchange == ecdhe_ecdsa ->
+ proplists:get_bool(ecdh, Algos) andalso
+ proplists:get_bool(ecdsa, Algos);
+is_acceptable_keyexchange(KeyExchange, Algos) when KeyExchange == ecdh_rsa;
+ KeyExchange == ecdhe_rsa ->
+ proplists:get_bool(ecdh, Algos) andalso
+ proplists:get_bool(rsa, Algos);
+is_acceptable_keyexchange(KeyExchange, Algos) when KeyExchange == rsa;
+ KeyExchange == rsa_psk ->
+ proplists:get_bool(rsa, Algos);
+is_acceptable_keyexchange(srp_anon, Algos) ->
+ proplists:get_bool(srp, Algos);
+is_acceptable_keyexchange(srp_dss, Algos) ->
+ proplists:get_bool(srp, Algos) andalso
+ proplists:get_bool(dss, Algos);
+is_acceptable_keyexchange(srp_rsa, Algos) ->
+ proplists:get_bool(srp, Algos) andalso
+ proplists:get_bool(rsa, Algos);
+is_acceptable_keyexchange(_KeyExchange, _Algos) ->
+ false.
+
+is_acceptable_cipher(null, _Algos) ->
+ true;
+is_acceptable_cipher(rc4_128, Algos) ->
+ proplists:get_bool(rc4, Algos);
+is_acceptable_cipher(des_cbc, Algos) ->
+ proplists:get_bool(des_cbc, Algos);
+is_acceptable_cipher('3des_ede_cbc', Algos) ->
+ proplists:get_bool(des3_cbc, Algos);
+is_acceptable_cipher(aes_128_cbc, Algos) ->
+ proplists:get_bool(aes_cbc128, Algos);
+is_acceptable_cipher(aes_256_cbc, Algos) ->
+ proplists:get_bool(aes_cbc256, Algos);
is_acceptable_cipher(Cipher, Algos)
when Cipher == aes_128_gcm;
Cipher == aes_256_gcm ->
proplists:get_bool(aes_gcm, Algos);
-is_acceptable_cipher(Cipher, Algos)
- when Cipher == chacha20_poly1305 ->
- proplists:get_bool(Cipher, Algos);
-is_acceptable_cipher(_, _) ->
- true.
+is_acceptable_cipher(Cipher, Algos) ->
+ proplists:get_bool(Cipher, Algos).
is_acceptable_hash(null, _Algos) ->
true;
diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl
index 304d1706f5..b6e4d5b433 100644
--- a/lib/ssl/src/ssl_connection.erl
+++ b/lib/ssl/src/ssl_connection.erl
@@ -1172,14 +1172,23 @@ handle_alert(#alert{level = ?WARNING} = Alert, StateName,
%%% Internal functions
%%--------------------------------------------------------------------
connection_info(#state{sni_hostname = SNIHostname,
- session = #session{cipher_suite = CipherSuite},
+ session = #session{cipher_suite = CipherSuite, ecc = ECCCurve},
protocol_cb = Connection,
negotiated_version = {_,_} = Version,
ssl_options = Opts}) ->
RecordCB = record_cb(Connection),
+ CipherSuiteDef = ssl_cipher:erl_suite_definition(CipherSuite),
+ IsNamedCurveSuite = lists:member(element(1,CipherSuiteDef),
+ [ecdh_ecdsa, ecdhe_ecdsa, ecdh_anon]),
+ CurveInfo = case ECCCurve of
+ {namedCurve, Curve} when IsNamedCurveSuite ->
+ [{ecc, {named_curve, pubkey_cert_records:namedCurves(Curve)}}];
+ _ ->
+ []
+ end,
[{protocol, RecordCB:protocol_version(Version)},
- {cipher_suite, ssl_cipher:erl_suite_definition(CipherSuite)},
- {sni_hostname, SNIHostname}] ++ ssl_options_list(Opts).
+ {cipher_suite, CipherSuiteDef},
+ {sni_hostname, SNIHostname} | CurveInfo] ++ ssl_options_list(Opts).
do_server_hello(Type, #hello_extensions{next_protocol_negotiation = NextProtocols} =
ServerHelloExt,
@@ -1430,13 +1439,14 @@ key_exchange(#state{role = server, private_key = Key, key_algorithm = Algo} = St
key_exchange(#state{role = server, key_algorithm = Algo,
hashsign_algorithm = HashSignAlgo,
private_key = PrivateKey,
+ session = #session{ecc = ECCCurve},
connection_states = ConnectionStates0,
negotiated_version = Version
} = State0, Connection)
when Algo == ecdhe_ecdsa; Algo == ecdhe_rsa;
Algo == ecdh_anon ->
- ECDHKeys = public_key:generate_key(select_curve(State0)),
+ ECDHKeys = public_key:generate_key(ECCCurve),
#{security_parameters := SecParams} =
ssl_record:pending_connection_state(ConnectionStates0, read),
#security_parameters{client_random = ClientRandom,
@@ -1740,12 +1750,13 @@ calculate_secret(#server_dh_params{dh_p = Prime, dh_g = Base,
Connection, certify, certify);
calculate_secret(#server_ecdh_params{curve = ECCurve, public = ECServerPubKey},
- State, Connection) ->
+ State=#state{session=Session}, Connection) ->
ECDHKeys = public_key:generate_key(ECCurve),
PremasterSecret =
ssl_handshake:premaster_secret(#'ECPoint'{point = ECServerPubKey}, ECDHKeys),
calculate_master_secret(PremasterSecret,
- State#state{diffie_hellman_keys = ECDHKeys},
+ State#state{diffie_hellman_keys = ECDHKeys,
+ session = Session#session{ecc = ECCurve}},
Connection, certify, certify);
calculate_secret(#server_psk_params{
@@ -1845,11 +1856,6 @@ cipher_role(server, Data, Session, #state{connection_states = ConnectionStates0
{Record, State} = prepare_connection(State1, Connection),
Connection:next_event(connection, Record, State).
-select_curve(#state{client_ecc = {[Curve|_], _}}) ->
- {namedCurve, Curve};
-select_curve(_) ->
- {namedCurve, ?secp256r1}.
-
is_anonymous(Algo) when Algo == dh_anon;
Algo == ecdh_anon;
Algo == psk;
diff --git a/lib/ssl/src/ssl_connection.hrl b/lib/ssl/src/ssl_connection.hrl
index f1e612a41b..fca3e11894 100644
--- a/lib/ssl/src/ssl_connection.hrl
+++ b/lib/ssl/src/ssl_connection.hrl
@@ -48,6 +48,7 @@
socket_options :: #socket_options{},
connection_states :: ssl_record:connection_states() | secret_printout(),
protocol_buffers :: term() | secret_printout() , %% #protocol_buffers{} from tls_record.hrl or dtls_recor.hrl
+ unprocessed_handshake_events = 0 :: integer(),
tls_handshake_history :: ssl_handshake:ssl_handshake_history() | secret_printout()
| 'undefined',
cert_db :: reference() | 'undefined',
@@ -81,7 +82,6 @@
expecting_next_protocol_negotiation = false ::boolean(),
expecting_finished = false ::boolean(),
negotiated_protocol = undefined :: undefined | binary(),
- client_ecc, % {Curves, PointFmt}
tracker :: pid() | 'undefined', %% Tracker process for listen socket
sni_hostname = undefined,
downgrade,
diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl
index 5b51ac0916..4acc745c5f 100644
--- a/lib/ssl/src/ssl_handshake.erl
+++ b/lib/ssl/src/ssl_handshake.erl
@@ -70,7 +70,7 @@
%% Extensions handling
-export([client_hello_extensions/6,
handle_client_hello_extensions/9, %% Returns server hello extensions
- handle_server_hello_extensions/9, select_curve/2
+ handle_server_hello_extensions/9, select_curve/2, select_curve/3
]).
%% MISC
@@ -120,11 +120,13 @@ server_hello_done() ->
#server_hello_done{}.
client_hello_extensions(Host, Version, CipherSuites,
- #ssl_options{signature_algs = SupportedHashSigns, versions = AllVersions} = SslOpts, ConnectionStates, Renegotiation) ->
+ #ssl_options{signature_algs = SupportedHashSigns,
+ eccs = SupportedECCs,
+ versions = AllVersions} = SslOpts, ConnectionStates, Renegotiation) ->
{EcPointFormats, EllipticCurves} =
case advertises_ec_ciphers(lists:map(fun ssl_cipher:suite_definition/1, CipherSuites)) of
true ->
- client_ecc_extensions(tls_v1, Version);
+ client_ecc_extensions(SupportedECCs);
false ->
{undefined, undefined}
end,
@@ -1169,8 +1171,9 @@ select_session(SuggestedSessionId, CipherSuites, HashSigns, Compressions, Port,
{resumed, Resumed}
end.
-supported_ecc({Major, Minor} = Version) when ((Major == 3) and (Minor >= 1)) orelse (Major > 3) ->
- Curves = tls_v1:ecc_curves(Version),
+%% Deprecated?
+supported_ecc({Major, Minor}) when ((Major == 3) and (Minor >= 1)) orelse (Major > 3) ->
+ Curves = tls_v1:ecc_curves(Minor),
#elliptic_curves{elliptic_curve_list = Curves};
supported_ecc(_) ->
#elliptic_curves{elliptic_curve_list = []}.
@@ -1454,12 +1457,12 @@ srp_user(#ssl_options{srp_identity = {UserName, _}}) ->
srp_user(_) ->
undefined.
-client_ecc_extensions(Module, Version) ->
+client_ecc_extensions(SupportedECCs) ->
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)},
+ EllipticCurves = SupportedECCs,
{EcPointFormats, EllipticCurves};
_ ->
{undefined, undefined}
@@ -1493,22 +1496,34 @@ advertises_ec_ciphers([{ecdh_anon, _,_,_} | _]) ->
true;
advertises_ec_ciphers([_| Rest]) ->
advertises_ec_ciphers(Rest).
-select_curve(#elliptic_curves{elliptic_curve_list = ClientCurves},
- #elliptic_curves{elliptic_curve_list = ServerCurves}) ->
- select_curve(ClientCurves, ServerCurves);
-select_curve(undefined, _) ->
+
+select_curve(Client, Server) ->
+ select_curve(Client, Server, false).
+
+select_curve(#elliptic_curves{elliptic_curve_list = ClientCurves},
+ #elliptic_curves{elliptic_curve_list = ServerCurves},
+ ServerOrder) ->
+ case ServerOrder of
+ false ->
+ select_shared_curve(ClientCurves, ServerCurves);
+ true ->
+ select_shared_curve(ServerCurves, ClientCurves)
+ end;
+select_curve(undefined, _, _) ->
%% Client did not send ECC extension use default curve if
%% ECC cipher is negotiated
- {namedCurve, ?secp256r1};
-select_curve(_, []) ->
+ {namedCurve, ?secp256r1}.
+
+select_shared_curve([], _) ->
no_curve;
-select_curve(Curves, [Curve| Rest]) ->
+select_shared_curve([Curve | Rest], Curves) ->
case lists:member(Curve, Curves) of
true ->
{namedCurve, Curve};
false ->
- select_curve(Curves, Rest)
+ select_shared_curve(Rest, Curves)
end.
+
%% RFC 6066, Section 3: Currently, the only server names supported are
%% DNS hostnames
sni(_, disable) ->
diff --git a/lib/ssl/src/ssl_internal.hrl b/lib/ssl/src/ssl_internal.hrl
index c19c1787ff..487d1fa096 100644
--- a/lib/ssl/src/ssl_internal.hrl
+++ b/lib/ssl/src/ssl_internal.hrl
@@ -140,6 +140,8 @@
crl_check :: boolean() | peer | best_effort,
crl_cache,
signature_algs,
+ eccs,
+ honor_ecc_order :: boolean(),
v2_hello_compatible :: boolean()
}).
diff --git a/lib/ssl/src/tls_connection.erl b/lib/ssl/src/tls_connection.erl
index 9b9031473a..932bb139c1 100644
--- a/lib/ssl/src/tls_connection.erl
+++ b/lib/ssl/src/tls_connection.erl
@@ -237,9 +237,7 @@ error(_, _, _) ->
#state{}) ->
gen_statem:state_function_result().
%%--------------------------------------------------------------------
-hello(internal, #client_hello{client_version = ClientVersion,
- extensions = #hello_extensions{ec_point_formats = EcPointFormats,
- elliptic_curves = EllipticCurves}} = Hello,
+hello(internal, #client_hello{client_version = ClientVersion} = Hello,
#state{connection_states = ConnectionStates0,
port = Port, session = #session{own_certificate = Cert} = Session0,
renegotiation = {Renegotiation, _},
@@ -265,7 +263,6 @@ hello(internal, #client_hello{client_version = ClientVersion,
negotiated_version = Version,
hashsign_algorithm = HashSign,
session = Session,
- client_ecc = {EllipticCurves, EcPointFormats},
negotiated_protocol = Protocol})
end;
hello(internal, #server_hello{} = Hello,
@@ -421,7 +418,7 @@ handle_common_event(internal, #ssl_tls{type = ?HANDSHAKE, fragment = Data},
connection ->
ssl_connection:hibernate_after(StateName, State, Events);
_ ->
- {next_state, StateName, State, Events}
+ {next_state, StateName, State#state{unprocessed_handshake_events = unprocessed_events(Events)}, Events}
end
catch throw:#alert{} = Alert ->
ssl_connection:handle_own_alert(Alert, Version, StateName, State0)
@@ -537,7 +534,9 @@ next_tls_record(Data, #state{protocol_buffers = #protocol_buffers{tls_record_buf
#alert{} = Alert ->
Alert
end.
-
+next_record(#state{unprocessed_handshake_events = N} = State) when N > 0 ->
+ {no_record, State#state{unprocessed_handshake_events = N-1}};
+
next_record(#state{protocol_buffers =
#protocol_buffers{tls_packets = [], tls_cipher_texts = [CT | Rest]}
= Buffers,
@@ -712,3 +711,10 @@ gen_info(Event, StateName, #state{negotiated_version = Version} = State) ->
Version, StateName, State)
end.
+unprocessed_events(Events) ->
+ %% The first handshake event will be processed immediately
+ %% as it is entered first in the event queue and
+ %% when it is processed there will be length(Events)-1
+ %% handshake events left to process before we should
+ %% process more TLS-records received on the socket.
+ erlang:length(Events)-1.
diff --git a/lib/ssl/src/tls_handshake.erl b/lib/ssl/src/tls_handshake.erl
index a2486bf752..2bd103c18a 100644
--- a/lib/ssl/src/tls_handshake.erl
+++ b/lib/ssl/src/tls_handshake.erl
@@ -160,13 +160,15 @@ handle_client_hello(Version, #client_hello{session_id = SugesstedId,
extensions = #hello_extensions{elliptic_curves = Curves,
signature_algs = ClientHashSigns} = HelloExt},
#ssl_options{versions = Versions,
- signature_algs = SupportedHashSigns} = SslOpts,
+ signature_algs = SupportedHashSigns,
+ eccs = SupportedECCs,
+ honor_ecc_order = ECCOrder} = SslOpts,
{Port, Session0, Cache, CacheCb, ConnectionStates0, Cert, _}, Renegotiation) ->
case tls_record:is_acceptable_version(Version, Versions) of
true ->
AvailableHashSigns = ssl_handshake:available_signature_algs(
ClientHashSigns, SupportedHashSigns, Cert, Version),
- ECCCurve = ssl_handshake:select_curve(Curves, ssl_handshake:supported_ecc(Version)),
+ ECCCurve = ssl_handshake:select_curve(Curves, SupportedECCs, ECCOrder),
{Type, #session{cipher_suite = CipherSuite} = Session1}
= ssl_handshake:select_session(SugesstedId, CipherSuites, AvailableHashSigns, Compressions,
Port, Session0#session{ecc = ECCCurve}, Version,
diff --git a/lib/ssl/src/tls_v1.erl b/lib/ssl/src/tls_v1.erl
index 711db77708..7f24ce5192 100644
--- a/lib/ssl/src/tls_v1.erl
+++ b/lib/ssl/src/tls_v1.erl
@@ -31,9 +31,18 @@
-export([master_secret/4, finished/5, certificate_verify/3, mac_hash/7,
setup_keys/8, suites/1, prf/5,
- ecc_curves/1, oid_to_enum/1, enum_to_oid/1,
+ ecc_curves/1, ecc_curves/2, oid_to_enum/1, enum_to_oid/1,
default_signature_algs/1, signature_algs/2]).
+-type named_curve() :: sect571r1 | sect571k1 | secp521r1 | brainpoolP512r1 |
+ sect409k1 | sect409r1 | brainpoolP384r1 | secp384r1 |
+ sect283k1 | sect283r1 | brainpoolP256r1 | secp256k1 | secp256r1 |
+ sect239k1 | sect233k1 | sect233r1 | secp224k1 | secp224r1 |
+ sect193r1 | sect193r2 | secp192k1 | secp192r1 | sect163k1 |
+ sect163r1 | sect163r2 | secp160k1 | secp160r1 | secp160r2.
+-type curves() :: [named_curve()].
+-export_type([curves/0, named_curve/0]).
+
%%====================================================================
%% Internal application API
%%====================================================================
@@ -399,13 +408,20 @@ is_pair(Hash, rsa, Hashs) ->
lists:member(Hash, AtLeastMd5).
%% list ECC curves in prefered order
-ecc_curves(_Minor) ->
- TLSCurves = [sect571r1,sect571k1,secp521r1,brainpoolP512r1,
- sect409k1,sect409r1,brainpoolP384r1,secp384r1,
- sect283k1,sect283r1,brainpoolP256r1,secp256k1,secp256r1,
- sect239k1,sect233k1,sect233r1,secp224k1,secp224r1,
- sect193r1,sect193r2,secp192k1,secp192r1,sect163k1,
- sect163r1,sect163r2,secp160k1,secp160r1,secp160r2],
+-spec ecc_curves(1..3 | all) -> [named_curve()].
+ecc_curves(all) ->
+ [sect571r1,sect571k1,secp521r1,brainpoolP512r1,
+ sect409k1,sect409r1,brainpoolP384r1,secp384r1,
+ sect283k1,sect283r1,brainpoolP256r1,secp256k1,secp256r1,
+ sect239k1,sect233k1,sect233r1,secp224k1,secp224r1,
+ sect193r1,sect193r2,secp192k1,secp192r1,sect163k1,
+ sect163r1,sect163r2,secp160k1,secp160r1,secp160r2];
+ecc_curves(Minor) ->
+ TLSCurves = ecc_curves(all),
+ ecc_curves(Minor, TLSCurves).
+
+-spec ecc_curves(1..3, [named_curve()]) -> [named_curve()].
+ecc_curves(_Minor, TLSCurves) ->
CryptoCurves = crypto:ec_curves(),
lists:foldr(fun(Curve, Curves) ->
case proplists:get_bool(Curve, CryptoCurves) of
@@ -414,6 +430,7 @@ ecc_curves(_Minor) ->
end
end, [], TLSCurves).
+
%% ECC curves from draft-ietf-tls-ecc-12.txt (Oct. 17, 2005)
oid_to_enum(?sect163k1) -> 1;
oid_to_enum(?sect163r1) -> 2;