Bug 1532312, fix transcript-hash calculation after handshake, r=mt
authorDaiki Ueno <dueno@redhat.com>
Mon, 08 Apr 2019 10:05:56 +0200
changeset 15078 eb03936b42bb51d1e96acc73ac25a3b2501090b9
parent 15077 e5e10a46b9adb75030467e75119bea687d7a51b5
child 15079 bb58098d38a521c6c8b42bddb2e78f45d16d70d7
push id3320
push userdueno@redhat.com
push dateMon, 08 Apr 2019 10:47:08 +0000
reviewersmt
bugs1532312
Bug 1532312, fix transcript-hash calculation after handshake, r=mt Summary: In post-handshake, Handshake Context should be: ``` ClientHello ... client Finished + CertificateRequest ``` while NSS continues feeding any handshake message after handshake. Reviewers: mt Reviewed By: mt Bug #: 1532312 Differential Revision: https://phabricator.services.mozilla.com/D21935
gtests/ssl_gtest/ssl_auth_unittest.cc
lib/ssl/ssl3con.c
lib/ssl/sslencode.c
lib/ssl/sslimpl.h
lib/ssl/sslsecur.c
lib/ssl/tls13con.c
lib/ssl/tls13con.h
lib/ssl/tls13hashstate.c
--- a/gtests/ssl_gtest/ssl_auth_unittest.cc
+++ b/gtests/ssl_gtest/ssl_auth_unittest.cc
@@ -315,16 +315,56 @@ TEST_F(TlsConnectStreamTls13, PostHandsh
   // Send 1st CertificateRequest.
   EXPECT_EQ(SECSuccess, SSL_SendCertificateRequest(server_->ssl_fd()))
       << "Unexpected error: " << PORT_ErrorToName(PORT_GetError());
   // Send 2nd CertificateRequest.
   EXPECT_EQ(SECFailure, SSL_SendCertificateRequest(server_->ssl_fd()));
   EXPECT_EQ(PR_WOULD_BLOCK_ERROR, PORT_GetError());
 }
 
+TEST_F(TlsConnectStreamTls13, PostHandshakeAuthBeforeKeyUpdate) {
+  client_->SetupClientAuth();
+  EXPECT_EQ(SECSuccess, SSL_OptionSet(client_->ssl_fd(),
+                                      SSL_ENABLE_POST_HANDSHAKE_AUTH, PR_TRUE));
+  Connect();
+  // Send CertificateRequest.
+  EXPECT_EQ(SECSuccess, SSL_SendCertificateRequest(server_->ssl_fd()))
+      << "Unexpected error: " << PORT_ErrorToName(PORT_GetError());
+  // Send KeyUpdate.
+  EXPECT_EQ(SECFailure, SSL_KeyUpdate(server_->ssl_fd(), PR_TRUE));
+  EXPECT_EQ(PR_WOULD_BLOCK_ERROR, PORT_GetError());
+}
+
+TEST_F(TlsConnectStreamTls13, PostHandshakeAuthDuringClientKeyUpdate) {
+  client_->SetupClientAuth();
+  EXPECT_EQ(SECSuccess, SSL_OptionSet(client_->ssl_fd(),
+                                      SSL_ENABLE_POST_HANDSHAKE_AUTH, PR_TRUE));
+  Connect();
+  CheckEpochs(3, 3);
+  // Send CertificateRequest from server.
+  EXPECT_EQ(SECSuccess, SSL_SendCertificateRequest(server_->ssl_fd()))
+      << "Unexpected error: " << PORT_ErrorToName(PORT_GetError());
+  // Send KeyUpdate from client.
+  EXPECT_EQ(SECSuccess, SSL_KeyUpdate(client_->ssl_fd(), PR_TRUE));
+  server_->SendData(50);   // server sends CertificateRequest
+  client_->SendData(50);   // client sends KeyUpdate
+  server_->ReadBytes(50);  // server receives KeyUpdate and defers response
+  CheckEpochs(4, 3);
+  client_->ReadBytes(50);  // client receives CertificateRequest
+  client_->SendData(
+      50);  // client sends Certificate, CertificateVerify, Finished
+  server_->ReadBytes(
+      50);  // server receives Certificate, CertificateVerify, Finished
+  client_->CheckEpochs(3, 4);
+  server_->CheckEpochs(4, 4);
+  server_->SendData(50);   // server sends KeyUpdate
+  client_->ReadBytes(50);  // client receives KeyUpdate
+  client_->CheckEpochs(4, 4);
+}
+
 TEST_F(TlsConnectStreamTls13, PostHandshakeAuthMissingExtension) {
   client_->SetupClientAuth();
   Connect();
   // Send CertificateRequest, should fail due to missing
   // post_handshake_auth extension.
   EXPECT_EQ(SECFailure, SSL_SendCertificateRequest(server_->ssl_fd()));
   EXPECT_EQ(SSL_ERROR_MISSING_POST_HANDSHAKE_AUTH_EXTENSION, PORT_GetError());
 }
--- a/lib/ssl/ssl3con.c
+++ b/lib/ssl/ssl3con.c
@@ -3714,16 +3714,20 @@ ssl3_RestartHandshakeHashes(sslSocket *s
     if (ss->ssl3.hs.md5) {
         PK11_DestroyContext(ss->ssl3.hs.md5, PR_TRUE);
         ss->ssl3.hs.md5 = NULL;
     }
     if (ss->ssl3.hs.sha) {
         PK11_DestroyContext(ss->ssl3.hs.sha, PR_TRUE);
         ss->ssl3.hs.sha = NULL;
     }
+    if (ss->ssl3.hs.shaPostHandshake) {
+        PK11_DestroyContext(ss->ssl3.hs.shaPostHandshake, PR_TRUE);
+        ss->ssl3.hs.shaPostHandshake = NULL;
+    }
 }
 
 /*
  * Handshake messages
  */
 /* Called from  ssl3_InitHandshakeHashes()
 **      ssl3_AppendHandshake()
 **      ssl3_HandleV2ClientHello()
@@ -3774,16 +3778,34 @@ ssl3_UpdateHandshakeHashes(sslSocket *ss
             ssl_MapLowLevelError(SSL_ERROR_SHA_DIGEST_FAILURE);
             return rv;
         }
     }
     return rv;
 }
 
 SECStatus
+ssl3_UpdatePostHandshakeHashes(sslSocket *ss, const unsigned char *b, unsigned int l)
+{
+    SECStatus rv = SECSuccess;
+
+    PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
+
+    PRINT_BUF(90, (ss, "post handshake hash input:", b, l));
+
+    PORT_Assert(ss->ssl3.hs.hashType == handshake_hash_single);
+    PORT_Assert(ss->version >= SSL_LIBRARY_VERSION_TLS_1_3);
+    rv = PK11_DigestOp(ss->ssl3.hs.shaPostHandshake, b, l);
+    if (rv != SECSuccess) {
+        PORT_SetError(SSL_ERROR_DIGEST_FAILURE);
+    }
+    return rv;
+}
+
+SECStatus
 ssl3_AppendHandshakeHeader(sslSocket *ss, SSLHandshakeType t, PRUint32 length)
 {
     SECStatus rv;
 
     /* If we already have a message in place, we need to enqueue it.
      * This empties the buffer. This is a convenient place to call
      * dtls_StageHandshakeMessage to mark the message boundary.
      */
