Bug 1471970, add support for post-handshake authentication, r=mt
authorDaiki Ueno <dueno@redhat.com>
Wed, 20 Feb 2019 10:25:50 +0100
changeset 15019 1f04eea8834ab44520d8990dee6dff43b144074f
parent 15018 c1a1ee048c07fef6ef889e5ff459ddbda8da0273
child 15020 20543fe8ce40f0bf4fb4a21016d3a3edbb2537c9
push id3271
push userdueno@redhat.com
push dateWed, 20 Feb 2019 12:04:50 +0000
reviewersmt
bugs1471970
Bug 1471970, add support for post-handshake authentication, r=mt Summary: This adds handling of the post_handshake_auth extension in CH and exposes tls13_SendCertificateRequest as an experimental API. For practical use, it might need another function that checks if the post_handshake_auth extension is received. Reviewers: mt Reviewed By: mt Bug #: 1471970 Differential Revision: https://phabricator.services.mozilla.com/D14154
gtests/ssl_gtest/ssl_auth_unittest.cc
lib/ssl/ssl.h
lib/ssl/ssl3con.c
lib/ssl/ssl3ext.c
lib/ssl/sslerr.h
lib/ssl/sslexp.h
lib/ssl/sslimpl.h
lib/ssl/sslsock.c
lib/ssl/sslt.h
lib/ssl/tls13con.c
lib/ssl/tls13con.h
lib/ssl/tls13exthandle.c
lib/ssl/tls13exthandle.h
--- a/gtests/ssl_gtest/ssl_auth_unittest.cc
+++ b/gtests/ssl_gtest/ssl_auth_unittest.cc
@@ -171,16 +171,331 @@ TEST_P(TlsConnectGenericPre13, ServerAut
 
 TEST_P(TlsConnectGeneric, ClientAuth) {
   client_->SetupClientAuth();
   server_->RequestClientAuth(true);
   Connect();
   CheckKeys();
 }
 
+class TlsCertificateRequestContextRecorder : public TlsHandshakeFilter {
+ public:
+  TlsCertificateRequestContextRecorder(const std::shared_ptr<TlsAgent>& a,
+                                       uint8_t handshake_type)
+      : TlsHandshakeFilter(a, {handshake_type}), buffer_(), filtered_(false) {
+    EnableDecryption();
+  }
+
+  bool filtered() const { return filtered_; }
+  const DataBuffer& buffer() const { return buffer_; }
+
+ protected:
+  virtual PacketFilter::Action FilterHandshake(const HandshakeHeader& header,
+                                               const DataBuffer& input,
+                                               DataBuffer* output) {
+    assert(1 < input.len());
+    size_t len = input.data()[0];
+    assert(len + 1 < input.len());
+    buffer_.Assign(input.data() + 1, len);
+    filtered_ = true;
+    return KEEP;
+  }
+
+ private:
+  DataBuffer buffer_;
+  bool filtered_;
+};
+
+// All stream only tests; DTLS isn't supported yet.
+
+TEST_F(TlsConnectStreamTls13, PostHandshakeAuth) {
+  EnsureTlsSetup();
+  auto capture_cert_req = MakeTlsFilter<TlsCertificateRequestContextRecorder>(
+      server_, kTlsHandshakeCertificateRequest);
+  auto capture_certificate =
+      MakeTlsFilter<TlsCertificateRequestContextRecorder>(
+          client_, kTlsHandshakeCertificate);
+  client_->SetupClientAuth();
+  EXPECT_EQ(SECSuccess, SSL_OptionSet(client_->ssl_fd(),
+                                      SSL_ENABLE_POST_HANDSHAKE_AUTH, PR_TRUE));
+  size_t called = 0;
+  server_->SetAuthCertificateCallback(
+      [&called](TlsAgent*, PRBool, PRBool) -> SECStatus {
+        called++;
+        return SECSuccess;
+      });
+  Connect();
+  EXPECT_EQ(0U, called);
+  EXPECT_FALSE(capture_cert_req->filtered());
+  EXPECT_FALSE(capture_certificate->filtered());
+  // Send CertificateRequest.
+  EXPECT_EQ(SECSuccess, SSL_SendCertificateRequest(server_->ssl_fd()))
+      << "Unexpected error: " << PORT_ErrorToName(PORT_GetError());
+  // Need to do a round-trip so that the post-handshake message is
+  // handled on both client and server.
+  server_->SendData(50);
+  client_->ReadBytes(50);
+  client_->SendData(50);
+  server_->ReadBytes(50);
+  EXPECT_EQ(1U, called);
+  EXPECT_TRUE(capture_cert_req->filtered());
+  EXPECT_TRUE(capture_certificate->filtered());
+  // Check if a non-empty request context is generated and it is
+  // properly sent back.
+  EXPECT_LT(0U, capture_cert_req->buffer().len());
+  EXPECT_EQ(capture_cert_req->buffer().len(),
+            capture_certificate->buffer().len());
+  EXPECT_EQ(0, memcmp(capture_cert_req->buffer().data(),
+                      capture_certificate->buffer().data(),
+                      capture_cert_req->buffer().len()));
+  ScopedCERTCertificate cert1(SSL_PeerCertificate(server_->ssl_fd()));
+  ScopedCERTCertificate cert2(SSL_LocalCertificate(client_->ssl_fd()));
+  EXPECT_TRUE(SECITEM_ItemsAreEqual(&cert1->derCert, &cert2->derCert));
+}
+
+static SECStatus GetClientAuthDataHook(void* self, PRFileDesc* fd,
+                                       CERTDistNames* caNames,
+                                       CERTCertificate** clientCert,
+                                       SECKEYPrivateKey** clientKey) {
+  ScopedCERTCertificate cert;
+  ScopedSECKEYPrivateKey priv;
+  // use a different certificate than TlsAgent::kClient
+  if (!TlsAgent::LoadCertificate(TlsAgent::kRsa2048, &cert, &priv)) {
+    return SECFailure;
+  }
+
+  *clientCert = cert.release();
+  *clientKey = priv.release();
+  return SECSuccess;
+}
+
+TEST_F(TlsConnectStreamTls13, PostHandshakeAuthMultiple) {
+  client_->SetupClientAuth();
+  EXPECT_EQ(SECSuccess, SSL_OptionSet(client_->ssl_fd(),
+                                      SSL_ENABLE_POST_HANDSHAKE_AUTH, PR_TRUE));
+  size_t called = 0;
+  server_->SetAuthCertificateCallback(
+      [&called](TlsAgent*, PRBool, PRBool) -> SECStatus {
+        called++;
+        return SECSuccess;
+      });
+  Connect();
+  EXPECT_EQ(0U, called);
+  EXPECT_EQ(nullptr, SSL_PeerCertificate(server_->ssl_fd()));
+  // Send 1st CertificateRequest.
+  EXPECT_EQ(SECSuccess, SSL_SendCertificateRequest(server_->ssl_fd()))
+      << "Unexpected error: " << PORT_ErrorToName(PORT_GetError());
+  server_->SendData(50);
+  client_->ReadBytes(50);
+  client_->SendData(50);
+  server_->ReadBytes(50);
+  EXPECT_EQ(1U, called);
+  ScopedCERTCertificate cert1(SSL_PeerCertificate(server_->ssl_fd()));
+  ScopedCERTCertificate cert2(SSL_LocalCertificate(client_->ssl_fd()));
+  EXPECT_TRUE(SECITEM_ItemsAreEqual(&cert1->derCert, &cert2->derCert));
+  // Send 2nd CertificateRequest.
+  EXPECT_EQ(SECSuccess, SSL_GetClientAuthDataHook(
+                            client_->ssl_fd(), GetClientAuthDataHook, nullptr));
+  EXPECT_EQ(SECSuccess, SSL_SendCertificateRequest(server_->ssl_fd()))
+      << "Unexpected error: " << PORT_ErrorToName(PORT_GetError());
+  server_->SendData(50);
+  client_->ReadBytes(50);
+  client_->SendData(50);
+  server_->ReadBytes(50);
+  EXPECT_EQ(2U, called);
+  ScopedCERTCertificate cert3(SSL_PeerCertificate(server_->ssl_fd()));
+  ScopedCERTCertificate cert4(SSL_LocalCertificate(client_->ssl_fd()));
+  EXPECT_TRUE(SECITEM_ItemsAreEqual(&cert3->derCert, &cert4->derCert));
+  EXPECT_FALSE(SECITEM_ItemsAreEqual(&cert3->derCert, &cert1->derCert));
+}
+
+TEST_F(TlsConnectStreamTls13, PostHandshakeAuthConcurrent) {
+  client_->SetupClientAuth();
+  EXPECT_EQ(SECSuccess, SSL_OptionSet(client_->ssl_fd(),
+                                      SSL_ENABLE_POST_HANDSHAKE_AUTH, PR_TRUE));
+  Connect();
+  // 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, 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());
+}
+
+TEST_F(TlsConnectStreamTls13, PostHandshakeAuthAfterClientAuth) {
+  client_->SetupClientAuth();
+  server_->RequestClientAuth(true);
+  EXPECT_EQ(SECSuccess, SSL_OptionSet(client_->ssl_fd(),
+                                      SSL_ENABLE_POST_HANDSHAKE_AUTH, PR_TRUE));
+  size_t called = 0;
+  server_->SetAuthCertificateCallback(
+      [&called](TlsAgent*, PRBool, PRBool) -> SECStatus {
+        called++;
+        return SECSuccess;
+      });
+  Connect();
+  EXPECT_EQ(1U, called);
+  ScopedCERTCertificate cert1(SSL_PeerCertificate(server_->ssl_fd()));
+  ScopedCERTCertificate cert2(SSL_LocalCertificate(client_->ssl_fd()));
+  EXPECT_TRUE(SECITEM_ItemsAreEqual(&cert1->derCert, &cert2->derCert));
+  // Send CertificateRequest.
+  EXPECT_EQ(SECSuccess, SSL_GetClientAuthDataHook(
+                            client_->ssl_fd(), GetClientAuthDataHook, nullptr));
+  EXPECT_EQ(SECSuccess, SSL_SendCertificateRequest(server_->ssl_fd()))
+      << "Unexpected error: " << PORT_ErrorToName(PORT_GetError());
+  server_->SendData(50);
+  client_->ReadBytes(50);
+  client_->SendData(50);
+  server_->ReadBytes(50);
+  EXPECT_EQ(2U, called);
+  ScopedCERTCertificate cert3(SSL_PeerCertificate(server_->ssl_fd()));
+  ScopedCERTCertificate cert4(SSL_LocalCertificate(client_->ssl_fd()));
+  EXPECT_TRUE(SECITEM_ItemsAreEqual(&cert3->derCert, &cert4->derCert));
+  EXPECT_FALSE(SECITEM_ItemsAreEqual(&cert3->derCert, &cert1->derCert));
+}
+
+// Damages the request context in a CertificateRequest message.
+// We don't modify a Certificate message instead, so that the client
+// can compute CertificateVerify correctly.
+class TlsDamageCertificateRequestContextFilter : public TlsHandshakeFilter {
+ public:
+  TlsDamageCertificateRequestContextFilter(const std::shared_ptr<TlsAgent>& a)
+      : TlsHandshakeFilter(a, {kTlsHandshakeCertificateRequest}) {
+    EnableDecryption();
+  }
+
+ protected:
+  virtual PacketFilter::Action FilterHandshake(const HandshakeHeader& header,
+                                               const DataBuffer& input,
+                                               DataBuffer* output) {
+    *output = input;
+    assert(1 < output->len());
+    // The request context has a 1 octet length.
+    output->data()[1] ^= 73;
+    return CHANGE;
+  }
+};
+
+TEST_F(TlsConnectStreamTls13, PostHandshakeAuthContextMismatch) {
+  EnsureTlsSetup();
+  MakeTlsFilter<TlsDamageCertificateRequestContextFilter>(server_);
+  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());
+  server_->SendData(50);
+  client_->ReadBytes(50);
+  client_->SendData(50);
+  server_->ExpectSendAlert(kTlsAlertIllegalParameter);
+  server_->ReadBytes(50);
+  EXPECT_EQ(SSL_ERROR_RX_MALFORMED_CERTIFICATE, PORT_GetError());
+  server_->ExpectReadWriteError();
+  server_->SendData(50);
+  client_->ExpectReceiveAlert(kTlsAlertIllegalParameter);
+  client_->ReadBytes(50);
+  EXPECT_EQ(SSL_ERROR_ILLEGAL_PARAMETER_ALERT, PORT_GetError());
+}
+
+// Replaces signature in a CertificateVerify message.
+class TlsDamageSignatureFilter : public TlsHandshakeFilter {
+ public:
+  TlsDamageSignatureFilter(const std::shared_ptr<TlsAgent>& a)
+      : TlsHandshakeFilter(a, {kTlsHandshakeCertificateVerify}) {
+    EnableDecryption();
+  }
+
+ protected:
+  virtual PacketFilter::Action FilterHandshake(const HandshakeHeader& header,
+                                               const DataBuffer& input,
+                                               DataBuffer* output) {
+    *output = input;
+    assert(2 < output->len());
+    // The signature follows a 2-octet signature scheme.
+    output->data()[2] ^= 73;
+    return CHANGE;
+  }
+};
+
+TEST_F(TlsConnectStreamTls13, PostHandshakeAuthBadSignature) {
+  EnsureTlsSetup();
+  MakeTlsFilter<TlsDamageSignatureFilter>(client_);
+  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());
+  server_->SendData(50);
+  client_->ReadBytes(50);
+  client_->SendData(50);
+  server_->ExpectSendAlert(kTlsAlertDecodeError);
+  server_->ReadBytes(50);
+  EXPECT_EQ(SSL_ERROR_RX_MALFORMED_CERT_VERIFY, PORT_GetError());
+}
+
+TEST_F(TlsConnectStreamTls13, PostHandshakeAuthDecline) {
+  EnsureTlsSetup();
+  auto capture_cert_req = MakeTlsFilter<TlsCertificateRequestContextRecorder>(
+      server_, kTlsHandshakeCertificateRequest);
+  auto capture_certificate =
+      MakeTlsFilter<TlsCertificateRequestContextRecorder>(
+          client_, kTlsHandshakeCertificate);
+  client_->SetupClientAuth();
+  EXPECT_EQ(SECSuccess, SSL_OptionSet(client_->ssl_fd(),
+                                      SSL_ENABLE_POST_HANDSHAKE_AUTH, PR_TRUE));
+  // Client to decline the certificate request.
+  EXPECT_EQ(SECSuccess,
+            SSL_GetClientAuthDataHook(
+                client_->ssl_fd(),
+                [](void*, PRFileDesc*, CERTDistNames*, CERTCertificate**,
+                   SECKEYPrivateKey**) -> SECStatus { return SECFailure; },
+                nullptr));
+  size_t called = 0;
+  server_->SetAuthCertificateCallback(
+      [&called](TlsAgent*, PRBool, PRBool) -> SECStatus {
+        called++;
+        return SECSuccess;
+      });
+  Connect();
+  EXPECT_EQ(0U, called);
+  // Send CertificateRequest.
+  EXPECT_EQ(SECSuccess, SSL_SendCertificateRequest(server_->ssl_fd()))
+      << "Unexpected error: " << PORT_ErrorToName(PORT_GetError());
+  server_->SendData(50);
+  client_->ReadBytes(50);
+  client_->SendData(50);
+  server_->ReadBytes(50);
+  // AuthCertificateCallback is not called, because the client sends
+  // an empty certificate_list.
+  EXPECT_EQ(0U, called);
+  EXPECT_TRUE(capture_cert_req->filtered());
+  EXPECT_TRUE(capture_certificate->filtered());
+  // Check if a non-empty request context is generated and it is
+  // properly sent back.
+  EXPECT_LT(0U, capture_cert_req->buffer().len());
+  EXPECT_EQ(capture_cert_req->buffer().len(),
+            capture_certificate->buffer().len());
+  EXPECT_EQ(0, memcmp(capture_cert_req->buffer().data(),
+                      capture_certificate->buffer().data(),
+                      capture_cert_req->buffer().len()));
+}
+
 // In TLS 1.3, the client sends its cert rejection on the
 // second flight, and since it has already received the
 // server's Finished, it transitions to complete and
 // then gets an alert from the server. The test harness
 // doesn't handle this right yet.
 TEST_P(TlsConnectStream, DISABLED_ClientAuthRequiredRejected) {
   server_->RequestClientAuth(true);
   ConnectExpectFail();
--- a/lib/ssl/ssl.h
+++ b/lib/ssl/ssl.h
@@ -294,16 +294,27 @@ SSL_IMPORT PRFileDesc *DTLS_ImportFD(PRF
 /* Enables the SSLv2-compatible ClientHello for servers. NSS does not support
  * SSLv2 and will never send an SSLv2-compatible ClientHello as a client.  An
  * NSS server with this option enabled will accept a ClientHello that is
  * v2-compatible as defined in Appendix E.1 of RFC 6101.
  *
  * This is disabled by default and will be removed in a future version. */
 #define SSL_ENABLE_V2_COMPATIBLE_HELLO 38
 
+/* Enables the post-handshake authentication in TLS 1.3.  If it is set
+ * to PR_TRUE, the client will send the "post_handshake_auth"
+ * extension to indicate that it will process CertificateRequest
+ * messages after handshake.
+ *
+ * This option applies only to clients.  For a server, the
+ * SSL_SendCertificateRequest can be used to request post-handshake
+ * authentication.
+ */
+#define SSL_ENABLE_POST_HANDSHAKE_AUTH 39
+
 #ifdef SSL_DEPRECATED_FUNCTION
 /* Old deprecated function names */
 SSL_IMPORT SECStatus SSL_Enable(PRFileDesc *fd, int option, PRIntn on);
 SSL_IMPORT SECStatus SSL_EnableDefault(int option, PRIntn on);
 #endif
 
 /* Set (and get) options for sockets and defaults for newly created sockets.
  *
--- a/lib/ssl/ssl3con.c
+++ b/lib/ssl/ssl3con.c
@@ -7388,16 +7388,19 @@ ssl3_CompleteHandleCertificateRequest(ss
                                       unsigned int signatureSchemeCount,
                                       CERTDistNames *ca_list)
 {
     SECStatus rv;
 
     if (ss->getClientAuthData != NULL) {
         PORT_Assert((ss->ssl3.hs.preliminaryInfo & ssl_preinfo_all) ==
                     ssl_preinfo_all);
+        PORT_Assert(ss->ssl3.clientPrivateKey == NULL);
+        PORT_Assert(ss->ssl3.clientCertificate == NULL);
+        PORT_Assert(ss->ssl3.clientCertChain == NULL);
         /* XXX Should pass cert_types and algorithms in this call!! */
         rv = (SECStatus)(*ss->getClientAuthData)(ss->getClientAuthDataArg,
                                                  ss->fd, ca_list,
                                                  &ss->ssl3.clientCertificate,
                                                  &ss->ssl3.clientPrivateKey);
     } else {
         rv = SECFailure; /* force it to send a no_certificate alert */
     }
@@ -10744,16 +10747,19 @@ ssl3_AuthCertificate(sslSocket *ss)
         }
 
         if (rv != SECSuccess) {
             ssl3_SendAlertForCertError(ss, errCode);
             goto loser;
         }
     }
 
