summaryrefslogtreecommitdiff
path: root/lib/ssl/test/property_test/ssl_eqc_chain.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/ssl/test/property_test/ssl_eqc_chain.erl')
-rw-r--r--lib/ssl/test/property_test/ssl_eqc_chain.erl411
1 files changed, 411 insertions, 0 deletions
diff --git a/lib/ssl/test/property_test/ssl_eqc_chain.erl b/lib/ssl/test/property_test/ssl_eqc_chain.erl
new file mode 100644
index 0000000000..e78dc3fc0e
--- /dev/null
+++ b/lib/ssl/test/property_test/ssl_eqc_chain.erl
@@ -0,0 +1,411 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2018-2020. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%
+
+-module(ssl_eqc_chain).
+
+%%-export([prop_tls_orded_path/1]).
+-compile(export_all).
+
+-proptest(eqc).
+-proptest([triq,proper]).
+
+-ifndef(EQC).
+-ifndef(PROPER).
+-ifndef(TRIQ).
+-define(EQC,true).
+-endif.
+-endif.
+-endif.
+
+-ifdef(EQC).
+-include_lib("eqc/include/eqc.hrl").
+-define(MOD_eqc,eqc).
+
+-else.
+-ifdef(PROPER).
+-include_lib("proper/include/proper.hrl").
+-define(MOD_eqc,proper).
+
+-else.
+-ifdef(TRIQ).
+-define(MOD_eqc,triq).
+-include_lib("triq/include/triq.hrl").
+
+-endif.
+-endif.
+-endif.
+
+-include_lib("public_key/include/public_key.hrl").
+%%--------------------------------------------------------------------
+%% Properties --------------------------------------------------------
+%%--------------------------------------------------------------------
+prop_tls_unordered_path(PrivDir) ->
+ ?FORALL({ClientOptions, ServerOptions}, ?LET(Version, tls_version(), unordered_options(Version, PrivDir)),
+ try
+ [TLSVersion] = proplists:get_value(versions, ClientOptions),
+ ssl_test_lib:basic_test(ClientOptions, ServerOptions, [{server_type, erlang},
+ {client_type, erlang},
+ {version, TLSVersion}
+ ])
+ of
+ _ ->
+ true
+ catch
+ _:_ ->
+ false
+ end
+ ).
+
+prop_tls_extraneous_path(PrivDir) ->
+ ?FORALL({ClientOptions, ServerOptions}, ?LET(Version, tls_version(), extraneous_options(Version, PrivDir)),
+ try
+ [TLSVersion] = proplists:get_value(versions, ClientOptions),
+ ssl_test_lib:basic_test(ClientOptions, ServerOptions, [{server_type, erlang},
+ {client_type, erlang},
+ {version, TLSVersion}
+ ])
+ of
+ _ ->
+ true
+ catch
+ _:_ ->
+ false
+ end
+ ).
+
+prop_tls_extraneous_paths() ->
+ ?FORALL({ClientOptions, ServerOptions}, ?LET(Version, tls_version(), extra_extraneous_options(Version)),
+ try
+ [TLSVersion] = proplists:get_value(versions, ClientOptions),
+ ssl_test_lib:basic_test(ClientOptions, ServerOptions, [{server_type, erlang},
+ {client_type, erlang},
+ {version, TLSVersion}
+ ])
+ of
+ _ ->
+ true
+ catch
+ _:_ ->
+ false
+ end
+ ).
+
+prop_tls_extraneous_and_unordered_path() ->
+ ?FORALL({ClientOptions, ServerOptions}, ?LET(Version, tls_version(), unordered_extraneous_options(Version)),
+ try
+ [TLSVersion] = proplists:get_value(versions, ClientOptions),
+ ssl_test_lib:basic_test(ClientOptions, ServerOptions, [{server_type, erlang},
+ {client_type, erlang},
+ {version, TLSVersion}
+ ])
+ of
+ _ ->
+ true
+ catch
+ _:_ ->
+ false
+ end
+ ).
+
+%%--------------------------------------------------------------------
+%% Chain Generators -----------------------------------------------
+%%--------------------------------------------------------------------
+tls_version() ->
+ Versions = [Version || Version <- ['tlsv1.3', 'tlsv1.2', 'tlsv1.1', 'tlsv1', 'dtlsv1.2', 'dtlsv1'],
+ ssl_test_lib:sufficient_crypto_support(Version)
+ ],
+ oneof(Versions).
+
+key_alg(Version) when Version == 'tlsv1.3';
+ Version == 'tlsv1.2';
+ Version == 'dtlsv1.2'->
+ oneof([rsa, ecdsa]);
+key_alg(_) ->
+ oneof([rsa]).
+
+server_options('tlsv1.3') ->
+ [{verify, verify_peer},
+ {fail_if_no_peer_cert, true},
+ {reuseaddr, true}];
+server_options(_) ->
+ [{verify, verify_peer},
+ {fail_if_no_peer_cert, true},
+ {reuse_sessions, false},
+ {reuseaddr, true}].
+
+client_options(_) ->
+ [{verify, verify_peer}].
+
+unordered_options(Version, PrivDir) ->
+ oneof([der_unordered_options(Version), pem_unordered_options(Version, PrivDir)]).
+
+der_unordered_options(Version) ->
+ ?LET(Alg, key_alg(Version), unordered_der_cert_chain_opts(Version, Alg)).
+
+pem_unordered_options(Version, PrivDir) ->
+ ?LET(Alg, key_alg(Version), unordered_pem_cert_chain_opts(Version, Alg, PrivDir)).
+
+unordered_der_cert_chain_opts(Version, Alg) ->
+ #{server_config := ServerConf,
+ client_config := ClientConf} = public_key:pkix_test_data(#{server_chain => #{root => root_key(Alg),
+ intermediates => intermediates(Alg, 4),
+ peer => peer_key(Alg)},
+ client_chain => #{root => root_key(Alg),
+ intermediates => intermediates(Alg, 4),
+ peer => peer_key(Alg)}}),
+ {client_options(Version) ++ [protocol(Version), {versions, [Version]} | unordered_der_conf(ClientConf)],
+ server_options(Version) ++ [protocol(Version), {versions, [Version]} | unordered_der_conf(ServerConf)]}.
+
+unordered_pem_cert_chain_opts(Version, Alg, PrivDir) ->
+ Index = integer_to_list(erlang:unique_integer()),
+ DerConfig = public_key:pkix_test_data(#{server_chain => #{root => root_key(Alg),
+ intermediates => intermediates(Alg, 4),
+ peer => peer_key(Alg)},
+ client_chain => #{root => root_key(Alg),
+ intermediates => intermediates(Alg, 4),
+ peer => peer_key(Alg)}}),
+
+ ClientBase = filename:join(PrivDir, "client_prop_test" ++ Index),
+ SeverBase = filename:join(PrivDir, "server_prop_test" ++ Index),
+ PemConfig = x509_test:gen_pem_config_files(DerConfig, ClientBase, SeverBase),
+ ClientConf = proplists:get_value(client_config, PemConfig),
+ ServerConf = proplists:get_value(server_config, PemConfig),
+ {client_options(Version) ++ [protocol(Version), {versions, [Version]} | unordered_pem_conf(ClientConf)],
+ server_options(Version) ++ [protocol(Version), {versions, [Version]} | unordered_pem_conf(ServerConf)]}.
+
+unordered_der_conf(Config) ->
+ Cert = proplists:get_value(cert, Config),
+ {ok, ExtractedCAs} = ssl_pkix_db:extract_trusted_certs({der, proplists:get_value(cacerts, Config)}),
+ {ok, _, [Cert | Path]} = ssl_certificate:certificate_chain(Cert, ets:new(foo, []), ExtractedCAs, []),
+ [{cert, [Cert | lists:reverse(Path)]}| proplists:delete(cert, Config)].
+
+unordered_pem_conf(Config) ->
+ CertFile = proplists:get_value(certfile, Config),
+ CACertFile = proplists:get_value(cacertfile, Config),
+ [{_, Cert, _}| _] = ssl_test_lib:pem_to_der(CertFile),
+ PemCAs = ssl_test_lib:pem_to_der(CACertFile),
+ DerList = [DerCert || {'Certificate', DerCert, not_encrypted} <- PemCAs],
+ {ok, ExtractedCAs} = ssl_pkix_db:extract_trusted_certs({der, DerList}),
+ {ok, _, [Cert | Path]} = ssl_certificate:certificate_chain(Cert, ets:new(foo, []), ExtractedCAs, []),
+ Unorded = lists:reverse(Path),
+ UnordedPemEntries = [{'Certificate', DerCert, not_encrypted} || DerCert <- Unorded],
+ PEM = public_key:pem_encode([{'Certificate', Cert, not_encrypted} |UnordedPemEntries]),
+ file:write_file(CertFile, PEM),
+ Config.
+
+extraneous_options(Version, PrivDir) ->
+ oneof([der_extraneous_options(Version),
+ pem_extraneous_options(Version, PrivDir)
+ ]).
+extra_extraneous_options(Version) ->
+ oneof([extra_der_extraneous_options(Version)]).
+
+der_extraneous_options(Version) ->
+ ?LET(Alg, key_alg(Version), extraneous_der_cert_chain_opts(Version, Alg)).
+
+pem_extraneous_options(Version, PrivDir) ->
+ ?LET(Alg, key_alg(Version), extraneous_pem_cert_chain_opts(Version, Alg, PrivDir)).
+
+extra_der_extraneous_options(Version) ->
+ ?LET(Alg, key_alg(Version), extra_extraneous_der_cert_chain_opts(Version, Alg)).
+
+unordered_extraneous_options(Version) ->
+ oneof([der_extraneous_and_unorder_options(Version)]).
+
+der_extraneous_and_unorder_options(Version) ->
+ ?LET(Alg, key_alg(Version), der_extraneous_and_unorder_chain(Version, Alg)).
+
+extraneous_der_cert_chain_opts(Version, Alg) ->
+ #{cert := OrgSRoot} = SRoot = public_key:pkix_test_root_cert("OTP test server ROOT", root_key(Alg)),
+ #{cert := OrgCRoot} = CRoot = public_key:pkix_test_root_cert("OTP test client ROOT", root_key(Alg)),
+
+ #{server_config := ServerConf0,
+ client_config := ClientConf0} = public_key:pkix_test_data(#{server_chain => #{root => SRoot,
+ intermediates => intermediates(Alg, 1),
+ peer => []},
+ client_chain => #{root => CRoot,
+ intermediates => intermediates(Alg, 1),
+ peer => []}}),
+
+ {ClientChain, ClientRoot} = extraneous_chain_and_root(ClientConf0, "OTP test client ROOT", 1),
+ {ServerChain, ServerRoot} = extraneous_chain_and_root(ServerConf0, "OTP test server ROOT", 1),
+
+
+ {client_options(Version) ++ [protocol(Version), {versions, [Version]} |
+ extraneous_der_conf(ClientChain, ServerRoot, [OrgSRoot], ClientConf0)],
+ server_options(Version) ++ [protocol(Version), {versions, [Version]} |
+ extraneous_der_conf(ServerChain, ClientRoot, [OrgCRoot], ServerConf0)]}.
+
+extraneous_pem_cert_chain_opts(Version, Alg, PrivDir) ->
+ #{cert := OrgSRoot} = SRoot = public_key:pkix_test_root_cert("OTP test server ROOT", root_key(Alg)),
+ #{cert := OrgCRoot} = CRoot = public_key:pkix_test_root_cert("OTP test client ROOT", root_key(Alg)),
+
+ #{server_config := ServerConf0,
+ client_config := ClientConf0} = public_key:pkix_test_data(#{server_chain => #{root => SRoot,
+ intermediates => intermediates(Alg, 1),
+ peer => []},
+ client_chain => #{root => CRoot,
+ intermediates => intermediates(Alg, 1),
+ peer => []}}),
+
+ {ClientChain, ClientRoot} = extraneous_chain_and_root(ClientConf0, "OTP test client ROOT", 1),
+ {ServerChain, ServerRoot} = extraneous_chain_and_root(ServerConf0, "OTP test server ROOT", 1),
+
+ %% Only use files in final step
+ {client_options(Version) ++ [protocol(Version), {versions, [Version]} |
+ extraneous_pem_conf(ClientChain, ServerRoot, OrgSRoot, ClientConf0, PrivDir)],
+ server_options(Version) ++ [protocol(Version), {versions, [Version]} |
+ extraneous_pem_conf(ServerChain, ClientRoot, OrgCRoot, ServerConf0, PrivDir)]}.
+
+extra_extraneous_der_cert_chain_opts(Version, Alg) ->
+
+ #{cert := OrgSRoot} = SRoot = public_key:pkix_test_root_cert("OTP test server ROOT", root_key(Alg)),
+ #{cert := OrgCRoot} = CRoot = public_key:pkix_test_root_cert("OTP test client ROOT", root_key(Alg)),
+
+ #{server_config := ServerConf0,
+ client_config := ClientConf0} = public_key:pkix_test_data(#{server_chain => #{root => SRoot,
+ intermediates => intermediates(Alg, 3),
+ peer => []},
+ client_chain => #{root => CRoot,
+ intermediates => intermediates(Alg, 3),
+ peer => []}}),
+
+
+ {ClientChain0, ClientRoot0} = extraneous_chain_and_root(ClientConf0, "OTP test client ROOT", 2),
+ {ServerChain0, ServerRoot0} = extraneous_chain_and_root(ServerConf0, "OTP test server ROOT", 2),
+
+ {ClientChain1, ClientRoot1} = extraneous_chain_and_root(ClientConf0, "OTP test client ROOT", 2),
+ {ServerChain1, ServerRoot1} = extraneous_chain_and_root(ServerConf0, "OTP test server ROOT", 2),
+
+ {ClientChain, ServerChain} = create_extraneous_chains(ClientChain0, ClientChain1,
+ ServerChain0, ServerChain1),
+
+ {client_options(Version) ++ [protocol(Version), {versions, [Version]} |
+ extraneous_der_conf(ClientChain, ServerRoot1, [OrgSRoot, ServerRoot0], ClientConf0)],
+ server_options(Version) ++ [protocol(Version), {versions, [Version]} |
+ extraneous_der_conf(ServerChain, ClientRoot1, [OrgCRoot, ClientRoot0], ServerConf0)]}.
+
+
+der_extraneous_and_unorder_chain(Version, Alg) ->
+
+ #{cert := OrgSRoot} = SRoot = public_key:pkix_test_root_cert("OTP test server ROOT", root_key(Alg)),
+ #{cert := OrgCRoot} = CRoot = public_key:pkix_test_root_cert("OTP test client ROOT", root_key(Alg)),
+
+ #{server_config := ServerConf0,
+ client_config := ClientConf0} =
+ public_key:pkix_test_data(#{server_chain => #{root => SRoot,
+ intermediates => intermediates(Alg, 3),
+ peer => []},
+ client_chain => #{root => CRoot,
+ intermediates => intermediates(Alg, 3),
+ peer => []}}),
+
+ {ClientChain0, ClientRoot0} = chain_and_root(ClientConf0),
+ {ServerChain0, ServerRoot0} = chain_and_root(ServerConf0),
+
+ {ClientChain1, ClientRoot1} = extraneous_chain_and_root(ClientConf0, "OTP test client ROOT", 2),
+ {ServerChain1, ServerRoot1} = extraneous_chain_and_root(ServerConf0, "OTP test server ROOT", 2),
+
+ {ClientChain, ServerChain} = create_extraneous_and_unorded(ClientChain0, ClientChain1,
+ ServerChain0, ServerChain1),
+
+ {client_options(Version) ++ [protocol(Version), {versions, [Version]} |
+ extraneous_der_conf(ClientChain, ServerRoot1, [OrgSRoot, ServerRoot0], ClientConf0)],
+ server_options(Version) ++ [protocol(Version), {versions, [Version]} |
+ extraneous_der_conf(ServerChain, ClientRoot1, [OrgCRoot, ClientRoot0], ServerConf0)]}.
+
+chain_and_root(Config) ->
+ OwnCert = proplists:get_value(cert, Config),
+ {ok, ExtractedCAs} = ssl_pkix_db:extract_trusted_certs({der, proplists:get_value(cacerts, Config)}),
+ {ok, Root, Chain} = ssl_certificate:certificate_chain(OwnCert, ets:new(foo, []), ExtractedCAs, []),
+ {Chain, Root}.
+
+extraneous_chain_and_root(Config, Name, 1) ->
+ #{cert := NewRoot, key := Key} = public_key:pkix_test_root_cert(Name, []),
+ {[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, []),
+ {[OwnCert, CA0, CA1, CA2, OldRoot], OldRoot} = chain_and_root(Config),
+ CA3 = new_intermediat(CA2, Key),
+ {[OwnCert, CA0, CA1, CA2, CA3], NewRoot}.
+
+extraneous_der_conf(Chain, NewRoot, OrgRoots,Config0) ->
+ CaCerts = proplists:get_value(cacerts, Config0),
+ Config1 = [{cert, Chain} | proplists:delete(cert, Config0)],
+ [{cacerts, [NewRoot | CaCerts -- OrgRoots]} | proplists:delete(cacerts, Config1)].
+
+extraneous_pem_conf(Chain, NewRoot, OldRoot, Config0, PrivDir) ->
+ Int = erlang:unique_integer(),
+ FileName = filename:join(PrivDir, "prop_test" ++ integer_to_list(Int)),
+ CaCerts = proplists:get_value(cacerts, Config0),
+ NewCas = [NewRoot | CaCerts -- [OldRoot]],
+ Entries = [{'Certificate', DerCert, not_encrypted} || DerCert <- Chain],
+ PemBin = public_key:pem_encode(Entries),
+ file:write_file(FileName, PemBin),
+ Config1 = [{cacerts, NewCas} | proplists:delete(cacerts, Config0)],
+ [{certfile, FileName} | proplists:delete(cert, Config1)].
+
+protocol('dtlsv1.2') ->
+ {protocol, dtls};
+protocol('dtlsv1') ->
+ {protocol, dtls};
+protocol(_) ->
+ {protocol,tls}.
+
+create_extraneous_chains([Client, _CCA0, _CCA1, CCA2, _CCA3], [Client, OCCA0, OCCA1, OCCA2, OCROOT],
+ [Server, _SCA0, _SCA1, SCA2, _SROOT], [Server, OSCA0, OSCA1, OSCA2, OSROOT]) ->
+ {[Client, OCCA0, OCCA1, CCA2, OCCA2, OCROOT], [Server, OSCA0, OSCA1, SCA2, OSCA2, OSROOT]}.
+create_extraneous_and_unorded([Client, _CCA0, _CCA1, CCA2, _CCA3], [Client, OCCA0, OCCA1, OCCA2, OCROOT],
+ [Server, _SCA0, _SCA1, SCA2, _SROOT], [Server, OSCA0, OSCA1, OSCA2, OSROOT]) ->
+ {[Client, OCCA0, CCA2, OCCA2, OCROOT, OCCA1], [Server, OSCA0, SCA2, OSCA2, OSROOT, OSCA1]}.
+
+root_key(ecdsa) ->
+ []; %% Just generate one
+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
+peer_key(rsa) ->
+ %% As rsa keygen is not guaranteed to be fast
+ [{key, ssl_test_lib:hardcode_rsa_key(6)}].
+
+intermediates(ecdsa, N) ->
+ lists:duplicate(N, []);
+intermediates(rsa, N) when N =< 4 ->
+ Default = lists:duplicate(N, []),
+ %% As rsa keygen is not guaranteed to be fast
+ hardcode_rsa_keys(Default, 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]).
+
+new_intermediat(CA0, Key) ->
+ OTPCert = public_key:pkix_decode_cert(CA0, otp),
+ TBSCert = OTPCert#'OTPCertificate'.tbsCertificate,
+ Num = TBSCert#'OTPTBSCertificate'.serialNumber,
+ public_key:pkix_sign(TBSCert#'OTPTBSCertificate'{serialNumber = Num+1}, Key).
+
+