Skip to content

Commit

Permalink
ssl: Revert checking of legacy schemes
Browse files Browse the repository at this point in the history
Revert back to original implementation for checking of legacy schemes as
it was broken by refactor in OTP-26.2.1 and later only partly fixed as the refactor
change how the code was interpreted. Now also add explicit test for these algorithms.
  • Loading branch information
IngelaAndin committed Oct 4, 2024
1 parent f4d130f commit 09cdd39
Show file tree
Hide file tree
Showing 6 changed files with 218 additions and 14 deletions.
14 changes: 12 additions & 2 deletions lib/ssl/src/ssl_cipher.erl
Original file line number Diff line number Diff line change
Expand Up @@ -560,10 +560,20 @@ hash_size(sha384) ->
hash_size(sha512) ->
64.

is_supported_sign({Hash, rsa} = SignAlgo, HashSigns) ->
%% Handle RSA and RSA_PSS_RSAE
is_supported_sign({Hash, rsa} = SignAlgo, HashSigns) -> %% ?rsaEncryption cert signalgo used
lists:member(SignAlgo, HashSigns) orelse
lists:member({Hash, rsa_pss_rsae}, HashSigns);
is_supported_sign(SignAlgo, HashSigns) ->
is_supported_sign(rsa_pkcs1_sha256 = SignAlgo, HashSigns) -> %% TLS-1.3 legacy scheme
lists:member(SignAlgo, HashSigns) orelse
lists:member(rsa_pss_rsae_sha256, HashSigns);
is_supported_sign(rsa_pkcs1_sha384 = SignAlgo, HashSigns) -> %% TLS-1.3 legacy scheme
lists:member(SignAlgo, HashSigns) orelse
lists:member(rsa_pss_rsae_sha384, HashSigns);
is_supported_sign(rsa_pkcs1_sha512 = SignAlgo, HashSigns) -> %% TLS-1.3 legacy scheme
lists:member(SignAlgo, HashSigns) orelse
lists:member(rsa_pss_rsae_sha512, HashSigns);
is_supported_sign(SignAlgo, HashSigns) -> %% Normal case, format (scheme or alg-pair) depends on version
lists:member(SignAlgo, HashSigns).

signature_scheme(rsa_pkcs1_sha256) -> ?RSA_PKCS1_SHA256;
Expand Down
2 changes: 1 addition & 1 deletion lib/ssl/src/ssl_handshake.erl
Original file line number Diff line number Diff line change
Expand Up @@ -1918,7 +1918,7 @@ client_signature_schemes(ClientHashSigns, undefined) ->
ClientHashSigns;
client_signature_schemes(_, #signature_algorithms_cert{
signature_scheme_list = ClientSignatureSchemes}) ->
ClientSignatureSchemes.
ssl_cipher:signature_schemes_1_2(ClientSignatureSchemes).


