aboutsummaryrefslogtreecommitdiffstats
path: root/lib/ssl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/ssl')
-rw-r--r--lib/ssl/doc/src/ssl.xml133
-rw-r--r--lib/ssl/src/dtls_connection.erl65
-rw-r--r--lib/ssl/src/dtls_handshake.erl6
-rw-r--r--lib/ssl/src/ssl.erl119
-rw-r--r--lib/ssl/src/ssl_connection.erl134
-rw-r--r--lib/ssl/src/ssl_connection.hrl15
-rw-r--r--lib/ssl/src/ssl_handshake.erl31
-rw-r--r--lib/ssl/src/ssl_internal.hrl3
-rw-r--r--lib/ssl/src/tls_connection.erl26
-rw-r--r--lib/ssl/test/Makefile2
-rw-r--r--lib/ssl/test/ssl_ECC.erl154
-rw-r--r--lib/ssl/test/ssl_ECC_SUITE.erl535
-rw-r--r--lib/ssl/test/ssl_ECC_openssl_SUITE.erl185
-rw-r--r--lib/ssl/test/ssl_basic_SUITE.erl92
-rw-r--r--lib/ssl/test/ssl_certificate_verify_SUITE.erl92
-rw-r--r--lib/ssl/test/ssl_test_lib.erl263
16 files changed, 1290 insertions, 565 deletions
diff --git a/lib/ssl/doc/src/ssl.xml b/lib/ssl/doc/src/ssl.xml
index 8c1b1541c7..029f29cdb3 100644
--- a/lib/ssl/doc/src/ssl.xml
+++ b/lib/ssl/doc/src/ssl.xml
@@ -197,6 +197,18 @@
| sect193r1 | sect193r2 | secp192k1 | secp192r1 | sect163k1
| sect163r1 | sect163r2 | secp160k1 | secp160r1 | secp160r2</c></p></item>
+ <tag><c>hello_extensions() =</c></tag>
+ <item><p><c>#{renegotiation_info =>
+ signature_algs => [{hash(), ecsda| rsa| dsa}] | undefined
+ alpn => binary() | undefined,
+ next_protocol_negotiation,
+ srp => string() | undefined,
+ ec_point_formats ,
+ elliptic_curves = [oid] | undefined
+ sni = string()}
+ }</c></p></item>
+
+
</taglist>
</section>
@@ -211,8 +223,16 @@
<tag><c>{protocol, tls | dtls}</c></tag>
<item><p>Choose TLS or DTLS protocol for the transport layer security.
Defaults to <c>tls</c> Introduced in OTP 20, DTLS support is considered
- experimental in this release. DTLS over other transports than UDP are not yet supported.</p></item>
-
+ experimental in this release. Other transports than UDP are not yet supported.</p></item>
+
+ <tag><c>{handshake, hello | full}</c></tag>
+ <item><p> Defaults to <c>full</c>. If hello is specified the handshake will
+ pause after the hello message and give the user a possibility make decisions
+ based on hello extensions before continuing or aborting the handshake by calling
+ <seealso marker="#handshake_continue-3"> handshake_continue/3</seealso> or
+ <seealso marker="#handshake_cancel-1"> handshake_cancel/1</seealso>
+ </p></item>
+
<tag><c>{cert, public_key:der_encoded()}</c></tag>
<item><p>The DER-encoded users certificate. If this option
is supplied, it overrides option <c>certfile</c>.</p></item>
@@ -919,15 +939,16 @@ fun(srp, Username :: string(), UserState :: term()) ->
<func>
<name>connect(Socket, SslOptions) -> </name>
- <name>connect(Socket, SslOptions, Timeout) -> {ok, TLSSocket}
+ <name>connect(Socket, SslOptions, Timeout) -> {ok, TLSSocket} | {ok, TLSSocket, Ext}
| {error, Reason}</name>
<fsummary>Upgrades a <c>gen_tcp</c>, or
equivalent, connected socket to an TLS socket.</fsummary>
<type>
<v>Socket = socket()</v>
- <v>SslOptions = [ssl_option()]</v>
+ <v>SslOptions = [{handshake, hello| full} | ssl_option()]</v>
<v>Timeout = integer() | infinity</v>
<v>TLSSocket = sslsocket()</v>
+ <v>Ext = hello_extensions()</v>
<v>Reason = term()</v>
</type>
<desc><p>Upgrades a <c>gen_tcp</c>, or equivalent,
@@ -938,14 +959,25 @@ fun(srp, Username :: string(), UserState :: term()) ->
the option <c>server_name_indication</c> shall also be specified,
if it is not no Server Name Indication extension will be sent,
and <seealso marker="public_key:public_key#pkix_verify_hostname-2">public_key:pkix_verify_hostname/2</seealso>
- will be called with the IP-address of the connection as <c>ReferenceID</c>, which is proably not what you want.</p></note>
+ will be called with the IP-address of the connection as <c>ReferenceID</c>, which is proably not what you want.</p>
+ </note>
+
+ <p> If the option <c>{handshake, hello}</c> is used the
+ handshake is paused after receiving the server hello message
+ and the success response is <c>{ok, TLSSocket, Ext}</c>
+ instead of <c>{ok, TLSSocket}</c>. Thereafter the handshake is continued or
+ canceled by calling <seealso marker="#handshake_continue-3">
+ <c>handshake_continue/3</c></seealso> or <seealso
+ marker="#handshake_cancel-1"><c>handshake_cancel/1</c></seealso>.
+ </p>
+
</desc>
</func>
<func>
<name>connect(Host, Port, Options) -></name>
<name>connect(Host, Port, Options, Timeout) ->
- {ok, SslSocket} | {error, Reason}</name>
+ {ok, SslSocket}| {ok, TLSSocket, Ext} | {error, Reason}</name>
<fsummary>Opens an TLS/DTLS connection to <c>Host</c>, <c>Port</c>.</fsummary>
<type>
<v>Host = host()</v>
@@ -972,6 +1004,16 @@ fun(srp, Username :: string(), UserState :: term()) ->
<c>dns_id</c> will be assumed with a fallback to <c>ip</c> if that fails. </p>
<note><p>According to good practices certificates should not use IP-addresses as "server names". It would
be very surprising if this happen outside a closed network. </p></note>
+
+
+ <p> If the option <c>{handshake, hello}</c> is used the
+ handshake is paused after receiving the server hello message
+ and the success response is <c>{ok, TLSSocket, Ext}</c>
+ instead of <c>{ok, TLSSocket}</c>. Thereafter the handshake is continued or
+ canceled by calling <seealso marker="#handshake_continue-3">
+ <c>handshake_continue/3</c></seealso> or <seealso
+ marker="#handshake_cancel-1"><c>handshake_cancel/1</c></seealso>.
+ </p>
</desc>
</func>
@@ -1113,6 +1155,85 @@ fun(srp, Username :: string(), UserState :: term()) ->
</func>
<func>
+ <name>handshake(Socket) -> </name>
+ <name>handshake(Socket, Timeout) -> {ok, Socket} | {error, Reason}</name>
+ <fsummary>Performs server-side SSL/TLS handshake.</fsummary>
+ <type>
+ <v>Socket = sslsocket()</v>
+ <v>Timeout = integer()</v>
+ <v>Reason = term()</v>
+ </type>
+ <desc>
+ <p>Performs the SSL/TLS/DTLS server-side handshake.</p>
+ <p><c>Socket</c> is a socket as returned by
+ <seealso marker="#transport_accept-2">ssl:transport_accept/[1,2]</seealso>.
+ </p>
+ </desc>
+ </func>
+
+ <func>
+ <name>handshake(Socket, SslOptions) -> </name>
+ <name>handshake(Socket, SslOptions, Timeout) -> {ok, Socket} | {ok, Socket, Ext} | {error, Reason}</name>
+ <fsummary>Performs server-side SSL/TLS/DTLS handshake.</fsummary>
+ <type>
+ <v>Socket = socket() | sslsocket() </v>
+ <v>Ext = hello_extensions()</v>
+ <v>SslOptions = [{handshake, hello| full} | ssl_option()]</v>
+ <v>Timeout = integer()</v>
+ <v>Reason = term()</v>
+ </type>
+ <desc>
+ <p>If <c>Socket</c> is a ordinary <c>socket()</c>: upgrades a <c>gen_tcp</c>,
+ or equivalent, socket to an SSL socket, that is, performs
+ the SSL/TLS server-side handshake and returns the SSL socket.</p>
+
+ <warning><p>The Socket shall be in passive mode ({active,
+ false}) before calling this function or the handshake can fail
+ due to a race condition.</p></warning>
+
+ <p>If <c>Socket</c> is an <c>sslsocket()</c>: provides extra SSL/TLS/DTLS
+ options to those specified in
+ <seealso marker="#listen-2">ssl:listen/2 </seealso> and then performs
+ the SSL/TLS/DTLS handshake.</p>
+
+ <p>
+ If option <c>{handshake, hello}</c> is specified the handshake is
+ paused after receiving the client hello message and the
+ sucess response is <c>{ok, TLSSocket, Ext}</c> instead of <c>{ok,
+ TLSSocket}</c>. Thereafter the handshake is continued or
+ canceled by calling <seealso marker="#handshake_continue-3">
+ <c>handshake_continue/3</c></seealso> or <seealso
+ marker="#handshake_cancel-1"><c>handshake_cancel/1</c></seealso>.
+ </p>
+ </desc>
+ </func>
+
+ <func>
+ <name>handshake_cancel(Socket) -> ok </name>
+ <fsummary>Cancel handshake with a fatal alert</fsummary>
+ <type>
+ <v>Socket = sslsocket()</v>
+ </type>
+ <desc>
+ <p>Cancel the handshake with a fatal <c>USER_CANCELED</c> alert.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name>handshake_continue(Socket, SSLOptions, Timeout) -> {ok, Socket} | {error, Reason}</name>
+ <fsummary>Continue the SSL/TLS handshake.</fsummary>
+ <type>
+ <v>Socket = sslsocket()</v>
+ <v>SslOptions = [ssl_option()]</v>
+ <v>Timeout = integer()</v>
+ <v>Reason = term()</v>
+ </type>
+ <desc>
+ <p>Continue the SSL/TLS handshake possiby with new, additional or changed options.</p>
+ </desc>
+ </func>
+
+ <func>
<name>listen(Port, Options) ->
{ok, ListenSocket} | {error, Reason}</name>
<fsummary>Creates an SSL listen socket.</fsummary>
diff --git a/lib/ssl/src/dtls_connection.erl b/lib/ssl/src/dtls_connection.erl
index 7bc7fc3fc6..220da71123 100644
--- a/lib/ssl/src/dtls_connection.erl
+++ b/lib/ssl/src/dtls_connection.erl
@@ -55,7 +55,7 @@
%% gen_statem state functions
-export([init/3, error/3, downgrade/3, %% Initiation and take down states
- hello/3, certify/3, cipher/3, abbreviated/3, %% Handshake states
+ hello/3, user_hello/3, certify/3, cipher/3, abbreviated/3, %% Handshake states
connection/3]).
%% gen_statem callbacks
-export([callback_mode/0, terminate/3, code_change/4, format_status/2]).
@@ -73,8 +73,7 @@ start_fsm(Role, Host, Port, Socket, {#ssl_options{erl_dist = false},_, Tracker}
{ok, Pid} = dtls_connection_sup:start_child([Role, Host, Port, Socket,
Opts, User, CbInfo]),
{ok, SslSocket} = ssl_connection:socket_control(?MODULE, Socket, Pid, CbModule, Tracker),
- ok = ssl_connection:handshake(SslSocket, Timeout),
- {ok, SslSocket}
+ ssl_connection:handshake(SslSocket, Timeout)
catch
error:{badmatch, {error, _} = Error} ->
Error
@@ -492,10 +491,11 @@ hello(enter, _, #state{role = client} = State0) ->
{State, Actions} = handle_flight_timer(State0),
{keep_state, State, Actions};
hello(internal, #client_hello{cookie = <<>>,
- client_version = Version} = Hello, #state{role = server,
- transport_cb = Transport,
- socket = Socket,
- protocol_specific = #{current_cookie_secret := Secret}} = State0) ->
+ client_version = Version} = Hello,
+ #state{role = server,
+ transport_cb = Transport,
+ socket = Socket,
+ protocol_specific = #{current_cookie_secret := Secret}} = State0) ->
{ok, {IP, Port}} = dtls_socket:peername(Transport, Socket),
Cookie = dtls_handshake:cookie(Secret, IP, Port, Hello),
%% FROM RFC 6347 regarding HelloVerifyRequest message:
@@ -509,24 +509,6 @@ hello(internal, #client_hello{cookie = <<>>,
{State2, Actions} = send_handshake(VerifyRequest, State1),
{Record, State} = next_record(State2),
next_event(?FUNCTION_NAME, Record, State#state{tls_handshake_history = ssl_handshake:init_handshake_history()}, Actions);
-hello(internal, #client_hello{cookie = Cookie} = Hello, #state{role = server,
- transport_cb = Transport,
- socket = Socket,
- protocol_specific = #{current_cookie_secret := Secret,
- previous_cookie_secret := PSecret}} = State0) ->
- {ok, {IP, Port}} = dtls_socket:peername(Transport, Socket),
- case dtls_handshake:cookie(Secret, IP, Port, Hello) of
- Cookie ->
- handle_client_hello(Hello, State0);
- _ ->
- case dtls_handshake:cookie(PSecret, IP, Port, Hello) of
- Cookie ->
- handle_client_hello(Hello, State0);
- _ ->
- %% Handle bad cookie as new cookie request RFC 6347 4.1.2
- hello(internal, Hello#client_hello{cookie = <<>>}, State0)
- end
- end;
hello(internal, #hello_verify_request{cookie = Cookie}, #state{role = client,
host = Host, port = Port,
ssl_options = SslOpts,
@@ -551,6 +533,34 @@ hello(internal, #hello_verify_request{cookie = Cookie}, #state{role = client,
Hello#client_hello.session_id}},
{Record, State} = next_record(State3),
next_event(?FUNCTION_NAME, Record, State, Actions);
+hello(internal, #client_hello{extensions = Extensions} = Hello, #state{ssl_options = #ssl_options{handshake = hello},
+ start_or_recv_from = From} = State) ->
+ {next_state, user_hello, State#state{start_or_recv_from = undefined,
+ hello = Hello},
+ [{reply, From, {ok, ssl_connection:map_extensions(Extensions)}}]};
+hello(internal, #server_hello{extensions = Extensions} = Hello, #state{ssl_options = #ssl_options{handshake = hello},
+ start_or_recv_from = From} = State) ->
+ {next_state, user_hello, State#state{start_or_recv_from = undefined,
+ hello = Hello},
+ [{reply, From, {ok, ssl_connection:map_extensions(Extensions)}}]};
+hello(internal, #client_hello{cookie = Cookie} = Hello, #state{role = server,
+ transport_cb = Transport,
+ socket = Socket,
+ protocol_specific = #{current_cookie_secret := Secret,
+ previous_cookie_secret := PSecret}} = State0) ->
+ {ok, {IP, Port}} = dtls_socket:peername(Transport, Socket),
+ case dtls_handshake:cookie(Secret, IP, Port, Hello) of
+ Cookie ->
+ handle_client_hello(Hello, State0);
+ _ ->
+ case dtls_handshake:cookie(PSecret, IP, Port, Hello) of
+ Cookie ->
+ handle_client_hello(Hello, State0);
+ _ ->
+ %% Handle bad cookie as new cookie request RFC 6347 4.1.2
+ hello(internal, Hello#client_hello{cookie = <<>>}, State0)
+ end
+ end;
hello(internal, #server_hello{} = Hello,
#state{connection_states = ConnectionStates0,
negotiated_version = ReqVersion,
@@ -577,6 +587,11 @@ hello(state_timeout, Event, State) ->
hello(Type, Event, State) ->
gen_handshake(?FUNCTION_NAME, Type, Event, State).
+user_hello(enter, _, State) ->
+ {keep_state, State};
+user_hello(Type, Event, State) ->
+ gen_handshake(?FUNCTION_NAME, Type, Event, State).
+
%%--------------------------------------------------------------------
-spec abbreviated(gen_statem:event_type(), term(), #state{}) ->
gen_statem:state_function_result().
diff --git a/lib/ssl/src/dtls_handshake.erl b/lib/ssl/src/dtls_handshake.erl
index 6071eece13..1a415a5f76 100644
--- a/lib/ssl/src/dtls_handshake.erl
+++ b/lib/ssl/src/dtls_handshake.erl
@@ -174,7 +174,9 @@ handle_client_hello(Version,
signature_algs = ClientHashSigns}
= HelloExt},
#ssl_options{versions = Versions,
- signature_algs = SupportedHashSigns} = SslOpts,
+ signature_algs = SupportedHashSigns,
+ eccs = SupportedECCs,
+ honor_ecc_order = ECCOrder} = SslOpts,
{Port, Session0, Cache, CacheCb, ConnectionStates0, Cert, _},
Renegotiation) ->
case dtls_record:is_acceptable_version(Version, Versions) of
@@ -182,7 +184,7 @@ handle_client_hello(Version,
TLSVersion = dtls_v1:corresponding_tls_version(Version),
AvailableHashSigns = ssl_handshake:available_signature_algs(
ClientHashSigns, SupportedHashSigns, Cert,TLSVersion),
- ECCCurve = ssl_handshake:select_curve(Curves, ssl_handshake:supported_ecc(TLSVersion)),
+ ECCCurve = ssl_handshake:select_curve(Curves, SupportedECCs, ECCOrder),
{Type, #session{cipher_suite = CipherSuite} = Session1}
= ssl_handshake:select_session(SugesstedId, CipherSuites,
AvailableHashSigns, Compressions,
diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl
index 4efd13a6fa..5b6d92ebf4 100644
--- a/lib/ssl/src/ssl.erl
+++ b/lib/ssl/src/ssl.erl
@@ -32,7 +32,9 @@
%% Socket handling
-export([connect/3, connect/2, connect/4,
listen/2, transport_accept/1, transport_accept/2,
- ssl_accept/1, ssl_accept/2, ssl_accept/3,
+ handshake/1, handshake/2, handshake/3,
+ handshake_continue/3, handshake_cancel/1,
+ ssl_accept/1, ssl_accept/2, ssl_accept/3,
controlling_process/2, peername/1, peercert/1, sockname/1,
close/1, close/2, shutdown/2, recv/2, recv/3, send/2,
getopts/2, setopts/2, getstat/1, getstat/2
@@ -45,7 +47,7 @@
format_error/1, renegotiate/1, prf/5, negotiated_protocol/1,
connection_information/1, connection_information/2]).
%% Misc
--export([handle_options/2, tls_version/1]).
+-export([handle_options/2, tls_version/1, new_ssl_options/3]).
-include("ssl_api.hrl").
-include("ssl_internal.hrl").
@@ -170,23 +172,54 @@ transport_accept(#sslsocket{pid = {ListenSocket,
ok | {ok, #sslsocket{}} | {error, reason()}.
-spec ssl_accept(#sslsocket{} | port(), [ssl_option()] | [ssl_option()| transport_option()], timeout()) ->
- {ok, #sslsocket{}} | {error, reason()}.
+ ok | {ok, #sslsocket{}} | {error, reason()}.
%%
%% Description: Performs accept on an ssl listen socket. e.i. performs
%% ssl handshake.
%%--------------------------------------------------------------------
ssl_accept(ListenSocket) ->
- ssl_accept(ListenSocket, infinity).
+ ssl_accept(ListenSocket, [], infinity).
+ssl_accept(Socket, Timeout) when (is_integer(Timeout) andalso Timeout >= 0) or (Timeout == infinity) ->
+ ssl_accept(Socket, [], Timeout);
+ssl_accept(ListenSocket, SslOptions) when is_port(ListenSocket) ->
+ ssl_accept(ListenSocket, SslOptions, infinity);
+ssl_accept(Socket, Timeout) ->
+ ssl_accept(Socket, [], Timeout).
+ssl_accept(Socket, SslOptions, Timeout) when is_port(Socket) ->
+ handshake(Socket, SslOptions, Timeout);
+ssl_accept(Socket, SslOptions, Timeout) ->
+ case handshake(Socket, SslOptions, Timeout) of
+ {ok, _} ->
+ ok;
+ Error ->
+ Error
+ end.
+%%--------------------------------------------------------------------
+-spec handshake(#sslsocket{}) -> {ok, #sslsocket{}} | {error, reason()}.
+-spec handshake(#sslsocket{} | port(), timeout()| [ssl_option()
+ | transport_option()]) ->
+ {ok, #sslsocket{}} | {error, reason()}.
-ssl_accept(#sslsocket{} = Socket, Timeout) when (is_integer(Timeout) andalso Timeout >= 0) or (Timeout == infinity) ->
+-spec handshake(#sslsocket{} | port(), [ssl_option()] | [ssl_option()| transport_option()], timeout()) ->
+ {ok, #sslsocket{}} | {error, reason()}.
+%%
+%% Description: Performs accept on an ssl listen socket. e.i. performs
+%% ssl handshake.
+%%--------------------------------------------------------------------
+handshake(ListenSocket) ->
+ handshake(ListenSocket, infinity).
+
+handshake(#sslsocket{} = Socket, Timeout) when (is_integer(Timeout) andalso Timeout >= 0) or
+ (Timeout == infinity) ->
ssl_connection:handshake(Socket, Timeout);
-ssl_accept(ListenSocket, SslOptions) when is_port(ListenSocket) ->
- ssl_accept(ListenSocket, SslOptions, infinity).
+handshake(ListenSocket, SslOptions) when is_port(ListenSocket) ->
+ handshake(ListenSocket, SslOptions, infinity).
-ssl_accept(#sslsocket{} = Socket, [], Timeout) when (is_integer(Timeout) andalso Timeout >= 0) or (Timeout == infinity)->
- ssl_accept(Socket, Timeout);
-ssl_accept(#sslsocket{fd = {_, _, _, Tracker}} = Socket, SslOpts, Timeout) when
+handshake(#sslsocket{} = Socket, [], Timeout) when (is_integer(Timeout) andalso Timeout >= 0) or
+ (Timeout == infinity)->
+ handshake(Socket, Timeout);
+handshake(#sslsocket{fd = {_, _, _, Tracker}} = Socket, SslOpts, Timeout) when
(is_integer(Timeout) andalso Timeout >= 0) or (Timeout == infinity)->
try
{ok, EmOpts, _} = tls_socket:get_all_opts(Tracker),
@@ -195,7 +228,7 @@ ssl_accept(#sslsocket{fd = {_, _, _, Tracker}} = Socket, SslOpts, Timeout) when
catch
Error = {error, _Reason} -> Error
end;
-ssl_accept(#sslsocket{pid = Pid, fd = {_, _, _}} = Socket, SslOpts, Timeout) when
+handshake(#sslsocket{pid = Pid, fd = {_, _, _}} = Socket, SslOpts, Timeout) when
(is_integer(Timeout) andalso Timeout >= 0) or (Timeout == infinity)->
try
{ok, EmOpts, _} = dtls_udp_listener:get_all_opts(Pid),
@@ -204,8 +237,8 @@ ssl_accept(#sslsocket{pid = Pid, fd = {_, _, _}} = Socket, SslOpts, Timeout) whe
catch
Error = {error, _Reason} -> Error
end;
-ssl_accept(Socket, SslOptions, Timeout) when is_port(Socket),
- (is_integer(Timeout) andalso Timeout >= 0) or (Timeout == infinity) ->
+handshake(Socket, SslOptions, Timeout) when is_port(Socket),
+ (is_integer(Timeout) andalso Timeout >= 0) or (Timeout == infinity) ->
{Transport,_,_,_} =
proplists:get_value(cb_info, SslOptions, {gen_tcp, tcp, tcp_closed, tcp_error}),
EmulatedOptions = tls_socket:emulated_options(),
@@ -215,13 +248,31 @@ ssl_accept(Socket, SslOptions, Timeout) when is_port(Socket),
{ok, #config{transport_info = CbInfo, ssl = SslOpts, emulated = EmOpts}} ->
ok = tls_socket:setopts(Transport, Socket, tls_socket:internal_inet_values()),
{ok, Port} = tls_socket:port(Transport, Socket),
- ssl_connection:ssl_accept(ConnetionCb, Port, Socket,
- {SslOpts,
- tls_socket:emulated_socket_options(EmOpts, #socket_options{}), undefined},
- self(), CbInfo, Timeout)
+ ssl_connection:handshake(ConnetionCb, Port, Socket,
+ {SslOpts,
+ tls_socket:emulated_socket_options(EmOpts, #socket_options{}), undefined},
+ self(), CbInfo, Timeout)
catch
Error = {error, _Reason} -> Error
end.
+
+%%--------------------------------------------------------------------
+-spec handshake_continue(#sslsocket{}, [ssl_option()], timeout()) ->
+ {ok, #sslsocket{}} | {error, reason()}.
+%%
+%%
+%% Description: Continues the handshke possible with newly supplied options.
+%%--------------------------------------------------------------------
+handshake_continue(Socket, SSLOptions, Timeout) ->
+ ssl_connection:handshake_continue(Socket, SSLOptions, Timeout).
+%%--------------------------------------------------------------------
+-spec handshake_cancel(#sslsocket{}) -> term().
+%%
+%% Description: Cancels the handshakes sending a close alert.
+%%--------------------------------------------------------------------
+handshake_cancel(Socket) ->
+ ssl_connection:handshake_cancel(Socket).
+
%%--------------------------------------------------------------------
-spec close(#sslsocket{}) -> term().
%%
@@ -476,8 +527,9 @@ eccs() ->
eccs_filter_supported(Curves).
%%--------------------------------------------------------------------
--spec eccs(tls_record:tls_version() | tls_record:tls_atom_version()) ->
- tls_v1:curves().
+-spec eccs(tls_record:tls_version() | tls_record:tls_atom_version() |
+ dtls_record:dtls_version() | dtls_record:dtls_atom_version()) ->
+ tls_v1:curves().
%% Description: returns the curves supported for a given version of
%% ssl/tls.
%%--------------------------------------------------------------------
@@ -486,13 +538,16 @@ eccs({3,0}) ->
eccs({3,_}) ->
Curves = tls_v1:ecc_curves(all),
eccs_filter_supported(Curves);
-eccs({_,_} = DTLSVersion) ->
- eccs(dtls_v1:corresponding_tls_version(DTLSVersion));
-eccs(DTLSAtomVersion) when DTLSAtomVersion == 'dtlsv1';
- DTLSAtomVersion == 'dtlsv2' ->
- eccs(dtls_record:protocol_version(DTLSAtomVersion));
-eccs(AtomVersion) when is_atom(AtomVersion) ->
- eccs(tls_record:protocol_version(AtomVersion)).
+eccs({254,_} = Version) ->
+ eccs(dtls_v1:corresponding_tls_version(Version));
+eccs(Version) when Version == 'tlsv1.2';
+ Version == 'tlsv1.1';
+ Version == tlsv1;
+ Version == sslv3 ->
+ eccs(tls_record:protocol_version(Version));
+eccs(Version) when Version == 'dtlsv1.2';
+ Version == 'dtlsv1'->
+ eccs(dtls_v1:corresponding_tls_version(dtls_record:protocol_version(Version))).
eccs_filter_supported(Curves) ->
CryptoCurves = crypto:ec_curves(),
@@ -881,7 +936,8 @@ handle_options(Opts0, Role, Host) ->
client, Role),
crl_check = handle_option(crl_check, Opts, false),
crl_cache = handle_option(crl_cache, Opts, {ssl_crl_cache, {internal, []}}),
- max_handshake_size = handle_option(max_handshake_size, Opts, ?DEFAULT_MAX_HANDSHAKE_SIZE)
+ max_handshake_size = handle_option(max_handshake_size, Opts, ?DEFAULT_MAX_HANDSHAKE_SIZE),
+ handshake = handle_option(handshake, Opts, full)
},
CbInfo = proplists:get_value(cb_info, Opts, default_cb_info(Protocol)),
@@ -897,8 +953,7 @@ handle_options(Opts0, Role, Host) ->
client_preferred_next_protocols, log_alert,
server_name_indication, honor_cipher_order, padding_check, crl_check, crl_cache,
fallback, signature_algs, eccs, honor_ecc_order, beast_mitigation,
- max_handshake_size],
-
+ max_handshake_size, handshake],
SockOpts = lists:foldl(fun(Key, PropList) ->
proplists:delete(Key, PropList)
end, Opts, SslOptions),
@@ -910,8 +965,6 @@ handle_options(Opts0, Role, Host) ->
inet_user = Sock, transport_info = CbInfo, connection_cb = ConnetionCb
}}.
-
-
handle_option(OptionName, Opts, Default, Role, Role) ->
handle_option(OptionName, Opts, Default);
handle_option(_, _, undefined = Value, _, _) ->
@@ -1139,6 +1192,10 @@ validate_option(protocol, Value = tls) ->
Value;
validate_option(protocol, Value = dtls) ->
Value;
+validate_option(handshake, hello = Value) ->
+ Value;
+validate_option(handshake, full = Value) ->
+ Value;
validate_option(Opt, Value) ->
throw({error, {options, {Opt, Value}}}).
diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl
index f493c93726..3f8c1f97f9 100644
--- a/lib/ssl/src/ssl_connection.erl
+++ b/lib/ssl/src/ssl_connection.erl
@@ -37,7 +37,9 @@
-include_lib("public_key/include/public_key.hrl").
%% Setup
--export([connect/8, ssl_accept/7, handshake/2, handshake/3,
+
+-export([connect/8, handshake/7, handshake/2, handshake/3,
+ handshake_continue/3, handshake_cancel/1,
socket_control/4, socket_control/5, start_or_recv_cancel_timer/2]).
%% User Events
@@ -57,11 +59,11 @@
%% Help functions for tls|dtls_connection.erl
-export([handle_session/7, ssl_config/3,
- prepare_connection/2, hibernate_after/3]).
+ prepare_connection/2, hibernate_after/3, map_extensions/1]).
%% General gen_statem state functions with extra callback argument
%% to determine if it is an SSL/TLS or DTLS gen_statem machine
--export([init/4, error/4, hello/4, abbreviated/4, certify/4, cipher/4,
+-export([init/4, error/4, hello/4, user_hello/4, abbreviated/4, certify/4, cipher/4,
connection/4, death_row/4, downgrade/4]).
%% gen_statem callbacks
@@ -93,7 +95,7 @@ connect(Connection, Host, Port, Socket, Options, User, CbInfo, Timeout) ->
{error, ssl_not_started}
end.
%%--------------------------------------------------------------------
--spec ssl_accept(tls_connection | dtls_connection,
+-spec handshake(tls_connection | dtls_connection,
inet:port_number(), port(),
{#ssl_options{}, #socket_options{}, undefined | pid()},
pid(), tuple(), timeout()) ->
@@ -102,7 +104,7 @@ connect(Connection, Host, Port, Socket, Options, User, CbInfo, Timeout) ->
%% Description: Performs accept on an ssl listen socket. e.i. performs
%% ssl handshake.
%%--------------------------------------------------------------------
-ssl_accept(Connection, Port, Socket, Opts, User, CbInfo, Timeout) ->
+handshake(Connection, Port, Socket, Opts, User, CbInfo, Timeout) ->
try Connection:start_fsm(server, "localhost", Port, Socket, Opts, User,
CbInfo, Timeout)
catch
@@ -111,32 +113,60 @@ ssl_accept(Connection, Port, Socket, Opts, User, CbInfo, Timeout) ->
end.
%%--------------------------------------------------------------------
--spec handshake(#sslsocket{}, timeout()) -> ok | {error, reason()}.
+-spec handshake(#sslsocket{}, timeout()) -> {ok, #sslsocket{}} |
+ {ok, #sslsocket{}, map()}| {error, reason()}.
%%
%% Description: Starts ssl handshake.
%%--------------------------------------------------------------------
-handshake(#sslsocket{pid = Pid}, Timeout) ->
+handshake(#sslsocket{pid = Pid} = Socket, Timeout) ->
case call(Pid, {start, Timeout}) of
connected ->
- ok;
+ {ok, Socket};
+ {ok, Ext} ->
+ {ok, Socket, Ext};
Error ->
Error
end.
%%--------------------------------------------------------------------
-spec handshake(#sslsocket{}, {#ssl_options{},#socket_options{}},
- timeout()) -> ok | {error, reason()}.
+ timeout()) -> {ok, #sslsocket{}} | {error, reason()}.
%%
%% Description: Starts ssl handshake with some new options
%%--------------------------------------------------------------------
-handshake(#sslsocket{pid = Pid}, SslOptions, Timeout) ->
+handshake(#sslsocket{pid = Pid} = Socket, SslOptions, Timeout) ->
case call(Pid, {start, SslOptions, Timeout}) of
connected ->
- ok;
+ {ok, Socket};
Error ->
Error
end.
+%%--------------------------------------------------------------------
+-spec handshake_continue(#sslsocket{}, [ssl_option()],
+ timeout()) -> {ok, #sslsocket{}}| {error, reason()}.
+%%
+%% Description: Continues handshake with new options
+%%--------------------------------------------------------------------
+handshake_continue(#sslsocket{pid = Pid} = Socket, SslOptions, Timeout) ->
+ case call(Pid, {handshake_continue, SslOptions, Timeout}) of
+ connected ->
+ {ok, Socket};
+ Error ->
+ Error
+ end.
+%%--------------------------------------------------------------------
+-spec handshake_cancel(#sslsocket{}) -> ok | {error, reason()}.
+%%
+%% Description: Cancels connection
+%%--------------------------------------------------------------------
+handshake_cancel(#sslsocket{pid = Pid}) ->
+ case call(Pid, cancel) of
+ closed ->
+ ok;
+ Error ->
+ Error
+ end.
%--------------------------------------------------------------------
-spec socket_control(tls_connection | dtls_connection, port(), pid(), atom()) ->
{ok, #sslsocket{}} | {error, reason()}.
@@ -527,6 +557,9 @@ handle_session(#server_hello{cipher_suite = CipherSuite,
-spec ssl_config(#ssl_options{}, client | server, #state{}) -> #state{}.
%%--------------------------------------------------------------------
ssl_config(Opts, Role, State) ->
+ ssl_config(Opts, Role, State, new).
+
+ssl_config(Opts, Role, State0, Type) ->
{ok, #{cert_db_ref := Ref,
cert_db_handle := CertDbHandle,
fileref_db_handle := FileRefHandle,
@@ -536,20 +569,26 @@ ssl_config(Opts, Role, State) ->
dh_params := DHParams,
own_certificate := OwnCert}} =
ssl_config:init(Opts, Role),
- Handshake = ssl_handshake:init_handshake_history(),
TimeStamp = erlang:monotonic_time(),
- Session = State#state.session,
- State#state{tls_handshake_history = Handshake,
- session = Session#session{own_certificate = OwnCert,
- time_stamp = TimeStamp},
- file_ref_db = FileRefHandle,
- cert_db_ref = Ref,
- cert_db = CertDbHandle,
- crl_db = CRLDbHandle,
- session_cache = CacheHandle,
- private_key = Key,
- diffie_hellman_params = DHParams,
- ssl_options = Opts}.
+ Session = State0#state.session,
+ State = State0#state{session = Session#session{own_certificate = OwnCert,
+ time_stamp = TimeStamp},
+ file_ref_db = FileRefHandle,
+ cert_db_ref = Ref,
+ cert_db = CertDbHandle,
+ crl_db = CRLDbHandle,
+ session_cache = CacheHandle,
+ private_key = Key,
+ diffie_hellman_params = DHParams,
+ ssl_options = Opts},
+ case Type of
+ new ->
+ Handshake = ssl_handshake:init_handshake_history(),
+ State#state{tls_handshake_history = Handshake};
+ continue ->
+ State
+ end.
+
%%====================================================================
%% gen_statem general state functions with connection cb argument
@@ -579,7 +618,8 @@ init({call, From}, {start, {Opts, EmOpts}, Timeout},
end,
State = ssl_config(SslOpts, Role, State0),
init({call, From}, {start, Timeout},
- State#state{ssl_options = SslOpts, socket_options = new_emulated(EmOpts, SockOpts)}, Connection)
+ State#state{ssl_options = SslOpts,
+ socket_options = new_emulated(EmOpts, SockOpts)}, Connection)
catch throw:Error ->
stop_and_reply(normal, {reply, From, {error, Error}}, State0)
end;
@@ -612,6 +652,23 @@ hello(info, Msg, State, _) ->
hello(Type, Msg, State, Connection) ->
handle_common_event(Type, Msg, ?FUNCTION_NAME, State, Connection).
+user_hello({call, From}, cancel, #state{negotiated_version = Version} = State, _) ->
+ gen_statem:reply(From, ok),
+ handle_own_alert(?ALERT_REC(?FATAL, ?USER_CANCELED, user_canceled),
+ Version, ?FUNCTION_NAME, State);
+user_hello({call, From}, {handshake_continue, NewOptions, Timeout}, #state{hello = Hello,
+ role = Role,
+ start_or_recv_from = RecvFrom,
+ ssl_options = Options0} = State0, _Connection) ->
+ Timer = start_or_recv_cancel_timer(Timeout, RecvFrom),
+ Options = ssl:handle_options(NewOptions, Options0#ssl_options{handshake = full}),
+ State = ssl_config(Options, Role, State0, continue),
+ {next_state, hello, State#state{start_or_recv_from = From,
+ timer = Timer},
+ [{next_event, internal, Hello}]};
+user_hello(_, _, _, _) ->
+ {keep_state_and_data, [postpone]}.
+
%%--------------------------------------------------------------------
-spec abbreviated(gen_statem:event_type(),
#hello_request{} | #finished{} | term(),
@@ -770,6 +827,14 @@ certify(internal, #certificate_request{},
Alg == srp_dss; Alg == srp_rsa; Alg == srp_anon ->
handle_own_alert(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE),
Version, ?FUNCTION_NAME, State);
+certify(internal, #certificate_request{},
+ #state{session = #session{own_certificate = undefined},
+ role = client} = State0, Connection) ->
+ %% The client does not have a certificate and will send an empty reply, the server may fail
+ %% or accept the connection by its own preference. No signature algorihms needed as there is
+ %% no certificate to verify.
+ {Record, State} = Connection:next_record(State0),
+ Connection:next_event(?FUNCTION_NAME, Record, State#state{client_certificate_requested = true});
certify(internal, #certificate_request{} = CertRequest,
#state{session = #session{own_certificate = Cert},
role = client,
@@ -2285,7 +2350,24 @@ hibernate_after(connection = StateName,
{next_state, StateName, State, [{timeout, HibernateAfter, hibernate} | Actions]};
hibernate_after(StateName, State, Actions) ->
{next_state, StateName, State, Actions}.
-
+
+map_extensions(#hello_extensions{renegotiation_info = RenegotiationInfo,
+ signature_algs = SigAlg,
+ alpn = Alpn,
+ next_protocol_negotiation = Next,
+ srp = SRP,
+ ec_point_formats = ECPointFmt,
+ elliptic_curves = ECCCurves,
+ sni = SNI}) ->
+ #{renegotiation_info => ssl_handshake:extension_value(RenegotiationInfo),
+ signature_algs => ssl_handshake:extension_value(SigAlg),
+ alpn => ssl_handshake:extension_value(Alpn),
+ srp => ssl_handshake:extension_value(SRP),
+ next_protocol => ssl_handshake:extension_value(Next),
+ ec_point_formats => ssl_handshake:extension_value(ECPointFmt),
+ elliptic_curves => ssl_handshake:extension_value(ECCCurves),
+ sni => ssl_handshake:extension_value(SNI)}.
+
terminate_alert(normal, Version, ConnectionStates, Connection) ->
Connection:encode_alert(?ALERT_REC(?WARNING, ?CLOSE_NOTIFY),
Version, ConnectionStates);
diff --git a/lib/ssl/src/ssl_connection.hrl b/lib/ssl/src/ssl_connection.hrl
index f9d2149170..a61ff747ae 100644
--- a/lib/ssl/src/ssl_connection.hrl
+++ b/lib/ssl/src/ssl_connection.hrl
@@ -61,7 +61,7 @@
client_certificate_requested = false :: boolean(),
key_algorithm :: ssl_cipher:key_algo(),
hashsign_algorithm = {undefined, undefined},
- cert_hashsign_algorithm,
+ cert_hashsign_algorithm = {undefined, undefined},
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(),
@@ -77,7 +77,8 @@
renegotiation :: undefined | {boolean(), From::term() | internal | peer},
start_or_recv_from :: term(),
timer :: undefined | reference(), % start_or_recive_timer
- %%send_queue :: queue:queue(),
+ %%send_queue :: queue:queue(),
+ hello, %%:: #client_hello{} | #server_hello{},
terminated = false ::boolean(),
allow_renegotiate = true ::boolean(),
expecting_next_protocol_negotiation = false ::boolean(),
@@ -88,11 +89,11 @@
sni_hostname = undefined,
downgrade,
flight_buffer = [] :: list() | map(), %% Buffer of TLS/DTLS records, used during the TLS handshake
- %% to when possible pack more than on TLS record into the
- %% underlaying packet format. Introduced by DTLS - RFC 4347.
- %% The mecahnism is also usefull in TLS although we do not
- %% need to worry about packet loss in TLS. In DTLS we need to track DTLS handshake seqnr
- flight_state = reliable, %% reliable | {retransmit, integer()}| {waiting, ref(), integer()} - last two is used in DTLS over udp.
+ %% to when possible pack more than one TLS record into the
+ %% underlaying packet format. Introduced by DTLS - RFC 4347.
+ %% The mecahnism is also usefull in TLS although we do not
+ %% need to worry about packet loss in TLS. In DTLS we need to track DTLS handshake seqnr
+ flight_state = reliable, %% reliable | {retransmit, integer()}| {waiting, ref(), integer()} - last two is used in DTLS over udp.
protocol_specific = #{} :: map()
}).
-define(DEFAULT_DIFFIE_HELLMAN_PARAMS,
diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl
index e1f813ea95..8ddd4623c1 100644
--- a/lib/ssl/src/ssl_handshake.erl
+++ b/lib/ssl/src/ssl_handshake.erl
@@ -53,7 +53,7 @@
-export([certify/7, certificate_verify/6, verify_signature/5,
master_secret/4, server_key_exchange_hash/2, verify_connection/6,
init_handshake_history/0, update_handshake_history/2, verify_server_key/5,
- select_version/3
+ select_version/3, extension_value/1
]).
%% Encode
@@ -139,8 +139,8 @@ certificate(OwnCert, CertDbHandle, CertDbRef, server) ->
case ssl_certificate:certificate_chain(OwnCert, CertDbHandle, CertDbRef) of
{ok, _, Chain} ->
#certificate{asn1_certificates = Chain};
- {error, _} ->
- ?ALERT_REC(?FATAL, ?INTERNAL_ERROR, server_has_no_suitable_certificates)
+ {error, Error} ->
+ ?ALERT_REC(?FATAL, ?INTERNAL_ERROR, {server_has_no_suitable_certificates, Error})
end.
%%--------------------------------------------------------------------
@@ -1091,12 +1091,6 @@ select_hashsign(_, Cert, _, _, Version) ->
%%
%% Description: Handles signature algorithms selection for certificate requests (client)
%%--------------------------------------------------------------------
-select_hashsign(#certificate_request{}, undefined, _, {Major, Minor}) when Major >= 3 andalso Minor >= 3->
- %% There client does not have a certificate and will send an empty reply, the server may fail
- %% or accept the connection by its own preference. No signature algorihms needed as there is
- %% no certificate to verify.
- {undefined, undefined};
-
select_hashsign(#certificate_request{hashsign_algorithms = #hash_sign_algos{hash_sign_algos = HashSigns},
certificate_types = Types}, Cert, SupportedHashSigns,
{Major, Minor}) when Major >= 3 andalso Minor >= 3->
@@ -1166,6 +1160,25 @@ srp_user(#ssl_options{srp_identity = {UserName, _}}) ->
srp_user(_) ->
undefined.
+extension_value(undefined) ->
+ undefined;
+extension_value(#sni{hostname = HostName}) ->
+ HostName;
+extension_value(#ec_point_formats{ec_point_format_list = List}) ->
+ List;
+extension_value(#elliptic_curves{elliptic_curve_list = List}) ->
+ List;
+extension_value(#hash_sign_algos{hash_sign_algos = Algos}) ->
+ Algos;
+extension_value(#alpn{extension_data = Data}) ->
+ Data;
+extension_value(#next_protocol_negotiation{extension_data = Data}) ->
+ Data;
+extension_value(#srp{username = Name}) ->
+ Name;
+extension_value(#renegotiation_info{renegotiated_connection = Data}) ->
+ Data.
+
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
diff --git a/lib/ssl/src/ssl_internal.hrl b/lib/ssl/src/ssl_internal.hrl
index d354910f33..5df00de0e5 100644
--- a/lib/ssl/src/ssl_internal.hrl
+++ b/lib/ssl/src/ssl_internal.hrl
@@ -144,7 +144,8 @@
signature_algs,
eccs,
honor_ecc_order :: boolean(),
- max_handshake_size :: integer()
+ max_handshake_size :: integer(),
+ handshake
}).
-record(socket_options,
diff --git a/lib/ssl/src/tls_connection.erl b/lib/ssl/src/tls_connection.erl
index c35378f18f..ef84c5320e 100644
--- a/lib/ssl/src/tls_connection.erl
+++ b/lib/ssl/src/tls_connection.erl
@@ -62,7 +62,7 @@
%% gen_statem state functions
-export([init/3, error/3, downgrade/3, %% Initiation and take down states
- hello/3, certify/3, cipher/3, abbreviated/3, %% Handshake states
+ hello/3, user_hello/3, certify/3, cipher/3, abbreviated/3, %% Handshake states
connection/3, death_row/3]).
%% gen_statem callbacks
-export([callback_mode/0, terminate/3, code_change/4, format_status/2]).
@@ -80,8 +80,7 @@ start_fsm(Role, Host, Port, Socket, {#ssl_options{erl_dist = false},_, Tracker}
{ok, Pid} = tls_connection_sup:start_child([Role, Host, Port, Socket,
Opts, User, CbInfo]),
{ok, SslSocket} = ssl_connection:socket_control(?MODULE, Socket, Pid, CbModule, Tracker),
- ok = ssl_connection:handshake(SslSocket, Timeout),
- {ok, SslSocket}
+ ssl_connection:handshake(SslSocket, Timeout)
catch
error:{badmatch, {error, _} = Error} ->
Error
@@ -94,8 +93,7 @@ start_fsm(Role, Host, Port, Socket, {#ssl_options{erl_dist = true},_, Tracker} =
{ok, Pid} = tls_connection_sup:start_child_dist([Role, Host, Port, Socket,
Opts, User, CbInfo]),
{ok, SslSocket} = ssl_connection:socket_control(?MODULE, Socket, Pid, CbModule, Tracker),
- ok = ssl_connection:handshake(SslSocket, Timeout),
- {ok, SslSocket}
+ ssl_connection:handshake(SslSocket, Timeout)
catch
error:{badmatch, {error, _} = Error} ->
Error
@@ -454,6 +452,16 @@ error(_, _, _) ->
#state{}) ->
gen_statem:state_function_result().
%%--------------------------------------------------------------------
+hello(internal, #client_hello{extensions = Extensions} = Hello, #state{ssl_options = #ssl_options{handshake = hello},
+ start_or_recv_from = From} = State) ->
+ {next_state, user_hello, State#state{start_or_recv_from = undefined,
+ hello = Hello},
+ [{reply, From, {ok, ssl_connection:map_extensions(Extensions)}}]};
+hello(internal, #server_hello{extensions = Extensions} = Hello, #state{ssl_options = #ssl_options{handshake = hello},
+ start_or_recv_from = From} = State) ->
+ {next_state, user_hello, State#state{start_or_recv_from = undefined,
+ hello = Hello},
+ [{reply, From, {ok, ssl_connection:map_extensions(Extensions)}}]};
hello(internal, #client_hello{client_version = ClientVersion} = Hello,
#state{connection_states = ConnectionStates0,
port = Port, session = #session{own_certificate = Cert} = Session0,
@@ -463,7 +471,6 @@ hello(internal, #client_hello{client_version = ClientVersion} = Hello,
negotiated_protocol = CurrentProtocol,
key_algorithm = KeyExAlg,
ssl_options = SslOpts} = State) ->
-
case tls_handshake:hello(Hello, SslOpts, {Port, Session0, Cache, CacheCb,
ConnectionStates0, Cert, KeyExAlg}, Renegotiation) of
#alert{} = Alert ->
@@ -482,7 +489,7 @@ hello(internal, #client_hello{client_version = ClientVersion} = Hello,
session = Session,
negotiated_protocol = Protocol})
end;
-hello(internal, #server_hello{} = Hello,
+hello(internal, #server_hello{} = Hello,
#state{connection_states = ConnectionStates0,
negotiated_version = ReqVersion,
role = client,
@@ -500,6 +507,9 @@ hello(info, Event, State) ->
hello(Type, Event, State) ->
gen_handshake(?FUNCTION_NAME, Type, Event, State).
+user_hello(Type, Event, State) ->
+ gen_handshake(?FUNCTION_NAME, Type, Event, State).
+
%%--------------------------------------------------------------------
-spec abbreviated(gen_statem:event_type(), term(), #state{}) ->
gen_statem:state_function_result().
@@ -746,7 +756,7 @@ gen_handshake(StateName, Type, Event,
malformed_handshake_data),
Version, StateName, State)
end.
-
+
gen_info(Event, connection = StateName, #state{negotiated_version = Version} = State) ->
try handle_info(Event, StateName, State) of
Result ->
diff --git a/lib/ssl/test/Makefile b/lib/ssl/test/Makefile
index 9347b56f39..845f5bee2e 100644
--- a/lib/ssl/test/Makefile
+++ b/lib/ssl/test/Makefile
@@ -57,6 +57,8 @@ MODULES = \
ssl_session_cache_SUITE \
ssl_to_openssl_SUITE \
ssl_ECC_SUITE \
+ ssl_ECC_openssl_SUITE \
+ ssl_ECC\
ssl_upgrade_SUITE\
ssl_sni_SUITE \
make_certs\
diff --git a/lib/ssl/test/ssl_ECC.erl b/lib/ssl/test/ssl_ECC.erl
new file mode 100644
index 0000000000..489a72e50e
--- /dev/null
+++ b/lib/ssl/test/ssl_ECC.erl
@@ -0,0 +1,154 @@
+
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2018-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.
+%% 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(ssl_ECC).
+
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+-include_lib("public_key/include/public_key.hrl").
+
+%% Test diffrent certificate chain types, note that it is the servers
+%% chain that affect what cipher suit that will be choosen
+
+%% ECDH_RSA
+client_ecdh_rsa_server_ecdh_rsa(Config) when is_list(Config) ->
+ Suites = all_rsa_suites(Config),
+ Default = ssl_test_lib:default_cert_chain_conf(),
+ {COpts, SOpts} = ssl_test_lib:make_ec_cert_chains([{server_chain, Default},
+ {client_chain, Default}],
+ ecdh_rsa, ecdh_rsa, Config),
+ ssl_test_lib:basic_test(ssl_test_lib:ssl_options(COpts, Config),
+ ssl_test_lib:ssl_options(SOpts, Config),
+ [{check_keyex, ecdh_rsa}, {ciphers, Suites} | proplists:delete(check_keyex, Config)]).
+client_ecdhe_rsa_server_ecdh_rsa(Config) when is_list(Config) ->
+ Suites = all_rsa_suites(Config),
+ Default = ssl_test_lib:default_cert_chain_conf(),
+ {COpts, SOpts} = ssl_test_lib:make_ec_cert_chains([{server_chain, Default},
+ {client_chain, Default}],
+ ecdhe_rsa, ecdh_rsa, Config),
+ ssl_test_lib:basic_test(ssl_test_lib:ssl_options(COpts, Config),
+ ssl_test_lib:ssl_options(SOpts, Config),
+ [{check_keyex, ecdh_rsa}, {ciphers, Suites} | proplists:delete(check_keyex, Config)]).
+client_ecdhe_ecdsa_server_ecdh_rsa(Config) when is_list(Config) ->
+ Suites = all_rsa_suites(Config),
+ Default = ssl_test_lib:default_cert_chain_conf(),
+ {COpts, SOpts} = ssl_test_lib:make_ec_cert_chains([{server_chain, Default},
+ {client_chain, Default}],
+ ecdhe_ecdsa, ecdh_rsa, Config),
+ ssl_test_lib:basic_test(ssl_test_lib:ssl_options(COpts, Config),
+ ssl_test_lib:ssl_options(SOpts, Config),
+ [{check_keyex, ecdh_rsa}, {ciphers, Suites} | proplists:delete(check_keyex, Config)]).
+
+%% ECDHE_RSA
+client_ecdh_rsa_server_ecdhe_rsa(Config) when is_list(Config) ->
+ Default = ssl_test_lib:default_cert_chain_conf(),
+ {COpts, SOpts} = ssl_test_lib:make_ec_cert_chains([{server_chain, Default},
+ {client_chain, Default}],
+ ecdh_rsa, ecdhe_rsa, Config),
+ ssl_test_lib:basic_test(ssl_test_lib:ssl_options(COpts, Config),
+ ssl_test_lib:ssl_options(SOpts, Config),
+ [{check_keyex, ecdhe_rsa} | proplists:delete(check_keyex, Config)]).
+client_ecdhe_rsa_server_ecdhe_rsa(Config) when is_list(Config) ->
+ Default = ssl_test_lib:default_cert_chain_conf(),
+ {COpts, SOpts} = ssl_test_lib:make_ec_cert_chains([{server_chain, Default},
+ {client_chain, Default}],
+ ecdhe_rsa, ecdhe_rsa, Config),
+ ssl_test_lib:basic_test(ssl_test_lib:ssl_options(COpts, Config),
+ ssl_test_lib:ssl_options(SOpts, Config),
+ [{check_keyex, ecdhe_rsa} | proplists:delete(check_keyex, Config)]).
+client_ecdhe_ecdsa_server_ecdhe_rsa(Config) when is_list(Config) ->
+ Default = ssl_test_lib:default_cert_chain_conf(),
+ {COpts, SOpts} = ssl_test_lib:make_ec_cert_chains([{server_chain, Default},
+ {client_chain, Default}],
+ ecdh_ecdsa, ecdhe_rsa, Config),
+ ssl_test_lib:basic_test(ssl_test_lib:ssl_options(COpts, Config),
+ ssl_test_lib:ssl_options(SOpts, Config),
+ [{check_keyex, ecdhe_rsa} | proplists:delete(check_keyex, Config)]).
+
+%% ECDH_ECDSA
+client_ecdh_ecdsa_server_ecdh_ecdsa(Config) when is_list(Config) ->
+ Ext = x509_test:extensions([{key_usage, [keyEncipherment]}]),
+ {COpts, SOpts} = ssl_test_lib:make_ec_cert_chains([{server_chain,
+ [[], [], [{extensions, Ext}]]},
+ {client_chain,
+ ssl_test_lib:default_cert_chain_conf()}],
+ ecdh_ecdsa, ecdh_ecdsa, Config),
+ ssl_test_lib:basic_test(ssl_test_lib:ssl_options(COpts, Config),
+ ssl_test_lib:ssl_options(SOpts, Config),
+ [{check_keyex, ecdh_ecdsa} | proplists:delete(check_keyex, Config)]).
+client_ecdhe_rsa_server_ecdh_ecdsa(Config) when is_list(Config) ->
+ Ext = x509_test:extensions([{key_usage, [keyEncipherment]}]),
+ {COpts, SOpts} = ssl_test_lib:make_ec_cert_chains([{server_chain,
+ [[], [], [{extensions, Ext}]]},
+ {client_chain,
+ ssl_test_lib:default_cert_chain_conf()}],
+ ecdhe_rsa, ecdh_ecdsa, Config),
+ ssl_test_lib:basic_test(ssl_test_lib:ssl_options(COpts, Config),
+ ssl_test_lib:ssl_options(SOpts, Config),
+ [{check_keyex, ecdh_ecdsa} | proplists:delete(check_keyex, Config)]).
+
+client_ecdhe_ecdsa_server_ecdh_ecdsa(Config) when is_list(Config) ->
+ Ext = x509_test:extensions([{key_usage, [keyEncipherment]}]),
+ {COpts, SOpts} = ssl_test_lib:make_ec_cert_chains([{server_chain,
+ [[], [], [{extensions, Ext}]]},
+ {client_chain,
+ ssl_test_lib:default_cert_chain_conf()}],
+ ecdhe_ecdsa, ecdh_ecdsa, Config),
+ ssl_test_lib:basic_test(ssl_test_lib:ssl_options(COpts, Config),
+ ssl_test_lib:ssl_options(SOpts, Config),
+ [{check_keyex, ecdh_ecdsa} | proplists:delete(check_keyex, Config)]).
+
+%% ECDHE_ECDSA
+client_ecdh_rsa_server_ecdhe_ecdsa(Config) when is_list(Config) ->
+ Default = ssl_test_lib:default_cert_chain_conf(),
+ {COpts, SOpts} = ssl_test_lib:make_ec_cert_chains([{server_chain, Default},
+ {client_chain, Default}],
+ ecdh_rsa, ecdhe_ecdsa, Config),
+ ssl_test_lib:basic_test(ssl_test_lib:ssl_options(COpts, Config),
+ ssl_test_lib:ssl_options(SOpts, Config),
+ [{check_keyex, ecdhe_ecdsa} | proplists:delete(check_keyex, Config)]).
+client_ecdh_ecdsa_server_ecdhe_ecdsa(Config) when is_list(Config) ->
+ Default = ssl_test_lib:default_cert_chain_conf(),
+ {COpts, SOpts} = ssl_test_lib:make_ec_cert_chains([{server_chain, Default},
+ {client_chain, Default}],
+ ecdh_ecdsa, ecdhe_ecdsa, Config),
+ ssl_test_lib:basic_test(ssl_test_lib:ssl_options(COpts, Config),
+ ssl_test_lib:ssl_options(SOpts, Config),
+ [{check_keyex, ecdhe_ecdsa} | proplists:delete(check_keyex, Config)]).
+client_ecdhe_ecdsa_server_ecdhe_ecdsa(Config) when is_list(Config) ->
+ Default = ssl_test_lib:default_cert_chain_conf(),
+ {COpts, SOpts} = ssl_test_lib:make_ec_cert_chains([{server_chain, Default},
+ {client_chain, Default}],
+ ecdhe_ecdsa, ecdhe_ecdsa, Config),
+ ssl_test_lib:basic_test(ssl_test_lib:ssl_options(COpts, Config),
+ ssl_test_lib:ssl_options(SOpts, Config),
+ [{check_keyex, ecdhe_ecdsa} | proplists:delete(check_keyex, Config)]).
+
+all_rsa_suites(Config) ->
+ Version = proplists:get_value(tls_version, Config),
+ All = ssl:cipher_suites(all, Version),
+ Default = ssl:cipher_suites(default, Version),
+ RSASuites = ssl:filter_cipher_suites(All,[{key_exchange, fun(rsa) -> true;(_) -> false end}]),
+ ssl:append_cipher_suites(RSASuites, Default).
diff --git a/lib/ssl/test/ssl_ECC_SUITE.erl b/lib/ssl/test/ssl_ECC_SUITE.erl
index f38c0a7416..6e2d86571a 100644
--- a/lib/ssl/test/ssl_ECC_SUITE.erl
+++ b/lib/ssl/test/ssl_ECC_SUITE.erl
@@ -43,52 +43,17 @@ all() ->
groups() ->
[
- {'tlsv1.2', [], all_versions_groups()},
- {'tlsv1.1', [], all_versions_groups()},
- {'tlsv1', [], all_versions_groups()},
- {'dtlsv1.2', [], all_versions_groups()},
- {'dtlsv1', [], all_versions_groups()},
- {'erlang_server', [], openssl_key_cert_combinations()},
- %%{'erlang_client', [], openssl_key_cert_combinations()},
- {'erlang', [], key_cert_combinations() ++ misc()
- ++ ecc_negotiation()}
+ {'tlsv1.2', [], test_cases()},
+ {'tlsv1.1', [], test_cases()},
+ {'tlsv1', [], test_cases()},
+ {'dtlsv1.2', [], test_cases()},
+ {'dtlsv1', [], test_cases()}
].
-all_versions_groups ()->
- [{group, 'erlang_server'},
- %%{group, 'erlang_client'},
- {group, 'erlang'}
- ].
-
-
-openssl_key_cert_combinations() ->
- ECDH_RSA = case ssl_test_lib:openssl_filter("ECDH-RSA") of
- [] ->
- [];
- _ ->
- server_ecdh_rsa()
- end,
-
- ECDHE_RSA = case ssl_test_lib:openssl_filter("ECDHE-RSA") of
- [] ->
- [];
- _ ->
- server_ecdhe_rsa()
- end,
- ECDH_ECDSA = case ssl_test_lib:openssl_filter("ECDH-ECDSA") of
- [] ->
- [];
- _ ->
- server_ecdhe_ecdsa()
- end,
-
- ECDHE_ECDSA = case ssl_test_lib:openssl_filter("ECDHE-ECDSA") of
- [] ->
- [];
- _ ->
- server_ecdhe_ecdsa()
- end,
- ECDH_RSA ++ ECDHE_RSA ++ ECDH_ECDSA ++ ECDHE_ECDSA.
+test_cases()->
+ key_cert_combinations()
+ ++ misc()
+ ++ ecc_negotiation().
key_cert_combinations() ->
server_ecdh_rsa() ++
@@ -116,7 +81,6 @@ server_ecdhe_ecdsa() ->
client_ecdh_ecdsa_server_ecdhe_ecdsa,
client_ecdhe_ecdsa_server_ecdhe_ecdsa].
-
misc()->
[client_ecdsa_server_ecdsa_with_raw_key].
@@ -142,9 +106,14 @@ init_per_suite(Config0) ->
end_per_suite(Config0),
try crypto:start() of
ok ->
- Config0
+ case ssl_test_lib:sufficient_crypto_support(cipher_ec) of
+ true ->
+ Config0;
+ false ->
+ {skip, "Crypto does not support ECC"}
+ end
catch _:_ ->
- {skip, "Crypto did not start"}
+ {skip, "Crypto did not start"}
end.
end_per_suite(_Config) ->
@@ -152,52 +121,14 @@ end_per_suite(_Config) ->
application:stop(crypto).
%%--------------------------------------------------------------------
-init_per_group(erlang_client = Group, Config) ->
- case ssl_test_lib:is_sane_ecc(openssl) of
- true ->
- common_init_per_group(Group, [{server_type, openssl},
- {client_type, erlang} | Config]);
- false ->
- {skip, "Known ECC bug in openssl"}
- end;
-
-init_per_group(erlang_server = Group, Config) ->
- case ssl_test_lib:is_sane_ecc(openssl) of
- true ->
- common_init_per_group(Group, [{server_type, erlang},
- {client_type, openssl} | Config]);
- false ->
- {skip, "Known ECC bug in openssl"}
- end;
-
-init_per_group(erlang = Group, Config) ->
- case ssl_test_lib:sufficient_crypto_support(Group) of
- true ->
- common_init_per_group(Group, [{server_type, erlang},
- {client_type, erlang} | Config]);
- false ->
- {skip, "Crypto does not support ECC"}
- end;
-
-init_per_group(openssl = Group, Config) ->
- case ssl_test_lib:sufficient_crypto_support(Group) of
- true ->
- common_init_per_group(Group, [{server_type, openssl},
- {client_type, openssl} | Config]);
- false ->
- {skip, "Crypto does not support ECC"}
- end;
-
-init_per_group(Group, Config) ->
- common_init_per_group(Group, Config).
-
-common_init_per_group(GroupName, Config) ->
+init_per_group(GroupName, Config) ->
case ssl_test_lib:is_tls_version(GroupName) of
true ->
- Config0 = ssl_test_lib:init_tls_version(GroupName, Config),
- [{tls_version, GroupName} | Config0];
- _ ->
- openssl_check(GroupName, Config)
+ [{tls_version, GroupName},
+ {server_type, erlang},
+ {client_type, erlang} | ssl_test_lib:init_tls_version(GroupName, Config)];
+ _ ->
+ Config
end.
end_per_group(GroupName, Config0) ->
@@ -215,7 +146,7 @@ init_per_testcase(TestCase, Config) ->
ssl_test_lib:ct_log_supported_protocol_versions(Config),
ct:log("Ciphers: ~p~n ", [ ssl:cipher_suites()]),
end_per_testcase(TestCase, Config),
- ssl_test_lib:clean_start(),
+ ssl:start(),
ct:timetrap({seconds, 15}),
Config.
@@ -226,104 +157,45 @@ end_per_testcase(_TestCase, Config) ->
%%--------------------------------------------------------------------
%% Test Cases --------------------------------------------------------
%%--------------------------------------------------------------------
-
%% Test diffrent certificate chain types, note that it is the servers
%% chain that affect what cipher suit that will be choosen
%% ECDH_RSA
client_ecdh_rsa_server_ecdh_rsa(Config) when is_list(Config) ->
- Default = ssl_test_lib:default_cert_chain_conf(),
- {COpts, SOpts} = ssl_test_lib:make_ec_cert_chains([{server_chain, Default},
- {client_chain, Default}],
- ecdh_rsa, ecdh_rsa, Config),
- basic_test(COpts, SOpts, [{check_keyex, ecdh_rsa} | proplists:delete(check_keyex, Config)]).
-client_ecdhe_rsa_server_ecdh_rsa(Config) when is_list(Config) ->
- Default = ssl_test_lib:default_cert_chain_conf(),
- {COpts, SOpts} = ssl_test_lib:make_ec_cert_chains([{server_chain, Default},
- {client_chain, Default}],
- ecdhe_rsa, ecdh_rsa, Config),
- basic_test(COpts, SOpts, [{check_keyex, ecdh_rsa} | proplists:delete(check_keyex, Config)]).
-client_ecdhe_ecdsa_server_ecdh_rsa(Config) when is_list(Config) ->
- Default = ssl_test_lib:default_cert_chain_conf(),
- {COpts, SOpts} = ssl_test_lib:make_ec_cert_chains([{server_chain, Default},
- {client_chain, Default}],
- ecdhe_ecdsa, ecdh_rsa, Config),
- basic_test(COpts, SOpts, [{check_keyex, ecdh_rsa} | proplists:delete(check_keyex, Config)]).
-
+ ssl_ECC:client_ecdh_rsa_server_ecdh_rsa(Config).
+client_ecdhe_rsa_server_ecdh_rsa(Config) when is_list(Config) ->
+ ssl_ECC:client_ecdhe_rsa_server_ecdh_rsa(Config).
+client_ecdhe_ecdsa_server_ecdh_rsa(Config) when is_list(Config) ->
+ ssl_ECC:client_ecdhe_ecdsa_server_ecdh_rsa(Config).
%% ECDHE_RSA
client_ecdh_rsa_server_ecdhe_rsa(Config) when is_list(Config) ->
- Default = ssl_test_lib:default_cert_chain_conf(),
- {COpts, SOpts} = ssl_test_lib:make_ec_cert_chains([{server_chain, Default},
- {client_chain, Default}],
- ecdh_rsa, ecdhe_rsa, Config),
- basic_test(COpts, SOpts, [{check_keyex, ecdhe_rsa} | proplists:delete(check_keyex, Config)]).
-client_ecdhe_rsa_server_ecdhe_rsa(Config) when is_list(Config) ->
- Default = ssl_test_lib:default_cert_chain_conf(),
- {COpts, SOpts} = ssl_test_lib:make_ec_cert_chains([{server_chain, Default},
- {client_chain, Default}],
- ecdhe_rsa, ecdhe_rsa, Config),
- basic_test(COpts, SOpts, [{check_keyex, ecdhe_rsa} | proplists:delete(check_keyex, Config)]).
-client_ecdhe_ecdsa_server_ecdhe_rsa(Config) when is_list(Config) ->
- Default = ssl_test_lib:default_cert_chain_conf(),
- {COpts, SOpts} = ssl_test_lib:make_ec_cert_chains([{server_chain, Default},
- {client_chain, Default}],
- ecdh_ecdsa, ecdhe_rsa, Config),
- basic_test(COpts, SOpts, [{check_keyex, ecdhe_rsa} | proplists:delete(check_keyex, Config)]).
-
+ ssl_ECC:client_ecdh_rsa_server_ecdhe_rsa(Config).
+client_ecdhe_rsa_server_ecdhe_rsa(Config) when is_list(Config) ->
+ ssl_ECC:client_ecdhe_rsa_server_ecdhe_rsa(Config).
+client_ecdhe_ecdsa_server_ecdhe_rsa(Config) when is_list(Config) ->
+ ssl_ECC:client_ecdhe_ecdsa_server_ecdhe_rsa(Config).
%% ECDH_ECDSA
-client_ecdh_ecdsa_server_ecdh_ecdsa(Config) when is_list(Config) ->
- Ext = x509_test:extensions([{key_usage, [keyEncipherment]}]),
- {COpts, SOpts} = ssl_test_lib:make_ec_cert_chains([{server_chain,
- [[], [], [{extensions, Ext}]]},
- {client_chain,
- ssl_test_lib:default_cert_chain_conf()}],
- ecdh_ecdsa, ecdh_ecdsa, Config),
- basic_test(COpts, SOpts,
- [{check_keyex, ecdh_ecdsa} | proplists:delete(check_keyex, Config)]).
-client_ecdhe_rsa_server_ecdh_ecdsa(Config) when is_list(Config) ->
- Ext = x509_test:extensions([{key_usage, [keyEncipherment]}]),
- {COpts, SOpts} = ssl_test_lib:make_ec_cert_chains([{server_chain,
- [[], [], [{extensions, Ext}]]},
- {client_chain,
- ssl_test_lib:default_cert_chain_conf()}],
- ecdhe_rsa, ecdh_ecdsa, Config),
- basic_test(COpts, SOpts, [{check_keyex, ecdh_ecdsa} | proplists:delete(check_keyex, Config)]).
-
-client_ecdhe_ecdsa_server_ecdh_ecdsa(Config) when is_list(Config) ->
- Ext = x509_test:extensions([{key_usage, [keyEncipherment]}]),
- {COpts, SOpts} = ssl_test_lib:make_ec_cert_chains([{server_chain,
- [[], [], [{extensions, Ext}]]},
- {client_chain,
- ssl_test_lib:default_cert_chain_conf()}],
- ecdhe_ecdsa, ecdh_ecdsa, Config),
- basic_test(COpts, SOpts,
- [{check_keyex, ecdh_ecdsa} | proplists:delete(check_keyex, Config)]).
-
+client_ecdh_ecdsa_server_ecdh_ecdsa(Config) when is_list(Config) ->
+ ssl_ECC:client_ecdh_ecdsa_server_ecdh_ecdsa(Config).
+client_ecdhe_rsa_server_ecdh_ecdsa(Config) when is_list(Config) ->
+ ssl_ECC:client_ecdhe_rsa_server_ecdh_ecdsa(Config).
+client_ecdhe_ecdsa_server_ecdh_ecdsa(Config) when is_list(Config) ->
+ ssl_ECC:client_ecdhe_ecdsa_server_ecdh_ecdsa(Config).
%% ECDHE_ECDSA
-client_ecdh_rsa_server_ecdhe_ecdsa(Config) when is_list(Config) ->
- Default = ssl_test_lib:default_cert_chain_conf(),
- {COpts, SOpts} = ssl_test_lib:make_ec_cert_chains([{server_chain, Default},
- {client_chain, Default}],
- ecdh_rsa, ecdhe_ecdsa, Config),
- basic_test(COpts, SOpts, [{check_keyex, ecdhe_ecdsa} | proplists:delete(check_keyex, Config)]).
-client_ecdh_ecdsa_server_ecdhe_ecdsa(Config) when is_list(Config) ->
- Default = ssl_test_lib:default_cert_chain_conf(),
- {COpts, SOpts} = ssl_test_lib:make_ec_cert_chains([{server_chain, Default},
- {client_chain, Default}],
- ecdh_ecdsa, ecdhe_ecdsa, Config),
- basic_test(COpts, SOpts, [{check_keyex, ecdhe_ecdsa} | proplists:delete(check_keyex, Config)]).
-client_ecdhe_ecdsa_server_ecdhe_ecdsa(Config) when is_list(Config) ->
- Default = ssl_test_lib:default_cert_chain_conf(),
- {COpts, SOpts} = ssl_test_lib:make_ec_cert_chains([{server_chain, Default},
- {client_chain, Default}],
- ecdhe_ecdsa, ecdhe_ecdsa, Config),
- basic_test(COpts, SOpts, [{check_keyex, ecdhe_ecdsa} | proplists:delete(check_keyex, Config)]).
+client_ecdh_rsa_server_ecdhe_ecdsa(Config) when is_list(Config) ->
+ ssl_ECC:client_ecdh_rsa_server_ecdhe_ecdsa(Config).
+client_ecdh_ecdsa_server_ecdhe_ecdsa(Config) when is_list(Config) ->
+ ssl_ECC:client_ecdh_ecdsa_server_ecdhe_ecdsa(Config).
+client_ecdhe_ecdsa_server_ecdhe_ecdsa(Config) when is_list(Config) ->
+ ssl_ECC:client_ecdhe_ecdsa_server_ecdhe_ecdsa(Config).
client_ecdsa_server_ecdsa_with_raw_key(Config) when is_list(Config) ->
Default = ssl_test_lib:default_cert_chain_conf(),
- {COpts, SOpts} = ssl_test_lib:make_ec_cert_chains([{server_chain, Default},
+ {COpts0, SOpts0} = ssl_test_lib:make_ec_cert_chains([{server_chain, Default},
{client_chain, Default}]
, ecdhe_ecdsa, ecdhe_ecdsa, Config),
+ COpts = ssl_test_lib:ssl_options(COpts0, Config),
+ SOpts = ssl_test_lib:ssl_options(SOpts0, Config),
ServerKeyFile = proplists:get_value(keyfile, SOpts),
{ok, PemBin} = file:read_file(ServerKeyFile),
PemEntries = public_key:pem_decode(PemBin),
@@ -331,331 +203,192 @@ client_ecdsa_server_ecdsa_with_raw_key(Config) when is_list(Config) ->
ServerKey = {'ECPrivateKey', Key},
SType = proplists:get_value(server_type, Config),
CType = proplists:get_value(client_type, Config),
- {Server, Port} = start_server_with_raw_key(SType,
- [{key, ServerKey} | proplists:delete(keyfile, SOpts)],
- Config),
- Client = start_client(CType, Port, COpts, Config),
- check_result(Server, SType, Client, CType),
- close(Server, Client).
+ {Server, Port} = ssl_test_lib:start_server_with_raw_key(SType,
+ [{key, ServerKey} | proplists:delete(keyfile, SOpts)],
+ Config),
+ Client = ssl_test_lib:start_client(CType, Port, COpts, Config),
+ ssl_test_lib:gen_check_result(Server, SType, Client, CType),
+ ssl_test_lib:stop(Server, Client).
ecc_default_order(Config) ->
Default = ssl_test_lib:default_cert_chain_conf(),
- {COpts, SOpts} = ssl_test_lib:make_ec_cert_chains([{server_chain, Default},
- {client_chain, Default}],
- ecdhe_ecdsa, ecdhe_ecdsa, Config),
+ {COpts0, SOpts0} = ssl_test_lib:make_ec_cert_chains([{server_chain, Default},
+ {client_chain, Default}],
+ ecdhe_ecdsa, ecdhe_ecdsa, Config),
+ COpts = ssl_test_lib:ssl_options(COpts0, Config),
+ SOpts = ssl_test_lib:ssl_options(SOpts0, Config),
ECCOpts = [],
- case supported_eccs([{eccs, [sect571r1]}]) of
- true -> ecc_test(sect571r1, COpts, SOpts, [], ECCOpts, Config);
+ case ssl_test_lib:supported_eccs([{eccs, [sect571r1]}]) of
+ true -> ssl_test_lib:ecc_test(sect571r1, COpts, SOpts, [], ECCOpts, Config);
false -> {skip, "unsupported named curves"}
end.
ecc_default_order_custom_curves(Config) ->
Default = ssl_test_lib:default_cert_chain_conf(),
- {COpts, SOpts} = ssl_test_lib:make_ec_cert_chains([{server_chain, Default},
- {client_chain, Default}],
- ecdhe_ecdsa, ecdhe_ecdsa, Config),
+ {COpts0, SOpts0} = ssl_test_lib:make_ec_cert_chains([{server_chain, Default},
+ {client_chain, Default}],
+ ecdhe_ecdsa, ecdhe_ecdsa, Config),
+ COpts = ssl_test_lib:ssl_options(COpts0, Config),
+ SOpts = ssl_test_lib:ssl_options(SOpts0, Config),
ECCOpts = [{eccs, [secp256r1, sect571r1]}],
- case supported_eccs(ECCOpts) of
- true -> ecc_test(sect571r1, COpts, SOpts, [], ECCOpts, Config);
+ case ssl_test_lib:supported_eccs(ECCOpts) of
+ true -> ssl_test_lib:ecc_test(sect571r1, COpts, SOpts, [], ECCOpts, Config);
false -> {skip, "unsupported named curves"}
end.
ecc_client_order(Config) ->
Default = ssl_test_lib:default_cert_chain_conf(),
- {COpts, SOpts} = ssl_test_lib:make_ec_cert_chains([{server_chain, Default},
- {client_chain, Default}],
- ecdhe_ecdsa, ecdhe_ecdsa, Config),
+ {COpts0, SOpts0} = ssl_test_lib:make_ec_cert_chains([{server_chain, Default},
+ {client_chain, Default}],
+ ecdhe_ecdsa, ecdhe_ecdsa, Config),
+ COpts = ssl_test_lib:ssl_options(COpts0, Config),
+ SOpts = ssl_test_lib:ssl_options(SOpts0, Config),
ECCOpts = [{honor_ecc_order, false}],
- case supported_eccs([{eccs, [sect571r1]}]) of
- true -> ecc_test(sect571r1, COpts, SOpts, [], ECCOpts, Config);
+ case ssl_test_lib:supported_eccs([{eccs, [sect571r1]}]) of
+ true -> ssl_test_lib:ecc_test(sect571r1, COpts, SOpts, [], ECCOpts, Config);
false -> {skip, "unsupported named curves"}
end.
ecc_client_order_custom_curves(Config) ->
Default = ssl_test_lib:default_cert_chain_conf(),
- {COpts, SOpts} = ssl_test_lib:make_ec_cert_chains([{server_chain, Default},
+ {COpts0, SOpts0} = ssl_test_lib:make_ec_cert_chains([{server_chain, Default},
{client_chain, Default}],
ecdhe_ecdsa, ecdhe_ecdsa, Config),
+ COpts = ssl_test_lib:ssl_options(COpts0, Config),
+ SOpts = ssl_test_lib:ssl_options(SOpts0, Config),
ECCOpts = [{honor_ecc_order, false}, {eccs, [secp256r1, sect571r1]}],
- case supported_eccs(ECCOpts) of
- true -> ecc_test(sect571r1, COpts, SOpts, [], ECCOpts, Config);
+ case ssl_test_lib:supported_eccs(ECCOpts) of
+ true -> ssl_test_lib:ecc_test(sect571r1, COpts, SOpts, [], ECCOpts, Config);
false -> {skip, "unsupported named curves"}
end.
ecc_unknown_curve(Config) ->
Default = ssl_test_lib:default_cert_chain_conf(),
- {COpts, SOpts} = ssl_test_lib:make_ec_cert_chains([{server_chain, Default},
+ {COpts0, SOpts0} = ssl_test_lib:make_ec_cert_chains([{server_chain, Default},
{client_chain, Default}],
ecdhe_ecdsa, ecdhe_ecdsa, Config),
+ COpts = ssl_test_lib:ssl_options(COpts0, Config),
+ SOpts = ssl_test_lib:ssl_options(SOpts0, Config),
ECCOpts = [{eccs, ['123_fake_curve']}],
- ecc_test_error(COpts, SOpts, [], ECCOpts, Config).
+ ssl_test_lib:ecc_test_error(COpts, SOpts, [], ECCOpts, Config).
client_ecdh_rsa_server_ecdhe_ecdsa_server_custom(Config) ->
Default = ssl_test_lib:default_cert_chain_conf(),
- {COpts, SOpts} = ssl_test_lib:make_ec_cert_chains([{server_chain, Default},
+ {COpts0, SOpts0} = ssl_test_lib:make_ec_cert_chains([{server_chain, Default},
{client_chain, Default}],
- ecdh_rsa, ecdhe_ecdsa, Config),
+ ecdh_rsa, ecdhe_ecdsa, Config),
+ COpts = ssl_test_lib:ssl_options(COpts0, Config),
+ SOpts = ssl_test_lib:ssl_options(SOpts0, Config),
ECCOpts = [{honor_ecc_order, true}, {eccs, [secp256r1, sect571r1]}],
- case supported_eccs(ECCOpts) of
- true -> ecc_test(secp256r1, COpts, SOpts, [], ECCOpts, Config);
+ case ssl_test_lib:supported_eccs(ECCOpts) of
+ true -> ssl_test_lib:ecc_test(secp256r1, COpts, SOpts, [], ECCOpts, Config);
false -> {skip, "unsupported named curves"}
end.
client_ecdh_rsa_server_ecdhe_rsa_server_custom(Config) ->
Default = ssl_test_lib:default_cert_chain_conf(),
- {COpts, SOpts} = ssl_test_lib:make_ec_cert_chains([{server_chain, Default},
+ {COpts0, SOpts0} = ssl_test_lib:make_ec_cert_chains([{server_chain, Default},
{client_chain, Default}],
ecdh_rsa, ecdhe_rsa, Config),
+ COpts = ssl_test_lib:ssl_options(COpts0, Config),
+ SOpts = ssl_test_lib:ssl_options(SOpts0, Config),
ECCOpts = [{honor_ecc_order, true}, {eccs, [secp256r1, sect571r1]}],
- case supported_eccs(ECCOpts) of
- true -> ecc_test(undefined, COpts, SOpts, [], ECCOpts, Config);
+
+ case ssl_test_lib:supported_eccs(ECCOpts) of
+ true -> ssl_test_lib:ecc_test(undefined, COpts, SOpts, [], ECCOpts, Config);
false -> {skip, "unsupported named curves"}
end.
client_ecdhe_rsa_server_ecdhe_ecdsa_server_custom(Config) ->
Default = ssl_test_lib:default_cert_chain_conf(),
- {COpts, SOpts} = ssl_test_lib:make_ec_cert_chains([{server_chain, Default},
+ {COpts0, SOpts0} = ssl_test_lib:make_ec_cert_chains([{server_chain, Default},
{client_chain, Default}],
ecdhe_rsa, ecdhe_ecdsa, Config),
+ COpts = ssl_test_lib:ssl_options(COpts0, Config),
+ SOpts = ssl_test_lib:ssl_options(SOpts0, Config),
ECCOpts = [{honor_ecc_order, true}, {eccs, [secp256r1, sect571r1]}],
- case supported_eccs(ECCOpts) of
- true -> ecc_test(secp256r1, COpts, SOpts, [], ECCOpts, Config);
+ case ssl_test_lib:supported_eccs(ECCOpts) of
+ true -> ssl_test_lib:ecc_test(secp256r1, COpts, SOpts, [], ECCOpts, Config);
false -> {skip, "unsupported named curves"}
end.
client_ecdhe_rsa_server_ecdhe_rsa_server_custom(Config) ->
Default = ssl_test_lib:default_cert_chain_conf(),
- {COpts, SOpts} = ssl_test_lib:make_ec_cert_chains([{server_chain, Default},
+ {COpts0, SOpts0} = ssl_test_lib:make_ec_cert_chains([{server_chain, Default},
{client_chain, Default}],
ecdhe_rsa, ecdhe_rsa, Config),
+
+ COpts = ssl_test_lib:ssl_options(COpts0, Config),
+ SOpts = ssl_test_lib:ssl_options(SOpts0, Config),
ECCOpts = [{honor_ecc_order, true}, {eccs, [secp256r1, sect571r1]}],
- case supported_eccs(ECCOpts) of
- true -> ecc_test(undefined, COpts, SOpts, [], ECCOpts, Config);
+ case ssl_test_lib:supported_eccs(ECCOpts) of
+ true -> ssl_test_lib:ecc_test(undefined, COpts, SOpts, [], ECCOpts, Config);
false -> {skip, "unsupported named curves"}
end.
client_ecdhe_rsa_server_ecdh_rsa_server_custom(Config) ->
Default = ssl_test_lib:default_cert_chain_conf(),
Ext = x509_test:extensions([{key_usage, [keyEncipherment]}]),
- {COpts, SOpts} = ssl_test_lib:make_ec_cert_chains([{server_chain, [[], [], [{extensions, Ext}]]},
+ {COpts0, SOpts0} = ssl_test_lib:make_ec_cert_chains([{server_chain, [[], [], [{extensions, Ext}]]},
{client_chain, Default}],
ecdhe_rsa, ecdh_rsa, Config),
+
+ COpts = ssl_test_lib:ssl_options(COpts0, Config),
+ SOpts = ssl_test_lib:ssl_options(SOpts0, Config),
ECCOpts = [{honor_ecc_order, true}, {eccs, [secp256r1, sect571r1]}],
- case supported_eccs(ECCOpts) of
- true -> ecc_test(undefined, COpts, SOpts, [], ECCOpts, Config);
+
+ case ssl_test_lib:supported_eccs(ECCOpts) of
+ true -> ssl_test_lib:ecc_test(undefined, COpts, SOpts, [], ECCOpts, Config);
false -> {skip, "unsupported named curves"}
end.
client_ecdhe_ecdsa_server_ecdhe_ecdsa_server_custom(Config) ->
Default = ssl_test_lib:default_cert_chain_conf(),
- {COpts, SOpts} = ssl_test_lib:make_ec_cert_chains([{server_chain, Default},
+ {COpts0, SOpts0} = ssl_test_lib:make_ec_cert_chains([{server_chain, Default},
{client_chain, Default}],
ecdhe_ecdsa, ecdhe_ecdsa, Config),
+ COpts = ssl_test_lib:ssl_options(COpts0, Config),
+ SOpts = ssl_test_lib:ssl_options(SOpts0, Config),
ECCOpts = [{honor_ecc_order, true}, {eccs, [secp256r1, sect571r1]}],
- case supported_eccs(ECCOpts) of
- true -> ecc_test(secp256r1, COpts, SOpts, [], ECCOpts, Config);
+ case ssl_test_lib:supported_eccs(ECCOpts) of
+ true -> ssl_test_lib:ecc_test(secp256r1, COpts, SOpts, [], ECCOpts, Config);
false -> {skip, "unsupported named curves"}
end.
client_ecdhe_ecdsa_server_ecdhe_rsa_server_custom(Config) ->
Default = ssl_test_lib:default_cert_chain_conf(),
- {COpts, SOpts} = ssl_test_lib:make_ec_cert_chains([{server_chain, Default},
+ {COpts0, SOpts0} = ssl_test_lib:make_ec_cert_chains([{server_chain, Default},
{client_chain, Default}],
ecdhe_ecdsa, ecdhe_rsa, Config),
+ COpts = ssl_test_lib:ssl_options(COpts0, Config),
+ SOpts = ssl_test_lib:ssl_options(SOpts0, Config),
ECCOpts = [{honor_ecc_order, true}, {eccs, [secp256r1, sect571r1]}],
- case supported_eccs(ECCOpts) of
- true -> ecc_test(undefined, COpts, SOpts, [], ECCOpts, Config);
+ case ssl_test_lib:supported_eccs(ECCOpts) of
+ true -> ssl_test_lib:ecc_test(undefined, COpts, SOpts, [], ECCOpts, Config);
false -> {skip, "unsupported named curves"}
end.
client_ecdhe_ecdsa_server_ecdhe_ecdsa_client_custom(Config) ->
Default = ssl_test_lib:default_cert_chain_conf(),
- {COpts, SOpts} = ssl_test_lib:make_ec_cert_chains([{server_chain, Default},
+ {COpts0, SOpts0} = ssl_test_lib:make_ec_cert_chains([{server_chain, Default},
{client_chain, Default}],
ecdhe_ecdsa, ecdhe_ecdsa, Config),
+ COpts = ssl_test_lib:ssl_options(COpts0, Config),
+ SOpts = ssl_test_lib:ssl_options(SOpts0, Config),
ECCOpts = [{eccs, [secp256r1, sect571r1]}],
- case supported_eccs(ECCOpts) of
- true -> ecc_test(secp256r1, COpts, SOpts, ECCOpts, [], Config);
+ case ssl_test_lib:supported_eccs(ECCOpts) of
+ true -> ssl_test_lib:ecc_test(secp256r1, COpts, SOpts, ECCOpts, [], Config);
false -> {skip, "unsupported named curves"}
end.
client_ecdhe_rsa_server_ecdhe_ecdsa_client_custom(Config) ->
Default = ssl_test_lib:default_cert_chain_conf(),
- {COpts, SOpts} = ssl_test_lib:make_ec_cert_chains([{server_chain, Default},
+ {COpts0, SOpts0} = ssl_test_lib:make_ec_cert_chains([{server_chain, Default},
{client_chain, Default}],
ecdhe_rsa, ecdhe_ecdsa, Config),
+ COpts = ssl_test_lib:ssl_options(COpts0, Config),
+ SOpts = ssl_test_lib:ssl_options(SOpts0, Config),
ECCOpts = [{eccs, [secp256r1, sect571r1]}],
- case supported_eccs(ECCOpts) of
- true -> ecc_test(secp256r1, COpts, SOpts, ECCOpts, [], Config);
+ case ssl_test_lib:supported_eccs(ECCOpts) of
+ true -> ssl_test_lib:ecc_test(secp256r1, COpts, SOpts, ECCOpts, [], Config);
false -> {skip, "unsupported named curves"}
end.
-
-%%--------------------------------------------------------------------
-%% Internal functions ------------------------------------------------
-%%--------------------------------------------------------------------
-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),
- check_result(Server, SType, Client, CType),
- close(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),
- ssl_test_lib:check_result(Server, ok, Client, ok),
- close(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"}},
- ssl_test_lib: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 = tls_record:protocol_version(tls_record:highest_protocol_version([])),
- 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 = ssl_test_lib:inet_port(node()),
- Version = tls_record:protocol_version(tls_record:highest_protocol_version([])),
- 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 = ssl_test_lib: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 = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {ssl_test_lib,
- check_key_exchange_send_active,
- [KeyEx]}},
- {options, [{verify, verify_peer} | ServerOpts]}]),
- {Server, ssl_test_lib:inet_port(Server)}.
-
-start_server_with_raw_key(erlang, ServerOpts, Config) ->
- {_, ServerNode, _} = ssl_test_lib:run_where(Config),
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {ssl_test_lib,
- send_recv_result_active,
- []}},
- {options,
- [{verify, verify_peer} | ServerOpts]}]),
- {Server, ssl_test_lib:inet_port(Server)}.
-
-start_server_ecc(erlang, ServerOpts, Expect, ECCOpts, Config) ->
- {_, ServerNode, _} = ssl_test_lib:run_where(Config),
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {?MODULE, check_ecc, [server, Expect]}},
- {options,
- ECCOpts ++
- [{verify, verify_peer} | ServerOpts]}]),
- {Server, ssl_test_lib:inet_port(Server)}.
-
-start_server_ecc_error(erlang, ServerOpts, ECCOpts, Config) ->
- {_, ServerNode, _} = ssl_test_lib:run_where(Config),
- Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0},
- {from, self()},
- {options,
- ECCOpts ++
- [{verify, verify_peer} | ServerOpts]}]),
- {Server, ssl_test_lib:inet_port(Server)}.
-
-check_result(Server, erlang, Client, erlang) ->
- ssl_test_lib:check_result(Server, ok, Client, ok);
-check_result(Server, erlang, _, _) ->
- ssl_test_lib:check_result(Server, ok);
-check_result(_, _, Client, erlang) ->
- ssl_test_lib:check_result(Client, ok);
-check_result(_,openssl, _, openssl) ->
- ok.
-
-openssl_check(erlang, Config) ->
- Config;
-openssl_check(_, Config) ->
- TLSVersion = proplists:get_value(tls_version, Config),
- case ssl_test_lib:check_sane_openssl_version(TLSVersion) of
- true ->
- Config;
- false ->
- {skip, "TLS version not supported by openssl"}
- end.
-
-close(Port1, Port2) when is_port(Port1), is_port(Port2) ->
- ssl_test_lib:close_port(Port1),
- ssl_test_lib:close_port(Port2);
-close(Port, Pid) when is_port(Port) ->
- ssl_test_lib:close_port(Port),
- ssl_test_lib:close(Pid);
-close(Pid, Port) when is_port(Port) ->
- ssl_test_lib:close_port(Port),
- ssl_test_lib:close(Pid);
-close(Client, Server) ->
- ssl_test_lib:close(Server),
- ssl_test_lib: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;
- false when Expect =:= undefined -> ok;
- Other -> {error, Role, Expect, Other}
- end.
-
diff --git a/lib/ssl/test/ssl_ECC_openssl_SUITE.erl b/lib/ssl/test/ssl_ECC_openssl_SUITE.erl
new file mode 100644
index 0000000000..ba609aa0dc
--- /dev/null
+++ b/lib/ssl/test/ssl_ECC_openssl_SUITE.erl
@@ -0,0 +1,185 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2018-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.
+%% 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(ssl_ECC_openssl_SUITE).
+
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+-include_lib("public_key/include/public_key.hrl").
+
+%%--------------------------------------------------------------------
+%% Common Test interface functions -----------------------------------
+%%--------------------------------------------------------------------
+
+all() ->
+ [
+ {group, 'tlsv1.2'},
+ {group, 'tlsv1.1'},
+ {group, 'tlsv1'},
+ {group, 'dtlsv1.2'},
+ {group, 'dtlsv1'}
+ ].
+
+groups() ->
+ [
+ {'tlsv1.2', [], test_cases()},
+ {'tlsv1.1', [], test_cases()},
+ {'tlsv1', [], test_cases()},
+ {'dtlsv1.2', [], test_cases()},
+ {'dtlsv1', [], test_cases()}
+ ].
+
+test_cases()->
+ %% cert_combinations().
+ server_ecdh_rsa().
+cert_combinations() ->
+ lists:append(lists:filtermap(fun({Name, Suites}) ->
+ case ssl_test_lib:openssl_filter(Name) of
+ [] ->
+ false;
+ [_|_] ->
+ {true, Suites}
+ end
+ end, [{"ECDH-RSA", server_ecdh_rsa()},
+ {"ECDHE-RSA", server_ecdhe_rsa()},
+ {"ECDH-ECDSA", server_ecdh_ecdsa()},
+ {"ECDHE-ECDSA", server_ecdhe_ecdsa()}
+ ])).
+server_ecdh_rsa() ->
+ [client_ecdh_rsa_server_ecdh_rsa,
+ client_ecdhe_rsa_server_ecdh_rsa,
+ client_ecdhe_ecdsa_server_ecdh_rsa].
+
+server_ecdhe_rsa() ->
+ [client_ecdh_rsa_server_ecdhe_rsa,
+ client_ecdhe_rsa_server_ecdhe_rsa,
+ client_ecdhe_ecdsa_server_ecdhe_rsa].
+
+server_ecdh_ecdsa() ->
+ [client_ecdh_ecdsa_server_ecdh_ecdsa,
+ client_ecdhe_rsa_server_ecdh_ecdsa,
+ client_ecdhe_ecdsa_server_ecdh_ecdsa].
+
+server_ecdhe_ecdsa() ->
+ [client_ecdh_rsa_server_ecdhe_ecdsa,
+ client_ecdh_ecdsa_server_ecdhe_ecdsa,
+ client_ecdhe_ecdsa_server_ecdhe_ecdsa].
+
+%%--------------------------------------------------------------------
+init_per_suite(Config0) ->
+ end_per_suite(Config0),
+ try crypto:start() of
+ ok ->
+ case ssl_test_lib:sufficient_crypto_support(cipher_ec) of
+ true ->
+ Config0;
+ false ->
+ {skip, "Crypto does not support ECC"}
+ end
+ catch _:_ ->
+ {skip, "Crypto did not start"}
+ end.
+
+end_per_suite(_Config) ->
+ application:stop(ssl),
+ application:stop(crypto).
+
+%%--------------------------------------------------------------------
+init_per_group(GroupName, Config) ->
+ case ssl_test_lib:is_tls_version(GroupName) of
+ true ->
+ case ssl_test_lib:check_sane_openssl_version(GroupName) of
+ true ->
+ [{tls_version, GroupName},
+ {server_type, erlang},
+ {client_type, openssl} | ssl_test_lib:init_tls_version(GroupName, Config)];
+ false ->
+ {skip, openssl_does_not_support_version}
+ end;
+ _ ->
+ Config
+ end.
+
+end_per_group(GroupName, Config0) ->
+ case ssl_test_lib:is_tls_version(GroupName) of
+ true ->
+ Config = ssl_test_lib:clean_tls_version(Config0),
+ proplists:delete(tls_version, Config);
+ false ->
+ Config0
+ end.
+
+%%--------------------------------------------------------------------
+
+init_per_testcase(TestCase, Config) ->
+ ssl_test_lib:ct_log_supported_protocol_versions(Config),
+ Version = proplists:get_value(tls_version, Config),
+ ct:log("Ciphers: ~p~n ", [ssl:cipher_suites(default, Version)]),
+ end_per_testcase(TestCase, Config),
+ ssl:start(),
+ ct:timetrap({seconds, 15}),
+ Config.
+
+end_per_testcase(_TestCase, Config) ->
+ application:stop(ssl),
+ Config.
+
+%%--------------------------------------------------------------------
+%% Test Cases --------------------------------------------------------
+%%--------------------------------------------------------------------
+
+%% Test diffrent certificate chain types, note that it is the servers
+%% chain that affect what cipher suit that will be choosen
+
+%% ECDH_RSA
+client_ecdh_rsa_server_ecdh_rsa(Config) when is_list(Config) ->
+ ssl_ECC:client_ecdh_rsa_server_ecdh_rsa(Config).
+client_ecdhe_rsa_server_ecdh_rsa(Config) when is_list(Config) ->
+ ssl_ECC:client_ecdhe_rsa_server_ecdh_rsa(Config).
+client_ecdhe_ecdsa_server_ecdh_rsa(Config) when is_list(Config) ->
+ ssl_ECC:client_ecdhe_ecdsa_server_ecdh_rsa(Config).
+%% ECDHE_RSA
+client_ecdh_rsa_server_ecdhe_rsa(Config) when is_list(Config) ->
+ ssl_ECC:client_ecdh_rsa_server_ecdhe_rsa(Config).
+client_ecdhe_rsa_server_ecdhe_rsa(Config) when is_list(Config) ->
+ ssl_ECC:client_ecdhe_rsa_server_ecdhe_rsa(Config).
+client_ecdhe_ecdsa_server_ecdhe_rsa(Config) when is_list(Config) ->
+ ssl_ECC:client_ecdhe_ecdsa_server_ecdhe_rsa(Config).
+%% ECDH_ECDSA
+client_ecdh_ecdsa_server_ecdh_ecdsa(Config) when is_list(Config) ->
+ ssl_ECC:client_ecdh_ecdsa_server_ecdh_ecdsa(Config).
+client_ecdhe_rsa_server_ecdh_ecdsa(Config) when is_list(Config) ->
+ ssl_ECC:client_ecdhe_rsa_server_ecdh_ecdsa(Config).
+client_ecdhe_ecdsa_server_ecdh_ecdsa(Config) when is_list(Config) ->
+ ssl_ECC:client_ecdhe_ecdsa_server_ecdh_ecdsa(Config).
+%% ECDHE_ECDSA
+client_ecdh_rsa_server_ecdhe_ecdsa(Config) when is_list(Config) ->
+ ssl_ECC:client_ecdh_rsa_server_ecdhe_ecdsa(Config).
+client_ecdh_ecdsa_server_ecdhe_ecdsa(Config) when is_list(Config) ->
+ ssl_ECC:client_ecdh_ecdsa_server_ecdhe_ecdsa(Config).
+client_ecdhe_ecdsa_server_ecdhe_ecdsa(Config) when is_list(Config) ->
+ ssl_ECC:client_ecdhe_ecdsa_server_ecdhe_ecdsa(Config).
+%%--------------------------------------------------------------------
+%% Internal functions ------------------------------------------------
+%%--------------------------------------------------------------------
diff --git a/lib/ssl/test/ssl_basic_SUITE.erl b/lib/ssl/test/ssl_basic_SUITE.erl
index a9901007db..fe4f02f100 100644
--- a/lib/ssl/test/ssl_basic_SUITE.erl
+++ b/lib/ssl/test/ssl_basic_SUITE.erl
@@ -164,7 +164,10 @@ api_tests() ->
accept_pool,
prf,
socket_options,
- cipher_suites
+ cipher_suites,
+ handshake_continue,
+ hello_client_cancel,
+ hello_server_cancel
].
api_tests_tls() ->
@@ -291,6 +294,7 @@ init_per_group(GroupName, Config) when GroupName == basic_tls;
->
ssl_test_lib:clean_tls_version(Config);
init_per_group(GroupName, Config) ->
+ ssl_test_lib:clean_tls_version(Config),
case ssl_test_lib:is_tls_version(GroupName) andalso ssl_test_lib:sufficient_crypto_support(GroupName) of
true ->
ssl_test_lib:init_tls_version(GroupName, Config);
@@ -629,6 +633,84 @@ new_options_in_accept(Config) when is_list(Config) ->
ssl_test_lib:close(Server),
ssl_test_lib:close(Client).
+
+%%--------------------------------------------------------------------
+handshake_continue() ->
+ [{doc, "Test API function ssl:handshake_continue/3"}].
+handshake_continue(Config) when is_list(Config) ->
+ ClientOpts = ssl_test_lib:ssl_options(client_verification_opts, Config),
+ ServerOpts = ssl_test_lib:ssl_options(server_verification_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {ssl_test_lib, send_recv_result_active, []}},
+ {options, ssl_test_lib:ssl_options([{reuseaddr, true}, {handshake, hello}],
+ Config)},
+ {continue_options, proplists:delete(reuseaddr, ServerOpts)}
+ ]),
+
+ Port = ssl_test_lib:inet_port(Server),
+
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {ssl_test_lib, send_recv_result_active, []}},
+ {options, ssl_test_lib:ssl_options([{handshake, hello}],
+ Config)},
+ {continue_options, proplists:delete(reuseaddr, ClientOpts)}]),
+
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+%%--------------------------------------------------------------------
+hello_client_cancel() ->
+ [{doc, "Test API function ssl:handshake_cancel/1 on the client side"}].
+hello_client_cancel(Config) when is_list(Config) ->
+ ServerOpts = ssl_test_lib:ssl_options(server_verification_opts, Config),
+ ServerOpts = ssl_test_lib:ssl_options(server_verification_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {options, ssl_test_lib:ssl_options([{handshake, hello}], Config)},
+ {continue_options, proplists:delete(reuseaddr, ServerOpts)}]),
+
+ Port = ssl_test_lib:inet_port(Server),
+
+ %% That is ssl:handshake_cancel returns ok
+ {connect_failed, ok} = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {options, ssl_test_lib:ssl_options([{handshake, hello}], Config)},
+ {continue_options, cancel}]),
+
+ ssl_test_lib:check_result(Server, {error, {tls_alert, "user canceled"}}).
+%%--------------------------------------------------------------------
+
+hello_server_cancel() ->
+ [{doc, "Test API function ssl:handshake_cancel/1 on the server side"}].
+hello_server_cancel(Config) when is_list(Config) ->
+ ClientOpts = ssl_test_lib:ssl_options(client_verification_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {options, ssl_test_lib:ssl_options([{handshake, hello}], Config)},
+ {continue_options, cancel}]),
+
+ Port = ssl_test_lib:inet_port(Server),
+
+ {connect_failed, _} = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {options, ssl_test_lib:ssl_options([{handshake, hello}], Config)},
+ {continue_options, proplists:delete(reuseaddr, ClientOpts)}]),
+
+ ssl_test_lib:check_result(Server, ok).
+
%%--------------------------------------------------------------------
prf() ->
[{doc,"Test that ssl:prf/5 uses the negotiated PRF."}].
@@ -962,7 +1044,7 @@ controller_dies(Config) when is_list(Config) ->
{mfa, {?MODULE,
controller_dies_result, [self(),
ClientMsg]}},
- {options, [{reuseaddr,true}|ClientOpts]}]),
+ {options, ClientOpts}]),
ct:sleep(?SLEEP), %% so that they are connected
exit(Server, killed),
@@ -987,7 +1069,7 @@ tls_client_closes_socket(Config) when is_list(Config) ->
Connect = fun() ->
{ok, _Socket} = rpc:call(ClientNode, gen_tcp, connect,
- [Hostname, Port, TcpOpts]),
+ [Hostname, Port, [binary]]),
%% Make sure that ssl_accept is called before
%% client process ends and closes socket.
ct:sleep(?SLEEP)
@@ -1811,7 +1893,7 @@ tls_send_close(Config) when is_list(Config) ->
{options, [{active, false} | ServerOpts]}]),
Port = ssl_test_lib:inet_port(Server),
{ok, TcpS} = rpc:call(ClientNode, gen_tcp, connect,
- [Hostname,Port,[binary, {active, false}, {reuseaddr, true}]]),
+ [Hostname,Port,[binary, {active, false}]]),
{ok, SslS} = rpc:call(ClientNode, ssl, connect,
[TcpS,[{active, false}|ClientOpts]]),
@@ -1955,7 +2037,7 @@ tls_upgrade(Config) when is_list(Config) ->
{host, Hostname},
{from, self()},
{mfa, {?MODULE, upgrade_result, []}},
- {tcp_options, TcpOpts},
+ {tcp_options, [binary]},
{ssl_options, ClientOpts}]),
ct:log("Testcase ~p, Client ~p Server ~p ~n",
diff --git a/lib/ssl/test/ssl_certificate_verify_SUITE.erl b/lib/ssl/test/ssl_certificate_verify_SUITE.erl
index 0bc265fa10..1de4c89d7f 100644
--- a/lib/ssl/test/ssl_certificate_verify_SUITE.erl
+++ b/lib/ssl/test/ssl_certificate_verify_SUITE.erl
@@ -40,14 +40,22 @@
%%--------------------------------------------------------------------
all() ->
[
- {group, tls},
- {group, dtls}
+ {group, 'tlsv1.2'},
+ {group, 'tlsv1.1'},
+ {group, 'tlsv1'},
+ {group, 'sslv3'},
+ {group, 'dtlsv1.2'},
+ {group, 'dtlsv1'}
].
groups() ->
[
- {tls, [], all_protocol_groups()},
- {dtls, [], all_protocol_groups()},
+ {'tlsv1.2', [], all_protocol_groups()},
+ {'tlsv1.1', [], all_protocol_groups()},
+ {'tlsv1', [], all_protocol_groups()},
+ {'sslv3', [], all_protocol_groups()},
+ {'dtlsv1.2', [], all_protocol_groups()},
+ {'dtlsv1', [], all_protocol_groups()},
{active, [], tests()},
{active_once, [], tests()},
{passive, [], tests()},
@@ -65,6 +73,7 @@ tests() ->
verify_none,
server_require_peer_cert_ok,
server_require_peer_cert_fail,
+ server_require_peer_cert_empty_ok,
server_require_peer_cert_partial_chain,
server_require_peer_cert_allow_partial_chain,
server_require_peer_cert_do_not_allow_partial_chain,
@@ -104,24 +113,6 @@ end_per_suite(_Config) ->
ssl:stop(),
application:stop(crypto).
-init_per_group(tls, Config0) ->
- Version = tls_record:protocol_version(tls_record:highest_protocol_version([])),
- ssl:stop(),
- application:load(ssl),
- application:set_env(ssl, protocol_version, Version),
- ssl:start(),
- Config = ssl_test_lib:init_tls_version(Version, Config0),
- [{version, tls_record:protocol_version(Version)} | Config];
-
-init_per_group(dtls, Config0) ->
- Version = dtls_record:protocol_version(dtls_record:highest_protocol_version([])),
- ssl:stop(),
- application:load(ssl),
- application:set_env(ssl, protocol_version, Version),
- ssl:start(),
- Config = ssl_test_lib:init_tls_version(Version, Config0),
- [{version, dtls_record:protocol_version(Version)} | Config];
-
init_per_group(active, Config) ->
[{active, true}, {receive_function, send_recv_result_active} | Config];
init_per_group(active_once, Config) ->
@@ -130,15 +121,24 @@ init_per_group(passive, Config) ->
[{active, false}, {receive_function, send_recv_result} | Config];
init_per_group(error_handling, Config) ->
[{active, false}, {receive_function, send_recv_result} | Config];
+init_per_group(GroupName, Config) ->
+ case ssl_test_lib:is_tls_version(GroupName) of
+ true ->
+ case ssl_test_lib:sufficient_crypto_support(GroupName) of
+ true ->
+ [{version, GroupName} | ssl_test_lib:init_tls_version(GroupName, Config)];
+ false ->
+ {skip, "Missing crypto support"}
+ end
+ end.
-init_per_group(_, Config) ->
- Config.
-
-end_per_group(GroupName, Config) when GroupName == tls;
- GroupName == dtls ->
- ssl_test_lib:clean_tls_version(Config);
-end_per_group(_GroupName, Config) ->
- Config.
+end_per_group(GroupName, Config) ->
+ case ssl_test_lib:is_tls_version(GroupName) of
+ true ->
+ ssl_test_lib:clean_tls_version(Config);
+ false ->
+ Config
+ end.
init_per_testcase(_TestCase, Config) ->
ssl:stop(),
@@ -306,6 +306,35 @@ server_require_peer_cert_fail(Config) when is_list(Config) ->
end.
%%--------------------------------------------------------------------
+server_require_peer_cert_empty_ok() ->
+ [{doc,"Test server option fail_if_no_peer_cert when peer sends cert"}].
+
+server_require_peer_cert_empty_ok(Config) when is_list(Config) ->
+ ServerOpts = [{verify, verify_peer}, {fail_if_no_peer_cert, false}
+ | ssl_test_lib:ssl_options(server_rsa_opts, Config)],
+ ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_opts, Config),
+ Active = proplists:get_value(active, Config),
+ ReceiveFunction = proplists:get_value(receive_function, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ ClientOpts = proplists:delete(keyfile, proplists:delete(certfile, ClientOpts0)),
+
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {ssl_test_lib, ReceiveFunction, []}},
+ {options, [{active, Active} | ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {ssl_test_lib, ReceiveFunction, []}},
+ {options, [{active, Active} | ClientOpts]}]),
+
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+%%--------------------------------------------------------------------
server_require_peer_cert_partial_chain() ->
[{doc, "Client sends an incompleate chain, by default not acceptable."}].
@@ -930,6 +959,7 @@ client_with_cert_cipher_suites_handshake(Config) when is_list(Config) ->
Config, "_sign_only_extensions"),
ClientOpts = ssl_test_lib:ssl_options(ClientOpts0, Config),
ServerOpts = ssl_test_lib:ssl_options(ServerOpts0, Config),
+ TLSVersion = ssl_test_lib:protocol_version(Config, tuple),
{ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
@@ -938,7 +968,7 @@ client_with_cert_cipher_suites_handshake(Config) when is_list(Config) ->
send_recv_result_active, []}},
{options, [{active, true},
{ciphers,
- ssl_test_lib:rsa_non_signed_suites(proplists:get_value(version, Config))}
+ ssl_test_lib:rsa_non_signed_suites(TLSVersion)}
| ServerOpts]}]),
Port = ssl_test_lib:inet_port(Server),
Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
diff --git a/lib/ssl/test/ssl_test_lib.erl b/lib/ssl/test/ssl_test_lib.erl
index e531494f0b..3a7e844cf8 100644
--- a/lib/ssl/test/ssl_test_lib.erl
+++ b/lib/ssl/test/ssl_test_lib.erl
@@ -79,17 +79,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 +121,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 +137,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 +183,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]),
@@ -187,8 +227,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 +294,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, infinity]) 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),
@@ -862,6 +945,163 @@ 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;
+ false when Expect == undefined -> ok;
+ false when Expect == secp256r1 andalso Role == client_no_ecc -> ok;
+ Other -> {error, Role, Expect, Other}
+ end.
+
inet_port(Pid) when is_pid(Pid)->
receive
{Pid, {port, Port}} ->
@@ -1185,10 +1425,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(_) ->