From c5ae65889fc0dbaf12bbcabc93410245bbc11cc1 Mon Sep 17 00:00:00 2001
From: Ingela Anderton Andin <ingela@erlang.org>
Date: Mon, 6 May 2013 08:49:20 +0200
Subject: ssl: Only send ECC-hello extension if ECC-cipher suites are
 advertised

---
 lib/ssl/src/ssl_cipher.erl           |  5 ++++
 lib/ssl/src/ssl_handshake.erl        | 50 ++++++++++++++++++++++++++++--------
 lib/ssl/test/ssl_npn_hello_SUITE.erl |  8 +++---
 lib/ssl/test/ssl_test_lib.erl        | 10 ++++++++
 4 files changed, 60 insertions(+), 13 deletions(-)

(limited to 'lib')

diff --git a/lib/ssl/src/ssl_cipher.erl b/lib/ssl/src/ssl_cipher.erl
index accea63344..dc413d6dfc 100644
--- a/lib/ssl/src/ssl_cipher.erl
+++ b/lib/ssl/src/ssl_cipher.erl
@@ -278,6 +278,11 @@ srp_suites() ->
 %% TLS v1.1 suites
 suite_definition(?TLS_NULL_WITH_NULL_NULL) ->
     {null, null, null, null};
+%% RFC 5746 - Not a real cipher suite used to signal empty "renegotiation_info" extension
+%% to avoid handshake failure from old servers that do not ignore
+%% hello extension data as they should.
+suite_definition(?TLS_EMPTY_RENEGOTIATION_INFO_SCSV) ->
+    {null, null, null, null};
 %% suite_definition(?TLS_RSA_WITH_NULL_MD5) ->
 %%     {rsa, null, md5, default_prf};
 %% suite_definition(?TLS_RSA_WITH_NULL_SHA) ->
diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl
index 1cca644956..e358cbe9bb 100644
--- a/lib/ssl/src/ssl_handshake.erl
+++ b/lib/ssl/src/ssl_handshake.erl
@@ -1368,8 +1368,8 @@ enc_hs(#client_hello{client_version = {Major, Minor},
     BinCipherSuites = list_to_binary(CipherSuites),
     CsLength = byte_size(BinCipherSuites),
     Extensions0 = hello_extensions(RenegotiationInfo, SRP, NextProtocolNegotiation)
-	++ hello_extensions(EcPointFormats)
-	++ hello_extensions(EllipticCurves),
+	++ ec_hello_extensions(lists:map(fun ssl_cipher:suite_definition/1, CipherSuites), EcPointFormats)
+	++ ec_hello_extensions(lists:map(fun ssl_cipher:suite_definition/1, CipherSuites), EllipticCurves),
     Extensions1 = if
 		      Major == 3, Minor >=3 -> Extensions0 ++ hello_extensions(HashSigns);
 		      true -> Extensions0
@@ -1384,20 +1384,21 @@ enc_hs(#client_hello{client_version = {Major, Minor},
 enc_hs(#server_hello{server_version = {Major, Minor},
 		     random = Random,
 		     session_id = Session_ID,
-		     cipher_suite = Cipher_suite,
+		     cipher_suite = CipherSuite,
 		     compression_method = Comp_method,
 		     renegotiation_info = RenegotiationInfo,
 		     ec_point_formats = EcPointFormats,
 		     elliptic_curves = EllipticCurves,
 		     next_protocol_negotiation = NextProtocolNegotiation}, _Version) ->
     SID_length = byte_size(Session_ID),
+    CipherSuites = [ssl_cipher:suite_definition(CipherSuite)],
     Extensions  = hello_extensions(RenegotiationInfo, NextProtocolNegotiation)
-	++ hello_extensions(EcPointFormats)
-	++ hello_extensions(EllipticCurves),
+	++ ec_hello_extensions(CipherSuites, EcPointFormats)
+	++ ec_hello_extensions(CipherSuites, EllipticCurves),
     ExtensionsBin = enc_hello_extensions(Extensions),
     {?SERVER_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary,
 		     ?BYTE(SID_length), Session_ID/binary,
-                     Cipher_suite/binary, ?BYTE(Comp_method), ExtensionsBin/binary>>};
+                     CipherSuite/binary, ?BYTE(Comp_method), ExtensionsBin/binary>>};
 enc_hs(#certificate{asn1_certificates = ASN1CertList}, _Version) ->
     ASN1Certs = certs_from_list(ASN1CertList),
     ACLen = erlang:iolist_size(ASN1Certs),
@@ -1519,6 +1520,24 @@ enc_sign(_HashSign, Sign, _Version) ->
 	SignLen = byte_size(Sign),
 	<<?UINT16(SignLen), Sign/binary>>.
 
+
+ec_hello_extensions(CipherSuites, #elliptic_curves{} = Info) ->
+    case advertises_ec_ciphers(CipherSuites) of
+	true ->
+	    [Info];
+	false ->
+	    []
+    end;
+ec_hello_extensions(CipherSuites, #ec_point_formats{} = Info) ->
+    case advertises_ec_ciphers(CipherSuites) of
+	true ->
+	    [Info];
+	false ->
+	    []
+    end;
+ec_hello_extensions(_, undefined) ->
+    [].
+
 hello_extensions(RenegotiationInfo, NextProtocolNegotiation) ->
     hello_extensions(RenegotiationInfo) ++ next_protocol_extension(NextProtocolNegotiation).
 
@@ -1527,15 +1546,26 @@ hello_extensions(RenegotiationInfo, SRP, NextProtocolNegotiation) ->
 	++ hello_extensions(SRP)
 	++ next_protocol_extension(NextProtocolNegotiation).
 
+advertises_ec_ciphers([]) ->
+    false;
+advertises_ec_ciphers([{ecdh_ecdsa, _,_,_} | _]) ->
+    true;
+advertises_ec_ciphers([{ecdhe_ecdsa, _,_,_} | _]) ->
+    true;
+advertises_ec_ciphers([{ecdh_rsa, _,_,_} | _]) ->
+    true;
+advertises_ec_ciphers([{ecdhe_rsa, _,_,_} | _]) ->
+    true;
+advertises_ec_ciphers([{ecdh_anon, _,_,_} | _]) ->
+    true;
+advertises_ec_ciphers([_| Rest]) ->
+    advertises_ec_ciphers(Rest).
+
 %% Renegotiation info
 hello_extensions(#renegotiation_info{renegotiated_connection = undefined}) ->
     [];
 hello_extensions(#renegotiation_info{} = Info) ->
     [Info];
-hello_extensions(#elliptic_curves{} = Info) ->
-    [Info];
-hello_extensions(#ec_point_formats{} = Info) ->
-    [Info];
 hello_extensions(#srp{} = Info) ->
     [Info];
 hello_extensions(#hash_sign_algos{} = Info) ->
diff --git a/lib/ssl/test/ssl_npn_hello_SUITE.erl b/lib/ssl/test/ssl_npn_hello_SUITE.erl
index 0bcc0b1fc2..43fa72ea28 100644
--- a/lib/ssl/test/ssl_npn_hello_SUITE.erl
+++ b/lib/ssl/test/ssl_npn_hello_SUITE.erl
@@ -25,6 +25,8 @@
 -compile(export_all).
 -include("ssl_handshake.hrl").
 -include("ssl_record.hrl").
+-include("ssl_cipher.hrl").
+-include("ssl_internal.hrl").
 -include_lib("common_test/include/ct.hrl").
 
 %%--------------------------------------------------------------------
@@ -98,7 +100,7 @@ create_client_handshake(Npn) ->
         client_version = {1, 2},
         random = <<1:256>>,
         session_id = <<>>,
-        cipher_suites = "",
+        cipher_suites = [?TLS_DHE_DSS_WITH_DES_CBC_SHA],
         compression_methods = "",
         next_protocol_negotiation = Npn,
         renegotiation_info = #renegotiation_info{}
@@ -109,7 +111,7 @@ create_server_handshake(Npn) ->
         server_version = {1, 2},
         random = <<1:256>>,
         session_id = <<>>,
-        cipher_suite = <<1,2>>,
+        cipher_suite = ?TLS_DHE_DSS_WITH_DES_CBC_SHA,
         compression_method = 1,
         next_protocol_negotiation = Npn,
         renegotiation_info = #renegotiation_info{}
@@ -121,7 +123,7 @@ create_connection_states() ->
             security_parameters = #security_parameters{
                 server_random = <<1:256>>,
                 compression_algorithm = 1,
-                cipher_suite = <<1, 2>>
+                cipher_suite = ?TLS_DHE_DSS_WITH_DES_CBC_SHA
             }
         },
 
diff --git a/lib/ssl/test/ssl_test_lib.erl b/lib/ssl/test/ssl_test_lib.erl
index 6b082b4eec..a8ff5187b6 100644
--- a/lib/ssl/test/ssl_test_lib.erl
+++ b/lib/ssl/test/ssl_test_lib.erl
@@ -827,6 +827,16 @@ psk_suites() ->
 	 {rsa_psk, aes_256_cbc, sha}],
     ssl_cipher:filter_suites(Suites).
 
+psk_anon_suites() ->
+    [{psk, rc4_128, sha},
+     {psk, '3des_ede_cbc', sha},
+     {psk, aes_128_cbc, sha},
+     {psk, aes_256_cbc, sha},
+     {dhe_psk, rc4_128, sha},
+     {dhe_psk, '3des_ede_cbc', sha},
+     {dhe_psk, aes_128_cbc, sha},
+     {dhe_psk, aes_256_cbc, sha}].
+
 srp_suites() ->
     Suites =
 	[{srp_anon, '3des_ede_cbc', sha},
-- 
cgit v1.2.3