Update secret generation for draft-19
[openssl.git] / ssl / tls13_enc.c
index 2dc7dad6299b30bce2c7097caeacf76ea8c0fcf9..cac4a424efc726f231ae9ee230b7e5de43bd31c4 100644 (file)
@@ -124,6 +124,8 @@ int tls13_generate_secret(SSL *s, const EVP_MD *md,
     size_t mdlen, prevsecretlen;
     int ret;
     EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL);
+    const char *derived_secret_label = "derived secret";
+    unsigned char preextractsec[EVP_MAX_MD_SIZE];
 
     if (pctx == NULL)
         return 0;
@@ -138,6 +140,26 @@ int tls13_generate_secret(SSL *s, const EVP_MD *md,
         prevsecret = default_zeros;
         prevsecretlen = 0;
     } else {
+        EVP_MD_CTX *mctx = EVP_MD_CTX_new();
+        unsigned char hash[EVP_MAX_MD_SIZE];
+
+        /* The pre-extract derive step uses a hash of no messages */
+        if (mctx == NULL
+                || EVP_DigestInit_ex(mctx, md, NULL) <= 0
+                || EVP_DigestFinal_ex(mctx, hash, NULL) <= 0) {
+            EVP_MD_CTX_free(mctx);
+            return 0;
+        }
+        EVP_MD_CTX_free(mctx);
+
+        /* Generate the pre-extract secret */
+        if (!tls13_hkdf_expand(s, md, prevsecret,
+                               (unsigned char *)derived_secret_label,
+                               sizeof(derived_secret_label) - 1, hash,
+                               preextractsec, mdlen))
+            return 0;
+
+        prevsecret = preextractsec;
         prevsecretlen = mdlen;
     }
 
@@ -152,6 +174,8 @@ int tls13_generate_secret(SSL *s, const EVP_MD *md,
                <= 0;
 
     EVP_PKEY_CTX_free(pctx);
+    if (prevsecret == preextractsec)
+        OPENSSL_cleanse(preextractsec, mdlen);
     return ret == 0;
 }
 
@@ -264,9 +288,16 @@ static int derive_secret_key_and_iv(SSL *s, int send, const EVP_MD *md,
     /* TODO(size_t): convert me */
     keylen = EVP_CIPHER_key_length(ciph);
     if (EVP_CIPHER_mode(ciph) == EVP_CIPH_CCM_MODE) {
+        uint32_t algenc;
+
         ivlen = EVP_CCM_TLS_IV_LEN;
-        if (s->s3->tmp.new_cipher->algorithm_enc
-                & (SSL_AES128CCM8 | SSL_AES256CCM8))
+        if (s->s3->tmp.new_cipher == NULL) {
+            /* We've not selected a cipher yet - we must be doing early data */
+            algenc = s->session->cipher->algorithm_enc;
+        } else {
+            algenc = s->s3->tmp.new_cipher->algorithm_enc;
+        }
+        if (algenc & (SSL_AES128CCM8 | SSL_AES256CCM8))
             taglen = EVP_CCM8_TLS_TAG_LEN;
          else
             taglen = EVP_CCM_TLS_TAG_LEN;
@@ -336,8 +367,8 @@ int tls13_change_cipher_state(SSL *s, int which)
     const unsigned char *label;
     size_t labellen, hashlen = 0;
     int ret = 0;
-    const EVP_MD *md;
-    const EVP_CIPHER *cipher;
+    const EVP_MD *md = NULL;
+    const EVP_CIPHER *cipher = NULL;
 
     if (which & SSL3_CC_READ) {
         if (s->enc_read_ctx != NULL) {
@@ -422,6 +453,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/client write handshake
+             * traffic secret is the same as the hash for the server
+             * write/client read handshake traffic secret. However, if we
+             * processed early data then we delay changing the server
+             * read/client write cipher state until later, and the handshake
+             * hashes have moved on. Therefore we use the value saved earlier
+             * when we did the server write/client read change cipher state.
+             */
+            hash = s->handshake_traffic_hash;
         } else {
             insecret = s->master_secret;
             label = client_application_traffic;
@@ -469,6 +510,9 @@ int tls13_change_cipher_state(SSL *s, int which)
     if (label == server_application_traffic)
         memcpy(s->server_finished_hash, hashval, hashlen);
 
+    if (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