@@ -11618,30 +11640,31 @@ ssl3_FinishHandshake(sslSocket *ss)
     ssl_FinishHandshake(ss);
 
     return SECSuccess;
 }
 
 SECStatus
 ssl_HashHandshakeMessageInt(sslSocket *ss, SSLHandshakeType ct,
                             PRUint32 dtlsSeq,
-                            const PRUint8 *b, PRUint32 length)
+                            const PRUint8 *b, PRUint32 length,
+                            sslUpdateHandshakeHashes updateHashes)
 {
     PRUint8 hdr[4];
     PRUint8 dtlsData[8];
     SECStatus rv;
 
     PRINT_BUF(50, (ss, "Hash handshake message:", b, length));
 
     hdr[0] = (PRUint8)ct;
     hdr[1] = (PRUint8)(length >> 16);
     hdr[2] = (PRUint8)(length >> 8);
     hdr[3] = (PRUint8)(length);
 
-    rv = ssl3_UpdateHandshakeHashes(ss, (unsigned char *)hdr, 4);
+    rv = updateHashes(ss, (unsigned char *)hdr, 4);
     if (rv != SECSuccess)
         return rv; /* err code already set. */
 
     /* Extra data to simulate a complete DTLS handshake fragment */
     if (IS_DTLS(ss)) {
         /* Sequence number */
         dtlsData[0] = MSB(dtlsSeq);
         dtlsData[1] = LSB(dtlsSeq);
@@ -11651,36 +11674,43 @@ ssl_HashHandshakeMessageInt(sslSocket *s
         dtlsData[3] = 0;
         dtlsData[4] = 0;
 
         /* Fragment length */
         dtlsData[5] = (PRUint8)(length >> 16);
         dtlsData[6] = (PRUint8)(length >> 8);
         dtlsData[7] = (PRUint8)(length);
 
-        rv = ssl3_UpdateHandshakeHashes(ss, (unsigned char *)dtlsData,
-                                        sizeof(dtlsData));
+        rv = updateHashes(ss, (unsigned char *)dtlsData, sizeof(dtlsData));
         if (rv != SECSuccess)
             return rv; /* err code already set. */
     }
 
     /* The message body */
-    rv = ssl3_UpdateHandshakeHashes(ss, b, length);
+    rv = updateHashes(ss, b, length);
     if (rv != SECSuccess)
         return rv; /* err code already set. */
 
     return SECSuccess;
 }
 
 SECStatus
 ssl_HashHandshakeMessage(sslSocket *ss, SSLHandshakeType ct,
                          const PRUint8 *b, PRUint32 length)
 {
     return ssl_HashHandshakeMessageInt(ss, ct, ss->ssl3.hs.recvMessageSeq,
-                                       b, length);
+                                       b, length, ssl3_UpdateHandshakeHashes);
+}
+
+SECStatus
+ssl_HashPostHandshakeMessage(sslSocket *ss, SSLHandshakeType ct,
+                             const PRUint8 *b, PRUint32 length)
+{
+    return ssl_HashHandshakeMessageInt(ss, ct, ss->ssl3.hs.recvMessageSeq,
+                                       b, length, ssl3_UpdatePostHandshakeHashes);
 }
 
 /* Called from ssl3_HandleHandshake() when it has gathered a complete ssl3
  * handshake message.
  * Caller must hold Handshake and RecvBuf locks.
  */
 SECStatus
 ssl3_HandleHandshakeMessage(sslSocket *ss, PRUint8 *b, PRUint32 length,
@@ -11709,19 +11739,21 @@ ssl3_HandleHandshakeMessage(sslSocket *s
         /* Defer hashing of these messages until the message handlers. */
         case ssl_hs_client_hello:
         case ssl_hs_server_hello:
         case ssl_hs_certificate_verify:
         case ssl_hs_finished:
             break;
 
         default:
-            rv = ssl_HashHandshakeMessage(ss, ss->ssl3.hs.msg_type, b, length);
-            if (rv != SECSuccess) {
-                return SECFailure;
+            if (!tls13_IsPostHandshake(ss)) {
+                rv = ssl_HashHandshakeMessage(ss, ss->ssl3.hs.msg_type, b, length);
+                if (rv != SECSuccess) {
+                    return SECFailure;
+                }
             }
     }
 
     PORT_SetError(0); /* each message starts with no error. */
 
     if (ss->ssl3.hs.ws == wait_certificate_status &&
         ss->ssl3.hs.msg_type != ssl_hs_certificate_status) {
         /* If we negotiated the certificate_status extension then we deferred
@@ -13124,16 +13156,19 @@ ssl3_DestroySSL3Info(sslSocket *ss)
 
     /* clean up handshake */
     if (ss->ssl3.hs.md5) {
         PK11_DestroyContext(ss->ssl3.hs.md5, PR_TRUE);
     }
     if (ss->ssl3.hs.sha) {
         PK11_DestroyContext(ss->ssl3.hs.sha, PR_TRUE);
     }
+    if (ss->ssl3.hs.shaPostHandshake) {
+        PK11_DestroyContext(ss->ssl3.hs.shaPostHandshake, PR_TRUE);
+    }
     if (ss->ssl3.hs.messages.buf) {
         sslBuffer_Clear(&ss->ssl3.hs.messages);
     }
 
     /* free the SSL3Buffer (msg_body) */
     PORT_Free(ss->ssl3.hs.msg_body.buf);
 
     SECITEM_FreeItem(&ss->ssl3.hs.newSessionTicket.ticket, PR_FALSE);
--- a/lib/ssl/sslencode.c
+++ b/lib/ssl/sslencode.c
@@ -5,16 +5,17 @@
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nss.h"
 #include "prnetdb.h"
 #include "ssl.h"
 #include "sslimpl.h"
+#include "sslproto.h"
 
 /* Helper function to encode an unsigned integer into a buffer. */
 static void
 ssl_EncodeUintX(PRUint8 *to, PRUint64 value, unsigned int bytes)
 {
     PRUint64 encoded;
 
     PORT_Assert(bytes > 0 && bytes <= sizeof(encoded));
@@ -258,19 +259,21 @@ ssl3_AppendHandshake(sslSocket *ss, cons
         rv = sslBuffer_Grow(&ss->sec.ci.sendBuf, PR_MAX(MIN_SEND_BUF_LENGTH,
                                                         PR_MIN(MAX_SEND_BUF_LENGTH, ss->sec.ci.sendBuf.len + bytes)));
         if (rv != SECSuccess)
             return SECFailure; /* sslBuffer_Grow sets a memory error code. */
         room = ss->sec.ci.sendBuf.space - ss->sec.ci.sendBuf.len;
     }
 
     PRINT_BUF(60, (ss, "Append to Handshake", (unsigned char *)void_src, bytes));
-    rv = ssl3_UpdateHandshakeHashes(ss, src, bytes);
-    if (rv != SECSuccess)
-        return SECFailure; /* error code set by ssl3_UpdateHandshakeHashes */
+    if (!ss->firstHsDone || ss->version < SSL_LIBRARY_VERSION_TLS_1_3) {
+        rv = ssl3_UpdateHandshakeHashes(ss, src, bytes);
+        if (rv != SECSuccess)
+            return SECFailure; /* error code set by ssl3_UpdateHandshakeHashes */
+    }
 
     while (bytes > room) {
         if (room > 0)
             PORT_Memcpy(ss->sec.ci.sendBuf.buf + ss->sec.ci.sendBuf.len, src,
                         room);
         ss->sec.ci.sendBuf.len += room;
         rv = ssl3_FlushHandshake(ss, ssl_SEND_FLAG_FORCE_INTO_BUFFER);
         if (rv != SECSuccess) {
--- a/lib/ssl/sslimpl.h
+++ b/lib/ssl/sslimpl.h
@@ -139,16 +139,21 @@ typedef enum {
 typedef enum {
     ticket_allow_early_data = 1,
     ticket_allow_psk_ke = 2,
     ticket_allow_psk_dhe_ke = 4,
     ticket_allow_psk_auth = 8,
     ticket_allow_psk_sign_auth = 16
 } TLS13SessionTicketFlags;
 
+typedef enum {
+    update_not_requested = 0,
+    update_requested = 1
+} tls13KeyUpdateRequest;
+
 struct sslNamedGroupDefStr {
     /* The name is the value that is encoded on the wire in TLS. */
     SSLNamedGroup name;
     /* The number of bits in the group. */
     unsigned int bits;
     /* The key exchange algorithm this group provides. */
     SSLKEAType keaType;
     /* The OID that identifies the group to PKCS11.  This also determines
@@ -605,16 +610,17 @@ typedef struct SSL3HandshakeStateStr {
     SSL3HandshakeHashType hashType;
     sslBuffer messages; /* Accumulated handshake messages */
     /* PKCS #11 mode:
      * SSL 3.0 - TLS 1.1 use both |md5| and |sha|. |md5| is used for MD5 and
      * |sha| for SHA-1.
      * TLS 1.2 and later use only |sha|, for SHA-256. */
     PK11Context *md5;
     PK11Context *sha;
+    PK11Context *shaPostHandshake;
     SSLSignatureScheme signatureScheme;
     const ssl3KEADef *kea_def;
     ssl3CipherSuite cipher_suite;
     const ssl3CipherSuiteDef *suite_def;
     sslBuffer msg_body; /* protected by recvBufLock */
                         /* partial handshake message from record layer */
     unsigned int header_bytes;
     /* number of bytes consumed from handshake */
@@ -738,16 +744,21 @@ struct ssl3StateStr {
     ssl3CipherSpec *prSpec; /* pending read spec. */
     ssl3CipherSpec *cwSpec; /* current write spec. */
     ssl3CipherSpec *pwSpec; /* pending write spec. */
 
     /* This is true after the peer requests a key update; false after a key
      * update is initiated locally. */
     PRBool peerRequestedKeyUpdate;
 
+    /* This is true if we deferred sending a key update as
+     * post-handshake auth is in progress. */
+    PRBool keyUpdateDeferred;
+    tls13KeyUpdateRequest deferredKeyUpdateRequest;
+
     /* This is true after the server requests client certificate;
      * false after the client certificate is received.  Used by the
      * server. */
     PRBool clientCertRequested;
 
     CERTCertificate *clientCertificate;   /* used by client */
     SECKEYPrivateKey *clientPrivateKey;   /* used by client */
     CERTCertificateList *clientCertChain; /* used by client */
@@ -1208,25 +1219,34 @@ extern SECStatus ssl_CipherPrefSetDefaul
 
 extern SECStatus ssl3_ConstrainRangeByPolicy(void);
 
 extern SECStatus ssl3_InitState(sslSocket *ss);
 extern SECStatus Null_Cipher(void *ctx, unsigned char *output, unsigned int *outputLen,
                              unsigned int maxOutputLen, const unsigned char *input,
                              unsigned int inputLen);
 extern void ssl3_RestartHandshakeHashes(sslSocket *ss);
+typedef SECStatus (*sslUpdateHandshakeHashes)(sslSocket *ss,
+                                              const unsigned char *b,
+                                              unsigned int l);
 extern SECStatus ssl3_UpdateHandshakeHashes(sslSocket *ss,
                                             const unsigned char *b,
                                             unsigned int l);
+extern SECStatus ssl3_UpdatePostHandshakeHashes(sslSocket *ss,
+                                                const unsigned char *b,
+                                                unsigned int l);
 SECStatus
 ssl_HashHandshakeMessageInt(sslSocket *ss, SSLHandshakeType type,
                             PRUint32 dtlsSeq,
-                            const PRUint8 *b, PRUint32 length);
+                            const PRUint8 *b, PRUint32 length,
+                            sslUpdateHandshakeHashes cb);
 SECStatus ssl_HashHandshakeMessage(sslSocket *ss, SSLHandshakeType type,
                                    const PRUint8 *b, PRUint32 length);
+SECStatus ssl_HashPostHandshakeMessage(sslSocket *ss, SSLHandshakeType type,
+                                       const PRUint8 *b, PRUint32 length);
 
 /* Returns PR_TRUE if we are still waiting for the server to complete its
  * response to our client second round. Once we've received the Finished from
  * the server then there is no need to check false start.
  */
 extern PRBool ssl3_WaitingForServerSecondRound(sslSocket *ss);
 
 extern PRInt32 ssl3_SendRecord(sslSocket *ss, ssl3CipherSpec *cwSpec,
--- a/lib/ssl/sslsecur.c
+++ b/lib/ssl/sslsecur.c
@@ -715,17 +715,18 @@ ssl_SecureShutdown(sslSocket *ss, int ns
 
 static SECStatus
 tls13_CheckKeyUpdate(sslSocket *ss, SSLSecretDirection dir)
 {
     PRBool keyUpdate;
     ssl3CipherSpec *spec;
     sslSequenceNumber seqNum;
     sslSequenceNumber margin;
-    SECStatus rv;
+    tls13KeyUpdateRequest keyUpdateRequest;
+    SECStatus rv = SECSuccess;
 
     /* Bug 1413368: enable for DTLS */
     if (ss->version < SSL_LIBRARY_VERSION_TLS_1_3 || IS_DTLS(ss)) {
         return SECSuccess;
     }
 
     /* If both sides update at the same number, then this will cause two updates
      * to happen at once. The problem is that the KeyUpdate itself consumes a
@@ -750,19 +751,25 @@ tls13_CheckKeyUpdate(sslSocket *ss, SSLS
     ssl_ReleaseSpecReadLock(ss);
     if (!keyUpdate) {
         return SECSuccess;
     }
 
     SSL_TRC(5, ("%d: SSL[%d]: automatic key update at %llx for %s cipher spec",
                 SSL_GETPID(), ss->fd, seqNum,
                 (dir == ssl_secret_read) ? "read" : "write"));
+    keyUpdateRequest = (dir == ssl_secret_read) ? update_requested : update_not_requested;
     ssl_GetSSL3HandshakeLock(ss);
-    rv = tls13_SendKeyUpdate(ss, (dir == ssl_secret_read) ? update_requested : update_not_requested,
-                             dir == ssl_secret_write /* buffer */);
+    if (ss->ssl3.clientCertRequested) {
+        ss->ssl3.keyUpdateDeferred = PR_TRUE;
+        ss->ssl3.deferredKeyUpdateRequest = keyUpdateRequest;
+    } else {
+        rv = tls13_SendKeyUpdate(ss, keyUpdateRequest,
+                                 dir == ssl_secret_write /* buffer */);
+    }
     ssl_ReleaseSSL3HandshakeLock(ss);
     return rv;
 }
 
 int
 ssl_SecureRecv(sslSocket *ss, unsigned char *buf, int len, int flags)
 {
     int rv = 0;
--- a/lib/ssl/tls13con.c
+++ b/lib/ssl/tls13con.c
@@ -254,16 +254,22 @@ tls13_CheckHsState(sslSocket *ss, int er
                 SSL_GETPID(), ss->fd,
                 error_name,
                 tls13_HandshakeState(TLS13_BASE_WAIT_STATE(ss->ssl3.hs.ws)),
                 func, file, line));
     tls13_FatalError(ss, err, unexpected_message);
     return SECFailure;
 }
 
+PRBool
+tls13_IsPostHandshake(const sslSocket *ss)
+{
+    return ss->version >= SSL_LIBRARY_VERSION_TLS_1_3 && ss->firstHsDone;
+}
+
 SSLHashType
 tls13_GetHashForCipherSuite(ssl3CipherSuite suite)
 {
     const ssl3CipherSuiteDef *cipherDef =
         ssl_LookupCipherSuiteDef(suite);
     PORT_Assert(cipherDef);
     if (!cipherDef) {
         return ssl_hash_none;
@@ -677,18 +683,19 @@ tls13_SendKeyUpdate(sslSocket *ss, tls13
     SECStatus rv;
 
     SSL_TRC(3, ("%d: TLS13[%d]: %s send key update, response %s",
                 SSL_GETPID(), ss->fd, SSL_ROLE(ss),
                 (request == update_requested) ? "requested"
                                               : "not requested"));
 
     PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
-
-    if (!ss->firstHsDone) {
+    PORT_Assert(!ss->sec.isServer || !ss->ssl3.clientCertRequested);
+
+    if (!tls13_IsPostHandshake(ss)) {
         PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
         return SECFailure;
     }
 
     rv = TLS13_CHECK_HS_STATE(ss, SEC_ERROR_LIBRARY_FAILURE,
                               idle_handshake);
     if (rv != SECSuccess) {
         return SECFailure;
@@ -736,21 +743,26 @@ SECStatus
 SSLExp_KeyUpdate(PRFileDesc *fd, PRBool requestUpdate)
 {
     SECStatus rv;
     sslSocket *ss = ssl_FindSocket(fd);
     if (!ss) {
         return SECFailure;
     }
 
-    if (!ss->firstHsDone) {
+    if (!tls13_IsPostHandshake(ss)) {
         PORT_SetError(SEC_ERROR_INVALID_ARGS);
         return SECFailure;
     }
 
+    if (ss->ssl3.clientCertRequested) {
+        PORT_SetError(PR_WOULD_BLOCK_ERROR);
+        return SECFailure;
+    }
+
     rv = TLS13_CHECK_HS_STATE(ss, SEC_ERROR_INVALID_ARGS,
                               idle_handshake);
     if (rv != SECSuccess) {
         return SECFailure;
     }
 
     ssl_GetSSL3HandshakeLock(ss);
     rv = tls13_SendKeyUpdate(ss, requestUpdate ? update_requested : update_not_requested,
@@ -781,17 +793,17 @@ tls13_HandleKeyUpdate(sslSocket *ss, PRU
 
     SSL_TRC(3, ("%d: TLS13[%d]: %s handle key update",
                 SSL_GETPID(), ss->fd, SSL_ROLE(ss)));
 
     PORT_Assert(ss->opt.noLocks || ssl_HaveRecvBufLock(ss));
     PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
 
     PORT_Assert(ss->firstHsDone);
-    if (!ss->firstHsDone) {
+    if (!tls13_IsPostHandshake(ss)) {
         FATAL_ERROR(ss, SSL_ERROR_RX_UNEXPECTED_KEY_UPDATE, unexpected_message);
         return SECFailure;
     }
 
     rv = TLS13_CHECK_HS_STATE(ss, SSL_ERROR_RX_UNEXPECTED_KEY_UPDATE,
                               idle_handshake);
     if (rv != SECSuccess) {
         /* We should never be idle_handshake prior to firstHsDone. */
@@ -815,17 +827,22 @@ tls13_HandleKeyUpdate(sslSocket *ss, PRU
 
     rv = tls13_UpdateTrafficKeys(ss, ssl_secret_read);
     if (rv != SECSuccess) {
         return SECFailure; /* Error code set by tls13_UpdateTrafficKeys. */
     }
 
     if (update == update_requested) {
         PRBool sendUpdate;
-        if (ss->ssl3.peerRequestedKeyUpdate) {
+        if (ss->ssl3.clientCertRequested) {
+            /* Post-handshake auth is in progress; defer sending a key update. */
+            ss->ssl3.keyUpdateDeferred = PR_TRUE;
+            ss->ssl3.deferredKeyUpdateRequest = update_not_requested;
+            sendUpdate = PR_FALSE;
+        } else if (ss->ssl3.peerRequestedKeyUpdate) {
             /* Only send an update if we have sent with the current spec.  This
              * prevents us from being forced to crank forward pointlessly. */
             ssl_GetSpecReadLock(ss);
             sendUpdate = ss->ssl3.cwSpec->nextSeqNum > 0;
             ssl_ReleaseSpecReadLock(ss);
         } else {
             sendUpdate = PR_TRUE;
         }
@@ -852,17 +869,17 @@ SSLExp_SendCertificateRequest(PRFileDesc
     }
 
     /* Not supported. */
     if (IS_DTLS(ss)) {
         PORT_SetError(SSL_ERROR_FEATURE_NOT_SUPPORTED_FOR_VERSION);
         return SECFailure;
     }
 
-    if (!ss->firstHsDone || ss->version < SSL_LIBRARY_VERSION_TLS_1_3) {
+    if (!tls13_IsPostHandshake(ss)) {
         PORT_SetError(SEC_ERROR_INVALID_ARGS);
         return SECFailure;
     }
 
     if (ss->ssl3.clientCertRequested) {
         PORT_SetError(PR_WOULD_BLOCK_ERROR);
         return SECFailure;
     }
@@ -2190,20 +2207,30 @@ tls13_HandleClientKeyShare(sslSocket *ss
  *         CertificateExtension certificate_extensions<0..2^16-1>;
  *     } CertificateRequest;
  */
 static SECStatus
 tls13_SendCertificateRequest(sslSocket *ss)
 {
     SECStatus rv;
     sslBuffer extensionBuf = SSL_BUFFER_EMPTY;
+    unsigned int offset = 0;
 
     SSL_TRC(3, ("%d: TLS13[%d]: begin send certificate_request",
                 SSL_GETPID(), ss->fd));
 
+    if (ss->firstHsDone) {
+        PORT_Assert(ss->ssl3.hs.shaPostHandshake == NULL);
+        ss->ssl3.hs.shaPostHandshake = PK11_CloneContext(ss->ssl3.hs.sha);
+        if (ss->ssl3.hs.shaPostHandshake == NULL) {
+            ssl_MapLowLevelError(SSL_ERROR_SHA_DIGEST_FAILURE);
+            return SECFailure;
+        }
+    }
+
     rv = ssl_ConstructExtensions(ss, &extensionBuf, ssl_hs_certificate_request);
     if (rv != SECSuccess) {
         return SECFailure; /* Code already set. */
     }
     /* We should always have at least one of these. */
     PORT_Assert(SSL_BUFFER_LEN(&extensionBuf) > 0);
 
     /* Create a new request context for post-handshake authentication */
@@ -2217,16 +2244,18 @@ tls13_SendCertificateRequest(sslSocket *
         }
 
         SECITEM_FreeItem(&ss->xtnData.certReqContext, PR_FALSE);
         rv = SECITEM_CopyItem(NULL, &ss->xtnData.certReqContext, &contextItem);
         if (rv != SECSuccess) {
             FATAL_ERROR(ss, SEC_ERROR_NO_MEMORY, internal_error);
             goto loser;
         }
+
+        offset = SSL_BUFFER_LEN(&ss->sec.ci.sendBuf);
     }
 
     rv = ssl3_AppendHandshakeHeader(ss, ssl_hs_certificate_request,
                                     1 + /* request context length */
                                         ss->xtnData.certReqContext.len +
                                         2 + /* extension length */
                                         SSL_BUFFER_LEN(&extensionBuf));
     if (rv != SECSuccess) {
@@ -2240,16 +2269,25 @@ tls13_SendCertificateRequest(sslSocket *
         goto loser; /* err set by AppendHandshake. */
     }
     /* Extensions. */
     rv = ssl3_AppendBufferToHandshakeVariable(ss, &extensionBuf, 2);
     if (rv != SECSuccess) {
         goto loser; /* err set by AppendHandshake. */
     }
 
+    if (ss->firstHsDone) {
+        rv = ssl3_UpdatePostHandshakeHashes(ss,
+                                            SSL_BUFFER_BASE(&ss->sec.ci.sendBuf) + offset,
+                                            SSL_BUFFER_LEN(&ss->sec.ci.sendBuf) - offset);
+        if (rv != SECSuccess) {
+            goto loser;
+        }
+    }
+
     sslBuffer_Clear(&extensionBuf);
     return SECSuccess;
 
 loser:
     sslBuffer_Clear(&extensionBuf);
     return SECFailure;
 }
 
@@ -2405,17 +2443,29 @@ tls13_HandleCertificateRequest(sslSocket
     } else {
         rv = TLS13_CHECK_HS_STATE(ss, SSL_ERROR_RX_UNEXPECTED_CERT_REQUEST,
                                   wait_cert_request);
     }
     if (rv != SECSuccess) {
         return SECFailure;
     }
 
-    if (ss->firstHsDone) {
+    if (tls13_IsPostHandshake(ss)) {
+        PORT_Assert(ss->ssl3.hs.shaPostHandshake == NULL);
+        ss->ssl3.hs.shaPostHandshake = PK11_CloneContext(ss->ssl3.hs.sha);
+        if (ss->ssl3.hs.shaPostHandshake == NULL) {
+            ssl_MapLowLevelError(SSL_ERROR_SHA_DIGEST_FAILURE);
+            return SECFailure;
+        }
+        rv = ssl_HashPostHandshakeMessage(ss, ssl_hs_certificate_request, b, length);
+        if (rv != SECSuccess) {
+            FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error);
+            return SECFailure;
+        }
+
         /* clean up anything left from previous handshake. */
         if (ss->ssl3.clientCertChain != NULL) {
             CERT_DestroyCertificateList(ss->ssl3.clientCertChain);
             ss->ssl3.clientCertChain = NULL;
         }
         if (ss->ssl3.clientCertificate != NULL) {
             CERT_DestroyCertificate(ss->ssl3.clientCertificate);
             ss->ssl3.clientCertificate = NULL;
@@ -2436,17 +2486,17 @@ tls13_HandleCertificateRequest(sslSocket
 
     rv = ssl3_ConsumeHandshakeVariable(ss, &context, 1, &b, &length);
     if (rv != SECSuccess) {
         return SECFailure;
     }
 
     /* Unless it is a post-handshake client auth, the certificate
      * request context must be empty. */
-    if (!ss->firstHsDone && context.len > 0) {
+    if (!tls13_IsPostHandshake(ss) && context.len > 0) {
         FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_CERT_REQUEST, illegal_parameter);
         return SECFailure;
     }
 
     rv = ssl3_ConsumeHandshakeVariable(ss, &extensionsData, 2, &b, &length);
     if (rv != SECSuccess) {
         return SECFailure;
     }
@@ -2496,16 +2546,19 @@ tls13_HandleCertificateRequest(sslSocket
             if (sendAlert != no_alert) {
                 FATAL_ERROR(ss, PORT_GetError(), sendAlert);
             } else {
                 LOG_ERROR(ss, PORT_GetError());
             }
             return SECFailure;
         }
         PORT_Assert(ss->ssl3.hs.ws == idle_handshake);
+        PORT_Assert(ss->ssl3.hs.shaPostHandshake != NULL);
+        PK11_DestroyContext(ss->ssl3.hs.shaPostHandshake, PR_TRUE);
+        ss->ssl3.hs.shaPostHandshake = NULL;
     } else {
         TLS13_SET_HS_STATE(ss, wait_server_cert);
     }
     return SECSuccess;
 }
 
 PRBool
 tls13_ShouldRequestClientAuth(sslSocket *ss)
@@ -3053,16 +3106,25 @@ tls13_HandleCertificate(sslSocket *ss, P
     if (rv != SECSuccess)
         return SECFailure;
 
     /* We can ignore any other cleartext from the client. */
     if (ss->sec.isServer && IS_DTLS(ss)) {
         ssl_CipherSpecReleaseByEpoch(ss, ssl_secret_read, TrafficKeyClearText);
         dtls_ReceivedFirstMessageInFlight(ss);
     }
+
+    if (ss->firstHsDone) {
+        rv = ssl_HashPostHandshakeMessage(ss, ssl_hs_certificate, b, length);
+        if (rv != SECSuccess) {
+            PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+            return SECFailure;
+        }
+    }
+
     /* Process the context string */
     rv = ssl3_ConsumeHandshakeVariable(ss, &context, 1, &b, &length);
     if (rv != SECSuccess)
         return SECFailure;
 
     if (ss->ssl3.clientCertRequested) {
         PORT_Assert(ss->sec.isServer);
         if (SECITEM_CompareItem(&context, &ss->xtnData.certReqContext) != 0) {
@@ -3610,17 +3672,21 @@ tls13_ComputeHandshakeHashes(sslSocket *
 
         if (PK11_DigestOp(ctx,
                           ss->ssl3.hs.messages.buf,
                           ss->ssl3.hs.messages.len) != SECSuccess) {
             ssl_MapLowLevelError(SSL_ERROR_SHA_DIGEST_FAILURE);
             goto loser;
         }
     } else {
-        ctx = PK11_CloneContext(ss->ssl3.hs.sha);
+        if (ss->firstHsDone) {
+            ctx = PK11_CloneContext(ss->ssl3.hs.shaPostHandshake);
+        } else {
+            ctx = PK11_CloneContext(ss->ssl3.hs.sha);
+        }
         if (!ctx) {
             ssl_MapLowLevelError(SSL_ERROR_SHA_DIGEST_FAILURE);
             return SECFailure;
         }
     }
 
     rv = PK11_DigestFinal(ctx, hashes->u.raw,
                           &hashes->len,
@@ -4064,17 +4130,21 @@ tls13_HandleCertificateVerify(sslSocket 
         return SECFailure;
     }
 
     rv = tls13_ComputeHandshakeHashes(ss, &hashes);
     if (rv != SECSuccess) {
         return SECFailure;
     }
 
-    rv = ssl_HashHandshakeMessage(ss, ssl_hs_certificate_verify, b, length);
+    if (ss->firstHsDone) {
+        rv = ssl_HashPostHandshakeMessage(ss, ssl_hs_certificate_verify, b, length);
+    } else {
+        rv = ssl_HashHandshakeMessage(ss, ssl_hs_certificate_verify, b, length);
+    }
     if (rv != SECSuccess) {
         PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
         return SECFailure;
     }
 
     rv = ssl_ConsumeSignatureScheme(ss, &b, &length, &sigScheme);
     if (rv != SECSuccess) {
         FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_CERT_VERIFY, illegal_parameter);
@@ -4124,20 +4194,16 @@ tls13_HandleCertificateVerify(sslSocket 
             ss, ss->xtnData.sigSchemes, ss->xtnData.numSigSchemes,
             &ss->xtnData.certReqAuthorities);
         if (rv != SECSuccess) {
             FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error);
             return rv;
         }
     }
 
-    if (ss->ssl3.clientCertRequested) {
-        PORT_Assert(ss->sec.isServer);
-        ss->ssl3.clientCertRequested = PR_FALSE;
-    }
     TLS13_SET_HS_STATE(ss, wait_finished);
 
     return SECSuccess;
 }
 
 static SECStatus
 tls13_ComputePskBinderHash(sslSocket *ss, unsigned int prefixLength,
                            SSL3Hashes *hashes)
@@ -4388,17 +4454,21 @@ tls13_CommonHandleFinished(sslSocket *ss
     ss->ssl3.hs.endOfFlight = PR_TRUE;
 
     rv = tls13_ComputeHandshakeHashes(ss, &hashes);
     if (rv != SECSuccess) {
         LOG_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE);
         return SECFailure;
     }
 
-    rv = ssl_HashHandshakeMessage(ss, ssl_hs_finished, b, length);
+    if (ss->firstHsDone) {
+        rv = ssl_HashPostHandshakeMessage(ss, ssl_hs_finished, b, length);
+    } else {
+        rv = ssl_HashHandshakeMessage(ss, ssl_hs_finished, b, length);
+    }
     if (rv != SECSuccess) {
         PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
         return SECFailure;
     }
 
     return tls13_VerifyFinished(ss, ssl_hs_finished,
                                 key, b, length, &hashes);
 }
@@ -4438,16 +4508,32 @@ tls13_ServerHandleFinished(sslSocket *ss
                                     ss->firstHsDone ? ss->ssl3.hs.clientTrafficSecret : ss->ssl3.hs.clientHsTrafficSecret,
                                     b, length);
     if (rv != SECSuccess) {
         return SECFailure;
     }
 
     if (ss->firstHsDone) {
         TLS13_SET_HS_STATE(ss, idle_handshake);
+
+        PORT_Assert(ss->ssl3.hs.shaPostHandshake != NULL);
+        PK11_DestroyContext(ss->ssl3.hs.shaPostHandshake, PR_TRUE);
+        ss->ssl3.hs.shaPostHandshake = NULL;
+
+        ss->ssl3.clientCertRequested = PR_FALSE;
+
+        if (ss->ssl3.keyUpdateDeferred) {
+            rv = tls13_SendKeyUpdate(ss, ss->ssl3.deferredKeyUpdateRequest,
+                                     PR_FALSE);
+            if (rv != SECSuccess) {
+                return SECFailure; /* error is set. */
+            }
+            ss->ssl3.keyUpdateDeferred = PR_FALSE;
+        }
+
         return SECSuccess;
     }
 
     if (!tls13_ShouldRequestClientAuth(ss) &&
         (ss->ssl3.hs.zeroRttState != ssl_0rtt_done)) {
         dtls_ReceivedFirstMessageInFlight(ss);
     }
 
@@ -4520,52 +4606,80 @@ tls13_FinishHandshake(sslSocket *ss)
 
 /* Do the parts of sending the client's second round that require
  * the XmitBuf lock. */
 static SECStatus
 tls13_SendClientSecondFlight(sslSocket *ss, PRBool sendClientCert,
                              SSL3AlertDescription *sendAlert)
 {
     SECStatus rv;
+    unsigned int offset = 0;
 
     PORT_Assert(ss->opt.noLocks || ssl_HaveXmitBufLock(ss));
 
     *sendAlert = internal_error;
 
+    if (ss->firstHsDone) {
+        offset = SSL_BUFFER_LEN(&ss->sec.ci.sendBuf);
+    }
+
     if (ss->ssl3.sendEmptyCert) {
         ss->ssl3.sendEmptyCert = PR_FALSE;
         rv = ssl3_SendEmptyCertificate(ss);
         /* Don't send verify */
         if (rv != SECSuccess) {
             return SECFailure; /* error code is set. */
         }
     } else if (sendClientCert) {
         rv = tls13_SendCertificate(ss);
         if (rv != SECSuccess) {
             return SECFailure; /* error code is set. */
         }
     }
+
+    if (ss->firstHsDone) {
+        rv = ssl3_UpdatePostHandshakeHashes(ss,
+                                            SSL_BUFFER_BASE(&ss->sec.ci.sendBuf) + offset,
+                                            SSL_BUFFER_LEN(&ss->sec.ci.sendBuf) - offset);
+        if (rv != SECSuccess) {
+            return SECFailure; /* error code is set. */
+        }
+    }
+
     if (ss->ssl3.hs.clientCertRequested) {
         SECITEM_FreeItem(&ss->xtnData.certReqContext, PR_FALSE);
         if (ss->xtnData.certReqAuthorities.arena) {
             PORT_FreeArena(ss->xtnData.certReqAuthorities.arena, PR_FALSE);
             ss->xtnData.certReqAuthorities.arena = NULL;
         }
         PORT_Memset(&ss->xtnData.certReqAuthorities, 0,
                     sizeof(ss->xtnData.certReqAuthorities));
         ss->ssl3.hs.clientCertRequested = PR_FALSE;
     }
 
     if (sendClientCert) {
+        if (ss->firstHsDone) {
+            offset = SSL_BUFFER_LEN(&ss->sec.ci.sendBuf);
+        }
+
         rv = tls13_SendCertificateVerify(ss, ss->ssl3.clientPrivateKey);
         SECKEY_DestroyPrivateKey(ss->ssl3.clientPrivateKey);
         ss->ssl3.clientPrivateKey = NULL;
         if (rv != SECSuccess) {
             return SECFailure; /* err is set. */
         }
+
+        if (ss->firstHsDone) {
+            rv = ssl3_UpdatePostHandshakeHashes(ss,
+                                                SSL_BUFFER_BASE(&ss->sec.ci.sendBuf) + offset,
+                                                SSL_BUFFER_LEN(&ss->sec.ci.sendBuf) - offset);
+            if (rv != SECSuccess) {
+                return SECFailure; /* error is set. */
+            }
+        }
     }
 
     rv = tls13_SendFinished(ss, ss->firstHsDone ? ss->ssl3.hs.clientTrafficSecret : ss->ssl3.hs.clientHsTrafficSecret);
     if (rv != SECSuccess) {
         return SECFailure; /* err code was set. */
     }
     rv = ssl3_FlushHandshake(ss, 0);
     if (rv != SECSuccess) {
@@ -4820,18 +4934,17 @@ SSLExp_SendSessionTicket(PRFileDesc *fd,
         return SECFailure;
     }
 
     if (IS_DTLS(ss)) {
         PORT_SetError(SSL_ERROR_FEATURE_NOT_SUPPORTED_FOR_VERSION);
         return SECFailure;
     }
 
-    if (!ss->sec.isServer || !ss->firstHsDone ||
-        ss->version < SSL_LIBRARY_VERSION_TLS_1_3 ||
+    if (!ss->sec.isServer || !tls13_IsPostHandshake(ss) ||
         tokenLen > 0xffff) {
         PORT_SetError(SEC_ERROR_INVALID_ARGS);
         return SECFailure;
     }
 
     ssl_GetSSL3HandshakeLock(ss);
     ssl_GetXmitBufLock(ss);
     rv = tls13_SendNewSessionTicket(ss, token, tokenLen);
@@ -4857,17 +4970,17 @@ tls13_HandleNewSessionTicket(sslSocket *
     SSL_TRC(3, ("%d: TLS13[%d]: handle new session ticket message",
                 SSL_GETPID(), ss->fd));
 
     rv = TLS13_CHECK_HS_STATE(ss, SSL_ERROR_RX_UNEXPECTED_NEW_SESSION_TICKET,
                               idle_handshake);
     if (rv != SECSuccess) {
         return SECFailure;
     }
-    if (!ss->firstHsDone || ss->sec.isServer) {
+    if (!tls13_IsPostHandshake(ss) || ss->sec.isServer) {
         FATAL_ERROR(ss, SSL_ERROR_RX_UNEXPECTED_NEW_SESSION_TICKET,
                     unexpected_message);
         return SECFailure;
     }
 
     ticket.received_timestamp = ssl_TimeUsec();
     rv = ssl3_ConsumeHandshakeNumber(ss, &ticket.ticket_lifetime_hint, 4, &b,
                                      &length);
--- a/lib/ssl/tls13con.h
+++ b/lib/ssl/tls13con.h
@@ -13,21 +13,16 @@
 #include "sslspec.h"
 
 typedef enum {
     tls13_extension_allowed,
     tls13_extension_disallowed,
     tls13_extension_unknown
 } tls13ExtensionStatus;
 
-typedef enum {
-    update_not_requested = 0,
-    update_requested = 1
-} tls13KeyUpdateRequest;
-
 #define TLS13_MAX_FINISHED_SIZE 64
 
 SECStatus tls13_UnprotectRecord(
     sslSocket *ss, ssl3CipherSpec *spec,
     SSL3Ciphertext *cText, sslBuffer *plaintext,
     SSLContentType *innerType,
     SSL3AlertDescription *alert);
 
@@ -42,16 +37,18 @@ void tls13_SetHsState(sslSocket *ss, SSL
 
 /* Return PR_TRUE if the socket is in one of the given states, else return
  * PR_FALSE. Only call the macro not the function, because the trailing
  * wait_invalid is needed to terminate the argument list. */
 PRBool tls13_InHsState(sslSocket *ss, ...);
 #define TLS13_IN_HS_STATE(ss, ...) \
     tls13_InHsState(ss, __VA_ARGS__, wait_invalid)
 
+PRBool tls13_IsPostHandshake(const sslSocket *ss);
+
 SSLHashType tls13_GetHashForCipherSuite(ssl3CipherSuite suite);
 SSLHashType tls13_GetHash(const sslSocket *ss);
 unsigned int tls13_GetHashSizeForHash(SSLHashType hash);
 unsigned int tls13_GetHashSize(const sslSocket *ss);
 CK_MECHANISM_TYPE tls13_GetHkdfMechanism(sslSocket *ss);
 CK_MECHANISM_TYPE tls13_GetHkdfMechanismForHash(SSLHashType hash);
 SECStatus tls13_ComputeHash(sslSocket *ss, SSL3Hashes *hashes,
                             const PRUint8 *buf, unsigned int len);
--- a/lib/ssl/tls13hashstate.c
+++ b/lib/ssl/tls13hashstate.c
@@ -152,33 +152,35 @@ tls13_RecoverHashState(sslSocket *ss,
     if (hashLen != tls13_GetHashSize(ss)) {
         FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_CLIENT_HELLO, illegal_parameter);
         return SECFailure;
     }
 
     /* Now reinject the message. */
     SSL_ASSERT_HASHES_EMPTY(ss);
     rv = ssl_HashHandshakeMessageInt(ss, ssl_hs_message_hash, 0,
-                                     SSL_READER_CURRENT(&reader), hashLen);
+                                     SSL_READER_CURRENT(&reader), hashLen,
+                                     ssl3_UpdateHandshakeHashes);
     if (rv != SECSuccess) {
         return SECFailure;
     }
 
     /* And finally reinject the HRR. */
     rv = tls13_ConstructHelloRetryRequest(ss, cipherSuite,
                                           selectedGroup,
                                           cookie, cookieLen,
                                           &messageBuf);
     if (rv != SECSuccess) {
         return SECFailure;
     }
 
     rv = ssl_HashHandshakeMessageInt(ss, ssl_hs_server_hello, 0,
                                      SSL_BUFFER_BASE(&messageBuf),
-                                     SSL_BUFFER_LEN(&messageBuf));
+                                     SSL_BUFFER_LEN(&messageBuf),
+                                     ssl3_UpdateHandshakeHashes);
     sslBuffer_Clear(&messageBuf);
     if (rv != SECSuccess) {
         return SECFailure;
     }
 
     *previousCipherSuite = cipherSuite;
     *previousGroup = selectedGroup;
     return SECSuccess;