aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/dialyzer/src/dialyzer_dataflow.erl2
-rw-r--r--lib/dialyzer/src/dialyzer_typesig.erl6
-rw-r--r--lib/dialyzer/test/small_SUITE_data/results/stacktrace5
-rw-r--r--lib/dialyzer/test/small_SUITE_data/src/stacktrace.erl73
-rw-r--r--lib/hipe/cerl/erl_bif_types.erl10
-rw-r--r--lib/kernel/doc/src/code.xml18
-rw-r--r--lib/ssl/doc/src/ssl.xml98
-rw-r--r--lib/ssl/doc/src/using_ssl.xml46
-rw-r--r--lib/ssl/src/dtls_v1.erl8
-rw-r--r--lib/ssl/src/ssl.erl147
-rw-r--r--lib/ssl/src/ssl_cipher.erl292
-rw-r--r--lib/ssl/test/ssl_basic_SUITE.erl168
-rw-r--r--lib/ssl/test/ssl_test_lib.erl81
-rw-r--r--lib/stdlib/doc/src/erl_tar.xml41
-rw-r--r--lib/stdlib/src/erl_tar.erl80
-rw-r--r--lib/stdlib/src/erl_tar.hrl16
-rw-r--r--lib/stdlib/test/tar_SUITE.erl30
17 files changed, 849 insertions, 272 deletions
diff --git a/lib/dialyzer/src/dialyzer_dataflow.erl b/lib/dialyzer/src/dialyzer_dataflow.erl
index 384912f983..32f58efc75 100644
--- a/lib/dialyzer/src/dialyzer_dataflow.erl
+++ b/lib/dialyzer/src/dialyzer_dataflow.erl
@@ -299,7 +299,7 @@ traverse(Tree, Map, State) ->
match_fail -> t_none();
raise -> t_none();
bs_init_writable -> t_from_term(<<>>);
- build_stacktrace -> t_list();
+ build_stacktrace -> erl_bif_types:type(erlang, build_stacktrace, 0);
Other -> erlang:error({'Unsupported primop', Other})
end,
{State, Map, Type};
diff --git a/lib/dialyzer/src/dialyzer_typesig.erl b/lib/dialyzer/src/dialyzer_typesig.erl
index 953c562083..dede475f98 100644
--- a/lib/dialyzer/src/dialyzer_typesig.erl
+++ b/lib/dialyzer/src/dialyzer_typesig.erl
@@ -418,7 +418,11 @@ traverse(Tree, DefinedVars, State) ->
match_fail -> throw(error);
raise -> throw(error);
bs_init_writable -> {State, t_from_term(<<>>)};
- build_stacktrace -> {State, t_list()};
+ build_stacktrace ->
+ V = mk_var(Tree),
+ Type = erl_bif_types:type(erlang, build_stacktrace, 0),
+ State1 = state__store_conj(V, sub, Type, State),
+ {State1, V};
Other -> erlang:error({'Unsupported primop', Other})
end;
'receive' ->
diff --git a/lib/dialyzer/test/small_SUITE_data/results/stacktrace b/lib/dialyzer/test/small_SUITE_data/results/stacktrace
new file mode 100644
index 0000000000..fd60881953
--- /dev/null
+++ b/lib/dialyzer/test/small_SUITE_data/results/stacktrace
@@ -0,0 +1,5 @@
+
+stacktrace.erl:11: The pattern {'a', 'b'} can never match the type [{atom(),atom(),[any()] | byte(),[{'file',string()} | {'line',pos_integer()}]}]
+stacktrace.erl:19: The pattern ['a', 'b'] can never match the type [{atom(),atom(),[any()] | byte(),[{'file',string()} | {'line',pos_integer()}]}]
+stacktrace.erl:44: The pattern {'a', 'b'} can never match the type [{atom(),atom(),[any()] | byte(),[{'file',string()} | {'line',pos_integer()}]}]
+stacktrace.erl:53: The pattern ['a', 'b'] can never match the type [{atom(),atom(),[any()] | byte(),[{'file',string()} | {'line',pos_integer()}]}]
diff --git a/lib/dialyzer/test/small_SUITE_data/src/stacktrace.erl b/lib/dialyzer/test/small_SUITE_data/src/stacktrace.erl
new file mode 100644
index 0000000000..de79e710e9
--- /dev/null
+++ b/lib/dialyzer/test/small_SUITE_data/src/stacktrace.erl
@@ -0,0 +1,73 @@
+-module(stacktrace).
+
+%% Check the stacktrace variable introduced in Erlang/OTP 21.0
+
+-export([t1/0, t2/0, t3/0, t4/0, s1/0, s2/0, s3/0, s4/0]).
+
+t1() ->
+ try foo:bar()
+ catch
+ E:P:S ->
+ {a,b} = S, % can never match
+ {E, P}
+ end.
+
+t2() ->
+ try foo:bar()
+ catch
+ E:P:S ->
+ [a,b] = S, % can never match
+ {E, P}
+ end.
+
+t3() ->
+ try foo:bar()
+ catch
+ E:P:S ->
+ [{m,f,[],[]}] = S,
+ {E, P}
+ end.
+
+t4() ->
+ try foo:bar()
+ catch
+ E:P:S ->
+ [{m,f,1,[{file,"tjo"},{line,95}]}] = S,
+ {E, P}
+ end.
+
+s1() ->
+ try foo:bar()
+ catch
+ E:P ->
+ S = erlang:get_stacktrace(),
+ {a,b} = S, % can never match
+ {E, P}
+ end.
+
+s2() ->
+ try foo:bar()
+ catch
+ E:P ->
+ S = erlang:get_stacktrace(),
+ [a,b] = S, % can never match
+ {E, P}
+ end.
+
+s3() ->
+ try foo:bar()
+ catch
+ E:P ->
+ S = erlang:get_stacktrace(),
+ [{m,f,[],[]}] = S,
+ {E, P}
+ end.
+
+s4() ->
+ try foo:bar()
+ catch
+ E:P ->
+ S = erlang:get_stacktrace(),
+ [{m,f,1,[{file,"tjo"},{line,95}]}] = S,
+ {E, P}
+ end.
diff --git a/lib/hipe/cerl/erl_bif_types.erl b/lib/hipe/cerl/erl_bif_types.erl
index 5fda857bf1..bfffb8db41 100644
--- a/lib/hipe/cerl/erl_bif_types.erl
+++ b/lib/hipe/cerl/erl_bif_types.erl
@@ -585,6 +585,13 @@ type(erlang, float, 1, Xs, Opaques) ->
%% Guard bif, needs to be here.
type(erlang, floor, 1, Xs, Opaques) ->
strict(erlang, floor, 1, Xs, fun (_) -> t_integer() end, Opaques);
+%% Primop, needs to be somewhere.
+type(erlang, build_stacktrace, 0, _, _Opaques) ->
+ t_list(t_tuple([t_module(),
+ t_atom(),
+ t_sup([t_arity(),t_list()]),
+ t_list(t_sup([t_tuple([t_atom('file'),t_string()]),
+ t_tuple([t_atom('line'),t_pos_integer()])]))]));
%% Guard bif, needs to be here.
type(erlang, hd, 1, Xs, Opaques) ->
strict(erlang, hd, 1, Xs, fun ([X]) -> t_cons_hd(X) end, Opaques);
@@ -2336,6 +2343,9 @@ arg_types(erlang, float, 1) ->
%% Guard bif, needs to be here.
arg_types(erlang, floor, 1) ->
[t_number()];
+%% Primop, needs to be somewhere.
+arg_types(erlang, build_stacktrace, 0) ->
+ [];
%% Guard bif, needs to be here.
arg_types(erlang, hd, 1) ->
[t_cons()];
diff --git a/lib/kernel/doc/src/code.xml b/lib/kernel/doc/src/code.xml
index c94f612c01..bd95819636 100644
--- a/lib/kernel/doc/src/code.xml
+++ b/lib/kernel/doc/src/code.xml
@@ -34,26 +34,28 @@
<p>This module contains the interface to the Erlang
<em>code server</em>, which deals with the loading of compiled
code into a running Erlang runtime system.</p>
- <p>The runtime system can be started in <em>embedded</em> or
- <em>interactive</em> mode. Which one is decided by command-line
+ <p>The runtime system can be started in <em>interactive</em> or
+ <em>embedded</em> mode. Which one is decided by the command-line
flag <c>-mode</c>:</p>
<pre>
% <input>erl -mode interactive</input></pre>
<p>The modes are as follows:</p>
<list type="bulleted">
<item>
- <p>In embedded mode, all code is loaded during system startup
- according to the boot script. (Code can also be loaded later
- by explicitly ordering the code server to do so).</p>
- </item>
- <item>
<p>In interactive mode, which is default, only some code is loaded
- during system startup, basically the modules needed by the runtime
+ during system startup, basically the modules needed by the runtime
system. Other code is dynamically loaded when first
referenced. When a call to a function in a certain module is
made, and the module is not loaded, the code server searches
for and tries to load the module.</p>
</item>
+ <item>
+ <p>In embedded mode, modules are not auto loaded. Trying to use
+ a module that has not been loaded results in an error. This mode is
+ recommended when the boot script loads all modules, as it is
+ typically done in OTP releases. (Code can still be loaded later
+ by explicitly ordering the code server to do so).</p>
+ </item>
</list>
<p>To prevent accidentally reloading of modules affecting the Erlang
runtime system, directories <c>kernel</c>, <c>stdlib</c>,
diff --git a/lib/ssl/doc/src/ssl.xml b/lib/ssl/doc/src/ssl.xml
index 80b639155b..4f72114ae9 100644
--- a/lib/ssl/doc/src/ssl.xml
+++ b/lib/ssl/doc/src/ssl.xml
@@ -141,23 +141,25 @@
<tag><c>sslsocket() =</c></tag>
<item><p>opaque()</p></item>
- <tag><marker id="type-protocol"/><c> protocol_versions() =</c></tag>
+ <tag><marker id="type-protocol"/><c> protocol_version() =</c></tag>
<item><p><c> ssl_tls_protocol() | dtls_protocol() </c></p></item>
- <tag><marker id="type-protocol"/><c> ssl_tls_protocol() =</c></tag>
<item><p><c>sslv3 | tlsv1 | 'tlsv1.1' | 'tlsv1.2'</c></p></item>
<tag><marker id="type-protocol"/><c> dtls_protocol() =</c></tag>
<item><p><c>'dtlsv1' | 'dtlsv1.2'</c></p></item>
<tag><c>ciphers() =</c></tag>
- <item><p><c>= [ciphersuite()] | string()</c></p>
- <p>According to old API.</p></item>
+ <item><p><c>= [ciphersuite()]</c></p>
+ <p>Tuples and string formats accepted by versions
+ before ssl-8.2.4 will be converted for backwards compatibility</p></item>
<tag><c>ciphersuite() =</c></tag>
-
- <item><p><c>{key_exchange(), cipher(), MAC::hash()} |
- {key_exchange(), cipher(), MAC::hash(), PRF::hash()}</c></p></item>
+ <item><p><c>
+ #{key_exchange := key_exchange(),
+ cipher := cipher(),
+ mac := MAC::hash() | aead,
+ prf := PRF::hash() | default_prf} </c></p></item>
<tag><c>key_exchange()=</c></tag>
<item><p><c>rsa | dhe_dss | dhe_rsa | dh_anon | psk | dhe_psk
@@ -174,6 +176,12 @@
<tag><c>prf_random() =</c></tag>
<item><p><c>client_random | server_random</c></p></item>
+ <tag><c>cipher_filters() =</c></tag>
+ <item><p><c> [{key_exchange | cipher | mac | prf, algo_filter()}])</c></p></item>
+
+ <tag><c>algo_filter() =</c></tag>
+ <item><p>fun(key_exchange() | cipher() | hash() | aead | default_prf) -> true | false </p></item>
+
<tag><c>srp_param_type() =</c></tag>
<item><p><c>srp_1024 | srp_1536 | srp_2048 | srp_3072
| srp_4096 | srp_6144 | srp_8192</c></p></item>
@@ -465,7 +473,8 @@ marker="public_key:public_key#pkix_path_validation-3">public_key:pkix_path_valid
marker="public_key:public_key#pkix_path_validation-3">public_key:pkix_path_validation/3</seealso>
with the selected CA as trusted anchor and the rest of the chain.</p></item>
- <tag><c>{versions, [protocol_versions()]}</c></tag>
+ <tag><c>{versions, [protocol_version()]}</c></tag>
+
<item><p>TLS protocol versions supported by started clients and servers.
This option overrides the application environment option
<c>protocol_version</c> and <c>dtls_protocol_version</c>. If the environment option is not set, it defaults
@@ -838,23 +847,55 @@ fun(srp, Username :: string(), UserState :: term()) ->
</section>
<funcs>
+
+ <func>
+ <name>append_cipher_suites(Deferred, Suites) -> ciphers() </name>
+ <fsummary></fsummary>
+ <type>
+ <v>Deferred = ciphers() | cipher_filters() </v>
+ <v>Suites = ciphers() </v>
+ </type>
+ <desc><p>Make <c>Deferred</c> suites become the least preferred
+ suites, that is put them at the end of the cipher suite list
+ <c>Suites</c> after removing them from <c>Suites</c> if
+ present. <c>Deferred</c> may be a list of cipher suits or a
+ list of filters in which case the filters are use on <c>Suites</c> to
+ extract the Deferred cipher list.</p>
+ </desc>
+ </func>
+
<func>
<name>cipher_suites() -></name>
- <name>cipher_suites(Type) -> ciphers()</name>
- <name>cipher_suites(Type, protocol_version()) -> ciphers()</name>
+ <name>cipher_suites(Type) -> old_ciphers()</name>
<fsummary>Returns a list of supported cipher suites.</fsummary>
<type>
<v>Type = erlang | openssl | all</v>
</type>
- <desc><p>Returns a list of supported cipher suites.
+ <desc>
+ <p>Returns a list of supported cipher suites.
+ This function will become deprecated in OTP 21, and replaced
+ by <seealso marker="#cipher_suites-2">ssl:cipher-suites/2</seealso>
<c>cipher_suites()</c> is equivalent to <c>cipher_suites(erlang).</c>
Type <c>openssl</c> is provided for backwards compatibility with the
old SSL, which used OpenSSL. <c>cipher_suites(all)</c> returns
all available cipher suites. The cipher suites not present
in <c>cipher_suites(erlang)</c> but included in
<c>cipher_suites(all)</c> are not used unless explicitly configured
- by the user. If the version option is not specified, the highest supported
- TLS version will be used to determine the supported cipher suites</p>
+ by the user.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name>cipher_suites(Supported, Version) -> ciphers()</name>
+ <fsummary>Returns a list of all default or
+ all supported cipher suites.</fsummary>
+ <type>
+ <v> Supported = default | all | anonymous </v>
+ <v> Version = protocol_version() </v>
+ </type>
+ <desc><p>Returns all default or all supported (except anonymous),
+ or all anonymous cipher suites for a
+ TLS version</p>
</desc>
</func>
@@ -1019,6 +1060,21 @@ fun(srp, Username :: string(), UserState :: term()) ->
</desc>
</func>
+ <func>
+ <name>filter_cipher_suites(Suites, Filters) -> ciphers()</name>
+ <fsummary></fsummary>
+ <type>
+ <v> Suites = ciphers()</v>
+ <v> Filters = cipher_filters()</v>
+ </type>
+ <desc><p>Removes cipher suites if any of the filter functions
+ returns false for any part of the cipher suite. This function
+ also calls default filter functions to make sure the cipher
+ suites are supported by crypto. If no filter function is supplied for some
+ part the default behaviour is fun(Algorithm) -> true.</p>
+ </desc>
+ </func>
+
<func>
<name>format_error(Reason) -> string()</name>
<fsummary>Returns an error string.</fsummary>
@@ -1116,6 +1172,22 @@ fun(srp, Username :: string(), UserState :: term()) ->
<p>Returns the address and port number of the peer.</p>
</desc>
</func>
+
+ <func>
+ <name>prepend_cipher_suites(Preferred, Suites) -> ciphers()</name>
+ <fsummary></fsummary>
+ <type>
+ <v>Preferred = ciphers() | cipher_filters() </v>
+ <v>Suites = ciphers() </v>
+ </type>
+ <desc><p>Make <c>Preferred</c> suites become the most preferred
+ suites that is put them at the head of the cipher suite list
+ <c>Suites</c> after removing them from <c>Suites</c> if
+ present. <c>Preferred</c> may be a list of cipher suits or a
+ list of filters in which case the filters are use on <c>Suites</c> to
+ extract the preferred cipher list. </p>
+ </desc>
+ </func>
<func>
<name>prf(Socket, Secret, Label, Seed, WantedLength) -> {ok, binary()} | {error, reason()}</name>
diff --git a/lib/ssl/doc/src/using_ssl.xml b/lib/ssl/doc/src/using_ssl.xml
index c369c3c133..3ef33df719 100644
--- a/lib/ssl/doc/src/using_ssl.xml
+++ b/lib/ssl/doc/src/using_ssl.xml
@@ -153,7 +153,51 @@ ok</code>
</section>
</section>
- <section>
+ <section>
+ <title>Customizing cipher suits</title>
+
+ <p>Fetch default cipher suite list for an TLS/DTLS version. Change default
+ to all to get all possible cipher suites.</p>
+ <code type="erl">1> Default = ssl:cipher_suites(default, 'tlsv1.2').
+ [#{cipher => aes_256_gcm,key_exchange => ecdhe_ecdsa,
+ mac => aead,prf => sha384}, ....]
+</code>
+
+ <p>In OTP 20 it is desirable to remove all cipher suites
+ that uses rsa kexchange (removed from default in 21) </p>
+ <code type="erl">2> NoRSA =
+ ssl:filter_cipher_suites(Default,
+ [{key_exchange, fun(rsa) -> false;
+ (_) -> true end}]).
+ [...]
+ </code>
+
+ <p> Pick just a few suites </p>
+ <code type="erl"> 3> Suites =
+ ssl:filter_cipher_suites(Default,
+ [{key_exchange, fun(ecdh_ecdsa) -> true;
+ (_) -> false end},
+ {cipher, fun(aes_128_cbc) ->true;
+ (_) ->false end}]).
+ [#{cipher => aes_128_cbc,key_exchange => ecdh_ecdsa,
+ mac => sha256,prf => sha256},
+ #{cipher => aes_128_cbc,key_exchange => ecdh_ecdsa,mac => sha,
+ prf => default_prf}]
+ </code>
+
+ <p> Make some particular suites the most preferred, or least
+ preferred by changing prepend to append.</p>
+ <code type="erl"> 4>ssl:prepend_cipher_suites(Suites, Default).
+ [#{cipher => aes_128_cbc,key_exchange => ecdh_ecdsa,
+ mac => sha256,prf => sha256},
+ #{cipher => aes_128_cbc,key_exchange => ecdh_ecdsa,mac => sha,
+ prf => default_prf},
+ #{cipher => aes_256_cbc,key_exchange => ecdhe_ecdsa,
+ mac => sha384,prf => sha384}, ...]
+ </code>
+ </section>
+
+ <section>
<title>Using an Engine Stored Key</title>
<p>Erlang ssl application is able to use private keys provided
diff --git a/lib/ssl/src/dtls_v1.erl b/lib/ssl/src/dtls_v1.erl
index 51ee8ec047..0f6344b6f7 100644
--- a/lib/ssl/src/dtls_v1.erl
+++ b/lib/ssl/src/dtls_v1.erl
@@ -21,7 +21,7 @@
-include("ssl_cipher.hrl").
--export([suites/1, all_suites/1, hmac_hash/3, ecc_curves/1,
+-export([suites/1, all_suites/1, anonymous_suites/1,hmac_hash/3, ecc_curves/1,
corresponding_tls_version/1, corresponding_dtls_version/1,
cookie_secret/0, cookie_timeout/0]).
@@ -40,6 +40,12 @@ all_suites(Version) ->
end,
ssl_cipher:all_suites(corresponding_tls_version(Version))).
+anonymous_suites(Version) ->
+ lists:filter(fun(Cipher) ->
+ is_acceptable_cipher(ssl_cipher:suite_definition(Cipher))
+ end,
+ ssl_cipher:anonymous_suites(corresponding_tls_version(Version))).
+
hmac_hash(MacAlg, MacSecret, Value) ->
tls_v1:hmac_hash(MacAlg, MacSecret, Value).
diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl
index a298012f26..0b035d31be 100644
--- a/lib/ssl/src/ssl.erl
+++ b/lib/ssl/src/ssl.erl
@@ -39,7 +39,9 @@
]).
%% SSL/TLS protocol handling
--export([cipher_suites/0, cipher_suites/1, cipher_suites/2, eccs/0, eccs/1, versions/0,
+-export([cipher_suites/0, cipher_suites/1, cipher_suites/2, filter_cipher_suites/2,
+ prepend_cipher_suites/2, append_cipher_suites/2,
+ eccs/0, eccs/1, versions/0,
format_error/1, renegotiate/1, prf/5, negotiated_protocol/1,
connection_information/1, connection_information/2]).
%% Misc
@@ -379,35 +381,91 @@ negotiated_protocol(#sslsocket{pid = Pid}) ->
cipher_suites() ->
cipher_suites(erlang).
%%--------------------------------------------------------------------
--spec cipher_suites(erlang | openssl | all) -> [ssl_cipher:old_erl_cipher_suite() | string()].
+-spec cipher_suites(erlang | openssl | all) ->
+ [ssl_cipher:old_erl_cipher_suite() | string()].
%% Description: Returns all supported cipher suites.
%%--------------------------------------------------------------------
cipher_suites(erlang) ->
- Version = tls_record:highest_protocol_version([]),
- cipher_suites(erlang, Version);
+ [ssl_cipher:erl_suite_definition(Suite) || Suite <- available_suites(default)];
+
cipher_suites(openssl) ->
- Version = tls_record:highest_protocol_version([]),
- cipher_suites(openssl, Version);
+ [ssl_cipher:openssl_suite_name(Suite) ||
+ Suite <- available_suites(default)];
+
cipher_suites(all) ->
- Version = tls_record:highest_protocol_version([]),
- cipher_suites(all, Version).
+ [ssl_cipher:erl_suite_definition(Suite) || Suite <- available_suites(all)].
%%--------------------------------------------------------------------
--spec cipher_suites(erlang | openssl | all, tls_record:tls_version() |
- dtls_record:dtls_version()) -> [ssl_cipher:old_erl_cipher_suite() | string()].
-%% Description: Returns all supported cipher suites.
+-spec cipher_suites(default | all | anonymous, tls_record:tls_version() | dtls_record:dtls_version() |
+ tls_record:tls_atom_version() | dtls_record:dtls_atom_version()) ->
+ [ssl_cipher:erl_cipher_suite()].
+%% Description: Returns all default and all supported cipher suites for a
+%% TLS/DTLS version
%%--------------------------------------------------------------------
-cipher_suites(Type, Version) when Version == 'dtlsv1';
- Version == 'dtlsv1.2' ->
- cipher_suites(Type, dtls_record:protocol_version(Version));
-cipher_suites(Type, Version) when is_atom(Version) ->
- cipher_suites(Type, tls_record:protocol_version(Version));
-cipher_suites(erlang, Version) ->
- [ssl_cipher:erl_suite_definition(Suite) || Suite <- available_suites(default, Version)];
-cipher_suites(openssl, Version) ->
- [ssl_cipher:openssl_suite_name(Suite) || Suite <- available_suites(default, Version)];
-cipher_suites(all, Version) ->
- [ssl_cipher:erl_suite_definition(Suite) || Suite <- available_suites(all, Version)].
+cipher_suites(Base, Version) when Version == 'tlsv1.2';
+ Version == 'tlsv1.1';
+ Version == tlsv1;
+ Version == sslv3 ->
+ cipher_suites(Base, tls_record:protocol_version(Version));
+cipher_suites(Base, Version) when Version == 'dtlsv1.2';
+ Version == 'dtlsv1'->
+ cipher_suites(Base, dtls_record:protocol_version(Version));
+cipher_suites(Base, Version) ->
+ [ssl_cipher:suite_definition(Suite) || Suite <- supported_suites(Base, Version)].
+
+%%--------------------------------------------------------------------
+-spec filter_cipher_suites([ssl_cipher:erl_cipher_suite()],
+ [{key_exchange | cipher | mac | prf, fun()}] | []) ->
+ [ssl_cipher:erl_cipher_suite()].
+%% Description: Removes cipher suites if any of the filter functions returns false
+%% for any part of the cipher suite. This function also calls default filter functions
+%% to make sure the cipher suite are supported by crypto.
+%%--------------------------------------------------------------------
+filter_cipher_suites(Suites, Filters0) ->
+ #{key_exchange_filters := KexF,
+ cipher_filters := CipherF,
+ mac_filters := MacF,
+ prf_filters := PrfF}
+ = ssl_cipher:crypto_support_filters(),
+ Filters = #{key_exchange_filters => add_filter(proplists:get_value(key_exchange, Filters0), KexF),
+ cipher_filters => add_filter(proplists:get_value(cipher, Filters0), CipherF),
+ mac_filters => add_filter(proplists:get_value(mac, Filters0), MacF),
+ prf_filters => add_filter(proplists:get_value(prf, Filters0), PrfF)},
+ ssl_cipher:filter_suites(Suites, Filters).
+%%--------------------------------------------------------------------
+-spec prepend_cipher_suites([ssl_cipher:erl_cipher_suite()] |
+ [{key_exchange | cipher | mac | prf, fun()}],
+ [ssl_cipher:erl_cipher_suite()]) ->
+ [ssl_cipher:erl_cipher_suite()].
+%% Description: Make <Preferred> suites become the most prefered
+%% suites that is put them at the head of the cipher suite list
+%% and remove them from <Suites> if present. <Preferred> may be a
+%% list of cipher suits or a list of filters in which case the
+%% filters are use on Suites to extract the the preferred
+%% cipher list.
+%% --------------------------------------------------------------------
+prepend_cipher_suites([First | _] = Preferred, Suites0) when is_map(First) ->
+ Suites = Preferred ++ (Suites0 -- Preferred),
+ Suites;
+prepend_cipher_suites(Filters, Suites) ->
+ Preferred = filter_cipher_suites(Suites, Filters),
+ Preferred ++ (Suites -- Preferred).
+%%--------------------------------------------------------------------
+-spec append_cipher_suites(Deferred :: [ssl_cipher:erl_cipher_suite()] |
+ [{key_exchange | cipher | mac | prf, fun()}],
+ [ssl_cipher:erl_cipher_suite()]) ->
+ [ssl_cipher:erl_cipher_suite()].
+%% Description: Make <Deferred> suites suites become the
+%% least prefered suites that is put them at the end of the cipher suite list
+%% and removed them from <Suites> if present.
+%%
+%%--------------------------------------------------------------------
+append_cipher_suites([First | _] = Deferred, Suites0) when is_map(First)->
+ Suites = (Suites0 -- Deferred) ++ Deferred,
+ Suites;
+append_cipher_suites(Filters, Suites) ->
+ Deferred = filter_cipher_suites(Suites, Filters),
+ (Suites -- Deferred) ++ Deferred.
%%--------------------------------------------------------------------
-spec eccs() -> tls_v1:curves().
@@ -661,14 +719,21 @@ tls_version({254, _} = Version) ->
%%%--------------------------------------------------------------
%%% Internal functions
%%%--------------------------------------------------------------------
-
%% Possible filters out suites not supported by crypto
-available_suites(default, Version) ->
+available_suites(default) ->
+ Version = tls_record:highest_protocol_version([]),
ssl_cipher:filter_suites(ssl_cipher:suites(Version));
-
-available_suites(all, Version) ->
+available_suites(all) ->
+ Version = tls_record:highest_protocol_version([]),
ssl_cipher:filter_suites(ssl_cipher:all_suites(Version)).
+supported_suites(default, Version) ->
+ ssl_cipher:suites(Version);
+supported_suites(all, Version) ->
+ ssl_cipher:all_suites(Version);
+supported_suites(anonymous, Version) ->
+ ssl_cipher:anonymous_suites(Version).
+
do_listen(Port, #config{transport_info = {Transport, _, _, _}} = Config, tls_connection) ->
tls_socket:listen(Transport, Port, Config);
@@ -1178,17 +1243,21 @@ handle_cipher_option(Value, Version) when is_list(Value) ->
binary_cipher_suites(Version, []) ->
%% Defaults to all supported suites that does
%% not require explicit configuration
- ssl_cipher:filter_suites(ssl_cipher:suites(tls_version(Version)));
+ default_binary_suites(Version);
+binary_cipher_suites(Version, [Map|_] = Ciphers0) when is_map(Map) ->
+ Ciphers = [ssl_cipher:suite(C) || C <- Ciphers0],
+ binary_cipher_suites(Version, Ciphers);
binary_cipher_suites(Version, [Tuple|_] = Ciphers0) when is_tuple(Tuple) ->
Ciphers = [ssl_cipher:suite(tuple_to_map(C)) || C <- Ciphers0],
binary_cipher_suites(Version, Ciphers);
binary_cipher_suites(Version, [Cipher0 | _] = Ciphers0) when is_binary(Cipher0) ->
- All = ssl_cipher:all_suites(tls_version(Version)),
+ All = ssl_cipher:all_suites(Version) ++
+ ssl_cipher:anonymous_suites(Version),
case [Cipher || Cipher <- Ciphers0, lists:member(Cipher, All)] of
[] ->
%% Defaults to all supported suites that does
%% not require explicit configuration
- ssl_cipher:filter_suites(ssl_cipher:suites(tls_version(Version)));
+ default_binary_suites(Version);
Ciphers ->
Ciphers
end;
@@ -1201,6 +1270,9 @@ binary_cipher_suites(Version, Ciphers0) ->
Ciphers = [ssl_cipher:openssl_suite(C) || C <- string:lexemes(Ciphers0, ":")],
binary_cipher_suites(Version, Ciphers).
+default_binary_suites(Version) ->
+ ssl_cipher:filter_suites(ssl_cipher:suites(Version)).
+
tuple_to_map({Kex, Cipher, Mac}) ->
#{key_exchange => Kex,
cipher => Cipher,
@@ -1209,9 +1281,19 @@ tuple_to_map({Kex, Cipher, Mac}) ->
tuple_to_map({Kex, Cipher, Mac, Prf}) ->
#{key_exchange => Kex,
cipher => Cipher,
- mac => Mac,
+ mac => tuple_to_map_mac(Cipher, Mac),
prf => Prf}.
+%% Backwards compatible
+tuple_to_map_mac(aes_128_gcm, _) ->
+ aead;
+tuple_to_map_mac(aes_256_gcm, _) ->
+ aead;
+tuple_to_map_mac(chacha20_poly1305, _) ->
+ aead;
+tuple_to_map_mac(_, MAC) ->
+ MAC.
+
handle_eccs_option(Value, Version) when is_list(Value) ->
{_Major, Minor} = tls_version(Version),
try tls_v1:ecc_curves(Minor, Value) of
@@ -1490,3 +1572,8 @@ reject_alpn_next_prot_options([Opt| AlpnNextOpts], Opts) ->
false ->
reject_alpn_next_prot_options(AlpnNextOpts, Opts)
end.
+
+add_filter(undefined, Filters) ->
+ Filters;
+add_filter(Filter, Filters) ->
+ [Filter | Filters].
diff --git a/lib/ssl/src/ssl_cipher.erl b/lib/ssl/src/ssl_cipher.erl
index 7c5cff3665..1d645e5782 100644
--- a/lib/ssl/src/ssl_cipher.erl
+++ b/lib/ssl/src/ssl_cipher.erl
@@ -36,9 +36,11 @@
-export([security_parameters/2, security_parameters/3, suite_definition/1,
erl_suite_definition/1,
cipher_init/3, decipher/6, cipher/5, decipher_aead/6, cipher_aead/6,
- suite/1, suites/1, all_suites/1,
- ec_keyed_suites/0, chacha_suites/1, anonymous_suites/1, psk_suites/1, srp_suites/0,
- rc4_suites/1, des_suites/1, rsa_suites/1, openssl_suite/1, openssl_suite_name/1, filter/2, filter_suites/1,
+ suite/1, suites/1, all_suites/1, crypto_support_filters/0,
+ ec_keyed_suites/0, chacha_suites/1, anonymous_suites/1, psk_suites/1, psk_suites_anon/1,
+ srp_suites/0, srp_suites_anon/0,
+ rc4_suites/1, des_suites/1, rsa_suites/1, openssl_suite/1, openssl_suite_name/1,
+ filter/2, filter_suites/1, filter_suites/2,
hash_algorithm/1, sign_algorithm/1, is_acceptable_hash/2, is_fallback/1,
random_bytes/1, calc_mac_hash/4,
is_stream_ciphersuite/1]).
@@ -53,7 +55,7 @@
-type key_algo() :: null | rsa | dhe_rsa | dhe_dss | ecdhe_ecdsa| ecdh_ecdsa | ecdh_rsa| srp_rsa| srp_dss | psk | dhe_psk | rsa_psk | dh_anon | ecdh_anon | srp_anon.
-type erl_cipher_suite() :: #{key_exchange := key_algo(),
cipher := cipher(),
- mac := hash(),
+ mac := hash() | aead,
prf := hash() | default_prf %% Old cipher suites, version dependent
}.
-type old_erl_cipher_suite() :: {key_algo(), cipher(), hash()} % Pre TLS 1.2
@@ -321,12 +323,12 @@ suites({_, Minor}) ->
all_suites({3, _} = Version) ->
suites(Version)
++ chacha_suites(Version)
- ++ anonymous_suites(Version)
++ psk_suites(Version)
++ srp_suites()
++ rc4_suites(Version)
++ des_suites(Version)
++ rsa_suites(Version);
+
all_suites(Version) ->
dtls_v1:all_suites(Version).
%%--------------------------------------------------------------------
@@ -350,12 +352,12 @@ chacha_suites(_) ->
%% if explicitly set by user. Intended only for testing.
%%--------------------------------------------------------------------
anonymous_suites({3, N}) ->
- anonymous_suites(N);
+ srp_suites_anon() ++ anonymous_suites(N);
anonymous_suites({254, _} = Version) ->
- anonymous_suites(dtls_v1:corresponding_tls_version(Version))
- -- [?TLS_DH_anon_WITH_RC4_128_MD5];
+ dtls_v1:anonymous_suites(Version);
anonymous_suites(N)
when N >= 3 ->
+ psk_suites_anon(N) ++
[?TLS_DH_anon_WITH_AES_128_GCM_SHA256,
?TLS_DH_anon_WITH_AES_256_GCM_SHA384,
?TLS_DH_anon_WITH_AES_128_CBC_SHA256,
@@ -364,20 +366,20 @@ anonymous_suites(N)
?TLS_ECDH_anon_WITH_AES_256_CBC_SHA,
?TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA,
?TLS_DH_anon_WITH_RC4_128_MD5];
-
-anonymous_suites(2) ->
+anonymous_suites(2 = N) ->
+ psk_suites_anon(N) ++
[?TLS_ECDH_anon_WITH_AES_128_CBC_SHA,
?TLS_ECDH_anon_WITH_AES_256_CBC_SHA,
?TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA,
?TLS_DH_anon_WITH_DES_CBC_SHA,
?TLS_DH_anon_WITH_RC4_128_MD5];
-
anonymous_suites(N) when N == 0;
N == 1 ->
- [?TLS_DH_anon_WITH_RC4_128_MD5,
- ?TLS_DH_anon_WITH_3DES_EDE_CBC_SHA,
- ?TLS_DH_anon_WITH_DES_CBC_SHA
- ].
+ psk_suites_anon(N) ++
+ [?TLS_DH_anon_WITH_RC4_128_MD5,
+ ?TLS_DH_anon_WITH_3DES_EDE_CBC_SHA,
+ ?TLS_DH_anon_WITH_DES_CBC_SHA
+ ].
%%--------------------------------------------------------------------
-spec psk_suites(ssl_record:ssl_version() | integer()) -> [cipher_suite()].
@@ -390,41 +392,52 @@ psk_suites({3, N}) ->
psk_suites(N)
when N >= 3 ->
[
- ?TLS_ECDHE_PSK_WITH_AES_256_GCM_SHA384,
- ?TLS_DHE_PSK_WITH_AES_256_GCM_SHA384,
?TLS_RSA_PSK_WITH_AES_256_GCM_SHA384,
+ ?TLS_RSA_PSK_WITH_AES_256_CBC_SHA384,
+ ?TLS_RSA_PSK_WITH_AES_128_GCM_SHA256,
+ ?TLS_RSA_PSK_WITH_AES_128_CBC_SHA256
+ ] ++ psk_suites(0);
+psk_suites(_) ->
+ [?TLS_RSA_PSK_WITH_AES_256_CBC_SHA,
+ ?TLS_RSA_PSK_WITH_AES_128_CBC_SHA,
+ ?TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA,
+ ?TLS_RSA_PSK_WITH_RC4_128_SHA].
+
+%%--------------------------------------------------------------------
+-spec psk_suites_anon(ssl_record:ssl_version() | integer()) -> [cipher_suite()].
+%%
+%% Description: Returns a list of the anonymous PSK cipher suites, only supported
+%% if explicitly set by user.
+%%--------------------------------------------------------------------
+psk_suites_anon({3, N}) ->
+ psk_suites_anon(N);
+psk_suites_anon(N)
+ when N >= 3 ->
+ [
+ ?TLS_DHE_PSK_WITH_AES_256_GCM_SHA384,
?TLS_PSK_WITH_AES_256_GCM_SHA384,
?TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384,
?TLS_DHE_PSK_WITH_AES_256_CBC_SHA384,
- ?TLS_RSA_PSK_WITH_AES_256_CBC_SHA384,
?TLS_PSK_WITH_AES_256_CBC_SHA384,
?TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256,
?TLS_DHE_PSK_WITH_AES_128_GCM_SHA256,
- ?TLS_RSA_PSK_WITH_AES_128_GCM_SHA256,
?TLS_PSK_WITH_AES_128_GCM_SHA256,
?TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256,
?TLS_DHE_PSK_WITH_AES_128_CBC_SHA256,
- ?TLS_RSA_PSK_WITH_AES_128_CBC_SHA256,
?TLS_PSK_WITH_AES_128_CBC_SHA256
- ] ++ psk_suites(0);
-psk_suites(_) ->
- [?TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA,
- ?TLS_DHE_PSK_WITH_AES_256_CBC_SHA,
- ?TLS_RSA_PSK_WITH_AES_256_CBC_SHA,
+ ] ++ psk_suites_anon(0);
+psk_suites_anon(_) ->
+ [?TLS_DHE_PSK_WITH_AES_256_CBC_SHA,
?TLS_PSK_WITH_AES_256_CBC_SHA,
?TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA,
?TLS_DHE_PSK_WITH_AES_128_CBC_SHA,
- ?TLS_RSA_PSK_WITH_AES_128_CBC_SHA,
?TLS_PSK_WITH_AES_128_CBC_SHA,
?TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA,
?TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA,
- ?TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA,
?TLS_PSK_WITH_3DES_EDE_CBC_SHA,
?TLS_ECDHE_PSK_WITH_RC4_128_SHA,
?TLS_DHE_PSK_WITH_RC4_128_SHA,
- ?TLS_RSA_PSK_WITH_RC4_128_SHA,
?TLS_PSK_WITH_RC4_128_SHA].
-
%%--------------------------------------------------------------------
-spec srp_suites() -> [cipher_suite()].
%%
@@ -432,15 +445,24 @@ psk_suites(_) ->
%% if explicitly set by user.
%%--------------------------------------------------------------------
srp_suites() ->
- [?TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA,
- ?TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA,
+ [?TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA,
?TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA,
- ?TLS_SRP_SHA_WITH_AES_128_CBC_SHA,
?TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA,
?TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA,
- ?TLS_SRP_SHA_WITH_AES_256_CBC_SHA,
?TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA,
?TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA].
+
+%%--------------------------------------------------------------------
+-spec srp_suites_anon() -> [cipher_suite()].
+%%
+%% Description: Returns a list of the SRP anonymous cipher suites, only supported
+%% if explicitly set by user.
+%%--------------------------------------------------------------------
+srp_suites_anon() ->
+ [?TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA,
+ ?TLS_SRP_SHA_WITH_AES_128_CBC_SHA,
+ ?TLS_SRP_SHA_WITH_AES_256_CBC_SHA].
+
%%--------------------------------------------------------------------
-spec rc4_suites(Version::ssl_record:ssl_version() | integer()) -> [cipher_suite()].
%%
@@ -750,32 +772,32 @@ suite_definition(?TLS_RSA_PSK_WITH_NULL_SHA) ->
suite_definition(?TLS_PSK_WITH_AES_128_GCM_SHA256) ->
#{key_exchange => psk,
cipher => aes_128_gcm,
- mac => null,
+ mac => aead,
prf => sha256};
suite_definition(?TLS_PSK_WITH_AES_256_GCM_SHA384) ->
#{key_exchange => psk,
cipher => aes_256_gcm,
- mac => null,
+ mac => aead,
prf => sha384};
suite_definition(?TLS_DHE_PSK_WITH_AES_128_GCM_SHA256) ->
#{key_exchange => dhe_psk,
cipher => aes_128_gcm,
- mac => null,
+ mac => aead,
prf => sha256};
suite_definition(?TLS_DHE_PSK_WITH_AES_256_GCM_SHA384) ->
#{key_exchange => dhe_psk,
cipher => aes_256_gcm,
- mac => null,
+ mac => aead,
prf => sha384};
suite_definition(?TLS_RSA_PSK_WITH_AES_128_GCM_SHA256) ->
#{key_exchange => rsa_psk,
cipher => aes_128_gcm,
- mac => null,
+ mac => aead,
prf => sha256};
suite_definition(?TLS_RSA_PSK_WITH_AES_256_GCM_SHA384) ->
#{key_exchange => rsa_psk,
cipher => aes_256_gcm,
- mac => null,
+ mac => aead,
prf => sha384};
suite_definition(?TLS_PSK_WITH_AES_128_CBC_SHA256) ->
#{key_exchange => psk,
@@ -1115,42 +1137,42 @@ suite_definition(?TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384) ->
suite_definition(?TLS_RSA_WITH_AES_128_GCM_SHA256) ->
#{key_exchange => rsa,
cipher => aes_128_gcm,
- mac => null,
+ mac => aead,
prf => sha256};
suite_definition(?TLS_RSA_WITH_AES_256_GCM_SHA384) ->
#{key_exchange => rsa,
cipher => aes_256_gcm,
- mac => null,
+ mac => aead,
prf => sha384};
suite_definition(?TLS_DHE_RSA_WITH_AES_128_GCM_SHA256) ->
#{key_exchange => dhe_rsa,
cipher => aes_128_gcm,
- mac => null,
+ mac => aead,
prf => sha256};
suite_definition(?TLS_DHE_RSA_WITH_AES_256_GCM_SHA384) ->
#{key_exchange => dhe_rsa,
cipher => aes_256_gcm,
- mac => null,
+ mac => aead,
prf => sha384};
suite_definition(?TLS_DH_RSA_WITH_AES_128_GCM_SHA256) ->
#{key_exchange => dh_rsa,
cipher => aes_128_gcm,
- mac => null,
+ mac => aead,
prf => sha256};
suite_definition(?TLS_DH_RSA_WITH_AES_256_GCM_SHA384) ->
#{key_exchange => dh_rsa,
cipher => aes_256_gcm,
- mac => null,
+ mac => aead,
prf => sha384};
suite_definition(?TLS_DHE_DSS_WITH_AES_128_GCM_SHA256) ->
#{key_exchange => dhe_dss,
cipher => aes_128_gcm,
- mac => null,
+ mac => aead,
prf => sha256};
suite_definition(?TLS_DHE_DSS_WITH_AES_256_GCM_SHA384) ->
#{key_exchange => dhe_dss,
cipher => aes_256_gcm,
- mac => null,
+ mac => aead,
prf => sha384};
suite_definition(?TLS_DH_DSS_WITH_AES_128_GCM_SHA256) ->
#{key_exchange => dh_dss,
@@ -1160,74 +1182,74 @@ suite_definition(?TLS_DH_DSS_WITH_AES_128_GCM_SHA256) ->
suite_definition(?TLS_DH_DSS_WITH_AES_256_GCM_SHA384) ->
#{key_exchange => dh_dss,
cipher => aes_256_gcm,
- mac => null,
+ mac => aead,
prf => sha384};
suite_definition(?TLS_DH_anon_WITH_AES_128_GCM_SHA256) ->
#{key_exchange => dh_anon,
cipher => aes_128_gcm,
- mac => null,
+ mac => aead,
prf => sha256};
suite_definition(?TLS_DH_anon_WITH_AES_256_GCM_SHA384) ->
#{key_exchange => dh_anon,
cipher => aes_256_gcm,
- mac => null,
+ mac => aead,
prf => sha384};
%% RFC 5289 ECC AES-GCM Cipher Suites
suite_definition(?TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256) ->
#{key_exchange => ecdhe_ecdsa,
cipher => aes_128_gcm,
- mac => null,
+ mac => aead,
prf => sha256};
suite_definition(?TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384) ->
#{key_exchange => ecdhe_ecdsa,
cipher => aes_256_gcm,
- mac => null,
+ mac => aead,
prf => sha384};
suite_definition(?TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256) ->
#{key_exchange => ecdh_ecdsa,
cipher => aes_128_gcm,
- mac => null,
+ mac => aead,
prf => sha256};
suite_definition(?TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384) ->
#{key_exchange => ecdh_ecdsa,
cipher => aes_256_gcm,
- mac => null,
+ mac => aead,
prf => sha384};
suite_definition(?TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) ->
#{key_exchange => ecdhe_rsa,
cipher => aes_128_gcm,
- mac => null,
+ mac => aead,
prf => sha256};
suite_definition(?TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) ->
#{key_exchange => ecdhe_rsa,
cipher => aes_256_gcm,
- mac => null,
+ mac => aead,
prf => sha384};
suite_definition(?TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256) ->
#{key_exchange => ecdh_rsa,
cipher => aes_128_gcm,
- mac => null,
+ mac => aead,
prf => sha256};
suite_definition(?TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384) ->
#{key_exchange => ecdh_rsa,
cipher => aes_256_gcm,
- mac => null,
+ mac => aead,
prf => sha384};
%% draft-agl-tls-chacha20poly1305-04 Chacha20/Poly1305 Suites
suite_definition(?TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256) ->
#{key_exchange => ecdhe_rsa,
cipher => chacha20_poly1305,
- mac => null,
+ mac => aead,
prf => sha256};
suite_definition(?TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256) ->
#{key_exchange => ecdhe_ecdsa,
cipher => chacha20_poly1305,
- mac => null,
+ mac => aead,
prf => sha256};
suite_definition(?TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256) ->
#{key_exchange => dhe_rsa,
cipher => chacha20_poly1305,
- mac => null,
+ mac => aead,
prf => sha256}.
%%--------------------------------------------------------------------
@@ -1428,32 +1450,32 @@ suite(#{key_exchange := rsa_psk,
%%% TLS 1.2 PSK Cipher Suites RFC 5487
suite(#{key_exchange := psk,
cipher := aes_128_gcm,
- mac := null,
+ mac := aead,
prf := sha256}) ->
?TLS_PSK_WITH_AES_128_GCM_SHA256;
suite(#{key_exchange := psk,
cipher := aes_256_gcm,
- mac := null,
+ mac := aead,
prf := sha384}) ->
?TLS_PSK_WITH_AES_256_GCM_SHA384;
suite(#{key_exchange := dhe_psk,
cipher := aes_128_gcm,
- mac := null,
+ mac := aead,
prf := sha256}) ->
?TLS_DHE_PSK_WITH_AES_128_GCM_SHA256;
suite(#{key_exchange := dhe_psk,
cipher := aes_256_gcm,
- mac := null,
+ mac := aead,
prf := sha384}) ->
?TLS_DHE_PSK_WITH_AES_256_GCM_SHA384;
suite(#{key_exchange := rsa_psk,
cipher := aes_128_gcm,
- mac := null,
+ mac := aead,
prf := sha256}) ->
?TLS_RSA_PSK_WITH_AES_128_GCM_SHA256;
suite(#{key_exchange := rsa_psk,
cipher := aes_256_gcm,
- mac := null,
+ mac := aead,
prf := sha384}) ->
?TLS_RSA_PSK_WITH_AES_256_GCM_SHA384;
suite(#{key_exchange := psk,
@@ -1739,119 +1761,119 @@ suite(#{key_exchange := ecdh_rsa,
%% RFC 5288 AES-GCM Cipher Suites
suite(#{key_exchange := rsa,
cipher := aes_128_gcm,
- mac := null,
+ mac := aead,
prf := sha256}) ->
?TLS_RSA_WITH_AES_128_GCM_SHA256;
suite(#{key_exchange := rsa,
cipher := aes_256_gcm,
- mac := null,
+ mac := aead,
prf := sha384}) ->
?TLS_RSA_WITH_AES_256_GCM_SHA384;
suite(#{key_exchange := dhe_rsa,
cipher := aes_128_gcm,
- mac := null,
+ mac := aead,
prf := sha256}) ->
?TLS_DHE_RSA_WITH_AES_128_GCM_SHA256;
suite(#{key_exchange := dhe_rsa,
cipher := aes_256_gcm,
- mac := null,
+ mac := aead,
prf := sha384}) ->
?TLS_DHE_RSA_WITH_AES_256_GCM_SHA384;
suite(#{key_exchange := dh_rsa,
cipher := aes_128_gcm,
- mac := null,
+ mac := aead,
prf := sha256}) ->
?TLS_DH_RSA_WITH_AES_128_GCM_SHA256;
suite(#{key_exchange := dh_rsa,
cipher := aes_256_gcm,
- mac := null,
+ mac := aead,
prf := sha384}) ->
?TLS_DH_RSA_WITH_AES_256_GCM_SHA384;
suite(#{key_exchange := dhe_dss,
cipher := aes_128_gcm,
- mac := null,
+ mac := aead,
prf := sha256}) ->
?TLS_DHE_DSS_WITH_AES_128_GCM_SHA256;
suite(#{key_exchange := dhe_dss,
cipher := aes_256_gcm,
- mac := null,
+ mac := aead,
prf := sha384}) ->
?TLS_DHE_DSS_WITH_AES_256_GCM_SHA384;
suite(#{key_exchange := dh_dss,
cipher := aes_128_gcm,
- mac := null,
+ mac := aead,
prf := sha256}) ->
?TLS_DH_DSS_WITH_AES_128_GCM_SHA256;
suite(#{key_exchange := dh_dss,
cipher := aes_256_gcm,
- mac := null,
+ mac := aead,
prf := sha384}) ->
?TLS_DH_DSS_WITH_AES_256_GCM_SHA384;
suite(#{key_exchange := dh_anon,
cipher := aes_128_gcm,
- mac := null,
+ mac := aead,
prf := sha256}) ->
?TLS_DH_anon_WITH_AES_128_GCM_SHA256;
suite(#{key_exchange := dh_anon,
cipher := aes_256_gcm,
- mac := null,
+ mac := aead,
prf := sha384}) ->
?TLS_DH_anon_WITH_AES_256_GCM_SHA384;
%% RFC 5289 ECC AES-GCM Cipher Suites
suite(#{key_exchange := ecdhe_ecdsa,
cipher := aes_128_gcm,
- mac := null,
+ mac := aead,
prf := sha256}) ->
?TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256;
suite(#{key_exchange := ecdhe_ecdsa,
cipher := aes_256_gcm,
- mac := null,
+ mac := aead,
prf := sha384}) ->
?TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384;
suite(#{key_exchange := ecdh_ecdsa,
cipher := aes_128_gcm,
- mac := null,
+ mac := aead,
prf := sha256}) ->
?TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256;
suite(#{key_exchange := ecdh_ecdsa,
cipher := aes_256_gcm,
- mac := null,
+ mac := aead,
prf := sha384}) ->
?TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384;
suite(#{key_exchange := ecdhe_rsa,
cipher := aes_128_gcm,
- mac := null,
+ mac := aead,
prf := sha256}) ->
?TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256;
suite(#{key_exchange := ecdhe_rsa,
cipher := aes_256_gcm,
- mac := null,
+ mac := aead,
prf := sha384}) ->
?TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384;
suite(#{key_exchange := ecdh_rsa,
cipher := aes_128_gcm,
- mac := null,
+ mac := aead,
prf := sha256}) ->
?TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256;
suite(#{key_exchange := ecdh_rsa,
cipher := aes_256_gcm,
- mac := null,
+ mac := aead,
prf := sha384}) ->
?TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384;
%% draft-agl-tls-chacha20poly1305-04 Chacha20/Poly1305 Suites
suite(#{key_exchange := ecdhe_rsa,
cipher := chacha20_poly1305,
- mac := null,
+ mac := aead,
prf := sha256}) ->
?TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256;
suite(#{key_exchange := ecdhe_ecdsa,
cipher := chacha20_poly1305,
- mac := null,
+ mac := aead,
prf := sha256}) ->
?TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256;
suite(#{key_exchange := dhe_rsa,
cipher := chacha20_poly1305,
- mac := null,
+ mac := aead,
prf := sha256}) ->
?TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256.
@@ -2011,9 +2033,9 @@ openssl_suite("ECDH-RSA-AES256-GCM-SHA384") ->
?TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384.
%%--------------------------------------------------------------------
--spec openssl_suite_name(cipher_suite()) -> openssl_cipher_suite().
+-spec openssl_suite_name(cipher_suite()) -> openssl_cipher_suite() | erl_cipher_suite().
%%
-%% Description: Return openssl cipher suite name.
+%% Description: Return openssl cipher suite name if possible
%%-------------------------------------------------------------------
openssl_suite_name(?TLS_DHE_RSA_WITH_AES_256_CBC_SHA) ->
"DHE-RSA-AES256-SHA";
@@ -2223,38 +2245,74 @@ filter(DerCert, Ciphers) ->
{_, ecdsa} ->
Ciphers1 -- rsa_signed_suites()
end.
-
%%--------------------------------------------------------------------
--spec filter_suites([cipher_suite()]) -> [cipher_suite()].
+-spec filter_suites([erl_cipher_suite()] | [cipher_suite()], map()) ->
+ [erl_cipher_suite()] | [cipher_suite()].
+%%
+%% Description: Filter suites using supplied filter funs
+%%-------------------------------------------------------------------
+filter_suites(Suites, Filters) ->
+ ApplyFilters = fun(Suite) ->
+ filter_suite(Suite, Filters)
+ end,
+ lists:filter(ApplyFilters, Suites).
+
+filter_suite(#{key_exchange := KeyExchange,
+ cipher := Cipher,
+ mac := Hash,
+ prf := Prf},
+ #{key_exchange_filters := KeyFilters,
+ cipher_filters := CipherFilters,
+ mac_filters := HashFilters,
+ prf_filters := PrfFilters}) ->
+ all_filters(KeyExchange, KeyFilters) andalso
+ all_filters(Cipher, CipherFilters) andalso
+ all_filters(Hash, HashFilters) andalso
+ all_filters(Prf, PrfFilters);
+filter_suite(Suite, Filters) ->
+ filter_suite(suite_definition(Suite), Filters).
+
+%%--------------------------------------------------------------------
+-spec filter_suites([erl_cipher_suite()] | [cipher_suite()]) ->
+ [erl_cipher_suite()] | [cipher_suite()].
%%
%% Description: Filter suites for algorithms supported by crypto.
%%-------------------------------------------------------------------
-filter_suites(Suites = [Value|_]) when is_map(Value) ->
- Algos = crypto:supports(),
- Hashs = proplists:get_value(hashs, Algos),
- lists:filter(fun(#{key_exchange := KeyExchange,
- cipher := Cipher,
- mac := Hash,
- prf := Prf}) ->
- is_acceptable_keyexchange(KeyExchange, proplists:get_value(public_keys, Algos)) andalso
- is_acceptable_cipher(Cipher, proplists:get_value(ciphers, Algos)) andalso
- is_acceptable_hash(Hash, Hashs) andalso
- is_acceptable_prf(Prf, Hashs)
- end, Suites);
-
filter_suites(Suites) ->
+ Filters = crypto_support_filters(),
+ filter_suites(Suites, Filters).
+
+all_filters(_, []) ->
+ true;
+all_filters(Value, [Filter| Rest]) ->
+ case Filter(Value) of
+ true ->
+ all_filters(Value, Rest);
+ false ->
+ false
+ end.
+crypto_support_filters() ->
Algos = crypto:supports(),
Hashs = proplists:get_value(hashs, Algos),
- lists:filter(fun(Suite) ->
- #{key_exchange := KeyExchange,
- cipher := Cipher,
- mac := Hash,
- prf := Prf} = suite_definition(Suite),
- is_acceptable_keyexchange(KeyExchange, proplists:get_value(public_keys, Algos)) andalso
- is_acceptable_cipher(Cipher, proplists:get_value(ciphers, Algos)) andalso
- is_acceptable_hash(Hash, Hashs) andalso
- is_acceptable_prf(Prf, Hashs)
- end, Suites).
+ #{key_exchange_filters =>
+ [fun(KeyExchange) ->
+ is_acceptable_keyexchange(KeyExchange,
+ proplists:get_value(public_keys, Algos))
+ end],
+ cipher_filters =>
+ [fun(Cipher) ->
+ is_acceptable_cipher(Cipher,
+ proplists:get_value(ciphers, Algos))
+ end],
+ mac_filters =>
+ [fun(Hash) ->
+ is_acceptable_hash(Hash, Hashs)
+ end],
+ prf_filters =>
+ [fun(Prf) ->
+ is_acceptable_prf(Prf,
+ proplists:get_value(hashs, Algos))
+ end]}.
is_acceptable_keyexchange(KeyExchange, _Algos) when KeyExchange == psk;
KeyExchange == null ->
diff --git a/lib/ssl/test/ssl_basic_SUITE.erl b/lib/ssl/test/ssl_basic_SUITE.erl
index f13bd53a7c..2781203557 100644
--- a/lib/ssl/test/ssl_basic_SUITE.erl
+++ b/lib/ssl/test/ssl_basic_SUITE.erl
@@ -163,7 +163,8 @@ api_tests() ->
server_name_indication_option,
accept_pool,
prf,
- socket_options
+ socket_options,
+ cipher_suites
].
api_tests_tls() ->
@@ -207,7 +208,7 @@ tls_cipher_tests() ->
rc4_ecdsa_cipher_suites].
cipher_tests() ->
- [cipher_suites,
+ [old_cipher_suites,
cipher_suites_mix,
ciphers_rsa_signed_certs,
ciphers_rsa_signed_certs_openssl_names,
@@ -704,8 +705,6 @@ secret_connection_info(Config) when is_list(Config) ->
ct:log("Testcase ~p, Client ~p Server ~p ~n",
[self(), Client, Server]),
-
- Version = ssl_test_lib:protocol_version(Config),
ssl_test_lib:check_result(Server, true, Client, true),
@@ -1130,11 +1129,16 @@ fallback(Config) when is_list(Config) ->
%%--------------------------------------------------------------------
cipher_format() ->
- [{doc, "Test that cipher conversion from tuples to binarys works"}].
+ [{doc, "Test that cipher conversion from maps | tuples | stings to binarys works"}].
cipher_format(Config) when is_list(Config) ->
- {ok, Socket} = ssl:listen(0, [{ciphers, ssl:cipher_suites()}]),
- ssl:close(Socket).
-
+ {ok, Socket0} = ssl:listen(0, [{ciphers, ssl:cipher_suites(default, 'tlsv1.2')}]),
+ ssl:close(Socket0),
+ %% Legacy
+ {ok, Socket1} = ssl:listen(0, [{ciphers, ssl:cipher_suites()}]),
+ ssl:close(Socket1),
+ {ok, Socket2} = ssl:listen(0, [{ciphers, ssl:cipher_suites(openssl)}]),
+ ssl:close(Socket2).
+
%%--------------------------------------------------------------------
peername() ->
@@ -1285,20 +1289,76 @@ sockname_result(S) ->
ssl:sockname(S).
%%--------------------------------------------------------------------
+
cipher_suites() ->
- [{doc,"Test API function cipher_suites/0"}].
+ [{doc,"Test API function cipher_suites/2, filter_cipher_suites/2"
+ " and prepend|append_cipher_suites/2"}].
cipher_suites(Config) when is_list(Config) ->
- MandatoryCipherSuiteTLS1_0TLS1_1 = {rsa,'3des_ede_cbc',sha},
- MandatoryCipherSuiteTLS1_0TLS1_2 = {rsa,'aes_128_cbc',sha} ,
- [_|_] = Suites = ssl:cipher_suites(),
- AllSuites = ssl:cipher_suites(all),
- %% The mandantory suites will no longer be supported by default
- %% due to security reasons
- true = lists:member(MandatoryCipherSuiteTLS1_0TLS1_1, AllSuites),
- true = lists:member(MandatoryCipherSuiteTLS1_0TLS1_2, AllSuites),
+ MandatoryCipherSuiteTLS1_0TLS1_1 = #{key_exchange => rsa,
+ cipher => '3des_ede_cbc',
+ mac => sha,
+ prf => default_prf},
+ MandatoryCipherSuiteTLS1_0TLS1_2 = #{key_exchange =>rsa,
+ cipher => 'aes_128_cbc',
+ mac => sha,
+ prf => default_prf},
+ Version = ssl_test_lib:protocol_version(Config),
+ All = [_|_] = ssl:cipher_suites(all, Version),
+ Default = [_|_] = ssl:cipher_suites(default, Version),
+ Anonymous = [_|_] = ssl:cipher_suites(anonymous, Version),
+ true = length(Default) < length(All),
+ Filters = [{key_exchange,
+ fun(dhe_rsa) ->
+ true;
+ (_) ->
+ false
+ end
+ },
+ {cipher,
+ fun(aes_256_cbc) ->
+ true;
+ (_) ->
+ false
+ end
+ },
+ {mac,
+ fun(sha) ->
+ true;
+ (_) ->
+ false
+ end
+ }
+ ],
+ Cipher = #{cipher => aes_256_cbc,
+ key_exchange => dhe_rsa,
+ mac => sha,
+ prf => default_prf},
+ [Cipher] = ssl:filter_cipher_suites(All, Filters),
+ [Cipher | Rest0] = ssl:prepend_cipher_suites([Cipher], Default),
+ [Cipher | Rest0] = ssl:prepend_cipher_suites(Filters, Default),
+ true = lists:member(Cipher, Default),
+ false = lists:member(Cipher, Rest0),
+ [Cipher | Rest1] = lists:reverse(ssl:append_cipher_suites([Cipher], Default)),
+ [Cipher | Rest1] = lists:reverse(ssl:append_cipher_suites(Filters, Default)),
+ true = lists:member(Cipher, Default),
+ false = lists:member(Cipher, Rest1),
+ [] = lists:dropwhile(fun(X) -> not lists:member(X, Default) end, Anonymous),
+ [] = lists:dropwhile(fun(X) -> not lists:member(X, All) end, Anonymous),
+ true = lists:member(MandatoryCipherSuiteTLS1_0TLS1_1, All),
+ true = lists:member(MandatoryCipherSuiteTLS1_0TLS1_2, All).
+
+%%--------------------------------------------------------------------
+
+old_cipher_suites() ->
+ [{doc,"Test API function cipher_suites/0"}].
+
+old_cipher_suites(Config) when is_list(Config) ->
+ MandatoryCipherSuite = {rsa, '3des_ede_cbc', sha},
+ [_|_] = Suites = ssl:cipher_suites(),
Suites = ssl:cipher_suites(erlang),
- [_|_] =ssl:cipher_suites(openssl).
+ [_|_] = ssl:cipher_suites(openssl),
+ true = lists:member(MandatoryCipherSuite, ssl:cipher_suites(all)).
%%--------------------------------------------------------------------
cipher_suites_mix() ->
@@ -3800,9 +3860,23 @@ rizzo() ->
vunrable to Rizzo/Dungon attack"}].
rizzo(Config) when is_list(Config) ->
- Ciphers = [X || X ={_,Y,_} <- ssl:cipher_suites(), Y =/= rc4_128],
Prop = proplists:get_value(tc_group_properties, Config),
Version = proplists:get_value(name, Prop),
+ NVersion = ssl_test_lib:protocol_version(Config, tuple),
+ Ciphers = ssl:filter_cipher_suites(ssl:cipher_suites(all, NVersion),
+ [{key_exchange,
+ fun(Alg) when Alg == ecdh_rsa; Alg == ecdhe_rsa->
+ true;
+ (_) ->
+ false
+ end},
+ {cipher,
+ fun(rc4_128) ->
+ false;
+ (_) ->
+ true
+ end}]),
+
run_send_recv_rizzo(Ciphers, Config, Version,
{?MODULE, send_recv_result_active_rizzo, []}).
%%--------------------------------------------------------------------
@@ -3814,8 +3888,13 @@ no_rizzo_rc4(Config) when is_list(Config) ->
Version = proplists:get_value(name, Prop),
NVersion = ssl_test_lib:protocol_version(Config, tuple),
%% Test uses RSA certs
- Ciphers = ssl_test_lib:rc4_suites(NVersion) -- [{ecdhe_ecdsa,rc4_128,sha},
- {ecdh_ecdsa,rc4_128,sha}],
+ Ciphers = ssl:filter_cipher_suites(ssl_test_lib:rc4_suites(NVersion),
+ [{key_exchange,
+ fun(Alg) when Alg == ecdh_rsa; Alg == ecdhe_rsa->
+ true;
+ (_) ->
+ false
+ end}]),
run_send_recv_rizzo(Ciphers, Config, Version,
{?MODULE, send_recv_result_active_no_rizzo, []}).
@@ -3826,10 +3905,21 @@ rizzo_one_n_minus_one(Config) when is_list(Config) ->
Prop = proplists:get_value(tc_group_properties, Config),
Version = proplists:get_value(name, Prop),
NVersion = ssl_test_lib:protocol_version(Config, tuple),
- AllSuites = ssl_test_lib:available_suites(NVersion),
- Ciphers = [X || X ={_,Y,_} <- AllSuites, Y =/= rc4_128],
+ Ciphers = ssl:filter_cipher_suites(ssl:cipher_suites(all, NVersion),
+ [{key_exchange,
+ fun(Alg) when Alg == ecdh_rsa; Alg == ecdhe_rsa->
+ true;
+ (_) ->
+ false
+ end},
+ {cipher,
+ fun(rc4_128) ->
+ false;
+ (_) ->
+ true
+ end}]),
run_send_recv_rizzo(Ciphers, Config, Version,
- {?MODULE, send_recv_result_active_rizzo, []}).
+ {?MODULE, send_recv_result_active_rizzo, []}).
rizzo_zero_n() ->
[{doc,"Test that the 0/n-split mitigation of Rizzo/Dungon attack can be explicitly selected"}].
@@ -3838,8 +3928,13 @@ rizzo_zero_n(Config) when is_list(Config) ->
Prop = proplists:get_value(tc_group_properties, Config),
Version = proplists:get_value(name, Prop),
NVersion = ssl_test_lib:protocol_version(Config, tuple),
- AllSuites = ssl_test_lib:available_suites(NVersion),
- Ciphers = [X || X ={_,Y,_} <- AllSuites, Y =/= rc4_128],
+ Ciphers = ssl:filter_cipher_suites(ssl:cipher_suites(default, NVersion),
+ [{cipher,
+ fun(rc4_128) ->
+ false;
+ (_) ->
+ true
+ end}]),
run_send_recv_rizzo(Ciphers, Config, Version,
{?MODULE, send_recv_result_active_no_rizzo, []}).
@@ -3847,9 +3942,16 @@ rizzo_disabled() ->
[{doc,"Test that the mitigation of Rizzo/Dungon attack can be explicitly disabled"}].
rizzo_disabled(Config) when is_list(Config) ->
- Ciphers = [X || X ={_,Y,_} <- ssl:cipher_suites(), Y =/= rc4_128],
Prop = proplists:get_value(tc_group_properties, Config),
Version = proplists:get_value(name, Prop),
+ NVersion = ssl_test_lib:protocol_version(Config, tuple),
+ Ciphers = ssl:filter_cipher_suites(ssl:cipher_suites(default, NVersion),
+ [{cipher,
+ fun(rc4_128) ->
+ false;
+ (_) ->
+ true
+ end}]),
run_send_recv_rizzo(Ciphers, Config, Version,
{?MODULE, send_recv_result_active_no_rizzo, []}).
@@ -4624,19 +4726,21 @@ rizzo_test(Cipher, Config, Version, Mfa) ->
[{Cipher, Error}]
end.
-client_server_opts({KeyAlgo,_,_}, Config)
+client_server_opts(#{key_exchange := KeyAlgo}, Config)
when KeyAlgo == rsa orelse
KeyAlgo == dhe_rsa orelse
- KeyAlgo == ecdhe_rsa ->
+ KeyAlgo == ecdhe_rsa orelse
+ KeyAlgo == rsa_psk orelse
+ KeyAlgo == srp_rsa ->
{ssl_test_lib:ssl_options(client_opts, Config),
ssl_test_lib:ssl_options(server_opts, Config)};
-client_server_opts({KeyAlgo,_,_}, Config) when KeyAlgo == dss orelse KeyAlgo == dhe_dss ->
+client_server_opts(#{key_exchange := KeyAlgo}, Config) when KeyAlgo == dss orelse KeyAlgo == dhe_dss ->
{ssl_test_lib:ssl_options(client_dsa_opts, Config),
ssl_test_lib:ssl_options(server_dsa_opts, Config)};
-client_server_opts({KeyAlgo,_,_}, Config) when KeyAlgo == ecdh_ecdsa orelse KeyAlgo == ecdhe_ecdsa ->
+client_server_opts(#{key_exchange := KeyAlgo}, Config) when KeyAlgo == ecdh_ecdsa orelse KeyAlgo == ecdhe_ecdsa ->
{ssl_test_lib:ssl_options(client_opts, Config),
ssl_test_lib:ssl_options(server_ecdsa_opts, Config)};
-client_server_opts({KeyAlgo,_,_}, Config) when KeyAlgo == ecdh_rsa ->
+client_server_opts(#{key_exchange := KeyAlgo}, Config) when KeyAlgo == ecdh_rsa ->
{ssl_test_lib:ssl_options(client_opts, Config),
ssl_test_lib:ssl_options(server_ecdh_rsa_opts, Config)}.
diff --git a/lib/ssl/test/ssl_test_lib.erl b/lib/ssl/test/ssl_test_lib.erl
index 7e983f5079..26ef311615 100644
--- a/lib/ssl/test/ssl_test_lib.erl
+++ b/lib/ssl/test/ssl_test_lib.erl
@@ -1024,59 +1024,50 @@ string_regex_filter(Str, Search) when is_list(Str) ->
string_regex_filter(_Str, _Search) ->
false.
-anonymous_suites({3,_ } = Version) ->
- [ssl_cipher:erl_suite_definition(S) || S <- ssl_cipher:filter_suites(ssl_cipher:anonymous_suites(Version))];
-anonymous_suites(DTLSVersion) ->
- Version = dtls_v1:corresponding_tls_version(DTLSVersion),
- [ssl_cipher:erl_suite_definition(S) || S <- ssl_cipher:filter_suites(ssl_cipher:anonymous_suites(Version)),
- not ssl_cipher:is_stream_ciphersuite(tuple_to_map(ssl_cipher:erl_suite_definition(S)))].
-
-psk_suites({3,_ } = Version) ->
- [ssl_cipher:erl_suite_definition(S) || S <- ssl_cipher:filter_suites(ssl_cipher:psk_suites(Version))];
-psk_suites(DTLSVersion) ->
- Version = dtls_v1:corresponding_tls_version(DTLSVersion),
- [ssl_cipher:erl_suite_definition(S) || S <- ssl_cipher:filter_suites(ssl_cipher:psk_suites(Version)),
- not ssl_cipher:is_stream_ciphersuite(tuple_to_map(ssl_cipher:erl_suite_definition(S)))].
-
-psk_anon_suites({3,_ } = Version) ->
- [Suite || Suite <- psk_suites(Version), is_psk_anon_suite(Suite)];
-psk_anon_suites(DTLSVersion) ->
- Version = dtls_v1:corresponding_tls_version(DTLSVersion),
- [Suite || Suite <- psk_suites(Version), is_psk_anon_suite(Suite),
- not ssl_cipher:is_stream_ciphersuite(tuple_to_map(Suite))].
+anonymous_suites(Version) ->
+ ssl:filter_cipher_suites([ssl_cipher:suite_definition(S) || S <- ssl_cipher:anonymous_suites(Version)],[]).
+psk_suites(Version) ->
+ ssl:filter_cipher_suites([ssl_cipher:suite_definition(S) || S <- ssl_cipher:psk_suites(Version)], []).
+
+psk_anon_suites(Version) ->
+ ssl:filter_cipher_suites([ssl_cipher:suite_definition(S) || S <- ssl_cipher:psk_suites_anon(Version)],
+ [{key_exchange,
+ fun(psk) ->
+ true;
+ (psk_dhe) ->
+ true;
+ (_) ->
+ false
+ end}]).
+
srp_suites() ->
- [ssl_cipher:erl_suite_definition(Suite) ||
- Suite <-
- ssl_cipher:filter_suites([tuple_to_map(S) ||
- S <- [{srp_anon,'3des_ede_cbc', sha},
- {srp_rsa, '3des_ede_cbc', sha},
- {srp_anon, aes_128_cbc, sha},
- {srp_rsa, aes_128_cbc, sha},
- {srp_anon, aes_256_cbc, sha},
- {srp_rsa, aes_256_cbc, sha}]])].
+ ssl:filter_cipher_suites([ssl_cipher:suite_definition(S) || S <- ssl_cipher:srp_suites()],
+ [{key_exchange,
+ fun(srp_rsa) ->
+ true;
+ (_) ->
+ false
+ end}]).
srp_anon_suites() ->
- [ssl_cipher:erl_suite_definition(Suite) ||
- Suite <-
- ssl_cipher:filter_suites([tuple_to_map(S) ||
- S <-[{srp_anon, '3des_ede_cbc', sha},
- {srp_anon, aes_128_cbc, sha},
- {srp_anon, aes_256_cbc, sha}]])].
+ ssl:filter_cipher_suites([ssl_cipher:suite_definition(S) || S <- ssl_cipher:srp_suites_anon()],
+ []).
srp_dss_suites() ->
- [ssl_cipher:erl_suite_definition(Suite) ||
- Suite <-
- ssl_cipher:filter_suites([tuple_to_map(S) ||
- S <- [{srp_dss, '3des_ede_cbc', sha},
- {srp_dss, aes_128_cbc, sha},
- {srp_dss, aes_256_cbc, sha}]])].
-
+ ssl:filter_cipher_suites([ssl_cipher:suite_definition(S) || S <- ssl_cipher:srp_suites()],
+ [{key_exchange,
+ fun(srp_dss) ->
+ true;
+ (_) ->
+ false
+ end}]).
chacha_suites(Version) ->
[ssl_cipher:erl_suite_definition(S) || S <- ssl_cipher:filter_suites(ssl_cipher:chacha_suites(Version))].
+
rc4_suites(Version) ->
- [ssl_cipher:erl_suite_definition(S) || S <- ssl_cipher:filter_suites(ssl_cipher:rc4_suites(Version))].
+ ssl:filter_cipher_suites([ssl_cipher:suite_definition(S) || S <-ssl_cipher:rc4_suites(Version)], []).
des_suites(Version) ->
- [ssl_cipher:erl_suite_definition(S) || S <- ssl_cipher:filter_suites(ssl_cipher:des_suites(Version))].
+ ssl:filter_cipher_suites([ssl_cipher:suite_definition(S) || S <-ssl_cipher:des_suites(Version)], []).
tuple_to_map({Kex, Cipher, Mac}) ->
#{key_exchange => Kex,
@@ -1417,7 +1408,9 @@ filter_suites(Ciphers0, AtomVersion) ->
Supported0 = ssl_cipher:suites(Version)
++ ssl_cipher:anonymous_suites(Version)
++ ssl_cipher:psk_suites(Version)
+ ++ ssl_cipher:psk_suites_anon(Version)
++ ssl_cipher:srp_suites()
+ ++ ssl_cipher:srp_suites_anon()
++ ssl_cipher:rc4_suites(Version),
Supported1 = ssl_cipher:filter_suites(Supported0),
Supported2 = [ssl_cipher:erl_suite_definition(S) || S <- Supported1],
diff --git a/lib/stdlib/doc/src/erl_tar.xml b/lib/stdlib/doc/src/erl_tar.xml
index 337028568a..caf8f4a96d 100644
--- a/lib/stdlib/doc/src/erl_tar.xml
+++ b/lib/stdlib/doc/src/erl_tar.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>2003</year><year>2017</year>
+ <year>2003</year><year>2018</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -136,6 +136,9 @@
<v>Filename = filename()|{NameInArchive,FilenameOrBin}</v>
<v>Options = [Option]</v>
<v>Option = dereference|verbose|{chunks,ChunkSize}</v>
+ <v>|{atime,non_neg_integer()}|{mtime,non_neg_integer()}</v>
+ <v>|{ctime,non_neg_integer()}|{uid,non_neg_integer()}</v>
+ <v>|{gid,non_neg_integer()}</v>
<v>ChunkSize = positive_integer()</v>
<v>RetValue = ok|{error,{Filename,Reason}}</v>
<v>Reason = term()</v>
@@ -167,6 +170,42 @@
<seealso marker="ssh:ssh_sftp#open_tar/3">
<c>ssh_sftp:open_tar/3</c></seealso>.</p>
</item>
+ <tag><c>{atime,non_neg_integer()}</c></tag>
+ <item>
+ <p>Sets the last time, as
+ <seealso marker="erts:time_correction#POSIX_Time">
+ POSIX time</seealso>, when the file was read. See also
+ <seealso marker="kernel:file#read_file_info/1">
+ <c>file:read_file_info/1</c></seealso>.</p>
+ </item>
+ <tag><c>{mtime,non_neg_integer()}</c></tag>
+ <item>
+ <p>Sets the last time, as
+ <seealso marker="erts:time_correction#POSIX_Time">
+ POSIX time</seealso>, when the file was written. See also
+ <seealso marker="kernel:file#read_file_info/1">
+ <c>file:read_file_info/1</c></seealso>.</p>
+ </item>
+ <tag><c>{ctime,non_neg_integer()}</c></tag>
+ <item>
+ <p>Sets the time, as
+ <seealso marker="erts:time_correction#POSIX_Time">
+ POSIX time</seealso>, when the file was created. See also
+ <seealso marker="kernel:file#read_file_info/1">
+ <c>file:read_file_info/1</c></seealso>.</p>
+ </item>
+ <tag><c>{uid,non_neg_integer()}</c></tag>
+ <item>
+ <p>Sets the file owner.
+ <seealso marker="kernel:file#read_file_info/1">
+ <c>file:read_file_info/1</c></seealso>.</p>
+ </item>
+ <tag><c>{gid,non_neg_integer()}</c></tag>
+ <item>
+ <p>Sets the group that the file owner belongs to.
+ <seealso marker="kernel:file#read_file_info/1">
+ <c>file:read_file_info/1</c></seealso>.</p>
+ </item>
</taglist>
</desc>
</func>
diff --git a/lib/stdlib/src/erl_tar.erl b/lib/stdlib/src/erl_tar.erl
index 5ee584d612..d8b8f466b1 100644
--- a/lib/stdlib/src/erl_tar.erl
+++ b/lib/stdlib/src/erl_tar.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2017. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2018. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -457,26 +457,61 @@ add(Reader, NameOrBin, NameInArchive, Options)
do_add(#reader{access=write}=Reader, Name, NameInArchive, Options)
when is_list(NameInArchive), is_list(Options) ->
- RF = fun(F) -> file:read_link_info(F, [{time, posix}]) end,
+ RF = apply_file_info_opts_fun(Options, read_link_info),
Opts = #add_opts{read_info=RF},
- add1(Reader, Name, NameInArchive, add_opts(Options, Opts));
+ add1(Reader, Name, NameInArchive, add_opts(Options, Options, Opts));
do_add(#reader{access=read},_,_,_) ->
{error, eacces};
do_add(Reader,_,_,_) ->
{error, {badarg, Reader}}.
-add_opts([dereference|T], Opts) ->
- RF = fun(F) -> file:read_file_info(F, [{time, posix}]) end,
- add_opts(T, Opts#add_opts{read_info=RF});
-add_opts([verbose|T], Opts) ->
- add_opts(T, Opts#add_opts{verbose=true});
-add_opts([{chunks,N}|T], Opts) ->
- add_opts(T, Opts#add_opts{chunk_size=N});
-add_opts([_|T], Opts) ->
- add_opts(T, Opts);
-add_opts([], Opts) ->
+add_opts([dereference|T], AllOptions, Opts) ->
+ RF = apply_file_info_opts_fun(AllOptions, read_file_info),
+ add_opts(T, AllOptions, Opts#add_opts{read_info=RF});
+add_opts([verbose|T], AllOptions, Opts) ->
+ add_opts(T, AllOptions, Opts#add_opts{verbose=true});
+add_opts([{chunks,N}|T], AllOptions, Opts) ->
+ add_opts(T, AllOptions, Opts#add_opts{chunk_size=N});
+add_opts([{atime,Value}|T], AllOptions, Opts) ->
+ add_opts(T, AllOptions, Opts#add_opts{atime=Value});
+add_opts([{mtime,Value}|T], AllOptions, Opts) ->
+ add_opts(T, AllOptions, Opts#add_opts{mtime=Value});
+add_opts([{ctime,Value}|T], AllOptions, Opts) ->
+ add_opts(T, AllOptions, Opts#add_opts{ctime=Value});
+add_opts([{uid,Value}|T], AllOptions, Opts) ->
+ add_opts(T, AllOptions, Opts#add_opts{uid=Value});
+add_opts([{gid,Value}|T], AllOptions, Opts) ->
+ add_opts(T, AllOptions, Opts#add_opts{gid=Value});
+add_opts([_|T], AllOptions, Opts) ->
+ add_opts(T, AllOptions, Opts);
+add_opts([], _AllOptions, Opts) ->
Opts.
+apply_file_info_opts(Opts, {ok, FileInfo}) ->
+ {ok, do_apply_file_info_opts(Opts, FileInfo)};
+apply_file_info_opts(_Opts, Other) ->
+ Other.
+
+do_apply_file_info_opts([{atime,Value}|T], FileInfo) ->
+ do_apply_file_info_opts(T, FileInfo#file_info{atime=Value});
+do_apply_file_info_opts([{mtime,Value}|T], FileInfo) ->
+ do_apply_file_info_opts(T, FileInfo#file_info{mtime=Value});
+do_apply_file_info_opts([{ctime,Value}|T], FileInfo) ->
+ do_apply_file_info_opts(T, FileInfo#file_info{ctime=Value});
+do_apply_file_info_opts([{uid,Value}|T], FileInfo) ->
+ do_apply_file_info_opts(T, FileInfo#file_info{uid=Value});
+do_apply_file_info_opts([{gid,Value}|T], FileInfo) ->
+ do_apply_file_info_opts(T, FileInfo#file_info{gid=Value});
+do_apply_file_info_opts([_|T], FileInfo) ->
+ do_apply_file_info_opts(T, FileInfo);
+do_apply_file_info_opts([], FileInfo) ->
+ FileInfo.
+
+apply_file_info_opts_fun(Options, InfoFunction) ->
+ fun(F) ->
+ apply_file_info_opts(Options, file:InfoFunction(F, [{time, posix}]))
+ end.
+
add1(#reader{}=Reader, Name, NameInArchive, #add_opts{read_info=ReadInfo}=Opts)
when is_list(Name) ->
Res = case ReadInfo(Name) of
@@ -515,9 +550,11 @@ add1(Reader, Bin, NameInArchive, Opts) when is_binary(Bin) ->
name = NameInArchive,
size = byte_size(Bin),
typeflag = ?TYPE_REGULAR,
- atime = Now,
- mtime = Now,
- ctime = Now,
+ atime = add_opts_time(Opts#add_opts.atime, Now),
+ mtime = add_opts_time(Opts#add_opts.mtime, Now),
+ ctime = add_opts_time(Opts#add_opts.ctime, Now),
+ uid = Opts#add_opts.uid,
+ gid = Opts#add_opts.gid,
mode = 8#100644},
{ok, Reader2} = add_header(Reader, Header, Opts),
Padding = skip_padding(byte_size(Bin)),
@@ -527,6 +564,9 @@ add1(Reader, Bin, NameInArchive, Opts) when is_binary(Bin) ->
{error, Reason} -> {error, {NameInArchive, Reason}}
end.
+add_opts_time(undefined, Now) -> Now;
+add_opts_time(Time, _Now) -> Time.
+
add_directory(Reader, DirName, NameInArchive, Info, Opts) ->
case file:list_dir(DirName) of
{ok, []} ->
@@ -1650,8 +1690,12 @@ write_file(Name, Bin) ->
case file:write_file(Name, Bin) of
ok -> ok;
{error,enoent} ->
- ok = make_dirs(Name, file),
- write_file(Name, Bin);
+ case make_dirs(Name, file) of
+ ok ->
+ write_file(Name, Bin);
+ {error,Reason} ->
+ throw({error, Reason})
+ end;
{error,Reason} ->
throw({error, Reason})
end.
diff --git a/lib/stdlib/src/erl_tar.hrl b/lib/stdlib/src/erl_tar.hrl
index cff0c2f500..5d6cecbb66 100644
--- a/lib/stdlib/src/erl_tar.hrl
+++ b/lib/stdlib/src/erl_tar.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2017. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2018. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -21,7 +21,12 @@
-record(add_opts, {
read_info, %% Fun to use for read file/link info.
chunk_size = 0, %% For file reading when sending to sftp. 0=do not chunk
- verbose = false}). %% Verbose on/off.
+ verbose = false, %% Verbose on/off.
+ atime = undefined,
+ mtime = undefined,
+ ctime = undefined,
+ uid = 0,
+ gid = 0}).
-type add_opts() :: #add_opts{}.
%% Options used when reading a tar archive.
@@ -36,7 +41,12 @@
-type add_opt() :: dereference |
verbose |
- {chunks, pos_integer()}.
+ {chunks, pos_integer()} |
+ {atime, non_neg_integer()} |
+ {mtime, non_neg_integer()} |
+ {ctime, non_neg_integer()} |
+ {uid, non_neg_integer()} |
+ {gid, non_neg_integer()}.
-type extract_opt() :: {cwd, string()} |
{files, [string()]} |
diff --git a/lib/stdlib/test/tar_SUITE.erl b/lib/stdlib/test/tar_SUITE.erl
index 4061008812..32a33283d1 100644
--- a/lib/stdlib/test/tar_SUITE.erl
+++ b/lib/stdlib/test/tar_SUITE.erl
@@ -28,7 +28,7 @@
extract_from_open_file/1, symlinks/1, open_add_close/1, cooked_compressed/1,
memory/1,unicode/1,read_other_implementations/1,
sparse/1, init/1, leading_slash/1, dotdot/1,
- roundtrip_metadata/1]).
+ roundtrip_metadata/1, apply_file_info_opts/1]).
-include_lib("common_test/include/ct.hrl").
-include_lib("kernel/include/file.hrl").
@@ -42,7 +42,8 @@ all() ->
extract_filtered,
symlinks, open_add_close, cooked_compressed, memory, unicode,
read_other_implementations,
- sparse,init,leading_slash,dotdot,roundtrip_metadata].
+ sparse,init,leading_slash,dotdot,roundtrip_metadata,
+ apply_file_info_opts].
groups() ->
[].
@@ -989,6 +990,31 @@ do_roundtrip_metadata(Dir, File) ->
ok
end.
+apply_file_info_opts(Config) when is_list(Config) ->
+ ok = file:set_cwd(proplists:get_value(priv_dir, Config)),
+
+ ok = file:make_dir("empty_directory"),
+ ok = file:write_file("file", "contents"),
+
+ Opts = [{atime, 0}, {mtime, 0}, {ctime, 0}, {uid, 0}, {gid, 0}],
+ TarFile = "reproducible.tar",
+ {ok, Tar} = erl_tar:open(TarFile, [write]),
+ ok = erl_tar:add(Tar, "file", Opts),
+ ok = erl_tar:add(Tar, "empty_directory", Opts),
+ ok = erl_tar:add(Tar, <<"contents">>, "memory_file", Opts),
+ erl_tar:close(Tar),
+
+ ok = file:make_dir("extracted"),
+ erl_tar:extract(TarFile, [{cwd, "extracted"}]),
+
+ {ok, #file_info{mtime=0}} =
+ file:read_file_info("extracted/empty_directory", [{time, posix}]),
+ {ok, #file_info{mtime=0}} =
+ file:read_file_info("extracted/file", [{time, posix}]),
+ {ok, #file_info{mtime=0}} =
+ file:read_file_info("extracted/memory_file", [{time, posix}]),
+
+ ok.
%% Delete the given list of files.
delete_files([]) -> ok;