diff options
author | Péter Dimitrov <peterdmv@users.noreply.github.com> | 2021-06-13 19:43:55 +0200 |
---|---|---|
committer | Ingela Anderton Andin <ingela@erlang.org> | 2021-07-19 16:37:22 +0200 |
commit | c33fc9cc9a218bd57d522a56a896efc70abd99a6 (patch) | |
tree | 5b610d8d51e3a81cf22da610822d76a456350d75 | |
parent | 3002f55f409f44122d3a45ac53bfd453e9aa2cb2 (diff) | |
download | erlang-c33fc9cc9a218bd57d522a56a896efc70abd99a6.tar.gz |
ssl: Fix signature algorithm selection for ECDSA
The signature selection algorithm has been changed to also verify
if the client supports signatures using the elliptic curve of the
server's public/private key pair.
The old algorithm only verified the signature algorithm and the
hash algorithm, not taking into account the elliptic curve of
the key in the server's certificate. This could result in
successful TLS connections with non-standard algorithm combinations
in the CertificateVerify message such as ECDSA with secp256r1 and
SHA512, when the server's certificate had a public key on an
unsupported elliptic curve, signed with a non-ECDSA signature
algorithm and hash algorithm combination e.g. RSA with SHA256.
-rw-r--r-- | lib/ssl/src/ssl_handshake.erl | 100 | ||||
-rw-r--r-- | lib/ssl/src/tls_handshake_1_3.erl | 47 | ||||
-rw-r--r-- | lib/ssl/test/property_test/ssl_eqc_chain.erl | 24 | ||||
-rw-r--r-- | lib/ssl/test/ssl_cert_SUITE.erl | 90 | ||||
-rw-r--r-- | lib/ssl/test/ssl_test_lib.erl | 26 |
5 files changed, 251 insertions, 36 deletions
diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl index de5490d232..8375e837e8 100644 --- a/lib/ssl/src/ssl_handshake.erl +++ b/lib/ssl/src/ssl_handshake.erl @@ -1521,7 +1521,7 @@ select_hashsign({#hash_sign_algos{hash_sign_algos = ClientHashSigns}, Cert, KeyExAlgo, SupportedHashSigns, {Major, Minor}) when Major >= 3 andalso Minor >= 3 -> ClientSignatureSchemes = get_signature_scheme(ClientSignatureSchemes0), - {SignAlgo0, Param, PublicKeyAlgo0, _} = get_cert_params(Cert), + {SignAlgo0, Param, PublicKeyAlgo0, _, _} = get_cert_params(Cert), SignAlgo = sign_algo(SignAlgo0), PublicKeyAlgo = public_key_algo(PublicKeyAlgo0), @@ -1575,7 +1575,7 @@ select_hashsign(#certificate_request{ Cert, SupportedHashSigns, {Major, Minor}) when Major >= 3 andalso Minor >= 3-> - {SignAlgo0, Param, PublicKeyAlgo0, _} = get_cert_params(Cert), + {SignAlgo0, Param, PublicKeyAlgo0, _, _} = get_cert_params(Cert), SignAlgo = sign_algo(SignAlgo0), PublicKeyAlgo = public_key_algo(PublicKeyAlgo0), @@ -1598,7 +1598,7 @@ select_hashsign(#certificate_request{ ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, no_suitable_signature_algorithm) end; select_hashsign(#certificate_request{certificate_types = Types}, Cert, _, Version) -> - {_, _, PublicKeyAlgo0, _} = get_cert_params(Cert), + {_, _, PublicKeyAlgo0, _, _} = get_cert_params(Cert), PublicKeyAlgo = public_key_algo(PublicKeyAlgo0), %% Check cert even for TLS 1.0/1.1 @@ -1615,6 +1615,7 @@ select_hashsign(#certificate_request{certificate_types = Types}, Cert, _, Versio %% - parameters of the signature algorithm %% - public key algorithm (key type) %% - RSA key size in bytes +%% - Elliptic Curve (public key) get_cert_params(Cert) -> #'OTPCertificate'{tbsCertificate = TBSCert, signatureAlgorithm = @@ -1630,7 +1631,98 @@ get_cert_params(Cert) -> _ -> undefined end, - {SignAlgo, Param, PublicKeyAlgo, RSAKeySize}. + Curve = get_ec_curve(TBSCert#'OTPTBSCertificate'.subjectPublicKeyInfo), + {SignAlgo, Param, PublicKeyAlgo, RSAKeySize, Curve}. + +get_ec_curve(#'OTPSubjectPublicKeyInfo'{ + algorithm = #'PublicKeyAlgorithm'{ + algorithm = ?'id-ecPublicKey', + parameters = {namedCurve, ?'secp256r1'}}}) -> + secp256r1; +get_ec_curve(#'OTPSubjectPublicKeyInfo'{ + algorithm = #'PublicKeyAlgorithm'{ + algorithm = ?'id-ecPublicKey', + parameters = {namedCurve, ?'secp384r1'}}}) -> + secp384r1; +get_ec_curve(#'OTPSubjectPublicKeyInfo'{ + algorithm = #'PublicKeyAlgorithm'{ + algorithm = ?'id-ecPublicKey', + parameters = {namedCurve, ?'secp521r1'}}}) -> + secp521r1; +get_ec_curve(#'OTPSubjectPublicKeyInfo'{ + algorithm = #'PublicKeyAlgorithm'{ + algorithm = ?'id-ecPublicKey', + parameters = {ecParameters, + #'ECParameters'{curve = #'Curve'{} = Curve, + base = Base, + order = Order, + cofactor = Cofactor}}}}) -> + curve_to_atom(Curve, Base, Order, Cofactor); +get_ec_curve(_) -> + unsupported. + +curve_to_atom(#'Curve'{ + a = <<255,255,255,255,0,0,0,1,0,0,0,0,0,0,0,0, + 0,0,0,0,255,255,255,255,255,255,255,255,255,255,255,252>>, + b = <<90,198,53,216,170,58,147,231,179,235,189,85,118,152,134,188, + 101,29,6,176,204,83,176,246,59,206,60,62,39,210,96,75>>, + seed = <<196,157,54,8,134,231,4,147,106,102,120,225,19,157,38,183, + 129,159,126,144>>}, + <<4,107,23,209,242,225,44,66,71,248,188,230,229,99,164,64, + 242,119,3,125,129,45,235,51,160,244,161,57,69,216,152,194, + 150,79,227,66,226,254,26,127,155,142,231,235,74,124,15,158, + 22,43,206,51,87,107,49,94,206,203,182,64,104,55,191,81, + 245>>, + 115792089210356248762697446949407573529996955224135760342422259061068512044369, + 1 + ) -> + secp256r1; +curve_to_atom(#'Curve'{ + a = <<255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,254, + 255,255,255,255,0,0,0,0,0,0,0,0,255,255,255,252>>, + b = <<179,49,47,167,226, 62,231,228,152,142,5,107,227,248,45,25, + 24,29,156,110,254,129,65,18,3,20,8,143,80,19,135,90, + 198,86,57,141,138,46,209,157,42,133,200,237,211,236,42,239>>, + seed = <<163,53,146,106,163,25,162,122,29,0,137,106,103,115,164,130, + 122,205,172,115>>}, + <<4,170,135,202,34,190,139,5,55,142,177,199,30,243,32,173, + 116,110,29,59,98,139,167,155,152,89,247,65,224,130,84,42, + 56,85,2,242,93,191,85,41,108,58,84,94,56,114,118,10, + 183,54,23,222,74,150,38,44,111,93,158,152,191,146,146,220, + 41,248,244,29,189,40,154,20,124,233,218,49,19,181,240,184, + 192,10,96,177,206,29,126,129,157,122,67,29,124,144,234,14, + 95>>, + 39402006196394479212279040100143613805079739270465446667946905279627659399113263569398956308152294913554433653942643, + 1) -> + secp384r1; +curve_to_atom(#'Curve'{ + a = <<1,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, + 255,252>>, + b = <<0,81,149,62,185,97,142,28,154,31,146,154,33,160,182,133, + 64,238,162,218,114,91,153,179,21,243,184,180,137,145,142,241, + 9,225,86,25,57,81,236,126,147,123,22,82,192,189,59,177, + 191,7,53,115,223,136,61,44,52,241,239,69,31,212,107,80, + 63,0>>, + seed = <<208,158,136,0,41,28,184,83,150,204,103,23,57,50,132,170, + 160,218,100,186>>}, + <<4,0,198,133,142,6,183,4,4,233,205,158,62,203,102,35, + 149,180,66,156,100,129,57,5,63,181,33,248,40,175,96,107, + 77,61,186,161,75,94,119,239,231,89,40,254,29,193,39,162, + 255,168,222,51,72,179,193,133,106,66,155,249,126,126,49,194, + 229,189,102,1,24,57,41,106,120,154,59,192,4,92,138,95, + 180,44,125,27,217,152,245,68,73,87,155,68,104,23,175,189, + 23,39,62,102,44,151,238,114,153,94,244,38,64,197,80,185, + 1,63,173,7,97,53,60,112,134,162,114,194,64,136,190,148, + 118,159,209,102,80>>, + 6864797660130609714981900799081393217269435300143305409394463459185543183397655394245057746333217197532963996371363321113864768612440380340372808892707005449, + 1) -> + secp521r1; +curve_to_atom(_, _, _, _) -> + unsupported. select_own_cert([OwnCert| _]) -> OwnCert; diff --git a/lib/ssl/src/tls_handshake_1_3.erl b/lib/ssl/src/tls_handshake_1_3.erl index cd9beecb3f..0340960fc8 100644 --- a/lib/ssl/src/tls_handshake_1_3.erl +++ b/lib/ssl/src/tls_handshake_1_3.erl @@ -650,13 +650,15 @@ do_start(#client_hello{cipher_suites = ClientCiphers, Cipher = Maybe(select_cipher_suite(HonorCipherOrder, ClientCiphers, ServerCiphers)), Groups = Maybe(select_common_groups(ServerGroups, ClientGroups)), Maybe(validate_client_key_share(ClientGroups, ClientShares)), - {PublicKeyAlgo, SignAlgo, SignHash, RSAKeySize} = get_certificate_params(Cert), + {PublicKeyAlgo, SignAlgo, SignHash, RSAKeySize, Curve} = get_certificate_params(Cert), %% Check if client supports signature algorithm of server certificate + %% TODO: We do validate the signature algorithm and signature hash but we could check + %% if the signing cert has a key on a curve supported by the client. Maybe(check_cert_sign_algo(SignAlgo, SignHash, ClientSignAlgs, ClientSignAlgsCert)), %% Select signature algorithm (used in CertificateVerify message). - SelectedSignAlg = Maybe(select_sign_algo(PublicKeyAlgo, RSAKeySize, ClientSignAlgs, ServerSignAlgs)), + SelectedSignAlg = Maybe(select_sign_algo(PublicKeyAlgo, RSAKeySize, ClientSignAlgs, ServerSignAlgs, Curve)), %% Select client public key. If no public key found in ClientShares or %% ClientShares is empty, trigger HelloRetryRequest as we were able @@ -1417,10 +1419,10 @@ process_certificate_request(#certificate_request_1_3{ ServerSignAlgsCert = get_signature_scheme_list( maps:get(signature_algs_cert, Extensions, undefined)), - {PublicKeyAlgo, SignAlgo, SignHash, MaybeRSAKeySize} = get_certificate_params(Cert), + {PublicKeyAlgo, SignAlgo, SignHash, MaybeRSAKeySize, Curve} = get_certificate_params(Cert), {Ref, Maybe} = maybe(), try - SelectedSignAlg = Maybe(select_sign_algo(PublicKeyAlgo, MaybeRSAKeySize, ServerSignAlgs, ClientSignAlgs)), + SelectedSignAlg = Maybe(select_sign_algo(PublicKeyAlgo, MaybeRSAKeySize, ServerSignAlgs, ClientSignAlgs, Curve)), %% Check if server supports signature algorithm of client certificate case check_cert_sign_algo(SignAlgo, SignHash, ServerSignAlgs, ServerSignAlgsCert) of ok -> @@ -2322,11 +2324,11 @@ check_cert_sign_algo(SignAlgo, SignHash, _, ClientSignAlgsCert) -> %% DSA keys are not supported by TLS 1.3 -select_sign_algo(dsa, _RSAKeySize, _PeerSignAlgs, _OwnSignAlgs) -> +select_sign_algo(dsa, _RSAKeySize, _PeerSignAlgs, _OwnSignAlgs, _Curve) -> {error, ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, no_suitable_public_key)}; -select_sign_algo(_, _RSAKeySize, [], _) -> +select_sign_algo(_, _RSAKeySize, [], _, _) -> {error, ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, no_suitable_signature_algorithm)}; -select_sign_algo(PublicKeyAlgo, RSAKeySize, [PeerSignAlg|PeerSignAlgs], OwnSignAlgs) -> +select_sign_algo(PublicKeyAlgo, RSAKeySize, [PeerSignAlg|PeerSignAlgs], OwnSignAlgs, Curve) -> {_, S, _} = ssl_cipher:scheme_to_components(PeerSignAlg), %% RSASSA-PKCS1-v1_5 and Legacy algorithms are not defined for use in signed %% TLS handshake messages: filter sha-1 and rsa_pkcs1. @@ -2342,25 +2344,35 @@ select_sign_algo(PublicKeyAlgo, RSAKeySize, [PeerSignAlg|PeerSignAlgs], OwnSignA lists:member(PeerSignAlg, OwnSignAlgs) of true -> validate_key_compatibility(PublicKeyAlgo, RSAKeySize, - [PeerSignAlg|PeerSignAlgs], OwnSignAlgs); + [PeerSignAlg|PeerSignAlgs], OwnSignAlgs, Curve); false -> - select_sign_algo(PublicKeyAlgo, RSAKeySize, PeerSignAlgs, OwnSignAlgs) + select_sign_algo(PublicKeyAlgo, RSAKeySize, PeerSignAlgs, OwnSignAlgs, Curve) end. -validate_key_compatibility(PublicKeyAlgo, RSAKeySize, [PeerSignAlg|PeerSignAlgs], OwnSignAlgs) +validate_key_compatibility(PublicKeyAlgo, RSAKeySize, [PeerSignAlg|PeerSignAlgs], OwnSignAlgs, Curve) when PublicKeyAlgo =:= rsa orelse PublicKeyAlgo =:= rsa_pss_pss -> - case is_rsa_key_compatible(RSAKeySize, PeerSignAlg) of + {Hash, Sign, _} = ssl_cipher:scheme_to_components(PeerSignAlg), + case (Sign =:= rsa_pss_rsae orelse Sign =:= rsa_pss_pss) andalso + is_rsa_key_compatible(RSAKeySize, Hash) of true -> {ok, PeerSignAlg}; false -> - select_sign_algo(PublicKeyAlgo, RSAKeySize, PeerSignAlgs, OwnSignAlgs) + select_sign_algo(PublicKeyAlgo, RSAKeySize, PeerSignAlgs, OwnSignAlgs, Curve) end; -validate_key_compatibility(_, _, [PeerSignAlg|_], _) -> +validate_key_compatibility(PublicKeyAlgo, RSAKeySize, [PeerSignAlg|PeerSignAlgs], OwnSignAlgs, Curve) + when PublicKeyAlgo =:= ecdsa -> + {_ , Sign, PeerCurve} = ssl_cipher:scheme_to_components(PeerSignAlg), + case Sign =:= ecdsa andalso Curve =:= PeerCurve of + true -> + {ok, PeerSignAlg}; + false -> + select_sign_algo(PublicKeyAlgo, RSAKeySize, PeerSignAlgs, OwnSignAlgs, Curve) + end; +validate_key_compatibility(_, _, [PeerSignAlg|_], _, _) -> {ok, PeerSignAlg}. -is_rsa_key_compatible(KeySize, SigAlg) -> - {Hash, _, _} = ssl_cipher:scheme_to_components(SigAlg), +is_rsa_key_compatible(KeySize, Hash) -> HashSize = ssl_cipher:hash_size(Hash), %% OpenSSL crypto lib defines a limit on the size of the random salt @@ -2382,6 +2394,7 @@ is_rsa_key_compatible(KeySize, SigAlg) -> do_check_cert_sign_algo(_, _, []) -> {error, ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, no_suitable_signature_algorithm)}; do_check_cert_sign_algo(SignAlgo, SignHash, [Scheme|T]) -> + %% ECDSA: curve is tied to the hash algorithm e.g. ecdsa_secp256r1_sha256 {Hash, Sign, _Curve} = ssl_cipher:scheme_to_components(Scheme), case compare_sign_algos(SignAlgo, SignHash, Sign, Hash) of true -> @@ -2406,11 +2419,11 @@ compare_sign_algos(_, _, _, _) -> false. get_certificate_params(Cert) -> - {SignAlgo0, Param, SubjectPublicKeyAlgo0, RSAKeySize} = + {SignAlgo0, Param, SubjectPublicKeyAlgo0, RSAKeySize, Curve} = ssl_handshake:get_cert_params(Cert), {SignHash, SignAlgo} = oids_to_atoms(SignAlgo0, Param), SubjectPublicKeyAlgo = public_key_algo(SubjectPublicKeyAlgo0), - {SubjectPublicKeyAlgo, SignAlgo, SignHash, RSAKeySize}. + {SubjectPublicKeyAlgo, SignAlgo, SignHash, RSAKeySize, Curve}. oids_to_atoms(?'id-RSASSA-PSS', #'RSASSA-PSS-params'{maskGenAlgorithm = #'MaskGenAlgorithm'{algorithm = ?'id-mgf1', diff --git a/lib/ssl/test/property_test/ssl_eqc_chain.erl b/lib/ssl/test/property_test/ssl_eqc_chain.erl index e108591776..bf949fdf1b 100644 --- a/lib/ssl/test/property_test/ssl_eqc_chain.erl +++ b/lib/ssl/test/property_test/ssl_eqc_chain.erl @@ -259,10 +259,10 @@ extraneous_der_cert_chain_opts(Version, Alg) -> #{server_config := ServerConf0, client_config := ClientConf0} = public_key:pkix_test_data(#{server_chain => #{root => SRoot, intermediates => intermediates(Alg, 1), - peer => []}, + peer => peer_key(ecdsa)}, client_chain => #{root => CRoot, intermediates => intermediates(Alg, 1), - peer => []}}), + peer => peer_key(ecdsa)}}), {ClientChain, ClientRoot} = extraneous_chain_and_root(ClientConf0, "OTP test client ROOT", 1), {ServerChain, ServerRoot} = extraneous_chain_and_root(ServerConf0, "OTP test server ROOT", 1), @@ -280,10 +280,10 @@ extraneous_pem_cert_chain_opts(Version, Alg, PrivDir) -> #{server_config := ServerConf0, client_config := ClientConf0} = public_key:pkix_test_data(#{server_chain => #{root => SRoot, intermediates => intermediates(Alg, 1), - peer => []}, + peer => peer_key(ecdsa)}, client_chain => #{root => CRoot, intermediates => intermediates(Alg, 1), - peer => []}}), + peer => peer_key(ecdsa)}}), {ClientChain, ClientRoot} = extraneous_chain_and_root(ClientConf0, "OTP test client ROOT", 1), {ServerChain, ServerRoot} = extraneous_chain_and_root(ServerConf0, "OTP test server ROOT", 1), @@ -301,10 +301,10 @@ extra_extraneous_der_cert_chain_opts(Version, Alg) -> #{server_config := ServerConf0, client_config := ClientConf0} = public_key:pkix_test_data(#{server_chain => #{root => SRoot, intermediates => intermediates(Alg, 3), - peer => []}, + peer => peer_key(ecdsa)}, client_chain => #{root => CRoot, intermediates => intermediates(Alg, 3), - peer => []}}), + peer => peer_key(ecdsa)}}), {ClientChain0, ClientRoot0} = extraneous_chain_and_root(ClientConf0, "OTP test client ROOT", 2), @@ -330,10 +330,10 @@ der_extraneous_and_unorder_chain(Version, Alg) -> client_config := ClientConf0} = public_key:pkix_test_data(#{server_chain => #{root => SRoot, intermediates => intermediates(Alg, 3), - peer => []}, + peer => peer_key(ecdsa)}, client_chain => #{root => CRoot, intermediates => intermediates(Alg, 3), - peer => []}}), + peer => peer_key(ecdsa)}}), {ClientChain0, ClientRoot0} = chain_and_root(ClientConf0), {ServerChain0, ServerRoot0} = chain_and_root(ServerConf0), @@ -370,12 +370,12 @@ chain_and_root(Config) -> {Chain, Root}. extraneous_chain_and_root(Config, Name, 1) -> - #{cert := NewRoot, key := Key} = public_key:pkix_test_root_cert(Name, []), + #{cert := NewRoot, key := Key} = public_key:pkix_test_root_cert(Name, root_key(ecdsa)), {[OwnCert, CA0, OldRoot], OldRoot} = chain_and_root(Config), CA1 = new_intermediat(CA0, Key), {[OwnCert, CA1, CA0], NewRoot}; extraneous_chain_and_root(Config, Name, 2) -> - #{cert := NewRoot, key := Key} = public_key:pkix_test_root_cert(Name, []), + #{cert := NewRoot, key := Key} = public_key:pkix_test_root_cert(Name, root_key(ecdsa)), {[OwnCert, CA0, CA1, CA2, OldRoot], OldRoot} = chain_and_root(Config), CA3 = new_intermediat(CA2, Key), {[OwnCert, CA0, CA1, CA2, CA3], NewRoot}. @@ -411,13 +411,13 @@ create_extraneous_and_unorded([Client, _CCA0, _CCA1, CCA2, _CCA3], [Client, OCCA {[Client, OCCA0, CCA2, OCCA2, OCROOT, OCCA1], [Server, OSCA0, SCA2, OSCA2, OSROOT, OSCA1]}. root_key(ecdsa) -> - []; %% Just generate one + [{key,{namedCurve, ?secp256r1}}]; %% Use a curve that will be default supported in all TLS versions root_key(rsa) -> %% As rsa keygen is not guaranteed to be fast [{key, ssl_test_lib:hardcode_rsa_key(6)}]. peer_key(ecdsa) -> - []; %% Just generate one + [{key, {namedCurve, ?secp256r1}}]; %% Use a curve that will be default supported in all TLS versions peer_key(rsa) -> %% As rsa keygen is not guaranteed to be fast [{key, ssl_test_lib:hardcode_rsa_key(6)}]. diff --git a/lib/ssl/test/ssl_cert_SUITE.erl b/lib/ssl/test/ssl_cert_SUITE.erl index a142115b55..a720b788b0 100644 --- a/lib/ssl/test/ssl_cert_SUITE.erl +++ b/lib/ssl/test/ssl_cert_SUITE.erl @@ -113,7 +113,13 @@ hello_retry_client_auth_empty_cert_rejected/0, hello_retry_client_auth_empty_cert_rejected/1, basic_rsa_1024/0, - basic_rsa_1024/1 + basic_rsa_1024/1, + signature_algorithms_bad_curve_secp256r1/0, + signature_algorithms_bad_curve_secp256r1/1, + signature_algorithms_bad_curve_secp384r1/0, + signature_algorithms_bad_curve_secp384r1/1, + signature_algorithms_bad_curve_secp521r1/0, + signature_algorithms_bad_curve_secp521r1/1 ]). %%-------------------------------------------------------------------- @@ -147,7 +153,10 @@ groups() -> {rsa_pss_rsae_1_3, [], all_version_tests() ++ rsa_tests() ++ tls_1_3_tests() ++ tls_1_3_rsa_tests()}, {rsa_pss_pss, [], all_version_tests() ++ rsa_tests()}, {rsa_pss_pss_1_3, [], all_version_tests() ++ rsa_tests() ++ tls_1_3_tests() ++ tls_1_3_rsa_tests()}, - {ecdsa_1_3, [], all_version_tests() ++ tls_1_3_tests()} + {ecdsa_1_3, [], all_version_tests() ++ tls_1_3_tests() ++ + [signature_algorithms_bad_curve_secp256r1, + signature_algorithms_bad_curve_secp384r1, + signature_algorithms_bad_curve_secp521r1]} ]. ssl_protocol_groups() -> @@ -330,14 +339,38 @@ do_init_per_group(Group, Config) -> end_per_group(GroupName, Config) -> ssl_test_lib:end_per_group(GroupName, Config). +init_per_testcase(signature_algorithms_bad_curve_secp256r1, Config) -> + init_rsa_ecdsa_opts(Config, secp256r1); +init_per_testcase(signature_algorithms_bad_curve_secp384r1, Config) -> + init_rsa_ecdsa_opts(Config, secp384r1); +init_per_testcase(signature_algorithms_bad_curve_secp521r1, Config) -> + init_rsa_ecdsa_opts(Config, secp521r1); init_per_testcase(_TestCase, Config) -> ssl_test_lib:ct_log_supported_protocol_versions(Config), ct:timetrap({seconds, 10}), Config. -end_per_testcase(_TestCase, Config) -> +end_per_testcase(_TestCase, Config) -> Config. +init_rsa_ecdsa_opts(Config0, Curve) -> + PKAlg = crypto:supports(public_keys), + case lists:member(ecdsa, PKAlg) andalso (lists:member(ecdh, PKAlg) orelse lists:member(dh, PKAlg)) of + true -> + Config = ssl_test_lib:make_rsa_ecdsa_cert(Config0, Curve), + COpts = proplists:get_value(client_rsa_ecdsa_opts, Config), + SOpts = proplists:get_value(server_rsa_ecdsa_opts, Config), + [{cert_key_alg, ecdsa} | + lists:delete(cert_key_alg, + [{client_cert_opts, COpts}, + {server_cert_opts, SOpts} | + lists:delete(server_cert_opts, + lists:delete(client_cert_opts, Config))] + )]; + false -> + {skip, "Missing EC crypto support"} + end. + %%-------------------------------------------------------------------- %% Test Cases -------------------------------------------------------- %%-------------------------------------------------------------------- @@ -1050,6 +1083,57 @@ hello_retry_client_auth_empty_cert_rejected(Config) -> ssl_test_lib:basic_alert(ClientOpts, ServerOpts, Config, certificate_required). %%-------------------------------------------------------------------- +signature_algorithms_bad_curve_secp256r1() -> + [{doc,"TLS 1.3: Test that the the client fails to connect " + "if server's certificate has a key using an unsupported curve."}]. + +signature_algorithms_bad_curve_secp256r1(Config) -> + ClientOpts0 = ssl_test_lib:ssl_options(client_cert_opts, Config), + ServerOpts0 = ssl_test_lib:ssl_options(server_cert_opts, Config), + %% Set versions + ServerOpts = [{versions, ['tlsv1.2','tlsv1.3']}, {log_level, debug}|ServerOpts0], + ClientOpts = [{versions, ['tlsv1.2','tlsv1.3']}, + {signature_algs, [ecdsa_secp384r1_sha384, + ecdsa_secp521r1_sha512, + {sha256,rsa}]}|ClientOpts0], + + ssl_test_lib:basic_alert(ClientOpts, ServerOpts, Config, insufficient_security). + +%%-------------------------------------------------------------------- +signature_algorithms_bad_curve_secp384r1() -> + [{doc,"TLS 1.3: Test that the the client fails to connect " + "if server's certificate has a key using an unsupported curve."}]. + +signature_algorithms_bad_curve_secp384r1(Config) -> + ClientOpts0 = ssl_test_lib:ssl_options(client_cert_opts, Config), + ServerOpts0 = ssl_test_lib:ssl_options(server_cert_opts, Config), + %% Set versions + ServerOpts = [{versions, ['tlsv1.2','tlsv1.3']}, {log_level, debug}|ServerOpts0], + ClientOpts = [{versions, ['tlsv1.2','tlsv1.3']}, + {signature_algs, [ecdsa_secp256r1_sha256, + ecdsa_secp521r1_sha512, + {sha256,rsa}]}|ClientOpts0], + + ssl_test_lib:basic_alert(ClientOpts, ServerOpts, Config, insufficient_security). + +%%-------------------------------------------------------------------- +signature_algorithms_bad_curve_secp521r1() -> + [{doc,"TLS 1.3: Test that the the client fails to connect " + "if server's certificate has a key using an unsupported curve."}]. + +signature_algorithms_bad_curve_secp521r1(Config) -> + ClientOpts0 = ssl_test_lib:ssl_options(client_cert_opts, Config), + ServerOpts0 = ssl_test_lib:ssl_options(server_cert_opts, Config), + %% Set versions + ServerOpts = [{versions, ['tlsv1.2','tlsv1.3']}, {log_level, debug}|ServerOpts0], + ClientOpts = [{versions, ['tlsv1.2','tlsv1.3']}, + {signature_algs, [ecdsa_secp256r1_sha256, + ecdsa_secp384r1_sha384, + {sha256,rsa}]}|ClientOpts0], + + ssl_test_lib:basic_alert(ClientOpts, ServerOpts, Config, insufficient_security). + +%%-------------------------------------------------------------------- basic_rsa_1024() -> [{doc, "TLS 1.3 (Basic): Test if connection can be established using 1024 bits RSA keys in certificates."}]. diff --git a/lib/ssl/test/ssl_test_lib.erl b/lib/ssl/test/ssl_test_lib.erl index 012eb9217e..bb86a675ea 100644 --- a/lib/ssl/test/ssl_test_lib.erl +++ b/lib/ssl/test/ssl_test_lib.erl @@ -158,6 +158,7 @@ make_dsa_cert/1, make_ecdsa_cert/1, make_ecdh_rsa_cert/1, + make_rsa_ecdsa_cert/2, make_rsa_cert_chains/3, make_dsa_cert_chains/3, make_ecc_cert_chains/3, @@ -1757,6 +1758,31 @@ make_ecdh_rsa_cert(Config) -> Config end. +make_rsa_ecdsa_cert(Config, Curve) -> + CryptoSupport = crypto:supports(), + case proplists:get_bool(ecdh, proplists:get_value(public_keys, CryptoSupport)) of + true -> + ClientFileBase = filename:join([proplists:get_value(priv_dir, Config), + "rsa_ecdsa_" ++ atom_to_list(Curve)]), + ServerFileBase = filename:join([proplists:get_value(priv_dir, Config), + "rsa_ecdsa_" ++ atom_to_list(Curve)]), + ClientChain = proplists:get_value(client_chain, Config, default_cert_chain_conf()), + ServerChain = proplists:get_value(server_chain, Config, default_cert_chain_conf()), + CertChainConf = gen_conf(ecdh_rsa, ecdh_rsa, ClientChain, ServerChain, Curve), + GenCertData = public_key:pkix_test_data(CertChainConf), + [{server_config, ServerConf}, + {client_config, ClientConf}] = + x509_test:gen_pem_config_files(GenCertData, ClientFileBase, ServerFileBase), + + [{server_rsa_ecdsa_opts, [{ssl_imp, new},{reuseaddr, true} | ServerConf]}, + {server_rsa_ecdsa_verify_opts, [{ssl_imp, new},{reuseaddr, true}, + {verify, verify_peer} | ServerConf]}, + {client_rsa_ecdsa_opts, ClientConf} | Config]; + _ -> + Config + end. + + start_upgrade_server(Args) -> Node = proplists:get_value(node, Args), Result = spawn_link(Node, ?MODULE, run_upgrade_server, [Args]), |