Bug 1540403 - draft-ietf-tls-subcerts-03, r=mt,jcj
authorChristopher Patton <chrispatton@gmail.com>
Tue, 25 Jun 2019 14:21:59 +1000
changeset 15201 4af0d6f452111f7b3ba3cc21917ddd62aaf2222e
parent 15200 0c5d37301637ed024de8c2cbdbecf144aae12163
child 15202 9069f985fc8a53060f5339377fabf985c6857efe
push id3420
push usermartin.thomson@gmail.com
push dateThu, 27 Jun 2019 04:32:20 +0000
reviewersmt, jcj
bugs1540403
Bug 1540403 - draft-ietf-tls-subcerts-03, r=mt,jcj Differential Revision: https://phabricator.services.mozilla.com/D25654
automation/abi-check/expected-report-libssl3.so.txt
cmd/selfserv/selfserv.c
cmd/tstclnt/tstclnt.c
gtests/ssl_gtest/manifest.mn
gtests/ssl_gtest/ssl_auth_unittest.cc
gtests/ssl_gtest/ssl_cert_ext_unittest.cc
gtests/ssl_gtest/ssl_gtest.gyp
gtests/ssl_gtest/tls_agent.cc
gtests/ssl_gtest/tls_agent.h
gtests/ssl_gtest/tls_subcerts_unittest.cc
lib/ssl/manifest.mn
lib/ssl/ssl.gyp
lib/ssl/ssl.h
lib/ssl/ssl3con.c
lib/ssl/ssl3ecc.c
lib/ssl/ssl3ext.c
lib/ssl/ssl3ext.h
lib/ssl/sslcert.c
lib/ssl/sslcert.h
lib/ssl/sslerr.h
lib/ssl/sslexp.h
lib/ssl/sslimpl.h
lib/ssl/sslinfo.c
lib/ssl/sslsock.c
lib/ssl/sslt.h
lib/ssl/tls13con.c
lib/ssl/tls13exthandle.c
lib/ssl/tls13exthandle.h
lib/ssl/tls13subcerts.c
lib/ssl/tls13subcerts.h
tests/common/certsetup.sh
tests/ssl_gtests/ssl_gtests.sh
--- a/automation/abi-check/expected-report-libssl3.so.txt
+++ b/automation/abi-check/expected-report-libssl3.so.txt
@@ -0,0 +1,22 @@
+
+2 functions with some indirect sub-type change:
+
+  [C]'function SECStatus SSL_ConfigServerCert(PRFileDesc*, CERTCertificate*, SECKEYPrivateKey*, const SSLExtraServerCertData*, unsigned int)' at sslcert.c:640:1 has some indirect sub-type changes:
+    parameter 4 of type 'const SSLExtraServerCertData*' has sub-type changes:
+      in pointed to type 'const SSLExtraServerCertData':
+        in unqualified underlying type 'typedef SSLExtraServerCertData' at sslt.h:291:1:
+          underlying type 'struct SSLExtraServerCertDataStr' at sslt.h:256:1 changed:
+            type size changed from 256 to 384 (in bits)
+            2 data member insertions:
+              'const SECItem* SSLExtraServerCertDataStr::delegCred', at offset 256 (in bits) at sslt.h:283:1
+              'const SECKEYPrivateKey* SSLExtraServerCertDataStr::delegCredPrivKey', at offset 320 (in bits) at sslt.h:290:1
+
+  [C]'function SECStatus SSL_GetChannelInfo(PRFileDesc*, SSLChannelInfo*, PRUintn)' at sslinfo.c:13:1 has some indirect sub-type changes:
+    parameter 2 of type 'SSLChannelInfo*' has sub-type changes:
+      in pointed to type 'typedef SSLChannelInfo' at sslt.h:357:1:
+        underlying type 'struct SSLChannelInfoStr' at sslt.h:272:1 changed:
+          type size hasn't changed
+          1 data member insertion:
+            'PRBool SSLChannelInfoStr::peerDelegCred', at offset 928 (in bits) at sslt.h:353:1
+
+
--- a/cmd/selfserv/selfserv.c
+++ b/cmd/selfserv/selfserv.c
@@ -1921,17 +1921,17 @@ server_main(
         }
     }
 
     /* This uses the legacy certificate API.  See mySSLSNISocketConfig() for the
      * new, prefered API. */
     for (i = 0; i < certNicknameIndex; i++) {
         if (cert[i] != NULL) {
             const SSLExtraServerCertData ocspData = {
-                ssl_auth_null, NULL, certStatus[i], NULL
+                ssl_auth_null, NULL, certStatus[i], NULL, NULL, NULL
             };
 
             secStatus = SSL_ConfigServerCert(model_sock, cert[i],
                                              privKey[i], &ocspData,
                                              sizeof(ocspData));
             if (secStatus != SECSuccess)
                 errExit("SSL_ConfigServerCert");
         }
--- a/cmd/tstclnt/tstclnt.c
+++ b/cmd/tstclnt/tstclnt.c
@@ -208,16 +208,19 @@ printSecurityInfo(PRFileDesc *fd)
                 csa->len);
     }
     scts = SSL_PeerSignedCertTimestamps(fd);
     if (scts && scts->len) {
         fprintf(stderr, "Received a Signed Certificate Timestamp of length"
                         " %u\n",
                 scts->len);
     }
