Fix changing of the cipher state when dealing with early data
authorMatt Caswell <matt@openssl.org>
Wed, 22 Feb 2017 14:09:42 +0000 (14:09 +0000)
committerMatt Caswell <matt@openssl.org>
Thu, 2 Mar 2017 17:44:15 +0000 (17:44 +0000)
Reviewed-by: Rich Salz <rsalz@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/2737)

ssl/ssl_lib.c
ssl/ssl_locl.h
ssl/statem/statem.h
ssl/statem/statem_srvr.c
ssl/tls13_enc.c

index a774452..2f62f4b 100644 (file)
@@ -1657,6 +1657,7 @@ int ssl_end_of_early_data_seen(SSL *s)
 {
     if (s->early_data_state == SSL_EARLY_DATA_READING) {
         s->early_data_state = SSL_EARLY_DATA_FINISHED_READING;
+        ossl_statem_finish_early_data(s);
         return 1;
     }
 
index 3175009..f849278 100644 (file)
@@ -1027,6 +1027,7 @@ struct ssl_st {
     unsigned char client_finished_secret[EVP_MAX_MD_SIZE];
     unsigned char server_finished_secret[EVP_MAX_MD_SIZE];
     unsigned char server_finished_hash[EVP_MAX_MD_SIZE];
+    unsigned char handshake_traffic_hash[EVP_MAX_MD_SIZE];
     unsigned char client_app_traffic_secret[EVP_MAX_MD_SIZE];
     unsigned char server_app_traffic_secret[EVP_MAX_MD_SIZE];
     EVP_CIPHER_CTX *enc_read_ctx; /* cryptographic state */
index 5bb74d0..c0c9cab 100644 (file)
@@ -129,3 +129,4 @@ __owur int ossl_statem_app_data_allowed(SSL *s);
 void ossl_statem_set_sctp_read_sock(SSL *s, int read_sock);
 __owur int ossl_statem_in_sctp_read_sock(SSL *s);
 #endif
+int ossl_statem_finish_early_data(SSL *s);
index 3d2d39b..0f68ddf 100644 (file)
@@ -787,10 +787,13 @@ WORK_STATE ossl_statem_server_post_work(SSL *s, WORK_STATE wst)
         if (SSL_IS_TLS13(s)) {
             if (!s->method->ssl3_enc->setup_key_block(s)
                 || !s->method->ssl3_enc->change_cipher_state(s,
-                        SSL3_CC_HANDSHAKE | SSL3_CHANGE_CIPHER_SERVER_WRITE)
-                || !s->method->ssl3_enc->change_cipher_state(s,
+                        SSL3_CC_HANDSHAKE | SSL3_CHANGE_CIPHER_SERVER_WRITE))
+                return WORK_ERROR;
+
+            if (s->ext.early_data != SSL_EARLY_DATA_ACCEPTED
+                && !s->method->ssl3_enc->change_cipher_state(s,
                         SSL3_CC_HANDSHAKE |SSL3_CHANGE_CIPHER_SERVER_READ))
-            return WORK_ERROR;
+                return WORK_ERROR;
         }
         break;
 
@@ -1104,6 +1107,15 @@ WORK_STATE ossl_statem_server_post_process_message(SSL *s, WORK_STATE wst)
     return WORK_FINISHED_CONTINUE;
 }
 
+int ossl_statem_finish_early_data(SSL *s)
+{
+    if (!s->method->ssl3_enc->change_cipher_state(s,
+                SSL3_CC_HANDSHAKE | SSL3_CHANGE_CIPHER_SERVER_READ))
+        return 0;
+
+    return 1;
+}
+
 #ifndef OPENSSL_NO_SRP
 static int ssl_check_srp_ext_ClientHello(SSL *s, int *al)
 {
index 2dc7dad..d42be60 100644 (file)
@@ -422,6 +422,16 @@ int tls13_change_cipher_state(SSL *s, int which)
             label = client_handshake_traffic;
             labellen = sizeof(client_handshake_traffic) - 1;
             log_label = CLIENT_HANDSHAKE_LABEL;
+            /*
+             * The hanshake hash used for the server read handshake traffic
+             * secret is the same as the hash for the server write handshake
+             * traffic secret. However, if we processed early data then we delay
+             * changing the server read cipher state until later, and the
+             * handshake hashes have moved on. Therefore we use the value saved
+             * earlier when we did the server write change cipher state.
+             */
+            if (s->server)
+                hash = s->handshake_traffic_hash;
         } else {
             insecret = s->master_secret;
             label = client_application_traffic;
@@ -469,6 +479,9 @@ int tls13_change_cipher_state(SSL *s, int which)
     if (label == server_application_traffic)
         memcpy(s->server_finished_hash, hashval, hashlen);
 
+    if (s->server && label == server_handshake_traffic)
+        memcpy(s->handshake_traffic_hash, hashval, hashlen);
+
     if (label == client_application_traffic) {
         /*
          * We also create the resumption master secret, but this time use the