aboutsummaryrefslogtreecommitdiffstats
path: root/lib/ssl/test/ssl_test_lib.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/ssl/test/ssl_test_lib.erl')
-rw-r--r--lib/ssl/test/ssl_test_lib.erl615
1 files changed, 550 insertions, 65 deletions
diff --git a/lib/ssl/test/ssl_test_lib.erl b/lib/ssl/test/ssl_test_lib.erl
index f9cc976815..7767d76a0d 100644
--- a/lib/ssl/test/ssl_test_lib.erl
+++ b/lib/ssl/test/ssl_test_lib.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2017. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2018. 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.
@@ -26,6 +26,7 @@
%% Note: This directive should only be used in test suites.
-compile(export_all).
+-compile(nowarn_export_all).
-record(sslsocket, { fd = nil, pid = nil}).
-define(SLEEP, 1000).
@@ -79,17 +80,21 @@ run_server(ListenSocket, Opts, N) ->
Pid ! {accepter, N, Server},
run_server(ListenSocket, Opts, N-1).
-do_run_server(_, {error, timeout} = Result, Opts) ->
+do_run_server(_, {error, _} = Result, Opts) ->
+ ct:log("Server error result ~p~n", [Result]),
+ Pid = proplists:get_value(from, Opts),
+ Pid ! {self(), Result};
+do_run_server(_, ok = Result, Opts) ->
+ ct:log("Server cancel result ~p~n", [Result]),
Pid = proplists:get_value(from, Opts),
Pid ! {self(), Result};
-
do_run_server(ListenSocket, AcceptSocket, Opts) ->
Node = proplists:get_value(node, Opts),
Pid = proplists:get_value(from, Opts),
Transport = proplists:get_value(transport, Opts, ssl),
{Module, Function, Args} = proplists:get_value(mfa, Opts),
ct:log("~p:~p~nServer: apply(~p,~p,~p)~n",
- [?MODULE,?LINE, Module, Function, [AcceptSocket | Args]]),
+ [?MODULE,?LINE, Module, Function, [AcceptSocket | Args]]),
case rpc:call(Node, Module, Function, [AcceptSocket | Args]) of
no_result_msg ->
ok;
@@ -117,7 +122,8 @@ connect(#sslsocket{} = ListenSocket, Opts) ->
ReconnectTimes = proplists:get_value(reconnect_times, Opts, 0),
Timeout = proplists:get_value(timeout, Opts, infinity),
SslOpts = proplists:get_value(ssl_extra_opts, Opts, []),
- AcceptSocket = connect(ListenSocket, Node, 1 + ReconnectTimes, dummy, Timeout, SslOpts),
+ ContOpts = proplists:get_value(continue_options, Opts, []),
+ AcceptSocket = connect(ListenSocket, Node, 1 + ReconnectTimes, dummy, Timeout, SslOpts, ContOpts),
case ReconnectTimes of
0 ->
AcceptSocket;
@@ -132,10 +138,45 @@ connect(ListenSocket, Opts) ->
[ListenSocket]),
AcceptSocket.
-connect(_, _, 0, AcceptSocket, _, _) ->
+connect(_, _, 0, AcceptSocket, _, _, _) ->
AcceptSocket;
-
-connect(ListenSocket, Node, N, _, Timeout, []) ->
+connect(ListenSocket, Node, _N, _, Timeout, SslOpts, cancel) ->
+ ct:log("ssl:transport_accept(~p)~n", [ListenSocket]),
+ {ok, AcceptSocket} = rpc:call(Node, ssl, transport_accept,
+ [ListenSocket]),
+ ct:log("~p:~p~nssl:handshake(~p,~p,~p)~n", [?MODULE,?LINE, AcceptSocket, SslOpts,Timeout]),
+
+ case rpc:call(Node, ssl, handshake, [AcceptSocket, SslOpts, Timeout]) of
+ {ok, Socket0, Ext} ->
+ ct:log("Ext ~p:~n", [Ext]),
+ ct:log("~p:~p~nssl:handshake_cancel(~p)~n", [?MODULE,?LINE, Socket0]),
+ rpc:call(Node, ssl, handshake_cancel, [Socket0]);
+ Result ->
+ ct:log("~p:~p~nssl:handshake@~p ret ~p",[?MODULE,?LINE, Node,Result]),
+ Result
+ end;
+connect(ListenSocket, Node, N, _, Timeout, SslOpts, [_|_] =ContOpts) ->
+ ct:log("ssl:transport_accept(~p)~n", [ListenSocket]),
+ {ok, AcceptSocket} = rpc:call(Node, ssl, transport_accept,
+ [ListenSocket]),
+ ct:log("~p:~p~nssl:handshake(~p,~p,~p)~n", [?MODULE,?LINE, AcceptSocket, SslOpts,Timeout]),
+
+ case rpc:call(Node, ssl, handshake, [AcceptSocket, SslOpts, Timeout]) of
+ {ok, Socket0, Ext} ->
+ ct:log("Ext ~p:~n", [Ext]),
+ ct:log("~p:~p~nssl:handshake_continue(~p,~p,~p)~n", [?MODULE,?LINE, Socket0, ContOpts,Timeout]),
+ case rpc:call(Node, ssl, handshake_continue, [Socket0, ContOpts, Timeout]) of
+ {ok, Socket} ->
+ connect(ListenSocket, Node, N-1, Socket, Timeout, SslOpts, ContOpts);
+ Error ->
+ ct:log("~p:~p~nssl:handshake_continue@~p ret ~p",[?MODULE,?LINE, Node,Error]),
+ Error
+ end;
+ Result ->
+ ct:log("~p:~p~nssl:handshake@~p ret ~p",[?MODULE,?LINE, Node,Result]),
+ Result
+ end;
+connect(ListenSocket, Node, N, _, Timeout, [], ContOpts) ->
ct:log("ssl:transport_accept(~p)~n", [ListenSocket]),
{ok, AcceptSocket} = rpc:call(Node, ssl, transport_accept,
[ListenSocket]),
@@ -143,12 +184,12 @@ connect(ListenSocket, Node, N, _, Timeout, []) ->
case rpc:call(Node, ssl, ssl_accept, [AcceptSocket, Timeout]) of
ok ->
- connect(ListenSocket, Node, N-1, AcceptSocket, Timeout, []);
+ connect(ListenSocket, Node, N-1, AcceptSocket, Timeout, [], ContOpts);
Result ->
ct:log("~p:~p~nssl:ssl_accept@~p ret ~p",[?MODULE,?LINE, Node,Result]),
Result
end;
-connect(ListenSocket, Node, _, _, Timeout, Opts) ->
+connect(ListenSocket, Node, _, _, Timeout, Opts, _) ->
ct:log("ssl:transport_accept(~p)~n", [ListenSocket]),
{ok, AcceptSocket} = rpc:call(Node, ssl, transport_accept,
[ListenSocket]),
@@ -156,6 +197,55 @@ connect(ListenSocket, Node, _, _, Timeout, Opts) ->
rpc:call(Node, ssl, ssl_accept, [AcceptSocket, Opts, Timeout]),
AcceptSocket.
+
+start_server_transport_abuse_socket(Args) ->
+ Result = spawn_link(?MODULE, transport_accept_abuse, [Args]),
+ receive
+ {listen, up} ->
+ Result
+ end.
+
+start_server_transport_control(Args) ->
+ Result = spawn_link(?MODULE, transport_switch_control, [Args]),
+ receive
+ {listen, up} ->
+ Result
+ end.
+
+
+transport_accept_abuse(Opts) ->
+ Node = proplists:get_value(node, Opts),
+ Port = proplists:get_value(port, Opts),
+ Options = proplists:get_value(options, Opts),
+ Pid = proplists:get_value(from, Opts),
+ Transport = proplists:get_value(transport, Opts, ssl),
+ ct:log("~p:~p~nssl:listen(~p, ~p)~n", [?MODULE,?LINE, Port, Options]),
+ {ok, ListenSocket} = rpc:call(Node, Transport, listen, [Port, Options]),
+ Pid ! {listen, up},
+ send_selected_port(Pid, Port, ListenSocket),
+ {ok, AcceptSocket} = rpc:call(Node, ssl, transport_accept,
+ [ListenSocket]),
+ {error, _} = rpc:call(Node, ssl, connection_information, [AcceptSocket]),
+ _ = rpc:call(Node, ssl, handshake, [AcceptSocket, infinity]),
+ Pid ! {self(), ok}.
+
+
+transport_switch_control(Opts) ->
+ Node = proplists:get_value(node, Opts),
+ Port = proplists:get_value(port, Opts),
+ Options = proplists:get_value(options, Opts),
+ Pid = proplists:get_value(from, Opts),
+ Transport = proplists:get_value(transport, Opts, ssl),
+ ct:log("~p:~p~nssl:listen(~p, ~p)~n", [?MODULE,?LINE, Port, Options]),
+ {ok, ListenSocket} = rpc:call(Node, Transport, listen, [Port, Options]),
+ Pid ! {listen, up},
+ send_selected_port(Pid, Port, ListenSocket),
+ {ok, AcceptSocket} = rpc:call(Node, ssl, transport_accept,
+ [ListenSocket]),
+ ok = rpc:call(Node, ssl, controlling_process, [AcceptSocket, self()]),
+ Pid ! {self(), ok}.
+
+
remove_close_msg(0) ->
ok;
remove_close_msg(ReconnectTimes) ->
@@ -187,8 +277,17 @@ run_client(Opts) ->
Pid = proplists:get_value(from, Opts),
Transport = proplists:get_value(transport, Opts, ssl),
Options = proplists:get_value(options, Opts),
+ ContOpts = proplists:get_value(continue_options, Opts, []),
ct:log("~p:~p~n~p:connect(~p, ~p)@~p~n", [?MODULE,?LINE, Transport, Host, Port, Node]),
ct:log("SSLOpts: ~p", [Options]),
+ case ContOpts of
+ [] ->
+ client_loop(Node, Host, Port, Pid, Transport, Options, Opts);
+ _ ->
+ client_cont_loop(Node, Host, Port, Pid, Transport, Options, ContOpts, Opts)
+ end.
+
+client_loop(Node, Host, Port, Pid, Transport, Options, Opts) ->
case rpc:call(Node, Transport, connect, [Host, Port, Options]) of
{ok, Socket} ->
Pid ! {connected, Socket},
@@ -245,6 +344,40 @@ run_client(Opts) ->
Pid ! {connect_failed, {badrpc,BadRPC}}
end.
+client_cont_loop(Node, Host, Port, Pid, Transport, Options, cancel, _Opts) ->
+ case rpc:call(Node, Transport, connect, [Host, Port, Options]) of
+ {ok, Socket, _} ->
+ Result = rpc:call(Node, Transport, handshake_cancel, [Socket]),
+ ct:log("~p:~p~nClient: Cancel: ~p ~n", [?MODULE,?LINE, Result]),
+ Pid ! {connect_failed, Result};
+ {error, Reason} ->
+ ct:log("~p:~p~nClient: connection failed: ~p ~n", [?MODULE,?LINE, Reason]),
+ Pid ! {connect_failed, Reason}
+ end;
+
+client_cont_loop(Node, Host, Port, Pid, Transport, Options, ContOpts, Opts) ->
+ case rpc:call(Node, Transport, connect, [Host, Port, Options]) of
+ {ok, Socket0, _} ->
+ ct:log("~p:~p~nClient: handshake_continue(~p, ~p, infinity) ~n", [?MODULE, ?LINE, Socket0, ContOpts]),
+ case rpc:call(Node, Transport, handshake_continue, [Socket0, ContOpts]) of
+ {ok, Socket} ->
+ Pid ! {connected, Socket},
+ {Module, Function, Args} = proplists:get_value(mfa, Opts),
+ ct:log("~p:~p~nClient: apply(~p,~p,~p)~n",
+ [?MODULE,?LINE, Module, Function, [Socket | Args]]),
+ case rpc:call(Node, Module, Function, [Socket | Args]) of
+ no_result_msg ->
+ ok;
+ Msg ->
+ ct:log("~p:~p~nClient Msg: ~p ~n", [?MODULE,?LINE, Msg]),
+ Pid ! {self(), Msg}
+ end
+ end;
+ {error, Reason} ->
+ ct:log("~p:~p~nClient: connection failed: ~p ~n", [?MODULE,?LINE, Reason]),
+ Pid ! {connect_failed, Reason}
+ end.
+
close(Pid) ->
ct:log("~p:~p~nClose ~p ~n", [?MODULE,?LINE, Pid]),
Monitor = erlang:monitor(process, Pid),
@@ -502,6 +635,17 @@ default_cert_chain_conf() ->
%% Use only default options
[[],[],[]].
+gen_conf(mix, mix, UserClient, UserServer) ->
+ ClientTag = conf_tag("client"),
+ ServerTag = conf_tag("server"),
+
+ DefaultClient = default_cert_chain_conf(),
+ DefaultServer = default_cert_chain_conf(),
+
+ ClientConf = merge_chain_spec(UserClient, DefaultClient, []),
+ ServerConf = merge_chain_spec(UserServer, DefaultServer, []),
+
+ new_format([{ClientTag, ClientConf}, {ServerTag, ServerConf}]);
gen_conf(ClientChainType, ServerChainType, UserClient, UserServer) ->
ClientTag = conf_tag("client"),
ServerTag = conf_tag("server"),
@@ -595,6 +739,46 @@ merge_spec(User, Default, [Conf | Rest], Acc) ->
merge_spec(User, Default, Rest, [{Conf, Value} | Acc])
end.
+make_mix_cert(Config) ->
+ Ext = x509_test:extensions([{key_usage, [digitalSignature]}]),
+ Digest = {digest, appropriate_sha(crypto:supports())},
+ CurveOid = hd(tls_v1:ecc_curves(0)),
+ Mix = proplists:get_value(mix, Config, peer_ecc),
+ ClientChainType =ServerChainType = mix,
+ {ClientChain, ServerChain} = mix(Mix, Digest, CurveOid, Ext),
+ CertChainConf = gen_conf(ClientChainType, ServerChainType, ClientChain, ServerChain),
+ ClientFileBase = filename:join([proplists:get_value(priv_dir, Config), "mix" ++ atom_to_list(Mix)]),
+ ServerFileBase = filename:join([proplists:get_value(priv_dir, Config), "mix" ++ atom_to_list(Mix)]),
+ GenCertData = public_key:pkix_test_data(CertChainConf),
+ [{server_config, ServerConf},
+ {client_config, ClientConf}] =
+ x509_test:gen_pem_config_files(GenCertData, ClientFileBase, ServerFileBase),
+ {[{verify, verify_peer} | ClientConf],
+ [{reuseaddr, true}, {verify, verify_peer} | ServerConf]
+ }.
+
+mix(peer_ecc, Digest, CurveOid, Ext) ->
+ ClientChain = [[Digest, {key, {namedCurve, CurveOid}}],
+ [Digest, {key, hardcode_rsa_key(1)}],
+ [Digest, {key, {namedCurve, CurveOid}}, {extensions, Ext}]
+ ],
+ ServerChain = [[Digest, {key, {namedCurve, CurveOid}}],
+ [Digest, {key, hardcode_rsa_key(2)}],
+ [Digest, {key, {namedCurve, CurveOid}},{extensions, Ext}]
+ ],
+ {ClientChain, ServerChain};
+
+mix(peer_rsa, Digest, CurveOid, Ext) ->
+ ClientChain = [[Digest, {key, {namedCurve, CurveOid}}],
+ [Digest, {key, {namedCurve, CurveOid}}],
+ [Digest, {key, hardcode_rsa_key(1)}, {extensions, Ext}]
+ ],
+ ServerChain = [[Digest, {key, {namedCurve, CurveOid}}],
+ [Digest, {key, {namedCurve, CurveOid}}],
+ [Digest, {key, hardcode_rsa_key(2)},{extensions, Ext}]
+ ],
+ {ClientChain, ServerChain}.
+
make_ecdsa_cert(Config) ->
CryptoSupport = crypto:supports(),
case proplists:get_bool(ecdsa, proplists:get_value(public_keys, CryptoSupport)) of
@@ -862,6 +1046,160 @@ accepters(Acc, N) ->
accepters([Server| Acc], N-1)
end.
+
+basic_test(COpts, SOpts, Config) ->
+ SType = proplists:get_value(server_type, Config),
+ CType = proplists:get_value(client_type, Config),
+ {Server, Port} = start_server(SType, SOpts, Config),
+ Client = start_client(CType, Port, COpts, Config),
+ gen_check_result(Server, SType, Client, CType),
+ stop(Server, Client).
+
+ecc_test(Expect, COpts, SOpts, CECCOpts, SECCOpts, Config) ->
+ {Server, Port} = start_server_ecc(erlang, SOpts, Expect, SECCOpts, Config),
+ Client = start_client_ecc(erlang, Port, COpts, Expect, CECCOpts, Config),
+ check_result(Server, ok, Client, ok),
+ stop(Server, Client).
+
+ecc_test_error(COpts, SOpts, CECCOpts, SECCOpts, Config) ->
+ {Server, Port} = start_server_ecc_error(erlang, SOpts, SECCOpts, Config),
+ Client = start_client_ecc_error(erlang, Port, COpts, CECCOpts, Config),
+ Error = {error, {tls_alert, "insufficient security"}},
+ check_result(Server, Error, Client, Error).
+
+start_client(openssl, Port, ClientOpts, Config) ->
+ Cert = proplists:get_value(certfile, ClientOpts),
+ Key = proplists:get_value(keyfile, ClientOpts),
+ CA = proplists:get_value(cacertfile, ClientOpts),
+ Version = ssl_test_lib:protocol_version(Config),
+ Exe = "openssl",
+ Args = ["s_client", "-verify", "2", "-port", integer_to_list(Port),
+ ssl_test_lib:version_flag(Version),
+ "-cert", Cert, "-CAfile", CA,
+ "-key", Key, "-host","localhost", "-msg", "-debug"],
+
+ OpenSslPort = ssl_test_lib:portable_open_port(Exe, Args),
+ true = port_command(OpenSslPort, "Hello world"),
+ OpenSslPort;
+
+start_client(erlang, Port, ClientOpts, Config) ->
+ {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config),
+ KeyEx = proplists:get_value(check_keyex, Config, false),
+ ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {ssl_test_lib, check_key_exchange_send_active, [KeyEx]}},
+ {options, [{verify, verify_peer} | ClientOpts]}]).
+
+
+start_client_ecc(erlang, Port, ClientOpts, Expect, ECCOpts, Config) ->
+ {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config),
+ ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, check_ecc, [client, Expect]}},
+ {options,
+ ECCOpts ++
+ [{verify, verify_peer} | ClientOpts]}]).
+
+start_client_ecc_error(erlang, Port, ClientOpts, ECCOpts, Config) ->
+ {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config),
+ ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {options,
+ ECCOpts ++
+ [{verify, verify_peer} | ClientOpts]}]).
+
+
+start_server(openssl, ServerOpts, Config) ->
+ Cert = proplists:get_value(certfile, ServerOpts),
+ Key = proplists:get_value(keyfile, ServerOpts),
+ CA = proplists:get_value(cacertfile, ServerOpts),
+ Port = inet_port(node()),
+ Version = protocol_version(Config),
+ Exe = "openssl",
+ Args = ["s_server", "-accept", integer_to_list(Port), ssl_test_lib:version_flag(Version),
+ "-verify", "2", "-cert", Cert, "-CAfile", CA,
+ "-key", Key, "-msg", "-debug"],
+ OpenSslPort = portable_open_port(Exe, Args),
+ true = port_command(OpenSslPort, "Hello world"),
+ {OpenSslPort, Port};
+start_server(erlang, ServerOpts, Config) ->
+ {_, ServerNode, _} = ssl_test_lib:run_where(Config),
+ KeyEx = proplists:get_value(check_keyex, Config, false),
+ Server = start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {ssl_test_lib,
+ check_key_exchange_send_active,
+ [KeyEx]}},
+ {options, [{verify, verify_peer} | ServerOpts]}]),
+ {Server, inet_port(Server)}.
+
+start_server_with_raw_key(erlang, ServerOpts, Config) ->
+ {_, ServerNode, _} = ssl_test_lib:run_where(Config),
+ Server = start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {ssl_test_lib,
+ send_recv_result_active,
+ []}},
+ {options,
+ [{verify, verify_peer} | ServerOpts]}]),
+ {Server, inet_port(Server)}.
+
+start_server_ecc(erlang, ServerOpts, Expect, ECCOpts, Config) ->
+ {_, ServerNode, _} = run_where(Config),
+ Server = start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, check_ecc, [server, Expect]}},
+ {options,
+ ECCOpts ++
+ [{verify, verify_peer} | ServerOpts]}]),
+ {Server, inet_port(Server)}.
+
+start_server_ecc_error(erlang, ServerOpts, ECCOpts, Config) ->
+ {_, ServerNode, _} = run_where(Config),
+ Server = start_server_error([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {options,
+ ECCOpts ++
+ [{verify, verify_peer} | ServerOpts]}]),
+ {Server, inet_port(Server)}.
+
+gen_check_result(Server, erlang, Client, erlang) ->
+ check_result(Server, ok, Client, ok);
+gen_check_result(Server, erlang, _, _) ->
+ check_result(Server, ok);
+gen_check_result(_, _, Client, erlang) ->
+ check_result(Client, ok);
+gen_check_result(_,openssl, _, openssl) ->
+ ok.
+
+stop(Port1, Port2) when is_port(Port1), is_port(Port2) ->
+ close_port(Port1),
+ close_port(Port2);
+stop(Port, Pid) when is_port(Port) ->
+ close_port(Port),
+ close(Pid);
+stop(Pid, Port) when is_port(Port) ->
+ close_port(Port),
+ close(Pid);
+stop(Client, Server) ->
+ close(Server),
+ close(Client).
+
+supported_eccs(Opts) ->
+ ToCheck = proplists:get_value(eccs, Opts, []),
+ Supported = ssl:eccs(),
+ lists:all(fun(Curve) -> lists:member(Curve, Supported) end, ToCheck).
+
+check_ecc(SSL, Role, Expect) ->
+ {ok, Data} = ssl:connection_information(SSL),
+ case lists:keyfind(ecc, 1, Data) of
+ {ecc, {named_curve, Expect}} -> ok;
+ Other -> {error, Role, Expect, Other}
+ end.
+
inet_port(Pid) when is_pid(Pid)->
receive
{Pid, {port, Port}} ->
@@ -930,13 +1268,13 @@ rsa_suites(CounterPart) ->
lists:member(cipher_atom(Cipher), Ciphers);
({ecdhe_rsa, Cipher, _}) when ECC == true ->
lists:member(cipher_atom(Cipher), Ciphers);
+ ({ecdhe_rsa, Cipher, _,_}) when ECC == true ->
+ lists:member(cipher_atom(Cipher), Ciphers);
({rsa, Cipher, _, _}) ->
lists:member(cipher_atom(Cipher), Ciphers);
({dhe_rsa, Cipher, _,_}) ->
lists:member(cipher_atom(Cipher), Ciphers);
- ({ecdhe_rsa, Cipher, _,_}) when ECC == true ->
- lists:member(cipher_atom(Cipher), Ciphers);
- (_) ->
+ (_) ->
false
end,
common_ciphers(CounterPart)).
@@ -946,13 +1284,13 @@ common_ciphers(crypto) ->
common_ciphers(openssl) ->
OpenSslSuites =
string:tokens(string:strip(os:cmd("openssl ciphers"), right, $\n), ":"),
- [ssl_cipher:erl_suite_definition(S)
+ [ssl_cipher_format:suite_definition(S)
|| S <- ssl_cipher:suites(tls_record:highest_protocol_version([])),
- lists:member(ssl_cipher:openssl_suite_name(S), OpenSslSuites)
+ lists:member(ssl_cipher_format:openssl_suite_name(S), OpenSslSuites)
].
available_suites(Version) ->
- [ssl_cipher:erl_suite_definition(Suite) ||
+ [ssl_cipher_format:suite_definition(Suite) ||
Suite <- ssl_cipher:filter_suites(ssl_cipher:suites(Version))].
@@ -1024,24 +1362,52 @@ string_regex_filter(Str, Search) when is_list(Str) ->
string_regex_filter(_Str, _Search) ->
false.
-anonymous_suites(Version) ->
- ssl:filter_cipher_suites([ssl_cipher:suite_definition(S) || S <- ssl_cipher:anonymous_suites(Version)],[]).
+ecdh_dh_anonymous_suites(Version) ->
+ ssl:filter_cipher_suites([ssl_cipher_format:suite_definition(S) || S <- ssl_cipher:anonymous_suites(Version)],
+ [{key_exchange,
+ fun(dh_anon) ->
+ true;
+ (ecdh_anon) ->
+ true;
+ (_) ->
+ false
+ end}]).
+psk_suites({3,_} = Version) ->
+ ssl:filter_cipher_suites([ssl_cipher_format:suite_definition(S) || S <- ssl_cipher:psk_suites(Version)], []);
psk_suites(Version) ->
- ssl:filter_cipher_suites([ssl_cipher:suite_definition(S) || S <- ssl_cipher:psk_suites(Version)], []).
+ ssl:filter_cipher_suites(psk_suites(dtls_v1:corresponding_tls_version(Version)),
+ [{cipher,
+ fun(rc4_128) ->
+ false;
+ (_) ->
+ true
+ end}]).
-psk_anon_suites(Version) ->
- ssl:filter_cipher_suites([ssl_cipher:suite_definition(S) || S <- ssl_cipher:psk_suites_anon(Version)],
+psk_anon_suites({3,_} = Version) ->
+ ssl:filter_cipher_suites([ssl_cipher_format:suite_definition(S) || S <- ssl_cipher:psk_suites_anon(Version)],
[{key_exchange,
fun(psk) ->
true;
- (psk_dhe) ->
+ (dhe_psk) ->
+ true;
+ (ecdhe_psk) ->
true;
(_) ->
false
+ end}]);
+
+psk_anon_suites(Version) ->
+ ssl:filter_cipher_suites(psk_anon_suites(dtls_v1:corresponding_tls_version(Version)),
+ [{cipher,
+ fun(rc4_128) ->
+ false;
+ (_) ->
+ true
end}]).
+
srp_suites() ->
- ssl:filter_cipher_suites([ssl_cipher:suite_definition(S) || S <- ssl_cipher:srp_suites()],
+ ssl:filter_cipher_suites([ssl_cipher_format:suite_definition(S) || S <- ssl_cipher:srp_suites()],
[{key_exchange,
fun(srp_rsa) ->
true;
@@ -1049,21 +1415,25 @@ srp_suites() ->
false
end}]).
srp_anon_suites() ->
- ssl:filter_cipher_suites([ssl_cipher:suite_definition(S) || S <- ssl_cipher:srp_suites_anon()],
+ ssl:filter_cipher_suites([ssl_cipher_format:suite_definition(S) || S <- ssl_cipher:srp_suites_anon()],
[]).
srp_dss_suites() ->
- ssl:filter_cipher_suites([ssl_cipher:suite_definition(S) || S <- ssl_cipher:srp_suites()],
+ ssl:filter_cipher_suites([ssl_cipher_format:suite_definition(S) || S <- ssl_cipher:srp_suites()],
[{key_exchange,
fun(srp_dss) ->
true;
(_) ->
false
end}]).
+chacha_suites(Version) ->
+ [ssl_cipher_format:suite_definition(S) || S <- ssl_cipher:filter_suites(ssl_cipher:chacha_suites(Version))].
+
+
rc4_suites(Version) ->
- ssl:filter_cipher_suites([ssl_cipher:suite_definition(S) || S <-ssl_cipher:rc4_suites(Version)], []).
+ ssl:filter_cipher_suites([ssl_cipher_format:suite_definition(S) || S <-ssl_cipher:rc4_suites(Version)], []).
des_suites(Version) ->
- ssl:filter_cipher_suites([ssl_cipher:suite_definition(S) || S <-ssl_cipher:des_suites(Version)], []).
+ ssl:filter_cipher_suites([ssl_cipher_format:suite_definition(S) || S <-ssl_cipher:des_suites(Version)], []).
tuple_to_map({Kex, Cipher, Mac}) ->
#{key_exchange => Kex,
@@ -1086,24 +1456,15 @@ der_to_pem(File, Entries) ->
cipher_result(Socket, Result) ->
{ok, Info} = ssl:connection_information(Socket),
- Result = {ok, {proplists:get_value(protocol, Info), proplists:get_value(cipher_suite, Info)}},
+ Result = {ok, {proplists:get_value(protocol, Info), proplists:get_value(selected_cipher_suite, Info)}},
ct:log("~p:~p~nSuccessfull connect: ~p~n", [?MODULE,?LINE, Result]),
%% Importante to send two packets here
%% to properly test "cipher state" handling
ssl:send(Socket, "Hello\n"),
- receive
- {ssl, Socket, "H"} ->
- ssl:send(Socket, " world\n"),
- receive_rizzo_duong_beast();
- {ssl, Socket, "Hello\n"} ->
- ssl:send(Socket, " world\n"),
- receive
- {ssl, Socket, " world\n"} ->
- ok
- end;
- Other ->
- {unexpected, Other}
- end.
+ "Hello\n" = active_recv(Socket, length( "Hello\n")),
+ ssl:send(Socket, " world\n"),
+ " world\n" = active_recv(Socket, length(" world\n")),
+ ok.
session_info_result(Socket) ->
{ok, Info} = ssl:connection_information(Socket, [session_id, cipher_suite]),
@@ -1181,10 +1542,7 @@ sufficient_crypto_support(Version)
when Version == 'tlsv1.2'; Version == 'dtlsv1.2' ->
CryptoSupport = crypto:supports(),
proplists:get_bool(sha256, proplists:get_value(hashs, CryptoSupport));
-sufficient_crypto_support(Group) when Group == ciphers_ec; %% From ssl_basic_SUITE
- Group == erlang_server; %% From ssl_ECC_SUITE
- Group == erlang_client; %% From ssl_ECC_SUITE
- Group == erlang -> %% From ssl_ECC_SUITE
+sufficient_crypto_support(cipher_ec) ->
CryptoSupport = crypto:supports(),
proplists:get_bool(ecdh, proplists:get_value(public_keys, CryptoSupport));
sufficient_crypto_support(_) ->
@@ -1193,16 +1551,36 @@ sufficient_crypto_support(_) ->
check_key_exchange_send_active(Socket, false) ->
send_recv_result_active(Socket);
check_key_exchange_send_active(Socket, KeyEx) ->
- {ok, [{cipher_suite, Suite}]} = ssl:connection_information(Socket, [cipher_suite]),
- true = check_key_exchange(Suite, KeyEx),
+ {ok, Info} =
+ ssl:connection_information(Socket, [cipher_suite, protocol]),
+ Suite = proplists:get_value(cipher_suite, Info),
+ Version = proplists:get_value(protocol, Info),
+ true = check_key_exchange(Suite, KeyEx, Version),
send_recv_result_active(Socket).
-check_key_exchange({KeyEx,_, _}, KeyEx) ->
+check_key_exchange({KeyEx,_, _}, KeyEx, _) ->
+ ct:pal("Kex: ~p", [KeyEx]),
true;
-check_key_exchange({KeyEx,_,_,_}, KeyEx) ->
+check_key_exchange({KeyEx,_,_,_}, KeyEx, _) ->
+ ct:pal("Kex: ~p", [KeyEx]),
true;
-check_key_exchange(KeyEx1, KeyEx2) ->
- ct:pal("Negotiated ~p Expected ~p", [KeyEx1, KeyEx2]),
+check_key_exchange(KeyEx1, KeyEx2, Version) ->
+ ct:pal("Kex: ~p ~p", [KeyEx1, KeyEx2]),
+ case Version of
+ 'tlsv1.2' ->
+ v_1_2_check(element(1, KeyEx1), KeyEx2);
+ 'dtlsv1.2' ->
+ v_1_2_check(element(1, KeyEx1), KeyEx2);
+ _ ->
+ ct:pal("Negotiated ~p Expected ~p", [KeyEx1, KeyEx2]),
+ false
+ end.
+
+v_1_2_check(ecdh_ecdsa, ecdh_rsa) ->
+ true;
+v_1_2_check(ecdh_rsa, ecdh_ecdsa) ->
+ true;
+v_1_2_check(_, _) ->
false.
send_recv_result_active(Socket) ->
@@ -1235,6 +1613,17 @@ send_recv_result_active_once(Socket) ->
ok
end.
+active_recv(Socket, N) ->
+ active_recv(Socket, N, []).
+
+active_recv(_Socket, 0, Acc) ->
+ Acc;
+active_recv(Socket, N, Acc) ->
+ receive
+ {ssl, Socket, Bytes} ->
+ active_recv(Socket, N-length(Bytes), Acc ++ Bytes)
+ end.
+
is_sane_ecc(openssl) ->
case os:cmd("openssl version") of
"OpenSSL 1.0.0a" ++ _ -> % Known bug in openssl
@@ -1264,7 +1653,7 @@ is_sane_ecc(crypto) ->
true
end;
is_sane_ecc(_) ->
- true.
+ sufficient_crypto_support(cipher_ec).
is_fips(openssl) ->
VersionStr = os:cmd("openssl version"),
@@ -1320,8 +1709,50 @@ openssl_dsa_support() ->
true;
"LibreSSL" ++ _ ->
false;
+ "OpenSSL 1.1" ++ _Rest ->
+ false;
"OpenSSL 1.0.1" ++ Rest ->
- hd(Rest) >= s;
+ hd(Rest) >= $s;
+ _ ->
+ true
+ end.
+
+%% Acctual support is tested elsewhere, this is to exclude some LibreSSL and OpenSSL versions
+openssl_sane_dtls() ->
+ case os:cmd("openssl version") of
+ "OpenSSL 0." ++ _ ->
+ false;
+ "OpenSSL 1.0.1s-freebsd" ++ _ ->
+ false;
+ "OpenSSL 1.0.2k-freebsd" ++ _ ->
+ false;
+ "OpenSSL 1.0.2" ++ _ ->
+ false;
+ "OpenSSL 1.0.0" ++ _ ->
+ false;
+ "OpenSSL" ++ _ ->
+ true;
+ "LibreSSL 2.7" ++ _ ->
+ true;
+ _ ->
+ false
+ end.
+openssl_sane_client_cert() ->
+ case os:cmd("openssl version") of
+ "LibreSSL 2.5.2" ++ _ ->
+ true;
+ "LibreSSL 2.4" ++ _ ->
+ false;
+ "LibreSSL 2.3" ++ _ ->
+ false;
+ "LibreSSL 2.1" ++ _ ->
+ false;
+ "LibreSSL 2.0" ++ _ ->
+ false;
+ "OpenSSL 1.0.1s-freebsd" ->
+ false;
+ "OpenSSL 1.0.0" ++ _ ->
+ false;
_ ->
true
end.
@@ -1332,6 +1763,10 @@ check_sane_openssl_version(Version) ->
case {Version, os:cmd("openssl version")} of
{'sslv3', "OpenSSL 1.0.2" ++ _} ->
false;
+ {'dtlsv1', _} ->
+ not is_fips(openssl);
+ {'dtlsv1.2', _} ->
+ not is_fips(openssl);
{_, "OpenSSL 1.0.2" ++ _} ->
true;
{_, "OpenSSL 1.0.1" ++ _} ->
@@ -1340,7 +1775,7 @@ check_sane_openssl_version(Version) ->
false;
{'tlsv1.1', "OpenSSL 1.0.0" ++ _} ->
false;
- {'dtlsv1.2', "OpenSSL 1.0.0" ++ _} ->
+ {'dtlsv1.2', "OpenSSL 1.0.2" ++ _} ->
false;
{'dtlsv1', "OpenSSL 1.0.0" ++ _} ->
false;
@@ -1394,10 +1829,10 @@ version_flag('dtlsv1') ->
"-dtls1".
filter_suites([Cipher | _] = Ciphers, AtomVersion) when is_list(Cipher)->
- filter_suites([ssl_cipher:openssl_suite(S) || S <- Ciphers],
+ filter_suites([ssl_cipher_format:openssl_suite(S) || S <- Ciphers],
AtomVersion);
filter_suites([Cipher | _] = Ciphers, AtomVersion) when is_binary(Cipher)->
- filter_suites([ssl_cipher:erl_suite_definition(S) || S <- Ciphers],
+ filter_suites([ssl_cipher_format:suite_definition(S) || S <- Ciphers],
AtomVersion);
filter_suites(Ciphers0, AtomVersion) ->
Version = tls_version(AtomVersion),
@@ -1409,7 +1844,7 @@ filter_suites(Ciphers0, AtomVersion) ->
++ ssl_cipher:srp_suites_anon()
++ ssl_cipher:rc4_suites(Version),
Supported1 = ssl_cipher:filter_suites(Supported0),
- Supported2 = [ssl_cipher:erl_suite_definition(S) || S <- Supported1],
+ Supported2 = [ssl_cipher_format:suite_definition(S) || S <- Supported1],
[Cipher || Cipher <- Ciphers0, lists:member(Cipher, Supported2)].
-define(OPENSSL_QUIT, "Q\n").
@@ -1462,8 +1897,13 @@ supports_ssl_tls_version(sslv2 = Version) ->
VersionFlag = version_flag(Version),
Exe = "openssl",
Args = ["s_client", VersionFlag],
+ [{trap_exit, Trap}] = process_info(self(), [trap_exit]),
+ process_flag(trap_exit, true),
Port = ssl_test_lib:portable_open_port(Exe, Args),
- do_supports_ssl_tls_version(Port, "")
+ Bool = do_supports_ssl_tls_version(Port, ""),
+ consume_port_exit(Port),
+ process_flag(trap_exit, Trap),
+ Bool
end;
supports_ssl_tls_version(Version) ->
@@ -1479,13 +1919,11 @@ do_supports_ssl_tls_version(Port, Acc) ->
case Acc ++ Data of
"unknown option" ++ _ ->
false;
- Error when length(Error) >= 11 ->
- case lists:member("error", string:tokens(Data, ":")) of
- true ->
- false;
- false ->
- do_supports_ssl_tls_version(Port, Error)
- end;
+ "s_client: Option unknown" ++ _->
+ false;
+ Info when length(Info) >= 24 ->
+ ct:pal("~p", [Info]),
+ true;
_ ->
do_supports_ssl_tls_version(Port, Acc ++ Data)
end
@@ -1555,10 +1993,14 @@ is_psk_anon_suite({psk, _,_}) ->
true;
is_psk_anon_suite({dhe_psk,_,_}) ->
true;
+is_psk_anon_suite({ecdhe_psk,_,_}) ->
+ true;
is_psk_anon_suite({psk, _,_,_}) ->
true;
is_psk_anon_suite({dhe_psk, _,_,_}) ->
true;
+is_psk_anon_suite({ecdhe_psk, _,_,_}) ->
+ true;
is_psk_anon_suite(_) ->
false.
@@ -1577,6 +2019,12 @@ tls_version('dtlsv1.2' = Atom) ->
tls_version(Atom) ->
tls_record:protocol_version(Atom).
+consume_port_exit(OpenSSLPort) ->
+ receive
+ {'EXIT', OpenSSLPort, _} ->
+ ok
+ end.
+
hardcode_rsa_key(1) ->
#'RSAPrivateKey'{
version = 'two-prime',
@@ -1676,3 +2124,40 @@ hardcode_dsa_key(3) ->
y = 48598545580251057979126570873881530215432219542526130654707948736559463436274835406081281466091739849794036308281564299754438126857606949027748889019480936572605967021944405048011118039171039273602705998112739400664375208228641666852589396502386172780433510070337359132965412405544709871654840859752776060358,
x = 1457508827177594730669011716588605181448418352823}.
+tcp_delivery_workaround(Server, ServerMsg, Client, ClientMsg) ->
+ receive
+ {Server, ServerMsg} ->
+ client_msg(Client, ClientMsg);
+ {Client, ClientMsg} ->
+ server_msg(Server, ServerMsg);
+ {Client, {error,closed}} ->
+ server_msg(Server, ServerMsg);
+ {Server, {error,closed}} ->
+ client_msg(Client, ClientMsg)
+ end.
+client_msg(Client, ClientMsg) ->
+ receive
+ {Client, ClientMsg} ->
+ ok;
+ {Client, {error,closed}} ->
+ ct:log("client got close"),
+ ok;
+ {Client, {error, Reason}} ->
+ ct:log("client got econnaborted: ~p", [Reason]),
+ ok;
+ Unexpected ->
+ ct:fail(Unexpected)
+ end.
+server_msg(Server, ServerMsg) ->
+ receive
+ {Server, ServerMsg} ->
+ ok;
+ {Server, {error,closed}} ->
+ ct:log("server got close"),
+ ok;
+ {Server, {error, Reason}} ->
+ ct:log("server got econnaborted: ~p", [Reason]),
+ ok;
+ Unexpected ->
+ ct:fail(Unexpected)
+ end.