Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
home:Ledest:erlang:24
erlang
3732-ssl-Handle-certificate-selection.patch
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
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
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor