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