diff options
author | Christopher Patton <chrispatton@gmail.com> | 2019-06-25 14:21:59 +1000 |
---|---|---|
committer | Christopher Patton <chrispatton@gmail.com> | 2019-06-25 14:21:59 +1000 |
commit | 62b1e1d91c19c50a5e4c2540fcf49f0d8cf073dd (patch) | |
tree | 0a740fbe2b15a3970fa0c373c74294d8202a68eb | |
parent | 89715eee6c2dbc17820977e466fb8b52a1e12784 (diff) | |
download | nss-hg-62b1e1d91c19c50a5e4c2540fcf49f0d8cf073dd.tar.gz |
Bug 1540403 - draft-ietf-tls-subcerts-03, r=mt,jcj
Differential Revision: https://phabricator.services.mozilla.com/D25654
32 files changed, 1522 insertions, 66 deletions
diff --git a/automation/abi-check/expected-report-libssl3.so.txt b/automation/abi-check/expected-report-libssl3.so.txt index e69de29bb..9f2f796b1 100644 --- a/automation/abi-check/expected-report-libssl3.so.txt +++ b/automation/abi-check/expected-report-libssl3.so.txt @@ -0,0 +1,22 @@ + +2 functions with some indirect sub-type change: + + [C]'function SECStatus SSL_ConfigServerCert(PRFileDesc*, CERTCertificate*, SECKEYPrivateKey*, const SSLExtraServerCertData*, unsigned int)' at sslcert.c:640:1 has some indirect sub-type changes: + parameter 4 of type 'const SSLExtraServerCertData*' has sub-type changes: + in pointed to type 'const SSLExtraServerCertData': + in unqualified underlying type 'typedef SSLExtraServerCertData' at sslt.h:291:1: + underlying type 'struct SSLExtraServerCertDataStr' at sslt.h:256:1 changed: + type size changed from 256 to 384 (in bits) + 2 data member insertions: + 'const SECItem* SSLExtraServerCertDataStr::delegCred', at offset 256 (in bits) at sslt.h:283:1 + 'const SECKEYPrivateKey* SSLExtraServerCertDataStr::delegCredPrivKey', at offset 320 (in bits) at sslt.h:290:1 + + [C]'function SECStatus SSL_GetChannelInfo(PRFileDesc*, SSLChannelInfo*, PRUintn)' at sslinfo.c:13:1 has some indirect sub-type changes: + parameter 2 of type 'SSLChannelInfo*' has sub-type changes: + in pointed to type 'typedef SSLChannelInfo' at sslt.h:357:1: + underlying type 'struct SSLChannelInfoStr' at sslt.h:272:1 changed: + type size hasn't changed + 1 data member insertion: + 'PRBool SSLChannelInfoStr::peerDelegCred', at offset 928 (in bits) at sslt.h:353:1 + + diff --git a/cmd/selfserv/selfserv.c b/cmd/selfserv/selfserv.c index 56b5ec28b..f2b1273b3 100644 --- a/cmd/selfserv/selfserv.c +++ b/cmd/selfserv/selfserv.c @@ -1926,7 +1926,7 @@ server_main( for (i = 0; i < certNicknameIndex; i++) { if (cert[i] != NULL) { const SSLExtraServerCertData ocspData = { - ssl_auth_null, NULL, certStatus[i], NULL + ssl_auth_null, NULL, certStatus[i], NULL, NULL, NULL }; secStatus = SSL_ConfigServerCert(model_sock, cert[i], diff --git a/cmd/tstclnt/tstclnt.c b/cmd/tstclnt/tstclnt.c index bc0cbfa76..12c6df045 100644 --- a/cmd/tstclnt/tstclnt.c +++ b/cmd/tstclnt/tstclnt.c @@ -213,6 +213,9 @@ printSecurityInfo(PRFileDesc *fd) " %u\n", scts->len); } + if (channel.peerDelegCred) { + fprintf(stderr, "Received a Delegated Credential\n"); + } } static void @@ -272,6 +275,7 @@ PrintParameterUsage() fprintf(stderr, "%-20s Enable false start.\n", "-g"); fprintf(stderr, "%-20s Enable the cert_status extension (OCSP stapling).\n", "-T"); fprintf(stderr, "%-20s Enable the signed_certificate_timestamp extension.\n", "-U"); + fprintf(stderr, "%-20s Enable the delegated credentials extension.\n", "-B"); fprintf(stderr, "%-20s Require fresh revocation info from side channel.\n" "%-20s -F once means: require for server cert only\n" "%-20s -F twice means: require for intermediates, too\n" @@ -993,6 +997,7 @@ char *versionString = NULL; PRBool handshakeComplete = PR_FALSE; char *encryptedSNIKeys = NULL; PRBool enablePostHandshakeAuth = PR_FALSE; +PRBool enableDelegatedCredentials = PR_FALSE; static int writeBytesToServer(PRFileDesc *s, const PRUint8 *buf, int nb) @@ -1365,6 +1370,14 @@ run() goto done; } + /* enable negotiation of delegated credentials (draft-ietf-tls-subcerts) */ + rv = SSL_OptionSet(s, SSL_ENABLE_DELEGATED_CREDENTIALS, enableDelegatedCredentials); + if (rv != SECSuccess) { + SECU_PrintError(progName, "error enabling delegated credentials"); + error = 1; + goto done; + } + /* enable extended master secret mode */ if (enableExtendedMasterSecret) { rv = SSL_OptionSet(s, SSL_ENABLE_EXTENDED_MASTER_SECRET, PR_TRUE); @@ -1715,12 +1728,11 @@ main(int argc, char **argv) } } - /* Note: 'B' was used in the past but removed in 3.28 - * 'z' was removed in 3.39 + /* Note: 'z' was removed in 3.39 * Please leave some time before reusing these. */ optstate = PL_CreateOptState(argc, argv, - "46A:CDEFGHI:J:KL:M:N:OP:QR:STUV:W:X:YZa:bc:d:fgh:m:n:op:qr:st:uvw:"); + "46A:BCDEFGHI:J:KL:M:N:OP:QR:STUV:W:X:YZa:bc:d:fgh:m:n:op:qr:st:uvw:"); while ((optstatus = PL_GetNextOpt(optstate)) == PL_OPT_OK) { switch (optstate->option) { case '?': @@ -1743,6 +1755,10 @@ main(int argc, char **argv) requestFile = PORT_Strdup(optstate->value); break; + case 'B': + enableDelegatedCredentials = PR_TRUE; + break; + case 'C': ++dumpServerChain; break; diff --git a/gtests/ssl_gtest/manifest.mn b/gtests/ssl_gtest/manifest.mn index 15a971b4d..9fb12421c 100644 --- a/gtests/ssl_gtest/manifest.mn +++ b/gtests/ssl_gtest/manifest.mn @@ -54,6 +54,7 @@ CPPSRCS = \ tls_hkdf_unittest.cc \ tls_filter.cc \ tls_protect.cc \ + tls_subcerts_unittest.cc \ tls_esni_unittest.cc \ $(SSLKEYLOGFILE_FILES) \ $(NULL) diff --git a/gtests/ssl_gtest/ssl_auth_unittest.cc b/gtests/ssl_gtest/ssl_auth_unittest.cc index 08ababbe6..340e98745 100644 --- a/gtests/ssl_gtest/ssl_auth_unittest.cc +++ b/gtests/ssl_gtest/ssl_auth_unittest.cc @@ -1322,11 +1322,11 @@ TEST_P(TlsConnectGeneric, AuthFailImmediate) { } static const SSLExtraServerCertData ServerCertDataRsaPkcs1Decrypt = { - ssl_auth_rsa_decrypt, nullptr, nullptr, nullptr}; + ssl_auth_rsa_decrypt, nullptr, nullptr, nullptr, nullptr, nullptr}; static const SSLExtraServerCertData ServerCertDataRsaPkcs1Sign = { - ssl_auth_rsa_sign, nullptr, nullptr, nullptr}; + ssl_auth_rsa_sign, nullptr, nullptr, nullptr, nullptr, nullptr}; static const SSLExtraServerCertData ServerCertDataRsaPss = { - ssl_auth_rsa_pss, nullptr, nullptr, nullptr}; + ssl_auth_rsa_pss, nullptr, nullptr, nullptr, nullptr, nullptr}; // Test RSA cert with usage=[signature, encipherment]. TEST_F(TlsAgentStreamTestServer, ConfigureCertRsaPkcs1SignAndKEX) { diff --git a/gtests/ssl_gtest/ssl_cert_ext_unittest.cc b/gtests/ssl_gtest/ssl_cert_ext_unittest.cc index 573c69c75..f725faa4c 100644 --- a/gtests/ssl_gtest/ssl_cert_ext_unittest.cc +++ b/gtests/ssl_gtest/ssl_cert_ext_unittest.cc @@ -64,8 +64,8 @@ static const uint8_t kSctValue[] = {0x01, 0x23, 0x45, 0x67, 0x89}; static const SECItem kSctItem = {siBuffer, const_cast<uint8_t*>(kSctValue), sizeof(kSctValue)}; static const DataBuffer kSctBuffer(kSctValue, sizeof(kSctValue)); -static const SSLExtraServerCertData kExtraSctData = {ssl_auth_null, nullptr, - nullptr, &kSctItem}; +static const SSLExtraServerCertData kExtraSctData = { + ssl_auth_null, nullptr, nullptr, &kSctItem, nullptr, nullptr}; // Test timestamps extraction during a successful handshake. TEST_P(TlsConnectGenericPre13, SignedCertificateTimestampsLegacy) { @@ -147,8 +147,8 @@ static const SECItem kOcspItems[] = { {siBuffer, const_cast<uint8_t*>(kOcspValue2), sizeof(kOcspValue2)}}; static const SECItemArray kOcspResponses = {const_cast<SECItem*>(kOcspItems), PR_ARRAY_SIZE(kOcspItems)}; -const static SSLExtraServerCertData kOcspExtraData = {ssl_auth_null, nullptr, - &kOcspResponses, nullptr}; +const static SSLExtraServerCertData kOcspExtraData = { + ssl_auth_null, nullptr, &kOcspResponses, nullptr, nullptr, nullptr}; TEST_P(TlsConnectGeneric, NoOcsp) { EnsureTlsSetup(); @@ -224,7 +224,7 @@ TEST_P(TlsConnectGeneric, OcspHugeSuccess) { const SECItemArray hugeOcspResponses = {const_cast<SECItem*>(hugeOcspItems), PR_ARRAY_SIZE(hugeOcspItems)}; const SSLExtraServerCertData hugeOcspExtraData = { - ssl_auth_null, nullptr, &hugeOcspResponses, nullptr}; + ssl_auth_null, nullptr, &hugeOcspResponses, nullptr, nullptr, nullptr}; // The value should be available during the AuthCertificateCallback client_->SetAuthCertificateCallback([&](TlsAgent* agent, bool checksig, diff --git a/gtests/ssl_gtest/ssl_gtest.gyp b/gtests/ssl_gtest/ssl_gtest.gyp index c0f6c1cd2..63229561b 100644 --- a/gtests/ssl_gtest/ssl_gtest.gyp +++ b/gtests/ssl_gtest/ssl_gtest.gyp @@ -54,7 +54,8 @@ 'tls_filter.cc', 'tls_hkdf_unittest.cc', 'tls_esni_unittest.cc', - 'tls_protect.cc' + 'tls_protect.cc', + 'tls_subcerts_unittest.cc' ], 'dependencies': [ '<(DEPTH)/exports.gyp:nss_exports', diff --git a/gtests/ssl_gtest/tls_agent.cc b/gtests/ssl_gtest/tls_agent.cc index 697da6df2..b958c9a08 100644 --- a/gtests/ssl_gtest/tls_agent.cc +++ b/gtests/ssl_gtest/tls_agent.cc @@ -47,6 +47,7 @@ const std::string TlsAgent::kServerEcdsa521 = "ecdsa521"; const std::string TlsAgent::kServerEcdhRsa = "ecdh_rsa"; const std::string TlsAgent::kServerEcdhEcdsa = "ecdh_ecdsa"; const std::string TlsAgent::kServerDsa = "dsa"; +const std::string TlsAgent::kDelegatorEcdsa256 = "delegator_ecdsa256"; static const uint8_t kCannedTls13ServerHello[] = { 0x03, 0x03, 0x9c, 0xbc, 0x14, 0x9b, 0x0e, 0x2e, 0xfa, 0x0d, 0xf3, @@ -137,6 +138,59 @@ void TlsAgent::SetState(State s) { return true; } +// Loads a key pair from the certificate identified by |id|. +/*static*/ bool TlsAgent::LoadKeyPairFromCert(const std::string& name, + ScopedSECKEYPublicKey* pub, + ScopedSECKEYPrivateKey* priv) { + ScopedCERTCertificate cert; + if (!TlsAgent::LoadCertificate(name, &cert, priv)) { + return false; + } + + pub->reset(SECKEY_ExtractPublicKey(&cert->subjectPublicKeyInfo)); + if (!pub->get()) { + return false; + } + + return true; +} + +void TlsAgent::DelegateCredential(const std::string& name, + const ScopedSECKEYPublicKey& dc_pub, + SSLSignatureScheme dc_cert_verify_alg, + PRUint32 dc_valid_for, PRTime now, + SECItem* dc) { + ScopedCERTCertificate cert; + ScopedSECKEYPrivateKey certPriv; + EXPECT_TRUE(TlsAgent::LoadCertificate(name, &cert, &certPriv)); + EXPECT_EQ(SECSuccess, + SSL_DelegateCredential(cert.get(), certPriv.get(), dc_pub.get(), + dc_cert_verify_alg, dc_valid_for, now, dc)); +} + +void TlsAgent::EnableDelegatedCredentials() { + ASSERT_TRUE(EnsureTlsSetup()); + SetOption(SSL_ENABLE_DELEGATED_CREDENTIALS, PR_TRUE); +} + +void TlsAgent::AddDelegatedCredential(const std::string& dc_name, + SSLSignatureScheme dc_cert_verify_alg, + PRUint32 dc_valid_for, PRTime now) { + ASSERT_TRUE(EnsureTlsSetup()); + + ScopedSECKEYPublicKey pub; + ScopedSECKEYPrivateKey priv; + EXPECT_TRUE(TlsAgent::LoadKeyPairFromCert(dc_name, &pub, &priv)); + + StackSECItem dc; + TlsAgent::DelegateCredential(name_, pub, dc_cert_verify_alg, dc_valid_for, + now, &dc); + + SSLExtraServerCertData extra_data = {ssl_auth_null, nullptr, nullptr, + nullptr, &dc, priv.get()}; + EXPECT_TRUE(ConfigServerCert(name_, true, &extra_data)); +} + bool TlsAgent::ConfigServerCert(const std::string& id, bool updateKeyBits, const SSLExtraServerCertData* serverCertData) { ScopedCERTCertificate cert; @@ -318,6 +372,10 @@ void TlsAgent::CheckCipherSuite(uint16_t suite) { EXPECT_EQ(csinfo_.cipherSuite, suite); } +void TlsAgent::CheckPeerDelegCred(bool expected) { + EXPECT_EQ(expected, info_.peerDelegCred); +} + void TlsAgent::RequestClientAuth(bool requireAuth) { ASSERT_EQ(SERVER, role_); diff --git a/gtests/ssl_gtest/tls_agent.h b/gtests/ssl_gtest/tls_agent.h index 0e1b3df6d..68766a4b2 100644 --- a/gtests/ssl_gtest/tls_agent.h +++ b/gtests/ssl_gtest/tls_agent.h @@ -76,6 +76,7 @@ class TlsAgent : public PollTarget { static const std::string kServerEcdhEcdsa; static const std::string kServerEcdhRsa; static const std::string kServerDsa; + static const std::string kDelegatorEcdsa256; // draft-ietf-tls-subcerts TlsAgent(const std::string& name, Role role, SSLProtocolVariant variant); virtual ~TlsAgent(); @@ -113,6 +114,26 @@ class TlsAgent : public PollTarget { static bool LoadCertificate(const std::string& name, ScopedCERTCertificate* cert, ScopedSECKEYPrivateKey* priv); + static bool LoadKeyPairFromCert(const std::string& name, + ScopedSECKEYPublicKey* pub, + ScopedSECKEYPrivateKey* priv); + + // Delegated credentials. + // + // Generate a delegated credential and sign it using the certificate + // associated with |name|. + static void DelegateCredential(const std::string& name, + const ScopedSECKEYPublicKey& dcPub, + SSLSignatureScheme dcCertVerifyAlg, + PRUint32 dcValidFor, PRTime now, SECItem* dc); + // Indicate support for the delegated credentials extension. + void EnableDelegatedCredentials(); + // Generate and configure a delegated credential to use in the handshake with + // clients that support this extension.. + void AddDelegatedCredential(const std::string& dc_name, + SSLSignatureScheme dcCertVerifyAlg, + PRUint32 dcValidFor, PRTime now); + bool ConfigServerCert(const std::string& name, bool updateKeyBits = false, const SSLExtraServerCertData* serverCertData = nullptr); bool ConfigServerCertWithChain(const std::string& name); @@ -163,6 +184,7 @@ class TlsAgent : public PollTarget { void DisableECDHEServerKeyReuse(); bool GetPeerChainLength(size_t* count); void CheckCipherSuite(uint16_t cipher_suite); + void CheckPeerDelegCred(bool expected); void SetResumptionTokenCallback(); bool MaybeSetResumptionToken(); void SetResumptionToken(const std::vector<uint8_t>& resumption_token) { diff --git a/gtests/ssl_gtest/tls_subcerts_unittest.cc b/gtests/ssl_gtest/tls_subcerts_unittest.cc new file mode 100644 index 000000000..4406ea84c --- /dev/null +++ b/gtests/ssl_gtest/tls_subcerts_unittest.cc @@ -0,0 +1,238 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include <ctime> + +#include "secerr.h" +#include "ssl.h" + +#include "gtest_utils.h" +#include "tls_agent.h" +#include "tls_connect.h" + +namespace nss_test { + +const std::string kDelegatorId = TlsAgent::kDelegatorEcdsa256; +const std::string kDCId = TlsAgent::kServerEcdsa256; +const SSLSignatureScheme kDCScheme = ssl_sig_ecdsa_secp256r1_sha256; +const PRUint32 kDCValidFor = 60 * 60 * 24 * 7 /* 1 week (seconds */; + +// Attempt to configure a DC when either the DC or DC private key is missing. +TEST_P(TlsConnectTls13, DCNotConfigured) { + // Load and delegate the credential. + ScopedSECKEYPublicKey pub; + ScopedSECKEYPrivateKey priv; + EXPECT_TRUE(TlsAgent::LoadKeyPairFromCert(kDCId, &pub, &priv)); + + StackSECItem dc; + TlsAgent::DelegateCredential(kDelegatorId, pub, kDCScheme, kDCValidFor, now(), + &dc); + + // Attempt to install the certificate and DC with a missing DC private key. + EnsureTlsSetup(); + SSLExtraServerCertData extra_data_missing_dc_priv_key = { + ssl_auth_null, nullptr, nullptr, nullptr, &dc, nullptr}; + EXPECT_FALSE(server_->ConfigServerCert(kDelegatorId, true, + &extra_data_missing_dc_priv_key)); + + // Attempt to install the certificate and with only the DC private key. + EnsureTlsSetup(); + SSLExtraServerCertData extra_data_missing_dc = { + ssl_auth_null, nullptr, nullptr, nullptr, nullptr, priv.get()}; + EXPECT_FALSE( + server_->ConfigServerCert(kDelegatorId, true, &extra_data_missing_dc)); +} + +// Connected with ECDSA-P256. +TEST_P(TlsConnectTls13, DCConnectEcdsaP256) { + Reset(kDelegatorId); + client_->EnableDelegatedCredentials(); + server_->AddDelegatedCredential(kDCId, kDCScheme, kDCValidFor, now()); + + auto cfilter = MakeTlsFilter<TlsExtensionCapture>( + client_, ssl_delegated_credentials_xtn); + Connect(); + + EXPECT_TRUE(cfilter->captured()); + client_->CheckPeerDelegCred(true); +} + +// Connected with ECDSA-P521. +TEST_P(TlsConnectTls13, DCConnectEcdsaP521) { + Reset(kDelegatorId); + client_->EnableDelegatedCredentials(); + server_->AddDelegatedCredential(TlsAgent::kServerEcdsa521, + ssl_sig_ecdsa_secp521r1_sha512, kDCValidFor, + now()); + client_->EnableDelegatedCredentials(); + + auto cfilter = MakeTlsFilter<TlsExtensionCapture>( + client_, ssl_delegated_credentials_xtn); + Connect(); + + EXPECT_TRUE(cfilter->captured()); + client_->CheckPeerDelegCred(true); +} + +// Connected with RSA-PSS. +TEST_P(TlsConnectTls13, DCConnectRsaPss) { + Reset(kDelegatorId); + client_->EnableDelegatedCredentials(); + server_->AddDelegatedCredential( + TlsAgent::kServerRsaPss, ssl_sig_rsa_pss_rsae_sha256, kDCValidFor, now()); + + auto cfilter = MakeTlsFilter<TlsExtensionCapture>( + client_, ssl_delegated_credentials_xtn); + Connect(); + + EXPECT_TRUE(cfilter->captured()); + client_->CheckPeerDelegCred(true); +} + +// Aborted because of incorrect DC signature algorithm indication. +TEST_P(TlsConnectTls13, DCAbortBadExpectedCertVerifyAlg) { + Reset(kDelegatorId); + client_->EnableDelegatedCredentials(); + server_->AddDelegatedCredential(TlsAgent::kServerEcdsa256, + ssl_sig_ecdsa_secp521r1_sha512, kDCValidFor, + now()); + ConnectExpectAlert(client_, kTlsAlertIllegalParameter); +} + +// Aborted because of invalid DC signature. +TEST_P(TlsConnectTls13, DCABortBadSignature) { + Reset(kDelegatorId); + EnsureTlsSetup(); + client_->EnableDelegatedCredentials(); + + ScopedSECKEYPublicKey pub; + ScopedSECKEYPrivateKey priv; + EXPECT_TRUE(TlsAgent::LoadKeyPairFromCert(kDCId, &pub, &priv)); + + StackSECItem dc; + TlsAgent::DelegateCredential(kDelegatorId, pub, kDCScheme, kDCValidFor, now(), + &dc); + + // Flip the first bit of the DC so that the signature is invalid. + dc.data[0] ^= 0x01; + + SSLExtraServerCertData extra_data = {ssl_auth_null, nullptr, nullptr, + nullptr, &dc, priv.get()}; + EXPECT_TRUE(server_->ConfigServerCert(kDelegatorId, true, &extra_data)); + + ConnectExpectAlert(client_, kTlsAlertIllegalParameter); +} + +// Aborted because of expired DC. +TEST_P(TlsConnectTls13, DCAbortExpired) { + Reset(kDelegatorId); + server_->AddDelegatedCredential(kDCId, kDCScheme, kDCValidFor, now()); + client_->EnableDelegatedCredentials(); + // When the client checks the time, it will be at least one second after the + // DC expired. + AdvanceTime((static_cast<PRTime>(kDCValidFor) + 1) * PR_USEC_PER_SEC); + ConnectExpectAlert(client_, kTlsAlertIllegalParameter); +} + +// Aborted because of invalid key usage. +TEST_P(TlsConnectTls13, DCAbortBadKeyUsage) { + // The sever does not have the delegationUsage extension. + Reset(TlsAgent::kServerEcdsa256); + client_->EnableDelegatedCredentials(); + server_->AddDelegatedCredential(kDCId, kDCScheme, kDCValidFor, now()); + ConnectExpectAlert(client_, kTlsAlertIllegalParameter); +} + +// Connected without DC because of no client indication. +TEST_P(TlsConnectTls13, DCConnectNoClientSupport) { + Reset(kDelegatorId); + server_->AddDelegatedCredential(kDCId, kDCScheme, kDCValidFor, now()); + + auto cfilter = MakeTlsFilter<TlsExtensionCapture>( + client_, ssl_delegated_credentials_xtn); + Connect(); + + EXPECT_FALSE(cfilter->captured()); + client_->CheckPeerDelegCred(false); +} + +// Connected without DC because of no server DC. +TEST_P(TlsConnectTls13, DCConnectNoServerSupport) { + Reset(kDelegatorId); + client_->EnableDelegatedCredentials(); + + auto cfilter = MakeTlsFilter<TlsExtensionCapture>( + client_, ssl_delegated_credentials_xtn); + Connect(); + + EXPECT_TRUE(cfilter->captured()); + client_->CheckPeerDelegCred(false); +} + +// Connected without DC because client doesn't support TLS 1.3. +TEST_P(TlsConnectTls13, DCConnectClientNoTls13) { + Reset(kDelegatorId); + client_->EnableDelegatedCredentials(); + server_->AddDelegatedCredential(kDCId, kDCScheme, kDCValidFor, now()); + + client_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_2, + SSL_LIBRARY_VERSION_TLS_1_2); + server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_2, + SSL_LIBRARY_VERSION_TLS_1_3); + + auto cfilter = MakeTlsFilter<TlsExtensionCapture>( + client_, ssl_delegated_credentials_xtn); + Connect(); + + // Should fallback to TLS 1.2 and not negotiate a DC. + EXPECT_FALSE(cfilter->captured()); + client_->CheckPeerDelegCred(false); +} + +// Connected without DC because server doesn't support TLS 1.3. +TEST_P(TlsConnectTls13, DCConnectServerNoTls13) { + Reset(kDelegatorId); + client_->EnableDelegatedCredentials(); + server_->AddDelegatedCredential(kDCId, kDCScheme, kDCValidFor, now()); + + client_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_2, + SSL_LIBRARY_VERSION_TLS_1_3); + server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_2, + SSL_LIBRARY_VERSION_TLS_1_2); + + auto cfilter = MakeTlsFilter<TlsExtensionCapture>( + client_, ssl_delegated_credentials_xtn); + Connect(); + + // Should fallback to TLS 1.2 and not negotiate a DC. The client will still + // send the indication because it supports 1.3. + EXPECT_TRUE(cfilter->captured()); + client_->CheckPeerDelegCred(false); +} + +// Connected without DC because client doesn't support the signature scheme. +TEST_P(TlsConnectTls13, DCConnectExpectedCertVerifyAlgNotSupported) { + Reset(kDelegatorId); + client_->EnableDelegatedCredentials(); + static const SSLSignatureScheme kClientSchemes[] = { + ssl_sig_ecdsa_secp256r1_sha256, + }; + client_->SetSignatureSchemes(kClientSchemes, PR_ARRAY_SIZE(kClientSchemes)); + + server_->AddDelegatedCredential(TlsAgent::kServerEcdsa521, + ssl_sig_ecdsa_secp521r1_sha512, kDCValidFor, + now()); + + auto cfilter = MakeTlsFilter<TlsExtensionCapture>( + client_, ssl_delegated_credentials_xtn); + Connect(); + + // Client sends indication, but the server doesn't send a DC. + EXPECT_TRUE(cfilter->captured()); + client_->CheckPeerDelegCred(false); +} + +} // namespace nss_test diff --git a/lib/ssl/manifest.mn b/lib/ssl/manifest.mn index 201a01049..83df8c0b8 100644 --- a/lib/ssl/manifest.mn +++ b/lib/ssl/manifest.mn @@ -57,6 +57,7 @@ CSRCS = \ sslgrp.c \ sslprimitive.c \ tls13esni.c \ + tls13subcerts.c \ $(NULL) LIBRARY_NAME = ssl diff --git a/lib/ssl/ssl.gyp b/lib/ssl/ssl.gyp index 7c89d80c1..ae8f8d94f 100644 --- a/lib/ssl/ssl.gyp +++ b/lib/ssl/ssl.gyp @@ -49,6 +49,7 @@ 'tls13hashstate.c', 'tls13hkdf.c', 'tls13replay.c', + 'tls13subcerts.c', ], 'conditions': [ [ 'OS=="win"', { diff --git a/lib/ssl/ssl.h b/lib/ssl/ssl.h index bfeddeff8..b914aa07f 100644 --- a/lib/ssl/ssl.h +++ b/lib/ssl/ssl.h @@ -310,6 +310,22 @@ SSL_IMPORT PRFileDesc *DTLS_ImportFD(PRFileDesc *model, PRFileDesc *fd); */ #define SSL_ENABLE_POST_HANDSHAKE_AUTH 39 +/* Enables the delegated credentials extension (draft-ietf-tls-subcerts). When + * enabled, a client that supports TLS 1.3 will indicate willingness to + * negotiate a delegated credential (DC). + * + * If support is indicated, the peer may use a DC to authenticate itself. The DC + * is sent as an extension to the peer's end-entity certificate; the end-entity + * certificate is used to verify the DC, which in turn is used to verify the + * handshake. DCs effectively extend the certificate chain by one, but only + * within the context of TLS. Once issued, DCs can't be revoked; in order to + * mitigate the damage in case the secret key is compromised, the DC is only + * valid for a short time (days, hours, or even minutes). + * + * This library implements draft-03 of the protocol spec. + */ +#define SSL_ENABLE_DELEGATED_CREDENTIALS 40 + #ifdef SSL_DEPRECATED_FUNCTION /* Old deprecated function names */ SSL_IMPORT SECStatus SSL_Enable(PRFileDesc *fd, int option, PRIntn on); diff --git a/lib/ssl/ssl3con.c b/lib/ssl/ssl3con.c index 67f162955..eca98bf16 100644 --- a/lib/ssl/ssl3con.c +++ b/lib/ssl/ssl3con.c @@ -1026,15 +1026,13 @@ ssl3_GetNewRandom(SSL3Random random) return rv; } -/* Called by ssl3_SendServerKeyExchange and ssl3_SendCertificateVerify */ SECStatus -ssl3_SignHashes(sslSocket *ss, SSL3Hashes *hash, SECKEYPrivateKey *key, - SECItem *buf) +ssl3_SignHashesWithPrivKey(SSL3Hashes *hash, SECKEYPrivateKey *key, + SSLSignatureScheme scheme, PRBool isTls, SECItem *buf) { SECStatus rv = SECFailure; PRBool doDerEncode = PR_FALSE; - PRBool isTLS = (PRBool)(ss->version > SSL_LIBRARY_VERSION_3_0); - PRBool useRsaPss = ssl_IsRsaPssSignatureScheme(ss->ssl3.hs.signatureScheme); + PRBool useRsaPss = ssl_IsRsaPssSignatureScheme(scheme); SECItem hashItem; buf->data = NULL; @@ -1045,7 +1043,7 @@ ssl3_SignHashes(sslSocket *ss, SSL3Hashes *hash, SECKEYPrivateKey *key, hashItem.len = hash->len; break; case dsaKey: - doDerEncode = isTLS; + doDerEncode = isTls; /* ssl_hash_none is used to specify the MD5/SHA1 concatenated hash. * In that case, we use just the SHA1 part. */ if (hash->hashAlg == ssl_hash_none) { @@ -1122,11 +1120,6 @@ ssl3_SignHashes(sslSocket *ss, SSL3Hashes *hash, SECKEYPrivateKey *key, } } - if (ss->sec.isServer) { - ss->sec.signatureScheme = ss->ssl3.hs.signatureScheme; - ss->sec.authType = - ssl_SignatureSchemeToAuthType(ss->ssl3.hs.signatureScheme); - } PRINT_BUF(60, (NULL, "signed hashes", (unsigned char *)buf->data, buf->len)); done: if (rv != SECSuccess && buf->data) { @@ -1136,10 +1129,33 @@ done: return rv; } -/* Called from ssl3_HandleServerKeyExchange, ssl3_HandleCertificateVerify */ +/* Called by ssl3_SendServerKeyExchange and ssl3_SendCertificateVerify */ SECStatus -ssl3_VerifySignedHashes(sslSocket *ss, SSLSignatureScheme scheme, SSL3Hashes *hash, - SECItem *buf) +ssl3_SignHashes(sslSocket *ss, SSL3Hashes *hash, SECKEYPrivateKey *key, + SECItem *buf) +{ + SECStatus rv = SECFailure; + PRBool isTLS = (PRBool)(ss->version > SSL_LIBRARY_VERSION_3_0); + SSLSignatureScheme scheme = ss->ssl3.hs.signatureScheme; + + rv = ssl3_SignHashesWithPrivKey(hash, key, scheme, isTLS, buf); + if (rv != SECSuccess) { + return SECFailure; + } + + if (ss->sec.isServer) { + ss->sec.signatureScheme = scheme; + ss->sec.authType = ssl_SignatureSchemeToAuthType(scheme); + } + + return SECSuccess; +} + +/* Called from ssl3_VerifySignedHashes and tls13_HandleCertificateVerify. */ +SECStatus +ssl3_VerifySignedHashesWithSpki(sslSocket *ss, CERTSubjectPublicKeyInfo *spki, + SSLSignatureScheme scheme, + SSL3Hashes *hash, SECItem *buf) { SECKEYPublicKey *key; SECItem *signature = NULL; @@ -1153,7 +1169,7 @@ ssl3_VerifySignedHashes(sslSocket *ss, SSLSignatureScheme scheme, SSL3Hashes *ha PRINT_BUF(60, (NULL, "check signed hashes", buf->data, buf->len)); - key = CERT_ExtractPublicKey(ss->sec.peerCert); + key = SECKEY_ExtractPublicKey(spki); if (key == NULL) { ssl_MapLowLevelError(SSL_ERROR_EXTRACT_PUBLIC_KEY_FAILURE); return SECFailure; @@ -1273,6 +1289,16 @@ loser: return rv; } +/* Called from ssl3_HandleServerKeyExchange, ssl3_HandleCertificateVerify */ +SECStatus +ssl3_VerifySignedHashes(sslSocket *ss, SSLSignatureScheme scheme, SSL3Hashes *hash, + SECItem *buf) +{ + CERTSubjectPublicKeyInfo *spki = &ss->sec.peerCert->subjectPublicKeyInfo; + return ssl3_VerifySignedHashesWithSpki( + ss, spki, scheme, hash, buf); +} + /* Caller must set hiLevel error code. */ /* Called from ssl3_ComputeDHKeyHash * which are called from ssl3_HandleServerKeyExchange. @@ -4113,7 +4139,7 @@ ssl_SignatureSchemeValid(SSLSignatureScheme scheme, SECOidTag spkiOid, } static SECStatus -ssl_SignatureSchemeFromPssSpki(CERTSubjectPublicKeyInfo *spki, +ssl_SignatureSchemeFromPssSpki(const CERTSubjectPublicKeyInfo *spki, SSLSignatureScheme *scheme) { SECKEYRSAPSSParams pssParam = { 0 }; @@ -4161,7 +4187,7 @@ loser: } static SECStatus -ssl_SignatureSchemeFromEcSpki(CERTSubjectPublicKeyInfo *spki, +ssl_SignatureSchemeFromEcSpki(const CERTSubjectPublicKeyInfo *spki, SSLSignatureScheme *scheme) { const sslNamedGroupDef *group; @@ -4198,8 +4224,8 @@ ssl_SignatureSchemeFromEcSpki(CERTSubjectPublicKeyInfo *spki, /* Newer signature schemes are designed so that a single SPKI can be used with * that scheme. This determines that scheme from the SPKI. If the SPKI doesn't * have a single scheme, |*scheme| is set to ssl_sig_none. */ -static SECStatus -ssl_SignatureSchemeFromSpki(CERTSubjectPublicKeyInfo *spki, +SECStatus +ssl_SignatureSchemeFromSpki(const CERTSubjectPublicKeyInfo *spki, PRBool isTls13, SSLSignatureScheme *scheme) { SECOidTag spkiOid = SECOID_GetAlgorithmTag(&spki->algorithm); @@ -4219,8 +4245,8 @@ ssl_SignatureSchemeFromSpki(CERTSubjectPublicKeyInfo *spki, return SECSuccess; } -static PRBool -ssl_SignatureSchemeEnabled(sslSocket *ss, SSLSignatureScheme scheme) +PRBool +ssl_SignatureSchemeEnabled(const sslSocket *ss, SSLSignatureScheme scheme) { unsigned int i; for (i = 0; i < ss->ssl3.signatureSchemeCount; ++i) { @@ -4250,21 +4276,20 @@ ssl_SignatureKeyMatchesSpkiOid(const ssl3KEADef *keaDef, SECOidTag spkiOid) } /* ssl3_CheckSignatureSchemeConsistency checks that the signature algorithm - * identifier in |scheme| is consistent with the public key in |cert|. It also + * identifier in |scheme| is consistent with the public key in |spki|. It also * checks the hash algorithm against the configured signature algorithms. If * all the tests pass, SECSuccess is returned. Otherwise, PORT_SetError is * called and SECFailure is returned. */ SECStatus ssl_CheckSignatureSchemeConsistency(sslSocket *ss, SSLSignatureScheme scheme, - CERTCertificate *cert) + CERTSubjectPublicKeyInfo *spki) { SSLSignatureScheme spkiScheme; PRBool isTLS13 = ss->version == SSL_LIBRARY_VERSION_TLS_1_3; SECOidTag spkiOid; SECStatus rv; - rv = ssl_SignatureSchemeFromSpki(&cert->subjectPublicKeyInfo, isTLS13, - &spkiScheme); + rv = ssl_SignatureSchemeFromSpki(spki, isTLS13, &spkiScheme); if (rv != SECSuccess) { return SECFailure; } @@ -4278,7 +4303,7 @@ ssl_CheckSignatureSchemeConsistency(sslSocket *ss, SSLSignatureScheme scheme, return SECSuccess; } - spkiOid = SECOID_GetAlgorithmTag(&cert->subjectPublicKeyInfo.algorithm); + spkiOid = SECOID_GetAlgorithmTag(&spki->algorithm); /* If we're a client, check that the signature algorithm matches the signing * key type of the cipher suite. */ @@ -6080,7 +6105,7 @@ ssl3_SendClientKeyExchange(sslSocket *ss) } /* Used by ssl_PickSignatureScheme(). */ -static PRBool +PRBool ssl_CanUseSignatureScheme(SSLSignatureScheme scheme, const SSLSignatureScheme *peerSchemes, unsigned int peerSchemeCount, @@ -6124,6 +6149,21 @@ ssl_CanUseSignatureScheme(SSLSignatureScheme scheme, } SECStatus +ssl_PrivateKeySupportsRsaPss(SECKEYPrivateKey *privKey, + PRBool *supportsRsaPss) +{ + PK11SlotInfo *slot; + slot = PK11_GetSlotFromPrivateKey(privKey); + if (!slot) { + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + *supportsRsaPss = PK11_DoesMechanism(slot, auth_alg_defs[ssl_auth_rsa_pss]); + PK11_FreeSlot(slot); + return SECSuccess; +} + +SECStatus ssl_PickSignatureScheme(sslSocket *ss, CERTCertificate *cert, SECKEYPublicKey *pubKey, @@ -6133,8 +6173,7 @@ ssl_PickSignatureScheme(sslSocket *ss, PRBool requireSha1) { unsigned int i; - PK11SlotInfo *slot; - PRBool slotDoesPss; + PRBool doesRsaPss; PRBool isTLS13 = ss->version >= SSL_LIBRARY_VERSION_TLS_1_3; SECStatus rv; SSLSignatureScheme scheme; @@ -6148,13 +6187,10 @@ ssl_PickSignatureScheme(sslSocket *ss, return SECFailure; } - slot = PK11_GetSlotFromPrivateKey(privKey); - if (!slot) { - PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + rv = ssl_PrivateKeySupportsRsaPss(privKey, &doesRsaPss); + if (rv != SECSuccess) { return SECFailure; } - slotDoesPss = PK11_DoesMechanism(slot, auth_alg_defs[ssl_auth_rsa_pss]); - PK11_FreeSlot(slot); /* If the certificate SPKI indicates a single scheme, don't search. */ rv = ssl_SignatureSchemeFromSpki(&cert->subjectPublicKeyInfo, @@ -6165,7 +6201,7 @@ ssl_PickSignatureScheme(sslSocket *ss, if (scheme != ssl_sig_none) { if (!ssl_SignatureSchemeEnabled(ss, scheme) || !ssl_CanUseSignatureScheme(scheme, peerSchemes, peerSchemeCount, - requireSha1, slotDoesPss)) { + requireSha1, doesRsaPss)) { PORT_SetError(SSL_ERROR_UNSUPPORTED_SIGNATURE_ALGORITHM); return SECFailure; } @@ -6182,7 +6218,7 @@ ssl_PickSignatureScheme(sslSocket *ss, if (ssl_SignatureSchemeValid(scheme, spkiOid, isTLS13) && ssl_CanUseSignatureScheme(scheme, peerSchemes, peerSchemeCount, - requireSha1, slotDoesPss)) { + requireSha1, doesRsaPss)) { ss->ssl3.hs.signatureScheme = scheme; return SECSuccess; } @@ -7073,8 +7109,8 @@ ssl_HandleDHServerKeyExchange(sslSocket *ss, PRUint8 *b, PRUint32 length) if (rv != SECSuccess) { goto alert_loser; /* malformed or unsupported. */ } - rv = ssl_CheckSignatureSchemeConsistency(ss, sigScheme, - ss->sec.peerCert); + rv = ssl_CheckSignatureSchemeConsistency( + ss, sigScheme, &ss->sec.peerCert->subjectPublicKeyInfo); if (rv != SECSuccess) { goto alert_loser; } @@ -9779,8 +9815,8 @@ ssl3_HandleCertificateVerify(sslSocket *ss, PRUint8 *b, PRUint32 length) if (rv != SECSuccess) { goto loser; /* malformed or unsupported. */ } - rv = ssl_CheckSignatureSchemeConsistency(ss, sigScheme, - ss->sec.peerCert); + rv = ssl_CheckSignatureSchemeConsistency( + ss, sigScheme, &ss->sec.peerCert->subjectPublicKeyInfo); if (rv != SECSuccess) { errCode = PORT_GetError(); desc = illegal_parameter; diff --git a/lib/ssl/ssl3ecc.c b/lib/ssl/ssl3ecc.c index 52d5bb515..d5ad372e5 100644 --- a/lib/ssl/ssl3ecc.c +++ b/lib/ssl/ssl3ecc.c @@ -548,8 +548,8 @@ ssl3_HandleECDHServerKeyExchange(sslSocket *ss, PRUint8 *b, PRUint32 length) errCode = PORT_GetError(); goto alert_loser; /* malformed or unsupported. */ } - rv = ssl_CheckSignatureSchemeConsistency(ss, sigScheme, - ss->sec.peerCert); + rv = ssl_CheckSignatureSchemeConsistency( + ss, sigScheme, &ss->sec.peerCert->subjectPublicKeyInfo); if (rv != SECSuccess) { errCode = PORT_GetError(); goto alert_loser; diff --git a/lib/ssl/ssl3ext.c b/lib/ssl/ssl3ext.c index e9fc096b7..032795bc4 100644 --- a/lib/ssl/ssl3ext.c +++ b/lib/ssl/ssl3ext.c @@ -16,6 +16,7 @@ #include "ssl3exthandle.h" #include "tls13err.h" #include "tls13exthandle.h" +#include "tls13subcerts.h" /* Callback function that handles a received extension. */ typedef SECStatus (*ssl3ExtensionHandlerFunc)(const sslSocket *ss, @@ -45,6 +46,7 @@ static const ssl3ExtensionHandler clientHelloHandlers[] = { { ssl_signature_algorithms_xtn, &ssl3_HandleSigAlgsXtn }, { ssl_extended_master_secret_xtn, &ssl3_HandleExtendedMasterSecretXtn }, { ssl_signed_cert_timestamp_xtn, &ssl3_ServerHandleSignedCertTimestampXtn }, + { ssl_delegated_credentials_xtn, &tls13_ServerHandleDelegatedCredentialsXtn }, { ssl_tls13_key_share_xtn, &tls13_ServerHandleKeyShareXtn }, { ssl_tls13_pre_shared_key_xtn, &tls13_ServerHandlePreSharedKeyXtn }, { ssl_tls13_early_data_xtn, &tls13_ServerHandleEarlyDataXtn }, @@ -96,6 +98,7 @@ static const ssl3ExtensionHandler newSessionTicketHandlers[] = { static const ssl3ExtensionHandler serverCertificateHandlers[] = { { ssl_signed_cert_timestamp_xtn, &ssl3_ClientHandleSignedCertTimestampXtn }, { ssl_cert_status_xtn, &ssl3_ClientHandleStatusRequestXtn }, + { ssl_delegated_credentials_xtn, &tls13_ClientHandleDelegatedCredentialsXtn }, { 0, NULL } }; @@ -127,6 +130,7 @@ static const sslExtensionBuilder clientHelloSendersTLS[] = { ssl_app_layer_protocol_xtn, &ssl3_ClientSendAppProtoXtn }, { ssl_use_srtp_xtn, &ssl3_ClientSendUseSRTPXtn }, { ssl_cert_status_xtn, &ssl3_ClientSendStatusRequestXtn }, + { ssl_delegated_credentials_xtn, &tls13_ClientSendDelegatedCredentialsXtn }, { ssl_signed_cert_timestamp_xtn, &ssl3_ClientSendSignedCertTimestampXtn }, { ssl_tls13_key_share_xtn, &tls13_ClientSendKeyShareXtn }, { ssl_tls13_early_data_xtn, &tls13_ClientSendEarlyDataXtn }, @@ -170,6 +174,7 @@ static const struct { } ssl_supported_extensions[] = { { ssl_server_name_xtn, ssl_ext_native_only }, { ssl_cert_status_xtn, ssl_ext_native }, + { ssl_delegated_credentials_xtn, ssl_ext_native }, { ssl_supported_groups_xtn, ssl_ext_native_only }, { ssl_ec_point_formats_xtn, ssl_ext_native }, { ssl_signature_algorithms_xtn, ssl_ext_native_only }, @@ -951,6 +956,9 @@ ssl3_InitExtensionData(TLSExtensionData *xtnData, const sslSocket *ss) ++advertisedMax; } xtnData->advertised = PORT_ZNewArray(PRUint16, advertisedMax); + xtnData->peerDelegCred = NULL; + xtnData->peerRequestedDelegCred = PR_FALSE; + xtnData->sendingDelegCredToPeer = PR_FALSE; } void @@ -969,6 +977,7 @@ ssl3_DestroyExtensionData(TLSExtensionData *xtnData) PORT_Free(xtnData->advertised); ssl_FreeEphemeralKeyPair(xtnData->esniPrivateKey); SECITEM_FreeItem(&xtnData->keyShareExtension, PR_FALSE); + tls13_DestroyDelegatedCredential(xtnData->peerDelegCred); } /* Free everything that has been allocated and then reset back to diff --git a/lib/ssl/ssl3ext.h b/lib/ssl/ssl3ext.h index d96b4cffe..97319c7d9 100644 --- a/lib/ssl/ssl3ext.h +++ b/lib/ssl/ssl3ext.h @@ -111,6 +111,19 @@ struct TLSExtensionDataStr { /* Pointer into |ss->esniKeys->keyShares| */ TLS13KeyShareEntry *peerEsniShare; PRUint8 esniNonce[TLS13_ESNI_NONCE_SIZE]; + + /* Delegated credentials. + * + * The delegated credential sent by the peer. Set by + * |tls13_ReadDelegatedCredential|. + */ + sslDelegatedCredential *peerDelegCred; + /* Whether the peer requested a delegated credential. */ + PRBool peerRequestedDelegCred; + /* Whether the host is committed to using a delegated credential. Set by + * |tls13_MaybeSetDelegatedCredential|. + */ + PRBool sendingDelegCredToPeer; }; typedef struct TLSExtensionStr { diff --git a/lib/ssl/sslcert.c b/lib/ssl/sslcert.c index 878df761e..d2c9480d5 100644 --- a/lib/ssl/sslcert.c +++ b/lib/ssl/sslcert.c @@ -8,10 +8,11 @@ #include "ssl.h" #include "sslimpl.h" -#include "secoid.h" /* for SECOID_GetAlgorithmTag */ -#include "pk11func.h" /* for PK11_ReferenceSlot */ -#include "nss.h" /* for NSS_RegisterShutdown */ -#include "prinit.h" /* for PR_CallOnceWithArg */ +#include "secoid.h" /* for SECOID_GetAlgorithmTag */ +#include "pk11func.h" /* for PK11_ReferenceSlot */ +#include "nss.h" /* for NSS_RegisterShutdown */ +#include "prinit.h" /* for PR_CallOnceWithArg */ +#include "tls13subcerts.h" /* for tls13_ReadDelegatedCredential */ /* This global item is used only in servers. It is is initialized by * SSL_ConfigSecureServer(), and is used in ssl3_SendCertificateRequest(). @@ -102,6 +103,8 @@ ssl_NewServerCert() sc->serverCertChain = NULL; sc->certStatusArray = NULL; sc->signedCertTimestamps.len = 0; + sc->delegCred.len = 0; + sc->delegCredKeyPair = NULL; return sc; } @@ -148,8 +151,17 @@ ssl_CopyServerCert(const sslServerCert *oc) } if (SECITEM_CopyItem(NULL, &sc->signedCertTimestamps, - &oc->signedCertTimestamps) != SECSuccess) + &oc->signedCertTimestamps) != SECSuccess) { goto loser; + } + + if (SECITEM_CopyItem(NULL, &sc->delegCred, &oc->delegCred) != SECSuccess) { + goto loser; + } + if (oc->delegCredKeyPair) { + sc->delegCredKeyPair = ssl_GetKeyPairRef(oc->delegCredKeyPair); + } + return sc; loser: ssl_FreeServerCert(sc); @@ -178,6 +190,12 @@ ssl_FreeServerCert(sslServerCert *sc) if (sc->signedCertTimestamps.len) { SECITEM_FreeItem(&sc->signedCertTimestamps, PR_FALSE); } + if (sc->delegCred.len) { + SECITEM_FreeItem(&sc->delegCred, PR_FALSE); + } + if (sc->delegCredKeyPair) { + ssl_FreeKeyPair(sc->delegCredKeyPair); + } PORT_ZFree(sc, sizeof(*sc)); } @@ -309,6 +327,79 @@ ssl_PopulateSignedCertTimestamps(sslServerCert *sc, return SECSuccess; } +/* Installs the given delegated credential (DC) and DC private key into the + * certificate. + * + * It's the caller's responsibility to ensure that the DC is well-formed and + * that the DC public key matches the DC private key. + */ +static SECStatus +ssl_PopulateDelegatedCredential(sslServerCert *sc, + const SECItem *delegCred, + const SECKEYPrivateKey *delegCredPrivKey) +{ + sslDelegatedCredential *dc = NULL; + + if (sc->delegCred.len) { + SECITEM_FreeItem(&sc->delegCred, PR_FALSE); + } + + if (sc->delegCredKeyPair) { + ssl_FreeKeyPair(sc->delegCredKeyPair); + sc->delegCredKeyPair = NULL; + } + + /* Both the DC and its private are present. */ + if (delegCred && delegCredPrivKey) { + SECStatus rv; + SECKEYPublicKey *pub; + SECKEYPrivateKey *priv; + + if (!delegCred->data || delegCred->len == 0) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + goto loser; + } + + /* Parse the DC. */ + rv = tls13_ReadDelegatedCredential(delegCred->data, delegCred->len, &dc); + if (rv != SECSuccess) { + goto loser; + } + + /* Make a copy of the DC. */ + rv = SECITEM_CopyItem(NULL, &sc->delegCred, delegCred); + if (rv != SECSuccess) { + goto loser; + } + + /* Make a copy of the DC private key. */ + priv = SECKEY_CopyPrivateKey(delegCredPrivKey); + if (!priv) { + goto loser; + } + + /* parse public key from the DC. */ + pub = SECKEY_ExtractPublicKey(dc->spki); + if (!pub) { + goto loser; + } + + sc->delegCredKeyPair = ssl_NewKeyPair(priv, pub); + + /* Attempting to configure either the DC or DC private key, but not both. */ + } else if (delegCred || delegCredPrivKey) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + goto loser; + } + + tls13_DestroyDelegatedCredential(dc); + return SECSuccess; + +loser: + tls13_DestroyDelegatedCredential(dc); + return SECFailure; +} + /* Find any existing certificates that overlap with the new certificate and * either remove any supported authentication types that overlap with the new * certificate or - if they have no types left - remove them entirely. */ @@ -379,6 +470,12 @@ ssl_ConfigCert(sslSocket *ss, sslAuthTypeMask authTypes, if (rv != SECSuccess) { goto loser; } + rv = ssl_PopulateDelegatedCredential(sc, data->delegCred, + data->delegCredPrivKey); + if (rv != SECSuccess) { + error_code = PORT_GetError(); + goto loser; + } ssl_ClearMatchingCerts(ss, sc->authTypes, sc->namedCurve); PR_APPEND_LINK(&sc->link, &ss->serverCerts); return SECSuccess; @@ -548,7 +645,7 @@ SSL_ConfigServerCert(PRFileDesc *fd, CERTCertificate *cert, sslKeyPair *keyPair; SECStatus rv; SSLExtraServerCertData dataCopy = { - ssl_auth_null, NULL, NULL, NULL + ssl_auth_null, NULL, NULL, NULL, NULL, NULL }; sslAuthTypeMask authTypes; diff --git a/lib/ssl/sslcert.h b/lib/ssl/sslcert.h index fb31d1389..8c2dadae4 100644 --- a/lib/ssl/sslcert.h +++ b/lib/ssl/sslcert.h @@ -41,6 +41,13 @@ typedef struct sslServerCertStr { ** timestamps item. */ SECItem signedCertTimestamps; + + /* The delegated credential (DC) to send to clients who indicate support for + * the ietf-draft-tls-subcerts extension. + */ + SECItem delegCred; + /* The key pair used to sign the handshake when serving a DC. */ + sslKeyPair *delegCredKeyPair; } sslServerCert; #define SSL_CERT_IS(c, t) ((c)->authTypes & (1 << (t))) diff --git a/lib/ssl/sslerr.h b/lib/ssl/sslerr.h index 9e72da28f..7100b0226 100644 --- a/lib/ssl/sslerr.h +++ b/lib/ssl/sslerr.h @@ -270,6 +270,10 @@ typedef enum { SSL_ERROR_RX_UNEXPECTED_RECORD_TYPE = (SSL_ERROR_BASE + 179), SSL_ERROR_MISSING_POST_HANDSHAKE_AUTH_EXTENSION = (SSL_ERROR_BASE + 180), SSL_ERROR_RX_CERTIFICATE_REQUIRED_ALERT = (SSL_ERROR_BASE + 181), + SSL_ERROR_DC_CERT_VERIFY_ALG_MISMATCH = (SSL_ERROR_BASE + 182), + SSL_ERROR_DC_BAD_SIGNATURE = (SSL_ERROR_BASE + 183), + SSL_ERROR_DC_INVALID_KEY_USAGE = (SSL_ERROR_BASE + 184), + SSL_ERROR_DC_EXPIRED = (SSL_ERROR_BASE + 185), SSL_ERROR_END_OF_LIST /* let the c compiler determine the value of this. */ } SSLErrorCodes; #endif /* NO_SECURITY_ERROR_ENUM */ diff --git a/lib/ssl/sslexp.h b/lib/ssl/sslexp.h index 96159f56b..8d8e3adb2 100644 --- a/lib/ssl/sslexp.h +++ b/lib/ssl/sslexp.h @@ -757,6 +757,36 @@ typedef PRTime(PR_CALLBACK *SSLTimeFunc)(void *arg); (PRFileDesc * _fd, SSLTimeFunc _f, void *_arg), \ (fd, f, arg)) +/* Create a delegated credential (DC) for the draft-ietf-tls-subcerts extension + * using the given certificate |cert| and its signing key |certPriv| and write + * the serialized DC to |out|. The + * parameters are: + * - the DC public key |dcPub|; + * - the DC signature scheme |dcCertVerifyAlg|, used to verify the handshake. + * - the DC time-to-live |dcValidFor|, the number of seconds from now for which + * the DC should be valid; and + * - the current time |now|. + * + * The signing algorithm used to verify the DC signature is deduced from + * |cert|. + * + * It's the caller's responsibility to ensure the input parameters are all + * valid. This procedure is meant primarily for testing; for this purpose it is + * useful to do no validation. + */ +#define SSL_DelegateCredential(cert, certPriv, dcPub, dcCertVerifyAlg, \ + dcValidFor, now, out) \ + SSL_EXPERIMENTAL_API("SSL_DelegateCredential", \ + (const CERTCertificate *_cert, \ + const SECKEYPrivateKey *_certPriv, \ + const SECKEYPublicKey *_dcPub, \ + SSLSignatureScheme _dcCertVerifyAlg, \ + PRUint32 _dcValidFor, \ + PRTime _now, \ + SECItem *_out), \ + (cert, certPriv, dcPub, dcCertVerifyAlg, dcValidFor, \ + now, out)) + /* Deprecated experimental APIs */ #define SSL_UseAltServerHelloType(fd, enable) SSL_DEPRECATED_EXPERIMENTAL_API #define SSL_SetupAntiReplay(a, b, c) SSL_DEPRECATED_EXPERIMENTAL_API diff --git a/lib/ssl/sslimpl.h b/lib/ssl/sslimpl.h index aa6f804ff..64a893990 100644 --- a/lib/ssl/sslimpl.h +++ b/lib/ssl/sslimpl.h @@ -37,6 +37,7 @@ typedef struct sslSocketStr sslSocket; typedef struct sslNamedGroupDefStr sslNamedGroupDef; typedef struct sslEsniKeysStr sslEsniKeys; +typedef struct sslDelegatedCredentialStr sslDelegatedCredential; typedef struct sslEphemeralKeyPairStr sslEphemeralKeyPair; typedef struct TLS13KeyShareEntryStr TLS13KeyShareEntry; @@ -279,6 +280,7 @@ typedef struct sslOptionsStr { unsigned int enableHelloDowngradeCheck : 1; unsigned int enableV2CompatibleHello : 1; unsigned int enablePostHandshakeAuth : 1; + unsigned int enableDelegatedCredentials : 1; } sslOptions; typedef enum { sslHandshakingUndetermined = 0, @@ -1465,6 +1467,11 @@ extern void ssl_FreeEphemeralKeyPairs(sslSocket *ss); extern SECStatus ssl_AppendPaddedDHKeyShare(sslBuffer *buf, const SECKEYPublicKey *pubKey, PRBool appendLength); +extern PRBool ssl_CanUseSignatureScheme(SSLSignatureScheme scheme, + const SSLSignatureScheme *peerSchemes, + unsigned int peerSchemeCount, + PRBool requireSha1, + PRBool slotDoesPss); extern const ssl3DHParams *ssl_GetDHEParams(const sslNamedGroupDef *groupDef); extern SECStatus ssl_SelectDHEGroup(sslSocket *ss, const sslNamedGroupDef **groupDef); @@ -1561,9 +1568,14 @@ extern SECStatus ssl3_ConsumeHandshakeNumber64(sslSocket *ss, PRUint64 *num, extern SECStatus ssl3_ConsumeHandshakeVariable(sslSocket *ss, SECItem *i, PRUint32 bytes, PRUint8 **b, PRUint32 *length); +extern SECStatus ssl_SignatureSchemeFromSpki(const CERTSubjectPublicKeyInfo *spki, + PRBool isTls13, + SSLSignatureScheme *scheme); +extern PRBool ssl_SignatureSchemeEnabled(const sslSocket *ss, + SSLSignatureScheme scheme); extern PRBool ssl_IsSupportedSignatureScheme(SSLSignatureScheme scheme); extern SECStatus ssl_CheckSignatureSchemeConsistency( - sslSocket *ss, SSLSignatureScheme scheme, CERTCertificate *cert); + sslSocket *ss, SSLSignatureScheme scheme, CERTSubjectPublicKeyInfo *spki); extern SECStatus ssl_ParseSignatureSchemes(const sslSocket *ss, PLArenaPool *arena, SSLSignatureScheme **schemesOut, unsigned int *numSchemesOut, @@ -1571,8 +1583,18 @@ extern SECStatus ssl_ParseSignatureSchemes(const sslSocket *ss, PLArenaPool *are unsigned int *len); extern SECStatus ssl_ConsumeSignatureScheme( sslSocket *ss, PRUint8 **b, PRUint32 *length, SSLSignatureScheme *out); +extern SECStatus ssl3_SignHashesWithPrivKey(SSL3Hashes *hash, + SECKEYPrivateKey *key, + SSLSignatureScheme scheme, + PRBool isTls, + SECItem *buf); extern SECStatus ssl3_SignHashes(sslSocket *ss, SSL3Hashes *hash, SECKEYPrivateKey *key, SECItem *buf); +extern SECStatus ssl3_VerifySignedHashesWithSpki(sslSocket *ss, + CERTSubjectPublicKeyInfo *spki, + SSLSignatureScheme scheme, + SSL3Hashes *hash, + SECItem *buf); extern SECStatus ssl3_VerifySignedHashes(sslSocket *ss, SSLSignatureScheme scheme, SSL3Hashes *hash, SECItem *buf); extern SECStatus ssl3_CacheWrappedSecret(sslSocket *ss, sslSessionID *sid, @@ -1696,6 +1718,8 @@ PRBool ssl3_CipherSuiteAllowedForVersionRange(ssl3CipherSuite cipherSuite, const SSLVersionRange *vrange); SECStatus ssl3_SelectServerCert(sslSocket *ss); +SECStatus ssl_PrivateKeySupportsRsaPss(SECKEYPrivateKey *privKey, + PRBool *supportsRsaPss); SECStatus ssl_PickSignatureScheme(sslSocket *ss, CERTCertificate *cert, SECKEYPublicKey *pubKey, diff --git a/lib/ssl/sslinfo.c b/lib/ssl/sslinfo.c index 979325940..4834605c1 100644 --- a/lib/ssl/sslinfo.c +++ b/lib/ssl/sslinfo.c @@ -7,6 +7,7 @@ #include "sslimpl.h" #include "sslproto.h" #include "tls13hkdf.h" +#include "tls13subcerts.h" SECStatus SSL_GetChannelInfo(PRFileDesc *fd, SSLChannelInfo *info, PRUintn len) @@ -79,6 +80,7 @@ SSL_GetChannelInfo(PRFileDesc *fd, SSLChannelInfo *info, PRUintn len) inf.signatureScheme = sid->sigScheme; } inf.resumed = ss->statelessResume || ss->ssl3.hs.isResuming; + inf.peerDelegCred = tls13_IsVerifyingWithDelegatedCredential(ss); if (sid) { unsigned int sidLen; diff --git a/lib/ssl/sslsock.c b/lib/ssl/sslsock.c index b53075be0..f6fa5addc 100644 --- a/lib/ssl/sslsock.c +++ b/lib/ssl/sslsock.c @@ -20,6 +20,7 @@ #include "pk11pqg.h" #include "pk11pub.h" #include "tls13esni.h" +#include "tls13subcerts.h" static const sslSocketOps ssl_default_ops = { /* No SSL. */ ssl_DefConnect, @@ -75,6 +76,7 @@ static sslOptions ssl_defaults = { .enableFalseStart = PR_FALSE, .cbcRandomIV = PR_TRUE, .enableOCSPStapling = PR_FALSE, + .enableDelegatedCredentials = PR_FALSE, .enableALPN = PR_TRUE, .reuseServerECDHEKey = PR_TRUE, .enableFallbackSCSV = PR_FALSE, @@ -793,6 +795,10 @@ SSL_OptionSet(PRFileDesc *fd, PRInt32 which, PRIntn val) ss->opt.enableOCSPStapling = val; break; + case SSL_ENABLE_DELEGATED_CREDENTIALS: + ss->opt.enableDelegatedCredentials = val; + break; + case SSL_ENABLE_NPN: break; @@ -963,6 +969,9 @@ SSL_OptionGet(PRFileDesc *fd, PRInt32 which, PRIntn *pVal) case SSL_ENABLE_OCSP_STAPLING: val = ss->opt.enableOCSPStapling; break; + case SSL_ENABLE_DELEGATED_CREDENTIALS: + val = ss->opt.enableDelegatedCredentials; + break; case SSL_ENABLE_NPN: val = PR_FALSE; break; @@ -1101,6 +1110,9 @@ SSL_OptionGetDefault(PRInt32 which, PRIntn *pVal) case SSL_ENABLE_OCSP_STAPLING: val = ssl_defaults.enableOCSPStapling; break; + case SSL_ENABLE_DELEGATED_CREDENTIALS: + val = ssl_defaults.enableDelegatedCredentials; + break; case SSL_ENABLE_NPN: val = PR_FALSE; break; @@ -1291,6 +1303,10 @@ SSL_OptionSetDefault(PRInt32 which, PRIntn val) ssl_defaults.enableOCSPStapling = val; break; + case SSL_ENABLE_DELEGATED_CREDENTIALS: + ssl_defaults.enableDelegatedCredentials = val; + break; + case SSL_ENABLE_NPN: break; @@ -4089,6 +4105,7 @@ struct { EXP(AeadDecrypt), EXP(AeadEncrypt), EXP(CreateAntiReplayContext), + EXP(DelegateCredential), EXP(DestroyAead), EXP(DestroyResumptionTokenInfo), EXP(EnableESNI), diff --git a/lib/ssl/sslt.h b/lib/ssl/sslt.h index 58b44fe2f..828fdcfdc 100644 --- a/lib/ssl/sslt.h +++ b/lib/ssl/sslt.h @@ -9,9 +9,10 @@ #ifndef __sslt_h_ #define __sslt_h_ +#include "certt.h" +#include "keyhi.h" #include "prtypes.h" #include "secitem.h" -#include "certt.h" typedef enum { ssl_hs_hello_request = 0, @@ -267,6 +268,26 @@ typedef struct SSLExtraServerCertDataStr { /* A serialized sign_certificate_timestamp extension, used to answer * requests from clients for this data. */ const SECItem* signedCertTimestamps; + + /* Delegated credentials. + * + * A serialized delegated credential (DC) to use for authentication to peers + * who indicate support for this extension (ietf-drafts-tls-subcerts). DCs + * are used opportunistically if (1) the client indicates support, (2) TLS + * 1.3 or higher is negotiated, and (3) the selected certificate is + * configured with a DC. + * + * Note that it's the caller's responsibility to ensure that the DC is + * well-formed. + */ + const SECItem* delegCred; + + /* The secret key corresponding to the |delegCred|. + * + * Note that it's the caller's responsibility to ensure that this matches + * the DC public key. + */ + const SECKEYPrivateKey* delegCredPrivKey; } SSLExtraServerCertData; typedef struct SSLChannelInfoStr { @@ -326,6 +347,11 @@ typedef struct SSLChannelInfoStr { * otherwise. */ PRBool resumed; + /* Indicates whether the peer used a delegated credential (DC) for + * authentication. + */ + PRBool peerDelegCred; + /* When adding new fields to this structure, please document the * NSS version in which they were added. */ } SSLChannelInfo; @@ -476,6 +502,7 @@ typedef enum { ssl_tls13_key_share_xtn = 51, ssl_next_proto_nego_xtn = 13172, /* Deprecated. */ ssl_renegotiation_info_xtn = 0xff01, + ssl_delegated_credentials_xtn = 0xff02, ssl_tls13_short_header_xtn = 0xff03, /* Deprecated. */ ssl_tls13_encrypted_sni_xtn = 0xffce, } SSLExtensionType; diff --git a/lib/ssl/tls13con.c b/lib/ssl/tls13con.c index 0ba694fb6..5f0866d0a 100644 --- a/lib/ssl/tls13con.c +++ b/lib/ssl/tls13con.c @@ -24,6 +24,7 @@ #include "tls13esni.h" #include "tls13exthandle.h" #include "tls13hashstate.h" +#include "tls13subcerts.h" static SECStatus tls13_SetCipherSpec(sslSocket *ss, PRUint16 epoch, SSLSecretDirection install, @@ -1589,6 +1590,20 @@ tls13_SelectServerCert(sslSocket *ss) if (rv == SECSuccess) { /* Found one. */ ss->sec.serverCert = cert; + + /* If we can use a delegated credential (DC) for authentication in + * the current handshake, then commit to using it now. We'll send a + * DC as an extension and use the DC private key to sign the + * handshake. + * + * This sets the signature scheme to be the signature scheme + * indicated by the DC. + */ + rv = tls13_MaybeSetDelegatedCredential(ss); + if (rv != SECSuccess) { + return SECFailure; /* Failure indicates an internal error. */ + } + ss->sec.authType = ss->ssl3.hs.kea_def_mutable.authKeyType = ssl_SignatureSchemeToAuthType(ss->ssl3.hs.signatureScheme); ss->sec.authKeyBits = cert->serverKeyBits; @@ -2620,7 +2635,14 @@ tls13_SendEncryptedServerSequence(sslSocket *ss) return SECFailure; /* error code is set. */ } - svrPrivKey = ss->sec.serverCert->serverKeyPair->privKey; + if (tls13_IsSigningWithDelegatedCredential(ss)) { + SSL_TRC(3, ("%d: TLS13[%d]: Signing with delegated credential", + SSL_GETPID(), ss->fd)); + svrPrivKey = ss->sec.serverCert->delegCredKeyPair->privKey; + } else { + svrPrivKey = ss->sec.serverCert->serverKeyPair->privKey; + } + rv = tls13_SendCertificateVerify(ss, svrPrivKey); if (rv != SECSuccess) { return SECFailure; /* err code is set. */ @@ -4120,6 +4142,8 @@ done: SECStatus tls13_HandleCertificateVerify(sslSocket *ss, PRUint8 *b, PRUint32 length) { + sslDelegatedCredential *dc = ss->xtnData.peerDelegCred; + CERTSubjectPublicKeyInfo *spki = NULL; SECItem signed_hash = { siBuffer, NULL, 0 }; SECStatus rv; SSLSignatureScheme sigScheme; @@ -4159,7 +4183,38 @@ tls13_HandleCertificateVerify(sslSocket *ss, PRUint8 *b, PRUint32 length) return SECFailure; } - rv = ssl_CheckSignatureSchemeConsistency(ss, sigScheme, ss->sec.peerCert); + /* Set the |spki| used to verify the handshake. When verifying with a + * delegated credential (DC), this corresponds to the DC public key; + * otherwise it correspond to the public key of the peer's end-entity + * certificate. + */ + if (tls13_IsVerifyingWithDelegatedCredential(ss)) { + /* DelegatedCredential.cred.expected_cert_verify_algorithm is expected + * to match CertificateVerify.scheme. + */ + if (sigScheme != dc->expectedCertVerifyAlg) { + FATAL_ERROR(ss, SSL_ERROR_DC_CERT_VERIFY_ALG_MISMATCH, illegal_parameter); + return SECFailure; + } + + /* Verify the DC has three steps: (1) use the peer's end-entity + * certificate to verify DelegatedCredential.signature, (2) check that + * the certificate has the correct key usage, and (3) check that the DC + * hasn't expired. + */ + rv = tls13_VerifyDelegatedCredential(ss, dc); + if (rv != SECSuccess) { /* Calls FATAL_ERROR() */ + return SECFailure; + } + + SSL_TRC(3, ("%d: TLS13[%d]: Verifying with delegated credential", + SSL_GETPID(), ss->fd)); + spki = dc->spki; + } else { + spki = &ss->sec.peerCert->subjectPublicKeyInfo; + } + + rv = ssl_CheckSignatureSchemeConsistency(ss, sigScheme, spki); if (rv != SECSuccess) { /* Error set already */ FATAL_ERROR(ss, PORT_GetError(), illegal_parameter); @@ -4184,7 +4239,8 @@ tls13_HandleCertificateVerify(sslSocket *ss, PRUint8 *b, PRUint32 length) return SECFailure; } - rv = ssl3_VerifySignedHashes(ss, sigScheme, &tbsHash, &signed_hash); + rv = ssl3_VerifySignedHashesWithSpki( + ss, spki, sigScheme, &tbsHash, &signed_hash); if (rv != SECSuccess) { FATAL_ERROR(ss, PORT_GetError(), decrypt_error); return SECFailure; @@ -5138,6 +5194,7 @@ static const struct { certificate) }, { ssl_cert_status_xtn, _M3(client_hello, certificate_request, certificate) }, + { ssl_delegated_credentials_xtn, _M2(client_hello, certificate) }, { ssl_tls13_cookie_xtn, _M2(client_hello, hello_retry_request) }, { ssl_tls13_certificate_authorities_xtn, _M1(certificate_request) }, { ssl_tls13_supported_versions_xtn, _M3(client_hello, server_hello, diff --git a/lib/ssl/tls13exthandle.c b/lib/ssl/tls13exthandle.c index 9d7c50efb..1f88016a1 100644 --- a/lib/ssl/tls13exthandle.c +++ b/lib/ssl/tls13exthandle.c @@ -14,6 +14,7 @@ #include "ssl3exthandle.h" #include "tls13esni.h" #include "tls13exthandle.h" +#include "tls13subcerts.h" SECStatus tls13_ServerSendStatusRequestXtn(const sslSocket *ss, TLSExtensionData *xtnData, @@ -1400,3 +1401,94 @@ tls13_ClientCheckEsniXtn(sslSocket *ss) return SECSuccess; } + +/* Indicates support for the delegated credentials extension. This should be + * hooked while processing the ClientHello. + */ +SECStatus +tls13_ClientSendDelegatedCredentialsXtn(const sslSocket *ss, + TLSExtensionData *xtnData, + sslBuffer *buf, PRBool *added) +{ + /* Only send the extension if support is enabled and the client can + * negotiate TLS 1.3. + */ + if (ss->vrange.max < SSL_LIBRARY_VERSION_TLS_1_3 || + !ss->opt.enableDelegatedCredentials) { + return SECSuccess; + } + + *added = PR_TRUE; + return SECSuccess; +} + +/* Parses the delegated credential (DC) offered by the server. This should be + * hooked while processing the server's CertificateVerify. + * + * Only the DC sent with the end-entity certificate is to be parsed. This is + * ensured by |tls13_HandleCertificateEntry|, which only processes extensions + * for the first certificate in the chain. + */ +SECStatus +tls13_ClientHandleDelegatedCredentialsXtn(const sslSocket *ss, + TLSExtensionData *xtnData, + SECItem *data) +{ + if (!ss->opt.enableDelegatedCredentials || + ss->version < SSL_LIBRARY_VERSION_TLS_1_3) { + ssl3_ExtSendAlert(ss, alert_fatal, illegal_parameter); + PORT_SetError(SSL_ERROR_RX_UNEXPECTED_EXTENSION); + return SECFailure; + } + + SECStatus rv = tls13_ReadDelegatedCredential(data->data, data->len, + &xtnData->peerDelegCred); + if (rv != SECSuccess) { + return SECFailure; /* code already set */ + } + + xtnData->negotiated[xtnData->numNegotiated++] = + ssl_delegated_credentials_xtn; + return SECSuccess; +} + +/* Adds the DC extension if we're committed to authenticating with a DC. */ +static SECStatus +tls13_ServerSendDelegatedCredentialsXtn(const sslSocket *ss, + TLSExtensionData *xtnData, + sslBuffer *buf, PRBool *added) +{ + if (tls13_IsSigningWithDelegatedCredential(ss)) { + const SECItem *dc = &ss->sec.serverCert->delegCred; + + if (tls13_IsSigningWithDelegatedCredential(ss)) { + SECStatus rv; + rv = sslBuffer_Append(buf, dc->data, dc->len); + if (rv != SECSuccess) { + return SECFailure; + } + } + + *added = PR_TRUE; + return SECSuccess; + } + + return SECSuccess; +} + +/* The client has indicated support of DCs. We can't act on this information + * until we've committed to signing with a DC, so just set a callback for + * sending the DC extension later. */ +SECStatus +tls13_ServerHandleDelegatedCredentialsXtn(const sslSocket *ss, + TLSExtensionData *xtnData, + SECItem *data) +{ + xtnData->peerRequestedDelegCred = PR_TRUE; + xtnData->negotiated[xtnData->numNegotiated++] = + ssl_delegated_credentials_xtn; + + return ssl3_RegisterExtensionSender( + ss, xtnData, ssl_delegated_credentials_xtn, + tls13_ServerSendDelegatedCredentialsXtn); +} diff --git a/lib/ssl/tls13exthandle.h b/lib/ssl/tls13exthandle.h index d36a4f935..abc7f9deb 100644 --- a/lib/ssl/tls13exthandle.h +++ b/lib/ssl/tls13exthandle.h @@ -99,5 +99,14 @@ SECStatus tls13_ClientSendPostHandshakeAuthXtn(const sslSocket *ss, SECStatus tls13_ServerHandlePostHandshakeAuthXtn(const sslSocket *ss, TLSExtensionData *xtnData, SECItem *data); +SECStatus tls13_ClientHandleDelegatedCredentialsXtn(const sslSocket *ss, + TLSExtensionData *xtnData, + SECItem *data); +SECStatus tls13_ClientSendDelegatedCredentialsXtn(const sslSocket *ss, + TLSExtensionData *xtnData, + sslBuffer *buf, PRBool *added); +SECStatus tls13_ServerHandleDelegatedCredentialsXtn(const sslSocket *ss, + TLSExtensionData *xtnData, + SECItem *data); #endif diff --git a/lib/ssl/tls13subcerts.c b/lib/ssl/tls13subcerts.c new file mode 100644 index 000000000..0610fae73 --- /dev/null +++ b/lib/ssl/tls13subcerts.c @@ -0,0 +1,594 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nss.h" +#include "pk11func.h" +#include "secder.h" +#include "ssl.h" +#include "sslproto.h" +#include "sslimpl.h" +#include "ssl3exthandle.h" +#include "tls13exthandle.h" +#include "tls13hkdf.h" +#include "tls13subcerts.h" + +/* Parses the delegated credential (DC) from the raw extension |b| of length + * |length|. Memory for the DC is allocated and set to |*dcp|. + * + * It's the caller's responsibility to invoke |tls13_DestroyDelegatedCredential| + * when this data is no longer needed. + */ +SECStatus +tls13_ReadDelegatedCredential(PRUint8 *b, PRUint32 length, + sslDelegatedCredential **dcp) +{ + sslDelegatedCredential *dc = NULL; + SECStatus rv; + PRUint64 n; + sslReadBuffer tmp; + sslReader rdr = SSL_READER(b, length); + + PORT_Assert(!*dcp); + + dc = PORT_ZNew(sslDelegatedCredential); + if (!dc) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + + /* Read the valid_time field of DelegatedCredential.cred. */ + rv = sslRead_ReadNumber(&rdr, 4, &n); + if (rv != SECSuccess) { + goto loser; + } + dc->validTime = n; + + /* Read the expected_cert_verify_algorithm field of + * DelegatedCredential.cred. */ + rv = sslRead_ReadNumber(&rdr, 2, &n); + if (rv != SECSuccess) { + goto loser; + } + dc->expectedCertVerifyAlg = n; + + /* Read the ASN1_subjectPublicKeyInfo field of DelegatedCredential.cred. */ + rv = sslRead_ReadVariable(&rdr, 3, &tmp); + if (rv != SECSuccess) { + goto loser; + } + rv = SECITEM_MakeItem(NULL, &dc->derSpki, tmp.buf, tmp.len); + if (rv != SECSuccess) { + goto loser; + } + + /* Parse the DER-encoded SubjectPublicKeyInfo. */ + dc->spki = SECKEY_DecodeDERSubjectPublicKeyInfo(&dc->derSpki); + if (!dc->spki) { + goto loser; + } + + /* Read the algorithm field of the DelegatedCredential. */ + rv = sslRead_ReadNumber(&rdr, 2, &n); + if (rv != SECSuccess) { + goto loser; + } + dc->alg = n; + + /* Read the signature field of the DelegatedCredential. */ + rv = sslRead_ReadVariable(&rdr, 2, &tmp); + if (rv != SECSuccess) { + goto loser; + } + rv = SECITEM_MakeItem(NULL, &dc->signature, tmp.buf, tmp.len); + if (rv != SECSuccess) { + goto loser; + } + + /* There should be nothing left to read. */ + if (SSL_READER_REMAINING(&rdr) > 0) { + goto loser; + } + + *dcp = dc; + return SECSuccess; + +loser: + tls13_DestroyDelegatedCredential(dc); + *dcp = NULL; + return SECFailure; +} + +/* Frees |dc| from the heap. */ +void +tls13_DestroyDelegatedCredential(sslDelegatedCredential *dc) +{ + if (!dc) { + return; + } + + SECKEY_DestroySubjectPublicKeyInfo(dc->spki); + SECITEM_FreeItem(&dc->derSpki, PR_FALSE); + SECITEM_FreeItem(&dc->signature, PR_FALSE); + PORT_ZFree(dc, sizeof(sslDelegatedCredential)); +} + +/* Sets |*certVerifyAlg| to the expected_cert_verify_algorithm field from the + * serialized DC |in|. Returns SECSuccess upon success; SECFailure indicates a + * decoding failure or the input wasn't long enough. + */ +static SECStatus +tls13_GetExpectedCertVerifyAlg(SECItem in, SSLSignatureScheme *certVerifyAlg) +{ + SECStatus rv; + PRUint64 n; + sslReader rdr = SSL_READER(in.data, in.len); + + if (in.len < 6) { /* Buffer too short to contain the first two params. */ + return SECFailure; + } + + rv = sslRead_ReadNumber(&rdr, 4, &n); + if (rv != SECSuccess) { + return SECFailure; + } + + rv = sslRead_ReadNumber(&rdr, 2, &n); + if (rv != SECSuccess) { + return SECFailure; + } + *certVerifyAlg = n; + + return SECSuccess; +} + +/* Returns PR_TRUE if the host is verifying the handshake with a DC. */ +PRBool +tls13_IsVerifyingWithDelegatedCredential(const sslSocket *ss) +{ + /* As of draft-ietf-subcerts-03, only the server may authenticate itself + * with a DC. + */ + if (ss->sec.isServer || + !ss->opt.enableDelegatedCredentials || + !ss->xtnData.peerDelegCred) { + return PR_FALSE; + } + + return PR_TRUE; +} + +/* Returns PR_TRUE if the host is signing the handshake with a DC. */ +PRBool +tls13_IsSigningWithDelegatedCredential(const sslSocket *ss) +{ + if (!ss->sec.isServer || + !ss->xtnData.sendingDelegCredToPeer || + !ss->xtnData.peerRequestedDelegCred) { + return PR_FALSE; + } + + return PR_TRUE; +} + +/* Commits to authenticating with a DC if all of the following conditions hold: + * - the negotiated protocol is TLS 1.3 or newer; + * - the selected certificate has a DC configured; + * - the peer has indicated support for this extension; + * - the peer has indicated support for the DC signature scheme; and + * - the host supports the DC signature scheme. + * + * It's the caller's responsibility to ensure that the version has been + * negotiated and the certificate has been selected. + */ +SECStatus +tls13_MaybeSetDelegatedCredential(sslSocket *ss) +{ + SECStatus rv; + PRBool doesRsaPss; + SECKEYPrivateKey *priv; + SSLSignatureScheme scheme; + + /* Assert that the host is the server (as of draft-ietf-subcerts-03, only + * the server may authenticate itself with a DC), the certificate has been + * chosen, TLS 1.3 or higher has been negotiated, and that the set of + * signature schemes supported by the client is known. + */ + PORT_Assert(ss->sec.isServer); + PORT_Assert(ss->sec.serverCert); + PORT_Assert(ss->version >= SSL_LIBRARY_VERSION_TLS_1_3); + PORT_Assert(ss->xtnData.sigSchemes); + + /* Check that the peer has indicated support and that a DC has been + * configured for the selected certificate. + */ + if (!ss->xtnData.peerRequestedDelegCred || + !ss->sec.serverCert->delegCred.len || + !ss->sec.serverCert->delegCredKeyPair) { + return SECSuccess; + } + + /* Check that the host and peer both support the signing algorithm used with + * the DC. + */ + rv = tls13_GetExpectedCertVerifyAlg(ss->sec.serverCert->delegCred, + &scheme); + if (rv != SECSuccess) { + return SECFailure; + } + + priv = ss->sec.serverCert->delegCredKeyPair->privKey; + rv = ssl_PrivateKeySupportsRsaPss(priv, &doesRsaPss); + if (rv != SECSuccess) { + return SECFailure; + } + + if (!ssl_SignatureSchemeEnabled(ss, scheme) || + !ssl_CanUseSignatureScheme(scheme, + ss->xtnData.sigSchemes, + ss->xtnData.numSigSchemes, + PR_FALSE /* requireSha1 */, + doesRsaPss)) { + return SECSuccess; + } + + /* Commit to sending a DC and set the handshake signature scheme to the + * indicated algorithm. + */ + ss->xtnData.sendingDelegCredToPeer = PR_TRUE; + ss->ssl3.hs.signatureScheme = scheme; + return SECSuccess; +} + +/* Serializes the DC up to the signature. */ +static SECStatus +tls13_AppendCredentialParams(sslBuffer *buf, sslDelegatedCredential *dc) +{ + SECStatus rv; + rv = sslBuffer_AppendNumber(buf, dc->validTime, 4); + if (rv != SECSuccess) { + return SECFailure; /* Error set by caller. */ + } + + rv = sslBuffer_AppendNumber(buf, dc->expectedCertVerifyAlg, 2); + if (rv != SECSuccess) { + return SECFailure; + } + + rv = sslBuffer_AppendVariable(buf, dc->derSpki.data, dc->derSpki.len, 3); + if (rv != SECSuccess) { + return SECFailure; + } + + rv = sslBuffer_AppendNumber(buf, dc->alg, 2); + if (rv != SECSuccess) { + return SECFailure; + } + + return SECSuccess; +} + +/* Serializes the DC signature. */ +static SECStatus +tls13_AppendCredentialSignature(sslBuffer *buf, sslDelegatedCredential *dc) +{ + SECStatus rv; + rv = sslBuffer_AppendVariable(buf, dc->signature.data, + dc->signature.len, 2); + if (rv != SECSuccess) { + return SECFailure; + } + + return SECSuccess; +} + +/* Hashes the message used to sign/verify the DC. */ +static SECStatus +tls13_HashCredentialSignatureMessage(SSL3Hashes *hash, + SSLSignatureScheme scheme, + const CERTCertificate *cert, + const sslBuffer *dcBuf) +{ + SECStatus rv; + PK11Context *ctx = NULL; + unsigned int hashLen; + + /* Set up hash context. */ + hash->hashAlg = ssl_SignatureSchemeToHashType(scheme); + ctx = PK11_CreateDigestContext(ssl3_HashTypeToOID(hash->hashAlg)); + if (!ctx) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + + static const PRUint8 kCtxStrPadding[64] = { + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + }; + + static const PRUint8 kCtxStr[] = "TLS, server delegated credentials"; + + /* Hash the message signed by the peer. */ + rv = SECSuccess; + rv |= PK11_DigestBegin(ctx); + rv |= PK11_DigestOp(ctx, kCtxStrPadding, sizeof kCtxStrPadding); + rv |= PK11_DigestOp(ctx, kCtxStr, 1 /* 0-byte */ + strlen((const char *)kCtxStr)); + rv |= PK11_DigestOp(ctx, cert->derCert.data, cert->derCert.len); + rv |= PK11_DigestOp(ctx, dcBuf->buf, dcBuf->len); + rv |= PK11_DigestFinal(ctx, hash->u.raw, &hashLen, sizeof hash->u.raw); + if (rv != SECSuccess) { + PORT_SetError(SSL_ERROR_SHA_DIGEST_FAILURE); + goto loser; + } + + hash->len = hashLen; + if (ctx) { + PK11_DestroyContext(ctx, PR_TRUE); + } + return SECSuccess; + +loser: + if (ctx) { + PK11_DestroyContext(ctx, PR_TRUE); + } + return SECFailure; +} + +/* Verifies the DC signature. */ +static SECStatus +tls13_VerifyCredentialSignature(sslSocket *ss, sslDelegatedCredential *dc) +{ + SECStatus rv = SECSuccess; + SSL3Hashes hash; + sslBuffer dcBuf = SSL_BUFFER_EMPTY; + CERTCertificate *cert = ss->sec.peerCert; + + /* Serialize the DC parameters. */ + rv = tls13_AppendCredentialParams(&dcBuf, dc); + if (rv != SECSuccess) { + goto loser; /* Error set by caller. */ + } + + /* Hash the message that was signed by the delegator. */ + rv = tls13_HashCredentialSignatureMessage(&hash, dc->alg, cert, &dcBuf); + if (rv != SECSuccess) { + FATAL_ERROR(ss, PORT_GetError(), internal_error); + goto loser; + } + + /* Verify the signature of the message. */ + rv = ssl3_VerifySignedHashesWithSpki( + ss, &cert->subjectPublicKeyInfo, dc->alg, &hash, &dc->signature); + if (rv != SECSuccess) { + FATAL_ERROR(ss, SSL_ERROR_DC_BAD_SIGNATURE, illegal_parameter); + goto loser; + } + + sslBuffer_Clear(&dcBuf); + return SECSuccess; + +loser: + sslBuffer_Clear(&dcBuf); + return SECFailure; +} + +/* Checks that the peer's end-entity certificate has the correct key usage. */ +static SECStatus +tls13_CheckCertDelegationUsage(sslSocket *ss) +{ + int i; + PRBool found; + CERTCertExtension *ext; + SECItem delegUsageOid = { siBuffer, NULL, 0 }; + const CERTCertificate *cert = ss->sec.peerCert; + + /* 1.3.6.1.4.1.44363.44, as defined in draft-ietf-tls-subcerts. */ + static unsigned char kDelegationUsageOid[] = { + 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0xda, 0x4b, 0x2c, + }; + + delegUsageOid.data = kDelegationUsageOid; + delegUsageOid.len = sizeof kDelegationUsageOid; + + /* The certificate must have the delegationUsage extension that authorizes + * it to negotiate delegated credentials. + */ + found = PR_FALSE; + for (i = 0; cert->extensions[i] != NULL; i++) { + ext = cert->extensions[i]; + if (SECITEM_CompareItem(&ext->id, &delegUsageOid) == SECEqual) { + found = PR_TRUE; + break; + } + } + + /* The certificate must also have the digitalSignature keyUsage set. */ + if (!found || + !cert->keyUsagePresent || + !(cert->keyUsage & KU_DIGITAL_SIGNATURE)) { + FATAL_ERROR(ss, SSL_ERROR_DC_INVALID_KEY_USAGE, illegal_parameter); + return SECFailure; + } + + return SECSuccess; +} + +static SECStatus +tls13_CheckCredentialExpiration(sslSocket *ss, sslDelegatedCredential *dc) +{ + SECStatus rv; + PRTime start, end /* microseconds */; + CERTCertificate *cert = ss->sec.peerCert; + + rv = DER_DecodeTimeChoice(&start, &cert->validity.notBefore); + if (rv != SECSuccess) { + FATAL_ERROR(ss, PORT_GetError(), internal_error); + return SECFailure; + } + + end = start + ((PRTime)dc->validTime * PR_USEC_PER_SEC); + if (ssl_Time(ss) > end) { + FATAL_ERROR(ss, SSL_ERROR_DC_EXPIRED, illegal_parameter); + return SECFailure; + } + + return SECSuccess; +} + +/* Returns SECSucces if |dc| is a DC for the current handshake; otherwise it + * returns SECFailure. A valid DC meets three requirements: (1) the signature + * was produced by the peer's end-entity certificate, (2) the end-entity + * certificate must have the correct key usage, and (3) the DC must not be + * expired. + * + * This function calls FATAL_ERROR() when an error occurs. + */ +SECStatus +tls13_VerifyDelegatedCredential(sslSocket *ss, + sslDelegatedCredential *dc) +{ + SECStatus rv; + PRTime start; + PRExplodedTime end; + CERTCertificate *cert = ss->sec.peerCert; + char endStr[256]; + + rv = DER_DecodeTimeChoice(&start, &cert->validity.notBefore); + if (rv != SECSuccess) { + FATAL_ERROR(ss, PORT_GetError(), internal_error); + return SECFailure; + } + + PR_ExplodeTime(start + (dc->validTime * PR_USEC_PER_SEC), + PR_GMTParameters, &end); + if (PR_FormatTime(endStr, sizeof(endStr), "%a %b %d %H:%M:%S %Y", &end)) { + SSL_TRC(20, ("%d: TLS13[%d]: Received delegated credential (expires %s)", + SSL_GETPID(), ss->fd, endStr)); + } else { + SSL_TRC(20, ("%d: TLS13[%d]: Received delegated credential", + SSL_GETPID(), ss->fd)); + } + + rv = SECSuccess; + rv |= tls13_VerifyCredentialSignature(ss, dc); + rv |= tls13_CheckCertDelegationUsage(ss); + rv |= tls13_CheckCredentialExpiration(ss, dc); + return rv; +} + +/* Returns a serialized DC with the given parameters. + * + * Note that this function is meant primarily for testing. In particular, it + * DOES NOT verify any of the following: + * - |certPriv| is the private key corresponding to |cert|; + * - that |checkCertKeyUsage(cert) == SECSuccess|; + * - |dcCertVerifyAlg| is consistent with |dcPub|; + * - |dcValidFor| is less than 7 days (the maximum permitted by the spec); or + * - validTime doesn't overflow a PRUint32. + * + * These conditions are things we want to test for, which is why we allow them + * here. A real API for creating DCs would want to explicitly check ALL of these + * conditions are met. + */ +SECStatus +SSLExp_DelegateCredential(const CERTCertificate *cert, + const SECKEYPrivateKey *certPriv, + const SECKEYPublicKey *dcPub, + SSLSignatureScheme dcCertVerifyAlg, + PRUint32 dcValidFor, + PRTime now, + SECItem *out) +{ + SECStatus rv; + SSL3Hashes hash; + SECItem *tmp = NULL; + SECKEYPrivateKey *tmpPriv = NULL; + sslDelegatedCredential *dc = NULL; + sslBuffer dcBuf = SSL_BUFFER_EMPTY; + + dc = PORT_ZNew(sslDelegatedCredential); + if (!dc) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + + /* Serialize the DC parameters. */ + PRTime start; + rv = DER_DecodeTimeChoice(&start, &cert->validity.notBefore); + if (rv != SECSuccess) { + goto loser; + } + dc->validTime = ((now - start) / PR_USEC_PER_SEC) + dcValidFor; + dc->expectedCertVerifyAlg = dcCertVerifyAlg; + + tmp = SECKEY_EncodeDERSubjectPublicKeyInfo(dcPub); + if (!tmp) { + goto loser; + } + /* Transfer |tmp| into |dc->derSpki|. */ + dc->derSpki.type = tmp->type; + dc->derSpki.data = tmp->data; + dc->derSpki.len = tmp->len; + PORT_Free(tmp); + + rv = ssl_SignatureSchemeFromSpki(&cert->subjectPublicKeyInfo, + PR_TRUE /* isTls13 */, &dc->alg); + if (rv != SECSuccess) { + goto loser; + } + PORT_Assert(dc->alg != ssl_sig_none); + + rv = tls13_AppendCredentialParams(&dcBuf, dc); + if (rv != SECSuccess) { + goto loser; + } + + /* Hash signature message. */ + rv = tls13_HashCredentialSignatureMessage(&hash, dc->alg, cert, &dcBuf); + if (rv != SECSuccess) { + goto loser; + } + + /* Sign the hash with the delegation key. + * + * The PK11 API discards const qualifiers, so we have to make a copy of + * |certPriv| and pass the copy to |ssl3_SignHashesWithPrivKey|. + */ + tmpPriv = SECKEY_CopyPrivateKey(certPriv); + rv = ssl3_SignHashesWithPrivKey(&hash, tmpPriv, dc->alg, + PR_TRUE /* isTls */, &dc->signature); + if (rv != SECSuccess) { + goto loser; + } + + /* Serialize the DC signature. */ + rv = tls13_AppendCredentialSignature(&dcBuf, dc); + if (rv != SECSuccess) { + goto loser; + } + + /* Copy the serialized DC to |out|. */ + rv = SECITEM_MakeItem(NULL, out, dcBuf.buf, dcBuf.len); + if (rv != SECSuccess) { + goto loser; + } + + SECKEY_DestroyPrivateKey(tmpPriv); + tls13_DestroyDelegatedCredential(dc); + sslBuffer_Clear(&dcBuf); + return SECSuccess; + +loser: + SECKEY_DestroyPrivateKey(tmpPriv); + tls13_DestroyDelegatedCredential(dc); + sslBuffer_Clear(&dcBuf); + return SECFailure; +} diff --git a/lib/ssl/tls13subcerts.h b/lib/ssl/tls13subcerts.h new file mode 100644 index 000000000..ce9996bb8 --- /dev/null +++ b/lib/ssl/tls13subcerts.h @@ -0,0 +1,56 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is PRIVATE to SSL. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __tls13subcerts_h_ +#define __tls13subcerts_h_ + +struct sslDelegatedCredentialStr { + /* The number of seconds for which the delegated credential (DC) is valid + * following the notBefore parameter of the delegation certificate. + */ + PRUint32 validTime; + + /* The signature algorithm of the DC public key. This expected to the same + * as CertificateVerify.scheme. + */ + SSLSignatureScheme expectedCertVerifyAlg; + + /* The DER-encoded SubjectPublicKeyInfo, the DC public key. + */ + SECItem derSpki; + + /* The decoded SubjectPublicKeyInfo parsed from |derSpki|. */ + CERTSubjectPublicKeyInfo *spki; + + /* The signature algorithm used to verify the DC signature. */ + SSLSignatureScheme alg; + + /* The DC signature. */ + SECItem signature; +}; + +SECStatus tls13_ReadDelegatedCredential(PRUint8 *b, + PRUint32 length, + sslDelegatedCredential **dcp); +void tls13_DestroyDelegatedCredential(sslDelegatedCredential *dc); + +PRBool tls13_IsVerifyingWithDelegatedCredential(const sslSocket *ss); +PRBool tls13_IsSigningWithDelegatedCredential(const sslSocket *ss); +SECStatus tls13_MaybeSetDelegatedCredential(sslSocket *ss); +SECStatus tls13_VerifyDelegatedCredential(sslSocket *ss, + sslDelegatedCredential *dc); + +SECStatus SSLExp_DelegateCredential(const CERTCertificate *cert, + const SECKEYPrivateKey *certPriv, + const SECKEYPublicKey *dcPub, + SSLSignatureScheme dcCertVerifyAlg, + PRUint32 dcValidFor, + PRTime now, + SECItem *out); + +#endif diff --git a/tests/common/certsetup.sh b/tests/common/certsetup.sh index f9ee459e1..32c6bc235 100644 --- a/tests/common/certsetup.sh +++ b/tests/common/certsetup.sh @@ -46,6 +46,11 @@ make_cert() { rsapss_chain) type_args=(-g 1024);sign=(-c rsa_pss_ca);type=rsa;; rsa_ca_rsapss_chain) type_args=(-g 1024 --pss-sign);sign=(-c rsa_ca);type=rsa;; ecdh_rsa) type_args=(-q nistp256);sign=(-c rsa_ca);type=ec ;; + delegator_p256) + touch empty.txt + type_args=(-q nistp256 --extGeneric 1.3.6.1.4.1.44363.44:not-critical:empty.txt) + type=ec + ;; esac msg="create certificate: $@" shift 2 diff --git a/tests/ssl_gtests/ssl_gtests.sh b/tests/ssl_gtests/ssl_gtests.sh index 6c088d8a6..d3a10303e 100755 --- a/tests/ssl_gtests/ssl_gtests.sh +++ b/tests/ssl_gtests/ssl_gtests.sh @@ -57,6 +57,7 @@ ssl_gtest_certs() { make_cert rsa_ca_rsa_pss_chain rsa_ca_rsapss_chain sign make_cert ecdh_rsa ecdh_rsa kex make_cert dsa dsa sign + make_cert delegator_ecdsa256 delegator_p256 sign } ############################## ssl_gtest_init ########################## |