aboutsummaryrefslogtreecommitdiffstats
path: root/lib/ssl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/ssl')
-rw-r--r--lib/ssl/doc/src/Makefile18
-rw-r--r--lib/ssl/doc/src/ssl.xml24
-rw-r--r--lib/ssl/examples/certs/Makefile6
-rw-r--r--lib/ssl/examples/src/Makefile8
-rw-r--r--lib/ssl/examples/src/client_server.erl24
-rw-r--r--lib/ssl/src/Makefile31
-rw-r--r--lib/ssl/src/ssl.appup.src2
-rw-r--r--lib/ssl/src/ssl.erl72
-rw-r--r--lib/ssl/src/ssl_alert.erl4
-rw-r--r--lib/ssl/src/ssl_alert.hrl4
-rw-r--r--lib/ssl/src/ssl_certificate.erl9
-rw-r--r--lib/ssl/src/ssl_certificate_db.erl193
-rw-r--r--lib/ssl/src/ssl_cipher.erl320
-rw-r--r--lib/ssl/src/ssl_cipher.hrl46
-rw-r--r--lib/ssl/src/ssl_connection.erl566
-rw-r--r--lib/ssl/src/ssl_handshake.erl532
-rw-r--r--lib/ssl/src/ssl_handshake.hrl29
-rw-r--r--lib/ssl/src/ssl_internal.hrl10
-rw-r--r--lib/ssl/src/ssl_manager.erl204
-rw-r--r--lib/ssl/src/ssl_record.erl40
-rw-r--r--lib/ssl/src/ssl_record.hrl13
-rw-r--r--lib/ssl/src/ssl_session.erl120
-rw-r--r--lib/ssl/src/ssl_session_cache.erl4
-rw-r--r--lib/ssl/src/ssl_ssl3.erl54
-rw-r--r--lib/ssl/src/ssl_tls1.erl188
-rw-r--r--lib/ssl/test/Makefile10
-rw-r--r--lib/ssl/test/ssl_basic_SUITE.erl412
-rw-r--r--lib/ssl/test/ssl_cipher_SUITE.erl66
-rw-r--r--lib/ssl/test/ssl_dist_SUITE.erl4
-rw-r--r--lib/ssl/test/ssl_handshake_SUITE.erl3
-rw-r--r--lib/ssl/test/ssl_packet_SUITE.erl355
-rw-r--r--lib/ssl/test/ssl_payload_SUITE.erl58
-rw-r--r--lib/ssl/test/ssl_session_cache_SUITE.erl1
-rw-r--r--lib/ssl/test/ssl_test_lib.erl34
-rw-r--r--lib/ssl/test/ssl_to_openssl_SUITE.erl594
-rw-r--r--lib/ssl/vsn.mk2
36 files changed, 2382 insertions, 1678 deletions
diff --git a/lib/ssl/doc/src/Makefile b/lib/ssl/doc/src/Makefile
index 5d808d6727..8736913c72 100644
--- a/lib/ssl/doc/src/Makefile
+++ b/lib/ssl/doc/src/Makefile
@@ -117,15 +117,15 @@ debug opt:
include $(ERL_TOP)/make/otp_release_targets.mk
release_docs_spec: docs
- $(INSTALL_DIR) $(RELSYSDIR)/doc/pdf
- $(INSTALL_DATA) $(TOP_PDF_FILE) $(RELSYSDIR)/doc/pdf
- $(INSTALL_DIR) $(RELSYSDIR)/doc/html
+ $(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf"
+ $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf"
+ $(INSTALL_DIR) "$(RELSYSDIR)/doc/html"
$(INSTALL_DATA) $(HTMLDIR)/* \
- $(RELSYSDIR)/doc/html
- $(INSTALL_DATA) $(INFO_FILE) $(RELSYSDIR)
- $(INSTALL_DIR) $(RELEASE_PATH)/man/man3
- $(INSTALL_DATA) $(MAN3DIR)/* $(RELEASE_PATH)/man/man3
- $(INSTALL_DIR) $(RELEASE_PATH)/man/man6
- $(INSTALL_DATA) $(MAN6_FILES) $(RELEASE_PATH)/man/man6
+ "$(RELSYSDIR)/doc/html"
+ $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)"
+ $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3"
+ $(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3"
+ $(INSTALL_DIR) "$(RELEASE_PATH)/man/man6"
+ $(INSTALL_DATA) $(MAN6_FILES) "$(RELEASE_PATH)/man/man6"
release_spec:
diff --git a/lib/ssl/doc/src/ssl.xml b/lib/ssl/doc/src/ssl.xml
index e019654685..5098d26a3a 100644
--- a/lib/ssl/doc/src/ssl.xml
+++ b/lib/ssl/doc/src/ssl.xml
@@ -36,12 +36,16 @@
<list type="bulleted">
<item>ssl requires the crypto and public_key applications.</item>
- <item>Supported SSL/TLS-versions are SSL-3.0 and TLS-1.0 </item>
+ <item>Supported SSL/TLS-versions are SSL-3.0 and TLS-1.0, experimental
+ support for TLS-1.1 and TLS-1.2 is also available (no support for elliptic curve cipher suites yet).</item>
<item>For security reasons sslv2 is not supported.</item>
<item>Ephemeral Diffie-Hellman cipher suites are supported
but not Diffie Hellman Certificates cipher suites.</item>
<item>Export cipher suites are not supported as the
U.S. lifted its export restrictions in early 2000.</item>
+ <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. </item>
</list>
@@ -75,7 +79,7 @@
{keyfile, path()} | {password, string()} |
{cacerts, [der_encoded()]} | {cacertfile, path()} |
|{dh, der_encoded()} | {dhfile, path()} | {ciphers, ciphers()} |
- {ssl_imp, ssl_imp()} | {reuse_sessions, boolean()} | {reuse_session, fun()}
+ {ssl_imp, ssl_imp()}| {reuse_sessions, boolean()} | {reuse_session, fun()}
</c></p>
<p><c>transportoption() = {CallbackModule, DataTag, ClosedTag}
@@ -106,7 +110,7 @@
<p><c>sslsocket() - opaque to the user. </c></p>
- <p><c>protocol() = sslv3 | tlsv1 </c></p>
+ <p><c>protocol() = sslv3 | tlsv1 | 'tlsv1.1' | 'tlsv1.2' </c></p>
<p><c>ciphers() = [ciphersuite()] | string() (according to old API)</c></p>
@@ -193,13 +197,13 @@
</item>
<tag>{depth, integer()}</tag>
- <item>Specifies the maximum
- verification depth, i.e. how far in a chain of certificates the
- verification process can proceed before the verification is
- considered to fail. Peer certificate = 0, CA certificate = 1,
- higher level CA certificate = 2, etc. The value 2 thus means
- that a chain can at most contain peer cert, CA cert, next CA
- cert, and an additional CA cert. The default value is 1.
+ <item>
+ The depth is the maximum number of non-self-issued
+ intermediate certificates that may follow the peer certificate
+ in a valid certification path. So if depth is 0 the PEER must
+ be signed by the trusted ROOT-CA directly, if 1 the path can
+ be PEER, CA, ROOT-CA, if it is 2 PEER, CA, CA, ROOT-CA and so
+ on. The default value is 1.
</item>
<tag>{verify_fun, {Verifyfun :: fun(), InitialUserState :: term()}}</tag>
diff --git a/lib/ssl/examples/certs/Makefile b/lib/ssl/examples/certs/Makefile
index a4f067ade6..549f11d709 100644
--- a/lib/ssl/examples/certs/Makefile
+++ b/lib/ssl/examples/certs/Makefile
@@ -54,8 +54,8 @@ docs:
include $(ERL_TOP)/make/otp_release_targets.mk
release_spec: opt
- $(INSTALL_DIR) $(RELSYSDIR)/examples/certs
+ $(INSTALL_DIR) "$(RELSYSDIR)/examples/certs"
tar cf - etc | \
- (cd $(RELSYSDIR)/examples/certs; tar xf -)
- chmod -R ug+rw $(RELSYSDIR)/examples
+ (cd "$(RELSYSDIR)/examples/certs"; tar xf -)
+ chmod -R ug+rw "$(RELSYSDIR)/examples"
release_docs_spec:
diff --git a/lib/ssl/examples/src/Makefile b/lib/ssl/examples/src/Makefile
index c5f31b689c..420634c97e 100644
--- a/lib/ssl/examples/src/Makefile
+++ b/lib/ssl/examples/src/Makefile
@@ -63,10 +63,10 @@ docs:
include $(ERL_TOP)/make/otp_release_targets.mk
release_spec: opt
- $(INSTALL_DIR) $(RELSYSDIR)/examples/src
- $(INSTALL_DIR) $(RELSYSDIR)/examples/ebin
- (cd ..; tar cf - src ebin | (cd $(RELSYSDIR)/examples; tar xf -))
- chmod -R ug+w $(RELSYSDIR)/examples
+ $(INSTALL_DIR) "$(RELSYSDIR)/examples/src"
+ $(INSTALL_DIR) "$(RELSYSDIR)/examples/ebin"
+ (cd ..; tar cf - src ebin | (cd "$(RELSYSDIR)/examples"; tar xf -))
+ chmod -R ug+w "$(RELSYSDIR)/examples"
release_docs_spec:
diff --git a/lib/ssl/examples/src/client_server.erl b/lib/ssl/examples/src/client_server.erl
index baf5a9185e..133a1764bc 100644
--- a/lib/ssl/examples/src/client_server.erl
+++ b/lib/ssl/examples/src/client_server.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2003-2012. 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
@@ -21,18 +21,14 @@
-module(client_server).
--export([start/0, start/1, init_connect/1]).
+-export([start/0, init_connect/1]).
start() ->
- start([ssl, subject]).
-
-start(CertOpts) ->
%% Start ssl application
+ application:start(crypto),
+ application:start(public_key),
application:start(ssl),
- %% Always seed
- ssl:seed("ellynatefttidppohjeh"),
-
%% Let the current process be the server that listens and accepts
%% Listen
{ok, LSock} = ssl:listen(0, mk_opts(listen)),
@@ -40,14 +36,14 @@ start(CertOpts) ->
io:fwrite("Listen: port = ~w.~n", [LPort]),
%% Spawn the client process that connects to the server
- spawn(?MODULE, init_connect, [{LPort, CertOpts}]),
+ spawn(?MODULE, init_connect, [LPort]),
%% Accept
{ok, ASock} = ssl:transport_accept(LSock),
ok = ssl:ssl_accept(ASock),
io:fwrite("Accept: accepted.~n"),
- {ok, Cert} = ssl:peercert(ASock, CertOpts),
- io:fwrite("Accept: peer cert:~n~p~n", [Cert]),
+ {ok, Cert} = ssl:peercert(ASock),
+ io:fwrite("Accept: peer cert:~n~p~n", [public_key:pkix_decode_cert(Cert, otp)]),
io:fwrite("Accept: sending \"hello\".~n"),
ssl:send(ASock, "hello"),
{error, closed} = ssl:recv(ASock, 0),
@@ -59,12 +55,12 @@ start(CertOpts) ->
%% Client connect
-init_connect({LPort, CertOpts}) ->
+init_connect(LPort) ->
{ok, Host} = inet:gethostname(),
{ok, CSock} = ssl:connect(Host, LPort, mk_opts(connect)),
io:fwrite("Connect: connected.~n"),
- {ok, Cert} = ssl:peercert(CSock, CertOpts),
- io:fwrite("Connect: peer cert:~n~p~n", [Cert]),
+ {ok, Cert} = ssl:peercert(CSock),
+ io:fwrite("Connect: peer cert:~n~p~n", [public_key:pkix_decode_cert(Cert, otp)]),
{ok, Data} = ssl:recv(CSock, 0),
io:fwrite("Connect: got data: ~p~n", [Data]),
io:fwrite("Connect: closing and terminating.~n"),
diff --git a/lib/ssl/src/Makefile b/lib/ssl/src/Makefile
index dc69b53b28..a08444b8dd 100644
--- a/lib/ssl/src/Makefile
+++ b/lib/ssl/src/Makefile
@@ -37,6 +37,9 @@ RELSYSDIR = $(RELEASE_PATH)/lib/ssl-$(VSN)
# Common Macros
# ----------------------------------------------------
+BEHAVIOUR_MODULES= \
+ ssl_session_cache_api
+
MODULES= \
ssl \
ssl_alert \
@@ -53,7 +56,6 @@ MODULES= \
ssl_handshake \
ssl_manager \
ssl_session \
- ssl_session_cache_api \
ssl_session_cache \
ssl_record \
ssl_ssl2 \
@@ -66,10 +68,15 @@ INTERNAL_HRL_FILES = \
ssl_alert.hrl ssl_cipher.hrl ssl_handshake.hrl ssl_internal.hrl \
ssl_record.hrl
-ERL_FILES= $(MODULES:%=%.erl)
+ERL_FILES= \
+ $(MODULES:%=%.erl) \
+ $(BEHAVIOUR_MODULES:%=%.erl)
+
TARGET_FILES= $(MODULES:%=$(EBIN)/%.$(EMULATOR))
+BEHAVIOUR_TARGET_FILES= $(BEHAVIOUR_MODULES:%=$(EBIN)/%.$(EMULATOR))
+
APP_FILE= ssl.app
APPUP_FILE= ssl.appup
@@ -83,6 +90,7 @@ APPUP_TARGET= $(EBIN)/$(APPUP_FILE)
# ----------------------------------------------------
EXTRA_ERLC_FLAGS = +warn_unused_vars
ERL_COMPILE_FLAGS += -I$(ERL_TOP)/lib/kernel/src \
+ -pz $(EBIN) \
-pz $(ERL_TOP)/lib/public_key/ebin \
$(EXTRA_ERLC_FLAGS) -DVSN=\"$(VSN)\"
@@ -91,6 +99,8 @@ ERL_COMPILE_FLAGS += -I$(ERL_TOP)/lib/kernel/src \
# Targets
# ----------------------------------------------------
+$(TARGET_FILES): $(BEHAVIOUR_TARGET_FILES)
+
debug opt: $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET)
clean:
@@ -105,23 +115,18 @@ $(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk
docs:
+
# ----------------------------------------------------
# Release Target
# ----------------------------------------------------
include $(ERL_TOP)/make/otp_release_targets.mk
release_spec: opt
- $(INSTALL_DIR) $(RELSYSDIR)/src
- $(INSTALL_DATA) $(ERL_FILES) $(INTERNAL_HRL_FILES) $(RELSYSDIR)/src
- $(INSTALL_DIR) $(RELSYSDIR)/ebin
- $(INSTALL_DATA) $(TARGET_FILES) $(APP_TARGET) \
- $(APPUP_TARGET) $(RELSYSDIR)/ebin
+ $(INSTALL_DIR) "$(RELSYSDIR)/src"
+ $(INSTALL_DATA) $(ERL_FILES) $(INTERNAL_HRL_FILES) "$(RELSYSDIR)/src"
+ $(INSTALL_DIR) "$(RELSYSDIR)/ebin"
+ $(INSTALL_DATA) $(BEHAVIOUR_TARGET_FILES) $(TARGET_FILES) $(APP_TARGET) \
+ $(APPUP_TARGET) "$(RELSYSDIR)/ebin"
release_docs_spec:
-
-
-
-
-
-
diff --git a/lib/ssl/src/ssl.appup.src b/lib/ssl/src/ssl.appup.src
index e346b1e9e6..76550fa04b 100644
--- a/lib/ssl/src/ssl.appup.src
+++ b/lib/ssl/src/ssl.appup.src
@@ -1,11 +1,13 @@
%% -*- erlang -*-
{"%VSN%",
[
+ {"5.0.1", [{restart_application, ssl}]},
{"5.0", [{restart_application, ssl}]},
{<<"4\\.*">>, [{restart_application, ssl}]},
{<<"3\\.*">>, [{restart_application, ssl}]}
],
[
+ {"5.0.1", [{restart_application, ssl}]},
{"5.0", [{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 0bcdffbeff..40d933a256 100644
--- a/lib/ssl/src/ssl.erl
+++ b/lib/ssl/src/ssl.erl
@@ -25,12 +25,13 @@
-export([start/0, start/1, stop/0, transport_accept/1,
transport_accept/2, ssl_accept/1, ssl_accept/2, ssl_accept/3,
- cipher_suites/0, cipher_suites/1, close/1, shutdown/2,
+ cipher_suites/0, cipher_suites/1, suite_definition/1,
+ close/1, shutdown/2,
connect/3, connect/2, connect/4, connection_info/1,
controlling_process/2, listen/2, pid/1, peername/1, peercert/1,
recv/2, recv/3, send/2, getopts/2, setopts/2, sockname/1,
versions/0, session_info/1, format_error/1,
- renegotiate/1, prf/5]).
+ renegotiate/1, prf/5, clear_pem_cache/0, random_bytes/1]).
-deprecated({pid, 1, next_major_release}).
@@ -304,6 +305,15 @@ peercert(#sslsocket{pid = Pid}) ->
end.
%%--------------------------------------------------------------------
+-spec suite_definition(cipher_suite()) -> erl_cipher_suite().
+%%
+%% Description: Return erlang cipher suite definition.
+%%--------------------------------------------------------------------
+suite_definition(S) ->
+ {KeyExchange, Cipher, Hash, _} = ssl_cipher:suite_definition(S),
+ {KeyExchange, Cipher, Hash}.
+
+%%--------------------------------------------------------------------
-spec cipher_suites() -> [erl_cipher_suite()].
-spec cipher_suites(erlang | openssl) -> [erl_cipher_suite()] | [string()].
@@ -314,7 +324,7 @@ cipher_suites() ->
cipher_suites(erlang) ->
Version = ssl_record:highest_protocol_version([]),
- [ssl_cipher:suite_definition(S) || S <- ssl_cipher:suites(Version)];
+ [suite_definition(S) || S <- ssl_cipher:suites(Version)];
cipher_suites(openssl) ->
Version = ssl_record:highest_protocol_version([]),
@@ -408,7 +418,7 @@ session_info(#sslsocket{pid = Pid, fd = new_ssl}) ->
versions() ->
Vsns = ssl_record:supported_protocol_versions(),
SupportedVsns = [ssl_record:protocol_version(Vsn) || Vsn <- Vsns],
- AvailableVsns = ?DEFAULT_SUPPORTED_VERSIONS,
+ AvailableVsns = ?ALL_SUPPORTED_VERSIONS,
[{ssl_app, ?VSN}, {supported, SupportedVsns}, {available, AvailableVsns}].
@@ -431,6 +441,15 @@ prf(#sslsocket{pid = Pid, fd = new_ssl},
Secret, Label, Seed, WantedLength) ->
ssl_connection:prf(Pid, Secret, Label, Seed, WantedLength).
+
+%%--------------------------------------------------------------------
+-spec clear_pem_cache() -> ok.
+%%
+%% Description: Clear the PEM cache
+%%--------------------------------------------------------------------
+clear_pem_cache() ->
+ ssl_manager:clear_pem_cache().
+
%%---------------------------------------------------------------
-spec format_error({error, term()}) -> list().
%%
@@ -465,6 +484,23 @@ format_error(Error) ->
Other
end.
+%%--------------------------------------------------------------------
+-spec random_bytes(integer()) -> binary().
+
+%%
+%% Description: Generates cryptographically secure random sequence if possible
+%% fallbacks on pseudo random function
+%%--------------------------------------------------------------------
+random_bytes(N) ->
+ try crypto:strong_rand_bytes(N) of
+ RandBytes ->
+ RandBytes
+ catch
+ error:low_entropy ->
+ crypto:rand_bytes(N)
+ end.
+
+
%%%--------------------------------------------------------------
%%% Internal functions
%%%--------------------------------------------------------------------
@@ -532,7 +568,7 @@ handle_options(Opts0, _Role) ->
throw({error, {eoptions, {verify, Value}}})
end,
- CertFile = handle_option(certfile, Opts, ""),
+ CertFile = handle_option(certfile, Opts, <<>>),
SSLOptions = #ssl_options{
versions = handle_option(versions, Opts, []),
@@ -619,8 +655,12 @@ validate_option(depth, Value) when is_integer(Value),
validate_option(cert, Value) when Value == undefined;
is_binary(Value) ->
Value;
-validate_option(certfile, Value) when Value == undefined; is_list(Value) ->
+validate_option(certfile, undefined = Value) ->
+ Value;
+validate_option(certfile, Value) when is_binary(Value) ->
Value;
+validate_option(certfile, Value) when is_list(Value) ->
+ list_to_binary(Value);
validate_option(key, undefined) ->
undefined;
@@ -631,8 +671,13 @@ validate_option(key, {KeyType, Value}) when is_binary(Value),
KeyType == 'DSAPrivateKey';
KeyType == 'PrivateKeyInfo' ->
{KeyType, Value};
-validate_option(keyfile, Value) when is_list(Value) ->
+
+validate_option(keyfile, undefined) ->
+ <<>>;
+validate_option(keyfile, Value) when is_binary(Value) ->
Value;
+validate_option(keyfile, Value) when is_list(Value), Value =/= "" ->
+ list_to_binary(Value);
validate_option(password, Value) when is_list(Value) ->
Value;
@@ -642,16 +687,20 @@ validate_option(cacerts, Value) when Value == undefined;
%% certfile must be present in some cases otherwhise it can be set
%% to the empty string.
validate_option(cacertfile, undefined) ->
- "";
-validate_option(cacertfile, Value) when is_list(Value), Value =/= "" ->
+ <<>>;
+validate_option(cacertfile, Value) when is_binary(Value) ->
Value;
+validate_option(cacertfile, Value) when is_list(Value), Value =/= ""->
+ list_to_binary(Value);
validate_option(dh, Value) when Value == undefined;
is_binary(Value) ->
Value;
validate_option(dhfile, undefined = Value) ->
Value;
-validate_option(dhfile, Value) when is_list(Value), Value =/= "" ->
+validate_option(dhfile, Value) when is_binary(Value) ->
Value;
+validate_option(dhfile, Value) when is_list(Value), Value =/= "" ->
+ list_to_binary(Value);
validate_option(ciphers, Value) when is_list(Value) ->
Version = ssl_record:highest_protocol_version([]),
try cipher_suites(Version, Value)
@@ -687,7 +736,8 @@ validate_option(Opt, Value) ->
validate_versions([], Versions) ->
Versions;
-validate_versions([Version | Rest], Versions) when Version == 'tlsv1.1';
+validate_versions([Version | Rest], Versions) when Version == 'tlsv1.2';
+ Version == 'tlsv1.1';
Version == tlsv1;
Version == sslv3 ->
validate_versions(Rest, Versions);
diff --git a/lib/ssl/src/ssl_alert.erl b/lib/ssl/src/ssl_alert.erl
index eb1228afa4..222b3f1ad7 100644
--- a/lib/ssl/src/ssl_alert.erl
+++ b/lib/ssl/src/ssl_alert.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2012. 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
@@ -84,6 +84,8 @@ description_txt(?DECOMPRESSION_FAILURE) ->
"decompression failure";
description_txt(?HANDSHAKE_FAILURE) ->
"handshake failure";
+description_txt(?NO_CERTIFICATE_RESERVED) ->
+ "No certificate reserved";
description_txt(?BAD_CERTIFICATE) ->
"bad certificate";
description_txt(?UNSUPPORTED_CERTIFICATE) ->
diff --git a/lib/ssl/src/ssl_alert.hrl b/lib/ssl/src/ssl_alert.hrl
index 6470b82d50..92548edab7 100644
--- a/lib/ssl/src/ssl_alert.hrl
+++ b/lib/ssl/src/ssl_alert.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2012. 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
@@ -43,6 +43,7 @@
%% record_overflow(22),
%% decompression_failure(30),
%% handshake_failure(40),
+%% no_certificate_RESERVED(41), %% Only sslv3
%% bad_certificate(42),
%% unsupported_certificate(43),
%% certificate_revoked(44),
@@ -69,6 +70,7 @@
-define(RECORD_OVERFLOW, 22).
-define(DECOMPRESSION_FAILURE, 30).
-define(HANDSHAKE_FAILURE, 40).
+-define(NO_CERTIFICATE_RESERVED, 41).
-define(BAD_CERTIFICATE, 42).
-define(UNSUPPORTED_CERTIFICATE, 43).
-define(CERTIFICATE_REVOKED, 44).
diff --git a/lib/ssl/src/ssl_certificate.erl b/lib/ssl/src/ssl_certificate.erl
index 0931b86782..86f5617b54 100644
--- a/lib/ssl/src/ssl_certificate.erl
+++ b/lib/ssl/src/ssl_certificate.erl
@@ -103,7 +103,7 @@ certificate_chain(OwnCert, CertDbHandle, CertsDbRef) ->
ErlCert = public_key:pkix_decode_cert(OwnCert, otp),
certificate_chain(ErlCert, OwnCert, CertDbHandle, CertsDbRef, [OwnCert]).
%%--------------------------------------------------------------------
--spec file_to_certificats(string(), term()) -> [der_cert()].
+-spec file_to_certificats(binary(), term()) -> [der_cert()].
%%
%% Description: Return list of DER encoded certificates.
%%--------------------------------------------------------------------
@@ -172,7 +172,12 @@ extensions_list(Extensions) ->
%% Description:
%%--------------------------------------------------------------------
signature_type(RSA) when RSA == ?sha1WithRSAEncryption;
- RSA == ?md5WithRSAEncryption ->
+ RSA == ?md5WithRSAEncryption;
+ RSA == ?sha224WithRSAEncryption;
+ RSA == ?sha256WithRSAEncryption;
+ RSA == ?sha384WithRSAEncryption;
+ RSA == ?sha512WithRSAEncryption
+ ->
rsa;
signature_type(?'id-dsa-with-sha1') ->
dsa.
diff --git a/lib/ssl/src/ssl_certificate_db.erl b/lib/ssl/src/ssl_certificate_db.erl
index cb2473576a..67d00f0da7 100644
--- a/lib/ssl/src/ssl_certificate_db.erl
+++ b/lib/ssl/src/ssl_certificate_db.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2012. 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
@@ -24,12 +24,13 @@
-module(ssl_certificate_db).
-include("ssl_internal.hrl").
-include_lib("public_key/include/public_key.hrl").
+-include_lib("kernel/include/file.hrl").
-export([create/0, remove/1, add_trusted_certs/3,
- remove_trusted_certs/2, lookup_trusted_cert/4, foldl/3,
- lookup_cached_certs/2, cache_pem_file/4, uncache_pem_file/2, lookup/2]).
-
--type time() :: {non_neg_integer(), non_neg_integer(), non_neg_integer()}.
+ remove_trusted_certs/2, insert/3, remove/2, clear/1, db_size/1,
+ ref_count/3, lookup_trusted_cert/4, foldl/3,
+ lookup_cached_pem/2, cache_pem_file/2, cache_pem_file/3,
+ lookup/2]).
%%====================================================================
%% Internal application API
@@ -43,9 +44,14 @@
%% the process that called create may call the other functions.
%%--------------------------------------------------------------------
create() ->
- [ets:new(ssl_otp_certificate_db, [set, protected]),
- ets:new(ssl_file_to_ref, [set, protected]),
- ets:new(ssl_pid_to_file, [bag, private])].
+ [%% Let connection process delete trusted certs
+ %% that can only belong to one connection. (Supplied directly
+ %% on DER format to ssl:connect/listen.)
+ 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])
+ ].
%%--------------------------------------------------------------------
-spec remove([db_handle()]) -> term().
@@ -53,7 +59,9 @@ create() ->
%% Description: Removes database db
%%--------------------------------------------------------------------
remove(Dbs) ->
- lists:foreach(fun(Db) -> true = ets:delete(Db) end, Dbs).
+ lists:foreach(fun(Db) ->
+ true = ets:delete(Db)
+ end, Dbs).
%%--------------------------------------------------------------------
-spec lookup_trusted_cert(db_handle(), certdb_ref(), serialnumber(), issuer()) ->
@@ -72,11 +80,14 @@ lookup_trusted_cert(DbHandle, Ref, SerialNumber, Issuer) ->
{ok, Certs}
end.
-lookup_cached_certs(DbHandle, File) ->
- ets:lookup(DbHandle, {file, File}).
+lookup_cached_pem([_, _, PemChache], MD5) ->
+ lookup_cached_pem(PemChache, MD5);
+lookup_cached_pem(PemChache, MD5) ->
+ lookup(MD5, PemChache).
%%--------------------------------------------------------------------
--spec add_trusted_certs(pid(), string() | {der, list()}, [db_handle()]) -> {ok, [db_handle()]}.
+-spec add_trusted_certs(pid(), {erlang:timestamp(), string()} |
+ {der, list()}, [db_handle()]) -> {ok, [db_handle()]}.
%%
%% Description: Adds the trusted certificates from file <File> to the
%% runtime database. Returns Ref that should be handed to lookup_trusted_cert
@@ -86,82 +97,55 @@ add_trusted_certs(_Pid, {der, DerList}, [CerDb, _,_]) ->
NewRef = make_ref(),
add_certs_from_der(DerList, NewRef, CerDb),
{ok, NewRef};
-add_trusted_certs(Pid, File, [CertsDb, FileToRefDb, PidToFileDb]) ->
- Ref = case lookup(File, FileToRefDb) of
- undefined ->
- NewRef = make_ref(),
- add_certs_from_file(File, NewRef, CertsDb),
- insert(File, NewRef, 1, FileToRefDb),
- NewRef;
- [OldRef] ->
- ref_count(File,FileToRefDb,1),
- OldRef
- end,
- insert(Pid, File, PidToFileDb),
- {ok, Ref}.
+
+add_trusted_certs(_Pid, File, [CertsDb, RefDb, PemChache] = Db) ->
+ MD5 = crypto:md5(File),
+ case lookup_cached_pem(Db, MD5) of
+ [{_Content, Ref}] ->
+ ref_count(Ref, RefDb, 1),
+ {ok, Ref};
+ [Content] ->
+ Ref = make_ref(),
+ update_counter(Ref, 1, RefDb),
+ insert(MD5, {Content, Ref}, PemChache),
+ add_certs_from_pem(Content, Ref, CertsDb),
+ {ok, Ref};
+ undefined ->
+ new_trusted_cert_entry({MD5, File}, Db)
+ end.
%%--------------------------------------------------------------------
--spec cache_pem_file(pid(), string(), time(), [db_handle()]) -> term().
+-spec cache_pem_file({binary(), binary()}, [db_handle()]) -> term().
+-spec cache_pem_file(reference(), {binary(), binary()}, [db_handle()]) -> term().
%%
%% Description: Cache file as binary in DB
%%--------------------------------------------------------------------
-cache_pem_file(Pid, File, Time, [CertsDb, _FileToRefDb, PidToFileDb]) ->
- {ok, PemBin} = file:read_file(File),
+cache_pem_file({MD5, File}, [_CertsDb, _RefDb, PemChache]) ->
+ {ok, PemBin} = file:read_file(File),
Content = public_key:pem_decode(PemBin),
- insert({file, File}, {Time, Content}, CertsDb),
- insert(Pid, File, PidToFileDb),
+ insert(MD5, Content, PemChache),
{ok, Content}.
-%--------------------------------------------------------------------
--spec uncache_pem_file(string(), [db_handle()]) -> no_return().
-%%
-%% Description: If a cached file is no longer valid (changed on disk)
-%% we must terminate the connections using the old file content, and
-%% when those processes are finish the cache will be cleaned. It is
-%% a rare but possible case a new ssl client/server is started with
-%% a filename with the same name as previously started client/server
-%% but with different content.
-%% --------------------------------------------------------------------
-uncache_pem_file(File, [_CertsDb, _FileToRefDb, PidToFileDb]) ->
- Pids = select(PidToFileDb, [{{'$1', File},[],['$$']}]),
- lists:foreach(fun([Pid]) ->
- exit(Pid, shutdown)
- end, Pids).
-
-%%--------------------------------------------------------------------
--spec remove_trusted_certs(pid(), [db_handle()]) -> term().
-
-%%
-%% Description: Removes trusted certs originating from
-%% the file associated to Pid from the runtime database.
-%%--------------------------------------------------------------------
-remove_trusted_certs(Pid, [CertsDb, FileToRefDb, PidToFileDb]) ->
- Files = lookup(Pid, PidToFileDb),
- delete(Pid, PidToFileDb),
- Clear = fun(File) ->
- delete({file,File}, CertsDb),
- try
- 0 = ref_count(File, FileToRefDb, -1),
- case lookup(File, FileToRefDb) of
- [Ref] when is_reference(Ref) ->
- remove_certs(Ref, CertsDb);
- _ -> ok
- end,
- delete(File, FileToRefDb)
- catch _:_ ->
- ok
- end
- end,
- case Files of
- undefined -> ok;
- _ ->
- [Clear(File) || File <- Files],
- ok
- end.
+cache_pem_file(Ref, {MD5, File}, [_CertsDb, _RefDb, PemChache]) ->
+ {ok, PemBin} = file:read_file(File),
+ Content = public_key:pem_decode(PemBin),
+ insert(MD5, {Content, Ref}, PemChache),
+ {ok, Content}.
+
+remove_trusted_certs(Ref, CertsDb) ->
+ remove_certs(Ref, CertsDb).
+
+%%--------------------------------------------------------------------
+-spec remove(term(), db_handle()) -> term().
+%%
+%% Description: Removes an element in a <Db>.
+%%--------------------------------------------------------------------
+remove(Key, Db) ->
+ _ = ets:delete(Db, Key).
%%--------------------------------------------------------------------
-spec lookup(term(), db_handle()) -> term() | undefined.
%%
-%% Description: Looks up an element in a certificat <Db>.
+%% Description: Looks up an element in a <Db>.
%%--------------------------------------------------------------------
lookup(Key, Db) ->
case ets:lookup(Db, Key) of
@@ -184,24 +168,44 @@ lookup(Key, Db) ->
%%--------------------------------------------------------------------
foldl(Fun, Acc0, Cache) ->
ets:foldl(Fun, Acc0, Cache).
-
+
%%--------------------------------------------------------------------
-%%% Internal functions
+-spec ref_count(term(), db_handle(), integer()) -> integer().
+%%
+%% Description: Updates a reference counter in a <Db>.
%%--------------------------------------------------------------------
-insert(Key, Data, Db) ->
- true = ets:insert(Db, {Key, Data}).
+ref_count(Key, Db, N) ->
+ ets:update_counter(Db,Key,N).
-insert(Key, Data, Count, Db) ->
- true = ets:insert(Db, {Key, Count, Data}).
+%%--------------------------------------------------------------------
+-spec clear(db_handle()) -> term().
+%%
+%% Description: Clears the cache
+%%--------------------------------------------------------------------
+clear(Db) ->
+ ets:delete_all_objects(Db).
-ref_count(Key, Db,N) ->
- ets:update_counter(Db,Key,N).
+%%--------------------------------------------------------------------
+-spec db_size(db_handle()) -> integer().
+%%
+%% Description: Returns the size of the db
+%%--------------------------------------------------------------------
+db_size(Db) ->
+ ets:info(Db, size).
-delete(Key, Db) ->
- _ = ets:delete(Db, Key).
+%%--------------------------------------------------------------------
+%%-spec insert(Key::term(), Data::term(), Db::db_handle()) -> no_return().
+%%
+%% Description: Inserts data into <Db>
+%%--------------------------------------------------------------------
+insert(Key, Data, Db) ->
+ true = ets:insert(Db, {Key, Data}).
-select(Db, MatchSpec)->
- ets:select(Db, MatchSpec).
+%%--------------------------------------------------------------------
+%%% Internal functions
+%%--------------------------------------------------------------------
+update_counter(Key, Count, Db) ->
+ true = ets:insert(Db, {Key, Count}).
remove_certs(Ref, CertsDb) ->
ets:match_delete(CertsDb, {{Ref, '_', '_'}, '_'}).
@@ -210,10 +214,8 @@ add_certs_from_der(DerList, Ref, CertsDb) ->
Add = fun(Cert) -> add_certs(Cert, Ref, CertsDb) end,
[Add(Cert) || Cert <- DerList].
-add_certs_from_file(File, Ref, CertsDb) ->
+add_certs_from_pem(PemEntries, Ref, CertsDb) ->
Add = fun(Cert) -> add_certs(Cert, Ref, CertsDb) end,
- {ok, PemBin} = file:read_file(File),
- PemEntries = public_key:pem_decode(PemBin),
[Add(Cert) || {'Certificate', Cert, not_encrypted} <- PemEntries].
add_certs(Cert, Ref, CertsDb) ->
@@ -229,3 +231,10 @@ add_certs(Cert, Ref, CertsDb) ->
"it could not be correctly decoded.~n", []),
error_logger:info_report(Report)
end.
+
+new_trusted_cert_entry(FileRef, [CertsDb, RefDb, _] = Db) ->
+ Ref = make_ref(),
+ update_counter(Ref, 1, RefDb),
+ {ok, Content} = cache_pem_file(Ref, FileRef, Db),
+ add_certs_from_pem(Content, Ref, CertsDb),
+ {ok, Ref}.
diff --git a/lib/ssl/src/ssl_cipher.erl b/lib/ssl/src/ssl_cipher.erl
index d43d312be8..567690a413 100644
--- a/lib/ssl/src/ssl_cipher.erl
+++ b/lib/ssl/src/ssl_cipher.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2012. 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
@@ -28,25 +28,27 @@
-include("ssl_internal.hrl").
-include("ssl_record.hrl").
-include("ssl_cipher.hrl").
+-include("ssl_handshake.hrl").
-include("ssl_alert.hrl").
-include_lib("public_key/include/public_key.hrl").
--export([security_parameters/2, suite_definition/1,
- decipher/5, cipher/4,
+-export([security_parameters/3, suite_definition/1,
+ decipher/5, cipher/5,
suite/1, suites/1, anonymous_suites/0,
- openssl_suite/1, openssl_suite_name/1, filter/2]).
+ openssl_suite/1, openssl_suite_name/1, filter/2,
+ hash_algorithm/1, sign_algorithm/1]).
-compile(inline).
%%--------------------------------------------------------------------
--spec security_parameters(cipher_suite(), #security_parameters{}) ->
+-spec security_parameters(tls_version(), cipher_suite(), #security_parameters{}) ->
#security_parameters{}.
%%
%% Description: Returns a security parameters record where the
%% cipher values has been updated according to <CipherSuite>
%%-------------------------------------------------------------------
-security_parameters(CipherSuite, SecParams) ->
- { _, Cipher, Hash} = suite_definition(CipherSuite),
+security_parameters(Version, CipherSuite, SecParams) ->
+ { _, Cipher, Hash, PrfHashAlg} = suite_definition(CipherSuite),
SecParams#security_parameters{
cipher_suite = CipherSuite,
bulk_cipher_algorithm = bulk_cipher_algorithm(Cipher),
@@ -55,20 +57,21 @@ security_parameters(CipherSuite, SecParams) ->
expanded_key_material_length = expanded_key_material(Cipher),
key_material_length = key_material(Cipher),
iv_size = iv_size(Cipher),
- mac_algorithm = mac_algorithm(Hash),
+ mac_algorithm = hash_algorithm(Hash),
+ prf_algorithm = prf_algorithm(PrfHashAlg, Version),
hash_size = hash_size(Hash)}.
%%--------------------------------------------------------------------
--spec cipher(cipher_enum(), #cipher_state{}, binary(), binary()) ->
+-spec cipher(cipher_enum(), #cipher_state{}, binary(), binary(), tls_version()) ->
{binary(), #cipher_state{}}.
%%
%% Description: Encrypts the data and the MAC using chipher described
%% by cipher_enum() and updating the cipher state
%%-------------------------------------------------------------------
-cipher(?NULL, CipherState, <<>>, Fragment) ->
+cipher(?NULL, CipherState, <<>>, Fragment, _Version) ->
GenStreamCipherList = [Fragment, <<>>],
{GenStreamCipherList, CipherState};
-cipher(?RC4, CipherState, Mac, Fragment) ->
+cipher(?RC4, CipherState, Mac, Fragment, _Version) ->
State0 = case CipherState#cipher_state.state of
undefined -> crypto:rc4_set_key(CipherState#cipher_state.key);
S -> S
@@ -76,32 +79,41 @@ cipher(?RC4, CipherState, Mac, Fragment) ->
GenStreamCipherList = [Fragment, Mac],
{State1, T} = crypto:rc4_encrypt_with_state(State0, GenStreamCipherList),
{T, CipherState#cipher_state{state = State1}};
-cipher(?DES, CipherState, Mac, Fragment) ->
+cipher(?DES, CipherState, Mac, Fragment, Version) ->
block_cipher(fun(Key, IV, T) ->
crypto:des_cbc_encrypt(Key, IV, T)
- end, block_size(des_cbc), CipherState, Mac, Fragment);
-cipher(?'3DES', CipherState, Mac, Fragment) ->
+ end, block_size(des_cbc), CipherState, Mac, Fragment, Version);
+cipher(?'3DES', CipherState, Mac, Fragment, Version) ->
block_cipher(fun(<<K1:8/binary, K2:8/binary, K3:8/binary>>, IV, T) ->
crypto:des3_cbc_encrypt(K1, K2, K3, IV, T)
- end, block_size(des_cbc), CipherState, Mac, Fragment);
-cipher(?AES, CipherState, Mac, Fragment) ->
+ end, block_size(des_cbc), CipherState, Mac, Fragment, Version);
+cipher(?AES, CipherState, Mac, Fragment, Version) ->
block_cipher(fun(Key, IV, T) when byte_size(Key) =:= 16 ->
crypto:aes_cbc_128_encrypt(Key, IV, T);
(Key, IV, T) when byte_size(Key) =:= 32 ->
crypto:aes_cbc_256_encrypt(Key, IV, T)
- end, block_size(aes_128_cbc), CipherState, Mac, Fragment).
-%% cipher(?IDEA, CipherState, Mac, Fragment) ->
-%% block_cipher(fun(Key, IV, T) ->
-%% crypto:idea_cbc_encrypt(Key, IV, T)
-%% end, block_size(idea_cbc), CipherState, Mac, Fragment);
-
-block_cipher(Fun, BlockSz, #cipher_state{key=Key, iv=IV} = CS0,
- Mac, Fragment) ->
+ end, block_size(aes_128_cbc), CipherState, Mac, Fragment, Version).
+
+build_cipher_block(BlockSz, Mac, Fragment) ->
TotSz = byte_size(Mac) + erlang:iolist_size(Fragment) + 1,
{PaddingLength, Padding} = get_padding(TotSz, BlockSz),
- L = [Fragment, Mac, PaddingLength, Padding],
+ [Fragment, Mac, PaddingLength, Padding].
+
+block_cipher(Fun, BlockSz, #cipher_state{key=Key, iv=IV} = CS0,
+ Mac, Fragment, {3, N})
+ when N == 0; N == 1 ->
+ L = build_cipher_block(BlockSz, Mac, Fragment),
T = Fun(Key, IV, L),
NextIV = next_iv(T, IV),
+ {T, CS0#cipher_state{iv=NextIV}};
+
+block_cipher(Fun, BlockSz, #cipher_state{key=Key, iv=IV} = CS0,
+ Mac, Fragment, {3, N})
+ when N == 2; N == 3 ->
+ NextIV = random_iv(IV),
+ L0 = build_cipher_block(BlockSz, Mac, Fragment),
+ L = [NextIV|L0],
+ T = Fun(Key, IV, L),
{T, CS0#cipher_state{iv=NextIV}}.
%%--------------------------------------------------------------------
@@ -147,19 +159,16 @@ decipher(?AES, HashSz, CipherState, Fragment, Version) ->
(Key, IV, T) when byte_size(Key) =:= 32 ->
crypto:aes_cbc_256_decrypt(Key, IV, T)
end, CipherState, HashSz, Fragment, Version).
-%% decipher(?IDEA, HashSz, CipherState, Fragment, Version) ->
-%% block_decipher(fun(Key, IV, T) ->
-%% crypto:idea_cbc_decrypt(Key, IV, T)
-%% end, CipherState, HashSz, Fragment, Version);
block_decipher(Fun, #cipher_state{key=Key, iv=IV} = CipherState0,
HashSz, Fragment, Version) ->
try
Text = Fun(Key, IV, Fragment),
- GBC = generic_block_cipher_from_bin(Text, HashSz),
+ NextIV = next_iv(Fragment, IV),
+ GBC = generic_block_cipher_from_bin(Version, Text, NextIV, HashSz),
Content = GBC#generic_block_cipher.content,
Mac = GBC#generic_block_cipher.mac,
- CipherState1 = CipherState0#cipher_state{iv=next_iv(Fragment, IV)},
+ CipherState1 = CipherState0#cipher_state{iv=GBC#generic_block_cipher.next_iv},
case is_correct_padding(GBC, Version) of
true ->
{Content, Mac, CipherState1};
@@ -187,8 +196,8 @@ block_decipher(Fun, #cipher_state{key=Key, iv=IV} = CipherState0,
%%--------------------------------------------------------------------
suites({3, 0}) ->
ssl_ssl3:suites();
-suites({3, N}) when N == 1; N == 2 ->
- ssl_tls1:suites().
+suites({3, N}) ->
+ ssl_tls1:suites(N).
%%--------------------------------------------------------------------
-spec anonymous_suites() -> [cipher_suite()].
@@ -201,10 +210,12 @@ anonymous_suites() ->
?TLS_DH_anon_WITH_DES_CBC_SHA,
?TLS_DH_anon_WITH_3DES_EDE_CBC_SHA,
?TLS_DH_anon_WITH_AES_128_CBC_SHA,
- ?TLS_DH_anon_WITH_AES_256_CBC_SHA].
+ ?TLS_DH_anon_WITH_AES_256_CBC_SHA,
+ ?TLS_DH_anon_WITH_AES_128_CBC_SHA256,
+ ?TLS_DH_anon_WITH_AES_256_CBC_SHA256].
%%--------------------------------------------------------------------
--spec suite_definition(cipher_suite()) -> erl_cipher_suite().
+-spec suite_definition(cipher_suite()) -> int_cipher_suite().
%%
%% Description: Return erlang cipher suite definition.
%% Note: Currently not supported suites are commented away.
@@ -212,56 +223,81 @@ anonymous_suites() ->
%%-------------------------------------------------------------------
%% TLS v1.1 suites
suite_definition(?TLS_NULL_WITH_NULL_NULL) ->
- {null, null, null};
+ {null, null, null, null};
%% suite_definition(?TLS_RSA_WITH_NULL_MD5) ->
-%% {rsa, null, md5};
+%% {rsa, null, md5, default_prf};
%% suite_definition(?TLS_RSA_WITH_NULL_SHA) ->
-%% {rsa, null, sha};
+%% {rsa, null, sha, default_prf};
suite_definition(?TLS_RSA_WITH_RC4_128_MD5) ->
- {rsa, rc4_128, md5};
-suite_definition(?TLS_RSA_WITH_RC4_128_SHA) ->
- {rsa, rc4_128, sha};
-%% suite_definition(?TLS_RSA_WITH_IDEA_CBC_SHA) ->
-%% {rsa, idea_cbc, sha};
-suite_definition(?TLS_RSA_WITH_DES_CBC_SHA) ->
- {rsa, des_cbc, sha};
+ {rsa, rc4_128, md5, default_prf};
+suite_definition(?TLS_RSA_WITH_RC4_128_SHA) ->
+ {rsa, rc4_128, sha, default_prf};
+suite_definition(?TLS_RSA_WITH_DES_CBC_SHA) ->
+ {rsa, des_cbc, sha, default_prf};
suite_definition(?TLS_RSA_WITH_3DES_EDE_CBC_SHA) ->
- {rsa, '3des_ede_cbc', sha};
+ {rsa, '3des_ede_cbc', sha, default_prf};
suite_definition(?TLS_DHE_DSS_WITH_DES_CBC_SHA) ->
- {dhe_dss, des_cbc, sha};
+ {dhe_dss, des_cbc, sha, default_prf};
suite_definition(?TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA) ->
- {dhe_dss, '3des_ede_cbc', sha};
+ {dhe_dss, '3des_ede_cbc', sha, default_prf};
suite_definition(?TLS_DHE_RSA_WITH_DES_CBC_SHA) ->
- {dhe_rsa, des_cbc, sha};
+ {dhe_rsa, des_cbc, sha, default_prf};
suite_definition(?TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA) ->
- {dhe_rsa, '3des_ede_cbc', sha};
+ {dhe_rsa, '3des_ede_cbc', sha, default_prf};
%%% TSL V1.1 AES suites
suite_definition(?TLS_RSA_WITH_AES_128_CBC_SHA) ->
- {rsa, aes_128_cbc, sha};
+ {rsa, aes_128_cbc, sha, default_prf};
suite_definition(?TLS_DHE_DSS_WITH_AES_128_CBC_SHA) ->
- {dhe_dss, aes_128_cbc, sha};
+ {dhe_dss, aes_128_cbc, sha, default_prf};
suite_definition(?TLS_DHE_RSA_WITH_AES_128_CBC_SHA) ->
- {dhe_rsa, aes_128_cbc, sha};
+ {dhe_rsa, aes_128_cbc, sha, default_prf};
suite_definition(?TLS_RSA_WITH_AES_256_CBC_SHA) ->
- {rsa, aes_256_cbc, sha};
+ {rsa, aes_256_cbc, sha, default_prf};
suite_definition(?TLS_DHE_DSS_WITH_AES_256_CBC_SHA) ->
- {dhe_dss, aes_256_cbc, sha};
+ {dhe_dss, aes_256_cbc, sha, default_prf};
suite_definition(?TLS_DHE_RSA_WITH_AES_256_CBC_SHA) ->
- {dhe_rsa, aes_256_cbc, sha};
+ {dhe_rsa, aes_256_cbc, sha, default_prf};
+
+%% TLS v1.2 suites
+
+%% suite_definition(?TLS_RSA_WITH_NULL_SHA) ->
+%% {rsa, null, sha, default_prf};
+suite_definition(?TLS_RSA_WITH_AES_128_CBC_SHA256) ->
+ {rsa, aes_128_cbc, sha256, default_prf};
+suite_definition(?TLS_RSA_WITH_AES_256_CBC_SHA256) ->
+ {rsa, aes_256_cbc, sha256, default_prf};
+suite_definition(?TLS_DHE_DSS_WITH_AES_128_CBC_SHA256) ->
+ {dhe_dss, aes_128_cbc, sha256, default_prf};
+suite_definition(?TLS_DHE_RSA_WITH_AES_128_CBC_SHA256) ->
+ {dhe_rsa, aes_128_cbc, sha256, default_prf};
+suite_definition(?TLS_DHE_DSS_WITH_AES_256_CBC_SHA256) ->
+ {dhe_dss, aes_256_cbc, sha256, default_prf};
+suite_definition(?TLS_DHE_RSA_WITH_AES_256_CBC_SHA256) ->
+ {dhe_rsa, aes_256_cbc, sha256, default_prf};
+
+%% not defined YET:
+%% TLS_DH_DSS_WITH_AES_128_CBC_SHA256 DH_DSS AES_128_CBC SHA256
+%% TLS_DH_RSA_WITH_AES_128_CBC_SHA256 DH_RSA AES_128_CBC SHA256
+%% TLS_DH_DSS_WITH_AES_256_CBC_SHA256 DH_DSS AES_256_CBC SHA256
+%% TLS_DH_RSA_WITH_AES_256_CBC_SHA256 DH_RSA AES_256_CBC SHA256
%%% DH-ANON deprecated by TLS spec and not available
%%% by default, but good for testing purposes.
suite_definition(?TLS_DH_anon_WITH_RC4_128_MD5) ->
- {dh_anon, rc4_128, md5};
+ {dh_anon, rc4_128, md5, default_prf};
suite_definition(?TLS_DH_anon_WITH_DES_CBC_SHA) ->
- {dh_anon, des_cbc, sha};
+ {dh_anon, des_cbc, sha, default_prf};
suite_definition(?TLS_DH_anon_WITH_3DES_EDE_CBC_SHA) ->
- {dh_anon, '3des_ede_cbc', sha};
+ {dh_anon, '3des_ede_cbc', sha, default_prf};
suite_definition(?TLS_DH_anon_WITH_AES_128_CBC_SHA) ->
- {dh_anon, aes_128_cbc, sha};
+ {dh_anon, aes_128_cbc, sha, default_prf};
suite_definition(?TLS_DH_anon_WITH_AES_256_CBC_SHA) ->
- {dh_anon, aes_256_cbc, sha}.
+ {dh_anon, aes_256_cbc, sha, default_prf};
+suite_definition(?TLS_DH_anon_WITH_AES_128_CBC_SHA256) ->
+ {dh_anon, aes_128_cbc, sha256, default_prf};
+suite_definition(?TLS_DH_anon_WITH_AES_256_CBC_SHA256) ->
+ {dh_anon, aes_256_cbc, sha256, default_prf}.
%%--------------------------------------------------------------------
-spec suite(erl_cipher_suite()) -> cipher_suite().
@@ -278,8 +314,6 @@ suite({rsa, rc4_128, md5}) ->
?TLS_RSA_WITH_RC4_128_MD5;
suite({rsa, rc4_128, sha}) ->
?TLS_RSA_WITH_RC4_128_SHA;
-%% suite({rsa, idea_cbc, sha}) ->
-%% ?TLS_RSA_WITH_IDEA_CBC_SHA;
suite({rsa, des_cbc, sha}) ->
?TLS_RSA_WITH_DES_CBC_SHA;
suite({rsa, '3des_ede_cbc', sha}) ->
@@ -315,7 +349,28 @@ suite({dhe_dss, aes_256_cbc, sha}) ->
suite({dhe_rsa, aes_256_cbc, sha}) ->
?TLS_DHE_RSA_WITH_AES_256_CBC_SHA;
suite({dh_anon, aes_256_cbc, sha}) ->
- ?TLS_DH_anon_WITH_AES_256_CBC_SHA.
+ ?TLS_DH_anon_WITH_AES_256_CBC_SHA;
+
+%% TLS v1.2 suites
+
+%% suite_definition(?TLS_RSA_WITH_NULL_SHA) ->
+%% {rsa, null, sha, sha256};
+suite({rsa, aes_128_cbc, sha256}) ->
+ ?TLS_RSA_WITH_AES_128_CBC_SHA256;
+suite({rsa, aes_256_cbc, sha256}) ->
+ ?TLS_RSA_WITH_AES_256_CBC_SHA256;
+suite({dhe_dss, aes_128_cbc, sha256}) ->
+ ?TLS_DHE_DSS_WITH_AES_128_CBC_SHA256;
+suite({dhe_rsa, aes_128_cbc, sha256}) ->
+ ?TLS_DHE_RSA_WITH_AES_128_CBC_SHA256;
+suite({dhe_dss, aes_256_cbc, sha256}) ->
+ ?TLS_DHE_DSS_WITH_AES_256_CBC_SHA256;
+suite({dhe_rsa, aes_256_cbc, sha256}) ->
+ ?TLS_DHE_RSA_WITH_AES_256_CBC_SHA256;
+suite({dh_anon, aes_128_cbc, sha256}) ->
+ ?TLS_DH_anon_WITH_AES_128_CBC_SHA256;
+suite({dh_anon, aes_256_cbc, sha256}) ->
+ ?TLS_DH_anon_WITH_AES_256_CBC_SHA256.
%%--------------------------------------------------------------------
-spec openssl_suite(openssl_cipher_suite()) -> cipher_suite().
@@ -323,6 +378,18 @@ suite({dh_anon, aes_256_cbc, sha}) ->
%% Description: Return TLS cipher suite definition.
%%--------------------------------------------------------------------
%% translate constants <-> openssl-strings
+openssl_suite("DHE-RSA-AES256-SHA256") ->
+ ?TLS_DHE_RSA_WITH_AES_256_CBC_SHA256;
+openssl_suite("DHE-DSS-AES256-SHA256") ->
+ ?TLS_DHE_DSS_WITH_AES_256_CBC_SHA256;
+openssl_suite("AES256-SHA256") ->
+ ?TLS_RSA_WITH_AES_256_CBC_SHA256;
+openssl_suite("DHE-RSA-AES128-SHA256") ->
+ ?TLS_DHE_RSA_WITH_AES_128_CBC_SHA256;
+openssl_suite("DHE-DSS-AES128-SHA256") ->
+ ?TLS_DHE_DSS_WITH_AES_128_CBC_SHA256;
+openssl_suite("AES128-SHA256") ->
+ ?TLS_RSA_WITH_AES_128_CBC_SHA256;
openssl_suite("DHE-RSA-AES256-SHA") ->
?TLS_DHE_RSA_WITH_AES_256_CBC_SHA;
openssl_suite("DHE-DSS-AES256-SHA") ->
@@ -341,8 +408,6 @@ openssl_suite("DHE-DSS-AES128-SHA") ->
?TLS_DHE_DSS_WITH_AES_128_CBC_SHA;
openssl_suite("AES128-SHA") ->
?TLS_RSA_WITH_AES_128_CBC_SHA;
-%%openssl_suite("IDEA-CBC-SHA") ->
-%% ?TLS_RSA_WITH_IDEA_CBC_SHA;
openssl_suite("RC4-SHA") ->
?TLS_RSA_WITH_RC4_128_SHA;
openssl_suite("RC4-MD5") ->
@@ -374,8 +439,6 @@ openssl_suite_name(?TLS_DHE_DSS_WITH_AES_128_CBC_SHA) ->
"DHE-DSS-AES128-SHA";
openssl_suite_name(?TLS_RSA_WITH_AES_128_CBC_SHA) ->
"AES128-SHA";
-%% openssl_suite_name(?TLS_RSA_WITH_IDEA_CBC_SHA) ->
-%% "IDEA-CBC-SHA";
openssl_suite_name(?TLS_RSA_WITH_RC4_128_SHA) ->
"RC4-SHA";
openssl_suite_name(?TLS_RSA_WITH_RC4_128_MD5) ->
@@ -384,6 +447,28 @@ openssl_suite_name(?TLS_DHE_RSA_WITH_DES_CBC_SHA) ->
"EDH-RSA-DES-CBC-SHA";
openssl_suite_name(?TLS_RSA_WITH_DES_CBC_SHA) ->
"DES-CBC-SHA";
+openssl_suite_name(?TLS_RSA_WITH_NULL_SHA256) ->
+ "NULL-SHA256";
+openssl_suite_name(?TLS_RSA_WITH_AES_128_CBC_SHA256) ->
+ "AES128-SHA256";
+openssl_suite_name(?TLS_RSA_WITH_AES_256_CBC_SHA256) ->
+ "AES256-SHA256";
+openssl_suite_name(?TLS_DH_DSS_WITH_AES_128_CBC_SHA256) ->
+ "DH-DSS-AES128-SHA256";
+openssl_suite_name(?TLS_DH_RSA_WITH_AES_128_CBC_SHA256) ->
+ "DH-RSA-AES128-SHA256";
+openssl_suite_name(?TLS_DHE_DSS_WITH_AES_128_CBC_SHA256) ->
+ "DHE-DSS-AES128-SHA256";
+openssl_suite_name(?TLS_DHE_RSA_WITH_AES_128_CBC_SHA256) ->
+ "DHE-RSA-AES128-SHA256";
+openssl_suite_name(?TLS_DH_DSS_WITH_AES_256_CBC_SHA256) ->
+ "DH-DSS-AES256-SHA256";
+openssl_suite_name(?TLS_DH_RSA_WITH_AES_256_CBC_SHA256) ->
+ "DH-RSA-AES256-SHA256";
+openssl_suite_name(?TLS_DHE_DSS_WITH_AES_256_CBC_SHA256) ->
+ "DHE-DSS-AES256-SHA256";
+openssl_suite_name(?TLS_DHE_RSA_WITH_AES_256_CBC_SHA256) ->
+ "DHE-RSA-AES256-SHA256";
%% No oppenssl name
openssl_suite_name(Cipher) ->
suite_definition(Cipher).
@@ -411,9 +496,6 @@ filter(DerCert, Ciphers) ->
bulk_cipher_algorithm(null) ->
?NULL;
-%% Not supported yet
-%% bulk_cipher_algorithm(idea_cbc) ->
-%% ?IDEA;
bulk_cipher_algorithm(rc4_128) ->
?RC4;
bulk_cipher_algorithm(des_cbc) ->
@@ -428,8 +510,7 @@ type(Cipher) when Cipher == null;
Cipher == rc4_128 ->
?STREAM;
-type(Cipher) when Cipher == idea_cbc;
- Cipher == des_cbc;
+type(Cipher) when Cipher == des_cbc;
Cipher == '3des_ede_cbc';
Cipher == aes_128_cbc;
Cipher == aes_256_cbc ->
@@ -437,8 +518,7 @@ type(Cipher) when Cipher == idea_cbc;
key_material(null) ->
0;
-key_material(Cipher) when Cipher == idea_cbc;
- Cipher == rc4_128 ->
+key_material(rc4_128) ->
16;
key_material(des_cbc) ->
8;
@@ -451,8 +531,7 @@ key_material(aes_256_cbc) ->
expanded_key_material(null) ->
0;
-expanded_key_material(Cipher) when Cipher == idea_cbc;
- Cipher == rc4_128 ->
+expanded_key_material(rc4_128) ->
16;
expanded_key_material(Cipher) when Cipher == des_cbc ->
8;
@@ -467,8 +546,7 @@ effective_key_bits(null) ->
0;
effective_key_bits(des_cbc) ->
56;
-effective_key_bits(Cipher) when Cipher == idea_cbc;
- Cipher == rc4_128;
+effective_key_bits(Cipher) when Cipher == rc4_128;
Cipher == aes_128_cbc ->
128;
effective_key_bits('3des_ede_cbc') ->
@@ -482,8 +560,7 @@ iv_size(Cipher) when Cipher == null;
iv_size(Cipher) ->
block_size(Cipher).
-block_size(Cipher) when Cipher == idea_cbc;
- Cipher == des_cbc;
+block_size(Cipher) when Cipher == des_cbc;
Cipher == '3des_ede_cbc' ->
8;
@@ -491,19 +568,51 @@ block_size(Cipher) when Cipher == aes_128_cbc;
Cipher == aes_256_cbc ->
16.
-mac_algorithm(null) ->
- ?NULL;
-mac_algorithm(md5) ->
- ?MD5;
-mac_algorithm(sha) ->
- ?SHA.
+prf_algorithm(default_prf, {3, N}) when N >= 3 ->
+ ?SHA256;
+prf_algorithm(default_prf, {3, _}) ->
+ ?MD5SHA;
+prf_algorithm(Algo, _) ->
+ hash_algorithm(Algo).
+
+hash_algorithm(null) -> ?NULL;
+hash_algorithm(md5) -> ?MD5;
+hash_algorithm(sha) -> ?SHA; %% Only sha always refers to "SHA-1"
+hash_algorithm(sha224) -> ?SHA224;
+hash_algorithm(sha256) -> ?SHA256;
+hash_algorithm(sha384) -> ?SHA384;
+hash_algorithm(sha512) -> ?SHA512;
+hash_algorithm(?NULL) -> null;
+hash_algorithm(?MD5) -> md5;
+hash_algorithm(?SHA) -> sha;
+hash_algorithm(?SHA224) -> sha224;
+hash_algorithm(?SHA256) -> sha256;
+hash_algorithm(?SHA384) -> sha384;
+hash_algorithm(?SHA512) -> sha512.
+
+sign_algorithm(anon) -> ?ANON;
+sign_algorithm(rsa) -> ?RSA;
+sign_algorithm(dsa) -> ?DSA;
+sign_algorithm(ecdsa) -> ?ECDSA;
+sign_algorithm(?ANON) -> anon;
+sign_algorithm(?RSA) -> rsa;
+sign_algorithm(?DSA) -> dsa;
+sign_algorithm(?ECDSA) -> ecdsa.
hash_size(null) ->
0;
hash_size(md5) ->
16;
hash_size(sha) ->
- 20.
+ 20;
+hash_size(sha256) ->
+ 32.
+%% Currently no supported cipher suites defaults to sha384 or sha512
+%% so these clauses are not needed at the moment.
+%% hash_size(sha384) ->
+%% 48;
+%% hash_size(sha512) ->
+%% 64.
%% RFC 5246: 6.2.3.2. CBC Block Cipher
%%
@@ -525,7 +634,8 @@ hash_size(sha) ->
%% We return the original (possibly invalid) PadLength in any case.
%% An invalid PadLength will be caught by is_correct_padding/2
%%
-generic_block_cipher_from_bin(T, HashSize) ->
+generic_block_cipher_from_bin({3, N}, T, IV, HashSize)
+ when N == 0; N == 1 ->
Sz1 = byte_size(T) - 1,
<<_:Sz1/binary, ?BYTE(PadLength0)>> = T,
PadLength = if
@@ -536,7 +646,20 @@ generic_block_cipher_from_bin(T, HashSize) ->
<<Content:CompressedLength/binary, Mac:HashSize/binary,
Padding:PadLength/binary, ?BYTE(PadLength0)>> = T,
#generic_block_cipher{content=Content, mac=Mac,
- padding=Padding, padding_length=PadLength0}.
+ padding=Padding, padding_length=PadLength0,
+ next_iv = IV};
+
+generic_block_cipher_from_bin({3, N}, T, IV, HashSize)
+ when N == 2; N == 3 ->
+ Sz1 = byte_size(T) - 1,
+ <<_:Sz1/binary, ?BYTE(PadLength)>> = T,
+ IVLength = byte_size(IV),
+ CompressedLength = byte_size(T) - IVLength - PadLength - 1 - HashSize,
+ <<NextIV:IVLength/binary, Content:CompressedLength/binary, Mac:HashSize/binary,
+ Padding:PadLength/binary, ?BYTE(PadLength)>> = T,
+ #generic_block_cipher{content=Content, mac=Mac,
+ padding=Padding, padding_length=PadLength,
+ next_iv = NextIV}.
generic_stream_cipher_from_bin(T, HashSz) ->
Sz = byte_size(T),
@@ -567,6 +690,10 @@ get_padding_aux(BlockSize, PadLength) ->
N = BlockSize - PadLength,
{N, list_to_binary(lists:duplicate(N, N))}.
+random_iv(IV) ->
+ IVSz = byte_size(IV),
+ ssl:random_bytes(IVSz).
+
next_iv(Bin, IV) ->
BinSz = byte_size(Bin),
IVSz = byte_size(IV),
@@ -578,16 +705,19 @@ rsa_signed_suites() ->
dhe_rsa_suites() ++ rsa_suites().
dhe_rsa_suites() ->
- [?TLS_DHE_RSA_WITH_AES_256_CBC_SHA,
+ [?TLS_DHE_RSA_WITH_AES_256_CBC_SHA256,
+ ?TLS_DHE_RSA_WITH_AES_256_CBC_SHA,
?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].
rsa_suites() ->
- [?TLS_RSA_WITH_AES_256_CBC_SHA,
+ [?TLS_RSA_WITH_AES_256_CBC_SHA256,
+ ?TLS_RSA_WITH_AES_256_CBC_SHA,
?TLS_RSA_WITH_3DES_EDE_CBC_SHA,
+ ?TLS_RSA_WITH_AES_128_CBC_SHA256,
?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].
@@ -596,8 +726,10 @@ dsa_signed_suites() ->
dhe_dss_suites().
dhe_dss_suites() ->
- [?TLS_DHE_DSS_WITH_AES_256_CBC_SHA,
+ [?TLS_DHE_DSS_WITH_AES_256_CBC_SHA256,
+ ?TLS_DHE_DSS_WITH_AES_256_CBC_SHA,
?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].
diff --git a/lib/ssl/src/ssl_cipher.hrl b/lib/ssl/src/ssl_cipher.hrl
index 8bd68cc190..0f439f8ed5 100644
--- a/lib/ssl/src/ssl_cipher.hrl
+++ b/lib/ssl/src/ssl_cipher.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2012. 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
@@ -28,8 +28,9 @@
-type cipher() :: null |rc4_128 | idea_cbc | des40_cbc | des_cbc | '3des_ede_cbc'
| aes_128_cbc | aes_256_cbc.
--type hash() :: null | sha | md5.
+-type hash() :: null | sha | md5 | sha256 | sha384 | sha512.
-type erl_cipher_suite() :: {key_algo(), cipher(), hash()}.
+-type int_cipher_suite() :: {key_algo(), cipher(), hash(), hash()}.
-type cipher_suite() :: binary().
-type cipher_enum() :: integer().
-type openssl_cipher_suite() :: string().
@@ -177,6 +178,47 @@
%% TLS_DH_anon_WITH_AES_256_CBC_SHA = { 0x00, 0x3A };
-define(TLS_DH_anon_WITH_AES_256_CBC_SHA, <<?BYTE(16#00), ?BYTE(16#3A)>>).
+%%% TLS 1.2 Cipher Suites RFC 5246
+
+%% TLS_RSA_WITH_NULL_SHA256 = { 0x00,0x3B };
+-define(TLS_RSA_WITH_NULL_SHA256, <<?BYTE(16#00), ?BYTE(16#3B)>>).
+
+%% TLS_RSA_WITH_AES_128_CBC_SHA256 = { 0x00,0x3C };
+-define(TLS_RSA_WITH_AES_128_CBC_SHA256, <<?BYTE(16#00), ?BYTE(16#3C)>>).
+
+%% TLS_RSA_WITH_AES_256_CBC_SHA256 = { 0x00,0x3D };
+-define(TLS_RSA_WITH_AES_256_CBC_SHA256, <<?BYTE(16#00), ?BYTE(16#3D)>>).
+
+%% TLS_DH_DSS_WITH_AES_128_CBC_SHA256 = { 0x00,0x3E };
+-define(TLS_DH_DSS_WITH_AES_128_CBC_SHA256, <<?BYTE(16#00), ?BYTE(16#3E)>>).
+
+%% TLS_DH_RSA_WITH_AES_128_CBC_SHA256 = { 0x00,0x3F };
+-define(TLS_DH_RSA_WITH_AES_128_CBC_SHA256, <<?BYTE(16#00), ?BYTE(16#3F)>>).
+
+%% TLS_DHE_DSS_WITH_AES_128_CBC_SHA256 = { 0x00,0x40 };
+-define(TLS_DHE_DSS_WITH_AES_128_CBC_SHA256, <<?BYTE(16#00), ?BYTE(16#40)>>).
+
+%% TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 = { 0x00,0x67 };
+-define(TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, <<?BYTE(16#00), ?BYTE(16#67)>>).
+
+%% TLS_DH_DSS_WITH_AES_256_CBC_SHA256 = { 0x00,0x68 };
+-define(TLS_DH_DSS_WITH_AES_256_CBC_SHA256, <<?BYTE(16#00), ?BYTE(16#68)>>).
+
+%% TLS_DH_RSA_WITH_AES_256_CBC_SHA256 = { 0x00,0x69 };
+-define(TLS_DH_RSA_WITH_AES_256_CBC_SHA256, <<?BYTE(16#00), ?BYTE(16#69)>>).
+
+%% TLS_DHE_DSS_WITH_AES_256_CBC_SHA256 = { 0x00,0x6A };
+-define(TLS_DHE_DSS_WITH_AES_256_CBC_SHA256, <<?BYTE(16#00), ?BYTE(16#6A)>>).
+
+%% TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 = { 0x00,0x6B };
+-define(TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, <<?BYTE(16#00), ?BYTE(16#6B)>>).
+
+%% TLS_DH_anon_WITH_AES_128_CBC_SHA256 = { 0x00,0x6C };
+-define(TLS_DH_anon_WITH_AES_128_CBC_SHA256, <<?BYTE(16#00), ?BYTE(16#6C)>>).
+
+%% TLS_DH_anon_WITH_AES_256_CBC_SHA256 = { 0x00,0x6D };
+-define(TLS_DH_anon_WITH_AES_256_CBC_SHA256, <<?BYTE(16#00), ?BYTE(16#6D)>>).
+
%%% Kerberos Cipher Suites
%% TLS_KRB5_WITH_DES_CBC_SHA = { 0x00,0x1E };
diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl
index 6c06baff98..ff2556c488 100644
--- a/lib/ssl/src/ssl_connection.erl
+++ b/lib/ssl/src/ssl_connection.erl
@@ -47,8 +47,8 @@
-export([start_link/7]).
%% gen_fsm callbacks
--export([init/1, hello/2, certify/2, cipher/2, connection/2,
- abbreviated/2, handle_event/3,
+-export([init/1, hello/2, certify/2, cipher/2,
+ abbreviated/2, connection/2, handle_event/3,
handle_sync_event/4, handle_info/3, terminate/3, code_change/4]).
-record(state, {
@@ -67,8 +67,7 @@
tls_packets = [], % Not yet handled decode ssl/tls packets.
tls_record_buffer, % binary() buffer of incomplete records
tls_handshake_buffer, % binary() buffer of incomplete handshakes
- %% {{md5_hash, sha_hash}, {prev_md5, prev_sha}} (binary())
- tls_handshake_hashes, % see above
+ tls_handshake_history, % tls_handshake_history()
tls_cipher_texts, % list() received but not deciphered yet
cert_db, %
session, % #session{} from ssl_handshake.hrl
@@ -78,18 +77,19 @@
supported_protocol_versions, % [atom()]
client_certificate_requested = false,
key_algorithm, % atom as defined by cipher_suite
+ hashsign_algorithm, % atom as defined by cipher_suite
public_key_info, % PKIX: {Algorithm, PublicKey, PublicKeyParams}
private_key, % PKIX: #'RSAPrivateKey'{}
diffie_hellman_params, % PKIX: #'DHParameter'{} relevant for server side
diffie_hellman_keys, % {PublicKey, PrivateKey}
premaster_secret, %
- cert_db_ref, % ets_table()
- from, % term(), where to reply
+ file_ref_db, % ets()
+ cert_db_ref, % ref()
bytes_to_read, % integer(), # bytes to read in passive mode
user_data_buffer, % binary()
log_alert, % boolean()
renegotiation, % {boolean(), From | internal | peer}
- recv_from, %
+ start_or_recv_from, % "gen_fsm From"
send_queue, % queue()
terminated = false, %
allow_renegotiate = true
@@ -297,39 +297,31 @@ prf(ConnectionPid, Secret, Label, Seed, WantedLength) ->
%% does not return until Module:init/1 has returned.
%%--------------------------------------------------------------------
start_link(Role, Host, Port, Socket, Options, User, CbInfo) ->
- gen_fsm:start_link(?MODULE, [Role, Host, Port, Socket, Options,
- User, CbInfo], []).
+ {ok, proc_lib:spawn_link(?MODULE, init, [[Role, Host, Port, Socket, Options, User, CbInfo]])}.
-%%====================================================================
-%% gen_fsm callbacks
-%%====================================================================
-%%--------------------------------------------------------------------
-%% Description:Whenever a gen_fsm is started using gen_fsm:start/[3,4] or
-%% gen_fsm:start_link/3,4, this function is called by the new process to
-%% initialize.
-%%--------------------------------------------------------------------
-init([Role, Host, Port, Socket, {SSLOpts0, _} = Options,
- User, CbInfo]) ->
+init([Role, Host, Port, Socket, {SSLOpts0, _} = Options, User, CbInfo]) ->
State0 = initial_state(Role, Host, Port, Socket, Options, User, CbInfo),
- Hashes0 = ssl_handshake:init_hashes(),
+ Handshake = ssl_handshake:init_handshake_history(),
TimeStamp = calendar:datetime_to_gregorian_seconds({date(), time()}),
try ssl_init(SSLOpts0, Role) of
- {ok, Ref, CertDbHandle, CacheHandle, OwnCert, Key, DHParams} ->
+ {ok, Ref, CertDbHandle, FileRefHandle, CacheHandle, OwnCert, Key, DHParams} ->
Session = State0#state.session,
- State = State0#state{tls_handshake_hashes = Hashes0,
+ State = State0#state{
+ tls_handshake_history = Handshake,
session = Session#session{own_certificate = OwnCert,
time_stamp = TimeStamp},
+ file_ref_db = FileRefHandle,
cert_db_ref = Ref,
cert_db = CertDbHandle,
session_cache = CacheHandle,
private_key = Key,
diffie_hellman_params = DHParams},
- {ok, hello, State, get_timeout(State)}
- catch
+ gen_fsm:enter_loop(?MODULE, [], hello, State, get_timeout(State))
+ catch
throw:Error ->
- {stop, Error}
+ gen_fsm:enter_loop(?MODULE, [], error, {Error,State0}, get_timeout(State0))
end.
-
+
%%--------------------------------------------------------------------
%% Description:There should be one instance of this function for each
%% possible state name. Whenever a gen_fsm receives an event sent
@@ -337,6 +329,7 @@ init([Role, Host, Port, Socket, {SSLOpts0, _} = Options,
%% same name as the current state name StateName is called to handle
%% the event. It is also called if a timeout occurs.
%%
+
%%--------------------------------------------------------------------
-spec hello(start | #hello_request{} | #client_hello{} | #server_hello{} | term(),
#state{}) -> gen_fsm_state_return().
@@ -344,23 +337,23 @@ init([Role, Host, Port, Socket, {SSLOpts0, _} = Options,
hello(start, #state{host = Host, port = Port, role = client,
ssl_options = SslOpts,
session = #session{own_certificate = Cert} = Session0,
+ session_cache = Cache, session_cache_cb = CacheCb,
transport_cb = Transport, socket = Socket,
- connection_states = ConnectionStates,
+ connection_states = ConnectionStates0,
renegotiation = {Renegotiation, _}} = State0) ->
- Hello = ssl_handshake:client_hello(Host, Port,
- ConnectionStates,
- SslOpts, Renegotiation, Cert),
+ Hello = ssl_handshake:client_hello(Host, Port, ConnectionStates0, SslOpts,
+ Cache, CacheCb, Renegotiation, Cert),
Version = Hello#client_hello.client_version,
- Hashes0 = ssl_handshake:init_hashes(),
- {BinMsg, CS2, Hashes1} =
- encode_handshake(Hello, Version, ConnectionStates, Hashes0),
+ Handshake0 = ssl_handshake:init_handshake_history(),
+ {BinMsg, ConnectionStates, Handshake} =
+ encode_handshake(Hello, Version, ConnectionStates0, Handshake0),
Transport:send(Socket, BinMsg),
- State1 = State0#state{connection_states = CS2,
- negotiated_version = Version, %% Requested version
+ State1 = State0#state{connection_states = ConnectionStates,
+ negotiated_version = Version, %% Requested version
session =
Session0#session{session_id = Hello#client_hello.session_id},
- tls_handshake_hashes = Hashes1},
+ tls_handshake_history = Handshake},
{Record, State} = next_record(State1),
next_state(hello, hello, Record, State);
@@ -382,19 +375,21 @@ hello(#server_hello{cipher_suite = CipherSuite,
ssl_options = SslOptions} = State0) ->
case ssl_handshake:hello(Hello, SslOptions, ConnectionStates0, Renegotiation) of
{Version, NewId, ConnectionStates} ->
- {KeyAlgorithm, _, _} =
+ {KeyAlgorithm, _, _, _} =
ssl_cipher:suite_definition(CipherSuite),
PremasterSecret = make_premaster_secret(ReqVersion, KeyAlgorithm),
State = State0#state{key_algorithm = KeyAlgorithm,
+ hashsign_algorithm = default_hashsign(Version, KeyAlgorithm),
negotiated_version = Version,
connection_states = ConnectionStates,
premaster_secret = PremasterSecret},
case ssl_session:is_new(OldId, NewId) of
true ->
- handle_new_session(NewId, CipherSuite, Compression, State);
+ handle_new_session(NewId, CipherSuite, Compression,
+ State#state{connection_states = ConnectionStates});
false ->
handle_resumed_session(NewId, State#state{connection_states = ConnectionStates})
end;
@@ -438,12 +433,13 @@ abbreviated(#hello_request{}, State0) ->
abbreviated(#finished{verify_data = Data} = Finished,
#state{role = server,
negotiated_version = Version,
- tls_handshake_hashes = Hashes,
+ tls_handshake_history = Handshake,
session = #session{master_secret = MasterSecret},
connection_states = ConnectionStates0} =
State) ->
case ssl_handshake:verify_connection(Version, Finished, client,
- MasterSecret, Hashes) of
+ get_current_connection_state_prf(ConnectionStates0, write),
+ MasterSecret, Handshake) of
verified ->
ConnectionStates = ssl_record:set_client_verify_data(current_both, Data, ConnectionStates0),
next_state_connection(abbreviated,
@@ -454,18 +450,19 @@ abbreviated(#finished{verify_data = Data} = Finished,
end;
abbreviated(#finished{verify_data = Data} = Finished,
- #state{role = client, tls_handshake_hashes = Hashes0,
+ #state{role = client, tls_handshake_history = Handshake0,
session = #session{master_secret = MasterSecret},
negotiated_version = Version,
connection_states = ConnectionStates0} = State) ->
case ssl_handshake:verify_connection(Version, Finished, server,
- MasterSecret, Hashes0) of
+ get_pending_connection_state_prf(ConnectionStates0, write),
+ MasterSecret, Handshake0) of
verified ->
ConnectionStates1 = ssl_record:set_server_verify_data(current_read, Data, ConnectionStates0),
- {ConnectionStates, Hashes} =
+ {ConnectionStates, Handshake} =
finalize_handshake(State#state{connection_states = ConnectionStates1}, abbreviated),
next_state_connection(abbreviated,
- ack_connection(State#state{tls_handshake_hashes = Hashes,
+ ack_connection(State#state{tls_handshake_history = Handshake,
connection_states =
ConnectionStates}));
#alert{} = Alert ->
@@ -552,8 +549,8 @@ certify(#server_hello_done{},
role = client} = State0) ->
case ssl_handshake:master_secret(Version, Session,
ConnectionStates0, client) of
- {MasterSecret, ConnectionStates1} ->
- State = State0#state{connection_states = ConnectionStates1},
+ {MasterSecret, ConnectionStates} ->
+ State = State0#state{connection_states = ConnectionStates},
client_certify_and_key_exchange(State);
#alert{} = Alert ->
handle_own_alert(Alert, Version, certify, State0),
@@ -569,10 +566,10 @@ certify(#server_hello_done{},
role = client} = State0) ->
case ssl_handshake:master_secret(Version, PremasterSecret,
ConnectionStates0, client) of
- {MasterSecret, ConnectionStates1} ->
+ {MasterSecret, ConnectionStates} ->
Session = Session0#session{master_secret = MasterSecret},
- State = State0#state{connection_states = ConnectionStates1,
- session = Session},
+ State = State0#state{connection_states = ConnectionStates,
+ session = Session},
client_certify_and_key_exchange(State);
#alert{} = Alert ->
handle_own_alert(Alert, Version, certify, State0),
@@ -643,15 +640,20 @@ cipher(#hello_request{}, State0) ->
{Record, State} = next_record(State0),
next_state(cipher, hello, Record, State);
-cipher(#certificate_verify{signature = Signature},
+cipher(#certificate_verify{signature = Signature, hashsign_algorithm = CertHashSign},
#state{role = server,
public_key_info = PublicKeyInfo,
negotiated_version = Version,
session = #session{master_secret = MasterSecret},
- tls_handshake_hashes = Hashes
+ hashsign_algorithm = ConnectionHashSign,
+ tls_handshake_history = Handshake
} = State0) ->
+ HashSign = case CertHashSign of
+ {_, _} -> CertHashSign;
+ _ -> ConnectionHashSign
+ end,
case ssl_handshake:certificate_verify(Signature, PublicKeyInfo,
- Version, MasterSecret, Hashes) of
+ Version, HashSign, MasterSecret, Handshake) of
valid ->
{Record, State} = next_record(State0),
next_state(cipher, cipher, Record, State);
@@ -667,10 +669,12 @@ cipher(#finished{verify_data = Data} = Finished,
role = Role,
session = #session{master_secret = MasterSecret}
= Session0,
- tls_handshake_hashes = Hashes0} = State) ->
+ connection_states = ConnectionStates0,
+ tls_handshake_history = Handshake0} = State) ->
case ssl_handshake:verify_connection(Version, Finished,
opposite_role(Role),
- MasterSecret, Hashes0) of
+ get_current_connection_state_prf(ConnectionStates0, read),
+ MasterSecret, Handshake0) of
verified ->
Session = register_session(Role, Host, Port, Session0),
cipher_role(Role, Data, Session, State);
@@ -691,22 +695,24 @@ cipher(Msg, State) ->
%%--------------------------------------------------------------------
connection(#hello_request{}, #state{host = Host, port = Port,
socket = Socket,
- session = #session{own_certificate = Cert},
+ session = #session{own_certificate = Cert} = Session0,
+ session_cache = Cache, session_cache_cb = CacheCb,
ssl_options = SslOpts,
negotiated_version = Version,
transport_cb = Transport,
connection_states = ConnectionStates0,
renegotiation = {Renegotiation, _},
- tls_handshake_hashes = Hashes0} = State0) ->
- Hello = ssl_handshake:client_hello(Host, Port, ConnectionStates0,
- SslOpts, Renegotiation, Cert),
+ tls_handshake_history = Handshake0} = State0) ->
+ Hello = ssl_handshake:client_hello(Host, Port, ConnectionStates0, SslOpts,
+ Cache, CacheCb, Renegotiation, Cert),
- {BinMsg, ConnectionStates1, Hashes1} =
- encode_handshake(Hello, Version, ConnectionStates0, Hashes0),
+ {BinMsg, ConnectionStates, Handshake} =
+ encode_handshake(Hello, Version, ConnectionStates0, Handshake0),
Transport:send(Socket, BinMsg),
{Record, State} = next_record(State0#state{connection_states =
- ConnectionStates1,
- tls_handshake_hashes = Hashes1}),
+ ConnectionStates,
+ session = Session0#session{session_id = Hello#client_hello.session_id},
+ tls_handshake_history = Handshake}),
next_state(connection, hello, Record, State);
connection(#client_hello{} = Hello, #state{role = server, allow_renegotiate = true} = State) ->
%% Mitigate Computational DoS attack
@@ -732,6 +738,7 @@ connection(timeout, State) ->
connection(Msg, State) ->
handle_unexpected_message(Msg, connection, State).
+
%%--------------------------------------------------------------------
%% Description: Whenever a gen_fsm receives an event sent using
%% gen_fsm:send_all_state_event/2, this function is called to handle
@@ -761,8 +768,8 @@ handle_sync_event({application_data, Data}, From, StateName,
State#state{send_queue = queue:in({From, Data}, Queue)},
get_timeout(State)};
-handle_sync_event(start, From, hello, State) ->
- hello(start, State#state{from = From});
+handle_sync_event(start, StartFrom, hello, State) ->
+ hello(start, State#state{start_or_recv_from = StartFrom});
%% The two clauses below could happen if a server upgrades a socket in
%% active mode. Note that in this case we are lucky that
@@ -774,8 +781,10 @@ handle_sync_event(start, From, hello, State) ->
%% they upgrade a active socket.
handle_sync_event(start, _, connection, State) ->
{reply, connected, connection, State, get_timeout(State)};
-handle_sync_event(start, From, StateName, State) ->
- {next_state, StateName, State#state{from = From}, get_timeout(State)};
+handle_sync_event(start, _From, error, {Error, State = #state{}}) ->
+ {stop, {shutdown, Error}, {error, Error}, State};
+handle_sync_event(start, StartFrom, StateName, State) ->
+ {next_state, StateName, State#state{start_or_recv_from = StartFrom}, get_timeout(State)};
handle_sync_event(close, _, StateName, State) ->
%% Run terminate before returning
@@ -806,13 +815,13 @@ handle_sync_event({shutdown, How0}, _, StateName,
{stop, normal, Error, State}
end;
-handle_sync_event({recv, N}, From, connection = StateName, State0) ->
- passive_receive(State0#state{bytes_to_read = N, recv_from = From}, StateName);
+handle_sync_event({recv, N}, RecvFrom, connection = StateName, State0) ->
+ passive_receive(State0#state{bytes_to_read = N, start_or_recv_from = RecvFrom}, StateName);
%% Doing renegotiate wait with handling request until renegotiate is
%% finished. Will be handled by next_state_is_connection/2.
-handle_sync_event({recv, N}, From, StateName, State) ->
- {next_state, StateName, State#state{bytes_to_read = N, recv_from = From},
+handle_sync_event({recv, N}, RecvFrom, StateName, State) ->
+ {next_state, StateName, State#state{bytes_to_read = N, start_or_recv_from = RecvFrom},
get_timeout(State)};
handle_sync_event({new_user, User}, _From, StateName,
@@ -910,14 +919,14 @@ handle_sync_event(info, _, StateName,
session = #session{cipher_suite = Suite}} = State) ->
AtomVersion = ssl_record:protocol_version(Version),
- {reply, {ok, {AtomVersion, ssl_cipher:suite_definition(Suite)}},
+ {reply, {ok, {AtomVersion, ssl:suite_definition(Suite)}},
StateName, State, get_timeout(State)};
handle_sync_event(session_info, _, StateName,
#state{session = #session{session_id = Id,
cipher_suite = Suite}} = State) ->
{reply, [{session_id, Id},
- {cipher_suite, ssl_cipher:suite_definition(Suite)}],
+ {cipher_suite, ssl:suite_definition(Suite)}],
StateName, State, get_timeout(State)};
handle_sync_event(peer_certificate, _, StateName,
@@ -963,9 +972,9 @@ handle_info({CloseTag, Socket}, StateName,
{stop, normal, State};
handle_info({ErrorTag, Socket, econnaborted}, StateName,
- #state{socket = Socket, from = User, role = Role,
+ #state{socket = Socket, start_or_recv_from = StartFrom, role = Role,
error_tag = ErrorTag} = State) when StateName =/= connection ->
- alert_user(User, ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE), Role),
+ alert_user(StartFrom, ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE), Role),
{stop, normal, State};
handle_info({ErrorTag, Socket, Reason}, StateName, #state{socket = Socket,
@@ -1002,16 +1011,19 @@ terminate(Reason, connection, #state{negotiated_version = Version,
connection_states = ConnectionStates,
transport_cb = Transport,
socket = Socket, send_queue = SendQueue,
- renegotiation = Renegotiate}) ->
+ renegotiation = Renegotiate} = State) ->
+ handle_trusted_certs_db(State),
notify_senders(SendQueue),
notify_renegotiater(Renegotiate),
BinAlert = terminate_alert(Reason, Version, ConnectionStates),
Transport:send(Socket, BinAlert),
workaround_transport_delivery_problems(Socket, Transport, Reason),
Transport:close(Socket);
+
terminate(Reason, _StateName, #state{transport_cb = Transport,
socket = Socket, send_queue = SendQueue,
- renegotiation = Renegotiate}) ->
+ renegotiation = Renegotiate} = State) ->
+ handle_trusted_certs_db(State),
notify_senders(SendQueue),
notify_renegotiater(Renegotiate),
workaround_transport_delivery_problems(Socket, Transport, Reason),
@@ -1059,12 +1071,12 @@ ssl_init(SslOpts, Role) ->
init_manager_name(SslOpts#ssl_options.erl_dist),
- {ok, CertDbRef, CertDbHandle, CacheHandle, OwnCert} = init_certificates(SslOpts, Role),
+ {ok, CertDbRef, CertDbHandle, FileRefHandle, PemCacheHandle, CacheHandle, OwnCert} = init_certificates(SslOpts, Role),
PrivateKey =
- init_private_key(CertDbHandle, SslOpts#ssl_options.key, SslOpts#ssl_options.keyfile,
+ init_private_key(PemCacheHandle, SslOpts#ssl_options.key, SslOpts#ssl_options.keyfile,
SslOpts#ssl_options.password, Role),
- DHParams = init_diffie_hellman(CertDbHandle, SslOpts#ssl_options.dh, SslOpts#ssl_options.dhfile, Role),
- {ok, CertDbRef, CertDbHandle, CacheHandle, OwnCert, PrivateKey, DHParams}.
+ DHParams = init_diffie_hellman(PemCacheHandle, SslOpts#ssl_options.dh, SslOpts#ssl_options.dhfile, Role),
+ {ok, CertDbRef, CertDbHandle, FileRefHandle, CacheHandle, OwnCert, PrivateKey, DHParams}.
init_manager_name(false) ->
put(ssl_manager, ssl_manager);
@@ -1075,7 +1087,7 @@ init_certificates(#ssl_options{cacerts = CaCerts,
cacertfile = CACertFile,
certfile = CertFile,
cert = Cert}, Role) ->
- {ok, CertDbRef, CertDbHandle, CacheHandle} =
+ {ok, CertDbRef, CertDbHandle, FileRefHandle, PemCacheHandle, CacheHandle} =
try
Certs = case CaCerts of
undefined ->
@@ -1083,38 +1095,38 @@ init_certificates(#ssl_options{cacerts = CaCerts,
_ ->
{der, CaCerts}
end,
- {ok, _, _, _} = ssl_manager:connection_init(Certs, Role)
+ {ok, _, _, _, _, _} = ssl_manager:connection_init(Certs, Role)
catch
Error:Reason ->
handle_file_error(?LINE, Error, Reason, CACertFile, ecacertfile,
erlang:get_stacktrace())
end,
- init_certificates(Cert, CertDbRef, CertDbHandle, CacheHandle, CertFile, Role).
+ init_certificates(Cert, CertDbRef, CertDbHandle, FileRefHandle, PemCacheHandle, CacheHandle, CertFile, Role).
-init_certificates(undefined, CertDbRef, CertDbHandle, CacheHandle, "", _) ->
- {ok, CertDbRef, CertDbHandle, CacheHandle, undefined};
+init_certificates(undefined, CertDbRef, CertDbHandle, FileRefHandle, PemCacheHandle, CacheHandle, <<>>, _) ->
+ {ok, CertDbRef, CertDbHandle, FileRefHandle, PemCacheHandle, CacheHandle, undefined};
-init_certificates(undefined, CertDbRef, CertDbHandle, CacheHandle, CertFile, client) ->
+init_certificates(undefined, CertDbRef, CertDbHandle, FileRefHandle, PemCacheHandle, CacheHandle, CertFile, client) ->
try
- [OwnCert] = ssl_certificate:file_to_certificats(CertFile, CertDbHandle),
- {ok, CertDbRef, CertDbHandle, CacheHandle, OwnCert}
+ [OwnCert] = ssl_certificate:file_to_certificats(CertFile, PemCacheHandle),
+ {ok, CertDbRef, CertDbHandle, FileRefHandle, PemCacheHandle, CacheHandle, OwnCert}
catch _Error:_Reason ->
- {ok, CertDbRef, CertDbHandle, CacheHandle, undefined}
+ {ok, CertDbRef, CertDbHandle, FileRefHandle, PemCacheHandle, CacheHandle, undefined}
end;
-init_certificates(undefined, CertDbRef, CertDbHandle, CacheRef, CertFile, server) ->
+init_certificates(undefined, CertDbRef, CertDbHandle, FileRefHandle, PemCacheHandle, CacheRef, CertFile, server) ->
try
- [OwnCert] = ssl_certificate:file_to_certificats(CertFile, CertDbHandle),
- {ok, CertDbRef, CertDbHandle, CacheRef, OwnCert}
+ [OwnCert] = ssl_certificate:file_to_certificats(CertFile, PemCacheHandle),
+ {ok, CertDbRef, CertDbHandle, FileRefHandle, PemCacheHandle, CacheRef, OwnCert}
catch
Error:Reason ->
handle_file_error(?LINE, Error, Reason, CertFile, ecertfile,
erlang:get_stacktrace())
end;
-init_certificates(Cert, CertDbRef, CertDbHandle, CacheRef, _, _) ->
- {ok, CertDbRef, CertDbHandle, CacheRef, Cert}.
+init_certificates(Cert, CertDbRef, CertDbHandle, FileRefHandle, PemCacheHandle, CacheRef, _, _) ->
+ {ok, CertDbRef, CertDbHandle, FileRefHandle, PemCacheHandle, CacheRef, Cert}.
-init_private_key(_, undefined, "", _Password, _Client) ->
+init_private_key(_, undefined, <<>>, _Password, _Client) ->
undefined;
init_private_key(DbHandle, undefined, KeyFile, Password, _) ->
try
@@ -1223,13 +1235,13 @@ certify_client(#state{client_certificate_requested = true, role = client,
cert_db_ref = CertDbRef,
session = #session{own_certificate = OwnCert},
socket = Socket,
- tls_handshake_hashes = Hashes0} = State) ->
+ tls_handshake_history = Handshake0} = State) ->
Certificate = ssl_handshake:certificate(OwnCert, CertDbHandle, CertDbRef, client),
- {BinCert, ConnectionStates1, Hashes1} =
- encode_handshake(Certificate, Version, ConnectionStates0, Hashes0),
+ {BinCert, ConnectionStates, Handshake} =
+ encode_handshake(Certificate, Version, ConnectionStates0, Handshake0),
Transport:send(Socket, BinCert),
- State#state{connection_states = ConnectionStates1,
- tls_handshake_hashes = Hashes1};
+ State#state{connection_states = ConnectionStates,
+ tls_handshake_history = Handshake};
certify_client(#state{client_certificate_requested = false} = State) ->
State.
@@ -1241,17 +1253,19 @@ verify_client_cert(#state{client_certificate_requested = true, role = client,
private_key = PrivateKey,
session = #session{master_secret = MasterSecret,
own_certificate = OwnCert},
- tls_handshake_hashes = Hashes0} = State) ->
+ hashsign_algorithm = HashSign,
+ tls_handshake_history = Handshake0} = State) ->
+ %%TODO: for TLS 1.2 we can choose a different/stronger HashSign combination for this.
case ssl_handshake:client_certificate_verify(OwnCert, MasterSecret,
- Version, PrivateKey, Hashes0) of
+ Version, HashSign, PrivateKey, Handshake0) of
#certificate_verify{} = Verified ->
- {BinVerified, ConnectionStates1, Hashes1} =
+ {BinVerified, ConnectionStates, Handshake} =
encode_handshake(Verified, Version,
- ConnectionStates0, Hashes0),
+ ConnectionStates0, Handshake0),
Transport:send(Socket, BinVerified),
- State#state{connection_states = ConnectionStates1,
- tls_handshake_hashes = Hashes1};
+ State#state{connection_states = ConnectionStates,
+ tls_handshake_history = Handshake};
ignore ->
State;
#alert{} = Alert ->
@@ -1261,7 +1275,7 @@ verify_client_cert(#state{client_certificate_requested = false} = State) ->
State.
do_server_hello(Type, #state{negotiated_version = Version,
- session = #session{session_id = SessId} = Session,
+ session = #session{session_id = SessId},
connection_states = ConnectionStates0,
renegotiation = {Renegotiation, _}}
= State0) when is_atom(Type) ->
@@ -1269,29 +1283,13 @@ do_server_hello(Type, #state{negotiated_version = Version,
ServerHello =
ssl_handshake:server_hello(SessId, Version,
ConnectionStates0, Renegotiation),
- State1 = server_hello(ServerHello, State0),
+ State = server_hello(ServerHello, State0),
case Type of
new ->
- new_server_hello(ServerHello, State1);
+ new_server_hello(ServerHello, State);
resumed ->
- ConnectionStates1 = State1#state.connection_states,
- case ssl_handshake:master_secret(Version, Session,
- ConnectionStates1, server) of
- {_, ConnectionStates2} ->
- State2 = State1#state{connection_states=ConnectionStates2,
- session = Session},
- {ConnectionStates, Hashes} =
- finalize_handshake(State2, abbreviated),
- State3 = State2#state{connection_states =
- ConnectionStates,
- tls_handshake_hashes = Hashes},
- {Record, State} = next_record(State3),
- next_state(hello, abbreviated, Record, State);
- #alert{} = Alert ->
- handle_own_alert(Alert, Version, hello, State1),
- {stop, normal, State1}
- end
+ resumed_server_hello(State)
end.
new_server_hello(#server_hello{cipher_suite = CipherSuite,
@@ -1314,6 +1312,27 @@ new_server_hello(#server_hello{cipher_suite = CipherSuite,
{stop, normal, State0}
end.
+resumed_server_hello(#state{session = Session,
+ connection_states = ConnectionStates0,
+ negotiated_version = Version} = State0) ->
+
+ case ssl_handshake:master_secret(Version, Session,
+ ConnectionStates0, server) of
+ {_, ConnectionStates1} ->
+ State1 = State0#state{connection_states = ConnectionStates1,
+ session = Session},
+ {ConnectionStates, Handshake} =
+ finalize_handshake(State1, abbreviated),
+ State2 = State1#state{connection_states =
+ ConnectionStates,
+ tls_handshake_history = Handshake},
+ {Record, State} = next_record(State2),
+ next_state(hello, abbreviated, Record, State);
+ #alert{} = Alert ->
+ handle_own_alert(Alert, Version, hello, State0),
+ {stop, normal, State0}
+ end.
+
handle_new_session(NewId, CipherSuite, Compression, #state{session = Session0} = State0) ->
Session = Session0#session{session_id = NewId,
cipher_suite = CipherSuite,
@@ -1329,10 +1348,10 @@ handle_resumed_session(SessId, #state{connection_states = ConnectionStates0,
Session = CacheCb:lookup(Cache, {{Host, Port}, SessId}),
case ssl_handshake:master_secret(Version, Session,
ConnectionStates0, client) of
- {_, ConnectionStates1} ->
+ {_, ConnectionStates} ->
{Record, State} =
next_record(State0#state{
- connection_states = ConnectionStates1,
+ connection_states = ConnectionStates,
session = Session}),
next_state(hello, abbreviated, Record, State);
#alert{} = Alert ->
@@ -1345,11 +1364,11 @@ client_certify_and_key_exchange(#state{negotiated_version = Version} =
State0) ->
try do_client_certify_and_key_exchange(State0) of
State1 = #state{} ->
- {ConnectionStates, Hashes} = finalize_handshake(State1, certify),
+ {ConnectionStates, Handshake} = finalize_handshake(State1, certify),
State2 = State1#state{connection_states = ConnectionStates,
%% Reinitialize
client_certificate_requested = false,
- tls_handshake_hashes = Hashes},
+ tls_handshake_history = Handshake},
{Record, State} = next_record(State2),
next_state(certify, cipher, Record, State)
catch
@@ -1372,29 +1391,30 @@ server_hello(ServerHello, #state{transport_cb = Transport,
socket = Socket,
negotiated_version = Version,
connection_states = ConnectionStates0,
- tls_handshake_hashes = Hashes0} = State) ->
+ tls_handshake_history = Handshake0} = State) ->
CipherSuite = ServerHello#server_hello.cipher_suite,
- {KeyAlgorithm, _, _} = ssl_cipher:suite_definition(CipherSuite),
- {BinMsg, ConnectionStates1, Hashes1} =
- encode_handshake(ServerHello, Version, ConnectionStates0, Hashes0),
+ {KeyAlgorithm, _, _, _} = ssl_cipher:suite_definition(CipherSuite),
+ {BinMsg, ConnectionStates1, Handshake1} =
+ encode_handshake(ServerHello, Version, ConnectionStates0, Handshake0),
Transport:send(Socket, BinMsg),
State#state{connection_states = ConnectionStates1,
- tls_handshake_hashes = Hashes1,
- key_algorithm = KeyAlgorithm}.
+ tls_handshake_history = Handshake1,
+ key_algorithm = KeyAlgorithm,
+ hashsign_algorithm = default_hashsign(Version, KeyAlgorithm)}.
server_hello_done(#state{transport_cb = Transport,
socket = Socket,
negotiated_version = Version,
- connection_states = ConnectionStates,
- tls_handshake_hashes = Hashes} = State) ->
+ connection_states = ConnectionStates0,
+ tls_handshake_history = Handshake0} = State) ->
HelloDone = ssl_handshake:server_hello_done(),
- {BinHelloDone, NewConnectionStates, NewHashes} =
- encode_handshake(HelloDone, Version, ConnectionStates, Hashes),
+ {BinHelloDone, ConnectionStates, Handshake} =
+ encode_handshake(HelloDone, Version, ConnectionStates0, Handshake0),
Transport:send(Socket, BinHelloDone),
- State#state{connection_states = NewConnectionStates,
- tls_handshake_hashes = NewHashes}.
+ State#state{connection_states = ConnectionStates,
+ tls_handshake_history = Handshake}.
certify_server(#state{key_algorithm = dh_anon} = State) ->
State;
@@ -1402,18 +1422,18 @@ certify_server(#state{key_algorithm = dh_anon} = State) ->
certify_server(#state{transport_cb = Transport,
socket = Socket,
negotiated_version = Version,
- connection_states = ConnectionStates,
- tls_handshake_hashes = Hashes,
+ connection_states = ConnectionStates0,
+ tls_handshake_history = Handshake0,
cert_db = CertDbHandle,
cert_db_ref = CertDbRef,
session = #session{own_certificate = OwnCert}} = State) ->
case ssl_handshake:certificate(OwnCert, CertDbHandle, CertDbRef, server) of
CertMsg = #certificate{} ->
- {BinCertMsg, NewConnectionStates, NewHashes} =
- encode_handshake(CertMsg, Version, ConnectionStates, Hashes),
+ {BinCertMsg, ConnectionStates, Handshake} =
+ encode_handshake(CertMsg, Version, ConnectionStates0, Handshake0),
Transport:send(Socket, BinCertMsg),
- State#state{connection_states = NewConnectionStates,
- tls_handshake_hashes = NewHashes
+ State#state{connection_states = ConnectionStates,
+ tls_handshake_history = Handshake
};
Alert = #alert{} ->
throw(Alert)
@@ -1422,11 +1442,12 @@ certify_server(#state{transport_cb = Transport,
key_exchange(#state{role = server, key_algorithm = rsa} = State) ->
State;
key_exchange(#state{role = server, key_algorithm = Algo,
+ hashsign_algorithm = HashSignAlgo,
diffie_hellman_params = #'DHParameter'{prime = P, base = G} = Params,
private_key = PrivateKey,
connection_states = ConnectionStates0,
negotiated_version = Version,
- tls_handshake_hashes = Hashes0,
+ tls_handshake_history = Handshake0,
socket = Socket,
transport_cb = Transport
} = State)
@@ -1439,16 +1460,16 @@ key_exchange(#state{role = server, key_algorithm = Algo,
SecParams = ConnectionState#connection_state.security_parameters,
#security_parameters{client_random = ClientRandom,
server_random = ServerRandom} = SecParams,
- Msg = ssl_handshake:key_exchange(server, {dh, Keys, Params,
- Algo, ClientRandom,
+ Msg = ssl_handshake:key_exchange(server, Version, {dh, Keys, Params,
+ HashSignAlgo, ClientRandom,
ServerRandom,
PrivateKey}),
- {BinMsg, ConnectionStates, Hashes1} =
- encode_handshake(Msg, Version, ConnectionStates0, Hashes0),
+ {BinMsg, ConnectionStates, Handshake} =
+ encode_handshake(Msg, Version, ConnectionStates0, Handshake0),
Transport:send(Socket, BinMsg),
State#state{connection_states = ConnectionStates,
diffie_hellman_keys = Keys,
- tls_handshake_hashes = Hashes1};
+ tls_handshake_history = Handshake};
key_exchange(#state{role = client,
connection_states = ConnectionStates0,
@@ -1457,56 +1478,61 @@ key_exchange(#state{role = client,
negotiated_version = Version,
premaster_secret = PremasterSecret,
socket = Socket, transport_cb = Transport,
- tls_handshake_hashes = Hashes0} = State) ->
- Msg = rsa_key_exchange(PremasterSecret, PublicKeyInfo),
- {BinMsg, ConnectionStates1, Hashes1} =
- encode_handshake(Msg, Version, ConnectionStates0, Hashes0),
+ tls_handshake_history = Handshake0} = State) ->
+ Msg = rsa_key_exchange(Version, PremasterSecret, PublicKeyInfo),
+ {BinMsg, ConnectionStates, Handshake} =
+ encode_handshake(Msg, Version, ConnectionStates0, Handshake0),
Transport:send(Socket, BinMsg),
- State#state{connection_states = ConnectionStates1,
- tls_handshake_hashes = Hashes1};
+ State#state{connection_states = ConnectionStates,
+ tls_handshake_history = Handshake};
key_exchange(#state{role = client,
connection_states = ConnectionStates0,
key_algorithm = Algorithm,
negotiated_version = Version,
diffie_hellman_keys = {DhPubKey, _},
socket = Socket, transport_cb = Transport,
- tls_handshake_hashes = Hashes0} = State)
+ tls_handshake_history = Handshake0} = State)
when Algorithm == dhe_dss;
Algorithm == dhe_rsa;
Algorithm == dh_anon ->
- Msg = ssl_handshake:key_exchange(client, {dh, DhPubKey}),
- {BinMsg, ConnectionStates1, Hashes1} =
- encode_handshake(Msg, Version, ConnectionStates0, Hashes0),
+ Msg = ssl_handshake:key_exchange(client, Version, {dh, DhPubKey}),
+ {BinMsg, ConnectionStates, Handshake} =
+ encode_handshake(Msg, Version, ConnectionStates0, Handshake0),
Transport:send(Socket, BinMsg),
- State#state{connection_states = ConnectionStates1,
- tls_handshake_hashes = Hashes1}.
+ State#state{connection_states = ConnectionStates,
+ tls_handshake_history = Handshake}.
-rsa_key_exchange(PremasterSecret, PublicKeyInfo = {Algorithm, _, _})
+rsa_key_exchange(Version, PremasterSecret, PublicKeyInfo = {Algorithm, _, _})
when Algorithm == ?rsaEncryption;
Algorithm == ?md2WithRSAEncryption;
Algorithm == ?md5WithRSAEncryption;
- Algorithm == ?sha1WithRSAEncryption ->
- ssl_handshake:key_exchange(client,
+ Algorithm == ?sha1WithRSAEncryption;
+ Algorithm == ?sha224WithRSAEncryption;
+ Algorithm == ?sha256WithRSAEncryption;
+ Algorithm == ?sha384WithRSAEncryption;
+ Algorithm == ?sha512WithRSAEncryption
+ ->
+ ssl_handshake:key_exchange(client, Version,
{premaster_secret, PremasterSecret,
PublicKeyInfo});
-rsa_key_exchange(_, _) ->
+rsa_key_exchange(_, _, _) ->
throw (?ALERT_REC(?FATAL,?HANDSHAKE_FAILURE)).
request_client_cert(#state{ssl_options = #ssl_options{verify = verify_peer},
connection_states = ConnectionStates0,
cert_db = CertDbHandle,
cert_db_ref = CertDbRef,
- tls_handshake_hashes = Hashes0,
+ tls_handshake_history = Handshake0,
negotiated_version = Version,
socket = Socket,
transport_cb = Transport} = State) ->
Msg = ssl_handshake:certificate_request(ConnectionStates0, CertDbHandle, CertDbRef),
- {BinMsg, ConnectionStates1, Hashes1} =
- encode_handshake(Msg, Version, ConnectionStates0, Hashes0),
+ {BinMsg, ConnectionStates, Handshake} =
+ encode_handshake(Msg, Version, ConnectionStates0, Handshake0),
Transport:send(Socket, BinMsg),
State#state{client_certificate_requested = true,
- connection_states = ConnectionStates1,
- tls_handshake_hashes = Hashes1};
+ connection_states = ConnectionStates,
+ tls_handshake_history = Handshake};
request_client_cert(#state{ssl_options = #ssl_options{verify = verify_none}} =
State) ->
State.
@@ -1532,14 +1558,16 @@ finished(#state{role = Role, socket = Socket, negotiated_version = Version,
transport_cb = Transport,
session = Session,
connection_states = ConnectionStates0,
- tls_handshake_hashes = Hashes0}, StateName) ->
+ tls_handshake_history = Handshake0}, StateName) ->
MasterSecret = Session#session.master_secret,
- Finished = ssl_handshake:finished(Version, Role, MasterSecret, Hashes0),
+ Finished = ssl_handshake:finished(Version, Role,
+ get_current_connection_state_prf(ConnectionStates0, write),
+ MasterSecret, Handshake0),
ConnectionStates1 = save_verify_data(Role, Finished, ConnectionStates0, StateName),
- {BinFinished, ConnectionStates, Hashes} =
- encode_handshake(Finished, Version, ConnectionStates1, Hashes0),
+ {BinFinished, ConnectionStates, Handshake} =
+ encode_handshake(Finished, Version, ConnectionStates1, Handshake0),
Transport:send(Socket, BinFinished),
- {ConnectionStates, Hashes}.
+ {ConnectionStates, Handshake}.
save_verify_data(client, #finished{verify_data = Data}, ConnectionStates, certify) ->
ssl_record:set_client_verify_data(current_write, Data, ConnectionStates);
@@ -1563,36 +1591,41 @@ handle_server_key(
#server_dh_params{dh_p = P,
dh_g = G,
dh_y = ServerPublicDhKey},
- signed_params = Signed},
- #state{public_key_info = PubKeyInfo,
- key_algorithm = KeyAlgo,
+ signed_params = Signed,
+ hashsign = HashSign},
+ #state{negotiated_version = Version,
+ public_key_info = PubKeyInfo,
connection_states = ConnectionStates} = State) ->
PLen = size(P),
GLen = size(G),
YLen = size(ServerPublicDhKey),
+ HashAlgo = connection_hash_algo(HashSign, State),
ConnectionState =
ssl_record:pending_connection_state(ConnectionStates, read),
SecParams = ConnectionState#connection_state.security_parameters,
#security_parameters{client_random = ClientRandom,
server_random = ServerRandom} = SecParams,
- Hash = ssl_handshake:server_key_exchange_hash(KeyAlgo,
+ Hash = ssl_handshake:server_key_exchange_hash(HashAlgo,
<<ClientRandom/binary,
ServerRandom/binary,
?UINT16(PLen), P/binary,
?UINT16(GLen), G/binary,
?UINT16(YLen),
ServerPublicDhKey/binary>>),
-
- case verify_dh_params(Signed, Hash, PubKeyInfo) of
+
+ case verify_dh_params(Version, Signed, Hash, HashAlgo, PubKeyInfo) of
true ->
dh_master_secret(P, G, ServerPublicDhKey, undefined, State);
false ->
?ALERT_REC(?FATAL, ?DECRYPT_ERROR)
end.
-verify_dh_params(Signed, Hashes, {?rsaEncryption, PubKey, _PubKeyParams}) ->
+verify_dh_params({3, Minor}, Signed, Hashes, HashAlgo, {?rsaEncryption, PubKey, _PubKeyParams})
+ when Minor >= 3 ->
+ public_key:verify({digest, Hashes}, HashAlgo, Signed, PubKey);
+verify_dh_params(_Version, Signed, Hashes, _HashAlgo, {?rsaEncryption, PubKey, _PubKeyParams}) ->
case public_key:decrypt_public(Signed, PubKey,
[{rsa_pad, rsa_pkcs1_padding}]) of
Hashes ->
@@ -1600,8 +1633,8 @@ verify_dh_params(Signed, Hashes, {?rsaEncryption, PubKey, _PubKeyParams}) ->
_ ->
false
end;
-verify_dh_params(Signed, Hash, {?'id-dsa', PublicKey, PublicKeyParams}) ->
- public_key:verify(Hash, none, Signed, {PublicKey, PublicKeyParams}).
+verify_dh_params(_Version, Signed, Hash, HashAlgo, {?'id-dsa', PublicKey, PublicKeyParams}) ->
+ public_key:verify({digest, Hash}, HashAlgo, Signed, {PublicKey, PublicKeyParams}).
dh_master_secret(Prime, Base, PublicDhKey, undefined, State) ->
PMpint = mpint_binary(Prime),
@@ -1635,26 +1668,26 @@ cipher_role(client, Data, Session, #state{connection_states = ConnectionStates0}
cipher_role(server, Data, Session, #state{connection_states = ConnectionStates0} = State) ->
ConnectionStates1 = ssl_record:set_client_verify_data(current_read, Data, ConnectionStates0),
- {ConnectionStates, Hashes} =
+ {ConnectionStates, Handshake} =
finalize_handshake(State#state{connection_states = ConnectionStates1,
session = Session}, cipher),
next_state_connection(cipher, ack_connection(State#state{connection_states =
ConnectionStates,
session = Session,
- tls_handshake_hashes =
- Hashes})).
+ tls_handshake_history =
+ Handshake})).
encode_alert(#alert{} = Alert, Version, ConnectionStates) ->
ssl_record:encode_alert_record(Alert, Version, ConnectionStates).
encode_change_cipher(#change_cipher_spec{}, Version, ConnectionStates) ->
ssl_record:encode_change_cipher_spec(Version, ConnectionStates).
-encode_handshake(HandshakeRec, Version, ConnectionStates0, Hashes0) ->
+encode_handshake(HandshakeRec, Version, ConnectionStates0, Handshake0) ->
Frag = ssl_handshake:encode_handshake(HandshakeRec, Version),
- Hashes1 = ssl_handshake:update_hashes(Hashes0, Frag),
+ Handshake1 = ssl_handshake:update_handshake_history(Handshake0, Frag),
{E, ConnectionStates1} =
ssl_record:encode_handshake(Frag, Version, ConnectionStates0),
- {E, ConnectionStates1, Hashes1}.
+ {E, ConnectionStates1, Handshake1}.
encode_packet(Data, #socket_options{packet=Packet}) ->
case Packet of
@@ -1697,7 +1730,7 @@ passive_receive(State0 = #state{user_data_buffer = Buffer}, StateName) ->
read_application_data(Data, #state{user_application = {_Mon, Pid},
socket_options = SOpts,
bytes_to_read = BytesToRead,
- recv_from = From,
+ start_or_recv_from = RecvFrom,
user_data_buffer = Buffer0} = State0) ->
Buffer1 = if
Buffer0 =:= <<>> -> Data;
@@ -1706,9 +1739,9 @@ read_application_data(Data, #state{user_application = {_Mon, Pid},
end,
case get_data(SOpts, BytesToRead, Buffer1) of
{ok, ClientData, Buffer} -> % Send data
- SocketOpt = deliver_app_data(SOpts, ClientData, Pid, From),
+ SocketOpt = deliver_app_data(SOpts, ClientData, Pid, RecvFrom),
State = State0#state{user_data_buffer = Buffer,
- recv_from = undefined,
+ start_or_recv_from = undefined,
bytes_to_read = 0,
socket_options = SocketOpt
},
@@ -1723,7 +1756,7 @@ read_application_data(Data, #state{user_application = {_Mon, Pid},
{more, Buffer} -> % no reply, we need more data
next_record(State0#state{user_data_buffer = Buffer});
{error,_Reason} -> %% Invalid packet in packet mode
- deliver_packet_error(SOpts, Buffer1, Pid, From),
+ deliver_packet_error(SOpts, Buffer1, Pid, RecvFrom),
{stop, normal, State0}
end.
@@ -1843,14 +1876,15 @@ format_reply(binary, _, N, Data) when N > 0 -> % Header mode
format_reply(binary, _, _, Data) ->
Data;
format_reply(list, Packet, _, Data)
- when Packet == http; Packet == {http, headers}; Packet == http_bin; Packet == {http_bin, headers}; Packet == httph;
- Packet == httph_bin->
+ when Packet == http; Packet == {http, headers};
+ Packet == http_bin; Packet == {http_bin, headers};
+ Packet == httph; Packet == httph_bin ->
Data;
format_reply(list, _,_, Data) ->
binary_to_list(Data).
header(0, <<>>) ->
- <<>>;
+ [];
header(_, <<>>) ->
[];
header(0, Binary) ->
@@ -1906,25 +1940,26 @@ next_state(Current, Next, #ssl_tls{type = ?HANDSHAKE, fragment = Data},
fun({#hello_request{} = Packet, _}, {next_state, connection = SName, State}) ->
%% This message should not be included in handshake
%% message hashes. Starts new handshake (renegotiation)
- Hs0 = ssl_handshake:init_hashes(),
- ?MODULE:SName(Packet, State#state{tls_handshake_hashes=Hs0,
+ Hs0 = ssl_handshake:init_handshake_history(),
+ ?MODULE:SName(Packet, State#state{tls_handshake_history=Hs0,
renegotiation = {true, peer}});
({#hello_request{} = Packet, _}, {next_state, SName, State}) ->
%% This message should not be included in handshake
%% message hashes. Already in negotiation so it will be ignored!
?MODULE:SName(Packet, State);
({#client_hello{} = Packet, Raw}, {next_state, connection = SName, State}) ->
- Hs0 = ssl_handshake:init_hashes(),
- Hs1 = ssl_handshake:update_hashes(Hs0, Raw),
- ?MODULE:SName(Packet, State#state{tls_handshake_hashes=Hs1,
+ Version = Packet#client_hello.client_version,
+ Hs0 = ssl_handshake:init_handshake_history(),
+ Hs1 = ssl_handshake:update_handshake_history(Hs0, Raw),
+ ?MODULE:SName(Packet, State#state{tls_handshake_history=Hs1,
renegotiation = {true, peer}});
- ({Packet, Raw}, {next_state, SName, State = #state{tls_handshake_hashes=Hs0}}) ->
- Hs1 = ssl_handshake:update_hashes(Hs0, Raw),
- ?MODULE:SName(Packet, State#state{tls_handshake_hashes=Hs1});
+ ({Packet, Raw}, {next_state, SName, State = #state{tls_handshake_history=Hs0}}) ->
+ Hs1 = ssl_handshake:update_handshake_history(Hs0, Raw),
+ ?MODULE:SName(Packet, State#state{tls_handshake_history=Hs1});
(_, StopState) -> StopState
end,
try
- {Packets, Buf} = ssl_handshake:get_tls_handshake(Data,Buf0),
+ {Packets, Buf} = ssl_handshake:get_tls_handshake(Version,Data,Buf0),
State = State0#state{tls_packets = Packets, tls_handshake_buffer = Buf},
handle_tls_handshake(Handle, Next, State)
catch throw:#alert{} = Alert ->
@@ -2004,22 +2039,22 @@ next_state_connection(StateName, #state{send_queue = Queue0,
next_state_is_connection(StateName, State)
end.
-%% In next_state_is_connection/1: clear tls_handshake_hashes,
+%% In next_state_is_connection/1: clear tls_handshake,
%% premaster_secret and public_key_info (only needed during handshake)
%% to reduce memory foot print of a connection.
next_state_is_connection(_, State =
- #state{recv_from = From,
+ #state{start_or_recv_from = RecvFrom,
socket_options =
- #socket_options{active = false}}) when From =/= undefined ->
+ #socket_options{active = false}}) when RecvFrom =/= undefined ->
passive_receive(State#state{premaster_secret = undefined,
public_key_info = undefined,
- tls_handshake_hashes = {<<>>, <<>>}}, connection);
+ tls_handshake_history = ssl_handshake:init_handshake_history()}, connection);
next_state_is_connection(StateName, State0) ->
{Record, State} = next_record_if_active(State0),
next_state(StateName, connection, Record, State#state{premaster_secret = undefined,
public_key_info = undefined,
- tls_handshake_hashes = {<<>>, <<>>}}).
+ tls_handshake_history = ssl_handshake:init_handshake_history()}).
register_session(client, Host, Port, #session{is_resumable = new} = Session0) ->
Session = Session0#session{is_resumable = true},
@@ -2073,7 +2108,7 @@ initial_state(Role, Host, Port, Socket, {SSLOptions, SocketOptions}, User,
log_alert = true,
session_cache_cb = SessionCacheCb,
renegotiation = {false, first},
- recv_from = undefined,
+ start_or_recv_from = undefined,
send_queue = queue:new()
}.
@@ -2177,7 +2212,7 @@ handle_alerts([Alert | Alerts], {next_state, StateName, State, _Timeout}) ->
handle_alerts(Alerts, handle_alert(Alert, StateName, State)).
handle_alert(#alert{level = ?FATAL} = Alert, StateName,
- #state{from = From, host = Host, port = Port, session = Session,
+ #state{start_or_recv_from = From, host = Host, port = Port, session = Session,
user_application = {_Mon, Pid},
log_alert = Log, role = Role, socket_options = Opts} = State) ->
invalidate_session(Role, Host, Port, Session),
@@ -2259,13 +2294,13 @@ handle_own_alert(Alert, Version, StateName,
ok
end.
-handle_normal_shutdown(Alert, _, #state{from = User, role = Role, renegotiation = {false, first}}) ->
- alert_user(User, Alert, Role);
+handle_normal_shutdown(Alert, _, #state{start_or_recv_from = StartFrom, role = Role, renegotiation = {false, first}}) ->
+ alert_user(StartFrom, Alert, Role);
handle_normal_shutdown(Alert, StateName, #state{socket_options = Opts,
user_application = {_Mon, Pid},
- from = User, role = Role}) ->
- alert_user(StateName, Opts, Pid, User, Alert, Role).
+ start_or_recv_from = RecvFrom, role = Role}) ->
+ alert_user(StateName, Opts, Pid, RecvFrom, Alert, Role).
handle_unexpected_message(Msg, Info, #state{negotiated_version = Version} = State) ->
Alert = ?ALERT_REC(?FATAL,?UNEXPECTED_MESSAGE),
@@ -2273,7 +2308,7 @@ handle_unexpected_message(Msg, Info, #state{negotiated_version = Version} = Stat
{stop, normal, State}.
make_premaster_secret({MajVer, MinVer}, rsa) ->
- Rand = crypto:rand_bytes(?NUM_OF_PREMASTERSECRET_BYTES-2),
+ Rand = ssl:random_bytes(?NUM_OF_PREMASTERSECRET_BYTES-2),
<<?BYTE(MajVer), ?BYTE(MinVer), Rand/binary>>;
make_premaster_secret(_, _) ->
undefined.
@@ -2291,17 +2326,17 @@ ack_connection(#state{renegotiation = {true, From}} = State) ->
gen_fsm:reply(From, ok),
State#state{renegotiation = undefined};
ack_connection(#state{renegotiation = {false, first},
- from = From} = State) when From =/= undefined ->
- gen_fsm:reply(From, connected),
- State#state{renegotiation = undefined};
+ start_or_recv_from = StartFrom} = State) when StartFrom =/= undefined ->
+ gen_fsm:reply(StartFrom, connected),
+ State#state{renegotiation = undefined, start_or_recv_from = undefined};
ack_connection(State) ->
State.
renegotiate(#state{role = client} = State) ->
%% Handle same way as if server requested
%% the renegotiation
- Hs0 = ssl_handshake:init_hashes(),
- connection(#hello_request{}, State#state{tls_handshake_hashes = Hs0});
+ Hs0 = ssl_handshake:init_handshake_history(),
+ connection(#hello_request{}, State#state{tls_handshake_history = Hs0});
renegotiate(#state{role = server,
socket = Socket,
transport_cb = Transport,
@@ -2309,13 +2344,13 @@ renegotiate(#state{role = server,
connection_states = ConnectionStates0} = State0) ->
HelloRequest = ssl_handshake:hello_request(),
Frag = ssl_handshake:encode_handshake(HelloRequest, Version),
- Hs0 = ssl_handshake:init_hashes(),
+ Hs0 = ssl_handshake:init_handshake_history(),
{BinMsg, ConnectionStates} =
ssl_record:encode_handshake(Frag, Version, ConnectionStates0),
Transport:send(Socket, BinMsg),
{Record, State} = next_record(State0#state{connection_states =
ConnectionStates,
- tls_handshake_hashes = Hs0}),
+ tls_handshake_history = Hs0}),
next_state(connection, hello, Record, State#state{allow_renegotiate = true}).
notify_senders(SendQueue) ->
@@ -2359,7 +2394,74 @@ linux_workaround_transport_delivery_problems(#alert{level = ?FATAL}, Socket) ->
linux_workaround_transport_delivery_problems(_, _) ->
ok.
-get_timeout(#state{ssl_options=#ssl_options{hibernate_after=undefined}}) ->
+get_timeout(#state{ssl_options=#ssl_options{hibernate_after = undefined}}) ->
infinity;
-get_timeout(#state{ssl_options=#ssl_options{hibernate_after=HibernateAfter}}) ->
+get_timeout(#state{ssl_options=#ssl_options{hibernate_after = HibernateAfter}}) ->
HibernateAfter.
+
+handle_trusted_certs_db(#state{ssl_options = #ssl_options{cacertfile = <<>>}}) ->
+ %% No trusted certs specified
+ ok;
+handle_trusted_certs_db(#state{cert_db_ref = Ref,
+ cert_db = CertDb,
+ ssl_options = #ssl_options{cacertfile = undefined}}) ->
+ %% Certs provided as DER directly can not be shared
+ %% with other connections and it is safe to delete them when the connection ends.
+ ssl_certificate_db:remove_trusted_certs(Ref, CertDb);
+handle_trusted_certs_db(#state{file_ref_db = undefined}) ->
+ %% Something went wrong early (typically cacertfile does not exist) so there is nothing to handle
+ ok;
+handle_trusted_certs_db(#state{cert_db_ref = Ref,
+ file_ref_db = RefDb,
+ ssl_options = #ssl_options{cacertfile = File}}) ->
+ case ssl_certificate_db:ref_count(Ref, RefDb, -1) of
+ 0 ->
+ ssl_manager:clean_cert_db(Ref, File);
+ _ ->
+ ok
+ end.
+
+get_current_connection_state_prf(CStates, Direction) ->
+ CS = ssl_record:current_connection_state(CStates, Direction),
+ CS#connection_state.security_parameters#security_parameters.prf_algorithm.
+get_pending_connection_state_prf(CStates, Direction) ->
+ CS = ssl_record:pending_connection_state(CStates, Direction),
+ CS#connection_state.security_parameters#security_parameters.prf_algorithm.
+
+connection_hash_algo({HashAlgo, _}, _State) ->
+ HashAlgo;
+connection_hash_algo(_, #state{hashsign_algorithm = {HashAlgo, _}}) ->
+ HashAlgo.
+
+%% RFC 5246, Sect. 7.4.1.4.1. Signature Algorithms
+%% If the client does not send the signature_algorithms extension, the
+%% server MUST do the following:
+%%
+%% - If the negotiated key exchange algorithm is one of (RSA, DHE_RSA,
+%% DH_RSA, RSA_PSK, ECDH_RSA, ECDHE_RSA), behave as if client had
+%% sent the value {sha1,rsa}.
+%%
+%% - If the negotiated key exchange algorithm is one of (DHE_DSS,
+%% DH_DSS), behave as if the client had sent the value {sha1,dsa}.
+%%
+%% - If the negotiated key exchange algorithm is one of (ECDH_ECDSA,
+%% ECDHE_ECDSA), behave as if the client had sent value {sha1,ecdsa}.
+
+default_hashsign(_Version = {Major, Minor}, KeyExchange)
+ when Major == 3 andalso Minor >= 3 andalso
+ (KeyExchange == rsa orelse
+ KeyExchange == dhe_rsa orelse
+ KeyExchange == dh_rsa) ->
+ {sha, rsa};
+default_hashsign(_Version, KeyExchange)
+ when KeyExchange == rsa;
+ KeyExchange == dhe_rsa;
+ KeyExchange == dh_rsa ->
+ {md5sha, rsa};
+default_hashsign(_Version, KeyExchange)
+ when KeyExchange == dhe_dss;
+ KeyExchange == dh_dss ->
+ {sha, dsa};
+default_hashsign(_Version, KeyExchange)
+ when KeyExchange == dh_anon ->
+ {null, anon}.
diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl
index 2e0a3de182..bb26302fff 100644
--- a/lib/ssl/src/ssl_handshake.erl
+++ b/lib/ssl/src/ssl_handshake.erl
@@ -30,13 +30,13 @@
-include("ssl_internal.hrl").
-include_lib("public_key/include/public_key.hrl").
--export([master_secret/4, client_hello/6, server_hello/4, hello/4,
+-export([master_secret/4, client_hello/8, server_hello/4, hello/4,
hello_request/0, certify/7, certificate/4,
- client_certificate_verify/5, certificate_verify/5,
- certificate_request/3, key_exchange/2, server_key_exchange_hash/2,
- finished/4, verify_connection/5, get_tls_handshake/2,
+ client_certificate_verify/6, certificate_verify/6,
+ certificate_request/3, key_exchange/3, server_key_exchange_hash/2,
+ finished/5, verify_connection/6, get_tls_handshake/3,
decode_client_key/3, server_hello_done/0,
- encode_handshake/2, init_hashes/0, update_hashes/2,
+ encode_handshake/2, init_handshake_history/0, update_handshake_history/2,
decrypt_premaster_secret/2, prf/5]).
-export([dec_hello_extensions/2]).
@@ -51,14 +51,17 @@
%%====================================================================
%%--------------------------------------------------------------------
-spec client_hello(host(), inet:port_number(), #connection_states{},
- #ssl_options{}, boolean(), der_cert()) -> #client_hello{}.
+ #ssl_options{}, integer(), atom(), boolean(), der_cert()) ->
+ #client_hello{}.
%%
%% Description: Creates a client hello message.
%%--------------------------------------------------------------------
-client_hello(Host, Port, ConnectionStates, #ssl_options{versions = Versions,
- ciphers = UserSuites}
- = SslOpts, Renegotiation, OwnCert) ->
-
+client_hello(Host, Port, ConnectionStates,
+ #ssl_options{versions = Versions,
+ ciphers = UserSuites
+ } = SslOpts,
+ Cache, CacheCb, Renegotiation, OwnCert) ->
+
Fun = fun(Version) ->
ssl_record:protocol_version(Version)
end,
@@ -67,15 +70,16 @@ client_hello(Host, Port, ConnectionStates, #ssl_options{versions = Versions,
SecParams = Pending#connection_state.security_parameters,
Ciphers = available_suites(UserSuites, Version),
- Id = ssl_manager:client_session_id(Host, Port, SslOpts, OwnCert),
+ Id = ssl_session:client_id({Host, Port, SslOpts}, Cache, CacheCb, OwnCert),
- #client_hello{session_id = Id,
+ #client_hello{session_id = Id,
client_version = Version,
cipher_suites = cipher_suites(Ciphers, Renegotiation),
compression_methods = ssl_record:compressions(),
random = SecParams#security_parameters.client_random,
- renegotiation_info =
- renegotiation_info(client, ConnectionStates, Renegotiation)
+ renegotiation_info =
+ renegotiation_info(client, ConnectionStates, Renegotiation),
+ hash_signs = default_hash_signs()
}.
%%--------------------------------------------------------------------
@@ -118,17 +122,18 @@ hello_request() ->
%%--------------------------------------------------------------------
hello(#server_hello{cipher_suite = CipherSuite, server_version = Version,
compression_method = Compression, random = Random,
- session_id = SessionId, renegotiation_info = Info},
+ session_id = SessionId, renegotiation_info = Info,
+ hash_signs = _HashSigns},
#ssl_options{secure_renegotiate = SecureRenegotation},
ConnectionStates0, Renegotiation) ->
-
+%%TODO: select hash and signature algorigthm
case ssl_record:is_acceptable_version(Version) of
true ->
case handle_renegotiation_info(client, Info, ConnectionStates0,
Renegotiation, SecureRenegotation, []) of
{ok, ConnectionStates1} ->
ConnectionStates =
- hello_pending_connection_states(client, CipherSuite, Random,
+ hello_pending_connection_states(client, Version, CipherSuite, Random,
Compression, ConnectionStates1),
{Version, SessionId, ConnectionStates};
#alert{} = Alert ->
@@ -140,10 +145,12 @@ hello(#server_hello{cipher_suite = CipherSuite, server_version = Version,
hello(#client_hello{client_version = ClientVersion, random = Random,
cipher_suites = CipherSuites,
- renegotiation_info = Info} = Hello,
+ renegotiation_info = Info,
+ hash_signs = _HashSigns} = Hello,
#ssl_options{versions = Versions,
secure_renegotiate = SecureRenegotation} = SslOpts,
{Port, Session0, Cache, CacheCb, ConnectionStates0, Cert}, Renegotiation) ->
+%% TODO: select hash and signature algorithm
Version = select_version(ClientVersion, Versions),
case ssl_record:is_acceptable_version(Version) of
true ->
@@ -161,6 +168,7 @@ hello(#client_hello{client_version = ClientVersion, random = Random,
{ok, ConnectionStates1} ->
ConnectionStates =
hello_pending_connection_states(server,
+ Version,
CipherSuite,
Random,
Compression,
@@ -212,18 +220,23 @@ certify(#certificate{asn1_certificates = ASN1Certs}, CertDbHandle, CertDbRef,
end, {Role, UserState0}}
end,
- {TrustedErlCert, CertPath} =
- ssl_certificate:trusted_cert_and_path(ASN1Certs, CertDbHandle, CertDbRef),
-
- case public_key:pkix_path_validation(TrustedErlCert,
- CertPath,
- [{max_path_length,
- MaxPathLen},
- {verify_fun, ValidationFunAndState}]) of
- {ok, {PublicKeyInfo,_}} ->
- {PeerCert, PublicKeyInfo};
- {error, Reason} ->
- path_validation_alert(Reason)
+ try
+ {TrustedErlCert, CertPath} =
+ ssl_certificate:trusted_cert_and_path(ASN1Certs, CertDbHandle, CertDbRef),
+ case public_key:pkix_path_validation(TrustedErlCert,
+ CertPath,
+ [{max_path_length,
+ MaxPathLen},
+ {verify_fun, ValidationFunAndState}]) of
+ {ok, {PublicKeyInfo,_}} ->
+ {PeerCert, PublicKeyInfo};
+ {error, Reason} ->
+ path_validation_alert(Reason)
+ end
+ catch
+ error:_ ->
+ %% ASN-1 decode of certificate somehow failed
+ ?ALERT_REC(?FATAL, ?CERTIFICATE_UNKNOWN)
end.
%%--------------------------------------------------------------------
@@ -254,54 +267,51 @@ certificate(OwnCert, CertDbHandle, CertDbRef, server) ->
%%--------------------------------------------------------------------
-spec client_certificate_verify(undefined | der_cert(), binary(),
- tls_version(), private_key(),
- {{binary(), binary()},{binary(), binary()}}) ->
+ tls_version(), term(), private_key(),
+ tls_handshake_history()) ->
#certificate_verify{} | ignore | #alert{}.
%%
%% Description: Creates a certificate_verify message, called by the client.
%%--------------------------------------------------------------------
-client_certificate_verify(undefined, _, _, _, _) ->
+client_certificate_verify(undefined, _, _, _, _, _) ->
ignore;
-client_certificate_verify(_, _, _, undefined, _) ->
+client_certificate_verify(_, _, _, _, undefined, _) ->
ignore;
client_certificate_verify(OwnCert, MasterSecret, Version,
- PrivateKey, {Hashes0, _}) ->
+ {HashAlgo, SignAlgo},
+ PrivateKey, {Handshake, _}) ->
case public_key:pkix_is_fixed_dh_cert(OwnCert) of
true ->
?ALERT_REC(?FATAL, ?UNSUPPORTED_CERTIFICATE);
- false ->
- Hashes =
- calc_certificate_verify(Version, MasterSecret,
- alg_oid(PrivateKey), Hashes0),
- Signed = digitally_signed(Hashes, PrivateKey),
- #certificate_verify{signature = Signed}
+ false ->
+ Hashes =
+ calc_certificate_verify(Version, HashAlgo, MasterSecret, Handshake),
+ Signed = digitally_signed(Version, Hashes, HashAlgo, PrivateKey),
+ #certificate_verify{signature = Signed, hashsign_algorithm = {HashAlgo, SignAlgo}}
end.
%%--------------------------------------------------------------------
--spec certificate_verify(binary(), public_key_info(), tls_version(),
- binary(), {_, {binary(), binary()}}) -> valid | #alert{}.
+-spec certificate_verify(binary(), public_key_info(), tls_version(), term(),
+ binary(), tls_handshake_history()) -> valid | #alert{}.
%%
%% Description: Checks that the certificate_verify message is valid.
%%--------------------------------------------------------------------
-certificate_verify(Signature, {?'rsaEncryption'= Algorithm, PublicKey, _}, Version,
- MasterSecret, {_, Hashes0}) ->
- Hashes = calc_certificate_verify(Version, MasterSecret,
- Algorithm, Hashes0),
- case public_key:decrypt_public(Signature, PublicKey,
- [{rsa_pad, rsa_pkcs1_padding}]) of
- Hashes ->
+certificate_verify(Signature, {?'rsaEncryption', PublicKey, _}, Version,
+ {HashAlgo, _SignAlgo}, MasterSecret, {_, Handshake}) ->
+ Hashes = calc_certificate_verify(Version, HashAlgo, MasterSecret, Handshake),
+ case certificate_verify_rsa(Hashes, HashAlgo, Signature, PublicKey, Version) of
+ true ->
valid;
_ ->
?ALERT_REC(?FATAL, ?BAD_CERTIFICATE)
end;
-certificate_verify(Signature, {?'id-dsa' = Algorithm, PublicKey, PublicKeyParams}, Version,
- MasterSecret, {_, Hashes0}) ->
- Hashes = calc_certificate_verify(Version, MasterSecret,
- Algorithm, Hashes0),
- case public_key:verify(Hashes, none, Signature, {PublicKey, PublicKeyParams}) of
- true ->
- valid;
- false ->
+certificate_verify(Signature, {?'id-dsa', PublicKey, PublicKeyParams}, Version,
+ {HashAlgo, _SignAlgo}, MasterSecret, {_, Handshake}) ->
+ Hashes = calc_certificate_verify(Version, HashAlgo, MasterSecret, Handshake),
+ case public_key:verify({digest, Hashes}, sha, Signature, {PublicKey, PublicKeyParams}) of
+ true ->
+ valid;
+ false ->
?ALERT_REC(?FATAL, ?BAD_CERTIFICATE)
end.
@@ -317,36 +327,38 @@ certificate_request(ConnectionStates, CertDbHandle, CertDbRef) ->
#security_parameters{cipher_suite = CipherSuite}} =
ssl_record:pending_connection_state(ConnectionStates, read),
Types = certificate_types(CipherSuite),
+ HashSigns = default_hash_signs(),
Authorities = certificate_authorities(CertDbHandle, CertDbRef),
#certificate_request{
certificate_types = Types,
+ hashsign_algorithms = HashSigns,
certificate_authorities = Authorities
}.
%%--------------------------------------------------------------------
--spec key_exchange(client | server,
+-spec key_exchange(client | server, tls_version(),
{premaster_secret, binary(), public_key_info()} |
{dh, binary()} |
- {dh, {binary(), binary()}, #'DHParameter'{}, key_algo(),
+ {dh, {binary(), binary()}, #'DHParameter'{}, {HashAlgo::atom(), SignAlgo::atom()},
binary(), binary(), private_key()}) ->
#client_key_exchange{} | #server_key_exchange{}.
%%
%% Description: Creates a keyexchange message.
%%--------------------------------------------------------------------
-key_exchange(client, {premaster_secret, Secret, {_, PublicKey, _}}) ->
+key_exchange(client, _Version, {premaster_secret, Secret, {_, PublicKey, _}}) ->
EncPremasterSecret =
encrypted_premaster_secret(Secret, PublicKey),
#client_key_exchange{exchange_keys = EncPremasterSecret};
-key_exchange(client, {dh, <<?UINT32(Len), PublicKey:Len/binary>>}) ->
+key_exchange(client, _Version, {dh, <<?UINT32(Len), PublicKey:Len/binary>>}) ->
#client_key_exchange{
exchange_keys = #client_diffie_hellman_public{
dh_public = PublicKey}
};
-key_exchange(server, {dh, {<<?UINT32(Len), PublicKey:Len/binary>>, _},
+key_exchange(server, Version, {dh, {<<?UINT32(Len), PublicKey:Len/binary>>, _},
#'DHParameter'{prime = P, base = G},
- KeyAlgo, ClientRandom, ServerRandom, PrivateKey}) ->
+ {HashAlgo, SignAlgo}, ClientRandom, ServerRandom, PrivateKey}) ->
<<?UINT32(_), PBin/binary>> = crypto:mpint(P),
<<?UINT32(_), GBin/binary>> = crypto:mpint(G),
PLen = byte_size(PBin),
@@ -355,20 +367,22 @@ key_exchange(server, {dh, {<<?UINT32(Len), PublicKey:Len/binary>>, _},
ServerDHParams = #server_dh_params{dh_p = PBin,
dh_g = GBin, dh_y = PublicKey},
- case KeyAlgo of
- dh_anon ->
+ case HashAlgo of
+ null ->
#server_key_exchange{params = ServerDHParams,
- signed_params = <<>>};
+ signed_params = <<>>,
+ hashsign = {null, anon}};
_ ->
Hash =
- server_key_exchange_hash(KeyAlgo, <<ClientRandom/binary,
+ server_key_exchange_hash(HashAlgo, <<ClientRandom/binary,
ServerRandom/binary,
?UINT16(PLen), PBin/binary,
?UINT16(GLen), GBin/binary,
?UINT16(YLen), PublicKey/binary>>),
- Signed = digitally_signed(Hash, PrivateKey),
+ Signed = digitally_signed(Version, Hash, HashAlgo, PrivateKey),
#server_key_exchange{params = ServerDHParams,
- signed_params = Signed}
+ signed_params = Signed,
+ hashsign = {HashAlgo, SignAlgo}}
end.
%%--------------------------------------------------------------------
@@ -398,10 +412,11 @@ master_secret(Version, PremasterSecret, ConnectionStates, Role) ->
ConnectionState =
ssl_record:pending_connection_state(ConnectionStates, read),
SecParams = ConnectionState#connection_state.security_parameters,
- #security_parameters{client_random = ClientRandom,
+ #security_parameters{prf_algorithm = PrfAlgo,
+ client_random = ClientRandom,
server_random = ServerRandom} = SecParams,
try master_secret(Version,
- calc_master_secret(Version,PremasterSecret,
+ calc_master_secret(Version,PrfAlgo,PremasterSecret,
ClientRandom, ServerRandom),
SecParams, ConnectionStates, Role)
catch
@@ -413,26 +428,26 @@ master_secret(Version, PremasterSecret, ConnectionStates, Role) ->
end.
%%--------------------------------------------------------------------
--spec finished(tls_version(), client | server, binary(), {{binary(), binary()},_}) ->
+-spec finished(tls_version(), client | server, integer(), binary(), tls_handshake_history()) ->
#finished{}.
%%
%% Description: Creates a handshake finished message
%%-------------------------------------------------------------------
-finished(Version, Role, MasterSecret, {Hashes, _}) -> % use the current hashes
+finished(Version, Role, PrfAlgo, MasterSecret, {Handshake, _}) -> % use the current handshake
#finished{verify_data =
- calc_finished(Version, Role, MasterSecret, Hashes)}.
+ calc_finished(Version, Role, PrfAlgo, MasterSecret, Handshake)}.
%%--------------------------------------------------------------------
--spec verify_connection(tls_version(), #finished{}, client | server, binary(),
- {_, {binary(), binary()}}) -> verified | #alert{}.
+-spec verify_connection(tls_version(), #finished{}, client | server, integer(), binary(),
+ tls_handshake_history()) -> verified | #alert{}.
%%
%% Description: Checks the ssl handshake finished message to verify
%% the connection.
%%-------------------------------------------------------------------
verify_connection(Version, #finished{verify_data = Data},
- Role, MasterSecret, {_, {MD5, SHA}}) ->
+ Role, PrfAlgo, MasterSecret, {_, Handshake}) ->
%% use the previous hashes
- case calc_finished(Version, Role, MasterSecret, {MD5, SHA}) of
+ case calc_finished(Version, Role, PrfAlgo, MasterSecret, Handshake) of
Data ->
verified;
_ ->
@@ -457,17 +472,17 @@ encode_handshake(Package, Version) ->
[MsgType, ?uint24(Len), Bin].
%%--------------------------------------------------------------------
--spec get_tls_handshake(binary(), binary() | iolist()) ->
+-spec get_tls_handshake(tls_version(), binary(), binary() | iolist()) ->
{[tls_handshake()], binary()}.
%%
%% Description: Given buffered and new data from ssl_record, collects
%% and returns it as a list of handshake messages, also returns leftover
%% data.
%%--------------------------------------------------------------------
-get_tls_handshake(Data, <<>>) ->
- get_tls_handshake_aux(Data, []);
-get_tls_handshake(Data, Buffer) ->
- get_tls_handshake_aux(list_to_binary([Buffer, Data]), []).
+get_tls_handshake(Version, Data, <<>>) ->
+ get_tls_handshake_aux(Version, Data, []);
+get_tls_handshake(Version, Data, Buffer) ->
+ get_tls_handshake_aux(Version, list_to_binary([Buffer, Data]), []).
%%--------------------------------------------------------------------
-spec decode_client_key(binary(), key_algo(), tls_version()) ->
@@ -479,39 +494,34 @@ decode_client_key(ClientKey, Type, Version) ->
dec_client_key(ClientKey, key_exchange_alg(Type), Version).
%%--------------------------------------------------------------------
--spec init_hashes() ->{{binary(), binary()}, {binary(), binary()}}.
+-spec init_handshake_history() -> tls_handshake_history().
%%
-%% Description: Calls crypto hash (md5 and sha) init functions to
-%% initalize the hash context.
+%% Description: Initialize the empty handshake history buffer.
%%--------------------------------------------------------------------
-init_hashes() ->
- T = {crypto:md5_init(), crypto:sha_init()},
- {T, T}.
+init_handshake_history() ->
+ {[], []}.
%%--------------------------------------------------------------------
--spec update_hashes({{binary(), binary()}, {binary(), binary()}}, Data ::term()) ->
- {{binary(), binary()}, {binary(), binary()}}.
+-spec update_handshake_history(tls_handshake_history(), Data ::term()) ->
+ tls_handshake_history().
%%
-%% Description: Calls crypto hash (md5 and sha) update functions to
-%% update the hash context with Data.
+%% Description: Update the handshake history buffer with Data.
%%--------------------------------------------------------------------
-update_hashes(Hashes, % special-case SSL2 client hello
- <<?CLIENT_HELLO, ?UINT24(_), ?BYTE(Major), ?BYTE(Minor),
- ?UINT16(CSLength), ?UINT16(0),
- ?UINT16(CDLength),
- CipherSuites:CSLength/binary,
- ChallengeData:CDLength/binary>>) ->
- update_hashes(Hashes,
- <<?CLIENT_HELLO, ?BYTE(Major), ?BYTE(Minor),
- ?UINT16(CSLength), ?UINT16(0),
- ?UINT16(CDLength),
- CipherSuites:CSLength/binary,
- ChallengeData:CDLength/binary>>);
-update_hashes({{MD50, SHA0}, _Prev}, Data) ->
- {MD51, SHA1} = {crypto:md5_update(MD50, Data),
- crypto:sha_update(SHA0, Data)},
- {{MD51, SHA1}, {MD50, SHA0}}.
+update_handshake_history(Handshake, % special-case SSL2 client hello
+ <<?CLIENT_HELLO, ?UINT24(_), ?BYTE(Major), ?BYTE(Minor),
+ ?UINT16(CSLength), ?UINT16(0),
+ ?UINT16(CDLength),
+ CipherSuites:CSLength/binary,
+ ChallengeData:CDLength/binary>>) ->
+ update_handshake_history(Handshake,
+ <<?CLIENT_HELLO, ?BYTE(Major), ?BYTE(Minor),
+ ?UINT16(CSLength), ?UINT16(0),
+ ?UINT16(CDLength),
+ CipherSuites:CSLength/binary,
+ ChallengeData:CDLength/binary>>);
+update_handshake_history({Handshake0, _Prev}, Data) ->
+ {[Data|Handshake0], Handshake0}.
%%--------------------------------------------------------------------
-spec decrypt_premaster_secret(binary(), #'RSAPrivateKey'{}) -> binary().
@@ -524,23 +534,22 @@ decrypt_premaster_secret(Secret, RSAPrivateKey) ->
[{rsa_pad, rsa_pkcs1_padding}])
catch
_:_ ->
+ io:format("decrypt_premaster_secret error"),
throw(?ALERT_REC(?FATAL, ?DECRYPT_ERROR))
end.
%%--------------------------------------------------------------------
--spec server_key_exchange_hash(rsa | dhe_rsa| dhe_dss | dh_anon, binary()) -> binary().
-
+-spec server_key_exchange_hash(md5sha | md5 | sha | sha224 |sha256 | sha384 | sha512, binary()) -> binary().
%%
%% Description: Calculate server key exchange hash
%%--------------------------------------------------------------------
-server_key_exchange_hash(Algorithm, Value) when Algorithm == rsa;
- Algorithm == dhe_rsa ->
+server_key_exchange_hash(md5sha, Value) ->
MD5 = crypto:md5(Value),
- SHA = crypto:sha(Value),
+ SHA = crypto:sha(Value),
<<MD5/binary, SHA/binary>>;
-server_key_exchange_hash(dhe_dss, Value) ->
- crypto:sha(Value).
+server_key_exchange_hash(Hash, Value) ->
+ crypto:hash(Hash, Value).
%%--------------------------------------------------------------------
-spec prf(tls_version(), binary(), binary(), [binary()], non_neg_integer()) ->
@@ -550,19 +559,20 @@ server_key_exchange_hash(dhe_dss, Value) ->
%%--------------------------------------------------------------------
prf({3,0}, _, _, _, _) ->
{error, undefined};
-prf({3,N}, Secret, Label, Seed, WantedLength)
- when N == 1; N == 2 ->
- {ok, ssl_tls1:prf(Secret, Label, Seed, WantedLength)}.
+prf({3,1}, Secret, Label, Seed, WantedLength) ->
+ {ok, ssl_tls1:prf(?MD5SHA, Secret, Label, Seed, WantedLength)};
+prf({3,_N}, Secret, Label, Seed, WantedLength) ->
+ {ok, ssl_tls1:prf(?SHA256, Secret, Label, Seed, WantedLength)}.
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
-get_tls_handshake_aux(<<?BYTE(Type), ?UINT24(Length),
+get_tls_handshake_aux(Version, <<?BYTE(Type), ?UINT24(Length),
Body:Length/binary,Rest/binary>>, Acc) ->
Raw = <<?BYTE(Type), ?UINT24(Length), Body/binary>>,
- H = dec_hs(Type, Body),
- get_tls_handshake_aux(Rest, [{H,Raw} | Acc]);
-get_tls_handshake_aux(Data, Acc) ->
+ H = dec_hs(Version, Type, Body),
+ get_tls_handshake_aux(Version, Rest, [{H,Raw} | Acc]);
+get_tls_handshake_aux(_Version, Data, Acc) ->
{lists:reverse(Acc), Data}.
path_validation_alert({bad_cert, cert_expired}) ->
@@ -584,24 +594,23 @@ path_validation_alert({bad_cert, unknown_ca}) ->
path_validation_alert(_) ->
?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE).
-select_session(Hello, Port, Session, Version,
+select_session(Hello, Port, Session, Version,
#ssl_options{ciphers = UserSuites} = SslOpts, Cache, CacheCb, Cert) ->
SuggestedSessionId = Hello#client_hello.session_id,
- SessionId = ssl_manager:server_session_id(Port, SuggestedSessionId,
- SslOpts, Cert),
-
- Suites = available_suites(Cert, UserSuites, Version),
- case ssl_session:is_new(SuggestedSessionId, SessionId) of
- true ->
- CipherSuite =
- select_cipher_suite(Hello#client_hello.cipher_suites, Suites),
+ {SessionId, Resumed} = ssl_session:server_id(Port, SuggestedSessionId,
+ SslOpts, Cert,
+ Cache, CacheCb),
+ Suites = available_suites(Cert, UserSuites, Version),
+ case Resumed of
+ undefined ->
+ CipherSuite = select_cipher_suite(Hello#client_hello.cipher_suites, Suites),
Compressions = Hello#client_hello.compression_methods,
Compression = select_compression(Compressions),
{new, Session#session{session_id = SessionId,
cipher_suite = CipherSuite,
compression_method = Compression}};
- false ->
- {resumed, CacheCb:lookup(Cache, {Port, SessionId})}
+ _ ->
+ {resumed, Resumed}
end.
available_suites(UserSuites, Version) ->
@@ -720,7 +729,7 @@ handle_renegotiation_info(ConnectionStates, SecureRenegotation) ->
%% hello messages
%% NOTE : Role is the role of the receiver of the hello message
%% currently being processed.
-hello_pending_connection_states(Role, CipherSuite, Random, Compression,
+hello_pending_connection_states(Role, Version, CipherSuite, Random, Compression,
ConnectionStates) ->
ReadState =
ssl_record:pending_connection_state(ConnectionStates, read),
@@ -728,30 +737,30 @@ hello_pending_connection_states(Role, CipherSuite, Random, Compression,
ssl_record:pending_connection_state(ConnectionStates, write),
NewReadSecParams =
- hello_security_parameters(Role, ReadState, CipherSuite,
+ hello_security_parameters(Role, Version, ReadState, CipherSuite,
Random, Compression),
NewWriteSecParams =
- hello_security_parameters(Role, WriteState, CipherSuite,
+ hello_security_parameters(Role, Version, WriteState, CipherSuite,
Random, Compression),
ssl_record:update_security_params(NewReadSecParams,
NewWriteSecParams,
ConnectionStates).
-hello_security_parameters(client, ConnectionState, CipherSuite, Random,
+hello_security_parameters(client, Version, ConnectionState, CipherSuite, Random,
Compression) ->
SecParams = ConnectionState#connection_state.security_parameters,
- NewSecParams = ssl_cipher:security_parameters(CipherSuite, SecParams),
+ NewSecParams = ssl_cipher:security_parameters(Version, CipherSuite, SecParams),
NewSecParams#security_parameters{
server_random = Random,
compression_algorithm = Compression
};
-hello_security_parameters(server, ConnectionState, CipherSuite, Random,
+hello_security_parameters(server, Version, ConnectionState, CipherSuite, Random,
Compression) ->
SecParams = ConnectionState#connection_state.security_parameters,
- NewSecParams = ssl_cipher:security_parameters(CipherSuite, SecParams),
+ NewSecParams = ssl_cipher:security_parameters(Version, CipherSuite, SecParams),
NewSecParams#security_parameters{
client_random = Random,
compression_algorithm = Compression
@@ -785,13 +794,14 @@ master_secret(Version, MasterSecret, #security_parameters{
client_random = ClientRandom,
server_random = ServerRandom,
hash_size = HashSize,
+ prf_algorithm = PrfAlgo,
key_material_length = KML,
expanded_key_material_length = EKML,
iv_size = IVS},
ConnectionStates, Role) ->
{ClientWriteMacSecret, ServerWriteMacSecret, ClientWriteKey,
ServerWriteKey, ClientIV, ServerIV} =
- setup_keys(Version, MasterSecret, ServerRandom,
+ setup_keys(Version, PrfAlgo, MasterSecret, ServerRandom,
ClientRandom, HashSize, KML, EKML, IVS),
ConnStates1 = ssl_record:set_master_secret(MasterSecret, ConnectionStates),
@@ -806,13 +816,13 @@ master_secret(Version, MasterSecret, #security_parameters{
ServerCipherState, Role)}.
-dec_hs(?HELLO_REQUEST, <<>>) ->
+dec_hs(_Version, ?HELLO_REQUEST, <<>>) ->
#hello_request{};
%% Client hello v2.
%% The server must be able to receive such messages, from clients that
%% are willing to use ssl v3 or higher, but have ssl v2 compatibility.
-dec_hs(?CLIENT_HELLO, <<?BYTE(Major), ?BYTE(Minor),
+dec_hs(_Version, ?CLIENT_HELLO, <<?BYTE(Major), ?BYTE(Minor),
?UINT16(CSLength), ?UINT16(0),
?UINT16(CDLength),
CipherSuites:CSLength/binary,
@@ -824,24 +834,27 @@ dec_hs(?CLIENT_HELLO, <<?BYTE(Major), ?BYTE(Minor),
compression_methods = [?NULL],
renegotiation_info = undefined
};
-dec_hs(?CLIENT_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary,
+dec_hs(_Version, ?CLIENT_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary,
?BYTE(SID_length), Session_ID:SID_length/binary,
?UINT16(Cs_length), CipherSuites:Cs_length/binary,
?BYTE(Cm_length), Comp_methods:Cm_length/binary,
Extensions/binary>>) ->
-
- RenegotiationInfo = proplists:get_value(renegotiation_info, dec_hello_extensions(Extensions),
- undefined),
+ HelloExtensions = dec_hello_extensions(Extensions),
+ RenegotiationInfo = proplists:get_value(renegotiation_info, HelloExtensions,
+ undefined),
+ HashSigns = proplists:get_value(hash_signs, HelloExtensions,
+ undefined),
#client_hello{
client_version = {Major,Minor},
random = Random,
session_id = Session_ID,
cipher_suites = from_2bytes(CipherSuites),
compression_methods = Comp_methods,
- renegotiation_info = RenegotiationInfo
+ renegotiation_info = RenegotiationInfo,
+ hash_signs = HashSigns
};
-dec_hs(?SERVER_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary,
+dec_hs(_Version, ?SERVER_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary,
?BYTE(SID_length), Session_ID:SID_length/binary,
Cipher_suite:2/binary, ?BYTE(Comp_method)>>) ->
#server_hello{
@@ -850,53 +863,81 @@ dec_hs(?SERVER_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary,
session_id = Session_ID,
cipher_suite = Cipher_suite,
compression_method = Comp_method,
- renegotiation_info = undefined};
+ renegotiation_info = undefined,
+ hash_signs = undefined};
-dec_hs(?SERVER_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary,
+dec_hs(_Version, ?SERVER_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary,
?BYTE(SID_length), Session_ID:SID_length/binary,
Cipher_suite:2/binary, ?BYTE(Comp_method),
?UINT16(ExtLen), Extensions:ExtLen/binary>>) ->
- RenegotiationInfo = proplists:get_value(renegotiation_info, dec_hello_extensions(Extensions, []),
- undefined),
+ HelloExtensions = dec_hello_extensions(Extensions, []),
+ RenegotiationInfo = proplists:get_value(renegotiation_info, HelloExtensions,
+ undefined),
+ HashSigns = proplists:get_value(hash_signs, HelloExtensions,
+ undefined),
#server_hello{
server_version = {Major,Minor},
random = Random,
session_id = Session_ID,
cipher_suite = Cipher_suite,
compression_method = Comp_method,
- renegotiation_info = RenegotiationInfo};
-dec_hs(?CERTIFICATE, <<?UINT24(ACLen), ASN1Certs:ACLen/binary>>) ->
+ renegotiation_info = RenegotiationInfo,
+ hash_signs = HashSigns};
+dec_hs(_Version, ?CERTIFICATE, <<?UINT24(ACLen), ASN1Certs:ACLen/binary>>) ->
#certificate{asn1_certificates = certs_to_list(ASN1Certs)};
-dec_hs(?SERVER_KEY_EXCHANGE, <<?UINT16(PLen), P:PLen/binary,
+dec_hs(_Version, ?SERVER_KEY_EXCHANGE, <<?UINT16(PLen), P:PLen/binary,
?UINT16(GLen), G:GLen/binary,
?UINT16(YLen), Y:YLen/binary,
?UINT16(0)>>) -> %% May happen if key_algorithm is dh_anon
#server_key_exchange{params = #server_dh_params{dh_p = P,dh_g = G,
dh_y = Y},
- signed_params = <<>>};
-dec_hs(?SERVER_KEY_EXCHANGE, <<?UINT16(PLen), P:PLen/binary,
+ signed_params = <<>>, hashsign = {null, anon}};
+dec_hs({Major, Minor}, ?SERVER_KEY_EXCHANGE, <<?UINT16(PLen), P:PLen/binary,
+ ?UINT16(GLen), G:GLen/binary,
+ ?UINT16(YLen), Y:YLen/binary,
+ ?BYTE(HashAlgo), ?BYTE(SignAlgo),
+ ?UINT16(Len), Sig:Len/binary>>)
+ when Major == 3, Minor >= 3 ->
+ #server_key_exchange{params = #server_dh_params{dh_p = P,dh_g = G,
+ dh_y = Y},
+ signed_params = Sig,
+ hashsign = {ssl_cipher:hash_algorithm(HashAlgo), ssl_cipher:sign_algorithm(SignAlgo)}};
+dec_hs(_Version, ?SERVER_KEY_EXCHANGE, <<?UINT16(PLen), P:PLen/binary,
?UINT16(GLen), G:GLen/binary,
?UINT16(YLen), Y:YLen/binary,
?UINT16(Len), Sig:Len/binary>>) ->
#server_key_exchange{params = #server_dh_params{dh_p = P,dh_g = G,
dh_y = Y},
- signed_params = Sig};
-dec_hs(?CERTIFICATE_REQUEST,
+ signed_params = Sig, hashsign = undefined};
+dec_hs({Major, Minor}, ?CERTIFICATE_REQUEST,
+ <<?BYTE(CertTypesLen), CertTypes:CertTypesLen/binary,
+ ?UINT16(HashSignsLen), HashSigns:HashSignsLen/binary,
+ ?UINT16(CertAuthsLen), CertAuths:CertAuthsLen/binary>>)
+ when Major == 3, Minor >= 3 ->
+ HashSignAlgos = [{ssl_cipher:hash_algorithm(Hash), ssl_cipher:sign_algorithm(Sign)} ||
+ <<?BYTE(Hash), ?BYTE(Sign)>> <= HashSigns],
+ #certificate_request{certificate_types = CertTypes,
+ hashsign_algorithms = #hash_sign_algos{hash_sign_algos = HashSignAlgos},
+ certificate_authorities = CertAuths};
+dec_hs(_Version, ?CERTIFICATE_REQUEST,
<<?BYTE(CertTypesLen), CertTypes:CertTypesLen/binary,
?UINT16(CertAuthsLen), CertAuths:CertAuthsLen/binary>>) ->
#certificate_request{certificate_types = CertTypes,
certificate_authorities = CertAuths};
-dec_hs(?SERVER_HELLO_DONE, <<>>) ->
+dec_hs(_Version, ?SERVER_HELLO_DONE, <<>>) ->
#server_hello_done{};
-dec_hs(?CERTIFICATE_VERIFY,<<?UINT16(_), Signature/binary>>)->
+dec_hs({Major, Minor}, ?CERTIFICATE_VERIFY,<<HashSign:2/binary, ?UINT16(SignLen), Signature:SignLen/binary>>)
+ when Major == 3, Minor >= 3 ->
+ #certificate_verify{hashsign_algorithm = hashsign_dec(HashSign), signature = Signature};
+dec_hs(_Version, ?CERTIFICATE_VERIFY,<<?UINT16(SignLen), Signature:SignLen/binary>>)->
#certificate_verify{signature = Signature};
-dec_hs(?CLIENT_KEY_EXCHANGE, PKEPMS) ->
+dec_hs(_Version, ?CLIENT_KEY_EXCHANGE, PKEPMS) ->
#client_key_exchange{exchange_keys = PKEPMS};
-dec_hs(?FINISHED, VerifyData) ->
+dec_hs(_Version, ?FINISHED, VerifyData) ->
#finished{verify_data = VerifyData};
-dec_hs(_, _) ->
+dec_hs(_, _, _) ->
throw(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE)).
dec_client_key(PKEPMS, ?KEY_EXCHANGE_RSA, {3, 0}) ->
@@ -930,6 +971,15 @@ dec_hello_extensions(<<?UINT16(?RENEGOTIATION_EXT), ?UINT16(Len), Info:Len/binar
dec_hello_extensions(Rest, [{renegotiation_info,
#renegotiation_info{renegotiated_connection = RenegotiateInfo}} | Acc]);
+dec_hello_extensions(<<?UINT16(?SIGNATURE_ALGORITHMS_EXT), ?UINT16(Len),
+ ExtData:Len/binary, Rest/binary>>, Acc) ->
+ SignAlgoListLen = Len - 2,
+ <<?UINT16(SignAlgoListLen), SignAlgoList/binary>> = ExtData,
+ HashSignAlgos = [{ssl_cipher:hash_algorithm(Hash), ssl_cipher:sign_algorithm(Sign)} ||
+ <<?BYTE(Hash), ?BYTE(Sign)>> <= SignAlgoList],
+ dec_hello_extensions(Rest, [{hash_signs,
+ #hash_sign_algos{hash_sign_algos = HashSignAlgos}} | Acc]);
+
%% Ignore data following the ClientHello (i.e.,
%% extensions) if not understood.
dec_hello_extensions(<<?UINT16(_), ?UINT16(Len), _Unknown:Len/binary, Rest/binary>>, Acc) ->
@@ -971,14 +1021,19 @@ enc_hs(#client_hello{client_version = {Major, Minor},
session_id = SessionID,
cipher_suites = CipherSuites,
compression_methods = CompMethods,
- renegotiation_info = RenegotiationInfo}, _Version) ->
+ renegotiation_info = RenegotiationInfo,
+ hash_signs = HashSigns}, _Version) ->
SIDLength = byte_size(SessionID),
BinCompMethods = list_to_binary(CompMethods),
CmLength = byte_size(BinCompMethods),
BinCipherSuites = list_to_binary(CipherSuites),
CsLength = byte_size(BinCipherSuites),
- Extensions = hello_extensions(RenegotiationInfo),
- ExtensionsBin = enc_hello_extensions(Extensions),
+ Extensions0 = hello_extensions(RenegotiationInfo),
+ Extensions1 = if
+ Major == 3, Minor >=3 -> Extensions0 ++ hello_extensions(HashSigns);
+ true -> Extensions0
+ end,
+ ExtensionsBin = enc_hello_extensions(Extensions1),
{?CLIENT_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary,
?BYTE(SIDLength), SessionID/binary,
?UINT16(CsLength), BinCipherSuites/binary,
@@ -1002,15 +1057,30 @@ enc_hs(#certificate{asn1_certificates = ASN1CertList}, _Version) ->
{?CERTIFICATE, <<?UINT24(ACLen), ASN1Certs:ACLen/binary>>};
enc_hs(#server_key_exchange{params = #server_dh_params{
dh_p = P, dh_g = G, dh_y = Y},
- signed_params = SignedParams}, _Version) ->
+ signed_params = SignedParams, hashsign = HashSign}, Version) ->
PLen = byte_size(P),
GLen = byte_size(G),
YLen = byte_size(Y),
- SignedLen = byte_size(SignedParams),
+ Signature = enc_sign(HashSign, SignedParams, Version),
{?SERVER_KEY_EXCHANGE, <<?UINT16(PLen), P/binary,
?UINT16(GLen), G/binary,
?UINT16(YLen), Y/binary,
- ?UINT16(SignedLen), SignedParams/binary>>
+ Signature/binary>>
+ };
+enc_hs(#certificate_request{certificate_types = CertTypes,
+ hashsign_algorithms = #hash_sign_algos{hash_sign_algos = HashSignAlgos},
+ certificate_authorities = CertAuths},
+ {Major, Minor})
+ when Major == 3, Minor >= 3 ->
+ HashSigns= << <<(ssl_cipher:hash_algorithm(Hash)):8, (ssl_cipher:sign_algorithm(Sign)):8>> ||
+ {Hash, Sign} <- HashSignAlgos >>,
+ CertTypesLen = byte_size(CertTypes),
+ HashSignsLen = byte_size(HashSigns),
+ CertAuthsLen = byte_size(CertAuths),
+ {?CERTIFICATE_REQUEST,
+ <<?BYTE(CertTypesLen), CertTypes/binary,
+ ?UINT16(HashSignsLen), HashSigns/binary,
+ ?UINT16(CertAuthsLen), CertAuths/binary>>
};
enc_hs(#certificate_request{certificate_types = CertTypes,
certificate_authorities = CertAuths},
@@ -1025,8 +1095,8 @@ enc_hs(#server_hello_done{}, _Version) ->
{?SERVER_HELLO_DONE, <<>>};
enc_hs(#client_key_exchange{exchange_keys = ExchangeKeys}, Version) ->
{?CLIENT_KEY_EXCHANGE, enc_cke(ExchangeKeys, Version)};
-enc_hs(#certificate_verify{signature = BinSig}, _) ->
- EncSig = enc_bin_sig(BinSig),
+enc_hs(#certificate_verify{signature = BinSig, hashsign_algorithm = HashSign}, Version) ->
+ EncSig = enc_sign(HashSign, BinSig, Version),
{?CERTIFICATE_VERIFY, EncSig};
enc_hs(#finished{verify_data = VerifyData}, _Version) ->
{?FINISHED, VerifyData}.
@@ -1040,14 +1110,23 @@ enc_cke(#client_diffie_hellman_public{dh_public = DHPublic}, _) ->
Len = byte_size(DHPublic),
<<?UINT16(Len), DHPublic/binary>>.
-enc_bin_sig(BinSig) ->
- Size = byte_size(BinSig),
- <<?UINT16(Size), BinSig/binary>>.
+enc_sign({HashAlg, SignAlg}, Signature, _Version = {Major, Minor})
+ when Major == 3, Minor >= 3->
+ SignLen = byte_size(Signature),
+ HashSign = hashsign_enc(HashAlg, SignAlg),
+ <<HashSign/binary, ?UINT16(SignLen), Signature/binary>>;
+enc_sign(_HashSign, Sign, _Version) ->
+ SignLen = byte_size(Sign),
+ <<?UINT16(SignLen), Sign/binary>>.
-%% Renegotiation info, only current extension
+hello_extensions(undefined) ->
+ [];
+%% Renegotiation info
hello_extensions(#renegotiation_info{renegotiated_connection = undefined}) ->
[];
hello_extensions(#renegotiation_info{} = Info) ->
+ [Info];
+hello_extensions(#hash_sign_algos{} = Info) ->
[Info].
enc_hello_extensions(Extensions) ->
@@ -1065,7 +1144,14 @@ enc_hello_extensions([#renegotiation_info{renegotiated_connection = ?byte(0) = I
enc_hello_extensions([#renegotiation_info{renegotiated_connection = Info} | Rest], Acc) ->
InfoLen = byte_size(Info),
Len = InfoLen +1,
- enc_hello_extensions(Rest, <<?UINT16(?RENEGOTIATION_EXT), ?UINT16(Len), ?BYTE(InfoLen), Info/binary, Acc/binary>>).
+ enc_hello_extensions(Rest, <<?UINT16(?RENEGOTIATION_EXT), ?UINT16(Len), ?BYTE(InfoLen), Info/binary, Acc/binary>>);
+
+enc_hello_extensions([#hash_sign_algos{hash_sign_algos = HashSignAlgos} | Rest], Acc) ->
+ SignAlgoList = << <<(ssl_cipher:hash_algorithm(Hash)):8, (ssl_cipher:sign_algorithm(Sign)):8>> ||
+ {Hash, Sign} <- HashSignAlgos >>,
+ ListLen = byte_size(SignAlgoList),
+ Len = ListLen + 2,
+ enc_hello_extensions(Rest, <<?UINT16(?SIGNATURE_ALGORITHMS_EXT), ?UINT16(Len), ?UINT16(ListLen), SignAlgoList/binary, Acc/binary>>).
from_3bytes(Bin3) ->
@@ -1093,6 +1179,14 @@ certificate_types({KeyExchange, _, _, _})
certificate_types(_) ->
<<?BYTE(?RSA_SIGN)>>.
+hashsign_dec(<<?BYTE(HashAlgo), ?BYTE(SignAlgo)>>) ->
+ {ssl_cipher:hash_algorithm(HashAlgo), ssl_cipher:sign_algorithm(SignAlgo)}.
+
+hashsign_enc(HashAlgo, SignAlgo) ->
+ Hash = ssl_cipher:hash_algorithm(HashAlgo),
+ Sign = ssl_cipher:sign_algorithm(SignAlgo),
+ <<?BYTE(Hash), ?BYTE(Sign)>>.
+
certificate_authorities(CertDbHandle, CertDbRef) ->
Authorities = certificate_authorities_from_db(CertDbHandle, CertDbRef),
Enc = fun(#'OTPCertificate'{tbsCertificate=TBSCert}) ->
@@ -1111,43 +1205,43 @@ certificate_authorities_from_db(CertDbHandle, CertDbRef) ->
[Cert | Acc];
(_, Acc) ->
Acc
- end,
+ end,
ssl_certificate_db:foldl(ConnectionCerts, [], CertDbHandle).
-digitally_signed(Hash, #'RSAPrivateKey'{} = Key) ->
+
+digitally_signed({3, Minor}, Hash, HashAlgo, Key) when Minor >= 3 ->
+ public_key:sign({digest, Hash}, HashAlgo, Key);
+digitally_signed(_Version, Hash, _HashAlgo, #'DSAPrivateKey'{} = Key) ->
+ public_key:sign({digest, Hash}, sha, Key);
+digitally_signed(_Version, Hash, _HashAlgo, #'RSAPrivateKey'{} = Key) ->
public_key:encrypt_private(Hash, Key,
- [{rsa_pad, rsa_pkcs1_padding}]);
-digitally_signed(Hash, #'DSAPrivateKey'{} = Key) ->
- public_key:sign(Hash, none, Key).
-
-calc_master_secret({3,0}, PremasterSecret, ClientRandom, ServerRandom) ->
+ [{rsa_pad, rsa_pkcs1_padding}]).
+
+calc_master_secret({3,0}, _PrfAlgo, PremasterSecret, ClientRandom, ServerRandom) ->
ssl_ssl3:master_secret(PremasterSecret, ClientRandom, ServerRandom);
-calc_master_secret({3,N},PremasterSecret, ClientRandom, ServerRandom)
- when N == 1; N == 2 ->
- ssl_tls1:master_secret(PremasterSecret, ClientRandom, ServerRandom).
+calc_master_secret({3,_}, PrfAlgo, PremasterSecret, ClientRandom, ServerRandom) ->
+ ssl_tls1:master_secret(PrfAlgo, PremasterSecret, ClientRandom, ServerRandom).
-setup_keys({3,0}, MasterSecret,
+setup_keys({3,0}, _PrfAlgo, MasterSecret,
ServerRandom, ClientRandom, HashSize, KML, EKML, IVS) ->
- ssl_ssl3:setup_keys(MasterSecret, ServerRandom,
+ ssl_ssl3:setup_keys(MasterSecret, ServerRandom,
ClientRandom, HashSize, KML, EKML, IVS);
-setup_keys({3,1}, MasterSecret,
+setup_keys({3,N}, PrfAlgo, MasterSecret,
ServerRandom, ClientRandom, HashSize, KML, _EKML, IVS) ->
- ssl_tls1:setup_keys(MasterSecret, ServerRandom, ClientRandom, HashSize,
+ ssl_tls1:setup_keys(N, PrfAlgo, MasterSecret, ServerRandom, ClientRandom, HashSize,
KML, IVS).
-calc_finished({3, 0}, Role, MasterSecret, Hashes) ->
- ssl_ssl3:finished(Role, MasterSecret, Hashes);
-calc_finished({3, N}, Role, MasterSecret, Hashes)
- when N == 1; N == 2 ->
- ssl_tls1:finished(Role, MasterSecret, Hashes).
+calc_finished({3, 0}, Role, _PrfAlgo, MasterSecret, Handshake) ->
+ ssl_ssl3:finished(Role, MasterSecret, lists:reverse(Handshake));
+calc_finished({3, N}, Role, PrfAlgo, MasterSecret, Handshake) ->
+ ssl_tls1:finished(Role, N, PrfAlgo, MasterSecret, lists:reverse(Handshake)).
-calc_certificate_verify({3, 0}, MasterSecret, Algorithm, Hashes) ->
- ssl_ssl3:certificate_verify(Algorithm, MasterSecret, Hashes);
-calc_certificate_verify({3, N}, _, Algorithm, Hashes)
- when N == 1; N == 2 ->
- ssl_tls1:certificate_verify(Algorithm, Hashes).
+calc_certificate_verify({3, 0}, HashAlgo, MasterSecret, Handshake) ->
+ ssl_ssl3:certificate_verify(HashAlgo, MasterSecret, lists:reverse(Handshake));
+calc_certificate_verify({3, N}, HashAlgo, _MasterSecret, Handshake) ->
+ ssl_tls1:certificate_verify(HashAlgo, N, lists:reverse(Handshake)).
key_exchange_alg(rsa) ->
?KEY_EXCHANGE_RSA;
@@ -1167,7 +1261,29 @@ apply_user_fun(Fun, OtpCert, ExtensionOrError, UserState0, SslState) ->
{unknown, {SslState, UserState}}
end.
-alg_oid(#'RSAPrivateKey'{}) ->
- ?'rsaEncryption';
-alg_oid(#'DSAPrivateKey'{}) ->
- ?'id-dsa'.
+certificate_verify_rsa(Hashes, sha, Signature, PublicKey, {Major, Minor})
+ when Major == 3, Minor >= 3 ->
+ public_key:verify({digest, Hashes}, sha, Signature, PublicKey);
+certificate_verify_rsa(Hashes, HashAlgo, Signature, PublicKey, {Major, Minor})
+ when Major == 3, Minor >= 3 ->
+ public_key:verify({digest, Hashes}, HashAlgo, Signature, PublicKey);
+certificate_verify_rsa(Hashes, _HashAlgo, Signature, PublicKey, _Version) ->
+ case public_key:decrypt_public(Signature, PublicKey,
+ [{rsa_pad, rsa_pkcs1_padding}]) of
+ Hashes -> true;
+ _ -> false
+ end.
+
+-define(TLSEXT_SIGALG_RSA(MD), {MD, rsa}).
+-define(TLSEXT_SIGALG_DSA(MD), {MD, dsa}).
+
+-define(TLSEXT_SIGALG(MD), ?TLSEXT_SIGALG_RSA(MD)).
+
+default_hash_signs() ->
+ #hash_sign_algos{hash_sign_algos =
+ [?TLSEXT_SIGALG(sha512),
+ ?TLSEXT_SIGALG(sha384),
+ ?TLSEXT_SIGALG(sha256),
+ ?TLSEXT_SIGALG(sha),
+ ?TLSEXT_SIGALG_DSA(sha),
+ ?TLSEXT_SIGALG_RSA(md5)]}.
diff --git a/lib/ssl/src/ssl_handshake.hrl b/lib/ssl/src/ssl_handshake.hrl
index fb0ebac7d1..cc17dc2975 100644
--- a/lib/ssl/src/ssl_handshake.hrl
+++ b/lib/ssl/src/ssl_handshake.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2012. 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,6 +31,13 @@
-type algo_oid() :: ?'rsaEncryption' | ?'id-dsa'.
-type public_key_params() :: #'Dss-Parms'{} | term().
-type public_key_info() :: {algo_oid(), #'RSAPublicKey'{} | integer() , public_key_params()}.
+-type tls_handshake_history() :: {[binary()], [binary()]}.
+
+%% Signature algorithms
+-define(ANON, 0).
+-define(RSA, 1).
+-define(DSA, 2).
+-define(ECDSA, 3).
-record(session, {
session_id,
@@ -89,7 +96,8 @@
session_id, % opaque SessionID<0..32>
cipher_suites, % cipher_suites<2..2^16-1>
compression_methods, % compression_methods<1..2^8-1>,
- renegotiation_info
+ renegotiation_info,
+ hash_signs % supported combinations of hashes/signature algos
}).
-record(server_hello, {
@@ -98,7 +106,8 @@
session_id, % opaque SessionID<0..32>
cipher_suite, % cipher_suites
compression_method, % compression_method
- renegotiation_info
+ renegotiation_info,
+ hash_signs % supported combinations of hashes/signature algos
}).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -129,7 +138,8 @@
-record(server_key_exchange, {
params, %% #server_rsa_params{} | #server_dh_params{}
- signed_params %% #signature{}
+ signed_params, %% #signature{}
+ hashsign %% term(atom(), atom())
}).
%% enum { anonymous, rsa, dsa } SignatureAlgorithm;
@@ -159,6 +169,7 @@
-record(certificate_request, {
certificate_types, %ClientCertificateType <1..2^8-1>
+ hashsign_algorithms, %%SignatureAndHashAlgorithm <2^16-1>;
certificate_authorities %DistinguishedName <0..2^16-1>
}).
@@ -193,6 +204,7 @@
%%% Certificate verify - RFC 4346 section 7.4.8
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-record(certificate_verify, {
+ hashsign_algorithm,
signature % binary()
}).
@@ -213,6 +225,15 @@
renegotiated_connection
}).
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Signature Algorithms RFC 5746 section 7.4.1.4.1.
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+-define(SIGNATURE_ALGORITHMS_EXT, 13).
+
+-record(hash_sign_algos, {
+ hash_sign_algos
+ }).
+
-endif. % -ifdef(ssl_handshake).
diff --git a/lib/ssl/src/ssl_internal.hrl b/lib/ssl/src/ssl_internal.hrl
index 18cfcdcd68..b8f2ae3b51 100644
--- a/lib/ssl/src/ssl_internal.hrl
+++ b/lib/ssl/src/ssl_internal.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2012. 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
@@ -34,7 +34,7 @@
-type host() :: inet:ip_address() | inet:hostname().
-type session_id() :: 0 | binary().
-type tls_version() :: {integer(), integer()}.
--type tls_atom_version() :: sslv3 | tlsv1.
+-type tls_atom_version() :: sslv3 | tlsv1 | 'tlsv1.1' | 'tlsv1.2'.
-type certdb_ref() :: reference().
-type db_handle() :: term().
-type key_algo() :: null | rsa | dhe_rsa | dhe_dss | dh_anon.
@@ -69,11 +69,11 @@
-define(TRUE, 0).
-define(FALSE, 1).
--define(DEFAULT_SUPPORTED_VERSIONS, [tlsv1, sslv3]). % TODO: This is temporary
-%-define(DEFAULT_SUPPORTED_VERSIONS, ['tlsv1.1', tlsv1, sslv3]).
+-define(DEFAULT_SUPPORTED_VERSIONS, [tlsv1, sslv3]). %% Add 'tlsv1.1' in R16
+-define(ALL_SUPPORTED_VERSIONS, ['tlsv1.2', 'tlsv1.1', tlsv1, sslv3]).
-record(ssl_options, {
- versions, % 'tlsv1.1' | tlsv1 | sslv3
+ versions, % 'tlsv1.2' | 'tlsv1.1' | tlsv1 | sslv3
verify, % verify_none | verify_peer
verify_fun, % fun(CertVerifyErrors) -> boolean()
fail_if_no_peer_cert, % boolean()
diff --git a/lib/ssl/src/ssl_manager.erl b/lib/ssl/src/ssl_manager.erl
index 6389ff03f5..af2bfa394d 100644
--- a/lib/ssl/src/ssl_manager.erl
+++ b/lib/ssl/src/ssl_manager.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2012. 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
@@ -30,9 +30,9 @@
-export([start_link/1, start_link_dist/1,
connection_init/2, cache_pem_file/2,
lookup_trusted_cert/4,
- client_session_id/4, server_session_id/4,
+ new_session_id/1, clean_cert_db/2,
register_session/2, register_session/3, invalidate_session/2,
- invalidate_session/3]).
+ invalidate_session/3, clear_pem_cache/0]).
% Spawn export
-export([init_session_validator/1]).
@@ -56,9 +56,12 @@
-define('24H_in_msec', 8640000).
-define('24H_in_sec', 8640).
+-define(GEN_UNIQUE_ID_MAX_TRIES, 10).
-define(SESSION_VALIDATION_INTERVAL, 60000).
--define(CERTIFICATE_CACHE_CLEANUP, 30000).
+-define(CLEAR_PEM_CACHE, 120000).
-define(CLEAN_SESSION_DB, 60000).
+-define(CLEAN_CERT_DB, 500).
+-define(NOT_TO_BIG, 10).
%%====================================================================
%% API
@@ -82,26 +85,46 @@ start_link_dist(Opts) ->
gen_server:start_link({local, ssl_manager_dist}, ?MODULE, [ssl_manager_dist, Opts], []).
%%--------------------------------------------------------------------
--spec connection_init(string()| {der, list()}, client | server) ->
- {ok, certdb_ref(), db_handle(), db_handle()}.
+-spec connection_init(binary()| {der, list()}, client | server) ->
+ {ok, certdb_ref(), db_handle(), db_handle(), db_handle(), db_handle()}.
%%
%% Description: Do necessary initializations for a new connection.
%%--------------------------------------------------------------------
+connection_init({der, _} = Trustedcerts, Role) ->
+ call({connection_init, Trustedcerts, Role});
+
+connection_init(<<>> = Trustedcerts, Role) ->
+ call({connection_init, Trustedcerts, Role});
+
connection_init(Trustedcerts, Role) ->
call({connection_init, Trustedcerts, Role}).
+
%%--------------------------------------------------------------------
--spec cache_pem_file(string(), term()) -> {ok, term()} | {error, reason()}.
+-spec cache_pem_file(binary(), term()) -> {ok, term()} | {error, reason()}.
%%
%% Description: Cach a pem file and return its content.
%%--------------------------------------------------------------------
cache_pem_file(File, DbHandle) ->
- try file:read_file_info(File) of
- {ok, #file_info{mtime = LastWrite}} ->
- cache_pem_file(File, LastWrite, DbHandle)
- catch
- _:Reason ->
- {error, Reason}
+ MD5 = crypto:md5(File),
+ case ssl_certificate_db:lookup_cached_pem(DbHandle, MD5) of
+ [{Content,_}] ->
+ {ok, Content};
+ [Content] ->
+ {ok, Content};
+ undefined ->
+ call({cache_pem, {MD5, File}})
end.
+
+%%--------------------------------------------------------------------
+-spec clear_pem_cache() -> ok.
+%%
+%% Description: Clear the PEM cache
+%%--------------------------------------------------------------------
+clear_pem_cache() ->
+ %% Not supported for distribution at the moement, should it be?
+ put(ssl_manager, ssl_manager),
+ call(unconditionally_clear_pem_cache).
+
%%--------------------------------------------------------------------
-spec lookup_trusted_cert(term(), reference(), serialnumber(), issuer()) ->
undefined |
@@ -114,22 +137,15 @@ lookup_trusted_cert(DbHandle, Ref, SerialNumber, Issuer) ->
ssl_certificate_db:lookup_trusted_cert(DbHandle, Ref, SerialNumber, Issuer).
%%--------------------------------------------------------------------
--spec client_session_id(host(), inet:port_number(), #ssl_options{},
- der_cert() | undefined) -> session_id().
+-spec new_session_id(integer()) -> session_id().
%%
-%% Description: Select a session id for the client.
+%% Description: Creates a session id for the server.
%%--------------------------------------------------------------------
-client_session_id(Host, Port, SslOpts, OwnCert) ->
- call({client_session_id, Host, Port, SslOpts, OwnCert}).
+new_session_id(Port) ->
+ call({new_session_id, Port}).
-%%--------------------------------------------------------------------
--spec server_session_id(host(), inet:port_number(), #ssl_options{},
- der_cert()) -> session_id().
-%%
-%% Description: Select a session id for the server.
-%%--------------------------------------------------------------------
-server_session_id(Port, SuggestedSessionId, SslOpts, OwnCert) ->
- call({server_session_id, Port, SuggestedSessionId, SslOpts, OwnCert}).
+clean_cert_db(Ref, File) ->
+ erlang:send_after(?CLEAN_CERT_DB, self(), {clean_cert_db, Ref, File}).
%%--------------------------------------------------------------------
-spec register_session(inet:port_number(), #session{}) -> ok.
@@ -177,6 +193,7 @@ init([Name, Opts]) ->
SessionCache = CacheCb:init(proplists:get_value(session_cb_init_args, Opts, [])),
Timer = erlang:send_after(SessionLifeTime * 1000,
self(), validate_sessions),
+ erlang:send_after(?CLEAR_PEM_CACHE, self(), clear_pem_cache),
{ok, #state{certificate_db = CertDb,
session_cache = SessionCache,
session_cache_cb = CacheCb,
@@ -194,55 +211,44 @@ init([Name, Opts]) ->
%%
%% Description: Handling call messages
%%--------------------------------------------------------------------
-handle_call({{connection_init, "", _Role}, Pid}, _From,
- #state{certificate_db = [CertDb |_],
+handle_call({{connection_init, <<>>, _Role}, _Pid}, _From,
+ #state{certificate_db = [CertDb, FileRefDb, PemChace],
session_cache = Cache} = State) ->
- erlang:monitor(process, Pid),
- Result = {ok, make_ref(),CertDb, Cache},
+ Result = {ok, make_ref(),CertDb, FileRefDb, PemChace, Cache},
{reply, Result, State};
handle_call({{connection_init, Trustedcerts, _Role}, Pid}, _From,
- #state{certificate_db = [CertDb|_] =Db,
+ #state{certificate_db = [CertDb, FileRefDb, PemChace] = Db,
session_cache = Cache} = State) ->
- erlang:monitor(process, Pid),
Result =
try
{ok, Ref} = ssl_certificate_db:add_trusted_certs(Pid, Trustedcerts, Db),
- {ok, Ref, CertDb, Cache}
+ {ok, Ref, CertDb, FileRefDb, PemChace, Cache}
catch
_:Reason ->
{error, Reason}
end,
{reply, Result, State};
-handle_call({{client_session_id, Host, Port, SslOpts, OwnCert}, _}, _,
- #state{session_cache = Cache,
- session_cache_cb = CacheCb} = State) ->
- Id = ssl_session:id({Host, Port, SslOpts}, Cache, CacheCb, OwnCert),
- {reply, Id, State};
-
-handle_call({{server_session_id, Port, SuggestedSessionId, SslOpts, OwnCert}, _},
+handle_call({{new_session_id,Port}, _},
_, #state{session_cache_cb = CacheCb,
- session_cache = Cache,
- session_lifetime = LifeTime} = State) ->
- Id = ssl_session:id(Port, SuggestedSessionId, SslOpts,
- Cache, CacheCb, LifeTime, OwnCert),
+ session_cache = Cache} = State) ->
+ Id = new_id(Port, ?GEN_UNIQUE_ID_MAX_TRIES, Cache, CacheCb),
{reply, Id, State};
-handle_call({{cache_pem, File, LastWrite}, Pid}, _,
+
+handle_call({{cache_pem, File}, _Pid}, _,
#state{certificate_db = Db} = State) ->
- try ssl_certificate_db:cache_pem_file(Pid, File, LastWrite, Db) of
+ try ssl_certificate_db:cache_pem_file(File, Db) of
Result ->
{reply, Result, State}
catch
_:Reason ->
{reply, {error, Reason}, State}
end;
-handle_call({{recache_pem, File, LastWrite}, Pid}, From,
- #state{certificate_db = Db} = State) ->
- ssl_certificate_db:uncache_pem_file(File, Db),
- cast({recache_pem, File, LastWrite, Pid, From}),
- {noreply, State}.
+handle_call({unconditionally_clear_pem_cache, _},_, #state{certificate_db = [_,_,PemChace]} = State) ->
+ ssl_certificate_db:clear(PemChace),
+ {reply, ok, State}.
%%--------------------------------------------------------------------
-spec handle_cast(msg(), #state{}) -> {noreply, #state{}}.
@@ -278,22 +284,7 @@ handle_cast({invalidate_session, Host, Port,
handle_cast({invalidate_session, Port, #session{session_id = ID} = Session},
#state{session_cache = Cache,
session_cache_cb = CacheCb} = State) ->
- invalidate_session(Cache, CacheCb, {Port, ID}, Session, State);
-
-handle_cast({recache_pem, File, LastWrite, Pid, From},
- #state{certificate_db = [_, FileToRefDb, _]} = State0) ->
- case ssl_certificate_db:lookup(File, FileToRefDb) of
- undefined ->
- {reply, Msg, State} =
- handle_call({{cache_pem, File, LastWrite}, Pid}, From, State0),
- gen_server:reply(From, Msg),
- {noreply, State};
- _ -> %% Send message to self letting cleanup messages be handled
- %% first so that no reference to the old version of file
- %% exists when we cache the new one.
- cast({recache_pem, File, LastWrite, Pid, From}),
- {noreply, State0}
- end.
+ invalidate_session(Cache, CacheCb, {Port, ID}, Session, State).
%%--------------------------------------------------------------------
-spec handle_info(msg(), #state{}) -> {noreply, #state{}}.
@@ -318,23 +309,38 @@ handle_info({delayed_clean_session, Key}, #state{session_cache = Cache,
CacheCb:delete(Cache, Key),
{noreply, State};
-handle_info({'EXIT', _, _}, State) ->
- %% Session validator died!! Do we need to take any action?
- %% maybe error log
+handle_info(clear_pem_cache, #state{certificate_db = [_,_,PemChace]} = State) ->
+ case ssl_certificate_db:db_size(PemChace) of
+ N when N < ?NOT_TO_BIG ->
+ ok;
+ _ ->
+ ssl_certificate_db:clear(PemChace)
+ end,
+ erlang:send_after(?CLEAR_PEM_CACHE, self(), clear_pem_cache),
{noreply, State};
-handle_info({'DOWN', _Ref, _Type, _Pid, ecacertfile}, State) ->
- {noreply, State};
-handle_info({'DOWN', _Ref, _Type, Pid, shutdown}, State) ->
- handle_info({remove_trusted_certs, Pid}, State);
-handle_info({'DOWN', _Ref, _Type, Pid, _Reason}, State) ->
- erlang:send_after(?CERTIFICATE_CACHE_CLEANUP, self(),
- {remove_trusted_certs, Pid}),
+handle_info({clean_cert_db, Ref, File},
+ #state{certificate_db = [CertDb,RefDb, PemCache]} = State) ->
+ case ssl_certificate_db:ref_count(Ref, RefDb, 0) of
+ 0 ->
+ MD5 = crypto:md5(File),
+ case ssl_certificate_db:lookup_cached_pem(PemCache, MD5) of
+ [{Content, Ref}] ->
+ ssl_certificate_db:insert(MD5, Content, PemCache);
+ undefined ->
+ ok
+ end,
+ ssl_certificate_db:remove(Ref, RefDb),
+ ssl_certificate_db:remove_trusted_certs(Ref, CertDb);
+ _ ->
+ ok
+ end,
{noreply, State};
-handle_info({remove_trusted_certs, Pid},
- #state{certificate_db = Db} = State) ->
- ssl_certificate_db:remove_trusted_certs(Pid, Db),
+
+handle_info({'EXIT', _, _}, State) ->
+ %% Session validator died!! Do we need to take any action?
+ %% maybe error log
{noreply, State};
handle_info(_Info, State) ->
@@ -406,19 +412,6 @@ session_validation({{Port, _}, Session}, LifeTime) ->
validate_session(Port, Session, LifeTime),
LifeTime.
-cache_pem_file(File, LastWrite, DbHandle) ->
- case ssl_certificate_db:lookup_cached_certs(DbHandle,File) of
- [{_, {Mtime, Content}}] ->
- case LastWrite of
- Mtime ->
- {ok, Content};
- _ ->
- call({recache_pem, File, LastWrite})
- end;
- [] ->
- call({cache_pem, File, LastWrite})
- end.
-
delay_time() ->
case application:get_env(ssl, session_delay_cleanup_time) of
{ok, Time} when is_integer(Time) ->
@@ -448,3 +441,28 @@ last_delay_timer({{_,_},_}, TRef, {LastServer, _}) ->
{LastServer, TRef};
last_delay_timer({_,_}, TRef, {_, LastClient}) ->
{TRef, LastClient}.
+
+%% If we can not generate a not allready in use session ID in
+%% ?GEN_UNIQUE_ID_MAX_TRIES we make the new session uncacheable The
+%% value of ?GEN_UNIQUE_ID_MAX_TRIES is stolen from open SSL which
+%% states : "If we can not find a session id in
+%% ?GEN_UNIQUE_ID_MAX_TRIES either the RAND code is broken or someone
+%% is trying to open roughly very close to 2^128 (or 2^256) SSL
+%% sessions to our server"
+new_id(_, 0, _, _) ->
+ <<>>;
+new_id(Port, Tries, Cache, CacheCb) ->
+ Id = crypto:rand_bytes(?NUM_OF_SESSION_ID_BYTES),
+ case CacheCb:lookup(Cache, {Port, Id}) of
+ undefined ->
+ Now = calendar:datetime_to_gregorian_seconds({date(), time()}),
+ %% New sessions can not be set to resumable
+ %% until handshake is compleate and the
+ %% other session values are set.
+ CacheCb:update(Cache, {Port, Id}, #session{session_id = Id,
+ is_resumable = false,
+ time_stamp = Now}),
+ Id;
+ _ ->
+ new_id(Port, Tries - 1, Cache, CacheCb)
+ end.
diff --git a/lib/ssl/src/ssl_record.erl b/lib/ssl/src/ssl_record.erl
index 830026c825..8e93ce4634 100644
--- a/lib/ssl/src/ssl_record.erl
+++ b/lib/ssl/src/ssl_record.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2012. 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
@@ -383,6 +383,8 @@ get_tls_records_aux(Data, Acc) ->
%% Description: Creates a protocol version record from a version atom
%% or vice versa.
%%--------------------------------------------------------------------
+protocol_version('tlsv1.2') ->
+ {3, 3};
protocol_version('tlsv1.1') ->
{3, 2};
protocol_version(tlsv1) ->
@@ -391,6 +393,8 @@ protocol_version(sslv3) ->
{3, 0};
protocol_version(sslv2) -> %% Backwards compatibility
{2, 0};
+protocol_version({3, 3}) ->
+ 'tlsv1.2';
protocol_version({3, 2}) ->
'tlsv1.1';
protocol_version({3, 1}) ->
@@ -445,9 +449,9 @@ supported_protocol_versions() ->
end,
case application:get_env(ssl, protocol_version) of
undefined ->
- lists:map(Fun, ?DEFAULT_SUPPORTED_VERSIONS);
+ lists:map(Fun, supported_protocol_versions([]));
{ok, []} ->
- lists:map(Fun, ?DEFAULT_SUPPORTED_VERSIONS);
+ lists:map(Fun, supported_protocol_versions([]));
{ok, Vsns} when is_list(Vsns) ->
Versions = lists:filter(fun is_acceptable_version/1, lists:map(Fun, Vsns)),
supported_protocol_versions(Versions);
@@ -457,7 +461,16 @@ supported_protocol_versions() ->
end.
supported_protocol_versions([]) ->
- ?DEFAULT_SUPPORTED_VERSIONS;
+ Vsns = case sufficient_tlsv1_2_crypto_support() of
+ true ->
+ %%?ALL_SUPPORTED_VERSIONS; %% Add TlS-1.2 as default in R16
+ ?DEFAULT_SUPPORTED_VERSIONS;
+ false ->
+ ?DEFAULT_SUPPORTED_VERSIONS
+ end,
+ application:set_env(ssl, protocol_version, Vsns),
+ Vsns;
+
supported_protocol_versions([_|_] = Vsns) ->
Vsns.
@@ -561,14 +574,14 @@ highest_protocol_version() ->
initial_connection_state(ConnectionEnd) ->
#connection_state{security_parameters =
- initial_security_params(ConnectionEnd),
+ initial_security_params(ConnectionEnd),
sequence_number = 0
}.
initial_security_params(ConnectionEnd) ->
SecParams = #security_parameters{connection_end = ConnectionEnd,
compression_algorithm = ?NULL},
- ssl_cipher:security_parameters(?TLS_NULL_WITH_NULL_NULL,
+ ssl_cipher:security_parameters(highest_protocol_version(), ?TLS_NULL_WITH_NULL_NULL,
SecParams).
empty_connection_state(ConnectionEnd) ->
@@ -633,7 +646,7 @@ cipher(Type, Version, Fragment, CS0) ->
BCA}
}} =
hash_and_bump_seqno(CS0, Type, Version, Length, Fragment),
- {Ciphered, CipherS1} = ssl_cipher:cipher(BCA, CipherS0, MacHash, Fragment),
+ {Ciphered, CipherS1} = ssl_cipher:cipher(BCA, CipherS0, MacHash, Fragment, Version),
CS2 = CS1#connection_state{cipher_state=CipherS1},
{Ciphered, CS2}.
@@ -687,6 +700,17 @@ mac_hash({_,_}, ?NULL, _MacSecret, _SeqNo, _Type,
mac_hash({3, 0}, MacAlg, MacSecret, SeqNo, Type, Length, Fragment) ->
ssl_ssl3:mac_hash(MacAlg, MacSecret, SeqNo, Type, Length, Fragment);
mac_hash({3, N} = Version, MacAlg, MacSecret, SeqNo, Type, Length, Fragment)
- when N =:= 1; N =:= 2 ->
+ when N =:= 1; N =:= 2; N =:= 3 ->
ssl_tls1:mac_hash(MacAlg, MacSecret, SeqNo, Type, Version,
Length, Fragment).
+
+sufficient_tlsv1_2_crypto_support() ->
+ Data = "Sampl",
+ Data2 = "e #1",
+ Key = <<0,1,2,3,16,17,18,19,32,33,34,35,48,49,50,51,4,5,6,7,20,21,22,23,36,37,38,39,
+ 52,53,54,55,8,9,10,11,24,25,26,27,40,41,42,43,56,57,58,59>>,
+ try
+ crypto:sha256_mac(Key, lists:flatten([Data, Data2])),
+ true
+ catch _:_ -> false
+ end.
diff --git a/lib/ssl/src/ssl_record.hrl b/lib/ssl/src/ssl_record.hrl
index 282d642138..f73da92a52 100644
--- a/lib/ssl/src/ssl_record.hrl
+++ b/lib/ssl/src/ssl_record.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2012. 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
@@ -47,6 +47,7 @@
key_material_length, % unit 8
expanded_key_material_length, % unit 8
mac_algorithm, % unit 8
+ prf_algorithm, % unit 8
hash_size, % unit 8
compression_algorithm, % unit 8
master_secret, % opaque 48
@@ -97,10 +98,15 @@
%-define(TRUE, 0). %% Already defined by ssl_internal.hrl
%-define(FALSE, 1). %% Already defined by ssl_internal.hrl
-%% MACAlgorithm
+%% MAC and PRF Algorithms
%-define(NULL, 0). %% Already defined by ssl_internal.hrl
-define(MD5, 1).
-define(SHA, 2).
+-define(MD5SHA, 4711). %% Not defined in protocol used to represent old prf
+-define(SHA224, 3).
+-define(SHA256, 4).
+-define(SHA384, 5).
+-define(SHA512, 6).
%% CompressionMethod
% -define(NULL, 0). %% Already defined by ssl_internal.hrl
@@ -176,7 +182,8 @@
content, % opaque content[TLSCompressed.length];
mac, % opaque MAC[CipherSpec.hash_size];
padding, % unit 8 padding[GenericBlockCipher.padding_length];
- padding_length % uint8 padding_length;
+ padding_length, % uint8 padding_length;
+ next_iv % opaque IV[SecurityParameters.record_iv_length];
}).
-endif. % -ifdef(ssl_record).
diff --git a/lib/ssl/src/ssl_session.erl b/lib/ssl/src/ssl_session.erl
index df5d7e0146..2ad422fc03 100644
--- a/lib/ssl/src/ssl_session.erl
+++ b/lib/ssl/src/ssl_session.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2012. 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
@@ -28,9 +28,9 @@
-include("ssl_internal.hrl").
%% Internal application API
--export([is_new/2, id/4, id/7, valid_session/2]).
+-export([is_new/2, client_id/4, server_id/6, valid_session/2]).
--define(GEN_UNIQUE_ID_MAX_TRIES, 10).
+-define('24H_in_sec', 8640).
-type seconds() :: integer().
@@ -48,13 +48,13 @@ is_new(_ClientSuggestion, _ServerDecision) ->
true.
%%--------------------------------------------------------------------
--spec id({host(), inet:port_number(), #ssl_options{}}, db_handle(), atom(),
+-spec client_id({host(), inet:port_number(), #ssl_options{}}, db_handle(), atom(),
undefined | binary()) -> binary().
%%
-%% Description: Should be called by the client side to get an id
+%% Description: Should be called by the client side to get an id
%% for the client hello message.
%%--------------------------------------------------------------------
-id(ClientInfo, Cache, CacheCb, OwnCert) ->
+client_id(ClientInfo, Cache, CacheCb, OwnCert) ->
case select_session(ClientInfo, Cache, CacheCb, OwnCert) of
no_session ->
<<>>;
@@ -62,27 +62,6 @@ id(ClientInfo, Cache, CacheCb, OwnCert) ->
SessionId
end.
-%%--------------------------------------------------------------------
--spec id(inet:port_number(), binary(), #ssl_options{}, db_handle(),
- atom(), seconds(), binary()) -> binary().
-%%
-%% Description: Should be called by the server side to get an id
-%% for the server hello message.
-%%--------------------------------------------------------------------
-id(Port, <<>>, _, Cache, CacheCb, _, _) ->
- new_id(Port, ?GEN_UNIQUE_ID_MAX_TRIES, Cache, CacheCb);
-
-id(Port, SuggestedSessionId, #ssl_options{reuse_sessions = ReuseEnabled,
- reuse_session = ReuseFun},
- Cache, CacheCb, SecondLifeTime, OwnCert) ->
- case is_resumable(SuggestedSessionId, Port, ReuseEnabled,
- ReuseFun, Cache, CacheCb, SecondLifeTime, OwnCert) of
- true ->
- SuggestedSessionId;
- false ->
- new_id(Port, ?GEN_UNIQUE_ID_MAX_TRIES, Cache, CacheCb)
- end.
-%%--------------------------------------------------------------------
-spec valid_session(#session{}, seconds()) -> boolean().
%%
%% Description: Check that the session has not expired
@@ -91,57 +70,51 @@ valid_session(#session{time_stamp = TimeStamp}, LifeTime) ->
Now = calendar:datetime_to_gregorian_seconds({date(), time()}),
Now - TimeStamp < LifeTime.
+server_id(Port, <<>>, _SslOpts, _Cert, _, _) ->
+ {ssl_manager:new_session_id(Port), undefined};
+server_id(Port, SuggestedId,
+ #ssl_options{reuse_sessions = ReuseEnabled,
+ reuse_session = ReuseFun},
+ Cert, Cache, CacheCb) ->
+ LifeTime = case application:get_env(ssl, session_lifetime) of
+ {ok, Time} when is_integer(Time) -> Time;
+ _ -> ?'24H_in_sec'
+ end,
+ case is_resumable(SuggestedId, Port, ReuseEnabled,ReuseFun,
+ Cache, CacheCb, LifeTime, Cert)
+ of
+ {true, Resumed} ->
+ {SuggestedId, Resumed};
+ {false, undefined} ->
+ {ssl_manager:new_session_id(Port), undefined}
+ end.
+
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
+select_session({_, _, #ssl_options{reuse_sessions=false}}, _Cache, _CacheCb, _OwnCert) ->
+ no_session;
select_session({HostIP, Port, SslOpts}, Cache, CacheCb, OwnCert) ->
Sessions = CacheCb:select_session(Cache, {HostIP, Port}),
select_session(Sessions, SslOpts, OwnCert).
select_session([], _, _) ->
no_session;
-
-select_session(Sessions, #ssl_options{ciphers = Ciphers,
- reuse_sessions = ReuseSession}, OwnCert) ->
- IsResumable =
- fun(Session) ->
- ReuseSession andalso resumable(Session#session.is_resumable) andalso
- lists:member(Session#session.cipher_suite, Ciphers)
- andalso (OwnCert == Session#session.own_certificate)
+select_session(Sessions, #ssl_options{ciphers = Ciphers}, OwnCert) ->
+ IsNotResumable =
+ fun([_Id, Session]) ->
+ not (resumable(Session#session.is_resumable) andalso
+ lists:member(Session#session.cipher_suite, Ciphers)
+ andalso (OwnCert == Session#session.own_certificate))
end,
- case [Id || [Id, Session] <- Sessions, IsResumable(Session)] of
- [] ->
- no_session;
- List ->
- hd(List)
- end.
-
-%% If we can not generate a not allready in use session ID in
-%% ?GEN_UNIQUE_ID_MAX_TRIES we make the new session uncacheable The
-%% value of ?GEN_UNIQUE_ID_MAX_TRIES is stolen from open SSL which
-%% states : "If we can not find a session id in
-%% ?GEN_UNIQUE_ID_MAX_TRIES either the RAND code is broken or someone
-%% is trying to open roughly very close to 2^128 (or 2^256) SSL
-%% sessions to our server"
-new_id(_, 0, _, _) ->
- <<>>;
-new_id(Port, Tries, Cache, CacheCb) ->
- Id = crypto:rand_bytes(?NUM_OF_SESSION_ID_BYTES),
- case CacheCb:lookup(Cache, {Port, Id}) of
- undefined ->
- Now = calendar:datetime_to_gregorian_seconds({date(), time()}),
- %% New sessions can not be set to resumable
- %% until handshake is compleate and the
- %% other session values are set.
- CacheCb:update(Cache, {Port, Id}, #session{session_id = Id,
- is_resumable = false,
- time_stamp = Now}),
- Id;
- _ ->
- new_id(Port, Tries - 1, Cache, CacheCb)
+ case lists:dropwhile(IsNotResumable, Sessions) of
+ [] -> no_session;
+ [[Id, _]|_] -> Id
end.
-is_resumable(SuggestedSessionId, Port, ReuseEnabled, ReuseFun, Cache,
+is_resumable(_, _, false, _, _, _, _, _) ->
+ {false, undefined};
+is_resumable(SuggestedSessionId, Port, true, ReuseFun, Cache,
CacheCb, SecondLifeTime, OwnCert) ->
case CacheCb:lookup(Cache, {Port, SuggestedSessionId}) of
#session{cipher_suite = CipherSuite,
@@ -149,14 +122,17 @@ is_resumable(SuggestedSessionId, Port, ReuseEnabled, ReuseFun, Cache,
compression_method = Compression,
is_resumable = IsResumable,
peer_certificate = PeerCert} = Session ->
- ReuseEnabled
- andalso resumable(IsResumable)
+ case resumable(IsResumable)
andalso (OwnCert == SessionOwnCert)
- andalso valid_session(Session, SecondLifeTime)
- andalso ReuseFun(SuggestedSessionId, PeerCert,
- Compression, CipherSuite);
+ andalso valid_session(Session, SecondLifeTime)
+ andalso ReuseFun(SuggestedSessionId, PeerCert,
+ Compression, CipherSuite)
+ of
+ true -> {true, Session};
+ false -> {false, undefined}
+ end;
undefined ->
- false
+ {false, undefined}
end.
resumable(new) ->
diff --git a/lib/ssl/src/ssl_session_cache.erl b/lib/ssl/src/ssl_session_cache.erl
index f9bbf905e1..5c6ee3c54c 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-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2012. 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
@@ -32,7 +32,7 @@
%% Description: Return table reference. Called by ssl_manager process.
%%--------------------------------------------------------------------
init(_) ->
- ets:new(cache_name(), [set, protected]).
+ ets:new(cache_name(), [ordered_set, protected]).
%%--------------------------------------------------------------------
%% Description: Handles cache table at termination of ssl manager.
diff --git a/lib/ssl/src/ssl_ssl3.erl b/lib/ssl/src/ssl_ssl3.erl
index f2926b2d2f..a11c5b8c0c 100644
--- a/lib/ssl/src/ssl_ssl3.erl
+++ b/lib/ssl/src/ssl_ssl3.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2012. 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
@@ -54,9 +54,9 @@ master_secret(PremasterSecret, ClientRandom, ServerRandom) ->
Block = generate_keyblock(PremasterSecret, ClientRandom, ServerRandom, 48),
Block.
--spec finished(client | server, binary(), {binary(), binary()}) -> binary().
+-spec finished(client | server, binary(), [binary()]) -> binary().
-finished(Role, MasterSecret, {MD5Hash, SHAHash}) ->
+finished(Role, MasterSecret, Handshake) ->
%% draft-ietf-tls-ssl-version3-00 - 5.6.9 Finished
%% struct {
%% opaque md5_hash[16];
@@ -70,13 +70,13 @@ finished(Role, MasterSecret, {MD5Hash, SHAHash}) ->
%% SHA(handshake_messages + Sender +
%% master_secret + pad1));
Sender = get_sender(Role),
- MD5 = handshake_hash(?MD5, MasterSecret, Sender, MD5Hash),
- SHA = handshake_hash(?SHA, MasterSecret, Sender, SHAHash),
+ MD5 = handshake_hash(?MD5, MasterSecret, Sender, Handshake),
+ SHA = handshake_hash(?SHA, MasterSecret, Sender, Handshake),
<<MD5/binary, SHA/binary>>.
--spec certificate_verify(OID::tuple(), binary(), {binary(), binary()}) -> binary().
+-spec certificate_verify(md5sha | sha, binary(), [binary()]) -> binary().
-certificate_verify(?'rsaEncryption', MasterSecret, {MD5Hash, SHAHash}) ->
+certificate_verify(md5sha, MasterSecret, Handshake) ->
%% md5_hash
%% MD5(master_secret + pad_2 +
%% MD5(handshake_messages + master_secret + pad_1));
@@ -84,15 +84,16 @@ certificate_verify(?'rsaEncryption', MasterSecret, {MD5Hash, SHAHash}) ->
%% SHA(master_secret + pad_2 +
%% SHA(handshake_messages + master_secret + pad_1));
- MD5 = handshake_hash(?MD5, MasterSecret, undefined, MD5Hash),
- SHA = handshake_hash(?SHA, MasterSecret, undefined, SHAHash),
+ MD5 = handshake_hash(?MD5, MasterSecret, undefined, Handshake),
+ SHA = handshake_hash(?SHA, MasterSecret, undefined, Handshake),
<<MD5/binary, SHA/binary>>;
-certificate_verify(?'id-dsa', MasterSecret, {_, SHAHash}) ->
+certificate_verify(sha, MasterSecret, Handshake) ->
%% sha_hash
%% SHA(master_secret + pad_2 +
%% SHA(handshake_messages + master_secret + pad_1));
- handshake_hash(?SHA, MasterSecret, undefined, SHAHash).
+
+ handshake_hash(?SHA, MasterSecret, undefined, Handshake).
-spec mac_hash(integer(), binary(), integer(), integer(), integer(), binary()) -> binary().
@@ -152,28 +153,17 @@ suites() ->
%%% Internal functions
%%--------------------------------------------------------------------
-hash(?MD5, Data) ->
+hash(?MD5, Data) ->
crypto:md5(Data);
-hash(?SHA, Data) ->
+hash(?SHA, Data) ->
crypto:sha(Data).
-hash_update(?MD5, Context, Data) ->
- crypto:md5_update(Context, Data);
-hash_update(?SHA, Context, Data) ->
- crypto:sha_update(Context, Data).
-
-hash_final(?MD5, Context) ->
- crypto:md5_final(Context);
-hash_final(?SHA, Context) ->
- crypto:sha_final(Context).
-
%%pad_1(?NULL) ->
%% "";
pad_1(?MD5) ->
<<"666666666666666666666666666666666666666666666666">>;
pad_1(?SHA) ->
<<"6666666666666666666666666666666666666666">>.
-
%%pad_2(?NULL) ->
%% "";
pad_2(?MD5) ->
@@ -189,19 +179,11 @@ mac_hash(Method, Secret, Data) ->
InnerHash = hash(Method, [Secret, pad_1(Method), Data]),
hash(Method, [Secret, pad_2(Method), InnerHash]).
-handshake_hash(Method, HandshakeHash, Extra) ->
- HSH = hash_update(Method, HandshakeHash, Extra),
- hash_final(Method, HSH).
-
-handshake_hash(Method, MasterSecret, undefined, HandshakeHash) ->
- InnerHash =
- handshake_hash(Method, HandshakeHash,
- [MasterSecret, pad_1(Method)]),
+handshake_hash(Method, MasterSecret, undefined, Handshake) ->
+ InnerHash = hash(Method, [Handshake, MasterSecret, pad_1(Method)]),
hash(Method, [MasterSecret, pad_2(Method), InnerHash]);
-handshake_hash(Method, MasterSecret, Sender, HandshakeHash) ->
- InnerHash =
- handshake_hash(Method, HandshakeHash,
- [Sender, MasterSecret, pad_1(Method)]),
+handshake_hash(Method, MasterSecret, Sender, Handshake) ->
+ InnerHash = hash(Method, [Handshake, Sender, MasterSecret, pad_1(Method)]),
hash(Method, [MasterSecret, pad_2(Method), InnerHash]).
get_sender(client) -> "CLNT";
diff --git a/lib/ssl/src/ssl_tls1.erl b/lib/ssl/src/ssl_tls1.erl
index c8aae34892..41dc1bf0dc 100644
--- a/lib/ssl/src/ssl_tls1.erl
+++ b/lib/ssl/src/ssl_tls1.erl
@@ -26,27 +26,29 @@
-include("ssl_cipher.hrl").
-include("ssl_internal.hrl").
--include("ssl_record.hrl").
+-include("ssl_record.hrl").
--export([master_secret/3, finished/3, certificate_verify/2, mac_hash/7,
- setup_keys/6, suites/0, prf/4]).
+-export([master_secret/4, finished/5, certificate_verify/3, mac_hash/7,
+ setup_keys/8, suites/1, prf/5]).
%%====================================================================
%% Internal application API
%%====================================================================
--spec master_secret(binary(), binary(), binary()) -> binary().
+-spec master_secret(integer(), binary(), binary(), binary()) -> binary().
-master_secret(PreMasterSecret, ClientRandom, ServerRandom) ->
- %% RFC 2246 & 4346 - 8.1 %% master_secret = PRF(pre_master_secret,
- %% "master secret", ClientHello.random +
- %% ServerHello.random)[0..47];
- prf(PreMasterSecret, <<"master secret">>,
+master_secret(PrfAlgo, PreMasterSecret, ClientRandom, ServerRandom) ->
+ %% RFC 2246 & 4346 && RFC 5246 - 8.1 %% master_secret = PRF(pre_master_secret,
+ %% "master secret", ClientHello.random +
+ %% ServerHello.random)[0..47];
+
+ prf(PrfAlgo, PreMasterSecret, <<"master secret">>,
[ClientRandom, ServerRandom], 48).
--spec finished(client | server, binary(), {binary(), binary()}) -> binary().
+-spec finished(client | server, integer(), integer(), binary(), [binary()]) -> binary().
-finished(Role, MasterSecret, {MD5Hash, SHAHash}) ->
+finished(Role, Version, PrfAlgo, MasterSecret, Handshake)
+ when Version == 1; Version == 2; PrfAlgo == ?MD5SHA ->
%% RFC 2246 & 4346 - 7.4.9. Finished
%% struct {
%% opaque verify_data[12];
@@ -55,26 +57,39 @@ finished(Role, MasterSecret, {MD5Hash, SHAHash}) ->
%% verify_data
%% PRF(master_secret, finished_label, MD5(handshake_messages) +
%% SHA-1(handshake_messages)) [0..11];
- MD5 = hash_final(?MD5, MD5Hash),
- SHA = hash_final(?SHA, SHAHash),
- prf(MasterSecret, finished_label(Role), [MD5, SHA], 12).
+ MD5 = crypto:md5(Handshake),
+ SHA = crypto:sha(Handshake),
+ prf(?MD5SHA, MasterSecret, finished_label(Role), [MD5, SHA], 12);
+
+finished(Role, Version, PrfAlgo, MasterSecret, Handshake)
+ when Version == 3 ->
+ %% RFC 5246 - 7.4.9. Finished
+ %% struct {
+ %% opaque verify_data[12];
+ %% } Finished;
+ %%
+ %% verify_data
+ %% PRF(master_secret, finished_label, Hash(handshake_messages)) [0..11];
+ Hash = crypto:hash(mac_algo(PrfAlgo), Handshake),
+ prf(PrfAlgo, MasterSecret, finished_label(Role), Hash, 12).
--spec certificate_verify(OID::tuple(), {binary(), binary()}) -> binary().
+-spec certificate_verify(md5sha | sha, integer(), [binary()]) -> binary().
-certificate_verify(?'rsaEncryption', {MD5Hash, SHAHash}) ->
- MD5 = hash_final(?MD5, MD5Hash),
- SHA = hash_final(?SHA, SHAHash),
+certificate_verify(md5sha, _Version, Handshake) ->
+ MD5 = crypto:md5(Handshake),
+ SHA = crypto:sha(Handshake),
<<MD5/binary, SHA/binary>>;
-certificate_verify(?'id-dsa', {_, SHAHash}) ->
- hash_final(?SHA, SHAHash).
+certificate_verify(HashAlgo, _Version, Handshake) ->
+ crypto:hash(HashAlgo, Handshake).
--spec setup_keys(binary(), binary(), binary(), integer(),
- integer(), integer()) -> {binary(), binary(), binary(),
+-spec setup_keys(integer(), integer(), binary(), binary(), binary(), integer(),
+ integer(), integer()) -> {binary(), binary(), binary(),
binary(), binary(), binary()}.
-setup_keys(MasterSecret, ServerRandom, ClientRandom, HashSize,
- KeyMatLen, IVSize) ->
+setup_keys(Version, _PrfAlgo, MasterSecret, ServerRandom, ClientRandom, HashSize,
+ KeyMatLen, IVSize)
+ when Version == 1 ->
%% RFC 2246 - 6.3. Key calculation
%% key_block = PRF(SecurityParameters.master_secret,
%% "key expansion",
@@ -88,36 +103,67 @@ setup_keys(MasterSecret, ServerRandom, ClientRandom, HashSize,
%% client_write_IV[SecurityParameters.IV_size]
%% server_write_IV[SecurityParameters.IV_size]
WantedLength = 2 * (HashSize + KeyMatLen + IVSize),
- KeyBlock = prf(MasterSecret, "key expansion",
+ KeyBlock = prf(?MD5SHA, MasterSecret, "key expansion",
[ServerRandom, ClientRandom], WantedLength),
<<ClientWriteMacSecret:HashSize/binary,
ServerWriteMacSecret:HashSize/binary,
ClientWriteKey:KeyMatLen/binary, ServerWriteKey:KeyMatLen/binary,
ClientIV:IVSize/binary, ServerIV:IVSize/binary>> = KeyBlock,
{ClientWriteMacSecret, ServerWriteMacSecret, ClientWriteKey,
- ServerWriteKey, ClientIV, ServerIV}.
+ ServerWriteKey, ClientIV, ServerIV};
+
+%% TLS v1.1
+setup_keys(Version, _PrfAlgo, MasterSecret, ServerRandom, ClientRandom, HashSize,
+ KeyMatLen, IVSize)
+ when Version == 2 ->
+ %% RFC 4346 - 6.3. Key calculation
+ %% key_block = PRF(SecurityParameters.master_secret,
+ %% "key expansion",
+ %% SecurityParameters.server_random +
+ %% SecurityParameters.client_random);
+ %% Then the key_block is partitioned as follows:
+ %% client_write_MAC_secret[SecurityParameters.hash_size]
+ %% server_write_MAC_secret[SecurityParameters.hash_size]
+ %% client_write_key[SecurityParameters.key_material_length]
+ %% server_write_key[SecurityParameters.key_material_length]
+ %%
+ %% RFC 4346 is incomplete, the client and server IVs have to
+ %% be generated just like for TLS 1.0
+ WantedLength = 2 * (HashSize + KeyMatLen + IVSize),
+ KeyBlock = prf(?MD5SHA, MasterSecret, "key expansion",
+ [ServerRandom, ClientRandom], WantedLength),
+ <<ClientWriteMacSecret:HashSize/binary,
+ ServerWriteMacSecret:HashSize/binary,
+ ClientWriteKey:KeyMatLen/binary, ServerWriteKey:KeyMatLen/binary,
+ ClientIV:IVSize/binary, ServerIV:IVSize/binary>> = KeyBlock,
+ {ClientWriteMacSecret, ServerWriteMacSecret, ClientWriteKey,
+ ServerWriteKey, ClientIV, ServerIV};
-%% TLS v1.1 uncomment when supported.
-%% setup_keys(MasterSecret, ServerRandom, ClientRandom, HashSize, KeyMatLen) ->
-%% %% RFC 4346 - 6.3. Key calculation
-%% %% key_block = PRF(SecurityParameters.master_secret,
-%% %% "key expansion",
-%% %% SecurityParameters.server_random +
-%% %% SecurityParameters.client_random);
-%% %% Then the key_block is partitioned as follows:
-%% %% client_write_MAC_secret[SecurityParameters.hash_size]
-%% %% server_write_MAC_secret[SecurityParameters.hash_size]
-%% %% client_write_key[SecurityParameters.key_material_length]
-%% %% server_write_key[SecurityParameters.key_material_length]
-%% WantedLength = 2 * (HashSize + KeyMatLen),
-%% KeyBlock = prf(MasterSecret, "key expansion",
-%% [ServerRandom, ClientRandom], WantedLength),
-%% <<ClientWriteMacSecret:HashSize/binary,
-%% ServerWriteMacSecret:HashSize/binary,
-%% ClientWriteKey:KeyMatLen/binary, ServerWriteKey:KeyMatLen/binary>>
-%% = KeyBlock,
-%% {ClientWriteMacSecret, ServerWriteMacSecret, ClientWriteKey,
-%% ServerWriteKey, undefined, undefined}.
+%% TLS v1.2
+setup_keys(Version, PrfAlgo, MasterSecret, ServerRandom, ClientRandom, HashSize,
+ KeyMatLen, IVSize)
+ when Version == 3 ->
+ %% RFC 5246 - 6.3. Key calculation
+ %% key_block = PRF(SecurityParameters.master_secret,
+ %% "key expansion",
+ %% SecurityParameters.server_random +
+ %% SecurityParameters.client_random);
+ %% Then the key_block is partitioned as follows:
+ %% client_write_MAC_secret[SecurityParameters.hash_size]
+ %% server_write_MAC_secret[SecurityParameters.hash_size]
+ %% client_write_key[SecurityParameters.key_material_length]
+ %% server_write_key[SecurityParameters.key_material_length]
+ %% client_write_IV[SecurityParameters.fixed_iv_length]
+ %% server_write_IV[SecurityParameters.fixed_iv_length]
+ WantedLength = 2 * (HashSize + KeyMatLen + IVSize),
+ KeyBlock = prf(PrfAlgo, MasterSecret, "key expansion",
+ [ServerRandom, ClientRandom], WantedLength),
+ <<ClientWriteMacSecret:HashSize/binary,
+ ServerWriteMacSecret:HashSize/binary,
+ ClientWriteKey:KeyMatLen/binary, ServerWriteKey:KeyMatLen/binary,
+ ClientIV:IVSize/binary, ServerIV:IVSize/binary>> = KeyBlock,
+ {ClientWriteMacSecret, ServerWriteMacSecret, ClientWriteKey,
+ ServerWriteKey, ClientIV, ServerIV}.
-spec mac_hash(integer(), binary(), integer(), integer(), tls_version(),
integer(), binary()) -> binary().
@@ -134,9 +180,9 @@ mac_hash(Method, Mac_write_secret, Seq_num, Type, {Major, Minor},
Fragment]),
Mac.
--spec suites() -> [cipher_suite()].
+-spec suites(1|2|3) -> [cipher_suite()].
-suites() ->
+suites(Minor) when Minor == 1; Minor == 2->
[
?TLS_DHE_RSA_WITH_AES_256_CBC_SHA,
?TLS_DHE_DSS_WITH_AES_256_CBC_SHA,
@@ -152,7 +198,19 @@ suites() ->
?TLS_RSA_WITH_RC4_128_MD5,
?TLS_DHE_RSA_WITH_DES_CBC_SHA,
?TLS_RSA_WITH_DES_CBC_SHA
- ].
+ ];
+
+suites(Minor) when Minor == 3 ->
+ [
+ ?TLS_DHE_RSA_WITH_AES_256_CBC_SHA256,
+ ?TLS_DHE_DSS_WITH_AES_256_CBC_SHA256,
+ ?TLS_RSA_WITH_AES_256_CBC_SHA256,
+ ?TLS_DHE_RSA_WITH_AES_128_CBC_SHA256,
+ ?TLS_DHE_DSS_WITH_AES_128_CBC_SHA256,
+ ?TLS_RSA_WITH_AES_128_CBC_SHA256
+ %% ?TLS_DH_anon_WITH_AES_128_CBC_SHA256,
+ %% ?TLS_DH_anon_WITH_AES_256_CBC_SHA256
+ ] ++ suites(2).
%%--------------------------------------------------------------------
%%% Internal functions
@@ -163,7 +221,19 @@ hmac_hash(?NULL, _, _) ->
hmac_hash(?MD5, Key, Value) ->
crypto:md5_mac(Key, Value);
hmac_hash(?SHA, Key, Value) ->
- crypto:sha_mac(Key, Value).
+ crypto:sha_mac(Key, Value);
+hmac_hash(?SHA256, Key, Value) ->
+ crypto:sha256_mac(Key, Value);
+hmac_hash(?SHA384, Key, Value) ->
+ crypto:sha384_mac(Key, Value);
+hmac_hash(?SHA512, Key, Value) ->
+ crypto:sha512_mac(Key, Value).
+
+mac_algo(?MD5) -> md5;
+mac_algo(?SHA) -> sha;
+mac_algo(?SHA256) -> sha256;
+mac_algo(?SHA384) -> sha384;
+mac_algo(?SHA512) -> sha512.
% First, we define a data expansion function, P_hash(secret, data) that
% uses a single hash function to expand a secret and seed into an
@@ -182,7 +252,7 @@ p_hash(_Secret, _Seed, WantedLength, _Method, _N, [Last | Acc])
when WantedLength =< 0 ->
Keep = byte_size(Last) + WantedLength,
<<B:Keep/binary, _/binary>> = Last,
- lists:reverse(Acc, [B]);
+ list_to_binary(lists:reverse(Acc, [B]));
p_hash(Secret, Seed, WantedLength, Method, N, Acc) ->
N1 = N+1,
Bin = hmac_hash(Method, Secret, [a(N1, Secret, Seed, Method), Seed]),
@@ -214,13 +284,18 @@ split_secret(BinSecret) ->
<<_:Div/binary, Secret2:EvenLength/binary>> = BinSecret,
{Secret1, Secret2}.
-prf(Secret, Label, Seed, WantedLength) ->
+prf(?MD5SHA, Secret, Label, Seed, WantedLength) ->
%% PRF(secret, label, seed) = P_MD5(S1, label + seed) XOR
%% P_SHA-1(S2, label + seed);
{S1, S2} = split_secret(Secret),
LS = list_to_binary([Label, Seed]),
crypto:exor(p_hash(S1, LS, WantedLength, ?MD5),
- p_hash(S2, LS, WantedLength, ?SHA)).
+ p_hash(S2, LS, WantedLength, ?SHA));
+
+prf(MAC, Secret, Label, Seed, WantedLength) ->
+ %% PRF(secret, label, seed) = P_SHA256(secret, label + seed);
+ LS = list_to_binary([Label, Seed]),
+ p_hash(Secret, LS, WantedLength, MAC).
%%%% Misc help functions %%%%
@@ -228,8 +303,3 @@ finished_label(client) ->
<<"client finished">>;
finished_label(server) ->
<<"server finished">>.
-
-hash_final(?MD5, Conntext) ->
- crypto:md5_final(Conntext);
-hash_final(?SHA, Conntext) ->
- crypto:sha_final(Conntext).
diff --git a/lib/ssl/test/Makefile b/lib/ssl/test/Makefile
index 6b1da63d08..a0f54c0359 100644
--- a/lib/ssl/test/Makefile
+++ b/lib/ssl/test/Makefile
@@ -120,11 +120,11 @@ include $(ERL_TOP)/make/otp_release_targets.mk
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)
- chmod -R u+w $(RELSYSDIR)
- @tar cf - *_SUITE_data | (cd $(RELSYSDIR); tar xf -)
+ $(INSTALL_DIR) "$(RELSYSDIR)"
+ $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(HRL_FILES_NEEDED_IN_TEST) $(COVER_FILE) "$(RELSYSDIR)"
+ $(INSTALL_DATA) ssl.spec ssl.cover "$(RELSYSDIR)"
+ chmod -R u+w "$(RELSYSDIR)"
+ @tar cf - *_SUITE_data | (cd "$(RELSYSDIR)"; tar xf -)
release_docs_spec:
diff --git a/lib/ssl/test/ssl_basic_SUITE.erl b/lib/ssl/test/ssl_basic_SUITE.erl
index 590ecf33ca..93f7209aea 100644
--- a/lib/ssl/test/ssl_basic_SUITE.erl
+++ b/lib/ssl/test/ssl_basic_SUITE.erl
@@ -27,9 +27,11 @@
-include_lib("common_test/include/ct.hrl").
-include_lib("public_key/include/public_key.hrl").
+-include("ssl_internal.hrl").
-include("ssl_alert.hrl").
-include("ssl_internal.hrl").
-include("ssl_record.hrl").
+-include("ssl_handshake.hrl").
-define('24H_in_sec', 86400).
-define(TIMEOUT, 60000).
@@ -50,10 +52,10 @@
%%--------------------------------------------------------------------
init_per_suite(Config0) ->
Dog = ssl_test_lib:timetrap(?LONG_TIMEOUT *2),
+ catch crypto:stop(),
try crypto:start() of
ok ->
application:start(public_key),
- ssl:start(),
%% make rsa certs using oppenssl
Result =
@@ -90,46 +92,28 @@ end_per_suite(_Config) ->
%% variable, but should NOT alter/remove any existing entries.
%% Description: Initialization before each test case
%%--------------------------------------------------------------------
-init_per_testcase(session_cache_process_list, Config) ->
- init_customized_session_cache(list, Config);
-
-init_per_testcase(session_cache_process_mnesia, Config) ->
- mnesia:start(),
- init_customized_session_cache(mnesia, Config);
-
-init_per_testcase(reuse_session_expired, Config0) ->
- Config = lists:keydelete(watchdog, 1, Config0),
- Dog = ssl_test_lib:timetrap(?EXPIRE * 1000 * 5),
- ssl:stop(),
- application:load(ssl),
- application:set_env(ssl, session_lifetime, ?EXPIRE),
- ssl:start(),
- [{watchdog, Dog} | Config];
-
init_per_testcase(no_authority_key_identifier, Config) ->
%% Clear cach so that root cert will not
%% be found.
- ssl:stop(),
- ssl:start(),
+ ssl:clear_pem_cache(),
Config;
-init_per_testcase(TestCase, Config) when TestCase == ciphers_rsa_signed_certs_ssl3;
- TestCase == ciphers_rsa_signed_certs_openssl_names_ssl3;
- TestCase == ciphers_dsa_signed_certs_ssl3;
- TestCase == ciphers_dsa_signed_certs_openssl_names_ssl3 ->
+init_per_testcase(protocol_versions, Config) ->
ssl:stop(),
application:load(ssl),
- application:set_env(ssl, protocol_version, sslv3),
+ %% For backwards compatibility sslv2 should be filtered out.
+ application:set_env(ssl, protocol_version, [sslv2, sslv3, tlsv1]),
ssl:start(),
Config;
-init_per_testcase(protocol_versions, Config) ->
+init_per_testcase(reuse_session_expired, Config0) ->
+ Config = lists:keydelete(watchdog, 1, Config0),
+ Dog = ssl_test_lib:timetrap(?EXPIRE * 1000 * 5),
ssl:stop(),
application:load(ssl),
- %% For backwards compatibility sslv2 should be filtered out.
- application:set_env(ssl, protocol_version, [sslv2, sslv3, tlsv1]),
+ application:set_env(ssl, session_lifetime, ?EXPIRE),
ssl:start(),
- Config;
+ [{watchdog, Dog} | Config];
init_per_testcase(empty_protocol_versions, Config) ->
ssl:stop(),
@@ -138,24 +122,15 @@ init_per_testcase(empty_protocol_versions, Config) ->
ssl:start(),
Config;
-init_per_testcase(different_ca_peer_sign, Config0) ->
- ssl_test_lib:make_mix_cert(Config0);
+%% init_per_testcase(different_ca_peer_sign, Config0) ->
+%% ssl_test_lib:make_mix_cert(Config0);
init_per_testcase(_TestCase, Config0) ->
+ test_server:format("TLS/SSL version ~p~n ", [ssl_record:supported_protocol_versions()]),
Config = lists:keydelete(watchdog, 1, Config0),
Dog = test_server:timetrap(?TIMEOUT),
[{watchdog, Dog} | Config].
-init_customized_session_cache(Type, Config0) ->
- Config = lists:keydelete(watchdog, 1, Config0),
- Dog = test_server:timetrap(?TIMEOUT),
- ssl:stop(),
- application:load(ssl),
- application:set_env(ssl, session_cb, ?MODULE),
- application:set_env(ssl, session_cb_init_args, [Type]),
- ssl:start(),
- [{watchdog, Dog} | Config].
-
%%--------------------------------------------------------------------
%% Function: end_per_testcase(TestCase, Config) -> _
%% Case - atom()
@@ -164,27 +139,10 @@ init_customized_session_cache(Type, Config0) ->
%% A list of key/value pairs, holding the test case configuration.
%% Description: Cleanup after each test case
%%--------------------------------------------------------------------
-end_per_testcase(session_cache_process_list, Config) ->
- application:unset_env(ssl, session_cb),
- end_per_testcase(default_action, Config);
-end_per_testcase(session_cache_process_mnesia, Config) ->
- application:unset_env(ssl, session_cb),
- application:unset_env(ssl, session_cb_init_args),
- mnesia:stop(),
- ssl:stop(),
- ssl:start(),
- end_per_testcase(default_action, Config);
end_per_testcase(reuse_session_expired, Config) ->
application:unset_env(ssl, session_lifetime),
end_per_testcase(default_action, Config);
-end_per_testcase(TestCase, Config) when TestCase == ciphers_rsa_signed_certs_ssl3;
- TestCase == ciphers_rsa_signed_certs_openssl_names_ssl3;
- TestCase == ciphers_dsa_signed_certs_ssl3;
- TestCase == ciphers_dsa_signed_certs_openssl_names_ssl3;
- TestCase == protocol_versions;
- TestCase == empty_protocol_versions->
- application:unset_env(ssl, protocol_version),
- end_per_testcase(default_action, Config);
+
end_per_testcase(_TestCase, Config) ->
Dog = ?config(watchdog, Config),
case Dog of
@@ -205,73 +163,170 @@ end_per_testcase(_TestCase, Config) ->
suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
- [app, alerts, connection_info, protocol_versions,
- empty_protocol_versions, controlling_process,
- controller_dies, client_closes_socket,
- connect_dist, peername, peercert, sockname, socket_options,
- invalid_inet_get_option, invalid_inet_get_option_not_list,
+ [
+ {group, basic},
+ {group, options},
+ {group, session},
+ {group, 'tlsv1.2'},
+ {group, 'tlsv1.1'},
+ {group, 'tlsv1'},
+ {group, 'sslv3'}
+ ].
+
+groups() ->
+ [{basic, [], basic_tests()},
+ {options, [], options_tests()},
+ {'tlsv1.2', [], all_versions_groups()},
+ {'tlsv1.1', [], all_versions_groups()},
+ {'tlsv1', [], all_versions_groups() ++ rizzo_tests()},
+ {'sslv3', [], all_versions_groups() ++ rizzo_tests()},
+ {api,[], api_tests()},
+ {certificate_verify, [], certificate_verify_tests()},
+ {session, [], session_tests()},
+ {renegotiate, [], renegotiate_tests()},
+ {ciphers, [], cipher_tests()},
+ {error_handling_tests, [], error_handling_tests()}
+ ].
+
+all_versions_groups ()->
+ [{group, api},
+ {group, certificate_verify},
+ {group, renegotiate},
+ {group, ciphers},
+ {group, error_handling_tests}].
+
+init_per_group(GroupName, Config) ->
+ case ssl_test_lib:is_tls_version(GroupName) of
+ true ->
+ case ssl_test_lib:sufficient_crypto_support(GroupName) of
+ true ->
+ ssl_test_lib:init_tls_version(GroupName),
+ Config;
+ false ->
+ {skip, "Missing crypto support"}
+ end;
+ _ ->
+ ssl:start(),
+ Config
+ end.
+
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+basic_tests() ->
+ [app,
+ alerts,
+ send_close,
+ connect_twice,
+ connect_dist
+ ].
+
+options_tests() ->
+ [der_input,
+ misc_ssl_options,
+ socket_options,
+ invalid_inet_get_option,
+ invalid_inet_get_option_not_list,
invalid_inet_get_option_improper_list,
- invalid_inet_set_option, invalid_inet_set_option_not_list,
+ invalid_inet_set_option,
+ invalid_inet_set_option_not_list,
invalid_inet_set_option_improper_list,
- misc_ssl_options, versions, cipher_suites, upgrade,
- upgrade_with_timeout, tcp_connect, tcp_connect_big, ipv6, ekeyfile,
- ecertfile, ecacertfile, eoptions, shutdown,
- shutdown_write, shutdown_both, shutdown_error,
- ciphers_rsa_signed_certs, ciphers_rsa_signed_certs_ssl3,
- ciphers_rsa_signed_certs_openssl_names,
- ciphers_rsa_signed_certs_openssl_names_ssl3,
- ciphers_dsa_signed_certs, ciphers_dsa_signed_certs_ssl3,
- ciphers_dsa_signed_certs_openssl_names,
- ciphers_dsa_signed_certs_openssl_names_ssl3,
- anonymous_cipher_suites,
- default_reject_anonymous,
- send_close,
- close_transport_accept, dh_params,
- server_verify_peer_passive, server_verify_peer_active,
+ dh_params,
+ ecertfile,
+ ecacertfile,
+ ekeyfile,
+ eoptions,
+ protocol_versions,
+ empty_protocol_versions,
+ ipv6,
+ reuseaddr].
+
+api_tests() ->
+ [connection_info,
+ peername,
+ peercert,
+ sockname,
+ versions,
+ controlling_process,
+ upgrade,
+ upgrade_with_timeout,
+ shutdown,
+ shutdown_write,
+ shutdown_both,
+ shutdown_error,
+ hibernate
+ ].
+
+certificate_verify_tests() ->
+ [server_verify_peer_passive,
+ server_verify_peer_active,
server_verify_peer_active_once,
- server_verify_none_passive, server_verify_none_active,
+ server_verify_none_passive,
+ server_verify_none_active,
server_verify_none_active_once,
- server_verify_no_cacerts, server_require_peer_cert_ok,
+ server_verify_no_cacerts,
+ server_require_peer_cert_ok,
server_require_peer_cert_fail,
server_verify_client_once_passive,
server_verify_client_once_active,
server_verify_client_once_active_once,
- client_verify_none_passive, client_verify_none_active,
+ client_verify_none_passive,
+ client_verify_none_active,
client_verify_none_active_once,
- reuse_session,
- reuse_session_expired,
- server_does_not_want_to_reuse_session,
- client_renegotiate, server_renegotiate,
- client_renegotiate_reused_session,
- server_renegotiate_reused_session,
- client_no_wrap_sequence_number,
- server_no_wrap_sequence_number, extended_key_usage_verify_peer,
+ extended_key_usage_verify_peer,
extended_key_usage_verify_none,
- no_authority_key_identifier, invalid_signature_client,
- invalid_signature_server, cert_expired,
+ invalid_signature_client,
+ invalid_signature_server,
+ cert_expired,
client_with_cert_cipher_suites_handshake,
verify_fun_always_run_client,
verify_fun_always_run_server,
- unknown_server_ca_fail, der_input,
+ unknown_server_ca_fail,
unknown_server_ca_accept_verify_none,
unknown_server_ca_accept_verify_peer,
unknown_server_ca_accept_backwardscompatibility,
- %%different_ca_peer_sign,
- no_reuses_session_server_restart_new_cert,
- no_reuses_session_server_restart_new_cert_file, reuseaddr,
- hibernate, connect_twice, renegotiate_dos_mitigate_active,
- renegotiate_dos_mitigate_passive,
- tcp_error_propagation_in_active_mode, rizzo, no_rizzo_rc4
+ no_authority_key_identifier
].
-groups() ->
- [].
+session_tests() ->
+ [reuse_session,
+ reuse_session_expired,
+ server_does_not_want_to_reuse_session,
+ no_reuses_session_server_restart_new_cert,
+ no_reuses_session_server_restart_new_cert_file].
-init_per_group(_GroupName, Config) ->
- Config.
+renegotiate_tests() ->
+ [client_renegotiate,
+ server_renegotiate,
+ client_renegotiate_reused_session,
+ server_renegotiate_reused_session,
+ client_no_wrap_sequence_number,
+ server_no_wrap_sequence_number,
+ renegotiate_dos_mitigate_active,
+ renegotiate_dos_mitigate_passive].
-end_per_group(_GroupName, Config) ->
- Config.
+cipher_tests() ->
+ [cipher_suites,
+ ciphers_rsa_signed_certs,
+ ciphers_rsa_signed_certs_openssl_names,
+ ciphers_dsa_signed_certs,
+ ciphers_dsa_signed_certs_openssl_names,
+ anonymous_cipher_suites,
+ default_reject_anonymous].
+
+error_handling_tests()->
+ [controller_dies,
+ client_closes_socket,
+ tcp_error_propagation_in_active_mode,
+ tcp_connect,
+ tcp_connect_big,
+ close_transport_accept
+ ].
+
+rizzo_tests() ->
+ [rizzo,
+ no_rizzo_rc4].
%% Test cases starts here.
%%--------------------------------------------------------------------
@@ -1724,21 +1779,7 @@ ciphers_rsa_signed_certs(Config) when is_list(Config) ->
ssl_record:protocol_version(ssl_record:highest_protocol_version([])),
Ciphers = ssl_test_lib:rsa_suites(),
- test_server:format("tls1 erlang cipher suites ~p~n", [Ciphers]),
- run_suites(Ciphers, Version, Config, rsa).
-
-ciphers_rsa_signed_certs_ssl3(doc) ->
- ["Test all rsa ssl cipher suites in ssl3"];
-
-ciphers_rsa_signed_certs_ssl3(suite) ->
- [];
-
-ciphers_rsa_signed_certs_ssl3(Config) when is_list(Config) ->
- Version =
- ssl_record:protocol_version({3,0}),
-
- Ciphers = ssl_test_lib:rsa_suites(),
- test_server:format("ssl3 erlang cipher suites ~p~n", [Ciphers]),
+ test_server:format("~p erlang cipher suites ~p~n", [Version, Ciphers]),
run_suites(Ciphers, Version, Config, rsa).
ciphers_rsa_signed_certs_openssl_names(doc) ->
@@ -1755,18 +1796,6 @@ ciphers_rsa_signed_certs_openssl_names(Config) when is_list(Config) ->
run_suites(Ciphers, Version, Config, rsa).
-ciphers_rsa_signed_certs_openssl_names_ssl3(doc) ->
- ["Test all dsa ssl cipher suites in ssl3"];
-
-ciphers_rsa_signed_certs_openssl_names_ssl3(suite) ->
- [];
-
-ciphers_rsa_signed_certs_openssl_names_ssl3(Config) when is_list(Config) ->
- Version = ssl_record:protocol_version({3,0}),
- Ciphers = ssl_test_lib:openssl_rsa_suites(),
- run_suites(Ciphers, Version, Config, rsa).
-
-
ciphers_dsa_signed_certs(doc) ->
["Test all dsa ssl cipher suites in highest support ssl/tls version"];
@@ -1778,23 +1807,8 @@ ciphers_dsa_signed_certs(Config) when is_list(Config) ->
ssl_record:protocol_version(ssl_record:highest_protocol_version([])),
Ciphers = ssl_test_lib:dsa_suites(),
- test_server:format("tls1 erlang cipher suites ~p~n", [Ciphers]),
- run_suites(Ciphers, Version, Config, dsa).
-
-ciphers_dsa_signed_certs_ssl3(doc) ->
- ["Test all dsa ssl cipher suites in ssl3"];
-
-ciphers_dsa_signed_certs_ssl3(suite) ->
- [];
-
-ciphers_dsa_signed_certs_ssl3(Config) when is_list(Config) ->
- Version =
- ssl_record:protocol_version({3,0}),
-
- Ciphers = ssl_test_lib:dsa_suites(),
- test_server:format("ssl3 erlang cipher suites ~p~n", [Ciphers]),
+ test_server:format("~p erlang cipher suites ~p~n", [Version, Ciphers]),
run_suites(Ciphers, Version, Config, dsa).
-
ciphers_dsa_signed_certs_openssl_names(doc) ->
["Test all dsa ssl cipher suites in highest support ssl/tls version"];
@@ -1810,18 +1824,6 @@ ciphers_dsa_signed_certs_openssl_names(Config) when is_list(Config) ->
test_server:format("tls1 openssl cipher suites ~p~n", [Ciphers]),
run_suites(Ciphers, Version, Config, dsa).
-
-ciphers_dsa_signed_certs_openssl_names_ssl3(doc) ->
- ["Test all dsa ssl cipher suites in ssl3"];
-
-ciphers_dsa_signed_certs_openssl_names_ssl3(suite) ->
- [];
-
-ciphers_dsa_signed_certs_openssl_names_ssl3(Config) when is_list(Config) ->
- Version = ssl_record:protocol_version({3,0}),
- Ciphers = ssl_test_lib:openssl_dsa_suites(),
- run_suites(Ciphers, Version, Config, dsa).
-
anonymous_cipher_suites(doc)->
["Test the anonymous ciphersuites"];
anonymous_cipher_suites(suite) ->
@@ -1858,7 +1860,7 @@ run_suites(Ciphers, Version, Config, Type) ->
end.
erlang_cipher_suite(Suite) when is_list(Suite)->
- ssl_cipher:suite_definition(ssl_cipher:openssl_suite(Suite));
+ ssl:suite_definition(ssl_cipher:openssl_suite(Suite));
erlang_cipher_suite(Suite) ->
Suite.
@@ -2085,7 +2087,9 @@ reuse_session_expired(Config) when is_list(Config) ->
Server ! listen,
%% Make sure session is unregistered due to expiration
- test_server:sleep((?EXPIRE+1) * 1000),
+ test_server:sleep((?EXPIRE+1)),
+ [{session_id, Id} |_] = SessionInfo,
+ make_sure_expired(Hostname, Port, Id),
Client2 =
ssl_test_lib:start_client([{node, ClientNode},
@@ -2104,6 +2108,22 @@ reuse_session_expired(Config) when is_list(Config) ->
ssl_test_lib:close(Client1),
ssl_test_lib:close(Client2).
+make_sure_expired(Host, Port, Id) ->
+ {status, _, _, StatusInfo} = sys:get_status(whereis(ssl_manager)),
+ [_, _,_, _, Prop] = StatusInfo,
+ State = ssl_test_lib:state(Prop),
+ Cache = element(2, State),
+ case ssl_session_cache:lookup(Cache, {{Host, Port}, Id}) of
+ undefined ->
+ ok;
+ #session{is_resumable = false} ->
+ ok;
+ _ ->
+ test_server:sleep(?SLEEP),
+ make_sure_expired(Host, Port, Id)
+ end.
+
+
%%--------------------------------------------------------------------
server_does_not_want_to_reuse_session(doc) ->
["Test reuse of sessions (short handshake)"];
@@ -3649,6 +3669,8 @@ no_reuses_session_server_restart_new_cert_file(Config) when is_list(Config) ->
ssl_test_lib:close(Server),
ssl_test_lib:close(Client0),
+ ssl:clear_pem_cache(),
+
NewServerOpts = new_config(PrivDir, DsaServerOpts),
Server1 =
@@ -3872,16 +3894,16 @@ tcp_error_propagation_in_active_mode(Config) when is_list(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, no_result, []}},
- {options, ServerOpts}]),
+ {from, self()},
+ {mfa, {ssl_test_lib, no_result, []}},
+ {options, ServerOpts}]),
Port = ssl_test_lib:inet_port(Server),
{Client, #sslsocket{pid=Pid} = SslSocket} = ssl_test_lib:start_client([return_socket,
- {node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {?MODULE, receive_msg, []}},
- {options, ClientOpts}]),
+ {node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, receive_msg, []}},
+ {options, ClientOpts}]),
{status, _, _, StatusInfo} = sys:get_status(Pid),
[_, _,_, _, Prop] = StatusInfo,
@@ -3892,6 +3914,32 @@ tcp_error_propagation_in_active_mode(Config) when is_list(Config) ->
Pid ! {tcp_error, Socket, etimedout},
ssl_test_lib:check_result(Client, {ssl_closed, SslSocket}).
+
+
+%%--------------------------------------------------------------------
+
+recv_error_handling(doc) ->
+ ["Special case of call error handling"];
+recv_error_handling(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_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, recv_close, []}},
+ {options, [{active, false} | ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+ {_Client, #sslsocket{} = SslSocket} = ssl_test_lib:start_client([return_socket,
+ {node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {ssl_test_lib, no_result, []}},
+ {options, ClientOpts}]),
+ ssl:close(SslSocket),
+ ssl_test_lib:check_result(Server, ok).
+
+
%%--------------------------------------------------------------------
rizzo(doc) -> ["Test that there is a 1/n-1-split for non RC4 in 'TLS < 1.1' as it is
@@ -3899,21 +3947,22 @@ rizzo(doc) -> ["Test that there is a 1/n-1-split for non RC4 in 'TLS < 1.1' as i
rizzo(Config) when is_list(Config) ->
Ciphers = [X || X ={_,Y,_} <- ssl:cipher_suites(), Y =/= rc4_128],
- run_send_recv_rizzo(Ciphers, Config, sslv3,
- {?MODULE, send_recv_result_active_rizzo, []}),
- run_send_recv_rizzo(Ciphers, Config, tlsv1,
+ Prop = ?config(tc_group_properties, Config),
+ Version = proplists:get_value(name, Prop),
+ run_send_recv_rizzo(Ciphers, Config, Version,
{?MODULE, send_recv_result_active_rizzo, []}).
-
+%%--------------------------------------------------------------------
no_rizzo_rc4(doc) ->
["Test that there is no 1/n-1-split for RC4 as it is not vunrable to Rizzo/Dungon attack"];
no_rizzo_rc4(Config) when is_list(Config) ->
Ciphers = [X || X ={_,Y,_} <- ssl:cipher_suites(),Y == rc4_128],
- run_send_recv_rizzo(Ciphers, Config, sslv3,
- {?MODULE, send_recv_result_active_no_rizzo, []}),
- run_send_recv_rizzo(Ciphers, Config, tlsv1,
+ Prop = ?config(tc_group_properties, Config),
+ Version = proplists:get_value(name, Prop),
+ run_send_recv_rizzo(Ciphers, Config, Version,
{?MODULE, send_recv_result_active_no_rizzo, []}).
+%%--------------------------------------------------------------------
run_send_recv_rizzo(Ciphers, Config, Version, Mfa) ->
Result = lists:map(fun(Cipher) ->
rizzo_test(Cipher, Config, Version, Mfa) end,
@@ -3967,6 +4016,15 @@ send_recv_result(Socket) ->
{ok,"Hello world"} = ssl:recv(Socket, 11),
ok.
+recv_close(Socket) ->
+ {error, closed} = ssl:recv(Socket, 11),
+ receive
+ {_,{error,closed}} ->
+ error_extra_close_sent_to_user_process
+ after 500 ->
+ ok
+ end.
+
send_recv_result_active(Socket) ->
ssl:send(Socket, "Hello world"),
receive
diff --git a/lib/ssl/test/ssl_cipher_SUITE.erl b/lib/ssl/test/ssl_cipher_SUITE.erl
index 99bc21e820..83beeb0131 100644
--- a/lib/ssl/test/ssl_cipher_SUITE.erl
+++ b/lib/ssl/test/ssl_cipher_SUITE.erl
@@ -27,6 +27,7 @@
-include("ssl_internal.hrl").
-include("ssl_record.hrl").
-include("ssl_cipher.hrl").
+-include("ssl_alert.hrl").
-define(TIMEOUT, 600000).
@@ -103,7 +104,7 @@ end_per_testcase(_TestCase, Config) ->
suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
- [aes_decipher_good, aes_decipher_fail].
+ [aes_decipher_good, aes_decipher_good_tls11, aes_decipher_fail, aes_decipher_fail_tls11].
groups() ->
[].
@@ -131,10 +132,39 @@ aes_decipher_good(Config) when is_list(Config) ->
190,162,74,31,186,227,119,155,94,74,119,79,169,193,240,160,
198,181,81,19,98,162,213,228,74,224,253,168,156,59,195,122,
108,101,107,242,20,15,169,150,163,107,101,94,93,104,241,165>>,
- Version = {3,3},
- Content = <<183,139,16,132,10,209,67,86,168,100,61,217,145,57,36,56,72,69,76,76,79,10>>,
+ Content = <<183,139,16,132,10,209,67,86,168,100,61,217,145,57,36,56, "HELLO\n">>,
Mac = <<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>>,
+ Version = {3,0},
{Content, Mac, _} = ssl_cipher:decipher(?AES, HashSz, CipherState, Fragment, Version),
+ Version1 = {3,1},
+ {Content, Mac, _} = ssl_cipher:decipher(?AES, HashSz, CipherState, Fragment, Version1),
+ ok.
+
+%%--------------------------------------------------------------------
+
+aes_decipher_good_tls11(doc) ->
+ ["Decipher a known TLS 1.1 cryptotext."];
+
+aes_decipher_good_tls11(suite) ->
+ [];
+
+%% the fragment is actuall a TLS 1.1 record, with
+%% Version = TLS 1.1, we get the correct NextIV in #cipher_state
+aes_decipher_good_tls11(Config) when is_list(Config) ->
+ HashSz = 32,
+ CipherState = #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,148>>},
+ Fragment = <<220,193,179,139,171,33,143,245,202,47,123,251,13,232,114,8,
+ 190,162,74,31,186,227,119,155,94,74,119,79,169,193,240,160,
+ 198,181,81,19,98,162,213,228,74,224,253,168,156,59,195,122,
+ 108,101,107,242,20,15,169,150,163,107,101,94,93,104,241,165>>,
+ Content = <<"HELLO\n">>,
+ NextIV = <<183,139,16,132,10,209,67,86,168,100,61,217,145,57,36,56>>,
+ Mac = <<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>>,
+ Version = {3,2},
+ {Content, Mac, #cipher_state{iv = NextIV}} = ssl_cipher:decipher(?AES, HashSz, CipherState, Fragment, Version),
+ Version1 = {3,2},
+ {Content, Mac, #cipher_state{iv = NextIV}} = ssl_cipher:decipher(?AES, HashSz, CipherState, Fragment, Version1),
ok.
%%--------------------------------------------------------------------
@@ -154,10 +184,38 @@ aes_decipher_fail(Config) when is_list(Config) ->
190,162,74,31,186,227,119,155,94,74,119,79,169,193,240,160,
198,181,81,19,98,162,213,228,74,224,253,168,156,59,195,122,
108,101,107,242,20,15,169,150,163,107,101,94,93,104,241,165>>,
- Version = {3,3},
+ Version = {3,0},
{Content, Mac, _} = ssl_cipher:decipher(?AES, HashSz, CipherState, Fragment, Version),
32 = byte_size(Content),
32 = byte_size(Mac),
+ Version1 = {3,1},
+ {Content1, Mac1, _} = ssl_cipher:decipher(?AES, HashSz, CipherState, Fragment, Version1),
+ 32 = byte_size(Content1),
+ 32 = byte_size(Mac1),
+ ok.
+
+%%--------------------------------------------------------------------
+
+aes_decipher_fail_tls11(doc) ->
+ ["Decipher a known TLS 1.1 cryptotext."];
+
+aes_decipher_fail_tls11(suite) ->
+ [];
+
+%% same as above, last byte of key replaced
+%% stricter padding checks in TLS 1.1 mean we get an alert instead
+aes_decipher_fail_tls11(Config) when is_list(Config) ->
+ HashSz = 32,
+ CipherState = #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>>},
+ Fragment = <<220,193,179,139,171,33,143,245,202,47,123,251,13,232,114,8,
+ 190,162,74,31,186,227,119,155,94,74,119,79,169,193,240,160,
+ 198,181,81,19,98,162,213,228,74,224,253,168,156,59,195,122,
+ 108,101,107,242,20,15,169,150,163,107,101,94,93,104,241,165>>,
+ Version = {3,2},
+ #alert{level = ?FATAL, description = ?BAD_RECORD_MAC} = ssl_cipher:decipher(?AES, HashSz, CipherState, Fragment, Version),
+ Version1 = {3,3},
+ #alert{level = ?FATAL, description = ?BAD_RECORD_MAC} = ssl_cipher:decipher(?AES, HashSz, CipherState, Fragment, Version1),
ok.
%%--------------------------------------------------------------------
diff --git a/lib/ssl/test/ssl_dist_SUITE.erl b/lib/ssl/test/ssl_dist_SUITE.erl
index 12d92c8ca1..818f7f1897 100644
--- a/lib/ssl/test/ssl_dist_SUITE.erl
+++ b/lib/ssl/test/ssl_dist_SUITE.erl
@@ -369,7 +369,7 @@ mk_node_cmdline(ListenPort, Name, Args) ->
_ -> "-name "
end,
{ok, Pwd} = file:get_cwd(),
- Prog ++ " "
+ "\"" ++ Prog ++ "\" "
++ Static ++ " "
++ NameSw ++ " " ++ Name ++ " "
++ "-pa " ++ Pa ++ " "
@@ -729,7 +729,7 @@ add_ssl_opts_config(Config) ->
[{ssl_opts, "-boot " ++ Script} | Config]
catch
_:_ ->
- [{ssl_opts, "-pa " ++ filename:dirname(code:which(ssl))}
+ [{ssl_opts, "-pa \"" ++ filename:dirname(code:which(ssl))++"\""}
| add_comment_config(
"Bootscript wasn't used since the test wasn't run on an "
"installed OTP system.",
diff --git a/lib/ssl/test/ssl_handshake_SUITE.erl b/lib/ssl/test/ssl_handshake_SUITE.erl
index 08c23b2d47..946865a3d8 100644
--- a/lib/ssl/test/ssl_handshake_SUITE.erl
+++ b/lib/ssl/test/ssl_handshake_SUITE.erl
@@ -48,7 +48,8 @@ decode_hello_handshake(_Config) ->
16#00, 16#00, 16#33, 16#74, 16#00, 16#07, 16#06, 16#73,
16#70, 16#64, 16#79, 16#2f, 16#32>>,
- {Records, _Buffer} = ssl_handshake:get_tls_handshake(HelloPacket, <<>>),
+ Version = {3, 0},
+ {Records, _Buffer} = ssl_handshake:get_tls_handshake(Version, HelloPacket, <<>>),
{Hello, _Data} = hd(Records),
#renegotiation_info{renegotiated_connection = <<0>>} = Hello#server_hello.renegotiation_info.
diff --git a/lib/ssl/test/ssl_packet_SUITE.erl b/lib/ssl/test/ssl_packet_SUITE.erl
index 4b74f57a60..8ce80cb725 100644
--- a/lib/ssl/test/ssl_packet_SUITE.erl
+++ b/lib/ssl/test/ssl_packet_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2012. 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 +53,7 @@
%% variable, but should NOT alter/remove any existing entries.
%%--------------------------------------------------------------------
init_per_suite(Config) ->
+ catch crypto:stop(),
try crypto:start() of
ok ->
application:start(public_key),
@@ -121,15 +122,56 @@ end_per_testcase(_TestCase, Config) ->
suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
+ [
+ {group, 'tlsv1.2'},
+ {group, 'tlsv1.1'},
+ {group, 'tlsv1'},
+ {group, 'sslv3'}
+ ].
+
+groups() ->
+ [{'tlsv1.2', [], packet_tests()},
+ {'tlsv1.1', [], packet_tests()},
+ {'tlsv1', [], packet_tests()},
+ {'sslv3', [], packet_tests()}].
+
+packet_tests() ->
+ active_packet_tests() ++ active_once_packet_tests() ++ passive_packet_tests() ++
+ [packet_send_to_large,
+ packet_cdr_decode, packet_cdr_decode_list,
+ packet_http_decode, packet_http_decode_list,
+ packet_http_bin_decode_multi,
+ packet_line_decode, packet_line_decode_list,
+ packet_asn1_decode, packet_asn1_decode_list,
+ packet_tpkt_decode, packet_tpkt_decode_list,
+ packet_sunrm_decode, packet_sunrm_decode_list].
+
+passive_packet_tests() ->
[packet_raw_passive_many_small,
packet_0_passive_many_small,
packet_1_passive_many_small,
packet_2_passive_many_small,
packet_4_passive_many_small,
- packet_raw_passive_some_big, packet_0_passive_some_big,
- packet_1_passive_some_big, packet_2_passive_some_big,
+ packet_raw_passive_some_big,
+ packet_0_passive_some_big,
+ packet_1_passive_some_big,
+ packet_2_passive_some_big,
packet_4_passive_some_big,
- packet_raw_active_once_many_small,
+ packet_httph_passive,
+ packet_httph_bin_passive,
+ packet_http_error_passive,
+ packet_wait_passive,
+ packet_size_passive,
+ packet_baddata_passive,
+ %% inet header option should be deprecated!
+ header_decode_one_byte_passive,
+ header_decode_two_bytes_passive,
+ header_decode_two_bytes_two_sent_passive,
+ header_decode_two_bytes_one_sent_passive
+ ].
+
+active_once_packet_tests() ->
+ [packet_raw_active_once_many_small,
packet_0_active_once_many_small,
packet_1_active_once_many_small,
packet_2_active_once_many_small,
@@ -139,44 +181,49 @@ all() ->
packet_1_active_once_some_big,
packet_2_active_once_some_big,
packet_4_active_once_some_big,
- packet_raw_active_many_small,
- packet_0_active_many_small, packet_1_active_many_small,
- packet_2_active_many_small, packet_4_active_many_small,
- packet_raw_active_some_big, packet_0_active_some_big,
- packet_1_active_some_big, packet_2_active_some_big,
- packet_4_active_some_big, packet_send_to_large,
- packet_wait_passive, packet_wait_active,
- packet_baddata_passive, packet_baddata_active,
- packet_size_passive, packet_size_active,
- packet_cdr_decode, packet_cdr_decode_list,
- packet_http_decode, packet_http_decode_list,
- packet_http_bin_decode_multi, packet_http_error_passive,
- packet_httph_active, packet_httph_bin_active,
- packet_httph_active_once, packet_httph_bin_active_once,
- packet_httph_passive, packet_httph_bin_passive,
- packet_line_decode, packet_line_decode_list,
- packet_asn1_decode, packet_asn1_decode_list,
- packet_tpkt_decode, packet_tpkt_decode_list,
- packet_sunrm_decode, packet_sunrm_decode_list,
- {group, header}
+ packet_httph_active_once,
+ packet_httph_bin_active_once
].
-groups() ->
- [{header, [], [ header_decode_one_byte,
- header_decode_two_bytes,
- header_decode_two_bytes_one_sent,
- header_decode_two_bytes_two_sent]}].
+active_packet_tests() ->
+ [packet_raw_active_many_small,
+ packet_0_active_many_small,
+ packet_1_active_many_small,
+ packet_2_active_many_small,
+ packet_4_active_many_small,
+ packet_raw_active_some_big,
+ packet_0_active_some_big,
+ packet_1_active_some_big,
+ packet_2_active_some_big,
+ packet_4_active_some_big,
+ packet_httph_active,
+ packet_httph_bin_active,
+ packet_wait_active,
+ packet_baddata_active,
+ packet_size_active,
+ %% inet header option should be deprecated!
+ header_decode_one_byte_active,
+ header_decode_two_bytes_active,
+ header_decode_two_bytes_two_sent_active,
+ header_decode_two_bytes_one_sent_active
+ ].
-init_per_group(header, Config) ->
- case ssl_record:highest_protocol_version(ssl_record:supported_protocol_versions()) of
- {3, N} when N < 2 ->
- {skip, ""};
+
+init_per_group(GroupName, Config) ->
+ case ssl_test_lib:is_tls_version(GroupName) of
+ true ->
+ case ssl_test_lib:sufficient_crypto_support(GroupName) of
+ true ->
+ ssl_test_lib:init_tls_version(GroupName),
+ Config;
+ false ->
+ {skip, "Missing crypto support"}
+ end;
_ ->
+ ssl:start(),
Config
- end;
+ end.
-init_per_group(_, Config) ->
- Config.
end_per_group(_GroupName, Config) ->
Config.
@@ -2435,11 +2482,11 @@ packet_sunrm_decode_list(Config) when is_list(Config) ->
ssl_test_lib:close(Client).
%%--------------------------------------------------------------------
-header_decode_one_byte(doc) ->
+header_decode_one_byte_active(doc) ->
["Test setting the packet option {header, 1}"];
-header_decode_one_byte(suite) ->
+header_decode_one_byte_active(suite) ->
[];
-header_decode_one_byte(Config) when is_list(Config) ->
+header_decode_one_byte_active(Config) when is_list(Config) ->
ClientOpts = ?config(client_opts, Config),
ServerOpts = ?config(server_opts, Config),
{ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
@@ -2448,7 +2495,7 @@ header_decode_one_byte(Config) when is_list(Config) ->
Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0},
{from, self()},
- {mfa, {?MODULE, server_header_decode,
+ {mfa, {?MODULE, server_header_decode_active,
[Data, [11 | <<"Hello world">>]]}},
{options, [{active, true}, binary,
{header,1}|ServerOpts]}]),
@@ -2457,7 +2504,7 @@ header_decode_one_byte(Config) when is_list(Config) ->
Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port},
{host, Hostname},
{from, self()},
- {mfa, {?MODULE, client_header_decode,
+ {mfa, {?MODULE, client_header_decode_active,
[Data, [11 | <<"Hello world">> ]]}},
{options, [{active, true}, {header, 1},
binary | ClientOpts]}]),
@@ -2469,11 +2516,11 @@ header_decode_one_byte(Config) when is_list(Config) ->
%%--------------------------------------------------------------------
-header_decode_two_bytes(doc) ->
+header_decode_two_bytes_active(doc) ->
["Test setting the packet option {header, 2}"];
-header_decode_two_bytes(suite) ->
+header_decode_two_bytes_active(suite) ->
[];
-header_decode_two_bytes(Config) when is_list(Config) ->
+header_decode_two_bytes_active(Config) when is_list(Config) ->
ClientOpts = ?config(client_opts, Config),
ServerOpts = ?config(server_opts, Config),
{ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
@@ -2482,7 +2529,7 @@ header_decode_two_bytes(Config) when is_list(Config) ->
Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0},
{from, self()},
- {mfa, {?MODULE, server_header_decode,
+ {mfa, {?MODULE, server_header_decode_active,
[Data, [11, $H | <<"ello world">> ]]}},
{options, [{active, true}, binary,
{header,2}|ServerOpts]}]),
@@ -2491,7 +2538,7 @@ header_decode_two_bytes(Config) when is_list(Config) ->
Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port},
{host, Hostname},
{from, self()},
- {mfa, {?MODULE, client_header_decode,
+ {mfa, {?MODULE, client_header_decode_active,
[Data, [11, $H | <<"ello world">> ]]}},
{options, [{active, true}, {header, 2},
binary | ClientOpts]}]),
@@ -2504,11 +2551,11 @@ header_decode_two_bytes(Config) when is_list(Config) ->
%%--------------------------------------------------------------------
-header_decode_two_bytes_two_sent(doc) ->
- ["Test setting the packet option {header, 2} and sending on byte"];
-header_decode_two_bytes_two_sent(suite) ->
+header_decode_two_bytes_two_sent_active(doc) ->
+ ["Test setting the packet option {header, 2} and sending two byte"];
+header_decode_two_bytes_two_sent_active(suite) ->
[];
-header_decode_two_bytes_two_sent(Config) when is_list(Config) ->
+header_decode_two_bytes_two_sent_active(Config) when is_list(Config) ->
ClientOpts = ?config(client_opts, Config),
ServerOpts = ?config(server_opts, Config),
{ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
@@ -2517,8 +2564,8 @@ header_decode_two_bytes_two_sent(Config) when is_list(Config) ->
Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0},
{from, self()},
- {mfa, {?MODULE, server_header_decode,
- [Data, [$H, $e | <<>> ]]}},
+ {mfa, {?MODULE, server_header_decode_active,
+ [Data, [$H, $e]]}},
{options, [{active, true}, binary,
{header,2}|ServerOpts]}]),
@@ -2526,8 +2573,8 @@ header_decode_two_bytes_two_sent(Config) when is_list(Config) ->
Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port},
{host, Hostname},
{from, self()},
- {mfa, {?MODULE, client_header_decode,
- [Data, [$H, $e | <<>> ]]}},
+ {mfa, {?MODULE, client_header_decode_active,
+ [Data, [$H, $e]]}},
{options, [{active, true}, {header, 2},
binary | ClientOpts]}]),
@@ -2539,11 +2586,11 @@ header_decode_two_bytes_two_sent(Config) when is_list(Config) ->
%%--------------------------------------------------------------------
-header_decode_two_bytes_one_sent(doc) ->
- ["Test setting the packet option {header, 2} and sending on byte"];
-header_decode_two_bytes_one_sent(suite) ->
+header_decode_two_bytes_one_sent_active(doc) ->
+ ["Test setting the packet option {header, 2} and sending one byte"];
+header_decode_two_bytes_one_sent_active(suite) ->
[];
-header_decode_two_bytes_one_sent(Config) when is_list(Config) ->
+header_decode_two_bytes_one_sent_active(Config) when is_list(Config) ->
ClientOpts = ?config(client_opts, Config),
ServerOpts = ?config(server_opts, Config),
{ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
@@ -2552,7 +2599,7 @@ header_decode_two_bytes_one_sent(Config) when is_list(Config) ->
Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0},
{from, self()},
- {mfa, {?MODULE, server_header_decode,
+ {mfa, {?MODULE, server_header_decode_active,
[Data, "H"]}},
{options, [{active, true}, binary,
{header,2}|ServerOpts]}]),
@@ -2561,7 +2608,7 @@ header_decode_two_bytes_one_sent(Config) when is_list(Config) ->
Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port},
{host, Hostname},
{from, self()},
- {mfa, {?MODULE, client_header_decode,
+ {mfa, {?MODULE, client_header_decode_active,
[Data, "H"]}},
{options, [{active, true}, {header, 2},
binary | ClientOpts]}]),
@@ -2571,6 +2618,143 @@ header_decode_two_bytes_one_sent(Config) when is_list(Config) ->
ssl_test_lib:close(Server),
ssl_test_lib:close(Client).
+%%--------------------------------------------------------------------
+
+header_decode_one_byte_passive(doc) ->
+ ["Test setting the packet option {header, 1}"];
+header_decode_one_byte_passive(suite) ->
+ [];
+header_decode_one_byte_passive(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Data = <<11:8, "Hello world">>,
+
+ Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, server_header_decode_passive,
+ [Data, [11 | <<"Hello world">>]]}},
+ {options, [{active, false}, binary,
+ {header,1}|ServerOpts]}]),
+
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, client_header_decode_passive,
+ [Data, [11 | <<"Hello world">> ]]}},
+ {options, [{active, false}, {header, 1},
+ binary | ClientOpts]}]),
+
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+%%--------------------------------------------------------------------
+
+header_decode_two_bytes_passive(doc) ->
+ ["Test setting the packet option {header, 2}"];
+header_decode_two_bytes_passive(suite) ->
+ [];
+header_decode_two_bytes_passive(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Data = <<11:8, "Hello world">>,
+
+ Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, server_header_decode_passive,
+ [Data, [11, $H | <<"ello world">> ]]}},
+ {options, [{active, false}, binary,
+ {header,2}|ServerOpts]}]),
+
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, client_header_decode_passive,
+ [Data, [11, $H | <<"ello world">> ]]}},
+ {options, [{active, false}, {header, 2},
+ binary | ClientOpts]}]),
+
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+
+%%--------------------------------------------------------------------
+
+header_decode_two_bytes_two_sent_passive(doc) ->
+ ["Test setting the packet option {header, 2} and sending two byte"];
+header_decode_two_bytes_two_sent_passive(suite) ->
+ [];
+header_decode_two_bytes_two_sent_passive(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Data = <<"He">>,
+
+ Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, server_header_decode_passive,
+ [Data, [$H, $e]]}},
+ {options, [{active, false}, binary,
+ {header,2}|ServerOpts]}]),
+
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, client_header_decode_passive,
+ [Data, [$H, $e]]}},
+ {options, [{active, false}, {header, 2},
+ binary | ClientOpts]}]),
+
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+
+%%--------------------------------------------------------------------
+
+header_decode_two_bytes_one_sent_passive(doc) ->
+ ["Test setting the packet option {header, 2} and sending one byte"];
+header_decode_two_bytes_one_sent_passive(suite) ->
+ [];
+header_decode_two_bytes_one_sent_passive(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Data = <<"H">>,
+
+ Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, server_header_decode_passive,
+ [Data, "H"]}},
+ {options, [{active, false}, binary,
+ {header,2}|ServerOpts]}]),
+
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, client_header_decode_passive,
+ [Data, "H"]}},
+ {options, [{active, false}, {header, 2},
+ binary | ClientOpts]}]),
+
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
%%--------------------------------------------------------------------
%% Internal functions
@@ -2757,29 +2941,52 @@ client_packet_decode(Socket, P1, P2, Packet) ->
Other2 -> exit({?LINE, Other2})
end.
-server_header_decode(Socket, Packet, Result) ->
+server_header_decode_active(Socket, Packet, Result) ->
receive
- {ssl, Socket, Result} -> ok;
- Other1 -> exit({?LINE, Other1})
- end,
- ok = ssl:send(Socket, Packet),
- receive
- {ssl, Socket, Result} -> ok;
- Other2 -> exit({?LINE, Other2})
+ {ssl, Socket, Result} ->
+ ok;
+ {ssl, Socket, Other1} ->
+ check_header_result(Result, Other1)
end,
ok = ssl:send(Socket, Packet).
-client_header_decode(Socket, Packet, Result) ->
+client_header_decode_active(Socket, Packet, Result) ->
ok = ssl:send(Socket, Packet),
receive
- {ssl, Socket, Result} -> ok;
- Other1 -> exit({?LINE, Other1})
+ {ssl, Socket, Result} ->
+ ok;
+ {ssl, Socket, Other1} ->
+ check_header_result(Result, Other1)
+ end.
+
+server_header_decode_passive(Socket, Packet, Result) ->
+ case ssl:recv(Socket, 0) of
+ {ok, Result} ->
+ ok;
+ {ok, Other} ->
+ check_header_result(Result, Other)
end,
+ ok = ssl:send(Socket, Packet).
+
+client_header_decode_passive(Socket, Packet, Result) ->
ok = ssl:send(Socket, Packet),
- receive
- {ssl, Socket, Result} -> ok;
- Other2 -> exit({?LINE, Other2})
+
+ case ssl:recv(Socket, 0) of
+ {ok, Result} ->
+ ok;
+ {ok, Other} ->
+ check_header_result(Result, Other)
end.
+
+%% The inet header option is a broken option as it does not buffer until it gets enough data.
+%% This check only checks that it has the same behavior as inet, but it is a quite useless
+%% option and the bitsynax makes it obsolete!
+check_header_result([Byte1 | _], [Byte1]) ->
+ ok;
+check_header_result([Byte1, Byte2 | _], [Byte1, Byte2]) ->
+ ok;
+check_header_result(_,Got) ->
+ exit({?LINE, Got}).
server_line_packet_decode(Socket, Packet) when is_binary(Packet) ->
[L1, L2] = string:tokens(binary_to_list(Packet), "\n"),
diff --git a/lib/ssl/test/ssl_payload_SUITE.erl b/lib/ssl/test/ssl_payload_SUITE.erl
index 24e86b3913..c97f97e70b 100644
--- a/lib/ssl/test/ssl_payload_SUITE.erl
+++ b/lib/ssl/test/ssl_payload_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2012. 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,6 +37,7 @@
%% variable, but should NOT alter/remove any existing entries.
%%--------------------------------------------------------------------
init_per_suite(Config) ->
+ catch crypto:stop(),
try crypto:start() of
ok ->
application:start(public_key),
@@ -102,23 +103,56 @@ end_per_testcase(_TestCase, Config) ->
suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
+ [
+ {group, 'tlsv1.2'},
+ {group, 'tlsv1.1'},
+ {group, 'tlsv1'},
+ {group, 'sslv3'}
+ ].
+
+groups() ->
+ [
+ {'tlsv1.2', [], payload_tests()},
+ {'tlsv1.1', [], payload_tests()},
+ {'tlsv1', [], payload_tests()},
+ {'sslv3', [], payload_tests()}
+ ].
+
+payload_tests() ->
[server_echos_passive_small,
server_echos_active_once_small,
- server_echos_active_small, client_echos_passive_small,
+ server_echos_active_small,
+ client_echos_passive_small,
client_echos_active_once_small,
- client_echos_active_small, server_echos_passive_big,
- server_echos_active_once_big, server_echos_active_big,
- client_echos_passive_big, client_echos_active_once_big,
- client_echos_active_big, server_echos_passive_huge,
- server_echos_active_once_huge, server_echos_active_huge,
+ client_echos_active_small,
+ server_echos_passive_big,
+ server_echos_active_once_big,
+ server_echos_active_big,
+ client_echos_passive_big,
+ client_echos_active_once_big,
+ client_echos_active_big,
+ server_echos_passive_huge,
+ server_echos_active_once_huge,
+ server_echos_active_huge,
client_echos_passive_huge,
- client_echos_active_once_huge, client_echos_active_huge].
+ client_echos_active_once_huge,
+ client_echos_active_huge].
-groups() ->
- [].
-init_per_group(_GroupName, Config) ->
- Config.
+init_per_group(GroupName, Config) ->
+ case ssl_test_lib:is_tls_version(GroupName) of
+ true ->
+ case ssl_test_lib:sufficient_crypto_support(GroupName) of
+ true ->
+ ssl_test_lib:init_tls_version(GroupName),
+ Config;
+ false ->
+ {skip, "Missing crypto support"}
+ end;
+ _ ->
+ ssl:start(),
+ Config
+ end.
end_per_group(_GroupName, Config) ->
Config.
diff --git a/lib/ssl/test/ssl_session_cache_SUITE.erl b/lib/ssl/test/ssl_session_cache_SUITE.erl
index 491aa893c2..6d758ecb01 100644
--- a/lib/ssl/test/ssl_session_cache_SUITE.erl
+++ b/lib/ssl/test/ssl_session_cache_SUITE.erl
@@ -49,6 +49,7 @@
%%--------------------------------------------------------------------
init_per_suite(Config0) ->
Dog = ssl_test_lib:timetrap(?LONG_TIMEOUT *2),
+ catch crypto:stop(),
try crypto:start() of
ok ->
application:start(public_key),
diff --git a/lib/ssl/test/ssl_test_lib.erl b/lib/ssl/test/ssl_test_lib.erl
index fa8a1826f2..63731ee25c 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-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2012. 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
@@ -150,7 +150,7 @@ run_client(Opts) ->
{ok, Socket} ->
Pid ! { connected, Socket },
test_server:format("Client: connected~n", []),
- %% In specail cases we want to know the client port, it will
+ %% In special cases we want to know the client port, it will
%% be indicated by sending {port, 0} in options list!
send_selected_port(Pid, proplists:get_value(port, Options), Socket),
{Module, Function, Args} = proplists:get_value(mfa, Opts),
@@ -708,3 +708,33 @@ state([{data,[{"StateData", State}]} | _]) ->
State;
state([_ | Rest]) ->
state(Rest).
+
+is_tls_version('tlsv1.2') ->
+ true;
+is_tls_version('tlsv1.1') ->
+ true;
+is_tls_version('tlsv1') ->
+ true;
+is_tls_version('sslv3') ->
+ true;
+is_tls_version(_) ->
+ false.
+
+init_tls_version(Version) ->
+ ssl:stop(),
+ application:load(ssl),
+ application:set_env(ssl, protocol_version, Version),
+ ssl:start().
+
+sufficient_crypto_support('tlsv1.2') ->
+ Data = "Sampl",
+ Data2 = "e #1",
+ Key = <<0,1,2,3,16,17,18,19,32,33,34,35,48,49,50,51,4,5,6,7,20,21,22,23,36,37,38,39,
+ 52,53,54,55,8,9,10,11,24,25,26,27,40,41,42,43,56,57,58,59>>,
+ try
+ crypto:sha256_mac(Key, lists:flatten([Data, Data2])),
+ true
+ catch _:_ -> false
+ end;
+sufficient_crypto_support(_) ->
+ true.
diff --git a/lib/ssl/test/ssl_to_openssl_SUITE.erl b/lib/ssl/test/ssl_to_openssl_SUITE.erl
index 01fca1f166..d446014f7b 100644
--- a/lib/ssl/test/ssl_to_openssl_SUITE.erl
+++ b/lib/ssl/test/ssl_to_openssl_SUITE.erl
@@ -50,6 +50,7 @@ init_per_suite(Config0) ->
false ->
{skip, "Openssl not found"};
_ ->
+ catch crypto:stop(),
try crypto:start() of
ok ->
application:start(public_key),
@@ -106,7 +107,8 @@ init_per_testcase(TestCase, Config0) ->
special_init(TestCase, Config)
when TestCase == erlang_client_openssl_server_renegotiate;
TestCase == erlang_client_openssl_server_no_wrap_sequence_number;
- TestCase == erlang_server_openssl_client_no_wrap_sequence_number ->
+ TestCase == erlang_server_openssl_client_no_wrap_sequence_number
+ ->
check_sane_openssl_renegotaite(Config);
special_init(ssl2_erlang_server_openssl_client, Config) ->
@@ -149,37 +151,59 @@ end_per_testcase(_, Config) ->
suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
- [erlang_client_openssl_server,
+ [
+ {group, basic},
+ {group, 'tlsv1.2'},
+ {group, 'tlsv1.1'},
+ {group, 'tlsv1'},
+ {group, 'sslv3'}
+ ].
+
+groups() ->
+ [{basic, [], basic_tests()},
+ {'tlsv1.2', [], all_versions_tests()},
+ {'tlsv1.1', [], all_versions_tests()},
+ {'tlsv1', [], all_versions_tests()},
+ {'sslv3', [], all_versions_tests()}].
+
+basic_tests() ->
+ [basic_erlang_client_openssl_server,
+ basic_erlang_server_openssl_client,
+ expired_session].
+
+all_versions_tests() ->
+ [
+ erlang_client_openssl_server,
erlang_server_openssl_client,
- tls1_erlang_client_openssl_server_dsa_cert,
- tls1_erlang_server_openssl_client_dsa_cert,
- ssl3_erlang_client_openssl_server_dsa_cert,
- ssl3_erlang_server_openssl_client_dsa_cert,
+ erlang_client_openssl_server_dsa_cert,
+ erlang_server_openssl_client_dsa_cert,
erlang_server_openssl_client_reuse_session,
erlang_client_openssl_server_renegotiate,
erlang_client_openssl_server_no_wrap_sequence_number,
erlang_server_openssl_client_no_wrap_sequence_number,
erlang_client_openssl_server_no_server_ca_cert,
- ssl3_erlang_client_openssl_server,
- ssl3_erlang_server_openssl_client,
- ssl3_erlang_client_openssl_server_client_cert,
- ssl3_erlang_server_openssl_client_client_cert,
- ssl3_erlang_server_erlang_client_client_cert,
- tls1_erlang_client_openssl_server,
- tls1_erlang_server_openssl_client,
- tls1_erlang_client_openssl_server_client_cert,
- tls1_erlang_server_openssl_client_client_cert,
- tls1_erlang_server_erlang_client_client_cert,
- ciphers_rsa_signed_certs, ciphers_dsa_signed_certs,
+ erlang_client_openssl_server_client_cert,
+ erlang_server_openssl_client_client_cert,
+ ciphers_rsa_signed_certs,
+ ciphers_dsa_signed_certs,
erlang_client_bad_openssl_server,
- expired_session,
- ssl2_erlang_server_openssl_client].
+ ssl2_erlang_server_openssl_client
+ ].
-groups() ->
- [].
-
-init_per_group(_GroupName, Config) ->
- Config.
+init_per_group(GroupName, Config) ->
+ case ssl_test_lib:is_tls_version(GroupName) of
+ true ->
+ case check_sane_openssl_version(GroupName) of
+ true ->
+ ssl_test_lib:init_tls_version(GroupName),
+ Config;
+ false ->
+ {skip, openssl_does_not_support_version}
+ end;
+ _ ->
+ ssl:start(),
+ Config
+ end.
end_per_group(_GroupName, Config) ->
Config.
@@ -187,12 +211,11 @@ end_per_group(_GroupName, Config) ->
%% Test cases starts here.
%%--------------------------------------------------------------------
-
-erlang_client_openssl_server(doc) ->
+basic_erlang_client_openssl_server(doc) ->
["Test erlang client with openssl server"];
-erlang_client_openssl_server(suite) ->
+basic_erlang_client_openssl_server(suite) ->
[];
-erlang_client_openssl_server(Config) when is_list(Config) ->
+basic_erlang_client_openssl_server(Config) when is_list(Config) ->
process_flag(trap_exit, true),
ServerOpts = ?config(server_opts, Config),
ClientOpts = ?config(client_opts, Config),
@@ -204,8 +227,8 @@ erlang_client_openssl_server(Config) when is_list(Config) ->
Port = ssl_test_lib:inet_port(node()),
CertFile = proplists:get_value(certfile, ServerOpts),
KeyFile = proplists:get_value(keyfile, ServerOpts),
-
- Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++
+
+ Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++
" -cert " ++ CertFile ++ " -key " ++ KeyFile,
test_server:format("openssl cmd: ~p~n", [Cmd]),
@@ -230,13 +253,12 @@ erlang_client_openssl_server(Config) when is_list(Config) ->
process_flag(trap_exit, false),
ok.
-
%%--------------------------------------------------------------------
-erlang_server_openssl_client(doc) ->
+basic_erlang_server_openssl_client(doc) ->
["Test erlang server with openssl client"];
-erlang_server_openssl_client(suite) ->
+basic_erlang_server_openssl_client(suite) ->
[];
-erlang_server_openssl_client(Config) when is_list(Config) ->
+basic_erlang_server_openssl_client(Config) when is_list(Config) ->
process_flag(trap_exit, true),
ServerOpts = ?config(server_opts, Config),
@@ -249,8 +271,8 @@ erlang_server_openssl_client(Config) when is_list(Config) ->
{mfa, {?MODULE, erlang_ssl_receive, [Data]}},
{options, ServerOpts}]),
Port = ssl_test_lib:inet_port(Server),
-
- Cmd = "openssl s_client -port " ++ integer_to_list(Port) ++
+
+ Cmd = "openssl s_client -port " ++ integer_to_list(Port) ++
" -host localhost",
test_server:format("openssl cmd: ~p~n", [Cmd]),
@@ -265,30 +287,26 @@ erlang_server_openssl_client(Config) when is_list(Config) ->
close_port(OpenSslPort),
process_flag(trap_exit, false),
ok.
-
-%%--------------------------------------------------------------------
-
-tls1_erlang_client_openssl_server_dsa_cert(doc) ->
- ["Test erlang server with openssl client"];
-tls1_erlang_client_openssl_server_dsa_cert(suite) ->
+%%--------------------------------------------------------------------
+erlang_client_openssl_server(doc) ->
+ ["Test erlang client with openssl server"];
+erlang_client_openssl_server(suite) ->
[];
-tls1_erlang_client_openssl_server_dsa_cert(Config) when is_list(Config) ->
+erlang_client_openssl_server(Config) when is_list(Config) ->
process_flag(trap_exit, true),
- ClientOpts = ?config(client_dsa_opts, Config),
- ServerOpts = ?config(server_dsa_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ ClientOpts = ?config(client_opts, Config),
{ClientNode, _, Hostname} = ssl_test_lib:run_where(Config),
-
+
Data = "From openssl to erlang",
Port = ssl_test_lib:inet_port(node()),
- CaCertFile = proplists:get_value(cacertfile, ServerOpts),
CertFile = proplists:get_value(certfile, ServerOpts),
KeyFile = proplists:get_value(keyfile, ServerOpts),
-
- Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++
- " -cert " ++ CertFile ++ " -CAfile " ++ CaCertFile
- ++ " -key " ++ KeyFile ++ " -Verify 2 -tls1 -msg",
+ Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])),
+ Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++ version_flag(Version) ++
+ " -cert " ++ CertFile ++ " -key " ++ KeyFile,
test_server:format("openssl cmd: ~p~n", [Cmd]),
@@ -302,44 +320,39 @@ tls1_erlang_client_openssl_server_dsa_cert(Config) when is_list(Config) ->
{mfa, {?MODULE,
erlang_ssl_receive, [Data]}},
{options, ClientOpts}]),
-
port_command(OpensslPort, Data),
- ssl_test_lib:check_result(Client, ok),
-
+ ssl_test_lib:check_result(Client, ok),
+
%% Clean close down! Server needs to be closed first !!
close_port(OpensslPort),
ssl_test_lib:close(Client),
process_flag(trap_exit, false),
ok.
-%%--------------------------------------------------------------------
-tls1_erlang_server_openssl_client_dsa_cert(doc) ->
+%%--------------------------------------------------------------------
+erlang_server_openssl_client(doc) ->
["Test erlang server with openssl client"];
-tls1_erlang_server_openssl_client_dsa_cert(suite) ->
+erlang_server_openssl_client(suite) ->
[];
-tls1_erlang_server_openssl_client_dsa_cert(Config) when is_list(Config) ->
+erlang_server_openssl_client(Config) when is_list(Config) ->
process_flag(trap_exit, true),
- ClientOpts = ?config(client_dsa_opts, Config),
- ServerOpts = ?config(server_dsa_verify_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
{_, ServerNode, _} = ssl_test_lib:run_where(Config),
Data = "From openssl to erlang",
- CaCertFile = proplists:get_value(cacertfile, ClientOpts),
- CertFile = proplists:get_value(certfile, ClientOpts),
- KeyFile = proplists:get_value(keyfile, ClientOpts),
-
+
Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
{from, self()},
{mfa, {?MODULE, erlang_ssl_receive, [Data]}},
{options, ServerOpts}]),
Port = ssl_test_lib:inet_port(Server),
-
- Cmd = "openssl s_client -port " ++ integer_to_list(Port) ++
- " -host localhost " ++ " -cert " ++ CertFile ++ " -CAfile " ++ CaCertFile
- ++ " -key " ++ KeyFile ++ " -tls1 -msg",
+ Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])),
+
+ Cmd = "openssl s_client -port " ++ integer_to_list(Port) ++ version_flag(Version) ++
+ " -host localhost",
test_server:format("openssl cmd: ~p~n", [Cmd]),
@@ -347,7 +360,7 @@ tls1_erlang_server_openssl_client_dsa_cert(Config) when is_list(Config) ->
port_command(OpenSslPort, Data),
ssl_test_lib:check_result(Server, ok),
-
+
%% Clean close down! Server needs to be closed first !!
ssl_test_lib:close(Server),
close_port(OpenSslPort),
@@ -356,11 +369,11 @@ tls1_erlang_server_openssl_client_dsa_cert(Config) when is_list(Config) ->
%%--------------------------------------------------------------------
-ssl3_erlang_client_openssl_server_dsa_cert(doc) ->
+erlang_client_openssl_server_dsa_cert(doc) ->
["Test erlang server with openssl client"];
-ssl3_erlang_client_openssl_server_dsa_cert(suite) ->
+erlang_client_openssl_server_dsa_cert(suite) ->
[];
-ssl3_erlang_client_openssl_server_dsa_cert(Config) when is_list(Config) ->
+erlang_client_openssl_server_dsa_cert(Config) when is_list(Config) ->
process_flag(trap_exit, true),
ClientOpts = ?config(client_dsa_opts, Config),
ServerOpts = ?config(server_dsa_opts, Config),
@@ -373,10 +386,11 @@ ssl3_erlang_client_openssl_server_dsa_cert(Config) when is_list(Config) ->
CaCertFile = proplists:get_value(cacertfile, ServerOpts),
CertFile = proplists:get_value(certfile, ServerOpts),
KeyFile = proplists:get_value(keyfile, ServerOpts),
-
- Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++
+ Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])),
+
+ Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++ version_flag(Version) ++
" -cert " ++ CertFile ++ " -CAfile " ++ CaCertFile
- ++ " -key " ++ KeyFile ++ " -Verify 2 -ssl3 -msg",
+ ++ " -key " ++ KeyFile ++ " -Verify 2 -msg",
test_server:format("openssl cmd: ~p~n", [Cmd]),
@@ -400,49 +414,46 @@ ssl3_erlang_client_openssl_server_dsa_cert(Config) when is_list(Config) ->
ssl_test_lib:close(Client),
process_flag(trap_exit, false),
ok.
-
-%%--------------------------------------------------------------------
-
-ssl3_erlang_server_openssl_client_dsa_cert(doc) ->
+%%--------------------------------------------------------------------
+erlang_server_openssl_client_dsa_cert(doc) ->
["Test erlang server with openssl client"];
-ssl3_erlang_server_openssl_client_dsa_cert(suite) ->
+erlang_server_openssl_client_dsa_cert(suite) ->
[];
-ssl3_erlang_server_openssl_client_dsa_cert(Config) when is_list(Config) ->
+erlang_server_openssl_client_dsa_cert(Config) when is_list(Config) ->
process_flag(trap_exit, true),
ClientOpts = ?config(client_dsa_opts, Config),
- ServerOpts = ?config(server_dsa_verify_opts, Config),
+ ServerOpts = ?config(server_dsa_verify_opts, Config),
{_, ServerNode, _} = ssl_test_lib:run_where(Config),
-
+
Data = "From openssl to erlang",
CaCertFile = proplists:get_value(cacertfile, ClientOpts),
CertFile = proplists:get_value(certfile, ClientOpts),
KeyFile = proplists:get_value(keyfile, ClientOpts),
-
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {?MODULE, erlang_ssl_receive, [Data]}},
- {options, ServerOpts}]),
+
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, erlang_ssl_receive, [Data]}},
+ {options, ServerOpts}]),
Port = ssl_test_lib:inet_port(Server),
-
- Cmd = "openssl s_client -port " ++ integer_to_list(Port) ++
- " -host localhost " ++ " -cert " ++ CertFile ++ " -CAfile " ++ CaCertFile
- ++ " -key " ++ KeyFile ++ " -ssl3 -msg",
+ Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])),
+ Cmd = "openssl s_client -port " ++ integer_to_list(Port) ++ version_flag(Version) ++
+ " -host localhost " ++ " -cert " ++ CertFile ++ " -CAfile " ++ CaCertFile
+ ++ " -key " ++ KeyFile ++ " -msg",
test_server:format("openssl cmd: ~p~n", [Cmd]),
-
- OpenSslPort = open_port({spawn, Cmd}, [stderr_to_stdout]),
+
+ OpenSslPort = open_port({spawn, Cmd}, [stderr_to_stdout]),
port_command(OpenSslPort, Data),
-
+
ssl_test_lib:check_result(Server, ok),
-
+
%% Clean close down! Server needs to be closed first !!
ssl_test_lib:close(Server),
close_port(OpenSslPort),
process_flag(trap_exit, false),
ok.
-
%%--------------------------------------------------------------------
erlang_server_openssl_client_reuse_session(doc) ->
@@ -464,8 +475,8 @@ erlang_server_openssl_client_reuse_session(Config) when is_list(Config) ->
{reconnect_times, 5},
{options, ServerOpts}]),
Port = ssl_test_lib:inet_port(Server),
-
- Cmd = "openssl s_client -port " ++ integer_to_list(Port) ++
+ Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])),
+ Cmd = "openssl s_client -port " ++ integer_to_list(Port) ++ version_flag(Version) ++
" -host localhost -reconnect",
test_server:format("openssl cmd: ~p~n", [Cmd]),
@@ -501,8 +512,9 @@ erlang_client_openssl_server_renegotiate(Config) when is_list(Config) ->
Port = ssl_test_lib:inet_port(node()),
CertFile = proplists:get_value(certfile, ServerOpts),
KeyFile = proplists:get_value(keyfile, ServerOpts),
-
- Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++
+ Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])),
+
+ Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++ version_flag(Version) ++
" -cert " ++ CertFile ++ " -key " ++ KeyFile ++ " -msg",
test_server:format("openssl cmd: ~p~n", [Cmd]),
@@ -552,8 +564,8 @@ erlang_client_openssl_server_no_wrap_sequence_number(Config) when is_list(Config
Port = ssl_test_lib:inet_port(node()),
CertFile = proplists:get_value(certfile, ServerOpts),
KeyFile = proplists:get_value(keyfile, ServerOpts),
-
- Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++
+ Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])),
+ Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++ version_flag(Version) ++
" -cert " ++ CertFile ++ " -key " ++ KeyFile ++ " -msg",
test_server:format("openssl cmd: ~p~n", [Cmd]),
@@ -600,10 +612,10 @@ erlang_server_openssl_client_no_wrap_sequence_number(Config) when is_list(Config
{from, self()},
{mfa, {ssl_test_lib,
trigger_renegotiate, [[Data, N+2]]}},
- {options, [{renegotiate_at, N} | ServerOpts]}]),
+ {options, [{renegotiate_at, N}, {reuse_sessions, false} | ServerOpts]}]),
Port = ssl_test_lib:inet_port(Server),
-
- Cmd = "openssl s_client -port " ++ integer_to_list(Port) ++
+ Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])),
+ Cmd = "openssl s_client -port " ++ integer_to_list(Port) ++ version_flag(Version) ++
" -host localhost -msg",
test_server:format("openssl cmd: ~p~n", [Cmd]),
@@ -639,8 +651,8 @@ erlang_client_openssl_server_no_server_ca_cert(Config) when is_list(Config) ->
Port = ssl_test_lib:inet_port(node()),
CertFile = proplists:get_value(certfile, ServerOpts),
KeyFile = proplists:get_value(keyfile, ServerOpts),
-
- Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++
+ Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])),
+ Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++ version_flag(Version) ++
" -cert " ++ CertFile ++ " -key " ++ KeyFile ++ " -msg",
test_server:format("openssl cmd: ~p~n", [Cmd]),
@@ -667,85 +679,11 @@ erlang_client_openssl_server_no_server_ca_cert(Config) when is_list(Config) ->
ok.
%%--------------------------------------------------------------------
-ssl3_erlang_client_openssl_server(doc) ->
- ["Test erlang client with openssl server"];
-ssl3_erlang_client_openssl_server(suite) ->
- [];
-ssl3_erlang_client_openssl_server(Config) when is_list(Config) ->
- process_flag(trap_exit, true),
- ServerOpts = ?config(server_opts, Config),
- ClientOpts = ?config(client_opts, Config),
-
- {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config),
-
- Port = ssl_test_lib:inet_port(node()),
- CertFile = proplists:get_value(certfile, ServerOpts),
- KeyFile = proplists:get_value(keyfile, ServerOpts),
-
- Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++
- " -cert " ++ CertFile ++ " -key " ++ KeyFile ++ " -ssl3",
-
- test_server:format("openssl cmd: ~p~n", [Cmd]),
-
- OpensslPort = open_port({spawn, Cmd}, [stderr_to_stdout]),
-
- wait_for_openssl_server(),
-
- Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {?MODULE,
- connection_info, [sslv3]}},
- {options,
- [{versions, [sslv3]} | ClientOpts]}]),
- ssl_test_lib:check_result(Client, ok),
-
- %% Clean close down! Server needs to be closed first !!
- close_port(OpensslPort),
- ssl_test_lib:close(Client),
- process_flag(trap_exit, false),
- ok.
-
-%%--------------------------------------------------------------------
-
-ssl3_erlang_server_openssl_client(doc) ->
- ["Test erlang server with openssl client"];
-ssl3_erlang_server_openssl_client(suite) ->
- [];
-ssl3_erlang_server_openssl_client(Config) when is_list(Config) ->
- process_flag(trap_exit, true),
- ServerOpts = ?config(server_opts, Config),
-
- {_, ServerNode, _} = ssl_test_lib:run_where(Config),
-
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa,
- {?MODULE, connection_info, [sslv3]}},
- {options,
- [{versions, [sslv3]} | ServerOpts]}]),
- Port = ssl_test_lib:inet_port(Server),
-
- Cmd = "openssl s_client -port " ++ integer_to_list(Port) ++
- " -host localhost -ssl3",
-
- test_server:format("openssl cmd: ~p~n", [Cmd]),
-
- OpenSslPort = open_port({spawn, Cmd}, [stderr_to_stdout]),
-
- ssl_test_lib:check_result(Server, ok),
- %% Clean close down! Server needs to be closed first !!
- ssl_test_lib:close(Server),
- close_port(OpenSslPort),
- process_flag(trap_exit, false),
- ok.
-
-%%--------------------------------------------------------------------
-ssl3_erlang_client_openssl_server_client_cert(doc) ->
+erlang_client_openssl_server_client_cert(doc) ->
["Test erlang client with openssl server when client sends cert"];
-ssl3_erlang_client_openssl_server_client_cert(suite) ->
+erlang_client_openssl_server_client_cert(suite) ->
[];
-ssl3_erlang_client_openssl_server_client_cert(Config) when is_list(Config) ->
+erlang_client_openssl_server_client_cert(Config) when is_list(Config) ->
process_flag(trap_exit, true),
ServerOpts = ?config(server_verification_opts, Config),
ClientOpts = ?config(client_verification_opts, Config),
@@ -758,10 +696,10 @@ ssl3_erlang_client_openssl_server_client_cert(Config) when is_list(Config) ->
CertFile = proplists:get_value(certfile, ServerOpts),
CaCertFile = proplists:get_value(cacertfile, ServerOpts),
KeyFile = proplists:get_value(keyfile, ServerOpts),
-
- Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++
+ Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])),
+ Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++ version_flag(Version) ++
" -cert " ++ CertFile ++ " -CAfile " ++ CaCertFile
- ++ " -key " ++ KeyFile ++ " -Verify 2 -ssl3",
+ ++ " -key " ++ KeyFile ++ " -Verify 2",
test_server:format("openssl cmd: ~p~n", [Cmd]),
@@ -787,11 +725,11 @@ ssl3_erlang_client_openssl_server_client_cert(Config) when is_list(Config) ->
%%--------------------------------------------------------------------
-ssl3_erlang_server_openssl_client_client_cert(doc) ->
+erlang_server_openssl_client_client_cert(doc) ->
["Test erlang server with openssl client when client sends cert"];
-ssl3_erlang_server_openssl_client_client_cert(suite) ->
+erlang_server_openssl_client_client_cert(suite) ->
[];
-ssl3_erlang_server_openssl_client_client_cert(Config) when is_list(Config) ->
+erlang_server_openssl_client_client_cert(Config) when is_list(Config) ->
process_flag(trap_exit, true),
ServerOpts = ?config(server_verification_opts, Config),
ClientOpts = ?config(client_verification_opts, Config),
@@ -812,10 +750,10 @@ ssl3_erlang_server_openssl_client_client_cert(Config) when is_list(Config) ->
CaCertFile = proplists:get_value(cacertfile, ClientOpts),
CertFile = proplists:get_value(certfile, ClientOpts),
KeyFile = proplists:get_value(keyfile, ClientOpts),
-
+ Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])),
Cmd = "openssl s_client -cert " ++ CertFile ++ " -CAfile " ++ CaCertFile
- ++ " -key " ++ KeyFile ++ " -port " ++ integer_to_list(Port) ++
- " -host localhost -ssl3",
+ ++ " -key " ++ KeyFile ++ " -port " ++ integer_to_list(Port) ++ version_flag(Version) ++
+ " -host localhost",
test_server:format("openssl cmd: ~p~n", [Cmd]),
@@ -833,15 +771,15 @@ ssl3_erlang_server_openssl_client_client_cert(Config) when is_list(Config) ->
%%--------------------------------------------------------------------
-ssl3_erlang_server_erlang_client_client_cert(doc) ->
+erlang_server_erlang_client_client_cert(doc) ->
["Test erlang server with erlang client when client sends cert"];
-ssl3_erlang_server_erlang_client_client_cert(suite) ->
+erlang_server_erlang_client_client_cert(suite) ->
[];
-ssl3_erlang_server_erlang_client_client_cert(Config) when is_list(Config) ->
+erlang_server_erlang_client_client_cert(Config) when is_list(Config) ->
process_flag(trap_exit, true),
ServerOpts = ?config(server_verification_opts, Config),
ClientOpts = ?config(client_verification_opts, Config),
-
+ Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])),
{ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
Data = "From erlang to erlang",
@@ -863,7 +801,7 @@ ssl3_erlang_server_erlang_client_client_cert(Config) when is_list(Config) ->
%% Due to 1/n-1 splitting countermeasure Rizzo/Duong-Beast
{mfa, {ssl, send, [Data]}},
{options,
- [{versions, [sslv3]} | ClientOpts]}]),
+ [{versions, [Version]} | ClientOpts]}]),
ssl_test_lib:check_result(Server, ok, Client, ok),
@@ -871,215 +809,8 @@ ssl3_erlang_server_erlang_client_client_cert(Config) when is_list(Config) ->
ssl_test_lib:close(Client),
process_flag(trap_exit, false),
ok.
-
-
-%%--------------------------------------------------------------------
-
-tls1_erlang_client_openssl_server(doc) ->
- ["Test erlang client with openssl server"];
-tls1_erlang_client_openssl_server(suite) ->
- [];
-tls1_erlang_client_openssl_server(Config) when is_list(Config) ->
- process_flag(trap_exit, true),
- ServerOpts = ?config(server_opts, Config),
- ClientOpts = ?config(client_opts, Config),
-
-
- test_server:format("Server Opts", [ServerOpts]),
-
- {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config),
-
- Port = ssl_test_lib:inet_port(node()),
- CertFile = proplists:get_value(certfile, ServerOpts),
- KeyFile = proplists:get_value(keyfile, ServerOpts),
-
- Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++
- " -cert " ++ CertFile ++ " -key " ++ KeyFile ++ " -tls1",
-
- test_server:format("openssl cmd: ~p~n", [Cmd]),
-
- OpensslPort = open_port({spawn, Cmd}, [stderr_to_stdout]),
-
- wait_for_openssl_server(),
-
- Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {?MODULE,
- connection_info, [tlsv1]}},
- {options,
- [{versions, [tlsv1]} | ClientOpts]}]),
-
- ssl_test_lib:check_result(Client, ok),
-
- %% Clean close down! Server needs to be closed first !!
- close_port(OpensslPort),
- ssl_test_lib:close(Client),
- process_flag(trap_exit, false),
- ok.
-
-%%--------------------------------------------------------------------
-
-tls1_erlang_server_openssl_client(doc) ->
- ["Test erlang server with openssl client"];
-tls1_erlang_server_openssl_client(suite) ->
- [];
-tls1_erlang_server_openssl_client(Config) when is_list(Config) ->
- process_flag(trap_exit, true),
- ServerOpts = ?config(server_opts, Config),
-
- {_, ServerNode, _} = ssl_test_lib:run_where(Config),
-
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa,
- {?MODULE, connection_info, [tlsv1]}},
- {options,
- [{versions, [tlsv1]} | ServerOpts]}]),
- Port = ssl_test_lib:inet_port(Server),
-
- Cmd = "openssl s_client -port " ++ integer_to_list(Port) ++
- " -host localhost -tls1",
-
- test_server:format("openssl cmd: ~p~n", [Cmd]),
-
- OpenSslPort = open_port({spawn, Cmd}, [stderr_to_stdout]),
-
- ssl_test_lib:check_result(Server, ok),
-
- %% Clean close down! Server needs to be closed first !!
- ssl_test_lib:close(Server),
- close_port(OpenSslPort),
- process_flag(trap_exit, false),
- ok.
-
-%%--------------------------------------------------------------------
-
-tls1_erlang_client_openssl_server_client_cert(doc) ->
- ["Test erlang client with openssl server when client sends cert"];
-tls1_erlang_client_openssl_server_client_cert(suite) ->
- [];
-tls1_erlang_client_openssl_server_client_cert(Config) when is_list(Config) ->
- process_flag(trap_exit, true),
- ServerOpts = ?config(server_verification_opts, Config),
- ClientOpts = ?config(client_verification_opts, Config),
-
- {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config),
-
- Data = "From openssl to erlang",
-
- Port = ssl_test_lib:inet_port(node()),
- CaCertFile = proplists:get_value(cacertfile, ServerOpts),
- CertFile = proplists:get_value(certfile, ServerOpts),
- KeyFile = proplists:get_value(keyfile, ServerOpts),
-
- Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++
- " -cert " ++ CertFile ++ " -CAfile " ++ CaCertFile
- ++ " -key " ++ KeyFile ++ " -Verify 2 -tls1",
-
- test_server:format("openssl cmd: ~p~n", [Cmd]),
-
- OpensslPort = open_port({spawn, Cmd}, [stderr_to_stdout]),
-
- wait_for_openssl_server(),
-
- Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {?MODULE,
- erlang_ssl_receive, [Data]}},
- {options, ClientOpts}]),
- port_command(OpensslPort, Data),
-
- ssl_test_lib:check_result(Client, ok),
-
- %% Clean close down! Server needs to be closed first !!
- close_port(OpensslPort),
- ssl_test_lib:close(Client),
- process_flag(trap_exit, false),
- ok.
-
%%--------------------------------------------------------------------
-tls1_erlang_server_openssl_client_client_cert(doc) ->
- ["Test erlang server with openssl client when client sends cert"];
-tls1_erlang_server_openssl_client_client_cert(suite) ->
- [];
-tls1_erlang_server_openssl_client_client_cert(Config) when is_list(Config) ->
- process_flag(trap_exit, true),
- ServerOpts = ?config(server_verification_opts, Config),
- ClientOpts = ?config(client_verification_opts, Config),
-
- {_, ServerNode, _} = 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]}},
- {options,
- [{verify , verify_peer}
- | ServerOpts]}]),
- Port = ssl_test_lib:inet_port(Server),
-
- CaCertFile = proplists:get_value(cacertfile, ClientOpts),
- CertFile = proplists:get_value(certfile, ClientOpts),
- KeyFile = proplists:get_value(keyfile, ClientOpts),
-
- Cmd = "openssl s_client -cert " ++ CertFile ++ " -CAfile " ++ CaCertFile
- ++ " -key " ++ KeyFile ++ " -port " ++ integer_to_list(Port) ++
- " -host localhost -tls1",
-
- test_server:format("openssl cmd: ~p~n", [Cmd]),
-
- OpenSslPort = open_port({spawn, Cmd}, [stderr_to_stdout]),
- port_command(OpenSslPort, Data),
-
- ssl_test_lib:check_result(Server, ok),
-
- %% Clean close down! Server needs to be closed first !!
- ssl_test_lib:close(Server),
- close_port(OpenSslPort),
- process_flag(trap_exit, false),
- ok.
-
-%%--------------------------------------------------------------------
-tls1_erlang_server_erlang_client_client_cert(doc) ->
- ["Test erlang server with erlang client when client sends cert"];
-tls1_erlang_server_erlang_client_client_cert(suite) ->
- [];
-tls1_erlang_server_erlang_client_client_cert(Config) when is_list(Config) ->
- process_flag(trap_exit, true),
- ServerOpts = ?config(server_verification_opts, Config),
- ClientOpts = ?config(client_verification_opts, Config),
-
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-
- Data = "From erlang to erlang",
-
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {?MODULE,
- erlang_ssl_receive, [Data]}},
- {options,
- [{verify , verify_peer}
- | ServerOpts]}]),
- Port = ssl_test_lib:inet_port(Server),
-
- Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {ssl, send, [Data]}},
- {options,
- [{versions, [tlsv1]} | ClientOpts]}]),
-
- ssl_test_lib:check_result(Server, ok, Client, ok),
- ssl_test_lib:close(Server),
- process_flag(trap_exit, false),
- ok.
-%%--------------------------------------------------------------------
-
ciphers_rsa_signed_certs(doc) ->
["Test cipher suites that uses rsa certs"];
@@ -1186,12 +917,6 @@ cipher(CipherSuite, Version, Config, ClientOpts, ServerOpts) ->
process_flag(trap_exit, false),
Return.
-
-version_flag(tlsv1) ->
- " -tls1 ";
-version_flag(sslv3) ->
- " -ssl3 ".
-
%%--------------------------------------------------------------------
erlang_client_bad_openssl_server(doc) ->
[""];
@@ -1207,8 +932,8 @@ erlang_client_bad_openssl_server(Config) when is_list(Config) ->
Port = ssl_test_lib:inet_port(node()),
CertFile = proplists:get_value(certfile, ServerOpts),
KeyFile = proplists:get_value(keyfile, ServerOpts),
-
- Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++
+ Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])),
+ Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++ version_flag(Version) ++
" -cert " ++ CertFile ++ " -key " ++ KeyFile ++ "",
test_server:format("openssl cmd: ~p~n", [Cmd]),
@@ -1222,7 +947,7 @@ erlang_client_bad_openssl_server(Config) when is_list(Config) ->
{from, self()},
{mfa, {?MODULE, server_sent_garbage, []}},
{options,
- [{versions, [tlsv1]} | ClientOpts]}]),
+ [{versions, [Version]} | ClientOpts]}]),
%% Send garbage
port_command(OpensslPort, ?OPENSSL_GARBAGE),
@@ -1241,7 +966,7 @@ erlang_client_bad_openssl_server(Config) when is_list(Config) ->
{from, self()},
{mfa, {ssl_test_lib, no_result_msg, []}},
{options,
- [{versions, [tlsv1]} | ClientOpts]}]),
+ [{versions, [Version]} | ClientOpts]}]),
%% Clean close down! Server needs to be closed first !!
close_port(OpensslPort),
@@ -1267,8 +992,8 @@ expired_session(Config) when is_list(Config) ->
Port = ssl_test_lib:inet_port(node()),
CertFile = proplists:get_value(certfile, ServerOpts),
KeyFile = proplists:get_value(keyfile, ServerOpts),
-
- Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++
+
+ Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++
" -cert " ++ CertFile ++ " -key " ++ KeyFile ++ "",
test_server:format("openssl cmd: ~p~n", [Cmd]),
@@ -1433,21 +1158,46 @@ wait_for_openssl_server() ->
%% more so than sleep!)
test_server:sleep(?SLEEP)
end.
-
+
+version_flag(tlsv1) ->
+ " -tls1 ";
+version_flag('tlsv1.1') ->
+ " -tls1_1 ";
+version_flag('tlsv1.2') ->
+ " -tls1_2 ";
+version_flag(sslv3) ->
+ " -ssl3 ".
+
check_sane_openssl_renegotaite(Config) ->
case os:cmd("openssl version") of
"OpenSSL 0.9.8" ++ _ ->
- {skip, "Known renegotiation bug in OppenSSL"};
+ {skip, "Known renegotiation bug in OpenSSL"};
"OpenSSL 0.9.7" ++ _ ->
- {skip, "Known renegotiation bug in OppenSSL"};
+ {skip, "Known renegotiation bug in OpenSSL"};
_ ->
Config
end.
check_sane_openssl_sslv2(Config) ->
case os:cmd("openssl version") of
- "OpenSSL 1.0.0" ++ _ ->
+ "OpenSSL 1." ++ _ ->
{skip, "sslv2 by default turned of in 1.*"};
_ ->
Config
end.
+
+check_sane_openssl_version(Version) ->
+ case {Version, os:cmd("openssl version")} of
+ {_, "OpenSSL 1.0.1" ++ _} ->
+ true;
+ {'tlsv1.2', "OpenSSL 1.0" ++ _} ->
+ false;
+ {'tlsv1.1', "OpenSSL 1.0" ++ _} ->
+ false;
+ {'tlsv1.2', "OpenSSL 0" ++ _} ->
+ false;
+ {'tlsv1.1', "OpenSSL 0" ++ _} ->
+ false;
+ {_, _} ->
+ true
+ end.
diff --git a/lib/ssl/vsn.mk b/lib/ssl/vsn.mk
index 0fccbfe908..e381b73c27 100644
--- a/lib/ssl/vsn.mk
+++ b/lib/ssl/vsn.mk
@@ -1 +1 @@
-SSL_VSN = 5.0.1
+SSL_VSN = 5.1