%%--------------------------------------------------------------------
Expand Down
18 changes: 14 additions & 4 deletions lib/ssl/src/tls_handshake.erl
Original file line number Diff line number Diff line change
Expand Up @@ -335,13 +335,13 @@ handle_client_hello(Version,
Renegotiation) ->
case tls_record:is_acceptable_version(Version, Versions) of
true ->
SupportedHashSigns =
ssl_handshake:supported_hashsigns(maps:get(signature_algs, SslOpts, undefined)),
SigAlgs = ssl_handshake:supported_hashsigns(maps:get(signature_algs, SslOpts, undefined)),
SigAlgsCert = signature_algs_cert(Version, SslOpts, SigAlgs),
Curves = maps:get(elliptic_curves, HelloExt, undefined),
ClientHashSigns = get_signature_ext(signature_algs, HelloExt, Version),
ClientSignatureSchemes = get_signature_ext(signature_algs_cert, HelloExt, Version),
AvailableHashSigns = ssl_handshake:available_signature_algs(
ClientHashSigns, SupportedHashSigns, Version),
ClientHashSigns, SigAlgs, Version),
ECCCurve = ssl_handshake:select_curve(Curves, SupportedECCs, ECCOrder),
{Type, #session{cipher_suite = CipherSuite,
own_certificates = [OwnCert |_]} = Session1}
Expand All @@ -356,7 +356,7 @@ handle_client_hello(Version,
#{key_exchange := KeyExAlg} = ssl_cipher_format:suite_bin_to_map(CipherSuite),
case ssl_handshake:select_hashsign({ClientHashSigns, ClientSignatureSchemes},
OwnCert, KeyExAlg,
SupportedHashSigns,
SigAlgsCert,
Version) of
#alert{} = Alert ->
throw(Alert);
Expand All @@ -372,6 +372,16 @@ handle_client_hello(Version,
throw(?ALERT_REC(?FATAL, ?PROTOCOL_VERSION))
end.

signature_algs_cert(Version, SslOpts, SigAlgs) when ?TLS_GTE(Version, ?TLS_1_2) ->
case maps:get(signature_algs_cert, SslOpts, undefined) of
undefined ->
SigAlgs;
SigAlgsCert ->
ssl_handshake:supported_hashsigns(SigAlgsCert)
end;
signature_algs_cert(_,_,_) ->
undefined.

handle_client_hello_extensions(Version, Type, Random, CipherSuites,
HelloExt, SslOpts, Session0, ConnectionStates0,
Renegotiation, HashSign) ->
Expand Down
18 changes: 13 additions & 5 deletions lib/ssl/test/ssl_cert_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -399,11 +399,17 @@ do_init_per_group(dsa = Alg, Config0) ->
Config = ssl_test_lib:make_dsa_cert(Config0),
COpts = proplists:get_value(client_dsa_opts, Config),
SOpts = proplists:get_value(server_dsa_opts, Config),
ShaDSA = case Version of
{3, 3} ->
[{signature_algs, [{sha, dsa}]}];
_ ->
[]
end,
[{cert_key_alg, dsa},
{extra_client, ssl_test_lib:sig_algs(Alg, Version) ++
[{ciphers, ssl_test_lib:dsa_suites(Version)}]},
[{ciphers, ssl_test_lib:dsa_suites(Version)}] ++ ShaDSA},
{extra_server, ssl_test_lib:sig_algs(Alg, Version) ++
[{ciphers, ssl_test_lib:dsa_suites(Version)}]} |
[{ciphers, ssl_test_lib:dsa_suites(Version)}] ++ ShaDSA} |
lists:delete(cert_key_alg,
[{client_cert_opts, COpts},
{server_cert_opts, SOpts} |
Expand Down Expand Up @@ -1209,13 +1215,15 @@ custom_groups(Config) ->
%% of CertificateRequest does not contain the algorithm of the client certificate).
%% ssl client sends an empty certificate.
unsupported_sign_algo_cert_client_auth() ->
[{doc,"TLS 1.3 (backported to TLS-1.2) : Test client authentication with unsupported signature_algorithm_cert"}].
[{doc,"TLS 1.3 (backported to TLS-1.2) : Test client authentication with unsupported "
"signature_algorithm_cert"}].

unsupported_sign_algo_cert_client_auth(Config) ->
ClientOpts = ssl_test_lib:ssl_options(client_cert_opts, Config),
ServerOpts0 = ssl_test_lib:ssl_options(server_cert_opts, Config),
ServerOpts = [{verify, verify_peer},
{signature_algs, [rsa_pkcs1_sha256, rsa_pkcs1_sha384, rsa_pss_rsae_sha256, rsa_pss_pss_sha256]},
{signature_algs,
[rsa_pkcs1_sha256, rsa_pkcs1_sha384, rsa_pss_rsae_sha256, rsa_pss_pss_sha256]},
%% Skip rsa_pkcs1_sha256!
{signature_algs_cert, [rsa_pkcs1_sha384, rsa_pkcs1_sha512]},
{fail_if_no_peer_cert, true}|ServerOpts0],
Expand All @@ -1224,7 +1232,7 @@ unsupported_sign_algo_cert_client_auth(Config) ->
'tlsv1.3' ->
ssl_test_lib:basic_alert(ClientOpts, ServerOpts, Config, certificate_required);
_ ->
ssl_test_lib:basic_alert(ClientOpts, ServerOpts, Config, insufficient_security)
ssl_test_lib:basic_alert(ClientOpts, ServerOpts, Config, unsupported_certificate)
end.

%%--------------------------------------------------------------------
Expand Down
1 change: 1 addition & 0 deletions lib/ssl/test/ssl_test_lib.erl
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@
make_rsa_1024_cert/1,
make_rsa_pss_pem/4,
make_rsa_sni_configs/0,
pss_params/1,
gen_conf/4,
make_mix_cert/1,
default_cert_chain_conf/0,
Expand Down
179 changes: 177 additions & 2 deletions lib/ssl/test/tls_1_3_version_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,16 @@
legacy_tls12_server_tls_client/1,
tls13_client_tls11_server/0,
tls13_client_tls11_server/1,
tls12_legacy_cert_sign/0,
tls12_legacy_cert_sign/1,
tls13_legacy_cert_sign/0,
tls13_legacy_cert_sign/1,
tls13_legacy_cert_sign_with_pss_rsae/0,
tls13_legacy_cert_sign_with_pss_rsae/1,
tls12_legacy_cert_sign_with_pss_rsae/0,
tls12_legacy_cert_sign_with_pss_rsae/1,
reject_legcy_cert/0,
reject_legcy_cert/1,
middle_box_tls13_client/0,
middle_box_tls13_client/1,
middle_box_tls12_enabled_client/0,
Expand Down Expand Up @@ -86,7 +96,7 @@ all() ->

groups() ->
[
{rsa, [], tls_1_3_1_2_tests() ++ legacy_tests()},
{rsa, [], [reject_legcy_cert | tls_1_3_1_2_tests() ++ legacy_tests()]},
{ecdsa, [], tls_1_3_1_2_tests()}
].

Expand Down Expand Up @@ -116,7 +126,11 @@ legacy_tests() ->
tls10_client_tls_server,
tls11_client_tls_server,
tls12_client_tls_server,
tls13_client_tls11_server
tls13_client_tls11_server,
tls13_legacy_cert_sign,
tls13_legacy_cert_sign_with_pss_rsae,
tls12_legacy_cert_sign,
tls12_legacy_cert_sign_with_pss_rsae
].

init_per_suite(Config) ->
Expand Down Expand Up @@ -342,6 +356,104 @@ legacy_tls12_server_tls_client(Config) when is_list(Config) ->
| ServerOpts0],
ssl_test_lib:basic_test(ClientOpts, ServerOpts, Config).


