aboutsummaryrefslogtreecommitdiffstats
path: root/lib/ssl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/ssl')
-rw-r--r--lib/ssl/doc/src/Makefile4
-rw-r--r--lib/ssl/doc/src/refman.xml19
-rw-r--r--lib/ssl/doc/src/ssl.xml83
-rw-r--r--lib/ssl/doc/src/ssl_app.xml4
-rw-r--r--lib/ssl/doc/src/ssl_crl_cache.xml66
-rw-r--r--lib/ssl/doc/src/ssl_crl_cache_api.xml99
-rw-r--r--lib/ssl/doc/src/ssl_session_cache_api.xml24
-rw-r--r--lib/ssl/src/Makefile9
-rw-r--r--lib/ssl/src/dtls_connection.erl5
-rw-r--r--lib/ssl/src/dtls_record.erl62
-rw-r--r--lib/ssl/src/ssl.app.src4
-rw-r--r--lib/ssl/src/ssl.appup.src8
-rw-r--r--lib/ssl/src/ssl.erl28
-rw-r--r--lib/ssl/src/ssl_certificate.erl56
-rw-r--r--lib/ssl/src/ssl_cipher.erl475
-rw-r--r--lib/ssl/src/ssl_cipher.hrl96
-rw-r--r--lib/ssl/src/ssl_config.erl37
-rw-r--r--lib/ssl/src/ssl_connection.erl11
-rw-r--r--lib/ssl/src/ssl_connection.hrl4
-rw-r--r--lib/ssl/src/ssl_crl.erl80
-rw-r--r--lib/ssl/src/ssl_crl_cache.erl179
-rw-r--r--lib/ssl/src/ssl_crl_cache_api.erl30
-rw-r--r--lib/ssl/src/ssl_handshake.erl182
-rw-r--r--lib/ssl/src/ssl_internal.hrl13
-rw-r--r--lib/ssl/src/ssl_manager.erl210
-rw-r--r--lib/ssl/src/ssl_pkix_db.erl76
-rw-r--r--lib/ssl/src/ssl_record.erl39
-rw-r--r--lib/ssl/src/ssl_record.hrl5
-rw-r--r--lib/ssl/src/ssl_session_cache.erl10
-rw-r--r--lib/ssl/src/ssl_v3.erl5
-rw-r--r--lib/ssl/src/tls_handshake.erl3
-rw-r--r--lib/ssl/src/tls_record.erl63
-rw-r--r--lib/ssl/src/tls_v1.erl35
-rw-r--r--lib/ssl/test/Makefile4
-rw-r--r--lib/ssl/test/erl_make_certs.erl10
-rw-r--r--lib/ssl/test/make_certs.erl89
-rw-r--r--lib/ssl/test/ssl.spec3
-rw-r--r--lib/ssl/test/ssl_basic_SUITE.erl76
-rw-r--r--lib/ssl/test/ssl_bench.spec1
-rw-r--r--lib/ssl/test/ssl_bench_SUITE.erl366
-rw-r--r--lib/ssl/test/ssl_cipher_SUITE.erl23
-rw-r--r--lib/ssl/test/ssl_crl_SUITE.erl598
-rw-r--r--lib/ssl/test/ssl_pem_cache_SUITE.erl6
-rw-r--r--lib/ssl/test/ssl_session_cache_SUITE.erl57
-rw-r--r--lib/ssl/test/ssl_test_lib.erl71
-rw-r--r--lib/ssl/test/ssl_upgrade_SUITE.erl164
-rw-r--r--lib/ssl/vsn.mk2
47 files changed, 2660 insertions, 834 deletions
diff --git a/lib/ssl/doc/src/Makefile b/lib/ssl/doc/src/Makefile
index fb12499ef7..cfbf98f6e3 100644
--- a/lib/ssl/doc/src/Makefile
+++ b/lib/ssl/doc/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1999-2012. All Rights Reserved.
+# Copyright Ericsson AB 1999-2015. 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
@@ -37,7 +37,7 @@ RELSYSDIR = $(RELEASE_PATH)/lib/$(APPLICATION)-$(VSN)
# Target Specs
# ----------------------------------------------------
XML_APPLICATION_FILES = refman.xml
-XML_REF3_FILES = ssl.xml ssl_session_cache_api.xml
+XML_REF3_FILES = ssl.xml ssl_crl_cache.xml ssl_crl_cache.xml ssl_session_cache_api.xml
XML_REF6_FILES = ssl_app.xml
XML_PART_FILES = release_notes.xml usersguide.xml
diff --git a/lib/ssl/doc/src/refman.xml b/lib/ssl/doc/src/refman.xml
index ae11198edb..d5f2219af9 100644
--- a/lib/ssl/doc/src/refman.xml
+++ b/lib/ssl/doc/src/refman.xml
@@ -4,7 +4,7 @@
<application xmlns:xi="http://www.w3.org/2001/XInclude">
<header>
<copyright>
- <year>1999</year><year>2013</year>
+ <year>1999</year><year>2015</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -28,23 +28,10 @@
<rev>B</rev>
<file>refman.sgml</file>
</header>
- <description>
- <p>The <em>SSL</em> application provides secure communication over
- sockets.
- </p>
- <p>This product includes software developed by the OpenSSL Project for
- use in the OpenSSL Toolkit (http://www.openssl.org/).
- </p>
- <p>This product includes cryptographic software written by Eric Young
- </p>
- <p>This product includes software written by Tim Hudson
- </p>
- <p>For full OpenSSL and SSLeay license texts, see <seealso marker="licenses#licenses">Licenses</seealso>.</p>
- </description>
<xi:include href="ssl_app.xml"/>
<xi:include href="ssl.xml"/>
+ <xi:include href="ssl_crl_cache.xml"/>
+ <xi:include href="ssl_crl_cache_api.xml"/>
<xi:include href="ssl_session_cache_api.xml"/>
</application>
diff --git a/lib/ssl/doc/src/ssl.xml b/lib/ssl/doc/src/ssl.xml
index 0c042f8571..47100c0d81 100644
--- a/lib/ssl/doc/src/ssl.xml
+++ b/lib/ssl/doc/src/ssl.xml
@@ -38,7 +38,9 @@
<item>ssl requires the crypto and public_key applications.</item>
<item>Supported SSL/TLS-versions are SSL-3.0, TLS-1.0,
TLS-1.1 and TLS-1.2.</item>
- <item>For security reasons sslv2 is not supported.</item>
+ <item>For security reasons SSL-2.0 is not supported.</item>
+ <item>For security reasons SSL-3.0 is no longer supported by default,
+ but may be configured.</item>
<item>Ephemeral Diffie-Hellman cipher suites are supported
but not Diffie Hellman Certificates cipher suites.</item>
<item>Elliptic Curve cipher suites are supported if crypto
@@ -49,9 +51,9 @@
<item>IDEA cipher suites are not supported as they have
become deprecated by the latest TLS spec so there is not any
real motivation to implement them.</item>
- <item>CRL and policy certificate extensions are not supported
- yet. However CRL verification is supported by public_key, only not integrated
- in ssl yet. </item>
+ <item>CRL validation is supported.</item>
+ <item>Policy certificate extensions are not supported
+ yet. </item>
<item>Support for 'Server Name Indication' extension client side
(RFC 6066 section 3).</item>
</list>
@@ -92,7 +94,7 @@
{log_alert, boolean()} | {server_name_indication, hostname() | disable}
</c></p>
- <p><c>transportoption() = {cb_info, {CallbackModule::atom(), DataTag::atom(), ClosedTag::atom(), ErrTag:atom()}}
+ <p><c>transportoption() = {cb_info, {CallbackModule :: atom(), DataTag :: atom(), ClosedTag :: atom(), ErrTag:atom()}}
- defaults to {gen_tcp, tcp, tcp_closed, tcp_error}. Can be used to customize
the transport layer. The callback module must implement a reliable transport
protocol and behave as gen_tcp and in addition have functions corresponding to
@@ -136,7 +138,7 @@
</c></p>
<p><c>cipher() = rc4_128 | des_cbc | '3des_ede_cbc'
- | aes_128_cbc | aes_256_cbc </c></p>
+ | aes_128_cbc | aes_256_cbc | aes_128_gcm | aes_256_gcm </c></p>
<p> <c>hash() = md5 | sha
</c></p>
@@ -299,10 +301,47 @@ fun(OtpCert :: #'OTPCertificate'{}, Event :: {bad_cert, Reason :: atom() | {revo
<item> Possible such reasons see <seealso
marker="public_key:public_key#pkix_path_validation-3"> public_key:pkix_path_validation/3 </seealso></item>
</taglist>
+ </item>
+
+ <tag>{crl_check, boolean() | peer | best_effort }</tag>
+ <item>
+ Perform CRL (Certificate Revocation List) verification
+ <seealso marker="public_key:public_key#pkix_crl_validate-3">
+ (public_key:pkix_crls_validate/3)</seealso> on all the certificates during the path validation
+ <seealso
+ marker="public_key:public_key#pkix_path_validation-3">(public_key:pkix_path_validation/3)
+ </seealso>
+ of the certificate chain. Defaults to false.
+
+ <p><c>peer</c> - check is only performed on
+ the peer certificate.</p>
+
+ <p><c>best_effort</c> - if certificate revocation status can not be determined
+ it will be accepted as valid.</p>
+ <p>The CA certificates specified for the connection will be used to
+ construct the certificate chain validating the CRLs.</p>
+
+ <p>The CRLs will be fetched from a local or external cache
+ <seealso marker="ssl:ssl_crl_cache_api">ssl_crl_cache_api(3)</seealso>.</p>
</item>
+ <tag>{crl_cache, {Module :: atom(), {DbHandle :: internal | term(), Args :: list()}}}</tag>
+ <item>
+ <p>Module defaults to ssl_crl_cache with <c> DbHandle </c> internal and an
+ empty argument list. The following arguments may be specified for the internal cache.</p>
+ <taglist>
+ <tag>{http, timeout()}</tag>
+ <item>
+ Enables fetching of CRLs specified as http URIs in<seealso
+ marker="public_key:cert_records"> X509 cerificate extensions.</seealso>
+ Requires the OTP inets application.
+ </item>
+ </taglist>
+ </item>
+
<tag>{partial_chain, fun(Chain::[DerCert]) -> {trusted_ca, DerCert} | unknown_ca </tag>
+
<item>
Claim an intermediat CA in the chain as trusted. TLS will then perform the public_key:pkix_path_validation/3
with the selected CA as trusted anchor and the rest of the chain.
@@ -311,7 +350,7 @@ fun(OtpCert :: #'OTPCertificate'{}, Event :: {bad_cert, Reason :: atom() | {revo
<tag>{versions, [protocol()]}</tag>
<item>TLS protocol versions that will be supported by started clients and servers.
This option overrides the application environment option <c>protocol_version</c>. If the
- environment option is not set it defaults to all versions supported by the SSL application. See also
+ environment option is not set it defaults to all versions, except SSL-3.0, supported by the SSL application. See also
<seealso marker="ssl:ssl_app">ssl(6)</seealso>
</item>
@@ -945,19 +984,37 @@ fun(srp, Username :: string(), UserState :: term()) ->
</func>
<func>
- <name>versions() ->
- [{SslAppVer, SupportedSslVer, AvailableSslVsn}]</name>
+ <name>versions() -> [versions_info()]</name>
<fsummary>Returns version information relevant for the
ssl application.</fsummary>
<type>
- <v>SslAppVer = string()</v>
- <v>SupportedSslVer = [protocol()]</v>
- <v>AvailableSslVsn = [protocol()]</v>
+ <v>versions_info() = {app_vsn, string()} | {supported | available, [protocol()] </v>
</type>
<desc>
<p>
Returns version information relevant for the
- ssl application.</p>
+ ssl application.
+ </p>
+ <taglist>
+ <tag>app_vsn</tag>
+ <item> The application version of the OTP ssl application.</item>
+
+ <tag>supported</tag>
+
+ <item>TLS/SSL versions supported by default.
+ Overridden by a versions option on
+ <seealso marker="#connect-2"> connect/[2,3,4]</seealso>, <seealso
+ marker="#listen-2"> listen/2</seealso> and <seealso
+ marker="#ssl_accept-2">ssl_accept/[1,2,3]</seealso>. For the
+ negotiated TLS/SSL version see <seealso
+ marker="#connection_info-1">ssl:connection_info/1
+ </seealso></item>
+
+ <tag>available</tag>
+ <item>All TLS/SSL versions that the Erlang ssl application
+ can support. Note that TLS 1.2 requires sufficient support
+ from the crypto application. </item>
+ </taglist>
</desc>
</func>
<func>
diff --git a/lib/ssl/doc/src/ssl_app.xml b/lib/ssl/doc/src/ssl_app.xml
index f1377cabda..e3a3fc27f2 100644
--- a/lib/ssl/doc/src/ssl_app.xml
+++ b/lib/ssl/doc/src/ssl_app.xml
@@ -75,10 +75,10 @@
</p>
</item>
- <tag><c><![CDATA[session_cb_init_args = list() <optional>]]></c></tag>
+ <tag><c><![CDATA[session_cb_init_args = proplist:proplist() <optional>]]></c></tag>
<item>
<p>
- List of arguments to the init function in session cache
+ List of additional user defined arguments to the init function in session cache
callback module, defaults to [].
</p>
</item>
diff --git a/lib/ssl/doc/src/ssl_crl_cache.xml b/lib/ssl/doc/src/ssl_crl_cache.xml
new file mode 100644
index 0000000000..b291c7b633
--- /dev/null
+++ b/lib/ssl/doc/src/ssl_crl_cache.xml
@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE erlref SYSTEM "erlref.dtd">
+
+<erlref>
+ <header>
+ <copyright>
+ <year>2015</year><year>2015</year>
+ <holder>Ericsson AB. All Rights Reserved.</holder>
+ </copyright>
+ <legalnotice>
+ 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.
+ </legalnotice>
+ <title>ssl_crl_cache</title>
+ <file>ssl_crl_cache.xml</file>
+ </header>
+
+ <module>ssl_crl_cache</module>
+ <modulesummary>CRL cache </modulesummary>
+ <description>
+ <p>
+ Implements an internal CRL (Certificate Revocation List) cache.
+ In addition to implementing the <seealso
+ marker="ssl_cache_crl_api"> ssl_cache_crl_api</seealso> behaviour
+ the following functions are available.
+ </p>
+ </description>
+
+ <funcs>
+ <func>
+ <name>insert(CRLSrc) -> ok | {error, Reason}</name>
+ <name>insert(URI, CRLSrc) -> ok | {error, Reason}</name>
+ <fsummary> </fsummary>
+ <type>
+ <v> CRLSrc = {file, string()} | {der, [ <seealso
+ marker="public_key:public_key"> der_encoded() </seealso> ]}</v>
+ <v> URI = http_uri:uri()</v>
+ <v> Reason = term()</v>
+ </type>
+ <desc>
+ Insert CRLs into the ssl applications local cache.
+ </desc>
+ </func>
+
+ <func>
+ <name>delete(Entries) -> ok | {error, Reason} </name>
+ <fsummary> </fsummary>
+ <type>
+ <v> Entries = http_uri:uri() | {file, string()} | {der, [<seealso
+ marker="public_key:public_key"> der_encoded() </seealso>]}</v>
+ <v> Reason = term()</v>
+ </type>
+ <desc>
+ Delete CRLs from the ssl applications local cache.
+ </desc>
+ </func>
+ </funcs>
+</erlref> \ No newline at end of file
diff --git a/lib/ssl/doc/src/ssl_crl_cache_api.xml b/lib/ssl/doc/src/ssl_crl_cache_api.xml
new file mode 100644
index 0000000000..3f518496be
--- /dev/null
+++ b/lib/ssl/doc/src/ssl_crl_cache_api.xml
@@ -0,0 +1,99 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE erlref SYSTEM "erlref.dtd">
+
+<erlref>
+ <header>
+ <copyright>
+ <year>2015</year><year>2015</year>
+ <holder>Ericsson AB. All Rights Reserved.</holder>
+ </copyright>
+ <legalnotice>
+ 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.
+ </legalnotice>
+ <title>ssl_crl_cache_api</title>
+ <file>ssl_crl_cache_api.xml</file>
+ </header>
+
+ <module>ssl_crl_cache_api</module>
+ <modulesummary>API for a SSL/TLS CRL (Certificate Revocation List) cache.</modulesummary>
+ <description>
+ <p>
+ When SSL/TLS performs certificate path validation according to
+ <url href="http://www.ietf.org/rfc/rfc5280.txt">RFC 5280 </url>
+ it should also perform CRL validation checks. To enable the CRL
+ checks the application needs access to CRLs. A database of CRLs
+ can be set up in many different ways. This module provides the
+ behavior of the API needed to integrate an arbitrary CRL cache
+ with the erlang ssl application. It is also used by the
+ application itself to provide a simple default implementation of
+ a CRL cache.
+ </p>
+ </description>
+
+ <section>
+ <title>Common Data Types</title>
+
+ <p>The following data types are used in the functions below:
+ </p>
+
+ <p><c>cache_ref() = opaque()</c></p>
+ <p> dist_point() = #'DistributionPoint'{} see <seealso
+ marker="public_key:cert_records"> X509 certificates records</seealso></p>
+ </section>
+
+ <funcs>
+ <func>
+ <name>lookup(DistributionPoint, DbHandle) -> not_available | CRLs </name>
+ <fsummary> </fsummary>
+ <type>
+ <v> DistributionPoint = dist_point() </v>
+ <v> DbHandle = cache_ref() </v>
+ <v> CRLs = [<seealso
+ marker="public_key:public_key">public_key:der_encoded()</seealso>] </v>
+ </type>
+ <desc> <p>Lookup the CRLs belonging to the distribution point <c> Distributionpoint </c> </p>.
+ This function may choose to only look in the cache or to follow distribution point
+ links depending on how the cache is administrated.
+ </desc>
+ </func>
+
+ <func>
+ <name>select(Issuer, DbHandle) -> CRLs </name>
+ <fsummary>Select the CRLs in the cache that are issued by <c>Issuer</c></fsummary>
+ <type>
+ <v> Issuer = <seealso
+ marker="public_key:public_key">public_key:issuer_name()</seealso></v>
+ <v> DbHandle = cache_ref() </v>
+ </type>
+ <desc>
+ <p>Select the CRLs in the cache that are issued by <c>Issuer</c> </p>
+ </desc>
+ </func>
+
+ <func>
+ <name>fresh_crl(DistributionPoint, CRL) -> FreshCRL</name>
+ <fsummary> <c>fun fresh_crl/2 </c> will be used as input option <c>update_crl</c> to
+ public_key:pkix_crls_validate/3 </fsummary>
+ <type>
+ <v> DistributionPoint = dist_point() </v>
+ <v> CRL = [<seealso
+ marker="public_key:public_key">public_key:der_encoded()</seealso>] </v>
+ <v> FreshCRL = [<seealso
+ marker="public_key:public_key">public_key:der_encoded()</seealso>] </v>
+ </type>
+ <desc>
+ <p> <c>fun fresh_crl/2 </c> will be used as input option <c>update_crl</c> to
+ <seealso marker="public_key#pkix_path_validation-3">public_key:pkix_crls_validate/3 </seealso> </p>
+ </desc>
+ </func>
+ </funcs>
+</erlref> \ No newline at end of file
diff --git a/lib/ssl/doc/src/ssl_session_cache_api.xml b/lib/ssl/doc/src/ssl_session_cache_api.xml
index 82de1784ca..9f87d31e90 100644
--- a/lib/ssl/doc/src/ssl_session_cache_api.xml
+++ b/lib/ssl/doc/src/ssl_session_cache_api.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1999</year><year>2013</year>
+ <year>1999</year><year>2015</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -79,17 +79,25 @@
</func>
<func>
- <name>init() -> opaque() </name>
+ <name>init(Args) -> opaque() </name>
<fsummary>Return cache reference</fsummary>
<type>
- <v></v>
+ <v>Args = proplists:proplist()</v>
+ <d>Will always include the property {role, client | server}. Currently this
+ is the only predefined property, there may also be user defined properties.
+ <seealso marker="ssl_app"> See also application environment variable
+ session_cb_init_args</seealso>
+ </d>
</type>
<desc>
<p>Performs possible initializations of the cache and returns
a reference to it that will be used as parameter to the other
- api functions. Will be called by the cache handling processes
- init function, hence putting the same requirements on it as
- a normal process init function.
+ API functions. Will be called by the cache handling processes
+ init function, hence putting the same requirements on it as a
+ normal process init function. Note that this function will be
+ called twice when starting the ssl application, once with the
+ role client and once with the role server, as the ssl application
+ must be prepared to take on both roles.
</p>
</desc>
</func>
@@ -111,14 +119,14 @@
<func>
<name>select_session(Cache, PartialKey) -> [session()]</name>
- <fsummary>>Selects sessions that could be reused.</fsummary>
+ <fsummary>Selects a sessions that could be reused.</fsummary>
<type>
<v> Cache = cache_ref()</v>
<v> PartialKey = partialkey()</v>
<v> Session = session()</v>
</type>
<desc>
- <p>Selects sessions that could be reused. Should be callable
+ <p>Selects a sessions that could be reused. Should be callable
from any process.
</p>
</desc>
diff --git a/lib/ssl/src/Makefile b/lib/ssl/src/Makefile
index 0c00a650b9..d71d3fc445 100644
--- a/lib/ssl/src/Makefile
+++ b/lib/ssl/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1999-2014. All Rights Reserved.
+# Copyright Ericsson AB 1999-2015. 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
@@ -38,7 +38,8 @@ RELSYSDIR = $(RELEASE_PATH)/lib/ssl-$(VSN)
# ----------------------------------------------------
BEHAVIOUR_MODULES= \
- ssl_session_cache_api
+ ssl_session_cache_api \
+ ssl_crl_cache_api
MODULES= \
ssl \
@@ -65,6 +66,8 @@ MODULES= \
ssl_manager \
ssl_session \
ssl_session_cache \
+ ssl_crl\
+ ssl_crl_cache \
ssl_socket \
ssl_listen_tracker_sup \
tls_record \
@@ -164,5 +167,5 @@ $(EBIN)/ssl_session_cache.$(EMULATOR): ssl_internal.hrl ssl_handshake.hrl
$(EBIN)/ssl_session_cache_api.$(EMULATOR): ssl_internal.hrl ssl_handshake.hrl
$(EBIN)/ssl_ssl3.$(EMULATOR): ssl_internal.hrl ssl_record.hrl ssl_cipher.hrl
$(EBIN)/ssl_tls1.$(EMULATOR): ssl_internal.hrl ssl_record.hrl ssl_cipher.hrl
-
+$(EBIN)/ssl_cache.$(EMULATOR): ssl_cache.erl ssl_internal.hrl ../../public_key/include/public_key.hrl
diff --git a/lib/ssl/src/dtls_connection.erl b/lib/ssl/src/dtls_connection.erl
index 508983ddac..f177a8610d 100644
--- a/lib/ssl/src/dtls_connection.erl
+++ b/lib/ssl/src/dtls_connection.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2013-2014. All Rights Reserved.
+%% Copyright Ericsson AB 2013-2015. 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
@@ -146,7 +146,7 @@ init([Role, Host, Port, Socket, {SSLOpts0, _} = Options, User, CbInfo]) ->
Handshake = ssl_handshake:init_handshake_history(),
TimeStamp = calendar:datetime_to_gregorian_seconds({date(), time()}),
try ssl_config:init(SSLOpts0, Role) of
- {ok, Ref, CertDbHandle, FileRefHandle, CacheHandle, OwnCert, Key, DHParams} ->
+ {ok, Ref, CertDbHandle, FileRefHandle, CacheHandle, CRLDbInfo, OwnCert, Key, DHParams} ->
Session = State0#state.session,
State = State0#state{
tls_handshake_history = Handshake,
@@ -155,6 +155,7 @@ init([Role, Host, Port, Socket, {SSLOpts0, _} = Options, User, CbInfo]) ->
file_ref_db = FileRefHandle,
cert_db_ref = Ref,
cert_db = CertDbHandle,
+ crl_db = CRLDbInfo,
session_cache = CacheHandle,
private_key = Key,
diffie_hellman_params = DHParams},
diff --git a/lib/ssl/src/dtls_record.erl b/lib/ssl/src/dtls_record.erl
index ae35dd7ea4..59b3ddec5c 100644
--- a/lib/ssl/src/dtls_record.erl
+++ b/lib/ssl/src/dtls_record.erl
@@ -120,6 +120,26 @@ get_dtls_records_aux(Data, Acc) ->
end.
encode_plain_text(Type, Version, Data,
+ #connection_states{current_write =
+ #connection_state{
+ epoch = Epoch,
+ sequence_number = Seq,
+ compression_state=CompS0,
+ security_parameters=
+ #security_parameters{
+ cipher_type = ?AEAD,
+ compression_algorithm=CompAlg}
+ }= WriteState0} = ConnectionStates) ->
+ {Comp, CompS1} = ssl_record:compress(CompAlg, Data, CompS0),
+ WriteState1 = WriteState0#connection_state{compression_state = CompS1},
+ AAD = calc_aad(Type, Version, Epoch, Seq),
+ {CipherFragment, WriteState} = ssl_record:cipher_aead(dtls_v1:corresponding_tls_version(Version),
+ Comp, WriteState1, AAD),
+ CipherText = encode_tls_cipher_text(Type, Version, Epoch, Seq, CipherFragment),
+ {CipherText, ConnectionStates#connection_states{current_write =
+ WriteState#connection_state{sequence_number = Seq +1}}};
+
+encode_plain_text(Type, Version, Data,
#connection_states{current_write=#connection_state{
epoch = Epoch,
sequence_number = Seq,
@@ -141,16 +161,44 @@ decode_cipher_text(#ssl_tls{type = Type, version = Version,
sequence_number = Seq,
fragment = CipherFragment} = CipherText,
#connection_states{current_read =
- #connection_state{compression_state = CompressionS0,
- security_parameters = SecParams} = ReadState0}
- = ConnnectionStates0) ->
- CompressAlg = SecParams#security_parameters.compression_algorithm,
+ #connection_state{
+ compression_state = CompressionS0,
+ security_parameters=
+ #security_parameters{
+ cipher_type = ?AEAD,
+ compression_algorithm=CompAlg}
+ } = ReadState0}= ConnnectionStates0) ->
+ AAD = calc_aad(Type, Version, Epoch, Seq),
+ case ssl_record:decipher_aead(dtls_v1:corresponding_tls_version(Version),
+ CipherFragment, ReadState0, AAD) of
+ {PlainFragment, ReadState1} ->
+ {Plain, CompressionS1} = ssl_record:uncompress(CompAlg,
+ PlainFragment, CompressionS0),
+ ConnnectionStates = ConnnectionStates0#connection_states{
+ current_read = ReadState1#connection_state{
+ compression_state = CompressionS1}},
+ {CipherText#ssl_tls{fragment = Plain}, ConnnectionStates};
+ #alert{} = Alert ->
+ Alert
+ end;
+
+decode_cipher_text(#ssl_tls{type = Type, version = Version,
+ epoch = Epoch,
+ sequence_number = Seq,
+ fragment = CipherFragment} = CipherText,
+ #connection_states{current_read =
+ #connection_state{
+ compression_state = CompressionS0,
+ security_parameters=
+ #security_parameters{
+ compression_algorithm=CompAlg}
+ } = ReadState0}= ConnnectionStates0) ->
{PlainFragment, Mac, ReadState1} = ssl_record:decipher(dtls_v1:corresponding_tls_version(Version),
CipherFragment, ReadState0, true),
MacHash = calc_mac_hash(ReadState1, Type, Version, Epoch, Seq, PlainFragment),
case ssl_record:is_correct_mac(Mac, MacHash) of
true ->
- {Plain, CompressionS1} = ssl_record:uncompress(CompressAlg,
+ {Plain, CompressionS1} = ssl_record:uncompress(CompAlg,
PlainFragment, CompressionS0),
ConnnectionStates = ConnnectionStates0#connection_states{
current_read = ReadState1#connection_state{
@@ -368,3 +416,7 @@ calc_mac_hash(#connection_state{mac_secret = MacSecret,
mac_hash(Version, MacAlg, MacSecret, SeqNo, Type, Length, Fragment) ->
dtls_v1:mac_hash(Version, MacAlg, MacSecret, SeqNo, Type,
Length, Fragment).
+
+calc_aad(Type, {MajVer, MinVer}, Epoch, SeqNo) ->
+ NewSeq = (Epoch bsl 48) + SeqNo,
+ <<NewSeq:64/integer, ?BYTE(Type), ?BYTE(MajVer), ?BYTE(MinVer)>>.
diff --git a/lib/ssl/src/ssl.app.src b/lib/ssl/src/ssl.app.src
index 36681e2897..955875fa95 100644
--- a/lib/ssl/src/ssl.app.src
+++ b/lib/ssl/src/ssl.app.src
@@ -39,6 +39,10 @@
ssl_manager,
ssl_pkix_db,
ssl_certificate,
+ %% CRL handling
+ ssl_crl,
+ ssl_crl_cache,
+ ssl_crl_cache_api,
%% App structure
ssl_app,
ssl_sup,
diff --git a/lib/ssl/src/ssl.appup.src b/lib/ssl/src/ssl.appup.src
index 7986722094..1476336039 100644
--- a/lib/ssl/src/ssl.appup.src
+++ b/lib/ssl/src/ssl.appup.src
@@ -1,14 +1,14 @@
%% -*- erlang -*-
{"%VSN%",
[
- {<<"5\\.3\\.[1-7]($|\\..*)">>, [{restart_application, ssl}]},
- {<<"5\\.[0-2]($|\\..*)">>, [{restart_application, ssl}]},
+ {<<"6\\..*">>, [{restart_application, ssl}]},
+ {<<"5\\..*">>, [{restart_application, ssl}]},
{<<"4\\..*">>, [{restart_application, ssl}]},
{<<"3\\..*">>, [{restart_application, ssl}]}
],
[
- {<<"5\\.3\\.[1-7]($|\\..*)">>, [{restart_application, ssl}]},
- {<<"5\\.[0-2]($|\\..*)">>, [{restart_application, ssl}]},
+ {<<"6\\..*">>, [{restart_application, ssl}]},
+ {<<"5\\..*">>, [{restart_application, ssl}]},
{<<"4\\..*">>, [{restart_application, ssl}]},
{<<"3\\..*">>, [{restart_application, ssl}]}
]
diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl
index 5f4ad7f013..623fa92121 100644
--- a/lib/ssl/src/ssl.erl
+++ b/lib/ssl/src/ssl.erl
@@ -353,12 +353,8 @@ cipher_suites(openssl) ->
|| S <- ssl_cipher:filter_suites(ssl_cipher:suites(Version))];
cipher_suites(all) ->
Version = tls_record:highest_protocol_version([]),
- Supported = ssl_cipher:all_suites(Version)
- ++ ssl_cipher:anonymous_suites()
- ++ ssl_cipher:psk_suites(Version)
- ++ ssl_cipher:srp_suites(),
- ssl_cipher:filter_suites([suite_definition(S) || S <- Supported]).
-
+ ssl_cipher:filter_suites([suite_definition(S)
+ || S <-ssl_cipher:all_suites(Version)]).
cipher_suites() ->
cipher_suites(erlang).
@@ -454,7 +450,7 @@ session_info(#sslsocket{pid = {Listen,_}}) when is_port(Listen) ->
versions() ->
Vsns = tls_record:supported_protocol_versions(),
SupportedVsns = [tls_record:protocol_version(Vsn) || Vsn <- Vsns],
- AvailableVsns = ?ALL_SUPPORTED_VERSIONS,
+ AvailableVsns = ?ALL_AVAILABLE_VERSIONS,
%% TODO Add DTLS versions when supported
[{ssl_app, ?VSN}, {supported, SupportedVsns}, {available, AvailableVsns}].
@@ -658,7 +654,9 @@ handle_options(Opts0) ->
honor_cipher_order = handle_option(honor_cipher_order, Opts, false),
protocol = proplists:get_value(protocol, Opts, tls),
padding_check = proplists:get_value(padding_check, Opts, true),
- fallback = proplists:get_value(fallback, Opts, false)
+ fallback = proplists:get_value(fallback, Opts, false),
+ crl_check = handle_option(crl_check, Opts, false),
+ crl_cache = handle_option(crl_cache, Opts, {ssl_crl_cache, {internal, []}})
},
CbInfo = proplists:get_value(cb_info, Opts, {gen_tcp, tcp, tcp_closed, tcp_error}),
@@ -671,7 +669,7 @@ handle_options(Opts0) ->
cb_info, renegotiate_at, secure_renegotiate, hibernate_after,
erl_dist, next_protocols_advertised,
client_preferred_next_protocols, log_alert,
- server_name_indication, honor_cipher_order, padding_check,
+ server_name_indication, honor_cipher_order, padding_check, crl_check, crl_cache,
fallback],
SockOpts = lists:foldl(fun(Key, PropList) ->
@@ -854,6 +852,12 @@ validate_option(padding_check, Value) when is_boolean(Value) ->
Value;
validate_option(fallback, Value) when is_boolean(Value) ->
Value;
+validate_option(crl_check, Value) when is_boolean(Value) ->
+ Value;
+validate_option(crl_check, Value) when (Value == best_effort) or (Value == peer) ->
+ Value;
+validate_option(crl_cache, {Cb, {_Handle, Options}} = Value) when is_atom(Cb) and is_list(Options) ->
+ Value;
validate_option(Opt, Value) ->
throw({error, {options, {Opt, Value}}}).
@@ -959,10 +963,7 @@ binary_cipher_suites(Version, [{_,_,_}| _] = Ciphers0) ->
binary_cipher_suites(Version, Ciphers);
binary_cipher_suites(Version, [Cipher0 | _] = Ciphers0) when is_binary(Cipher0) ->
- All = ssl_cipher:suites(Version)
- ++ ssl_cipher:anonymous_suites()
- ++ ssl_cipher:psk_suites(Version)
- ++ ssl_cipher:srp_suites(),
+ All = ssl_cipher:all_suites(Version),
case [Cipher || Cipher <- Ciphers0, lists:member(Cipher, All)] of
[] ->
%% Defaults to all supported suites that does
@@ -1189,3 +1190,4 @@ handle_verify_options(Opts, CaCerts) ->
Value ->
throw({error, {options, {verify, Value}}})
end.
+
diff --git a/lib/ssl/src/ssl_certificate.erl b/lib/ssl/src/ssl_certificate.erl
index 30d224fee2..34e4a8b447 100644
--- a/lib/ssl/src/ssl_certificate.erl
+++ b/lib/ssl/src/ssl_certificate.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2014 All Rights Reserved.
+%% Copyright Ericsson AB 2007-2015 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
@@ -33,7 +33,8 @@
-export([trusted_cert_and_path/4,
certificate_chain/3,
file_to_certificats/2,
- validate_extension/3,
+ file_to_crls/2,
+ validate/3,
is_valid_extkey_usage/2,
is_valid_key_usage/2,
select_extension/2,
@@ -83,16 +84,19 @@ trusted_cert_and_path(CertChain, CertDbHandle, CertDbRef, PartialChainHandler) -
end.
%%--------------------------------------------------------------------
--spec certificate_chain(undefined | binary(), db_handle(), certdb_ref()) ->
- {error, no_cert} | {ok, [der_cert()]}.
+-spec certificate_chain(undefined | binary() | #'OTPCertificate'{} , db_handle(), certdb_ref()) ->
+ {error, no_cert} | {ok, #'OTPCertificate'{} | undefined, [der_cert()]}.
%%
%% Description: Return the certificate chain to send to peer.
%%--------------------------------------------------------------------
certificate_chain(undefined, _, _) ->
{error, no_cert};
-certificate_chain(OwnCert, CertDbHandle, CertsDbRef) ->
+certificate_chain(OwnCert, CertDbHandle, CertsDbRef) when is_binary(OwnCert) ->
ErlCert = public_key:pkix_decode_cert(OwnCert, otp),
- certificate_chain(ErlCert, OwnCert, CertDbHandle, CertsDbRef, [OwnCert]).
+ certificate_chain(ErlCert, OwnCert, CertDbHandle, CertsDbRef, [OwnCert]);
+certificate_chain(OwnCert, CertDbHandle, CertsDbRef) ->
+ DerCert = public_key:pkix_encode('OTPCertificate', OwnCert, otp),
+ certificate_chain(OwnCert, DerCert, CertDbHandle, CertsDbRef, [DerCert]).
%%--------------------------------------------------------------------
-spec file_to_certificats(binary(), term()) -> [der_cert()].
%%
@@ -101,29 +105,39 @@ certificate_chain(OwnCert, CertDbHandle, CertsDbRef) ->
file_to_certificats(File, DbHandle) ->
{ok, List} = ssl_manager:cache_pem_file(File, DbHandle),
[Bin || {'Certificate', Bin, not_encrypted} <- List].
+
%%--------------------------------------------------------------------
--spec validate_extension(term(), {extension, #'Extension'{}} | {bad_cert, atom()} | valid,
- term()) -> {valid, term()} |
- {fail, tuple()} |
- {unknown, term()}.
+-spec file_to_crls(binary(), term()) -> [der_cert()].
+%%
+%% Description: Return list of DER encoded certificates.
+%%--------------------------------------------------------------------
+file_to_crls(File, DbHandle) ->
+ {ok, List} = ssl_manager:cache_pem_file(File, DbHandle),
+ [Bin || {'CertificateList', Bin, not_encrypted} <- List].
+
+%%--------------------------------------------------------------------
+-spec validate(term(), {extension, #'Extension'{}} | {bad_cert, atom()} | valid,
+ term()) -> {valid, term()} |
+ {fail, tuple()} |
+ {unknown, term()}.
%%
%% Description: Validates ssl/tls specific extensions
%%--------------------------------------------------------------------
-validate_extension(_,{extension, #'Extension'{extnID = ?'id-ce-extKeyUsage',
- extnValue = KeyUse}}, Role) ->
+validate(_,{extension, #'Extension'{extnID = ?'id-ce-extKeyUsage',
+ extnValue = KeyUse}}, {Role, _,_, _, _}) ->
case is_valid_extkey_usage(KeyUse, Role) of
true ->
{valid, Role};
false ->
{fail, {bad_cert, invalid_ext_key_usage}}
end;
-validate_extension(_, {bad_cert, _} = Reason, _) ->
- {fail, Reason};
-validate_extension(_, {extension, _}, Role) ->
+validate(_, {extension, _}, Role) ->
{unknown, Role};
-validate_extension(_, valid, Role) ->
+validate(_, {bad_cert, _} = Reason, _) ->
+ {fail, Reason};
+validate(_, valid, Role) ->
{valid, Role};
-validate_extension(_, valid_peer, Role) ->
+validate(_, valid_peer, Role) ->
{valid, Role}.
%%--------------------------------------------------------------------
@@ -194,14 +208,14 @@ certificate_chain(OtpCert, _Cert, CertDbHandle, CertsDbRef, Chain) ->
%% certificate. The verification of the
%% cert chain will fail if guess is
%% incorrect.
- {ok, lists:reverse(Chain)}
+ {ok, undefined, lists:reverse(Chain)}
end;
{{ok, {SerialNr, Issuer}}, SelfSigned} ->
certificate_chain(CertDbHandle, CertsDbRef, Chain, SerialNr, Issuer, SelfSigned)
end.
-certificate_chain(_,_, Chain, _SerialNr, _Issuer, true) ->
- {ok, lists:reverse(Chain)};
+certificate_chain(_, _, [RootCert | _] = Chain, _, _, true) ->
+ {ok, RootCert, lists:reverse(Chain)};
certificate_chain(CertDbHandle, CertsDbRef, Chain, SerialNr, Issuer, _SelfSigned) ->
case ssl_manager:lookup_trusted_cert(CertDbHandle, CertsDbRef,
@@ -214,7 +228,7 @@ certificate_chain(CertDbHandle, CertsDbRef, Chain, SerialNr, Issuer, _SelfSigned
%% The trusted cert may be obmitted from the chain as the
%% counter part needs to have it anyway to be able to
%% verify it.
- {ok, lists:reverse(Chain)}
+ {ok, undefined, lists:reverse(Chain)}
end.
find_issuer(OtpCert, CertDbHandle) ->
diff --git a/lib/ssl/src/ssl_cipher.erl b/lib/ssl/src/ssl_cipher.erl
index bec0055353..8584e56d6c 100644
--- a/lib/ssl/src/ssl_cipher.erl
+++ b/lib/ssl/src/ssl_cipher.erl
@@ -33,9 +33,10 @@
-include_lib("public_key/include/public_key.hrl").
-export([security_parameters/2, security_parameters/3, suite_definition/1,
- decipher/6, cipher/5, suite/1, suites/1, all_suites/1,
- ec_keyed_suites/0, anonymous_suites/0, psk_suites/1, srp_suites/0,
- openssl_suite/1, openssl_suite_name/1, filter/2, filter_suites/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, anonymous_suites/1, psk_suites/1, srp_suites/0,
+ rc4_suites/1, openssl_suite/1, openssl_suite_name/1, filter/2, filter_suites/1,
hash_algorithm/1, sign_algorithm/1, is_acceptable_hash/2, is_fallback/1]).
-export_type([cipher_suite/0,
@@ -43,7 +44,7 @@
key_algo/0]).
-type cipher() :: null |rc4_128 | idea_cbc | des40_cbc | des_cbc | '3des_ede_cbc'
- | aes_128_cbc | aes_256_cbc.
+ | aes_128_cbc | aes_256_cbc | aes_128_gcm | aes_256_gcm | chacha20_poly1305.
-type hash() :: null | sha | md5 | sha224 | sha256 | sha384 | sha512.
-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_algo(), cipher(), hash()}.
@@ -87,20 +88,32 @@ security_parameters(Version, CipherSuite, SecParams) ->
hash_size = hash_size(Hash)}.
%%--------------------------------------------------------------------
+-spec cipher_init(cipher_enum(), binary(), binary()) -> #cipher_state{}.
+%%
+%% Description: Initializes the #cipher_state according to BCA
+%%-------------------------------------------------------------------
+cipher_init(?RC4, IV, Key) ->
+ State = crypto:stream_init(rc4, Key),
+ #cipher_state{iv = IV, key = Key, state = State};
+cipher_init(?AES_GCM, IV, Key) ->
+ <<Nonce:64>> = ssl:random_bytes(8),
+ #cipher_state{iv = IV, key = Key, nonce = Nonce};
+cipher_init(_BCA, IV, Key) ->
+ #cipher_state{iv = IV, key = Key}.
+
+%%--------------------------------------------------------------------
-spec cipher(cipher_enum(), #cipher_state{}, binary(), iodata(), ssl_record:ssl_version()) ->
{binary(), #cipher_state{}}.
%%
%% Description: Encrypts the data and the MAC using chipher described
%% by cipher_enum() and updating the cipher state
+%% Used for "MAC then Cipher" suites where first an HMAC of the
+%% data is calculated and the data plus the HMAC is ecncrypted.
%%-------------------------------------------------------------------
cipher(?NULL, CipherState, <<>>, Fragment, _Version) ->
GenStreamCipherList = [Fragment, <<>>],
{GenStreamCipherList, CipherState};
-cipher(?RC4, CipherState, Mac, Fragment, _Version) ->
- State0 = case CipherState#cipher_state.state of
- undefined -> crypto:stream_init(rc4, CipherState#cipher_state.key);
- S -> S
- end,
+cipher(?RC4, CipherState = #cipher_state{state = State0}, Mac, Fragment, _Version) ->
GenStreamCipherList = [Fragment, Mac],
{State1, T} = crypto:stream_encrypt(State0, GenStreamCipherList),
{T, CipherState#cipher_state{state = State1}};
@@ -112,13 +125,40 @@ cipher(?'3DES', CipherState, Mac, Fragment, Version) ->
block_cipher(fun(<<K1:8/binary, K2:8/binary, K3:8/binary>>, IV, T) ->
crypto:block_encrypt(des3_cbc, [K1, K2, K3], IV, T)
end, block_size(des_cbc), CipherState, Mac, Fragment, Version);
-cipher(?AES, CipherState, Mac, Fragment, Version) ->
+cipher(?AES_CBC, CipherState, Mac, Fragment, Version) ->
block_cipher(fun(Key, IV, T) when byte_size(Key) =:= 16 ->
crypto:block_encrypt(aes_cbc128, Key, IV, T);
(Key, IV, T) when byte_size(Key) =:= 32 ->
crypto:block_encrypt(aes_cbc256, Key, IV, T)
end, block_size(aes_128_cbc), CipherState, Mac, Fragment, Version).
+%%--------------------------------------------------------------------
+-spec cipher_aead(cipher_enum(), #cipher_state{}, integer(), binary(), iodata(), ssl_record:ssl_version()) ->
+ {binary(), #cipher_state{}}.
+%%
+%% Description: Encrypts the data and protects associated data (AAD) using chipher
+%% described by cipher_enum() and updating the cipher state
+%% Use for suites that use authenticated encryption with associated data (AEAD)
+%%-------------------------------------------------------------------
+cipher_aead(?AES_GCM, CipherState, SeqNo, AAD, Fragment, Version) ->
+ aead_cipher(aes_gcm, CipherState, SeqNo, AAD, Fragment, Version);
+cipher_aead(?CHACHA20_POLY1305, CipherState, SeqNo, AAD, Fragment, Version) ->
+ aead_cipher(chacha20_poly1305, CipherState, SeqNo, AAD, Fragment, Version).
+
+aead_cipher(chacha20_poly1305, #cipher_state{key=Key} = CipherState, SeqNo, AAD0, Fragment, _Version) ->
+ CipherLen = erlang:iolist_size(Fragment),
+ AAD = <<AAD0/binary, ?UINT16(CipherLen)>>,
+ Nonce = <<SeqNo:64/integer>>,
+ {Content, CipherTag} = crypto:block_encrypt(chacha20_poly1305, Key, Nonce, {AAD, Fragment}),
+ {<<Content/binary, CipherTag/binary>>, CipherState};
+aead_cipher(Type, #cipher_state{key=Key, iv = IV0, nonce = Nonce} = CipherState, _SeqNo, AAD0, Fragment, _Version) ->
+ CipherLen = erlang:iolist_size(Fragment),
+ AAD = <<AAD0/binary, ?UINT16(CipherLen)>>,
+ <<Salt:4/bytes, _/binary>> = IV0,
+ IV = <<Salt/binary, Nonce:64/integer>>,
+ {Content, CipherTag} = crypto:block_encrypt(Type, Key, IV, {AAD, Fragment}),
+ {<<Nonce:64/integer, Content/binary, CipherTag/binary>>, CipherState#cipher_state{nonce = Nonce + 1}}.
+
build_cipher_block(BlockSz, Mac, Fragment) ->
TotSz = byte_size(Mac) + erlang:iolist_size(Fragment) + 1,
{PaddingLength, Padding} = get_padding(TotSz, BlockSz),
@@ -148,14 +188,12 @@ block_cipher(Fun, BlockSz, #cipher_state{key=Key, iv=IV} = CS0,
%%
%% Description: Decrypts the data and the MAC using cipher described
%% by cipher_enum() and updating the cipher state.
+%% Used for "MAC then Cipher" suites where first the data is decrypted
+%% and the an HMAC of the decrypted data is checked
%%-------------------------------------------------------------------
decipher(?NULL, _HashSz, CipherState, Fragment, _, _) ->
{Fragment, <<>>, CipherState};
-decipher(?RC4, HashSz, CipherState, Fragment, _, _) ->
- State0 = case CipherState#cipher_state.state of
- undefined -> crypto:stream_init(rc4, CipherState#cipher_state.key);
- S -> S
- end,
+decipher(?RC4, HashSz, CipherState = #cipher_state{state = State0}, Fragment, _, _) ->
try crypto:stream_decrypt(State0, Fragment) of
{State, Text} ->
GSC = generic_stream_cipher_from_bin(Text, HashSz),
@@ -179,13 +217,26 @@ decipher(?'3DES', HashSz, CipherState, Fragment, Version, PaddingCheck) ->
block_decipher(fun(<<K1:8/binary, K2:8/binary, K3:8/binary>>, IV, T) ->
crypto:block_decrypt(des3_cbc, [K1, K2, K3], IV, T)
end, CipherState, HashSz, Fragment, Version, PaddingCheck);
-decipher(?AES, HashSz, CipherState, Fragment, Version, PaddingCheck) ->
+decipher(?AES_CBC, HashSz, CipherState, Fragment, Version, PaddingCheck) ->
block_decipher(fun(Key, IV, T) when byte_size(Key) =:= 16 ->
crypto:block_decrypt(aes_cbc128, Key, IV, T);
(Key, IV, T) when byte_size(Key) =:= 32 ->
crypto:block_decrypt(aes_cbc256, Key, IV, T)
end, CipherState, HashSz, Fragment, Version, PaddingCheck).
+%%--------------------------------------------------------------------
+-spec decipher_aead(cipher_enum(), #cipher_state{}, integer(), binary(), binary(), ssl_record:ssl_version()) ->
+ {binary(), binary(), #cipher_state{}} | #alert{}.
+%%
+%% Description: Decrypts the data and checks the associated data (AAD) MAC using
+%% cipher described by cipher_enum() and updating the cipher state.
+%% Use for suites that use authenticated encryption with associated data (AEAD)
+%%-------------------------------------------------------------------
+decipher_aead(?AES_GCM, CipherState, SeqNo, AAD, Fragment, Version) ->
+ aead_decipher(aes_gcm, CipherState, SeqNo, AAD, Fragment, Version);
+decipher_aead(?CHACHA20_POLY1305, CipherState, SeqNo, AAD, Fragment, Version) ->
+ aead_decipher(chacha20_poly1305, CipherState, SeqNo, AAD, Fragment, Version).
+
block_decipher(Fun, #cipher_state{key=Key, iv=IV} = CipherState0,
HashSz, Fragment, Version, PaddingCheck) ->
try
@@ -215,6 +266,35 @@ block_decipher(Fun, #cipher_state{key=Key, iv=IV} = CipherState0,
%% bad_record_mac alert to hide the specific type of the error."
?ALERT_REC(?FATAL, ?BAD_RECORD_MAC)
end.
+
+aead_ciphertext_to_state(chacha20_poly1305, SeqNo, _IV, AAD0, Fragment, _Version) ->
+ CipherLen = size(Fragment) - 16,
+ <<CipherText:CipherLen/bytes, CipherTag:16/bytes>> = Fragment,
+ AAD = <<AAD0/binary, ?UINT16(CipherLen)>>,
+ Nonce = <<SeqNo:64/integer>>,
+ {Nonce, AAD, CipherText, CipherTag};
+aead_ciphertext_to_state(_, _SeqNo, <<Salt:4/bytes, _/binary>>, AAD0, Fragment, _Version) ->
+ CipherLen = size(Fragment) - 24,
+ <<ExplicitNonce:8/bytes, CipherText:CipherLen/bytes, CipherTag:16/bytes>> = Fragment,
+ AAD = <<AAD0/binary, ?UINT16(CipherLen)>>,
+ Nonce = <<Salt/binary, ExplicitNonce/binary>>,
+ {Nonce, AAD, CipherText, CipherTag}.
+
+aead_decipher(Type, #cipher_state{key = Key, iv = IV} = CipherState,
+ SeqNo, AAD0, Fragment, Version) ->
+ try
+ {Nonce, AAD, CipherText, CipherTag} = aead_ciphertext_to_state(Type, SeqNo, IV, AAD0, Fragment, Version),
+ case crypto:block_decrypt(Type, Key, Nonce, {AAD, CipherText, CipherTag}) of
+ Content when is_binary(Content) ->
+ {Content, CipherState};
+ _ ->
+ ?ALERT_REC(?FATAL, ?BAD_RECORD_MAC)
+ end
+ catch
+ _:_ ->
+ ?ALERT_REC(?FATAL, ?BAD_RECORD_MAC)
+ end.
+
%%--------------------------------------------------------------------
-spec suites(ssl_record:ssl_version()) -> [cipher_suite()].
%%
@@ -227,16 +307,27 @@ suites({3, N}) ->
all_suites(Version) ->
suites(Version)
- ++ ssl_cipher:anonymous_suites()
- ++ ssl_cipher:psk_suites(Version)
- ++ ssl_cipher:srp_suites().
+ ++ anonymous_suites(Version)
+ ++ psk_suites(Version)
+ ++ srp_suites()
+ ++ rc4_suites(Version).
%%--------------------------------------------------------------------
--spec anonymous_suites() -> [cipher_suite()].
+-spec anonymous_suites(ssl_record:ssl_version() | integer()) -> [cipher_suite()].
%%
%% Description: Returns a list of the anonymous cipher suites, only supported
%% if explicitly set by user. Intended only for testing.
%%--------------------------------------------------------------------
-anonymous_suites() ->
+
+anonymous_suites({3, N}) ->
+ anonymous_suites(N);
+
+anonymous_suites(N)
+ when N >= 3 ->
+ [?TLS_DH_anon_WITH_AES_128_GCM_SHA256,
+ ?TLS_DH_anon_WITH_AES_256_GCM_SHA384
+ ] ++ anonymous_suites(0);
+
+anonymous_suites(_) ->
[?TLS_DH_anon_WITH_RC4_128_MD5,
?TLS_DH_anon_WITH_DES_CBC_SHA,
?TLS_DH_anon_WITH_3DES_EDE_CBC_SHA,
@@ -260,13 +351,20 @@ psk_suites({3, N}) ->
psk_suites(N)
when N >= 3 ->
- psk_suites(0) ++
- [?TLS_DHE_PSK_WITH_AES_256_CBC_SHA384,
- ?TLS_RSA_PSK_WITH_AES_256_CBC_SHA384,
- ?TLS_PSK_WITH_AES_256_CBC_SHA384,
- ?TLS_DHE_PSK_WITH_AES_128_CBC_SHA256,
- ?TLS_RSA_PSK_WITH_AES_128_CBC_SHA256,
- ?TLS_PSK_WITH_AES_128_CBC_SHA256];
+ [
+ ?TLS_DHE_PSK_WITH_AES_256_GCM_SHA384,
+ ?TLS_RSA_PSK_WITH_AES_256_GCM_SHA384,
+ ?TLS_PSK_WITH_AES_256_GCM_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_DHE_PSK_WITH_AES_128_GCM_SHA256,
+ ?TLS_RSA_PSK_WITH_AES_128_GCM_SHA256,
+ ?TLS_PSK_WITH_AES_128_GCM_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_DHE_PSK_WITH_AES_256_CBC_SHA,
@@ -298,6 +396,24 @@ srp_suites() ->
?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 rc4_suites(Version::ssl_record:ssl_version()) -> [cipher_suite()].
+%%
+%% Description: Returns a list of the RSA|(ECDH/RSA)| (ECDH/ECDSA)
+%% with RC4 cipher suites, only supported if explicitly set by user.
+%% Are not considered secure any more. Other RC4 suites already
+%% belonged to the user configured only category.
+%%--------------------------------------------------------------------
+rc4_suites({3, 0}) ->
+ [?TLS_RSA_WITH_RC4_128_SHA,
+ ?TLS_RSA_WITH_RC4_128_MD5];
+rc4_suites({3, N}) when N =< 3 ->
+ [?TLS_ECDHE_ECDSA_WITH_RC4_128_SHA,
+ ?TLS_ECDHE_RSA_WITH_RC4_128_SHA,
+ ?TLS_RSA_WITH_RC4_128_SHA,
+ ?TLS_RSA_WITH_RC4_128_MD5,
+ ?TLS_ECDH_ECDSA_WITH_RC4_128_SHA,
+ ?TLS_ECDH_RSA_WITH_RC4_128_SHA].
%%--------------------------------------------------------------------
-spec suite_definition(cipher_suite()) -> int_cipher_suite().
@@ -418,6 +534,19 @@ suite_definition(?TLS_RSA_PSK_WITH_AES_256_CBC_SHA) ->
%%% TLS 1.2 PSK Cipher Suites RFC 5487
+suite_definition(?TLS_PSK_WITH_AES_128_GCM_SHA256) ->
+ {psk, aes_128_gcm, null, sha256};
+suite_definition(?TLS_PSK_WITH_AES_256_GCM_SHA384) ->
+ {psk, aes_256_gcm, null, sha384};
+suite_definition(?TLS_DHE_PSK_WITH_AES_128_GCM_SHA256) ->
+ {dhe_psk, aes_128_gcm, null, sha256};
+suite_definition(?TLS_DHE_PSK_WITH_AES_256_GCM_SHA384) ->
+ {dhe_psk, aes_256_gcm, null, sha384};
+suite_definition(?TLS_RSA_PSK_WITH_AES_128_GCM_SHA256) ->
+ {rsa_psk, aes_128_gcm, null, sha256};
+suite_definition(?TLS_RSA_PSK_WITH_AES_256_GCM_SHA384) ->
+ {rsa_psk, aes_256_gcm, null, sha384};
+
suite_definition(?TLS_PSK_WITH_AES_128_CBC_SHA256) ->
{psk, aes_128_cbc, sha256, default_prf};
suite_definition(?TLS_PSK_WITH_AES_256_CBC_SHA384) ->
@@ -537,7 +666,59 @@ suite_definition(?TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384) ->
suite_definition(?TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256) ->
{ecdh_rsa, aes_128_cbc, sha256, sha256};
suite_definition(?TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384) ->
- {ecdh_rsa, aes_256_cbc, sha384, sha384}.
+ {ecdh_rsa, aes_256_cbc, sha384, sha384};
+
+%% RFC 5288 AES-GCM Cipher Suites
+suite_definition(?TLS_RSA_WITH_AES_128_GCM_SHA256) ->
+ {rsa, aes_128_gcm, null, sha256};
+suite_definition(?TLS_RSA_WITH_AES_256_GCM_SHA384) ->
+ {rsa, aes_256_gcm, null, sha384};
+suite_definition(?TLS_DHE_RSA_WITH_AES_128_GCM_SHA256) ->
+ {dhe_rsa, aes_128_gcm, null, sha256};
+suite_definition(?TLS_DHE_RSA_WITH_AES_256_GCM_SHA384) ->
+ {dhe_rsa, aes_256_gcm, null, sha384};
+suite_definition(?TLS_DH_RSA_WITH_AES_128_GCM_SHA256) ->
+ {dh_rsa, aes_128_gcm, null, sha256};
+suite_definition(?TLS_DH_RSA_WITH_AES_256_GCM_SHA384) ->
+ {dh_rsa, aes_256_gcm, null, sha384};
+suite_definition(?TLS_DHE_DSS_WITH_AES_128_GCM_SHA256) ->
+ {dhe_dss, aes_128_gcm, null, sha256};
+suite_definition(?TLS_DHE_DSS_WITH_AES_256_GCM_SHA384) ->
+ {dhe_dss, aes_256_gcm, null, sha384};
+suite_definition(?TLS_DH_DSS_WITH_AES_128_GCM_SHA256) ->
+ {dh_dss, aes_128_gcm, null, sha256};
+suite_definition(?TLS_DH_DSS_WITH_AES_256_GCM_SHA384) ->
+ {dh_dss, aes_256_gcm, null, sha384};
+suite_definition(?TLS_DH_anon_WITH_AES_128_GCM_SHA256) ->
+ {dh_anon, aes_128_gcm, null, sha256};
+suite_definition(?TLS_DH_anon_WITH_AES_256_GCM_SHA384) ->
+ {dh_anon, aes_256_gcm, null, sha384};
+
+%% RFC 5289 ECC AES-GCM Cipher Suites
+suite_definition(?TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256) ->
+ {ecdhe_ecdsa, aes_128_gcm, null, sha256};
+suite_definition(?TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384) ->
+ {ecdhe_ecdsa, aes_256_gcm, null, sha384};
+suite_definition(?TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256) ->
+ {ecdh_ecdsa, aes_128_gcm, null, sha256};
+suite_definition(?TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384) ->
+ {ecdh_ecdsa, aes_256_gcm, null, sha384};
+suite_definition(?TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) ->
+ {ecdhe_rsa, aes_128_gcm, null, sha256};
+suite_definition(?TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) ->
+ {ecdhe_rsa, aes_256_gcm, null, sha384};
+suite_definition(?TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256) ->
+ {ecdh_rsa, aes_128_gcm, null, sha256};
+suite_definition(?TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384) ->
+ {ecdh_rsa, aes_256_gcm, null, sha384};
+
+%% draft-agl-tls-chacha20poly1305-04 Chacha20/Poly1305 Suites
+suite_definition(?TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256) ->
+ {ecdhe_rsa, chacha20_poly1305, null, sha256};
+suite_definition(?TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256) ->
+ {ecdhe_ecdsa, chacha20_poly1305, null, sha256};
+suite_definition(?TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256) ->
+ {dhe_rsa, chacha20_poly1305, null, sha256}.
%%--------------------------------------------------------------------
-spec suite(erl_cipher_suite()) -> cipher_suite().
@@ -641,6 +822,19 @@ suite({rsa_psk, aes_256_cbc,sha}) ->
%%% TLS 1.2 PSK Cipher Suites RFC 5487
+suite({psk, aes_128_gcm, null}) ->
+ ?TLS_PSK_WITH_AES_128_GCM_SHA256;
+suite({psk, aes_256_gcm, null}) ->
+ ?TLS_PSK_WITH_AES_256_GCM_SHA384;
+suite({dhe_psk, aes_128_gcm, null}) ->
+ ?TLS_DHE_PSK_WITH_AES_128_GCM_SHA256;
+suite({dhe_psk, aes_256_gcm, null}) ->
+ ?TLS_DHE_PSK_WITH_AES_256_GCM_SHA384;
+suite({rsa_psk, aes_128_gcm, null}) ->
+ ?TLS_RSA_PSK_WITH_AES_128_GCM_SHA256;
+suite({rsa_psk, aes_256_gcm, null}) ->
+ ?TLS_RSA_PSK_WITH_AES_256_GCM_SHA384;
+
suite({psk, aes_128_cbc, sha256}) ->
?TLS_PSK_WITH_AES_128_CBC_SHA256;
suite({psk, aes_256_cbc, sha384}) ->
@@ -760,7 +954,60 @@ suite({ecdhe_rsa, aes_256_cbc, sha384}) ->
suite({ecdh_rsa, aes_128_cbc, sha256}) ->
?TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256;
suite({ecdh_rsa, aes_256_cbc, sha384}) ->
- ?TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384.
+ ?TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384;
+
+%% RFC 5288 AES-GCM Cipher Suites
+suite({rsa, aes_128_gcm, null}) ->
+ ?TLS_RSA_WITH_AES_128_GCM_SHA256;
+suite({rsa, aes_256_gcm, null}) ->
+ ?TLS_RSA_WITH_AES_256_GCM_SHA384;
+suite({dhe_rsa, aes_128_gcm, null}) ->
+ ?TLS_DHE_RSA_WITH_AES_128_GCM_SHA256;
+suite({dhe_rsa, aes_256_gcm, null}) ->
+ ?TLS_DHE_RSA_WITH_AES_256_GCM_SHA384;
+suite({dh_rsa, aes_128_gcm, null}) ->
+ ?TLS_DH_RSA_WITH_AES_128_GCM_SHA256;
+suite({dh_rsa, aes_256_gcm, null}) ->
+ ?TLS_DH_RSA_WITH_AES_256_GCM_SHA384;
+suite({dhe_dss, aes_128_gcm, null}) ->
+ ?TLS_DHE_DSS_WITH_AES_128_GCM_SHA256;
+suite({dhe_dss, aes_256_gcm, null}) ->
+ ?TLS_DHE_DSS_WITH_AES_256_GCM_SHA384;
+suite({dh_dss, aes_128_gcm, null}) ->
+ ?TLS_DH_DSS_WITH_AES_128_GCM_SHA256;
+suite({dh_dss, aes_256_gcm, null}) ->
+ ?TLS_DH_DSS_WITH_AES_256_GCM_SHA384;
+suite({dh_anon, aes_128_gcm, null}) ->
+ ?TLS_DH_anon_WITH_AES_128_GCM_SHA256;
+suite({dh_anon, aes_256_gcm, null}) ->
+ ?TLS_DH_anon_WITH_AES_256_GCM_SHA384;
+
+%% RFC 5289 ECC AES-GCM Cipher Suites
+suite({ecdhe_ecdsa, aes_128_gcm, null}) ->
+ ?TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256;
+suite({ecdhe_ecdsa, aes_256_gcm, null}) ->
+ ?TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384;
+suite({ecdh_ecdsa, aes_128_gcm, null}) ->
+ ?TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256;
+suite({ecdh_ecdsa, aes_256_gcm, null}) ->
+ ?TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384;
+suite({ecdhe_rsa, aes_128_gcm, null}) ->
+ ?TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256;
+suite({ecdhe_rsa, aes_256_gcm, null}) ->
+ ?TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384;
+suite({ecdh_rsa, aes_128_gcm, null}) ->
+ ?TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256;
+suite({ecdh_rsa, aes_256_gcm, null}) ->
+ ?TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384;
+
+
+%% draft-agl-tls-chacha20poly1305-04 Chacha20/Poly1305 Suites
+suite({ecdhe_rsa, chacha20_poly1305, null}) ->
+ ?TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256;
+suite({ecdhe_ecdsa, chacha20_poly1305, null}) ->
+ ?TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256;
+suite({dhe_rsa, chacha20_poly1305, null}) ->
+ ?TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256.
%%--------------------------------------------------------------------
-spec openssl_suite(openssl_cipher_suite()) -> cipher_suite().
@@ -875,7 +1122,47 @@ openssl_suite("ECDHE-RSA-AES256-SHA384") ->
openssl_suite("ECDH-RSA-AES128-SHA256") ->
?TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256;
openssl_suite("ECDH-RSA-AES256-SHA384") ->
- ?TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384.
+ ?TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384;
+
+%% RFC 5288 AES-GCM Cipher Suites
+openssl_suite("AES128-GCM-SHA256") ->
+ ?TLS_RSA_WITH_AES_128_GCM_SHA256;
+openssl_suite("AES256-GCM-SHA384") ->
+ ?TLS_RSA_WITH_AES_256_GCM_SHA384;
+openssl_suite("DHE-RSA-AES128-GCM-SHA256") ->
+ ?TLS_DHE_RSA_WITH_AES_128_GCM_SHA256;
+openssl_suite("DHE-RSA-AES256-GCM-SHA384") ->
+ ?TLS_DHE_RSA_WITH_AES_256_GCM_SHA384;
+openssl_suite("DH-RSA-AES128-GCM-SHA256") ->
+ ?TLS_DH_RSA_WITH_AES_128_GCM_SHA256;
+openssl_suite("DH-RSA-AES256-GCM-SHA384") ->
+ ?TLS_DH_RSA_WITH_AES_256_GCM_SHA384;
+openssl_suite("DHE-DSS-AES128-GCM-SHA256") ->
+ ?TLS_DHE_DSS_WITH_AES_128_GCM_SHA256;
+openssl_suite("DHE-DSS-AES256-GCM-SHA384") ->
+ ?TLS_DHE_DSS_WITH_AES_256_GCM_SHA384;
+openssl_suite("DH-DSS-AES128-GCM-SHA256") ->
+ ?TLS_DH_DSS_WITH_AES_128_GCM_SHA256;
+openssl_suite("DH-DSS-AES256-GCM-SHA384") ->
+ ?TLS_DH_DSS_WITH_AES_256_GCM_SHA384;
+
+%% RFC 5289 ECC AES-GCM Cipher Suites
+openssl_suite("ECDHE-ECDSA-AES128-GCM-SHA256") ->
+ ?TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256;
+openssl_suite("ECDHE-ECDSA-AES256-GCM-SHA384") ->
+ ?TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384;
+openssl_suite("ECDH-ECDSA-AES128-GCM-SHA256") ->
+ ?TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256;
+openssl_suite("ECDH-ECDSA-AES256-GCM-SHA384") ->
+ ?TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384;
+openssl_suite("ECDHE-RSA-AES128-GCM-SHA256") ->
+ ?TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256;
+openssl_suite("ECDHE-RSA-AES256-GCM-SHA384") ->
+ ?TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384;
+openssl_suite("ECDH-RSA-AES128-GCM-SHA256") ->
+ ?TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256;
+openssl_suite("ECDH-RSA-AES256-GCM-SHA384") ->
+ ?TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384.
%%--------------------------------------------------------------------
-spec openssl_suite_name(cipher_suite()) -> openssl_cipher_suite().
@@ -1012,6 +1299,46 @@ openssl_suite_name(?TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256) ->
openssl_suite_name(?TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384) ->
"ECDH-RSA-AES256-SHA384";
+%% RFC 5288 AES-GCM Cipher Suites
+openssl_suite_name(?TLS_RSA_WITH_AES_128_GCM_SHA256) ->
+ "AES128-GCM-SHA256";
+openssl_suite_name(?TLS_RSA_WITH_AES_256_GCM_SHA384) ->
+ "AES256-GCM-SHA384";
+openssl_suite_name(?TLS_DHE_RSA_WITH_AES_128_GCM_SHA256) ->
+ "DHE-RSA-AES128-GCM-SHA256";
+openssl_suite_name(?TLS_DHE_RSA_WITH_AES_256_GCM_SHA384) ->
+ "DHE-RSA-AES256-GCM-SHA384";
+openssl_suite_name(?TLS_DH_RSA_WITH_AES_128_GCM_SHA256) ->
+ "DH-RSA-AES128-GCM-SHA256";
+openssl_suite_name(?TLS_DH_RSA_WITH_AES_256_GCM_SHA384) ->
+ "DH-RSA-AES256-GCM-SHA384";
+openssl_suite_name(?TLS_DHE_DSS_WITH_AES_128_GCM_SHA256) ->
+ "DHE-DSS-AES128-GCM-SHA256";
+openssl_suite_name(?TLS_DHE_DSS_WITH_AES_256_GCM_SHA384) ->
+ "DHE-DSS-AES256-GCM-SHA384";
+openssl_suite_name(?TLS_DH_DSS_WITH_AES_128_GCM_SHA256) ->
+ "DH-DSS-AES128-GCM-SHA256";
+openssl_suite_name(?TLS_DH_DSS_WITH_AES_256_GCM_SHA384) ->
+ "DH-DSS-AES256-GCM-SHA384";
+
+%% RFC 5289 ECC AES-GCM Cipher Suites
+openssl_suite_name(?TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256) ->
+ "ECDHE-ECDSA-AES128-GCM-SHA256";
+openssl_suite_name(?TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384) ->
+ "ECDHE-ECDSA-AES256-GCM-SHA384";
+openssl_suite_name(?TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256) ->
+ "ECDH-ECDSA-AES128-GCM-SHA256";
+openssl_suite_name(?TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384) ->
+ "ECDH-ECDSA-AES256-GCM-SHA384";
+openssl_suite_name(?TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) ->
+ "ECDHE-RSA-AES128-GCM-SHA256";
+openssl_suite_name(?TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) ->
+ "ECDHE-RSA-AES256-GCM-SHA384";
+openssl_suite_name(?TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256) ->
+ "ECDH-RSA-AES128-GCM-SHA256";
+openssl_suite_name(?TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384) ->
+ "ECDH-RSA-AES256-GCM-SHA384";
+
%% No oppenssl name
openssl_suite_name(Cipher) ->
suite_definition(Cipher).
@@ -1095,6 +1422,13 @@ is_acceptable_keyexchange(KeyExchange, Algos)
is_acceptable_keyexchange(_, _) ->
true.
+is_acceptable_cipher(Cipher, Algos)
+ when Cipher == aes_128_gcm;
+ Cipher == aes_256_gcm ->
+ proplists:get_bool(aes_gcm, Algos);
+is_acceptable_cipher(Cipher, Algos)
+ when Cipher == chacha20_poly1305 ->
+ proplists:get_bool(Cipher, Algos);
is_acceptable_cipher(_, _) ->
true.
@@ -1125,7 +1459,12 @@ bulk_cipher_algorithm('3des_ede_cbc') ->
?'3DES';
bulk_cipher_algorithm(Cipher) when Cipher == aes_128_cbc;
Cipher == aes_256_cbc ->
- ?AES.
+ ?AES_CBC;
+bulk_cipher_algorithm(Cipher) when Cipher == aes_128_gcm;
+ Cipher == aes_256_gcm ->
+ ?AES_GCM;
+bulk_cipher_algorithm(chacha20_poly1305) ->
+ ?CHACHA20_POLY1305.
type(Cipher) when Cipher == null;
Cipher == rc4_128 ->
@@ -1135,7 +1474,11 @@ type(Cipher) when Cipher == des_cbc;
Cipher == '3des_ede_cbc';
Cipher == aes_128_cbc;
Cipher == aes_256_cbc ->
- ?BLOCK.
+ ?BLOCK;
+type(Cipher) when Cipher == aes_128_gcm;
+ Cipher == aes_256_gcm;
+ Cipher == chacha20_poly1305 ->
+ ?AEAD.
key_material(null) ->
0;
@@ -1148,6 +1491,12 @@ key_material('3des_ede_cbc') ->
key_material(aes_128_cbc) ->
16;
key_material(aes_256_cbc) ->
+ 32;
+key_material(aes_128_gcm) ->
+ 16;
+key_material(aes_256_gcm) ->
+ 32;
+key_material(chacha20_poly1305) ->
32.
expanded_key_material(null) ->
@@ -1159,7 +1508,10 @@ expanded_key_material(Cipher) when Cipher == des_cbc ->
expanded_key_material('3des_ede_cbc') ->
24;
expanded_key_material(Cipher) when Cipher == aes_128_cbc;
- Cipher == aes_256_cbc ->
+ Cipher == aes_256_cbc;
+ Cipher == aes_128_gcm;
+ Cipher == aes_256_gcm;
+ Cipher == chacha20_poly1305 ->
unknown.
@@ -1168,16 +1520,25 @@ effective_key_bits(null) ->
effective_key_bits(des_cbc) ->
56;
effective_key_bits(Cipher) when Cipher == rc4_128;
- Cipher == aes_128_cbc ->
+ Cipher == aes_128_cbc;
+ Cipher == aes_128_gcm ->
128;
effective_key_bits('3des_ede_cbc') ->
168;
-effective_key_bits(aes_256_cbc) ->
+effective_key_bits(Cipher) when Cipher == aes_256_cbc;
+ Cipher == aes_256_gcm;
+ Cipher == chacha20_poly1305 ->
256.
iv_size(Cipher) when Cipher == null;
- Cipher == rc4_128 ->
+ Cipher == rc4_128;
+ Cipher == chacha20_poly1305->
0;
+
+iv_size(Cipher) when Cipher == aes_128_gcm;
+ Cipher == aes_256_gcm ->
+ 4;
+
iv_size(Cipher) ->
block_size(Cipher).
@@ -1186,7 +1547,10 @@ block_size(Cipher) when Cipher == des_cbc;
8;
block_size(Cipher) when Cipher == aes_128_cbc;
- Cipher == aes_256_cbc ->
+ Cipher == aes_256_cbc;
+ Cipher == aes_128_gcm;
+ Cipher == aes_256_gcm;
+ Cipher == chacha20_poly1305 ->
16.
prf_algorithm(default_prf, {3, N}) when N >= 3 ->
@@ -1342,10 +1706,15 @@ dhe_rsa_suites() ->
?TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA,
?TLS_DHE_RSA_WITH_AES_128_CBC_SHA256,
?TLS_DHE_RSA_WITH_AES_128_CBC_SHA,
- ?TLS_DHE_RSA_WITH_DES_CBC_SHA].
+ ?TLS_DHE_RSA_WITH_DES_CBC_SHA,
+ ?TLS_DHE_RSA_WITH_AES_128_GCM_SHA256,
+ ?TLS_DHE_RSA_WITH_AES_256_GCM_SHA384,
+ ?TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256].
psk_rsa_suites() ->
- [?TLS_RSA_PSK_WITH_AES_256_CBC_SHA384,
+ [?TLS_RSA_PSK_WITH_AES_256_GCM_SHA384,
+ ?TLS_RSA_PSK_WITH_AES_128_GCM_SHA256,
+ ?TLS_RSA_PSK_WITH_AES_256_CBC_SHA384,
?TLS_RSA_PSK_WITH_AES_128_CBC_SHA256,
?TLS_RSA_PSK_WITH_AES_256_CBC_SHA,
?TLS_RSA_PSK_WITH_AES_128_CBC_SHA,
@@ -1365,7 +1734,9 @@ rsa_suites() ->
?TLS_RSA_WITH_AES_128_CBC_SHA,
?TLS_RSA_WITH_RC4_128_SHA,
?TLS_RSA_WITH_RC4_128_MD5,
- ?TLS_RSA_WITH_DES_CBC_SHA].
+ ?TLS_RSA_WITH_DES_CBC_SHA,
+ ?TLS_RSA_WITH_AES_128_GCM_SHA256,
+ ?TLS_RSA_WITH_AES_256_GCM_SHA384].
ecdh_rsa_suites() ->
[?TLS_ECDH_RSA_WITH_NULL_SHA,
@@ -1374,7 +1745,9 @@ ecdh_rsa_suites() ->
?TLS_ECDH_RSA_WITH_AES_128_CBC_SHA,
?TLS_ECDH_RSA_WITH_AES_256_CBC_SHA,
?TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256,
- ?TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384].
+ ?TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384,
+ ?TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256,
+ ?TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384].
ecdhe_rsa_suites() ->
[?TLS_ECDHE_RSA_WITH_NULL_SHA,
@@ -1383,7 +1756,10 @@ ecdhe_rsa_suites() ->
?TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
?TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
?TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
- ?TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384].
+ ?TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384,
+ ?TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
+ ?TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
+ ?TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256].
dsa_signed_suites() ->
dhe_dss_suites() ++ srp_dss_suites().
@@ -1394,7 +1770,9 @@ dhe_dss_suites() ->
?TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA,
?TLS_DHE_DSS_WITH_AES_128_CBC_SHA256,
?TLS_DHE_DSS_WITH_AES_128_CBC_SHA,
- ?TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA].
+ ?TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA,
+ ?TLS_DHE_DSS_WITH_AES_128_GCM_SHA256,
+ ?TLS_DHE_DSS_WITH_AES_256_GCM_SHA384].
srp_dss_suites() ->
[?TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA,
@@ -1418,7 +1796,9 @@ ecdh_ecdsa_suites() ->
?TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA,
?TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA,
?TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256,
- ?TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384].
+ ?TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384,
+ ?TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256,
+ ?TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384].
ecdhe_ecdsa_suites() ->
[?TLS_ECDHE_ECDSA_WITH_NULL_SHA,
@@ -1427,7 +1807,10 @@ ecdhe_ecdsa_suites() ->
?TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
?TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
?TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
- ?TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384].
+ ?TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384,
+ ?TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
+ ?TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
+ ?TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256].
filter_keyuse(OtpCert, Ciphers, Suites, SignSuites) ->
TBSCert = OtpCert#'OTPCertificate'.tbsCertificate,
diff --git a/lib/ssl/src/ssl_cipher.hrl b/lib/ssl/src/ssl_cipher.hrl
index 3e50563f0a..8689a3c68b 100644
--- a/lib/ssl/src/ssl_cipher.hrl
+++ b/lib/ssl/src/ssl_cipher.hrl
@@ -46,7 +46,8 @@
-record(cipher_state, {
iv,
key,
- state
+ state,
+ nonce
}).
%%% TLS_NULL_WITH_NULL_NULL is specified and is the initial state of a
@@ -399,6 +400,24 @@
%%% TLS 1.2 PSK Cipher Suites RFC 5487
+%% TLS_PSK_WITH_AES_128_GCM_SHA256 = {0x00,0xA8};
+-define(TLS_PSK_WITH_AES_128_GCM_SHA256, <<?BYTE(16#00), ?BYTE(16#A8)>>).
+
+%% TLS_PSK_WITH_AES_256_GCM_SHA384 = {0x00,0xA9};
+-define(TLS_PSK_WITH_AES_256_GCM_SHA384, <<?BYTE(16#00), ?BYTE(16#A9)>>).
+
+%% TLS_DHE_PSK_WITH_AES_128_GCM_SHA256 = {0x00,0xAA};
+-define(TLS_DHE_PSK_WITH_AES_128_GCM_SHA256, <<?BYTE(16#00), ?BYTE(16#AA)>>).
+
+%% TLS_DHE_PSK_WITH_AES_256_GCM_SHA384 = {0x00,0xAB};
+-define(TLS_DHE_PSK_WITH_AES_256_GCM_SHA384, <<?BYTE(16#00), ?BYTE(16#AB)>>).
+
+%% TLS_RSA_PSK_WITH_AES_128_GCM_SHA256 = {0x00,0xAC};
+-define(TLS_RSA_PSK_WITH_AES_128_GCM_SHA256, <<?BYTE(16#00), ?BYTE(16#AC)>>).
+
+%% TLS_RSA_PSK_WITH_AES_256_GCM_SHA384 = {0x00,0xAD};
+-define(TLS_RSA_PSK_WITH_AES_256_GCM_SHA384, <<?BYTE(16#00), ?BYTE(16#AD)>>).
+
%% TLS_PSK_WITH_AES_128_CBC_SHA256 = {0x00,0xAE};
-define(TLS_PSK_WITH_AES_128_CBC_SHA256, <<?BYTE(16#00), ?BYTE(16#AE)>>).
@@ -464,4 +483,79 @@
%% TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA = { 0xC0,0x22 };
-define(TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA, <<?BYTE(16#C0), ?BYTE(16#22)>>).
+%%% AES-GCM Cipher Suites RFC 5288
+
+%% TLS_RSA_WITH_AES_128_GCM_SHA256 = {0x00,0x9C}
+-define(TLS_RSA_WITH_AES_128_GCM_SHA256, <<?BYTE(16#00), ?BYTE(16#9C)>>).
+
+%% TLS_RSA_WITH_AES_256_GCM_SHA384 = {0x00,0x9D}
+-define(TLS_RSA_WITH_AES_256_GCM_SHA384, <<?BYTE(16#00), ?BYTE(16#9D)>>).
+
+%% TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 = {0x00,0x9E}
+-define(TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, <<?BYTE(16#00), ?BYTE(16#9E)>>).
+
+%% TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 = {0x00,0x9F}
+-define(TLS_DHE_RSA_WITH_AES_256_GCM_SHA384, <<?BYTE(16#00), ?BYTE(16#9F)>>).
+
+%% TLS_DH_RSA_WITH_AES_128_GCM_SHA256 = {0x00,0xA0}
+-define(TLS_DH_RSA_WITH_AES_128_GCM_SHA256, <<?BYTE(16#00), ?BYTE(16#A0)>>).
+
+%% TLS_DH_RSA_WITH_AES_256_GCM_SHA384 = {0x00,0xA1}
+-define(TLS_DH_RSA_WITH_AES_256_GCM_SHA384, <<?BYTE(16#00), ?BYTE(16#A1)>>).
+
+%% TLS_DHE_DSS_WITH_AES_128_GCM_SHA256 = {0x00,0xA2}
+-define(TLS_DHE_DSS_WITH_AES_128_GCM_SHA256, <<?BYTE(16#00), ?BYTE(16#A2)>>).
+
+%% TLS_DHE_DSS_WITH_AES_256_GCM_SHA384 = {0x00,0xA3}
+-define(TLS_DHE_DSS_WITH_AES_256_GCM_SHA384, <<?BYTE(16#00), ?BYTE(16#A3)>>).
+
+%% TLS_DH_DSS_WITH_AES_128_GCM_SHA256 = {0x00,0xA4}
+-define(TLS_DH_DSS_WITH_AES_128_GCM_SHA256, <<?BYTE(16#00), ?BYTE(16#A4)>>).
+
+%% TLS_DH_DSS_WITH_AES_256_GCM_SHA384 = {0x00,0xA5}
+-define(TLS_DH_DSS_WITH_AES_256_GCM_SHA384, <<?BYTE(16#00), ?BYTE(16#A5)>>).
+
+%% TLS_DH_anon_WITH_AES_128_GCM_SHA256 = {0x00,0xA6}
+-define(TLS_DH_anon_WITH_AES_128_GCM_SHA256, <<?BYTE(16#00), ?BYTE(16#A6)>>).
+
+%% TLS_DH_anon_WITH_AES_256_GCM_SHA384 = {0x00,0xA7}
+-define(TLS_DH_anon_WITH_AES_256_GCM_SHA384, <<?BYTE(16#00), ?BYTE(16#A7)>>).
+
+%%% ECC AES-GCM Cipher Suites RFC 5289
+
+%% TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 = {0xC0,0x2B};
+-define(TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, <<?BYTE(16#C0), ?BYTE(16#2B)>>).
+
+%% TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 = {0xC0,0x2C};
+-define(TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, <<?BYTE(16#C0), ?BYTE(16#2C)>>).
+
+%% TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256 = {0xC0,0x2D};
+-define(TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256, <<?BYTE(16#C0), ?BYTE(16#2D)>>).
+
+%% TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384 = {0xC0,0x2E};
+-define(TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384, <<?BYTE(16#C0), ?BYTE(16#2E)>>).
+
+%% TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 = {0xC0,0x2F};
+-define(TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, <<?BYTE(16#C0), ?BYTE(16#2F)>>).
+
+%% TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 = {0xC0,0x30};
+-define(TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, <<?BYTE(16#C0), ?BYTE(16#30)>>).
+
+%% TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256 = {0xC0,0x31};
+-define(TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256, <<?BYTE(16#C0), ?BYTE(16#31)>>).
+
+%% TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384 = {0xC0,0x32};
+-define(TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384, <<?BYTE(16#C0), ?BYTE(16#32)>>).
+
+%%% Chacha20/Poly1305 Suites draft-agl-tls-chacha20poly1305-04
+
+%% TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 = {0xcc, 0x13}
+-define(TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, <<?BYTE(16#CC), ?BYTE(16#13)>>).
+
+%% TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 = {0xcc, 0x14}
+-define(TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, <<?BYTE(16#CC), ?BYTE(16#14)>>).
+
+%% TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256 = {0xcc, 0x15}
+-define(TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256, <<?BYTE(16#CC), ?BYTE(16#15)>>).
+
-endif. % -ifdef(ssl_cipher).
diff --git a/lib/ssl/src/ssl_config.erl b/lib/ssl/src/ssl_config.erl
index 545b8aa0f6..fc8b214a29 100644
--- a/lib/ssl/src/ssl_config.erl
+++ b/lib/ssl/src/ssl_config.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2015. 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
@@ -31,13 +31,13 @@ init(SslOpts, Role) ->
init_manager_name(SslOpts#ssl_options.erl_dist),
- {ok, CertDbRef, CertDbHandle, FileRefHandle, PemCacheHandle, CacheHandle, OwnCert}
+ {ok, CertDbRef, CertDbHandle, FileRefHandle, PemCacheHandle, CacheHandle, CRLDbHandle, OwnCert}
= init_certificates(SslOpts, Role),
PrivateKey =
init_private_key(PemCacheHandle, SslOpts#ssl_options.key, SslOpts#ssl_options.keyfile,
SslOpts#ssl_options.password, Role),
DHParams = init_diffie_hellman(PemCacheHandle, SslOpts#ssl_options.dh, SslOpts#ssl_options.dhfile, Role),
- {ok, CertDbRef, CertDbHandle, FileRefHandle, CacheHandle, OwnCert, PrivateKey, DHParams}.
+ {ok, CertDbRef, CertDbHandle, FileRefHandle, CacheHandle, CRLDbHandle, OwnCert, PrivateKey, DHParams}.
init_manager_name(false) ->
put(ssl_manager, ssl_manager:manager_name(normal));
@@ -46,9 +46,11 @@ init_manager_name(true) ->
init_certificates(#ssl_options{cacerts = CaCerts,
cacertfile = CACertFile,
- certfile = CertFile,
- cert = Cert}, Role) ->
- {ok, CertDbRef, CertDbHandle, FileRefHandle, PemCacheHandle, CacheHandle} =
+ certfile = CertFile,
+ cert = Cert,
+ crl_cache = CRLCache
+ }, Role) ->
+ {ok, CertDbRef, CertDbHandle, FileRefHandle, PemCacheHandle, CacheHandle, CRLDbInfo} =
try
Certs = case CaCerts of
undefined ->
@@ -56,39 +58,40 @@ init_certificates(#ssl_options{cacerts = CaCerts,
_ ->
{der, CaCerts}
end,
- {ok, _, _, _, _, _} = ssl_manager:connection_init(Certs, Role)
+ {ok, _, _, _, _, _, _} = ssl_manager:connection_init(Certs, Role, CRLCache)
catch
_:Reason ->
file_error(CACertFile, {cacertfile, Reason})
end,
init_certificates(Cert, CertDbRef, CertDbHandle, FileRefHandle, PemCacheHandle,
- CacheHandle, CertFile, Role).
+ CacheHandle, CRLDbInfo, CertFile, Role).
-init_certificates(undefined, CertDbRef, CertDbHandle, FileRefHandle, PemCacheHandle, CacheHandle, <<>>, _) ->
- {ok, CertDbRef, CertDbHandle, FileRefHandle, PemCacheHandle, CacheHandle, undefined};
+init_certificates(undefined, CertDbRef, CertDbHandle, FileRefHandle, PemCacheHandle, CacheHandle,
+ CRLDbInfo, <<>>, _) ->
+ {ok, CertDbRef, CertDbHandle, FileRefHandle, PemCacheHandle, CacheHandle, CRLDbInfo, undefined};
init_certificates(undefined, CertDbRef, CertDbHandle, FileRefHandle, PemCacheHandle,
- CacheHandle, CertFile, client) ->
+ CacheHandle, CRLDbInfo, CertFile, client) ->
try
%% Ignoring potential proxy-certificates see:
%% http://dev.globus.org/wiki/Security/ProxyFileFormat
[OwnCert|_] = ssl_certificate:file_to_certificats(CertFile, PemCacheHandle),
- {ok, CertDbRef, CertDbHandle, FileRefHandle, PemCacheHandle, CacheHandle, OwnCert}
+ {ok, CertDbRef, CertDbHandle, FileRefHandle, PemCacheHandle, CacheHandle, CRLDbInfo, OwnCert}
catch _Error:_Reason ->
- {ok, CertDbRef, CertDbHandle, FileRefHandle, PemCacheHandle, CacheHandle, undefined}
+ {ok, CertDbRef, CertDbHandle, FileRefHandle, PemCacheHandle, CacheHandle, CRLDbInfo, undefined}
end;
init_certificates(undefined, CertDbRef, CertDbHandle, FileRefHandle,
- PemCacheHandle, CacheRef, CertFile, server) ->
+ PemCacheHandle, CacheRef, CRLDbInfo, CertFile, server) ->
try
[OwnCert|_] = ssl_certificate:file_to_certificats(CertFile, PemCacheHandle),
- {ok, CertDbRef, CertDbHandle, FileRefHandle, PemCacheHandle, CacheRef, OwnCert}
+ {ok, CertDbRef, CertDbHandle, FileRefHandle, PemCacheHandle, CacheRef, CRLDbInfo, OwnCert}
catch
_:Reason ->
file_error(CertFile, {certfile, Reason})
end;
-init_certificates(Cert, CertDbRef, CertDbHandle, FileRefHandle, PemCacheHandle, CacheRef, _, _) ->
- {ok, CertDbRef, CertDbHandle, FileRefHandle, PemCacheHandle, CacheRef, Cert}.
+init_certificates(Cert, CertDbRef, CertDbHandle, FileRefHandle, PemCacheHandle, CacheRef, CRLDbInfo, _, _) ->
+ {ok, CertDbRef, CertDbHandle, FileRefHandle, PemCacheHandle, CacheRef, CRLDbInfo, Cert}.
init_private_key(_, undefined, <<>>, _Password, _Client) ->
undefined;
diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl
index b6059eac58..08d0145aa7 100644
--- a/lib/ssl/src/ssl_connection.erl
+++ b/lib/ssl/src/ssl_connection.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2013-2014. All Rights Reserved.
+%% Copyright Ericsson AB 2013-2015. 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
@@ -411,11 +411,15 @@ certify(#certificate{} = Cert,
role = Role,
cert_db = CertDbHandle,
cert_db_ref = CertDbRef,
+ crl_db = CRLDbInfo,
ssl_options = Opts} = State, Connection) ->
- case ssl_handshake:certify(Cert, CertDbHandle, CertDbRef, Opts#ssl_options.depth,
+ case ssl_handshake:certify(Cert, CertDbHandle, CertDbRef,
+ Opts#ssl_options.depth,
Opts#ssl_options.verify,
Opts#ssl_options.verify_fun,
Opts#ssl_options.partial_chain,
+ Opts#ssl_options.crl_check,
+ CRLDbInfo,
Role) of
{PeerCert, PublicKeyInfo} ->
handle_peer_cert(Role, PeerCert, PublicKeyInfo,
@@ -964,7 +968,7 @@ format_status(terminate, [_, State]) ->
%%% Internal functions
%%--------------------------------------------------------------------
ssl_config(Opts, Role, State) ->
- {ok, Ref, CertDbHandle, FileRefHandle, CacheHandle, OwnCert, Key, DHParams} =
+ {ok, Ref, CertDbHandle, FileRefHandle, CacheHandle, CRLDbInfo, OwnCert, Key, DHParams} =
ssl_config:init(Opts, Role),
Handshake = ssl_handshake:init_handshake_history(),
TimeStamp = calendar:datetime_to_gregorian_seconds({date(), time()}),
@@ -975,6 +979,7 @@ ssl_config(Opts, Role, State) ->
file_ref_db = FileRefHandle,
cert_db_ref = Ref,
cert_db = CertDbHandle,
+ crl_db = CRLDbInfo,
session_cache = CacheHandle,
private_key = Key,
diffie_hellman_params = DHParams,
diff --git a/lib/ssl/src/ssl_connection.hrl b/lib/ssl/src/ssl_connection.hrl
index b9a1ef3a84..ac3b26e4bf 100644
--- a/lib/ssl/src/ssl_connection.hrl
+++ b/lib/ssl/src/ssl_connection.hrl
@@ -1,8 +1,7 @@
-
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2013-2014. All Rights Reserved.
+%% Copyright Ericsson AB 2013-2015. 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
@@ -53,6 +52,7 @@
session :: #session{} | secret_printout(),
session_cache :: db_handle(),
session_cache_cb :: atom(),
+ crl_db :: term(),
negotiated_version :: ssl_record:ssl_version(),
client_certificate_requested = false :: boolean(),
key_algorithm :: ssl_cipher:key_algo(),
diff --git a/lib/ssl/src/ssl_crl.erl b/lib/ssl/src/ssl_crl.erl
new file mode 100644
index 0000000000..1a08d3c80a
--- /dev/null
+++ b/lib/ssl/src/ssl_crl.erl
@@ -0,0 +1,80 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2015-2015. 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%
+
+%----------------------------------------------------------------------
+%% Purpose: CRL handling
+%%----------------------------------------------------------------------
+
+-module(ssl_crl).
+
+-include("ssl_alert.hrl").
+-include("ssl_internal.hrl").
+-include_lib("public_key/include/public_key.hrl").
+
+-export([trusted_cert_and_path/3]).
+
+trusted_cert_and_path(CRL, {SerialNumber, Issuer},{Db, DbRef} = DbHandle) ->
+ case ssl_pkix_db:lookup_trusted_cert(Db, DbRef, SerialNumber, Issuer) of
+ undefined ->
+ trusted_cert_and_path(CRL, issuer_not_found, DbHandle);
+ {ok, {_, OtpCert}} ->
+ {ok, Root, Chain} = ssl_certificate:certificate_chain(OtpCert, Db, DbRef),
+ {ok, Root, lists:reverse(Chain)}
+ end;
+
+trusted_cert_and_path(CRL, issuer_not_found, {Db, DbRef} = DbHandle) ->
+ try find_issuer(CRL, DbHandle) of
+ OtpCert ->
+ {ok, Root, Chain} = ssl_certificate:certificate_chain(OtpCert, Db, DbRef),
+ {ok, Root, lists:reverse(Chain)}
+ catch
+ throw:_ ->
+ {error, issuer_not_found}
+ end.
+
+find_issuer(CRL, {Db,_}) ->
+ Issuer = public_key:pkix_normalize_name(public_key:pkix_crl_issuer(CRL)),
+ IsIssuerFun =
+ fun({_Key, {_Der,ErlCertCandidate}}, Acc) ->
+ verify_crl_issuer(CRL, ErlCertCandidate, Issuer, Acc);
+ (_, Acc) ->
+ Acc
+ end,
+
+ try ssl_pkix_db:foldl(IsIssuerFun, issuer_not_found, Db) of
+ issuer_not_found ->
+ {error, issuer_not_found}
+ catch
+ {ok, IssuerCert} ->
+ IssuerCert
+ end.
+
+
+verify_crl_issuer(CRL, ErlCertCandidate, Issuer, NotIssuer) ->
+ TBSCert = ErlCertCandidate#'OTPCertificate'.tbsCertificate,
+ case public_key:pkix_normalize_name(TBSCert#'OTPTBSCertificate'.subject) of
+ Issuer ->
+ case public_key:pkix_crl_verify(CRL, ErlCertCandidate) of
+ true ->
+ throw({ok, ErlCertCandidate});
+ false ->
+ NotIssuer
+ end;
+ _ ->
+ NotIssuer
+ end.
diff --git a/lib/ssl/src/ssl_crl_cache.erl b/lib/ssl/src/ssl_crl_cache.erl
new file mode 100644
index 0000000000..b9d6a61c3b
--- /dev/null
+++ b/lib/ssl/src/ssl_crl_cache.erl
@@ -0,0 +1,179 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2015-2015. 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%
+
+%----------------------------------------------------------------------
+%% Purpose: Simple default CRL cache
+%%----------------------------------------------------------------------
+
+-module(ssl_crl_cache).
+
+-include("ssl_internal.hrl").
+-include_lib("public_key/include/public_key.hrl").
+
+-behaviour(ssl_crl_cache_api).
+
+-export([lookup/2, select/2, fresh_crl/2]).
+-export([insert/1, insert/2, delete/1]).
+
+%%====================================================================
+%% Cache callback API
+%%====================================================================
+
+lookup(#'DistributionPoint'{distributionPoint = {fullName, Names}},
+ CRLDbInfo) ->
+ get_crls(Names, CRLDbInfo);
+lookup(_,_) ->
+ not_available.
+
+select(Issuer, {{_Cache, Mapping},_}) ->
+ case ssl_pkix_db:lookup(Issuer, Mapping) of
+ undefined ->
+ [];
+ CRLs ->
+ CRLs
+ end.
+
+fresh_crl(#'DistributionPoint'{distributionPoint = {fullName, Names}}, CRL) ->
+ case get_crls(Names, undefined) of
+ not_available ->
+ CRL;
+ [NewCRL] ->
+ NewCRL
+ end.
+
+%%====================================================================
+%% API
+%%====================================================================
+
+insert(CRLs) ->
+ insert(?NO_DIST_POINT, CRLs).
+
+insert(URI, {file, File}) when is_list(URI) ->
+ case file:read_file(File) of
+ {ok, PemBin} ->
+ PemEntries = public_key:pem_decode(PemBin),
+ CRLs = [ CRL || {'CertificateList', CRL, not_encrypted}
+ <- PemEntries],
+ do_insert(URI, CRLs);
+ Error ->
+ Error
+ end;
+insert(URI, {der, CRLs}) ->
+ do_insert(URI, CRLs).
+
+delete({file, File}) ->
+ case file:read_file(File) of
+ {ok, PemBin} ->
+ PemEntries = public_key:pem_decode(PemBin),
+ CRLs = [ CRL || {'CertificateList', CRL, not_encrypted}
+ <- PemEntries],
+ ssl_manager:delete_crls({?NO_DIST_POINT, CRLs});
+ Error ->
+ Error
+ end;
+delete({der, CRLs}) ->
+ ssl_manager:delete_crls({?NO_DIST_POINT, CRLs});
+
+delete(URI) ->
+ case http_uri:parse(URI) of
+ {ok, {http, _, _ , _, Path,_}} ->
+ ssl_manager:delete_crls(string:strip(Path, left, $/));
+ _ ->
+ {error, {only_http_distribution_points_supported, URI}}
+ end.
+
+%%--------------------------------------------------------------------
+%%% Internal functions
+%%--------------------------------------------------------------------
+do_insert(URI, CRLs) ->
+ case http_uri:parse(URI) of
+ {ok, {http, _, _ , _, Path,_}} ->
+ ssl_manager:insert_crls(string:strip(Path, left, $/), CRLs);
+ _ ->
+ {error, {only_http_distribution_points_supported, URI}}
+ end.
+
+get_crls([], _) ->
+ not_available;
+get_crls([{uniformResourceIdentifier, "http"++_ = URL} | Rest],
+ CRLDbInfo) ->
+ case cache_lookup(URL, CRLDbInfo) of
+ [] ->
+ handle_http(URL, Rest, CRLDbInfo);
+ CRLs ->
+ CRLs
+ end;
+get_crls([ _| Rest], CRLDbInfo) ->
+ %% unsupported CRL location
+ get_crls(Rest, CRLDbInfo).
+
+http_lookup(URL, Rest, CRLDbInfo, Timeout) ->
+ case application:ensure_started(inets) of
+ ok ->
+ http_get(URL, Rest, CRLDbInfo, Timeout);
+ _ ->
+ get_crls(Rest, CRLDbInfo)
+ end.
+
+http_get(URL, Rest, CRLDbInfo, Timeout) ->
+ case httpc:request(get, {URL, [{"connection", "close"}]},
+ [{timeout, Timeout}], [{body_format, binary}]) of
+ {ok, {_Status, _Headers, Body}} ->
+ case Body of
+ <<"-----BEGIN", _/binary>> ->
+ Pem = public_key:pem_decode(Body),
+ lists:filtermap(fun({'CertificateList',
+ CRL, not_encrypted}) ->
+ {true, CRL};
+ (_) ->
+ false
+ end, Pem);
+ _ ->
+ try public_key:der_decode('CertificateList', Body) of
+ _ ->
+ [Body]
+ catch
+ _:_ ->
+ get_crls(Rest, CRLDbInfo)
+ end
+ end;
+ {error, _Reason} ->
+ get_crls(Rest, CRLDbInfo)
+ end.
+
+cache_lookup(_, undefined) ->
+ [];
+cache_lookup(URL, {{Cache, _}, _}) ->
+ {ok, {_, _, _ , _, Path,_}} = http_uri:parse(URL),
+ case ssl_pkix_db:lookup(string:strip(Path, left, $/), Cache) of
+ undefined ->
+ [];
+ CRLs ->
+ CRLs
+ end.
+
+handle_http(URI, Rest, {_, [{http, Timeout}]} = CRLDbInfo) ->
+ CRLs = http_lookup(URI, Rest, CRLDbInfo, Timeout),
+ %% Uncomment to improve performance, but need to
+ %% implement cache limit and or cleaning to prevent
+ %% DoS attack possibilities
+ %%insert(URI, {der, CRLs}),
+ CRLs;
+handle_http(_, Rest, CRLDbInfo) ->
+ get_crls(Rest, CRLDbInfo).
+
diff --git a/lib/ssl/src/ssl_crl_cache_api.erl b/lib/ssl/src/ssl_crl_cache_api.erl
new file mode 100644
index 0000000000..0915ba12e5
--- /dev/null
+++ b/lib/ssl/src/ssl_crl_cache_api.erl
@@ -0,0 +1,30 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2015-2015. 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_crl_cache_api).
+
+-include_lib("public_key/include/public_key.hrl").
+
+-type db_handle() :: term().
+
+-callback lookup(#'DistributionPoint'{}, db_handle()) -> not_available | [public_key:der_encode()].
+-callback select(term(), db_handle()) -> [public_key:der_encode()].
+-callback fresh_crl(#'DistributionPoint'{}, public_key:der_encode()) -> public_key:der_encode().
diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl
index 88ccb94e0b..5c5f386c6f 100644
--- a/lib/ssl/src/ssl_handshake.erl
+++ b/lib/ssl/src/ssl_handshake.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2013-2014. All Rights Reserved.
+%% Copyright Ericsson AB 2013-2015. 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
@@ -49,7 +49,7 @@
finished/5, next_protocol/1]).
%% Handle handshake messages
--export([certify/8, client_certificate_verify/6, certificate_verify/6, verify_signature/5,
+-export([certify/10, client_certificate_verify/6, certificate_verify/6, verify_signature/5,
master_secret/5, server_key_exchange_hash/2, verify_connection/6,
init_handshake_history/0, update_handshake_history/2, verify_server_key/5
]).
@@ -149,7 +149,7 @@ client_hello_extensions(Host, Version, CipherSuites, SslOpts, ConnectionStates,
certificate(OwnCert, CertDbHandle, CertDbRef, client) ->
Chain =
case ssl_certificate:certificate_chain(OwnCert, CertDbHandle, CertDbRef) of
- {ok, CertChain} ->
+ {ok, _, CertChain} ->
CertChain;
{error, _} ->
%% If no suitable certificate is available, the client
@@ -161,7 +161,7 @@ certificate(OwnCert, CertDbHandle, CertDbRef, client) ->
certificate(OwnCert, CertDbHandle, CertDbRef, server) ->
case ssl_certificate:certificate_chain(OwnCert, CertDbHandle, CertDbRef) of
- {ok, Chain} ->
+ {ok, _, Chain} ->
#certificate{asn1_certificates = Chain};
{error, _} ->
?ALERT_REC(?FATAL, ?INTERNAL_ERROR)
@@ -383,49 +383,24 @@ verify_signature(_Version, Hash, {HashAlgo, ecdsa}, Signature,
%%--------------------------------------------------------------------
-spec certify(#certificate{}, db_handle(), certdb_ref(), integer() | nolimit,
- verify_peer | verify_none, {fun(), term}, fun(),
+ verify_peer | verify_none, {fun(), term}, fun(), term(), term(),
client | server) -> {der_cert(), public_key_info()} | #alert{}.
%%
%% Description: Handles a certificate handshake message
%%--------------------------------------------------------------------
certify(#certificate{asn1_certificates = ASN1Certs}, CertDbHandle, CertDbRef,
- MaxPathLen, _Verify, VerifyFunAndState, PartialChain, Role) ->
+ MaxPathLen, _Verify, ValidationFunAndState0, PartialChain, CRLCheck, CRLDbHandle, Role) ->
[PeerCert | _] = ASN1Certs,
-
- ValidationFunAndState =
- case VerifyFunAndState of
- undefined ->
- {fun(OtpCert, ExtensionOrVerifyResult, SslState) ->
- ssl_certificate:validate_extension(OtpCert,
- ExtensionOrVerifyResult, SslState)
- end, Role};
- {Fun, UserState0} ->
- {fun(OtpCert, {extension, _} = Extension, {SslState, UserState}) ->
- case ssl_certificate:validate_extension(OtpCert,
- Extension,
- SslState) of
- {valid, NewSslState} ->
- {valid, {NewSslState, UserState}};
- {fail, Reason} ->
- apply_user_fun(Fun, OtpCert, Reason, UserState,
- SslState);
- {unknown, _} ->
- apply_user_fun(Fun, OtpCert,
- Extension, UserState, SslState)
- end;
- (OtpCert, VerifyResult, {SslState, UserState}) ->
- apply_user_fun(Fun, OtpCert, VerifyResult, UserState,
- SslState)
- end, {Role, UserState0}}
- end,
+
+ ValidationFunAndState = validation_fun_and_state(ValidationFunAndState0, Role,
+ CertDbHandle, CertDbRef, CRLCheck, CRLDbHandle),
try
- {TrustedErlCert, CertPath} =
+ {TrustedCert, CertPath} =
ssl_certificate:trusted_cert_and_path(ASN1Certs, CertDbHandle, CertDbRef, PartialChain),
- case public_key:pkix_path_validation(TrustedErlCert,
- CertPath,
- [{max_path_length,
- MaxPathLen},
+ case public_key:pkix_path_validation(TrustedCert,
+ CertPath,
+ [{max_path_length, MaxPathLen},
{verify_fun, ValidationFunAndState}]) of
{ok, {PublicKeyInfo,_}} ->
{PeerCert, PublicKeyInfo};
@@ -1374,15 +1349,66 @@ sni1(Hostname) ->
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
+validation_fun_and_state({Fun, UserState0}, Role, CertDbHandle, CertDbRef, CRLCheck, CRLDbHandle) ->
+ {fun(OtpCert, {extension, _} = Extension, {SslState, UserState}) ->
+ case ssl_certificate:validate(OtpCert,
+ Extension,
+ SslState) of
+ {valid, NewSslState} ->
+ {valid, {NewSslState, UserState}};
+ {fail, Reason} ->
+ apply_user_fun(Fun, OtpCert, Reason, UserState,
+ SslState);
+ {unknown, _} ->
+ apply_user_fun(Fun, OtpCert,
+ Extension, UserState, SslState)
+ end;
+ (OtpCert, VerifyResult, {SslState, UserState}) ->
+ apply_user_fun(Fun, OtpCert, VerifyResult, UserState,
+ SslState)
+ end, {{Role, CertDbHandle, CertDbRef, CRLCheck, CRLDbHandle}, UserState0}};
+validation_fun_and_state(undefined, Role, CertDbHandle, CertDbRef, CRLCheck, CRLDbHandle) ->
+ {fun(OtpCert, {extension, _} = Extension, SslState) ->
+ ssl_certificate:validate(OtpCert,
+ Extension,
+ SslState);
+ (OtpCert, VerifyResult, SslState) when (VerifyResult == valid) or (VerifyResult == valid_peer) ->
+ case crl_check(OtpCert, CRLCheck, CertDbHandle, CertDbRef, CRLDbHandle, VerifyResult) of
+ valid ->
+ {VerifyResult, SslState};
+ Reason ->
+ {fail, Reason}
+ end;
+ (OtpCert, VerifyResult, SslState) ->
+ ssl_certificate:validate(OtpCert,
+ VerifyResult,
+ SslState)
+ end, {Role, CertDbHandle, CertDbRef, CRLCheck, CRLDbHandle}}.
+
+apply_user_fun(Fun, OtpCert, VerifyResult, UserState0,
+ {_, CertDbHandle, CertDbRef, CRLCheck, CRLDbHandle} = SslState) when
+ (VerifyResult == valid) or (VerifyResult == valid_peer) ->
+ case Fun(OtpCert, VerifyResult, UserState0) of
+ {Valid, UserState} when (Valid == valid) or (Valid == valid_peer) ->
+ case crl_check(OtpCert, CRLCheck, CertDbHandle, CertDbRef, CRLDbHandle, VerifyResult) of
+ valid ->
+ {Valid, {SslState, UserState}};
+ Result ->
+ apply_user_fun(Fun, OtpCert, Result, UserState, SslState)
+ end;
+ {fail, _} = Fail ->
+ Fail
+ end;
apply_user_fun(Fun, OtpCert, ExtensionOrError, UserState0, SslState) ->
case Fun(OtpCert, ExtensionOrError, UserState0) of
- {valid, UserState} ->
- {valid, {SslState, UserState}};
+ {Valid, UserState} when (Valid == valid) or (Valid == valid_peer)->
+ {Valid, {SslState, UserState}};
{fail, _} = Fail ->
Fail;
{unknown, UserState} ->
{unknown, {SslState, UserState}}
end.
+
path_validation_alert({bad_cert, cert_expired}) ->
?ALERT_REC(?FATAL, ?CERTIFICATE_EXPIRED);
path_validation_alert({bad_cert, invalid_issuer}) ->
@@ -1393,8 +1419,10 @@ path_validation_alert({bad_cert, name_not_permitted}) ->
?ALERT_REC(?FATAL, ?BAD_CERTIFICATE);
path_validation_alert({bad_cert, unknown_critical_extension}) ->
?ALERT_REC(?FATAL, ?UNSUPPORTED_CERTIFICATE);
-path_validation_alert({bad_cert, cert_revoked}) ->
+path_validation_alert({bad_cert, {revoked, _}}) ->
?ALERT_REC(?FATAL, ?CERTIFICATE_REVOKED);
+path_validation_alert({bad_cert, revocation_status_undetermined}) ->
+ ?ALERT_REC(?FATAL, ?BAD_CERTIFICATE);
path_validation_alert({bad_cert, selfsigned_peer}) ->
?ALERT_REC(?FATAL, ?BAD_CERTIFICATE);
path_validation_alert({bad_cert, unknown_ca}) ->
@@ -1435,6 +1463,7 @@ calc_finished({3, N}, Role, PrfAlgo, MasterSecret, Handshake) ->
master_secret(_RecordCB, Version, MasterSecret,
#security_parameters{
+ bulk_cipher_algorithm = BCA,
client_random = ClientRandom,
server_random = ServerRandom,
hash_size = HashSize,
@@ -1453,8 +1482,8 @@ master_secret(_RecordCB, Version, MasterSecret,
ssl_record:set_mac_secret(ClientWriteMacSecret, ServerWriteMacSecret,
Role, ConnStates1),
- ClientCipherState = #cipher_state{iv = ClientIV, key = ClientWriteKey},
- ServerCipherState = #cipher_state{iv = ServerIV, key = ServerWriteKey},
+ ClientCipherState = ssl_cipher:cipher_init(BCA, ClientIV, ClientWriteKey),
+ ServerCipherState = ssl_cipher:cipher_init(BCA, ServerIV, ServerWriteKey),
{MasterSecret,
ssl_record:set_pending_cipher_state(ConnStates2, ClientCipherState,
ServerCipherState, Role)}.
@@ -1953,3 +1982,70 @@ handle_psk_identity(_PSKIdentity, LookupFun)
error;
handle_psk_identity(PSKIdentity, {Fun, UserState}) ->
Fun(psk, PSKIdentity, UserState).
+
+crl_check(_, false, _,_,_, _) ->
+ valid;
+crl_check(_, peer, _, _,_, valid) -> %% Do not check CAs with this option.
+ valid;
+crl_check(OtpCert, Check, CertDbHandle, CertDbRef, {Callback, CRLDbHandle}, _) ->
+ Options = [{issuer_fun, {fun(_DP, CRL, Issuer, DBInfo) ->
+ ssl_crl:trusted_cert_and_path(CRL, Issuer, DBInfo)
+ end, {CertDbHandle, CertDbRef}}},
+ {update_crl, fun(DP, CRL) -> Callback:fresh_crl(DP, CRL) end}
+ ],
+ case dps_and_crls(OtpCert, Callback, CRLDbHandle, ext) of
+ no_dps ->
+ case dps_and_crls(OtpCert, Callback, CRLDbHandle, same_issuer) of
+ [] ->
+ valid; %% No relevant CRL existed
+ DpsAndCRls ->
+ crl_check_same_issuer(OtpCert, Check, DpsAndCRls, Options)
+ end;
+ DpsAndCRLs -> %% This DP list may be empty if relevant CRLs existed
+ %% but could not be retrived, will result in {bad_cert, revocation_status_undetermined}
+ case public_key:pkix_crls_validate(OtpCert, DpsAndCRLs, Options) of
+ {bad_cert, revocation_status_undetermined} ->
+ crl_check_same_issuer(OtpCert, Check, dps_and_crls(OtpCert, Callback,
+ CRLDbHandle, same_issuer), Options);
+ Other ->
+ Other
+ end
+ end.
+
+crl_check_same_issuer(OtpCert, best_effort, Dps, Options) ->
+ case public_key:pkix_crls_validate(OtpCert, Dps, Options) of
+ {bad_cert, revocation_status_undetermined} ->
+ valid;
+ Other ->
+ Other
+ end;
+crl_check_same_issuer(OtpCert, _, Dps, Options) ->
+ public_key:pkix_crls_validate(OtpCert, Dps, Options).
+
+dps_and_crls(OtpCert, Callback, CRLDbHandle, ext) ->
+ case public_key:pkix_dist_points(OtpCert) of
+ [] ->
+ no_dps;
+ DistPoints ->
+ distpoints_lookup(DistPoints, Callback, CRLDbHandle)
+ end;
+
+dps_and_crls(OtpCert, Callback, CRLDbHandle, same_issuer) ->
+ DP = #'DistributionPoint'{distributionPoint = {fullName, GenNames}} =
+ public_key:pkix_dist_point(OtpCert),
+ CRLs = lists:flatmap(fun({directoryName, Issuer}) ->
+ Callback:select(Issuer, CRLDbHandle);
+ (_) ->
+ []
+ end, GenNames),
+ [{DP, {CRL, public_key:der_decode('CertificateList', CRL)}} || CRL <- CRLs].
+
+distpoints_lookup([], _, _) ->
+ [];
+distpoints_lookup([DistPoint | Rest], Callback, CRLDbHandle) ->
+ case Callback:lookup(DistPoint, CRLDbHandle) of
+ not_available ->
+ distpoints_lookup(Rest, Callback, CRLDbHandle);
+ CRLs ->
+ [{DistPoint, {CRL, public_key:der_decode('CertificateList', CRL)}} || CRL <- CRLs]
+ end.
diff --git a/lib/ssl/src/ssl_internal.hrl b/lib/ssl/src/ssl_internal.hrl
index 88105cac5a..8df79f9e8c 100644
--- a/lib/ssl/src/ssl_internal.hrl
+++ b/lib/ssl/src/ssl_internal.hrl
@@ -61,14 +61,19 @@
-define(CDR_HDR_SIZE, 12).
-define(DEFAULT_TIMEOUT, 5000).
+-define(NO_DIST_POINT, "http://dummy/no_distribution_point").
+-define(NO_DIST_POINT_PATH, "dummy/no_distribution_point").
%% Common enumerate values in for SSL-protocols
-define(NULL, 0).
-define(TRUE, 0).
-define(FALSE, 1).
--define(ALL_SUPPORTED_VERSIONS, ['tlsv1.2', 'tlsv1.1', tlsv1, sslv3]).
--define(MIN_SUPPORTED_VERSIONS, ['tlsv1.1', tlsv1, sslv3]).
+%% sslv3 is considered insecure due to lack of padding check (Poodle attack)
+%% Keep as interop with legacy software but do not support as default
+-define(ALL_AVAILABLE_VERSIONS, ['tlsv1.2', 'tlsv1.1', tlsv1, sslv3]).
+-define(ALL_SUPPORTED_VERSIONS, ['tlsv1.2', 'tlsv1.1', tlsv1]).
+-define(MIN_SUPPORTED_VERSIONS, ['tlsv1.1', tlsv1]).
-define(ALL_DATAGRAM_SUPPORTED_VERSIONS, ['dtlsv1.2', dtlsv1]).
-define(MIN_DATAGRAM_SUPPORTED_VERSIONS, ['dtlsv1.2', dtlsv1]).
@@ -119,7 +124,9 @@
%% the client?
honor_cipher_order = false,
padding_check = true,
- fallback = false
+ fallback = false,
+ crl_check,
+ crl_cache
}).
-record(socket_options,
diff --git a/lib/ssl/src/ssl_manager.erl b/lib/ssl/src/ssl_manager.erl
index c4f1f7f193..396013825e 100644
--- a/lib/ssl/src/ssl_manager.erl
+++ b/lib/ssl/src/ssl_manager.erl
@@ -26,10 +26,11 @@
%% Internal application API
-export([start_link/1, start_link_dist/1,
- connection_init/2, cache_pem_file/2,
+ connection_init/3, cache_pem_file/2,
lookup_trusted_cert/4,
new_session_id/1, clean_cert_db/2,
register_session/2, register_session/3, invalidate_session/2,
+ insert_crls/2, insert_crls/3, delete_crls/1, delete_crls/2,
invalidate_session/3, invalidate_pem/1, clear_pem_cache/0, manager_name/1]).
% Spawn export
@@ -44,7 +45,8 @@
-include_lib("kernel/include/file.hrl").
-record(state, {
- session_cache,
+ session_cache_client,
+ session_cache_server,
session_cache_cb,
session_lifetime,
certificate_db,
@@ -99,19 +101,21 @@ start_link_dist(Opts) ->
gen_server:start_link({local, DistMangerName}, ?MODULE, [DistMangerName, Opts], []).
%%--------------------------------------------------------------------
--spec connection_init(binary()| {der, list()}, client | server) ->
- {ok, certdb_ref(), db_handle(), db_handle(), db_handle(), db_handle()}.
+-spec connection_init(binary()| {der, list()}, client | server,
+ {Cb :: atom(), Handle:: term()}) ->
+ {ok, certdb_ref(), db_handle(), db_handle(),
+ db_handle(), db_handle(), CRLInfo::term()}.
%%
%% Description: Do necessary initializations for a new connection.
%%--------------------------------------------------------------------
-connection_init({der, _} = Trustedcerts, Role) ->
- call({connection_init, Trustedcerts, Role});
+connection_init({der, _} = Trustedcerts, Role, CRLCache) ->
+ call({connection_init, Trustedcerts, Role, CRLCache});
-connection_init(<<>> = Trustedcerts, Role) ->
- call({connection_init, Trustedcerts, Role});
+connection_init(<<>> = Trustedcerts, Role, CRLCache) ->
+ call({connection_init, Trustedcerts, Role, CRLCache});
-connection_init(Trustedcerts, Role) ->
- call({connection_init, Trustedcerts, Role}).
+connection_init(Trustedcerts, Role, CRLCache) ->
+ call({connection_init, Trustedcerts, Role, CRLCache}).
%%--------------------------------------------------------------------
-spec cache_pem_file(binary(), term()) -> {ok, term()} | {error, reason()}.
@@ -123,7 +127,7 @@ cache_pem_file(File, DbHandle) ->
[{Content,_}] ->
{ok, Content};
[Content] ->
- {ok, Content};
+ {ok, Content};
undefined ->
call({cache_pem, File})
end.
@@ -192,11 +196,28 @@ invalidate_session(Host, Port, Session) ->
invalidate_session(Port, Session) ->
cast({invalidate_session, Port, Session}).
-
-spec invalidate_pem(File::binary()) -> ok.
invalidate_pem(File) ->
cast({invalidate_pem, File}).
+insert_crls(Path, CRLs)->
+ insert_crls(Path, CRLs, normal).
+insert_crls(?NO_DIST_POINT_PATH = Path, CRLs, ManagerType)->
+ put(ssl_manager, manager_name(ManagerType)),
+ cast({insert_crls, Path, CRLs});
+insert_crls(Path, CRLs, ManagerType)->
+ put(ssl_manager, manager_name(ManagerType)),
+ call({insert_crls, Path, CRLs}).
+
+delete_crls(Path)->
+ delete_crls(Path, normal).
+delete_crls(?NO_DIST_POINT_PATH = Path, ManagerType)->
+ put(ssl_manager, manager_name(ManagerType)),
+ cast({delete_crls, Path});
+delete_crls(Path, ManagerType)->
+ put(ssl_manager, manager_name(ManagerType)),
+ call({delete_crls, Path}).
+
%%====================================================================
%% gen_server callbacks
%%====================================================================
@@ -215,13 +236,17 @@ init([Name, Opts]) ->
SessionLifeTime =
proplists:get_value(session_lifetime, Opts, ?'24H_in_sec'),
CertDb = ssl_pkix_db:create(),
- SessionCache = CacheCb:init(proplists:get_value(session_cb_init_args, Opts, [])),
+ ClientSessionCache = CacheCb:init([{role, client} |
+ proplists:get_value(session_cb_init_args, Opts, [])]),
+ ServerSessionCache = CacheCb:init([{role, server} |
+ proplists:get_value(session_cb_init_args, Opts, [])]),
Timer = erlang:send_after(SessionLifeTime * 1000 + 5000,
self(), validate_sessions),
Interval = pem_check_interval(),
erlang:send_after(Interval, self(), clear_pem_cache),
{ok, #state{certificate_db = CertDb,
- session_cache = SessionCache,
+ session_cache_client = ClientSessionCache,
+ session_cache_server = ServerSessionCache,
session_cache_cb = CacheCb,
session_lifetime = SessionLifeTime,
session_validation_timer = Timer,
@@ -240,32 +265,38 @@ init([Name, Opts]) ->
%%
%% Description: Handling call messages
%%--------------------------------------------------------------------
-handle_call({{connection_init, <<>>, _Role}, _Pid}, _From,
- #state{certificate_db = [CertDb, FileRefDb, PemChace],
- session_cache = Cache} = State) ->
- Result = {ok, make_ref(),CertDb, FileRefDb, PemChace, Cache},
- {reply, Result, State};
-
-handle_call({{connection_init, Trustedcerts, _Role}, Pid}, _From,
- #state{certificate_db = [CertDb, FileRefDb, PemChace] = Db,
- session_cache = Cache} = State) ->
- Result =
- try
- {ok, Ref} = ssl_pkix_db:add_trusted_certs(Pid, Trustedcerts, Db),
- {ok, Ref, CertDb, FileRefDb, PemChace, Cache}
- catch
- _:Reason ->
- {error, Reason}
- end,
- {reply, Result, State};
-
-handle_call({{new_session_id,Port}, _},
+handle_call({{connection_init, <<>>, Role, {CRLCb, UserCRLDb}}, _Pid}, _From,
+ #state{certificate_db = [CertDb, FileRefDb, PemChace | _] = Db} = State) ->
+ Ref = make_ref(),
+ Result = {ok, Ref, CertDb, FileRefDb, PemChace, session_cache(Role, State), {CRLCb, crl_db_info(Db, UserCRLDb)}},
+ {reply, Result, State#state{certificate_db = Db}};
+
+handle_call({{connection_init, Trustedcerts, Role, {CRLCb, UserCRLDb}}, Pid}, _From,
+ #state{certificate_db = [CertDb, FileRefDb, PemChace | _] = Db} = State) ->
+ case add_trusted_certs(Pid, Trustedcerts, Db) of
+ {ok, Ref} ->
+ {reply, {ok, Ref, CertDb, FileRefDb, PemChace, session_cache(Role, State),
+ {CRLCb, crl_db_info(Db, UserCRLDb)}}, State};
+ {error, _} = Error ->
+ {reply, Error, State}
+ end;
+
+handle_call({{insert_crls, Path, CRLs}, _}, _From,
+ #state{certificate_db = Db} = State) ->
+ ssl_pkix_db:add_crls(Db, Path, CRLs),
+ {reply, ok, State};
+
+handle_call({{delete_crls, CRLsOrPath}, _}, _From,
+ #state{certificate_db = Db} = State) ->
+ ssl_pkix_db:remove_crls(Db, CRLsOrPath),
+ {reply, ok, State};
+
+handle_call({{new_session_id, Port}, _},
_, #state{session_cache_cb = CacheCb,
- session_cache = Cache} = State) ->
+ session_cache_server = Cache} = State) ->
Id = new_id(Port, ?GEN_UNIQUE_ID_MAX_TRIES, Cache, CacheCb),
{reply, Id, State};
-
handle_call({{cache_pem,File}, _Pid}, _,
#state{certificate_db = Db} = State) ->
try ssl_pkix_db:cache_pem_file(File, Db) of
@@ -275,7 +306,7 @@ handle_call({{cache_pem,File}, _Pid}, _,
_:Reason ->
{reply, {error, Reason}, State}
end;
-handle_call({unconditionally_clear_pem_cache, _},_, #state{certificate_db = [_,_,PemChace]} = State) ->
+handle_call({unconditionally_clear_pem_cache, _},_, #state{certificate_db = [_,_,PemChace | _]} = State) ->
ssl_pkix_db:clear(PemChace),
{reply, ok, State}.
@@ -288,16 +319,22 @@ handle_call({unconditionally_clear_pem_cache, _},_, #state{certificate_db = [_,_
%% Description: Handling cast messages
%%--------------------------------------------------------------------
handle_cast({register_session, Host, Port, Session},
- #state{session_cache = Cache,
+ #state{session_cache_client = Cache,
session_cache_cb = CacheCb} = State) ->
TimeStamp = calendar:datetime_to_gregorian_seconds({date(), time()}),
NewSession = Session#session{time_stamp = TimeStamp},
- CacheCb:update(Cache, {{Host, Port},
- NewSession#session.session_id}, NewSession),
+
+ case CacheCb:select_session(Cache, {Host, Port}) of
+ no_session ->
+ CacheCb:update(Cache, {{Host, Port},
+ NewSession#session.session_id}, NewSession);
+ Sessions ->
+ register_unique_session(Sessions, NewSession, CacheCb, Cache, {Host, Port})
+ end,
{noreply, State};
handle_cast({register_session, Port, Session},
- #state{session_cache = Cache,
+ #state{session_cache_server = Cache,
session_cache_cb = CacheCb} = State) ->
TimeStamp = calendar:datetime_to_gregorian_seconds({date(), time()}),
NewSession = Session#session{time_stamp = TimeStamp},
@@ -306,17 +343,28 @@ handle_cast({register_session, Port, Session},
handle_cast({invalidate_session, Host, Port,
#session{session_id = ID} = Session},
- #state{session_cache = Cache,
+ #state{session_cache_client = Cache,
session_cache_cb = CacheCb} = State) ->
invalidate_session(Cache, CacheCb, {{Host, Port}, ID}, Session, State);
handle_cast({invalidate_session, Port, #session{session_id = ID} = Session},
- #state{session_cache = Cache,
+ #state{session_cache_server = Cache,
session_cache_cb = CacheCb} = State) ->
invalidate_session(Cache, CacheCb, {Port, ID}, Session, State);
+
+handle_cast({insert_crls, Path, CRLs},
+ #state{certificate_db = Db} = State) ->
+ ssl_pkix_db:add_crls(Db, Path, CRLs),
+ {noreply, State};
+
+handle_cast({delete_crls, CRLsOrPath},
+ #state{certificate_db = Db} = State) ->
+ ssl_pkix_db:remove_crls(Db, CRLsOrPath),
+ {noreply, State};
+
handle_cast({invalidate_pem, File},
- #state{certificate_db = [_, _, PemCache]} = State) ->
+ #state{certificate_db = [_, _, PemCache | _]} = State) ->
ssl_pkix_db:remove(File, PemCache),
{noreply, State}.
@@ -329,21 +377,23 @@ handle_cast({invalidate_pem, File},
%% Description: Handling all non call/cast messages
%%-------------------------------------------------------------------
handle_info(validate_sessions, #state{session_cache_cb = CacheCb,
- session_cache = Cache,
+ session_cache_client = ClientCache,
+ session_cache_server = ServerCache,
session_lifetime = LifeTime
} = State) ->
Timer = erlang:send_after(?SESSION_VALIDATION_INTERVAL,
self(), validate_sessions),
- start_session_validator(Cache, CacheCb, LifeTime),
+ start_session_validator(ClientCache, CacheCb, LifeTime),
+ start_session_validator(ServerCache, CacheCb, LifeTime),
{noreply, State#state{session_validation_timer = Timer}};
-handle_info({delayed_clean_session, Key}, #state{session_cache = Cache,
- session_cache_cb = CacheCb
- } = State) ->
+
+handle_info({delayed_clean_session, Key, Cache}, #state{session_cache_cb = CacheCb
+ } = State) ->
CacheCb:delete(Cache, Key),
{noreply, State};
-handle_info(clear_pem_cache, #state{certificate_db = [_,_,PemChace],
+handle_info(clear_pem_cache, #state{certificate_db = [_,_,PemChace | _],
clear_pem_cache = Interval,
last_pem_check = CheckPoint} = State) ->
NewCheckPoint = os:timestamp(),
@@ -351,9 +401,8 @@ handle_info(clear_pem_cache, #state{certificate_db = [_,_,PemChace],
erlang:send_after(Interval, self(), clear_pem_cache),
{noreply, State#state{last_pem_check = NewCheckPoint}};
-
handle_info({clean_cert_db, Ref, File},
- #state{certificate_db = [CertDb,RefDb, PemCache]} = State) ->
+ #state{certificate_db = [CertDb,RefDb, PemCache | _]} = State) ->
case ssl_pkix_db:lookup(Ref, RefDb) of
undefined -> %% Alredy cleaned
@@ -380,12 +429,14 @@ handle_info(_Info, State) ->
%% The return value is ignored.
%%--------------------------------------------------------------------
terminate(_Reason, #state{certificate_db = Db,
- session_cache = SessionCache,
+ session_cache_client = ClientSessionCache,
+ session_cache_server = ServerSessionCache,
session_cache_cb = CacheCb,
session_validation_timer = Timer}) ->
erlang:cancel_timer(Timer),
ssl_pkix_db:remove(Db),
- CacheCb:terminate(SessionCache),
+ catch CacheCb:terminate(ClientSessionCache),
+ catch CacheCb:terminate(ServerSessionCache),
ok.
%%--------------------------------------------------------------------
@@ -458,7 +509,7 @@ invalidate_session(Cache, CacheCb, Key, Session, #state{last_delay_timer = LastT
%% up the session data but new connections should not get to use this session.
CacheCb:update(Cache, Key, Session#session{is_resumable = false}),
TRef =
- erlang:send_after(delay_time(), self(), {delayed_clean_session, Key}),
+ erlang:send_after(delay_time(), self(), {delayed_clean_session, Key, Cache}),
{noreply, State#state{last_delay_timer = last_delay_timer(Key, TRef, LastTimer)}}
end.
@@ -507,6 +558,37 @@ clean_cert_db(Ref, CertDb, RefDb, PemCache, File) ->
ok
end.
+%% Do not let dumb clients create a gigantic session table
+%% for itself creating big delays at connection time.
+register_unique_session(Sessions, Session, CacheCb, Cache, PartialKey) ->
+ case exists_equivalent(Session , Sessions) of
+ true ->
+ ok;
+ false ->
+ CacheCb:update(Cache, {PartialKey,
+ Session#session.session_id}, Session)
+ end.
+
+exists_equivalent(_, []) ->
+ false;
+exists_equivalent(#session{
+ peer_certificate = PeerCert,
+ own_certificate = OwnCert,
+ compression_method = Compress,
+ cipher_suite = CipherSuite,
+ srp_username = SRP,
+ ecc = ECC} ,
+ [#session{
+ peer_certificate = PeerCert,
+ own_certificate = OwnCert,
+ compression_method = Compress,
+ cipher_suite = CipherSuite,
+ srp_username = SRP,
+ ecc = ECC} | _]) ->
+ true;
+exists_equivalent(Session, [ _ | Rest]) ->
+ exists_equivalent(Session, Rest).
+
start_pem_cache_validator(PemCache, CheckPoint) ->
spawn_link(?MODULE, init_pem_cache_validator,
[[get(ssl_manager), PemCache, CheckPoint]]).
@@ -542,3 +624,21 @@ is_before_checkpoint(Time, CheckPoint) ->
calendar:datetime_to_gregorian_seconds(calendar:now_to_datetime(CheckPoint)) -
calendar:datetime_to_gregorian_seconds(Time) > 0.
+add_trusted_certs(Pid, Trustedcerts, Db) ->
+ try
+ ssl_pkix_db:add_trusted_certs(Pid, Trustedcerts, Db)
+ catch
+ _:Reason ->
+ {error, Reason}
+ end.
+
+session_cache(client, #state{session_cache_client = Cache}) ->
+ Cache;
+session_cache(server, #state{session_cache_server = Cache}) ->
+ Cache.
+
+crl_db_info([_,_,_,Local], {internal, Info}) ->
+ {Local, Info};
+crl_db_info(_, UserCRLDb) ->
+ UserCRLDb.
+
diff --git a/lib/ssl/src/ssl_pkix_db.erl b/lib/ssl/src/ssl_pkix_db.erl
index 8531445ba4..d7b7e3eae3 100644
--- a/lib/ssl/src/ssl_pkix_db.erl
+++ b/lib/ssl/src/ssl_pkix_db.erl
@@ -27,9 +27,9 @@
-include_lib("public_key/include/public_key.hrl").
-include_lib("kernel/include/file.hrl").
--export([create/0, remove/1, add_trusted_certs/3,
+-export([create/0, add_crls/3, remove_crls/2, remove/1, add_trusted_certs/3,
remove_trusted_certs/2, insert/3, remove/2, clear/1, db_size/1,
- ref_count/3, lookup_trusted_cert/4, foldl/3,
+ ref_count/3, lookup_trusted_cert/4, foldl/3, select_cert_by_issuer/2,
lookup_cached_pem/2, cache_pem_file/2, cache_pem_file/3,
lookup/2]).
@@ -51,16 +51,24 @@ create() ->
ets:new(ssl_otp_cacertificate_db, [set, public]),
%% Let connection processes call ref_count/3 directly
ets:new(ssl_otp_ca_file_ref, [set, public]),
- ets:new(ssl_otp_pem_cache, [set, protected])
+ ets:new(ssl_otp_pem_cache, [set, protected]),
+ %% Default cache
+ {ets:new(ssl_otp_crl_cache, [set, protected]),
+ ets:new(ssl_otp_crl_issuer_mapping, [bag, protected])}
].
%%--------------------------------------------------------------------
--spec remove([db_handle()]) -> ok.
+-spec remove([db_handle()]) -> ok.
%%
%% Description: Removes database db
%%--------------------------------------------------------------------
remove(Dbs) ->
- lists:foreach(fun(Db) ->
+ lists:foreach(fun({Db0, Db1}) ->
+ true = ets:delete(Db0),
+ true = ets:delete(Db1);
+ (undefined) ->
+ ok;
+ (Db) ->
true = ets:delete(Db)
end, Dbs).
@@ -81,7 +89,7 @@ lookup_trusted_cert(DbHandle, Ref, SerialNumber, Issuer) ->
{ok, Certs}
end.
-lookup_cached_pem([_, _, PemChache], File) ->
+lookup_cached_pem([_, _, PemChache | _], File) ->
lookup_cached_pem(PemChache, File);
lookup_cached_pem(PemChache, File) ->
lookup(File, PemChache).
@@ -94,12 +102,12 @@ lookup_cached_pem(PemChache, File) ->
%% runtime database. Returns Ref that should be handed to lookup_trusted_cert
%% together with the cert serialnumber and issuer.
%%--------------------------------------------------------------------
-add_trusted_certs(_Pid, {der, DerList}, [CerDb, _,_]) ->
+add_trusted_certs(_Pid, {der, DerList}, [CertDb, _,_ | _]) ->
NewRef = make_ref(),
- add_certs_from_der(DerList, NewRef, CerDb),
+ add_certs_from_der(DerList, NewRef, CertDb),
{ok, NewRef};
-add_trusted_certs(_Pid, File, [CertsDb, RefDb, PemChache] = Db) ->
+add_trusted_certs(_Pid, File, [CertsDb, RefDb, PemChache | _] = Db) ->
case lookup_cached_pem(Db, File) of
[{_Content, Ref}] ->
ref_count(Ref, RefDb, 1),
@@ -118,14 +126,15 @@ add_trusted_certs(_Pid, File, [CertsDb, RefDb, PemChache] = Db) ->
%% Description: Cache file as binary in DB
%%--------------------------------------------------------------------
-spec cache_pem_file(binary(), [db_handle()]) -> {ok, term()}.
-cache_pem_file(File, [_CertsDb, _RefDb, PemChache]) ->
+cache_pem_file(File, [_CertsDb, _RefDb, PemChache | _]) ->
{ok, PemBin} = file:read_file(File),
Content = public_key:pem_decode(PemBin),
insert(File, Content, PemChache),
{ok, Content}.
+
-spec cache_pem_file(reference(), binary(), [db_handle()]) -> {ok, term()}.
-cache_pem_file(Ref, File, [_CertsDb, _RefDb, PemChache]) ->
+cache_pem_file(Ref, File, [_CertsDb, _RefDb, PemChache| _]) ->
{ok, PemBin} = file:read_file(File),
Content = public_key:pem_decode(PemBin),
insert(File, {Content, Ref}, PemChache),
@@ -149,6 +158,15 @@ remove(Key, Db) ->
ok.
%%--------------------------------------------------------------------
+-spec remove(term(), term(), db_handle()) -> ok.
+%%
+%% Description: Removes an element in a <Db>.
+%%--------------------------------------------------------------------
+remove(Key, Data, Db) ->
+ ets:delete_object(Db, {Key, Data}),
+ ok.
+
+%%--------------------------------------------------------------------
-spec lookup(term(), db_handle()) -> [term()] | undefined.
%%
%% Description: Looks up an element in a <Db>.
@@ -175,6 +193,10 @@ lookup(Key, Db) ->
foldl(Fun, Acc0, Cache) ->
ets:foldl(Fun, Acc0, Cache).
+
+select_cert_by_issuer(Cache, Issuer) ->
+ ets:select(Cache, [{{{'_','_', Issuer},{'_', '$1'}},[],['$$']}]).
+
%%--------------------------------------------------------------------
-spec ref_count(term(), db_handle(), integer()) -> integer().
%%
@@ -244,9 +266,39 @@ add_certs(Cert, Ref, CertsDb) ->
error_logger:info_report(Report)
end.
-new_trusted_cert_entry(File, [CertsDb, RefDb, _] = Db) ->
+new_trusted_cert_entry(File, [CertsDb, RefDb, _ | _] = Db) ->
Ref = make_ref(),
update_counter(Ref, 1, RefDb),
{ok, Content} = cache_pem_file(Ref, File, Db),
add_certs_from_pem(Content, Ref, CertsDb),
{ok, Ref}.
+
+add_crls([_,_,_, {_, Mapping} | _], ?NO_DIST_POINT, CRLs) ->
+ [add_crls(CRL, Mapping) || CRL <- CRLs];
+add_crls([_,_,_, {Cache, Mapping} | _], Path, CRLs) ->
+ insert(Path, CRLs, Cache),
+ [add_crls(CRL, Mapping) || CRL <- CRLs].
+
+add_crls(CRL, Mapping) ->
+ insert(crl_issuer(CRL), CRL, Mapping).
+
+remove_crls([_,_,_, {_, Mapping} | _], {?NO_DIST_POINT, CRLs}) ->
+ [rm_crls(CRL, Mapping) || CRL <- CRLs];
+
+remove_crls([_,_,_, {Cache, Mapping} | _], Path) ->
+ case lookup(Path, Cache) of
+ undefined ->
+ ok;
+ CRLs ->
+ remove(Path, Cache),
+ [rm_crls(CRL, Mapping) || CRL <- CRLs]
+ end.
+
+rm_crls(CRL, Mapping) ->
+ remove(crl_issuer(CRL), CRL, Mapping).
+
+crl_issuer(DerCRL) ->
+ CRL = public_key:der_decode('CertificateList', DerCRL),
+ TBSCRL = CRL#'CertificateList'.tbsCertList,
+ TBSCRL#'TBSCertList'.issuer.
+
diff --git a/lib/ssl/src/ssl_record.erl b/lib/ssl/src/ssl_record.erl
index 025a46bf65..a02375a947 100644
--- a/lib/ssl/src/ssl_record.erl
+++ b/lib/ssl/src/ssl_record.erl
@@ -48,7 +48,8 @@
-export([compress/3, uncompress/3, compressions/0]).
%% Payload encryption/decryption
--export([cipher/4, decipher/4, is_correct_mac/2]).
+-export([cipher/4, decipher/4, is_correct_mac/2,
+ cipher_aead/4, decipher_aead/4]).
-export_type([ssl_version/0, ssl_atom_version/0]).
@@ -376,6 +377,23 @@ cipher(Version, Fragment,
{CipherFragment, CipherS1} =
ssl_cipher:cipher(BulkCipherAlgo, CipherS0, MacHash, Fragment, Version),
{CipherFragment, WriteState0#connection_state{cipher_state = CipherS1}}.
+%%--------------------------------------------------------------------
+-spec cipher_aead(ssl_version(), iodata(), #connection_state{}, MacHash::binary()) ->
+ {CipherFragment::binary(), #connection_state{}}.
+%%
+%% Description: Payload encryption
+%%--------------------------------------------------------------------
+cipher_aead(Version, Fragment,
+ #connection_state{cipher_state = CipherS0,
+ sequence_number = SeqNo,
+ security_parameters=
+ #security_parameters{bulk_cipher_algorithm =
+ BulkCipherAlgo}
+ } = WriteState0, AAD) ->
+
+ {CipherFragment, CipherS1} =
+ ssl_cipher:cipher_aead(BulkCipherAlgo, CipherS0, SeqNo, AAD, Fragment, Version),
+ {CipherFragment, WriteState0#connection_state{cipher_state = CipherS1}}.
%%--------------------------------------------------------------------
-spec decipher(ssl_version(), binary(), #connection_state{}, boolean()) -> {binary(), binary(), #connection_state{}} | #alert{}.
@@ -397,6 +415,25 @@ decipher(Version, CipherFragment,
Alert
end.
%%--------------------------------------------------------------------
+-spec decipher_aead(ssl_version(), binary(), #connection_state{}, binary()) -> {binary(), binary(), #connection_state{}} | #alert{}.
+%%
+%% Description: Payload decryption
+%%--------------------------------------------------------------------
+decipher_aead(Version, CipherFragment,
+ #connection_state{sequence_number = SeqNo,
+ security_parameters =
+ #security_parameters{bulk_cipher_algorithm =
+ BulkCipherAlgo},
+ cipher_state = CipherS0
+ } = ReadState, AAD) ->
+ case ssl_cipher:decipher_aead(BulkCipherAlgo, CipherS0, SeqNo, AAD, CipherFragment, Version) of
+ {PlainFragment, CipherS1} ->
+ CS1 = ReadState#connection_state{cipher_state = CipherS1},
+ {PlainFragment, CS1};
+ #alert{} = Alert ->
+ Alert
+ end.
+%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
empty_connection_state(ConnectionEnd) ->
diff --git a/lib/ssl/src/ssl_record.hrl b/lib/ssl/src/ssl_record.hrl
index 6aab35d6da..53b5f2399b 100644
--- a/lib/ssl/src/ssl_record.hrl
+++ b/lib/ssl/src/ssl_record.hrl
@@ -90,11 +90,14 @@
-define('3DES', 4).
-define(DES40, 5).
-define(IDEA, 6).
--define(AES, 7).
+-define(AES_CBC, 7).
+-define(AES_GCM, 8).
+-define(CHACHA20_POLY1305, 9).
%% CipherType
-define(STREAM, 0).
-define(BLOCK, 1).
+-define(AEAD, 2).
%% IsExportable
%-define(TRUE, 0). %% Already defined by ssl_internal.hrl
diff --git a/lib/ssl/src/ssl_session_cache.erl b/lib/ssl/src/ssl_session_cache.erl
index 5c6ee3c54c..b011732f2c 100644
--- a/lib/ssl/src/ssl_session_cache.erl
+++ b/lib/ssl/src/ssl_session_cache.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2014. 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
@@ -31,8 +31,8 @@
%%--------------------------------------------------------------------
%% Description: Return table reference. Called by ssl_manager process.
%%--------------------------------------------------------------------
-init(_) ->
- ets:new(cache_name(), [ordered_set, protected]).
+init(Options) ->
+ ets:new(cache_name(proplists:get_value(role, Options)), [ordered_set, protected]).
%%--------------------------------------------------------------------
%% Description: Handles cache table at termination of ssl manager.
@@ -87,5 +87,5 @@ select_session(Cache, PartialKey) ->
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
-cache_name() ->
- ssl_otp_session_cache.
+cache_name(Name) ->
+ list_to_atom(atom_to_list(Name) ++ "_ssl_otp_session_cache").
diff --git a/lib/ssl/src/ssl_v3.erl b/lib/ssl/src/ssl_v3.erl
index 68f7f5dee2..169b39be32 100644
--- a/lib/ssl/src/ssl_v3.erl
+++ b/lib/ssl/src/ssl_v3.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2014. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2015. 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
@@ -143,9 +143,6 @@ suites() ->
?TLS_DHE_RSA_WITH_AES_128_CBC_SHA,
?TLS_DHE_DSS_WITH_AES_128_CBC_SHA,
?TLS_RSA_WITH_AES_128_CBC_SHA,
- %%?TLS_RSA_WITH_IDEA_CBC_SHA,
- ?TLS_RSA_WITH_RC4_128_SHA,
- ?TLS_RSA_WITH_RC4_128_MD5,
?TLS_RSA_WITH_DES_CBC_SHA
].
diff --git a/lib/ssl/src/tls_handshake.erl b/lib/ssl/src/tls_handshake.erl
index b0b6d5a8e3..548ec4aebe 100644
--- a/lib/ssl/src/tls_handshake.erl
+++ b/lib/ssl/src/tls_handshake.erl
@@ -82,8 +82,7 @@ client_hello(Host, Port, ConnectionStates,
boolean()) ->
{tls_record:tls_version(), session_id(), #connection_states{}, binary() | undefined}|
{tls_record:tls_version(), {resumed | new, #session{}}, #connection_states{},
- [binary()] | undefined,
- [ssl_handshake:oid()] | undefined, [ssl_handshake:oid()] | undefined} |
+ #hello_extensions{}} |
#alert{}.
%%
%% Description: Handles a recieved hello message
diff --git a/lib/ssl/src/tls_record.erl b/lib/ssl/src/tls_record.erl
index 168b2c8fd3..14a49ac7da 100644
--- a/lib/ssl/src/tls_record.erl
+++ b/lib/ssl/src/tls_record.erl
@@ -132,6 +132,23 @@ encode_plain_text(Type, Version, Data,
sequence_number = Seq,
compression_state=CompS0,
security_parameters=
+ #security_parameters{
+ cipher_type = ?AEAD,
+ compression_algorithm=CompAlg}
+ }= WriteState0} = ConnectionStates) ->
+ {Comp, CompS1} = ssl_record:compress(CompAlg, Data, CompS0),
+ WriteState1 = WriteState0#connection_state{compression_state = CompS1},
+ AAD = calc_aad(Type, Version, WriteState1),
+ {CipherFragment, WriteState} = ssl_record:cipher_aead(Version, Comp, WriteState1, AAD),
+ CipherText = encode_tls_cipher_text(Type, Version, CipherFragment),
+ {CipherText, ConnectionStates#connection_states{current_write = WriteState#connection_state{sequence_number = Seq +1}}};
+
+encode_plain_text(Type, Version, Data,
+ #connection_states{current_write =
+ #connection_state{
+ sequence_number = Seq,
+ compression_state=CompS0,
+ security_parameters=
#security_parameters{compression_algorithm=CompAlg}
}= WriteState0} = ConnectionStates) ->
{Comp, CompS1} = ssl_record:compress(CompAlg, Data, CompS0),
@@ -154,14 +171,39 @@ decode_cipher_text(#ssl_tls{type = Type, version = Version,
compression_state = CompressionS0,
sequence_number = Seq,
security_parameters=
- #security_parameters{compression_algorithm = CompressAlg}
+ #security_parameters{
+ cipher_type = ?AEAD,
+ compression_algorithm=CompAlg}
+ } = ReadState0} = ConnnectionStates0, _) ->
+ AAD = calc_aad(Type, Version, ReadState0),
+ case ssl_record:decipher_aead(Version, CipherFragment, ReadState0, AAD) of
+ {PlainFragment, ReadState1} ->
+ {Plain, CompressionS1} = ssl_record:uncompress(CompAlg,
+ PlainFragment, CompressionS0),
+ ConnnectionStates = ConnnectionStates0#connection_states{
+ current_read = ReadState1#connection_state{
+ sequence_number = Seq + 1,
+ compression_state = CompressionS1}},
+ {CipherText#ssl_tls{fragment = Plain}, ConnnectionStates};
+ #alert{} = Alert ->
+ Alert
+ end;
+
+decode_cipher_text(#ssl_tls{type = Type, version = Version,
+ fragment = CipherFragment} = CipherText,
+ #connection_states{current_read =
+ #connection_state{
+ compression_state = CompressionS0,
+ sequence_number = Seq,
+ security_parameters=
+ #security_parameters{compression_algorithm=CompAlg}
} = ReadState0} = ConnnectionStates0, PaddingCheck) ->
case ssl_record:decipher(Version, CipherFragment, ReadState0, PaddingCheck) of
{PlainFragment, Mac, ReadState1} ->
MacHash = calc_mac_hash(Type, Version, PlainFragment, ReadState1),
case ssl_record:is_correct_mac(Mac, MacHash) of
true ->
- {Plain, CompressionS1} = ssl_record:uncompress(CompressAlg,
+ {Plain, CompressionS1} = ssl_record:uncompress(CompAlg,
PlainFragment, CompressionS0),
ConnnectionStates = ConnnectionStates0#connection_states{
current_read = ReadState1#connection_state{
@@ -276,8 +318,17 @@ supported_protocol_versions([]) ->
Vsns;
supported_protocol_versions([_|_] = Vsns) ->
- Vsns.
-
+ case sufficient_tlsv1_2_crypto_support() of
+ true ->
+ Vsns;
+ false ->
+ case Vsns -- ['tlsv1.2'] of
+ [] ->
+ ?MIN_SUPPORTED_VERSIONS;
+ NewVsns ->
+ NewVsns
+ end
+ end.
%%--------------------------------------------------------------------
%%
%% Description: ssl version 2 is not acceptable security risks are too big.
@@ -331,3 +382,7 @@ calc_mac_hash(Type, Version,
mac_hash(Version, SecPars#security_parameters.mac_algorithm,
MacSecret, SeqNo, Type,
Length, PlainFragment).
+
+calc_aad(Type, {MajVer, MinVer},
+ #connection_state{sequence_number = SeqNo}) ->
+ <<SeqNo:64/integer, ?BYTE(Type), ?BYTE(MajVer), ?BYTE(MinVer)>>.
diff --git a/lib/ssl/src/tls_v1.erl b/lib/ssl/src/tls_v1.erl
index 7a5f9c1b38..559fc1d6a8 100644
--- a/lib/ssl/src/tls_v1.erl
+++ b/lib/ssl/src/tls_v1.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2014. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2015. 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
@@ -208,38 +208,55 @@ suites(Minor) when Minor == 1; Minor == 2 ->
?TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA,
?TLS_ECDH_RSA_WITH_AES_128_CBC_SHA,
?TLS_RSA_WITH_AES_128_CBC_SHA,
-
- ?TLS_ECDHE_ECDSA_WITH_RC4_128_SHA,
- ?TLS_ECDHE_RSA_WITH_RC4_128_SHA,
- ?TLS_RSA_WITH_RC4_128_SHA,
- ?TLS_RSA_WITH_RC4_128_MD5,
?TLS_DHE_RSA_WITH_DES_CBC_SHA,
- ?TLS_ECDH_ECDSA_WITH_RC4_128_SHA,
- ?TLS_ECDH_RSA_WITH_RC4_128_SHA,
-
?TLS_RSA_WITH_DES_CBC_SHA
];
suites(3) ->
[
+ ?TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
+ ?TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
+
+ ?TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
+ ?TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
?TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384,
?TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384,
+ ?TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384,
+ ?TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384,
?TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384,
?TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384,
+ ?TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
+ ?TLS_DHE_RSA_WITH_AES_256_GCM_SHA384,
+ ?TLS_DHE_DSS_WITH_AES_256_GCM_SHA384,
?TLS_DHE_RSA_WITH_AES_256_CBC_SHA256,
?TLS_DHE_DSS_WITH_AES_256_CBC_SHA256,
+ ?TLS_RSA_WITH_AES_256_GCM_SHA384,
?TLS_RSA_WITH_AES_256_CBC_SHA256,
+ ?TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
+ ?TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
?TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
?TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
+ ?TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256,
+ ?TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256,
?TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256,
?TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256,
+ ?TLS_DHE_RSA_WITH_AES_128_GCM_SHA256,
+ ?TLS_DHE_DSS_WITH_AES_128_GCM_SHA256,
?TLS_DHE_RSA_WITH_AES_128_CBC_SHA256,
?TLS_DHE_DSS_WITH_AES_128_CBC_SHA256,
+ ?TLS_RSA_WITH_AES_128_GCM_SHA256,
?TLS_RSA_WITH_AES_128_CBC_SHA256
+
+ %% not supported
+ %% ?TLS_DH_RSA_WITH_AES_256_GCM_SHA384,
+ %% ?TLS_DH_DSS_WITH_AES_256_GCM_SHA384,
+ %% ?TLS_DH_RSA_WITH_AES_128_GCM_SHA256,
+ %% ?TLS_DH_DSS_WITH_AES_128_GCM_SHA256
] ++ suites(2).
+
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
diff --git a/lib/ssl/test/Makefile b/lib/ssl/test/Makefile
index 0d241707d9..09cc5981e7 100644
--- a/lib/ssl/test/Makefile
+++ b/lib/ssl/test/Makefile
@@ -37,6 +37,7 @@ VSN=$(GS_VSN)
MODULES = \
ssl_test_lib \
ssl_basic_SUITE \
+ ssl_bench_SUITE \
ssl_cipher_SUITE \
ssl_certificate_verify_SUITE\
ssl_crl_SUITE\
@@ -50,6 +51,7 @@ MODULES = \
ssl_session_cache_SUITE \
ssl_to_openssl_SUITE \
ssl_ECC_SUITE \
+ ssl_upgrade_SUITE\
make_certs\
erl_make_certs
@@ -131,7 +133,7 @@ release_spec: opt
release_tests_spec: opt
$(INSTALL_DIR) "$(RELSYSDIR)"
$(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(HRL_FILES_NEEDED_IN_TEST) $(COVER_FILE) "$(RELSYSDIR)"
- $(INSTALL_DATA) ssl.spec ssl.cover "$(RELSYSDIR)"
+ $(INSTALL_DATA) ssl.spec ssl_bench.spec ssl.cover "$(RELSYSDIR)"
chmod -R u+w "$(RELSYSDIR)"
@tar cf - *_SUITE_data | (cd "$(RELSYSDIR)"; tar xf -)
diff --git a/lib/ssl/test/erl_make_certs.erl b/lib/ssl/test/erl_make_certs.erl
index daf4466f11..b534c0130e 100644
--- a/lib/ssl/test/erl_make_certs.erl
+++ b/lib/ssl/test/erl_make_certs.erl
@@ -204,7 +204,7 @@ issuer_der(Issuer) ->
Subject.
subject(undefined, IsRootCA) ->
- User = if IsRootCA -> "RootCA"; true -> user() end,
+ User = if IsRootCA -> "RootCA"; true -> os:getenv("USER", "test_user") end,
Opts = [{email, User ++ "@erlang.org"},
{name, User},
{city, "Stockholm"},
@@ -215,14 +215,6 @@ subject(undefined, IsRootCA) ->
subject(Opts, _) ->
subject(Opts).
-user() ->
- case os:getenv("USER") of
- false ->
- "test_user";
- User ->
- User
- end.
-
subject(SubjectOpts) when is_list(SubjectOpts) ->
Encode = fun(Opt) ->
{Type,Value} = subject_enc(Opt),
diff --git a/lib/ssl/test/make_certs.erl b/lib/ssl/test/make_certs.erl
index 15a7e118ff..77631f62d3 100644
--- a/lib/ssl/test/make_certs.erl
+++ b/lib/ssl/test/make_certs.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2015. 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
@@ -324,8 +324,9 @@ eval_cmd(Port, Cmd) ->
ok
end,
receive
- {Port, {exit_status, Status}} when Status /= 0 ->
- %% io:fwrite("exit status: ~w~n", [Status]),
+ {Port, {exit_status, 0}} ->
+ ok;
+ {Port, {exit_status, Status}} ->
exit({eval_cmd, Cmd, Status})
after 0 ->
ok
@@ -369,7 +370,7 @@ req_cnf(C) ->
"subjectKeyIdentifier = hash\n"
"subjectAltName = email:copy\n"].
-ca_cnf(C) ->
+ca_cnf(C = #config{issuing_distribution_point = true}) ->
["# Purpose: Configuration for CAs.\n"
"\n"
"ROOTDIR = $ENV::ROOTDIR\n"
@@ -446,5 +447,83 @@ ca_cnf(C) ->
"subjectAltName = email:copy\n"
"issuerAltName = issuer:copy\n"
"crlDistributionPoints=@crl_section\n"
- ].
+ ];
+ca_cnf(C = #config{issuing_distribution_point = false}) ->
+ ["# Purpose: Configuration for CAs.\n"
+ "\n"
+ "ROOTDIR = $ENV::ROOTDIR\n"
+ "default_ca = ca\n"
+ "\n"
+
+ "[ca]\n"
+ "dir = $ROOTDIR/", C#config.commonName, "\n"
+ "certs = $dir/certs\n"
+ "crl_dir = $dir/crl\n"
+ "database = $dir/index.txt\n"
+ "new_certs_dir = $dir/newcerts\n"
+ "certificate = $dir/cert.pem\n"
+ "serial = $dir/serial\n"
+ "crl = $dir/crl.pem\n",
+ ["crlnumber = $dir/crlnumber\n" || C#config.v2_crls],
+ "private_key = $dir/private/key.pem\n"
+ "RANDFILE = $dir/private/RAND\n"
+ "\n"
+ "x509_extensions = user_cert\n",
+ ["crl_extensions = crl_ext\n" || C#config.v2_crls],
+ "unique_subject = no\n"
+ "default_days = 3600\n"
+ "default_md = md5\n"
+ "preserve = no\n"
+ "policy = policy_match\n"
+ "\n"
+
+ "[policy_match]\n"
+ "commonName = supplied\n"
+ "organizationalUnitName = optional\n"
+ "organizationName = match\n"
+ "countryName = match\n"
+ "localityName = match\n"
+ "emailAddress = supplied\n"
+ "\n"
+
+ "[crl_ext]\n"
+ "authorityKeyIdentifier=keyid:always,issuer:always\n",
+ %["issuingDistributionPoint=critical, @idpsec\n" || C#config.issuing_distribution_point],
+
+ %"[idpsec]\n"
+ %"fullname=URI:http://localhost:8000/",C#config.commonName,"/crl.pem\n"
+
+ "[user_cert]\n"
+ "basicConstraints = CA:false\n"
+ "keyUsage = nonRepudiation, digitalSignature, keyEncipherment\n"
+ "subjectKeyIdentifier = hash\n"
+ "authorityKeyIdentifier = keyid,issuer:always\n"
+ "subjectAltName = email:copy\n"
+ "issuerAltName = issuer:copy\n"
+ %"crlDistributionPoints=@crl_section\n"
+
+ %%"[crl_section]\n"
+ %% intentionally invalid
+ %%"URI.1=http://localhost/",C#config.commonName,"/crl.pem\n"
+ %%"URI.2=http://localhost:",integer_to_list(C#config.crl_port),"/",C#config.commonName,"/crl.pem\n"
+ %%"\n"
+
+ "[user_cert_digital_signature_only]\n"
+ "basicConstraints = CA:false\n"
+ "keyUsage = digitalSignature\n"
+ "subjectKeyIdentifier = hash\n"
+ "authorityKeyIdentifier = keyid,issuer:always\n"
+ "subjectAltName = email:copy\n"
+ "issuerAltName = issuer:copy\n"
+ "\n"
+
+ "[ca_cert]\n"
+ "basicConstraints = critical,CA:true\n"
+ "keyUsage = cRLSign, keyCertSign\n"
+ "subjectKeyIdentifier = hash\n"
+ "authorityKeyIdentifier = keyid:always,issuer:always\n"
+ "subjectAltName = email:copy\n"
+ "issuerAltName = issuer:copy\n"
+ %"crlDistributionPoints=@crl_section\n"
+ ].
diff --git a/lib/ssl/test/ssl.spec b/lib/ssl/test/ssl.spec
index fc7c1bbb82..86e14c033e 100644
--- a/lib/ssl/test/ssl.spec
+++ b/lib/ssl/test/ssl.spec
@@ -1 +1,4 @@
{suites,"../ssl_test",all}.
+{skip_cases, "../ssl_test",
+ ssl_bench_SUITE, [setup_sequential, setup_concurrent, payload_simple],
+ "Benchmarks run separately"}.
diff --git a/lib/ssl/test/ssl_basic_SUITE.erl b/lib/ssl/test/ssl_basic_SUITE.erl
index df9432a43b..50d5fb411f 100644
--- a/lib/ssl/test/ssl_basic_SUITE.erl
+++ b/lib/ssl/test/ssl_basic_SUITE.erl
@@ -65,7 +65,7 @@ groups() ->
{'tlsv1.2', [], all_versions_groups()},
{'tlsv1.1', [], all_versions_groups()},
{'tlsv1', [], all_versions_groups() ++ rizzo_tests()},
- {'sslv3', [], all_versions_groups() ++ rizzo_tests()},
+ {'sslv3', [], all_versions_groups() ++ rizzo_tests() ++ [ciphersuite_vs_version]},
{api,[], api_tests()},
{session, [], session_tests()},
{renegotiate, [], renegotiate_tests()},
@@ -91,6 +91,7 @@ basic_tests() ->
connect_twice,
connect_dist,
clear_pem_cache,
+ defaults,
fallback
].
@@ -117,7 +118,6 @@ options_tests() ->
tcp_reuseaddr,
honor_server_cipher_order,
honor_client_cipher_order,
- ciphersuite_vs_version,
unordered_protocol_versions_server,
unordered_protocol_versions_client
].
@@ -178,6 +178,9 @@ cipher_tests() ->
srp_cipher_suites,
srp_anon_cipher_suites,
srp_dsa_cipher_suites,
+ rc4_rsa_cipher_suites,
+ rc4_ecdh_rsa_cipher_suites,
+ rc4_ecdsa_cipher_suites,
default_reject_anonymous].
cipher_tests_ec() ->
@@ -347,7 +350,7 @@ alerts(Config) when is_list(Config) ->
end, Alerts).
%%--------------------------------------------------------------------
new_options_in_accept() ->
- [{doc,"Test that you can set ssl options in ssl_accept/3 and not tcp upgrade"}].
+ [{doc,"Test that you can set ssl options in ssl_accept/3 and not only in tcp upgrade"}].
new_options_in_accept(Config) when is_list(Config) ->
ClientOpts = ?config(client_opts, Config),
ServerOpts0 = ?config(server_dsa_opts, Config),
@@ -365,7 +368,9 @@ new_options_in_accept(Config) when is_list(Config) ->
{host, Hostname},
{from, self()},
{mfa, {?MODULE, connection_info_result, []}},
- {options, [{versions, [sslv3]} | ClientOpts]}]),
+ {options, [{versions, [sslv3]},
+ {ciphers,[{rsa,rc4_128,sha}
+ ]} | ClientOpts]}]),
ct:log("Testcase ~p, Client ~p Server ~p ~n",
[self(), Client, Server]),
@@ -395,7 +400,7 @@ connection_info(Config) when is_list(Config) ->
{from, self()},
{mfa, {?MODULE, connection_info_result, []}},
{options,
- [{ciphers,[{rsa,rc4_128,sha,no_export}]} |
+ [{ciphers,[{rsa,des_cbc,sha,no_export}]} |
ClientOpts]}]),
ct:log("Testcase ~p, Client ~p Server ~p ~n",
@@ -404,7 +409,7 @@ connection_info(Config) when is_list(Config) ->
Version =
tls_record:protocol_version(tls_record:highest_protocol_version([])),
- ServerMsg = ClientMsg = {ok, {Version, {rsa,rc4_128,sha}}},
+ ServerMsg = ClientMsg = {ok, {Version, {rsa, des_cbc, sha}}},
ssl_test_lib:check_result(Server, ServerMsg, Client, ClientMsg),
@@ -633,7 +638,7 @@ clear_pem_cache(Config) when is_list(Config) ->
{status, _, _, StatusInfo} = sys:get_status(whereis(ssl_manager)),
[_, _,_, _, Prop] = StatusInfo,
State = ssl_test_lib:state(Prop),
- [_,FilRefDb, _] = element(5, State),
+ [_,FilRefDb |_] = element(6, State),
{Server, Client} = basic_verify_test_no_close(Config),
2 = ets:info(FilRefDb, size),
ssl:clear_pem_cache(),
@@ -1811,6 +1816,32 @@ srp_dsa_cipher_suites(Config) when is_list(Config) ->
Version = tls_record:protocol_version(tls_record:highest_protocol_version([])),
Ciphers = ssl_test_lib:srp_dss_suites(),
run_suites(Ciphers, Version, Config, srp_dsa).
+%%-------------------------------------------------------------------
+rc4_rsa_cipher_suites()->
+ [{doc, "Test the RC4 ciphersuites"}].
+rc4_rsa_cipher_suites(Config) when is_list(Config) ->
+ NVersion = tls_record:highest_protocol_version([]),
+ Version = tls_record:protocol_version(NVersion),
+ Ciphers = ssl_test_lib:rc4_suites(NVersion),
+ run_suites(Ciphers, Version, Config, rc4_rsa).
+%-------------------------------------------------------------------
+rc4_ecdh_rsa_cipher_suites()->
+ [{doc, "Test the RC4 ciphersuites"}].
+rc4_ecdh_rsa_cipher_suites(Config) when is_list(Config) ->
+ NVersion = tls_record:highest_protocol_version([]),
+ Version = tls_record:protocol_version(NVersion),
+ Ciphers = ssl_test_lib:rc4_suites(NVersion),
+ run_suites(Ciphers, Version, Config, rc4_ecdh_rsa).
+
+%%-------------------------------------------------------------------
+rc4_ecdsa_cipher_suites()->
+ [{doc, "Test the RC4 ciphersuites"}].
+rc4_ecdsa_cipher_suites(Config) when is_list(Config) ->
+ NVersion = tls_record:highest_protocol_version([]),
+ Version = tls_record:protocol_version(NVersion),
+ Ciphers = ssl_test_lib:rc4_suites(NVersion),
+ run_suites(Ciphers, Version, Config, rc4_ecdsa).
+
%%--------------------------------------------------------------------
default_reject_anonymous()->
[{doc,"Test that by default anonymous cipher suites are rejected "}].
@@ -2371,7 +2402,7 @@ der_input(Config) when is_list(Config) ->
{status, _, _, StatusInfo} = sys:get_status(whereis(ssl_manager)),
[_, _,_, _, Prop] = StatusInfo,
State = ssl_test_lib:state(Prop),
- [CADb | _] = element(5, State),
+ [CADb | _] = element(6, State),
[] = ets:tab2list(CADb).
%%--------------------------------------------------------------------
@@ -2539,6 +2570,16 @@ no_reuses_session_server_restart_new_cert_file(Config) when is_list(Config) ->
ssl_test_lib:close(Client1).
%%--------------------------------------------------------------------
+defaults(Config) when is_list(Config)->
+ [_,
+ {supported, Supported},
+ {available, Available}]
+ = ssl:versions(),
+ true = lists:member(sslv3, Available),
+ false = lists:member(sslv3, Supported),
+ false = lists:member({rsa,rc4_128,sha}, ssl:cipher_suites()),
+ true = lists:member({rsa,rc4_128,sha}, ssl:cipher_suites(all)).
+%%--------------------------------------------------------------------
reuseaddr() ->
[{doc,"Test reuseaddr option"}].
@@ -2663,6 +2704,8 @@ honor_cipher_order(Config, Honor, ServerCiphers, ClientCiphers, Expected) ->
ssl_test_lib:close(Client).
%%--------------------------------------------------------------------
+ciphersuite_vs_version() ->
+ [{doc,"Test a SSLv3 client can not negotiate a TLSv* cipher suite."}].
ciphersuite_vs_version(Config) when is_list(Config) ->
{_ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
@@ -3726,8 +3769,20 @@ run_suites(Ciphers, Version, Config, Type) ->
?config(server_ecdsa_opts, Config)};
ecdh_rsa ->
{?config(client_opts, Config),
- ?config(server_ecdh_rsa_opts, Config)}
- end,
+ ?config(server_ecdh_rsa_opts, Config)};
+ rc4_rsa ->
+ {?config(client_opts, Config),
+ [{ciphers, Ciphers} |
+ ?config(server_opts, Config)]};
+ rc4_ecdh_rsa ->
+ {?config(client_opts, Config),
+ [{ciphers, Ciphers} |
+ ?config(server_ecdh_rsa_opts, Config)]};
+ rc4_ecdsa ->
+ {?config(client_opts, Config),
+ [{ciphers, Ciphers} |
+ ?config(server_ecdsa_opts, Config)]}
+ end,
Result = lists:map(fun(Cipher) ->
cipher(Cipher, Version, Config, ClientOpts, ServerOpts) end,
@@ -3748,6 +3803,7 @@ erlang_cipher_suite(Suite) ->
cipher(CipherSuite, Version, Config, ClientOpts, ServerOpts) ->
%% process_flag(trap_exit, true),
ct:log("Testing CipherSuite ~p~n", [CipherSuite]),
+ ct:log("Server Opts ~p~n", [ServerOpts]),
{ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
ErlangCipherSuite = erlang_cipher_suite(CipherSuite),
diff --git a/lib/ssl/test/ssl_bench.spec b/lib/ssl/test/ssl_bench.spec
new file mode 100644
index 0000000000..d2f75b4203
--- /dev/null
+++ b/lib/ssl/test/ssl_bench.spec
@@ -0,0 +1 @@
+{suites,"../ssl_test",[ssl_bench_SUITE]}.
diff --git a/lib/ssl/test/ssl_bench_SUITE.erl b/lib/ssl/test/ssl_bench_SUITE.erl
new file mode 100644
index 0000000000..b6b3769922
--- /dev/null
+++ b/lib/ssl/test/ssl_bench_SUITE.erl
@@ -0,0 +1,366 @@
+%%%-------------------------------------------------------------------
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2014. 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/.2
+%%
+%% 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_bench_SUITE).
+-compile(export_all).
+-include_lib("common_test/include/ct_event.hrl").
+
+-define(remote_host, "NETMARKS_REMOTE_HOST").
+
+suite() -> [{ct_hooks,[{ts_install_cth,[{nodenames,2}]}]}].
+
+all() -> [{group, setup}, {group, payload}].
+
+groups() ->
+ [{setup, [{repeat, 3}], [setup_sequential, setup_concurrent]},
+ {payload, [{repeat, 3}], [payload_simple]}
+ ].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, _Config) ->
+ ok.
+
+init_per_suite(Config) ->
+ try
+ Server = setup(ssl, node()),
+ [{server_node, Server}|Config]
+ catch _:_ ->
+ {skipped, "Benchmark machines only"}
+ end.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_testcase(_Func, Conf) ->
+ Conf.
+
+end_per_testcase(_Func, _Conf) ->
+ ok.
+
+
+-define(COUNT, 400).
+-define(TC(Cmd), tc(fun() -> Cmd end, ?MODULE, ?LINE)).
+
+-define(FPROF_CLIENT, false).
+-define(FPROF_SERVER, false).
+-define(EPROF_CLIENT, false).
+-define(EPROF_SERVER, false).
+-define(PERCEPT_SERVER, false).
+
+%% Current numbers gives roughly a testcase per minute on todays hardware..
+
+setup_sequential(Config) ->
+ Server = proplists:get_value(server_node, Config),
+ Server =/= undefined orelse error(no_server),
+ {ok, Result} = do_test(ssl, setup_connection, ?COUNT * 20, 1, Server),
+ ct_event:notify(#event{name = benchmark_data,
+ data=[{value, Result},
+ {suite, "ssl"}, {name, "Sequential setup"}]}),
+ ok.
+
+setup_concurrent(Config) ->
+ Server = proplists:get_value(server_node, Config),
+ Server =/= undefined orelse error(no_server),
+ {ok, Result} = do_test(ssl, setup_connection, ?COUNT, 100, Server),
+ ct_event:notify(#event{name = benchmark_data,
+ data=[{value, Result},
+ {suite, "ssl"}, {name, "Concurrent setup"}]}),
+ ok.
+
+payload_simple(Config) ->
+ Server = proplists:get_value(server_node, Config),
+ Server =/= undefined orelse error(no_server),
+ {ok, Result} = do_test(ssl, payload, ?COUNT*300, 10, Server),
+ ct_event:notify(#event{name = benchmark_data,
+ data=[{value, Result},
+ {suite, "ssl"}, {name, "Payload simple"}]}),
+ ok.
+
+
+ssl() ->
+ test(ssl, ?COUNT, node()).
+
+test(Type, Count, Host) ->
+ Server = setup(Type, Host),
+ (do_test(Type, setup_connection, Count * 20, 1, Server)),
+ (do_test(Type, setup_connection, Count, 100, Server)),
+ (do_test(Type, payload, Count*300, 10, Server)),
+ ok.
+
+do_test(Type, TC, Loop, ParallellConnections, Server) ->
+ _ = ssl:stop(),
+ {ok, _} = ensure_all_started(ssl, []),
+
+ {ok, {SPid, Host, Port}} = rpc:call(Server, ?MODULE, setup_server_init,
+ [Type, TC, Loop, ParallellConnections]),
+ link(SPid),
+ Me = self(),
+ Test = fun(Id) ->
+ CData = client_init(Me, Type, TC, Host, Port),
+ receive
+ go ->
+ ?FPROF_CLIENT andalso Id =:= 1 andalso
+ start_profile(fprof, [self(),new]),
+ ?EPROF_CLIENT andalso Id =:= 1 andalso
+ start_profile(eprof, [ssl_connection_sup, ssl_manager]),
+ ok = ?MODULE:TC(Loop, Type, CData),
+ ?FPROF_CLIENT andalso Id =:= 1 andalso
+ stop_profile(fprof, "test_connection_client_res.fprof"),
+ ?EPROF_CLIENT andalso Id =:= 1 andalso
+ stop_profile(eprof, "test_connection_client_res.eprof"),
+ Me ! self()
+ end
+ end,
+ Spawn = fun(Id) ->
+ Pid = spawn(fun() -> Test(Id) end),
+ receive {Pid, init} -> Pid end
+ end,
+ Pids = [Spawn(Id) || Id <- lists:seq(ParallellConnections, 1, -1)],
+ Run = fun() ->
+ [Pid ! go || Pid <- Pids],
+ [receive Pid -> ok end || Pid <- Pids]
+ end,
+ {TimeInMicro, _} = timer:tc(Run),
+ TotalTests = ParallellConnections * Loop,
+ TestPerSecond = 1000000 * TotalTests div TimeInMicro,
+ io:format("TC ~p ~p ~p ~p 1/s~n", [TC, Type, ParallellConnections, TestPerSecond]),
+ unlink(SPid),
+ SPid ! quit,
+ {ok, TestPerSecond}.
+
+server_init(ssl, setup_connection, _, _, Server) ->
+ {ok, Socket} = ssl:listen(0, ssl_opts(listen)),
+ {ok, {_Host, Port}} = ssl:sockname(Socket),
+ {ok, Host} = inet:gethostname(),
+ ?FPROF_SERVER andalso start_profile(fprof, [whereis(ssl_manager), new]),
+ %%?EPROF_SERVER andalso start_profile(eprof, [ssl_connection_sup, ssl_manager]),
+ ?EPROF_SERVER andalso start_profile(eprof, [ssl_manager]),
+ ?PERCEPT_SERVER andalso percept:profile("/tmp/ssl_server.percept"),
+ Server ! {self(), {init, Host, Port}},
+ Test = fun(TSocket) ->
+ ok = ssl:ssl_accept(TSocket),
+ ssl:close(TSocket)
+ end,
+ setup_server_connection(Socket, Test);
+server_init(ssl, payload, Loop, _, Server) ->
+ {ok, Socket} = ssl:listen(0, ssl_opts(listen)),
+ {ok, {_Host, Port}} = ssl:sockname(Socket),
+ {ok, Host} = inet:gethostname(),
+ Server ! {self(), {init, Host, Port}},
+ Test = fun(TSocket) ->
+ ok = ssl:ssl_accept(TSocket),
+ Size = byte_size(msg()),
+ server_echo(TSocket, Size, Loop),
+ ssl:close(TSocket)
+ end,
+ setup_server_connection(Socket, Test);
+
+server_init(Type, Tc, _, _, Server) ->
+ io:format("No server init code for ~p ~p~n",[Type, Tc]),
+ Server ! {self(), no_init}.
+
+client_init(Master, ssl, setup_connection, Host, Port) ->
+ Master ! {self(), init},
+ {Host, Port, ssl_opts(connect)};
+client_init(Master, ssl, payload, Host, Port) ->
+ {ok, Sock} = ssl:connect(Host, Port, ssl_opts(connect)),
+ Master ! {self(), init},
+ Size = byte_size(msg()),
+ {Sock, Size};
+client_init(_Me, Type, Tc, Host, Port) ->
+ io:format("No client init code for ~p ~p~n",[Type, Tc]),
+ {Host, Port}.
+
+setup_server_connection(LSocket, Test) ->
+ receive quit ->
+ ?FPROF_SERVER andalso stop_profile(fprof, "test_server_res.fprof"),
+ ?EPROF_SERVER andalso stop_profile(eprof, "test_server_res.eprof"),
+ ?PERCEPT_SERVER andalso stop_profile(percept, "/tmp/ssl_server.percept"),
+ ok
+ after 0 ->
+ case ssl:transport_accept(LSocket, 2000) of
+ {ok, TSocket} -> spawn_link(fun() -> Test(TSocket) end);
+ {error, timeout} -> ok
+ end,
+ setup_server_connection(LSocket, Test)
+ end.
+
+server_echo(Socket, Size, Loop) when Loop > 0 ->
+ {ok, Msg} = ssl:recv(Socket, Size),
+ ok = ssl:send(Socket, Msg),
+ server_echo(Socket, Size, Loop-1);
+server_echo(_, _, _) -> ok.
+
+setup_connection(N, ssl, Env = {Host, Port, Opts}) when N > 0 ->
+ case ssl:connect(Host, Port, Opts) of
+ {ok, Sock} ->
+ ssl:close(Sock),
+ setup_connection(N-1, ssl, Env);
+ {error, Error} ->
+ io:format("Error: ~p (~p)~n",[Error, length(erlang:ports())]),
+ setup_connection(N, ssl, Env)
+ end;
+setup_connection(_, _, _) ->
+ ok.
+
+payload(Loop, ssl, D = {Socket, Size}) when Loop > 0 ->
+ ok = ssl:send(Socket, msg()),
+ {ok, _} = ssl:recv(Socket, Size),
+ payload(Loop-1, ssl, D);
+payload(_, _, {Socket, _}) ->
+ ssl:close(Socket).
+
+msg() ->
+ <<"Hello",
+ 0:(512*8),
+ "asdlkjsafsdfoierwlejsdlkfjsdf",
+ 1:(512*8),
+ "asdlkjsafsdfoierwlejsdlkfjsdf">>.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+setup(_Type, nonode@nohost) ->
+ exit(dist_not_enabled);
+setup(Type, _This) ->
+ Host = case os:getenv(?remote_host) of
+ false ->
+ {ok, This} = inet:gethostname(),
+ This;
+ RemHost ->
+ RemHost
+ end,
+ Node = list_to_atom("perf_server@" ++ Host),
+ SlaveArgs = case init:get_argument(pa) of
+ {ok, PaPaths} ->
+ lists:append([" -pa " ++ P || [P] <- PaPaths]);
+ _ -> []
+ end,
+ %% io:format("Slave args: ~p~n",[SlaveArgs]),
+ Prog =
+ case os:find_executable("erl") of
+ false -> "erl";
+ P -> P
+ end,
+ io:format("Prog = ~p~n", [Prog]),
+
+ case net_adm:ping(Node) of
+ pong -> ok;
+ pang ->
+ {ok, Node} = slave:start(Host, perf_server, SlaveArgs, no_link, Prog)
+ end,
+ Path = code:get_path(),
+ true = rpc:call(Node, code, set_path, [Path]),
+ ok = rpc:call(Node, ?MODULE, setup_server, [Type, node()]),
+ io:format("Client (~p) using ~s~n",[node(), code:which(ssl)]),
+ (Node =:= node()) andalso restrict_schedulers(client),
+ Node.
+
+setup_server(_Type, ClientNode) ->
+ (ClientNode =:= node()) andalso restrict_schedulers(server),
+ io:format("Server (~p) using ~s~n",[node(), code:which(ssl)]),
+ ok.
+
+
+ensure_all_started(App, Ack) ->
+ case application:start(App) of
+ ok -> {ok, [App|Ack]};
+ {error, {not_started, Dep}} ->
+ {ok, Ack1} = ensure_all_started(Dep, Ack),
+ ensure_all_started(App, Ack1);
+ {error, {already_started, _}} ->
+ {ok, Ack}
+ end.
+
+setup_server_init(Type, Tc, Loop, PC) ->
+ _ = ssl:stop(),
+ {ok, _} = ensure_all_started(ssl, []),
+ Me = self(),
+ Pid = spawn_link(fun() -> server_init(Type, Tc, Loop, PC, Me) end),
+ Res = receive
+ {Pid, {init, Host, Port}} -> {ok, {Pid, Host, Port}};
+ {Pid, Error} -> {error, Error}
+ end,
+ unlink(Pid),
+ Res.
+
+restrict_schedulers(Type) ->
+ %% We expect this to run on 8 core machine
+ Extra0 = 1,
+ Extra = if (Type =:= server) -> -Extra0; true -> Extra0 end,
+ Scheds = erlang:system_info(schedulers),
+ erlang:system_flag(schedulers_online, (Scheds div 2) + Extra).
+
+tc(Fun, Mod, Line) ->
+ case timer:tc(Fun) of
+ {_,{'EXIT',Reason}} ->
+ io:format("Process EXITED ~p:~p \n", [Mod, Line]),
+ exit(Reason);
+ {_T,R={error,_}} ->
+ io:format("Process Error ~p:~p \n", [Mod, Line]),
+ R;
+ {T,R} ->
+ io:format("~p:~p: Time: ~p\n", [Mod, Line, T]),
+ R
+ end.
+
+start_profile(eprof, Procs) ->
+ profiling = eprof:start_profiling(Procs),
+ io:format("(E)Profiling ...",[]);
+start_profile(fprof, Procs) ->
+ fprof:trace([start, {procs, Procs}]),
+ io:format("(F)Profiling ...",[]).
+
+stop_profile(percept, File) ->
+ percept:stop_profile(),
+ percept:analyze(File),
+ {started, _Host, Port} = percept:start_webserver(),
+ wx:new(),
+ wx_misc:launchDefaultBrowser("http://" ++ net_adm:localhost() ++ ":" ++ integer_to_list(Port)),
+ ok;
+stop_profile(eprof, File) ->
+ profiling_stopped = eprof:stop_profiling(),
+ eprof:log(File),
+ io:format(".analysed => ~s ~n",[File]),
+ eprof:analyze(total),
+ eprof:stop();
+stop_profile(fprof, File) ->
+ fprof:trace(stop),
+ io:format("..collect..",[]),
+ fprof:profile(),
+ fprof:analyse([{dest, File},{totals, true}]),
+ io:format(".analysed => ~s ~n",[File]),
+ fprof:stop(),
+ ok.
+
+ssl_opts(listen) ->
+ [{backlog, 500} | ssl_opts("server")];
+ssl_opts(connect) ->
+ [{verify, verify_peer}
+ | ssl_opts("client")];
+ssl_opts(Role) ->
+ Dir = filename:join([code:lib_dir(ssl), "examples", "certs", "etc"]),
+ [{active, false},
+ {depth, 2},
+ {reuseaddr, true},
+ {mode,binary},
+ {nodelay, true},
+ {ciphers, [{dhe_rsa,aes_256_cbc,sha}]},
+ {cacertfile, filename:join([Dir, Role, "cacerts.pem"])},
+ {certfile, filename:join([Dir, Role, "cert.pem"])},
+ {keyfile, filename:join([Dir, Role, "key.pem"])}].
diff --git a/lib/ssl/test/ssl_cipher_SUITE.erl b/lib/ssl/test/ssl_cipher_SUITE.erl
index 0e48b674e0..3433f9a445 100644
--- a/lib/ssl/test/ssl_cipher_SUITE.erl
+++ b/lib/ssl/test/ssl_cipher_SUITE.erl
@@ -84,13 +84,11 @@ aes_decipher_good(Config) when is_list(Config) ->
decipher_check_good(HashSz, CipherState, {3,3}).
%%--------------------------------------------------------------------
-
aes_decipher_fail() ->
[{doc,"Decipher a known cryptotext using a incorrect key"}].
aes_decipher_fail(Config) when is_list(Config) ->
HashSz = 32,
-
CipherState = incorrect_cipher_state(),
decipher_check_fail(HashSz, CipherState, {3,0}),
decipher_check_fail(HashSz, CipherState, {3,1}),
@@ -111,36 +109,36 @@ padding_test(Config) when is_list(Config) ->
%%--------------------------------------------------------------------
decipher_check_good(HashSz, CipherState, Version) ->
{Content, NextIV, Mac} = content_nextiv_mac(Version),
- {Content, Mac, #cipher_state{iv = NextIV}} =
- ssl_cipher:decipher(?AES, HashSz, CipherState, aes_fragment(Version), Version, true).
+ {Content, Mac, _} =
+ ssl_cipher:decipher(?AES_CBC, HashSz, CipherState, aes_fragment(Version), Version, true).
decipher_check_fail(HashSz, CipherState, Version) ->
{Content, NextIV, Mac} = content_nextiv_mac(Version),
true = {Content, Mac, #cipher_state{iv = NextIV}} =/=
- ssl_cipher:decipher(?AES, HashSz, CipherState, aes_fragment(Version), Version, true).
+ ssl_cipher:decipher(?AES_CBC, HashSz, CipherState, aes_fragment(Version), Version, true).
pad_test(HashSz, CipherState, {3,0} = Version) ->
%% 3.0 does not have padding test
{Content, NextIV, Mac} = badpad_content_nextiv_mac(Version),
{Content, Mac, #cipher_state{iv = NextIV}} =
- ssl_cipher:decipher(?AES, HashSz, CipherState, badpad_aes_fragment({3,0}), {3,0}, true),
+ ssl_cipher:decipher(?AES_CBC, HashSz, CipherState, badpad_aes_fragment({3,0}), {3,0}, true),
{Content, Mac, #cipher_state{iv = NextIV}} =
- ssl_cipher:decipher(?AES, HashSz, CipherState, badpad_aes_fragment({3,0}), {3,0}, false);
+ ssl_cipher:decipher(?AES_CBC, HashSz, CipherState, badpad_aes_fragment({3,0}), {3,0}, false);
pad_test(HashSz, CipherState, {3,1} = Version) ->
%% 3.1 should have padding test, but may be disabled
{Content, NextIV, Mac} = badpad_content_nextiv_mac(Version),
BadCont = badpad_content(Content),
{Content, Mac, #cipher_state{iv = NextIV}} =
- ssl_cipher:decipher(?AES, HashSz, CipherState, badpad_aes_fragment({3,1}) , {3,1}, false),
+ ssl_cipher:decipher(?AES_CBC, HashSz, CipherState, badpad_aes_fragment({3,1}) , {3,1}, false),
{BadCont, Mac, #cipher_state{iv = NextIV}} =
- ssl_cipher:decipher(?AES, HashSz, CipherState, badpad_aes_fragment({3,1}), {3,1}, true);
+ ssl_cipher:decipher(?AES_CBC, HashSz, CipherState, badpad_aes_fragment({3,1}), {3,1}, true);
pad_test(HashSz, CipherState, Version) ->
%% 3.2 and 3.3 must have padding test
{Content, NextIV, Mac} = badpad_content_nextiv_mac(Version),
BadCont = badpad_content(Content),
- {BadCont, Mac, #cipher_state{iv = NextIV}} = ssl_cipher:decipher(?AES, HashSz, CipherState,
+ {BadCont, Mac, #cipher_state{iv = NextIV}} = ssl_cipher:decipher(?AES_CBC, HashSz, CipherState,
badpad_aes_fragment(Version), Version, false),
- {BadCont, Mac, #cipher_state{iv = NextIV}} = ssl_cipher:decipher(?AES, HashSz, CipherState,
+ {BadCont, Mac, #cipher_state{iv = NextIV}} = ssl_cipher:decipher(?AES_CBC, HashSz, CipherState,
badpad_aes_fragment(Version), Version, true).
aes_fragment({3,N}) when N == 0; N == 1->
@@ -164,7 +162,7 @@ badpad_aes_fragment(_) ->
content_nextiv_mac({3,N}) when N == 0; N == 1 ->
{<<"HELLO\n">>,
- <<33,0, 177,251, 91,44, 247,53, 183,198, 165,63, 20,194, 159,107>>,
+ <<72,196,247,97,62,213,222,109,210,204,217,186,172,184, 197,148>>,
<<71,136,212,107,223,200,70,232,127,116,148,205,232,35,158,113,237,174,15,217,192,168,35,8,6,107,107,233,25,174,90,111>>};
content_nextiv_mac(_) ->
{<<"HELLO\n">>,
@@ -193,3 +191,4 @@ correct_cipher_state() ->
incorrect_cipher_state() ->
#cipher_state{iv = <<59,201,85,117,188,206,224,136,5,109,46,70,104,79,4,9>>,
key = <<72,196,247,97,62,213,222,109,210,204,217,186,172,184,197,254>>}.
+
diff --git a/lib/ssl/test/ssl_crl_SUITE.erl b/lib/ssl/test/ssl_crl_SUITE.erl
index bad0949ec4..c6bf8898ad 100644
--- a/lib/ssl/test/ssl_crl_SUITE.erl
+++ b/lib/ssl/test/ssl_crl_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2015. 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
@@ -26,43 +26,40 @@
-include_lib("common_test/include/ct.hrl").
-include_lib("public_key/include/public_key.hrl").
--define(TIMEOUT, 120000).
-define(LONG_TIMEOUT, 600000).
--define(SLEEP, 1000).
--define(OPENSSL_RENEGOTIATE, "R\n").
--define(OPENSSL_QUIT, "Q\n").
--define(OPENSSL_GARBAGE, "P\n").
--define(EXPIRE, 10).
%%--------------------------------------------------------------------
%% Common Test interface functions -----------------------------------
%%--------------------------------------------------------------------
-suite() -> [{ct_hooks,[ts_install_cth]}].
+suite() ->
+ [{ct_hooks,[ts_install_cth]}].
all() ->
[
- {group, basic},
- {group, v1_crl},
- {group, idp_crl}
+ {group, check_true},
+ {group, check_peer},
+ {group, check_best_effort}
].
groups() ->
- [{basic, [], basic_tests()},
- {v1_crl, [], v1_crl_tests()},
- {idp_crl, [], idp_crl_tests()}].
+ [
+ {check_true, [], [{group, v2_crl},
+ {group, v1_crl},
+ {group, idp_crl}]},
+ {check_peer, [], [{group, v2_crl},
+ {group, v1_crl},
+ {group, idp_crl}]},
+ {check_best_effort, [], [{group, v2_crl},
+ {group, v1_crl},
+ {group, idp_crl}]},
+ {v2_crl, [], basic_tests()},
+ {v1_crl, [], basic_tests()},
+ {idp_crl, [], basic_tests()}].
basic_tests() ->
[crl_verify_valid, crl_verify_revoked].
-v1_crl_tests() ->
- [crl_verify_valid, crl_verify_revoked].
-
-idp_crl_tests() ->
- [crl_verify_valid, crl_verify_revoked].
-
-%%%================================================================
-%%% Suite init/end
init_per_suite(Config0) ->
Dog = ct:timetrap(?LONG_TIMEOUT *2),
@@ -70,10 +67,7 @@ init_per_suite(Config0) ->
false ->
{skip, "Openssl not found"};
_ ->
- TLSVersion = ?config(tls_version, Config0),
OpenSSL_version = (catch os:cmd("openssl version")),
- ct:log("TLS version: ~p~nOpenSSL version: ~p~n~n~p:module_info(): ~p~n~nssl:module_info(): ~p~n",
- [TLSVersion, OpenSSL_version, ?MODULE, ?MODULE:module_info(), ssl:module_info()]),
case ssl_test_lib:enough_openssl_crl_support(OpenSSL_version) of
false ->
{skip, io_lib:format("Bad openssl version: ~p",[OpenSSL_version])};
@@ -81,7 +75,6 @@ init_per_suite(Config0) ->
catch crypto:stop(),
try crypto:start() of
ok ->
- ssl:start(),
{ok, Hostname0} = inet:gethostname(),
IPfamily =
case lists:member(list_to_atom(Hostname0), ct:get_config(ipv6_hosts,[])) of
@@ -89,8 +82,7 @@ init_per_suite(Config0) ->
false -> inet
end,
[{ipfamily,IPfamily}, {watchdog, Dog}, {openssl_version,OpenSSL_version} | Config0]
- catch _C:_E ->
- ct:log("crypto:start() caught ~p:~p",[_C,_E]),
+ catch _:_ ->
{skip, "Crypto did not start"}
end
end
@@ -100,443 +92,175 @@ end_per_suite(_Config) ->
ssl:stop(),
application:stop(crypto).
-%%%================================================================
-%%% Group init/end
-
-init_per_group(Group, Config) ->
- ssl:start(),
- inets:start(),
- CertDir = filename:join(?config(priv_dir, Config), Group),
- DataDir = ?config(data_dir, Config),
- ServerRoot = make_dir_path([?config(priv_dir,Config), Group, tmp]),
- %% start a HTTP server to serve the CRLs
- {ok, Httpd} = inets:start(httpd, [{ipfamily, ?config(ipfamily,Config)},
- {server_name, "localhost"}, {port, 0},
- {server_root, ServerRoot},
- {document_root, CertDir},
- {modules, [mod_get]}
- ]),
- [{port,Port}] = httpd:info(Httpd, [port]),
- ct:log("~p:~p~nHTTPD IP family=~p, port=~p~n", [?MODULE, ?LINE, ?config(ipfamily,Config), Port]),
- CertOpts = [{crl_port,Port}|cert_opts(Group)],
- Result = make_certs:all(DataDir, CertDir, CertOpts),
- ct:log("~p:~p~nmake_certs:all(~n DataDir=~p,~n CertDir=~p,~n ServerRoot=~p~n Opts=~p~n) returned ~p~n", [?MODULE,?LINE,DataDir, CertDir, ServerRoot, CertOpts, Result]),
- [{make_cert_result, Result}, {cert_dir, CertDir}, {httpd, Httpd} | Config].
-
-cert_opts(v1_crl) -> [{v2_crls, false}];
-cert_opts(idp_crl) -> [{issuing_distribution_point, true}];
-cert_opts(_) -> [].
-
-make_dir_path(PathComponents) ->
- lists:foldl(fun(F,P0) -> file:make_dir(P=filename:join(P0,F)), P end,
- "",
- PathComponents).
-
+init_per_group(check_true, Config) ->
+ [{crl_check, true} | Config];
+init_per_group(check_peer, Config) ->
+ [{crl_check, peer} | Config];
+init_per_group(check_best_effort, Config) ->
+ [{crl_check, best_effort} | Config];
+init_per_group(Group, Config0) ->
+ case is_idp(Group) of
+ true ->
+ [{idp_crl, true} | Config0];
+ false ->
+ DataDir = ?config(data_dir, Config0),
+ CertDir = filename:join(?config(priv_dir, Config0), Group),
+ {CertOpts, Config} = init_certs(CertDir, Group, Config0),
+ Result = make_certs:all(DataDir, CertDir, CertOpts),
+ [{make_cert_result, Result}, {cert_dir, CertDir}, {idp_crl, false} | Config]
+ end.
end_per_group(_GroupName, Config) ->
- case ?config(httpd, Config) of
- undefined -> ok;
- Pid ->
- ct:log("Stop httpd ~p",[Pid]),
- ok = inets:stop(httpd, Pid)
- ,ct:log("Stopped",[])
- end,
- inets:stop(),
+
Config.
+init_per_testcase(Case, Config0) ->
+ case ?config(idp_crl, Config0) of
+ true ->
+ end_per_testcase(Case, Config0),
+ inets:start(),
+ ssl:start(),
+ ServerRoot = make_dir_path([?config(priv_dir, Config0), idp_crl, tmp]),
+ %% start a HTTP server to serve the CRLs
+ {ok, Httpd} = inets:start(httpd, [{ipfamily, ?config(ipfamily, Config0)},
+ {server_name, "localhost"}, {port, 0},
+ {server_root, ServerRoot},
+ {document_root,
+ filename:join(?config(priv_dir, Config0), idp_crl)}
+ ]),
+ [{port,Port}] = httpd:info(Httpd, [port]),
+ Config = [{httpd_port, Port} | Config0],
+ DataDir = ?config(data_dir, Config),
+ CertDir = filename:join(?config(priv_dir, Config0), idp_crl),
+ {CertOpts, Config} = init_certs(CertDir, idp_crl, Config),
+ Result = make_certs:all(DataDir, CertDir, CertOpts),
+ [{make_cert_result, Result}, {cert_dir, CertDir} | Config];
+ false ->
+ end_per_testcase(Case, Config0),
+ ssl:start(),
+ Config0
+ end.
+
+end_per_testcase(_, Config) ->
+ case ?config(idp_crl, Config) of
+ true ->
+ ssl:stop(),
+ inets:stop();
+ false ->
+ ssl:stop()
+ end.
+
%%%================================================================
%%% Test cases
+%%%================================================================
crl_verify_valid() ->
[{doc,"Verify a simple valid CRL chain"}].
crl_verify_valid(Config) when is_list(Config) ->
- process_flag(trap_exit, true),
PrivDir = ?config(cert_dir, Config),
- ServerOpts = [{keyfile, filename:join([PrivDir, "server", "key.pem"])},
- {certfile, filename:join([PrivDir, "server", "cert.pem"])},
- {cacertfile, filename:join([PrivDir, "server", "cacerts.pem"])}],
-
+ Check = ?config(crl_check, Config),
+ ServerOpts = [{keyfile, filename:join([PrivDir, "server", "key.pem"])},
+ {certfile, filename:join([PrivDir, "server", "cert.pem"])},
+ {cacertfile, filename:join([PrivDir, "server", "cacerts.pem"])}],
+ ClientOpts = case ?config(idp_crl, Config) of
+ true ->
+ [{cacertfile, filename:join([PrivDir, "server", "cacerts.pem"])},
+ {crl_check, Check},
+ {crl_cache, {ssl_crl_cache, {internal, [{http, 5000}]}}},
+ {verify, verify_peer}];
+ false ->
+ [{cacertfile, filename:join([PrivDir, "server", "cacerts.pem"])},
+ {crl_check, Check},
+ {verify, verify_peer}]
+ end,
{ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
- Data = "From openssl to erlang",
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {?MODULE, erlang_ssl_receive, [Data]}},
- %{mfa, {ssl_test_lib, no_result, []}},
- {options, ServerOpts}]),
- ct:log("~p:~p~nreturn from ssl_test_lib:start_server:~n~p",[?MODULE,?LINE,Server]),
- Port = ssl_test_lib:inet_port(Server),
-
- CACerts = load_cert(filename:join([PrivDir, "erlangCA", "cacerts.pem"])),
-
- ClientOpts = [{cacerts, CACerts},
- {verify, verify_peer},
- {verify_fun, {fun validate_function/3, {CACerts, []}}}],
-
-
- ct:log("~p:~p~ncalling ssl_test_lib:start_client",[?MODULE,?LINE]),
- Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {?MODULE,
- erlang_ssl_send, [Data]}},
- %{mfa, {ssl_test_lib, no_result, []}},
- {options, ClientOpts}]),
- ct:log("~p:~p~nreturn from ssl_test_lib:start_client:~n~p",[?MODULE,?LINE,Client]),
-
- ssl_test_lib:check_result(Client, ok, Server, ok),
-
- %% Clean close down! Server needs to be closed first !!
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client),
- process_flag(trap_exit, false).
+ ssl_crl_cache:insert({file, filename:join([PrivDir, "erlangCA", "crl.pem"])}),
+ ssl_crl_cache:insert({file, filename:join([PrivDir, "otpCA", "crl.pem"])}),
+
+ crl_verify_valid(Hostname, ServerNode, ServerOpts, ClientNode, ClientOpts).
crl_verify_revoked() ->
- [{doc,"Verify a simple valid CRL chain"}].
-crl_verify_revoked(Config) when is_list(Config) ->
- process_flag(trap_exit, true),
+ [{doc,"Verify a simple CRL chain when peer cert is reveoked"}].
+crl_verify_revoked(Config) when is_list(Config) ->
PrivDir = ?config(cert_dir, Config),
+ Check = ?config(crl_check, Config),
ServerOpts = [{keyfile, filename:join([PrivDir, "revoked", "key.pem"])},
- {certfile, filename:join([PrivDir, "revoked", "cert.pem"])},
- {cacertfile, filename:join([PrivDir, "revoked", "cacerts.pem"])}],
- ct:log("~p:~p~nserver opts ~p~n", [?MODULE,?LINE, ServerOpts]),
+ {certfile, filename:join([PrivDir, "revoked", "cert.pem"])},
+ {cacertfile, filename:join([PrivDir, "revoked", "cacerts.pem"])}],
{ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- %{mfa, {?MODULE, erlang_ssl_receive, [Data]}},
- {mfa, {ssl_test_lib, no_result, []}},
- {options, ServerOpts}]),
+ Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {options, ServerOpts}]),
Port = ssl_test_lib:inet_port(Server),
+
+ ssl_crl_cache:insert({file, filename:join([PrivDir, "erlangCA", "crl.pem"])}),
+ ssl_crl_cache:insert({file, filename:join([PrivDir, "otpCA", "crl.pem"])}),
+
+ ClientOpts = case ?config(idp_crl, Config) of
+ true ->
+ [{cacertfile, filename:join([PrivDir, "revoked", "cacerts.pem"])},
+ {crl_cache, {ssl_crl_cache, {internal, [{http, 5000}]}}},
+ {crl_check, Check},
+ {verify, verify_peer}];
+ false ->
+ [{cacertfile, filename:join([PrivDir, "revoked", "cacerts.pem"])},
+ {crl_check, Check},
+ {verify, verify_peer}]
+ end,
+
+ Client = ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {options, ClientOpts}]),
+ receive
+ {Server, AlertOrColse} ->
+ ct:pal("Server Alert or Close ~p", [AlertOrColse])
+ end,
+ ssl_test_lib:check_result(Client, {error, {tls_alert, "certificate revoked"}}).
- CACerts = load_cert(filename:join([PrivDir, "erlangCA", "cacerts.pem"])),
- ClientOpts = [{cacerts, CACerts},
- {verify, verify_peer},
- {verify_fun, {fun validate_function/3, {CACerts, []}}}],
- {connect_failed, _} = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+crl_verify_valid(Hostname, ServerNode, ServerOpts, ClientNode, ClientOpts) ->
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {ssl_test_lib,
+ send_recv_result_active, []}},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
{host, Hostname},
{from, self()},
- %{mfa, {?MODULE,
- %erlang_ssl_receive, [Data]}},
- {mfa, {ssl_test_lib, no_result, []}},
+ {mfa, {ssl_test_lib,
+ send_recv_result_active, []}},
{options, ClientOpts}]),
+
+ ssl_test_lib:check_result(Client, ok, Server, ok),
- %% Clean close down! Server needs to be closed first !!
ssl_test_lib:close(Server),
- process_flag(trap_exit, false).
-
-%%%================================================================
-%%% Lib
-
-erlang_ssl_receive(Socket, Data) ->
- ct:log("~p:~p~nConnection info: ~p~n",
- [?MODULE,?LINE, ssl:connection_info(Socket)]),
- receive
- {ssl, Socket, Data} ->
- ct:log("~p:~p~nReceived ~p~n",[?MODULE,?LINE, Data]),
- %% open_ssl server sometimes hangs waiting in blocking read
- ssl:send(Socket, "Got it"),
- ok;
- {ssl, Socket, Byte} when length(Byte) == 1 ->
- erlang_ssl_receive(Socket, tl(Data));
- {Port, {data,Debug}} when is_port(Port) ->
- ct:log("~p:~p~nopenssl ~s~n",[?MODULE,?LINE, Debug]),
- erlang_ssl_receive(Socket,Data);
- Other ->
- ct:fail({unexpected_message, Other})
- after 4000 ->
- ct:fail({did_not_get, Data})
- end.
-
-
-erlang_ssl_send(Socket, Data) ->
- ct:log("~p:~p~nConnection info: ~p~n",
- [?MODULE,?LINE, ssl:connection_info(Socket)]),
- ssl:send(Socket, Data),
- ok.
-
-load_certs(undefined) ->
- undefined;
-load_certs(CertDir) ->
- case file:list_dir(CertDir) of
- {ok, Certs} ->
- load_certs(lists:map(fun(Cert) -> filename:join(CertDir, Cert)
- end, Certs), []);
- {error, _} ->
- undefined
- end.
-
-load_certs([], Acc) ->
- ct:log("~p:~p~nSuccessfully loaded ~p CA certificates~n", [?MODULE,?LINE, length(Acc)]),
- Acc;
-load_certs([Cert|Certs], Acc) ->
- case filelib:is_dir(Cert) of
- true ->
- load_certs(Certs, Acc);
- _ ->
- %ct:log("~p:~p~nLoading certificate ~p~n", [?MODULE,?LINE, Cert]),
- load_certs(Certs, load_cert(Cert) ++ Acc)
- end.
-
-load_cert(Cert) ->
- {ok, Bin} = file:read_file(Cert),
- case filename:extension(Cert) of
- ".der" ->
- %% no decoding necessary
- [Bin];
- _ ->
- %% assume PEM otherwise
- Contents = public_key:pem_decode(Bin),
- [DER || {Type, DER, Cipher} <- Contents, Type == 'Certificate', Cipher == 'not_encrypted']
- end.
-
-%% @doc Validator function for SSL negotiation.
-%%
-validate_function(Cert, valid_peer, State) ->
- ct:log("~p:~p~nvaliding peer ~p with ~p intermediate certs~n",
- [?MODULE,?LINE, get_common_name(Cert),
- length(element(2, State))]),
- %% peer certificate validated, now check the CRL
- Res = (catch check_crl(Cert, State)),
- ct:log("~p:~p~nCRL validate result for ~p: ~p~n",
- [?MODULE,?LINE, get_common_name(Cert), Res]),
- {Res, State};
-validate_function(Cert, valid, {TrustedCAs, IntermediateCerts}=State) ->
- case public_key:pkix_is_self_signed(Cert) of
- true ->
- ct:log("~p:~p~nroot certificate~n",[?MODULE,?LINE]),
- %% this is a root cert, no CRL
- {valid, {TrustedCAs, [Cert|IntermediateCerts]}};
- false ->
- %% check is valid CA certificate, add to the list of
- %% intermediates
- Res = (catch check_crl(Cert, State)),
- ct:log("~p:~p~nCRL intermediate CA validate result for ~p: ~p~n",
- [?MODULE,?LINE, get_common_name(Cert), Res]),
- {Res, {TrustedCAs, [Cert|IntermediateCerts]}}
- end;
-validate_function(_Cert, _Event, State) ->
- %ct:log("~p:~p~nignoring event ~p~n", [?MODULE,?LINE, _Event]),
- {valid, State}.
+ ssl_test_lib:close(Client).
-%% @doc Given a certificate, find CRL distribution points for the given
-%% certificate, fetch, and attempt to validate each CRL through
-%% issuer_function/4.
-%%
-check_crl(Cert, State) ->
- %% pull the CRL distribution point(s) out of the certificate, if any
- ct:log("~p:~p~ncheck_crl(~n Cert=~p,~nState=~p~n)",[?MODULE,?LINE,Cert,State]),
- case pubkey_cert:select_extension(
- ?'id-ce-cRLDistributionPoints',
- pubkey_cert:extensions_list(Cert#'OTPCertificate'.tbsCertificate#'OTPTBSCertificate'.extensions)) of
- undefined ->
- ct:log("~p:~p~nno CRL distribution points for ~p~n",
- [?MODULE,?LINE, get_common_name(Cert)]),
- %% fail; we can't validate if there's no CRL
- no_crl;
- CRLExtension ->
- ct:log("~p:~p~nCRLExtension=~p)",[?MODULE,?LINE,CRLExtension]),
- CRLDistPoints = CRLExtension#'Extension'.extnValue,
- DPointsAndCRLs = lists:foldl(fun(Point, Acc) ->
- %% try to read the CRL over http or from a
- %% local file
- case fetch_point(Point) of
- not_available ->
- ct:log("~p:~p~nfetch_point returned~n~p~n)",[?MODULE,?LINE,not_available]),
- Acc;
- Res ->
- ct:log("~p:~p~nfetch_point returned~n~p~n)",[?MODULE,?LINE,Res]),
- [{Point, Res} | Acc]
- end
- end, [], CRLDistPoints),
- public_key:pkix_crls_validate(Cert,
- DPointsAndCRLs,
- [{issuer_fun,
- {fun issuer_function/4, State}}])
- end.
-
-%% @doc Given a list of distribution points for CRLs, certificates and
-%% both trusted and intermediary certificates, attempt to build and
-%% authority chain back via build_chain to verify that it is valid.
-%%
-issuer_function(_DP, CRL, _Issuer, {TrustedCAs, IntermediateCerts}) ->
- %% XXX the 'Issuer' we get passed here is the AuthorityKeyIdentifier,
- %% which we are not currently smart enough to understand
- %% Read the CA certs out of the file
- ct:log("~p:~p~nissuer_function(~nCRL=~p,~nLast param=~p)",[?MODULE,?LINE,CRL, {TrustedCAs, IntermediateCerts}]),
- Certs = [public_key:pkix_decode_cert(DER, otp) || DER <- TrustedCAs],
- %% get the real issuer out of the CRL
- Issuer = public_key:pkix_normalize_name(
- pubkey_cert_records:transform(
- CRL#'CertificateList'.tbsCertList#'TBSCertList'.issuer, decode)),
- %% assume certificates are ordered from root to tip
- case find_issuer(Issuer, IntermediateCerts ++ Certs) of
- undefined ->
- ct:log("~p:~p~nunable to find certificate matching CRL issuer ~p~n",
- [?MODULE,?LINE, Issuer]),
- error;
- IssuerCert ->
- ct:log("~p:~p~nIssuerCert=~p~n)",[?MODULE,?LINE,IssuerCert]),
- case build_chain({public_key:pkix_encode('OTPCertificate',
- IssuerCert,
- otp),
- IssuerCert}, IntermediateCerts, Certs, []) of
- undefined ->
- error;
- {OTPCert, Path} ->
- {ok, OTPCert, Path}
- end
- end.
-
-%% @doc Attempt to build authority chain back using intermediary
-%% certificates, falling back on trusted certificates if the
-%% intermediary chain of certificates does not fully extend to the
-%% root.
-%%
-%% Returns: {RootCA :: #OTPCertificate{}, Chain :: [der_encoded()]}
-%%
-build_chain({DER, Cert}, IntCerts, TrustedCerts, Acc) ->
- %% check if this cert is self-signed, if it is, we've reached the
- %% root of the chain
- Issuer = public_key:pkix_normalize_name(
- Cert#'OTPCertificate'.tbsCertificate#'OTPTBSCertificate'.issuer),
- Subject = public_key:pkix_normalize_name(
- Cert#'OTPCertificate'.tbsCertificate#'OTPTBSCertificate'.subject),
- case Issuer == Subject of
- true ->
- case find_issuer(Issuer, TrustedCerts) of
- undefined ->
- ct:log("~p:~p~nself-signed certificate is NOT trusted~n",[?MODULE,?LINE]),
- undefined;
- TrustedCert ->
- %% return the cert from the trusted list, to prevent
- %% issuer spoofing
- {TrustedCert,
- [public_key:pkix_encode(
- 'OTPCertificate', TrustedCert, otp)|Acc]}
- end;
- false ->
- Match = lists:foldl(
- fun(C, undefined) ->
- S = public_key:pkix_normalize_name(C#'OTPCertificate'.tbsCertificate#'OTPTBSCertificate'.subject),
- %% compare the subject to the current issuer
- case Issuer == S of
- true ->
- %% we've found our man
- {public_key:pkix_encode('OTPCertificate', C, otp), C};
- false ->
- undefined
- end;
- (_E, A) ->
- %% already matched
- A
- end, undefined, IntCerts),
- case Match of
- undefined when IntCerts /= TrustedCerts ->
- %% continue the chain by using the trusted CAs
- ct:log("~p:~p~nRan out of intermediate certs, switching to trusted certs~n",[?MODULE,?LINE]),
- build_chain({DER, Cert}, TrustedCerts, TrustedCerts, Acc);
- undefined ->
- ct:log("Can't construct chain of trust beyond ~p~n",
- [?MODULE,?LINE, get_common_name(Cert)]),
- %% can't find the current cert's issuer
- undefined;
- Match ->
- build_chain(Match, IntCerts, TrustedCerts, [DER|Acc])
- end
- end.
-
-%% @doc Given a certificate and a list of trusted or intermediary
-%% certificates, attempt to find a match in the list or bail with
-%% undefined.
-find_issuer(Issuer, Certs) ->
- lists:foldl(
- fun(OTPCert, undefined) ->
- %% check if this certificate matches the issuer
- Normal = public_key:pkix_normalize_name(
- OTPCert#'OTPCertificate'.tbsCertificate#'OTPTBSCertificate'.subject),
- case Normal == Issuer of
- true ->
- OTPCert;
- false ->
- undefined
- end;
- (_E, Acc) ->
- %% already found a match
- Acc
- end, undefined, Certs).
-
-%% @doc Find distribution points for a given CRL and then attempt to
-%% fetch the CRL from the first available.
-fetch_point(#'DistributionPoint'{distributionPoint={fullName, Names}}) ->
- Decoded = [{NameType,
- pubkey_cert_records:transform(Name, decode)}
- || {NameType, Name} <- Names],
- ct:log("~p:~p~ncall fetch(~nDecoded=~p~n)",[?MODULE,?LINE,Decoded]),
- fetch(Decoded).
-
-%% @doc Given a list of locations to retrieve a CRL from, attempt to
-%% retrieve either from a file or http resource and bail as soon as
-%% it can be found.
-%%
-%% Currently, only hand a armored PEM or DER encoded file, with
-%% defaulting to DER.
-%%
-fetch([]) ->
- not_available;
-fetch([{uniformResourceIdentifier, "http"++_=URL}|Rest]) ->
- ct:log("~p:~p~ngetting CRL from ~p~n", [?MODULE,?LINE, URL]),
- case httpc:request(get, {URL, []}, [], [{body_format, binary}]) of
- {ok, {_Status, _Headers, Body}} ->
- case Body of
- <<"-----BEGIN", _/binary>> ->
- ct:log("~p:~p~npublic_key:pem_decode,~nBody=~p~n)",[?MODULE,?LINE,Body]),
- [{'CertificateList',
- DER, _}=CertList] = public_key:pem_decode(Body),
- ct:log("~p:~p~npublic_key:pem_entry_decode,~nCertList=~p~n)",[?MODULE,?LINE,CertList]),
- {DER, public_key:pem_entry_decode(CertList)};
- _ ->
- ct:log("~p:~p~npublic_key:pem_entry_decode,~nBody=~p~n)",[?MODULE,?LINE,{'CertificateList', Body, not_encrypted}]),
- %% assume DER encoded
- try
- public_key:pem_entry_decode({'CertificateList', Body, not_encrypted})
- of
- CertList -> {Body, CertList}
- catch
- _C:_E ->
- ct:log("~p:~p~nfailed DER assumption~nRest=~p", [?MODULE,?LINE,Rest]),
- fetch(Rest)
- end
- end;
- {error, _Reason} ->
- ct:log("~p:~p~nfailed to get CRL ~p~n", [?MODULE,?LINE, _Reason]),
- fetch(Rest);
- Other ->
- ct:log("~p:~p~nreally failed to get CRL ~p~n", [?MODULE,?LINE, Other]),
- fetch(Rest)
- end;
-fetch([Loc|Rest]) ->
- %% unsupported CRL location
- ct:log("~p:~p~nunable to fetch CRL from unsupported location ~p~n",
- [?MODULE,?LINE, Loc]),
- fetch(Rest).
+%%--------------------------------------------------------------------
+%% Internal functions ------------------------------------------------
+%%--------------------------------------------------------------------
+is_idp(idp_crl) ->
+ true;
+is_idp(_) ->
+ false.
+
+init_certs(_,v1_crl, Config) ->
+ {[{v2_crls, false}], Config};
+init_certs(_, idp_crl, Config) ->
+ Port = ?config(httpd_port, Config),
+ {[{crl_port,Port},
+ {issuing_distribution_point, true}], Config
+ };
+init_certs(_,_,Config) ->
+ {[], Config}.
-%% get the common name attribute out of an OTPCertificate record
-get_common_name(OTPCert) ->
- %% You'd think there'd be an easier way than this giant mess, but I
- %% couldn't find one.
- {rdnSequence, Subject} = OTPCert#'OTPCertificate'.tbsCertificate#'OTPTBSCertificate'.subject,
- case [Attribute#'AttributeTypeAndValue'.value || [Attribute] <- Subject,
- Attribute#'AttributeTypeAndValue'.type == ?'id-at-commonName'] of
- [Att] ->
- case Att of
- {teletexString, Str} -> Str;
- {printableString, Str} -> Str;
- {utf8String, Bin} -> binary_to_list(Bin)
- end;
- _ ->
- unknown
- end.
+make_dir_path(PathComponents) ->
+ lists:foldl(fun(F,P0) -> file:make_dir(P=filename:join(P0,F)), P end,
+ "",
+ PathComponents).
diff --git a/lib/ssl/test/ssl_pem_cache_SUITE.erl b/lib/ssl/test/ssl_pem_cache_SUITE.erl
index 843079e2fe..23584dfcdf 100644
--- a/lib/ssl/test/ssl_pem_cache_SUITE.erl
+++ b/lib/ssl/test/ssl_pem_cache_SUITE.erl
@@ -113,9 +113,9 @@ get_pem_cache() ->
{status, _, _, StatusInfo} = sys:get_status(whereis(ssl_manager)),
[_, _,_, _, Prop] = StatusInfo,
State = ssl_test_lib:state(Prop),
- case element(5, State) of
- [_CertDb, _FileRefDb, PemChace] ->
- PemChace;
+ case element(6, State) of
+ [_CertDb, _FileRefDb, PemCache| _] ->
+ PemCache;
_ ->
undefined
end.
diff --git a/lib/ssl/test/ssl_session_cache_SUITE.erl b/lib/ssl/test/ssl_session_cache_SUITE.erl
index c31f6c2d7d..36d086338e 100644
--- a/lib/ssl/test/ssl_session_cache_SUITE.erl
+++ b/lib/ssl/test/ssl_session_cache_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2015. 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
@@ -108,8 +108,12 @@ init_customized_session_cache(Type, Config0) ->
ssl:stop(),
application:load(ssl),
application:set_env(ssl, session_cb, ?MODULE),
- application:set_env(ssl, session_cb_init_args, [Type]),
+ application:set_env(ssl, session_cb_init_args, [{type, Type}]),
ssl:start(),
+ catch (end_per_testcase(list_to_atom("session_cache_process" ++ atom_to_list(Type)),
+ Config)),
+ ets:new(ssl_test, [named_table, public, set]),
+ ets:insert(ssl_test, {type, Type}),
[{watchdog, Dog} | Config].
end_per_testcase(session_cache_process_list, Config) ->
@@ -126,7 +130,11 @@ end_per_testcase(session_cleanup, Config) ->
application:unset_env(ssl, session_delay_cleanup_time),
application:unset_env(ssl, session_lifetime),
end_per_testcase(default_action, Config);
-end_per_testcase(_TestCase, Config) ->
+end_per_testcase(Case, Config) when Case == session_cache_process_list;
+ Case == session_cache_process_mnesia ->
+ ets:delete(ssl_test),
+ Config;
+end_per_testcase(_, Config) ->
Config.
%%--------------------------------------------------------------------
@@ -164,12 +172,13 @@ session_cleanup(Config)when is_list(Config) ->
{status, _, _, StatusInfo} = sys:get_status(whereis(ssl_manager)),
[_, _,_, _, Prop] = StatusInfo,
State = ssl_test_lib:state(Prop),
- Cache = element(2, State),
- SessionTimer = element(6, State),
+ ClientCache = element(2, State),
+ ServerCache = element(3, State),
+ SessionTimer = element(7, State),
Id = proplists:get_value(session_id, SessionInfo),
- CSession = ssl_session_cache:lookup(Cache, {{Hostname, Port}, Id}),
- SSession = ssl_session_cache:lookup(Cache, {Port, Id}),
+ CSession = ssl_session_cache:lookup(ClientCache, {{Hostname, Port}, Id}),
+ SSession = ssl_session_cache:lookup(ServerCache, {Port, Id}),
true = CSession =/= undefined,
true = SSession =/= undefined,
@@ -185,8 +194,8 @@ session_cleanup(Config)when is_list(Config) ->
ct:sleep(?SLEEP), %% Make sure clean has had time to run
- undefined = ssl_session_cache:lookup(Cache, {{Hostname, Port}, Id}),
- undefined = ssl_session_cache:lookup(Cache, {Port, Id}),
+ undefined = ssl_session_cache:lookup(ClientCache, {{Hostname, Port}, Id}),
+ undefined = ssl_session_cache:lookup(ServerCache, {Port, Id}),
process_flag(trap_exit, false),
ssl_test_lib:close(Server),
@@ -208,7 +217,7 @@ get_delay_timers() ->
{status, _, _, StatusInfo} = sys:get_status(whereis(ssl_manager)),
[_, _,_, _, Prop] = StatusInfo,
State = ssl_test_lib:state(Prop),
- case element(7, State) of
+ case element(8, State) of
{undefined, undefined} ->
ct:sleep(?SLEEP),
get_delay_timers();
@@ -236,16 +245,16 @@ session_cache_process_mnesia(Config) when is_list(Config) ->
%%% Session cache API callbacks
%%--------------------------------------------------------------------
-init([Type]) ->
- ets:new(ssl_test, [named_table, public, set]),
- ets:insert(ssl_test, {type, Type}),
- case Type of
+init(Opts) ->
+ case proplists:get_value(type, Opts) of
list ->
spawn(fun() -> session_loop([]) end);
mnesia ->
mnesia:start(),
- {atomic,ok} = mnesia:create_table(sess_cache, []),
- sess_cache
+ Name = atom_to_list(proplists:get_value(role, Opts)),
+ TabName = list_to_atom(Name ++ "sess_cache"),
+ {atomic,ok} = mnesia:create_table(TabName, []),
+ TabName
end.
session_cb() ->
@@ -258,7 +267,7 @@ terminate(Cache) ->
Cache ! terminate;
mnesia ->
catch {atomic,ok} =
- mnesia:delete_table(sess_cache)
+ mnesia:delete_table(Cache)
end.
lookup(Cache, Key) ->
@@ -268,10 +277,10 @@ lookup(Cache, Key) ->
receive {Cache, Res} -> Res end;
mnesia ->
case mnesia:transaction(fun() ->
- mnesia:read(sess_cache,
+ mnesia:read(Cache,
Key, read)
end) of
- {atomic, [{sess_cache, Key, Value}]} ->
+ {atomic, [{Cache, Key, Value}]} ->
Value;
_ ->
undefined
@@ -285,8 +294,8 @@ update(Cache, Key, Value) ->
mnesia ->
{atomic, ok} =
mnesia:transaction(fun() ->
- mnesia:write(sess_cache,
- {sess_cache, Key, Value}, write)
+ mnesia:write(Cache,
+ {Cache, Key, Value}, write)
end)
end.
@@ -297,7 +306,7 @@ delete(Cache, Key) ->
mnesia ->
{atomic, ok} =
mnesia:transaction(fun() ->
- mnesia:delete(sess_cache, Key)
+ mnesia:delete(Cache, Key)
end)
end.
@@ -308,7 +317,7 @@ foldl(Fun, Acc, Cache) ->
receive {Cache, Res} -> Res end;
mnesia ->
Foldl = fun() ->
- mnesia:foldl(Fun, Acc, sess_cache)
+ mnesia:foldl(Fun, Acc, Cache)
end,
{atomic, Res} = mnesia:transaction(Foldl),
Res
@@ -325,7 +334,7 @@ select_session(Cache, PartialKey) ->
mnesia ->
Sel = fun() ->
mnesia:select(Cache,
- [{{sess_cache,{PartialKey,'$1'}, '$2'},
+ [{{Cache,{PartialKey,'$1'}, '$2'},
[],['$$']}])
end,
{atomic, Res} = mnesia:transaction(Sel),
diff --git a/lib/ssl/test/ssl_test_lib.erl b/lib/ssl/test/ssl_test_lib.erl
index 74d71263de..7d0546210c 100644
--- a/lib/ssl/test/ssl_test_lib.erl
+++ b/lib/ssl/test/ssl_test_lib.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2014. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2015. 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
@@ -187,6 +187,7 @@ run_client(Opts) ->
Transport = proplists:get_value(transport, Opts, ssl),
Options = proplists:get_value(options, Opts),
ct:log("~p:~p~n~p:connect(~p, ~p)@~p~n", [?MODULE,?LINE, Transport, Host, Port, Node]),
+ ct:log("SSLOpts: ~p", [Options]),
case rpc:call(Node, Transport, connect, [Host, Port, Options]) of
{ok, Socket} ->
Pid ! {connected, Socket},
@@ -253,7 +254,6 @@ check_result(Server, ServerMsg, Client, ClientMsg) ->
{Port, {data,Debug}} when is_port(Port) ->
ct:log("~p:~p~nopenssl ~s~n",[?MODULE,?LINE, Debug]),
check_result(Server, ServerMsg, Client, ClientMsg);
-
Unexpected ->
Reason = {{expected, {Client, ClientMsg}},
{expected, {Server, ServerMsg}}, {got, Unexpected}},
@@ -267,6 +267,9 @@ check_result(Pid, Msg) ->
{Port, {data,Debug}} when is_port(Port) ->
ct:log("~p:~p~nopenssl ~s~n",[?MODULE,?LINE, Debug]),
check_result(Pid,Msg);
+ %% {Port, {exit_status, Status}} when is_port(Port) ->
+ %% ct:log("~p:~p Exit status: ~p~n",[?MODULE,?LINE, Status]),
+ %% check_result(Pid, Msg);
Unexpected ->
Reason = {{expected, {Pid, Msg}},
{got, Unexpected}},
@@ -811,48 +814,34 @@ openssl_rsa_suites(CounterPart) ->
false ->
"DSS | ECDHE | ECDH"
end,
- lists:filter(fun(Str) ->
- case re:run(Str, Names,[]) of
- nomatch ->
- false;
- _ ->
- true
- end
- end, Ciphers).
+ lists:filter(fun(Str) -> string_regex_filter(Str, Names)
+ end, Ciphers).
openssl_dsa_suites() ->
Ciphers = ssl:cipher_suites(openssl),
- lists:filter(fun(Str) ->
- case re:run(Str,"DSS",[]) of
- nomatch ->
- false;
- _ ->
- true
- end
+ lists:filter(fun(Str) -> string_regex_filter(Str, "DSS")
end, Ciphers).
openssl_ecdsa_suites() ->
Ciphers = ssl:cipher_suites(openssl),
- lists:filter(fun(Str) ->
- case re:run(Str,"ECDHE-ECDSA",[]) of
- nomatch ->
- false;
- _ ->
- true
- end
+ lists:filter(fun(Str) -> string_regex_filter(Str, "ECDHE-ECDSA")
end, Ciphers).
openssl_ecdh_rsa_suites() ->
Ciphers = ssl:cipher_suites(openssl),
- lists:filter(fun(Str) ->
- case re:run(Str,"ECDH-RSA",[]) of
- nomatch ->
- false;
- _ ->
- true
- end
+ lists:filter(fun(Str) -> string_regex_filter(Str, "ECDH-RSA")
end, Ciphers).
+string_regex_filter(Str, Search) when is_list(Str) ->
+ case re:run(Str, Search, []) of
+ nomatch ->
+ false;
+ _ ->
+ true
+ end;
+string_regex_filter(_Str, _Search) ->
+ false.
+
anonymous_suites() ->
Suites =
[{dh_anon, rc4_128, md5},
@@ -860,6 +849,8 @@ anonymous_suites() ->
{dh_anon, '3des_ede_cbc', sha},
{dh_anon, aes_128_cbc, sha},
{dh_anon, aes_256_cbc, sha},
+ {dh_anon, aes_128_gcm, null},
+ {dh_anon, aes_256_gcm, null},
{ecdh_anon,rc4_128,sha},
{ecdh_anon,'3des_ede_cbc',sha},
{ecdh_anon,aes_128_cbc,sha},
@@ -885,8 +876,13 @@ psk_suites() ->
{rsa_psk, aes_128_cbc, sha},
{rsa_psk, aes_256_cbc, sha},
{rsa_psk, aes_128_cbc, sha256},
- {rsa_psk, aes_256_cbc, sha384}
-],
+ {rsa_psk, aes_256_cbc, sha384},
+ {psk, aes_128_gcm, null},
+ {psk, aes_256_gcm, null},
+ {dhe_psk, aes_128_gcm, null},
+ {dhe_psk, aes_256_gcm, null},
+ {rsa_psk, aes_128_gcm, null},
+ {rsa_psk, aes_256_gcm, null}],
ssl_cipher:filter_suites(Suites).
psk_anon_suites() ->
@@ -925,6 +921,10 @@ srp_dss_suites() ->
{srp_dss, aes_256_cbc, sha}],
ssl_cipher:filter_suites(Suites).
+rc4_suites(Version) ->
+ Suites = ssl_cipher:rc4_suites(Version),
+ ssl_cipher:filter_suites(Suites).
+
pem_to_der(File) ->
{ok, PemBin} = file:read_file(File),
public_key:pem_decode(PemBin).
@@ -1130,9 +1130,10 @@ version_flag(sslv3) ->
filter_suites(Ciphers0) ->
Version = tls_record:highest_protocol_version([]),
Supported0 = ssl_cipher:suites(Version)
- ++ ssl_cipher:anonymous_suites()
+ ++ ssl_cipher:anonymous_suites(Version)
++ ssl_cipher:psk_suites(Version)
- ++ ssl_cipher:srp_suites(),
+ ++ ssl_cipher:srp_suites()
+ ++ ssl_cipher:rc4_suites(Version),
Supported1 = ssl_cipher:filter_suites(Supported0),
Supported2 = [ssl:suite_definition(S) || S <- Supported1],
[Cipher || Cipher <- Ciphers0, lists:member(Cipher, Supported2)].
diff --git a/lib/ssl/test/ssl_upgrade_SUITE.erl b/lib/ssl/test/ssl_upgrade_SUITE.erl
new file mode 100644
index 0000000000..c83fb367dc
--- /dev/null
+++ b/lib/ssl/test/ssl_upgrade_SUITE.erl
@@ -0,0 +1,164 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2014-2015. 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/.2
+%%
+%% 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_upgrade_SUITE).
+
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+
+-record(state, {
+ config,
+ server,
+ client,
+ soft
+ }).
+
+all() ->
+ [
+ minor_upgrade,
+ major_upgrade
+ ].
+
+init_per_suite(Config0) ->
+ catch crypto:stop(),
+ try {crypto:start(), erlang:system_info({wordsize, internal}) == erlang:system_info({wordsize, external})} of
+ {ok, true} ->
+ case ct_release_test:init(Config0) of
+ {skip, Reason} ->
+ {skip, Reason};
+ Config ->
+ Result =
+ (catch make_certs:all(?config(data_dir, Config),
+ ?config(priv_dir, Config))),
+ ct:log("Make certs ~p~n", [Result]),
+ ssl_test_lib:cert_options(Config)
+ end;
+ {ok, false} ->
+ {skip, "Test server will not handle halfwordemulator correctly. Skip as halfwordemulator is deprecated"}
+ catch _:_ ->
+ {skip, "Crypto did not start"}
+ end.
+
+end_per_suite(Config) ->
+ ct_release_test:cleanup(Config),
+ crypto:stop().
+
+init_per_testcase(_TestCase, Config) ->
+ Config.
+end_per_testcase(_TestCase, Config) ->
+ Config.
+
+major_upgrade(Config) when is_list(Config) ->
+ ct_release_test:upgrade(ssl, major,{?MODULE, #state{config = Config}}, Config).
+
+minor_upgrade(Config) when is_list(Config) ->
+ ct_release_test:upgrade(ssl, minor,{?MODULE, #state{config = Config}}, Config).
+
+upgrade_init(CTData, #state{config = Config} = State) ->
+ {ok, {_, _, Up, _Down}} = ct_release_test:get_appup(CTData, ssl),
+ ct:pal("Up: ~p", [Up]),
+ Soft = is_soft(Up), %% It is symmetrical, if upgrade is soft so is downgrade
+ case Soft of
+ true ->
+ {Server, Client} = soft_start_connection(Config),
+ State#state{server = Server, client = Client,
+ soft = Soft};
+ false ->
+ State#state{soft = Soft}
+ end.
+
+upgrade_upgraded(_, #state{soft = false, config = Config} = State) ->
+ {Server, Client} = restart_start_connection(Config),
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client),
+ State;
+
+upgrade_upgraded(_, #state{server = Server0, client = Client0,
+ config = Config, soft = true} = State) ->
+ Server0 ! changed_version,
+ Client0 ! changed_version,
+ ssl_test_lib:check_result(Server0, ok, Client0, ok),
+ ssl_test_lib:close(Server0),
+ ssl_test_lib:close(Client0),
+ {Server, Client} = soft_start_connection(Config),
+ State#state{server = Server, client = Client}.
+
+upgrade_downgraded(_, #state{soft = false, config = Config} = State) ->
+ {Server, Client} = restart_start_connection(Config),
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client),
+ State;
+
+upgrade_downgraded(_, #state{server = Server, client = Client, soft = true} = State) ->
+ Server ! changed_version,
+ Client ! changed_version,
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client),
+ State.
+
+use_connection(Socket) ->
+ ssl_test_lib:send_recv_result_active(Socket),
+ receive
+ changed_version ->
+ ssl_test_lib:send_recv_result_active(Socket)
+ end.
+
+soft_start_connection(Config) ->
+ ClientOpts = ?config(client_verification_opts, Config),
+ ServerOpts = ?config(server_verification_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, use_connection, []}},
+ {options, ServerOpts}]),
+
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, use_connection, []}},
+ {options, ClientOpts}]),
+ {Server, Client}.
+
+restart_start_connection(Config) ->
+ ClientOpts = ?config(client_verification_opts, Config),
+ ServerOpts = ?config(server_verification_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {ssl_test_lib, send_recv_result_active, []}},
+ {options, ServerOpts}]),
+
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {ssl_test_lib, send_recv_result_active, []}},
+ {options, ClientOpts}]),
+ {Server, Client}.
+
+is_soft([{restart_application, ssl}]) ->
+ false;
+is_soft(_) ->
+ true.
+
diff --git a/lib/ssl/vsn.mk b/lib/ssl/vsn.mk
index bda974da0e..171147adf2 100644
--- a/lib/ssl/vsn.mk
+++ b/lib/ssl/vsn.mk
@@ -1 +1 @@
-SSL_VSN = 5.3.8
+SSL_VSN = 7.0