Bug 1550889 - land NSS NSS_3_45_BETA1 UPGRADE_NSS_RELEASE, r=me
authorJ.C. Jones <jc@mozilla.com>
Mon, 01 Jul 2019 21:19:55 +0000
changeset 540521 ec3a228a84f28cbc4049549e44fab77ef227163b
parent 540430 26bf5c05734f636d58fd350b8758a849d9d12868
child 540522 b7ea2e7247cf4e414db111b644b14104aec8ba19
push id11529
push userarchaeopteryx@coole-files.de
push dateThu, 04 Jul 2019 15:22:33 +0000
treeherdermozilla-beta@ebb510a784b8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersme
bugs1550889
milestone69.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1550889 - land NSS NSS_3_45_BETA1 UPGRADE_NSS_RELEASE, r=me
security/nss/Makefile
security/nss/TAG-INFO
security/nss/automation/abi-check/expected-report-libssl3.so.txt
security/nss/cmd/selfserv/selfserv.c
security/nss/cmd/tstclnt/tstclnt.c
security/nss/coreconf/coreconf.dep
security/nss/gtests/ssl_gtest/manifest.mn
security/nss/gtests/ssl_gtest/ssl_auth_unittest.cc
security/nss/gtests/ssl_gtest/ssl_cert_ext_unittest.cc
security/nss/gtests/ssl_gtest/ssl_gtest.gyp
security/nss/gtests/ssl_gtest/tls_agent.cc
security/nss/gtests/ssl_gtest/tls_agent.h
security/nss/gtests/ssl_gtest/tls_subcerts_unittest.cc
security/nss/help.txt
security/nss/lib/ssl/manifest.mn
security/nss/lib/ssl/ssl.gyp
security/nss/lib/ssl/ssl.h
security/nss/lib/ssl/ssl3con.c
security/nss/lib/ssl/ssl3ecc.c
security/nss/lib/ssl/ssl3ext.c
security/nss/lib/ssl/ssl3ext.h
security/nss/lib/ssl/sslcert.c
security/nss/lib/ssl/sslcert.h
security/nss/lib/ssl/sslerr.h
security/nss/lib/ssl/sslexp.h
security/nss/lib/ssl/sslimpl.h
security/nss/lib/ssl/sslinfo.c
security/nss/lib/ssl/sslsock.c
security/nss/lib/ssl/sslt.h
security/nss/lib/ssl/tls13con.c
security/nss/lib/ssl/tls13exthandle.c
security/nss/lib/ssl/tls13exthandle.h
security/nss/lib/ssl/tls13subcerts.c
security/nss/lib/ssl/tls13subcerts.h
security/nss/tests/common/certsetup.sh
security/nss/tests/ssl_gtests/ssl_gtests.sh
--- a/security/nss/Makefile
+++ b/security/nss/Makefile
@@ -81,26 +81,24 @@ ifdef USE_DEBUG_RTL
 NSPR_CONFIGURE_OPTS += --enable-debug-rtl
 endif
 ifdef USE_STATIC_RTL
 NSPR_CONFIGURE_OPTS += --enable-static-rtl
 endif
 ifdef NS_USE_GCC
 NSPR_CONFIGURE_ENV = CC=gcc CXX=g++
 endif
+# Make sure to remove -arch arguments. NSPR can't handle that.
+remove_arch = $(filter-out __REMOVEME%,$(subst $(NULL) -arch , __REMOVEME,$(1)))
 ifdef CC
-NSPR_CONFIGURE_ENV = CC="$(CC)"
+NSPR_CONFIGURE_ENV = CC="$(call remove_arch,$(CC))"
 endif
 ifdef CCC
-NSPR_CONFIGURE_ENV += CXX="$(CCC)"
+NSPR_CONFIGURE_ENV += CXX="$(call remove_arch,$(CCC))"
 endif
-# Remove -arch definitions. NSPR can't handle that.
-NSPR_CONFIGURE_ENV := $(filter-out -arch x86_64,$(NSPR_CONFIGURE_ENV))
-NSPR_CONFIGURE_ENV := $(filter-out -arch i386,$(NSPR_CONFIGURE_ENV))
-NSPR_CONFIGURE_ENV := $(filter-out -arch ppc,$(NSPR_CONFIGURE_ENV))
 
 #
 # Some pwd commands on Windows (for example, the pwd
 # command in Cygwin) return a pathname that begins
 # with a (forward) slash.  When such a pathname is
 # passed to Windows build tools (for example, cl), it
 # is mistaken as a command-line option.  If that is the case,
 # we use a relative pathname as NSPR's prefix on Windows.
@@ -152,9 +150,8 @@ clean_docs:
 
 nss_RelEng_bld: import all
 
 package:
 	$(MAKE) -C pkg publish
 
 latest:
 	echo $(OBJDIR_NAME) > $(CORE_DEPTH)/../dist/latest