tls13_legacy_cert_sign() ->
[{doc,"Test that a TLS 1.3 client can connect to TLS-1.3 server with pkcs1_SHA2 cert"}].

tls13_legacy_cert_sign(Config) when is_list(Config) ->
ClientOpts = [{versions, ['tlsv1.3']},
{signature_algs, rsa_pss_rsae_algs() ++ legacy_rsa_algs()}],
ServerOpts = [{versions, ['tlsv1.3']},
{signature_algs, rsa_pss_rsae_algs()},
{signature_algs_cert, legacy_rsa_algs()}],

test_rsa_pcks1_cert(sha256, ClientOpts, ServerOpts, Config),
test_rsa_pcks1_cert(sha512, ClientOpts, ServerOpts, Config),
test_rsa_pcks1_cert(sha384, ClientOpts, ServerOpts, Config).

tls13_legacy_cert_sign_with_pss_rsae() ->
[{doc,"Test that a TLS 1.3 enabled client can connect to legacy TLS-1.2 server with legacy pkcs1_SHA2 cert"}].

tls13_legacy_cert_sign_with_pss_rsae(Config) when is_list(Config) ->
ClientOpts = [{versions, ['tlsv1.3', 'tlsv1.2']},
{signature_algs, rsa_pss_rsae_algs()},
{signature_algs_cert, legacy_rsa_algs()}
],
ServerOpts = [{versions, ['tlsv1.2']},
{signature_algs, rsa_pss_rsae_algs()},
{signature_algs_cert, legacy_rsa_algs()}
],

