aboutsummaryrefslogtreecommitdiffstats
path: root/lib/ssl/src
diff options
context:
space:
mode:
Diffstat (limited to 'lib/ssl/src')
-rw-r--r--lib/ssl/src/Makefile1
-rw-r--r--lib/ssl/src/dtls_connection.erl7
-rw-r--r--lib/ssl/src/dtls_handshake.erl5
-rw-r--r--lib/ssl/src/inet6_tls_dist.erl46
-rw-r--r--lib/ssl/src/inet_tls_dist.erl95
-rw-r--r--lib/ssl/src/ssl.app.src1
-rw-r--r--lib/ssl/src/ssl.erl158
-rw-r--r--lib/ssl/src/ssl_certificate.erl22
-rw-r--r--lib/ssl/src/ssl_cipher.erl57
-rw-r--r--lib/ssl/src/ssl_connection.erl90
-rw-r--r--lib/ssl/src/ssl_connection.hrl23
-rw-r--r--lib/ssl/src/ssl_handshake.erl211
-rw-r--r--lib/ssl/src/ssl_handshake.hrl2
-rw-r--r--lib/ssl/src/ssl_internal.hrl17
-rw-r--r--lib/ssl/src/ssl_tls_dist_proxy.erl35
-rw-r--r--lib/ssl/src/ssl_v3.erl4
-rw-r--r--lib/ssl/src/tls_connection.erl23
-rw-r--r--lib/ssl/src/tls_handshake.erl48
-rw-r--r--lib/ssl/src/tls_v1.erl64
19 files changed, 606 insertions, 303 deletions
diff --git a/lib/ssl/src/Makefile b/lib/ssl/src/Makefile
index 790328dc45..7a7a373487 100644
--- a/lib/ssl/src/Makefile
+++ b/lib/ssl/src/Makefile
@@ -51,6 +51,7 @@ MODULES= \
ssl_dist_sup\
ssl_sup \
inet_tls_dist \
+ inet6_tls_dist \
ssl_certificate\
ssl_pkix_db\
ssl_cipher \
diff --git a/lib/ssl/src/dtls_connection.erl b/lib/ssl/src/dtls_connection.erl
index 153d3fef48..e490de7eeb 100644
--- a/lib/ssl/src/dtls_connection.erl
+++ b/lib/ssl/src/dtls_connection.erl
@@ -196,8 +196,7 @@ hello(start, #state{host = Host, port = Port, role = client,
{Record, State} = next_record(State1),
next_state(hello, hello, Record, State);
-hello(Hello = #client_hello{client_version = ClientVersion,
- extensions = #hello_extensions{hash_signs = HashSigns}},
+hello(Hello = #client_hello{client_version = ClientVersion},
State = #state{connection_states = ConnectionStates0,
port = Port, session = #session{own_certificate = Cert} = Session0,
renegotiation = {Renegotiation, _},
@@ -209,9 +208,7 @@ hello(Hello = #client_hello{client_version = ClientVersion,
{Version, {Type, Session},
ConnectionStates,
#hello_extensions{ec_point_formats = EcPointFormats,
- elliptic_curves = EllipticCurves} = ServerHelloExt} ->
- HashSign = ssl_handshake:select_hashsign(HashSigns, Cert,
- dtls_v1:corresponding_tls_version(Version)),
+ elliptic_curves = EllipticCurves} = ServerHelloExt, HashSign} ->
ssl_connection:hello({common_client_hello, Type, ServerHelloExt, HashSign},
State#state{connection_states = ConnectionStates,
negotiated_version = Version,
diff --git a/lib/ssl/src/dtls_handshake.erl b/lib/ssl/src/dtls_handshake.erl
index 22c0ce7a13..50c84b712f 100644
--- a/lib/ssl/src/dtls_handshake.erl
+++ b/lib/ssl/src/dtls_handshake.erl
@@ -94,7 +94,10 @@ hello(#server_hello{server_version = Version, random = Random,
hello(#client_hello{client_version = ClientVersion}, _Options, {_,_,_,_,ConnectionStates,_}, _Renegotiation) ->
%% Return correct typ to make dialyzer happy until we have time to make the real imp.
- {ClientVersion, {new, #session{}}, ConnectionStates, #hello_extensions{}}.
+ HashSigns = tls_v1:default_signature_algs(dtls_v1:corresponding_tls_version(ClientVersion)),
+ {ClientVersion, {new, #session{}}, ConnectionStates, #hello_extensions{},
+ %% Placeholder for real hasign handling
+ hd(HashSigns)}.
%% hello(Address, Port,
%% #ssl_tls{epoch = _Epoch, sequence_number = _Seq,
diff --git a/lib/ssl/src/inet6_tls_dist.erl b/lib/ssl/src/inet6_tls_dist.erl
new file mode 100644
index 0000000000..ffd7296f93
--- /dev/null
+++ b/lib/ssl/src/inet6_tls_dist.erl
@@ -0,0 +1,46 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2015. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+-module(inet6_tls_dist).
+
+-export([childspecs/0, listen/1, accept/1, accept_connection/5,
+ setup/5, close/1, select/1]).
+
+childspecs() ->
+ inet_tls_dist:childspecs().
+
+select(Node) ->
+ inet_tls_dist:gen_select(inet6_tcp, Node).
+
+listen(Name) ->
+ inet_tls_dist:gen_listen(inet6_tcp, Name).
+
+accept(Listen) ->
+ inet_tls_dist:gen_accept(inet6_tcp, Listen).
+
+accept_connection(AcceptPid, Socket, MyNode, Allowed, SetupTime) ->
+ inet_tls_dist:gen_accept_connection(inet6_tcp, AcceptPid, Socket, MyNode, Allowed, SetupTime).
+
+setup(Node, Type, MyNode, LongOrShortNames,SetupTime) ->
+ inet_tls_dist:gen_setup(inet6_tcp, Node, Type, MyNode, LongOrShortNames,SetupTime).
+
+close(Socket) ->
+ inet_tls_dist:close(Socket).
diff --git a/lib/ssl/src/inet_tls_dist.erl b/lib/ssl/src/inet_tls_dist.erl
index 6fe99a81c5..ec26142a75 100644
--- a/lib/ssl/src/inet_tls_dist.erl
+++ b/lib/ssl/src/inet_tls_dist.erl
@@ -24,6 +24,10 @@
-export([childspecs/0, listen/1, accept/1, accept_connection/5,
setup/5, close/1, select/1, is_node_name/1]).
+%% Generalized dist API
+-export([gen_listen/2, gen_accept/2, gen_accept_connection/6,
+ gen_setup/6, gen_select/2]).
+
-include_lib("kernel/include/net_address.hrl").
-include_lib("kernel/include/dist.hrl").
-include_lib("kernel/include/dist_util.hrl").
@@ -33,9 +37,15 @@ childspecs() ->
permanent, infinity, supervisor, [ssl_dist_sup]}]}.
select(Node) ->
+ gen_select(inet_tcp, Node).
+
+gen_select(Driver, Node) ->
case split_node(atom_to_list(Node), $@, []) of
- [_,_Host] ->
- true;
+ [_, Host] ->
+ case inet:getaddr(Host, Driver:family()) of
+ {ok, _} -> true;
+ _ -> false
+ end;
_ ->
false
end.
@@ -46,23 +56,35 @@ is_node_name(_) ->
false.
listen(Name) ->
- ssl_tls_dist_proxy:listen(Name).
+ gen_listen(inet_tcp, Name).
+
+gen_listen(Driver, Name) ->
+ ssl_tls_dist_proxy:listen(Driver, Name).
accept(Listen) ->
- ssl_tls_dist_proxy:accept(Listen).
+ gen_accept(inet_tcp, Listen).
+
+gen_accept(Driver, Listen) ->
+ ssl_tls_dist_proxy:accept(Driver, Listen).
accept_connection(AcceptPid, Socket, MyNode, Allowed, SetupTime) ->
+ gen_accept_connection(inet_tcp, AcceptPid, Socket, MyNode, Allowed, SetupTime).
+
+gen_accept_connection(Driver, AcceptPid, Socket, MyNode, Allowed, SetupTime) ->
Kernel = self(),
- spawn_link(fun() -> do_accept(Kernel, AcceptPid, Socket,
+ spawn_link(fun() -> do_accept(Driver, Kernel, AcceptPid, Socket,
MyNode, Allowed, SetupTime) end).
setup(Node, Type, MyNode, LongOrShortNames,SetupTime) ->
+ gen_setup(inet_tcp, Node, Type, MyNode, LongOrShortNames,SetupTime).
+
+gen_setup(Driver, Node, Type, MyNode, LongOrShortNames,SetupTime) ->
Kernel = self(),
- spawn_opt(fun() -> do_setup(Kernel, Node, Type, MyNode, LongOrShortNames, SetupTime) end, [link, {priority, max}]).
+ spawn_opt(fun() -> do_setup(Driver, Kernel, Node, Type, MyNode, LongOrShortNames, SetupTime) end, [link, {priority, max}]).
-do_setup(Kernel, Node, Type, MyNode, LongOrShortNames, SetupTime) ->
- [Name, Address] = splitnode(Node, LongOrShortNames),
- case inet:getaddr(Address, inet) of
+do_setup(Driver, Kernel, Node, Type, MyNode, LongOrShortNames, SetupTime) ->
+ [Name, Address] = splitnode(Driver, Node, LongOrShortNames),
+ case inet:getaddr(Address, Driver:family()) of
{ok, Ip} ->
Timer = dist_util:start_timer(SetupTime),
case erl_epmd:port_please(Name, Ip) of
@@ -70,7 +92,7 @@ do_setup(Kernel, Node, Type, MyNode, LongOrShortNames, SetupTime) ->
?trace("port_please(~p) -> version ~p~n",
[Node,Version]),
dist_util:reset_timer(Timer),
- case ssl_tls_dist_proxy:connect(Ip, TcpPort) of
+ case ssl_tls_dist_proxy:connect(Driver, Ip, TcpPort) of
{ok, Socket} ->
HSData = connect_hs_data(Kernel, Node, MyNode, Socket,
Timer, Version, Ip, TcpPort, Address,
@@ -99,12 +121,12 @@ close(Socket) ->
gen_tcp:close(Socket),
ok.
-do_accept(Kernel, AcceptPid, Socket, MyNode, Allowed, SetupTime) ->
+do_accept(Driver, Kernel, AcceptPid, Socket, MyNode, Allowed, SetupTime) ->
process_flag(priority, max),
receive
{AcceptPid, controller} ->
Timer = dist_util:start_timer(SetupTime),
- case check_ip(Socket) of
+ case check_ip(Driver, Socket) of
true ->
HSData = accept_hs_data(Kernel, MyNode, Socket, Timer, Allowed),
dist_util:handshake_other_started(HSData);
@@ -118,12 +140,12 @@ do_accept(Kernel, AcceptPid, Socket, MyNode, Allowed, SetupTime) ->
%% Do only accept new connection attempts from nodes at our
%% own LAN, if the check_ip environment parameter is true.
%% ------------------------------------------------------------
-check_ip(Socket) ->
+check_ip(Driver, Socket) ->
case application:get_env(check_ip) of
{ok, true} ->
case get_ifs(Socket) of
{ok, IFs, IP} ->
- check_ip(IFs, IP);
+ check_ip(Driver, IFs, IP);
_ ->
?shutdown(no_node)
end;
@@ -142,37 +164,21 @@ get_ifs(Socket) ->
Error
end.
-check_ip([{OwnIP, _, Netmask}|IFs], PeerIP) ->
- case {mask(Netmask, PeerIP), mask(Netmask, OwnIP)} of
+check_ip(Driver, [{OwnIP, _, Netmask}|IFs], PeerIP) ->
+ case {Driver:mask(Netmask, PeerIP), Driver:mask(Netmask, OwnIP)} of
{M, M} -> true;
_ -> check_ip(IFs, PeerIP)
end;
-check_ip([], PeerIP) ->
+check_ip(_Driver, [], PeerIP) ->
{false, PeerIP}.
-mask({M1,M2,M3,M4}, {IP1,IP2,IP3,IP4}) ->
- {M1 band IP1,
- M2 band IP2,
- M3 band IP3,
- M4 band IP4};
-
-mask({M1,M2,M3,M4, M5, M6, M7, M8}, {IP1,IP2,IP3,IP4, IP5, IP6, IP7, IP8}) ->
- {M1 band IP1,
- M2 band IP2,
- M3 band IP3,
- M4 band IP4,
- M5 band IP5,
- M6 band IP6,
- M7 band IP7,
- M8 band IP8}.
-
%% If Node is illegal terminate the connection setup!!
-splitnode(Node, LongOrShortNames) ->
+splitnode(Driver, Node, LongOrShortNames) ->
case split_node(atom_to_list(Node), $@, []) of
[Name|Tail] when Tail =/= [] ->
Host = lists:append(Tail),
- check_node(Name, Node, Host, LongOrShortNames);
+ check_node(Driver, Name, Node, Host, LongOrShortNames);
[_] ->
error_logger:error_msg("** Nodename ~p illegal, no '@' character **~n",
[Node]),
@@ -182,15 +188,20 @@ splitnode(Node, LongOrShortNames) ->
?shutdown(Node)
end.
-check_node(Name, Node, Host, LongOrShortNames) ->
+check_node(Driver, Name, Node, Host, LongOrShortNames) ->
case split_node(Host, $., []) of
[_] when LongOrShortNames == longnames ->
- error_logger:error_msg("** System running to use "
- "fully qualified "
- "hostnames **~n"
- "** Hostname ~s is illegal **~n",
- [Host]),
- ?shutdown(Node);
+ case Driver:parse_address(Host) of
+ {ok, _} ->
+ [Name, Host];
+ _ ->
+ error_logger:error_msg("** System running to use "
+ "fully qualified "
+ "hostnames **~n"
+ "** Hostname ~s is illegal **~n",
+ [Host]),
+ ?shutdown(Node)
+ end;
[_, _ | _] when LongOrShortNames == shortnames ->
error_logger:error_msg("** System NOT running to use fully qualified "
"hostnames **~n"
diff --git a/lib/ssl/src/ssl.app.src b/lib/ssl/src/ssl.app.src
index 619ab7b610..1a2bf90ccf 100644
--- a/lib/ssl/src/ssl.app.src
+++ b/lib/ssl/src/ssl.app.src
@@ -31,6 +31,7 @@
ssl_listen_tracker_sup,
%% Erlang Distribution over SSL/TLS
inet_tls_dist,
+ inet6_tls_dist,
ssl_tls_dist_proxy,
ssl_dist_sup,
%% SSL/TLS session handling
diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl
index 6551308935..9045f8fef9 100644
--- a/lib/ssl/src/ssl.erl
+++ b/lib/ssl/src/ssl.erl
@@ -37,7 +37,7 @@
close/1, close/2, shutdown/2, recv/2, recv/3, send/2, getopts/2, setopts/2
]).
%% SSL/TLS protocol handling
--export([cipher_suites/0, cipher_suites/1, suite_definition/1,
+-export([cipher_suites/0, cipher_suites/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]).
@@ -60,22 +60,19 @@
-spec start() -> ok | {error, reason()}.
-spec start(permanent | transient | temporary) -> ok | {error, reason()}.
%%
-%% Description: Utility function that starts the ssl,
-%% crypto and public_key applications. Default type
-%% is temporary. see application(3)
+%% Description: Utility function that starts the ssl and applications
+%% that it depends on.
+%% see application(3)
%%--------------------------------------------------------------------
start() ->
- application:start(crypto),
- application:start(asn1),
- application:start(public_key),
- application:start(ssl).
-
+ start(temporary).
start(Type) ->
- application:start(crypto, Type),
- application:start(asn1),
- application:start(public_key, Type),
- application:start(ssl, Type).
-
+ case application:ensure_all_started(ssl, Type) of
+ {ok, _} ->
+ ok;
+ Other ->
+ Other
+ end.
%%--------------------------------------------------------------------
-spec stop() -> ok.
%%
@@ -105,7 +102,7 @@ connect(Socket, SslOptions0, Timeout) when is_port(Socket),
{gen_tcp, tcp, tcp_closed, tcp_error}),
EmulatedOptions = ssl_socket:emulated_options(),
{ok, SocketValues} = ssl_socket:getopts(Transport, Socket, EmulatedOptions),
- try handle_options(SslOptions0 ++ SocketValues) of
+ try handle_options(SslOptions0 ++ SocketValues, client) of
{ok, #config{transport_info = CbInfo, ssl = SslOptions, emulated = EmOpts,
connection_cb = ConnectionCb}} ->
@@ -127,7 +124,7 @@ connect(Host, Port, Options) ->
connect(Host, Port, Options, infinity).
connect(Host, Port, Options, Timeout) when (is_integer(Timeout) andalso Timeout > 0) or (Timeout == infinity) ->
- try handle_options(Options) of
+ try handle_options(Options, client) of
{ok, Config} ->
do_connect(Host,Port,Config,Timeout)
catch
@@ -145,7 +142,7 @@ listen(_Port, []) ->
{error, nooptions};
listen(Port, Options0) ->
try
- {ok, Config} = handle_options(Options0),
+ {ok, Config} = handle_options(Options0, server),
ConnectionCb = connection_cb(Options0),
#config{transport_info = {Transport, _, _, _}, inet_user = Options, connection_cb = ConnectionCb,
ssl = SslOpts, emulated = EmOpts} = Config,
@@ -233,7 +230,7 @@ ssl_accept(Socket, SslOptions, Timeout) when is_port(Socket),
EmulatedOptions = ssl_socket:emulated_options(),
{ok, SocketValues} = ssl_socket:getopts(Transport, Socket, EmulatedOptions),
ConnetionCb = connection_cb(SslOptions),
- try handle_options(SslOptions ++ SocketValues) of
+ try handle_options(SslOptions ++ SocketValues, server) of
{ok, #config{transport_info = CbInfo, ssl = SslOpts, emulated = EmOpts}} ->
ok = ssl_socket:setopts(Transport, Socket, ssl_socket:internal_inet_values()),
{ok, Port} = ssl_socket:port(Transport, Socket),
@@ -315,24 +312,32 @@ controlling_process(#sslsocket{pid = {Listen,
%%
%% Description: Return SSL information for the connection
%%--------------------------------------------------------------------
-connection_information(#sslsocket{pid = Pid}) when is_pid(Pid) -> ssl_connection:connection_information(Pid);
-connection_information(#sslsocket{pid = {Listen, _}}) when is_port(Listen) -> {error, enotconn}.
-
+connection_information(#sslsocket{pid = Pid}) when is_pid(Pid) ->
+ case ssl_connection:connection_information(Pid) of
+ {ok, Info} ->
+ {ok, [Item || Item = {_Key, Value} <- Info, Value =/= undefined]};
+ Error ->
+ Error
+ end;
+connection_information(#sslsocket{pid = {Listen, _}}) when is_port(Listen) ->
+ {error, enotconn}.
%%--------------------------------------------------------------------
--spec connection_information(#sslsocket{}, [atom]) -> {ok, list()} | {error, reason()}.
+-spec connection_information(#sslsocket{}, [atom()]) -> {ok, list()} | {error, reason()}.
%%
%% Description: Return SSL information for the connection
%%--------------------------------------------------------------------
connection_information(#sslsocket{} = SSLSocket, Items) ->
case connection_information(SSLSocket) of
- {ok, I} ->
- {ok, lists:filter(fun({K, _}) -> lists:foldl(fun(K1, Acc) when K1 =:= K -> Acc + 1; (_, Acc) -> Acc end, 0, Items) > 0 end, I)};
- E ->
- E
+ {ok, Info} ->
+ {ok, [Item || Item = {Key, Value} <- Info, lists:member(Key, Items),
+ Value =/= undefined]};
+ Error ->
+ Error
end.
%%--------------------------------------------------------------------
+%% Deprecated
-spec connection_info(#sslsocket{}) -> {ok, {tls_record:tls_atom_version(), ssl_cipher:erl_cipher_suite()}} |
{error, reason()}.
%%
@@ -372,15 +377,6 @@ peercert(#sslsocket{pid = {Listen, _}}) when is_port(Listen) ->
{error, enotconn}.
%%--------------------------------------------------------------------
--spec suite_definition(ssl_cipher:cipher_suite()) -> ssl_cipher:erl_cipher_suite().
-%%
-%% Description: Return erlang cipher suite definition.
-%%--------------------------------------------------------------------
-suite_definition(S) ->
- {KeyExchange, Cipher, Hash, _} = ssl_cipher:suite_definition(S),
- {KeyExchange, Cipher, Hash}.
-
-%%--------------------------------------------------------------------
-spec negotiated_protocol(#sslsocket{}) -> {ok, binary()} | {error, reason()}.
%%
%% Description: Returns the protocol that has been negotiated. If no
@@ -410,7 +406,7 @@ negotiated_next_protocol(Socket) ->
%%--------------------------------------------------------------------
cipher_suites(erlang) ->
Version = tls_record:highest_protocol_version([]),
- ssl_cipher:filter_suites([suite_definition(S)
+ ssl_cipher:filter_suites([ssl_cipher:erl_suite_definition(S)
|| S <- ssl_cipher:suites(Version)]);
cipher_suites(openssl) ->
Version = tls_record:highest_protocol_version([]),
@@ -418,7 +414,7 @@ cipher_suites(openssl) ->
|| S <- ssl_cipher:filter_suites(ssl_cipher:suites(Version))];
cipher_suites(all) ->
Version = tls_record:highest_protocol_version([]),
- ssl_cipher:filter_suites([suite_definition(S)
+ ssl_cipher:filter_suites([ssl_cipher:erl_suite_definition(S)
|| S <-ssl_cipher:all_suites(Version)]).
cipher_suites() ->
cipher_suites(erlang).
@@ -630,7 +626,8 @@ handle_options(Opts0, #ssl_options{protocol = Protocol, cacerts = CaCerts0,
cacertfile = CaCertFile0} = InheritedSslOpts) ->
RecordCB = record_cb(Protocol),
CaCerts = handle_option(cacerts, Opts0, CaCerts0),
- {Verify, FailIfNoPeerCert, CaCertDefault, VerifyFun, PartialChainHanlder} = handle_verify_options(Opts0, CaCerts),
+ {Verify, FailIfNoPeerCert, CaCertDefault, VerifyFun, PartialChainHanlder,
+ VerifyClientOnce} = handle_verify_options(Opts0, CaCerts),
CaCertFile = case proplists:get_value(cacertfile, Opts0, CaCertFile0) of
undefined ->
CaCertDefault;
@@ -643,11 +640,12 @@ handle_options(Opts0, #ssl_options{protocol = Protocol, cacerts = CaCerts0,
verify = Verify,
verify_fun = VerifyFun,
partial_chain = PartialChainHanlder,
- fail_if_no_peer_cert = FailIfNoPeerCert},
+ fail_if_no_peer_cert = FailIfNoPeerCert,
+ verify_client_once = VerifyClientOnce},
SslOpts1 = lists:foldl(fun(Key, PropList) ->
proplists:delete(Key, PropList)
end, Opts0, [cacerts, cacertfile, verify, verify_fun, partial_chain,
- fail_if_no_peer_cert]),
+ fail_if_no_peer_cert, verify_client_once]),
case handle_option(versions, SslOpts1, []) of
[] ->
new_ssl_options(SslOpts1, NewVerifyOpts, RecordCB);
@@ -655,10 +653,10 @@ handle_options(Opts0, #ssl_options{protocol = Protocol, cacerts = CaCerts0,
Versions = [RecordCB:protocol_version(Vsn) || Vsn <- Value],
new_ssl_options(proplists:delete(versions, SslOpts1),
NewVerifyOpts#ssl_options{versions = Versions}, record_cb(Protocol))
- end.
+ end;
%% Handle all options in listen and connect
-handle_options(Opts0) ->
+handle_options(Opts0, Role) ->
Opts = proplists:expand([{binary, [{mode, binary}]},
{list, [{mode, list}]}], Opts0),
assert_proplist(Opts),
@@ -667,7 +665,7 @@ handle_options(Opts0) ->
ReuseSessionFun = fun(_, _, _, _) -> true end,
CaCerts = handle_option(cacerts, Opts, undefined),
- {Verify, FailIfNoPeerCert, CaCertDefault, VerifyFun, PartialChainHanlder} =
+ {Verify, FailIfNoPeerCert, CaCertDefault, VerifyFun, PartialChainHanlder, VerifyClientOnce} =
handle_verify_options(Opts, CaCerts),
CertFile = handle_option(certfile, Opts, <<>>),
@@ -686,7 +684,7 @@ handle_options(Opts0) ->
verify_fun = VerifyFun,
partial_chain = PartialChainHanlder,
fail_if_no_peer_cert = FailIfNoPeerCert,
- verify_client_once = handle_option(verify_client_once, Opts, false),
+ verify_client_once = VerifyClientOnce,
depth = handle_option(depth, Opts, 1),
cert = handle_option(cert, Opts, undefined),
certfile = CertFile,
@@ -702,11 +700,17 @@ handle_options(Opts0) ->
srp_identity = handle_option(srp_identity, Opts, undefined),
ciphers = handle_cipher_option(proplists:get_value(ciphers, Opts, []),
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)),
+ RecordCb:highest_protocol_version(Versions)),
%% Server side option
reuse_session = handle_option(reuse_session, Opts, ReuseSessionFun),
reuse_sessions = handle_option(reuse_sessions, Opts, true),
secure_renegotiate = handle_option(secure_renegotiate, Opts, false),
- client_renegotiation = handle_option(client_renegotiation, Opts, true),
+ client_renegotiation = handle_option(client_renegotiation, Opts,
+ default_option_role(server, true, Role),
+ server, Role),
renegotiate_at = handle_option(renegotiate_at, Opts, ?DEFAULT_RENEGOTIATE_AT),
hibernate_after = handle_option(hibernate_after, Opts, undefined),
erl_dist = handle_option(erl_dist, Opts, false),
@@ -723,10 +727,16 @@ handle_options(Opts0) ->
server_name_indication = handle_option(server_name_indication, Opts, undefined),
sni_hosts = handle_option(sni_hosts, Opts, []),
sni_fun = handle_option(sni_fun, Opts, undefined),
- honor_cipher_order = handle_option(honor_cipher_order, Opts, false),
+ honor_cipher_order = handle_option(honor_cipher_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),
- fallback = proplists:get_value(fallback, Opts, false),
+ fallback = handle_option(fallback, Opts,
+ proplists:get_value(fallback, Opts,
+ default_option_role(client,
+ false, Role)),
+ client, Role),
crl_check = handle_option(crl_check, Opts, false),
crl_cache = handle_option(crl_cache, Opts, {ssl_crl_cache, {internal, []}})
},
@@ -743,7 +753,7 @@ handle_options(Opts0) ->
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],
+ fallback, signature_algs],
SockOpts = lists:foldl(fun(Key, PropList) ->
proplists:delete(Key, PropList)
@@ -756,6 +766,13 @@ handle_options(Opts0) ->
inet_user = SockOpts, transport_info = CbInfo, connection_cb = ConnetionCb
}}.
+
+
+handle_option(OptionName, Opts, Default, Role, Role) ->
+ handle_option(OptionName, Opts, Default);
+handle_option(_, _, undefined = Value, _, _) ->
+ Value.
+
handle_option(sni_fun, Opts, Default) ->
OptFun = validate_option(sni_fun,
proplists:get_value(sni_fun, Opts, Default)),
@@ -772,7 +789,6 @@ handle_option(OptionName, Opts, Default) ->
validate_option(OptionName,
proplists:get_value(OptionName, Opts, Default)).
-
validate_option(versions, Versions) ->
validate_versions(Versions, Versions);
validate_option(verify, Value)
@@ -977,6 +993,18 @@ validate_option(crl_cache, {Cb, {_Handle, Options}} = Value) when is_atom(Cb) an
validate_option(Opt, Value) ->
throw({error, {options, {Opt, Value}}}).
+handle_hashsigns_option(Value, {Major, Minor} = Version) when is_list(Value)
+ andalso Major >= 3 andalso Minor >= 3->
+ case tls_v1:signature_algs(Version, Value) of
+ [] ->
+ throw({error, {options, no_supported_algorithms, {signature_algs, Value}}});
+ _ ->
+ Value
+ end;
+handle_hashsigns_option(_, {Major, Minor} = Version) when Major >= 3 andalso Minor >= 3->
+ handle_hashsigns_option(tls_v1:default_signature_algs(Version), Version);
+handle_hashsigns_option(_, _Version) ->
+ undefined.
validate_options([]) ->
[];
@@ -1216,7 +1244,8 @@ emulated_socket_options(InetValues, #socket_options{
new_ssl_options([], #ssl_options{} = Opts, _) ->
Opts;
new_ssl_options([{verify_client_once, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
- new_ssl_options(Rest, Opts#ssl_options{verify_client_once = validate_option(verify_client_once, Value)}, RecordCB);
+ new_ssl_options(Rest, Opts#ssl_options{verify_client_once =
+ validate_option(verify_client_once, Value)}, RecordCB);
new_ssl_options([{depth, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
new_ssl_options(Rest, Opts#ssl_options{depth = validate_option(depth, Value)}, RecordCB);
new_ssl_options([{cert, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
@@ -1272,6 +1301,13 @@ 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([{signature_algs, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
+ new_ssl_options(Rest,
+ Opts#ssl_options{signature_algs =
+ handle_hashsigns_option(Value,
+ RecordCB:highest_protocol_version())},
+ RecordCB);
+
new_ssl_options([{Key, Value} | _Rest], #ssl_options{}, _) ->
throw({error, {options, {Key, Value}}}).
@@ -1280,6 +1316,12 @@ handle_verify_options(Opts, CaCerts) ->
DefaultVerifyNoneFun =
{fun(_,{bad_cert, _}, UserState) ->
{valid, UserState};
+ (_,{extension, #'Extension'{critical = true}}, UserState) ->
+ %% This extension is marked as critical, so
+ %% certificate verification should fail if we don't
+ %% understand the extension. However, this is
+ %% `verify_none', so let's accept it anyway.
+ {valid, UserState};
(_,{extension, _}, UserState) ->
{unknown, UserState};
(_, valid, UserState) ->
@@ -1295,29 +1337,35 @@ handle_verify_options(Opts, CaCerts) ->
PartialChainHanlder = handle_option(partial_chain, Opts,
fun(_) -> unknown_ca end),
+ VerifyClientOnce = handle_option(verify_client_once, Opts, false),
+
%% Handle 0, 1, 2 for backwards compatibility
case proplists:get_value(verify, Opts, verify_none) of
0 ->
{verify_none, false,
ca_cert_default(verify_none, VerifyNoneFun, CaCerts),
- VerifyNoneFun, PartialChainHanlder};
+ VerifyNoneFun, PartialChainHanlder, VerifyClientOnce};
1 ->
{verify_peer, false,
ca_cert_default(verify_peer, UserVerifyFun, CaCerts),
- UserVerifyFun, PartialChainHanlder};
+ UserVerifyFun, PartialChainHanlder, VerifyClientOnce};
2 ->
{verify_peer, true,
ca_cert_default(verify_peer, UserVerifyFun, CaCerts),
- UserVerifyFun, PartialChainHanlder};
+ UserVerifyFun, PartialChainHanlder, VerifyClientOnce};
verify_none ->
{verify_none, false,
ca_cert_default(verify_none, VerifyNoneFun, CaCerts),
- VerifyNoneFun, PartialChainHanlder};
+ VerifyNoneFun, PartialChainHanlder, VerifyClientOnce};
verify_peer ->
{verify_peer, UserFailIfNoPeerCert,
ca_cert_default(verify_peer, UserVerifyFun, CaCerts),
- UserVerifyFun, PartialChainHanlder};
+ UserVerifyFun, PartialChainHanlder, VerifyClientOnce};
Value ->
throw({error, {options, {verify, Value}}})
end.
+default_option_role(Role, Value, Role) ->
+ Value;
+default_option_role(_,_,_) ->
+ undefined.
diff --git a/lib/ssl/src/ssl_certificate.erl b/lib/ssl/src/ssl_certificate.erl
index 4658e76ab1..e9dc5764a3 100644
--- a/lib/ssl/src/ssl_certificate.erl
+++ b/lib/ssl/src/ssl_certificate.erl
@@ -56,15 +56,15 @@
%% errors. Returns {RootCert, Path, VerifyErrors}
%%--------------------------------------------------------------------
trusted_cert_and_path(CertChain, CertDbHandle, CertDbRef, PartialChainHandler) ->
- Path = [Cert | _] = lists:reverse(CertChain),
- OtpCert = public_key:pkix_decode_cert(Cert, otp),
+ Path = [BinCert | _] = lists:reverse(CertChain),
+ OtpCert = public_key:pkix_decode_cert(BinCert, otp),
SignedAndIssuerID =
case public_key:pkix_is_self_signed(OtpCert) of
true ->
{ok, IssuerId} = public_key:pkix_issuer_id(OtpCert, self),
{self, IssuerId};
false ->
- other_issuer(OtpCert, CertDbHandle)
+ other_issuer(OtpCert, BinCert, CertDbHandle)
end,
case SignedAndIssuerID of
@@ -187,7 +187,7 @@ public_key_type(?'id-ecPublicKey') ->
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
-certificate_chain(OtpCert, _Cert, CertDbHandle, CertsDbRef, Chain) ->
+certificate_chain(OtpCert, BinCert, CertDbHandle, CertsDbRef, Chain) ->
IssuerAndSelfSigned =
case public_key:pkix_is_self_signed(OtpCert) of
true ->
@@ -200,7 +200,7 @@ certificate_chain(OtpCert, _Cert, CertDbHandle, CertsDbRef, Chain) ->
{_, true = SelfSigned} ->
certificate_chain(CertDbHandle, CertsDbRef, Chain, ignore, ignore, SelfSigned);
{{error, issuer_not_found}, SelfSigned} ->
- case find_issuer(OtpCert, CertDbHandle) of
+ case find_issuer(OtpCert, BinCert, CertDbHandle) of
{ok, {SerialNr, Issuer}} ->
certificate_chain(CertDbHandle, CertsDbRef, Chain,
SerialNr, Issuer, SelfSigned);
@@ -232,12 +232,12 @@ certificate_chain(CertDbHandle, CertsDbRef, Chain, SerialNr, Issuer, _SelfSigned
{ok, undefined, lists:reverse(Chain)}
end.
-find_issuer(OtpCert, CertDbHandle) ->
+find_issuer(OtpCert, BinCert, CertDbHandle) ->
IsIssuerFun =
fun({_Key, {_Der, #'OTPCertificate'{} = ErlCertCandidate}}, Acc) ->
case public_key:pkix_is_issuer(OtpCert, ErlCertCandidate) of
true ->
- case verify_cert_signer(OtpCert, ErlCertCandidate#'OTPCertificate'.tbsCertificate) of
+ case verify_cert_signer(BinCert, ErlCertCandidate#'OTPCertificate'.tbsCertificate) of
true ->
throw(public_key:pkix_issuer_id(ErlCertCandidate, self));
false ->
@@ -265,9 +265,9 @@ is_valid_extkey_usage(KeyUse, server) ->
%% Server wants to verify client
is_valid_key_usage(KeyUse, ?'id-kp-clientAuth').
-verify_cert_signer(OtpCert, SignerTBSCert) ->
+verify_cert_signer(BinCert, SignerTBSCert) ->
PublicKey = public_key(SignerTBSCert#'OTPTBSCertificate'.subjectPublicKeyInfo),
- public_key:pkix_verify(public_key:pkix_encode('OTPCertificate', OtpCert, otp), PublicKey).
+ public_key:pkix_verify(BinCert, PublicKey).
public_key(#'OTPSubjectPublicKeyInfo'{algorithm = #'PublicKeyAlgorithm'{algorithm = ?'id-ecPublicKey',
parameters = Params},
@@ -281,12 +281,12 @@ public_key(#'OTPSubjectPublicKeyInfo'{algorithm = #'PublicKeyAlgorithm'{algorith
subjectPublicKey = Key}) ->
{Key, Params}.
-other_issuer(OtpCert, CertDbHandle) ->
+other_issuer(OtpCert, BinCert, CertDbHandle) ->
case public_key:pkix_issuer_id(OtpCert, other) of
{ok, IssuerId} ->
{other, IssuerId};
{error, issuer_not_found} ->
- case find_issuer(OtpCert, CertDbHandle) of
+ case find_issuer(OtpCert, BinCert, CertDbHandle) of
{ok, IssuerId} ->
{other, IssuerId};
Other ->
diff --git a/lib/ssl/src/ssl_cipher.erl b/lib/ssl/src/ssl_cipher.erl
index 8c2a16ba96..cbe3a2a056 100644
--- a/lib/ssl/src/ssl_cipher.erl
+++ b/lib/ssl/src/ssl_cipher.erl
@@ -34,22 +34,27 @@
-include_lib("public_key/include/public_key.hrl").
-export([security_parameters/2, security_parameters/3, suite_definition/1,
+ erl_suite_definition/1,
cipher_init/3, decipher/6, cipher/5, decipher_aead/6, cipher_aead/6,
suite/1, suites/1, all_suites/1,
ec_keyed_suites/0, anonymous_suites/1, psk_suites/1, srp_suites/0,
- rc4_suites/1, openssl_suite/1, openssl_suite_name/1, filter/2, filter_suites/1,
+ rc4_suites/1, des_suites/1, openssl_suite/1, openssl_suite_name/1, filter/2, filter_suites/1,
hash_algorithm/1, sign_algorithm/1, is_acceptable_hash/2, is_fallback/1]).
-export_type([cipher_suite/0,
erl_cipher_suite/0, openssl_cipher_suite/0,
- key_algo/0]).
+ hash/0, key_algo/0, sign_algo/0]).
-type cipher() :: null |rc4_128 | idea_cbc | des40_cbc | 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 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.
--type erl_cipher_suite() :: {key_algo(), cipher(), hash()}.
--type int_cipher_suite() :: {key_algo(), cipher(), hash(), hash() | default_prf}.
+-type erl_cipher_suite() :: {key_algo(), cipher(), hash()} % Pre TLS 1.2
+ %% TLS 1.2, internally PRE TLS 1.2 will use default_prf
+ | {key_algo(), cipher(), hash(), hash() | default_prf}.
+
+
-type cipher_suite() :: binary().
-type cipher_enum() :: integer().
-type openssl_cipher_suite() :: string().
@@ -311,7 +316,8 @@ all_suites(Version) ->
++ anonymous_suites(Version)
++ psk_suites(Version)
++ srp_suites()
- ++ rc4_suites(Version).
+ ++ rc4_suites(Version)
+ ++ des_suites(Version).
%%--------------------------------------------------------------------
-spec anonymous_suites(ssl_record:ssl_version() | integer()) -> [cipher_suite()].
%%
@@ -415,9 +421,19 @@ rc4_suites({3, N}) when N =< 3 ->
?TLS_RSA_WITH_RC4_128_MD5,
?TLS_ECDH_ECDSA_WITH_RC4_128_SHA,
?TLS_ECDH_RSA_WITH_RC4_128_SHA].
+%%--------------------------------------------------------------------
+-spec des_suites(Version::ssl_record:ssl_version()) -> [cipher_suite()].
+%%
+%% Description: Returns a list of the cipher suites
+%% with DES cipher, only supported if explicitly set by user.
+%% Are not considered secure any more.
+%%--------------------------------------------------------------------
+des_suites(_)->
+ [?TLS_DHE_RSA_WITH_DES_CBC_SHA,
+ ?TLS_RSA_WITH_DES_CBC_SHA].
%%--------------------------------------------------------------------
--spec suite_definition(cipher_suite()) -> int_cipher_suite().
+-spec suite_definition(cipher_suite()) -> erl_cipher_suite().
%%
%% Description: Return erlang cipher suite definition.
%% Note: Currently not supported suites are commented away.
@@ -722,6 +738,20 @@ suite_definition(?TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256) ->
{dhe_rsa, chacha20_poly1305, null, sha256}.
%%--------------------------------------------------------------------
+-spec erl_suite_definition(cipher_suite()) -> erl_cipher_suite().
+%%
+%% Description: Return erlang cipher suite definition. Filters last value
+%% for now (compatibility reasons).
+%%--------------------------------------------------------------------
+erl_suite_definition(S) ->
+ case suite_definition(S) of
+ {KeyExchange, Cipher, Hash, default_prf} ->
+ {KeyExchange, Cipher, Hash};
+ Suite ->
+ Suite
+ end.
+
+%%--------------------------------------------------------------------
-spec suite(erl_cipher_suite()) -> cipher_suite().
%%
%% Description: Return TLS cipher suite definition.
@@ -1384,18 +1414,14 @@ filter(DerCert, Ciphers) ->
%%
%% Description: Filter suites for algorithms supported by crypto.
%%-------------------------------------------------------------------
-filter_suites(Suites = [{_,_,_}|_]) ->
+filter_suites(Suites = [Value|_]) when is_tuple(Value) ->
Algos = crypto:supports(),
+ Hashs = proplists:get_value(hashs, Algos),
lists:filter(fun({KeyExchange, Cipher, Hash}) ->
is_acceptable_keyexchange(KeyExchange, proplists:get_value(public_keys, Algos)) andalso
is_acceptable_cipher(Cipher, proplists:get_value(ciphers, Algos)) andalso
- is_acceptable_hash(Hash, proplists:get_value(hashs, Algos))
- end, Suites);
-
-filter_suites(Suites = [{_,_,_,_}|_]) ->
- Algos = crypto:supports(),
- Hashs = proplists:get_value(hashs, Algos),
- lists:filter(fun({KeyExchange, Cipher, Hash, Prf}) ->
+ is_acceptable_hash(Hash, proplists:get_value(hashs, Algos));
+ ({KeyExchange, Cipher, Hash, Prf}) ->
is_acceptable_keyexchange(KeyExchange, proplists:get_value(public_keys, Algos)) andalso
is_acceptable_cipher(Cipher, proplists:get_value(ciphers, Algos)) andalso
is_acceptable_hash(Hash, Hashs) andalso
@@ -1714,7 +1740,8 @@ dhe_rsa_suites() ->
?TLS_DHE_RSA_WITH_DES_CBC_SHA,
?TLS_DHE_RSA_WITH_AES_128_GCM_SHA256,
?TLS_DHE_RSA_WITH_AES_256_GCM_SHA384,
- ?TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256].
+ ?TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256
+ ].
psk_rsa_suites() ->
[?TLS_RSA_PSK_WITH_AES_256_GCM_SHA384,
diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl
index 241871dc38..f774873269 100644
--- a/lib/ssl/src/ssl_connection.erl
+++ b/lib/ssl/src/ssl_connection.erl
@@ -304,13 +304,9 @@ hello(#hello_request{}, #state{role = client} = State0, Connection) ->
{Record, State} = Connection:next_record(State0),
Connection:next_state(hello, hello, Record, State);
-hello({common_client_hello, Type, ServerHelloExt, NegotiatedHashSign},
+hello({common_client_hello, Type, ServerHelloExt},
State, Connection) ->
- do_server_hello(Type, ServerHelloExt,
- %% Note NegotiatedHashSign is only negotiated for real if
- %% if TLS version is at least TLS-1.2
- State#state{hashsign_algorithm = NegotiatedHashSign}, Connection);
-
+ do_server_hello(Type, ServerHelloExt, State, Connection);
hello(timeout, State, _) ->
{next_state, hello, State, hibernate};
@@ -442,7 +438,8 @@ certify(#server_key_exchange{exchange_keys = Keys},
Alg == srp_dss; Alg == srp_rsa; Alg == srp_anon ->
Params = ssl_handshake:decode_server_key(Keys, Alg, Version),
- HashSign = negotiated_hashsign(Params#server_key_params.hashsign, Alg, Version),
+ %% Use negotiated value if TLS-1.2 otherwhise return default
+ HashSign = negotiated_hashsign(Params#server_key_params.hashsign, Alg, PubKeyInfo, Version),
case is_anonymous(Alg) of
true ->
calculate_secret(Params#server_key_params.params,
@@ -464,11 +461,18 @@ certify(#server_key_exchange{} = Msg,
certify(#certificate_request{hashsign_algorithms = HashSigns},
#state{session = #session{own_certificate = Cert},
- negotiated_version = Version} = State0, Connection) ->
- HashSign = ssl_handshake:select_hashsign(HashSigns, Cert, Version),
- {Record, State} = Connection:next_record(State0#state{client_certificate_requested = true}),
- Connection:next_state(certify, certify, Record,
- State#state{cert_hashsign_algorithm = HashSign});
+ key_algorithm = KeyExAlg,
+ ssl_options = #ssl_options{signature_algs = SupportedHashSigns},
+ negotiated_version = Version} = State0, Connection) ->
+
+ case ssl_handshake:select_hashsign(HashSigns, Cert, KeyExAlg, SupportedHashSigns, Version) of
+ #alert {} = Alert ->
+ Connection:handle_own_alert(Alert, Version, certify, State0);
+ NegotiatedHashSign ->
+ {Record, State} = Connection:next_record(State0#state{client_certificate_requested = true}),
+ Connection:next_state(certify, certify, Record,
+ State#state{cert_hashsign_algorithm = NegotiatedHashSign})
+ end;
%% PSK and RSA_PSK might bypass the Server-Key-Exchange
certify(#server_hello_done{},
@@ -576,13 +580,15 @@ cipher(#hello_request{}, State0, Connection) ->
cipher(#certificate_verify{signature = Signature, hashsign_algorithm = CertHashSign},
#state{role = server,
- public_key_info = {Algo, _, _} =PublicKeyInfo,
+ key_algorithm = KexAlg,
+ public_key_info = PublicKeyInfo,
negotiated_version = Version,
session = #session{master_secret = MasterSecret},
tls_handshake_history = Handshake
} = State0, Connection) ->
-
- HashSign = ssl_handshake:select_hashsign_algs(CertHashSign, Algo, Version),
+
+ %% Use negotiated value if TLS-1.2 otherwhise return default
+ HashSign = negotiated_hashsign(CertHashSign, KexAlg, PublicKeyInfo, Version),
case ssl_handshake:certificate_verify(Signature, PublicKeyInfo,
Version, HashSign, MasterSecret, Handshake) of
valid ->
@@ -836,15 +842,22 @@ handle_sync_event(session_info, _, StateName,
#state{session = #session{session_id = Id,
cipher_suite = Suite}} = State) ->
{reply, [{session_id, Id},
- {cipher_suite, ssl:suite_definition(Suite)}],
+ {cipher_suite, ssl_cipher:erl_suite_definition(Suite)}],
StateName, State, get_timeout(State)};
handle_sync_event(peer_certificate, _, StateName,
#state{session = #session{peer_certificate = Cert}}
= State) ->
{reply, {ok, Cert}, StateName, State, get_timeout(State)};
-handle_sync_event(connection_information, _, StateName, #state{sni_hostname = SNIHostname, session = #session{cipher_suite = CipherSuite}, negotiated_version = Version} = State) ->
- {reply, {ok, [{protocol, tls_record:protocol_version(Version)}, {cipher_suite, ssl:suite_definition(CipherSuite)}, {sni_hostname, SNIHostname}]}, StateName, State, get_timeout(State)}.
+handle_sync_event(connection_information, _, StateName, State) ->
+ Info = connection_info(State),
+ {reply, {ok, Info}, StateName, State, get_timeout(State)}.
+connection_info(#state{sni_hostname = SNIHostname,
+ session = #session{cipher_suite = CipherSuite},
+ negotiated_version = Version, ssl_options = Opts}) ->
+ [{protocol, tls_record:protocol_version(Version)},
+ {cipher_suite, ssl_cipher:erl_suite_definition(CipherSuite)},
+ {sni_hostname, SNIHostname}] ++ ssl_options_list(Opts).
handle_info({ErrorTag, Socket, econnaborted}, StateName,
#state{socket = Socket, transport_cb = Transport,
@@ -1441,7 +1454,8 @@ rsa_psk_key_exchange(Version, PskIdentity, PremasterSecret, PublicKeyInfo = {Alg
rsa_psk_key_exchange(_, _, _, _) ->
throw (?ALERT_REC(?FATAL,?HANDSHAKE_FAILURE)).
-request_client_cert(#state{ssl_options = #ssl_options{verify = verify_peer},
+request_client_cert(#state{ssl_options = #ssl_options{verify = verify_peer,
+ signature_algs = SupportedHashSigns},
connection_states = ConnectionStates0,
cert_db = CertDbHandle,
cert_db_ref = CertDbRef,
@@ -1449,7 +1463,9 @@ request_client_cert(#state{ssl_options = #ssl_options{verify = verify_peer},
#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),
+ HashSigns = ssl_handshake:available_signature_algs(SupportedHashSigns, Version, [Version]),
+ Msg = ssl_handshake:certificate_request(CipherSuite, CertDbHandle, CertDbRef,
+ HashSigns, Version),
State = Connection:send_handshake(Msg, State0),
State#state{client_certificate_requested = true};
@@ -1874,14 +1890,40 @@ make_premaster_secret({MajVer, MinVer}, rsa) ->
make_premaster_secret(_, _) ->
undefined.
-negotiated_hashsign(undefined, Alg, Version) ->
+negotiated_hashsign(undefined, KexAlg, PubKeyInfo, Version) ->
%% Not negotiated choose default
- case is_anonymous(Alg) of
+ case is_anonymous(KexAlg) of
true ->
{null, anon};
false ->
- ssl_handshake:select_hashsign_algs(Alg, Version)
+ {PubAlg, _, _} = PubKeyInfo,
+ ssl_handshake:select_hashsign_algs(undefined, PubAlg, Version)
end;
-negotiated_hashsign(HashSign = {_, _}, _, _) ->
+negotiated_hashsign(HashSign = {_, _}, _, _, _) ->
HashSign.
+ssl_options_list(SslOptions) ->
+ Fileds = record_info(fields, ssl_options),
+ Values = tl(tuple_to_list(SslOptions)),
+ ssl_options_list(Fileds, Values, []).
+
+ssl_options_list([],[], Acc) ->
+ lists:reverse(Acc);
+%% Skip internal options, only return user options
+ssl_options_list([protocol | Keys], [_ | Values], Acc) ->
+ ssl_options_list(Keys, Values, Acc);
+ssl_options_list([erl_dist | Keys], [_ | Values], Acc) ->
+ ssl_options_list(Keys, Values, Acc);
+ssl_options_list([renegotiate_at | Keys], [_ | Values], Acc) ->
+ ssl_options_list(Keys, Values, Acc);
+ssl_options_list([ciphers = Key | Keys], [Value | Values], Acc) ->
+ ssl_options_list(Keys, Values,
+ [{Key, lists:map(
+ fun(Suite) ->
+ ssl_cipher:erl_suite_definition(Suite)
+ end, Value)}
+ | Acc]);
+ssl_options_list([Key | Keys], [Value | Values], Acc) ->
+ ssl_options_list(Keys, Values, [{Key, Value} | Acc]).
+
+
diff --git a/lib/ssl/src/ssl_connection.hrl b/lib/ssl/src/ssl_connection.hrl
index 9a58f2b8f7..bb41ef2b62 100644
--- a/lib/ssl/src/ssl_connection.hrl
+++ b/lib/ssl/src/ssl_connection.hrl
@@ -48,27 +48,28 @@
socket_options :: #socket_options{},
connection_states :: #connection_states{} | secret_printout(),
protocol_buffers :: term() | secret_printout() , %% #protocol_buffers{} from tls_record.hrl or dtls_recor.hrl
- tls_handshake_history :: ssl_handshake:ssl_handshake_history() | secret_printout(),
- cert_db :: reference(),
+ tls_handshake_history :: ssl_handshake:ssl_handshake_history() | secret_printout()
+ | 'undefined',
+ cert_db :: reference() | 'undefined',
session :: #session{} | secret_printout(),
session_cache :: db_handle(),
session_cache_cb :: atom(),
crl_db :: term(),
- negotiated_version :: ssl_record:ssl_version(),
+ negotiated_version :: ssl_record:ssl_version() | 'undefined',
client_certificate_requested = false :: boolean(),
key_algorithm :: ssl_cipher:key_algo(),
hashsign_algorithm = {undefined, undefined},
cert_hashsign_algorithm,
- public_key_info :: ssl_handshake:public_key_info(),
- private_key :: public_key:private_key() | secret_printout(),
+ public_key_info :: ssl_handshake:public_key_info() | 'undefined',
+ private_key :: public_key:private_key() | secret_printout() | 'undefined',
diffie_hellman_params:: #'DHParameter'{} | undefined | secret_printout(),
diffie_hellman_keys :: {PublicKey :: binary(), PrivateKey :: binary()} | #'ECPrivateKey'{} | undefined | secret_printout(),
- psk_identity :: binary(), % server psk identity hint
- srp_params :: #srp_user{} | secret_printout(),
- srp_keys ::{PublicKey :: binary(), PrivateKey :: binary()} | secret_printout(),
- premaster_secret :: binary() | secret_printout() ,
+ psk_identity :: binary() | 'undefined', % server psk identity hint
+ srp_params :: #srp_user{} | secret_printout() | 'undefined',
+ srp_keys ::{PublicKey :: binary(), PrivateKey :: binary()} | secret_printout() | 'undefined',
+ premaster_secret :: binary() | secret_printout() | 'undefined',
file_ref_db :: db_handle(),
- cert_db_ref :: certdb_ref(),
+ cert_db_ref :: certdb_ref() | 'undefined',
bytes_to_read :: undefined | integer(), %% bytes to read in passive mode
user_data_buffer :: undefined | binary() | secret_printout(),
renegotiation :: undefined | {boolean(), From::term() | internal | peer},
@@ -81,7 +82,7 @@
expecting_finished = false ::boolean(),
negotiated_protocol = undefined :: undefined | binary(),
client_ecc, % {Curves, PointFmt}
- tracker :: pid(), %% Tracker process for listen socket
+ tracker :: pid() | 'undefined', %% Tracker process for listen socket
sni_hostname = undefined
}).
diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl
index e9e140836b..644903cf4b 100644
--- a/lib/ssl/src/ssl_handshake.erl
+++ b/lib/ssl/src/ssl_handshake.erl
@@ -46,7 +46,7 @@
%% Handshake messages
-export([hello_request/0, server_hello/4, server_hello_done/0,
- certificate/4, certificate_request/4, key_exchange/3,
+ certificate/4, certificate_request/5, key_exchange/3,
finished/5, next_protocol/1]).
%% Handle handshake messages
@@ -64,8 +64,8 @@
]).
%% Cipher suites handling
--export([available_suites/2, cipher_suites/2,
- select_session/10, supported_ecc/1]).
+-export([available_suites/2, available_signature_algs/3, cipher_suites/2,
+ select_session/11, supported_ecc/1]).
%% Extensions handling
-export([client_hello_extensions/6,
@@ -74,8 +74,8 @@
]).
%% MISC
--export([select_version/3, prf/5, select_hashsign/3,
- select_hashsign_algs/2, select_hashsign_algs/3,
+-export([select_version/3, prf/5, select_hashsign/5,
+ select_hashsign_algs/3,
premaster_secret/2, premaster_secret/3, premaster_secret/4]).
%%====================================================================
@@ -120,7 +120,8 @@ server_hello(SessionId, Version, ConnectionStates, Extensions) ->
server_hello_done() ->
#server_hello_done{}.
-client_hello_extensions(Host, Version, CipherSuites, SslOpts, ConnectionStates, Renegotiation) ->
+client_hello_extensions(Host, Version, CipherSuites,
+ #ssl_options{signature_algs = SupportedHashSigns, versions = AllVersions} = SslOpts, ConnectionStates, Renegotiation) ->
{EcPointFormats, EllipticCurves} =
case advertises_ec_ciphers(lists:map(fun ssl_cipher:suite_definition/1, CipherSuites)) of
true ->
@@ -134,7 +135,7 @@ client_hello_extensions(Host, Version, CipherSuites, SslOpts, ConnectionStates,
renegotiation_info = renegotiation_info(tls_record, client,
ConnectionStates, Renegotiation),
srp = SRP,
- hash_signs = advertised_hash_signs(Version),
+ signature_algs = available_signature_algs(SupportedHashSigns, Version, AllVersions),
ec_point_formats = EcPointFormats,
elliptic_curves = EllipticCurves,
alpn = encode_alpn(SslOpts#ssl_options.alpn_advertised_protocols, Renegotiation),
@@ -203,14 +204,14 @@ client_certificate_verify(OwnCert, MasterSecret, Version,
end.
%%--------------------------------------------------------------------
--spec certificate_request(ssl_cipher:cipher_suite(), db_handle(), certdb_ref(), ssl_record:ssl_version()) ->
- #certificate_request{}.
+-spec certificate_request(ssl_cipher:cipher_suite(), db_handle(),
+ certdb_ref(), #hash_sign_algos{}, ssl_record:ssl_version()) ->
+ #certificate_request{}.
%%
%% Description: Creates a certificate_request message, called by the server.
%%--------------------------------------------------------------------
-certificate_request(CipherSuite, CertDbHandle, CertDbRef, Version) ->
+certificate_request(CipherSuite, CertDbHandle, CertDbRef, HashSigns, Version) ->
Types = certificate_types(ssl_cipher:suite_definition(CipherSuite), Version),
- HashSigns = advertised_hash_signs(Version),
Authorities = certificate_authorities(CertDbHandle, CertDbRef),
#certificate_request{
certificate_types = Types,
@@ -351,6 +352,9 @@ verify_server_key(#server_key_params{params_bin = EncParams,
%%
%% Description: Checks that the certificate_verify message is valid.
%%--------------------------------------------------------------------
+certificate_verify(_, _, _, undefined, _, _) ->
+ ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE);
+
certificate_verify(Signature, PublicKeyInfo, Version,
HashSign = {HashAlgo, _}, MasterSecret, {_, Handshake}) ->
Hash = calc_certificate_verify(Version, HashAlgo, MasterSecret, Handshake),
@@ -379,10 +383,11 @@ verify_signature(_Version, Hash, _HashAlgo, Signature, {?rsaEncryption, PubKey,
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,
+verify_signature(_, Hash, {HashAlgo, _SignAlg}, 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}, fun(), term(), term(),
@@ -573,43 +578,46 @@ prf({3,_N}, Secret, Label, Seed, WantedLength) ->
%%--------------------------------------------------------------------
--spec select_hashsign(#hash_sign_algos{}| undefined, undefined | binary(), ssl_record:ssl_version()) ->
- {atom(), atom()} | undefined.
+-spec select_hashsign(#hash_sign_algos{} | undefined, undefined | binary(),
+ atom(), [atom()], ssl_record:ssl_version()) ->
+ {atom(), atom()} | undefined | #alert{}.
%%
-%% Description:
+%% Description: Handles signature_algorithms extension
%%--------------------------------------------------------------------
-select_hashsign(_, undefined, _Version) ->
+select_hashsign(_, undefined, _, _, _Version) ->
{null, anon};
%% The signature_algorithms extension was introduced with TLS 1.2. Ignore it if we have
%% negotiated a lower version.
-select_hashsign(#hash_sign_algos{hash_sign_algos = HashSigns}, Cert, {Major, Minor} = Version)
- when Major >= 3 andalso Minor >= 3 ->
- #'OTPCertificate'{tbsCertificate = TBSCert} =public_key:pkix_decode_cert(Cert, otp),
+select_hashsign(HashSigns, Cert, KeyExAlgo,
+ undefined, {Major, Minor} = Version) when Major >= 3 andalso Minor >= 3->
+ select_hashsign(HashSigns, Cert, KeyExAlgo, tls_v1:default_signature_algs(Version), Version);
+select_hashsign(#hash_sign_algos{hash_sign_algos = HashSigns}, Cert, KeyExAlgo, SupportedHashSigns,
+ {Major, Minor}) when Major >= 3 andalso Minor >= 3 ->
+ #'OTPCertificate'{tbsCertificate = TBSCert} = public_key:pkix_decode_cert(Cert, otp),
#'OTPSubjectPublicKeyInfo'{algorithm = {_,Algo, _}} = TBSCert#'OTPTBSCertificate'.subjectPublicKeyInfo,
- DefaultHashSign = {_, Sign} = select_hashsign_algs(undefined, Algo, Version),
- case lists:filter(fun({sha, dsa}) ->
+ Sign = cert_sign(Algo),
+ case lists:filter(fun({sha, dsa = S}) when S == Sign ->
true;
({_, dsa}) ->
false;
- ({Hash, S}) when S == Sign ->
- ssl_cipher:is_acceptable_hash(Hash,
- proplists:get_value(hashs, crypto:supports()));
+ ({_, _} = Algos) ->
+ is_acceptable_hash_sign(Algos, Sign, KeyExAlgo, SupportedHashSigns);
(_) ->
false
end, HashSigns) of
[] ->
- DefaultHashSign;
- [HashSign| _] ->
+ ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY);
+ [HashSign | _] ->
HashSign
end;
-select_hashsign(_, Cert, Version) ->
+select_hashsign(_, Cert, _, _, Version) ->
#'OTPCertificate'{tbsCertificate = TBSCert} = public_key:pkix_decode_cert(Cert, otp),
#'OTPSubjectPublicKeyInfo'{algorithm = {_,Algo, _}} = TBSCert#'OTPTBSCertificate'.subjectPublicKeyInfo,
select_hashsign_algs(undefined, Algo, Version).
%%--------------------------------------------------------------------
--spec select_hashsign_algs(#hash_sign_algos{}| undefined, oid(), ssl_record:ssl_version()) ->
+-spec select_hashsign_algs({atom(), atom()}| undefined, oid(), ssl_record:ssl_version()) ->
{atom(), atom()}.
%% Description: For TLS 1.2 hash function and signature algorithm pairs can be
@@ -642,24 +650,6 @@ select_hashsign_algs(undefined, ?rsaEncryption, _) ->
select_hashsign_algs(undefined, ?'id-dsa', _) ->
{sha, dsa}.
--spec select_hashsign_algs(atom(), ssl_record:ssl_version()) -> {atom(), atom()}.
-%% Wrap function to keep the knowledge of the default values in
-%% one place only
-select_hashsign_algs(Alg, Version) when (Alg == rsa orelse
- Alg == dhe_rsa orelse
- Alg == dh_rsa orelse
- Alg == ecdhe_rsa orelse
- Alg == ecdh_rsa orelse
- Alg == srp_rsa) ->
- select_hashsign_algs(undefined, ?rsaEncryption, Version);
-select_hashsign_algs(Alg, Version) when (Alg == dhe_dss orelse
- Alg == dh_dss orelse
- Alg == srp_dss) ->
- select_hashsign_algs(undefined, ?'id-dsa', Version);
-select_hashsign_algs(Alg, Version) when (Alg == ecdhe_ecdsa orelse
- Alg == ecdh_ecdsa) ->
- select_hashsign_algs(undefined, ?'id-ecPublicKey', Version).
-
%%--------------------------------------------------------------------
-spec master_secret(atom(), ssl_record:ssl_version(), #session{} | binary(), #connection_states{},
client | server) -> {binary(), #connection_states{}} | #alert{}.
@@ -1063,9 +1053,56 @@ available_suites(UserSuites, Version) ->
lists:member(Suite, ssl_cipher:all_suites(Version))
end, UserSuites).
-available_suites(ServerCert, UserSuites, Version, Curve) ->
+available_suites(ServerCert, UserSuites, Version, undefined, Curve) ->
ssl_cipher:filter(ServerCert, available_suites(UserSuites, Version))
- -- unavailable_ecc_suites(Curve).
+ -- unavailable_ecc_suites(Curve);
+available_suites(ServerCert, UserSuites, Version, HashSigns, Curve) ->
+ Suites = available_suites(ServerCert, UserSuites, Version, undefined, Curve),
+ filter_hashsigns(Suites, [ssl_cipher:suite_definition(Suite) || Suite <- Suites], HashSigns, []).
+filter_hashsigns([], [], _, Acc) ->
+ lists:reverse(Acc);
+filter_hashsigns([Suite | Suites], [{KeyExchange,_,_,_} | Algos], HashSigns,
+ Acc) when KeyExchange == dhe_ecdsa;
+ KeyExchange == ecdhe_ecdsa ->
+ do_filter_hashsigns(ecdsa, Suite, Suites, Algos, HashSigns, Acc);
+
+filter_hashsigns([Suite | Suites], [{KeyExchange,_,_,_} | Algos], HashSigns,
+ Acc) when KeyExchange == rsa;
+ KeyExchange == dhe_rsa;
+ KeyExchange == ecdhe_rsa;
+ KeyExchange == srp_rsa;
+ KeyExchange == rsa_psk ->
+ do_filter_hashsigns(rsa, Suite, Suites, Algos, HashSigns, Acc);
+filter_hashsigns([Suite | Suites], [{KeyExchange,_,_,_} | Algos], HashSigns, Acc) when
+ KeyExchange == dhe_dss;
+ KeyExchange == srp_dss ->
+ do_filter_hashsigns(dsa, Suite, Suites, Algos, HashSigns, Acc);
+filter_hashsigns([Suite | Suites], [{KeyExchange,_,_,_} | Algos], HashSigns, Acc) when
+ KeyExchange == dh_dss;
+ KeyExchange == dh_rsa;
+ KeyExchange == dh_ecdsa;
+ KeyExchange == ecdh_rsa;
+ KeyExchange == ecdh_ecdsa ->
+ %% Fixed DH certificates MAY be signed with any hash/signature
+ %% algorithm pair appearing in the hash_sign extension. The names
+ %% DH_DSS, DH_RSA, ECDH_ECDSA, and ECDH_RSA are historical.
+ filter_hashsigns(Suites, Algos, HashSigns, [Suite| Acc]);
+filter_hashsigns([Suite | Suites], [{KeyExchange,_,_,_} | Algos], HashSigns, Acc) when
+ KeyExchange == dh_anon;
+ KeyExchange == ecdh_anon;
+ KeyExchange == srp_anon;
+ KeyExchange == psk;
+ KeyExchange == dhe_psk ->
+ %% In this case hashsigns is not used as the kexchange is anonaymous
+ filter_hashsigns(Suites, Algos, HashSigns, [Suite| Acc]).
+
+do_filter_hashsigns(SignAlgo, Suite, Suites, Algos, HashSigns, Acc) ->
+ case lists:keymember(SignAlgo, 2, HashSigns) of
+ true ->
+ filter_hashsigns(Suites, Algos, HashSigns, [Suite| Acc]);
+ false ->
+ filter_hashsigns(Suites, Algos, HashSigns, Acc)
+ end.
unavailable_ecc_suites(no_curve) ->
ssl_cipher:ec_keyed_suites();
@@ -1077,17 +1114,17 @@ cipher_suites(Suites, false) ->
cipher_suites(Suites, true) ->
Suites.
-select_session(SuggestedSessionId, CipherSuites, Compressions, Port, #session{ecc = ECCCurve} =
+select_session(SuggestedSessionId, CipherSuites, HashSigns, Compressions, Port, #session{ecc = ECCCurve} =
Session, Version,
- #ssl_options{ciphers = UserSuites, honor_cipher_order = HCO} = SslOpts,
+ #ssl_options{ciphers = UserSuites, honor_cipher_order = HonorCipherOrder} = SslOpts,
Cache, CacheCb, Cert) ->
{SessionId, Resumed} = ssl_session:server_id(Port, SuggestedSessionId,
SslOpts, Cert,
Cache, CacheCb),
case Resumed of
undefined ->
- Suites = available_suites(Cert, UserSuites, Version, ECCCurve),
- CipherSuite = select_cipher_suite(CipherSuites, Suites, HCO),
+ Suites = available_suites(Cert, UserSuites, Version, HashSigns, ECCCurve),
+ CipherSuite = select_cipher_suite(CipherSuites, Suites, HonorCipherOrder),
Compression = select_compression(Compressions),
{new, Session#session{session_id = SessionId,
cipher_suite = CipherSuite,
@@ -1155,7 +1192,7 @@ handle_client_hello_extensions(RecordCB, Random, ClientCipherSuites,
#hello_extensions{renegotiation_info = Info,
srp = SRP,
ec_point_formats = ECCFormat,
- alpn = ALPN,
+ alpn = ALPN,
next_protocol_negotiation = NextProtocolNegotiation}, Version,
#ssl_options{secure_renegotiate = SecureRenegotation,
alpn_preferred_protocols = ALPNPreferredProtocols} = Opts,
@@ -1324,7 +1361,7 @@ handle_renegotiation_info(_RecordCB, ConnectionStates, SecureRenegotation) ->
hello_extensions_list(#hello_extensions{renegotiation_info = RenegotiationInfo,
srp = SRP,
- hash_signs = HashSigns,
+ signature_algs = HashSigns,
ec_point_formats = EcPointFormats,
elliptic_curves = EllipticCurves,
alpn = ALPN,
@@ -1799,7 +1836,7 @@ dec_hello_extensions(<<?UINT16(?SIGNATURE_ALGORITHMS_EXT), ?UINT16(Len),
<<?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 =
+ dec_hello_extensions(Rest, Acc#hello_extensions{signature_algs =
#hash_sign_algos{hash_sign_algos = HashSignAlgos}});
dec_hello_extensions(<<?UINT16(?ELLIPTIC_CURVES_EXT), ?UINT16(Len),
@@ -1899,7 +1936,7 @@ from_2bytes(<<?UINT16(N), Rest/binary>>, 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 ->
+ 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;
@@ -2008,27 +2045,16 @@ is_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(_) ->
+available_signature_algs(undefined, _, _) ->
+ undefined;
+available_signature_algs(SupportedHashSigns, {Major, Minor}, AllVersions) when Major >= 3 andalso Minor >= 3 ->
+ case tls_record:lowest_protocol_version(AllVersions) of
+ {3, 3} ->
+ #hash_sign_algos{hash_sign_algos = SupportedHashSigns};
+ _ ->
+ undefined
+ end;
+available_signature_algs(_, _, _) ->
undefined.
psk_secret(PSKIdentity, PSKLookup) ->
@@ -2072,12 +2098,9 @@ crl_check(OtpCert, Check, CertDbHandle, CertDbRef, {Callback, CRLDbHandle}, _) -
],
case dps_and_crls(OtpCert, Callback, CRLDbHandle, ext) of
no_dps ->
- case dps_and_crls(OtpCert, Callback, CRLDbHandle, same_issuer) of
- [] ->
- valid; %% No relevant CRL existed
- DpsAndCRls ->
- crl_check_same_issuer(OtpCert, Check, DpsAndCRls, Options)
- end;
+ crl_check_same_issuer(OtpCert, Check,
+ dps_and_crls(OtpCert, Callback, CRLDbHandle, same_issuer),
+ Options);
DpsAndCRLs -> %% This DP list may be empty if relevant CRLs existed
%% but could not be retrived, will result in {bad_cert, revocation_status_undetermined}
case public_key:pkix_crls_validate(OtpCert, DpsAndCRLs, Options) of
@@ -2126,3 +2149,25 @@ distpoints_lookup([DistPoint | Rest], Callback, CRLDbHandle) ->
CRLs ->
[{DistPoint, {CRL, public_key:der_decode('CertificateList', CRL)}} || CRL <- CRLs]
end.
+
+cert_sign(?rsaEncryption) ->
+ rsa;
+cert_sign(?'id-ecPublicKey') ->
+ ecdsa;
+cert_sign(?'id-dsa') ->
+ dsa;
+cert_sign(Alg) ->
+ {_, Sign} =public_key:pkix_sign_types(Alg),
+ Sign.
+
+is_acceptable_hash_sign({_, Sign} = Algos, Sign, _, SupportedHashSigns) ->
+ is_acceptable_hash_sign(Algos, SupportedHashSigns);
+is_acceptable_hash_sign(Algos,_, KeyExAlgo, SupportedHashSigns) when KeyExAlgo == dh_ecdsa;
+ KeyExAlgo == ecdh_rsa;
+ KeyExAlgo == ecdh_ecdsa ->
+ is_acceptable_hash_sign(Algos, SupportedHashSigns);
+is_acceptable_hash_sign(_,_,_,_) ->
+ false.
+is_acceptable_hash_sign(Algos, SupportedHashSigns) ->
+ lists:member(Algos, SupportedHashSigns).
+
diff --git a/lib/ssl/src/ssl_handshake.hrl b/lib/ssl/src/ssl_handshake.hrl
index 58b4d5a23d..b74a65939b 100644
--- a/lib/ssl/src/ssl_handshake.hrl
+++ b/lib/ssl/src/ssl_handshake.hrl
@@ -95,7 +95,7 @@
-record(hello_extensions, {
renegotiation_info,
- hash_signs, % supported combinations of hashes/signature algos
+ signature_algs, % supported combinations of hashes/signature algos
alpn,
next_protocol_negotiation = undefined, % [binary()]
srp,
diff --git a/lib/ssl/src/ssl_internal.hrl b/lib/ssl/src/ssl_internal.hrl
index 8c7ed9c0d1..9c52f5a315 100644
--- a/lib/ssl/src/ssl_internal.hrl
+++ b/lib/ssl/src/ssl_internal.hrl
@@ -93,16 +93,16 @@
validate_extensions_fun,
depth :: integer(),
certfile :: binary(),
- cert :: public_key:der_encoded() | secret_printout(),
+ cert :: public_key:der_encoded() | secret_printout() | 'undefined',
keyfile :: binary(),
- key :: {'RSAPrivateKey' | 'DSAPrivateKey' | 'ECPrivateKey' | 'PrivateKeyInfo', public_key:der_encoded()} | secret_printout(),
- password :: string() | secret_printout(),
- cacerts :: [public_key:der_encoded()] | secret_printout(),
+ key :: {'RSAPrivateKey' | 'DSAPrivateKey' | 'ECPrivateKey' | 'PrivateKeyInfo', public_key:der_encoded()} | secret_printout() | 'undefined',
+ password :: string() | secret_printout() | 'undefined',
+ cacerts :: [public_key:der_encoded()] | secret_printout() | 'undefined',
cacertfile :: binary(),
dh :: public_key:der_encoded() | secret_printout(),
- dhfile :: binary() | secret_printout(),
+ dhfile :: binary() | secret_printout() | 'undefined',
user_lookup_fun, % server option, fun to lookup the user
- psk_identity :: binary() | secret_printout() ,
+ psk_identity :: binary() | secret_printout() | 'undefined',
srp_identity, % client option {User, Password}
ciphers, %
%% Local policy for the server if it want's to reuse the session
@@ -118,7 +118,7 @@
%% undefined if not hibernating, or number of ms of
%% inactivity after which ssl_connection will go into
%% hibernation
- hibernate_after :: boolean(),
+ hibernate_after :: boolean() | 'undefined',
%% This option should only be set to true by inet_tls_dist
erl_dist = false :: boolean(),
alpn_advertised_protocols = undefined :: [binary()] | undefined ,
@@ -135,7 +135,8 @@
padding_check = true :: boolean(),
fallback = false :: boolean(),
crl_check :: boolean() | peer | best_effort,
- crl_cache
+ crl_cache,
+ signature_algs
}).
-record(socket_options,
diff --git a/lib/ssl/src/ssl_tls_dist_proxy.erl b/lib/ssl/src/ssl_tls_dist_proxy.erl
index 211badef56..8a39bde255 100644
--- a/lib/ssl/src/ssl_tls_dist_proxy.erl
+++ b/lib/ssl/src/ssl_tls_dist_proxy.erl
@@ -20,7 +20,7 @@
-module(ssl_tls_dist_proxy).
--export([listen/1, accept/1, connect/2, get_tcp_address/1]).
+-export([listen/2, accept/2, connect/3, get_tcp_address/1]).
-export([init/1, start_link/0, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3, ssl_options/2]).
@@ -39,14 +39,14 @@
%% Internal application API
%%====================================================================
-listen(Name) ->
- gen_server:call(?MODULE, {listen, Name}, infinity).
+listen(Driver, Name) ->
+ gen_server:call(?MODULE, {listen, Driver, Name}, infinity).
-accept(Listen) ->
- gen_server:call(?MODULE, {accept, Listen}, infinity).
+accept(Driver, Listen) ->
+ gen_server:call(?MODULE, {accept, Driver, Listen}, infinity).
-connect(Ip, Port) ->
- gen_server:call(?MODULE, {connect, Ip, Port}, infinity).
+connect(Driver, Ip, Port) ->
+ gen_server:call(?MODULE, {connect, Driver, Ip, Port}, infinity).
do_listen(Options) ->
@@ -108,10 +108,11 @@ init([]) ->
process_flag(priority, max),
{ok, #state{}}.
-handle_call({listen, Name}, _From, State) ->
+handle_call({listen, Driver, Name}, _From, State) ->
case gen_tcp:listen(0, [{active, false}, {packet,?PPRE}, {ip, loopback}]) of
{ok, Socket} ->
- {ok, World} = do_listen([{active, false}, binary, {packet,?PPRE}, {reuseaddr, true}]),
+ {ok, World} = do_listen([{active, false}, binary, {packet,?PPRE}, {reuseaddr, true},
+ Driver:family()]),
{ok, TcpAddress} = get_tcp_address(Socket),
{ok, WorldTcpAddress} = get_tcp_address(World),
{_,Port} = WorldTcpAddress#net_address.address,
@@ -126,15 +127,15 @@ handle_call({listen, Name}, _From, State) ->
{reply, Error, State}
end;
-handle_call({accept, Listen}, {From, _}, State = #state{listen={_, World}}) ->
+handle_call({accept, _Driver, Listen}, {From, _}, State = #state{listen={_, World}}) ->
Self = self(),
ErtsPid = spawn_link(fun() -> accept_loop(Self, erts, Listen, From) end),
WorldPid = spawn_link(fun() -> accept_loop(Self, world, World, Listen) end),
{reply, ErtsPid, State#state{accept_loop={ErtsPid, WorldPid}}};
-handle_call({connect, Ip, Port}, {From, _}, State) ->
+handle_call({connect, Driver, Ip, Port}, {From, _}, State) ->
Me = self(),
- Pid = spawn_link(fun() -> setup_proxy(Ip, Port, Me) end),
+ Pid = spawn_link(fun() -> setup_proxy(Driver, Ip, Port, Me) end),
receive
{Pid, go_ahead, LPort} ->
Res = {ok, Socket} = try_connect(LPort),
@@ -194,6 +195,11 @@ accept_loop(Proxy, erts = Type, Listen, Extra) ->
{_Kernel, unsupported_protocol} ->
exit(unsupported_protocol)
end;
+ {error, closed} ->
+ %% The listening socket is closed: the proxy process is
+ %% shutting down. Exit normally, to avoid generating a
+ %% spurious error report.
+ exit(normal);
Error ->
exit(Error)
end,
@@ -263,10 +269,11 @@ try_connect(Port) ->
try_connect(Port)
end.
-setup_proxy(Ip, Port, Parent) ->
+setup_proxy(Driver, Ip, Port, Parent) ->
process_flag(trap_exit, true),
Opts = connect_options(get_ssl_options(client)),
- case ssl:connect(Ip, Port, [{active, true}, binary, {packet,?PPRE}, nodelay()] ++ Opts) of
+ case ssl:connect(Ip, Port, [{active, true}, binary, {packet,?PPRE}, nodelay(),
+ Driver:family()] ++ Opts) of
{ok, World} ->
{ok, ErtsL} = gen_tcp:listen(0, [{active, true}, {ip, loopback}, binary, {packet,?PPRE}]),
{ok, #net_address{address={_,LPort}}} = get_tcp_address(ErtsL),
diff --git a/lib/ssl/src/ssl_v3.erl b/lib/ssl/src/ssl_v3.erl
index f169059a75..f98ea83771 100644
--- a/lib/ssl/src/ssl_v3.erl
+++ b/lib/ssl/src/ssl_v3.erl
@@ -143,9 +143,7 @@ suites() ->
?TLS_RSA_WITH_3DES_EDE_CBC_SHA,
?TLS_DHE_RSA_WITH_AES_128_CBC_SHA,
?TLS_DHE_DSS_WITH_AES_128_CBC_SHA,
- ?TLS_RSA_WITH_AES_128_CBC_SHA,
- ?TLS_DHE_RSA_WITH_DES_CBC_SHA,
- ?TLS_RSA_WITH_DES_CBC_SHA
+ ?TLS_RSA_WITH_AES_128_CBC_SHA
].
%%--------------------------------------------------------------------
diff --git a/lib/ssl/src/tls_connection.erl b/lib/ssl/src/tls_connection.erl
index c3f0206d25..93716d31b8 100644
--- a/lib/ssl/src/tls_connection.erl
+++ b/lib/ssl/src/tls_connection.erl
@@ -182,8 +182,7 @@ hello(start, #state{host = Host, port = Port, role = client,
next_state(hello, hello, Record, State);
hello(Hello = #client_hello{client_version = ClientVersion,
- extensions = #hello_extensions{hash_signs = HashSigns,
- ec_point_formats = EcPointFormats,
+ extensions = #hello_extensions{ec_point_formats = EcPointFormats,
elliptic_curves = EllipticCurves}},
State = #state{connection_states = ConnectionStates0,
port = Port, session = #session{own_certificate = Cert} = Session0,
@@ -191,27 +190,28 @@ hello(Hello = #client_hello{client_version = ClientVersion,
session_cache = Cache,
session_cache_cb = CacheCb,
negotiated_protocol = CurrentProtocol,
+ key_algorithm = KeyExAlg,
ssl_options = SslOpts}) ->
+
case tls_handshake:hello(Hello, SslOpts, {Port, Session0, Cache, CacheCb,
- ConnectionStates0, Cert}, Renegotiation) of
+ ConnectionStates0, Cert, KeyExAlg}, Renegotiation) of
#alert{} = Alert ->
handle_own_alert(Alert, ClientVersion, hello, State);
{Version, {Type, Session},
- ConnectionStates, Protocol0, ServerHelloExt} ->
-
+ ConnectionStates, Protocol0, ServerHelloExt, HashSign} ->
Protocol = case Protocol0 of
- undefined -> CurrentProtocol;
- _ -> Protocol0
- end,
-
- HashSign = ssl_handshake:select_hashsign(HashSigns, Cert, Version),
- ssl_connection:hello({common_client_hello, Type, ServerHelloExt, HashSign},
+ undefined -> CurrentProtocol;
+ _ -> Protocol0
+ end,
+ ssl_connection:hello({common_client_hello, Type, ServerHelloExt},
State#state{connection_states = ConnectionStates,
negotiated_version = Version,
+ hashsign_algorithm = HashSign,
session = Session,
client_ecc = {EllipticCurves, EcPointFormats},
negotiated_protocol = Protocol}, ?MODULE)
end;
+
hello(Hello = #server_hello{},
#state{connection_states = ConnectionStates0,
negotiated_version = ReqVersion,
@@ -1069,3 +1069,4 @@ handle_sni_extension(#client_hello{extensions = HelloExtensions}, State0) ->
end;
handle_sni_extension(_, State0) ->
State0.
+
diff --git a/lib/ssl/src/tls_handshake.erl b/lib/ssl/src/tls_handshake.erl
index 0a6cb9f92d..ef718c13df 100644
--- a/lib/ssl/src/tls_handshake.erl
+++ b/lib/ssl/src/tls_handshake.erl
@@ -56,7 +56,7 @@ client_hello(Host, Port, ConnectionStates,
Version = tls_record:highest_protocol_version(Versions),
Pending = ssl_record:pending_connection_state(ConnectionStates, read),
SecParams = Pending#connection_state.security_parameters,
- AvailableCipherSuites = ssl_handshake:available_suites(UserSuites, Version),
+ AvailableCipherSuites = ssl_handshake:available_suites(UserSuites, Version),
Extensions = ssl_handshake:client_hello_extensions(Host, Version,
AvailableCipherSuites,
SslOpts, ConnectionStates, Renegotiation),
@@ -80,13 +80,13 @@ client_hello(Host, Port, ConnectionStates,
-spec hello(#server_hello{} | #client_hello{}, #ssl_options{},
#connection_states{} | {inet:port_number(), #session{}, db_handle(),
atom(), #connection_states{},
- binary() | undefined},
+ binary() | undefined, ssl_cipher:key_algo()},
boolean()) ->
{tls_record:tls_version(), session_id(),
#connection_states{}, alpn | npn, binary() | undefined}|
{tls_record:tls_version(), {resumed | new, #session{}},
#connection_states{}, binary() | undefined,
- #hello_extensions{}} |
+ #hello_extensions{}, {ssl_cipher:hash(), ssl_cipher:sign_algo()} | undefined} |
#alert{}.
%%
%% Description: Handles a recieved hello message
@@ -149,26 +149,35 @@ get_tls_handshake(Version, Data, Buffer) ->
%%% Internal functions
%%--------------------------------------------------------------------
handle_client_hello(Version, #client_hello{session_id = SugesstedId,
- cipher_suites = CipherSuites,
- compression_methods = Compressions,
- random = Random,
- extensions = #hello_extensions{elliptic_curves = Curves} = HelloExt},
- #ssl_options{versions = Versions} = SslOpts,
- {Port, Session0, Cache, CacheCb, ConnectionStates0, Cert}, Renegotiation) ->
+ cipher_suites = CipherSuites,
+ compression_methods = Compressions,
+ random = Random,
+ extensions = #hello_extensions{elliptic_curves = Curves,
+ signature_algs = ClientHashSigns} = HelloExt},
+ #ssl_options{versions = Versions,
+ signature_algs = SupportedHashSigns} = SslOpts,
+ {Port, Session0, Cache, CacheCb, ConnectionStates0, Cert, _}, Renegotiation) ->
case tls_record:is_acceptable_version(Version, Versions) of
true ->
+ AvailableHashSigns = available_signature_algs(ClientHashSigns, SupportedHashSigns, Cert, Version),
ECCCurve = ssl_handshake:select_curve(Curves, ssl_handshake:supported_ecc(Version)),
{Type, #session{cipher_suite = CipherSuite} = Session1}
- = ssl_handshake:select_session(SugesstedId, CipherSuites, Compressions,
+ = ssl_handshake:select_session(SugesstedId, CipherSuites, AvailableHashSigns, Compressions,
Port, Session0#session{ecc = ECCCurve}, Version,
SslOpts, Cache, CacheCb, Cert),
case CipherSuite of
no_suite ->
?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY);
_ ->
- handle_client_hello_extensions(Version, Type, Random, CipherSuites, HelloExt,
- SslOpts, Session1, ConnectionStates0,
- Renegotiation)
+ {KeyExAlg,_,_,_} = ssl_cipher:suite_definition(CipherSuite),
+ case ssl_handshake:select_hashsign(ClientHashSigns, Cert, KeyExAlg, SupportedHashSigns, Version) of
+ #alert{} = Alert ->
+ Alert;
+ HashSign ->
+ handle_client_hello_extensions(Version, Type, Random, CipherSuites, HelloExt,
+ SslOpts, Session1, ConnectionStates0,
+ Renegotiation, HashSign)
+ end
end;
false ->
?ALERT_REC(?FATAL, ?PROTOCOL_VERSION)
@@ -245,14 +254,14 @@ enc_handshake(HandshakeMsg, Version) ->
handle_client_hello_extensions(Version, Type, Random, CipherSuites,
- HelloExt, SslOpts, Session0, ConnectionStates0, Renegotiation) ->
+ HelloExt, SslOpts, Session0, ConnectionStates0, Renegotiation, HashSign) ->
try ssl_handshake:handle_client_hello_extensions(tls_record, Random, CipherSuites,
HelloExt, Version, SslOpts,
Session0, ConnectionStates0, Renegotiation) of
#alert{} = Alert ->
Alert;
{Session, ConnectionStates, Protocol, ServerHelloExt} ->
- {Version, {Type, Session}, ConnectionStates, Protocol, ServerHelloExt}
+ {Version, {Type, Session}, ConnectionStates, Protocol, ServerHelloExt, HashSign}
catch throw:Alert ->
Alert
end.
@@ -269,3 +278,12 @@ handle_server_hello_extensions(Version, SessionId, Random, CipherSuite,
{Version, SessionId, ConnectionStates, ProtoExt, Protocol}
end.
+available_signature_algs(undefined, SupportedHashSigns, _, {Major, Minor}) when (Major < 3) andalso (Minor < 3) ->
+ SupportedHashSigns;
+available_signature_algs(#hash_sign_algos{hash_sign_algos = ClientHashSigns}, SupportedHashSigns,
+ _, {Major, Minor}) when (Major < 3) andalso (Minor < 3) ->
+ ordsets:intersection(ClientHashSigns, SupportedHashSigns);
+available_signature_algs(_, _, _, _) ->
+ undefined.
+
+
diff --git a/lib/ssl/src/tls_v1.erl b/lib/ssl/src/tls_v1.erl
index 71e5f349dd..03cef633d5 100644
--- a/lib/ssl/src/tls_v1.erl
+++ b/lib/ssl/src/tls_v1.erl
@@ -31,7 +31,8 @@
-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, oid_to_enum/1, enum_to_oid/1,
+ default_signature_algs/1, signature_algs/2]).
%%====================================================================
%% Internal application API
@@ -208,9 +209,7 @@ suites(Minor) when Minor == 1; Minor == 2 ->
?TLS_DHE_DSS_WITH_AES_128_CBC_SHA,
?TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA,
?TLS_ECDH_RSA_WITH_AES_128_CBC_SHA,
- ?TLS_RSA_WITH_AES_128_CBC_SHA,
- ?TLS_DHE_RSA_WITH_DES_CBC_SHA,
- ?TLS_RSA_WITH_DES_CBC_SHA
+ ?TLS_RSA_WITH_AES_128_CBC_SHA
];
suites(3) ->
[
@@ -258,6 +257,52 @@ suites(3) ->
] ++ suites(2).
+
+signature_algs({3, 3}, HashSigns) ->
+ CryptoSupports = crypto:supports(),
+ Hashes = proplists:get_value(hashs, CryptoSupports),
+ PubKeys = proplists:get_value(public_keys, CryptoSupports),
+ Supported = lists:foldl(fun({Hash, dsa = Sign} = Alg, Acc) ->
+ case proplists:get_bool(dss, PubKeys)
+ andalso proplists:get_bool(Hash, Hashes)
+ andalso is_pair(Hash, Sign, Hashes)
+ of
+ true ->
+ [Alg | Acc];
+ false ->
+ Acc
+ end;
+ ({Hash, Sign} = Alg, Acc) ->
+ case proplists:get_bool(Sign, PubKeys)
+ andalso proplists:get_bool(Hash, Hashes)
+ andalso is_pair(Hash, Sign, Hashes)
+ of
+ true ->
+ [Alg | Acc];
+ false ->
+ Acc
+ end
+ end, [], HashSigns),
+ lists:reverse(Supported).
+
+default_signature_algs({3, 3} = Version) ->
+ Default = [%% SHA2
+ {sha512, ecdsa},
+ {sha512, rsa},
+ {sha384, ecdsa},
+ {sha384, rsa},
+ {sha256, ecdsa},
+ {sha256, rsa},
+ {sha224, ecdsa},
+ {sha224, rsa},
+ %% SHA
+ {sha, ecdsa},
+ {sha, rsa},
+ {sha, dsa}],
+ signature_algs(Version, Default);
+default_signature_algs(_) ->
+ undefined.
+
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
@@ -342,6 +387,17 @@ finished_label(client) ->
finished_label(server) ->
<<"server finished">>.
+is_pair(sha, dsa, _) ->
+ true;
+is_pair(_, dsa, _) ->
+ false;
+is_pair(Hash, ecdsa, Hashs) ->
+ AtLeastSha = Hashs -- [md2,md4,md5],
+ lists:member(Hash, AtLeastSha);
+is_pair(Hash, rsa, Hashs) ->
+ AtLeastMd5 = Hashs -- [md2,md4],
+ lists:member(Hash, AtLeastMd5).
+
%% list ECC curves in prefered order
ecc_curves(_Minor) ->
TLSCurves = [sect571r1,sect571k1,secp521r1,brainpoolP512r1,