+    if (ss->sec.ci.sid->peerCert) {
+        CERT_DestroyCertificate(ss->sec.ci.sid->peerCert);
+    }
     ss->sec.ci.sid->peerCert = CERT_DupCertificate(ss->sec.peerCert);
 
     if (!ss->sec.isServer) {
         CERTCertificate *cert = ss->sec.peerCert;
 
         /* set the server authentication type and size from the value
         ** in the cert. */
         SECKEYPublicKey *pubKey = CERT_ExtractPublicKey(cert);
--- a/lib/ssl/ssl3ext.c
+++ b/lib/ssl/ssl3ext.c
@@ -46,16 +46,17 @@ static const ssl3ExtensionHandler client
     { ssl_extended_master_secret_xtn, &ssl3_HandleExtendedMasterSecretXtn },
     { ssl_signed_cert_timestamp_xtn, &ssl3_ServerHandleSignedCertTimestampXtn },
     { ssl_tls13_key_share_xtn, &tls13_ServerHandleKeyShareXtn },
     { ssl_tls13_pre_shared_key_xtn, &tls13_ServerHandlePreSharedKeyXtn },
     { ssl_tls13_early_data_xtn, &tls13_ServerHandleEarlyDataXtn },
     { ssl_tls13_psk_key_exchange_modes_xtn, &tls13_ServerHandlePskModesXtn },
     { ssl_tls13_cookie_xtn, &tls13_ServerHandleCookieXtn },
     { ssl_tls13_encrypted_sni_xtn, &tls13_ServerHandleEsniXtn },
+    { ssl_tls13_post_handshake_auth_xtn, &tls13_ServerHandlePostHandshakeAuthXtn },
     { ssl_record_size_limit_xtn, &ssl_HandleRecordSizeLimitXtn },
     { 0, NULL }
 };
 
 /* These two tables are used by the client, to handle server hello
  * extensions. */
 static const ssl3ExtensionHandler serverHelloHandlersTLS[] = {
     { ssl_server_name_xtn, &ssl3_HandleServerNameXtn },
@@ -133,16 +134,17 @@ static const sslExtensionBuilder clientH
        * time out or terminate the connection if the last extension in the
        * client hello is empty. They are not intolerant of TLS 1.2, so list
        * signature_algorithms at the end. See bug 1243641. */
       { ssl_tls13_supported_versions_xtn, &tls13_ClientSendSupportedVersionsXtn },
       { ssl_signature_algorithms_xtn, &ssl3_SendSigAlgsXtn },
       { ssl_tls13_cookie_xtn, &tls13_ClientSendHrrCookieXtn },
       { ssl_tls13_psk_key_exchange_modes_xtn, &tls13_ClientSendPskModesXtn },
       { ssl_tls13_encrypted_sni_xtn, &tls13_ClientSendEsniXtn },
+      { ssl_tls13_post_handshake_auth_xtn, &tls13_ClientSendPostHandshakeAuthXtn },
       { ssl_record_size_limit_xtn, &ssl_SendRecordSizeLimitXtn },
       /* The pre_shared_key extension MUST be last. */
       { ssl_tls13_pre_shared_key_xtn, &tls13_ClientSendPreSharedKeyXtn },
       { 0, NULL }
     };
 
 static const sslExtensionBuilder clientHelloSendersSSL3[] = {
     { ssl_renegotiation_info_xtn, &ssl3_SendRenegotiationInfoXtn },
--- a/lib/ssl/sslerr.h
+++ b/lib/ssl/sslerr.h
@@ -263,15 +263,16 @@ typedef enum {
     SSL_ERROR_HANDSHAKE_FAILED = (SSL_ERROR_BASE + 172),
     SSL_ERROR_BAD_RESUMPTION_TOKEN_ERROR = (SSL_ERROR_BASE + 173),
     SSL_ERROR_RX_MALFORMED_DTLS_ACK = (SSL_ERROR_BASE + 174),
     SSL_ERROR_DH_KEY_TOO_LONG = (SSL_ERROR_BASE + 175),
     SSL_ERROR_RX_MALFORMED_ESNI_KEYS = (SSL_ERROR_BASE + 176),
     SSL_ERROR_RX_MALFORMED_ESNI_EXTENSION = (SSL_ERROR_BASE + 177),
     SSL_ERROR_MISSING_ESNI_EXTENSION = (SSL_ERROR_BASE + 178),
     SSL_ERROR_RX_UNEXPECTED_RECORD_TYPE = (SSL_ERROR_BASE + 179),
+    SSL_ERROR_MISSING_POST_HANDSHAKE_AUTH_EXTENSION = (SSL_ERROR_BASE + 180),
     SSL_ERROR_END_OF_LIST   /* let the c compiler determine the value of this. */
 } SSLErrorCodes;
 #endif /* NO_SECURITY_ERROR_ENUM */
 
 /* clang-format on */
 
 #endif /* __SSL_ERR_H_ */
--- a/lib/ssl/sslexp.h
+++ b/lib/ssl/sslexp.h
@@ -345,16 +345,37 @@ typedef SSLHelloRetryRequestAction(PR_CA
  * The |requestUpdate| flag determines whether to request an update from the
  * remote peer.
  */
 #define SSL_KeyUpdate(fd, requestUpdate)                            \
     SSL_EXPERIMENTAL_API("SSL_KeyUpdate",                           \
                          (PRFileDesc * _fd, PRBool _requestUpdate), \
                          (fd, requestUpdate))
 
+/* This function allows a server application to trigger
+ * re-authentication (TLS 1.3 only) after handshake.
+ *
+ * This function will cause a CertificateRequest message to be sent by
+ * a server.  This can be called once at a time, and is not allowed
+ * until an answer is received.
+ *
+ * The AuthCertificateCallback is called when the answer is received.
+ * If the answer is accepted by the server, the value returned by
+ * SSL_PeerCertificate() is replaced.  If you need to remember all the
+ * certificates, you will need to call SSL_PeerCertificate() and save
+ * what you get before calling this.
+ *
+ * If the AuthCertificateCallback returns SECFailure, the connection
+ * is aborted.
+ */
+#define SSL_SendCertificateRequest(fd)                 \
+    SSL_EXPERIMENTAL_API("SSL_SendCertificateRequest", \
+                         (PRFileDesc * _fd),           \
+                         (fd))
+
 /*
  * Session cache API.
  */
 
 /*
  * Information that can be retrieved about a resumption token.
  * See SSL_GetResumptionTokenInfo for details about how to use this API.
  * Note that peerCert points to a certificate in the NSS database and must be
--- a/lib/ssl/sslimpl.h
+++ b/lib/ssl/sslimpl.h
@@ -267,16 +267,17 @@ typedef struct sslOptionsStr {
     unsigned int enableExtendedMS : 1;
     unsigned int enableSignedCertTimestamps : 1;
     unsigned int requireDHENamedGroups : 1;
     unsigned int enable0RttData : 1;
     unsigned int enableTls13CompatMode : 1;
     unsigned int enableDtlsShortHeader : 1;
     unsigned int enableHelloDowngradeCheck : 1;
     unsigned int enableV2CompatibleHello : 1;
+    unsigned int enablePostHandshakeAuth : 1;
 } sslOptions;
 
 typedef enum { sslHandshakingUndetermined = 0,
                sslHandshakingAsClient,
                sslHandshakingAsServer
 } sslHandshakingType;
 
 #define SSL_LOCK_RANK_SPEC 255
@@ -737,16 +738,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 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 */
     PRBool sendEmptyCert;                 /* used by client */
 
     PRUint8 policy;
     /* This says what cipher suites we can do, and should
      * be either SSL_ALLOWED or SSL_RESTRICTED
--- a/lib/ssl/sslsock.c
+++ b/lib/ssl/sslsock.c
@@ -81,17 +81,18 @@ static sslOptions ssl_defaults = {
     .enableServerDhe = PR_TRUE,
     .enableExtendedMS = PR_FALSE,
     .enableSignedCertTimestamps = PR_FALSE,
     .requireDHENamedGroups = PR_FALSE,
     .enable0RttData = PR_FALSE,
     .enableTls13CompatMode = PR_FALSE,
     .enableDtlsShortHeader = PR_FALSE,
     .enableHelloDowngradeCheck = PR_FALSE,
-    .enableV2CompatibleHello = PR_FALSE
+    .enableV2CompatibleHello = PR_FALSE,
+    .enablePostHandshakeAuth = PR_FALSE
 };
 
 /*
  * default range of enabled SSL/TLS protocols
  */
 static SSLVersionRange versions_defaults_stream = {
     SSL_LIBRARY_VERSION_TLS_1_0,
     SSL_LIBRARY_VERSION_TLS_1_2
@@ -837,16 +838,20 @@ SSL_OptionSet(PRFileDesc *fd, PRInt32 wh
         case SSL_ENABLE_HELLO_DOWNGRADE_CHECK:
             ss->opt.enableHelloDowngradeCheck = val;
             break;
 
         case SSL_ENABLE_V2_COMPATIBLE_HELLO:
             ss->opt.enableV2CompatibleHello = val;
             break;
 
+        case SSL_ENABLE_POST_HANDSHAKE_AUTH:
+            ss->opt.enablePostHandshakeAuth = val;
+            break;
+
         default:
             PORT_SetError(SEC_ERROR_INVALID_ARGS);
             rv = SECFailure;
     }
 
     /* We can't use the macros for releasing the locks here,
      * because ss->opt.noLocks might have changed just above.
      * We must release these locks (monitors) here, if we aquired them above,
@@ -985,16 +990,19 @@ SSL_OptionGet(PRFileDesc *fd, PRInt32 wh
             val = ss->opt.enableDtlsShortHeader;
             break;
         case SSL_ENABLE_HELLO_DOWNGRADE_CHECK:
             val = ss->opt.enableHelloDowngradeCheck;
             break;
         case SSL_ENABLE_V2_COMPATIBLE_HELLO:
             val = ss->opt.enableV2CompatibleHello;
             break;
+        case SSL_ENABLE_POST_HANDSHAKE_AUTH:
+            val = ss->opt.enablePostHandshakeAuth;
+            break;
         default:
             PORT_SetError(SEC_ERROR_INVALID_ARGS);
             rv = SECFailure;
     }
 
     ssl_ReleaseSSL3HandshakeLock(ss);
     ssl_Release1stHandshakeLock(ss);
 
@@ -1117,16 +1125,19 @@ SSL_OptionGetDefault(PRInt32 which, PRIn
             val = ssl_defaults.enableDtlsShortHeader;
             break;
         case SSL_ENABLE_HELLO_DOWNGRADE_CHECK:
             val = ssl_defaults.enableHelloDowngradeCheck;
             break;
         case SSL_ENABLE_V2_COMPATIBLE_HELLO:
             val = ssl_defaults.enableV2CompatibleHello;
             break;
+        case SSL_ENABLE_POST_HANDSHAKE_AUTH:
+            val = ssl_defaults.enablePostHandshakeAuth;
+            break;
         default:
             PORT_SetError(SEC_ERROR_INVALID_ARGS);
             rv = SECFailure;
     }
 
     *pVal = val;
     return rv;
 }
@@ -1320,16 +1331,20 @@ SSL_OptionSetDefault(PRInt32 which, PRIn
         case SSL_ENABLE_HELLO_DOWNGRADE_CHECK:
             ssl_defaults.enableHelloDowngradeCheck = val;
             break;
 
         case SSL_ENABLE_V2_COMPATIBLE_HELLO:
             ssl_defaults.enableV2CompatibleHello = val;
             break;
 
+        case SSL_ENABLE_POST_HANDSHAKE_AUTH:
+            ssl_defaults.enablePostHandshakeAuth = val;
+            break;
+
         default:
             PORT_SetError(SEC_ERROR_INVALID_ARGS);
             return SECFailure;
     }
     return SECSuccess;
 }
 
 SECStatus
@@ -4033,16 +4048,17 @@ struct {
     EXP(GetExtensionSupport),
     EXP(GetResumptionTokenInfo),
     EXP(HelloRetryRequestCallback),
     EXP(InstallExtensionHooks),
     EXP(KeyUpdate),
     EXP(RecordLayerData),
     EXP(RecordLayerWriteCallback),
     EXP(SecretCallback),
+    EXP(SendCertificateRequest),
     EXP(SendSessionTicket),
     EXP(SetESNIKeyPair),
     EXP(SetMaxEarlyDataSize),
     EXP(SetResumptionTokenCallback),
     EXP(SetResumptionToken),
     EXP(SetupAntiReplay),
 #endif
     { "", NULL }
--- a/lib/ssl/sslt.h
+++ b/lib/ssl/sslt.h
@@ -466,16 +466,17 @@ typedef enum {
     /* 40 was used in draft versions of TLS 1.3; it is now reserved. */
     ssl_tls13_pre_shared_key_xtn = 41,
     ssl_tls13_early_data_xtn = 42,
     ssl_tls13_supported_versions_xtn = 43,
     ssl_tls13_cookie_xtn = 44,
     ssl_tls13_psk_key_exchange_modes_xtn = 45,
     ssl_tls13_ticket_early_data_info_xtn = 46, /* Deprecated. */
     ssl_tls13_certificate_authorities_xtn = 47,
+    ssl_tls13_post_handshake_auth_xtn = 49,
     ssl_signature_algorithms_cert_xtn = 50,
     ssl_tls13_key_share_xtn = 51,
     ssl_next_proto_nego_xtn = 13172, /* Deprecated. */
     ssl_renegotiation_info_xtn = 0xff01,
     ssl_tls13_short_header_xtn = 0xff03, /* Deprecated. */
     ssl_tls13_encrypted_sni_xtn = 0xffce,
 } SSLExtensionType;
 
--- a/lib/ssl/tls13con.c
+++ b/lib/ssl/tls13con.c
@@ -51,16 +51,17 @@ static SECStatus tls13_SendHelloRetryReq
 
 static SECStatus tls13_HandleServerKeyShare(sslSocket *ss);
 static SECStatus tls13_HandleEncryptedExtensions(sslSocket *ss, PRUint8 *b,
                                                  PRUint32 length);
 static SECStatus tls13_SendCertificate(sslSocket *ss);
 static SECStatus tls13_HandleCertificate(
     sslSocket *ss, PRUint8 *b, PRUint32 length);
 static SECStatus tls13_ReinjectHandshakeTranscript(sslSocket *ss);
+static SECStatus tls13_SendCertificateRequest(sslSocket *ss);
 static SECStatus tls13_HandleCertificateRequest(sslSocket *ss, PRUint8 *b,
                                                 PRUint32 length);
 static SECStatus
 tls13_SendCertificateVerify(sslSocket *ss, SECKEYPrivateKey *privKey);
 static SECStatus tls13_HandleCertificateVerify(
     sslSocket *ss, PRUint8 *b, PRUint32 length);
 static SECStatus tls13_RecoverWrappedSharedSecret(sslSocket *ss,
                                                   sslSessionID *sid);
@@ -99,16 +100,19 @@ static SECStatus tls13_ComputeEarlySecre
 static SECStatus tls13_ComputeHandshakeSecrets(sslSocket *ss);
 static SECStatus tls13_ComputeApplicationSecrets(sslSocket *ss);
 static SECStatus tls13_ComputeFinalSecrets(sslSocket *ss);
 static SECStatus tls13_ComputeFinished(
     sslSocket *ss, PK11SymKey *baseKey, const SSL3Hashes *hashes,
     PRBool sending, PRUint8 *output, unsigned int *outputLen,
     unsigned int maxOutputLen);
 static SECStatus tls13_SendClientSecondRound(sslSocket *ss);
+static SECStatus tls13_SendClientSecondFlight(sslSocket *ss,
+                                              PRBool sendClientCert,
+                                              SSL3AlertDescription *sendAlert);
 static SECStatus tls13_FinishHandshake(sslSocket *ss);
 
 const char kHkdfLabelClient[] = "c";
 const char kHkdfLabelServer[] = "s";
 const char kHkdfLabelDerivedSecret[] = "derived";
 const char kHkdfLabelPskBinderKey[] = "res binder";
 const char kHkdfLabelEarlyTrafficSecret[] = "e traffic";
 const char kHkdfLabelEarlyExporterSecret[] = "e exp master";
@@ -821,16 +825,66 @@ tls13_HandleKeyUpdate(sslSocket *ss, PRU
         }
         ss->ssl3.peerRequestedKeyUpdate = PR_TRUE;
     }
 
     return SECSuccess;
 }
 
 SECStatus