test_rsa_pcks1_cert(sha256, ClientOpts, ServerOpts, Config),
test_rsa_pcks1_cert(sha512, ClientOpts, ServerOpts, Config),
test_rsa_pcks1_cert(sha384, ClientOpts, ServerOpts, Config).

tls12_legacy_cert_sign() ->
[{doc,"Test that a TLS 1.2 client (with old configuration) can connect to TLS-1.2 server with pkcs1_SHA2 cert"}].

tls12_legacy_cert_sign(Config) when is_list(Config) ->
ClientOpts = [{versions, ['tlsv1.2']},
{signature_algs, rsa_algs()}],
ServerOpts = [{versions, ['tlsv1.2']},
{signature_algs, rsa_algs()}],

test_rsa_pcks1_cert(sha256, ClientOpts, ServerOpts, Config),
test_rsa_pcks1_cert(sha512, ClientOpts, ServerOpts, Config),
test_rsa_pcks1_cert(sha384, ClientOpts, ServerOpts, Config).

tls12_legacy_cert_sign_with_pss_rsae() ->
[{doc,"Test that a modern TLS 1.2 client can connect to TLS-1.2 server with legacy pkcs1_SHA2 cert"}].

tls12_legacy_cert_sign_with_pss_rsae(Config) when is_list(Config) ->
ClientOpts = [{versions, ['tlsv1.2']},
{signature_algs, rsa_pss_rsae_algs() ++ rsa_algs()}
],
ServerOpts = [{versions, ['tlsv1.2']},
{signature_algs, rsa_pss_rsae_algs()},
{signature_algs_cert, legacy_rsa_algs()}
],

test_rsa_pcks1_cert(sha256, ClientOpts, ServerOpts, Config),
test_rsa_pcks1_cert(sha512, ClientOpts, ServerOpts, Config),
test_rsa_pcks1_cert(sha384, ClientOpts, ServerOpts, Config).

reject_legcy_cert() ->
[{doc,"Test that client sends empty cert if does only have legacy pkcs1_SHA2 cert that is not supported by the server"
"and do not make connection with client that requires better cert and only option is legacy pkcs1_SHA2 cert"}].

reject_legcy_cert(Config) when is_list(Config) ->
reject_legcy_cert('tlsv1.3', certificate_required, Config),
reject_legcy_cert('tlsv1.2', handshake_failure, Config).

reject_legcy_cert(Version, Alert, Config) ->
COpts = [{signature_algs, rsa_pss_pss_algs() ++ rsa_pss_rsae_algs()},
{versions, [Version]}
],
SOpts = [{verify, verify_peer},
{fail_if_no_peer_cert, true},
{signature_algs, rsa_pss_pss_algs()},
{versions, [Version]}
],
#{client_config := ClientOpts0,
server_config := ServerOpts0} =
public_key:pkix_test_data(#{client_chain =>
#{root => root_key(sha256),
intermedites => intermediates(sha256, 1),
peer => peer_key(sha256)},
server_chain =>
#{root => root_key(sha256, ssl_test_lib:pss_params(sha256)),
intermedites => intermediates(sha256,
ssl_test_lib:pss_params(sha256),
1),
peer => peer_key(sha256, ssl_test_lib:pss_params(sha256))
}}),
ClientOpts = ClientOpts0 ++ COpts,
ServerOpts = ServerOpts0 ++ SOpts,
ssl_test_lib:basic_alert(ClientOpts, ServerOpts, Config, Alert),
RevClientOpts = ServerOpts0 ++ [{signature_algs, rsa_pss_pss_algs()}],
RevServerOtps = ClientOpts0 ++ [{signature_algs, rsa_pss_pss_algs() ++ rsa_pss_rsae_algs()}],
ssl_test_lib:basic_alert(RevClientOpts, RevServerOtps, Config, insufficient_security).

