Use ChaCha only if prioritized by clnt
authorTodd Short <tshort@akamai.com>
Mon, 21 Dec 2015 20:19:29 +0000 (15:19 -0500)
committerPauli <paul.dale@oracle.com>
Wed, 29 Nov 2017 21:13:08 +0000 (07:13 +1000)
IFF the client has ChaCha first, and server cipher priority is used,
and the new SSL_OP_PRIORITIZE_CHACHA_FOR_MOBILE option is used,
then reprioritize ChaCha above everything else. This way, A matching
ChaCha cipher will be selected if there is a match. If no ChaCha ciphers
match, then the other ciphers are used.

Reviewed-by: Paul Dale <paul.dale@oracle.com>
Reviewed-by: Tim Hudson <tjh@openssl.org>
Reviewed-by: Andy Polyakov <appro@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/4436)

16 files changed:
apps/apps.h
doc/man1/s_server.pod
doc/man3/SSL_CONF_cmd.pod
doc/man3/SSL_CTX_set_options.pod
include/openssl/ssl.h
ssl/s3_lib.c
ssl/ssl_conf.c
test/handshake_helper.c
test/handshake_helper.h
test/recipes/80-test_ssl_new.t
test/ssl-tests/25-cipher.conf [new file with mode: 0644]
test/ssl-tests/25-cipher.conf.in [new file with mode: 0644]
test/ssl_test.c
test/ssl_test_ctx.c
test/ssl_test_ctx.h
test/ssl_test_ctx_test.c

