Allow the server to change the ciphersuite on resume
authorMatt Caswell <matt@openssl.org>
Fri, 26 May 2017 16:59:34 +0000 (17:59 +0100)
committerMatt Caswell <matt@openssl.org>
Fri, 16 Jun 2017 09:57:59 +0000 (10:57 +0100)
Reviewed-by: Ben Kaduk <kaduk@mit.edu>
(Merged from https://github.com/openssl/openssl/pull/3623)

crypto/err/openssl.txt
include/openssl/sslerr.h
ssl/s3_lib.c
ssl/ssl_err.c
ssl/statem/statem_clnt.c
ssl/statem/statem_srvr.c

index e85c9a0..37e3166 100644 (file)
@@ -2158,6 +2158,7 @@ SSL_R_CCS_RECEIVED_EARLY:133:ccs received early
 SSL_R_CERTIFICATE_VERIFY_FAILED:134:certificate verify failed
 SSL_R_CERT_CB_ERROR:377:cert cb error
 SSL_R_CERT_LENGTH_MISMATCH:135:cert length mismatch
+SSL_R_CIPHERSUITE_DIGEST_HAS_CHANGED:218:ciphersuite digest has changed
 SSL_R_CIPHER_CODE_WRONG_LENGTH:137:cipher code wrong length
 SSL_R_CIPHER_OR_HASH_UNAVAILABLE:138:cipher or hash unavailable
 SSL_R_CLIENTHELLO_TLSEXT:226:clienthello tlsext
index fdb59f4..8dfc974 100644 (file)
@@ -403,6 +403,7 @@ int ERR_load_SSL_strings(void);
 # define SSL_R_CERTIFICATE_VERIFY_FAILED                  134
 # define SSL_R_CERT_CB_ERROR                              377
 # define SSL_R_CERT_LENGTH_MISMATCH                       135
+# define SSL_R_CIPHERSUITE_DIGEST_HAS_CHANGED             218
 # define SSL_R_CIPHER_CODE_WRONG_LENGTH                   137
 # define SSL_R_CIPHER_OR_HASH_UNAVAILABLE                 138
 # define SSL_R_CLIENTHELLO_TLSEXT                         226
index ffbe663..e8bda66 100644 (file)
@@ -3728,11 +3728,24 @@ const SSL_CIPHER *ssl3_choose_cipher(SSL *s, STACK_OF(SSL_CIPHER) *clnt,
             (DTLS_VERSION_LT(s->version, c->min_dtls) ||
              DTLS_VERSION_GT(s->version, c->max_dtls)))
             continue;
-        /*
-         * Since TLS 1.3 ciphersuites can be used with any auth or
-         * key exchange scheme skip tests.
-         */
-        if (!SSL_IS_TLS13(s)) {
+
+        if (SSL_IS_TLS13(s)) {
+            /*
+             * We must choose a ciphersuite that has a digest compatible with
+             * the session, unless we're going to do an HRR in which case we
+             * will just choose our most preferred ciphersuite regardless of
+             * whether it is compatible with the session or not.
+             */
+            if (s->hit
+                    && !s->hello_retry_request
+                    && ssl_md(c->algorithm2)
+                       != ssl_md(s->session->cipher->algorithm2))
+                continue;
+        } else {
+            /*
+             * These tests do not apply to TLS 1.3 ciphersuites because they can
+             * be used with any auth or key exchange scheme.
+             */
             mask_k = s->s3->tmp.mask_k;
             mask_a = s->s3->tmp.mask_a;
 #ifndef OPENSSL_NO_SRP
index 29e6648..eccc5af 100644 (file)
@@ -622,6 +622,8 @@ static const ERR_STRING_DATA SSL_str_reasons[] = {
     {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_CERT_CB_ERROR), "cert cb error"},
     {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_CERT_LENGTH_MISMATCH),
     "cert length mismatch"},
+    {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_CIPHERSUITE_DIGEST_HAS_CHANGED),
+    "ciphersuite digest has changed"},
     {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_CIPHER_CODE_WRONG_LENGTH),
     "cipher code wrong length"},
     {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_CIPHER_OR_HASH_UNAVAILABLE),
index 020589f..7dd921e 100644 (file)
@@ -1268,9 +1268,26 @@ static int set_client_ciphersuite(SSL *s, const unsigned char *cipherchars)
     if (s->session->cipher != NULL)
         s->session->cipher_id = s->session->cipher->id;
     if (s->hit && (s->session->cipher_id != c->id)) {
-        SSLerr(SSL_F_SET_CLIENT_CIPHERSUITE,
-               SSL_R_OLD_SESSION_CIPHER_NOT_RETURNED);
-        return 0;
+        if (SSL_IS_TLS13(s)) {
+            /*
+             * In TLSv1.3 it is valid for the server to select a different
+             * ciphersuite as long as the hash is the same.
+             */
+            if (ssl_md(c->algorithm2)
+                    != ssl_md(s->session->cipher->algorithm2)) {
+                SSLerr(SSL_F_SET_CLIENT_CIPHERSUITE,
+                       SSL_R_CIPHERSUITE_DIGEST_HAS_CHANGED);
+                return 0;
+            }
+        } else {
+            /*
+             * Prior to TLSv1.3 resuming a session always meant using the same
+             * ciphersuite.
+             */
+            SSLerr(SSL_F_SET_CLIENT_CIPHERSUITE,
+                   SSL_R_OLD_SESSION_CIPHER_NOT_RETURNED);
+            return 0;
+        }
     }
     s->s3->tmp.new_cipher = c;
 
index e2d618c..8137a7d 100644 (file)
@@ -1647,8 +1647,12 @@ static int tls_early_post_process_client_hello(SSL *s, int *pal)
         }
     }
 
-    /* If it is a hit, check that the cipher is in the list */
-    if (s->hit) {
+    /*
+     * If it is a hit, check that the cipher is in the list. In TLSv1.3 we can
+     * resume with a differnt cipher as long as the hash is the same so this
+     * check does not apply.
+     */
+    if (!SSL_IS_TLS13(s) && s->hit) {
         j = 0;
         id = s->session->cipher->id;
 
@@ -1850,7 +1854,7 @@ static int tls_early_post_process_client_hello(SSL *s, int *pal)
      * Given s->session->ciphers and SSL_get_ciphers, we must pick a cipher
      */
 
-    if (!s->hit || s->hello_retry_request) {
+    if (!s->hit || SSL_IS_TLS13(s)) {
         sk_SSL_CIPHER_free(s->session->ciphers);
         s->session->ciphers = ciphers;
         if (ciphers == NULL) {
@@ -1956,7 +1960,7 @@ WORK_STATE tls_post_process_client_hello(SSL *s, WORK_STATE wst)
         wst = WORK_MORE_B;
     }
     if (wst == WORK_MORE_B) {
-        if (!s->hit || s->hello_retry_request) {
+        if (!s->hit || SSL_IS_TLS13(s)) {
             /* Let cert callback update server certificates if required */
             if (s->cert->cert_cb) {
                 int rv = s->cert->cert_cb(s, s->cert->cert_cb_arg);
@@ -1980,7 +1984,7 @@ WORK_STATE tls_post_process_client_hello(SSL *s, WORK_STATE wst)
                        SSL_R_NO_SHARED_CIPHER);
                 goto f_err;
             }
-            if (SSL_IS_TLS13(s) && s->s3->tmp.new_cipher != NULL
+            if (s->hello_retry_request && s->s3->tmp.new_cipher != NULL
                     && s->s3->tmp.new_cipher->id != cipher->id) {
                 /*
                  * A previous HRR picked a different ciphersuite to the one we