+SSLExp_SendCertificateRequest(PRFileDesc *fd)
+{
+    SECStatus rv;
+    sslSocket *ss = ssl_FindSocket(fd);
+    if (!ss) {
+        return SECFailure;
+    }
+
+    /* 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) {
+        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;
+    }
+
+    if (!ssl3_ExtensionNegotiated(ss, ssl_tls13_post_handshake_auth_xtn)) {
+        PORT_SetError(SSL_ERROR_MISSING_POST_HANDSHAKE_AUTH_EXTENSION);
+        return SECFailure;
+    }
+
+    ssl_GetSSL3HandshakeLock(ss);
+
+    rv = tls13_SendCertificateRequest(ss);
+    if (rv == SECSuccess) {
+        ssl_GetXmitBufLock(ss);
+        rv = ssl3_FlushHandshake(ss, 0);
+        ssl_ReleaseXmitBufLock(ss);
+        ss->ssl3.clientCertRequested = PR_TRUE;
+    }
+
+    ssl_ReleaseSSL3HandshakeLock(ss);
+    return rv;
+}
+
+SECStatus
 tls13_HandlePostHelloHandshakeMessage(sslSocket *ss, PRUint8 *b, PRUint32 length)
 {
     if (ss->sec.isServer && ss->ssl3.hs.zeroRttIgnore != ssl_0rtt_ignore_none) {
         SSL_TRC(3, ("%d: TLS13[%d]: %s successfully decrypted handshake after"
                     "failed 0-RTT",
                     SSL_GETPID(), ss->fd));
         ss->ssl3.hs.zeroRttIgnore = ssl_0rtt_ignore_none;
     }
@@ -2134,26 +2188,46 @@ tls13_SendCertificateRequest(sslSocket *
 
     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 */
+    if (ss->firstHsDone) {
+        PRUint8 context[16];
+        SECItem contextItem = { siBuffer, context, sizeof(context) };
+
+        rv = PK11_GenerateRandom(context, sizeof(context));
+        if (rv != SECSuccess) {
+            goto loser;
+        }
+
+        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;
+        }
+    }
+
     rv = ssl3_AppendHandshakeHeader(ss, ssl_hs_certificate_request,
-                                    1 + 0 + /* empty request context */
+                                    1 + /* request context length */
+                                        ss->xtnData.certReqContext.len +
                                         2 + /* extension length */
                                         SSL_BUFFER_LEN(&extensionBuf));
     if (rv != SECSuccess) {
         goto loser; /* err set by AppendHandshake. */
     }
 
     /* Context. */