+    if (channel.peerDelegCred) {
+        fprintf(stderr, "Received a Delegated Credential\n");
+    }
 }
 
 static void
 PrintUsageHeader()
 {
     fprintf(stderr,
             "Usage:  %s -h host [-a 1st_hs_name ] [-a 2nd_hs_name ] [-p port]\n"
             "  [-D | -d certdir] [-C] [-b | -R root-module] \n"
@@ -267,16 +270,17 @@ PrintParameterUsage()
     fprintf(stderr, "%-20s Verbose progress reporting.\n", "-v");
     fprintf(stderr, "%-20s Ping the server and then exit.\n", "-q");
     fprintf(stderr, "%-20s Timeout for server ping (default: no timeout).\n", "-t seconds");
     fprintf(stderr, "%-20s Renegotiate N times (resuming session if N>1).\n", "-r N");
     fprintf(stderr, "%-20s Enable the session ticket extension.\n", "-u");
     fprintf(stderr, "%-20s Enable false start.\n", "-g");
     fprintf(stderr, "%-20s Enable the cert_status extension (OCSP stapling).\n", "-T");
     fprintf(stderr, "%-20s Enable the signed_certificate_timestamp extension.\n", "-U");
+    fprintf(stderr, "%-20s Enable the delegated credentials extension.\n", "-B");
     fprintf(stderr, "%-20s Require fresh revocation info from side channel.\n"
                     "%-20s -F once means: require for server cert only\n"
                     "%-20s -F twice means: require for intermediates, too\n"
                     "%-20s (Connect, handshake with server, disable dynamic download\n"
                     "%-20s  of OCSP/CRL, verify cert using CERT_PKIXVerifyCert.)\n"
                     "%-20s Exit code:\n"
                     "%-20s 0: have fresh and valid revocation data, status good\n"
                     "%-20s 1: cert failed to verify, prior to revocation checking\n"
@@ -988,16 +992,17 @@ PRBool enableAltServerHello = PR_FALSE;
 PRBool useDTLS = PR_FALSE;
 PRBool actAsServer = PR_FALSE;
 PRBool stopAfterHandshake = PR_FALSE;
 PRBool requestToExit = PR_FALSE;
 char *versionString = NULL;
 PRBool handshakeComplete = PR_FALSE;
 char *encryptedSNIKeys = NULL;
 PRBool enablePostHandshakeAuth = PR_FALSE;
+PRBool enableDelegatedCredentials = PR_FALSE;
 
 static int
 writeBytesToServer(PRFileDesc *s, const PRUint8 *buf, int nb)
 {
     SECStatus rv;
     const PRUint8 *bufp = buf;
     PRPollDesc pollDesc;
 
@@ -1360,16 +1365,24 @@ run()
     /* enable cert status (OCSP stapling). */
     rv = SSL_OptionSet(s, SSL_ENABLE_OCSP_STAPLING, enableCertStatus);
     if (rv != SECSuccess) {
         SECU_PrintError(progName, "error enabling cert status (OCSP stapling)");
         error = 1;
         goto done;
     }
 
+    /* enable negotiation of delegated credentials (draft-ietf-tls-subcerts) */
+    rv = SSL_OptionSet(s, SSL_ENABLE_DELEGATED_CREDENTIALS, enableDelegatedCredentials);
+    if (rv != SECSuccess) {
+        SECU_PrintError(progName, "error enabling delegated credentials");
+        error = 1;
+        goto done;
+    }
+
     /* enable extended master secret mode */
     if (enableExtendedMasterSecret) {
         rv = SSL_OptionSet(s, SSL_ENABLE_EXTENDED_MASTER_SECRET, PR_TRUE);
         if (rv != SECSuccess) {
             SECU_PrintError(progName, "error enabling extended master secret");
             error = 1;
             goto done;
         }
@@ -1710,22 +1723,21 @@ main(int argc, char **argv)
     tmp = PR_GetEnvSecure("NSS_DEBUG_TIMEOUT");
     if (tmp && tmp[0]) {
         int sec = PORT_Atoi(tmp);
         if (sec > 0) {
             maxInterval = PR_SecondsToInterval(sec);
         }
     }
 
-    /* Note: 'B' was used in the past but removed in 3.28
-     *       'z' was removed in 3.39
+    /* Note: 'z' was removed in 3.39
      * Please leave some time before reusing these.
      */
     optstate = PL_CreateOptState(argc, argv,
-                                 "46A:CDEFGHI:J:KL:M:N:OP:QR:STUV:W:X:YZa:bc:d:fgh:m:n:op:qr:st:uvw:");
+                                 "46A:BCDEFGHI:J:KL:M:N:OP:QR:STUV:W:X:YZa:bc:d:fgh:m:n:op:qr:st:uvw:");
     while ((optstatus = PL_GetNextOpt(optstate)) == PL_OPT_OK) {
         switch (optstate->option) {
             case '?':
             default:
                 Usage();
                 break;
 
             case '4':
@@ -1738,16 +1750,20 @@ main(int argc, char **argv)
                 if (!allowIPv6)
                     Usage();
                 break;
 
             case 'A':
                 requestFile = PORT_Strdup(optstate->value);
                 break;
 
+            case 'B':
+                enableDelegatedCredentials = PR_TRUE;
+                break;
+
             case 'C':
                 ++dumpServerChain;
                 break;
 
             case 'D':
                 openDB = PR_FALSE;
                 break;
 
--- a/gtests/ssl_gtest/manifest.mn
+++ b/gtests/ssl_gtest/manifest.mn
@@ -49,16 +49,17 @@ CPPSRCS = \
       ssl_versionpolicy_unittest.cc \
       selfencrypt_unittest.cc \
       test_io.cc \
       tls_agent.cc \
       tls_connect.cc \
       tls_hkdf_unittest.cc \
       tls_filter.cc \
       tls_protect.cc \
+      tls_subcerts_unittest.cc \
       tls_esni_unittest.cc \
       $(SSLKEYLOGFILE_FILES) \
       $(NULL)
 
 INCLUDES += -I$(CORE_DEPTH)/gtests/google_test/gtest/include \
             -I$(CORE_DEPTH)/gtests/common \
             -I$(CORE_DEPTH)/cpputil
 
--- a/gtests/ssl_gtest/ssl_auth_unittest.cc
+++ b/gtests/ssl_gtest/ssl_auth_unittest.cc
@@ -1317,21 +1317,21 @@ TEST_P(TlsConnectGeneric, AuthFailImmedi
   client_->SetAuthCertificateCallback(AuthCompleteFail);
 
   StartConnect();
   ConnectExpectAlert(client_, kTlsAlertBadCertificate);
   client_->CheckErrorCode(SSL_ERROR_BAD_CERTIFICATE);
 }
 
 static const SSLExtraServerCertData ServerCertDataRsaPkcs1Decrypt = {
-    ssl_auth_rsa_decrypt, nullptr, nullptr, nullptr};
+    ssl_auth_rsa_decrypt, nullptr, nullptr, nullptr, nullptr, nullptr};
 static const SSLExtraServerCertData ServerCertDataRsaPkcs1Sign = {
-    ssl_auth_rsa_sign, nullptr, nullptr, nullptr};
+    ssl_auth_rsa_sign, nullptr, nullptr, nullptr, nullptr, nullptr};
 static const SSLExtraServerCertData ServerCertDataRsaPss = {
-    ssl_auth_rsa_pss, nullptr, nullptr, nullptr};
+    ssl_auth_rsa_pss, nullptr, nullptr, nullptr, nullptr, nullptr};
 
 // Test RSA cert with usage=[signature, encipherment].
 TEST_F(TlsAgentStreamTestServer, ConfigureCertRsaPkcs1SignAndKEX) {
   Reset(TlsAgent::kServerRsa);
 
   PRFileDesc* ssl_fd = agent_->ssl_fd();
   EXPECT_TRUE(SSLInt_HasCertWithAuthType(ssl_fd, ssl_auth_rsa_decrypt));
   EXPECT_TRUE(SSLInt_HasCertWithAuthType(ssl_fd, ssl_auth_rsa_sign));
--- a/gtests/ssl_gtest/ssl_cert_ext_unittest.cc
+++ b/gtests/ssl_gtest/ssl_cert_ext_unittest.cc
@@ -59,18 +59,18 @@ class SignedCertificateTimestampsExtract
   std::unique_ptr<DataBuffer> auth_timestamps_;
   std::unique_ptr<DataBuffer> handshake_timestamps_;
 };
 
 static const uint8_t kSctValue[] = {0x01, 0x23, 0x45, 0x67, 0x89};
 static const SECItem kSctItem = {siBuffer, const_cast<uint8_t*>(kSctValue),
                                  sizeof(kSctValue)};
 static const DataBuffer kSctBuffer(kSctValue, sizeof(kSctValue));
-static const SSLExtraServerCertData kExtraSctData = {ssl_auth_null, nullptr,
-                                                     nullptr, &kSctItem};
+static const SSLExtraServerCertData kExtraSctData = {
+    ssl_auth_null, nullptr, nullptr, &kSctItem, nullptr, nullptr};
 
 // Test timestamps extraction during a successful handshake.
 TEST_P(TlsConnectGenericPre13, SignedCertificateTimestampsLegacy) {
   EnsureTlsSetup();
 
   // We have to use the legacy API consistently here for configuring certs.
   // Also, this doesn't work in TLS 1.3 because this only configures the SCT for
   // RSA decrypt and PKCS#1 signing, not PSS.
@@ -142,18 +142,18 @@ static SECStatus CheckNoOCSP(TlsAgent* a
 
 static const uint8_t kOcspValue1[] = {1, 2, 3, 4, 5, 6};
 static const uint8_t kOcspValue2[] = {7, 8, 9};
 static const SECItem kOcspItems[] = {
     {siBuffer, const_cast<uint8_t*>(kOcspValue1), sizeof(kOcspValue1)},
     {siBuffer, const_cast<uint8_t*>(kOcspValue2), sizeof(kOcspValue2)}};
 static const SECItemArray kOcspResponses = {const_cast<SECItem*>(kOcspItems),
                                             PR_ARRAY_SIZE(kOcspItems)};
-const static SSLExtraServerCertData kOcspExtraData = {ssl_auth_null, nullptr,
-                                                      &kOcspResponses, nullptr};
+const static SSLExtraServerCertData kOcspExtraData = {
+    ssl_auth_null, nullptr, &kOcspResponses, nullptr, nullptr, nullptr};
 
 TEST_P(TlsConnectGeneric, NoOcsp) {
   EnsureTlsSetup();
   client_->SetAuthCertificateCallback(CheckNoOCSP);
   Connect();
 }
 
 // The client doesn't get OCSP stapling unless it asks.
@@ -219,17 +219,17 @@ TEST_P(TlsConnectGeneric, OcspHugeSucces
 
   uint8_t hugeOcspValue[16385];
   memset(hugeOcspValue, 0xa1, sizeof(hugeOcspValue));
   const SECItem hugeOcspItems[] = {
       {siBuffer, const_cast<uint8_t*>(hugeOcspValue), sizeof(hugeOcspValue)}};
   const SECItemArray hugeOcspResponses = {const_cast<SECItem*>(hugeOcspItems),
                                           PR_ARRAY_SIZE(hugeOcspItems)};
   const SSLExtraServerCertData hugeOcspExtraData = {
-      ssl_auth_null, nullptr, &hugeOcspResponses, nullptr};
+      ssl_auth_null, nullptr, &hugeOcspResponses, nullptr, nullptr, nullptr};
 
   // The value should be available during the AuthCertificateCallback
   client_->SetAuthCertificateCallback([&](TlsAgent* agent, bool checksig,
                                           bool isServer) -> SECStatus {
     const SECItemArray* ocsp = SSL_PeerStapledOCSPResponses(agent->ssl_fd());
     if (!ocsp) {
       return SECFailure;
     }
--- a/gtests/ssl_gtest/ssl_gtest.gyp
+++ b/gtests/ssl_gtest/ssl_gtest.gyp
@@ -49,17 +49,18 @@
         'ssl_version_unittest.cc',
         'ssl_versionpolicy_unittest.cc',
         'test_io.cc',
         'tls_agent.cc',
         'tls_connect.cc',
         'tls_filter.cc',
         'tls_hkdf_unittest.cc',
         'tls_esni_unittest.cc',
-        'tls_protect.cc'
+        'tls_protect.cc',
+        'tls_subcerts_unittest.cc'
       ],
       'dependencies': [
         '<(DEPTH)/exports.gyp:nss_exports',
         '<(DEPTH)/lib/util/util.gyp:nssutil3',
         '<(DEPTH)/gtests/google_test/google_test.gyp:gtest',
         '<(DEPTH)/lib/smime/smime.gyp:smime',
         '<(DEPTH)/lib/ssl/ssl.gyp:ssl',
         '<(DEPTH)/lib/nss/nss.gyp:nss_static',
--- a/gtests/ssl_gtest/tls_agent.cc
+++ b/gtests/ssl_gtest/tls_agent.cc
@@ -42,16 +42,17 @@ const std::string TlsAgent::kServerRsaSi
 const std::string TlsAgent::kServerRsaPss = "rsa_pss";
 const std::string TlsAgent::kServerRsaDecrypt = "rsa_decrypt";
 const std::string TlsAgent::kServerEcdsa256 = "ecdsa256";
 const std::string TlsAgent::kServerEcdsa384 = "ecdsa384";
 const std::string TlsAgent::kServerEcdsa521 = "ecdsa521";
 const std::string TlsAgent::kServerEcdhRsa = "ecdh_rsa";
 const std::string TlsAgent::kServerEcdhEcdsa = "ecdh_ecdsa";
 const std::string TlsAgent::kServerDsa = "dsa";
+const std::string TlsAgent::kDelegatorEcdsa256 = "delegator_ecdsa256";
 
 static const uint8_t kCannedTls13ServerHello[] = {
     0x03, 0x03, 0x9c, 0xbc, 0x14, 0x9b, 0x0e, 0x2e, 0xfa, 0x0d, 0xf3,
     0xf0, 0x5c, 0x70, 0x7a, 0xe0, 0xd1, 0x9b, 0x3e, 0x5a, 0x44, 0x6b,
     0xdf, 0xe5, 0xc2, 0x28, 0x64, 0xf7, 0x00, 0xc1, 0x9c, 0x08, 0x76,
     0x08, 0x00, 0x13, 0x01, 0x00, 0x00, 0x2e, 0x00, 0x33, 0x00, 0x24,
     0x00, 0x1d, 0x00, 0x20, 0xc2, 0xcf, 0x23, 0x17, 0x64, 0x23, 0x03,
     0xf0, 0xfb, 0x45, 0x98, 0x26, 0xd1, 0x65, 0x24, 0xa1, 0x6c, 0xa9,
@@ -132,16 +133,69 @@ void TlsAgent::SetState(State s) {
 
   priv->reset(PK11_FindKeyByAnyCert(cert->get(), nullptr));
   EXPECT_NE(nullptr, priv->get());
   if (!priv->get()) return false;
 
   return true;
 }
 
+// Loads a key pair from the certificate identified by |id|.
+/*static*/ bool TlsAgent::LoadKeyPairFromCert(const std::string& name,
+                                              ScopedSECKEYPublicKey* pub,
+                                              ScopedSECKEYPrivateKey* priv) {
+  ScopedCERTCertificate cert;
+  if (!TlsAgent::LoadCertificate(name, &cert, priv)) {
+    return false;
+  }
+
+  pub->reset(SECKEY_ExtractPublicKey(&cert->subjectPublicKeyInfo));
+  if (!pub->get()) {
+    return false;
+  }
+
+  return true;
+}
+
+void TlsAgent::DelegateCredential(const std::string& name,
+                                  const ScopedSECKEYPublicKey& dc_pub,
+                                  SSLSignatureScheme dc_cert_verify_alg,
+                                  PRUint32 dc_valid_for, PRTime now,
+                                  SECItem* dc) {
+  ScopedCERTCertificate cert;
+  ScopedSECKEYPrivateKey certPriv;
+  EXPECT_TRUE(TlsAgent::LoadCertificate(name, &cert, &certPriv));
+  EXPECT_EQ(SECSuccess,
+            SSL_DelegateCredential(cert.get(), certPriv.get(), dc_pub.get(),
+                                   dc_cert_verify_alg, dc_valid_for, now, dc));
+}
+
+void TlsAgent::EnableDelegatedCredentials() {
+  ASSERT_TRUE(EnsureTlsSetup());
+  SetOption(SSL_ENABLE_DELEGATED_CREDENTIALS, PR_TRUE);
+}
+
+void TlsAgent::AddDelegatedCredential(const std::string& dc_name,
+                                      SSLSignatureScheme dc_cert_verify_alg,
+                                      PRUint32 dc_valid_for, PRTime now) {
+  ASSERT_TRUE(EnsureTlsSetup());
+
+  ScopedSECKEYPublicKey pub;
+  ScopedSECKEYPrivateKey priv;
+  EXPECT_TRUE(TlsAgent::LoadKeyPairFromCert(dc_name, &pub, &priv));
+
+  StackSECItem dc;
+  TlsAgent::DelegateCredential(name_, pub, dc_cert_verify_alg, dc_valid_for,
+                               now, &dc);
+
+  SSLExtraServerCertData extra_data = {ssl_auth_null, nullptr, nullptr,
+                                       nullptr,       &dc,     priv.get()};
+  EXPECT_TRUE(ConfigServerCert(name_, true, &extra_data));
+}
+
 bool TlsAgent::ConfigServerCert(const std::string& id, bool updateKeyBits,
                                 const SSLExtraServerCertData* serverCertData) {
   ScopedCERTCertificate cert;
   ScopedSECKEYPrivateKey priv;
   if (!TlsAgent::LoadCertificate(id, &cert, &priv)) {
     return false;
   }
 
@@ -313,16 +367,20 @@ bool TlsAgent::GetPeerChainLength(size_t
 
   return true;
 }
 
 void TlsAgent::CheckCipherSuite(uint16_t suite) {
   EXPECT_EQ(csinfo_.cipherSuite, suite);
 }
 
+void TlsAgent::CheckPeerDelegCred(bool expected) {
+  EXPECT_EQ(expected, info_.peerDelegCred);
+}
+
 void TlsAgent::RequestClientAuth(bool requireAuth) {
   ASSERT_EQ(SERVER, role_);
 
   SetOption(SSL_REQUEST_CERTIFICATE, PR_TRUE);
   SetOption(SSL_REQUIRE_CERTIFICATE, requireAuth ? PR_TRUE : PR_FALSE);
 
   EXPECT_EQ(SECSuccess, SSL_AuthCertificateHook(
                             ssl_fd(), &TlsAgent::ClientAuthenticated, this));
--- a/gtests/ssl_gtest/tls_agent.h
+++ b/gtests/ssl_gtest/tls_agent.h
@@ -71,16 +71,17 @@ class TlsAgent : public PollTarget {
   static const std::string kServerRsaPss;
   static const std::string kServerRsaDecrypt;
   static const std::string kServerEcdsa256;
   static const std::string kServerEcdsa384;
   static const std::string kServerEcdsa521;
   static const std::string kServerEcdhEcdsa;
   static const std::string kServerEcdhRsa;
   static const std::string kServerDsa;
+  static const std::string kDelegatorEcdsa256;  // draft-ietf-tls-subcerts
 
   TlsAgent(const std::string& name, Role role, SSLProtocolVariant variant);
   virtual ~TlsAgent();
 
   void SetPeer(std::shared_ptr<TlsAgent>& peer) {
     adapter_->SetPeer(peer->adapter_);
   }
 
@@ -108,16 +109,36 @@ class TlsAgent : public PollTarget {
   void PrepareForRenegotiate();
   // Prepares for renegotiation, then actually triggers it.
   void StartRenegotiate();
   void SetAntiReplayContext(ScopedSSLAntiReplayContext& ctx);
 
   static bool LoadCertificate(const std::string& name,
                               ScopedCERTCertificate* cert,
                               ScopedSECKEYPrivateKey* priv);
+  static bool LoadKeyPairFromCert(const std::string& name,
+                                  ScopedSECKEYPublicKey* pub,
+                                  ScopedSECKEYPrivateKey* priv);
+
+  // Delegated credentials.
+  //
+  // Generate a delegated credential and sign it using the certificate
+  // associated with |name|.
+  static void DelegateCredential(const std::string& name,
+                                 const ScopedSECKEYPublicKey& dcPub,
+                                 SSLSignatureScheme dcCertVerifyAlg,
+                                 PRUint32 dcValidFor, PRTime now, SECItem* dc);
+  // Indicate support for the delegated credentials extension.
+  void EnableDelegatedCredentials();
+  // Generate and configure a delegated credential to use in the handshake with
+  // clients that support this extension..
+  void AddDelegatedCredential(const std::string& dc_name,
+                              SSLSignatureScheme dcCertVerifyAlg,
+                              PRUint32 dcValidFor, PRTime now);
+
   bool ConfigServerCert(const std::string& name, bool updateKeyBits = false,
                         const SSLExtraServerCertData* serverCertData = nullptr);
   bool ConfigServerCertWithChain(const std::string& name);
   bool EnsureTlsSetup(PRFileDesc* modelSocket = nullptr);
 
   void SetupClientAuth();
   void RequestClientAuth(bool requireAuth);
 
@@ -158,16 +179,17 @@ class TlsAgent : public PollTarget {
   void CheckExtendedMasterSecret(bool expected);
   void CheckEarlyDataAccepted(bool expected);
   void SetDowngradeCheckVersion(uint16_t version);
   void CheckSecretsDestroyed();
   void ConfigNamedGroups(const std::vector<SSLNamedGroup>& groups);
   void DisableECDHEServerKeyReuse();
   bool GetPeerChainLength(size_t* count);
   void CheckCipherSuite(uint16_t cipher_suite);
+  void CheckPeerDelegCred(bool expected);
   void SetResumptionTokenCallback();
   bool MaybeSetResumptionToken();
   void SetResumptionToken(const std::vector<uint8_t>& resumption_token) {
     resumption_token_ = resumption_token;
   }
   const std::vector<uint8_t>& GetResumptionToken() const {
     return resumption_token_;
   }
new file mode 100644
--- /dev/null
+++ b/gtests/ssl_gtest/tls_subcerts_unittest.cc
@@ -0,0 +1,238 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* 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 <ctime>
+
+#include "secerr.h"
+#include "ssl.h"
+
+#include "gtest_utils.h"
+#include "tls_agent.h"
+#include "tls_connect.h"
+
+namespace nss_test {
+
+const std::string kDelegatorId = TlsAgent::kDelegatorEcdsa256;
+const std::string kDCId = TlsAgent::kServerEcdsa256;
+const SSLSignatureScheme kDCScheme = ssl_sig_ecdsa_secp256r1_sha256;
+const PRUint32 kDCValidFor = 60 * 60 * 24 * 7 /* 1 week (seconds */;
+
+// Attempt to configure a DC when either the DC or DC private key is missing.
+TEST_P(TlsConnectTls13, DCNotConfigured) {
+  // Load and delegate the credential.
+  ScopedSECKEYPublicKey pub;
+  ScopedSECKEYPrivateKey priv;
+  EXPECT_TRUE(TlsAgent::LoadKeyPairFromCert(kDCId, &pub, &priv));
+
+  StackSECItem dc;
+  TlsAgent::DelegateCredential(kDelegatorId, pub, kDCScheme, kDCValidFor, now(),
+                               &dc);
+
+  // Attempt to install the certificate and DC with a missing DC private key.
+  EnsureTlsSetup();
+  SSLExtraServerCertData extra_data_missing_dc_priv_key = {
+      ssl_auth_null, nullptr, nullptr, nullptr, &dc, nullptr};
+  EXPECT_FALSE(server_->ConfigServerCert(kDelegatorId, true,
+                                         &extra_data_missing_dc_priv_key));
+
+  // Attempt to install the certificate and with only the DC private key.
+  EnsureTlsSetup();
+  SSLExtraServerCertData extra_data_missing_dc = {
+      ssl_auth_null, nullptr, nullptr, nullptr, nullptr, priv.get()};
+  EXPECT_FALSE(
+      server_->ConfigServerCert(kDelegatorId, true, &extra_data_missing_dc));
+}
+
+// Connected with ECDSA-P256.
+TEST_P(TlsConnectTls13, DCConnectEcdsaP256) {
+  Reset(kDelegatorId);
+  client_->EnableDelegatedCredentials();
+  server_->AddDelegatedCredential(kDCId, kDCScheme, kDCValidFor, now());
+
+  auto cfilter = MakeTlsFilter<TlsExtensionCapture>(
+      client_, ssl_delegated_credentials_xtn);
+  Connect();
+
+  EXPECT_TRUE(cfilter->captured());
+  client_->CheckPeerDelegCred(true);
+}
+
+// Connected with ECDSA-P521.
+TEST_P(TlsConnectTls13, DCConnectEcdsaP521) {
+  Reset(kDelegatorId);
+  client_->EnableDelegatedCredentials();
+  server_->AddDelegatedCredential(TlsAgent::kServerEcdsa521,
+                                  ssl_sig_ecdsa_secp521r1_sha512, kDCValidFor,
+                                  now());
+  client_->EnableDelegatedCredentials();
+
+  auto cfilter = MakeTlsFilter<TlsExtensionCapture>(
+      client_, ssl_delegated_credentials_xtn);
+  Connect();
+
+  EXPECT_TRUE(cfilter->captured());
+  client_->CheckPeerDelegCred(true);
+}
+
+// Connected with RSA-PSS.
+TEST_P(TlsConnectTls13, DCConnectRsaPss) {
+  Reset(kDelegatorId);
+  client_->EnableDelegatedCredentials();
+  server_->AddDelegatedCredential(
+      TlsAgent::kServerRsaPss, ssl_sig_rsa_pss_rsae_sha256, kDCValidFor, now());
+
+  auto cfilter = MakeTlsFilter<TlsExtensionCapture>(
+      client_, ssl_delegated_credentials_xtn);
+  Connect();
+
+  EXPECT_TRUE(cfilter->captured());
+  client_->CheckPeerDelegCred(true);
+}
+
+// Aborted because of incorrect DC signature algorithm indication.
+TEST_P(TlsConnectTls13, DCAbortBadExpectedCertVerifyAlg) {
+  Reset(kDelegatorId);
+  client_->EnableDelegatedCredentials();
+  server_->AddDelegatedCredential(TlsAgent::kServerEcdsa256,
+                                  ssl_sig_ecdsa_secp521r1_sha512, kDCValidFor,
+                                  now());
+  ConnectExpectAlert(client_, kTlsAlertIllegalParameter);
+}
+
+// Aborted because of invalid DC signature.
+TEST_P(TlsConnectTls13, DCABortBadSignature) {
+  Reset(kDelegatorId);
+  EnsureTlsSetup();
+  client_->EnableDelegatedCredentials();
+
+  ScopedSECKEYPublicKey pub;
+  ScopedSECKEYPrivateKey priv;
+  EXPECT_TRUE(TlsAgent::LoadKeyPairFromCert(kDCId, &pub, &priv));
+
+  StackSECItem dc;
+  TlsAgent::DelegateCredential(kDelegatorId, pub, kDCScheme, kDCValidFor, now(),
+                               &dc);
+
+  // Flip the first bit of the DC so that the signature is invalid.
+  dc.data[0] ^= 0x01;
+
+  SSLExtraServerCertData extra_data = {ssl_auth_null, nullptr, nullptr,
+                                       nullptr,       &dc,     priv.get()};
+  EXPECT_TRUE(server_->ConfigServerCert(kDelegatorId, true, &extra_data));
+
+  ConnectExpectAlert(client_, kTlsAlertIllegalParameter);
+}
+
+// Aborted because of expired DC.
+TEST_P(TlsConnectTls13, DCAbortExpired) {
+  Reset(kDelegatorId);
+  server_->AddDelegatedCredential(kDCId, kDCScheme, kDCValidFor, now());
+  client_->EnableDelegatedCredentials();
+  // When the client checks the time, it will be at least one second after the
+  // DC expired.
+  AdvanceTime((static_cast<PRTime>(kDCValidFor) + 1) * PR_USEC_PER_SEC);
+  ConnectExpectAlert(client_, kTlsAlertIllegalParameter);
+}
+
+// Aborted because of invalid key usage.
+TEST_P(TlsConnectTls13, DCAbortBadKeyUsage) {
+  // The sever does not have the delegationUsage extension.
+  Reset(TlsAgent::kServerEcdsa256);
+  client_->EnableDelegatedCredentials();
+  server_->AddDelegatedCredential(kDCId, kDCScheme, kDCValidFor, now());
+  ConnectExpectAlert(client_, kTlsAlertIllegalParameter);
+}
+
+// Connected without DC because of no client indication.
+TEST_P(TlsConnectTls13, DCConnectNoClientSupport) {
+  Reset(kDelegatorId);
+  server_->AddDelegatedCredential(kDCId, kDCScheme, kDCValidFor, now());
+
+  auto cfilter = MakeTlsFilter<TlsExtensionCapture>(
+      client_, ssl_delegated_credentials_xtn);
+  Connect();
+
+  EXPECT_FALSE(cfilter->captured());
+  client_->CheckPeerDelegCred(false);
+}
+
+// Connected without DC because of no server DC.
+TEST_P(TlsConnectTls13, DCConnectNoServerSupport) {
+  Reset(kDelegatorId);
+  client_->EnableDelegatedCredentials();
+
+  auto cfilter = MakeTlsFilter<TlsExtensionCapture>(
+      client_, ssl_delegated_credentials_xtn);
+  Connect();
+
+  EXPECT_TRUE(cfilter->captured());
+  client_->CheckPeerDelegCred(false);
+}
+
+// Connected without DC because client doesn't support TLS 1.3.
+TEST_P(TlsConnectTls13, DCConnectClientNoTls13) {
+  Reset(kDelegatorId);
+  client_->EnableDelegatedCredentials();
+  server_->AddDelegatedCredential(kDCId, kDCScheme, kDCValidFor, now());
+
+  client_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_2,
+                           SSL_LIBRARY_VERSION_TLS_1_2);
+  server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_2,
+                           SSL_LIBRARY_VERSION_TLS_1_3);
+
+  auto cfilter = MakeTlsFilter<TlsExtensionCapture>(
+      client_, ssl_delegated_credentials_xtn);
+  Connect();
+
+  // Should fallback to TLS 1.2 and not negotiate a DC.
+  EXPECT_FALSE(cfilter->captured());
+  client_->CheckPeerDelegCred(false);
+}
+
+// Connected without DC because server doesn't support TLS 1.3.
+TEST_P(TlsConnectTls13, DCConnectServerNoTls13) {
+  Reset(kDelegatorId);
+  client_->EnableDelegatedCredentials();
+  server_->AddDelegatedCredential(kDCId, kDCScheme, kDCValidFor, now());
+
+  client_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_2,
+                           SSL_LIBRARY_VERSION_TLS_1_3);
+  server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_2,
+                           SSL_LIBRARY_VERSION_TLS_1_2);
+
+  auto cfilter = MakeTlsFilter<TlsExtensionCapture>(
+      client_, ssl_delegated_credentials_xtn);
+  Connect();
+
+  // Should fallback to TLS 1.2 and not negotiate a DC. The client will still
+  // send the indication because it supports 1.3.
+  EXPECT_TRUE(cfilter->captured());
+  client_->CheckPeerDelegCred(false);
+}
+
+// Connected without DC because client doesn't support the signature scheme.
+TEST_P(TlsConnectTls13, DCConnectExpectedCertVerifyAlgNotSupported) {
+  Reset(kDelegatorId);
+  client_->EnableDelegatedCredentials();
+  static const SSLSignatureScheme kClientSchemes[] = {
+      ssl_sig_ecdsa_secp256r1_sha256,
+  };
+  client_->SetSignatureSchemes(kClientSchemes, PR_ARRAY_SIZE(kClientSchemes));
+
+  server_->AddDelegatedCredential(TlsAgent::kServerEcdsa521,
+                                  ssl_sig_ecdsa_secp521r1_sha512, kDCValidFor,
+                                  now());
+
+  auto cfilter = MakeTlsFilter<TlsExtensionCapture>(
+      client_, ssl_delegated_credentials_xtn);
+  Connect();
+
+  // Client sends indication, but the server doesn't send a DC.
+  EXPECT_TRUE(cfilter->captured());
+  client_->CheckPeerDelegCred(false);
+}
+
+}  // namespace nss_test
--- a/lib/ssl/manifest.mn
+++ b/lib/ssl/manifest.mn
@@ -52,15 +52,16 @@ CSRCS = \
         tls13exthandle.c \
         tls13hashstate.c \
         tls13hkdf.c \
         tls13replay.c \
         sslcert.c \
         sslgrp.c \
         sslprimitive.c \
         tls13esni.c \
+        tls13subcerts.c \
         $(NULL)
 
 LIBRARY_NAME = ssl
 LIBRARY_VERSION = 3
 
 # This part of the code, including all sub-dirs, can be optimized for size
 export ALLOW_OPT_CODE_SIZE = 1
--- a/lib/ssl/ssl.gyp
+++ b/lib/ssl/ssl.gyp
@@ -44,16 +44,17 @@
         'ssltrace.c',
         'sslver.c',
         'tls13con.c',
         'tls13esni.c',
         'tls13exthandle.c',
         'tls13hashstate.c',
         'tls13hkdf.c',
         'tls13replay.c',
+        'tls13subcerts.c',
       ],
       'conditions': [
         [ 'OS=="win"', {
           'sources': [
             'win32err.c',
           ],
           'defines': [
             'IN_LIBSSL',
--- a/lib/ssl/ssl.h
+++ b/lib/ssl/ssl.h
@@ -305,16 +305,32 @@ SSL_IMPORT PRFileDesc *DTLS_ImportFD(PRF
  * 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
 
+/* Enables the delegated credentials extension (draft-ietf-tls-subcerts). When
+ * enabled, a client that supports TLS 1.3 will indicate willingness to
+ * negotiate a delegated credential (DC).
+ *
+ * If support is indicated, the peer may use a DC to authenticate itself. The DC
+ * is sent as an extension to the peer's end-entity certificate; the end-entity
+ * certificate is used to verify the DC, which in turn is used to verify the
+ * handshake. DCs effectively extend the certificate chain by one, but only
+ * within the context of TLS. Once issued, DCs can't be revoked; in order to
+ * mitigate the damage in case the secret key is compromised, the DC is only
+ * valid for a short time (days, hours, or even minutes).
+ *
+ * This library implements draft-03 of the protocol spec.
+ */
+#define SSL_ENABLE_DELEGATED_CREDENTIALS 40
+
 #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
@@ -1021,36 +1021,34 @@ ssl3_GetNewRandom(SSL3Random random)
 
     rv = PK11_GenerateRandom(random, SSL3_RANDOM_LENGTH);
     if (rv != SECSuccess) {
         ssl_MapLowLevelError(SSL_ERROR_GENERATE_RANDOM_FAILURE);
     }
     return rv;
 }
 
-/* Called by ssl3_SendServerKeyExchange and ssl3_SendCertificateVerify */
-SECStatus
-ssl3_SignHashes(sslSocket *ss, SSL3Hashes *hash, SECKEYPrivateKey *key,
-                SECItem *buf)
+SECStatus
+ssl3_SignHashesWithPrivKey(SSL3Hashes *hash, SECKEYPrivateKey *key,
+                           SSLSignatureScheme scheme, PRBool isTls, SECItem *buf)
 {
     SECStatus rv = SECFailure;
     PRBool doDerEncode = PR_FALSE;
-    PRBool isTLS = (PRBool)(ss->version > SSL_LIBRARY_VERSION_3_0);
-    PRBool useRsaPss = ssl_IsRsaPssSignatureScheme(ss->ssl3.hs.signatureScheme);
+    PRBool useRsaPss = ssl_IsRsaPssSignatureScheme(scheme);
     SECItem hashItem;
 
     buf->data = NULL;
 
     switch (SECKEY_GetPrivateKeyType(key)) {
         case rsaKey:
             hashItem.data = hash->u.raw;
             hashItem.len = hash->len;
             break;
         case dsaKey:
-            doDerEncode = isTLS;
+            doDerEncode = isTls;
             /* ssl_hash_none is used to specify the MD5/SHA1 concatenated hash.
              * In that case, we use just the SHA1 part. */
             if (hash->hashAlg == ssl_hash_none) {
                 hashItem.data = hash->u.s.sha;
                 hashItem.len = sizeof(hash->u.s.sha);
             } else {
                 hashItem.data = hash->u.raw;
                 hashItem.len = hash->len;
@@ -1117,48 +1115,66 @@ ssl3_SignHashes(sslSocket *ss, SSL3Hashe
         if (rv == SECSuccess) {
             PORT_Free(buf->data); /* discard unencoded signature. */
             *buf = derSig;        /* give caller encoded signature. */
         } else if (derSig.data) {
             PORT_Free(derSig.data);
         }
     }
 
-    if (ss->sec.isServer) {
-        ss->sec.signatureScheme = ss->ssl3.hs.signatureScheme;
-        ss->sec.authType =
-            ssl_SignatureSchemeToAuthType(ss->ssl3.hs.signatureScheme);
-    }
     PRINT_BUF(60, (NULL, "signed hashes", (unsigned char *)buf->data, buf->len));
 done:
     if (rv != SECSuccess && buf->data) {
         PORT_Free(buf->data);
         buf->data = NULL;
     }
     return rv;
 }
 
-/* Called from ssl3_HandleServerKeyExchange, ssl3_HandleCertificateVerify */
-SECStatus
-ssl3_VerifySignedHashes(sslSocket *ss, SSLSignatureScheme scheme, SSL3Hashes *hash,
-                        SECItem *buf)
+/* Called by ssl3_SendServerKeyExchange and ssl3_SendCertificateVerify */
+SECStatus
+ssl3_SignHashes(sslSocket *ss, SSL3Hashes *hash, SECKEYPrivateKey *key,
+                SECItem *buf)
+{
+    SECStatus rv = SECFailure;
+    PRBool isTLS = (PRBool)(ss->version > SSL_LIBRARY_VERSION_3_0);
+    SSLSignatureScheme scheme = ss->ssl3.hs.signatureScheme;
+
+    rv = ssl3_SignHashesWithPrivKey(hash, key, scheme, isTLS, buf);
+    if (rv != SECSuccess) {
+        return SECFailure;
+    }
+
+    if (ss->sec.isServer) {
+        ss->sec.signatureScheme = scheme;
+        ss->sec.authType = ssl_SignatureSchemeToAuthType(scheme);
+    }
+
+    return SECSuccess;
+}
+
+/* Called from ssl3_VerifySignedHashes and tls13_HandleCertificateVerify. */
+SECStatus
+ssl3_VerifySignedHashesWithSpki(sslSocket *ss, CERTSubjectPublicKeyInfo *spki,
+                                SSLSignatureScheme scheme,
+                                SSL3Hashes *hash, SECItem *buf)
 {
     SECKEYPublicKey *key;
     SECItem *signature = NULL;
     SECStatus rv = SECFailure;
     SECItem hashItem;
     SECOidTag encAlg;
     SECOidTag hashAlg;
     void *pwArg = ss->pkcs11PinArg;
     PRBool isRsaPssScheme = ssl_IsRsaPssSignatureScheme(scheme);
 
     PRINT_BUF(60, (NULL, "check signed hashes",
                    buf->data, buf->len));
 
-    key = CERT_ExtractPublicKey(ss->sec.peerCert);
+    key = SECKEY_ExtractPublicKey(spki);
     if (key == NULL) {
         ssl_MapLowLevelError(SSL_ERROR_EXTRACT_PUBLIC_KEY_FAILURE);
         return SECFailure;
     }
 
     hashAlg = ssl3_HashTypeToOID(hash->hashAlg);
     switch (SECKEY_GetPublicKeyType(key)) {
         case rsaKey:
@@ -1268,16 +1284,26 @@ loser:
     SECKEY_DestroyPublicKey(key);
 #ifdef UNSAFE_FUZZER_MODE
     rv = SECSuccess;
     PORT_SetError(0);
 #endif
     return rv;
 }
 
+/* Called from ssl3_HandleServerKeyExchange, ssl3_HandleCertificateVerify */
+SECStatus
+ssl3_VerifySignedHashes(sslSocket *ss, SSLSignatureScheme scheme, SSL3Hashes *hash,
+                        SECItem *buf)
+{
+    CERTSubjectPublicKeyInfo *spki = &ss->sec.peerCert->subjectPublicKeyInfo;
+    return ssl3_VerifySignedHashesWithSpki(
+        ss, spki, scheme, hash, buf);
+}
+
 /* Caller must set hiLevel error code. */
 /* Called from ssl3_ComputeDHKeyHash
  * which are called from ssl3_HandleServerKeyExchange.
  *
  * hashAlg: ssl_hash_none indicates the pre-1.2, MD5/SHA1 combination hash.
  */
 SECStatus
 ssl3_ComputeCommonKeyHash(SSLHashType hashAlg,
@@ -4108,17 +4134,17 @@ ssl_SignatureSchemeValid(SSLSignatureSch
         /* With TLS 1.3, EC keys should have been selected based on calling
          * ssl_SignatureSchemeFromSpki(), reject them otherwise. */
         return spkiOid != SEC_OID_ANSIX962_EC_PUBLIC_KEY;
     }
     return PR_TRUE;
 }
 
 static SECStatus
-ssl_SignatureSchemeFromPssSpki(CERTSubjectPublicKeyInfo *spki,
+ssl_SignatureSchemeFromPssSpki(const CERTSubjectPublicKeyInfo *spki,
                                SSLSignatureScheme *scheme)
 {
     SECKEYRSAPSSParams pssParam = { 0 };
     PORTCheapArenaPool arena;
     SECStatus rv;
 
     /* The key doesn't have parameters, boo. */
     if (!spki->algorithm.parameters.len) {
@@ -4156,17 +4182,17 @@ ssl_SignatureSchemeFromPssSpki(CERTSubje
 
 loser:
     PORT_DestroyCheapArena(&arena);
     PORT_SetError(SSL_ERROR_BAD_CERTIFICATE);
     return SECFailure;
 }
 
 static SECStatus
-ssl_SignatureSchemeFromEcSpki(CERTSubjectPublicKeyInfo *spki,
+ssl_SignatureSchemeFromEcSpki(const CERTSubjectPublicKeyInfo *spki,
                               SSLSignatureScheme *scheme)
 {
     const sslNamedGroupDef *group;
     SECKEYPublicKey *key;
 
     key = SECKEY_ExtractPublicKey(spki);
     if (!key) {
         PORT_SetError(SSL_ERROR_BAD_CERTIFICATE);
@@ -4193,18 +4219,18 @@ ssl_SignatureSchemeFromEcSpki(CERTSubjec
     }
     PORT_SetError(SSL_ERROR_BAD_CERTIFICATE);
     return SECFailure;
 }
 
 /* Newer signature schemes are designed so that a single SPKI can be used with
  * that scheme.  This determines that scheme from the SPKI. If the SPKI doesn't
  * have a single scheme, |*scheme| is set to ssl_sig_none. */
-static SECStatus
-ssl_SignatureSchemeFromSpki(CERTSubjectPublicKeyInfo *spki,
+SECStatus
+ssl_SignatureSchemeFromSpki(const CERTSubjectPublicKeyInfo *spki,
                             PRBool isTls13, SSLSignatureScheme *scheme)
 {
     SECOidTag spkiOid = SECOID_GetAlgorithmTag(&spki->algorithm);
 
     if (spkiOid == SEC_OID_PKCS1_RSA_PSS_SIGNATURE) {
         return ssl_SignatureSchemeFromPssSpki(spki, scheme);
     }
 
@@ -4214,18 +4240,18 @@ ssl_SignatureSchemeFromSpki(CERTSubjectP
     if (isTls13 && spkiOid == SEC_OID_ANSIX962_EC_PUBLIC_KEY) {
         return ssl_SignatureSchemeFromEcSpki(spki, scheme);
     }
 
     *scheme = ssl_sig_none;
     return SECSuccess;
 }
 
-static PRBool
-ssl_SignatureSchemeEnabled(sslSocket *ss, SSLSignatureScheme scheme)
+PRBool
+ssl_SignatureSchemeEnabled(const sslSocket *ss, SSLSignatureScheme scheme)
 {
     unsigned int i;
     for (i = 0; i < ss->ssl3.signatureSchemeCount; ++i) {
         if (scheme == ss->ssl3.signatureSchemes[i]) {
             return PR_TRUE;
         }
     }
     return PR_FALSE;
@@ -4245,45 +4271,44 @@ ssl_SignatureKeyMatchesSpkiOid(const ssl
             return keaDef->signKeyType == ecKey;
         default:
             break;
     }
     return PR_FALSE;
 }
 
 /* ssl3_CheckSignatureSchemeConsistency checks that the signature algorithm
- * identifier in |scheme| is consistent with the public key in |cert|. It also
+ * identifier in |scheme| is consistent with the public key in |spki|. It also
  * checks the hash algorithm against the configured signature algorithms.  If
  * all the tests pass, SECSuccess is returned. Otherwise, PORT_SetError is
  * called and SECFailure is returned. */
 SECStatus
 ssl_CheckSignatureSchemeConsistency(sslSocket *ss, SSLSignatureScheme scheme,
-                                    CERTCertificate *cert)
+                                    CERTSubjectPublicKeyInfo *spki)
 {
     SSLSignatureScheme spkiScheme;
     PRBool isTLS13 = ss->version == SSL_LIBRARY_VERSION_TLS_1_3;
     SECOidTag spkiOid;
     SECStatus rv;
 
-    rv = ssl_SignatureSchemeFromSpki(&cert->subjectPublicKeyInfo, isTLS13,
-                                     &spkiScheme);
+    rv = ssl_SignatureSchemeFromSpki(spki, isTLS13, &spkiScheme);
     if (rv != SECSuccess) {
         return SECFailure;
     }
     if (spkiScheme != ssl_sig_none) {
         /* The SPKI in the certificate can only be used for a single scheme. */
         if (spkiScheme != scheme ||
             !ssl_SignatureSchemeEnabled(ss, scheme)) {
             PORT_SetError(SSL_ERROR_INCORRECT_SIGNATURE_ALGORITHM);
             return SECFailure;
         }
         return SECSuccess;
     }
 
-    spkiOid = SECOID_GetAlgorithmTag(&cert->subjectPublicKeyInfo.algorithm);
+    spkiOid = SECOID_GetAlgorithmTag(&spki->algorithm);
 
     /* If we're a client, check that the signature algorithm matches the signing
      * key type of the cipher suite. */
     if (!isTLS13 && !ss->sec.isServer) {
         if (!ssl_SignatureKeyMatchesSpkiOid(ss->ssl3.hs.kea_def, spkiOid)) {
             PORT_SetError(SSL_ERROR_INCORRECT_SIGNATURE_ALGORITHM);
             return SECFailure;
         }
@@ -6075,17 +6100,17 @@ ssl3_SendClientKeyExchange(sslSocket *ss
     SSL_TRC(3, ("%d: SSL3[%d]: DONE sending client_key_exchange",
                 SSL_GETPID(), ss->fd));
 
     SECKEY_DestroyPublicKey(serverKey);
     return rv; /* err code already set. */
 }
 
 /* Used by ssl_PickSignatureScheme(). */
-static PRBool
+PRBool
 ssl_CanUseSignatureScheme(SSLSignatureScheme scheme,
                           const SSLSignatureScheme *peerSchemes,
                           unsigned int peerSchemeCount,
                           PRBool requireSha1,
                           PRBool slotDoesPss)
 {
     SSLHashType hashType;
     SECOidTag hashOID;
@@ -6119,75 +6144,86 @@ ssl_CanUseSignatureScheme(SSLSignatureSc
         if (peerSchemes[i] == scheme) {
             return PR_TRUE;
         }
     }
     return PR_FALSE;
 }
 
 SECStatus
+ssl_PrivateKeySupportsRsaPss(SECKEYPrivateKey *privKey,
+                             PRBool *supportsRsaPss)
+{
+    PK11SlotInfo *slot;
+    slot = PK11_GetSlotFromPrivateKey(privKey);
+    if (!slot) {
+        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+        return SECFailure;
+    }
+    *supportsRsaPss = PK11_DoesMechanism(slot, auth_alg_defs[ssl_auth_rsa_pss]);
+    PK11_FreeSlot(slot);
+    return SECSuccess;
+}
+
+SECStatus
 ssl_PickSignatureScheme(sslSocket *ss,
                         CERTCertificate *cert,
                         SECKEYPublicKey *pubKey,
                         SECKEYPrivateKey *privKey,
                         const SSLSignatureScheme *peerSchemes,
                         unsigned int peerSchemeCount,
                         PRBool requireSha1)
 {
     unsigned int i;
-    PK11SlotInfo *slot;
-    PRBool slotDoesPss;
+    PRBool doesRsaPss;
     PRBool isTLS13 = ss->version >= SSL_LIBRARY_VERSION_TLS_1_3;
     SECStatus rv;
     SSLSignatureScheme scheme;
     SECOidTag spkiOid;
 
     /* We can't require SHA-1 in TLS 1.3. */
     PORT_Assert(!(requireSha1 && isTLS13));
     if (!pubKey || !privKey) {
         PORT_Assert(0);
         PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
         return SECFailure;
     }
 
-    slot = PK11_GetSlotFromPrivateKey(privKey);
-    if (!slot) {
-        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
-        return SECFailure;
-    }
-    slotDoesPss = PK11_DoesMechanism(slot, auth_alg_defs[ssl_auth_rsa_pss]);
-    PK11_FreeSlot(slot);
+    rv = ssl_PrivateKeySupportsRsaPss(privKey, &doesRsaPss);
+    if (rv != SECSuccess) {
+        return SECFailure;
+    }
 
     /* If the certificate SPKI indicates a single scheme, don't search. */
     rv = ssl_SignatureSchemeFromSpki(&cert->subjectPublicKeyInfo,
                                      isTLS13, &scheme);
     if (rv != SECSuccess) {
         return SECFailure;
     }
     if (scheme != ssl_sig_none) {
         if (!ssl_SignatureSchemeEnabled(ss, scheme) ||
             !ssl_CanUseSignatureScheme(scheme, peerSchemes, peerSchemeCount,
-                                       requireSha1, slotDoesPss)) {
+                                       requireSha1, doesRsaPss)) {
             PORT_SetError(SSL_ERROR_UNSUPPORTED_SIGNATURE_ALGORITHM);
             return SECFailure;
         }
         ss->ssl3.hs.signatureScheme = scheme;
         return SECSuccess;
     }
 
     spkiOid = SECOID_GetAlgorithmTag(&cert->subjectPublicKeyInfo.algorithm);
 
     /* Now we have to search based on the key type. Go through our preferred
      * schemes in order and find the first that can be used. */
     for (i = 0; i < ss->ssl3.signatureSchemeCount; ++i) {
         scheme = ss->ssl3.signatureSchemes[i];
 
         if (ssl_SignatureSchemeValid(scheme, spkiOid, isTLS13) &&
             ssl_CanUseSignatureScheme(scheme, peerSchemes, peerSchemeCount,
-                                      requireSha1, slotDoesPss)) {
+                                      requireSha1, doesRsaPss)) {
             ss->ssl3.hs.signatureScheme = scheme;
             return SECSuccess;
         }
     }
 
     PORT_SetError(SSL_ERROR_UNSUPPORTED_SIGNATURE_ALGORITHM);
     return SECFailure;
 }
@@ -7068,18 +7104,18 @@ ssl_HandleDHServerKeyExchange(sslSocket 
         goto alert_loser;
     }
 
     if (ss->version >= SSL_LIBRARY_VERSION_TLS_1_2) {
         rv = ssl_ConsumeSignatureScheme(ss, &b, &length, &sigScheme);
         if (rv != SECSuccess) {
             goto alert_loser; /* malformed or unsupported. */
         }
-        rv = ssl_CheckSignatureSchemeConsistency(ss, sigScheme,
-                                                 ss->sec.peerCert);
+        rv = ssl_CheckSignatureSchemeConsistency(
+            ss, sigScheme, &ss->sec.peerCert->subjectPublicKeyInfo);
         if (rv != SECSuccess) {
             goto alert_loser;
         }
         hashAlg = ssl_SignatureSchemeToHashType(sigScheme);
     } else {
         /* Use ssl_hash_none to represent the MD5+SHA1 combo. */
         hashAlg = ssl_hash_none;
         sigScheme = ssl_sig_none;
@@ -9774,18 +9810,18 @@ ssl3_HandleCertificateVerify(sslSocket *
     PORT_Assert(ss->ssl3.prSpec->version <= SSL_LIBRARY_VERSION_TLS_1_2);
 
     if (ss->ssl3.prSpec->version == SSL_LIBRARY_VERSION_TLS_1_2) {
         PORT_Assert(ss->ssl3.hs.hashType == handshake_hash_record);
         rv = ssl_ConsumeSignatureScheme(ss, &b, &length, &sigScheme);
         if (rv != SECSuccess) {
             goto loser; /* malformed or unsupported. */
         }
-        rv = ssl_CheckSignatureSchemeConsistency(ss, sigScheme,
-                                                 ss->sec.peerCert);
+        rv = ssl_CheckSignatureSchemeConsistency(
+            ss, sigScheme, &ss->sec.peerCert->subjectPublicKeyInfo);
         if (rv != SECSuccess) {
             errCode = PORT_GetError();
             desc = illegal_parameter;
             goto alert_loser;
         }
 
         rv = ssl3_ComputeHandshakeHash(ss->ssl3.hs.messages.buf,
                                        ss->ssl3.hs.messages.len,
--- a/lib/ssl/ssl3ecc.c
+++ b/lib/ssl/ssl3ecc.c
@@ -543,18 +543,18 @@ ssl3_HandleECDHServerKeyExchange(sslSock
 
     PORT_Assert(ss->ssl3.prSpec->version <= SSL_LIBRARY_VERSION_TLS_1_2);
     if (ss->ssl3.prSpec->version == SSL_LIBRARY_VERSION_TLS_1_2) {
         rv = ssl_ConsumeSignatureScheme(ss, &b, &length, &sigScheme);
         if (rv != SECSuccess) {
             errCode = PORT_GetError();
             goto alert_loser; /* malformed or unsupported. */
         }
-        rv = ssl_CheckSignatureSchemeConsistency(ss, sigScheme,
-                                                 ss->sec.peerCert);
+        rv = ssl_CheckSignatureSchemeConsistency(
+            ss, sigScheme, &ss->sec.peerCert->subjectPublicKeyInfo);
         if (rv != SECSuccess) {
             errCode = PORT_GetError();
             goto alert_loser;
         }
         hashAlg = ssl_SignatureSchemeToHashType(sigScheme);
     } else {
         /* Use ssl_hash_none to represent the MD5+SHA1 combo. */
         hashAlg = ssl_hash_none;
--- a/lib/ssl/ssl3ext.c
+++ b/lib/ssl/ssl3ext.c
@@ -11,16 +11,17 @@
 #include "nssrenam.h"
 #include "nss.h"
 #include "ssl.h"
 #include "sslimpl.h"
 #include "sslproto.h"
 #include "ssl3exthandle.h"
 #include "tls13err.h"
 #include "tls13exthandle.h"
+#include "tls13subcerts.h"
 
 /* Callback function that handles a received extension. */
 typedef SECStatus (*ssl3ExtensionHandlerFunc)(const sslSocket *ss,
                                               TLSExtensionData *xtnData,
                                               SECItem *data);
 
 /* Row in a table of hello extension handlers. */
 typedef struct {
@@ -40,16 +41,17 @@ static const ssl3ExtensionHandler client
     { ssl_session_ticket_xtn, &ssl3_ServerHandleSessionTicketXtn },
     { ssl_renegotiation_info_xtn, &ssl3_HandleRenegotiationInfoXtn },
     { ssl_app_layer_protocol_xtn, &ssl3_ServerHandleAppProtoXtn },
     { ssl_use_srtp_xtn, &ssl3_ServerHandleUseSRTPXtn },
     { ssl_cert_status_xtn, &ssl3_ServerHandleStatusRequestXtn },
     { ssl_signature_algorithms_xtn, &ssl3_HandleSigAlgsXtn },
     { ssl_extended_master_secret_xtn, &ssl3_HandleExtendedMasterSecretXtn },
     { ssl_signed_cert_timestamp_xtn, &ssl3_ServerHandleSignedCertTimestampXtn },
+    { ssl_delegated_credentials_xtn, &tls13_ServerHandleDelegatedCredentialsXtn },
     { 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 },
@@ -91,16 +93,17 @@ static const ssl3ExtensionHandler newSes
       &tls13_ClientHandleTicketEarlyDataXtn },
     { 0, NULL }
 };
 
 /* This table is used by the client to handle server certificates in TLS 1.3 */
 static const ssl3ExtensionHandler serverCertificateHandlers[] = {
     { ssl_signed_cert_timestamp_xtn, &ssl3_ClientHandleSignedCertTimestampXtn },
     { ssl_cert_status_xtn, &ssl3_ClientHandleStatusRequestXtn },
+    { ssl_delegated_credentials_xtn, &tls13_ClientHandleDelegatedCredentialsXtn },
     { 0, NULL }
 };
 
 static const ssl3ExtensionHandler certificateRequestHandlers[] = {
     { ssl_signature_algorithms_xtn, &ssl3_HandleSigAlgsXtn },
     { ssl_tls13_certificate_authorities_xtn,
       &tls13_ClientHandleCertAuthoritiesXtn },
     { 0, NULL }
@@ -122,16 +125,17 @@ static const sslExtensionBuilder clientH
       { ssl_extended_master_secret_xtn, &ssl3_SendExtendedMasterSecretXtn },
       { ssl_renegotiation_info_xtn, &ssl3_SendRenegotiationInfoXtn },
       { ssl_supported_groups_xtn, &ssl_SendSupportedGroupsXtn },
       { ssl_ec_point_formats_xtn, &ssl3_SendSupportedPointFormatsXtn },
       { ssl_session_ticket_xtn, &ssl3_ClientSendSessionTicketXtn },
       { ssl_app_layer_protocol_xtn, &ssl3_ClientSendAppProtoXtn },
       { ssl_use_srtp_xtn, &ssl3_ClientSendUseSRTPXtn },
       { ssl_cert_status_xtn, &ssl3_ClientSendStatusRequestXtn },
+      { ssl_delegated_credentials_xtn, &tls13_ClientSendDelegatedCredentialsXtn },
       { ssl_signed_cert_timestamp_xtn, &ssl3_ClientSendSignedCertTimestampXtn },
       { ssl_tls13_key_share_xtn, &tls13_ClientSendKeyShareXtn },
       { ssl_tls13_early_data_xtn, &tls13_ClientSendEarlyDataXtn },
       /* Some servers (e.g. WebSphere Application Server 7.0 and Tomcat) will
        * 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 },
@@ -165,16 +169,17 @@ static const sslExtensionBuilder tls13_h
 };
 
 static const struct {
     SSLExtensionType type;
     SSLExtensionSupport support;
 } ssl_supported_extensions[] = {
     { ssl_server_name_xtn, ssl_ext_native_only },
     { ssl_cert_status_xtn, ssl_ext_native },
+    { ssl_delegated_credentials_xtn, ssl_ext_native },
     { ssl_supported_groups_xtn, ssl_ext_native_only },
     { ssl_ec_point_formats_xtn, ssl_ext_native },
     { ssl_signature_algorithms_xtn, ssl_ext_native_only },
     { ssl_use_srtp_xtn, ssl_ext_native },
     { ssl_app_layer_protocol_xtn, ssl_ext_native_only },
     { ssl_signed_cert_timestamp_xtn, ssl_ext_native },
     { ssl_padding_xtn, ssl_ext_native },
     { ssl_extended_master_secret_xtn, ssl_ext_native_only },
@@ -946,16 +951,19 @@ ssl3_InitExtensionData(TLSExtensionData 
         ++advertisedMax; /* For the RI SCSV, which we also track. */
     }
     for (cursor = PR_NEXT_LINK(&ss->extensionHooks);
          cursor != &ss->extensionHooks;
          cursor = PR_NEXT_LINK(cursor)) {
         ++advertisedMax;
     }
     xtnData->advertised = PORT_ZNewArray(PRUint16, advertisedMax);
+    xtnData->peerDelegCred = NULL;
+    xtnData->peerRequestedDelegCred = PR_FALSE;
+    xtnData->sendingDelegCredToPeer = PR_FALSE;
 }
 
 void
 ssl3_DestroyExtensionData(TLSExtensionData *xtnData)
 {
     ssl3_FreeSniNameArray(xtnData);
     PORT_Free(xtnData->sigSchemes);
     SECITEM_FreeItem(&xtnData->nextProto, PR_FALSE);
@@ -964,16 +972,17 @@ ssl3_DestroyExtensionData(TLSExtensionDa
     SECITEM_FreeItem(&xtnData->applicationToken, PR_FALSE);
     if (xtnData->certReqAuthorities.arena) {
         PORT_FreeArena(xtnData->certReqAuthorities.arena, PR_FALSE);
         xtnData->certReqAuthorities.arena = NULL;
     }
     PORT_Free(xtnData->advertised);
     ssl_FreeEphemeralKeyPair(xtnData->esniPrivateKey);
     SECITEM_FreeItem(&xtnData->keyShareExtension, PR_FALSE);
+    tls13_DestroyDelegatedCredential(xtnData->peerDelegCred);
 }
 
 /* Free everything that has been allocated and then reset back to
  * the starting state. */
 void
 ssl3_ResetExtensionData(TLSExtensionData *xtnData, const sslSocket *ss)
 {
     ssl3_DestroyExtensionData(xtnData);
--- a/lib/ssl/ssl3ext.h
+++ b/lib/ssl/ssl3ext.h
@@ -106,16 +106,29 @@ struct TLSExtensionDataStr {
 
     /* ESNI working state */
     SECItem keyShareExtension;
     ssl3CipherSuite esniSuite;
     sslEphemeralKeyPair *esniPrivateKey;
     /* Pointer into |ss->esniKeys->keyShares| */
     TLS13KeyShareEntry *peerEsniShare;
     PRUint8 esniNonce[TLS13_ESNI_NONCE_SIZE];
+
+    /* Delegated credentials.
+     *
+     * The delegated credential sent by the peer. Set by
+     * |tls13_ReadDelegatedCredential|.
+     */
+    sslDelegatedCredential *peerDelegCred;
+    /* Whether the peer requested a delegated credential. */
+    PRBool peerRequestedDelegCred;
+    /* Whether the host is committed to using a delegated credential. Set by
+     * |tls13_MaybeSetDelegatedCredential|.
+     */
+    PRBool sendingDelegCredToPeer;
 };
 
 typedef struct TLSExtensionStr {
     PRCList link;  /* The linked list link */
     PRUint16 type; /* Extension type */
     SECItem data;  /* Pointers into the handshake data. */
 } TLSExtension;
 
--- a/lib/ssl/sslcert.c
+++ b/lib/ssl/sslcert.c
@@ -3,20 +3,21 @@
  * SSL server certificate configuration functions.
  *
  * 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 "ssl.h"
 #include "sslimpl.h"
-#include "secoid.h"   /* for SECOID_GetAlgorithmTag */
-#include "pk11func.h" /* for PK11_ReferenceSlot */
-#include "nss.h"      /* for NSS_RegisterShutdown */
-#include "prinit.h"   /* for PR_CallOnceWithArg */
+#include "secoid.h"        /* for SECOID_GetAlgorithmTag */
+#include "pk11func.h"      /* for PK11_ReferenceSlot */
+#include "nss.h"           /* for NSS_RegisterShutdown */
+#include "prinit.h"        /* for PR_CallOnceWithArg */
+#include "tls13subcerts.h" /* for tls13_ReadDelegatedCredential */
 
 /* This global item is used only in servers.  It is is initialized by
  * SSL_ConfigSecureServer(), and is used in ssl3_SendCertificateRequest().
  */
 static struct {
     PRCallOnceType setup;
     CERTDistNames *names;
 } ssl_server_ca_list;
@@ -97,16 +98,18 @@ ssl_NewServerCert()
         return NULL;
     }
     sc->authTypes = 0;
     sc->namedCurve = NULL;
     sc->serverCert = NULL;
     sc->serverCertChain = NULL;
     sc->certStatusArray = NULL;
     sc->signedCertTimestamps.len = 0;
+    sc->delegCred.len = 0;
+    sc->delegCredKeyPair = NULL;
     return sc;
 }
 
 sslServerCert *
 ssl_CopyServerCert(const sslServerCert *oc)
 {
     sslServerCert *sc;
 
@@ -143,18 +146,27 @@ ssl_CopyServerCert(const sslServerCert *
         sc->certStatusArray = SECITEM_DupArray(NULL, oc->certStatusArray);
         if (!sc->certStatusArray)
             goto loser;
     } else {
         sc->certStatusArray = NULL;
     }
 
     if (SECITEM_CopyItem(NULL, &sc->signedCertTimestamps,
-                         &oc->signedCertTimestamps) != SECSuccess)
+                         &oc->signedCertTimestamps) != SECSuccess) {
+        goto loser;
+    }
+
+    if (SECITEM_CopyItem(NULL, &sc->delegCred, &oc->delegCred) != SECSuccess) {
         goto loser;
+    }
+    if (oc->delegCredKeyPair) {
+        sc->delegCredKeyPair = ssl_GetKeyPairRef(oc->delegCredKeyPair);
+    }
+
     return sc;
 loser:
     ssl_FreeServerCert(sc);
     return NULL;
 }
 
 void
 ssl_FreeServerCert(sslServerCert *sc)
@@ -173,16 +185,22 @@ ssl_FreeServerCert(sslServerCert *sc)
         ssl_FreeKeyPair(sc->serverKeyPair);
     }
     if (sc->certStatusArray) {
         SECITEM_FreeArray(sc->certStatusArray, PR_TRUE);
     }
     if (sc->signedCertTimestamps.len) {
         SECITEM_FreeItem(&sc->signedCertTimestamps, PR_FALSE);
     }
+    if (sc->delegCred.len) {
+        SECITEM_FreeItem(&sc->delegCred, PR_FALSE);
+    }
+    if (sc->delegCredKeyPair) {
+        ssl_FreeKeyPair(sc->delegCredKeyPair);
+    }
     PORT_ZFree(sc, sizeof(*sc));
 }
 
 const sslServerCert *
 ssl_FindServerCert(const sslSocket *ss, SSLAuthType authType,
                    const sslNamedGroupDef *namedCurve)
 {
     PRCList *cursor;
@@ -304,16 +322,89 @@ ssl_PopulateSignedCertTimestamps(sslServ
     }
     if (signedCertTimestamps && signedCertTimestamps->len) {
         return SECITEM_CopyItem(NULL, &sc->signedCertTimestamps,
                                 signedCertTimestamps);
     }
     return SECSuccess;
 }
 
+/* Installs the given delegated credential (DC) and DC private key into the
+ * certificate.
+ *
+ * It's the caller's responsibility to ensure that the DC is well-formed and
+ * that the DC public key matches the DC private key.
+ */
+static SECStatus
+ssl_PopulateDelegatedCredential(sslServerCert *sc,
+                                const SECItem *delegCred,
+                                const SECKEYPrivateKey *delegCredPrivKey)
+{
+    sslDelegatedCredential *dc = NULL;
+
+    if (sc->delegCred.len) {
+        SECITEM_FreeItem(&sc->delegCred, PR_FALSE);
+    }
+
+    if (sc->delegCredKeyPair) {
+        ssl_FreeKeyPair(sc->delegCredKeyPair);
+        sc->delegCredKeyPair = NULL;
+    }
+
+    /* Both the DC and its private are present. */
+    if (delegCred && delegCredPrivKey) {
+        SECStatus rv;
+        SECKEYPublicKey *pub;
+        SECKEYPrivateKey *priv;
+
+        if (!delegCred->data || delegCred->len == 0) {
+            PORT_SetError(SEC_ERROR_INVALID_ARGS);
+            goto loser;
+        }
+
+        /* Parse the DC. */
+        rv = tls13_ReadDelegatedCredential(delegCred->data, delegCred->len, &dc);
+        if (rv != SECSuccess) {
+            goto loser;
+        }
+
+        /* Make a copy of the DC. */
+        rv = SECITEM_CopyItem(NULL, &sc->delegCred, delegCred);
+        if (rv != SECSuccess) {
+            goto loser;
+        }
+
+        /* Make a copy of the DC private key. */
+        priv = SECKEY_CopyPrivateKey(delegCredPrivKey);
+        if (!priv) {
+            goto loser;
+        }
+
+        /* parse public key from the DC. */
+        pub = SECKEY_ExtractPublicKey(dc->spki);
+        if (!pub) {
+            goto loser;
+        }
+
+        sc->delegCredKeyPair = ssl_NewKeyPair(priv, pub);
+
+        /* Attempting to configure either the DC or DC private key, but not both. */
+    } else if (delegCred || delegCredPrivKey) {
+        PORT_SetError(SEC_ERROR_INVALID_ARGS);
+        goto loser;
+    }
+
+    tls13_DestroyDelegatedCredential(dc);
+    return SECSuccess;
+
+loser:
+    tls13_DestroyDelegatedCredential(dc);
+    return SECFailure;
+}
+
 /* Find any existing certificates that overlap with the new certificate and
  * either remove any supported authentication types that overlap with the new
  * certificate or - if they have no types left - remove them entirely. */
 static void
 ssl_ClearMatchingCerts(sslSocket *ss, sslAuthTypeMask authTypes,
                        const sslNamedGroupDef *namedCurve)
 {
     PRCList *cursor = PR_NEXT_LINK(&ss->serverCerts);
@@ -374,16 +465,22 @@ ssl_ConfigCert(sslSocket *ss, sslAuthTyp
     rv = ssl_PopulateOCSPResponses(sc, data->stapledOCSPResponses);
     if (rv != SECSuccess) {
         goto loser;
     }
     rv = ssl_PopulateSignedCertTimestamps(sc, data->signedCertTimestamps);
     if (rv != SECSuccess) {
         goto loser;
     }
+    rv = ssl_PopulateDelegatedCredential(sc, data->delegCred,
+                                         data->delegCredPrivKey);
+    if (rv != SECSuccess) {
+        error_code = PORT_GetError();
+        goto loser;
+    }
     ssl_ClearMatchingCerts(ss, sc->authTypes, sc->namedCurve);
     PR_APPEND_LINK(&sc->link, &ss->serverCerts);
     return SECSuccess;
 
 loser:
     ssl_FreeServerCert(sc);
     PORT_SetError(error_code);
     return SECFailure;
@@ -543,17 +640,17 @@ SECStatus
 SSL_ConfigServerCert(PRFileDesc *fd, CERTCertificate *cert,
                      SECKEYPrivateKey *key,
                      const SSLExtraServerCertData *data, unsigned int data_len)
 {
     sslSocket *ss;
     sslKeyPair *keyPair;
     SECStatus rv;
     SSLExtraServerCertData dataCopy = {
-        ssl_auth_null, NULL, NULL, NULL
+        ssl_auth_null, NULL, NULL, NULL, NULL, NULL
     };
     sslAuthTypeMask authTypes;
 
     ss = ssl_FindSocket(fd);
     if (!ss) {
         return SECFailure;
     }
 
--- a/lib/ssl/sslcert.h
+++ b/lib/ssl/sslcert.h
@@ -36,16 +36,23 @@ typedef struct sslServerCertStr {
     unsigned int serverKeyBits;
     /* Each certificate needs its own status. */
     SECItemArray *certStatusArray;
     /* Serialized signed certificate timestamps to be sent to the client
     ** in a TLS extension (server only). Each certificate needs its own
     ** timestamps item.
     */
     SECItem signedCertTimestamps;
+
+    /* The delegated credential (DC) to send to clients who indicate support for
+     * the ietf-draft-tls-subcerts extension.
+     */
+    SECItem delegCred;
+    /* The key pair used to sign the handshake when serving a DC. */
+    sslKeyPair *delegCredKeyPair;
 } sslServerCert;
 
 #define SSL_CERT_IS(c, t) ((c)->authTypes & (1 << (t)))
 #define SSL_CERT_IS_ONLY(c, t) ((c)->authTypes == (1 << (t)))
 #define SSL_CERT_IS_EC(c)                         \
     ((c)->authTypes & ((1 << ssl_auth_ecdsa) |    \
                        (1 << ssl_auth_ecdh_rsa) | \
                        (1 << ssl_auth_ecdh_ecdsa)))
--- a/lib/ssl/sslerr.h
+++ b/lib/ssl/sslerr.h
@@ -265,15 +265,19 @@ typedef enum {
     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_RX_CERTIFICATE_REQUIRED_ALERT = (SSL_ERROR_BASE + 181),
+    SSL_ERROR_DC_CERT_VERIFY_ALG_MISMATCH = (SSL_ERROR_BASE + 182),
+    SSL_ERROR_DC_BAD_SIGNATURE = (SSL_ERROR_BASE + 183),
+    SSL_ERROR_DC_INVALID_KEY_USAGE = (SSL_ERROR_BASE + 184),
+    SSL_ERROR_DC_EXPIRED = (SSL_ERROR_BASE + 185),
     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
@@ -752,16 +752,46 @@ typedef struct SSLAeadContextStr SSLAead
  * function to NULL to use PR_Now().*/
 typedef PRTime(PR_CALLBACK *SSLTimeFunc)(void *arg);
 
 #define SSL_SetTimeFunc(fd, f, arg)                                      \
     SSL_EXPERIMENTAL_API("SSL_SetTimeFunc",                              \
                          (PRFileDesc * _fd, SSLTimeFunc _f, void *_arg), \
                          (fd, f, arg))
 
+/* Create a delegated credential (DC) for the draft-ietf-tls-subcerts extension
+ * using the given certificate |cert| and its signing key |certPriv| and write
+ * the serialized DC to |out|. The
+ * parameters are:
+ *  - the DC public key |dcPub|;
+ *  - the DC signature scheme |dcCertVerifyAlg|, used to verify the handshake.
+ *  - the DC time-to-live |dcValidFor|, the number of seconds from now for which
+ *    the DC should be valid; and
+ *  - the current time |now|.
+ *
+ *  The signing algorithm used to verify the DC signature is deduced from
+ *  |cert|.
+ *
+ *  It's the caller's responsibility to ensure the input parameters are all
+ *  valid. This procedure is meant primarily for testing; for this purpose it is
+ *  useful to do no validation.
+ */
+#define SSL_DelegateCredential(cert, certPriv, dcPub, dcCertVerifyAlg,        \
+                               dcValidFor, now, out)                          \
+    SSL_EXPERIMENTAL_API("SSL_DelegateCredential",                            \
+                         (const CERTCertificate *_cert,                       \
+                          const SECKEYPrivateKey *_certPriv,                  \
+                          const SECKEYPublicKey *_dcPub,                      \
+                          SSLSignatureScheme _dcCertVerifyAlg,                \
+                          PRUint32 _dcValidFor,                               \
+                          PRTime _now,                                        \
+                          SECItem *_out),                                     \
+                         (cert, certPriv, dcPub, dcCertVerifyAlg, dcValidFor, \
+                          now, out))
+
 /* Deprecated experimental APIs */
 #define SSL_UseAltServerHelloType(fd, enable) SSL_DEPRECATED_EXPERIMENTAL_API
 #define SSL_SetupAntiReplay(a, b, c) SSL_DEPRECATED_EXPERIMENTAL_API
 #define SSL_InitAntiReplay(a, b, c) SSL_DEPRECATED_EXPERIMENTAL_API
 
 SEC_END_PROTOS
 
 #endif /* __sslexp_h_ */
--- a/lib/ssl/sslimpl.h
+++ b/lib/ssl/sslimpl.h
@@ -32,16 +32,17 @@
 #include "prclist.h"
 #include "private/pprthred.h"
 
 #include "sslt.h" /* for some formerly private types, now public */
 
 typedef struct sslSocketStr sslSocket;
 typedef struct sslNamedGroupDefStr sslNamedGroupDef;
 typedef struct sslEsniKeysStr sslEsniKeys;
+typedef struct sslDelegatedCredentialStr sslDelegatedCredential;
 typedef struct sslEphemeralKeyPairStr sslEphemeralKeyPair;
 typedef struct TLS13KeyShareEntryStr TLS13KeyShareEntry;
 
 #include "sslencode.h"
 #include "sslexp.h"
 #include "ssl3ext.h"
 #include "sslspec.h"
 
@@ -274,16 +275,17 @@ typedef struct sslOptionsStr {
     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;
+    unsigned int enableDelegatedCredentials : 1;
 } sslOptions;
 
 typedef enum { sslHandshakingUndetermined = 0,
                sslHandshakingAsClient,
                sslHandshakingAsServer
 } sslHandshakingType;
 
 #define SSL_LOCK_RANK_SPEC 255
@@ -1460,16 +1462,21 @@ extern sslEphemeralKeyPair *ssl_LookupEp
     sslSocket *ss, const sslNamedGroupDef *groupDef);
 extern PRBool ssl_HaveEphemeralKeyPair(const sslSocket *ss,
                                        const sslNamedGroupDef *groupDef);
 extern void ssl_FreeEphemeralKeyPairs(sslSocket *ss);
 
 extern SECStatus ssl_AppendPaddedDHKeyShare(sslBuffer *buf,
                                             const SECKEYPublicKey *pubKey,
                                             PRBool appendLength);
+extern PRBool ssl_CanUseSignatureScheme(SSLSignatureScheme scheme,
+                                        const SSLSignatureScheme *peerSchemes,
+                                        unsigned int peerSchemeCount,
+                                        PRBool requireSha1,
+                                        PRBool slotDoesPss);
 extern const ssl3DHParams *ssl_GetDHEParams(const sslNamedGroupDef *groupDef);
 extern SECStatus ssl_SelectDHEGroup(sslSocket *ss,
                                     const sslNamedGroupDef **groupDef);
 extern SECStatus ssl_CreateDHEKeyPair(const sslNamedGroupDef *groupDef,
                                       const ssl3DHParams *params,
                                       sslEphemeralKeyPair **keyPair);
 extern PRBool ssl_IsValidDHEShare(const SECItem *dh_p, const SECItem *dh_Ys);
 extern SECStatus ssl_ValidateDHENamedGroup(sslSocket *ss,
@@ -1556,28 +1563,43 @@ extern SECStatus ssl3_ConsumeHandshakeNu
                                              PRUint32 bytes, PRUint8 **b,
                                              PRUint32 *length);
 extern SECStatus ssl3_ConsumeHandshakeNumber64(sslSocket *ss, PRUint64 *num,
                                                PRUint32 bytes, PRUint8 **b,
                                                PRUint32 *length);
 extern SECStatus ssl3_ConsumeHandshakeVariable(sslSocket *ss, SECItem *i,
                                                PRUint32 bytes, PRUint8 **b,
                                                PRUint32 *length);
+extern SECStatus ssl_SignatureSchemeFromSpki(const CERTSubjectPublicKeyInfo *spki,
+                                             PRBool isTls13,
+                                             SSLSignatureScheme *scheme);
+extern PRBool ssl_SignatureSchemeEnabled(const sslSocket *ss,
+                                         SSLSignatureScheme scheme);
 extern PRBool ssl_IsSupportedSignatureScheme(SSLSignatureScheme scheme);
 extern SECStatus ssl_CheckSignatureSchemeConsistency(
-    sslSocket *ss, SSLSignatureScheme scheme, CERTCertificate *cert);
+    sslSocket *ss, SSLSignatureScheme scheme, CERTSubjectPublicKeyInfo *spki);
 extern SECStatus ssl_ParseSignatureSchemes(const sslSocket *ss, PLArenaPool *arena,
                                            SSLSignatureScheme **schemesOut,
                                            unsigned int *numSchemesOut,
                                            unsigned char **b,
                                            unsigned int *len);
 extern SECStatus ssl_ConsumeSignatureScheme(
     sslSocket *ss, PRUint8 **b, PRUint32 *length, SSLSignatureScheme *out);
+extern SECStatus ssl3_SignHashesWithPrivKey(SSL3Hashes *hash,
+                                            SECKEYPrivateKey *key,
+                                            SSLSignatureScheme scheme,
+                                            PRBool isTls,
+                                            SECItem *buf);
 extern SECStatus ssl3_SignHashes(sslSocket *ss, SSL3Hashes *hash,
                                  SECKEYPrivateKey *key, SECItem *buf);
+extern SECStatus ssl3_VerifySignedHashesWithSpki(sslSocket *ss,
+                                                 CERTSubjectPublicKeyInfo *spki,
+                                                 SSLSignatureScheme scheme,
+                                                 SSL3Hashes *hash,
+                                                 SECItem *buf);
 extern SECStatus ssl3_VerifySignedHashes(sslSocket *ss, SSLSignatureScheme scheme,
                                          SSL3Hashes *hash, SECItem *buf);
 extern SECStatus ssl3_CacheWrappedSecret(sslSocket *ss, sslSessionID *sid,
                                          PK11SymKey *secret);
 extern void ssl3_FreeSniNameArray(TLSExtensionData *xtnData);
 
 /* Hello Extension related routines. */
 extern void ssl3_SetSIDSessionTicket(sslSessionID *sid,
@@ -1691,16 +1713,18 @@ SECStatus ssl3_FillInCachedSID(sslSocket
                                PK11SymKey *secret);
 const ssl3CipherSuiteDef *ssl_LookupCipherSuiteDef(ssl3CipherSuite suite);
 const ssl3CipherSuiteCfg *ssl_LookupCipherSuiteCfg(ssl3CipherSuite suite,
                                                    const ssl3CipherSuiteCfg *suites);
 PRBool ssl3_CipherSuiteAllowedForVersionRange(ssl3CipherSuite cipherSuite,
                                               const SSLVersionRange *vrange);
 
 SECStatus ssl3_SelectServerCert(sslSocket *ss);
+SECStatus ssl_PrivateKeySupportsRsaPss(SECKEYPrivateKey *privKey,
+                                       PRBool *supportsRsaPss);
 SECStatus ssl_PickSignatureScheme(sslSocket *ss,
                                   CERTCertificate *cert,
                                   SECKEYPublicKey *pubKey,
                                   SECKEYPrivateKey *privKey,
                                   const SSLSignatureScheme *peerSchemes,
                                   unsigned int peerSchemeCount,
                                   PRBool requireSha1);
 SECOidTag ssl3_HashTypeToOID(SSLHashType hashType);
--- a/lib/ssl/sslinfo.c
+++ b/lib/ssl/sslinfo.c
@@ -2,16 +2,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 "pk11pub.h"
 #include "ssl.h"
 #include "sslimpl.h"
 #include "sslproto.h"
 #include "tls13hkdf.h"
+#include "tls13subcerts.h"
 
 SECStatus
 SSL_GetChannelInfo(PRFileDesc *fd, SSLChannelInfo *info, PRUintn len)
 {
     sslSocket *ss;
     SSLChannelInfo inf;
     sslSessionID *sid;
 
@@ -74,16 +75,17 @@ SSL_GetChannelInfo(PRFileDesc *fd, SSLCh
         inf.authKeyBits = ss->sec.authKeyBits;
         inf.signatureScheme = ss->sec.signatureScheme;
         /* If this is a resumed session, signatureScheme isn't set in ss->sec.
          * Use the signature scheme from the previous handshake. */
         if (inf.signatureScheme == ssl_sig_none && sid->sigScheme) {
             inf.signatureScheme = sid->sigScheme;
         }
         inf.resumed = ss->statelessResume || ss->ssl3.hs.isResuming;
+        inf.peerDelegCred = tls13_IsVerifyingWithDelegatedCredential(ss);
 
         if (sid) {
             unsigned int sidLen;
 
             inf.creationTime = sid->creationTime / PR_USEC_PER_SEC;
             inf.lastAccessTime = sid->lastAccessTime / PR_USEC_PER_SEC;
             inf.expirationTime = sid->expirationTime / PR_USEC_PER_SEC;
             inf.extendedMasterSecretUsed =
--- a/lib/ssl/sslsock.c
+++ b/lib/ssl/sslsock.c
@@ -15,16 +15,17 @@
 #include "sslimpl.h"
 #include "sslproto.h"
 #include "nspr.h"
 #include "private/pprio.h"
 #include "nss.h"
 #include "pk11pqg.h"
 #include "pk11pub.h"
 #include "tls13esni.h"
+#include "tls13subcerts.h"
 
 static const sslSocketOps ssl_default_ops = { /* No SSL. */
                                               ssl_DefConnect,
                                               NULL,
                                               ssl_DefBind,
                                               ssl_DefListen,
                                               ssl_DefShutdown,
                                               ssl_DefClose,
@@ -70,16 +71,17 @@ static sslOptions ssl_defaults = {
     .noLocks = PR_FALSE,
     .enableSessionTickets = PR_FALSE,
     .enableDeflate = PR_FALSE,
     .enableRenegotiation = SSL_RENEGOTIATE_REQUIRES_XTN,
     .requireSafeNegotiation = PR_FALSE,
     .enableFalseStart = PR_FALSE,
     .cbcRandomIV = PR_TRUE,
     .enableOCSPStapling = PR_FALSE,
+    .enableDelegatedCredentials = PR_FALSE,
     .enableALPN = PR_TRUE,
     .reuseServerECDHEKey = PR_TRUE,
     .enableFallbackSCSV = PR_FALSE,
     .enableServerDhe = PR_TRUE,
     .enableExtendedMS = PR_FALSE,
     .enableSignedCertTimestamps = PR_FALSE,
     .requireDHENamedGroups = PR_FALSE,
     .enable0RttData = PR_FALSE,
@@ -788,16 +790,20 @@ SSL_OptionSet(PRFileDesc *fd, PRInt32 wh
         case SSL_CBC_RANDOM_IV:
             ss->opt.cbcRandomIV = val;
             break;
 
         case SSL_ENABLE_OCSP_STAPLING:
             ss->opt.enableOCSPStapling = val;
             break;
 
+        case SSL_ENABLE_DELEGATED_CREDENTIALS:
+            ss->opt.enableDelegatedCredentials = val;
+            break;
+
         case SSL_ENABLE_NPN:
             break;
 
         case SSL_ENABLE_ALPN:
             ss->opt.enableALPN = val;
             break;
 
         case SSL_REUSE_SERVER_ECDHE_KEY:
@@ -958,16 +964,19 @@ SSL_OptionGet(PRFileDesc *fd, PRInt32 wh
             val = ss->opt.enableFalseStart;
             break;
         case SSL_CBC_RANDOM_IV:
             val = ss->opt.cbcRandomIV;
             break;
         case SSL_ENABLE_OCSP_STAPLING:
             val = ss->opt.enableOCSPStapling;
             break;
+        case SSL_ENABLE_DELEGATED_CREDENTIALS:
+            val = ss->opt.enableDelegatedCredentials;
+            break;
         case SSL_ENABLE_NPN:
             val = PR_FALSE;
             break;
         case SSL_ENABLE_ALPN:
             val = ss->opt.enableALPN;
             break;
         case SSL_REUSE_SERVER_ECDHE_KEY:
             val = ss->opt.reuseServerECDHEKey;
@@ -1096,16 +1105,19 @@ SSL_OptionGetDefault(PRInt32 which, PRIn
             val = ssl_defaults.enableFalseStart;
             break;
         case SSL_CBC_RANDOM_IV:
             val = ssl_defaults.cbcRandomIV;
             break;
         case SSL_ENABLE_OCSP_STAPLING:
             val = ssl_defaults.enableOCSPStapling;
             break;
+        case SSL_ENABLE_DELEGATED_CREDENTIALS:
+            val = ssl_defaults.enableDelegatedCredentials;
+            break;
         case SSL_ENABLE_NPN:
             val = PR_FALSE;
             break;
         case SSL_ENABLE_ALPN:
             val = ssl_defaults.enableALPN;
             break;
         case SSL_REUSE_SERVER_ECDHE_KEY:
             val = ssl_defaults.reuseServerECDHEKey;
@@ -1286,16 +1298,20 @@ SSL_OptionSetDefault(PRInt32 which, PRIn
         case SSL_CBC_RANDOM_IV:
             ssl_defaults.cbcRandomIV = val;
             break;
 
         case SSL_ENABLE_OCSP_STAPLING:
             ssl_defaults.enableOCSPStapling = val;
             break;
 
+        case SSL_ENABLE_DELEGATED_CREDENTIALS:
+            ssl_defaults.enableDelegatedCredentials = val;
+            break;
+
         case SSL_ENABLE_NPN:
             break;
 
         case SSL_ENABLE_ALPN:
             ssl_defaults.enableALPN = val;
             break;
 
         case SSL_REUSE_SERVER_ECDHE_KEY:
@@ -4084,16 +4100,17 @@ SSL_CanBypass(CERTCertificate *cert, SEC
 struct {
     const char *const name;
     void *function;
 } ssl_experimental_functions[] = {
 #ifndef SSL_DISABLE_EXPERIMENTAL_API
     EXP(AeadDecrypt),
     EXP(AeadEncrypt),
     EXP(CreateAntiReplayContext),
+    EXP(DelegateCredential),
     EXP(DestroyAead),
     EXP(DestroyResumptionTokenInfo),
     EXP(EnableESNI),
     EXP(EncodeESNIKeys),
     EXP(GetCurrentEpoch),
     EXP(GetExtensionSupport),
     EXP(GetResumptionTokenInfo),
     EXP(HelloRetryRequestCallback),
--- a/lib/ssl/sslt.h
+++ b/lib/ssl/sslt.h
@@ -4,19 +4,20 @@
  *
  * 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/. */
 
 #ifndef __sslt_h_
 #define __sslt_h_
 
+#include "certt.h"
+#include "keyhi.h"
 #include "prtypes.h"
 #include "secitem.h"
-#include "certt.h"
 
 typedef enum {
     ssl_hs_hello_request = 0,
     ssl_hs_client_hello = 1,
     ssl_hs_server_hello = 2,
     ssl_hs_hello_verify_request = 3,
     ssl_hs_new_session_ticket = 4,
     ssl_hs_end_of_early_data = 5,
@@ -262,16 +263,36 @@ typedef struct SSLExtraServerCertDataStr
     /* The remainder of the certificate chain. */
     const CERTCertificateList* certChain;
     /* A set of one or more stapled OCSP responses for the certificate.  This is
      * used to generate the OCSP stapling answer provided by the server. */
     const SECItemArray* stapledOCSPResponses;
     /* A serialized sign_certificate_timestamp extension, used to answer
      * requests from clients for this data. */
     const SECItem* signedCertTimestamps;
+
+    /* Delegated credentials.
+     *
+     * A serialized delegated credential (DC) to use for authentication to peers
+     * who indicate support for this extension (ietf-drafts-tls-subcerts). DCs
+     * are used opportunistically if (1) the client indicates support, (2) TLS
+     * 1.3 or higher is negotiated, and (3) the selected certificate is
+     * configured with a DC.
+     *
+     * Note that it's the caller's responsibility to ensure that the DC is
+     * well-formed.
+     */
+    const SECItem* delegCred;
+
+    /* The secret key corresponding to the |delegCred|.
+     *
+     * Note that it's the caller's responsibility to ensure that this matches
+     * the DC public key.
+     */
+    const SECKEYPrivateKey* delegCredPrivKey;
 } SSLExtraServerCertData;
 
 typedef struct SSLChannelInfoStr {
     /* On return, SSL_GetChannelInfo sets |length| to the smaller of
      * the |len| argument and the length of the struct used by NSS.
      * Callers must ensure the application uses a version of NSS that
      * isn't older than the version used at compile time. */
     PRUint32 length;
@@ -321,16 +342,21 @@ typedef struct SSLChannelInfoStr {
     /* The following fields were added in NSS 3.34. */
     /* When the session was resumed this holds the key exchange group of the
      * original handshake. */
     SSLNamedGroup originalKeaGroup;
     /* This field is PR_TRUE when the session is resumed and PR_FALSE
      * otherwise. */
     PRBool resumed;
 
+    /* Indicates whether the peer used a delegated credential (DC) for
+     * authentication.
+     */
+    PRBool peerDelegCred;
+
     /* When adding new fields to this structure, please document the
      * NSS version in which they were added. */
 } SSLChannelInfo;
 
 /* Preliminary channel info */
 #define ssl_preinfo_version (1U << 0)
 #define ssl_preinfo_cipher_suite (1U << 1)
 #define ssl_preinfo_0rtt_cipher_suite (1U << 2)
@@ -471,16 +497,17 @@ typedef enum {
     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_delegated_credentials_xtn = 0xff02,
     ssl_tls13_short_header_xtn = 0xff03, /* Deprecated. */
     ssl_tls13_encrypted_sni_xtn = 0xffce,
 } SSLExtensionType;
 
 /* This is the old name for the supported_groups extensions. */
 #define ssl_elliptic_curves_xtn ssl_supported_groups_xtn
 
 /* SSL_MAX_EXTENSIONS includes the maximum number of extensions that are
--- a/lib/ssl/tls13con.c
+++ b/lib/ssl/tls13con.c
@@ -19,16 +19,17 @@
 #include "sslerr.h"
 #include "ssl3exthandle.h"
 #include "tls13hkdf.h"
 #include "tls13con.h"
 #include "tls13err.h"
 #include "tls13esni.h"
 #include "tls13exthandle.h"
 #include "tls13hashstate.h"
+#include "tls13subcerts.h"
 
 static SECStatus tls13_SetCipherSpec(sslSocket *ss, PRUint16 epoch,
                                      SSLSecretDirection install,
                                      PRBool deleteSecret);
 static SECStatus tls13_AESGCM(const ssl3KeyMaterial *keys,
                               PRBool doDecrypt,
                               unsigned char *out,
                               unsigned int *outlen,
@@ -1584,16 +1585,30 @@ tls13_SelectServerCert(sslSocket *ss)
                                      cert->serverKeyPair->pubKey,
                                      cert->serverKeyPair->privKey,
                                      ss->xtnData.sigSchemes,
                                      ss->xtnData.numSigSchemes,
                                      PR_FALSE);
         if (rv == SECSuccess) {
             /* Found one. */
             ss->sec.serverCert = cert;
+
+            /* If we can use a delegated credential (DC) for authentication in
+             * the current handshake, then commit to using it now. We'll send a
+             * DC as an extension and use the DC private key to sign the
+             * handshake.
+             *
+             * This sets the signature scheme to be the signature scheme
+             * indicated by the DC.
+             */
+            rv = tls13_MaybeSetDelegatedCredential(ss);
+            if (rv != SECSuccess) {
+                return SECFailure; /* Failure indicates an internal error. */
+            }
+
             ss->sec.authType = ss->ssl3.hs.kea_def_mutable.authKeyType =
                 ssl_SignatureSchemeToAuthType(ss->ssl3.hs.signatureScheme);
             ss->sec.authKeyBits = cert->serverKeyBits;
             return SECSuccess;
         }
     }
 
     FATAL_ERROR(ss, SSL_ERROR_UNSUPPORTED_SIGNATURE_ALGORITHM,
@@ -2615,17 +2630,24 @@ tls13_SendEncryptedServerSequence(sslSoc
     if (ss->ssl3.hs.signatureScheme != ssl_sig_none) {
         SECKEYPrivateKey *svrPrivKey;
 
         rv = tls13_SendCertificate(ss);
         if (rv != SECSuccess) {
             return SECFailure; /* error code is set. */
         }
 
-        svrPrivKey = ss->sec.serverCert->serverKeyPair->privKey;
+        if (tls13_IsSigningWithDelegatedCredential(ss)) {
+            SSL_TRC(3, ("%d: TLS13[%d]: Signing with delegated credential",
+                        SSL_GETPID(), ss->fd));
+            svrPrivKey = ss->sec.serverCert->delegCredKeyPair->privKey;
+        } else {
+            svrPrivKey = ss->sec.serverCert->serverKeyPair->privKey;
+        }
+
         rv = tls13_SendCertificateVerify(ss, svrPrivKey);
         if (rv != SECSuccess) {
             return SECFailure; /* err code is set. */
         }
     }
 
     rv = tls13_SendFinished(ss, ss->ssl3.hs.serverHsTrafficSecret);
     if (rv != SECSuccess) {
@@ -4115,16 +4137,18 @@ done:
 
 /* Called from tls13_CompleteHandleHandshakeMessage() when it has deciphered a complete
  * tls13 CertificateVerify message
  * Caller must hold Handshake and RecvBuf locks.
  */
 SECStatus
 tls13_HandleCertificateVerify(sslSocket *ss, PRUint8 *b, PRUint32 length)
 {
+    sslDelegatedCredential *dc = ss->xtnData.peerDelegCred;
+    CERTSubjectPublicKeyInfo *spki = NULL;
     SECItem signed_hash = { siBuffer, NULL, 0 };
     SECStatus rv;
     SSLSignatureScheme sigScheme;
     SSLHashType hashAlg;
     SSL3Hashes tbsHash;
     SSL3Hashes hashes;
 
     SSL_TRC(3, ("%d: TLS13[%d]: handle certificate_verify handshake",
@@ -4154,17 +4178,48 @@ tls13_HandleCertificateVerify(sslSocket 
     }
 
     rv = ssl_ConsumeSignatureScheme(ss, &b, &length, &sigScheme);
     if (rv != SECSuccess) {
         FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_CERT_VERIFY, illegal_parameter);
         return SECFailure;
     }
 
-    rv = ssl_CheckSignatureSchemeConsistency(ss, sigScheme, ss->sec.peerCert);
+    /* Set the |spki| used to verify the handshake. When verifying with a
+     * delegated credential (DC), this corresponds to the DC public key;
+     * otherwise it correspond to the public key of the peer's end-entity
+     * certificate.
+     */
+    if (tls13_IsVerifyingWithDelegatedCredential(ss)) {
+        /* DelegatedCredential.cred.expected_cert_verify_algorithm is expected
+         * to match CertificateVerify.scheme.
+         */
+        if (sigScheme != dc->expectedCertVerifyAlg) {
+            FATAL_ERROR(ss, SSL_ERROR_DC_CERT_VERIFY_ALG_MISMATCH, illegal_parameter);
+            return SECFailure;
+        }
+
+        /* Verify the DC has three steps: (1) use the peer's end-entity
+         * certificate to verify DelegatedCredential.signature, (2) check that
+         * the certificate has the correct key usage, and (3) check that the DC
+         * hasn't expired.
+         */
+        rv = tls13_VerifyDelegatedCredential(ss, dc);
+        if (rv != SECSuccess) { /* Calls FATAL_ERROR() */
+            return SECFailure;
+        }
+
+        SSL_TRC(3, ("%d: TLS13[%d]: Verifying with delegated credential",
+                    SSL_GETPID(), ss->fd));
+        spki = dc->spki;
+    } else {
+        spki = &ss->sec.peerCert->subjectPublicKeyInfo;
+    }
+
+    rv = ssl_CheckSignatureSchemeConsistency(ss, sigScheme, spki);
     if (rv != SECSuccess) {
         /* Error set already */
         FATAL_ERROR(ss, PORT_GetError(), illegal_parameter);
         return SECFailure;
     }
     hashAlg = ssl_SignatureSchemeToHashType(sigScheme);
 
     rv = tls13_AddContextToHashes(ss, &hashes, hashAlg, PR_FALSE, &tbsHash);
@@ -4179,17 +4234,18 @@ tls13_HandleCertificateVerify(sslSocket 
         return SECFailure;
     }
 
     if (length != 0) {
         FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_CERT_VERIFY, decode_error);
         return SECFailure;
     }
 
-    rv = ssl3_VerifySignedHashes(ss, sigScheme, &tbsHash, &signed_hash);
+    rv = ssl3_VerifySignedHashesWithSpki(
+        ss, spki, sigScheme, &tbsHash, &signed_hash);
     if (rv != SECSuccess) {
         FATAL_ERROR(ss, PORT_GetError(), decrypt_error);
         return SECFailure;
     }
 
     /* Set the auth type. */
     if (!ss->sec.isServer) {
         ss->sec.authType = ssl_SignatureSchemeToAuthType(sigScheme);
@@ -5133,16 +5189,17 @@ static const struct {
     { ssl_tls13_pre_shared_key_xtn, _M2(client_hello, server_hello) },
     { ssl_tls13_psk_key_exchange_modes_xtn, _M1(client_hello) },
     { ssl_tls13_early_data_xtn, _M3(client_hello, encrypted_extensions,
                                     new_session_ticket) },
     { ssl_signed_cert_timestamp_xtn, _M3(client_hello, certificate_request,
                                          certificate) },
     { ssl_cert_status_xtn, _M3(client_hello, certificate_request,
                                certificate) },
+    { ssl_delegated_credentials_xtn, _M2(client_hello, 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_post_handshake_auth_xtn, _M1(client_hello) }
 };
--- a/lib/ssl/tls13exthandle.c
+++ b/lib/ssl/tls13exthandle.c
@@ -9,16 +9,17 @@
 #include "ssl.h"
 #include "sslproto.h"
 #include "sslimpl.h"
 #include "pk11pub.h"
 #include "ssl3ext.h"
 #include "ssl3exthandle.h"
 #include "tls13esni.h"
 #include "tls13exthandle.h"
+#include "tls13subcerts.h"
 
 SECStatus
 tls13_ServerSendStatusRequestXtn(const sslSocket *ss, TLSExtensionData *xtnData,
                                  sslBuffer *buf, PRBool *added)
 {
     const sslServerCert *serverCert = ss->sec.serverCert;
     const SECItem *item;
     SECStatus rv;
@@ -1395,8 +1396,99 @@ tls13_ClientCheckEsniXtn(sslSocket *ss)
                               ss->xtnData.esniNonce,
                               sizeof(ss->xtnData.esniNonce))) {
         FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_ESNI_EXTENSION, illegal_parameter);
         return SECFailure;
     }
 
     return SECSuccess;
 }
+
+/* Indicates support for the delegated credentials extension. This should be
+ * hooked while processing the ClientHello.
+ */
+SECStatus
+tls13_ClientSendDelegatedCredentialsXtn(const sslSocket *ss,
+                                        TLSExtensionData *xtnData,
+                                        sslBuffer *buf, PRBool *added)
+{
+    /* Only send the extension if support is enabled and the client can
+     * negotiate TLS 1.3.
+     */
+    if (ss->vrange.max < SSL_LIBRARY_VERSION_TLS_1_3 ||
+        !ss->opt.enableDelegatedCredentials) {
+        return SECSuccess;
+    }
+
+    *added = PR_TRUE;
+    return SECSuccess;
+}
+
+/* Parses the delegated credential (DC) offered by the server. This should be
+ * hooked while processing the server's CertificateVerify.
+ *
+ * Only the DC sent with the end-entity certificate is to be parsed. This is
+ * ensured by |tls13_HandleCertificateEntry|, which only processes extensions
+ * for the first certificate in the chain.
+ */
+SECStatus
+tls13_ClientHandleDelegatedCredentialsXtn(const sslSocket *ss,
+                                          TLSExtensionData *xtnData,
+                                          SECItem *data)
+{
+    if (!ss->opt.enableDelegatedCredentials ||
+        ss->version < SSL_LIBRARY_VERSION_TLS_1_3) {
+        ssl3_ExtSendAlert(ss, alert_fatal, illegal_parameter);
+        PORT_SetError(SSL_ERROR_RX_UNEXPECTED_EXTENSION);
+        return SECFailure;
+    }
+
+    SECStatus rv = tls13_ReadDelegatedCredential(data->data, data->len,
+                                                 &xtnData->peerDelegCred);
+    if (rv != SECSuccess) {
+        return SECFailure; /* code already set */
+    }
+
+    xtnData->negotiated[xtnData->numNegotiated++] =
+        ssl_delegated_credentials_xtn;
+    return SECSuccess;
+}
+
+/* Adds the DC extension if we're committed to authenticating with a DC. */
+static SECStatus
+tls13_ServerSendDelegatedCredentialsXtn(const sslSocket *ss,
+                                        TLSExtensionData *xtnData,
+                                        sslBuffer *buf, PRBool *added)
+{
+    if (tls13_IsSigningWithDelegatedCredential(ss)) {
+        const SECItem *dc = &ss->sec.serverCert->delegCred;
+
+        if (tls13_IsSigningWithDelegatedCredential(ss)) {
+            SECStatus rv;
+            rv = sslBuffer_Append(buf, dc->data, dc->len);
+            if (rv != SECSuccess) {
+                return SECFailure;
+            }
+        }
+
+        *added = PR_TRUE;
+        return SECSuccess;
+    }
+
+    return SECSuccess;
+}
+
+/* The client has indicated support of DCs. We can't act on this information
+ * until we've committed to signing with a DC, so just set a callback for
+ * sending the DC extension later. */
+SECStatus
+tls13_ServerHandleDelegatedCredentialsXtn(const sslSocket *ss,
+                                          TLSExtensionData *xtnData,
+                                          SECItem *data)
+{
+    xtnData->peerRequestedDelegCred = PR_TRUE;
+    xtnData->negotiated[xtnData->numNegotiated++] =
+        ssl_delegated_credentials_xtn;
+
+    return ssl3_RegisterExtensionSender(
+        ss, xtnData, ssl_delegated_credentials_xtn,
+        tls13_ServerSendDelegatedCredentialsXtn);
+}
--- a/lib/ssl/tls13exthandle.h
+++ b/lib/ssl/tls13exthandle.h
@@ -94,10 +94,19 @@ SECStatus tls13_ServerHandleEsniXtn(cons
                                     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);
+SECStatus tls13_ClientHandleDelegatedCredentialsXtn(const sslSocket *ss,
+                                                    TLSExtensionData *xtnData,
+                                                    SECItem *data);
+SECStatus tls13_ClientSendDelegatedCredentialsXtn(const sslSocket *ss,
+                                                  TLSExtensionData *xtnData,
+                                                  sslBuffer *buf, PRBool *added);
+SECStatus tls13_ServerHandleDelegatedCredentialsXtn(const sslSocket *ss,
+                                                    TLSExtensionData *xtnData,
+                                                    SECItem *data);
 
 #endif
new file mode 100644
--- /dev/null
+++ b/lib/ssl/tls13subcerts.c
@@ -0,0 +1,594 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * 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 "pk11func.h"
+#include "secder.h"
+#include "ssl.h"
+#include "sslproto.h"
+#include "sslimpl.h"
+#include "ssl3exthandle.h"
+#include "tls13exthandle.h"
+#include "tls13hkdf.h"
+#include "tls13subcerts.h"
+
+/* Parses the delegated credential (DC) from the raw extension |b| of length
+ * |length|. Memory for the DC is allocated and set to |*dcp|.
+ *
+ * It's the caller's responsibility to invoke |tls13_DestroyDelegatedCredential|
+ * when this data is no longer needed.
+ */
+SECStatus
+tls13_ReadDelegatedCredential(PRUint8 *b, PRUint32 length,
+                              sslDelegatedCredential **dcp)
+{
+    sslDelegatedCredential *dc = NULL;
+    SECStatus rv;
+    PRUint64 n;
+    sslReadBuffer tmp;
+    sslReader rdr = SSL_READER(b, length);
+
+    PORT_Assert(!*dcp);
+
+    dc = PORT_ZNew(sslDelegatedCredential);
+    if (!dc) {
+        PORT_SetError(SEC_ERROR_NO_MEMORY);
+        goto loser;
+    }
+
+    /* Read the valid_time field of DelegatedCredential.cred. */
+    rv = sslRead_ReadNumber(&rdr, 4, &n);
+    if (rv != SECSuccess) {
+        goto loser;
+    }
+    dc->validTime = n;
+
+    /* Read the expected_cert_verify_algorithm field of
+     * DelegatedCredential.cred. */
+    rv = sslRead_ReadNumber(&rdr, 2, &n);
+    if (rv != SECSuccess) {
+        goto loser;
+    }
+    dc->expectedCertVerifyAlg = n;
+
+    /* Read the ASN1_subjectPublicKeyInfo field of DelegatedCredential.cred. */
+    rv = sslRead_ReadVariable(&rdr, 3, &tmp);
+    if (rv != SECSuccess) {
+        goto loser;
+    }
+    rv = SECITEM_MakeItem(NULL, &dc->derSpki, tmp.buf, tmp.len);
+    if (rv != SECSuccess) {
+        goto loser;
+    }
+
+    /* Parse the DER-encoded SubjectPublicKeyInfo. */
+    dc->spki = SECKEY_DecodeDERSubjectPublicKeyInfo(&dc->derSpki);
+    if (!dc->spki) {
+        goto loser;
+    }
+
+    /* Read the algorithm field of the DelegatedCredential. */
+    rv = sslRead_ReadNumber(&rdr, 2, &n);
+    if (rv != SECSuccess) {
+        goto loser;
+    }
+    dc->alg = n;
+
+    /* Read the signature field of the DelegatedCredential. */
+    rv = sslRead_ReadVariable(&rdr, 2, &tmp);
+    if (rv != SECSuccess) {
+        goto loser;
+    }
+    rv = SECITEM_MakeItem(NULL, &dc->signature, tmp.buf, tmp.len);
+    if (rv != SECSuccess) {
+        goto loser;
+    }
+
+    /* There should be nothing left to read. */
+    if (SSL_READER_REMAINING(&rdr) > 0) {
+        goto loser;
+    }
+
+    *dcp = dc;
+    return SECSuccess;
+
+loser:
+    tls13_DestroyDelegatedCredential(dc);
+    *dcp = NULL;
+    return SECFailure;
+}
+
+/* Frees |dc| from the heap. */
+void
+tls13_DestroyDelegatedCredential(sslDelegatedCredential *dc)
+{
+    if (!dc) {
+        return;
+    }
+
+    SECKEY_DestroySubjectPublicKeyInfo(dc->spki);
+    SECITEM_FreeItem(&dc->derSpki, PR_FALSE);
+    SECITEM_FreeItem(&dc->signature, PR_FALSE);
+    PORT_ZFree(dc, sizeof(sslDelegatedCredential));
+}
+
+/* Sets |*certVerifyAlg| to the expected_cert_verify_algorithm field from the
+ * serialized DC |in|. Returns SECSuccess upon success; SECFailure indicates a
+ * decoding failure or the input wasn't long enough.
+ */
+static SECStatus
+tls13_GetExpectedCertVerifyAlg(SECItem in, SSLSignatureScheme *certVerifyAlg)
+{
+    SECStatus rv;
+    PRUint64 n;
+    sslReader rdr = SSL_READER(in.data, in.len);
+
+    if (in.len < 6) { /* Buffer too short to contain the first two params. */
+        return SECFailure;
+    }
+
+    rv = sslRead_ReadNumber(&rdr, 4, &n);
+    if (rv != SECSuccess) {
+        return SECFailure;
+    }
+
+    rv = sslRead_ReadNumber(&rdr, 2, &n);
+    if (rv != SECSuccess) {
+        return SECFailure;
+    }
+    *certVerifyAlg = n;
+
+    return SECSuccess;
+}
+
+/* Returns PR_TRUE if the host is verifying the handshake with a DC. */
+PRBool
+tls13_IsVerifyingWithDelegatedCredential(const sslSocket *ss)
+{
+    /* As of draft-ietf-subcerts-03, only the server may authenticate itself
+     * with a DC.
+     */
+    if (ss->sec.isServer ||
+        !ss->opt.enableDelegatedCredentials ||
+        !ss->xtnData.peerDelegCred) {
+        return PR_FALSE;
+    }
+
+    return PR_TRUE;
+}
+
+/* Returns PR_TRUE if the host is signing the handshake with a DC. */
+PRBool
+tls13_IsSigningWithDelegatedCredential(const sslSocket *ss)
+{
+    if (!ss->sec.isServer ||
+        !ss->xtnData.sendingDelegCredToPeer ||
+        !ss->xtnData.peerRequestedDelegCred) {
+        return PR_FALSE;
+    }
+
+    return PR_TRUE;
+}
+
+/* Commits to authenticating with a DC if all of the following conditions hold:
+ *  - the negotiated protocol is TLS 1.3 or newer;
+ *  - the selected certificate has a DC configured;
+ *  - the peer has indicated support for this extension;
+ *  - the peer has indicated support for the DC signature scheme; and
+ *  - the host supports the DC signature scheme.
+ *
+ * It's the caller's responsibility to ensure that the version has been
+ * negotiated and the certificate has been selected.
+ */
+SECStatus
+tls13_MaybeSetDelegatedCredential(sslSocket *ss)
+{
+    SECStatus rv;
+    PRBool doesRsaPss;
+    SECKEYPrivateKey *priv;
+    SSLSignatureScheme scheme;
+
+    /* Assert that the host is the server (as of draft-ietf-subcerts-03, only
+     * the server may authenticate itself with a DC), the certificate has been
+     * chosen, TLS 1.3 or higher has been negotiated, and that the set of
+     * signature schemes supported by the client is known.
+     */
+    PORT_Assert(ss->sec.isServer);
+    PORT_Assert(ss->sec.serverCert);
+    PORT_Assert(ss->version >= SSL_LIBRARY_VERSION_TLS_1_3);
+    PORT_Assert(ss->xtnData.sigSchemes);
+
+    /* Check that the peer has indicated support and that a DC has been
+     * configured for the selected certificate.
+     */
+    if (!ss->xtnData.peerRequestedDelegCred ||
+        !ss->sec.serverCert->delegCred.len ||
+        !ss->sec.serverCert->delegCredKeyPair) {
+        return SECSuccess;
+    }
+
+    /* Check that the host and peer both support the signing algorithm used with
+     * the DC.
+     */
+    rv = tls13_GetExpectedCertVerifyAlg(ss->sec.serverCert->delegCred,
+                                        &scheme);
+    if (rv != SECSuccess) {
+        return SECFailure;
+    }
+
+    priv = ss->sec.serverCert->delegCredKeyPair->privKey;
+    rv = ssl_PrivateKeySupportsRsaPss(priv, &doesRsaPss);
+    if (rv != SECSuccess) {
+        return SECFailure;
+    }
+
+    if (!ssl_SignatureSchemeEnabled(ss, scheme) ||
+        !ssl_CanUseSignatureScheme(scheme,
+                                   ss->xtnData.sigSchemes,
+                                   ss->xtnData.numSigSchemes,
+                                   PR_FALSE /* requireSha1 */,
+                                   doesRsaPss)) {
+        return SECSuccess;
+    }
+
+    /* Commit to sending a DC and set the handshake signature scheme to the
+     * indicated algorithm.
+     */
+    ss->xtnData.sendingDelegCredToPeer = PR_TRUE;
+    ss->ssl3.hs.signatureScheme = scheme;
+    return SECSuccess;
+}
+
+/* Serializes the DC up to the signature. */
+static SECStatus
+tls13_AppendCredentialParams(sslBuffer *buf, sslDelegatedCredential *dc)
+{
+    SECStatus rv;
+    rv = sslBuffer_AppendNumber(buf, dc->validTime, 4);
+    if (rv != SECSuccess) {
+        return SECFailure; /* Error set by caller. */
+    }
+
+    rv = sslBuffer_AppendNumber(buf, dc->expectedCertVerifyAlg, 2);
+    if (rv != SECSuccess) {
+        return SECFailure;
+    }
+
+    rv = sslBuffer_AppendVariable(buf, dc->derSpki.data, dc->derSpki.len, 3);
+    if (rv != SECSuccess) {
+        return SECFailure;
+    }
+
+    rv = sslBuffer_AppendNumber(buf, dc->alg, 2);
+    if (rv != SECSuccess) {
+        return SECFailure;
+    }
+
+    return SECSuccess;
+}
+
+/* Serializes the DC signature. */
+static SECStatus
+tls13_AppendCredentialSignature(sslBuffer *buf, sslDelegatedCredential *dc)
+{
+    SECStatus rv;
+    rv = sslBuffer_AppendVariable(buf, dc->signature.data,
+                                  dc->signature.len, 2);
+    if (rv != SECSuccess) {
+        return SECFailure;
+    }
+
+    return SECSuccess;
+}
+
+/* Hashes the message used to sign/verify the DC. */
+static SECStatus
+tls13_HashCredentialSignatureMessage(SSL3Hashes *hash,
+                                     SSLSignatureScheme scheme,
+                                     const CERTCertificate *cert,
+                                     const sslBuffer *dcBuf)
+{
+    SECStatus rv;
+    PK11Context *ctx = NULL;
+    unsigned int hashLen;
+
+    /* Set up hash context. */
+    hash->hashAlg = ssl_SignatureSchemeToHashType(scheme);
+    ctx = PK11_CreateDigestContext(ssl3_HashTypeToOID(hash->hashAlg));
+    if (!ctx) {
+        PORT_SetError(SEC_ERROR_NO_MEMORY);
+        goto loser;
+    }
+
+    static const PRUint8 kCtxStrPadding[64] = {
+        0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+        0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+        0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+        0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+        0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+        0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+        0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+        0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+    };
+
+    static const PRUint8 kCtxStr[] = "TLS, server delegated credentials";
+
+    /* Hash the message signed by the peer. */
+    rv = SECSuccess;
+    rv |= PK11_DigestBegin(ctx);
+    rv |= PK11_DigestOp(ctx, kCtxStrPadding, sizeof kCtxStrPadding);
+    rv |= PK11_DigestOp(ctx, kCtxStr, 1 /* 0-byte */ + strlen((const char *)kCtxStr));
+    rv |= PK11_DigestOp(ctx, cert->derCert.data, cert->derCert.len);
+    rv |= PK11_DigestOp(ctx, dcBuf->buf, dcBuf->len);
+    rv |= PK11_DigestFinal(ctx, hash->u.raw, &hashLen, sizeof hash->u.raw);
+    if (rv != SECSuccess) {
+        PORT_SetError(SSL_ERROR_SHA_DIGEST_FAILURE);
+        goto loser;
+    }
+
+    hash->len = hashLen;
+    if (ctx) {
+        PK11_DestroyContext(ctx, PR_TRUE);
+    }
+    return SECSuccess;
+
+loser:
+    if (ctx) {
+        PK11_DestroyContext(ctx, PR_TRUE);
+    }
+    return SECFailure;
+}
+
+/* Verifies the DC signature. */
+static SECStatus
+tls13_VerifyCredentialSignature(sslSocket *ss, sslDelegatedCredential *dc)
+{
+    SECStatus rv = SECSuccess;
+    SSL3Hashes hash;
+    sslBuffer dcBuf = SSL_BUFFER_EMPTY;
+    CERTCertificate *cert = ss->sec.peerCert;
+
+    /* Serialize the DC parameters. */
+    rv = tls13_AppendCredentialParams(&dcBuf, dc);
+    if (rv != SECSuccess) {
+        goto loser; /* Error set by caller. */
+    }
+
+    /* Hash the message that was signed by the delegator. */
+    rv = tls13_HashCredentialSignatureMessage(&hash, dc->alg, cert, &dcBuf);
+    if (rv != SECSuccess) {
+        FATAL_ERROR(ss, PORT_GetError(), internal_error);
+        goto loser;
+    }
+
+    /* Verify the signature of the message. */
+    rv = ssl3_VerifySignedHashesWithSpki(
+        ss, &cert->subjectPublicKeyInfo, dc->alg, &hash, &dc->signature);
+    if (rv != SECSuccess) {
+        FATAL_ERROR(ss, SSL_ERROR_DC_BAD_SIGNATURE, illegal_parameter);
+        goto loser;
+    }
+
+    sslBuffer_Clear(&dcBuf);
+    return SECSuccess;
+
+loser:
+    sslBuffer_Clear(&dcBuf);
+    return SECFailure;
+}
+
+/* Checks that the peer's end-entity certificate has the correct key usage. */
+static SECStatus
+tls13_CheckCertDelegationUsage(sslSocket *ss)
+{
+    int i;
+    PRBool found;
+    CERTCertExtension *ext;
+    SECItem delegUsageOid = { siBuffer, NULL, 0 };
+    const CERTCertificate *cert = ss->sec.peerCert;
+
+    /* 1.3.6.1.4.1.44363.44, as defined in draft-ietf-tls-subcerts. */
+    static unsigned char kDelegationUsageOid[] = {
+        0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0xda, 0x4b, 0x2c,
+    };
+
+    delegUsageOid.data = kDelegationUsageOid;
+    delegUsageOid.len = sizeof kDelegationUsageOid;
+
+    /* The certificate must have the delegationUsage extension that authorizes
+     * it to negotiate delegated credentials.
+     */
+    found = PR_FALSE;
+    for (i = 0; cert->extensions[i] != NULL; i++) {
+        ext = cert->extensions[i];
+        if (SECITEM_CompareItem(&ext->id, &delegUsageOid) == SECEqual) {
+            found = PR_TRUE;
+            break;
+        }
+    }
+
+    /* The certificate must also have the digitalSignature keyUsage set. */
+    if (!found ||
+        !cert->keyUsagePresent ||
+        !(cert->keyUsage & KU_DIGITAL_SIGNATURE)) {
+        FATAL_ERROR(ss, SSL_ERROR_DC_INVALID_KEY_USAGE, illegal_parameter);
+        return SECFailure;
+    }
+
+    return SECSuccess;
+}
+
+static SECStatus
+tls13_CheckCredentialExpiration(sslSocket *ss, sslDelegatedCredential *dc)
+{
+    SECStatus rv;
+    PRTime start, end /* microseconds */;
+    CERTCertificate *cert = ss->sec.peerCert;
+
+    rv = DER_DecodeTimeChoice(&start, &cert->validity.notBefore);
+    if (rv != SECSuccess) {
+        FATAL_ERROR(ss, PORT_GetError(), internal_error);
+        return SECFailure;
+    }
+
+    end = start + ((PRTime)dc->validTime * PR_USEC_PER_SEC);
+    if (ssl_Time(ss) > end) {
+        FATAL_ERROR(ss, SSL_ERROR_DC_EXPIRED, illegal_parameter);
+        return SECFailure;
+    }
+
+    return SECSuccess;
+}
+
+/* Returns SECSucces if |dc| is a DC for the current handshake; otherwise it
+ * returns SECFailure. A valid DC meets three requirements: (1) the signature
+ * was produced by the peer's end-entity certificate, (2) the end-entity
+ * certificate must have the correct key usage, and (3) the DC must not be
+ * expired.
+ *
+ * This function calls FATAL_ERROR() when an error occurs.
+ */
+SECStatus
+tls13_VerifyDelegatedCredential(sslSocket *ss,
+                                sslDelegatedCredential *dc)
+{
+    SECStatus rv;
+    PRTime start;
+    PRExplodedTime end;
+    CERTCertificate *cert = ss->sec.peerCert;
+    char endStr[256];
+
+    rv = DER_DecodeTimeChoice(&start, &cert->validity.notBefore);
+    if (rv != SECSuccess) {
+        FATAL_ERROR(ss, PORT_GetError(), internal_error);
+        return SECFailure;
+    }
+
+    PR_ExplodeTime(start + (dc->validTime * PR_USEC_PER_SEC),
+                   PR_GMTParameters, &end);
+    if (PR_FormatTime(endStr, sizeof(endStr), "%a %b %d %H:%M:%S %Y", &end)) {
+        SSL_TRC(20, ("%d: TLS13[%d]: Received delegated credential (expires %s)",
+                     SSL_GETPID(), ss->fd, endStr));
+    } else {
+        SSL_TRC(20, ("%d: TLS13[%d]: Received delegated credential",
+                     SSL_GETPID(), ss->fd));
+    }
+
+    rv = SECSuccess;
+    rv |= tls13_VerifyCredentialSignature(ss, dc);
+    rv |= tls13_CheckCertDelegationUsage(ss);
+    rv |= tls13_CheckCredentialExpiration(ss, dc);
+    return rv;
+}
+
+/* Returns a serialized DC with the given parameters.
+ *
+ * Note that this function is meant primarily for testing. In particular, it
+ * DOES NOT verify any of the following:
+ *  - |certPriv| is the private key corresponding to |cert|;
+ *  - that |checkCertKeyUsage(cert) == SECSuccess|;
+ *  - |dcCertVerifyAlg| is consistent with |dcPub|;
+ *  - |dcValidFor| is less than 7 days (the maximum permitted by the spec); or
+ *  - validTime doesn't overflow a PRUint32.
+ *
+ * These conditions are things we want to test for, which is why we allow them
+ * here. A real API for creating DCs would want to explicitly check ALL of these
+ * conditions are met.
+ */
+SECStatus
+SSLExp_DelegateCredential(const CERTCertificate *cert,
+                          const SECKEYPrivateKey *certPriv,
+                          const SECKEYPublicKey *dcPub,
+                          SSLSignatureScheme dcCertVerifyAlg,
+                          PRUint32 dcValidFor,
+                          PRTime now,
+                          SECItem *out)
+{
+    SECStatus rv;
+    SSL3Hashes hash;
+    SECItem *tmp = NULL;
+    SECKEYPrivateKey *tmpPriv = NULL;
+    sslDelegatedCredential *dc = NULL;
+    sslBuffer dcBuf = SSL_BUFFER_EMPTY;
+
+    dc = PORT_ZNew(sslDelegatedCredential);
+    if (!dc) {
+        PORT_SetError(SEC_ERROR_NO_MEMORY);
+        goto loser;
+    }
+
+    /* Serialize the DC parameters. */
+    PRTime start;
+    rv = DER_DecodeTimeChoice(&start, &cert->validity.notBefore);
+    if (rv != SECSuccess) {
+        goto loser;
+    }
+    dc->validTime = ((now - start) / PR_USEC_PER_SEC) + dcValidFor;
+    dc->expectedCertVerifyAlg = dcCertVerifyAlg;
+
+    tmp = SECKEY_EncodeDERSubjectPublicKeyInfo(dcPub);
+    if (!tmp) {
+        goto loser;
+    }
+    /* Transfer |tmp| into |dc->derSpki|. */
+    dc->derSpki.type = tmp->type;
+    dc->derSpki.data = tmp->data;
+    dc->derSpki.len = tmp->len;
+    PORT_Free(tmp);
+
+    rv = ssl_SignatureSchemeFromSpki(&cert->subjectPublicKeyInfo,
+                                     PR_TRUE /* isTls13 */, &dc->alg);
+    if (rv != SECSuccess) {
+        goto loser;
+    }
+    PORT_Assert(dc->alg != ssl_sig_none);
+
+    rv = tls13_AppendCredentialParams(&dcBuf, dc);
+    if (rv != SECSuccess) {
+        goto loser;
+    }
+
+    /* Hash signature message. */
+    rv = tls13_HashCredentialSignatureMessage(&hash, dc->alg, cert, &dcBuf);
+    if (rv != SECSuccess) {
+        goto loser;
+    }
+
+    /* Sign the hash with the delegation key.
+     *
+     * The PK11 API discards const qualifiers, so we have to make a copy of
+     * |certPriv| and pass the copy to |ssl3_SignHashesWithPrivKey|.
+     */
+    tmpPriv = SECKEY_CopyPrivateKey(certPriv);
+    rv = ssl3_SignHashesWithPrivKey(&hash, tmpPriv, dc->alg,
+                                    PR_TRUE /* isTls */, &dc->signature);
+    if (rv != SECSuccess) {
+        goto loser;
+    }
+
+    /* Serialize the DC signature. */
+    rv = tls13_AppendCredentialSignature(&dcBuf, dc);
+    if (rv != SECSuccess) {
+        goto loser;
+    }
+
+    /* Copy the serialized DC to |out|. */
+    rv = SECITEM_MakeItem(NULL, out, dcBuf.buf, dcBuf.len);
+    if (rv != SECSuccess) {
+        goto loser;
+    }
+
+    SECKEY_DestroyPrivateKey(tmpPriv);
+    tls13_DestroyDelegatedCredential(dc);
+    sslBuffer_Clear(&dcBuf);
+    return SECSuccess;
+
+loser:
+    SECKEY_DestroyPrivateKey(tmpPriv);
+    tls13_DestroyDelegatedCredential(dc);
+    sslBuffer_Clear(&dcBuf);
+    return SECFailure;
+}
new file mode 100644
--- /dev/null
+++ b/lib/ssl/tls13subcerts.h
@@ -0,0 +1,56 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is PRIVATE to SSL.
+ *
+ * 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/. */
+
+#ifndef __tls13subcerts_h_
+#define __tls13subcerts_h_
+
+struct sslDelegatedCredentialStr {
+    /* The number of seconds for which the delegated credential (DC) is valid
+     * following the notBefore parameter of the delegation certificate.
+     */
+    PRUint32 validTime;
+
+    /* The signature algorithm of the DC public key. This expected to the same
+     * as CertificateVerify.scheme.
+     */
+    SSLSignatureScheme expectedCertVerifyAlg;
+
+    /* The DER-encoded SubjectPublicKeyInfo, the DC public key.
+     */
+    SECItem derSpki;
+
+    /* The decoded SubjectPublicKeyInfo parsed from |derSpki|. */
+    CERTSubjectPublicKeyInfo *spki;
+
+    /* The signature algorithm used to verify the DC signature. */
+    SSLSignatureScheme alg;
+
+    /* The DC signature. */
+    SECItem signature;
+};
+
+SECStatus tls13_ReadDelegatedCredential(PRUint8 *b,
+                                        PRUint32 length,
+                                        sslDelegatedCredential **dcp);
+void tls13_DestroyDelegatedCredential(sslDelegatedCredential *dc);
+
+PRBool tls13_IsVerifyingWithDelegatedCredential(const sslSocket *ss);
+PRBool tls13_IsSigningWithDelegatedCredential(const sslSocket *ss);
+SECStatus tls13_MaybeSetDelegatedCredential(sslSocket *ss);
+SECStatus tls13_VerifyDelegatedCredential(sslSocket *ss,
+                                          sslDelegatedCredential *dc);
+
+SECStatus SSLExp_DelegateCredential(const CERTCertificate *cert,
+                                    const SECKEYPrivateKey *certPriv,
+                                    const SECKEYPublicKey *dcPub,
+                                    SSLSignatureScheme dcCertVerifyAlg,
+                                    PRUint32 dcValidFor,
+                                    PRTime now,
+                                    SECItem *out);
+
+#endif
--- a/tests/common/certsetup.sh
+++ b/tests/common/certsetup.sh
@@ -41,16 +41,21 @@ make_cert() {
     p384) type_args=(-q secp384r1);type=ec ;;
     p521) type_args=(-q secp521r1);type=ec ;;
     rsa_ca) type_args=(-g 1024);trust='CT,CT,CT';type=rsa ;;
     rsa_chain) type_args=(-g 1024);sign=(-c rsa_ca);type=rsa;;
     rsapss_ca) type_args=(-g 1024 --pss);trust='CT,CT,CT';type=rsa ;;
     rsapss_chain) type_args=(-g 1024);sign=(-c rsa_pss_ca);type=rsa;;
     rsa_ca_rsapss_chain) type_args=(-g 1024 --pss-sign);sign=(-c rsa_ca);type=rsa;;
     ecdh_rsa) type_args=(-q nistp256);sign=(-c rsa_ca);type=ec ;;
+    delegator_p256)
+        touch empty.txt
+        type_args=(-q nistp256 --extGeneric 1.3.6.1.4.1.44363.44:not-critical:empty.txt)
+        type=ec
+        ;;
   esac
   msg="create certificate: $@"
   shift 2
   counter=$(($counter + 1))
   certscript $@ | ${BINDIR}/certutil -S \
     -z "$R_NOISE_FILE" -d "$PROFILEDIR" \
     -n $name -s "CN=$name" -t "$trust" "${sign[@]}" -m "$counter" \
     -w -2 -v 120 -k "$type" "${type_args[@]}" "${sighash[@]}" -1 -2
--- a/tests/ssl_gtests/ssl_gtests.sh
+++ b/tests/ssl_gtests/ssl_gtests.sh
@@ -52,16 +52,17 @@ ssl_gtest_certs() {
   make_cert ecdh_ecdsa p256 kex
   make_cert rsa_ca rsa_ca ca
   make_cert rsa_chain rsa_chain sign
   make_cert rsa_pss_ca rsapss_ca ca
   make_cert rsa_pss_chain rsapss_chain sign
   make_cert rsa_ca_rsa_pss_chain rsa_ca_rsapss_chain sign
   make_cert ecdh_rsa ecdh_rsa kex
   make_cert dsa dsa sign
+  make_cert delegator_ecdsa256 delegator_p256 sign
 }
 
 ############################## ssl_gtest_init ##########################
 # local shell function to initialize this script
 ########################################################################
 ssl_gtest_init()
 {
   SCRIPTNAME=ssl_gtest.sh      # sourced - $0 would point to all.sh