index a279d42..bb89eae 100644 (file)
@@ -204,6 +204,7 @@ int set_cert_times(X509 *x, const char *startdate, const char *enddate,
         OPT_S_NOTLS1_3, OPT_S_BUGS, OPT_S_NO_COMP, OPT_S_NOTICKET, \
         OPT_S_SERVERPREF, OPT_S_LEGACYRENEG, OPT_S_LEGACYCONN, \
         OPT_S_ONRESUMP, OPT_S_NOLEGACYCONN, OPT_S_ALLOW_NO_DHE_KEX, \
+        OPT_S_PRIORITIZE_CHACHA, \
         OPT_S_STRICT, OPT_S_SIGALGS, OPT_S_CLIENTSIGALGS, OPT_S_GROUPS, \
         OPT_S_CURVES, OPT_S_NAMEDCURVE, OPT_S_CIPHER, \
         OPT_S_RECORD_PADDING, OPT_S_DEBUGBROKE, OPT_S_COMP, \
@@ -233,6 +234,8 @@ int set_cert_times(X509 *x, const char *startdate, const char *enddate,
             "Disallow initial connection to servers that don't support RI"}, \
         {"allow_no_dhe_kex", OPT_S_ALLOW_NO_DHE_KEX, '-', \
             "In TLSv1.3 allow non-(ec)dhe based key exchange on resumption"}, \
+        {"prioritize_chacha", OPT_S_PRIORITIZE_CHACHA, '-', \
+            "Prioritize ChaCha ciphers when preferred by clients"}, \
         {"strict", OPT_S_STRICT, '-', \
             "Enforce strict certificate checks as per TLS standard"}, \
         {"sigalgs", OPT_S_SIGALGS, 's', \
@@ -270,6 +273,7 @@ int set_cert_times(X509 *x, const char *startdate, const char *enddate,
         case OPT_S_ONRESUMP: \
         case OPT_S_NOLEGACYCONN: \
         case OPT_S_ALLOW_NO_DHE_KEX: \
+        case OPT_S_PRIORITIZE_CHACHA: \
         case OPT_S_STRICT: \
         case OPT_S_SIGALGS: \
         case OPT_S_CLIENTSIGALGS: \
index 0e28e4e..ad04359 100644 (file)
@@ -105,6 +105,7 @@ B<openssl> B<s_server>
 [B<-no_resumption_on_reneg>]
 [B<-no_legacy_server_connect>]
 [B<-allow_no_dhe_kex>]
+[B<-prioritize_chacha>]
 [B<-strict>]
 [B<-sigalgs val>]
 [B<-client_sigalgs val>]
@@ -510,6 +511,10 @@ Disable RFC4507bis session ticket support.
 
 Use the server's cipher preferences, rather than the client's preferences.
 
+=item B<-prioritize_chacha>
+
+Prioritize ChaCha ciphers when preferred by clients. Requires B<-serverpref>.
+
 =item B<-no_resumption_on_reneg>
 
 Set the B<SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION> option.
@@ -718,7 +723,10 @@ L<SSL_CTX_set_max_pipelines(3)>
 
 =head1 HISTORY
 
-The -no_alt_chains options was first added to OpenSSL 1.1.0.
+The -no_alt_chains option was first added to OpenSSL 1.1.0.
+
+The -allow-no-dhe-kex and -prioritize_chacha options were first added to
+OpenSSL 1.1.1.
 
 =head1 COPYRIGHT
 
index a812186..06b98bd 100644 (file)
@@ -171,6 +171,13 @@ Use server and not client preference order when determining which cipher suite,
 signature algorithm or elliptic curve to use for an incoming connection.
 Equivalent to B<SSL_OP_CIPHER_SERVER_PREFERENCE>. Only used by servers.
 
+=item B<-prioritize_chacha>
+
+Prioritize ChaCha ciphers when the client has a ChaCha20 cipher at the top of
+its preference list. This usually indicates a client without AES hardware
+acceleration (e.g. mobile) is in use. Equivalent to B<SSL_OP_PRIORITIZE_CHACHA>.
+Only used by servers. Requires B<-serverpref>.
+
 =item B<-no_resumption_on_reneg>
 
 set SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION flag. Only used by servers.
@@ -382,21 +389,26 @@ B<Bugs>: enable various bug workarounds. Same as B<SSL_OP_ALL>.
 B<DHSingle>: enable single use DH keys, set by default. Inverse of
 B<SSL_OP_DH_SINGLE>. Only used by servers.
 
-B<ECDHSingle> enable single use ECDH keys, set by default. Inverse of
+B<ECDHSingle>: enable single use ECDH keys, set by default. Inverse of
 B<SSL_OP_ECDH_SINGLE>. Only used by servers.
 
-B<ServerPreference> use server and not client preference order when
+B<ServerPreference>: use server and not client preference order when
 determining which cipher suite, signature algorithm or elliptic curve
 to use for an incoming connection.  Equivalent to
 B<SSL_OP_CIPHER_SERVER_PREFERENCE>. Only used by servers.
 
-B<NoResumptionOnRenegotiation> set
+B<PrioritizeChaCha>: prioritizes ChaCha ciphers when the client has a
+ChaCha20 cipher at the top of its preference list. This usually indicates
+a mobile client is in use. Equivalent to B<SSL_OP_PRIORITIZE_CHACHA>.
+Only used by servers.
+
+B<NoResumptionOnRenegotiation>: set
 B<SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION> flag. Only used by servers.
 
-B<UnsafeLegacyRenegotiation> permits the use of unsafe legacy renegotiation.
+B<UnsafeLegacyRenegotiation>: permits the use of unsafe legacy renegotiation.
 Equivalent to B<SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION>.
 
-B<UnsafeLegacyServerConnect> permits the use of unsafe legacy renegotiation
+B<UnsafeLegacyServerConnect>: permits the use of unsafe legacy renegotiation
 for OpenSSL clients only. Equivalent to B<SSL_OP_LEGACY_SERVER_CONNECT>.
 Set by default.
 
@@ -595,9 +607,11 @@ B<SSL_CONF_TYPE_UNKNOWN>.
 
 B<MinProtocol> and B<MaxProtocol> where added in OpenSSL 1.1.0.
 
+B<AllowNoDHEKEX> and B<PrioritizeChaCha> were added in OpenSSL 1.1.1.
+
 =head1 COPYRIGHT
 
-Copyright 2012-2016 The OpenSSL Project Authors. All Rights Reserved.
+Copyright 2012-2017 The OpenSSL Project Authors. All Rights Reserved.
 
 Licensed under the OpenSSL license (the "License").  You may not use
 this file except in compliance with the License.  You can obtain a copy
index bd7f111..072fdb7 100644 (file)
@@ -180,6 +180,15 @@ messages, and ignore renegotiation requests via ClientHello.
 In TLSv1.3 allow a non-(ec)dhe based key exchange mode on resumption. This means
 that there will be no forward secrecy for the resumed session.
 
+=item SSL_OP_PRIORITIZE_CHACHA
+
+When SSL_OP_CIPHER_SERVER_PREFERENCE is set, temporarily reprioritize
+ChaCha20-Poly1305 ciphers to the top of the server cipher list if a
+ChaCha20-Poly1305 cipher is at the top of the client cipher list. This helps
+those clients (e.g. mobile) use ChaCha20-Poly1305 if that cipher is anywhere
+in the server cipher list; but still allows other clients to use AES and other
+ciphers. Requires B<SSL_OP_CIPHER_SERVER_PREFERENCE>.
+
 =back
 
 The following options no longer have any effect but their identifiers are
@@ -306,9 +315,11 @@ L<dhparam(1)>
 The attempt to always try to use secure renegotiation was added in
 Openssl 0.9.8m.
 
+B<SSL_OP_PRIORITIZE_CHACHA> was added in OpenSSL 1.1.1.
+
 =head1 COPYRIGHT
 
-Copyright 2001-2016 The OpenSSL Project Authors. All Rights Reserved.
+Copyright 2001-2017 The OpenSSL Project Authors. All Rights Reserved.
 
 Licensed under the OpenSSL license (the "License").  You may not use
 this file except in compliance with the License.  You can obtain a copy
index 176425a..a5251b5 100644 (file)
@@ -338,6 +338,9 @@ typedef int (*SSL_verify_cb)(int preverify_ok, X509_STORE_CTX *x509_ctx);
 # define SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION        0x00040000U
 /* Disable encrypt-then-mac */
 # define SSL_OP_NO_ENCRYPT_THEN_MAC                      0x00080000U
+/* Prioritize Chacha20Poly1305 when client does.
+ * Modifies SSL_OP_CIPHER_SERVER_PREFERENCE */
+# define SSL_OP_PRIORITIZE_CHACHA                        0x00200000U
 /*
  * Set on servers to choose the cipher according to the server's preferences
  */
index f4423b2..c063fff 100644 (file)
@@ -4120,6 +4120,9 @@ const SSL_CIPHER *ssl3_choose_cipher(SSL *s, STACK_OF(SSL_CIPHER) *clnt,
     STACK_OF(SSL_CIPHER) *prio, *allow;
     int i, ii, ok;
     unsigned long alg_k = 0, alg_a = 0, mask_k = 0, mask_a = 0;
+#ifndef OPENSSL_NO_CHACHA
+    STACK_OF(SSL_CIPHER) *prio_chacha = NULL;
+#endif
 
     /* Let's see which ciphers we can support */
 
@@ -4145,9 +4148,53 @@ const SSL_CIPHER *ssl3_choose_cipher(SSL *s, STACK_OF(SSL_CIPHER) *clnt,
     }
 #endif
 
-    if (s->options & SSL_OP_CIPHER_SERVER_PREFERENCE || tls1_suiteb(s)) {
+    /* SUITE-B takes precedence over server preference and ChaCha priortiy */
+    if (tls1_suiteb(s)) {
+        prio = srvr;
+        allow = clnt;
+    } else if (s->options & SSL_OP_CIPHER_SERVER_PREFERENCE) {
         prio = srvr;
         allow = clnt;
+#ifndef OPENSSL_NO_CHACHA
+        /* If ChaCha20 is at the top of the client preference list,
+           and there are ChaCha20 ciphers in the server list, then
+           temporarily prioritize all ChaCha20 ciphers in the servers list. */
+        if (s->options & SSL_OP_PRIORITIZE_CHACHA && sk_SSL_CIPHER_num(clnt) > 0) {
+            c = sk_SSL_CIPHER_value(clnt, 0);
+            if (c->algorithm_enc == SSL_CHACHA20POLY1305) {
+                /* ChaCha20 is client preferred, check server... */
+                int num = sk_SSL_CIPHER_num(srvr);
+                int found = 0;
+                for (i = 0; i < num; i++) {
+                    c = sk_SSL_CIPHER_value(srvr, i);
+                    if (c->algorithm_enc == SSL_CHACHA20POLY1305) {
+                        found = 1;
+                        break;
+                    }
+                }
+                if (found) {
+                    prio_chacha = sk_SSL_CIPHER_new_null();
+                    /* if reserve fails, then there's likely a memory issue */
+                    if (prio_chacha != NULL) {
+                        /* Put all ChaCha20 at the top, starting with the one we just found */
+                        sk_SSL_CIPHER_push(prio_chacha, c);
+                        for (i++; i < num; i++) {
+                            c = sk_SSL_CIPHER_value(srvr, i);
+                            if (c->algorithm_enc == SSL_CHACHA20POLY1305)
+                                sk_SSL_CIPHER_push(prio_chacha, c);
+                        }
+                        /* Pull in the rest */
+                        for (i = 0; i < num; i++) {
+                            c = sk_SSL_CIPHER_value(srvr, i);
+                            if (c->algorithm_enc != SSL_CHACHA20POLY1305)
+                                sk_SSL_CIPHER_push(prio_chacha, c);
+                        }
+                        prio = prio_chacha;
+                    }
+                }
+            }
+        }
+# endif
     } else {
         prio = clnt;
         allow = srvr;
@@ -4229,6 +4276,9 @@ const SSL_CIPHER *ssl3_choose_cipher(SSL *s, STACK_OF(SSL_CIPHER) *clnt,
             break;
         }
     }
+#ifndef OPENSSL_NO_CHACHA
+    sk_SSL_CIPHER_free(prio_chacha);
+#endif
     return ret;
 }
 
index 1a8dc15..fe090ae 100644 (file)
@@ -368,7 +368,8 @@ static int cmd_Options(SSL_CONF_CTX *cctx, const char *value)
                      SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION),
         SSL_FLAG_TBL_INV("EncryptThenMac", SSL_OP_NO_ENCRYPT_THEN_MAC),
         SSL_FLAG_TBL("NoRenegotiation", SSL_OP_NO_RENEGOTIATION),
-        SSL_FLAG_TBL("AllowNoDHEKEX", SSL_OP_ALLOW_NO_DHE_KEX)
+        SSL_FLAG_TBL("AllowNoDHEKEX", SSL_OP_ALLOW_NO_DHE_KEX),
+        SSL_FLAG_TBL("PrioritizeChaCha", SSL_OP_PRIORITIZE_CHACHA)
     };
     if (value == NULL)
         return -3;
@@ -588,6 +589,7 @@ static const ssl_conf_cmd_tbl ssl_conf_cmds[] = {
     SSL_CONF_CMD_SWITCH("no_resumption_on_reneg", SSL_CONF_FLAG_SERVER),
     SSL_CONF_CMD_SWITCH("no_legacy_server_connect", SSL_CONF_FLAG_SERVER),
     SSL_CONF_CMD_SWITCH("allow_no_dhe_kex", 0),
+    SSL_CONF_CMD_SWITCH("prioritize_chacha", SSL_CONF_FLAG_SERVER),
     SSL_CONF_CMD_SWITCH("strict", 0),
     SSL_CONF_CMD_STRING(SignatureAlgorithms, "sigalgs", 0),
     SSL_CONF_CMD_STRING(ClientSignatureAlgorithms, "client_sigalgs", 0),
@@ -660,6 +662,8 @@ static const ssl_switch_tbl ssl_cmd_switches[] = {
     {SSL_OP_LEGACY_SERVER_CONNECT, SSL_TFLAG_INV},
     /* allow_no_dhe_kex */
     {SSL_OP_ALLOW_NO_DHE_KEX, 0},
+    /* chacha reprioritization */
+    {SSL_OP_PRIORITIZE_CHACHA, 0},
     {SSL_CERT_FLAG_TLS_STRICT, SSL_TFLAG_CERT}, /* strict */
 };
 
index 188ec9e..78eaa01 100644 (file)
@@ -39,6 +39,7 @@ void HANDSHAKE_RESULT_free(HANDSHAKE_RESULT *result)
     OPENSSL_free(result->server_alpn_negotiated);
     sk_X509_NAME_pop_free(result->server_ca_names, X509_NAME_free);
     sk_X509_NAME_pop_free(result->client_ca_names, X509_NAME_free);
+    OPENSSL_free(result->cipher);
     OPENSSL_free(result);
 }
 
@@ -1324,6 +1325,7 @@ static HANDSHAKE_RESULT *do_handshake_internal(
     EVP_PKEY *tmp_key;
     const STACK_OF(X509_NAME) *names;
     time_t start;
+    const char* cipher;
 
     if (ret == NULL)
         return NULL;
@@ -1543,6 +1545,9 @@ static HANDSHAKE_RESULT *do_handshake_internal(
     ret->client_resumed = SSL_session_reused(client.ssl);
     ret->server_resumed = SSL_session_reused(server.ssl);
 
+    cipher = SSL_CIPHER_get_name(SSL_get_current_cipher(client.ssl));
+    ret->cipher = dup_str((const unsigned char*)cipher, strlen(cipher));
+
     if (session_out != NULL)
         *session_out = SSL_get1_session(client.ssl);
 
index 96c670e..9dcbeb7 100644 (file)
@@ -64,6 +64,7 @@ typedef struct handshake_result {
     STACK_OF(X509_NAME) *client_ca_names;
     /* Session id status */
     ssl_session_id_t session_id;
+    char *cipher;
 } HANDSHAKE_RESULT;
 
 HANDSHAKE_RESULT *HANDSHAKE_RESULT_new(void);
index 42bf462..3b1447b 100644 (file)
@@ -28,7 +28,7 @@ map { s/\^// } @conf_files if $^O eq "VMS";
 
 # We hard-code the number of tests to double-check that the globbing above
 # finds all files as expected.
-plan tests => 24;  # = scalar @conf_srcs
+plan tests => 25;  # = scalar @conf_srcs
 
 # Some test results depend on the configuration of enabled protocols. We only
 # verify generated sources in the default configuration.
diff --git a/test/ssl-tests/25-cipher.conf b/test/ssl-tests/25-cipher.conf
new file mode 100644 (file)
index 0000000..101ee7c
--- /dev/null
@@ -0,0 +1,244 @@
+# Generated with generate_ssl_tests.pl
+
+num_tests = 9
+
+test-0 = 0-cipher-server-1
+test-1 = 1-cipher-server-2
+test-2 = 2-cipher-server-client-list
+test-3 = 3-cipher-server-pref-1
+test-4 = 4-cipher-server-pref-2
+test-5 = 5-cipher-server-pref-client-list
+test-6 = 6-cipher-server-pref-not-mobile
+test-7 = 7-cipher-server-pref-mobile
+test-8 = 8-cipher-server-pref-mobile2
+# ===========================================================
+
+[0-cipher-server-1]
+ssl_conf = 0-cipher-server-1-ssl
+
+[0-cipher-server-1-ssl]
+server = 0-cipher-server-1-server
+client = 0-cipher-server-1-client
+
+[0-cipher-server-1-server]
+Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
+CipherString = ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256
+MaxProtocol = TLSv1.2
+PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
+
+[0-cipher-server-1-client]
+CipherString = ECDHE-RSA-AES256-SHA384
+MaxProtocol = TLSv1.2
+VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem
+VerifyMode = Peer
+
+[test-0]
+ExpectedCipher = ECDHE-RSA-AES256-SHA384
+
+
+# ===========================================================
+
+[1-cipher-server-2]
+ssl_conf = 1-cipher-server-2-ssl
+
+[1-cipher-server-2-ssl]
+server = 1-cipher-server-2-server
+client = 1-cipher-server-2-client
+
+[1-cipher-server-2-server]
+Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
+CipherString = ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256
+MaxProtocol = TLSv1.2
+PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
+
+[1-cipher-server-2-client]
+CipherString = ECDHE-RSA-AES128-SHA256
+MaxProtocol = TLSv1.2
+VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem
+VerifyMode = Peer
+
+[test-1]
+ExpectedCipher = ECDHE-RSA-AES128-SHA256
+
+
+# ===========================================================
+
+[2-cipher-server-client-list]
+ssl_conf = 2-cipher-server-client-list-ssl
+
+[2-cipher-server-client-list-ssl]
+server = 2-cipher-server-client-list-server
+client = 2-cipher-server-client-list-client
+
+[2-cipher-server-client-list-server]
+Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
+CipherString = ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256
+MaxProtocol = TLSv1.2
+PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
+
+[2-cipher-server-client-list-client]
+CipherString = ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384
+MaxProtocol = TLSv1.2
+VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem
+VerifyMode = Peer
+
+[test-2]
+ExpectedCipher = ECDHE-RSA-AES128-SHA256
+
+
+# ===========================================================
+
+[3-cipher-server-pref-1]
+ssl_conf = 3-cipher-server-pref-1-ssl
+
+[3-cipher-server-pref-1-ssl]
+server = 3-cipher-server-pref-1-server
+client = 3-cipher-server-pref-1-client
+
+[3-cipher-server-pref-1-server]
+Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
+CipherString = ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256
+MaxProtocol = TLSv1.2
+Options = ServerPreference
+PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
+
+[3-cipher-server-pref-1-client]
+CipherString = ECDHE-RSA-AES256-SHA384
+MaxProtocol = TLSv1.2
+VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem
+VerifyMode = Peer
+
+[test-3]
+ExpectedCipher = ECDHE-RSA-AES256-SHA384
+
+
+# ===========================================================
+
+[4-cipher-server-pref-2]
+ssl_conf = 4-cipher-server-pref-2-ssl
+
+[4-cipher-server-pref-2-ssl]
+server = 4-cipher-server-pref-2-server
+client = 4-cipher-server-pref-2-client
+
+[4-cipher-server-pref-2-server]
+Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
+CipherString = ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256
+MaxProtocol = TLSv1.2
+Options = ServerPreference
+PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
+
+[4-cipher-server-pref-2-client]
+CipherString = ECDHE-RSA-AES128-SHA256
+MaxProtocol = TLSv1.2
+VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem
+VerifyMode = Peer
+
+[test-4]
+ExpectedCipher = ECDHE-RSA-AES128-SHA256
+
+
+# ===========================================================
+
+[5-cipher-server-pref-client-list]
+ssl_conf = 5-cipher-server-pref-client-list-ssl
+
+[5-cipher-server-pref-client-list-ssl]
+server = 5-cipher-server-pref-client-list-server
+client = 5-cipher-server-pref-client-list-client
+
+[5-cipher-server-pref-client-list-server]
+Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
+CipherString = ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256
+MaxProtocol = TLSv1.2
+Options = ServerPreference
+PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
+
+[5-cipher-server-pref-client-list-client]
+CipherString = ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384
+MaxProtocol = TLSv1.2
+VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem
+VerifyMode = Peer
+
+[test-5]
+ExpectedCipher = ECDHE-RSA-AES256-SHA384
+
+
+# ===========================================================
+
+[6-cipher-server-pref-not-mobile]
+ssl_conf = 6-cipher-server-pref-not-mobile-ssl
+
+[6-cipher-server-pref-not-mobile-ssl]
+server = 6-cipher-server-pref-not-mobile-server
+client = 6-cipher-server-pref-not-mobile-client
+
+[6-cipher-server-pref-not-mobile-server]
+Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
+CipherString = ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-CHACHA20-POLY1305
+MaxProtocol = TLSv1.2
+Options = ServerPreference
+PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
+
+[6-cipher-server-pref-not-mobile-client]
+CipherString = ECDHE-RSA-CHACHA20-POLY1305:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384
+MaxProtocol = TLSv1.2
+VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem
+VerifyMode = Peer
+
+[test-6]
+ExpectedCipher = ECDHE-RSA-AES256-SHA384
+
+
+# ===========================================================
+
+[7-cipher-server-pref-mobile]
+ssl_conf = 7-cipher-server-pref-mobile-ssl
+
+[7-cipher-server-pref-mobile-ssl]
+server = 7-cipher-server-pref-mobile-server
+client = 7-cipher-server-pref-mobile-client
+
+[7-cipher-server-pref-mobile-server]
+Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
+CipherString = ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-CHACHA20-POLY1305
+MaxProtocol = TLSv1.2
+Options = ServerPreference,PrioritizeChaCha
+PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
+
+[7-cipher-server-pref-mobile-client]
+CipherString = ECDHE-RSA-CHACHA20-POLY1305:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384
+MaxProtocol = TLSv1.2
+VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem
+VerifyMode = Peer
+
+[test-7]
+ExpectedCipher = ECDHE-RSA-CHACHA20-POLY1305
+
+
+# ===========================================================
+
+[8-cipher-server-pref-mobile2]
+ssl_conf = 8-cipher-server-pref-mobile2-ssl
+
+[8-cipher-server-pref-mobile2-ssl]
+server = 8-cipher-server-pref-mobile2-server
+client = 8-cipher-server-pref-mobile2-client
+
+[8-cipher-server-pref-mobile2-server]
+Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
+CipherString = ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-CHACHA20-POLY1305
+MaxProtocol = TLSv1.2
+Options = ServerPreference,PrioritizeChaCha
+PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
+
+[8-cipher-server-pref-mobile2-client]
+CipherString = ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-CHACHA20-POLY1305
+MaxProtocol = TLSv1.2
+VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem
+VerifyMode = Peer
+
+[test-8]
+ExpectedCipher = ECDHE-RSA-AES256-SHA384
+
+
diff --git a/test/ssl-tests/25-cipher.conf.in b/test/ssl-tests/25-cipher.conf.in
new file mode 100644 (file)
index 0000000..c4b0b05
--- /dev/null
@@ -0,0 +1,151 @@
+# -*- mode: perl; -*-
+# Copyright 2017 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+
+## Test version negotiation
+
+use strict;
+use warnings;
+
+package ssltests;
+
+
+our @tests = (
+    {
+        name => "cipher-server-1",
+        server => {
+           "MaxProtocol" => "TLSv1.2",
+           "CipherString" => "ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256",
+       },
+        client => {
+           "MaxProtocol" => "TLSv1.2",
+           "CipherString" => "ECDHE-RSA-AES256-SHA384"
+        },
+        test => {
+            "ExpectedCipher" => "ECDHE-RSA-AES256-SHA384",
+        },
+    },
+    {
+        name => "cipher-server-2",
+        server => {
+           "MaxProtocol" => "TLSv1.2",
+           "CipherString" => "ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256",
+       },
+        client => {
+           "MaxProtocol" => "TLSv1.2",
+           "CipherString" => "ECDHE-RSA-AES128-SHA256"
+        },
+        test => {
+            "ExpectedCipher" => "ECDHE-RSA-AES128-SHA256",
+        },
+    },
+    {
+        name => "cipher-server-client-list",
+        server => {
+           "MaxProtocol" => "TLSv1.2",
+           "CipherString" => "ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256",
+       },
+        client => {
+           "MaxProtocol" => "TLSv1.2",
+           "CipherString" => "ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384",
+        },
+        test => {
+            "ExpectedCipher" => "ECDHE-RSA-AES128-SHA256",
+        },
+    },
+    {
+        name => "cipher-server-pref-1",
+        server => {
+           "MaxProtocol" => "TLSv1.2",
+           "CipherString" => "ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256",
+           "Options" => "ServerPreference",
+       },
+        client => {
+           "MaxProtocol" => "TLSv1.2",
+           "CipherString" => "ECDHE-RSA-AES256-SHA384"
+        },
+        test => {
+            "ExpectedCipher" => "ECDHE-RSA-AES256-SHA384",
+        },
+    },
+    {
+        name => "cipher-server-pref-2",
+        server => {
+           "MaxProtocol" => "TLSv1.2",
+           "CipherString" => "ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256",
+           "Options" => "ServerPreference",
+       },
+        client => {
+           "MaxProtocol" => "TLSv1.2",
+           "CipherString" => "ECDHE-RSA-AES128-SHA256"
+        },
+        test => {
+            "ExpectedCipher" => "ECDHE-RSA-AES128-SHA256",
+        },
+    },
+    {
+        name => "cipher-server-pref-client-list",
+        server => {
+           "MaxProtocol" => "TLSv1.2",
+           "CipherString" => "ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256",
+           "Options" => "ServerPreference",
+       },
+        client => {
+           "MaxProtocol" => "TLSv1.2",
+           "CipherString" => "ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384",
+        },
+        test => {
+            "ExpectedCipher" => "ECDHE-RSA-AES256-SHA384",
+        },
+    },
+    {
+        name => "cipher-server-pref-not-mobile",
+        server => {
+           "MaxProtocol" => "TLSv1.2",
+           "CipherString" => "ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-CHACHA20-POLY1305",
+           "Options" => "ServerPreference",
+       },
+        client => {
+           "MaxProtocol" => "TLSv1.2",
+           "CipherString" => "ECDHE-RSA-CHACHA20-POLY1305:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384",
+        },
+        test => {
+            "ExpectedCipher" => "ECDHE-RSA-AES256-SHA384",
+        },
+    },
+    {
+        name => "cipher-server-pref-mobile",
+        server => {
+           "MaxProtocol" => "TLSv1.2",
+           "CipherString" => "ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-CHACHA20-POLY1305",
+           "Options" => "ServerPreference,PrioritizeChaCha",
+       },
+        client => {
+           "MaxProtocol" => "TLSv1.2",
+           "CipherString" => "ECDHE-RSA-CHACHA20-POLY1305:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384",
+        },
+        test => {
+            "ExpectedCipher" => "ECDHE-RSA-CHACHA20-POLY1305",
+        },
+    },
+    {
+        name => "cipher-server-pref-mobile2",
+        server => {
+           "MaxProtocol" => "TLSv1.2",
+           "CipherString" => "ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-CHACHA20-POLY1305",
+           "Options" => "ServerPreference,PrioritizeChaCha",
+       },
+        client => {
+           "MaxProtocol" => "TLSv1.2",
+           "CipherString" => "ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-CHACHA20-POLY1305",
+        },
+        test => {
+            "ExpectedCipher" => "ECDHE-RSA-AES256-SHA384",
+        },
+    },
+);
index dcdd867..a21a0f7 100644 (file)
@@ -318,6 +318,18 @@ static int check_client_ca_names(HANDSHAKE_RESULT *result,
                           result->client_ca_names);
 }
 
+static int check_cipher(HANDSHAKE_RESULT *result, SSL_TEST_CTX *test_ctx)
+{
+    if (test_ctx->expected_cipher == NULL)
+        return 1;
+    if (!TEST_ptr(result->cipher))
+        return 0;
+    if (!TEST_str_eq(test_ctx->expected_cipher,
+                     result->cipher))
+        return 0;
+    return 1;
+}
+
 /*
  * This could be further simplified by constructing an expected
  * HANDSHAKE_RESULT, and implementing comparison methods for
@@ -338,6 +350,7 @@ static int check_test(HANDSHAKE_RESULT *result, SSL_TEST_CTX *test_ctx)
 #ifndef OPENSSL_NO_NEXTPROTONEG
         ret &= check_npn(result, test_ctx);
 #endif
+        ret &= check_cipher(result, test_ctx);
         ret &= check_alpn(result, test_ctx);
         ret &= check_resumption(result, test_ctx);
         ret &= check_tmp_key(result, test_ctx);
index 62417ac..71445c5 100644 (file)
@@ -615,6 +615,10 @@ __owur static int parse_expected_client_ca_names(SSL_TEST_CTX *test_ctx,
     return parse_expected_ca_names(&test_ctx->expected_client_ca_names, value);
 }
 
+/* ExpectedCipher */
+
+IMPLEMENT_SSL_TEST_STRING_OPTION(SSL_TEST_CTX, test, expected_cipher)
+
 /* Known test options and their corresponding parse methods. */
 
 /* Top-level options. */
@@ -650,6 +654,7 @@ static const ssl_test_ctx_option ssl_test_ctx_options[] = {
     { "ExpectedClientSignType", &parse_expected_client_sign_type },
     { "ExpectedClientCANames", &parse_expected_client_ca_names },
     { "UseSCTP", &parse_test_use_sctp },
+    { "ExpectedCipher", &parse_test_expected_cipher },
 };
 
 /* Nested client options. */
@@ -728,6 +733,7 @@ void SSL_TEST_CTX_free(SSL_TEST_CTX *ctx)
     OPENSSL_free(ctx->expected_alpn_protocol);
     sk_X509_NAME_pop_free(ctx->expected_server_ca_names, X509_NAME_free);
     sk_X509_NAME_pop_free(ctx->expected_client_ca_names, X509_NAME_free);
+    OPENSSL_free(ctx->expected_cipher);
     OPENSSL_free(ctx);
 }
 
index cec6b77..2d7b0c2 100644 (file)
@@ -210,6 +210,7 @@ typedef struct {
     int use_sctp;
     /* Whether to expect a session id from the server */
     ssl_session_id_t session_id_expected;
+    char *expected_cipher;
 } SSL_TEST_CTX;
 
 const char *ssl_test_result_name(ssl_test_result_t result);
index d064511..70ebb2a 100644 (file)
@@ -93,6 +93,8 @@ static int testctx_eq(SSL_TEST_CTX *ctx, SSL_TEST_CTX *ctx2)
                             ctx2->expected_npn_protocol)
             || !TEST_str_eq(ctx->expected_alpn_protocol,
                             ctx2->expected_alpn_protocol)
+            || !TEST_str_eq(ctx->expected_cipher,
+                            ctx2->expected_cipher)
             || !TEST_int_eq(ctx->resumption_expected,
                             ctx2->resumption_expected)
             || !TEST_int_eq(ctx->session_id_expected,