-    rv = ssl3_AppendHandshakeNumber(ss, 0, 1);
+    rv = ssl3_AppendHandshakeVariable(ss, ss->xtnData.certReqContext.data,
+                                      ss->xtnData.certReqContext.len, 1);
     if (rv != SECSuccess) {
         goto loser; /* err set by AppendHandshake. */
     }
     /* Extensions. */
     rv = ssl3_AppendBufferToHandshakeVariable(ss, &extensionBuf, 2);
     if (rv != SECSuccess) {
         goto loser; /* err set by AppendHandshake. */
     }
@@ -2307,35 +2381,59 @@ tls13_HandleCertificateRequest(sslSocket
 
     SSL_TRC(3, ("%d: TLS13[%d]: handle certificate_request sequence",
                 SSL_GETPID(), ss->fd));
 
     PORT_Assert(ss->opt.noLocks || ssl_HaveRecvBufLock(ss));
     PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
 
     /* Client */
-    rv = TLS13_CHECK_HS_STATE(ss, SSL_ERROR_RX_UNEXPECTED_CERT_REQUEST,
-                              wait_cert_request);
+    if (ss->opt.enablePostHandshakeAuth) {
+        rv = TLS13_CHECK_HS_STATE(ss, SSL_ERROR_RX_UNEXPECTED_CERT_REQUEST,
+                                  wait_cert_request, idle_handshake);
+    } else {
+        rv = TLS13_CHECK_HS_STATE(ss, SSL_ERROR_RX_UNEXPECTED_CERT_REQUEST,
+                                  wait_cert_request);
+    }
     if (rv != SECSuccess) {
         return SECFailure;
     }
 
-    PORT_Assert(ss->ssl3.clientCertChain == NULL);
-    PORT_Assert(ss->ssl3.clientCertificate == NULL);
-    PORT_Assert(ss->ssl3.clientPrivateKey == NULL);
-    PORT_Assert(!ss->ssl3.hs.clientCertRequested);
+    if (ss->firstHsDone) {
+        /* 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;
+        }
+        if (ss->ssl3.clientPrivateKey != NULL) {
+            SECKEY_DestroyPrivateKey(ss->ssl3.clientPrivateKey);
+            ss->ssl3.clientPrivateKey = NULL;
+        }
+        SECITEM_FreeItem(&ss->xtnData.certReqContext, PR_FALSE);
+        ss->xtnData.certReqContext.data = NULL;
+    } else {
+        PORT_Assert(ss->ssl3.clientCertChain == NULL);
+        PORT_Assert(ss->ssl3.clientCertificate == NULL);
+        PORT_Assert(ss->ssl3.clientPrivateKey == NULL);
+        PORT_Assert(!ss->ssl3.hs.clientCertRequested);
+        PORT_Assert(ss->xtnData.certReqContext.data == NULL);
+    }
 
     rv = ssl3_ConsumeHandshakeVariable(ss, &context, 1, &b, &length);
     if (rv != SECSuccess) {
         return SECFailure;
     }
 
-    /* We don't support post-handshake client auth, the certificate request
-     * context must always be empty. */
-    if (context.len > 0) {
+    /* Unless it is a post-handshake client auth, the certificate
+     * request context must be empty. */
+    if (!ss->firstHsDone && 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;
     }
@@ -2359,17 +2457,45 @@ tls13_HandleCertificateRequest(sslSocket
     }
 
     rv = SECITEM_CopyItem(NULL, &ss->xtnData.certReqContext, &context);
     if (rv != SECSuccess) {
         return SECFailure;
     }
 
     ss->ssl3.hs.clientCertRequested = PR_TRUE;
-    TLS13_SET_HS_STATE(ss, wait_server_cert);
+
+    if (ss->firstHsDone) {
+        SSL3AlertDescription sendAlert = no_alert;
+
+        /* Request a client certificate. */
+        rv = ssl3_CompleteHandleCertificateRequest(
+            ss, ss->xtnData.sigSchemes, ss->xtnData.numSigSchemes,
+            &ss->xtnData.certReqAuthorities);
+        if (rv != SECSuccess) {
+            FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error);
+            return rv;
+        }
+
+        ssl_GetXmitBufLock(ss);
+        rv = tls13_SendClientSecondFlight(ss, !ss->ssl3.sendEmptyCert,
+                                          &sendAlert);
+        ssl_ReleaseXmitBufLock(ss);
+        if (rv != SECSuccess) {
+            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);
+    } else {
+        TLS13_SET_HS_STATE(ss, wait_server_cert);
+    }
     return SECSuccess;
 }
 
 PRBool
 tls13_ShouldRequestClientAuth(sslSocket *ss)
 {
     return ss->opt.requestCertificate &&
            ss->ssl3.hs.kea_def->authKeyType != ssl_auth_psk;
@@ -2895,18 +3021,23 @@ tls13_HandleCertificate(sslSocket *ss, P
     ssl3CertNode *lastCert = NULL;
 
     SSL_TRC(3, ("%d: TLS13[%d]: handle certificate handshake",
                 SSL_GETPID(), ss->fd));
     PORT_Assert(ss->opt.noLocks || ssl_HaveRecvBufLock(ss));
     PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
 
     if (ss->sec.isServer) {
-        rv = TLS13_CHECK_HS_STATE(ss, SSL_ERROR_RX_UNEXPECTED_CERTIFICATE,
-                                  wait_client_cert);
+        if (ss->ssl3.clientCertRequested) {
+            rv = TLS13_CHECK_HS_STATE(ss, SSL_ERROR_RX_UNEXPECTED_CERTIFICATE,
+                                      idle_handshake);
+        } else {
+            rv = TLS13_CHECK_HS_STATE(ss, SSL_ERROR_RX_UNEXPECTED_CERTIFICATE,
+                                      wait_client_cert);
+        }
     } else {
         rv = TLS13_CHECK_HS_STATE(ss, SSL_ERROR_RX_UNEXPECTED_CERTIFICATE,
                                   wait_cert_request, wait_server_cert);
     }
     if (rv != SECSuccess)
         return SECFailure;
 
     /* We can ignore any other cleartext from the client. */
@@ -2914,20 +3045,22 @@ tls13_HandleCertificate(sslSocket *ss, P
         ssl_CipherSpecReleaseByEpoch(ss, ssl_secret_read, TrafficKeyClearText);
         dtls_ReceivedFirstMessageInFlight(ss);
     }
     /* Process the context string */
     rv = ssl3_ConsumeHandshakeVariable(ss, &context, 1, &b, &length);
     if (rv != SECSuccess)
         return SECFailure;
 
-    if (context.len) {
-        /* The context string MUST be empty */
-        FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_CERTIFICATE, illegal_parameter);
-        return SECFailure;
+    if (ss->ssl3.clientCertRequested) {
+        PORT_Assert(ss->sec.isServer);
+        if (SECITEM_CompareItem(&context, &ss->xtnData.certReqContext) != 0) {
+            FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_CERTIFICATE, illegal_parameter);
+            return SECFailure;
+        }
     }
 
     rv = ssl3_ConsumeHandshakeVariable(ss, &certList, 3, &b, &length);
     if (rv != SECSuccess) {
         return SECFailure;
     }
     if (length) {
         FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_CERTIFICATE, illegal_parameter);
@@ -3984,16 +4117,20 @@ 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)
@@ -4285,22 +4422,28 @@ tls13_ServerHandleFinished(sslSocket *ss
     SECStatus rv;
 
     PORT_Assert(ss->opt.noLocks || ssl_HaveRecvBufLock(ss));
     PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
 
     SSL_TRC(3, ("%d: TLS13[%d]: server handle finished handshake",
                 SSL_GETPID(), ss->fd));
 
-    rv = tls13_CommonHandleFinished(ss, ss->ssl3.hs.clientHsTrafficSecret,
+    rv = tls13_CommonHandleFinished(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);
+        return SECSuccess;
+    }
+
     if (!tls13_ShouldRequestClientAuth(ss) &&
         (ss->ssl3.hs.zeroRttState != ssl_0rtt_done)) {
         dtls_ReceivedFirstMessageInFlight(ss);
     }
 
     rv = tls13_SetCipherSpec(ss, TrafficKeyApplicationData,
                              ssl_secret_read, PR_FALSE);
     if (rv != SECSuccess) {
@@ -4408,17 +4551,17 @@ tls13_SendClientSecondFlight(sslSocket *
         rv = tls13_SendCertificateVerify(ss, ss->ssl3.clientPrivateKey);
         SECKEY_DestroyPrivateKey(ss->ssl3.clientPrivateKey);
         ss->ssl3.clientPrivateKey = NULL;
         if (rv != SECSuccess) {
             return SECFailure; /* err is set. */
         }
     }
 
-    rv = tls13_SendFinished(ss, ss->ssl3.hs.clientHsTrafficSecret);
+    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) {
         /* No point in sending an alert here because we're not going to
          * be able to send it if we couldn't flush the handshake. */
         *sendAlert = no_alert;
@@ -4863,17 +5006,18 @@ static const struct {
                                          certificate) },
     { ssl_cert_status_xtn, _M3(client_hello, certificate_request,
                                certificate) },
     { ssl_tls13_cookie_xtn, _M2(client_hello, hello_retry_request) },
     { ssl_tls13_certificate_authorities_xtn, _M1(certificate_request) },
     { ssl_tls13_supported_versions_xtn, _M3(client_hello, server_hello,
                                             hello_retry_request) },
     { ssl_record_size_limit_xtn, _M2(client_hello, encrypted_extensions) },
-    { ssl_tls13_encrypted_sni_xtn, _M2(client_hello, encrypted_extensions) }
+    { ssl_tls13_encrypted_sni_xtn, _M2(client_hello, encrypted_extensions) },
+    { ssl_tls13_post_handshake_auth_xtn, _M1(client_hello) }
 };
 
 tls13ExtensionStatus
 tls13_ExtensionStatus(PRUint16 extension, SSLHandshakeType message)
 {
     unsigned int i;
 
     PORT_Assert((message == ssl_hs_client_hello) ||
--- a/lib/ssl/tls13con.h
+++ b/lib/ssl/tls13con.h
@@ -126,16 +126,17 @@ SECStatus SSLExp_HelloRetryRequestCallba
                                            SSLHelloRetryRequestCallback cb,
                                            void *arg);
 SECStatus tls13_SendKeyUpdate(sslSocket *ss, tls13KeyUpdateRequest request,
                               PRBool buffer);
 SECStatus SSLExp_KeyUpdate(PRFileDesc *fd, PRBool requestUpdate);
 PRBool tls13_MaybeTls13(sslSocket *ss);
 SSLAEADCipher tls13_GetAead(const ssl3BulkCipherDef *cipherDef);
 void tls13_SetSpecRecordVersion(sslSocket *ss, ssl3CipherSpec *spec);
+SECStatus SSLExp_SendCertificateRequest(PRFileDesc *fd);
 
 /* Use this instead of FATAL_ERROR when no alert shall be sent. */
 #define LOG_ERROR(ss, prError)                                                     \
     do {                                                                           \
         SSL_TRC(3, ("%d: TLS13[%d]: fatal error %d in %s (%s:%d)",                 \
                     SSL_GETPID(), ss->fd, prError, __func__, __FILE__, __LINE__)); \
         PORT_SetError(prError);                                                    \
     } while (0)
--- a/lib/ssl/tls13exthandle.c
+++ b/lib/ssl/tls13exthandle.c
@@ -910,16 +910,47 @@ tls13_ServerHandleCookieXtn(const sslSoc
     }
 
     /* Keep track of negotiated extensions. */
     xtnData->negotiated[xtnData->numNegotiated++] = ssl_tls13_cookie_xtn;
 
     return SECSuccess;
 }
 
+SECStatus
+tls13_ClientSendPostHandshakeAuthXtn(const sslSocket *ss,
+                                     TLSExtensionData *xtnData,
+                                     sslBuffer *buf, PRBool *added)
+{
+    SSL_TRC(3, ("%d: TLS13[%d]: send post_handshake_auth extension",
+                SSL_GETPID(), ss->fd));
+
+    *added = ss->opt.enablePostHandshakeAuth;
+    return SECSuccess;
+}
+
+SECStatus
+tls13_ServerHandlePostHandshakeAuthXtn(const sslSocket *ss,
+                                       TLSExtensionData *xtnData,
+                                       SECItem *data)
+{
+    SSL_TRC(3, ("%d: TLS13[%d]: handle post_handshake_auth extension",
+                SSL_GETPID(), ss->fd));
+
+    if (data->len) {
+        PORT_SetError(SSL_ERROR_RX_MALFORMED_CLIENT_HELLO);
+        return SECFailure;
+    }
+
+    /* Keep track of negotiated extensions. */
+    xtnData->negotiated[xtnData->numNegotiated++] = ssl_tls13_post_handshake_auth_xtn;
+
+    return SECSuccess;
+}
+
 /*
  *     enum { psk_ke(0), psk_dhe_ke(1), (255) } PskKeyExchangeMode;
  *
  *     struct {
  *         PskKeyExchangeMode ke_modes<1..255>;
  *     } PskKeyExchangeModes;
  */
 SECStatus
--- a/lib/ssl/tls13exthandle.h
+++ b/lib/ssl/tls13exthandle.h
@@ -88,10 +88,16 @@ SECStatus tls13_DecodeKeyShareEntry(sslR
 PRUint32 tls13_SizeOfKeyShareEntry(const SECKEYPublicKey *pubKey);
 SECStatus tls13_EncodeKeyShareEntry(sslBuffer *buf, SSLNamedGroup group,
                                     SECKEYPublicKey *pubKey);
 SECStatus tls13_ClientSendEsniXtn(const sslSocket *ss, TLSExtensionData *xtnData,
                                   sslBuffer *buf, PRBool *added);
 SECStatus tls13_ServerHandleEsniXtn(const sslSocket *ss, TLSExtensionData *xtnData,
                                     SECItem *data);
 SECStatus tls13_ClientCheckEsniXtn(sslSocket *ss);
+SECStatus tls13_ClientSendPostHandshakeAuthXtn(const sslSocket *ss,
+                                               TLSExtensionData *xtnData,
+                                               sslBuffer *buf, PRBool *added);
+SECStatus tls13_ServerHandlePostHandshakeAuthXtn(const sslSocket *ss,
+                                                 TLSExtensionData *xtnData,
+                                                 SECItem *data);
 
 #endif