middle_box_tls13_client() ->
[{doc,"Test that a TLS 1.3 client can connect to a 1.3 server with and without middle box compatible mode."}].
middle_box_tls13_client(Config) when is_list(Config) ->
Expand Down Expand Up @@ -529,3 +641,66 @@ create_bad_client_certfile(NewClientCertFile, ClientOpts) ->
ClientOTPTbsCert = ClientOTPCert#'OTPCertificate'.tbsCertificate,
NewClientDerCert = public_key:pkix_sign(ClientOTPTbsCert, Key),
ssl_test_lib:der_to_pem(NewClientCertFile, [{'Certificate', NewClientDerCert, not_encrypted}]).

test_rsa_pcks1_cert(SHA, COpts, SOpts, Config) ->
#{client_config := ClientOpts,
server_config := ServerOpts} = public_key:pkix_test_data(#{server_chain => #{root => root_key(SHA),
intermediates => intermediates(SHA, 1),
peer => peer_key(SHA)},
client_chain => #{root => root_key(SHA),
intermediates => intermediates(SHA, 1),
peer => peer_key(SHA)}}),
ssl_test_lib:basic_test(COpts ++ ClientOpts, SOpts ++ ServerOpts, Config).


root_key(SHA) ->
root_key(SHA, undefined).

peer_key(SHA) ->
peer_key(SHA, undefined).

intermediates(SHA, N) ->
intermediates(SHA, undefined, N).

root_key(SHA, undefined) ->
%% As rsa keygen is not guaranteed to be fast
[{digest, SHA},{key, ssl_test_lib:hardcode_rsa_key(6)}];
root_key(SHA, Params) ->
[{digest, SHA}, {key, {ssl_test_lib:hardcode_rsa_key(6), Params}}].

peer_key(SHA, undefined) ->
%% As rsa keygen is not guaranteed to be fast
[{digest, SHA}, {key, ssl_test_lib:hardcode_rsa_key(5)}];
peer_key(SHA, Params) ->
[{digest, SHA}, {key, {ssl_test_lib:hardcode_rsa_key(5), Params}}].

intermediates(SHA, undefined, N) when N =< 2 ->
Default = lists:duplicate(N, [{digest, SHA}]),
%% As rsa keygen is not guaranteed to be fast
hardcode_rsa_keys(Default, N, []);
intermediates(SHA, Params, N) when N =< 2 ->
Default = lists:duplicate(N, [{digest, SHA}]),
%% As rsa keygen is not guaranteed to be fast
hardcode_rsa_keys(Default, Params, N, []).

hardcode_rsa_keys([], 0, Acc) ->
Acc;
hardcode_rsa_keys([Head | Tail], N, Acc) ->
hardcode_rsa_keys(Tail, N-1, [[{key, ssl_test_lib:hardcode_rsa_key(N)} | Head] | Acc]).

hardcode_rsa_keys([], _, 0, Acc) ->
Acc;
hardcode_rsa_keys([Head | Tail], Params, N, Acc) ->
hardcode_rsa_keys(Tail, N-1, [[{key, {ssl_test_lib:hardcode_rsa_key(N)}, Params} | Head] | Acc]).

rsa_algs() ->
[{sha512, rsa}, {sha384, rsa}, {sha256, rsa}].

legacy_rsa_algs() ->
[rsa_pkcs1_sha512,rsa_pkcs1_sha384,rsa_pkcs1_sha256].

rsa_pss_rsae_algs() ->
[rsa_pss_rsae_sha512,rsa_pss_rsae_sha384,rsa_pss_rsae_sha256].

rsa_pss_pss_algs() ->
[rsa_pss_pss_sha512,rsa_pss_pss_sha384,rsa_pss_pss_sha256].

0 comments on commit 09cdd39

Please sign in to comment.