summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristopher Patton <chrispatton@gmail.com>2019-06-25 14:21:59 +1000
committerChristopher Patton <chrispatton@gmail.com>2019-06-25 14:21:59 +1000
commit62b1e1d91c19c50a5e4c2540fcf49f0d8cf073dd (patch)
tree0a740fbe2b15a3970fa0c373c74294d8202a68eb
parent89715eee6c2dbc17820977e466fb8b52a1e12784 (diff)
downloadnss-hg-62b1e1d91c19c50a5e4c2540fcf49f0d8cf073dd.tar.gz
Bug 1540403 - draft-ietf-tls-subcerts-03, r=mt,jcj
Differential Revision: https://phabricator.services.mozilla.com/D25654
-rw-r--r--automation/abi-check/expected-report-libssl3.so.txt22
-rw-r--r--cmd/selfserv/selfserv.c2
-rw-r--r--cmd/tstclnt/tstclnt.c22
-rw-r--r--gtests/ssl_gtest/manifest.mn1
-rw-r--r--gtests/ssl_gtest/ssl_auth_unittest.cc6
-rw-r--r--gtests/ssl_gtest/ssl_cert_ext_unittest.cc10
-rw-r--r--gtests/ssl_gtest/ssl_gtest.gyp3
-rw-r--r--gtests/ssl_gtest/tls_agent.cc58
-rw-r--r--gtests/ssl_gtest/tls_agent.h22
-rw-r--r--gtests/ssl_gtest/tls_subcerts_unittest.cc238
-rw-r--r--lib/ssl/manifest.mn1
-rw-r--r--lib/ssl/ssl.gyp1
-rw-r--r--lib/ssl/ssl.h16
-rw-r--r--lib/ssl/ssl3con.c116
-rw-r--r--lib/ssl/ssl3ecc.c4
-rw-r--r--lib/ssl/ssl3ext.c9
-rw-r--r--lib/ssl/ssl3ext.h13
-rw-r--r--lib/ssl/sslcert.c109
-rw-r--r--lib/ssl/sslcert.h7
-rw-r--r--lib/ssl/sslerr.h4
-rw-r--r--lib/ssl/sslexp.h30
-rw-r--r--lib/ssl/sslimpl.h26
-rw-r--r--lib/ssl/sslinfo.c2
-rw-r--r--lib/ssl/sslsock.c17
-rw-r--r--lib/ssl/sslt.h29
-rw-r--r--lib/ssl/tls13con.c63
-rw-r--r--lib/ssl/tls13exthandle.c92
-rw-r--r--lib/ssl/tls13exthandle.h9
-rw-r--r--lib/ssl/tls13subcerts.c594
-rw-r--r--lib/ssl/tls13subcerts.h56
-rw-r--r--tests/common/certsetup.sh5
-rwxr-xr-xtests/ssl_gtests/ssl_gtests.sh1
32 files changed, 1522 insertions, 66 deletions
diff --git a/automation/abi-check/expected-report-libssl3.so.txt b/automation/abi-check/expected-report-libssl3.so.txt
index e69de29bb..9f2f796b1 100644
--- a/automation/abi-check/expected-report-libssl3.so.txt
+++ b/automation/abi-check/expected-report-libssl3.so.txt
@@ -0,0 +1,22 @@
+
+2 functions with some indirect sub-type change:
+
+ [C]'function SECStatus SSL_ConfigServerCert(PRFileDesc*, CERTCertificate*, SECKEYPrivateKey*, const SSLExtraServerCertData*, unsigned int)' at sslcert.c:640:1 has some indirect sub-type changes:
+ parameter 4 of type 'const SSLExtraServerCertData*' has sub-type changes:
+ in pointed to type 'const SSLExtraServerCertData':
+ in unqualified underlying type 'typedef SSLExtraServerCertData' at sslt.h:291:1:
+ underlying type 'struct SSLExtraServerCertDataStr' at sslt.h:256:1 changed:
+ type size changed from 256 to 384 (in bits)
+ 2 data member insertions:
+ 'const SECItem* SSLExtraServerCertDataStr::delegCred', at offset 256 (in bits) at sslt.h:283:1
+ 'const SECKEYPrivateKey* SSLExtraServerCertDataStr::delegCredPrivKey', at offset 320 (in bits) at sslt.h:290:1
+
+ [C]'function SECStatus SSL_GetChannelInfo(PRFileDesc*, SSLChannelInfo*, PRUintn)' at sslinfo.c:13:1 has some indirect sub-type changes:
+ parameter 2 of type 'SSLChannelInfo*' has sub-type changes:
+ in pointed to type 'typedef SSLChannelInfo' at sslt.h:357:1:
+ underlying type 'struct SSLChannelInfoStr' at sslt.h:272:1 changed:
+ type size hasn't changed
+ 1 data member insertion:
+ 'PRBool SSLChannelInfoStr::peerDelegCred', at offset 928 (in bits) at sslt.h:353:1
+
+
diff --git a/cmd/selfserv/selfserv.c b/cmd/selfserv/selfserv.c
index 56b5ec28b..f2b1273b3 100644
--- a/cmd/selfserv/selfserv.c
+++ b/cmd/selfserv/selfserv.c
@@ -1926,7 +1926,7 @@ server_main(
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],
diff --git a/cmd/tstclnt/tstclnt.c b/cmd/tstclnt/tstclnt.c
index bc0cbfa76..12c6df045 100644
--- a/cmd/tstclnt/tstclnt.c
+++ b/cmd/tstclnt/tstclnt.c
@@ -213,6 +213,9 @@ printSecurityInfo(PRFileDesc *fd)
" %u\n",
scts->len);
}
+ if (channel.peerDelegCred) {
+ fprintf(stderr, "Received a Delegated Credential\n");
+ }
}
static void
@@ -272,6 +275,7 @@ PrintParameterUsage()
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"
@@ -993,6 +997,7 @@ 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)
@@ -1365,6 +1370,14 @@ run()
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);
@@ -1715,12 +1728,11 @@ main(int argc, char **argv)
}
}
- /* 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 '?':
@@ -1743,6 +1755,10 @@ main(int argc, char **argv)
requestFile = PORT_Strdup(optstate->value);
break;
+ case 'B':
+ enableDelegatedCredentials = PR_TRUE;
+ break;
+
case 'C':
++dumpServerChain;
break;
diff --git a/gtests/ssl_gtest/manifest.mn b/gtests/ssl_gtest/manifest.mn
index 15a971b4d..9fb12421c 100644
--- a/gtests/ssl_gtest/manifest.mn
+++ b/gtests/ssl_gtest/manifest.mn
@@ -54,6 +54,7 @@ CPPSRCS = \
tls_hkdf_unittest.cc \
tls_filter.cc \
tls_protect.cc \
+ tls_subcerts_unittest.cc \
tls_esni_unittest.cc \
$(SSLKEYLOGFILE_FILES) \
$(NULL)
diff --git a/gtests/ssl_gtest/ssl_auth_unittest.cc b/gtests/ssl_gtest/ssl_auth_unittest.cc
index 08ababbe6..340e98745 100644
--- a/gtests/ssl_gtest/ssl_auth_unittest.cc
+++ b/gtests/ssl_gtest/ssl_auth_unittest.cc
@@ -1322,11 +1322,11 @@ TEST_P(TlsConnectGeneric, AuthFailImmediate) {
}
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) {
diff --git a/gtests/ssl_gtest/ssl_cert_ext_unittest.cc b/gtests/ssl_gtest/ssl_cert_ext_unittest.cc
index 573c69c75..f725faa4c 100644
--- a/gtests/ssl_gtest/ssl_cert_ext_unittest.cc
+++ b/gtests/ssl_gtest/ssl_cert_ext_unittest.cc
@@ -64,8 +64,8 @@ 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) {
@@ -147,8 +147,8 @@ static const SECItem kOcspItems[] = {
{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();
@@ -224,7 +224,7 @@ TEST_P(TlsConnectGeneric, OcspHugeSuccess) {
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,
diff --git a/gtests/ssl_gtest/ssl_gtest.gyp b/gtests/ssl_gtest/ssl_gtest.gyp
index c0f6c1cd2..63229561b 100644
--- a/gtests/ssl_gtest/ssl_gtest.gyp
+++ b/gtests/ssl_gtest/ssl_gtest.gyp
@@ -54,7 +54,8 @@
'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',
diff --git a/gtests/ssl_gtest/tls_agent.cc b/gtests/ssl_gtest/tls_agent.cc
index 697da6df2..b958c9a08 100644
--- a/gtests/ssl_gtest/tls_agent.cc
+++ b/gtests/ssl_gtest/tls_agent.cc
@@ -47,6 +47,7 @@ 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,
@@ -137,6 +138,59 @@ void TlsAgent::SetState(State s) {
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;
@@ -318,6 +372,10 @@ 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_);
diff --git a/gtests/ssl_gtest/tls_agent.h b/gtests/ssl_gtest/tls_agent.h
index 0e1b3df6d..68766a4b2 100644
--- a/gtests/ssl_gtest/tls_agent.h
+++ b/gtests/ssl_gtest/tls_agent.h
@@ -76,6 +76,7 @@ class TlsAgent : public PollTarget {
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();
@@ -113,6 +114,26 @@ class TlsAgent : public PollTarget {
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);
@@ -163,6 +184,7 @@ class TlsAgent : public PollTarget {
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) {
diff --git a/gtests/ssl_gtest/tls_subcerts_unittest.cc b/gtests/ssl_gtest/tls_subcerts_unittest.cc
new file mode 100644
index 000000000..4406ea84c
--- /dev/null
+++ b/gtests/ssl_gtest/tls_subcerts_unittest.cc
@@ -0,0 +1,238 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <ctime>
+
+#include "secerr.h"
+#include "ssl.h"
+
+#include "gtest_utils.h"
+#include "tls_agent.h"
+#include "tls_connect.h"
+
+namespace nss_test {
+
+const std::string kDelegatorId = TlsAgent::kDelegatorEcdsa256;
+const std::string kDCId = TlsAgent::kServerEcdsa256;
+const SSLSignatureScheme kDCScheme = ssl_sig_ecdsa_secp256r1_sha256;
+const PRUint32 kDCValidFor = 60 * 60 * 24 * 7 /* 1 week (seconds */;
+
+// Attempt to configure a DC when either the DC or DC private key is missing.
+TEST_P(TlsConnectTls13, DCNotConfigured) {
+ // Load and delegate the credential.
+ ScopedSECKEYPublicKey pub;
+ ScopedSECKEYPrivateKey priv;
+ EXPECT_TRUE(TlsAgent::LoadKeyPairFromCert(kDCId, &pub, &priv));
+
+ StackSECItem dc;
+ TlsAgent::DelegateCredential(kDelegatorId, pub, kDCScheme, kDCValidFor, now(),
+ &dc);
+
+ // Attempt to install the certificate and DC with a missing DC private key.
+ EnsureTlsSetup();
+ SSLExtraServerCertData extra_data_missing_dc_priv_key = {
+ ssl_auth_null, nullptr, nullptr, nullptr, &dc, nullptr};
+ EXPECT_FALSE(server_->ConfigServerCert(kDelegatorId, true,
+ &extra_data_missing_dc_priv_key));
+
+ // Attempt to install the certificate and with only the DC private key.
+ EnsureTlsSetup();
+ SSLExtraServerCertData extra_data_missing_dc = {
+ ssl_auth_null, nullptr, nullptr, nullptr, nullptr, priv.get()};
+ EXPECT_FALSE(
+ server_->ConfigServerCert(kDelegatorId, true, &extra_data_missing_dc));
+}
+
+// Connected with ECDSA-P256.
+TEST_P(TlsConnectTls13, DCConnectEcdsaP256) {
+ Reset(kDelegatorId);
+ client_->EnableDelegatedCredentials();
+ server_->AddDelegatedCredential(kDCId, kDCScheme, kDCValidFor, now());
+
+ auto cfilter = MakeTlsFilter<TlsExtensionCapture>(
+ client_, ssl_delegated_credentials_xtn);
+ Connect();
+
+ EXPECT_TRUE(cfilter->captured());
+ client_->CheckPeerDelegCred(true);
+}
+
+// Connected with ECDSA-P521.
+TEST_P(TlsConnectTls13, DCConnectEcdsaP521) {
+ Reset(kDelegatorId);
+ client_->EnableDelegatedCredentials();
+ server_->AddDelegatedCredential(TlsAgent::kServerEcdsa521,
+ ssl_sig_ecdsa_secp521r1_sha512, kDCValidFor,
+ now());
+ client_->EnableDelegatedCredentials();
+
+ auto cfilter = MakeTlsFilter<TlsExtensionCapture>(
+ client_, ssl_delegated_credentials_xtn);
+ Connect();
+
+ EXPECT_TRUE(cfilter->captured());
+ client_->CheckPeerDelegCred(true);
+}
+
+// Connected with RSA-PSS.
+TEST_P(TlsConnectTls13, DCConnectRsaPss) {
+ Reset(kDelegatorId);
+ client_->EnableDelegatedCredentials();
+ server_->AddDelegatedCredential(
+ TlsAgent::kServerRsaPss, ssl_sig_rsa_pss_rsae_sha256, kDCValidFor, now());
+
+ auto cfilter = MakeTlsFilter<TlsExtensionCapture>(
+ client_, ssl_delegated_credentials_xtn);
+ Connect();
+
+ EXPECT_TRUE(cfilter->captured());
+ client_->CheckPeerDelegCred(true);
+}
+
+// Aborted because of incorrect DC signature algorithm indication.
+TEST_P(TlsConnectTls13, DCAbortBadExpectedCertVerifyAlg) {
+ Reset(kDelegatorId);
+ client_->EnableDelegatedCredentials();
+ server_->AddDelegatedCredential(TlsAgent::kServerEcdsa256,
+ ssl_sig_ecdsa_secp521r1_sha512, kDCValidFor,
+ now());
+ ConnectExpectAlert(client_, kTlsAlertIllegalParameter);
+}
+
+// Aborted because of invalid DC signature.
+TEST_P(TlsConnectTls13, DCABortBadSignature) {
+ Reset(kDelegatorId);
+ EnsureTlsSetup();
+ client_->EnableDelegatedCredentials();
+
+ ScopedSECKEYPublicKey pub;
+ ScopedSECKEYPrivateKey priv;
+ EXPECT_TRUE(TlsAgent::LoadKeyPairFromCert(kDCId, &pub, &priv));
+
+ StackSECItem dc;
+ TlsAgent::DelegateCredential(kDelegatorId, pub, kDCScheme, kDCValidFor, now(),
+ &dc);
+
+ // Flip the first bit of the DC so that the signature is invalid.
+ dc.data[0] ^= 0x01;
+
+ SSLExtraServerCertData extra_data = {ssl_auth_null, nullptr, nullptr,
+ nullptr, &dc, priv.get()};
+ EXPECT_TRUE(server_->ConfigServerCert(kDelegatorId, true, &extra_data));
+
+ ConnectExpectAlert(client_, kTlsAlertIllegalParameter);
+}
+
+// Aborted because of expired DC.
+TEST_P(TlsConnectTls13, DCAbortExpired) {
+ Reset(kDelegatorId);
+ server_->AddDelegatedCredential(kDCId, kDCScheme, kDCValidFor, now());
+ client_->EnableDelegatedCredentials();
+ // When the client checks the time, it will be at least one second after the
+ // DC expired.
+ AdvanceTime((static_cast<PRTime>(kDCValidFor) + 1) * PR_USEC_PER_SEC);
+ ConnectExpectAlert(client_, kTlsAlertIllegalParameter);
+}
+
+// Aborted because of invalid key usage.
+TEST_P(TlsConnectTls13, DCAbortBadKeyUsage) {
+ // The sever does not have the delegationUsage extension.
+ Reset(TlsAgent::kServerEcdsa256);
+ client_->EnableDelegatedCredentials();
+ server_->AddDelegatedCredential(kDCId, kDCScheme, kDCValidFor, now());
+ ConnectExpectAlert(client_, kTlsAlertIllegalParameter);
+}
+
+// Connected without DC because of no client indication.
+TEST_P(TlsConnectTls13, DCConnectNoClientSupport) {
+ Reset(kDelegatorId);
+ server_->AddDelegatedCredential(kDCId, kDCScheme, kDCValidFor, now());
+
+ auto cfilter = MakeTlsFilter<TlsExtensionCapture>(
+ client_, ssl_delegated_credentials_xtn);
+ Connect();
+
+ EXPECT_FALSE(cfilter->captured());
+ client_->CheckPeerDelegCred(false);
+}
+
+// Connected without DC because of no server DC.
+TEST_P(TlsConnectTls13, DCConnectNoServerSupport) {
+ Reset(kDelegatorId);
+ client_->EnableDelegatedCredentials();
+
+ auto cfilter = MakeTlsFilter<TlsExtensionCapture>(
+ client_, ssl_delegated_credentials_xtn);
+ Connect();
+
+ EXPECT_TRUE(cfilter->captured());
+ client_->CheckPeerDelegCred(false);
+}
+
+// Connected without DC because client doesn't support TLS 1.3.
+TEST_P(TlsConnectTls13, DCConnectClientNoTls13) {
+ Reset(kDelegatorId);
+ client_->EnableDelegatedCredentials();
+ server_->AddDelegatedCredential(kDCId, kDCScheme, kDCValidFor, now());
+
+ client_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_2,
+ SSL_LIBRARY_VERSION_TLS_1_2);
+ server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_2,
+ SSL_LIBRARY_VERSION_TLS_1_3);
+
+ auto cfilter = MakeTlsFilter<TlsExtensionCapture>(
+ client_, ssl_delegated_credentials_xtn);
+ Connect();
+
+ // Should fallback to TLS 1.2 and not negotiate a DC.
+ EXPECT_FALSE(cfilter->captured());
+ client_->CheckPeerDelegCred(false);
+}
+
+// Connected without DC because server doesn't support TLS 1.3.
+TEST_P(TlsConnectTls13, DCConnectServerNoTls13) {
+ Reset(kDelegatorId);
+ client_->EnableDelegatedCredentials();
+ server_->AddDelegatedCredential(kDCId, kDCScheme, kDCValidFor, now());
+
+ client_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_2,
+ SSL_LIBRARY_VERSION_TLS_1_3);
+ server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_2,
+ SSL_LIBRARY_VERSION_TLS_1_2);
+
+ auto cfilter = MakeTlsFilter<TlsExtensionCapture>(
+ client_, ssl_delegated_credentials_xtn);
+ Connect();
+
+ // Should fallback to TLS 1.2 and not negotiate a DC. The client will still
+ // send the indication because it supports 1.3.
+ EXPECT_TRUE(cfilter->captured());
+ client_->CheckPeerDelegCred(false);
+}
+
+// Connected without DC because client doesn't support the signature scheme.
+TEST_P(TlsConnectTls13, DCConnectExpectedCertVerifyAlgNotSupported) {
+ Reset(kDelegatorId);
+ client_->EnableDelegatedCredentials();
+ static const SSLSignatureScheme kClientSchemes[] = {
+ ssl_sig_ecdsa_secp256r1_sha256,
+ };
+ client_->SetSignatureSchemes(kClientSchemes, PR_ARRAY_SIZE(kClientSchemes));
+
+ server_->AddDelegatedCredential(TlsAgent::kServerEcdsa521,
+ ssl_sig_ecdsa_secp521r1_sha512, kDCValidFor,
+ now());
+
+ auto cfilter = MakeTlsFilter<TlsExtensionCapture>(
+ client_, ssl_delegated_credentials_xtn);
+ Connect();
+
+ // Client sends indication, but the server doesn't send a DC.
+ EXPECT_TRUE(cfilter->captured());
+ client_->CheckPeerDelegCred(false);
+}
+
+} // namespace nss_test
diff --git a/lib/ssl/manifest.mn b/lib/ssl/manifest.mn
index 201a01049..83df8c0b8 100644
--- a/lib/ssl/manifest.mn
+++ b/lib/ssl/manifest.mn
@@ -57,6 +57,7 @@ CSRCS = \
sslgrp.c \
sslprimitive.c \
tls13esni.c \
+ tls13subcerts.c \
$(NULL)
LIBRARY_NAME = ssl
diff --git a/lib/ssl/ssl.gyp b/lib/ssl/ssl.gyp
index 7c89d80c1..ae8f8d94f 100644
--- a/lib/ssl/ssl.gyp
+++ b/lib/ssl/ssl.gyp
@@ -49,6 +49,7 @@
'tls13hashstate.c',
'tls13hkdf.c',
'tls13replay.c',
+ 'tls13subcerts.c',
],
'conditions': [
[ 'OS=="win"', {
diff --git a/lib/ssl/ssl.h b/lib/ssl/ssl.h
index bfeddeff8..b914aa07f 100644
--- a/lib/ssl/ssl.h
+++ b/lib/ssl/ssl.h
@@ -310,6 +310,22 @@ SSL_IMPORT PRFileDesc *DTLS_ImportFD(PRFileDesc *model, PRFileDesc *fd);
*/
#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);
diff --git a/lib/ssl/ssl3con.c b/lib/ssl/ssl3con.c
index 67f162955..eca98bf16 100644
--- a/lib/ssl/ssl3con.c
+++ b/lib/ssl/ssl3con.c
@@ -1026,15 +1026,13 @@ ssl3_GetNewRandom(SSL3Random random)
return rv;
}
-/* Called by ssl3_SendServerKeyExchange and ssl3_SendCertificateVerify */
SECStatus
-ssl3_SignHashes(sslSocket *ss, SSL3Hashes *hash, SECKEYPrivateKey *key,
- SECItem *buf)
+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;
@@ -1045,7 +1043,7 @@ ssl3_SignHashes(sslSocket *ss, SSL3Hashes *hash, SECKEYPrivateKey *key,
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) {
@@ -1122,11 +1120,6 @@ ssl3_SignHashes(sslSocket *ss, SSL3Hashes *hash, SECKEYPrivateKey *key,
}
}
- 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) {
@@ -1136,10 +1129,33 @@ done:
return rv;
}
-/* Called from ssl3_HandleServerKeyExchange, ssl3_HandleCertificateVerify */
+/* Called by ssl3_SendServerKeyExchange and ssl3_SendCertificateVerify */
SECStatus
-ssl3_VerifySignedHashes(sslSocket *ss, SSLSignatureScheme scheme, SSL3Hashes *hash,
- SECItem *buf)
+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;
@@ -1153,7 +1169,7 @@ ssl3_VerifySignedHashes(sslSocket *ss, SSLSignatureScheme scheme, SSL3Hashes *ha
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;
@@ -1273,6 +1289,16 @@ loser:
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.
@@ -4113,7 +4139,7 @@ ssl_SignatureSchemeValid(SSLSignatureScheme scheme, SECOidTag spkiOid,
}
static SECStatus
-ssl_SignatureSchemeFromPssSpki(CERTSubjectPublicKeyInfo *spki,
+ssl_SignatureSchemeFromPssSpki(const CERTSubjectPublicKeyInfo *spki,
SSLSignatureScheme *scheme)
{
SECKEYRSAPSSParams pssParam = { 0 };
@@ -4161,7 +4187,7 @@ loser:
}
static SECStatus
-ssl_SignatureSchemeFromEcSpki(CERTSubjectPublicKeyInfo *spki,
+ssl_SignatureSchemeFromEcSpki(const CERTSubjectPublicKeyInfo *spki,
SSLSignatureScheme *scheme)
{
const sslNamedGroupDef *group;
@@ -4198,8 +4224,8 @@ ssl_SignatureSchemeFromEcSpki(CERTSubjectPublicKeyInfo *spki,
/* 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);
@@ -4219,8 +4245,8 @@ ssl_SignatureSchemeFromSpki(CERTSubjectPublicKeyInfo *spki,
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) {
@@ -4250,21 +4276,20 @@ ssl_SignatureKeyMatchesSpkiOid(const ssl3KEADef *keaDef, SECOidTag spkiOid)
}
/* 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;
}
@@ -4278,7 +4303,7 @@ ssl_CheckSignatureSchemeConsistency(sslSocket *ss, SSLSignatureScheme scheme,
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. */
@@ -6080,7 +6105,7 @@ ssl3_SendClientKeyExchange(sslSocket *ss)
}
/* Used by ssl_PickSignatureScheme(). */
-static PRBool
+PRBool
ssl_CanUseSignatureScheme(SSLSignatureScheme scheme,
const SSLSignatureScheme *peerSchemes,
unsigned int peerSchemeCount,
@@ -6124,6 +6149,21 @@ ssl_CanUseSignatureScheme(SSLSignatureScheme scheme,
}
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,
@@ -6133,8 +6173,7 @@ ssl_PickSignatureScheme(sslSocket *ss,
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;
@@ -6148,13 +6187,10 @@ ssl_PickSignatureScheme(sslSocket *ss,
return SECFailure;
}
- slot = PK11_GetSlotFromPrivateKey(privKey);
- if (!slot) {
- PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ rv = ssl_PrivateKeySupportsRsaPss(privKey, &doesRsaPss);
+ if (rv != SECSuccess) {
return SECFailure;
}
- slotDoesPss = PK11_DoesMechanism(slot, auth_alg_defs[ssl_auth_rsa_pss]);
- PK11_FreeSlot(slot);
/* If the certificate SPKI indicates a single scheme, don't search. */
rv = ssl_SignatureSchemeFromSpki(&cert->subjectPublicKeyInfo,
@@ -6165,7 +6201,7 @@ ssl_PickSignatureScheme(sslSocket *ss,
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;
}
@@ -6182,7 +6218,7 @@ ssl_PickSignatureScheme(sslSocket *ss,
if (ssl_SignatureSchemeValid(scheme, spkiOid, isTLS13) &&
ssl_CanUseSignatureScheme(scheme, peerSchemes, peerSchemeCount,
- requireSha1, slotDoesPss)) {
+ requireSha1, doesRsaPss)) {
ss->ssl3.hs.signatureScheme = scheme;
return SECSuccess;
}
@@ -7073,8 +7109,8 @@ ssl_HandleDHServerKeyExchange(sslSocket *ss, PRUint8 *b, PRUint32 length)
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;
}
@@ -9779,8 +9815,8 @@ ssl3_HandleCertificateVerify(sslSocket *ss, PRUint8 *b, PRUint32 length)
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;
diff --git a/lib/ssl/ssl3ecc.c b/lib/ssl/ssl3ecc.c
index 52d5bb515..d5ad372e5 100644
--- a/lib/ssl/ssl3ecc.c
+++ b/lib/ssl/ssl3ecc.c
@@ -548,8 +548,8 @@ ssl3_HandleECDHServerKeyExchange(sslSocket *ss, PRUint8 *b, PRUint32 length)
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;
diff --git a/lib/ssl/ssl3ext.c b/lib/ssl/ssl3ext.c
index e9fc096b7..032795bc4 100644
--- a/lib/ssl/ssl3ext.c
+++ b/lib/ssl/ssl3ext.c
@@ -16,6 +16,7 @@
#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,
@@ -45,6 +46,7 @@ static const ssl3ExtensionHandler clientHelloHandlers[] = {
{ 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 },
@@ -96,6 +98,7 @@ static const ssl3ExtensionHandler newSessionTicketHandlers[] = {
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 }
};
@@ -127,6 +130,7 @@ static const sslExtensionBuilder clientHelloSendersTLS[] =
{ 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 },
@@ -170,6 +174,7 @@ static const struct {
} 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 },
@@ -951,6 +956,9 @@ ssl3_InitExtensionData(TLSExtensionData *xtnData, const sslSocket *ss)
++advertisedMax;
}
xtnData->advertised = PORT_ZNewArray(PRUint16, advertisedMax);
+ xtnData->peerDelegCred = NULL;
+ xtnData->peerRequestedDelegCred = PR_FALSE;
+ xtnData->sendingDelegCredToPeer = PR_FALSE;
}
void
@@ -969,6 +977,7 @@ ssl3_DestroyExtensionData(TLSExtensionData *xtnData)
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
diff --git a/lib/ssl/ssl3ext.h b/lib/ssl/ssl3ext.h
index d96b4cffe..97319c7d9 100644
--- a/lib/ssl/ssl3ext.h
+++ b/lib/ssl/ssl3ext.h
@@ -111,6 +111,19 @@ struct TLSExtensionDataStr {
/* 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 {
diff --git a/lib/ssl/sslcert.c b/lib/ssl/sslcert.c
index 878df761e..d2c9480d5 100644
--- a/lib/ssl/sslcert.c
+++ b/lib/ssl/sslcert.c
@@ -8,10 +8,11 @@
#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().
@@ -102,6 +103,8 @@ ssl_NewServerCert()
sc->serverCertChain = NULL;
sc->certStatusArray = NULL;
sc->signedCertTimestamps.len = 0;
+ sc->delegCred.len = 0;
+ sc->delegCredKeyPair = NULL;
return sc;
}
@@ -148,8 +151,17 @@ ssl_CopyServerCert(const sslServerCert *oc)
}
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);
@@ -178,6 +190,12 @@ ssl_FreeServerCert(sslServerCert *sc)
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));
}
@@ -309,6 +327,79 @@ ssl_PopulateSignedCertTimestamps(sslServerCert *sc,
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. */
@@ -379,6 +470,12 @@ ssl_ConfigCert(sslSocket *ss, sslAuthTypeMask authTypes,
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;
@@ -548,7 +645,7 @@ SSL_ConfigServerCert(PRFileDesc *fd, CERTCertificate *cert,
sslKeyPair *keyPair;
SECStatus rv;
SSLExtraServerCertData dataCopy = {
- ssl_auth_null, NULL, NULL, NULL
+ ssl_auth_null, NULL, NULL, NULL, NULL, NULL
};
sslAuthTypeMask authTypes;
diff --git a/lib/ssl/sslcert.h b/lib/ssl/sslcert.h
index fb31d1389..8c2dadae4 100644
--- a/lib/ssl/sslcert.h
+++ b/lib/ssl/sslcert.h
@@ -41,6 +41,13 @@ typedef struct sslServerCertStr {
** 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)))
diff --git a/lib/ssl/sslerr.h b/lib/ssl/sslerr.h
index 9e72da28f..7100b0226 100644
--- a/lib/ssl/sslerr.h
+++ b/lib/ssl/sslerr.h
@@ -270,6 +270,10 @@ typedef enum {
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 */
diff --git a/lib/ssl/sslexp.h b/lib/ssl/sslexp.h
index 96159f56b..8d8e3adb2 100644
--- a/lib/ssl/sslexp.h
+++ b/lib/ssl/sslexp.h
@@ -757,6 +757,36 @@ typedef PRTime(PR_CALLBACK *SSLTimeFunc)(void *arg);
(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
diff --git a/lib/ssl/sslimpl.h b/lib/ssl/sslimpl.h
index aa6f804ff..64a893990 100644
--- a/lib/ssl/sslimpl.h
+++ b/lib/ssl/sslimpl.h
@@ -37,6 +37,7 @@
typedef struct sslSocketStr sslSocket;
typedef struct sslNamedGroupDefStr sslNamedGroupDef;
typedef struct sslEsniKeysStr sslEsniKeys;
+typedef struct sslDelegatedCredentialStr sslDelegatedCredential;
typedef struct sslEphemeralKeyPairStr sslEphemeralKeyPair;
typedef struct TLS13KeyShareEntryStr TLS13KeyShareEntry;
@@ -279,6 +280,7 @@ typedef struct sslOptionsStr {
unsigned int enableHelloDowngradeCheck : 1;
unsigned int enableV2CompatibleHello : 1;
unsigned int enablePostHandshakeAuth : 1;
+ unsigned int enableDelegatedCredentials : 1;
} sslOptions;
typedef enum { sslHandshakingUndetermined = 0,
@@ -1465,6 +1467,11 @@ 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);
@@ -1561,9 +1568,14 @@ extern SECStatus ssl3_ConsumeHandshakeNumber64(sslSocket *ss, PRUint64 *num,
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,
@@ -1571,8 +1583,18 @@ extern SECStatus ssl_ParseSignatureSchemes(const sslSocket *ss, PLArenaPool *are
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,
@@ -1696,6 +1718,8 @@ 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,
diff --git a/lib/ssl/sslinfo.c b/lib/ssl/sslinfo.c
index 979325940..4834605c1 100644
--- a/lib/ssl/sslinfo.c
+++ b/lib/ssl/sslinfo.c
@@ -7,6 +7,7 @@
#include "sslimpl.h"
#include "sslproto.h"
#include "tls13hkdf.h"
+#include "tls13subcerts.h"
SECStatus
SSL_GetChannelInfo(PRFileDesc *fd, SSLChannelInfo *info, PRUintn len)
@@ -79,6 +80,7 @@ SSL_GetChannelInfo(PRFileDesc *fd, SSLChannelInfo *info, PRUintn len)
inf.signatureScheme = sid->sigScheme;
}
inf.resumed = ss->statelessResume || ss->ssl3.hs.isResuming;
+ inf.peerDelegCred = tls13_IsVerifyingWithDelegatedCredential(ss);
if (sid) {
unsigned int sidLen;
diff --git a/lib/ssl/sslsock.c b/lib/ssl/sslsock.c
index b53075be0..f6fa5addc 100644
--- a/lib/ssl/sslsock.c
+++ b/lib/ssl/sslsock.c
@@ -20,6 +20,7 @@
#include "pk11pqg.h"
#include "pk11pub.h"
#include "tls13esni.h"
+#include "tls13subcerts.h"
static const sslSocketOps ssl_default_ops = { /* No SSL. */
ssl_DefConnect,
@@ -75,6 +76,7 @@ static sslOptions ssl_defaults = {
.enableFalseStart = PR_FALSE,
.cbcRandomIV = PR_TRUE,
.enableOCSPStapling = PR_FALSE,
+ .enableDelegatedCredentials = PR_FALSE,
.enableALPN = PR_TRUE,
.reuseServerECDHEKey = PR_TRUE,
.enableFallbackSCSV = PR_FALSE,
@@ -793,6 +795,10 @@ SSL_OptionSet(PRFileDesc *fd, PRInt32 which, PRIntn val)
ss->opt.enableOCSPStapling = val;
break;
+ case SSL_ENABLE_DELEGATED_CREDENTIALS:
+ ss->opt.enableDelegatedCredentials = val;
+ break;
+
case SSL_ENABLE_NPN:
break;
@@ -963,6 +969,9 @@ SSL_OptionGet(PRFileDesc *fd, PRInt32 which, PRIntn *pVal)
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;
@@ -1101,6 +1110,9 @@ SSL_OptionGetDefault(PRInt32 which, PRIntn *pVal)
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;
@@ -1291,6 +1303,10 @@ SSL_OptionSetDefault(PRInt32 which, PRIntn val)
ssl_defaults.enableOCSPStapling = val;
break;
+ case SSL_ENABLE_DELEGATED_CREDENTIALS:
+ ssl_defaults.enableDelegatedCredentials = val;
+ break;
+
case SSL_ENABLE_NPN:
break;
@@ -4089,6 +4105,7 @@ struct {
EXP(AeadDecrypt),
EXP(AeadEncrypt),
EXP(CreateAntiReplayContext),
+ EXP(DelegateCredential),
EXP(DestroyAead),
EXP(DestroyResumptionTokenInfo),
EXP(EnableESNI),
diff --git a/lib/ssl/sslt.h b/lib/ssl/sslt.h
index 58b44fe2f..828fdcfdc 100644
--- a/lib/ssl/sslt.h
+++ b/lib/ssl/sslt.h
@@ -9,9 +9,10 @@
#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,
@@ -267,6 +268,26 @@ typedef struct SSLExtraServerCertDataStr {
/* 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 {
@@ -326,6 +347,11 @@ typedef struct SSLChannelInfoStr {
* 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;
@@ -476,6 +502,7 @@ typedef enum {
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;
diff --git a/lib/ssl/tls13con.c b/lib/ssl/tls13con.c
index 0ba694fb6..5f0866d0a 100644
--- a/lib/ssl/tls13con.c
+++ b/lib/ssl/tls13con.c
@@ -24,6 +24,7 @@
#include "tls13esni.h"
#include "tls13exthandle.h"
#include "tls13hashstate.h"
+#include "tls13subcerts.h"
static SECStatus tls13_SetCipherSpec(sslSocket *ss, PRUint16 epoch,
SSLSecretDirection install,
@@ -1589,6 +1590,20 @@ tls13_SelectServerCert(sslSocket *ss)
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;
@@ -2620,7 +2635,14 @@ tls13_SendEncryptedServerSequence(sslSocket *ss)
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. */
@@ -4120,6 +4142,8 @@ done:
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;
@@ -4159,7 +4183,38 @@ tls13_HandleCertificateVerify(sslSocket *ss, PRUint8 *b, PRUint32 length)
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);
@@ -4184,7 +4239,8 @@ tls13_HandleCertificateVerify(sslSocket *ss, PRUint8 *b, PRUint32 length)
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;
@@ -5138,6 +5194,7 @@ static const struct {
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,
diff --git a/lib/ssl/tls13exthandle.c b/lib/ssl/tls13exthandle.c
index 9d7c50efb..1f88016a1 100644
--- a/lib/ssl/tls13exthandle.c
+++ b/lib/ssl/tls13exthandle.c
@@ -14,6 +14,7 @@
#include "ssl3exthandle.h"
#include "tls13esni.h"
#include "tls13exthandle.h"
+#include "tls13subcerts.h"
SECStatus
tls13_ServerSendStatusRequestXtn(const sslSocket *ss, TLSExtensionData *xtnData,
@@ -1400,3 +1401,94 @@ tls13_ClientCheckEsniXtn(sslSocket *ss)
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);
+}
diff --git a/lib/ssl/tls13exthandle.h b/lib/ssl/tls13exthandle.h
index d36a4f935..abc7f9deb 100644
--- a/lib/ssl/tls13exthandle.h
+++ b/lib/ssl/tls13exthandle.h
@@ -99,5 +99,14 @@ SECStatus tls13_ClientSendPostHandshakeAuthXtn(const sslSocket *ss,
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
diff --git a/lib/ssl/tls13subcerts.c b/lib/ssl/tls13subcerts.c
new file mode 100644
index 000000000..0610fae73
--- /dev/null
+++ b/lib/ssl/tls13subcerts.c
@@ -0,0 +1,594 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nss.h"
+#include "pk11func.h"
+#include "secder.h"
+#include "ssl.h"
+#include "sslproto.h"
+#include "sslimpl.h"
+#include "ssl3exthandle.h"
+#include "tls13exthandle.h"
+#include "tls13hkdf.h"
+#include "tls13subcerts.h"
+
+/* Parses the delegated credential (DC) from the raw extension |b| of length
+ * |length|. Memory for the DC is allocated and set to |*dcp|.
+ *
+ * It's the caller's responsibility to invoke |tls13_DestroyDelegatedCredential|
+ * when this data is no longer needed.
+ */
+SECStatus
+tls13_ReadDelegatedCredential(PRUint8 *b, PRUint32 length,
+ sslDelegatedCredential **dcp)
+{
+ sslDelegatedCredential *dc = NULL;
+ SECStatus rv;
+ PRUint64 n;
+ sslReadBuffer tmp;
+ sslReader rdr = SSL_READER(b, length);
+
+ PORT_Assert(!*dcp);
+
+ dc = PORT_ZNew(sslDelegatedCredential);
+ if (!dc) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+
+ /* Read the valid_time field of DelegatedCredential.cred. */
+ rv = sslRead_ReadNumber(&rdr, 4, &n);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+ dc->validTime = n;
+
+ /* Read the expected_cert_verify_algorithm field of
+ * DelegatedCredential.cred. */
+ rv = sslRead_ReadNumber(&rdr, 2, &n);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+ dc->expectedCertVerifyAlg = n;
+
+ /* Read the ASN1_subjectPublicKeyInfo field of DelegatedCredential.cred. */
+ rv = sslRead_ReadVariable(&rdr, 3, &tmp);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+ rv = SECITEM_MakeItem(NULL, &dc->derSpki, tmp.buf, tmp.len);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+
+ /* Parse the DER-encoded SubjectPublicKeyInfo. */
+ dc->spki = SECKEY_DecodeDERSubjectPublicKeyInfo(&dc->derSpki);
+ if (!dc->spki) {
+ goto loser;
+ }
+
+ /* Read the algorithm field of the DelegatedCredential. */
+ rv = sslRead_ReadNumber(&rdr, 2, &n);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+ dc->alg = n;
+
+ /* Read the signature field of the DelegatedCredential. */
+ rv = sslRead_ReadVariable(&rdr, 2, &tmp);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+ rv = SECITEM_MakeItem(NULL, &dc->signature, tmp.buf, tmp.len);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+
+ /* There should be nothing left to read. */
+ if (SSL_READER_REMAINING(&rdr) > 0) {
+ goto loser;
+ }
+
+ *dcp = dc;
+ return SECSuccess;
+
+loser:
+ tls13_DestroyDelegatedCredential(dc);
+ *dcp = NULL;
+ return SECFailure;
+}
+
+/* Frees |dc| from the heap. */
+void
+tls13_DestroyDelegatedCredential(sslDelegatedCredential *dc)
+{
+ if (!dc) {
+ return;
+ }
+
+ SECKEY_DestroySubjectPublicKeyInfo(dc->spki);
+ SECITEM_FreeItem(&dc->derSpki, PR_FALSE);
+ SECITEM_FreeItem(&dc->signature, PR_FALSE);
+ PORT_ZFree(dc, sizeof(sslDelegatedCredential));
+}
+
+/* Sets |*certVerifyAlg| to the expected_cert_verify_algorithm field from the
+ * serialized DC |in|. Returns SECSuccess upon success; SECFailure indicates a
+ * decoding failure or the input wasn't long enough.
+ */
+static SECStatus
+tls13_GetExpectedCertVerifyAlg(SECItem in, SSLSignatureScheme *certVerifyAlg)
+{
+ SECStatus rv;
+ PRUint64 n;
+ sslReader rdr = SSL_READER(in.data, in.len);
+
+ if (in.len < 6) { /* Buffer too short to contain the first two params. */
+ return SECFailure;
+ }
+
+ rv = sslRead_ReadNumber(&rdr, 4, &n);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+
+ rv = sslRead_ReadNumber(&rdr, 2, &n);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+ *certVerifyAlg = n;
+
+ return SECSuccess;
+}
+
+/* Returns PR_TRUE if the host is verifying the handshake with a DC. */
+PRBool
+tls13_IsVerifyingWithDelegatedCredential(const sslSocket *ss)
+{
+ /* As of draft-ietf-subcerts-03, only the server may authenticate itself
+ * with a DC.
+ */
+ if (ss->sec.isServer ||
+ !ss->opt.enableDelegatedCredentials ||
+ !ss->xtnData.peerDelegCred) {
+ return PR_FALSE;
+ }
+
+ return PR_TRUE;
+}
+
+/* Returns PR_TRUE if the host is signing the handshake with a DC. */
+PRBool
+tls13_IsSigningWithDelegatedCredential(const sslSocket *ss)
+{
+ if (!ss->sec.isServer ||
+ !ss->xtnData.sendingDelegCredToPeer ||
+ !ss->xtnData.peerRequestedDelegCred) {
+ return PR_FALSE;
+ }
+
+ return PR_TRUE;
+}
+
+/* Commits to authenticating with a DC if all of the following conditions hold:
+ * - the negotiated protocol is TLS 1.3 or newer;
+ * - the selected certificate has a DC configured;
+ * - the peer has indicated support for this extension;
+ * - the peer has indicated support for the DC signature scheme; and
+ * - the host supports the DC signature scheme.
+ *
+ * It's the caller's responsibility to ensure that the version has been
+ * negotiated and the certificate has been selected.
+ */
+SECStatus
+tls13_MaybeSetDelegatedCredential(sslSocket *ss)
+{
+ SECStatus rv;
+ PRBool doesRsaPss;
+ SECKEYPrivateKey *priv;
+ SSLSignatureScheme scheme;
+
+ /* Assert that the host is the server (as of draft-ietf-subcerts-03, only
+ * the server may authenticate itself with a DC), the certificate has been
+ * chosen, TLS 1.3 or higher has been negotiated, and that the set of
+ * signature schemes supported by the client is known.
+ */
+ PORT_Assert(ss->sec.isServer);
+ PORT_Assert(ss->sec.serverCert);
+ PORT_Assert(ss->version >= SSL_LIBRARY_VERSION_TLS_1_3);
+ PORT_Assert(ss->xtnData.sigSchemes);
+
+ /* Check that the peer has indicated support and that a DC has been
+ * configured for the selected certificate.
+ */
+ if (!ss->xtnData.peerRequestedDelegCred ||
+ !ss->sec.serverCert->delegCred.len ||
+ !ss->sec.serverCert->delegCredKeyPair) {
+ return SECSuccess;
+ }
+
+ /* Check that the host and peer both support the signing algorithm used with
+ * the DC.
+ */
+ rv = tls13_GetExpectedCertVerifyAlg(ss->sec.serverCert->delegCred,
+ &scheme);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+
+ priv = ss->sec.serverCert->delegCredKeyPair->privKey;
+ rv = ssl_PrivateKeySupportsRsaPss(priv, &doesRsaPss);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+
+ if (!ssl_SignatureSchemeEnabled(ss, scheme) ||
+ !ssl_CanUseSignatureScheme(scheme,
+ ss->xtnData.sigSchemes,
+ ss->xtnData.numSigSchemes,
+ PR_FALSE /* requireSha1 */,
+ doesRsaPss)) {
+ return SECSuccess;
+ }
+
+ /* Commit to sending a DC and set the handshake signature scheme to the
+ * indicated algorithm.
+ */
+ ss->xtnData.sendingDelegCredToPeer = PR_TRUE;
+ ss->ssl3.hs.signatureScheme = scheme;
+ return SECSuccess;
+}
+
+/* Serializes the DC up to the signature. */
+static SECStatus
+tls13_AppendCredentialParams(sslBuffer *buf, sslDelegatedCredential *dc)
+{
+ SECStatus rv;
+ rv = sslBuffer_AppendNumber(buf, dc->validTime, 4);
+ if (rv != SECSuccess) {
+ return SECFailure; /* Error set by caller. */
+ }
+
+ rv = sslBuffer_AppendNumber(buf, dc->expectedCertVerifyAlg, 2);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+
+ rv = sslBuffer_AppendVariable(buf, dc->derSpki.data, dc->derSpki.len, 3);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+
+ rv = sslBuffer_AppendNumber(buf, dc->alg, 2);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+
+ return SECSuccess;
+}
+
+/* Serializes the DC signature. */
+static SECStatus
+tls13_AppendCredentialSignature(sslBuffer *buf, sslDelegatedCredential *dc)
+{
+ SECStatus rv;
+ rv = sslBuffer_AppendVariable(buf, dc->signature.data,
+ dc->signature.len, 2);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+
+ return SECSuccess;
+}
+
+/* Hashes the message used to sign/verify the DC. */
+static SECStatus
+tls13_HashCredentialSignatureMessage(SSL3Hashes *hash,
+ SSLSignatureScheme scheme,
+ const CERTCertificate *cert,
+ const sslBuffer *dcBuf)
+{
+ SECStatus rv;
+ PK11Context *ctx = NULL;
+ unsigned int hashLen;
+
+ /* Set up hash context. */
+ hash->hashAlg = ssl_SignatureSchemeToHashType(scheme);
+ ctx = PK11_CreateDigestContext(ssl3_HashTypeToOID(hash->hashAlg));
+ if (!ctx) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+
+ static const PRUint8 kCtxStrPadding[64] = {
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ };
+
+ static const PRUint8 kCtxStr[] = "TLS, server delegated credentials";
+
+ /* Hash the message signed by the peer. */
+ rv = SECSuccess;
+ rv |= PK11_DigestBegin(ctx);
+ rv |= PK11_DigestOp(ctx, kCtxStrPadding, sizeof kCtxStrPadding);
+ rv |= PK11_DigestOp(ctx, kCtxStr, 1 /* 0-byte */ + strlen((const char *)kCtxStr));
+ rv |= PK11_DigestOp(ctx, cert->derCert.data, cert->derCert.len);
+ rv |= PK11_DigestOp(ctx, dcBuf->buf, dcBuf->len);
+ rv |= PK11_DigestFinal(ctx, hash->u.raw, &hashLen, sizeof hash->u.raw);
+ if (rv != SECSuccess) {
+ PORT_SetError(SSL_ERROR_SHA_DIGEST_FAILURE);
+ goto loser;
+ }
+
+ hash->len = hashLen;
+ if (ctx) {
+ PK11_DestroyContext(ctx, PR_TRUE);
+ }
+ return SECSuccess;
+
+loser:
+ if (ctx) {
+ PK11_DestroyContext(ctx, PR_TRUE);
+ }
+ return SECFailure;
+}
+
+/* Verifies the DC signature. */
+static SECStatus
+tls13_VerifyCredentialSignature(sslSocket *ss, sslDelegatedCredential *dc)
+{
+ SECStatus rv = SECSuccess;
+ SSL3Hashes hash;
+ sslBuffer dcBuf = SSL_BUFFER_EMPTY;
+ CERTCertificate *cert = ss->sec.peerCert;
+
+ /* Serialize the DC parameters. */
+ rv = tls13_AppendCredentialParams(&dcBuf, dc);
+ if (rv != SECSuccess) {
+ goto loser; /* Error set by caller. */
+ }
+
+ /* Hash the message that was signed by the delegator. */
+ rv = tls13_HashCredentialSignatureMessage(&hash, dc->alg, cert, &dcBuf);
+ if (rv != SECSuccess) {
+ FATAL_ERROR(ss, PORT_GetError(), internal_error);
+ goto loser;
+ }
+
+ /* Verify the signature of the message. */
+ rv = ssl3_VerifySignedHashesWithSpki(
+ ss, &cert->subjectPublicKeyInfo, dc->alg, &hash, &dc->signature);
+ if (rv != SECSuccess) {
+ FATAL_ERROR(ss, SSL_ERROR_DC_BAD_SIGNATURE, illegal_parameter);
+ goto loser;
+ }
+
+ sslBuffer_Clear(&dcBuf);
+ return SECSuccess;
+
+loser:
+ sslBuffer_Clear(&dcBuf);
+ return SECFailure;
+}
+
+/* Checks that the peer's end-entity certificate has the correct key usage. */
+static SECStatus
+tls13_CheckCertDelegationUsage(sslSocket *ss)
+{
+ int i;
+ PRBool found;
+ CERTCertExtension *ext;
+ SECItem delegUsageOid = { siBuffer, NULL, 0 };
+ const CERTCertificate *cert = ss->sec.peerCert;
+
+ /* 1.3.6.1.4.1.44363.44, as defined in draft-ietf-tls-subcerts. */
+ static unsigned char kDelegationUsageOid[] = {
+ 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0xda, 0x4b, 0x2c,
+ };
+
+ delegUsageOid.data = kDelegationUsageOid;
+ delegUsageOid.len = sizeof kDelegationUsageOid;
+
+ /* The certificate must have the delegationUsage extension that authorizes
+ * it to negotiate delegated credentials.
+ */
+ found = PR_FALSE;
+ for (i = 0; cert->extensions[i] != NULL; i++) {
+ ext = cert->extensions[i];
+ if (SECITEM_CompareItem(&ext->id, &delegUsageOid) == SECEqual) {
+ found = PR_TRUE;
+ break;
+ }
+ }
+
+ /* The certificate must also have the digitalSignature keyUsage set. */
+ if (!found ||
+ !cert->keyUsagePresent ||
+ !(cert->keyUsage & KU_DIGITAL_SIGNATURE)) {
+ FATAL_ERROR(ss, SSL_ERROR_DC_INVALID_KEY_USAGE, illegal_parameter);
+ return SECFailure;
+ }
+
+ return SECSuccess;
+}
+
+static SECStatus
+tls13_CheckCredentialExpiration(sslSocket *ss, sslDelegatedCredential *dc)
+{
+ SECStatus rv;
+ PRTime start, end /* microseconds */;
+ CERTCertificate *cert = ss->sec.peerCert;
+
+ rv = DER_DecodeTimeChoice(&start, &cert->validity.notBefore);
+ if (rv != SECSuccess) {
+ FATAL_ERROR(ss, PORT_GetError(), internal_error);
+ return SECFailure;
+ }
+
+ end = start + ((PRTime)dc->validTime * PR_USEC_PER_SEC);
+ if (ssl_Time(ss) > end) {
+ FATAL_ERROR(ss, SSL_ERROR_DC_EXPIRED, illegal_parameter);
+ return SECFailure;
+ }
+
+ return SECSuccess;
+}
+
+/* Returns SECSucces if |dc| is a DC for the current handshake; otherwise it
+ * returns SECFailure. A valid DC meets three requirements: (1) the signature
+ * was produced by the peer's end-entity certificate, (2) the end-entity
+ * certificate must have the correct key usage, and (3) the DC must not be
+ * expired.
+ *
+ * This function calls FATAL_ERROR() when an error occurs.
+ */
+SECStatus
+tls13_VerifyDelegatedCredential(sslSocket *ss,
+ sslDelegatedCredential *dc)
+{
+ SECStatus rv;
+ PRTime start;
+ PRExplodedTime end;
+ CERTCertificate *cert = ss->sec.peerCert;
+ char endStr[256];
+
+ rv = DER_DecodeTimeChoice(&start, &cert->validity.notBefore);
+ if (rv != SECSuccess) {
+ FATAL_ERROR(ss, PORT_GetError(), internal_error);
+ return SECFailure;
+ }
+
+ PR_ExplodeTime(start + (dc->validTime * PR_USEC_PER_SEC),
+ PR_GMTParameters, &end);
+ if (PR_FormatTime(endStr, sizeof(endStr), "%a %b %d %H:%M:%S %Y", &end)) {
+ SSL_TRC(20, ("%d: TLS13[%d]: Received delegated credential (expires %s)",
+ SSL_GETPID(), ss->fd, endStr));
+ } else {
+ SSL_TRC(20, ("%d: TLS13[%d]: Received delegated credential",
+ SSL_GETPID(), ss->fd));
+ }
+
+ rv = SECSuccess;
+ rv |= tls13_VerifyCredentialSignature(ss, dc);
+ rv |= tls13_CheckCertDelegationUsage(ss);
+ rv |= tls13_CheckCredentialExpiration(ss, dc);
+ return rv;
+}
+
+/* Returns a serialized DC with the given parameters.
+ *
+ * Note that this function is meant primarily for testing. In particular, it
+ * DOES NOT verify any of the following:
+ * - |certPriv| is the private key corresponding to |cert|;
+ * - that |checkCertKeyUsage(cert) == SECSuccess|;
+ * - |dcCertVerifyAlg| is consistent with |dcPub|;
+ * - |dcValidFor| is less than 7 days (the maximum permitted by the spec); or
+ * - validTime doesn't overflow a PRUint32.
+ *
+ * These conditions are things we want to test for, which is why we allow them
+ * here. A real API for creating DCs would want to explicitly check ALL of these
+ * conditions are met.
+ */
+SECStatus
+SSLExp_DelegateCredential(const CERTCertificate *cert,
+ const SECKEYPrivateKey *certPriv,
+ const SECKEYPublicKey *dcPub,
+ SSLSignatureScheme dcCertVerifyAlg,
+ PRUint32 dcValidFor,
+ PRTime now,
+ SECItem *out)
+{
+ SECStatus rv;
+ SSL3Hashes hash;
+ SECItem *tmp = NULL;
+ SECKEYPrivateKey *tmpPriv = NULL;
+ sslDelegatedCredential *dc = NULL;
+ sslBuffer dcBuf = SSL_BUFFER_EMPTY;
+
+ dc = PORT_ZNew(sslDelegatedCredential);
+ if (!dc) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+
+ /* Serialize the DC parameters. */
+ PRTime start;
+ rv = DER_DecodeTimeChoice(&start, &cert->validity.notBefore);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+ dc->validTime = ((now - start) / PR_USEC_PER_SEC) + dcValidFor;
+ dc->expectedCertVerifyAlg = dcCertVerifyAlg;
+
+ tmp = SECKEY_EncodeDERSubjectPublicKeyInfo(dcPub);
+ if (!tmp) {
+ goto loser;
+ }
+ /* Transfer |tmp| into |dc->derSpki|. */
+ dc->derSpki.type = tmp->type;
+ dc->derSpki.data = tmp->data;
+ dc->derSpki.len = tmp->len;
+ PORT_Free(tmp);
+
+ rv = ssl_SignatureSchemeFromSpki(&cert->subjectPublicKeyInfo,
+ PR_TRUE /* isTls13 */, &dc->alg);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+ PORT_Assert(dc->alg != ssl_sig_none);
+
+ rv = tls13_AppendCredentialParams(&dcBuf, dc);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+
+ /* Hash signature message. */
+ rv = tls13_HashCredentialSignatureMessage(&hash, dc->alg, cert, &dcBuf);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+
+ /* Sign the hash with the delegation key.
+ *
+ * The PK11 API discards const qualifiers, so we have to make a copy of
+ * |certPriv| and pass the copy to |ssl3_SignHashesWithPrivKey|.
+ */
+ tmpPriv = SECKEY_CopyPrivateKey(certPriv);
+ rv = ssl3_SignHashesWithPrivKey(&hash, tmpPriv, dc->alg,
+ PR_TRUE /* isTls */, &dc->signature);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+
+ /* Serialize the DC signature. */
+ rv = tls13_AppendCredentialSignature(&dcBuf, dc);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+
+ /* Copy the serialized DC to |out|. */
+ rv = SECITEM_MakeItem(NULL, out, dcBuf.buf, dcBuf.len);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+
+ SECKEY_DestroyPrivateKey(tmpPriv);
+ tls13_DestroyDelegatedCredential(dc);
+ sslBuffer_Clear(&dcBuf);
+ return SECSuccess;
+
+loser:
+ SECKEY_DestroyPrivateKey(tmpPriv);
+ tls13_DestroyDelegatedCredential(dc);
+ sslBuffer_Clear(&dcBuf);
+ return SECFailure;
+}
diff --git a/lib/ssl/tls13subcerts.h b/lib/ssl/tls13subcerts.h
new file mode 100644
index 000000000..ce9996bb8
--- /dev/null
+++ b/lib/ssl/tls13subcerts.h
@@ -0,0 +1,56 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is PRIVATE to SSL.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef __tls13subcerts_h_
+#define __tls13subcerts_h_
+
+struct sslDelegatedCredentialStr {
+ /* The number of seconds for which the delegated credential (DC) is valid
+ * following the notBefore parameter of the delegation certificate.
+ */
+ PRUint32 validTime;
+
+ /* The signature algorithm of the DC public key. This expected to the same
+ * as CertificateVerify.scheme.
+ */
+ SSLSignatureScheme expectedCertVerifyAlg;
+
+ /* The DER-encoded SubjectPublicKeyInfo, the DC public key.
+ */
+ SECItem derSpki;
+
+ /* The decoded SubjectPublicKeyInfo parsed from |derSpki|. */
+ CERTSubjectPublicKeyInfo *spki;
+
+ /* The signature algorithm used to verify the DC signature. */
+ SSLSignatureScheme alg;
+
+ /* The DC signature. */
+ SECItem signature;
+};
+
+SECStatus tls13_ReadDelegatedCredential(PRUint8 *b,
+ PRUint32 length,
+ sslDelegatedCredential **dcp);
+void tls13_DestroyDelegatedCredential(sslDelegatedCredential *dc);
+
+PRBool tls13_IsVerifyingWithDelegatedCredential(const sslSocket *ss);
+PRBool tls13_IsSigningWithDelegatedCredential(const sslSocket *ss);
+SECStatus tls13_MaybeSetDelegatedCredential(sslSocket *ss);
+SECStatus tls13_VerifyDelegatedCredential(sslSocket *ss,
+ sslDelegatedCredential *dc);
+
+SECStatus SSLExp_DelegateCredential(const CERTCertificate *cert,
+ const SECKEYPrivateKey *certPriv,
+ const SECKEYPublicKey *dcPub,
+ SSLSignatureScheme dcCertVerifyAlg,
+ PRUint32 dcValidFor,
+ PRTime now,
+ SECItem *out);
+
+#endif
diff --git a/tests/common/certsetup.sh b/tests/common/certsetup.sh
index f9ee459e1..32c6bc235 100644
--- a/tests/common/certsetup.sh
+++ b/tests/common/certsetup.sh
@@ -46,6 +46,11 @@ make_cert() {
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
diff --git a/tests/ssl_gtests/ssl_gtests.sh b/tests/ssl_gtests/ssl_gtests.sh
index 6c088d8a6..d3a10303e 100755
--- a/tests/ssl_gtests/ssl_gtests.sh
+++ b/tests/ssl_gtests/ssl_gtests.sh
@@ -57,6 +57,7 @@ ssl_gtest_certs() {
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 ##########################