File 3732-ssl-Handle-certificate-selection.patch of Package erlang
From e165a3368dafcf3b8290f2658f27ffa885c145b9 Mon Sep 17 00:00:00 2001
From: Ingela Anderton Andin <ingela@erlang.org>
Date: Thu, 5 May 2022 15:56:33 +0200
Subject: [PATCH 2/2] ssl: Handle certificate selection
Handle fallback server cert in TLS-1.3
See RFC 8446 section 4.4.2.2.
Also handle client certificate selection with regards to
certificate_authorities extension in TLS-1.3
See RFC 8446 section 4.4.2.3 and similar for 'certificate_authorities'
in the TLS-1.2 'CertificateRequest' message. Although
TLS-1.2 spec is vague so keep backwards compatible default value for
client certificate selection.
Adjust partial chain test strategy, that is we should test this on the
Erlang's side and partial chain means shortened chain not to be
mixed up with incomplete chains.
---
lib/ssl/src/ssl_certificate.erl | 48 +++++--
lib/ssl/src/tls_dtls_connection.erl | 4 +-
lib/ssl/src/tls_handshake_1_3.erl | 97 ++++++++------
lib/ssl/test/openssl_client_cert_SUITE.erl | 105 +++++++++++----
lib/ssl/test/openssl_server_cert_SUITE.erl | 24 ++--
lib/ssl/test/ssl_api_SUITE.erl | 72 ++++++++++-
lib/ssl/test/ssl_cert_SUITE.erl | 46 +++----
lib/ssl/test/ssl_cert_tests.erl | 142 +++++++++++++--------
8 files changed, 360 insertions(+), 178 deletions(-)
diff --git a/lib/ssl/src/ssl_certificate.erl b/lib/ssl/src/ssl_certificate.erl
index 3d360803ac..7305552b82 100644
--- a/lib/ssl/src/ssl_certificate.erl
+++ b/lib/ssl/src/ssl_certificate.erl
@@ -309,19 +309,24 @@ find_cross_sign_root_paths([_ | Rest] = Path, CertDbHandle, CertDbRef, Invalidat
end.
handle_cert_auths(Chain, [], _, _) ->
- %% If we have no authorities extension to check we just accept
- %% first choice
+ %% If we have no authorities extension (or corresponding
+ %% 'certificate_authorities' in the certificate request message in
+ %% TLS-1.2 is empty) to check we just accept first choice.
{ok, Chain};
handle_cert_auths([Cert], CertAuths, CertDbHandle, CertDbRef) ->
- {ok, {_, [Cert | _] = EChain}, {_, [_ | DCerts]}} = certificate_chain(Cert, CertDbHandle, CertDbRef, [], both),
- case cert_auth_member(cert_subjects(DCerts), CertAuths) of
- true ->
- {ok, EChain};
- false ->
- {error, EChain, not_in_auth_domain}
+ case certificate_chain(Cert, CertDbHandle, CertDbRef, [], both) of
+ {ok, {_, [Cert | _] = EChain}, {_, [_ | DCerts]}} ->
+ case cert_auth_member(cert_issuers(DCerts), CertAuths) of
+ true ->
+ {ok, EChain};
+ false ->
+ {error, EChain, not_in_auth_domain}
+ end;
+ _ ->
+ {ok, [Cert]}
end;
handle_cert_auths([_ | Certs] = EChain, CertAuths, _, _) ->
- case cert_auth_member(cert_subjects(Certs), CertAuths) of
+ case cert_auth_member(cert_issuers(Certs), CertAuths) of
true ->
{ok, EChain};
false ->
@@ -790,13 +795,28 @@ subject(Cert) ->
{_Serial,Subject} = public_key:pkix_subject_id(Cert),
Subject.
-cert_subjects([], Acc) ->
+issuer(Cert) ->
+ case public_key:pkix_is_self_signed(Cert) of
+ true ->
+ subject(Cert);
+ false ->
+ case is_binary(Cert) of
+ true ->
+ #'OTPCertificate'{tbsCertificate = TBSCert} = public_key:pkix_decode_cert(Cert, otp),
+ public_key:pkix_normalize_name(TBSCert#'OTPTBSCertificate'.issuer);
+ false ->
+ #'OTPCertificate'{tbsCertificate = TBSCert} = Cert,
+ public_key:pkix_normalize_name(TBSCert#'OTPTBSCertificate'.issuer)
+ end
+ end.
+
+cert_issuers([], Acc) ->
Acc;
-cert_subjects([Cert | Rest], Acc) ->
- cert_subjects(Rest, [subject(Cert) | Acc]).
+cert_issuers([Cert | Rest], Acc) ->
+ cert_issuers(Rest, [issuer(Cert) | Acc]).
-cert_subjects(OTPCerts) ->
- cert_subjects(OTPCerts, []).
+cert_issuers(OTPCerts) ->
+ cert_issuers(OTPCerts, []).
cert_auth_member(ChainSubjects, CertAuths) ->
CommonAuthorities = sets:intersection(sets:from_list(ChainSubjects), sets:from_list(CertAuths)),
diff --git a/lib/ssl/src/tls_dtls_connection.erl b/lib/ssl/src/tls_dtls_connection.erl
index ce0372b84e..dcada6cdbf 100644
--- a/lib/ssl/src/tls_dtls_connection.erl
+++ b/lib/ssl/src/tls_dtls_connection.erl
@@ -1652,7 +1652,7 @@ select_client_cert_key_pair(Session0, CertRequest, CertKeyPairs, SupportedHashSi
select_client_cert_key_pair(Session0, CertRequest, CertKeyPairs, SupportedHashSigns, TLSVersion, CertDbHandle, CertDbRef, undefined).
select_client_cert_key_pair(Session0,_,[], _, _,_,_, undefined) ->
- %% No certificate compliant with signing algorithms found: empty certificate will be sent
+ %% No certificate compliant with supported algorithms: empty certificate will be sent
Session0#session{own_certificates = [[]],
private_key = #{}};
select_client_cert_key_pair(_,_,[], _, _,_,_,#session{}=Session) ->
@@ -1662,7 +1662,7 @@ select_client_cert_key_pair(Session0, #certificate_request{certificate_authoriti
[#{private_key := PrivateKey, certs := [Cert| _] = Certs} | Rest],
SupportedHashSigns, TLSVersion, CertDbHandle, CertDbRef, Default) ->
case ssl_handshake:select_hashsign(CertRequest, Cert, SupportedHashSigns, TLSVersion) of
- #alert {} ->
+ #alert{} ->
select_client_cert_key_pair(Session0, CertRequest, Rest, SupportedHashSigns, TLSVersion, CertDbHandle, CertDbRef, Default);
SelectedHashSign ->
case ssl_certificate:handle_cert_auths(Certs, CertAuths, CertDbHandle, CertDbRef) of
diff --git a/lib/ssl/src/tls_handshake_1_3.erl b/lib/ssl/src/tls_handshake_1_3.erl
index e145044533..ad9fd6ab7d 100644
--- a/lib/ssl/src/tls_handshake_1_3.erl
+++ b/lib/ssl/src/tls_handshake_1_3.erl
@@ -2425,8 +2425,12 @@ get_certificate_params(Cert) ->
oids_to_atoms(?'id-RSASSA-PSS', #'RSASSA-PSS-params'{maskGenAlgorithm =
#'MaskGenAlgorithm'{algorithm = ?'id-mgf1',
parameters = #'HashAlgorithm'{algorithm = HashOid}}}) ->
- Hash = public_key:pkix_hash_type(HashOid),
- {Hash, rsa_pss_pss};
+ case public_key:pkix_hash_type(HashOid) of
+ sha ->
+ {sha1, rsa_pss_pss};
+ Hash ->
+ {Hash, rsa_pss_pss}
+ end;
oids_to_atoms(SignAlgo, _) ->
case public_key:pkix_sign_types(SignAlgo) of
{sha, Sign} ->
@@ -2969,55 +2973,75 @@ supported_groups_from_extensions(Extensions) ->
{ok, undefined}
end.
-select_server_cert_key_pair(_,[], _,_,_,_, {error, _} = Return) ->
- Return;
select_server_cert_key_pair(_,[], _,_,_,_, #session{}=Session) ->
+ %% Conformant Cert-Key pair with advertised signature algorithm is
+ %% selected.
+ {ok, Session};
+select_server_cert_key_pair(_,[], _,_,_,_, {fallback, #session{}=Session}) ->
+ %% Use fallback Cert-Key pair as no conformant pair to the advertised
+ %% signature algorithms was found.
{ok, Session};
select_server_cert_key_pair(_,[], _,_,_,_, undefined) ->
- {error, ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, unable_to_send_certificate_verifiable_by_client)};
+ {error, ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, unable_to_supply_acceptable_cert)};
select_server_cert_key_pair(Session, [#{private_key := Key, certs := [Cert| _] = Certs} | Rest],
ClientSignAlgs, ClientSignAlgsCert, CertAuths,
- #state{static_env = #static_env{cert_db = CertDbHandle,
- cert_db_ref = CertDbRef} = State},
- Default) ->
+ #state{static_env = #static_env{cert_db = CertDbHandle,
+ cert_db_ref = CertDbRef} = State},
+ Default0) ->
{_, SignAlgo, SignHash, _, _} = get_certificate_params(Cert),
%% TODO: We do validate the signature algorithm and signature hash but we could also check
%% if the signing cert has a key on a curve supported by the client for ECDSA/EDDSA certs
case check_cert_sign_algo(SignAlgo, SignHash, ClientSignAlgs, ClientSignAlgsCert) of
ok ->
case ssl_certificate:handle_cert_auths(Certs, CertAuths, CertDbHandle, CertDbRef) of
- {ok, EncodeChain} ->
+ {ok, EncodeChain} -> %% Chain fullfills certificate_authorities extension
{ok, Session#session{own_certificates = EncodeChain, private_key = Key}};
{error, EncodeChain, not_in_auth_domain} ->
+ %% If this is the first chain to fulfill the signing requirement, use it as default,
+ %% if not later alternative also fulfills certificate_authorities extension
Default = Session#session{own_certificates = EncodeChain, private_key = Key},
- select_server_cert_key_pair(Session, Rest, ClientSignAlgs, ClientSignAlgsCert, CertAuths, State, Default)
+ select_server_cert_key_pair(Session, Rest, ClientSignAlgs, ClientSignAlgsCert,
+ CertAuths, State, default_or_fallback(Default0, Default))
end;
- Error ->
- select_server_cert_key_pair(Session, Rest, ClientSignAlgs, ClientSignAlgsCert, CertAuths, State,
- default_cert_key_pair_return(Default, Error))
+ _ ->
+ %% If the server cannot produce a certificate chain that is signed only
+ %% via the indicated supported algorithms, then it SHOULD continue the
+ %% handshake by sending the client a certificate chain of its choice
+ case SignHash of
+ sha1 ->
+ %% According to "Server Certificate Selection - RFC 8446"
+ %% Never send cert using sha1 unless client allows it
+ select_server_cert_key_pair(Session, Rest, ClientSignAlgs, ClientSignAlgsCert,
+ CertAuths, State, Default0);
+ _ ->
+ %% If there does not exist a default or fallback from previous alternatives
+ %% use this alternative as fallback.
+ Fallback = {fallback, Session#session{own_certificates = Certs, private_key = Key}},
+ select_server_cert_key_pair(Session, Rest, ClientSignAlgs, ClientSignAlgsCert,
+ CertAuths, State,
+ default_or_fallback(Default0, Fallback))
+ end
end.
+default_or_fallback(undefined, DefaultOrFallback) ->
+ DefaultOrFallback;
+default_or_fallback({fallback, _}, #session{} = Default) ->
+ Default;
+default_or_fallback(Default, _) ->
+ Default.
+
select_client_cert_key_pair(Session0,
[#{private_key := NoKey, certs := [[]] = NoCerts}],
_,_,_,_,_,_) ->
%% No certificate supplied : send empty certificate
Session0#session{own_certificates = NoCerts,
private_key = NoKey};
-select_client_cert_key_pair(Session0, CertKeyPairs, ServerSignAlgs, ServerSignAlgsCert,
- ClientSignAlgs, CertDbHandle, CertDbRef, CertAuths) ->
- select_client_cert_key_pair(Session0, CertKeyPairs, ServerSignAlgs, ServerSignAlgsCert,
- ClientSignAlgs, CertDbHandle, CertDbRef, CertAuths, undefined).
-
-select_client_cert_key_pair(Session, [],_,_,_,_,_,_, undefined = Default) ->
- %% No certificate compliant with supported algorithms : send empty certificate in state 'wait_finished'
- Session#session{own_certificates = Default,
- private_key = Default};
-select_client_cert_key_pair(_,[],_,_,_,_,_,_,#session{}=Session) ->
- %% No certificate compliant with guide lines send default
- Session;
-
+select_client_cert_key_pair(Session, [],_,_,_,_,_,_) ->
+ %% No certificate compliant with supported algorithms and extensison : send empty certificate in state 'wait_finished'
+ Session#session{own_certificates = [[]],
+ private_key = #{}};
select_client_cert_key_pair(Session0, [#{private_key := Key, certs := [Cert| _] = Certs} | Rest],
- ServerSignAlgs, ServerSignAlgsCert, ClientSignAlgs, CertDbHandle, CertDbRef, CertAuths, Default) ->
+ ServerSignAlgs, ServerSignAlgsCert, ClientSignAlgs, CertDbHandle, CertDbRef, CertAuths) ->
{PublicKeyAlgo, SignAlgo, SignHash, MaybeRSAKeySize, Curve} = get_certificate_params(Cert),
case select_sign_algo(PublicKeyAlgo, MaybeRSAKeySize, ServerSignAlgs, ClientSignAlgs, Curve) of
{ok, SelectedSignAlg} ->
@@ -3030,25 +3054,16 @@ select_client_cert_key_pair(Session0, [#{private_key := Key, certs := [Cert| _]
own_certificates = EncodedChain,
private_key = Key
};
- {error, EncodedChain, not_in_auth_domain} ->
- Session = Session0#session{sign_alg = SelectedSignAlg,
- own_certificates = EncodedChain,
- private_key = Key
- },
- select_client_cert_key_pair(Session, Rest, ServerSignAlgs, ServerSignAlgsCert,
- ClientSignAlgs, CertDbHandle, CertDbRef, CertAuths,
- default_cert_key_pair_return(Default, Session))
+ {error, _, not_in_auth_domain} ->
+ select_client_cert_key_pair(Session0, Rest, ServerSignAlgs, ServerSignAlgsCert,
+ ClientSignAlgs, CertDbHandle, CertDbRef, CertAuths)
end;
_ ->
select_client_cert_key_pair(Session0, Rest, ServerSignAlgs, ServerSignAlgsCert, ClientSignAlgs,
- CertDbHandle, CertDbRef, CertAuths, Default)
+ CertDbHandle, CertDbRef, CertAuths)
end;
{error, _} ->
select_client_cert_key_pair(Session0, Rest, ServerSignAlgsCert, ServerSignAlgsCert, ClientSignAlgs,
- CertDbHandle, CertDbRef, CertAuths, Default)
+ CertDbHandle, CertDbRef, CertAuths)
end.
-default_cert_key_pair_return(undefined, Session) ->
- Session;
-default_cert_key_pair_return(Default, _) ->
- Default.
diff --git a/lib/ssl/test/openssl_client_cert_SUITE.erl b/lib/ssl/test/openssl_client_cert_SUITE.erl
index 43db4bad42..e895f70861 100644
--- a/lib/ssl/test/openssl_client_cert_SUITE.erl
+++ b/lib/ssl/test/openssl_client_cert_SUITE.erl
@@ -44,12 +44,10 @@
client_auth_empty_cert_accepted/1,
client_auth_empty_cert_rejected/0,
client_auth_empty_cert_rejected/1,
- client_auth_partial_chain/0,
- client_auth_partial_chain/1,
- client_auth_allow_partial_chain/0,
- client_auth_allow_partial_chain/1,
- client_auth_do_not_allow_partial_chain/0,
- client_auth_do_not_allow_partial_chain/1,
+ client_auth_use_partial_chain/0,
+ client_auth_use_partial_chain/1,
+ client_auth_do_not_use_partial_chain/0,
+ client_auth_do_not_use_partial_chain/1,
client_auth_partial_chain_fun_fail/0,
client_auth_partial_chain_fun_fail/1,
missing_root_cert_no_auth/0,
@@ -144,9 +142,8 @@ all_version_tests() ->
auth,
client_auth_empty_cert_accepted,
client_auth_empty_cert_rejected,
- client_auth_partial_chain,
- client_auth_allow_partial_chain,
- client_auth_do_not_allow_partial_chain,
+ client_auth_use_partial_chain,
+ client_auth_do_not_use_partial_chain,
client_auth_partial_chain_fun_fail,
missing_root_cert_no_auth
].
@@ -374,27 +371,87 @@ client_auth_empty_cert_rejected() ->
client_auth_empty_cert_rejected(Config) ->
ssl_cert_tests:client_auth_empty_cert_rejected(Config).
%%--------------------------------------------------------------------
-client_auth_partial_chain() ->
- ssl_cert_tests:client_auth_partial_chain().
-client_auth_partial_chain(Config) when is_list(Config) ->
- ssl_cert_tests:client_auth_partial_chain(Config).
-
-%%--------------------------------------------------------------------
-client_auth_allow_partial_chain() ->
- ssl_cert_tests:client_auth_allow_partial_chain().
-client_auth_allow_partial_chain(Config) when is_list(Config) ->
- ssl_cert_tests:client_auth_allow_partial_chain(Config).
+%% Have to use partial chain functionality on side running Erlang (we are not testing OpenSSL features)
+client_auth_use_partial_chain() ->
+ [{doc, "Server does not trust an intermediat CA and fails the connetion as ROOT has expired"}].
+client_auth_use_partial_chain(Config) when is_list(Config) ->
+ Prop = proplists:get_value(tc_group_properties, Config),
+ DefaultCertConf = ssl_test_lib:default_ecc_cert_chain_conf(proplists:get_value(name, Prop)),
+ {Year, Month, Day} = date(),
+ #{client_config := ClientOpts0,
+ server_config := ServerOpts0} = ssl_test_lib:make_cert_chains_pem(proplists:get_value(cert_key_alg, Config),
+ [{client_chain,
+ [[{validity, {{Year-2, Month, Day},
+ {Year-1, Month, Day}}}],
+ [],
+ []
+ ]},
+ {server_chain, DefaultCertConf}],
+ Config, "use_partial_chain"),
+ ClientOpts = ssl_test_lib:ssl_options(extra_client, ClientOpts0, Config),
+ CaCertsFile = proplists:get_value(cacertfile, ClientOpts),
+ ClientCACerts = [DerCA || {'Certificate', DerCA, _} <- ssl_test_lib:pem_to_der(CaCertsFile)],
+ [_, IntermidiateCA, _] = ClientCACerts,
+ PartialChain = fun(CertChain) ->
+ case lists:member(IntermidiateCA, CertChain) of
+ true ->
+ {trusted_ca, IntermidiateCA};
+ false ->
+ unknown_ca
+ end
+ end,
+ ServerOpts = [{verify, verify_peer}, {fail_if_no_peer_cert, true}, {partial_chain, PartialChain} |
+ ssl_test_lib:ssl_options(extra_server, ServerOpts0, Config)],
+ ssl_test_lib:basic_test(ClientOpts, ServerOpts, Config).
%%--------------------------------------------------------------------
-client_auth_do_not_allow_partial_chain() ->
- ssl_cert_tests:client_auth_do_not_allow_partial_chain().
-client_auth_do_not_allow_partial_chain(Config) when is_list(Config) ->
- ssl_cert_tests:client_auth_do_not_allow_partial_chain(Config).
+%% Have to use partial chain functionality on side running Erlang (we are not testing OpenSSL features)
+client_auth_do_not_use_partial_chain() ->
+ ssl_cert_tests:client_auth_do_not_use_partial_chain().
+client_auth_do_not_use_partial_chain(Config) when is_list(Config) ->
+ Prop = proplists:get_value(tc_group_properties, Config),
+ DefaultCertConf = ssl_test_lib:default_ecc_cert_chain_conf(proplists:get_value(name, Prop)),
+ {Year, Month, Day} = date(),
+ #{client_config := ClientOpts0,
+ server_config := ServerOpts0} = ssl_test_lib:make_cert_chains_pem(proplists:get_value(cert_key_alg, Config),
+ [{client_chain,
+ [[{validity, {{Year-2, Month, Day},
+ {Year-1, Month, Day}}}],
+ [],
+ []
+ ]},
+ {server_chain, DefaultCertConf}], Config, "do_not_use_partial_chain"),
+ PartialChain = fun(_CertChain) ->
+ unknown_ca
+ end,
+ ClientOpts = ssl_test_lib:ssl_options(extra_client, ClientOpts0, Config),
+ ServerOpts = [{verify, verify_peer}, {fail_if_no_peer_cert, true}, {partial_chain, PartialChain} |
+ ssl_test_lib:ssl_options(extra_server, ServerOpts0, Config)],
+ ssl_test_lib:basic_alert(ClientOpts, ServerOpts, Config, certificate_expired).
%%--------------------------------------------------------------------
+%% Have to use partial chain functionality on side running Erlang (we are not testing OpenSSL features)
client_auth_partial_chain_fun_fail() ->
ssl_cert_tests:client_auth_partial_chain_fun_fail().
client_auth_partial_chain_fun_fail(Config) when is_list(Config) ->
- ssl_cert_tests:client_auth_partial_chain_fun_fail(Config).
+ Prop = proplists:get_value(tc_group_properties, Config),
+ DefaultCertConf = ssl_test_lib:default_ecc_cert_chain_conf(proplists:get_value(name, Prop)),
+ {Year, Month, Day} = date(),
+ #{client_config := ClientOpts0,
+ server_config := ServerOpts0} = ssl_test_lib:make_cert_chains_pem(proplists:get_value(cert_key_alg, Config),
+ [{client_chain,
+ [[{validity, {{Year-2, Month, Day},
+ {Year-1, Month, Day}}}],
+ [],
+ []
+ ]},
+ {server_chain, DefaultCertConf}], Config, "do_not_use_partial_chain"),
+ PartialChain = fun(_CertChain) ->
+ error(crash_on_purpose)
+ end,
+ ClientOpts = ssl_test_lib:ssl_options(extra_client, ClientOpts0, Config),
+ ServerOpts = [{verify, verify_peer}, {fail_if_no_peer_cert, true}, {partial_chain, PartialChain} |
+ ssl_test_lib:ssl_options(extra_server, ServerOpts0, Config)],
+ ssl_test_lib:basic_alert(ClientOpts, ServerOpts, Config, certificate_expired).
%%--------------------------------------------------------------------
missing_root_cert_no_auth() ->
diff --git a/lib/ssl/test/openssl_server_cert_SUITE.erl b/lib/ssl/test/openssl_server_cert_SUITE.erl
index 701de76461..cbcb57ded1 100644
--- a/lib/ssl/test/openssl_server_cert_SUITE.erl
+++ b/lib/ssl/test/openssl_server_cert_SUITE.erl
@@ -47,10 +47,10 @@
client_auth_empty_cert_rejected/1,
client_auth_partial_chain/0,
client_auth_partial_chain/1,
- client_auth_allow_partial_chain/0,
- client_auth_allow_partial_chain/1,
- client_auth_do_not_allow_partial_chain/0,
- client_auth_do_not_allow_partial_chain/1,
+ client_auth_use_partial_chain/0,
+ client_auth_use_partial_chain/1,
+ client_auth_do_not_use_partial_chain/0,
+ client_auth_do_not_use_partial_chain/1,
client_auth_partial_chain_fun_fail/0,
client_auth_partial_chain_fun_fail/1,
missing_root_cert_no_auth/0,
@@ -394,15 +394,15 @@ client_auth_partial_chain(Config) when is_list(Config) ->
ssl_cert_tests:client_auth_partial_chain(Config).
%%--------------------------------------------------------------------
-client_auth_allow_partial_chain() ->
- ssl_cert_tests:client_auth_allow_partial_chain().
-client_auth_allow_partial_chain(Config) when is_list(Config) ->
- ssl_cert_tests:client_auth_allow_partial_chain(Config).
+client_auth_use_partial_chain() ->
+ ssl_cert_tests:client_auth_use_partial_chain().
+client_auth_use_partial_chain(Config) when is_list(Config) ->
+ ssl_cert_tests:client_auth_use_partial_chain(Config).
%%--------------------------------------------------------------------
-client_auth_do_not_allow_partial_chain() ->
- ssl_cert_tests:client_auth_do_not_allow_partial_chain().
-client_auth_do_not_allow_partial_chain(Config) when is_list(Config) ->
- ssl_cert_tests:client_auth_do_not_allow_partial_chain(Config).
+client_auth_do_not_use_partial_chain() ->
+ ssl_cert_tests:client_auth_do_not_use_partial_chain().
+client_auth_do_not_use_partial_chain(Config) when is_list(Config) ->
+ ssl_cert_tests:client_auth_do_not_use_partial_chain(Config).
%%--------------------------------------------------------------------
client_auth_partial_chain_fun_fail() ->
diff --git a/lib/ssl/test/ssl_api_SUITE.erl b/lib/ssl/test/ssl_api_SUITE.erl
index 7d0e84776c..1e675d5e27 100644
--- a/lib/ssl/test/ssl_api_SUITE.erl
+++ b/lib/ssl/test/ssl_api_SUITE.erl
@@ -53,6 +53,8 @@
peercert_with_client_cert/1,
select_best_cert/0,
select_best_cert/1,
+ select_sha1_cert/0,
+ select_sha1_cert/1,
connection_information/0,
connection_information/1,
secret_connection_info/0,
@@ -274,6 +276,7 @@ gen_api_tests() ->
peercert,
peercert_with_client_cert,
select_best_cert,
+ select_sha1_cert,
connection_information,
secret_connection_info,
keylog_connection_info,
@@ -548,6 +551,23 @@ select_best_cert(Config) when is_list(Config) ->
Conf)
end, Conf).
+
+
+%%--------------------------------------------------------------------
+select_sha1_cert() ->
+ [{doc,"Use cert signed with rsa and sha1"}].
+
+select_sha1_cert(Config) when is_list(Config) ->
+ Version = ssl_test_lib:protocol_version(Config),
+ TestConf = public_key:pkix_test_data(#{server_chain => #{root => [{digest, sha},{key, ssl_test_lib:hardcode_rsa_key(1)}],
+ intermediates => [[{digest, sha}, {key, ssl_test_lib:hardcode_rsa_key(2)}]],
+ peer => [{digest, sha}, {key, ssl_test_lib:hardcode_rsa_key(3)}]
+ },
+ client_chain => #{root => [{digest, sha},{key, ssl_test_lib:hardcode_rsa_key(3)}],
+ intermediates => [[{digest, sha},{key, ssl_test_lib:hardcode_rsa_key(2)}]],
+ peer => [{digest, sha}, {key, ssl_test_lib:hardcode_rsa_key(1)}]}}),
+ test_sha1_cert_conf(Version, TestConf, Config).
+
%%--------------------------------------------------------------------
connection_information() ->
[{doc,"Test the API function ssl:connection_information/1"}].
@@ -3337,20 +3357,20 @@ test_config('dtlsv1.2', Config) ->
[{#{server_config => [{certs_keys, [#{certfile => SRSAPSSRSAECert, keyfile => SRSAPSSRSAEKey},
#{certfile => SRSAPSSCert, keyfile => SRSAPSSKey}]},
{verify, verify_peer},
- {cacertfile, CRSAPSSCACerts}],
+ {cacertfile, SRSAPSSCACerts}],
client_config => [{certs_keys, [#{certfile => CRSAPSSRSAECert, keyfile => CRSAPSSRSAEKey},
#{certfile => CRSAPSSCert, keyfile => CRSAPSSKey}]},
{verify, verify_peer},
- {cacertfile, SRSAPSSCACerts}]
+ {cacertfile, CRSAPSSCACerts}]
},
{client_peer, pem_to_der_cert(SRSAPSSCert)}, {server_peer, pem_to_der_cert(CRSAPSSCert)}},
{#{server_config => [{certs_keys, [#{certfile => SRSAPSSRSAECert, keyfile => SRSAPSSRSAEKey},
#{certfile => SRSAPSSCert, keyfile => SRSAPSSKey}]},
{verify, verify_peer},
- {cacertfile, CRSAPSSRSAECACerts}],
+ {cacertfile, SRSAPSSRSAECACerts}],
client_config => [{certs_keys, [#{certfile => CRSAPSSRSAECert, keyfile => CRSAPSSRSAEKey}]},
{verify, verify_peer}, {signature_algs, [rsa_pss_rsae_sha256]},
- {cacertfile, SRSAPSSRSAECACerts}]
+ {cacertfile, CRSAPSSRSAECACerts}]
},
{client_peer, pem_to_der_cert(SRSAPSSRSAECert)}, {server_peer, pem_to_der_cert(CRSAPSSRSAECert)}}
];
@@ -3372,11 +3392,11 @@ test_config(_, Config) ->
[{#{server_config => [{certs_keys, [#{certfile => SRSA2Cert, keyfile => SRSA2Key},
#{certfile => SRSA1Cert, keyfile => SRSA1Key}]},
{verify, verify_peer},
- {cacertfile, CRSA2CACerts}],
+ {cacertfile, SRSA2CACerts}],
client_config => [{certs_keys, [#{certfile => CRSA2Cert, keyfile => CRSA2Key},
#{certfile => CRSA1Cert, keyfile => CRSA1Key}]},
{verify, verify_peer},
- {cacertfile, SRSA2CACerts}]
+ {cacertfile, CRSA2CACerts}]
}, {client_peer, pem_to_der_cert(SRSA2Cert)}, {server_peer, pem_to_der_cert(CRSA2Cert)}}].
check_peercert(Socket, Cert) ->
@@ -3421,3 +3441,43 @@ get_single_options(CertOptName, KeyOptName, CaOptName, Opts) ->
pem_to_der_cert(Pem) ->
[{'Certificate', Der, _}] = ssl_test_lib:pem_to_der(Pem),
Der.
+
+test_sha1_cert_conf('tlsv1.3', #{client_config := ClientOpts, server_config := ServerOpts}, Config) ->
+ ssl_test_lib:basic_test([{verify, verify_peer} | ClientOpts], ServerOpts, Config),
+ SigAlgs = [%% ECDSA
+ ecdsa_secp521r1_sha512,
+ ecdsa_secp384r1_sha384,
+ ecdsa_secp256r1_sha256,
+ %% RSASSA-PSS
+ rsa_pss_pss_sha512,
+ rsa_pss_pss_sha384,
+ rsa_pss_pss_sha256,
+ rsa_pss_rsae_sha512,
+ rsa_pss_rsae_sha384,
+ rsa_pss_rsae_sha256,
+ %% EDDSA
+ eddsa_ed25519,
+ eddsa_ed448
+ ],
+ ssl_test_lib:basic_alert([{verify, verify_peer}, {signature_algs, SigAlgs} | ClientOpts],
+ [{signature_algs, SigAlgs ++ [rsa_pkcs1_sha1]} | ServerOpts], Config, handshake_failure);
+
+test_sha1_cert_conf('tlsv1.2', #{client_config := ClientOpts, server_config := ServerOpts}, Config) ->
+ SigAlgs = [%% SHA2
+ {sha512, ecdsa},
+ {sha512, rsa},
+ {sha384, ecdsa},
+ {sha384, rsa},
+ {sha256, ecdsa},
+ {sha256, rsa},
+ {sha224, ecdsa},
+ {sha224, rsa},
+ %% SHA
+ {sha, ecdsa},
+ {sha, rsa},
+ {sha, dsa}],
+ ssl_test_lib:basic_test( [{verify, verify_peer}, {signature_algs, SigAlgs} | ClientOpts],
+ [{signature_algs, SigAlgs} | ServerOpts], Config);
+
+test_sha1_cert_conf(_, #{client_config := ClientOpts, server_config := ServerOpts}, Config) ->
+ ssl_test_lib:basic_test([{verify, verify_peer} | ClientOpts], ServerOpts, Config).
diff --git a/lib/ssl/test/ssl_cert_SUITE.erl b/lib/ssl/test/ssl_cert_SUITE.erl
index 6918a1bc22..986cd0ead6 100644
--- a/lib/ssl/test/ssl_cert_SUITE.erl
+++ b/lib/ssl/test/ssl_cert_SUITE.erl
@@ -46,12 +46,12 @@
client_auth_empty_cert_accepted/1,
client_auth_empty_cert_rejected/0,
client_auth_empty_cert_rejected/1,
- client_auth_partial_chain/0,
- client_auth_partial_chain/1,
- client_auth_allow_partial_chain/0,
- client_auth_allow_partial_chain/1,
- client_auth_do_not_allow_partial_chain/0,
- client_auth_do_not_allow_partial_chain/1,
+ client_auth_no_suitable_chain/0,
+ client_auth_no_suitable_chain/1,
+ client_auth_use_partial_chain/0,
+ client_auth_use_partial_chain/1,
+ client_auth_do_not_use_partial_chain/0,
+ client_auth_do_not_use_partial_chain/1,
client_auth_partial_chain_fun_fail/0,
client_auth_partial_chain_fun_fail/1,
client_auth_sni/0,
@@ -186,6 +186,7 @@ tls_1_3_tests() ->
[
hello_retry_request,
custom_groups,
+ client_auth_no_suitable_chain,
hello_retry_client_auth,
hello_retry_client_auth_empty_cert_accepted,
hello_retry_client_auth_empty_cert_rejected
@@ -221,9 +222,8 @@ all_version_tests() ->
auth,
client_auth_empty_cert_accepted,
client_auth_empty_cert_rejected,
- client_auth_partial_chain,
- client_auth_allow_partial_chain,
- client_auth_do_not_allow_partial_chain,
+ client_auth_use_partial_chain,
+ client_auth_do_not_use_partial_chain,
client_auth_partial_chain_fun_fail,
client_auth_sni,
missing_root_cert_no_auth,
@@ -449,21 +449,21 @@ client_auth_empty_cert_rejected() ->
client_auth_empty_cert_rejected(Config) ->
ssl_cert_tests:client_auth_empty_cert_rejected(Config).
%%--------------------------------------------------------------------
-client_auth_partial_chain() ->
- ssl_cert_tests:client_auth_partial_chain().
-client_auth_partial_chain(Config) when is_list(Config) ->
- ssl_cert_tests:client_auth_partial_chain(Config).
+client_auth_no_suitable_chain() ->
+ ssl_cert_tests:client_auth_no_suitable_chain().
+client_auth_no_suitable_chain(Config) when is_list(Config) ->
+ ssl_cert_tests:client_auth_no_suitable_chain(Config).
%%--------------------------------------------------------------------
-client_auth_allow_partial_chain() ->
- ssl_cert_tests:client_auth_allow_partial_chain().
-client_auth_allow_partial_chain(Config) when is_list(Config) ->
- ssl_cert_tests:client_auth_allow_partial_chain(Config).
+client_auth_use_partial_chain() ->
+ ssl_cert_tests:client_auth_use_partial_chain().
+client_auth_use_partial_chain(Config) when is_list(Config) ->
+ ssl_cert_tests:client_auth_use_partial_chain(Config).
%%--------------------------------------------------------------------
-client_auth_do_not_allow_partial_chain() ->
- ssl_cert_tests:client_auth_do_not_allow_partial_chain().
-client_auth_do_not_allow_partial_chain(Config) when is_list(Config) ->
- ssl_cert_tests:client_auth_do_not_allow_partial_chain(Config).
+client_auth_do_not_use_partial_chain() ->
+ ssl_cert_tests:client_auth_do_not_use_partial_chain().
+client_auth_do_not_use_partial_chain(Config) when is_list(Config) ->
+ ssl_cert_tests:client_auth_do_not_use_partial_chain(Config).
%%--------------------------------------------------------------------
client_auth_partial_chain_fun_fail() ->
@@ -859,8 +859,8 @@ cert_expired(Config) when is_list(Config) ->
#{client_config := ClientOpts0,
server_config := ServerOpts0} = ssl_test_lib:make_cert_chains_der(proplists:get_value(cert_key_alg, Config),
[{server_chain,
- [[],
- [{validity, {{Year-2, Month, Day},
+ [[],
+ [{validity, {{Year-2, Month, Day},
{Year-1, Month, Day}}}],
[]
]},
diff --git a/lib/ssl/test/ssl_cert_tests.erl b/lib/ssl/test/ssl_cert_tests.erl
index c7a88e961e..834159b022 100644
--- a/lib/ssl/test/ssl_cert_tests.erl
+++ b/lib/ssl/test/ssl_cert_tests.erl
@@ -32,12 +32,12 @@
client_auth_empty_cert_accepted/1,
client_auth_empty_cert_rejected/0,
client_auth_empty_cert_rejected/1,
- client_auth_partial_chain/0,
- client_auth_partial_chain/1,
- client_auth_allow_partial_chain/0,
- client_auth_allow_partial_chain/1,
- client_auth_do_not_allow_partial_chain/0,
- client_auth_do_not_allow_partial_chain/1,
+ client_auth_no_suitable_chain/0,
+ client_auth_no_suitable_chain/1,
+ client_auth_use_partial_chain/0,
+ client_auth_use_partial_chain/1,
+ client_auth_do_not_use_partial_chain/0,
+ client_auth_do_not_use_partial_chain/1,
client_auth_partial_chain_fun_fail/0,
client_auth_partial_chain_fun_fail/1,
client_auth_sni/0,
@@ -99,8 +99,7 @@ auth(Config) ->
%%--------------------------------------------------------------------
client_auth_empty_cert_accepted() ->
- [{doc,"Test client authentication when client sends an empty certificate and "
- "fail_if_no_peer_cert is set to false."}].
+ [{doc,"Client sends empty cert chain as no cert is configured and server allows it"}].
client_auth_empty_cert_accepted(Config) ->
ClientOpts = proplists:delete(keyfile,
@@ -112,8 +111,7 @@ client_auth_empty_cert_accepted(Config) ->
ssl_test_lib:basic_test(ClientOpts, ServerOpts, Config).
%%--------------------------------------------------------------------
client_auth_empty_cert_rejected() ->
- [{doc,"Test client authentication when client sends an empty certificate and "
- "fail_if_no_peer_cert is set to true."}].
+ [{doc,"Client sends empty cert chain as no cert is configured"}].
client_auth_empty_cert_rejected(Config) ->
ServerOpts = [{verify, verify_peer}, {fail_if_no_peer_cert, true}
@@ -131,10 +129,10 @@ client_auth_empty_cert_rejected(Config) ->
ssl_test_lib:basic_alert(ClientOpts, ServerOpts, Config, handshake_failure)
end.
%%--------------------------------------------------------------------
-client_auth_partial_chain() ->
- [{doc, "Client sends an incomplete chain, by default not acceptable."}].
+client_auth_no_suitable_chain() ->
+ [{doc, "Client sends an empty cert chain as no suitable chain is found."}].
-client_auth_partial_chain(Config) when is_list(Config) ->
+client_auth_no_suitable_chain(Config) when is_list(Config) ->
ServerOpts = [{verify, verify_peer}, {fail_if_no_peer_cert, true}
| ssl_test_lib:ssl_options(extra_server, server_cert_opts, Config)],
ClientOpts0 = ssl_test_lib:ssl_options(extra_client, client_cert_opts, Config),
@@ -142,19 +140,36 @@ client_auth_partial_chain(Config) when is_list(Config) ->
[{_,RootCA,_} | _] = public_key:pem_decode(ClientCAs),
ClientOpts = [{cacerts, [RootCA]} |
proplists:delete(cacertfile, ClientOpts0)],
- ssl_test_lib:basic_alert(ClientOpts, ServerOpts, Config, unknown_ca).
-
-%%--------------------------------------------------------------------
-client_auth_allow_partial_chain() ->
- [{doc, "Server trusts intermediat CA and accepts a partial chain. (partial_chain option)"}].
+ Version = proplists:get_value(version,Config),
-client_auth_allow_partial_chain(Config) when is_list(Config) ->
- ServerOpts0 = [{verify, verify_peer}, {fail_if_no_peer_cert, true}
- | ssl_test_lib:ssl_options(extra_server, server_cert_opts, Config)],
- ClientOpts = ssl_test_lib:ssl_options(extra_client, client_cert_opts, Config),
- {ok, ClientCAs} = file:read_file(proplists:get_value(cacertfile, ClientOpts)),
- [{_,_,_}, {_, IntermidiateCA, _} | _] = public_key:pem_decode(ClientCAs),
+ case Version of
+ 'tlsv1.3' ->
+ ssl_test_lib:basic_alert(ClientOpts, ServerOpts, Config, certificate_required);
+ _ ->
+ ssl_test_lib:basic_alert(ClientOpts, ServerOpts, Config, handshake_failure)
+ end.
+
+%%--------------------------------------------------------------------
+client_auth_use_partial_chain() ->
+ [{doc, "Client trusts intermediat CA and verifies the shorter chain."}].
+
+client_auth_use_partial_chain(Config) when is_list(Config) ->
+ Prop = proplists:get_value(tc_group_properties, Config),
+ DefaultCertConf = ssl_test_lib:default_ecc_cert_chain_conf(proplists:get_value(name, Prop)),
+ {Year, Month, Day} = date(),
+ #{client_config := ClientOpts0,
+ server_config := ServerOpts0} = ssl_test_lib:make_cert_chains_der(proplists:get_value(cert_key_alg, Config),
+ [{server_chain,
+ [[{validity, {{Year-2, Month, Day},
+ {Year-1, Month, Day}}}],
+ [],
+ []
+ ]},
+ {client_chain, DefaultCertConf}]),
+ ClientOpts = ssl_test_lib:ssl_options(extra_client, ClientOpts0, Config),
+ ServerOpts = ssl_test_lib:ssl_options(extra_server, ServerOpts0, Config),
+ [_, IntermidiateCA, _] = proplists:get_value(cacerts, ServerOpts),
PartialChain = fun(CertChain) ->
case lists:member(IntermidiateCA, CertChain) of
true ->
@@ -163,52 +178,58 @@ client_auth_allow_partial_chain(Config) when is_list(Config) ->
unknown_ca
end
end,
- ServerOpts = [{cacerts, [IntermidiateCA]},
- {partial_chain, PartialChain} |
- proplists:delete(cacertfile, ServerOpts0)],
-
- ssl_test_lib:basic_test(ClientOpts, ServerOpts, Config).
+ ssl_test_lib:basic_test([{verify, verify_peer}, {partial_chain, PartialChain} |ClientOpts], ServerOpts, Config).
%%--------------------------------------------------------------------
-client_auth_do_not_allow_partial_chain() ->
- [{doc, "Server does not accept the chain sent by the client as ROOT CA is unknown, "
- "and we do not choose to trust the intermediate CA. (partial_chain option)"}].
-
-client_auth_do_not_allow_partial_chain(Config) when is_list(Config) ->
- ServerOpts0 = [{verify, verify_peer}, {fail_if_no_peer_cert, true}
- | ssl_test_lib:ssl_options(extra_server, server_cert_opts, Config)],
- ClientOpts = ssl_test_lib:ssl_options(extra_client, client_cert_opts, Config),
- {ok, ServerCAs} = file:read_file(proplists:get_value(cacertfile, ServerOpts0)),
- [{_,_,_}, {_, IntermidiateCA, _} | _] = public_key:pem_decode(ServerCAs),
-
+client_auth_do_not_use_partial_chain() ->
+ [{doc, "Client does not trust an intermediat CA and fails the connetion as ROOT has expired"}].
+
+client_auth_do_not_use_partial_chain(Config) when is_list(Config) ->
+ Prop = proplists:get_value(tc_group_properties, Config),
+ DefaultCertConf = ssl_test_lib:default_ecc_cert_chain_conf(proplists:get_value(name, Prop)),
+ {Year, Month, Day} = date(),
+ #{client_config := ClientOpts0,
+ server_config := ServerOpts0} = ssl_test_lib:make_cert_chains_der(proplists:get_value(cert_key_alg, Config),
+ [{server_chain,
+ [[{validity, {{Year-2, Month, Day},
+ {Year-1, Month, Day}}}],
+ [],
+ []
+ ]},
+ {client_chain, DefaultCertConf}]),
PartialChain = fun(_CertChain) ->
unknown_ca
end,
- ServerOpts = [{cacerts, [IntermidiateCA]},
- {partial_chain, PartialChain} |
- proplists:delete(cacertfile, ServerOpts0)],
- ssl_test_lib:basic_alert(ClientOpts, ServerOpts, Config, unknown_ca).
+ ClientOpts = [{verify, verify_peer}, {partial_chain, PartialChain} | ssl_test_lib:ssl_options(extra_client, ClientOpts0, Config)],
+ ServerOpts = ssl_test_lib:ssl_options(extra_server, ServerOpts0, Config),
+ ssl_test_lib:basic_alert(ClientOpts, ServerOpts, Config, certificate_expired).
%%--------------------------------------------------------------------
client_auth_partial_chain_fun_fail() ->
- [{doc, "If parial_chain fun crashes, treat it as if it returned unkown_ca"}].
+ [{doc, "If partial_chain fun crashes, treat it as if it returned unkown_ca"}].
client_auth_partial_chain_fun_fail(Config) when is_list(Config) ->
- ServerOpts0 = [{verify, verify_peer}, {fail_if_no_peer_cert, true}
- | ssl_test_lib:ssl_options(extra_server, server_cert_opts, Config)],
- ClientOpts = ssl_test_lib:ssl_options(extra_client, client_cert_opts, Config),
+ Prop = proplists:get_value(tc_group_properties, Config),
+ DefaultCertConf = ssl_test_lib:default_ecc_cert_chain_conf(proplists:get_value(name, Prop)),
+ {Year, Month, Day} = date(),
+ #{client_config := ClientOpts0,
+ server_config := ServerOpts0} = ssl_test_lib:make_cert_chains_der(proplists:get_value(cert_key_alg, Config),
+ [{server_chain,
+ [[{validity, {{Year-2, Month, Day},
+ {Year-1, Month, Day}}}],
+ [],
+ []
+ ]},
+ {client_chain, DefaultCertConf}]),
- {ok, ServerCAs} = file:read_file(proplists:get_value(cacertfile, ServerOpts0)),
- [{_,_,_}, {_, IntermidiateCA, _} | _] = public_key:pem_decode(ServerCAs),
PartialChain = fun(_CertChain) ->
error(crash_on_purpose)
end,
- ServerOpts = [{cacerts, [IntermidiateCA]},
- {partial_chain, PartialChain} |
- proplists:delete(cacertfile, ServerOpts0)],
+ ClientOpts = [{verify, verify_peer}, {partial_chain, PartialChain} | ssl_test_lib:ssl_options(extra_client, ClientOpts0, Config)],
+ ServerOpts = [ssl_test_lib:ssl_options(extra_server, ServerOpts0, Config)],
+ ssl_test_lib:basic_alert(ClientOpts, ServerOpts, Config, certificate_expired).
- ssl_test_lib:basic_alert(ClientOpts, ServerOpts, Config, unknown_ca).
%%--------------------------------------------------------------------
client_auth_sni() ->
@@ -286,8 +307,17 @@ invalid_signature_client(Config) when is_list(Config) ->
NewClientDerCert = public_key:pkix_sign(ClientOTPTbsCert, Key),
ssl_test_lib:der_to_pem(NewClientCertFile, [{'Certificate', NewClientDerCert, not_encrypted}]),
ClientOpts = [{certfile, NewClientCertFile} | proplists:delete(certfile, ClientOpts0)],
- ServerOpts = [{verify, verify_peer} | ServerOpts0],
- ssl_test_lib:basic_alert(ClientOpts, ServerOpts, Config, unknown_ca).
+ ServerOpts = [{verify, verify_peer}, {fail_if_no_peer_cert, true} | ServerOpts0],
+ Version = proplists:get_value(version,Config),
+
+ case Version of
+ 'tlsv1.3' ->
+ %% Client will not be able to create chain to send that matches
+ %% certificate authorities
+ ssl_test_lib:basic_alert(ClientOpts, ServerOpts, Config, certificate_required);
+ _ ->
+ ssl_test_lib:basic_alert(ClientOpts, ServerOpts, Config, unknown_ca)
+ end.
%%--------------------------------------------------------------------
invalid_signature_server() ->
--
2.35.3