-
--- a/security/nss/TAG-INFO
+++ b/security/nss/TAG-INFO
@@ -1,1 +1,1 @@
-0c5d37301637
+NSS_3_45_BETA1
--- a/security/nss/automation/abi-check/expected-report-libssl3.so.txt
+++ b/security/nss/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/security/nss/cmd/selfserv/selfserv.c
+++ b/security/nss/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/security/nss/cmd/tstclnt/tstclnt.c
+++ b/security/nss/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/security/nss/coreconf/coreconf.dep
+++ b/security/nss/coreconf/coreconf.dep
@@ -5,9 +5,8 @@
 
 /*
  * A dummy header file that is a dependency for all the object files.
  * Used to force a full recompilation of NSS in Mozilla's Tinderbox
  * depend builds.  See comments in rules.mk.
  */
 
 #error "Do not include this header file."
-
--- a/security/nss/gtests/ssl_gtest/manifest.mn
+++ b/security/nss/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/security/nss/gtests/ssl_gtest/ssl_auth_unittest.cc
+++ b/security/nss/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/security/nss/gtests/ssl_gtest/ssl_cert_ext_unittest.cc
+++ b/security/nss/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/security/nss/gtests/ssl_gtest/ssl_gtest.gyp
+++ b/security/nss/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/security/nss/gtests/ssl_gtest/tls_agent.cc
+++ b/security/nss/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/security/nss/gtests/ssl_gtest/tls_agent.h
+++ b/security/nss/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/security/nss/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/security/nss/help.txt
+++ b/security/nss/help.txt
@@ -1,10 +1,10 @@
 Usage: build.sh [-h] [-c|-cc] [-v] [-j <n>] [--gyp|-g] [--opt|-o]
-                [-t <x64|x86|...>|--target=<x64|x86|...>]
+                [-t <x64|ia32|...>|--target=<x64|ia32|...>]
                 [--clang|--gcc|--msvc] [--scan-build[=dir]] [--disable-tests]
                 [--pprof] [--asan] [--msan] [--ubsan[=bool,shift,...]
                 [--fuzz[=tls|oss]] [--sancov[=edge|bb|func|...]]
                 [--emit-llvm] [--no-zdefs] [--static] [--ct-verif]
                 [--nspr|--with-nspr=<include>:<lib>|--system-nspr]
                 [--system-sqlite] [--enable-fips] [--enable-libpkix]
                 [--mozpkix-only] [-D<gyp-option>]
 
@@ -14,17 +14,17 @@ NSS build tool options:
 
     -h               display this help and exit
     -c               clean before build
     -cc              clean without building
     -v               verbose build
     -j <n>           run at most <n> concurrent jobs
     --gyp|-g         force a rerun of gyp
     --opt|-o         do an opt build
-    --target|-t      specify target architecture (e.g., x86, x64, aarch64)
+    --target|-t      specify target architecture (e.g., ia32, x64, aarch64)
     --clang          build with clang and clang++
     --gcc            build with gcc and g++
     --msvc           build with MSVC
     --scan-build     run the build with scan-build
                      --scan-build=<dir> sets the output path for scan-build
     --disable-tests  don't build tests and corresponding cmdline utils
     --pprof          build with gperftool support
     --asan           enable address sanitizer
--- a/security/nss/lib/ssl/manifest.mn
+++ b/security/nss/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/security/nss/lib/ssl/ssl.gyp
+++ b/security/nss/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/security/nss/lib/ssl/ssl.h
+++ b/security/nss/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/security/nss/lib/ssl/ssl3con.c
+++ b/security/nss/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/security/nss/lib/ssl/ssl3ecc.c
+++ b/security/nss/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/security/nss/lib/ssl/ssl3ext.c
+++ b/security/nss/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/security/nss/lib/ssl/ssl3ext.h
+++ b/security/nss/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/security/nss/lib/ssl/sslcert.c
+++ b/security/nss/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/security/nss/lib/ssl/sslcert.h
+++ b/security/nss/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/security/nss/lib/ssl/sslerr.h
+++ b/security/nss/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/security/nss/lib/ssl/sslexp.h
+++ b/security/nss/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/security/nss/lib/ssl/sslimpl.h
+++ b/security/nss/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/security/nss/lib/ssl/sslinfo.c
+++ b/security/nss/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/security/nss/lib/ssl/sslsock.c
+++ b/security/nss/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/security/nss/lib/ssl/sslt.h
+++ b/security/nss/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/security/nss/lib/ssl/tls13con.c
+++ b/security/nss/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/security/nss/lib/ssl/tls13exthandle.c
+++ b/security/nss/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/security/nss/lib/ssl/tls13exthandle.h
+++ b/security/nss/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/security/nss/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/security/nss/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/security/nss/tests/common/certsetup.sh
+++ b/security/nss/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/security/nss/tests/ssl_gtests/ssl_gtests.sh
+++ b/security/nss/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