aboutsummaryrefslogtreecommitdiffstats
path: root/lib/ssl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/ssl')
-rw-r--r--lib/ssl/doc/src/notes.xml29
-rw-r--r--lib/ssl/doc/src/ssl.xml40
-rw-r--r--lib/ssl/src/dtls_handshake.erl2
-rw-r--r--lib/ssl/src/ssl.erl38
-rw-r--r--lib/ssl/src/ssl_connection.erl56
-rw-r--r--lib/ssl/src/ssl_handshake.erl274
-rw-r--r--lib/ssl/src/tls_connection.erl46
-rw-r--r--lib/ssl/src/tls_handshake.erl7
-rw-r--r--lib/ssl/src/tls_sender.erl41
-rw-r--r--lib/ssl/test/make_certs.erl6
-rw-r--r--lib/ssl/test/ssl_alpn_handshake_SUITE.erl23
-rw-r--r--lib/ssl/test/ssl_basic_SUITE.erl46
-rw-r--r--lib/ssl/test/ssl_certificate_verify_SUITE.erl54
-rw-r--r--lib/ssl/test/ssl_handshake_SUITE.erl64
-rw-r--r--lib/ssl/test/ssl_packet_SUITE.erl52
-rw-r--r--lib/ssl/test/ssl_test_lib.erl38
16 files changed, 589 insertions, 227 deletions
diff --git a/lib/ssl/doc/src/notes.xml b/lib/ssl/doc/src/notes.xml
index c54acfcf53..d117641496 100644
--- a/lib/ssl/doc/src/notes.xml
+++ b/lib/ssl/doc/src/notes.xml
@@ -27,6 +27,35 @@
</header>
<p>This document describes the changes made to the SSL application.</p>
+<section><title>SSL 9.0.3</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Correct alert handling with new TLS sender process, from
+ ssl-9.0.2. CLOSE ALERTS could under some circumstances be
+ encoded using an incorrect cipher state. This would cause
+ the peer to regard them as unknown messages.</p>
+ <p>
+ Own Id: OTP-15337 Aux Id: ERL-738 </p>
+ </item>
+ <item>
+ <p>
+ Correct handling of socket packet option with new TLS
+ sender process, from ssl-9.0.2. When changing the socket
+ option {packet, 1|2|3|4} with ssl:setopts/2 the option
+ must internally be propagated to the sender process as
+ well as the reader process as this particular option also
+ affects the data to be sent.</p>
+ <p>
+ Own Id: OTP-15348 Aux Id: ERL-747 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>SSL 9.0.2</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/ssl/doc/src/ssl.xml b/lib/ssl/doc/src/ssl.xml
index 6efa022a79..ef99ace351 100644
--- a/lib/ssl/doc/src/ssl.xml
+++ b/lib/ssl/doc/src/ssl.xml
@@ -209,7 +209,24 @@
elliptic_curves => [oid] | undefined,
sni => string() | undefined}
}</c></p></item>
-
+
+ <tag><c>signature_scheme() =</c></tag>
+ <item>
+ <p><c>rsa_pkcs1_sha256</c></p>
+ <p><c>| rsa_pkcs1_sha384</c></p>
+ <p><c>| rsa_pkcs1_sha512</c></p>
+ <p><c>| ecdsa_secp256r1_sha256</c></p>
+ <p><c>| ecdsa_secp384r1_sha384</c></p>
+ <p><c>| ecdsa_secp521r1_sha512</c></p>
+ <p><c>| rsa_pss_rsae_sha256</c></p>
+ <p><c>| rsa_pss_rsae_sha384</c></p>
+ <p><c>| rsa_pss_rsae_sha512</c></p>
+ <p><c>| rsa_pss_pss_sha256</c></p>
+ <p><c>| rsa_pss_pss_sha384</c></p>
+ <p><c>| rsa_pss_pss_sha512</c></p>
+ <p><c>| rsa_pkcs1_sha1</c></p>
+ <p><c>| ecdsa_sha1</c></p>
+ </item>
</taglist>
</section>
@@ -709,6 +726,26 @@ fun(srp, Username :: string(), UserState :: term()) ->
that may be selected. Default support for {md5, rsa} removed in ssl-8.0
</p>
</item>
+ <tag><marker id="signature_algs_cert"/><c>{signature_algs_cert, [signature_scheme()]}</c></tag>
+ <item>
+ <p>
+ In addition to the signature_algorithms extension from TLS 1.2,
+ <url href="http://www.ietf.org/rfc/rfc8446.txt#section-4.2.3">TLS 1.3
+ (RFC 5246 Section 4.2.3)</url>adds the signature_algorithms_cert extension
+ which enables having special requirements on the signatures used in the
+ certificates that differs from the requirements on digital signatures as a whole.
+ If this is not required this extension is not needed.
+ </p>
+ <p>
+ The client will send a signature_algorithms_cert extension (ClientHello),
+ if TLS version 1.3 or later is used, and the signature_algs_cert option is
+ explicitly specified. By default, only the signature_algs extension is sent.
+ </p>
+ <p>
+ The signature schemes shall be ordered according to the client's preference
+ (favorite choice first).
+ </p>
+ </item>
</taglist>
</section>
@@ -860,7 +897,6 @@ fun(srp, Username :: string(), UserState :: term()) ->
negotiation, introduced in TLS-1.2. The algorithms will also be offered to the client if a
client certificate is requested. For more details see the <seealso marker="#client_signature_algs">corresponding client option</seealso>.
</p> </item>
-
</taglist>
</section>
diff --git a/lib/ssl/src/dtls_handshake.erl b/lib/ssl/src/dtls_handshake.erl
index c8daa11433..55aa8174a3 100644
--- a/lib/ssl/src/dtls_handshake.erl
+++ b/lib/ssl/src/dtls_handshake.erl
@@ -194,7 +194,7 @@ handle_client_hello(Version,
?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY);
_ ->
#{key_exchange := KeyExAlg} = ssl_cipher_format:suite_definition(CipherSuite),
- case ssl_handshake:select_hashsign(ClientHashSigns, Cert, KeyExAlg,
+ case ssl_handshake:select_hashsign({ClientHashSigns, undefined}, Cert, KeyExAlg,
SupportedHashSigns, TLSVersion) of
#alert{} = Alert ->
Alert;
diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl
index ef9aac34bf..3319aadd68 100644
--- a/lib/ssl/src/ssl.erl
+++ b/lib/ssl/src/ssl.erl
@@ -614,6 +614,25 @@ getopts(#sslsocket{}, OptionTags) ->
%%
%% Description: Sets options
%%--------------------------------------------------------------------
+setopts(#sslsocket{pid = [Pid, Sender]}, Options0) when is_pid(Pid), is_list(Options0) ->
+ try proplists:expand([{binary, [{mode, binary}]},
+ {list, [{mode, list}]}], Options0) of
+ Options ->
+ case proplists:get_value(packet, Options, undefined) of
+ undefined ->
+ ssl_connection:set_opts(Pid, Options);
+ PacketOpt ->
+ case tls_sender:setopts(Sender, [{packet, PacketOpt}]) of
+ ok ->
+ ssl_connection:set_opts(Pid, Options);
+ Error ->
+ Error
+ end
+ end
+ catch
+ _:_ ->
+ {error, {options, {not_a_proplist, Options0}}}
+ end;
setopts(#sslsocket{pid = [Pid|_]}, Options0) when is_pid(Pid), is_list(Options0) ->
try proplists:expand([{binary, [{mode, binary}]},
{list, [{mode, list}]}], Options0) of
@@ -975,10 +994,7 @@ handle_options(Opts0, Role, Host) ->
proplists:get_value(
signature_algs_cert,
Opts,
- default_option_role(server,
- tls_v1:default_signature_schemes(HighestVersion),
- Role
- )),
+ undefined), %% Do not send by default
tls_version(HighestVersion)),
%% Server side option
reuse_session = handle_option(reuse_session, Opts, ReuseSessionFun),
@@ -1041,8 +1057,8 @@ handle_options(Opts0, Role, Host) ->
alpn_preferred_protocols, next_protocols_advertised,
client_preferred_next_protocols, log_alert, log_level,
server_name_indication, honor_cipher_order, padding_check, crl_check, crl_cache,
- fallback, signature_algs, eccs, honor_ecc_order, beast_mitigation,
- max_handshake_size, handshake, customize_hostname_check],
+ fallback, signature_algs, signature_algs_cert, eccs, honor_ecc_order,
+ beast_mitigation, max_handshake_size, handshake, customize_hostname_check],
SockOpts = lists:foldl(fun(Key, PropList) ->
proplists:delete(Key, PropList)
end, Opts, SslOptions),
@@ -1326,8 +1342,6 @@ handle_signature_algorithms_option(Value, Version) when is_list(Value)
_ ->
Value
end;
-handle_signature_algorithms_option(_, Version) when Version >= {3, 4} ->
- handle_signature_algorithms_option(tls_v1:default_signature_schemes(Version), Version);
handle_signature_algorithms_option(_, _Version) ->
undefined.
@@ -1645,6 +1659,14 @@ new_ssl_options([{signature_algs, Value} | Rest], #ssl_options{} = Opts, RecordC
handle_hashsigns_option(Value,
tls_version(RecordCB:highest_protocol_version()))},
RecordCB);
+new_ssl_options([{signature_algs_cert, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
+ new_ssl_options(
+ Rest,
+ Opts#ssl_options{signature_algs_cert =
+ handle_signature_algorithms_option(
+ Value,
+ tls_version(RecordCB:highest_protocol_version()))},
+ RecordCB);
new_ssl_options([{protocol, dtls = Value} | Rest], #ssl_options{} = Opts, dtls_record = RecordCB) ->
new_ssl_options(Rest, Opts#ssl_options{protocol = Value}, RecordCB);
new_ssl_options([{protocol, tls = Value} | Rest], #ssl_options{} = Opts, tls_record = RecordCB) ->
diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl
index 33d60ee0e6..6e602eac23 100644
--- a/lib/ssl/src/ssl_connection.erl
+++ b/lib/ssl/src/ssl_connection.erl
@@ -335,21 +335,12 @@ prf(ConnectionPid, Secret, Label, Seed, WantedLength) ->
%%====================================================================
%% Alert and close handling
%%====================================================================
-handle_own_alert(Alert, Version, StateName,
+handle_own_alert(Alert, _, StateName,
#state{role = Role,
- transport_cb = Transport,
- socket = Socket,
protocol_cb = Connection,
- connection_states = ConnectionStates,
ssl_options = SslOpts} = State) ->
try %% Try to tell the other side
- {BinMsg, _} =
- Connection:encode_alert(Alert, Version, ConnectionStates),
- Connection:send(Transport, Socket, BinMsg),
- Report = #{direction => outbound,
- protocol => 'tls_record',
- message => BinMsg},
- ssl_logger:debug(SslOpts#ssl_options.log_level, Report, #{domain => [otp,ssl,tls_record]})
+ send_alert(Alert, StateName, State)
catch _:_ -> %% Can crash if we are in a uninitialized state
ignore
end,
@@ -857,7 +848,9 @@ certify(internal, #certificate_request{} = CertRequest,
role = client,
ssl_options = #ssl_options{signature_algs = SupportedHashSigns},
negotiated_version = Version} = State0, Connection) ->
- case ssl_handshake:select_hashsign(CertRequest, Cert, SupportedHashSigns, ssl:tls_version(Version)) of
+ case ssl_handshake:select_hashsign(CertRequest, Cert,
+ SupportedHashSigns,
+ ssl:tls_version(Version)) of
#alert {} = Alert ->
handle_own_alert(Alert, Version, ?FUNCTION_NAME, State0);
NegotiatedHashSign ->
@@ -1170,24 +1163,20 @@ handle_call({close, {Pid, Timeout}}, From, StateName, State0, Connection) when i
%% we must recive the close alert from the peer before releasing the
%% transport socket.
{next_state, downgrade, State#state{terminated = true}, [{timeout, Timeout, downgrade}]};
-handle_call({close, _} = Close, From, StateName, State, Connection) ->
+handle_call({close, _} = Close, From, StateName, State, _Connection) ->
%% Run terminate before returning so that the reuseaddr
%% inet-option works properly
- Result = Connection:terminate(Close, StateName, State#state{terminated = true}),
+ Result = terminate(Close, StateName, State),
stop_and_reply(
{shutdown, normal},
- {reply, From, Result}, State);
-handle_call({shutdown, How0}, From, _,
+ {reply, From, Result}, State#state{terminated = true});
+handle_call({shutdown, How0}, From, StateName,
#state{transport_cb = Transport,
- negotiated_version = Version,
- connection_states = ConnectionStates,
- socket = Socket} = State, Connection) ->
+ socket = Socket} = State, _) ->
case How0 of
How when How == write; How == both ->
- Alert = ?ALERT_REC(?WARNING, ?CLOSE_NOTIFY),
- {BinMsg, _} =
- Connection:encode_alert(Alert, Version, ConnectionStates),
- Connection:send(Transport, Socket, BinMsg);
+ send_alert(?ALERT_REC(?WARNING, ?CLOSE_NOTIFY),
+ StateName, State);
_ ->
ok
end,
@@ -1353,14 +1342,20 @@ terminate({shutdown, own_alert}, _StateName, #state{
_ ->
Connection:close({timeout, ?DEFAULT_TIMEOUT}, Socket, Transport, undefined, undefined)
end;
+terminate(downgrade = Reason, connection, #state{protocol_cb = Connection,
+ transport_cb = Transport, socket = Socket
+ } = State) ->
+ handle_trusted_certs_db(State),
+ Connection:close(Reason, Socket, Transport, undefined, undefined);
terminate(Reason, connection, #state{protocol_cb = Connection,
- connection_states = ConnectionStates,
- ssl_options = #ssl_options{padding_check = Check},
- transport_cb = Transport, socket = Socket
- } = State) ->
+ connection_states = ConnectionStates,
+ ssl_options = #ssl_options{padding_check = Check},
+ transport_cb = Transport, socket = Socket
+ } = State) ->
handle_trusted_certs_db(State),
Alert = terminate_alert(Reason),
- ok = Connection:send_alert_in_connection(Alert, State),
+ %% Send the termination ALERT if possible
+ catch (ok = Connection:send_alert_in_connection(Alert, State)),
Connection:close(Reason, Socket, Transport, ConnectionStates, Check);
terminate(Reason, _StateName, #state{transport_cb = Transport, protocol_cb = Connection,
socket = Socket
@@ -1397,6 +1392,11 @@ format_status(terminate, [_, StateName, State]) ->
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
+send_alert(Alert, connection, #state{protocol_cb = Connection} = State) ->
+ Connection:send_alert_in_connection(Alert, State);
+send_alert(Alert, _, #state{protocol_cb = Connection} = State) ->
+ Connection:send_alert(Alert, State).
+
connection_info(#state{sni_hostname = SNIHostname,
session = #session{session_id = SessionId,
cipher_suite = CipherSuite, ecc = ECCCurve},
diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl
index ba0b670091..1e57dfd710 100644
--- a/lib/ssl/src/ssl_handshake.erl
+++ b/lib/ssl/src/ssl_handshake.erl
@@ -1015,12 +1015,17 @@ client_hello_extensions(Version, CipherSuites,
{3,4} ->
HelloExtensions#{client_hello_versions =>
#client_hello_versions{versions = Versions},
- signature_algs_cert =>
- #signature_scheme_list{signature_scheme_list = SignatureSchemes}};
+ signature_algs_cert =>
+ signature_scheme_list(SignatureSchemes)};
_Else ->
HelloExtensions
end.
+signature_scheme_list(undefined) ->
+ undefined;
+signature_scheme_list(SignatureSchemes) ->
+ #signature_scheme_list{signature_scheme_list = SignatureSchemes}.
+
handle_client_hello_extensions(RecordCB, Random, ClientCipherSuites,
Exts, Version,
#ssl_options{secure_renegotiate = SecureRenegotation,
@@ -1128,26 +1133,50 @@ select_hashsign(_, _, KeyExAlgo, _, _Version) when KeyExAlgo == dh_anon;
{null, anon};
%% The signature_algorithms extension was introduced with TLS 1.2. Ignore it if we have
%% negotiated a lower version.
-select_hashsign(HashSigns, Cert, KeyExAlgo,
- undefined, {Major, Minor} = Version) when Major >= 3 andalso Minor >= 3->
- select_hashsign(HashSigns, Cert, KeyExAlgo, tls_v1:default_signature_algs(Version), Version);
-select_hashsign(#hash_sign_algos{hash_sign_algos = HashSigns}, Cert, KeyExAlgo, SupportedHashSigns,
- {Major, Minor}) when Major >= 3 andalso Minor >= 3 ->
- #'OTPCertificate'{tbsCertificate = TBSCert} = public_key:pkix_decode_cert(Cert, otp),
- #'OTPSubjectPublicKeyInfo'{algorithm = {_, SubjAlgo, _}} =
- TBSCert#'OTPTBSCertificate'.subjectPublicKeyInfo,
-
- SubSign = sign_algo(SubjAlgo),
-
- case lists:filter(fun({_, S} = Algos) when S == SubSign ->
- is_acceptable_hash_sign(Algos, KeyExAlgo, SupportedHashSigns);
- (_) ->
- false
- end, HashSigns) of
- [] ->
- ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, no_suitable_signature_algorithm);
- [HashSign | _] ->
- HashSign
+select_hashsign({ClientHashSigns, ClientSignatureSchemes},
+ Cert, KeyExAlgo, undefined, {Major, Minor} = Version)
+ when Major >= 3 andalso Minor >= 3->
+ select_hashsign({ClientHashSigns, ClientSignatureSchemes}, Cert, KeyExAlgo,
+ tls_v1:default_signature_algs(Version), Version);
+select_hashsign({#hash_sign_algos{hash_sign_algos = ClientHashSigns},
+ ClientSignatureSchemes0},
+ Cert, KeyExAlgo, SupportedHashSigns, {Major, Minor})
+ when Major >= 3 andalso Minor >= 3 ->
+ ClientSignatureSchemes = get_signature_scheme(ClientSignatureSchemes0),
+ {SignAlgo0, Param, PublicKeyAlgo0} = get_cert_params(Cert),
+ SignAlgo = sign_algo(SignAlgo0),
+ PublicKeyAlgo = public_key_algo(PublicKeyAlgo0),
+
+ %% RFC 5246 (TLS 1.2)
+ %% If the client provided a "signature_algorithms" extension, then all
+ %% certificates provided by the server MUST be signed by a
+ %% hash/signature algorithm pair that appears in that extension.
+ %%
+ %% RFC 8446 (TLS 1.3)
+ %% TLS 1.3 provides two extensions for indicating which signature
+ %% algorithms may be used in digital signatures. The
+ %% "signature_algorithms_cert" extension applies to signatures in
+ %% certificates and the "signature_algorithms" extension, which
+ %% originally appeared in TLS 1.2, applies to signatures in
+ %% CertificateVerify messages.
+ %%
+ %% If no "signature_algorithms_cert" extension is
+ %% present, then the "signature_algorithms" extension also applies to
+ %% signatures appearing in certificates.
+ case is_supported_sign(SignAlgo, Param, ClientHashSigns, ClientSignatureSchemes) of
+ true ->
+ case lists:filter(fun({_, S} = Algos) when S == PublicKeyAlgo ->
+ is_acceptable_hash_sign(Algos, KeyExAlgo, SupportedHashSigns);
+ (_) ->
+ false
+ end, ClientHashSigns) of
+ [] ->
+ ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, no_suitable_signature_algorithm);
+ [HashSign | _] ->
+ HashSign
+ end;
+ false ->
+ ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, no_suitable_signature_algorithm)
end;
select_hashsign(_, Cert, _, _, Version) ->
#'OTPCertificate'{tbsCertificate = TBSCert} = public_key:pkix_decode_cert(Cert, otp),
@@ -1161,21 +1190,23 @@ select_hashsign(_, Cert, _, _, Version) ->
%%
%% Description: Handles signature algorithms selection for certificate requests (client)
%%--------------------------------------------------------------------
-select_hashsign(#certificate_request{hashsign_algorithms = #hash_sign_algos{hash_sign_algos = HashSigns},
- certificate_types = Types}, Cert, SupportedHashSigns,
+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->
- #'OTPCertificate'{tbsCertificate = TBSCert} = public_key:pkix_decode_cert(Cert, otp),
- #'OTPCertificate'{tbsCertificate = TBSCert,
- signatureAlgorithm = {_,SignAlgo, _}} = public_key:pkix_decode_cert(Cert, otp),
- #'OTPSubjectPublicKeyInfo'{algorithm = {_, SubjAlgo, _}} =
- TBSCert#'OTPTBSCertificate'.subjectPublicKeyInfo,
-
- Sign = sign_algo(SignAlgo),
- SubSign = sign_algo(SubjAlgo),
-
- case is_acceptable_cert_type(SubSign, HashSigns, Types) andalso is_supported_sign(Sign, HashSigns) of
+ {SignAlgo0, Param, PublicKeyAlgo0} = get_cert_params(Cert),
+ SignAlgo = sign_algo(SignAlgo0),
+ PublicKeyAlgo = public_key_algo(PublicKeyAlgo0),
+
+ case is_acceptable_cert_type(PublicKeyAlgo, Types) andalso
+ %% certificate_request has no "signature_algorithms_cert"
+ %% extension in TLS 1.2.
+ is_supported_sign(SignAlgo, Param, HashSigns, undefined) of
true ->
- case lists:filter(fun({_, S} = Algos) when S == SubSign ->
+ case lists:filter(fun({_, S} = Algos) when S == PublicKeyAlgo ->
is_acceptable_hash_sign(Algos, SupportedHashSigns);
(_) ->
false
@@ -1188,8 +1219,38 @@ select_hashsign(#certificate_request{hashsign_algorithms = #hash_sign_algos{hash
false ->
?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, no_suitable_signature_algorithm)
end;
-select_hashsign(#certificate_request{}, Cert, _, Version) ->
- select_hashsign(undefined, Cert, undefined, [], Version).
+select_hashsign(#certificate_request{certificate_types = Types}, Cert, _, Version) ->
+ {_, _, PublicKeyAlgo0} = get_cert_params(Cert),
+ PublicKeyAlgo = public_key_algo(PublicKeyAlgo0),
+
+ %% Check cert even for TLS 1.0/1.1
+ case is_acceptable_cert_type(PublicKeyAlgo, Types) of
+ true ->
+ select_hashsign(undefined, Cert, undefined, [], Version);
+ false ->
+ ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, no_suitable_signature_algorithm)
+ end.
+
+
+%% Gets the relevant parameters of a certificate:
+%% - signature algorithm
+%% - parameters of the signature algorithm
+%% - public key algorithm (key type)
+get_cert_params(Cert) ->
+ #'OTPCertificate'{tbsCertificate = TBSCert,
+ signatureAlgorithm =
+ {_,SignAlgo, Param}} = public_key:pkix_decode_cert(Cert, otp),
+ #'OTPSubjectPublicKeyInfo'{algorithm = {_, PublicKeyAlgo, _}} =
+ TBSCert#'OTPTBSCertificate'.subjectPublicKeyInfo,
+ {SignAlgo, Param, PublicKeyAlgo}.
+
+
+get_signature_scheme(undefined) ->
+ undefined;
+get_signature_scheme(#signature_scheme_list{
+ signature_scheme_list = ClientSignatureSchemes}) ->
+ ClientSignatureSchemes.
+
%%--------------------------------------------------------------------
-spec select_hashsign_algs({atom(), atom()}| undefined, oid(), ssl_record:ssl_version()) ->
@@ -1258,33 +1319,30 @@ int_to_bin(I) ->
L = (length(integer_to_list(I, 16)) + 1) div 2,
<<I:(L*8)>>.
-certificate_types(_, {N, M}) when N >= 3 andalso M >= 3 ->
- case proplists:get_bool(ecdsa,
- proplists:get_value(public_keys, crypto:supports())) of
- true ->
- <<?BYTE(?ECDSA_SIGN), ?BYTE(?RSA_SIGN), ?BYTE(?DSS_SIGN)>>;
- false ->
- <<?BYTE(?RSA_SIGN), ?BYTE(?DSS_SIGN)>>
- end;
-
-certificate_types(#{key_exchange := KeyExchange}, _) when KeyExchange == rsa;
- KeyExchange == dh_rsa;
- KeyExchange == dhe_rsa;
- KeyExchange == ecdhe_rsa ->
- <<?BYTE(?RSA_SIGN)>>;
-
-certificate_types(#{key_exchange := KeyExchange}, _) when KeyExchange == dh_dss;
- KeyExchange == dhe_dss;
- KeyExchange == srp_dss ->
- <<?BYTE(?DSS_SIGN)>>;
-
-certificate_types(#{key_exchange := KeyExchange}, _) when KeyExchange == dh_ecdsa;
- KeyExchange == dhe_ecdsa;
- KeyExchange == ecdh_ecdsa;
- KeyExchange == ecdhe_ecdsa ->
- <<?BYTE(?ECDSA_SIGN)>>;
+%% TLS 1.0+
+%% The end-entity certificate provided by the client MUST contain a
+%% key that is compatible with certificate_types.
+certificate_types(_, {N, M}) when N >= 3 andalso M >= 1 ->
+ ECDSA = supported_cert_type_or_empty(ecdsa, ?ECDSA_SIGN),
+ RSA = supported_cert_type_or_empty(rsa, ?RSA_SIGN),
+ DSS = supported_cert_type_or_empty(dss, ?DSS_SIGN),
+ <<ECDSA/binary,RSA/binary,DSS/binary>>;
+%% SSL 3.0
certificate_types(_, _) ->
- <<?BYTE(?RSA_SIGN)>>.
+ RSA = supported_cert_type_or_empty(rsa, ?RSA_SIGN),
+ DSS = supported_cert_type_or_empty(dss, ?DSS_SIGN),
+ <<RSA/binary,DSS/binary>>.
+
+%% Returns encoded certificate_type if algorithm is supported
+supported_cert_type_or_empty(Algo, Type) ->
+ case proplists:get_bool(
+ Algo,
+ proplists:get_value(public_keys, crypto:supports())) of
+ true ->
+ <<?BYTE(Type)>>;
+ false ->
+ <<>>
+ end.
certificate_authorities(CertDbHandle, CertDbRef) ->
Authorities = certificate_authorities_from_db(CertDbHandle, CertDbRef),
@@ -2355,17 +2413,6 @@ handle_srp_extension(undefined, Session) ->
handle_srp_extension(#srp{username = Username}, Session) ->
Session#session{srp_username = Username}.
-
-sign_algo(?rsaEncryption) ->
- rsa;
-sign_algo(?'id-ecPublicKey') ->
- ecdsa;
-sign_algo(?'id-dsa') ->
- dsa;
-sign_algo(Alg) ->
- {_, Sign} =public_key:pkix_sign_types(Alg),
- Sign.
-
is_acceptable_hash_sign( _, KeyExAlgo, _) when
KeyExAlgo == psk;
KeyExAlgo == dhe_psk;
@@ -2381,15 +2428,80 @@ is_acceptable_hash_sign(Algos,_, SupportedHashSigns) ->
is_acceptable_hash_sign(Algos, SupportedHashSigns) ->
lists:member(Algos, SupportedHashSigns).
-is_acceptable_cert_type(Sign, _HashSigns, Types) ->
+is_acceptable_cert_type(Sign, Types) ->
lists:member(sign_type(Sign), binary_to_list(Types)).
-is_supported_sign(Sign, HashSigns) ->
- [] =/= lists:dropwhile(fun({_, S}) when S =/= Sign ->
- true;
- (_)->
- false
- end, HashSigns).
+%% signature_algorithms_cert = undefined
+is_supported_sign(SignAlgo, _, HashSigns, undefined) ->
+ lists:member(SignAlgo, HashSigns);
+
+%% {'SignatureAlgorithm',{1,2,840,113549,1,1,11},'NULL'}
+is_supported_sign({Hash, Sign}, 'NULL', _, SignatureSchemes) ->
+ Fun = fun (Scheme, Acc) ->
+ {H0, S0, _} = ssl_cipher:scheme_to_components(Scheme),
+ S1 = case S0 of
+ rsa_pkcs1 -> rsa;
+ S -> S
+ end,
+ H1 = case H0 of
+ sha1 -> sha;
+ H -> H
+ end,
+ Acc orelse (Sign =:= S1 andalso
+ Hash =:= H1)
+ end,
+ lists:foldl(Fun, false, SignatureSchemes);
+
+%% TODO: Implement validation for the curve used in the signature
+%% RFC 3279 - 2.2.3 ECDSA Signature Algorithm
+%% When the ecdsa-with-SHA1 algorithm identifier appears as the
+%% algorithm field in an AlgorithmIdentifier, the encoding MUST omit the
+%% parameters field. That is, the AlgorithmIdentifier SHALL be a
+%% SEQUENCE of one component: the OBJECT IDENTIFIER ecdsa-with-SHA1.
+%%
+%% The elliptic curve parameters in the subjectPublicKeyInfo field of
+%% the certificate of the issuer SHALL apply to the verification of the
+%% signature.
+is_supported_sign({Hash, Sign}, _Param, _, SignatureSchemes) ->
+ Fun = fun (Scheme, Acc) ->
+ {H0, S0, _} = ssl_cipher:scheme_to_components(Scheme),
+ S1 = case S0 of
+ rsa_pkcs1 -> rsa;
+ S -> S
+ end,
+ H1 = case H0 of
+ sha1 -> sha;
+ H -> H
+ end,
+ Acc orelse (Sign =:= S1 andalso
+ Hash =:= H1)
+ end,
+ lists:foldl(Fun, false, SignatureSchemes).
+
+%% SupportedPublicKeyAlgorithms PUBLIC-KEY-ALGORITHM-CLASS ::= {
+%% dsa | rsa-encryption | dh | kea | ec-public-key }
+public_key_algo(?rsaEncryption) ->
+ rsa;
+public_key_algo(?'id-ecPublicKey') ->
+ ecdsa;
+public_key_algo(?'id-dsa') ->
+ dsa.
+
+%% SupportedSignatureAlgorithms SIGNATURE-ALGORITHM-CLASS ::= {
+%% dsa-with-sha1 | dsaWithSHA1 | md2-with-rsa-encryption |
+%% md5-with-rsa-encryption | sha1-with-rsa-encryption | sha-1with-rsa-encryption |
+%% sha224-with-rsa-encryption |
+%% sha256-with-rsa-encryption |
+%% sha384-with-rsa-encryption |
+%% sha512-with-rsa-encryption |
+%% ecdsa-with-sha1 |
+%% ecdsa-with-sha224 |
+%% ecdsa-with-sha256 |
+%% ecdsa-with-sha384 |
+%% ecdsa-with-sha512 }
+sign_algo(Alg) ->
+ public_key:pkix_sign_types(Alg).
+
sign_type(rsa) ->
?RSA_SIGN;
sign_type(dsa) ->
diff --git a/lib/ssl/src/tls_connection.erl b/lib/ssl/src/tls_connection.erl
index f85e00ea50..8ded2cbff7 100644
--- a/lib/ssl/src/tls_connection.erl
+++ b/lib/ssl/src/tls_connection.erl
@@ -57,7 +57,9 @@
empty_connection_state/2]).
%% Alert and close handling
--export([send_alert/2, send_alert_in_connection/2, encode_alert/3, close/5, protocol_name/0]).
+-export([send_alert/2, send_alert_in_connection/2,
+ send_sync_alert/2,
+ encode_alert/3, close/5, protocol_name/0]).
%% Data handling
-export([encode_data/3, passive_receive/2, next_record_if_active/1,
@@ -364,21 +366,38 @@ encode_alert(#alert{} = Alert, Version, ConnectionStates) ->
send_alert(Alert, #state{negotiated_version = Version,
socket = Socket,
- protocol_cb = Connection,
transport_cb = Transport,
connection_states = ConnectionStates0,
ssl_options = SslOpts} = StateData0) ->
- {BinMsg, ConnectionStates} =
- Connection:encode_alert(Alert, Version, ConnectionStates0),
- Connection:send(Transport, Socket, BinMsg),
+ {BinMsg, ConnectionStates} = encode_alert(Alert, Version, ConnectionStates0),
+ send(Transport, Socket, BinMsg),
Report = #{direction => outbound,
protocol => 'tls_record',
message => BinMsg},
ssl_logger:debug(SslOpts#ssl_options.log_level, Report, #{domain => [otp,ssl,tls_record]}),
StateData0#state{connection_states = ConnectionStates}.
-send_alert_in_connection(Alert, #state{protocol_specific = #{sender := Sender}}) ->
+%% If an ALERT sent in the connection state, should cause the TLS
+%% connection to end, we need to synchronize with the tls_sender
+%% process so that the ALERT if possible (that is the tls_sender process is
+%% not blocked) is sent before the connection process terminates and
+%% thereby closes the transport socket.
+send_alert_in_connection(#alert{level = ?FATAL} = Alert, State) ->
+ send_sync_alert(Alert, State);
+send_alert_in_connection(#alert{description = ?CLOSE_NOTIFY} = Alert, State) ->
+ send_sync_alert(Alert, State);
+send_alert_in_connection(Alert,
+ #state{protocol_specific = #{sender := Sender}}) ->
tls_sender:send_alert(Sender, Alert).
+send_sync_alert(Alert, #state{protocol_specific = #{sender := Sender}}= State) ->
+ tls_sender:send_and_ack_alert(Sender, Alert),
+ receive
+ {Sender, ack_alert} ->
+ ok
+ after ?DEFAULT_TIMEOUT ->
+ %% Sender is blocked terminate anyway
+ throw({stop, {shutdown, own_alert}, State})
+ end.
%% User closes or recursive call!
close({close, Timeout}, Socket, Transport = gen_tcp, _,_) ->
@@ -536,7 +555,9 @@ hello(internal, #client_hello{client_version = ClientVersion} = Hello,
case tls_handshake:hello(Hello, SslOpts, {Port, Session0, Cache, CacheCb,
ConnectionStates0, Cert, KeyExAlg}, Renegotiation) of
#alert{} = Alert ->
- ssl_connection:handle_own_alert(Alert, ClientVersion, hello, State);
+ ssl_connection:handle_own_alert(Alert, ClientVersion, hello,
+ State#state{negotiated_version
+ = ClientVersion});
{Version, {Type, Session},
ConnectionStates, Protocol0, ServerHelloExt, HashSign} ->
Protocol = case Protocol0 of
@@ -559,7 +580,8 @@ hello(internal, #server_hello{} = Hello,
ssl_options = SslOptions} = State) ->
case tls_handshake:hello(Hello, SslOptions, ConnectionStates0, Renegotiation) of
#alert{} = Alert ->
- ssl_connection:handle_own_alert(Alert, ReqVersion, hello, State);
+ ssl_connection:handle_own_alert(Alert, ReqVersion, hello,
+ State#state{negotiated_version = ReqVersion});
{Version, NewId, ConnectionStates, ProtoExt, Protocol} ->
ssl_connection:handle_session(Hello,
Version, NewId, ConnectionStates, ProtoExt, Protocol, State)
@@ -667,8 +689,8 @@ callback_mode() ->
state_functions.
terminate(Reason, StateName, State) ->
- ensure_sender_terminate(Reason, State),
- catch ssl_connection:terminate(Reason, StateName, State).
+ catch ssl_connection:terminate(Reason, StateName, State),
+ ensure_sender_terminate(Reason, State).
format_status(Type, Data) ->
ssl_connection:format_status(Type, Data).
@@ -827,8 +849,8 @@ handle_info({CloseTag, Socket}, StateName,
%% and then receive the final message.
next_event(StateName, no_record, State)
end;
-handle_info({'EXIT', Pid, Reason}, _,
- #state{protocol_specific = Pid} = State) ->
+handle_info({'EXIT', Sender, Reason}, _,
+ #state{protocol_specific = #{sender := Sender}} = State) ->
{stop, {shutdown, sender_died, Reason}, State};
handle_info(Msg, StateName, State) ->
ssl_connection:StateName(info, Msg, State, ?MODULE).
diff --git a/lib/ssl/src/tls_handshake.erl b/lib/ssl/src/tls_handshake.erl
index 050b4be870..b39a7732e7 100644
--- a/lib/ssl/src/tls_handshake.erl
+++ b/lib/ssl/src/tls_handshake.erl
@@ -276,6 +276,7 @@ handle_client_hello(Version,
true ->
Curves = maps:get(elliptic_curves, HelloExt, undefined),
ClientHashSigns = maps:get(signature_algs, HelloExt, undefined),
+ ClientSignatureSchemes = maps:get(signature_algs_cert, HelloExt, undefined),
AvailableHashSigns = ssl_handshake:available_signature_algs(
ClientHashSigns, SupportedHashSigns, Cert, Version),
ECCCurve = ssl_handshake:select_curve(Curves, SupportedECCs, ECCOrder),
@@ -289,8 +290,10 @@ handle_client_hello(Version,
?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, no_suitable_ciphers);
_ ->
#{key_exchange := KeyExAlg} = ssl_cipher_format:suite_definition(CipherSuite),
- case ssl_handshake:select_hashsign(ClientHashSigns, Cert, KeyExAlg,
- SupportedHashSigns, Version) of
+ case ssl_handshake:select_hashsign({ClientHashSigns, ClientSignatureSchemes},
+ Cert, KeyExAlg,
+ SupportedHashSigns,
+ Version) of
#alert{} = Alert ->
Alert;
HashSign ->
diff --git a/lib/ssl/src/tls_sender.erl b/lib/ssl/src/tls_sender.erl
index db67d7ddff..1c3c44cfe5 100644
--- a/lib/ssl/src/tls_sender.erl
+++ b/lib/ssl/src/tls_sender.erl
@@ -28,7 +28,8 @@
-include("ssl_api.hrl").
%% API
--export([start/0, start/1, initialize/2, send_data/2, send_alert/2, renegotiate/1,
+-export([start/0, start/1, initialize/2, send_data/2, send_alert/2,
+ send_and_ack_alert/2, setopts/2, renegotiate/1,
update_connection_state/3, dist_tls_socket/1, dist_handshake_complete/3]).
%% gen_statem callbacks
@@ -80,7 +81,7 @@ initialize(Pid, InitMsg) ->
gen_statem:call(Pid, {self(), InitMsg}).
%%--------------------------------------------------------------------
--spec send_data(pid(), iodata()) -> ok.
+-spec send_data(pid(), iodata()) -> ok | {error, term()}.
%% Description: Send application data
%%--------------------------------------------------------------------
send_data(Pid, AppData) ->
@@ -89,13 +90,27 @@ send_data(Pid, AppData) ->
%%--------------------------------------------------------------------
-spec send_alert(pid(), #alert{}) -> _.
-%% Description: TLS connection process wants to end an Alert
+%% Description: TLS connection process wants to send an Alert
%% in the connection state.
%%--------------------------------------------------------------------
send_alert(Pid, Alert) ->
gen_statem:cast(Pid, Alert).
%%--------------------------------------------------------------------
+-spec send_and_ack_alert(pid(), #alert{}) -> _.
+%% Description: TLS connection process wants to send an Alert
+%% in the connection state and recive an ack.
+%%--------------------------------------------------------------------
+send_and_ack_alert(Pid, Alert) ->
+ gen_statem:cast(Pid, {ack_alert, Alert}).
+%%--------------------------------------------------------------------
+-spec setopts(pid(), [{packet, integer() | atom()}]) -> ok | {error, term()}.
+%% Description: Send application data
+%%--------------------------------------------------------------------
+setopts(Pid, Opts) ->
+ call(Pid, {set_opts, Opts}).
+
+%%--------------------------------------------------------------------
-spec renegotiate(pid()) -> {ok, WriteState::map()} | {error, closed}.
%% Description: So TLS connection process can synchronize the
%% encryption state to be used when handshaking.
@@ -192,6 +207,8 @@ connection({call, From}, {application_data, AppData},
Data ->
send_application_data(Data, From, ?FUNCTION_NAME, StateData)
end;
+connection({call, From}, {set_opts, _} = Call, StateData) ->
+ handle_call(From, Call, ?FUNCTION_NAME, StateData);
connection({call, From}, dist_get_tls_socket,
#data{protocol_cb = Connection,
transport_cb = Transport,
@@ -207,6 +224,10 @@ connection({call, From}, {dist_handshake_complete, _Node, DHandle}, #data{connec
process_flag(priority, normal),
Events = dist_data_events(DHandle, []),
{next_state, ?FUNCTION_NAME, StateData#data{dist_handle = DHandle}, [{reply, From, ok} | Events]};
+connection(cast, {ack_alert, #alert{} = Alert}, #data{connection_pid = Pid} =StateData0) ->
+ StateData = send_tls_alert(Alert, StateData0),
+ Pid ! {self(), ack_alert},
+ {next_state, ?FUNCTION_NAME, StateData};
connection(cast, #alert{} = Alert, StateData0) ->
StateData = send_tls_alert(Alert, StateData0),
{next_state, ?FUNCTION_NAME, StateData};
@@ -241,6 +262,8 @@ connection(info, Msg, StateData) ->
StateData :: term()) ->
gen_statem:event_handler_result(atom()).
%%--------------------------------------------------------------------
+handshake({call, From}, {set_opts, _} = Call, StateData) ->
+ handle_call(From, Call, ?FUNCTION_NAME, StateData);
handshake({call, _}, _, _) ->
{keep_state_and_data, [postpone]};
handshake(cast, {new_write, WritesState, Version},
@@ -285,6 +308,9 @@ code_change(_OldVsn, State, Data, _Extra) ->
%%%===================================================================
%%% Internal functions
%%%===================================================================
+handle_call(From, {set_opts, Opts}, StateName, #data{socket_options = SockOpts} = StateData) ->
+ {next_state, StateName, StateData#data{socket_options = set_opts(SockOpts, Opts)}, [{reply, From, ok}]}.
+
handle_info({'DOWN', Monitor, _, _, Reason}, _,
#data{connection_monitor = Monitor,
dist_handle = Handle} = StateData) when Handle =/= undefined->
@@ -303,6 +329,11 @@ send_tls_alert(Alert, #data{negotiated_version = Version,
{BinMsg, ConnectionStates} =
Connection:encode_alert(Alert, Version, ConnectionStates0),
Connection:send(Transport, Socket, BinMsg),
+ %% TODO: fix ssl_options for this process
+ %% Report = #{direction => outbound,
+ %% protocol => 'tls_record',
+ %% message => BinMsg},
+ %% ssl_logger:debug(SslOpts#ssl_options.log_level, Report, #{domain => [otp,ssl,tls_record]}),
StateData0#data{connection_states = ConnectionStates}.
send_application_data(Data, From, StateName,
@@ -351,6 +382,10 @@ encode_size_packet(Bin, Size, Max) ->
false ->
<<Len:Size, Bin/binary>>
end.
+
+set_opts(SocketOptions, [{packet, N}]) ->
+ SocketOptions#socket_options{packet = N}.
+
time_to_renegotiate(_Data,
#{current_write := #{sequence_number := Num}},
RenegotiateAt) ->
diff --git a/lib/ssl/test/make_certs.erl b/lib/ssl/test/make_certs.erl
index 8fe7c54549..578f6a731a 100644
--- a/lib/ssl/test/make_certs.erl
+++ b/lib/ssl/test/make_certs.erl
@@ -365,7 +365,7 @@ req_cnf(Root, C) ->
"default_bits = ", integer_to_list(C#config.default_bits), "\n"
"RANDFILE = $ROOTDIR/RAND\n"
"encrypt_key = no\n"
- "default_md = md5\n"
+ "default_md = sha1\n"
"#string_mask = pkix\n"
"x509_extensions = ca_ext\n"
"prompt = no\n"
@@ -415,7 +415,7 @@ ca_cnf(
["crl_extensions = crl_ext\n" || C#config.v2_crls],
"unique_subject = no\n"
"default_days = 3600\n"
- "default_md = md5\n"
+ "default_md = sha1\n"
"preserve = no\n"
"policy = policy_match\n"
"\n"
@@ -499,7 +499,7 @@ ca_cnf(
["crl_extensions = crl_ext\n" || C#config.v2_crls],
"unique_subject = no\n"
"default_days = 3600\n"
- "default_md = md5\n"
+ "default_md = sha1\n"
"preserve = no\n"
"policy = policy_match\n"
"\n"
diff --git a/lib/ssl/test/ssl_alpn_handshake_SUITE.erl b/lib/ssl/test/ssl_alpn_handshake_SUITE.erl
index 27062d4801..04c4b257d9 100644
--- a/lib/ssl/test/ssl_alpn_handshake_SUITE.erl
+++ b/lib/ssl/test/ssl_alpn_handshake_SUITE.erl
@@ -155,7 +155,7 @@ empty_client(Config) when is_list(Config) ->
run_failing_handshake(Config,
[{alpn_advertised_protocols, []}],
[{alpn_preferred_protocols, [<<"spdy/2">>, <<"spdy/3">>, <<"http/2">>]}],
- {connect_failed,{tls_alert,"no application protocol"}}).
+ {error,{tls_alert,"no application protocol"}}).
%--------------------------------------------------------------------------------
@@ -163,7 +163,7 @@ empty_server(Config) when is_list(Config) ->
run_failing_handshake(Config,
[{alpn_advertised_protocols, [<<"http/1.0">>, <<"http/1.1">>]}],
[{alpn_preferred_protocols, []}],
- {connect_failed,{tls_alert,"no application protocol"}}).
+ {error,{tls_alert,"no application protocol"}}).
%--------------------------------------------------------------------------------
@@ -171,7 +171,7 @@ empty_client_empty_server(Config) when is_list(Config) ->
run_failing_handshake(Config,
[{alpn_advertised_protocols, []}],
[{alpn_preferred_protocols, []}],
- {connect_failed,{tls_alert,"no application protocol"}}).
+ {error,{tls_alert,"no application protocol"}}).
%--------------------------------------------------------------------------------
@@ -179,7 +179,7 @@ no_matching_protocol(Config) when is_list(Config) ->
run_failing_handshake(Config,
[{alpn_advertised_protocols, [<<"http/1.0">>, <<"http/1.1">>]}],
[{alpn_preferred_protocols, [<<"spdy/2">>, <<"spdy/3">>, <<"http/2">>]}],
- {connect_failed,{tls_alert,"no application protocol"}}).
+ {error,{tls_alert,"no application protocol"}}).
%--------------------------------------------------------------------------------
@@ -342,18 +342,19 @@ run_failing_handshake(Config, ClientExtraOpts, ServerExtraOpts, ExpectedResult)
ServerOpts = ServerExtraOpts ++ ssl_test_lib:ssl_options(server_rsa_opts, Config),
{ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0},
{from, self()},
{mfa, {?MODULE, placeholder, []}},
{options, ServerOpts}]),
Port = ssl_test_lib:inet_port(Server),
- ExpectedResult
- = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {?MODULE, placeholder, []}},
- {options, ClientOpts}]).
+ Client = ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, placeholder, []}},
+ {options, ClientOpts}]),
+ ssl_test_lib:check_result(Server, ExpectedResult,
+ Client, ExpectedResult).
run_handshake(Config, ClientExtraOpts, ServerExtraOpts, ExpectedProtocol) ->
Data = "hello world",
diff --git a/lib/ssl/test/ssl_basic_SUITE.erl b/lib/ssl/test/ssl_basic_SUITE.erl
index 1970c16f1d..97b794c3e2 100644
--- a/lib/ssl/test/ssl_basic_SUITE.erl
+++ b/lib/ssl/test/ssl_basic_SUITE.erl
@@ -1183,16 +1183,16 @@ fallback(Config) when is_list(Config) ->
Port = ssl_test_lib:inet_port(Server),
- Client =
- ssl_test_lib:start_client_error([{node, ClientNode},
- {port, Port}, {host, Hostname},
- {from, self()}, {options,
- [{fallback, true},
- {versions, ['tlsv1']}
- | ClientOpts]}]),
+ Client =
+ ssl_test_lib:start_client_error([{node, ClientNode},
+ {port, Port}, {host, Hostname},
+ {from, self()}, {options,
+ [{fallback, true},
+ {versions, ['tlsv1']}
+ | ClientOpts]}]),
- ssl_test_lib:check_result(Server, {error,{tls_alert,"inappropriate fallback"}},
- Client, {error,{tls_alert,"inappropriate fallback"}}).
+ ssl_test_lib:check_result(Server, {error,{tls_alert,"inappropriate fallback"}},
+ Client, {error,{tls_alert,"inappropriate fallback"}}).
%%--------------------------------------------------------------------
cipher_format() ->
@@ -2645,14 +2645,14 @@ default_reject_anonymous(Config) when is_list(Config) ->
{options, ServerOpts}]),
Port = ssl_test_lib:inet_port(Server),
Client = ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {options,
- [{ciphers,[CipherSuite]} |
- ClientOpts]}]),
+ {host, Hostname},
+ {from, self()},
+ {options,
+ [{ciphers,[CipherSuite]} |
+ ClientOpts]}]),
ssl_test_lib:check_result(Server, {error, {tls_alert, "insufficient security"}},
- Client, {error, {tls_alert, "insufficient security"}}).
+ Client, {error, {tls_alert, "insufficient security"}}).
%%--------------------------------------------------------------------
ciphers_ecdsa_signed_certs() ->
@@ -3570,14 +3570,14 @@ conf_signature_algs(Config) when is_list(Config) ->
ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
{from, self()},
{mfa, {ssl_test_lib, send_recv_result, []}},
- {options, [{active, false}, {signature_algs, [{sha256, rsa}]} | ServerOpts]}]),
+ {options, [{active, false}, {signature_algs, [{sha, rsa}]} | 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, []}},
- {options, [{active, false}, {signature_algs, [{sha256, rsa}]} | ClientOpts]}]),
+ {options, [{active, false}, {signature_algs, [{sha, rsa}]} | ClientOpts]}]),
ct:log("Testcase ~p, Client ~p Server ~p ~n",
[self(), Client, Server]),
@@ -3605,14 +3605,14 @@ no_common_signature_algs(Config) when is_list(Config) ->
| ServerOpts]}]),
Port = ssl_test_lib:inet_port(Server),
Client = ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {options, [{signature_algs, [{sha384, rsa}]}
- | ClientOpts]}]),
+ {host, Hostname},
+ {from, self()},
+ {options, [{signature_algs, [{sha384, rsa}]}
+ | ClientOpts]}]),
ssl_test_lib:check_result(Server, {error, {tls_alert, "insufficient security"}},
- Client, {error, {tls_alert, "insufficient security"}}).
-
+ Client, {error, {tls_alert, "insufficient security"}}).
+
%%--------------------------------------------------------------------
tls_dont_crash_on_handshake_garbage() ->
diff --git a/lib/ssl/test/ssl_certificate_verify_SUITE.erl b/lib/ssl/test/ssl_certificate_verify_SUITE.erl
index f677bf8a6e..bddcc2514d 100644
--- a/lib/ssl/test/ssl_certificate_verify_SUITE.erl
+++ b/lib/ssl/test/ssl_certificate_verify_SUITE.erl
@@ -620,8 +620,8 @@ cert_expired(Config) when is_list(Config) ->
{from, self()},
{options, [{verify, verify_peer}, {active, Active} | ClientOpts]}]),
- tcp_delivery_workaround(Server, {error, {tls_alert, "certificate expired"}},
- Client, {error, {tls_alert, "certificate expired"}}).
+ ssl_test_lib:check_result(Server, {error, {tls_alert, "certificate expired"}},
+ Client, {error, {tls_alert, "certificate expired"}}).
two_digits_str(N) when N < 10 ->
lists:flatten(io_lib:format("0~p", [N]));
@@ -729,8 +729,8 @@ critical_extension_verify_server(Config) when is_list(Config) ->
%% This certificate has a critical extension that we don't
%% understand. Therefore, verification should fail.
- tcp_delivery_workaround(Server, {error, {tls_alert, "unsupported certificate"}},
- Client, {error, {tls_alert, "unsupported certificate"}}),
+ ssl_test_lib:check_result(Server, {error, {tls_alert, "unsupported certificate"}},
+ Client, {error, {tls_alert, "unsupported certificate"}}),
ssl_test_lib:close(Server).
%%--------------------------------------------------------------------
@@ -909,8 +909,8 @@ invalid_signature_server(Config) when is_list(Config) ->
{from, self()},
{options, [{verify, verify_peer} | ClientOpts]}]),
- tcp_delivery_workaround(Server, {error, {tls_alert, "unknown ca"}},
- Client, {error, {tls_alert, "unknown ca"}}).
+ ssl_test_lib:check_result(Server, {error, {tls_alert, "unknown ca"}},
+ Client, {error, {tls_alert, "unknown ca"}}).
%%--------------------------------------------------------------------
@@ -946,8 +946,8 @@ invalid_signature_client(Config) when is_list(Config) ->
{from, self()},
{options, NewClientOpts}]),
- tcp_delivery_workaround(Server, {error, {tls_alert, "unknown ca"}},
- Client, {error, {tls_alert, "unknown ca"}}).
+ ssl_test_lib:check_result(Server, {error, {tls_alert, "unknown ca"}},
+ Client, {error, {tls_alert, "unknown ca"}}).
%%--------------------------------------------------------------------
@@ -1236,41 +1236,3 @@ incomplete_chain(Config) when is_list(Config) ->
%% Internal functions ------------------------------------------------
%%--------------------------------------------------------------------
-tcp_delivery_workaround(Server, ServerMsg, Client, ClientMsg) ->
- receive
- {Server, ServerMsg} ->
- client_msg(Client, ClientMsg);
- {Client, ClientMsg} ->
- server_msg(Server, ServerMsg);
- {Client, {error,closed}} ->
- server_msg(Server, ServerMsg);
- {Server, {error,closed}} ->
- client_msg(Client, ClientMsg)
- end.
-
-client_msg(Client, ClientMsg) ->
- receive
- {Client, ClientMsg} ->
- ok;
- {Client, {error,closed}} ->
- ct:log("client got close"),
- ok;
- {Client, {error, Reason}} ->
- ct:log("client got econnaborted: ~p", [Reason]),
- ok;
- Unexpected ->
- ct:fail(Unexpected)
- end.
-server_msg(Server, ServerMsg) ->
- receive
- {Server, ServerMsg} ->
- ok;
- {Server, {error,closed}} ->
- ct:log("server got close"),
- ok;
- {Server, {error, Reason}} ->
- ct:log("server got econnaborted: ~p", [Reason]),
- ok;
- Unexpected ->
- ct:fail(Unexpected)
- end.
diff --git a/lib/ssl/test/ssl_handshake_SUITE.erl b/lib/ssl/test/ssl_handshake_SUITE.erl
index ef1f6be286..861fecc554 100644
--- a/lib/ssl/test/ssl_handshake_SUITE.erl
+++ b/lib/ssl/test/ssl_handshake_SUITE.erl
@@ -25,6 +25,7 @@
-compile(export_all).
-include_lib("common_test/include/ct.hrl").
+-include("ssl_alert.hrl").
-include("ssl_internal.hrl").
-include("tls_handshake.hrl").
-include_lib("public_key/include/public_key.hrl").
@@ -41,7 +42,7 @@ all() -> [decode_hello_handshake,
decode_empty_server_sni_correctly,
select_proper_tls_1_2_rsa_default_hashsign,
ignore_hassign_extension_pre_tls_1_2,
- unorded_chain].
+ unorded_chain, signature_algorithms].
%%--------------------------------------------------------------------
init_per_suite(Config) ->
@@ -55,7 +56,9 @@ init_per_group(_GroupName, Config) ->
end_per_group(_,Config) ->
Config.
-init_per_testcase(ignore_hassign_extension_pre_tls_1_2, Config0) ->
+init_per_testcase(TC, Config0) when
+ TC =:= ignore_hassign_extension_pre_tls_1_2 orelse
+ TC =:= signature_algorithms ->
catch crypto:stop(),
try crypto:start() of
ok ->
@@ -163,11 +166,11 @@ ignore_hassign_extension_pre_tls_1_2(Config) ->
Opts = proplists:get_value(server_opts, Config),
CertFile = proplists:get_value(certfile, Opts),
[{_, Cert, _}] = ssl_test_lib:pem_to_der(CertFile),
- HashSigns = #hash_sign_algos{hash_sign_algos = [{sha512, rsa}, {sha, dsa}]},
- {sha512, rsa} = ssl_handshake:select_hashsign(HashSigns, Cert, ecdhe_rsa, tls_v1:default_signature_algs({3,3}), {3,3}),
+ HashSigns = #hash_sign_algos{hash_sign_algos = [{sha512, rsa}, {sha, dsa}, {sha, rsa}]},
+ {sha512, rsa} = ssl_handshake:select_hashsign({HashSigns, undefined}, Cert, ecdhe_rsa, tls_v1:default_signature_algs({3,3}), {3,3}),
%%% Ignore
- {md5sha, rsa} = ssl_handshake:select_hashsign(HashSigns, Cert, ecdhe_rsa, tls_v1:default_signature_algs({3,2}), {3,2}),
- {md5sha, rsa} = ssl_handshake:select_hashsign(HashSigns, Cert, ecdhe_rsa, tls_v1:default_signature_algs({3,0}), {3,0}).
+ {md5sha, rsa} = ssl_handshake:select_hashsign({HashSigns, undefined}, Cert, ecdhe_rsa, tls_v1:default_signature_algs({3,2}), {3,2}),
+ {md5sha, rsa} = ssl_handshake:select_hashsign({HashSigns, undefined}, Cert, ecdhe_rsa, tls_v1:default_signature_algs({3,0}), {3,0}).
unorded_chain(Config) when is_list(Config) ->
DefConf = ssl_test_lib:default_cert_chain_conf(),
@@ -188,6 +191,55 @@ unorded_chain(Config) when is_list(Config) ->
ssl_certificate:certificate_chain(PeerCert, ets:new(foo, []), ExtractedCerts, UnordedChain).
+signature_algorithms(Config) ->
+ Opts = proplists:get_value(server_opts, Config),
+ CertFile = proplists:get_value(certfile, Opts),
+ io:format("Cert = ~p~n", [CertFile]),
+ [{_, Cert, _}] = ssl_test_lib:pem_to_der(CertFile),
+ HashSigns0 = #hash_sign_algos{
+ hash_sign_algos = [{sha512, rsa},
+ {sha, dsa},
+ {sha, rsa}]},
+ Schemes0 = #signature_scheme_list{
+ signature_scheme_list = [rsa_pkcs1_sha1,
+ ecdsa_sha1]},
+ {sha512, rsa} = ssl_handshake:select_hashsign(
+ {HashSigns0, Schemes0},
+ Cert, ecdhe_rsa,
+ tls_v1:default_signature_algs({3,3}),
+ {3,3}),
+ HashSigns1 = #hash_sign_algos{
+ hash_sign_algos = [{sha, dsa},
+ {sha, rsa}]},
+ {sha, rsa} = ssl_handshake:select_hashsign(
+ {HashSigns1, Schemes0},
+ Cert, ecdhe_rsa,
+ tls_v1:default_signature_algs({3,3}),
+ {3,3}),
+ Schemes1 = #signature_scheme_list{
+ signature_scheme_list = [rsa_pkcs1_sha256,
+ ecdsa_sha1]},
+ %% Signature not supported
+ #alert{} = ssl_handshake:select_hashsign(
+ {HashSigns1, Schemes1},
+ Cert, ecdhe_rsa,
+ tls_v1:default_signature_algs({3,3}),
+ {3,3}),
+ %% No scheme, hashsign is used
+ {sha, rsa} = ssl_handshake:select_hashsign(
+ {HashSigns1, undefined},
+ Cert, ecdhe_rsa,
+ tls_v1:default_signature_algs({3,3}),
+ {3,3}),
+ HashSigns2 = #hash_sign_algos{
+ hash_sign_algos = [{sha, dsa}]},
+ %% Signature not supported
+ #alert{} = ssl_handshake:select_hashsign(
+ {HashSigns2, Schemes1},
+ Cert, ecdhe_rsa,
+ tls_v1:default_signature_algs({3,3}),
+ {3,3}).
+
%%--------------------------------------------------------------------
%% Internal functions ------------------------------------------------
%%--------------------------------------------------------------------
diff --git a/lib/ssl/test/ssl_packet_SUITE.erl b/lib/ssl/test/ssl_packet_SUITE.erl
index 3261244ace..ebf8ddbfac 100644
--- a/lib/ssl/test/ssl_packet_SUITE.erl
+++ b/lib/ssl/test/ssl_packet_SUITE.erl
@@ -141,6 +141,7 @@ socket_active_packet_tests() ->
packet_4_active_some_big,
packet_wait_active,
packet_size_active,
+ packet_switch,
%% inet header option should be deprecated!
header_decode_one_byte_active,
header_decode_two_bytes_active,
@@ -702,6 +703,34 @@ packet_size_passive(Config) when is_list(Config) ->
ssl_test_lib:close(Server),
ssl_test_lib:close(Client).
+
+%%--------------------------------------------------------------------
+packet_switch() ->
+ [{doc,"Test packet option {packet, 2} followd by {packet, 4}"}].
+
+packet_switch(Config) when is_list(Config) ->
+ ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
+ ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, send_switch_packet ,["Hello World", 4]}},
+ {options, [{nodelay, true},{packet, 2} | ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, recv_switch_packet, ["Hello World", 4]}},
+ {options, [{nodelay, true}, {packet, 2} |
+ ClientOpts]}]),
+
+ ssl_test_lib:check_result(Client, ok),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+
%%--------------------------------------------------------------------
packet_cdr_decode() ->
[{doc,"Test setting the packet option {packet, cdr}, {mode, binary}"}].
@@ -2286,3 +2315,26 @@ client_reject_packet_opt(Config, PacketOpt) ->
ClientOpts]}]),
ssl_test_lib:check_result(Client, {error, {options, {not_supported, PacketOpt}}}).
+
+
+send_switch_packet(SslSocket, Data, NextPacket) ->
+ ssl:send(SslSocket, Data),
+ receive
+ {ssl, SslSocket, "Hello World"} ->
+ ssl:setopts(SslSocket, [{packet, NextPacket}]),
+ ssl:send(SslSocket, Data),
+ receive
+ {ssl, SslSocket, "Hello World"} ->
+ ok
+ end
+ end.
+recv_switch_packet(SslSocket, Data, NextPacket) ->
+ receive
+ {ssl, SslSocket, "Hello World"} ->
+ ssl:send(SslSocket, Data),
+ ssl:setopts(SslSocket, [{packet, NextPacket}]),
+ receive
+ {ssl, SslSocket, "Hello World"} ->
+ ssl:send(SslSocket, Data)
+ end
+ end.
diff --git a/lib/ssl/test/ssl_test_lib.erl b/lib/ssl/test/ssl_test_lib.erl
index f3235f5614..39a5bcaad6 100644
--- a/lib/ssl/test/ssl_test_lib.erl
+++ b/lib/ssl/test/ssl_test_lib.erl
@@ -1003,7 +1003,6 @@ ecc_test_error(COpts, SOpts, CECCOpts, SECCOpts, 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),
@@ -2061,3 +2060,40 @@ hardcode_dsa_key(3) ->
y = 48598545580251057979126570873881530215432219542526130654707948736559463436274835406081281466091739849794036308281564299754438126857606949027748889019480936572605967021944405048011118039171039273602705998112739400664375208228641666852589396502386172780433510070337359132965412405544709871654840859752776060358,
x = 1457508827177594730669011716588605181448418352823}.
+tcp_delivery_workaround(Server, ServerMsg, Client, ClientMsg) ->
+ receive
+ {Server, ServerMsg} ->
+ client_msg(Client, ClientMsg);
+ {Client, ClientMsg} ->
+ server_msg(Server, ServerMsg);
+ {Client, {error,closed}} ->
+ server_msg(Server, ServerMsg);
+ {Server, {error,closed}} ->
+ client_msg(Client, ClientMsg)
+ end.
+client_msg(Client, ClientMsg) ->
+ receive
+ {Client, ClientMsg} ->
+ ok;
+ {Client, {error,closed}} ->
+ ct:log("client got close"),
+ ok;
+ {Client, {error, Reason}} ->
+ ct:log("client got econnaborted: ~p", [Reason]),
+ ok;
+ Unexpected ->
+ ct:fail(Unexpected)
+ end.
+server_msg(Server, ServerMsg) ->
+ receive
+ {Server, ServerMsg} ->
+ ok;
+ {Server, {error,closed}} ->
+ ct:log("server got close"),
+ ok;
+ {Server, {error, Reason}} ->
+ ct:log("server got econnaborted: ~p", [Reason]),
+ ok;
+ Unexpected ->
+ ct:fail(Unexpected)
+ end.