aboutsummaryrefslogtreecommitdiffstats
path: root/lib/ssl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/ssl')
-rw-r--r--lib/ssl/doc/src/new_ssl.xml14
-rw-r--r--lib/ssl/doc/src/notes.xml67
-rw-r--r--lib/ssl/src/ssl_certificate.erl34
-rw-r--r--lib/ssl/src/ssl_cipher.erl120
-rw-r--r--lib/ssl/src/ssl_connection.erl71
-rw-r--r--lib/ssl/src/ssl_handshake.erl91
-rw-r--r--lib/ssl/src/ssl_record.erl1
-rw-r--r--lib/ssl/src/ssl_ssl3.erl11
-rw-r--r--lib/ssl/src/ssl_tls1.erl9
-rw-r--r--lib/ssl/test/Makefile13
-rw-r--r--lib/ssl/test/erl_make_certs.erl412
-rw-r--r--lib/ssl/test/ssl_basic_SUITE.erl225
-rw-r--r--lib/ssl/test/ssl_test_lib.erl58
-rw-r--r--lib/ssl/test/ssl_to_openssl_SUITE.erl91
-rw-r--r--lib/ssl/vsn.mk7
15 files changed, 984 insertions, 240 deletions
diff --git a/lib/ssl/doc/src/new_ssl.xml b/lib/ssl/doc/src/new_ssl.xml
index 4ffaa9d96a..69298759bd 100644
--- a/lib/ssl/doc/src/new_ssl.xml
+++ b/lib/ssl/doc/src/new_ssl.xml
@@ -22,7 +22,6 @@
The Initial Developer of the Original Code is Ericsson AB.
</legalnotice>
-
<title>ssl</title>
<prepared>Ingela Anderton Andin</prepared>
<responsible>Ingela Anderton Andin</responsible>
@@ -83,7 +82,7 @@
meaningless pid.</item>
<item>New API functions are
ssl:shutdown/2, ssl:cipher_suites/[0,1] and
- ssl:versions/0</item>
+ ssl:versions/0, ssl:renegotiate/1</item>
<item>CRL and policy certificate
extensions are not supported yet. </item>
<item>Supported SSL/TLS-versions are SSL-3.0 and TLS-1.0 </item>
@@ -408,6 +407,17 @@ end
</desc>
</func>
+ <func>
+ <name>format_error(Reason) -> string()</name>
+ <fsummary>Return an error string.</fsummary>
+ <type>
+ <v>Reason = term()</v>
+ </type>
+ <desc>
+ <p>Presents the error returned by an ssl function as a printable string.</p>
+ </desc>
+ </func>
+
<func>
<name>getopts(Socket) -> </name>
<name>getopts(Socket, OptionNames) ->
diff --git a/lib/ssl/doc/src/notes.xml b/lib/ssl/doc/src/notes.xml
index 9d13427677..8028e94484 100644
--- a/lib/ssl/doc/src/notes.xml
+++ b/lib/ssl/doc/src/notes.xml
@@ -30,6 +30,73 @@
</header>
<p>This document describes the changes made to the SSL application.
</p>
+
+ <section><title>SSL 3.11.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fixed handling of several ssl/tls packets arriving at the
+ same time. This was broken during a refactoring of the
+ code.</p>
+ <p>
+ Own Id: OTP-8679</p>
+ </item>
+ </list>
+ </section>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Added missing checks for padding and Mac value. Removed
+ code for export ciphers and DH certificates as we decided
+ not to support them.</p>
+ <p>
+ Own Id: OTP-7047</p>
+ </item>
+ <item>
+ <p>
+ New ssl will no longer return esslerrssl to be backwards
+ compatible with old ssl as this hids infomation from the
+ user. format_error/1 has been updated to support new ssl.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-7049</p>
+ </item>
+ <item>
+ <p>
+ New ssl now supports secure renegotiation as described by
+ RFC 5746.</p>
+ <p>
+ Own Id: OTP-8568</p>
+ </item>
+ <item>
+ <p>
+ New ssl now support client/server-certificates signed by
+ dsa keys.</p>
+ <p>
+ Own Id: OTP-8587</p>
+ </item>
+ <item>
+ <p>
+ Alert handling has been improved to better handle
+ unexpected but valid messages and the implementation is
+ also changed to avoid timing related issues that could
+ cause different error messages depending on network
+ latency. Packet handling was sort of broken but would
+ mostly work as expected when socket was in binary mode.
+ This has now been fixed.</p>
+ <p>
+ Own Id: OTP-8588</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>SSL 3.11</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/ssl/src/ssl_certificate.erl b/lib/ssl/src/ssl_certificate.erl
index 37d5646673..9aa31ae8a4 100644
--- a/lib/ssl/src/ssl_certificate.erl
+++ b/lib/ssl/src/ssl_certificate.erl
@@ -34,7 +34,13 @@
-export([trusted_cert_and_path/3,
certificate_chain/2,
file_to_certificats/1,
- validate_extensions/6]).
+ validate_extensions/6,
+ is_valid_extkey_usage/2,
+ is_valid_key_usage/2,
+ select_extension/2,
+ extensions_list/1,
+ signature_type/1
+ ]).
%%====================================================================
%% Internal application API
@@ -112,7 +118,28 @@ validate_extensions([Extension | Rest], ValidationState, UnknownExtensions,
Verify, AccErr, Role) ->
validate_extensions(Rest, ValidationState, [Extension | UnknownExtensions],
Verify, AccErr, Role).
-
+
+is_valid_key_usage(KeyUse, Use) ->
+ lists:member(Use, KeyUse).
+
+ select_extension(_, []) ->
+ undefined;
+select_extension(Id, [#'Extension'{extnID = Id} = Extension | _]) ->
+ Extension;
+select_extension(Id, [_ | Extensions]) ->
+ select_extension(Id, Extensions).
+
+extensions_list(asn1_NOVALUE) ->
+ [];
+extensions_list(Extensions) ->
+ Extensions.
+
+signature_type(RSA) when RSA == ?sha1WithRSAEncryption;
+ RSA == ?md5WithRSAEncryption ->
+ rsa;
+signature_type(?'id-dsa-with-sha1') ->
+ dsa.
+
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
@@ -188,9 +215,6 @@ is_valid_extkey_usage(KeyUse, server) ->
%% Server wants to verify client
is_valid_key_usage(KeyUse, ?'id-kp-clientAuth').
-is_valid_key_usage(KeyUse, Use) ->
- lists:member(Use, KeyUse).
-
not_valid_extension(Error, true, _) ->
throw(Error);
not_valid_extension(Error, false, AccErrors) ->
diff --git a/lib/ssl/src/ssl_cipher.erl b/lib/ssl/src/ssl_cipher.erl
index f425886ce5..2a71df8ee1 100644
--- a/lib/ssl/src/ssl_cipher.erl
+++ b/lib/ssl/src/ssl_cipher.erl
@@ -30,11 +30,12 @@
-include("ssl_cipher.hrl").
-include("ssl_alert.hrl").
-include("ssl_debug.hrl").
+-include_lib("public_key/include/public_key.hrl").
-export([security_parameters/2, suite_definition/1,
decipher/5, cipher/4,
suite/1, suites/1,
- openssl_suite/1, openssl_suite_name/1]).
+ openssl_suite/1, openssl_suite_name/1, filter/2]).
-compile(inline).
@@ -240,7 +241,7 @@ suite_definition(?TLS_RSA_WITH_3DES_EDE_CBC_SHA) ->
suite_definition(?TLS_DHE_DSS_WITH_DES_CBC_SHA) ->
{dhe_dss, des_cbc, sha};
suite_definition(?TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA) ->
- {dhe_dss, '3des_ede_cbc'};
+ {dhe_dss, '3des_ede_cbc', sha};
suite_definition(?TLS_DHE_RSA_WITH_DES_CBC_SHA) ->
{dhe_rsa, des_cbc, sha};
suite_definition(?TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA) ->
@@ -260,25 +261,6 @@ suite_definition(?TLS_DHE_DSS_WITH_AES_256_CBC_SHA) ->
suite_definition(?TLS_DHE_RSA_WITH_AES_256_CBC_SHA) ->
{dhe_rsa, aes_256_cbc, sha}.
-%% TODO: support kerbos key exchange?
-%% TSL V1.1 KRB SUITES
-%% suite_definition(?TLS_KRB5_WITH_DES_CBC_SHA) ->
-%% {krb5, des_cbc, sha};
-%% suite_definition(?TLS_KRB5_WITH_3DES_EDE_CBC_SHA) ->
-%% {krb5, '3des_ede_cbc', sha};
-%% suite_definition(?TLS_KRB5_WITH_RC4_128_SHA) ->
-%% {krb5, rc4_128, sha};
-%% suite_definition(?TLS_KRB5_WITH_IDEA_CBC_SHA) ->
-%% {krb5, idea_cbc, sha};
-%% suite_definition(?TLS_KRB5_WITH_DES_CBC_MD5) ->
-%% {krb5, des_cbc, md5};
-%% suite_definition(?TLS_KRB5_WITH_3DES_EDE_CBC_MD5) ->
-%% {krb5, '3des_ede_cbc', md5};
-%% suite_definition(?TLS_KRB5_WITH_RC4_128_MD5) ->
-%% {krb5, rc4_128, md5};
-%% suite_definition(?TLS_KRB5_WITH_IDEA_CBC_MD5) ->
-%% {krb5, idea_cbc, md5};
-
%% TLS v1.1 suites
%%suite({rsa, null, md5}) ->
%% ?TLS_RSA_WITH_NULL_MD5;
@@ -312,8 +294,8 @@ suite({dhe_rsa, '3des_ede_cbc', sha}) ->
%%% TSL V1.1 AES suites
suite({rsa, aes_128_cbc, sha}) ->
?TLS_RSA_WITH_AES_128_CBC_SHA;
-%% suite({dhe_dss, aes_128_cbc, sha}) ->
-%% ?TLS_DHE_DSS_WITH_AES_128_CBC_SHA;
+suite({dhe_dss, aes_128_cbc, sha}) ->
+ ?TLS_DHE_DSS_WITH_AES_128_CBC_SHA;
suite({dhe_rsa, aes_128_cbc, sha}) ->
?TLS_DHE_RSA_WITH_AES_128_CBC_SHA;
%% suite({dh_anon, aes_128_cbc, sha}) ->
@@ -327,29 +309,8 @@ suite({dhe_rsa, aes_256_cbc, sha}) ->
%% suite({dh_anon, aes_256_cbc, sha}) ->
%% ?TLS_DH_anon_WITH_AES_256_CBC_SHA.
-%% TODO: support kerbos key exchange?
-%% TSL V1.1 KRB SUITES
-%% suite({krb5, des_cbc, sha}) ->
-%% ?TLS_KRB5_WITH_DES_CBC_SHA;
-%% suite({krb5_cbc, '3des_ede_cbc', sha}) ->
-%% ?TLS_KRB5_WITH_3DES_EDE_CBC_SHA;
-%% suite({krb5, rc4_128, sha}) ->
-%% ?TLS_KRB5_WITH_RC4_128_SHA;
-%% suite({krb5_cbc, idea_cbc, sha}) ->
-%% ?TLS_KRB5_WITH_IDEA_CBC_SHA;
-%% suite({krb5_cbc, md5}) ->
-%% ?TLS_KRB5_WITH_DES_CBC_MD5;
-%% suite({krb5_ede_cbc, des_cbc, md5}) ->
-%% ?TLS_KRB5_WITH_3DES_EDE_CBC_MD5;
-%% suite({krb5_128, rc4_128, md5}) ->
-%% ?TLS_KRB5_WITH_RC4_128_MD5;
-%% suite({krb5, idea_cbc, md5}) ->
-%% ?TLS_KRB5_WITH_IDEA_CBC_MD5;
%% translate constants <-> openssl-strings
-%% TODO: Is there a pattern in the nameing
-%% that is useable to make a nicer function defention?
-
openssl_suite("DHE-RSA-AES256-SHA") ->
?TLS_DHE_RSA_WITH_AES_256_CBC_SHA;
openssl_suite("DHE-DSS-AES256-SHA") ->
@@ -368,17 +329,12 @@ 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;
-%% TODO: Do we want to support this?
-%% openssl_suite("DHE-DSS-RC4-SHA") ->
-%% ?TLS_DHE_DSS_WITH_RC4_128_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") ->
?TLS_RSA_WITH_RC4_128_MD5;
-%% openssl_suite("DHE-DSS-RC4-SHA") ->
-%% ?TLS_DHE_DSS_WITH_RC4_128_SHA;
openssl_suite("EDH-RSA-DES-CBC-SHA") ->
?TLS_DHE_RSA_WITH_DES_CBC_SHA;
openssl_suite("DES-CBC-SHA") ->
@@ -412,14 +368,22 @@ 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_DHE_DSS_WITH_RC4_128_SHA) ->
-%% "DHE-DSS-RC4-SHA";
-
%% No oppenssl name
openssl_suite_name(Cipher) ->
suite_definition(Cipher).
+filter(undefined, Ciphers) ->
+ Ciphers;
+filter(DerCert, Ciphers) ->
+ {ok, OtpCert} = public_key:pkix_decode_cert(DerCert, otp),
+ SigAlg = OtpCert#'OTPCertificate'.signatureAlgorithm,
+ case ssl_certificate:signature_type(SigAlg#'SignatureAlgorithm'.algorithm) of
+ rsa ->
+ filter_rsa(OtpCert, Ciphers -- dsa_signed_suites());
+ dsa ->
+ Ciphers -- rsa_signed_suites()
+ end.
+
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
@@ -567,3 +531,53 @@ next_iv(Bin, IV) ->
<<_:FirstPart/binary, NextIV:IVSz/binary>> = Bin,
NextIV.
+rsa_signed_suites() ->
+ dhe_rsa_suites() ++ rsa_suites().
+
+dhe_rsa_suites() ->
+ [?TLS_DHE_RSA_WITH_AES_256_CBC_SHA,
+ ?TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA,
+ ?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_3DES_EDE_CBC_SHA,
+ ?TLS_RSA_WITH_AES_128_CBC_SHA,
+ %%?TLS_RSA_WITH_IDEA_CBC_SHA,
+ ?TLS_RSA_WITH_RC4_128_SHA,
+ ?TLS_RSA_WITH_RC4_128_MD5,
+ ?TLS_RSA_WITH_DES_CBC_SHA].
+
+dsa_signed_suites() ->
+ dhe_dss_suites().
+
+dhe_dss_suites() ->
+ [?TLS_DHE_DSS_WITH_AES_256_CBC_SHA,
+ ?TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA,
+ ?TLS_DHE_DSS_WITH_AES_128_CBC_SHA,
+ ?TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA].
+
+filter_rsa(OtpCert, RsaCiphers) ->
+ TBSCert = OtpCert#'OTPCertificate'.tbsCertificate,
+ TBSExtensions = TBSCert#'OTPTBSCertificate'.extensions,
+ Extensions = ssl_certificate:extensions_list(TBSExtensions),
+ case ssl_certificate:select_extension(?'id-ce-keyUsage', Extensions) of
+ undefined ->
+ RsaCiphers;
+ #'Extension'{extnValue = KeyUse} ->
+ Result = filter_rsa_suites(keyEncipherment,
+ KeyUse, RsaCiphers, rsa_suites()),
+ filter_rsa_suites(digitalSignature,
+ KeyUse, Result, dhe_rsa_suites())
+ end.
+
+filter_rsa_suites(Use, KeyUse, CipherSuits, RsaSuites) ->
+ case ssl_certificate:is_valid_key_usage(KeyUse, Use) of
+ true ->
+ CipherSuits;
+ false ->
+ CipherSuits -- RsaSuites
+ end.
+
+
diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl
index 644c2772b2..abd1b59011 100644
--- a/lib/ssl/src/ssl_connection.erl
+++ b/lib/ssl/src/ssl_connection.erl
@@ -65,13 +65,14 @@
ssl_options, % #ssl_options{}
socket_options, % #socket_options{}
connection_states, % #connection_states{} from ssl_record.hrl
+ 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_cipher_texts, % list() received but not deciphered yet
own_cert, % binary()
- session, % #session{} from ssl_handshake.erl
+ session, % #session{} from ssl_handshake.hrl
session_cache, %
session_cache_cb, %
negotiated_version, % #protocol_version{}
@@ -280,12 +281,12 @@ start_link(Role, Host, Port, Socket, Options, User, CbInfo) ->
%% gen_fsm:start_link/3,4, this function is called by the new process to
%% initialize.
%%--------------------------------------------------------------------
-init([Role, Host, Port, Socket, {SSLOpts, _} = Options,
+init([Role, Host, Port, Socket, {SSLOpts0, _} = Options,
User, CbInfo]) ->
State0 = initial_state(Role, Host, Port, Socket, Options, User, CbInfo),
Hashes0 = ssl_handshake:init_hashes(),
- try ssl_init(SSLOpts, Role) of
+ try ssl_init(SSLOpts0, Role) of
{ok, Ref, CacheRef, OwnCert, Key, DHParams} ->
State = State0#state{tls_handshake_hashes = Hashes0,
own_cert = OwnCert,
@@ -317,10 +318,14 @@ hello(start, #state{host = Host, port = Port, role = client,
ssl_options = SslOpts,
transport_cb = Transport, socket = Socket,
connection_states = ConnectionStates,
+ own_cert = Cert,
renegotiation = {Renegotiation, _}}
= State0) ->
+
Hello = ssl_handshake:client_hello(Host, Port,
- ConnectionStates, SslOpts, Renegotiation),
+ ConnectionStates,
+ SslOpts, Cert,
+ Renegotiation),
Version = Hello#client_hello.client_version,
Hashes0 = ssl_handshake:init_hashes(),
@@ -401,10 +406,11 @@ hello(Hello = #client_hello{client_version = ClientVersion},
renegotiation = {Renegotiation, _},
session_cache = Cache,
session_cache_cb = CacheCb,
- ssl_options = SslOpts}) ->
+ ssl_options = SslOpts,
+ own_cert = Cert}) ->
case ssl_handshake:hello(Hello, SslOpts, {Port, Session0, Cache, CacheCb,
- ConnectionStates0}, Renegotiation) of
+ ConnectionStates0, Cert}, Renegotiation) of
{Version, {Type, Session}, ConnectionStates} ->
do_server_hello(Type, State#state{connection_states =
ConnectionStates,
@@ -700,13 +706,14 @@ connection(#hello_request{}, #state{host = Host, port = Port,
socket = Socket,
ssl_options = SslOpts,
negotiated_version = Version,
+ own_cert = Cert,
transport_cb = Transport,
connection_states = ConnectionStates0,
renegotiation = {Renegotiation, _},
tls_handshake_hashes = Hashes0} = State0) ->
Hello = ssl_handshake:client_hello(Host, Port,
- ConnectionStates0, SslOpts, Renegotiation),
+ ConnectionStates0, SslOpts, Cert, Renegotiation),
{BinMsg, ConnectionStates1, Hashes1} =
encode_handshake(Hello, Version, ConnectionStates0, Hashes0),
@@ -1485,15 +1492,15 @@ handle_server_key(
SecParams = ConnectionState#connection_state.security_parameters,
#security_parameters{client_random = ClientRandom,
server_random = ServerRandom} = SecParams,
- Hash = ssl_handshake:server_key_exchange_hash(KeyAlgo,
- <<ClientRandom/binary,
+ Plain = ssl_handshake:server_key_exchange_plain(KeyAlgo,
+ <<ClientRandom/binary,
ServerRandom/binary,
- ?UINT16(PLen), P/binary,
- ?UINT16(GLen), G/binary,
- ?UINT16(YLen),
+ ?UINT16(PLen), P/binary,
+ ?UINT16(GLen), G/binary,
+ ?UINT16(YLen),
ServerPublicDhKey/binary>>),
-
- case verify_dh_params(Signed, Hash, PubKeyInfo) of
+
+ case verify_dh_params(Signed, Plain, PubKeyInfo) of
true ->
PMpint = mpint_binary(P),
GMpint = mpint_binary(G),
@@ -1517,14 +1524,18 @@ handle_server_key(
?ALERT_REC(?FATAL,?HANDSHAKE_FAILURE)
end.
-verify_dh_params(Signed, Hash, {?rsaEncryption, PubKey, _PubKeyparams}) ->
+
+verify_dh_params(Signed, Hashes, {?rsaEncryption, PubKey, _PubKeyParams}) ->
case public_key:decrypt_public(Signed, PubKey,
[{rsa_pad, rsa_pkcs1_padding}]) of
- Hash ->
+ Hashes ->
true;
_ ->
false
- end.
+ end;
+verify_dh_params(Signed, Plain, {?'id-dsa', PublicKey, PublicKeyParams}) ->
+ public_key:verify_signature(Plain, sha, Signed, PublicKey, PublicKeyParams).
+
encode_alert(#alert{} = Alert, Version, ConnectionStates) ->
?DBG_TERM(Alert),
@@ -1727,9 +1738,23 @@ opposite_role(server) ->
send_user(Pid, Msg) ->
Pid ! Msg.
+handle_tls_handshake(Handle, StateName, #state{tls_packets = [Packet]} = State) ->
+ FsmReturn = {next_state, StateName, State#state{tls_packets = []}},
+ Handle(Packet, FsmReturn);
+
+handle_tls_handshake(Handle, StateName, #state{tls_packets = [Packet | Packets]} = State0) ->
+ FsmReturn = {next_state, StateName, State0#state{tls_packets = Packets}},
+ case Handle(Packet, FsmReturn) of
+ {next_state, NextStateName, State} ->
+ handle_tls_handshake(Handle, NextStateName, State);
+ {stop, _,_} = Stop ->
+ Stop
+ end.
+
next_state(_, #alert{} = Alert, #state{negotiated_version = Version} = State) ->
handle_own_alert(Alert, Version, decipher_error, State),
{stop, normal, State};
+
next_state(Next, no_record, State) ->
{next_state, Next, State};
@@ -1764,8 +1789,8 @@ next_state(StateName, #ssl_tls{type = ?HANDSHAKE, fragment = Data},
end,
try
{Packets, Buf} = ssl_handshake:get_tls_handshake(Data,Buf0, KeyAlg,Version),
- Start = {next_state, StateName, State0#state{tls_handshake_buffer = Buf}},
- lists:foldl(Handle, Start, Packets)
+ State = State0#state{tls_packets = Packets, tls_handshake_buffer = Buf},
+ handle_tls_handshake(Handle, StateName, State)
catch throw:#alert{} = Alert ->
handle_own_alert(Alert, Version, StateName, State0),
{stop, normal, State0}
@@ -1802,17 +1827,19 @@ next_tls_record(Data, #state{tls_record_buffer = Buf0,
Alert
end.
-next_record(#state{tls_cipher_texts = [], socket = Socket} = State) ->
+next_record(#state{tls_packets = [], tls_cipher_texts = [], socket = Socket} = State) ->
inet:setopts(Socket, [{active,once}]),
{no_record, State};
-next_record(#state{tls_cipher_texts = [CT | Rest],
+next_record(#state{tls_packets = [], tls_cipher_texts = [CT | Rest],
connection_states = ConnStates0} = State) ->
case ssl_record:decode_cipher_text(CT, ConnStates0) of
{Plain, ConnStates} ->
{Plain, State#state{tls_cipher_texts = Rest, connection_states = ConnStates}};
#alert{} = Alert ->
{Alert, State}
- end.
+ end;
+next_record(State) ->
+ {no_record, State}.
next_record_if_active(State =
#state{socket_options =
diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl
index 454d726f0d..c8245e2fb4 100644
--- a/lib/ssl/src/ssl_handshake.erl
+++ b/lib/ssl/src/ssl_handshake.erl
@@ -31,11 +31,11 @@
-include("ssl_debug.hrl").
-include_lib("public_key/include/public_key.hrl").
--export([master_secret/4, client_hello/5, server_hello/4, hello/4,
+-export([master_secret/4, client_hello/6, server_hello/4, hello/4,
hello_request/0, certify/7, certificate/3,
client_certificate_verify/6,
certificate_verify/6, certificate_request/2,
- key_exchange/2, server_key_exchange_hash/2, finished/4,
+ key_exchange/2, server_key_exchange_plain/2, finished/4,
verify_connection/5,
get_tls_handshake/4,
server_hello_done/0, sig_alg/1,
@@ -46,7 +46,7 @@
%% Internal application API
%%====================================================================
%%--------------------------------------------------------------------
-%% Function: client_hello(Host, Port, ConnectionStates, SslOpts) ->
+%% Function: client_hello(Host, Port, ConnectionStates, SslOpts, Cert, Renegotiation) ->
%% #client_hello{}
%% Host
%% Port
@@ -56,8 +56,8 @@
%% Description: Creates a client hello message.
%%--------------------------------------------------------------------
client_hello(Host, Port, ConnectionStates, #ssl_options{versions = Versions,
- ciphers = Ciphers}
- = SslOpts, Renegotiation) ->
+ ciphers = UserSuites}
+ = SslOpts, Cert, Renegotiation) ->
Fun = fun(Version) ->
ssl_record:protocol_version(Version)
@@ -65,7 +65,8 @@ client_hello(Host, Port, ConnectionStates, #ssl_options{versions = Versions,
Version = ssl_record:highest_protocol_version(lists:map(Fun, Versions)),
Pending = ssl_record:pending_connection_state(ConnectionStates, read),
SecParams = Pending#connection_state.security_parameters,
-
+ Ciphers = available_suites(Cert, UserSuites, Version),
+
Id = ssl_manager:client_session_id(Host, Port, SslOpts),
#client_hello{session_id = Id,
@@ -150,14 +151,14 @@ hello(#client_hello{client_version = ClientVersion, random = Random,
renegotiation_info = Info} = Hello,
#ssl_options{versions = Versions,
secure_renegotiate = SecureRenegotation} = SslOpts,
- {Port, Session0, Cache, CacheCb, ConnectionStates0}, Renegotiation) ->
+ {Port, Session0, Cache, CacheCb, ConnectionStates0, Cert}, Renegotiation) ->
Version = select_version(ClientVersion, Versions),
case ssl_record:is_acceptable_version(Version) of
true ->
{Type, #session{cipher_suite = CipherSuite,
compression_method = Compression} = Session}
= select_session(Hello, Port, Session0, Version,
- SslOpts, Cache, CacheCb),
+ SslOpts, Cache, CacheCb, Cert),
case CipherSuite of
no_suite ->
?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY);
@@ -316,8 +317,12 @@ certificate_verify(Signature, {_, PublicKey, _}, Version,
valid;
_ ->
?ALERT_REC(?FATAL, ?BAD_CERTIFICATE)
- end.
-%% TODO dsa clause
+ end;
+certificate_verify(Signature, {_, PublicKey, PublicKeyParams}, Version,
+ MasterSecret, dhe_dss = Algorithm, {_, Hashes0}) ->
+ Hashes = calc_certificate_verify(Version, MasterSecret,
+ Algorithm, Hashes0),
+ public_key:verify_signature(Hashes, sha, Signature, PublicKey, PublicKeyParams).
%%--------------------------------------------------------------------
%% Function: certificate_request(ConnectionStates, CertDbRef) ->
@@ -356,7 +361,7 @@ key_exchange(client, {dh, <<?UINT32(Len), PublicKey:Len/binary>>}) ->
dh_public = PublicKey}
};
-key_exchange(server, {dh, {<<?UINT32(_), PublicKey/binary>>, _},
+key_exchange(server, {dh, {<<?UINT32(Len), PublicKey:Len/binary>>, _},
#'DHParameter'{prime = P, base = G},
KeyAlgo, ClientRandom, ServerRandom, PrivateKey}) ->
<<?UINT32(_), PBin/binary>> = crypto:mpint(P),
@@ -365,15 +370,14 @@ key_exchange(server, {dh, {<<?UINT32(_), PublicKey/binary>>, _},
GLen = byte_size(GBin),
YLen = byte_size(PublicKey),
ServerDHParams = #server_dh_params{dh_p = PBin,
- dh_g = GBin, dh_y = PublicKey},
-
- Hash =
- server_key_exchange_hash(KeyAlgo, <<ClientRandom/binary,
- ServerRandom/binary,
- ?UINT16(PLen), PBin/binary,
- ?UINT16(GLen), GBin/binary,
- ?UINT16(YLen), PublicKey/binary>>),
- Signed = digitally_signed(Hash, PrivateKey),
+ dh_g = GBin, dh_y = PublicKey},
+ Plain =
+ server_key_exchange_plain(KeyAlgo, <<ClientRandom/binary,
+ ServerRandom/binary,
+ ?UINT16(PLen), PBin/binary,
+ ?UINT16(GLen), GBin/binary,
+ ?UINT16(YLen), PublicKey/binary>>),
+ Signed = digitally_signed(Plain, PrivateKey),
#server_key_exchange{params = ServerDHParams,
signed_params = Signed}.
@@ -524,18 +528,12 @@ path_validation_alert(_, _) ->
?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE).
select_session(Hello, Port, Session, Version,
- #ssl_options{ciphers = UserSuites} = SslOpts, Cache, CacheCb) ->
+ #ssl_options{ciphers = UserSuites} = SslOpts, Cache, CacheCb, Cert) ->
SuggestedSessionId = Hello#client_hello.session_id,
SessionId = ssl_manager:server_session_id(Port, SuggestedSessionId,
SslOpts),
- Suites = case UserSuites of
- [] ->
- ssl_cipher:suites(Version);
- _ ->
- UserSuites
- end,
-
+ Suites = available_suites(Cert, UserSuites, Version),
case ssl_session:is_new(SuggestedSessionId, SessionId) of
true ->
CipherSuite =
@@ -549,7 +547,14 @@ select_session(Hello, Port, Session, Version,
{resumed, CacheCb:lookup(Cache, {Port, SessionId})}
end.
-
+available_suites(Cert, UserSuites, Version) ->
+ case UserSuites of
+ [] ->
+ ssl_cipher:filter(Cert, ssl_cipher:suites(Version));
+ _ ->
+ ssl_cipher:filter(Cert, UserSuites)
+ end.
+
cipher_suites(Suites, false) ->
[?TLS_EMPTY_RENEGOTIATION_INFO_SCSV | Suites];
cipher_suites(Suites, true) ->
@@ -812,7 +817,7 @@ dec_hs(?CERTIFICATE, <<?UINT24(ACLen), ASN1Certs:ACLen/binary>>, _, _) ->
dec_hs(?SERVER_KEY_EXCHANGE, <<?UINT16(PLen), P:PLen/binary,
?UINT16(GLen), G:GLen/binary,
?UINT16(YLen), Y:YLen/binary,
- ?UINT16(_), Sig/binary>>,
+ ?UINT16(Len), Sig:Len/binary>>,
?KEY_EXCHANGE_DIFFIE_HELLMAN, _) ->
#server_key_exchange{params = #server_dh_params{dh_p = P,dh_g = G,
dh_y = Y},
@@ -820,7 +825,6 @@ dec_hs(?SERVER_KEY_EXCHANGE, <<?UINT16(PLen), P:PLen/binary,
dec_hs(?CERTIFICATE_REQUEST,
<<?BYTE(CertTypesLen), CertTypes:CertTypesLen/binary,
?UINT16(CertAuthsLen), CertAuths:CertAuthsLen/binary>>, _, _) ->
- %% TODO: maybe we should chop up CertAuths into a list?
#certificate_request{certificate_types = CertTypes,
certificate_authorities = CertAuths};
dec_hs(?SERVER_HELLO_DONE, <<>>, _, _) ->
@@ -1086,9 +1090,8 @@ certificate_authorities_from_db(CertDbRef, PrevKey, Acc) ->
digitally_signed(Hashes, #'RSAPrivateKey'{} = Key) ->
public_key:encrypt_private(Hashes, Key,
[{rsa_pad, rsa_pkcs1_padding}]);
-digitally_signed(Hashes, #'DSAPrivateKey'{} = Key) ->
- public_key:sign(Hashes, Key).
-
+digitally_signed(Plain, #'DSAPrivateKey'{} = Key) ->
+ public_key:sign(Plain, Key).
calc_master_secret({3,0}, PremasterSecret, ClientRandom, ServerRandom) ->
ssl_ssl3:master_secret(PremasterSecret, ClientRandom, ServerRandom);
@@ -1119,23 +1122,15 @@ calc_certificate_verify({3, N}, _, Algorithm, Hashes)
when N == 1; N == 2 ->
ssl_tls1:certificate_verify(Algorithm, Hashes).
-server_key_exchange_hash(Algorithm, Value) when Algorithm == rsa;
+server_key_exchange_plain(Algorithm, Value) when Algorithm == rsa;
Algorithm == dhe_rsa ->
- MD5Context = crypto:md5_init(),
- NewMD5Context = crypto:md5_update(MD5Context, Value),
- MD5 = crypto:md5_final(NewMD5Context),
-
- SHAContext = crypto:sha_init(),
- NewSHAContext = crypto:sha_update(SHAContext, Value),
- SHA = crypto:sha_final(NewSHAContext),
-
+ MD5 = crypto:md5(Value),
+ SHA = crypto:sha(Value),
<<MD5/binary, SHA/binary>>;
-server_key_exchange_hash(dhe_dss, Value) ->
- SHAContext = crypto:sha_init(),
- NewSHAContext = crypto:sha_update(SHAContext, Value),
- crypto:sha_final(NewSHAContext).
-
+server_key_exchange_plain(dhe_dss, Value) ->
+ %% Hash will be done by crypto.
+ Value.
sig_alg(dh_anon) ->
?SIGNATURE_ANONYMOUS;
diff --git a/lib/ssl/src/ssl_record.erl b/lib/ssl/src/ssl_record.erl
index 7c4b0ee959..6b7cffaa7d 100644
--- a/lib/ssl/src/ssl_record.erl
+++ b/lib/ssl/src/ssl_record.erl
@@ -705,7 +705,6 @@ hash_and_bump_seqno(#connection_state{sequence_number = SeqNo,
is_correct_mac(Mac, Mac) ->
true;
is_correct_mac(_M,_H) ->
- io:format("Mac ~p ~n Hash: ~p~n",[_M, _H]),
false.
mac_hash({_,_}, ?NULL, _MacSecret, _SeqNo, _Type,
diff --git a/lib/ssl/src/ssl_ssl3.erl b/lib/ssl/src/ssl_ssl3.erl
index 1bf8c2b458..1cecd10e81 100644
--- a/lib/ssl/src/ssl_ssl3.erl
+++ b/lib/ssl/src/ssl_ssl3.erl
@@ -138,21 +138,18 @@ setup_keys(MasterSecret, ServerRandom, ClientRandom, HS, KML, _EKML, IVS) ->
suites() ->
[
- %% TODO: uncomment when supported
?TLS_DHE_RSA_WITH_AES_256_CBC_SHA,
- %% ?TLS_DHE_DSS_WITH_AES_256_CBC_SHA,
+ ?TLS_DHE_DSS_WITH_AES_256_CBC_SHA,
?TLS_RSA_WITH_AES_256_CBC_SHA,
?TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA,
- %% ?TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA,
+ ?TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA,
?TLS_RSA_WITH_3DES_EDE_CBC_SHA,
?TLS_DHE_RSA_WITH_AES_128_CBC_SHA,
- %% ?TLS_DHE_DSS_WITH_AES_128_CBC_SHA,
+ ?TLS_DHE_DSS_WITH_AES_128_CBC_SHA,
?TLS_RSA_WITH_AES_128_CBC_SHA,
- %%?TLS_DHE_DSS_WITH_RC4_128_SHA,
- %% ?TLS_RSA_WITH_IDEA_CBC_SHA, Not supported: in later openssl version than OTP requires
+ %% ?TLS_RSA_WITH_IDEA_CBC_SHA,
?TLS_RSA_WITH_RC4_128_SHA,
?TLS_RSA_WITH_RC4_128_MD5,
- %%?TLS_DHE_DSS_WITH_RC4_128_SHA,
?TLS_RSA_WITH_DES_CBC_SHA
].
diff --git a/lib/ssl/src/ssl_tls1.erl b/lib/ssl/src/ssl_tls1.erl
index 900b8e166d..70db632835 100644
--- a/lib/ssl/src/ssl_tls1.erl
+++ b/lib/ssl/src/ssl_tls1.erl
@@ -134,22 +134,19 @@ mac_hash(Method, Mac_write_secret, Seq_num, Type, {Major, Minor},
suites() ->
[
- %% TODO: uncomment when supported
?TLS_DHE_RSA_WITH_AES_256_CBC_SHA,
- %%?TLS_DHE_DSS_WITH_AES_256_CBC_SHA,
+ ?TLS_DHE_DSS_WITH_AES_256_CBC_SHA,
?TLS_RSA_WITH_AES_256_CBC_SHA,
?TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA,
- %%?TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA,
+ ?TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA,
?TLS_RSA_WITH_3DES_EDE_CBC_SHA,
?TLS_DHE_RSA_WITH_AES_128_CBC_SHA,
- %%?TLS_DHE_DSS_WITH_AES_128_CBC_SHA,
+ ?TLS_DHE_DSS_WITH_AES_128_CBC_SHA,
?TLS_RSA_WITH_AES_128_CBC_SHA,
- %%?TLS_DHE_DSS_WITH_RC4_128_SHA,
%%?TLS_RSA_WITH_IDEA_CBC_SHA,
?TLS_RSA_WITH_RC4_128_SHA,
?TLS_RSA_WITH_RC4_128_MD5,
?TLS_DHE_RSA_WITH_DES_CBC_SHA,
- %%TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA
?TLS_RSA_WITH_DES_CBC_SHA
].
diff --git a/lib/ssl/test/Makefile b/lib/ssl/test/Makefile
index bd86120c98..d35cafc47b 100644
--- a/lib/ssl/test/Makefile
+++ b/lib/ssl/test/Makefile
@@ -1,19 +1,19 @@
#
# %CopyrightBegin%
-#
-# Copyright Ericsson AB 1999-2009. All Rights Reserved.
-#
+#
+# Copyright Ericsson AB 1999-2010. All Rights Reserved.
+#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
# compliance with the License. You should have received a copy of the
# Erlang Public License along with this software. If not, it can be
# retrieved online at http://www.erlang.org/.
-#
+#
# Software distributed under the License is distributed on an "AS IS"
# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
# the License for the specific language governing rights and limitations
# under the License.
-#
+#
# %CopyrightEnd%
#
@@ -50,7 +50,8 @@ MODULES = \
old_ssl_protocol_SUITE \
old_transport_accept_SUITE \
old_ssl_dist_SUITE \
- make_certs
+ make_certs\
+ erl_make_certs
ERL_FILES = $(MODULES:%=%.erl)
diff --git a/lib/ssl/test/erl_make_certs.erl b/lib/ssl/test/erl_make_certs.erl
new file mode 100644
index 0000000000..1d2cea6c72
--- /dev/null
+++ b/lib/ssl/test/erl_make_certs.erl
@@ -0,0 +1,412 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%% Create test certificates
+
+-module(erl_make_certs).
+-include_lib("public_key/include/public_key.hrl").
+
+-export([make_cert/1, gen_rsa/1, verify_signature/3, write_pem/3]).
+-compile(export_all).
+
+%%--------------------------------------------------------------------
+%% @doc Create and return a der encoded certificate
+%% Option Default
+%% -------------------------------------------------------
+%% digest sha1
+%% validity {date(), date() + week()}
+%% version 3
+%% subject [] list of the following content
+%% {name, Name}
+%% {email, Email}
+%% {city, City}
+%% {state, State}
+%% {org, Org}
+%% {org_unit, OrgUnit}
+%% {country, Country}
+%% {serial, Serial}
+%% {title, Title}
+%% {dnQualifer, DnQ}
+%% issuer = {Issuer, IssuerKey} true (i.e. a ca cert is created)
+%% (obs IssuerKey migth be {Key, Password}
+%% key = KeyFile|KeyBin|rsa|dsa Subject PublicKey rsa or dsa generates key
+%%
+%%
+%% (OBS: The generated keys are for testing only)
+%% @spec ([{::atom(), ::term()}]) -> {Cert::binary(), Key::binary()}
+%% @end
+%%--------------------------------------------------------------------
+
+make_cert(Opts) ->
+ SubjectPrivateKey = get_key(Opts),
+ {TBSCert, IssuerKey} = make_tbs(SubjectPrivateKey, Opts),
+ Cert = public_key:sign(TBSCert, IssuerKey),
+ true = verify_signature(Cert, IssuerKey, undef), %% verify that the keys where ok
+ {Cert, encode_key(SubjectPrivateKey)}.
+
+%%--------------------------------------------------------------------
+%% @doc Writes pem files in Dir with FileName ++ ".pem" and FileName ++ "_key.pem"
+%% @spec (::string(), ::string(), {Cert,Key}) -> ok
+%% @end
+%%--------------------------------------------------------------------
+write_pem(Dir, FileName, {Cert, Key = {_,_,not_encrypted}}) when is_binary(Cert) ->
+ ok = public_key:der_to_pem(filename:join(Dir, FileName ++ ".pem"), [{cert, Cert, not_encrypted}]),
+ ok = public_key:der_to_pem(filename:join(Dir, FileName ++ "_key.pem"), [Key]).
+
+%%--------------------------------------------------------------------
+%% @doc Creates a rsa key (OBS: for testing only)
+%% the size are in bytes
+%% @spec (::integer()) -> {::atom(), ::binary(), ::opaque()}
+%% @end
+%%--------------------------------------------------------------------
+gen_rsa(Size) when is_integer(Size) ->
+ Key = gen_rsa2(Size),
+ {Key, encode_key(Key)}.
+
+%%--------------------------------------------------------------------
+%% @doc Creates a dsa key (OBS: for testing only)
+%% the sizes are in bytes
+%% @spec (::integer()) -> {::atom(), ::binary(), ::opaque()}
+%% @end
+%%--------------------------------------------------------------------
+gen_dsa(LSize,NSize) when is_integer(LSize), is_integer(NSize) ->
+ Key = gen_dsa2(LSize, NSize),
+ {Key, encode_key(Key)}.
+
+%%--------------------------------------------------------------------
+%% @doc Verifies cert signatures
+%% @spec (::binary(), ::tuple()) -> ::boolean()
+%% @end
+%%--------------------------------------------------------------------
+verify_signature(DerEncodedCert, DerKey, KeyParams) ->
+ Key = decode_key(DerKey),
+ case Key of
+ #'RSAPrivateKey'{modulus=Mod, publicExponent=Exp} ->
+ public_key:verify_signature(DerEncodedCert,
+ #'RSAPublicKey'{modulus=Mod, publicExponent=Exp},
+ 'NULL');
+ #'DSAPrivateKey'{p=P, q=Q, g=G, y=Y} ->
+ public_key:verify_signature(DerEncodedCert, Y, #'Dss-Parms'{p=P, q=Q, g=G});
+
+ _ ->
+ public_key:verify_signature(DerEncodedCert, Key, KeyParams)
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%% Implementation %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+get_key(Opts) ->
+ case proplists:get_value(key, Opts) of
+ undefined -> make_key(rsa, Opts);
+ rsa -> make_key(rsa, Opts);
+ dsa -> make_key(dsa, Opts);
+ Key ->
+ Password = proplists:get_value(password, Opts, no_passwd),
+ decode_key(Key, Password)
+ end.
+
+decode_key({Key, Pw}) ->
+ decode_key(Key, Pw);
+decode_key(Key) ->
+ decode_key(Key, no_passwd).
+
+
+decode_key(#'RSAPublicKey'{} = Key,_) ->
+ Key;
+decode_key(#'RSAPrivateKey'{} = Key,_) ->
+ Key;
+decode_key(#'DSAPrivateKey'{} = Key,_) ->
+ Key;
+decode_key(Der = {_,_,_}, Pw) ->
+ {ok, Key} = public_key:decode_private_key(Der, Pw),
+ Key;
+decode_key(FileOrDer, Pw) ->
+ {ok, [KeyInfo]} = public_key:pem_to_der(FileOrDer),
+ decode_key(KeyInfo, Pw).
+
+encode_key(Key = #'RSAPrivateKey'{}) ->
+ {ok, Der} = 'OTP-PUB-KEY':encode('RSAPrivateKey', Key),
+ {rsa_private_key, list_to_binary(Der), not_encrypted};
+encode_key(Key = #'DSAPrivateKey'{}) ->
+ {ok, Der} = 'OTP-PUB-KEY':encode('DSAPrivateKey', Key),
+ {dsa_private_key, list_to_binary(Der), not_encrypted}.
+
+make_tbs(SubjectKey, Opts) ->
+ Version = list_to_atom("v"++integer_to_list(proplists:get_value(version, Opts, 3))),
+ {Issuer, IssuerKey} = issuer(Opts, SubjectKey),
+
+ {Algo, Parameters} = sign_algorithm(IssuerKey, Opts),
+
+ SignAlgo = #'SignatureAlgorithm'{algorithm = Algo,
+ parameters = Parameters},
+
+ {#'OTPTBSCertificate'{serialNumber = trunc(random:uniform()*100000000)*10000 + 1,
+ signature = SignAlgo,
+ issuer = Issuer,
+ validity = validity(Opts),
+ subject = subject(proplists:get_value(subject, Opts),false),
+ subjectPublicKeyInfo = publickey(SubjectKey),
+ version = Version,
+ extensions = extensions(Opts)
+ }, IssuerKey}.
+
+issuer(Opts, SubjectKey) ->
+ IssuerProp = proplists:get_value(issuer, Opts, true),
+ case IssuerProp of
+ true -> %% Self signed
+ {subject(proplists:get_value(subject, Opts), true), SubjectKey};
+ {Issuer, IssuerKey} when is_binary(Issuer) ->
+ {issuer_der(Issuer), decode_key(IssuerKey)};
+ {File, IssuerKey} when is_list(File) ->
+ {ok, [{cert, Cert, _}|_]} = public_key:pem_to_der(File),
+ {issuer_der(Cert), decode_key(IssuerKey)}
+ end.
+
+issuer_der(Issuer) ->
+ {ok, Decoded} = public_key:pkix_decode_cert(Issuer, otp),
+ #'OTPCertificate'{tbsCertificate=Tbs} = Decoded,
+ #'OTPTBSCertificate'{subject=Subject} = Tbs,
+ Subject.
+
+subject(undefined, IsCA) ->
+ User = if IsCA -> "CA"; true -> os:getenv("USER") end,
+ Opts = [{email, User ++ "@erlang.org"},
+ {name, User},
+ {city, "Stockholm"},
+ {country, "SE"},
+ {org, "erlang"},
+ {org_unit, "testing dep"}],
+ subject(Opts);
+subject(Opts, _) ->
+ subject(Opts).
+
+subject(SubjectOpts) when is_list(SubjectOpts) ->
+ Encode = fun(Opt) ->
+ {Type,Value} = subject_enc(Opt),
+ [#'AttributeTypeAndValue'{type=Type, value=Value}]
+ end,
+ {rdnSequence, [Encode(Opt) || Opt <- SubjectOpts]}.
+
+%% Fill in the blanks
+subject_enc({name, Name}) -> {?'id-at-commonName', {printableString, Name}};
+subject_enc({email, Email}) -> {?'id-emailAddress', Email};
+subject_enc({city, City}) -> {?'id-at-localityName', {printableString, City}};
+subject_enc({state, State}) -> {?'id-at-stateOrProvinceName', {printableString, State}};
+subject_enc({org, Org}) -> {?'id-at-organizationName', {printableString, Org}};
+subject_enc({org_unit, OrgUnit}) -> {?'id-at-organizationalUnitName', {printableString, OrgUnit}};
+subject_enc({country, Country}) -> {?'id-at-countryName', Country};
+subject_enc({serial, Serial}) -> {?'id-at-serialNumber', Serial};
+subject_enc({title, Title}) -> {?'id-at-title', {printableString, Title}};
+subject_enc({dnQualifer, DnQ}) -> {?'id-at-dnQualifier', DnQ};
+subject_enc(Other) -> Other.
+
+
+extensions(Opts) ->
+ case proplists:get_value(extensions, Opts, []) of
+ false ->
+ asn1_NOVALUE;
+ Exts ->
+ lists:flatten([extension(Ext) || Ext <- default_extensions(Exts)])
+ end.
+
+default_extensions(Exts) ->
+ Def = [{key_usage,undefined},
+ {subject_altname, undefined},
+ {issuer_altname, undefined},
+ {basic_constraints, default},
+ {name_constraints, undefined},
+ {policy_constraints, undefined},
+ {ext_key_usage, undefined},
+ {inhibit_any, undefined},
+ {auth_key_id, undefined},
+ {subject_key_id, undefined},
+ {policy_mapping, undefined}],
+ Filter = fun({Key, _}, D) -> lists:keydelete(Key, 1, D) end,
+ Exts ++ lists:foldl(Filter, Def, Exts).
+
+extension({_, undefined}) -> [];
+extension({basic_constraints, Data}) ->
+ case Data of
+ default ->
+ #'Extension'{extnID = ?'id-ce-basicConstraints',
+ extnValue = #'BasicConstraints'{cA=true},
+ critical=true};
+ false ->
+ [];
+ Len when is_integer(Len) ->
+ #'Extension'{extnID = ?'id-ce-basicConstraints',
+ extnValue = #'BasicConstraints'{cA=true, pathLenConstraint=Len},
+ critical=true};
+ _ ->
+ #'Extension'{extnID = ?'id-ce-basicConstraints',
+ extnValue = Data}
+ end;
+extension({Id, Data, Critical}) ->
+ #'Extension'{extnID = Id, extnValue = Data, critical = Critical}.
+
+
+publickey(#'RSAPrivateKey'{modulus=N, publicExponent=E}) ->
+ Public = #'RSAPublicKey'{modulus=N, publicExponent=E},
+ Algo = #'PublicKeyAlgorithm'{algorithm= ?rsaEncryption, parameters='NULL'},
+ #'OTPSubjectPublicKeyInfo'{algorithm = Algo,
+ subjectPublicKey = Public};
+publickey(#'DSAPrivateKey'{p=P, q=Q, g=G, y=Y}) ->
+ Algo = #'PublicKeyAlgorithm'{algorithm= ?'id-dsa',
+ parameters=#'Dss-Parms'{p=P, q=Q, g=G}},
+ #'OTPSubjectPublicKeyInfo'{algorithm = Algo, subjectPublicKey = Y}.
+
+validity(Opts) ->
+ DefFrom0 = date(),
+ DefTo0 = calendar:gregorian_days_to_date(calendar:date_to_gregorian_days(date())+7),
+ {DefFrom, DefTo} = proplists:get_value(validity, Opts, {DefFrom0, DefTo0}),
+ Format = fun({Y,M,D}) -> lists:flatten(io_lib:format("~w~2..0w~2..0w000000Z",[Y,M,D])) end,
+ #'Validity'{notBefore={generalTime, Format(DefFrom)},
+ notAfter ={generalTime, Format(DefTo)}}.
+
+sign_algorithm(#'RSAPrivateKey'{}, Opts) ->
+ Type = case proplists:get_value(digest, Opts, sha1) of
+ sha1 -> ?'sha1WithRSAEncryption';
+ sha512 -> ?'sha512WithRSAEncryption';
+ sha384 -> ?'sha384WithRSAEncryption';
+ sha256 -> ?'sha256WithRSAEncryption';
+ md5 -> ?'md5WithRSAEncryption';
+ md2 -> ?'md2WithRSAEncryption'
+ end,
+ {Type, 'NULL'};
+sign_algorithm(#'DSAPrivateKey'{p=P, q=Q, g=G}, _Opts) ->
+ {?'id-dsa-with-sha1', #'Dss-Parms'{p=P, q=Q, g=G}}.
+
+make_key(rsa, _Opts) ->
+ %% (OBS: for testing only)
+ gen_rsa2(64);
+make_key(dsa, _Opts) ->
+ gen_dsa2(128, 20). %% Bytes i.e. {1024, 160}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% RSA key generation (OBS: for testing only)
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+-define(SMALL_PRIMES, [65537,97,89,83,79,73,71,67,61,59,53,
+ 47,43,41,37,31,29,23,19,17,13,11,7,5,3]).
+
+gen_rsa2(Size) ->
+ P = prime(Size),
+ Q = prime(Size),
+ N = P*Q,
+ Tot = (P - 1) * (Q - 1),
+ [E|_] = lists:dropwhile(fun(Candidate) -> (Tot rem Candidate) == 0 end, ?SMALL_PRIMES),
+ {D1,D2} = extended_gcd(E, Tot),
+ D = erlang:max(D1,D2),
+ case D < E of
+ true ->
+ gen_rsa2(Size);
+ false ->
+ {Co1,Co2} = extended_gcd(Q, P),
+ Co = erlang:max(Co1,Co2),
+ #'RSAPrivateKey'{version = 'two-prime',
+ modulus = N,
+ publicExponent = E,
+ privateExponent = D,
+ prime1 = P,
+ prime2 = Q,
+ exponent1 = D rem (P-1),
+ exponent2 = D rem (Q-1),
+ coefficient = Co
+ }
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% DSA key generation (OBS: for testing only)
+%% See http://en.wikipedia.org/wiki/Digital_Signature_Algorithm
+%% and the fips_186-3.pdf
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+gen_dsa2(LSize, NSize) ->
+ Q = prime(NSize), %% Choose N-bit prime Q
+ X0 = prime(LSize),
+ P0 = prime((LSize div 2) +1),
+
+ %% Choose L-bit prime modulus P such that p–1 is a multiple of q.
+ case dsa_search(X0 div (2*Q*P0), P0, Q, 1000) of
+ error ->
+ gen_dsa2(LSize, NSize);
+ P ->
+ G = crypto:mod_exp(2, (P-1) div Q, P), % Choose G a number whose multiplicative order modulo p is q.
+ %% such that This may be done by setting g = h^(p–1)/q mod p, commonly h=2 is used.
+
+ X = prime(20), %% Choose x by some random method, where 0 < x < q.
+ Y = crypto:mod_exp(G, X, P), %% Calculate y = g^x mod p.
+
+ #'DSAPrivateKey'{version=0, p=P, q=Q, g=G, y=Y, x=X}
+ end.
+
+%% See fips_186-3.pdf
+dsa_search(T, P0, Q, Iter) when Iter > 0 ->
+ P = 2*T*Q*P0 + 1,
+ case is_prime(crypto:mpint(P), 50) of
+ true -> P;
+ false -> dsa_search(T+1, P0, Q, Iter-1)
+ end;
+dsa_search(_,_,_,_) ->
+ error.
+
+
+%%%%%%% Crypto Math %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+prime(ByteSize) ->
+ Rand = odd_rand(ByteSize),
+ crypto:erlint(prime_odd(Rand, 0)).
+
+prime_odd(Rand, N) ->
+ case is_prime(Rand, 50) of
+ true ->
+ Rand;
+ false ->
+ NotPrime = crypto:erlint(Rand),
+ prime_odd(crypto:mpint(NotPrime+2), N+1)
+ end.
+
+%% see http://en.wikipedia.org/wiki/Fermat_primality_test
+is_prime(_, 0) -> true;
+is_prime(Candidate, Test) ->
+ CoPrime = odd_rand(<<0,0,0,4, 10000:32>>, Candidate),
+ case crypto:mod_exp(CoPrime, Candidate, Candidate) of
+ CoPrime -> is_prime(Candidate, Test-1);
+ _ -> false
+ end.
+
+odd_rand(Size) ->
+ Min = 1 bsl (Size*8-1),
+ Max = (1 bsl (Size*8))-1,
+ odd_rand(crypto:mpint(Min), crypto:mpint(Max)).
+
+odd_rand(Min,Max) ->
+ Rand = <<Sz:32, _/binary>> = crypto:rand_uniform(Min,Max),
+ BitSkip = (Sz+4)*8-1,
+ case Rand of
+ Odd = <<_:BitSkip, 1:1>> -> Odd;
+ Even = <<_:BitSkip, 0:1>> ->
+ crypto:mpint(crypto:erlint(Even)+1)
+ end.
+
+extended_gcd(A, B) ->
+ case A rem B of
+ 0 ->
+ {0, 1};
+ N ->
+ {X, Y} = extended_gcd(B, N),
+ {Y, X-Y*(A div B)}
+ end.
diff --git a/lib/ssl/test/ssl_basic_SUITE.erl b/lib/ssl/test/ssl_basic_SUITE.erl
index ad87cfcba1..0d9a912e30 100644
--- a/lib/ssl/test/ssl_basic_SUITE.erl
+++ b/lib/ssl/test/ssl_basic_SUITE.erl
@@ -53,11 +53,15 @@
init_per_suite(Config) ->
crypto:start(),
ssl:start(),
+
+ %% make rsa certs using oppenssl
Result =
(catch make_certs:all(?config(data_dir, Config),
?config(priv_dir, Config))),
test_server:format("Make certs ~p~n", [Result]),
- ssl_test_lib:cert_options(Config).
+
+ NewConfig = ssl_test_lib:make_dsa_cert(Config),
+ ssl_test_lib:cert_options(NewConfig).
%%--------------------------------------------------------------------
%% Function: end_per_suite(Config) -> _
@@ -105,8 +109,10 @@ init_per_testcase(no_authority_key_identifier, Config) ->
ssl:start(),
Config;
-init_per_testcase(TestCase, Config) when TestCase == ciphers_ssl3;
- TestCase == ciphers_ssl3_openssl_names ->
+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 ->
ssl:stop(),
application:load(ssl),
application:set_env(ssl, protocol_version, sslv3),
@@ -124,7 +130,6 @@ init_per_testcase(protocol_versions, Config) ->
init_per_testcase(empty_protocol_versions, Config) ->
ssl:stop(),
application:load(ssl),
- %% For backwards compatibility sslv2 should be filtered out.
application:set_env(ssl, protocol_version, []),
ssl:start(),
Config;
@@ -165,8 +170,10 @@ end_per_testcase(session_cache_process_mnesia, 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_ssl3;
- TestCase == ciphers_ssl3_openssl_names;
+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),
@@ -193,30 +200,37 @@ all(doc) ->
all(suite) ->
[app, alerts, connection_info, protocol_versions,
- empty_protocol_versions, controlling_process, controller_dies,
- client_closes_socket, peercert, connect_dist, peername, sockname,
- socket_options, misc_ssl_options, versions, cipher_suites,
- upgrade, upgrade_with_timeout, tcp_connect, ipv6, ekeyfile,
- ecertfile, ecacertfile, eoptions, shutdown, shutdown_write,
- shutdown_both, shutdown_error, ciphers, ciphers_ssl3,
- ciphers_openssl_names, send_close,
- close_transport_accept, dh_params, server_verify_peer_passive,
- server_verify_peer_active, server_verify_peer_active_once,
- server_verify_none_passive, server_verify_none_active,
- server_verify_none_active_once, 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_active_once,
- session_cache_process_list, session_cache_process_mnesia,
- 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,
- validate_extensions_fun, no_authority_key_identifier,
- invalid_signature_client, invalid_signature_server, cert_expired
+ empty_protocol_versions, controlling_process, controller_dies,
+ client_closes_socket, peercert, connect_dist, peername, sockname,
+ socket_options, misc_ssl_options, versions, cipher_suites,
+ upgrade, upgrade_with_timeout, tcp_connect, 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,
+ send_close,
+ close_transport_accept, dh_params, server_verify_peer_passive,
+ server_verify_peer_active, server_verify_peer_active_once,
+ server_verify_none_passive, server_verify_none_active,
+ server_verify_none_active_once, 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_active_once,
+ session_cache_process_list, session_cache_process_mnesia,
+ 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,
+ validate_extensions_fun, no_authority_key_identifier,
+ invalid_signature_client, invalid_signature_server, cert_expired
].
%% Test cases starts here.
@@ -1394,66 +1408,129 @@ shutdown_error(Config) when is_list(Config) ->
{error, closed} = ssl:shutdown(Listen, read_write).
%%-------------------------------------------------------------------
-ciphers(doc) ->
- ["Test all ssl cipher suites in highest support ssl/tls version"];
+ciphers_rsa_signed_certs(doc) ->
+ ["Test all rsa ssl cipher suites in highest support ssl/tls version"];
-ciphers(suite) ->
+ciphers_rsa_signed_certs(suite) ->
[];
-ciphers(Config) when is_list(Config) ->
+ciphers_rsa_signed_certs(Config) when is_list(Config) ->
Version =
ssl_record:protocol_version(ssl_record:highest_protocol_version([])),
- Ciphers = ssl:cipher_suites(),
+ Ciphers = ssl_test_lib:rsa_suites(),
test_server:format("tls1 erlang cipher suites ~p~n", [Ciphers]),
- Result = lists:map(fun(Cipher) ->
- cipher(Cipher, Version, Config) end,
- Ciphers),
- case lists:flatten(Result) of
- [] ->
- ok;
- Error ->
- test_server:format("Cipher suite errors: ~p~n", [Error]),
- test_server:fail(cipher_suite_failed_see_test_case_log)
- end.
+ run_suites(Ciphers, Version, Config, rsa).
-ciphers_ssl3(doc) ->
- ["Test all ssl cipher suites in ssl3"];
+ciphers_rsa_signed_certs_ssl3(doc) ->
+ ["Test all rsa ssl cipher suites in ssl3"];
-ciphers_ssl3(suite) ->
+ciphers_rsa_signed_certs_ssl3(suite) ->
[];
-ciphers_ssl3(Config) when is_list(Config) ->
+ciphers_rsa_signed_certs_ssl3(Config) when is_list(Config) ->
Version =
ssl_record:protocol_version({3,0}),
- Ciphers = ssl:cipher_suites(),
+ Ciphers = ssl_test_lib:rsa_suites(),
test_server:format("ssl3 erlang cipher suites ~p~n", [Ciphers]),
- Result = lists:map(fun(Cipher) ->
- cipher(Cipher, Version, Config) end,
- Ciphers),
- case lists:flatten(Result) of
- [] ->
- ok;
- Error ->
- test_server:format("Cipher suite errors: ~p~n", [Error]),
- test_server:fail(cipher_suite_failed_see_test_case_log)
- end.
+ run_suites(Ciphers, Version, Config, rsa).
+
+ciphers_rsa_signed_certs_openssl_names(doc) ->
+ ["Test all rsa ssl cipher suites in highest support ssl/tls version"];
+
+ciphers_rsa_signed_certs_openssl_names(suite) ->
+ [];
+
+ciphers_rsa_signed_certs_openssl_names(Config) when is_list(Config) ->
+ Version =
+ ssl_record:protocol_version(ssl_record:highest_protocol_version([])),
+ Ciphers = ssl_test_lib:openssl_rsa_suites(),
+ test_server:format("tls1 openssl cipher suites ~p~n", [Ciphers]),
+ run_suites(Ciphers, Version, Config, rsa).
-ciphers_openssl_names(doc) ->
- ["Test all ssl cipher suites in highest support ssl/tls version"];
+
+ciphers_rsa_signed_certs_openssl_names_ssl3(doc) ->
+ ["Test all dsa ssl cipher suites in ssl3"];
-ciphers_openssl_names(suite) ->
+ciphers_rsa_signed_certs_openssl_names_ssl3(suite) ->
[];
-ciphers_openssl_names(Config) when is_list(Config) ->
+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"];
+
+ciphers_dsa_signed_certs(suite) ->
+ [];
+
+ciphers_dsa_signed_certs(Config) when is_list(Config) ->
Version =
ssl_record:protocol_version(ssl_record:highest_protocol_version([])),
- Ciphers = ssl:cipher_suites(openssl),
+ 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]),
+ 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"];
+
+ciphers_dsa_signed_certs_openssl_names(suite) ->
+ [];
+
+ciphers_dsa_signed_certs_openssl_names(Config) when is_list(Config) ->
+ Version =
+ ssl_record:protocol_version(ssl_record:highest_protocol_version([])),
+
+ Ciphers = ssl_test_lib:openssl_dsa_suites(),
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).
+
+
+run_suites(Ciphers, Version, Config, Type) ->
+ {ClientOpts, ServerOpts} =
+ case Type of
+ rsa ->
+ {?config(client_opts, Config),
+ ?config(server_opts, Config)};
+ dsa ->
+ {?config(client_opts, Config),
+ ?config(server_dsa_opts, Config)}
+ end,
+
Result = lists:map(fun(Cipher) ->
- cipher(Cipher, Version, Config) end,
+ cipher(Cipher, Version, Config, ClientOpts, ServerOpts) end,
Ciphers),
case lists:flatten(Result) of
[] ->
@@ -1463,12 +1540,14 @@ ciphers_openssl_names(Config) when is_list(Config) ->
test_server:fail(cipher_suite_failed_see_test_case_log)
end.
+erlang_cipher_suite(Suite) when is_list(Suite)->
+ ssl_cipher:suite_definition(ssl_cipher:openssl_suite(Suite));
+erlang_cipher_suite(Suite) ->
+ Suite.
-cipher(CipherSuite, Version, Config) ->
+cipher(CipherSuite, Version, Config, ClientOpts, ServerOpts) ->
process_flag(trap_exit, true),
test_server:format("Testing CipherSuite ~p~n", [CipherSuite]),
- 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()},
@@ -1507,11 +1586,6 @@ cipher(CipherSuite, Version, Config) ->
[{ErlangCipherSuite, Error}]
end.
-erlang_cipher_suite(Suite) when is_list(Suite)->
- ssl_cipher:suite_definition(ssl_cipher:openssl_suite(Suite));
-erlang_cipher_suite(Suite) ->
- Suite.
-
%%--------------------------------------------------------------------
reuse_session(doc) ->
["Test reuse of sessions (short handshake)"];
@@ -2664,7 +2738,7 @@ invalid_signature_client(Config) when is_list(Config) ->
{host, Hostname},
{from, self()},
{options, NewClientOpts}]),
-
+
tcp_delivery_workaround(Server, {error, "bad certificate"},
Client, {error,"bad certificate"}).
@@ -2971,4 +3045,3 @@ erlang_ssl_receive(Socket, Data) ->
after ?SLEEP * 3 ->
test_server:fail({did_not_get, Data})
end.
-
diff --git a/lib/ssl/test/ssl_test_lib.erl b/lib/ssl/test/ssl_test_lib.erl
index 46b6eb401d..d11acc8130 100644
--- a/lib/ssl/test/ssl_test_lib.erl
+++ b/lib/ssl/test/ssl_test_lib.erl
@@ -318,6 +318,25 @@ cert_options(Config) ->
| Config].
+make_dsa_cert(Config) ->
+ ServerCaInfo = {ServerCaCert, _} = erl_make_certs:make_cert([{key, dsa}]),
+ {ServerCert, ServerCertKey} = erl_make_certs:make_cert([{key, dsa}, {issuer, ServerCaInfo}]),
+ ServerCaCertFile = filename:join([?config(priv_dir, Config),
+ "server", "dsa_cacerts.pem"]),
+ ServerCertFile = filename:join([?config(priv_dir, Config),
+ "server", "dsa_cert.pem"]),
+ ServerKeyFile = filename:join([?config(priv_dir, Config),
+ "server", "dsa_key.pem"]),
+
+ public_key:der_to_pem(ServerCaCertFile, [{cert, ServerCaCert, not_encrypted}]),
+ public_key:der_to_pem(ServerCertFile, [{cert, ServerCert, not_encrypted}]),
+ public_key:der_to_pem(ServerKeyFile, [ServerCertKey]),
+
+ [{server_dsa_opts, [{ssl_imp, new},{reuseaddr, true},
+ {cacertfile, ServerCaCertFile},
+ {certfile, ServerCertFile}, {keyfile, ServerKeyFile}]} | Config].
+
+
start_upgrade_server(Args) ->
Result = spawn_link(?MODULE, run_upgrade_server, [Args]),
receive
@@ -529,3 +548,42 @@ send_selected_port(Pid, 0, Socket) ->
Pid ! {self(), {port, NewPort}};
send_selected_port(_,_,_) ->
ok.
+
+rsa_suites() ->
+ lists:filter(fun({dhe_dss, _, _}) ->
+ false;
+ (_) ->
+ true
+ end,
+ ssl:cipher_suites()).
+
+dsa_suites() ->
+ lists:filter(fun({dhe_dss, _, _}) ->
+ true;
+ (_) ->
+ false
+ end,
+ ssl:cipher_suites()).
+
+
+openssl_rsa_suites() ->
+ Ciphers = ssl:cipher_suites(openssl),
+ lists:filter(fun(Str) ->
+ case re:run(Str,"DSS",[]) of
+ nomatch ->
+ true;
+ _ ->
+ false
+ end
+ end, Ciphers).
+
+openssl_dsa_suites() ->
+ Ciphers = ssl:cipher_suites(openssl),
+ lists:filter(fun(Str) ->
+ case re:run(Str,"DSS",[]) of
+ nomatch ->
+ false;
+ _ ->
+ true
+ end
+ end, Ciphers).
diff --git a/lib/ssl/test/ssl_to_openssl_SUITE.erl b/lib/ssl/test/ssl_to_openssl_SUITE.erl
index 1c18f10038..e4c77b2fb4 100644
--- a/lib/ssl/test/ssl_to_openssl_SUITE.erl
+++ b/lib/ssl/test/ssl_to_openssl_SUITE.erl
@@ -56,7 +56,8 @@ init_per_suite(Config) ->
(catch make_certs:all(?config(data_dir, Config),
?config(priv_dir, Config))),
test_server:format("Make certs ~p~n", [Result]),
- ssl_test_lib:cert_options(Config)
+ NewConfig = ssl_test_lib:make_dsa_cert(Config),
+ ssl_test_lib:cert_options(NewConfig)
end.
%%--------------------------------------------------------------------
@@ -142,6 +143,7 @@ all(doc) ->
all(suite) ->
[erlang_client_openssl_server,
erlang_server_openssl_client,
+ 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,
@@ -157,7 +159,8 @@ all(suite) ->
tls1_erlang_client_openssl_server_client_cert,
tls1_erlang_server_openssl_client_client_cert,
tls1_erlang_server_erlang_client_client_cert,
- ciphers,
+ ciphers_rsa_signed_certs,
+ ciphers_dsa_signed_certs,
erlang_client_bad_openssl_server,
expired_session,
ssl2_erlang_server_openssl_client
@@ -247,6 +250,43 @@ erlang_server_openssl_client(Config) when is_list(Config) ->
%%--------------------------------------------------------------------
+erlang_server_openssl_client_dsa_cert(doc) ->
+ ["Test erlang server with openssl client"];
+erlang_server_openssl_client_dsa_cert(suite) ->
+ [];
+erlang_server_openssl_client_dsa_cert(Config) when is_list(Config) ->
+ process_flag(trap_exit, true),
+ ServerOpts = ?config(server_dsa_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, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+
+ Cmd = "openssl s_client -port " ++ integer_to_list(Port) ++
+ " -host localhost -tls1 -msg",
+
+ 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),
+
+ ssl_test_lib:close(Server),
+
+ close_port(OpenSslPort),
+ process_flag(trap_exit, false),
+ ok.
+
+
+%%--------------------------------------------------------------------
+
erlang_server_openssl_client_reuse_session(doc) ->
["Test erlang server with openssl client that reconnects with the"
"same session id, to test reusing of sessions."];
@@ -881,19 +921,46 @@ tls1_erlang_server_erlang_client_client_cert(Config) when is_list(Config) ->
ok.
%%--------------------------------------------------------------------
-ciphers(doc) ->
- [""];
+ciphers_rsa_signed_certs(doc) ->
+ ["Test cipher suites that uses rsa certs"];
+
+ciphers_rsa_signed_certs(suite) ->
+ [];
+
+ciphers_rsa_signed_certs(Config) when is_list(Config) ->
+ Version =
+ ssl_record:protocol_version(ssl_record:highest_protocol_version([])),
+
+ Ciphers = ssl_test_lib:rsa_suites(),
+ run_suites(Ciphers, Version, Config, rsa).
+
+
+ciphers_dsa_signed_certs(doc) ->
+ ["Test cipher suites that uses dsa certs"];
-ciphers(suite) ->
+ciphers_dsa_signed_certs(suite) ->
[];
-ciphers(Config) when is_list(Config) ->
+ciphers_dsa_signed_certs(Config) when is_list(Config) ->
Version =
ssl_record:protocol_version(ssl_record:highest_protocol_version([])),
- Ciphers = ssl:cipher_suites(),
+ Ciphers = ssl_test_lib:dsa_suites(),
+ run_suites(Ciphers, Version, Config, dsa).
+
+run_suites(Ciphers, Version, Config, Type) ->
+ {ClientOpts, ServerOpts} =
+ case Type of
+ rsa ->
+ {?config(client_opts, Config),
+ ?config(server_opts, Config)};
+ dsa ->
+ {?config(client_opts, Config),
+ ?config(server_dsa_opts, Config)}
+ end,
+
Result = lists:map(fun(Cipher) ->
- cipher(Cipher, Version, Config) end,
+ cipher(Cipher, Version, Config, ClientOpts, ServerOpts) end,
Ciphers),
case lists:flatten(Result) of
[] ->
@@ -902,12 +969,12 @@ ciphers(Config) when is_list(Config) ->
test_server:format("Cipher suite errors: ~p~n", [Error]),
test_server:fail(cipher_suite_failed_see_test_case_log)
end.
-
-cipher(CipherSuite, Version, Config) ->
+
+
+
+cipher(CipherSuite, Version, Config, ClientOpts, ServerOpts) ->
process_flag(trap_exit, true),
test_server:format("Testing CipherSuite ~p~n", [CipherSuite]),
- ClientOpts = ?config(client_opts, Config),
- ServerOpts = ?config(server_opts, Config),
{ClientNode, _ServerNode, Hostname} = ssl_test_lib:run_where(Config),
Port = ssl_test_lib:inet_port(node()),
diff --git a/lib/ssl/vsn.mk b/lib/ssl/vsn.mk
index e3db7008e3..5d8be1cd0b 100644
--- a/lib/ssl/vsn.mk
+++ b/lib/ssl/vsn.mk
@@ -19,9 +19,12 @@
SSL_VSN = 3.11.1
-TICKETS = OTP-8588 \
+TICKETS = OTP-8679 \
+ OTP-7047 \
+ OTP-7049 \
OTP-8568 \
- OTP-7049
+ OTP-8587 \
+ OTP-8588
#TICKETS_3.11 = OTP-8517 \
# OTP-7046 \