diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
commit | 36d22d82aa202bb199967e9512281e9a53db42c9 (patch) | |
tree | 105e8c98ddea1c1e4784a60a5a6410fa416be2de /security/nss/gtests/ssl_gtest/ssl_auth_unittest.cc | |
parent | Initial commit. (diff) | |
download | firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip |
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'security/nss/gtests/ssl_gtest/ssl_auth_unittest.cc')
-rw-r--r-- | security/nss/gtests/ssl_gtest/ssl_auth_unittest.cc | 2261 |
1 files changed, 2261 insertions, 0 deletions
diff --git a/security/nss/gtests/ssl_gtest/ssl_auth_unittest.cc b/security/nss/gtests/ssl_gtest/ssl_auth_unittest.cc new file mode 100644 index 0000000000..edd2479a78 --- /dev/null +++ b/security/nss/gtests/ssl_gtest/ssl_auth_unittest.cc @@ -0,0 +1,2261 @@ +/* -*- 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 "secerr.h" +#include "ssl.h" +#include "sslerr.h" +#include "sslproto.h" + +extern "C" { +// This is not something that should make you happy. +#include "libssl_internals.h" +} + +#include "gtest_utils.h" +#include "nss_scoped_ptrs.h" +#include "tls_connect.h" +#include "tls_filter.h" +#include "tls_parser.h" + +namespace nss_test { + +TEST_P(TlsConnectGeneric, ServerAuthBigRsa) { + Reset(TlsAgent::kRsa2048); + Connect(); + CheckKeys(); +} + +TEST_P(TlsConnectGeneric, ServerAuthRsaChain) { + Reset("rsa_chain"); + Connect(); + CheckKeys(); + size_t chain_length; + EXPECT_TRUE(client_->GetPeerChainLength(&chain_length)); + EXPECT_EQ(2UL, chain_length); +} + +TEST_P(TlsConnectTls12Plus, ServerAuthRsaPss) { + static const SSLSignatureScheme kSignatureSchemePss[] = { + ssl_sig_rsa_pss_pss_sha256}; + + Reset(TlsAgent::kServerRsaPss); + client_->SetSignatureSchemes(kSignatureSchemePss, + PR_ARRAY_SIZE(kSignatureSchemePss)); + server_->SetSignatureSchemes(kSignatureSchemePss, + PR_ARRAY_SIZE(kSignatureSchemePss)); + Connect(); + CheckKeys(ssl_kea_ecdh, ssl_grp_ec_curve25519, ssl_auth_rsa_pss, + ssl_sig_rsa_pss_pss_sha256); +} + +// PSS doesn't work with TLS 1.0 or 1.1 because we can't signal it. +TEST_P(TlsConnectPre12, ServerAuthRsaPssFails) { + static const SSLSignatureScheme kSignatureSchemePss[] = { + ssl_sig_rsa_pss_pss_sha256}; + + Reset(TlsAgent::kServerRsaPss); + client_->SetSignatureSchemes(kSignatureSchemePss, + PR_ARRAY_SIZE(kSignatureSchemePss)); + server_->SetSignatureSchemes(kSignatureSchemePss, + PR_ARRAY_SIZE(kSignatureSchemePss)); + ConnectExpectAlert(server_, kTlsAlertHandshakeFailure); + server_->CheckErrorCode(SSL_ERROR_NO_CYPHER_OVERLAP); + client_->CheckErrorCode(SSL_ERROR_NO_CYPHER_OVERLAP); +} + +// Check that a PSS certificate with no parameters works. +TEST_P(TlsConnectTls12Plus, ServerAuthRsaPssNoParameters) { + static const SSLSignatureScheme kSignatureSchemePss[] = { + ssl_sig_rsa_pss_pss_sha256}; + + Reset("rsa_pss_noparam"); + client_->SetSignatureSchemes(kSignatureSchemePss, + PR_ARRAY_SIZE(kSignatureSchemePss)); + server_->SetSignatureSchemes(kSignatureSchemePss, + PR_ARRAY_SIZE(kSignatureSchemePss)); + Connect(); + CheckKeys(ssl_kea_ecdh, ssl_grp_ec_curve25519, ssl_auth_rsa_pss, + ssl_sig_rsa_pss_pss_sha256); +} + +TEST_P(TlsConnectGeneric, ServerAuthRsaPssChain) { + Reset("rsa_pss_chain"); + Connect(); + CheckKeys(); + size_t chain_length; + EXPECT_TRUE(client_->GetPeerChainLength(&chain_length)); + EXPECT_EQ(2UL, chain_length); +} + +TEST_P(TlsConnectGeneric, ServerAuthRsaCARsaPssChain) { + Reset("rsa_ca_rsa_pss_chain"); + Connect(); + CheckKeys(); + size_t chain_length; + EXPECT_TRUE(client_->GetPeerChainLength(&chain_length)); + EXPECT_EQ(2UL, chain_length); +} + +TEST_P(TlsConnectGeneric, ServerAuthRejected) { + EnsureTlsSetup(); + client_->SetAuthCertificateCallback( + [](TlsAgent*, PRBool, PRBool) -> SECStatus { return SECFailure; }); + ConnectExpectAlert(client_, kTlsAlertBadCertificate); + client_->CheckErrorCode(SSL_ERROR_BAD_CERTIFICATE); + server_->CheckErrorCode(SSL_ERROR_BAD_CERT_ALERT); + EXPECT_EQ(TlsAgent::STATE_ERROR, client_->state()); +} + +struct AuthCompleteArgs : public PollTarget { + AuthCompleteArgs(const std::shared_ptr<TlsAgent>& a, PRErrorCode c) + : agent(a), code(c) {} + + std::shared_ptr<TlsAgent> agent; + PRErrorCode code; +}; + +static void CallAuthComplete(PollTarget* target, Event event) { + EXPECT_EQ(TIMER_EVENT, event); + auto args = reinterpret_cast<AuthCompleteArgs*>(target); + std::cerr << args->agent->role_str() << ": call SSL_AuthCertificateComplete " + << (args->code ? PR_ErrorToName(args->code) : "no error") + << std::endl; + EXPECT_EQ(SECSuccess, + SSL_AuthCertificateComplete(args->agent->ssl_fd(), args->code)); + args->agent->Handshake(); // Make the TlsAgent aware of the error. + delete args; +} + +// Install an AuthCertificateCallback that blocks when called. Then +// SSL_AuthCertificateComplete is called on a very short timer. This allows any +// processing that might follow the callback to complete. +static void SetDeferredAuthCertificateCallback(std::shared_ptr<TlsAgent> agent, + PRErrorCode code) { + auto args = new AuthCompleteArgs(agent, code); + agent->SetAuthCertificateCallback( + [args](TlsAgent*, PRBool, PRBool) -> SECStatus { + // This can't be 0 or we race the message from the client to the server, + // and tests assume that we lose that race. + std::shared_ptr<Poller::Timer> timer_handle; + Poller::Instance()->SetTimer(1U, args, CallAuthComplete, &timer_handle); + return SECWouldBlock; + }); +} + +TEST_P(TlsConnectTls13, ServerAuthRejectAsync) { + SetDeferredAuthCertificateCallback(client_, SEC_ERROR_REVOKED_CERTIFICATE); + ConnectExpectAlert(client_, kTlsAlertCertificateRevoked); + // We only detect the error here when we attempt to handshake, so all the + // client learns is that the handshake has already failed. + client_->CheckErrorCode(SSL_ERROR_HANDSHAKE_FAILED); + server_->CheckErrorCode(SSL_ERROR_REVOKED_CERT_ALERT); +} + +// In TLS 1.2 and earlier, this will result in the client sending its Finished +// before learning that the server certificate is bad. That means that the +// server will believe that the handshake is complete. +TEST_P(TlsConnectGenericPre13, ServerAuthRejectAsync) { + SetDeferredAuthCertificateCallback(client_, SEC_ERROR_EXPIRED_CERTIFICATE); + client_->ExpectSendAlert(kTlsAlertCertificateExpired); + server_->ExpectReceiveAlert(kTlsAlertCertificateExpired); + ConnectExpectFailOneSide(TlsAgent::CLIENT); + client_->CheckErrorCode(SSL_ERROR_HANDSHAKE_FAILED); + + // The server might not receive the alert that the client sends, which would + // cause the test to fail when it cleans up. Reset expectations. + server_->ExpectReceiveAlert(kTlsAlertCloseNotify, kTlsAlertWarning); +} + +class TlsCertificateRequestContextRecorder : public TlsHandshakeFilter { + public: + TlsCertificateRequestContextRecorder(const std::shared_ptr<TlsAgent>& a, + uint8_t handshake_type) + : TlsHandshakeFilter(a, {handshake_type}), buffer_(), filtered_(false) { + EnableDecryption(); + } + + bool filtered() const { return filtered_; } + const DataBuffer& buffer() const { return buffer_; } + + protected: + virtual PacketFilter::Action FilterHandshake(const HandshakeHeader& header, + const DataBuffer& input, + DataBuffer* output) { + assert(1 < input.len()); + size_t len = input.data()[0]; + assert(len + 1 < input.len()); + buffer_.Assign(input.data() + 1, len); + filtered_ = true; + return KEEP; + } + + private: + DataBuffer buffer_; + bool filtered_; +}; + +using ClientAuthParam = + std::tuple<SSLProtocolVariant, uint16_t, ClientAuthCallbackType>; + +class TlsConnectClientAuth + : public TlsConnectTestBase, + public testing::WithParamInterface<ClientAuthParam> { + public: + TlsConnectClientAuth() + : TlsConnectTestBase(std::get<0>(GetParam()), std::get<1>(GetParam())) {} +}; + +// Wrapper classes for tests that target specific versions + +class TlsConnectClientAuth13 : public TlsConnectClientAuth {}; + +class TlsConnectClientAuth12 : public TlsConnectClientAuth {}; + +class TlsConnectClientAuthStream13 : public TlsConnectClientAuth {}; + +class TlsConnectClientAuthPre13 : public TlsConnectClientAuth {}; + +class TlsConnectClientAuth12Plus : public TlsConnectClientAuth {}; + +std::string getClientAuthTestName( + testing::TestParamInfo<ClientAuthParam> info) { + auto param = info.param; + auto variant = std::get<0>(param); + auto version = std::get<1>(param); + auto callback_type = std::get<2>(param); + + std::string output = std::string(); + switch (variant) { + case ssl_variant_stream: + output.append("TLS"); + break; + case ssl_variant_datagram: + output.append("DTLS"); + break; + } + output.append(VersionString(version).replace(1, 1, "")); + switch (callback_type) { + case ClientAuthCallbackType::kAsyncImmediate: + output.append("AsyncImmediate"); + break; + case ClientAuthCallbackType::kAsyncDelay: + output.append("AsyncDelay"); + break; + case ClientAuthCallbackType::kSync: + output.append("Sync"); + break; + case ClientAuthCallbackType::kNone: + output.append("None"); + break; + } + return output; +} + +auto kClientAuthCallbacks = testing::Values( + ClientAuthCallbackType::kAsyncImmediate, + ClientAuthCallbackType::kAsyncDelay, ClientAuthCallbackType::kSync, + ClientAuthCallbackType::kNone); + +INSTANTIATE_TEST_SUITE_P( + ClientAuthGenericStream, TlsConnectClientAuth, + testing::Combine(TlsConnectTestBase::kTlsVariantsStream, + TlsConnectTestBase::kTlsVAll, kClientAuthCallbacks), + getClientAuthTestName); + +INSTANTIATE_TEST_SUITE_P( + ClientAuthGenericDatagram, TlsConnectClientAuth, + testing::Combine(TlsConnectTestBase::kTlsVariantsDatagram, + TlsConnectTestBase::kTlsV11Plus, kClientAuthCallbacks), + getClientAuthTestName); + +INSTANTIATE_TEST_SUITE_P(ClientAuth13, TlsConnectClientAuth13, + testing::Combine(TlsConnectTestBase::kTlsVariantsAll, + TlsConnectTestBase::kTlsV13, + kClientAuthCallbacks), + getClientAuthTestName); + +INSTANTIATE_TEST_SUITE_P( + ClientAuth13, TlsConnectClientAuthStream13, + testing::Combine(TlsConnectTestBase::kTlsVariantsStream, + TlsConnectTestBase::kTlsV13, kClientAuthCallbacks), + getClientAuthTestName); + +INSTANTIATE_TEST_SUITE_P(ClientAuth12, TlsConnectClientAuth12, + testing::Combine(TlsConnectTestBase::kTlsVariantsAll, + TlsConnectTestBase::kTlsV12, + kClientAuthCallbacks), + getClientAuthTestName); + +INSTANTIATE_TEST_SUITE_P( + ClientAuthPre13Stream, TlsConnectClientAuthPre13, + testing::Combine(TlsConnectTestBase::kTlsVariantsStream, + TlsConnectTestBase::kTlsV10ToV12, kClientAuthCallbacks), + getClientAuthTestName); + +INSTANTIATE_TEST_SUITE_P( + ClientAuthPre13Datagram, TlsConnectClientAuthPre13, + testing::Combine(TlsConnectTestBase::kTlsVariantsDatagram, + TlsConnectTestBase::kTlsV11V12, kClientAuthCallbacks), + getClientAuthTestName); + +INSTANTIATE_TEST_SUITE_P(ClientAuth12Plus, TlsConnectClientAuth12Plus, + testing::Combine(TlsConnectTestBase::kTlsVariantsAll, + TlsConnectTestBase::kTlsV12Plus, + kClientAuthCallbacks), + getClientAuthTestName); + +TEST_P(TlsConnectClientAuth, ClientAuth) { + EnsureTlsSetup(); + client_->SetupClientAuth(std::get<2>(GetParam()), true); + server_->RequestClientAuth(true); + Connect(); + CheckKeys(); + client_->CheckClientAuthCompleted(); +} + +// All stream only tests; PostHandshakeAuth isn't supported for DTLS. + +TEST_P(TlsConnectClientAuthStream13, PostHandshakeAuth) { + EnsureTlsSetup(); + client_->SetupClientAuth(std::get<2>(GetParam()), true); + auto capture_cert_req = MakeTlsFilter<TlsCertificateRequestContextRecorder>( + server_, kTlsHandshakeCertificateRequest); + auto capture_certificate = + MakeTlsFilter<TlsCertificateRequestContextRecorder>( + client_, kTlsHandshakeCertificate); + client_->SetOption(SSL_ENABLE_POST_HANDSHAKE_AUTH, PR_TRUE); + size_t called = 0; + server_->SetAuthCertificateCallback( + [&called](TlsAgent*, PRBool, PRBool) -> SECStatus { + called++; + return SECSuccess; + }); + Connect(); + EXPECT_EQ(0U, called); + EXPECT_FALSE(capture_cert_req->filtered()); + EXPECT_FALSE(capture_certificate->filtered()); + // Send CertificateRequest. + EXPECT_EQ(SECSuccess, SSL_SendCertificateRequest(server_->ssl_fd())) + << "Unexpected error: " << PORT_ErrorToName(PORT_GetError()); + // Need to do a round-trip so that the post-handshake message is + // handled on both client and server. + server_->SendData(50); + client_->ReadBytes(50); + client_->ClientAuthCallbackComplete(); + client_->SendData(50); + server_->ReadBytes(50); + + EXPECT_EQ(1U, called); + ASSERT_TRUE(capture_cert_req->filtered()); + ASSERT_TRUE(capture_certificate->filtered()); + + client_->CheckClientAuthCompleted(); + // Check if a non-empty request context is generated and it is + // properly sent back. + EXPECT_LT(0U, capture_cert_req->buffer().len()); + EXPECT_EQ(capture_cert_req->buffer().len(), + capture_certificate->buffer().len()); + EXPECT_EQ(0, memcmp(capture_cert_req->buffer().data(), + capture_certificate->buffer().data(), + capture_cert_req->buffer().len())); + ScopedCERTCertificate cert1(SSL_PeerCertificate(server_->ssl_fd())); + ASSERT_NE(nullptr, cert1.get()); + ScopedCERTCertificate cert2(SSL_LocalCertificate(client_->ssl_fd())); + ASSERT_NE(nullptr, cert2.get()); + EXPECT_TRUE(SECITEM_ItemsAreEqual(&cert1->derCert, &cert2->derCert)); +} + +TEST_P(TlsConnectClientAuthStream13, PostHandshakeAuthAfterResumption) { + ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET); + ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3); + Connect(); + + SendReceive(); // Need to read so that we absorb the session tickets. + CheckKeys(); + + // Resume the connection. + Reset(); + + ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET); + ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3); + ExpectResumption(RESUME_TICKET); + + client_->SetupClientAuth(std::get<2>(GetParam()), true); + client_->SetOption(SSL_ENABLE_POST_HANDSHAKE_AUTH, PR_TRUE); + Connect(); + SendReceive(); + + size_t called = 0; + server_->SetAuthCertificateCallback( + [&called](TlsAgent*, PRBool, PRBool) -> SECStatus { + called++; + return SECSuccess; + }); + EXPECT_EQ(SECSuccess, SSL_SendCertificateRequest(server_->ssl_fd())) + << "Unexpected error: " << PORT_ErrorToName(PORT_GetError()); + + server_->SendData(50); + client_->ReadBytes(50); + client_->ClientAuthCallbackComplete(); + client_->SendData(50); + server_->ReadBytes(50); + + client_->CheckClientAuthCompleted(); + EXPECT_EQ(1U, called); + + ScopedCERTCertificate cert1(SSL_PeerCertificate(server_->ssl_fd())); + ASSERT_NE(nullptr, cert1.get()); + ScopedCERTCertificate cert2(SSL_LocalCertificate(client_->ssl_fd())); + ASSERT_NE(nullptr, cert2.get()); + EXPECT_TRUE(SECITEM_ItemsAreEqual(&cert1->derCert, &cert2->derCert)); +} + +static SECStatus GetClientAuthDataHook(void* self, PRFileDesc* fd, + CERTDistNames* caNames, + CERTCertificate** clientCert, + SECKEYPrivateKey** clientKey) { + ScopedCERTCertificate cert; + ScopedSECKEYPrivateKey priv; + // use a different certificate than TlsAgent::kClient + if (!TlsAgent::LoadCertificate(TlsAgent::kRsa2048, &cert, &priv)) { + return SECFailure; + } + + *clientCert = cert.release(); + *clientKey = priv.release(); + return SECSuccess; +} + +typedef struct AutoClientTestStr { + SECStatus result; + const std::string cert; +} AutoClientTest; + +typedef struct AutoClientResultsStr { + AutoClientTest isRsa2048; + AutoClientTest isClient; + AutoClientTest isNull; + bool hookCalled; +} AutoClientResults; + +void VerifyClientCertMatch(CERTCertificate* clientCert, + const std::string expectedName) { + const char* name = clientCert->nickname; + std::cout << "Match name=\"" << name << "\" expected=\"" << expectedName + << "\"" << std::endl; + EXPECT_TRUE(PORT_Strcmp(name, expectedName.c_str()) == 0) + << " Certmismatch: \"" << name << "\" != \"" << expectedName << "\""; +} + +static SECStatus GetAutoClientAuthDataHook(void* expectResults, PRFileDesc* fd, + CERTDistNames* caNames, + CERTCertificate** clientCert, + SECKEYPrivateKey** clientKey) { + AutoClientResults& results = *(AutoClientResults*)expectResults; + SECStatus rv; + + results.hookCalled = true; + *clientCert = NULL; + *clientKey = NULL; + rv = NSS_GetClientAuthData((void*)TlsAgent::kRsa2048.c_str(), fd, caNames, + clientCert, clientKey); + if (rv == SECSuccess) { + VerifyClientCertMatch(*clientCert, results.isRsa2048.cert); + CERT_DestroyCertificate(*clientCert); + SECKEY_DestroyPrivateKey(*clientKey); + *clientCert = NULL; + *clientKey = NULL; + } + EXPECT_EQ(results.isRsa2048.result, rv); + + rv = NSS_GetClientAuthData((void*)TlsAgent::kClient.c_str(), fd, caNames, + clientCert, clientKey); + if (rv == SECSuccess) { + VerifyClientCertMatch(*clientCert, results.isClient.cert); + CERT_DestroyCertificate(*clientCert); + SECKEY_DestroyPrivateKey(*clientKey); + *clientCert = NULL; + *clientKey = NULL; + } + EXPECT_EQ(results.isClient.result, rv); + EXPECT_EQ(*clientCert, nullptr); + EXPECT_EQ(*clientKey, nullptr); + rv = NSS_GetClientAuthData(NULL, fd, caNames, clientCert, clientKey); + if (rv == SECSuccess) { + VerifyClientCertMatch(*clientCert, results.isNull.cert); + // return this result + } + EXPECT_EQ(results.isNull.result, rv); + return rv; +} + +// while I would have liked to use a new INSTANTIATE macro the +// generates the following three tests, figuring out how to make that +// work on top of the existing TlsConnect* plumbing hurts my head. +TEST_P(TlsConnectTls12, AutoClientSelectRsaPss) { + AutoClientResults rsa = {{SECSuccess, TlsAgent::kRsa2048}, + {SECSuccess, TlsAgent::kClient}, + {SECSuccess, TlsAgent::kDelegatorRsaPss2048}, + false}; + static const SSLSignatureScheme kSchemes[] = {ssl_sig_rsa_pss_pss_sha256, + ssl_sig_rsa_pkcs1_sha256, + ssl_sig_rsa_pkcs1_sha1}; + Reset("rsa_pss_noparam"); + client_->SetupClientAuth(); + server_->RequestClientAuth(true); + EXPECT_EQ(SECSuccess, + SSL_GetClientAuthDataHook(client_->ssl_fd(), + GetAutoClientAuthDataHook, (void*)&rsa)); + server_->SetSignatureSchemes(kSchemes, PR_ARRAY_SIZE(kSchemes)); + client_->SetSignatureSchemes(kSchemes, PR_ARRAY_SIZE(kSchemes)); + Connect(); + EXPECT_TRUE(rsa.hookCalled); +} + +TEST_P(TlsConnectTls12, AutoClientSelectEcc) { + AutoClientResults ecc = {{SECFailure, TlsAgent::kClient}, + {SECFailure, TlsAgent::kClient}, + {SECSuccess, TlsAgent::kDelegatorEcdsa256}, + false}; + static const SSLSignatureScheme kSchemes[] = {ssl_sig_ecdsa_secp256r1_sha256}; + client_->SetupClientAuth(); + server_->RequestClientAuth(true); + EXPECT_EQ(SECSuccess, + SSL_GetClientAuthDataHook(client_->ssl_fd(), + GetAutoClientAuthDataHook, (void*)&ecc)); + server_->SetSignatureSchemes(kSchemes, PR_ARRAY_SIZE(kSchemes)); + client_->SetSignatureSchemes(kSchemes, PR_ARRAY_SIZE(kSchemes)); + Connect(); + EXPECT_TRUE(ecc.hookCalled); +} + +TEST_P(TlsConnectTls12, AutoClientSelectDsa) { + AutoClientResults dsa = {{SECFailure, TlsAgent::kClient}, + {SECFailure, TlsAgent::kClient}, + {SECSuccess, TlsAgent::kServerDsa}, + false}; + static const SSLSignatureScheme kSchemes[] = {ssl_sig_dsa_sha256}; + client_->SetupClientAuth(); + server_->RequestClientAuth(true); + EXPECT_EQ(SECSuccess, + SSL_GetClientAuthDataHook(client_->ssl_fd(), + GetAutoClientAuthDataHook, (void*)&dsa)); + server_->SetSignatureSchemes(kSchemes, PR_ARRAY_SIZE(kSchemes)); + client_->SetSignatureSchemes(kSchemes, PR_ARRAY_SIZE(kSchemes)); + Connect(); + EXPECT_TRUE(dsa.hookCalled); +} + +TEST_P(TlsConnectClientAuthStream13, PostHandshakeAuthMultiple) { + client_->SetupClientAuth(std::get<2>(GetParam()), true); + EXPECT_EQ(SECSuccess, SSL_OptionSet(client_->ssl_fd(), + SSL_ENABLE_POST_HANDSHAKE_AUTH, PR_TRUE)); + size_t called = 0; + server_->SetAuthCertificateCallback( + [&called](TlsAgent*, PRBool, PRBool) -> SECStatus { + called++; + return SECSuccess; + }); + Connect(); + EXPECT_EQ(0U, called); + EXPECT_EQ(nullptr, SSL_PeerCertificate(server_->ssl_fd())); + // Send 1st CertificateRequest. + EXPECT_EQ(SECSuccess, SSL_SendCertificateRequest(server_->ssl_fd())) + << "Unexpected error: " << PORT_ErrorToName(PORT_GetError()); + + server_->SendData(50); + client_->ReadBytes(50); + client_->ClientAuthCallbackComplete(); + client_->ReadBytes(50); + client_->SendData(50); + server_->ReadBytes(50); + EXPECT_EQ(1U, called); + client_->CheckClientAuthCompleted(1); + ScopedCERTCertificate cert1(SSL_PeerCertificate(server_->ssl_fd())); + ASSERT_NE(nullptr, cert1.get()); + ScopedCERTCertificate cert2(SSL_LocalCertificate(client_->ssl_fd())); + ASSERT_NE(nullptr, cert2.get()); + EXPECT_TRUE(SECITEM_ItemsAreEqual(&cert1->derCert, &cert2->derCert)); + // Send 2nd CertificateRequest. + client_->SetupClientAuth(std::get<2>(GetParam()), true); + EXPECT_EQ(SECSuccess, SSL_SendCertificateRequest(server_->ssl_fd())) + << "Unexpected error: " << PORT_ErrorToName(PORT_GetError()); + + server_->SendData(50); + client_->ReadBytes(50); + client_->ClientAuthCallbackComplete(); + client_->ReadBytes(50); + client_->SendData(50); + server_->ReadBytes(50); + client_->CheckClientAuthCompleted(2); + EXPECT_EQ(2U, called); + ScopedCERTCertificate cert3(SSL_PeerCertificate(server_->ssl_fd())); + ASSERT_NE(nullptr, cert3.get()); + ScopedCERTCertificate cert4(SSL_LocalCertificate(client_->ssl_fd())); + ASSERT_NE(nullptr, cert4.get()); + EXPECT_TRUE(SECITEM_ItemsAreEqual(&cert3->derCert, &cert4->derCert)); +} + +TEST_P(TlsConnectClientAuthStream13, PostHandshakeAuthConcurrent) { + client_->SetupClientAuth(std::get<2>(GetParam()), true); + EXPECT_EQ(SECSuccess, SSL_OptionSet(client_->ssl_fd(), + SSL_ENABLE_POST_HANDSHAKE_AUTH, PR_TRUE)); + Connect(); + // Send 1st CertificateRequest. + EXPECT_EQ(SECSuccess, SSL_SendCertificateRequest(server_->ssl_fd())) + << "Unexpected error: " << PORT_ErrorToName(PORT_GetError()); + // Send 2nd CertificateRequest. + EXPECT_EQ(SECFailure, SSL_SendCertificateRequest(server_->ssl_fd())); + EXPECT_EQ(PR_WOULD_BLOCK_ERROR, PORT_GetError()); +} + +TEST_P(TlsConnectClientAuthStream13, PostHandshakeAuthBeforeKeyUpdate) { + client_->SetupClientAuth(std::get<2>(GetParam()), true); + EXPECT_EQ(SECSuccess, SSL_OptionSet(client_->ssl_fd(), + SSL_ENABLE_POST_HANDSHAKE_AUTH, PR_TRUE)); + Connect(); + // Send CertificateRequest. + EXPECT_EQ(SECSuccess, SSL_SendCertificateRequest(server_->ssl_fd())) + << "Unexpected error: " << PORT_ErrorToName(PORT_GetError()); + // Send KeyUpdate. + EXPECT_EQ(SECFailure, SSL_KeyUpdate(server_->ssl_fd(), PR_TRUE)); + EXPECT_EQ(PR_WOULD_BLOCK_ERROR, PORT_GetError()); +} + +TEST_P(TlsConnectClientAuthStream13, PostHandshakeAuthDuringClientKeyUpdate) { + client_->SetupClientAuth(std::get<2>(GetParam()), true); + ; + EXPECT_EQ(SECSuccess, SSL_OptionSet(client_->ssl_fd(), + SSL_ENABLE_POST_HANDSHAKE_AUTH, PR_TRUE)); + Connect(); + CheckEpochs(3, 3); + // Send CertificateRequest from server. + EXPECT_EQ(SECSuccess, SSL_SendCertificateRequest(server_->ssl_fd())) + << "Unexpected error: " << PORT_ErrorToName(PORT_GetError()); + // Send KeyUpdate from client. + EXPECT_EQ(SECSuccess, SSL_KeyUpdate(client_->ssl_fd(), PR_TRUE)); + server_->SendData(50); // server sends CertificateRequest + client_->SendData(50); // client sends KeyUpdate + server_->ReadBytes(50); // server receives KeyUpdate and defers response + CheckEpochs(4, 3); + client_->ReadBytes(60); // client receives CertificateRequest + client_->ClientAuthCallbackComplete(); + client_->ReadBytes(50); // Finish reading the remaining bytes + client_->SendData( + 50); // client sends Certificate, CertificateVerify, Finished + server_->ReadBytes( + 50); // server receives Certificate, CertificateVerify, Finished + client_->CheckClientAuthCompleted(); + client_->CheckEpochs(3, 4); + server_->CheckEpochs(4, 4); + server_->SendData(50); // server sends KeyUpdate + client_->ReadBytes(50); // client receives KeyUpdate + client_->CheckEpochs(4, 4); +} + +TEST_P(TlsConnectClientAuthStream13, PostHandshakeAuthMissingExtension) { + client_->SetupClientAuth(std::get<2>(GetParam()), true); + Connect(); + // Send CertificateRequest, should fail due to missing + // post_handshake_auth extension. + EXPECT_EQ(SECFailure, SSL_SendCertificateRequest(server_->ssl_fd())); + EXPECT_EQ(SSL_ERROR_MISSING_POST_HANDSHAKE_AUTH_EXTENSION, PORT_GetError()); +} + +TEST_P(TlsConnectClientAuthStream13, PostHandshakeAuthAfterClientAuth) { + client_->SetupClientAuth(std::get<2>(GetParam()), true); + server_->RequestClientAuth(true); + EXPECT_EQ(SECSuccess, SSL_OptionSet(client_->ssl_fd(), + SSL_ENABLE_POST_HANDSHAKE_AUTH, PR_TRUE)); + size_t called = 0; + server_->SetAuthCertificateCallback( + [&called](TlsAgent*, PRBool, PRBool) -> SECStatus { + called++; + return SECSuccess; + }); + Connect(); + EXPECT_EQ(1U, called); + ScopedCERTCertificate cert1(SSL_PeerCertificate(server_->ssl_fd())); + ASSERT_NE(nullptr, cert1.get()); + ScopedCERTCertificate cert2(SSL_LocalCertificate(client_->ssl_fd())); + ASSERT_NE(nullptr, cert2.get()); + EXPECT_TRUE(SECITEM_ItemsAreEqual(&cert1->derCert, &cert2->derCert)); + // Send CertificateRequest. + EXPECT_EQ(SECSuccess, SSL_GetClientAuthDataHook( + client_->ssl_fd(), GetClientAuthDataHook, nullptr)); + EXPECT_EQ(SECSuccess, SSL_SendCertificateRequest(server_->ssl_fd())) + << "Unexpected error: " << PORT_ErrorToName(PORT_GetError()); + server_->SendData(50); + client_->ReadBytes(50); + client_->SendData(50); + server_->ReadBytes(50); + EXPECT_EQ(2U, called); + ScopedCERTCertificate cert3(SSL_PeerCertificate(server_->ssl_fd())); + ASSERT_NE(nullptr, cert3.get()); + ScopedCERTCertificate cert4(SSL_LocalCertificate(client_->ssl_fd())); + ASSERT_NE(nullptr, cert4.get()); + EXPECT_TRUE(SECITEM_ItemsAreEqual(&cert3->derCert, &cert4->derCert)); + EXPECT_FALSE(SECITEM_ItemsAreEqual(&cert3->derCert, &cert1->derCert)); +} + +// Damages the request context in a CertificateRequest message. +// We don't modify a Certificate message instead, so that the client +// can compute CertificateVerify correctly. +class TlsDamageCertificateRequestContextFilter : public TlsHandshakeFilter { + public: + TlsDamageCertificateRequestContextFilter(const std::shared_ptr<TlsAgent>& a) + : TlsHandshakeFilter(a, {kTlsHandshakeCertificateRequest}) { + EnableDecryption(); + } + + protected: + virtual PacketFilter::Action FilterHandshake(const HandshakeHeader& header, + const DataBuffer& input, + DataBuffer* output) { + *output = input; + assert(1 < output->len()); + // The request context has a 1 octet length. + output->data()[1] ^= 73; + return CHANGE; + } +}; + +TEST_P(TlsConnectClientAuthStream13, PostHandshakeAuthContextMismatch) { + EnsureTlsSetup(); + MakeTlsFilter<TlsDamageCertificateRequestContextFilter>(server_); + client_->SetupClientAuth(std::get<2>(GetParam()), true); + EXPECT_EQ(SECSuccess, SSL_OptionSet(client_->ssl_fd(), + SSL_ENABLE_POST_HANDSHAKE_AUTH, PR_TRUE)); + Connect(); + // Send CertificateRequest. + EXPECT_EQ(SECSuccess, SSL_SendCertificateRequest(server_->ssl_fd())) + << "Unexpected error: " << PORT_ErrorToName(PORT_GetError()); + server_->SendData(50); + client_->ReadBytes(50); + client_->ClientAuthCallbackComplete(); + client_->ReadBytes(50); + client_->SendData(50); + server_->ExpectSendAlert(kTlsAlertIllegalParameter); + server_->ReadBytes(50); + EXPECT_EQ(SSL_ERROR_RX_MALFORMED_CERTIFICATE, PORT_GetError()); + server_->ExpectReadWriteError(); + server_->SendData(50); + client_->ExpectReceiveAlert(kTlsAlertIllegalParameter); + client_->ReadBytes(50); + EXPECT_EQ(SSL_ERROR_ILLEGAL_PARAMETER_ALERT, PORT_GetError()); +} + +// Replaces signature in a CertificateVerify message. +class TlsDamageSignatureFilter : public TlsHandshakeFilter { + public: + TlsDamageSignatureFilter(const std::shared_ptr<TlsAgent>& a) + : TlsHandshakeFilter(a, {kTlsHandshakeCertificateVerify}) { + EnableDecryption(); + } + + protected: + virtual PacketFilter::Action FilterHandshake(const HandshakeHeader& header, + const DataBuffer& input, + DataBuffer* output) { + *output = input; + assert(2 < output->len()); + // The signature follows a 2-octet signature scheme. + output->data()[2] ^= 73; + return CHANGE; + } +}; + +TEST_P(TlsConnectClientAuthStream13, PostHandshakeAuthBadSignature) { + EnsureTlsSetup(); + MakeTlsFilter<TlsDamageSignatureFilter>(client_); + client_->SetupClientAuth(std::get<2>(GetParam()), true); + EXPECT_EQ(SECSuccess, SSL_OptionSet(client_->ssl_fd(), + SSL_ENABLE_POST_HANDSHAKE_AUTH, PR_TRUE)); + Connect(); + // Send CertificateRequest. + EXPECT_EQ(SECSuccess, SSL_SendCertificateRequest(server_->ssl_fd())) + << "Unexpected error: " << PORT_ErrorToName(PORT_GetError()); + server_->SendData(50); + client_->ReadBytes(50); + client_->ClientAuthCallbackComplete(); + client_->SendData(50); + client_->CheckClientAuthCompleted(); + server_->ExpectSendAlert(kTlsAlertDecodeError); + server_->ReadBytes(50); + EXPECT_EQ(SSL_ERROR_RX_MALFORMED_CERT_VERIFY, PORT_GetError()); +} + +TEST_P(TlsConnectClientAuthStream13, PostHandshakeAuthDecline) { + EnsureTlsSetup(); + auto capture_cert_req = MakeTlsFilter<TlsCertificateRequestContextRecorder>( + server_, kTlsHandshakeCertificateRequest); + auto capture_certificate = + MakeTlsFilter<TlsCertificateRequestContextRecorder>( + client_, kTlsHandshakeCertificate); + client_->SetupClientAuth(std::get<2>(GetParam()), true); + EXPECT_EQ(SECSuccess, SSL_OptionSet(client_->ssl_fd(), + SSL_ENABLE_POST_HANDSHAKE_AUTH, PR_TRUE)); + EXPECT_EQ(SECSuccess, + SSL_OptionSet(server_->ssl_fd(), SSL_REQUIRE_CERTIFICATE, + SSL_REQUIRE_ALWAYS)); + // Client to decline the certificate request. + EXPECT_EQ(SECSuccess, + SSL_GetClientAuthDataHook( + client_->ssl_fd(), + [](void*, PRFileDesc*, CERTDistNames*, CERTCertificate**, + SECKEYPrivateKey**) -> SECStatus { return SECFailure; }, + nullptr)); + size_t called = 0; + server_->SetAuthCertificateCallback( + [&called](TlsAgent*, PRBool, PRBool) -> SECStatus { + called++; + return SECSuccess; + }); + Connect(); + EXPECT_EQ(0U, called); + // Send CertificateRequest. + EXPECT_EQ(SECSuccess, SSL_SendCertificateRequest(server_->ssl_fd())) + << "Unexpected error: " << PORT_ErrorToName(PORT_GetError()); + server_->SendData(50); // send Certificate Request + client_->ReadBytes(50); // read Certificate Request + client_->SendData(50); // send empty Certificate+Finished + server_->ExpectSendAlert(kTlsAlertCertificateRequired); + server_->ReadBytes(50); // read empty Certificate+Finished + server_->ExpectReadWriteError(); + server_->SendData(50); // send alert + // AuthCertificateCallback is not called, because the client sends + // an empty certificate_list. + EXPECT_EQ(0U, called); + EXPECT_TRUE(capture_cert_req->filtered()); + EXPECT_TRUE(capture_certificate->filtered()); + // Check if a non-empty request context is generated and it is + // properly sent back. + EXPECT_LT(0U, capture_cert_req->buffer().len()); + EXPECT_EQ(capture_cert_req->buffer().len(), + capture_certificate->buffer().len()); + EXPECT_EQ(0, memcmp(capture_cert_req->buffer().data(), + capture_certificate->buffer().data(), + capture_cert_req->buffer().len())); +} + +// Check if post-handshake auth still works when session tickets are enabled: +// https://bugzilla.mozilla.org/show_bug.cgi?id=1553443 +TEST_P(TlsConnectClientAuthStream13, + PostHandshakeAuthWithSessionTicketsEnabled) { + EnsureTlsSetup(); + client_->SetupClientAuth(std::get<2>(GetParam()), true); + EXPECT_EQ(SECSuccess, SSL_OptionSet(client_->ssl_fd(), + SSL_ENABLE_POST_HANDSHAKE_AUTH, PR_TRUE)); + EXPECT_EQ(SECSuccess, SSL_OptionSet(client_->ssl_fd(), + SSL_ENABLE_SESSION_TICKETS, PR_TRUE)); + EXPECT_EQ(SECSuccess, SSL_OptionSet(server_->ssl_fd(), + SSL_ENABLE_SESSION_TICKETS, PR_TRUE)); + size_t called = 0; + server_->SetAuthCertificateCallback( + [&called](TlsAgent*, PRBool, PRBool) -> SECStatus { + called++; + return SECSuccess; + }); + Connect(); + EXPECT_EQ(0U, called); + // Send CertificateRequest. + EXPECT_EQ(SECSuccess, SSL_GetClientAuthDataHook( + client_->ssl_fd(), GetClientAuthDataHook, nullptr)); + EXPECT_EQ(SECSuccess, SSL_SendCertificateRequest(server_->ssl_fd())) + << "Unexpected error: " << PORT_ErrorToName(PORT_GetError()); + server_->SendData(50); + client_->ReadBytes(50); + client_->SendData(50); + server_->ReadBytes(50); + EXPECT_EQ(1U, called); + ScopedCERTCertificate cert1(SSL_PeerCertificate(server_->ssl_fd())); + ASSERT_NE(nullptr, cert1.get()); + ScopedCERTCertificate cert2(SSL_LocalCertificate(client_->ssl_fd())); + ASSERT_NE(nullptr, cert2.get()); + EXPECT_TRUE(SECITEM_ItemsAreEqual(&cert1->derCert, &cert2->derCert)); +} + +TEST_P(TlsConnectClientAuthPre13, ClientAuthRequiredRejected) { + client_->SetupClientAuth(std::get<2>(GetParam()), false); + server_->RequestClientAuth(true); + ConnectExpectAlert(server_, kTlsAlertBadCertificate); + client_->CheckErrorCode(SSL_ERROR_BAD_CERT_ALERT); + server_->CheckErrorCode(SSL_ERROR_NO_CERTIFICATE); +} + +// In TLS 1.3, the client will claim that the connection is done and then +// receive the alert afterwards. So drive the handshake manually. +TEST_P(TlsConnectClientAuth13, ClientAuthRequiredRejected) { + client_->SetupClientAuth(std::get<2>(GetParam()), false); + server_->RequestClientAuth(true); + StartConnect(); + client_->Handshake(); // CH + server_->Handshake(); // SH.. (no resumption) + + client_->Handshake(); // Next message + ASSERT_EQ(TlsAgent::STATE_CONNECTED, client_->state()); + client_->CheckClientAuthCompleted(); + ExpectAlert(server_, kTlsAlertCertificateRequired); + server_->Handshake(); // Alert + server_->CheckErrorCode(SSL_ERROR_NO_CERTIFICATE); + client_->Handshake(); // Receive Alert + client_->CheckErrorCode(SSL_ERROR_RX_CERTIFICATE_REQUIRED_ALERT); +} + +TEST_P(TlsConnectClientAuth, ClientAuthRequestedRejected) { + client_->SetupClientAuth(std::get<2>(GetParam()), false); + server_->RequestClientAuth(false); + Connect(); + CheckKeys(); +} + +TEST_P(TlsConnectClientAuth, ClientAuthEcdsa) { + Reset(TlsAgent::kServerEcdsa256); + client_->SetupClientAuth(std::get<2>(GetParam()), true); + server_->RequestClientAuth(true); + Connect(); + CheckKeys(ssl_kea_ecdh, ssl_auth_ecdsa); +} + +TEST_P(TlsConnectClientAuth, ClientAuthWithEch) { + Reset(TlsAgent::kServerEcdsa256); + EnsureTlsSetup(); + SetupEch(client_, server_); + client_->SetupClientAuth(std::get<2>(GetParam()), true); + server_->RequestClientAuth(true); + Connect(); + CheckKeys(ssl_kea_ecdh, ssl_auth_ecdsa); +} + +TEST_P(TlsConnectClientAuth, ClientAuthBigRsa) { + Reset(TlsAgent::kServerRsa, TlsAgent::kRsa2048); + client_->SetupClientAuth(std::get<2>(GetParam()), true); + server_->RequestClientAuth(true); + Connect(); + CheckKeys(); +} + +// Offset is the position in the captured buffer where the signature sits. +static void CheckSigScheme(std::shared_ptr<TlsHandshakeRecorder>& capture, + size_t offset, std::shared_ptr<TlsAgent>& peer, + uint16_t expected_scheme, size_t expected_size) { + EXPECT_LT(offset + 2U, capture->buffer().len()); + + uint32_t scheme = 0; + capture->buffer().Read(offset, 2, &scheme); + EXPECT_EQ(expected_scheme, static_cast<uint16_t>(scheme)); + + ScopedCERTCertificate remote_cert(SSL_PeerCertificate(peer->ssl_fd())); + ASSERT_NE(nullptr, remote_cert.get()); + ScopedSECKEYPublicKey remote_key(CERT_ExtractPublicKey(remote_cert.get())); + ASSERT_NE(nullptr, remote_key.get()); + EXPECT_EQ(expected_size, SECKEY_PublicKeyStrengthInBits(remote_key.get())); +} + +// The server should prefer SHA-256 by default, even for the small key size used +// in the default certificate. +TEST_P(TlsConnectTls12, ServerAuthCheckSigAlg) { + EnsureTlsSetup(); + auto capture_ske = MakeTlsFilter<TlsHandshakeRecorder>( + server_, kTlsHandshakeServerKeyExchange); + Connect(); + CheckKeys(); + + const DataBuffer& buffer = capture_ske->buffer(); + EXPECT_LT(3U, buffer.len()); + EXPECT_EQ(3U, buffer.data()[0]) << "curve_type == named_curve"; + uint32_t tmp; + EXPECT_TRUE(buffer.Read(1, 2, &tmp)) << "read NamedCurve"; + EXPECT_EQ(ssl_grp_ec_curve25519, tmp); + EXPECT_TRUE(buffer.Read(3, 1, &tmp)) << " read ECPoint"; + CheckSigScheme(capture_ske, 4 + tmp, client_, ssl_sig_rsa_pss_rsae_sha256, + 1024); +} + +TEST_P(TlsConnectClientAuth12, ClientAuthCheckSigAlg) { + EnsureTlsSetup(); + auto capture_cert_verify = MakeTlsFilter<TlsHandshakeRecorder>( + client_, kTlsHandshakeCertificateVerify); + client_->SetupClientAuth(std::get<2>(GetParam()), true); + server_->RequestClientAuth(true); + Connect(); + CheckKeys(); + + CheckSigScheme(capture_cert_verify, 0, server_, ssl_sig_rsa_pkcs1_sha1, 1024); +} + +TEST_P(TlsConnectClientAuth12, ClientAuthBigRsaCheckSigAlg) { + Reset(TlsAgent::kServerRsa, TlsAgent::kRsa2048); + auto capture_cert_verify = MakeTlsFilter<TlsHandshakeRecorder>( + client_, kTlsHandshakeCertificateVerify); + client_->SetupClientAuth(std::get<2>(GetParam()), true); + server_->RequestClientAuth(true); + Connect(); + CheckKeys(); + CheckSigScheme(capture_cert_verify, 0, server_, ssl_sig_rsa_pss_rsae_sha256, + 2048); +} + +// Check if CertificateVerify signed with rsa_pss_rsae_* is properly +// rejected when the certificate is RSA-PSS. +// +// This only works under TLS 1.2, because PSS doesn't work with TLS +// 1.0 or TLS 1.1 and the TLS 1.3 1-RTT handshake is partially +// successful at the client side. +TEST_P(TlsConnectClientAuth12, ClientAuthInconsistentRsaeSignatureScheme) { + static const SSLSignatureScheme kSignatureSchemePss[] = { + ssl_sig_rsa_pss_pss_sha256, ssl_sig_rsa_pss_rsae_sha256}; + + Reset(TlsAgent::kServerRsa, "rsa_pss"); + client_->SetSignatureSchemes(kSignatureSchemePss, + PR_ARRAY_SIZE(kSignatureSchemePss)); + server_->SetSignatureSchemes(kSignatureSchemePss, + PR_ARRAY_SIZE(kSignatureSchemePss)); + client_->SetupClientAuth(std::get<2>(GetParam()), true); + server_->RequestClientAuth(true); + + EnsureTlsSetup(); + + MakeTlsFilter<TlsReplaceSignatureSchemeFilter>(client_, + ssl_sig_rsa_pss_rsae_sha256); + + ConnectExpectAlert(server_, kTlsAlertIllegalParameter); +} + +// Check if CertificateVerify signed with rsa_pss_pss_* is properly +// rejected when the certificate is RSA. +// +// This only works under TLS 1.2, because PSS doesn't work with TLS +// 1.0 or TLS 1.1 and the TLS 1.3 1-RTT handshake is partially +// successful at the client side. +TEST_P(TlsConnectClientAuth12, ClientAuthInconsistentPssSignatureScheme) { + static const SSLSignatureScheme kSignatureSchemePss[] = { + ssl_sig_rsa_pss_rsae_sha256, ssl_sig_rsa_pss_pss_sha256}; + + Reset(TlsAgent::kServerRsa, "rsa"); + client_->SetSignatureSchemes(kSignatureSchemePss, + PR_ARRAY_SIZE(kSignatureSchemePss)); + server_->SetSignatureSchemes(kSignatureSchemePss, + PR_ARRAY_SIZE(kSignatureSchemePss)); + client_->SetupClientAuth(std::get<2>(GetParam()), true); + server_->RequestClientAuth(true); + + EnsureTlsSetup(); + + MakeTlsFilter<TlsReplaceSignatureSchemeFilter>(client_, + ssl_sig_rsa_pss_pss_sha256); + + ConnectExpectAlert(server_, kTlsAlertIllegalParameter); +} + +TEST_P(TlsConnectClientAuth13, ClientAuthPkcs1SignatureScheme) { + static const SSLSignatureScheme kSignatureScheme[] = { + ssl_sig_rsa_pkcs1_sha256, ssl_sig_rsa_pss_rsae_sha256}; + + Reset(TlsAgent::kServerRsa, "rsa"); + client_->SetSignatureSchemes(kSignatureScheme, + PR_ARRAY_SIZE(kSignatureScheme)); + server_->SetSignatureSchemes(kSignatureScheme, + PR_ARRAY_SIZE(kSignatureScheme)); + client_->SetupClientAuth(std::get<2>(GetParam()), true); + server_->RequestClientAuth(true); + + auto capture_cert_verify = MakeTlsFilter<TlsHandshakeRecorder>( + client_, kTlsHandshakeCertificateVerify); + capture_cert_verify->EnableDecryption(); + + Connect(); + CheckSigScheme(capture_cert_verify, 0, server_, ssl_sig_rsa_pss_rsae_sha256, + 1024); +} + +// Client should refuse to connect without a usable signature scheme. +TEST_P(TlsConnectClientAuth13, ClientAuthPkcs1SignatureSchemeOnly) { + static const SSLSignatureScheme kSignatureScheme[] = { + ssl_sig_rsa_pkcs1_sha256}; + + Reset(TlsAgent::kServerRsa, "rsa"); + client_->SetSignatureSchemes(kSignatureScheme, + PR_ARRAY_SIZE(kSignatureScheme)); + client_->SetupClientAuth(std::get<2>(GetParam()), true); + client_->StartConnect(); + client_->Handshake(); + EXPECT_EQ(TlsAgent::STATE_ERROR, client_->state()); + client_->CheckErrorCode(SSL_ERROR_NO_SUPPORTED_SIGNATURE_ALGORITHM); +} + +// Though the client has a usable signature scheme, when a certificate is +// requested, it can't produce one. +TEST_P(TlsConnectClientAuth13, ClientAuthPkcs1AndEcdsaScheme) { + static const SSLSignatureScheme kSignatureScheme[] = { + ssl_sig_rsa_pkcs1_sha256, ssl_sig_ecdsa_secp256r1_sha256}; + + Reset(TlsAgent::kServerRsa, "rsa"); + client_->SetSignatureSchemes(kSignatureScheme, + PR_ARRAY_SIZE(kSignatureScheme)); + client_->SetupClientAuth(std::get<2>(GetParam()), true); + server_->RequestClientAuth(true); + + ConnectExpectAlert(server_, kTlsAlertHandshakeFailure); + server_->CheckErrorCode(SSL_ERROR_UNSUPPORTED_SIGNATURE_ALGORITHM); + client_->CheckErrorCode(SSL_ERROR_NO_CYPHER_OVERLAP); +} + +class TlsZeroCertificateRequestSigAlgsFilter : public TlsHandshakeFilter { + public: + TlsZeroCertificateRequestSigAlgsFilter(const std::shared_ptr<TlsAgent>& a) + : TlsHandshakeFilter(a, {kTlsHandshakeCertificateRequest}) {} + virtual PacketFilter::Action FilterHandshake( + const TlsHandshakeFilter::HandshakeHeader& header, + const DataBuffer& input, DataBuffer* output) { + TlsParser parser(input); + std::cerr << "Zeroing CertReq.supported_signature_algorithms" << std::endl; + + DataBuffer cert_types; + if (!parser.ReadVariable(&cert_types, 1)) { + ADD_FAILURE(); + return KEEP; + } + + if (!parser.SkipVariable(2)) { + ADD_FAILURE(); + return KEEP; + } + + DataBuffer cas; + if (!parser.ReadVariable(&cas, 2)) { + ADD_FAILURE(); + return KEEP; + } + + size_t idx = 0; + + // Write certificate types. + idx = output->Write(idx, cert_types.len(), 1); + idx = output->Write(idx, cert_types); + + // Write zero signature algorithms. + idx = output->Write(idx, 0U, 2); + + // Write certificate authorities. + idx = output->Write(idx, cas.len(), 2); + idx = output->Write(idx, cas); + + return CHANGE; + } +}; + +// Check that we send an alert when the server doesn't provide any +// supported_signature_algorithms in the CertificateRequest message. +TEST_P(TlsConnectClientAuth12, ClientAuthNoSigAlgs) { + EnsureTlsSetup(); + MakeTlsFilter<TlsZeroCertificateRequestSigAlgsFilter>(server_); + auto capture_cert_verify = MakeTlsFilter<TlsHandshakeRecorder>( + client_, kTlsHandshakeCertificateVerify); + client_->SetupClientAuth(std::get<2>(GetParam()), true); + server_->RequestClientAuth(true); + + ConnectExpectAlert(client_, kTlsAlertHandshakeFailure); + + server_->CheckErrorCode(SSL_ERROR_HANDSHAKE_FAILURE_ALERT); + client_->CheckErrorCode(SSL_ERROR_UNSUPPORTED_SIGNATURE_ALGORITHM); +} + +static SECStatus GetEcClientAuthDataHook(void* self, PRFileDesc* fd, + CERTDistNames* caNames, + CERTCertificate** clientCert, + SECKEYPrivateKey** clientKey) { + ScopedCERTCertificate cert; + ScopedSECKEYPrivateKey priv; + // use a different certificate than TlsAgent::kClient + if (!TlsAgent::LoadCertificate(TlsAgent::kServerEcdsa256, &cert, &priv)) { + return SECFailure; + } + + *clientCert = cert.release(); + *clientKey = priv.release(); + return SECSuccess; +} + +TEST_P(TlsConnectClientAuth12Plus, ClientAuthDisjointSchemes) { + EnsureTlsSetup(); + client_->SetupClientAuth(std::get<2>(GetParam()), true); + server_->RequestClientAuth(true); + + SSLSignatureScheme server_scheme = ssl_sig_rsa_pss_rsae_sha256; + std::vector<SSLSignatureScheme> client_schemes{ + ssl_sig_rsa_pss_rsae_sha256, ssl_sig_ecdsa_secp256r1_sha256}; + SECStatus rv = + SSL_SignatureSchemePrefSet(server_->ssl_fd(), &server_scheme, 1); + EXPECT_EQ(SECSuccess, rv); + rv = SSL_SignatureSchemePrefSet( + client_->ssl_fd(), client_schemes.data(), + static_cast<unsigned int>(client_schemes.size())); + EXPECT_EQ(SECSuccess, rv); + + // Select an EC cert that's incompatible with server schemes. + EXPECT_EQ(SECSuccess, + SSL_GetClientAuthDataHook(client_->ssl_fd(), + GetEcClientAuthDataHook, nullptr)); + + StartConnect(); + client_->Handshake(); // CH + server_->Handshake(); // SH + client_->Handshake(); + if (version_ >= SSL_LIBRARY_VERSION_TLS_1_3) { + ASSERT_EQ(TlsAgent::STATE_CONNECTED, client_->state()); + ExpectAlert(server_, kTlsAlertCertificateRequired); + server_->Handshake(); // Alert + server_->CheckErrorCode(SSL_ERROR_NO_CERTIFICATE); + client_->Handshake(); // Receive Alert + client_->CheckErrorCode(SSL_ERROR_RX_CERTIFICATE_REQUIRED_ALERT); + } else { + ASSERT_EQ(TlsAgent::STATE_CONNECTING, client_->state()); + ExpectAlert(server_, kTlsAlertBadCertificate); + server_->Handshake(); // Alert + server_->CheckErrorCode(SSL_ERROR_NO_CERTIFICATE); + client_->Handshake(); // Receive Alert + client_->CheckErrorCode(SSL_ERROR_BAD_CERT_ALERT); + } +} + +TEST_P(TlsConnectClientAuthStream13, PostHandshakeAuthDisjointSchemes) { + EnsureTlsSetup(); + SSLSignatureScheme server_scheme = ssl_sig_rsa_pss_rsae_sha256; + std::vector<SSLSignatureScheme> client_schemes{ + ssl_sig_rsa_pss_rsae_sha256, ssl_sig_ecdsa_secp256r1_sha256}; + SECStatus rv = + SSL_SignatureSchemePrefSet(server_->ssl_fd(), &server_scheme, 1); + EXPECT_EQ(SECSuccess, rv); + rv = SSL_SignatureSchemePrefSet( + client_->ssl_fd(), client_schemes.data(), + static_cast<unsigned int>(client_schemes.size())); + EXPECT_EQ(SECSuccess, rv); + + client_->SetupClientAuth(std::get<2>(GetParam()), true); + client_->SetOption(SSL_ENABLE_POST_HANDSHAKE_AUTH, PR_TRUE); + + // Select an EC cert that's incompatible with server schemes. + EXPECT_EQ(SECSuccess, + SSL_GetClientAuthDataHook(client_->ssl_fd(), + GetEcClientAuthDataHook, nullptr)); + + Connect(); + + // Send CertificateRequest. + EXPECT_EQ(SECSuccess, SSL_SendCertificateRequest(server_->ssl_fd())) + << "Unexpected error: " << PORT_ErrorToName(PORT_GetError()); + + // Need to do a round-trip so that the post-handshake message is + // handled on both client and server. + server_->SendData(50); + client_->ReadBytes(50); + client_->SendData(50); + server_->ReadBytes(50); + + ScopedCERTCertificate cert1(SSL_PeerCertificate(server_->ssl_fd())); + ASSERT_EQ(nullptr, cert1.get()); + ScopedCERTCertificate cert2(SSL_LocalCertificate(client_->ssl_fd())); + ASSERT_EQ(nullptr, cert2.get()); +} + +static const SSLSignatureScheme kSignatureSchemeEcdsaSha384[] = { + ssl_sig_ecdsa_secp384r1_sha384}; +static const SSLSignatureScheme kSignatureSchemeEcdsaSha256[] = { + ssl_sig_ecdsa_secp256r1_sha256}; +static const SSLSignatureScheme kSignatureSchemeRsaSha384[] = { + ssl_sig_rsa_pkcs1_sha384}; +static const SSLSignatureScheme kSignatureSchemeRsaSha256[] = { + ssl_sig_rsa_pkcs1_sha256}; + +static SSLNamedGroup NamedGroupForEcdsa384(uint16_t version) { + // NSS tries to match the group size to the symmetric cipher. In TLS 1.1 and + // 1.0, TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA is the highest priority suite, so + // we use P-384. With TLS 1.2 on we pick AES-128 GCM so use x25519. + if (version <= SSL_LIBRARY_VERSION_TLS_1_1) { + return ssl_grp_ec_secp384r1; + } + return ssl_grp_ec_curve25519; +} + +// When signature algorithms match up, this should connect successfully; even +// for TLS 1.1 and 1.0, where they should be ignored. +TEST_P(TlsConnectGeneric, SignatureAlgorithmServerAuth) { + Reset(TlsAgent::kServerEcdsa384); + client_->SetSignatureSchemes(kSignatureSchemeEcdsaSha384, + PR_ARRAY_SIZE(kSignatureSchemeEcdsaSha384)); + server_->SetSignatureSchemes(kSignatureSchemeEcdsaSha384, + PR_ARRAY_SIZE(kSignatureSchemeEcdsaSha384)); + Connect(); + CheckKeys(ssl_kea_ecdh, NamedGroupForEcdsa384(version_), ssl_auth_ecdsa, + ssl_sig_ecdsa_secp384r1_sha384); +} + +// Here the client picks a single option, which should work in all versions. +// Defaults on the server include the first option. +TEST_P(TlsConnectGeneric, SignatureAlgorithmClientOnly) { + const SSLSignatureAndHashAlg clientAlgorithms[] = { + {ssl_hash_sha384, ssl_sign_ecdsa}, + {ssl_hash_sha384, ssl_sign_rsa}, // supported but unusable + {ssl_hash_md5, ssl_sign_ecdsa} // unsupported and ignored + }; + Reset(TlsAgent::kServerEcdsa384); + EnsureTlsSetup(); + // Use the old API for this function. + EXPECT_EQ(SECSuccess, + SSL_SignaturePrefSet(client_->ssl_fd(), clientAlgorithms, + PR_ARRAY_SIZE(clientAlgorithms))); + Connect(); + CheckKeys(ssl_kea_ecdh, NamedGroupForEcdsa384(version_), ssl_auth_ecdsa, + ssl_sig_ecdsa_secp384r1_sha384); +} + +// Here the server picks a single option, which should work in all versions. +// Defaults on the client include the provided option. +TEST_P(TlsConnectGeneric, SignatureAlgorithmServerOnly) { + Reset(TlsAgent::kServerEcdsa384); + server_->SetSignatureSchemes(kSignatureSchemeEcdsaSha384, + PR_ARRAY_SIZE(kSignatureSchemeEcdsaSha384)); + Connect(); + CheckKeys(ssl_kea_ecdh, NamedGroupForEcdsa384(version_), ssl_auth_ecdsa, + ssl_sig_ecdsa_secp384r1_sha384); +} + +// In TLS 1.2, curve and hash aren't bound together. +TEST_P(TlsConnectTls12, SignatureSchemeCurveMismatch) { + Reset(TlsAgent::kServerEcdsa256); + client_->SetSignatureSchemes(kSignatureSchemeEcdsaSha384, + PR_ARRAY_SIZE(kSignatureSchemeEcdsaSha384)); + Connect(); +} + +// In TLS 1.3, curve and hash are coupled. +TEST_P(TlsConnectTls13, SignatureSchemeCurveMismatch) { + Reset(TlsAgent::kServerEcdsa256); + client_->SetSignatureSchemes(kSignatureSchemeEcdsaSha384, + PR_ARRAY_SIZE(kSignatureSchemeEcdsaSha384)); + ConnectExpectAlert(server_, kTlsAlertHandshakeFailure); + server_->CheckErrorCode(SSL_ERROR_UNSUPPORTED_SIGNATURE_ALGORITHM); + client_->CheckErrorCode(SSL_ERROR_NO_CYPHER_OVERLAP); +} + +// Configuring a P-256 cert with only SHA-384 signatures is OK in TLS 1.2. +TEST_P(TlsConnectTls12, SignatureSchemeBadConfig) { + Reset(TlsAgent::kServerEcdsa256); // P-256 cert can't be used. + server_->SetSignatureSchemes(kSignatureSchemeEcdsaSha384, + PR_ARRAY_SIZE(kSignatureSchemeEcdsaSha384)); + Connect(); +} + +// A P-256 certificate in TLS 1.3 needs a SHA-256 signature scheme. +TEST_P(TlsConnectTls13, SignatureSchemeBadConfig) { + Reset(TlsAgent::kServerEcdsa256); // P-256 cert can't be used. + server_->SetSignatureSchemes(kSignatureSchemeEcdsaSha384, + PR_ARRAY_SIZE(kSignatureSchemeEcdsaSha384)); + ConnectExpectAlert(server_, kTlsAlertHandshakeFailure); + server_->CheckErrorCode(SSL_ERROR_UNSUPPORTED_SIGNATURE_ALGORITHM); + client_->CheckErrorCode(SSL_ERROR_NO_CYPHER_OVERLAP); +} + +// Where there is no overlap on signature schemes, we still connect successfully +// if we aren't going to use a signature. +TEST_P(TlsConnectGenericPre13, SignatureAlgorithmNoOverlapStaticRsa) { + client_->SetSignatureSchemes(kSignatureSchemeRsaSha384, + PR_ARRAY_SIZE(kSignatureSchemeRsaSha384)); + server_->SetSignatureSchemes(kSignatureSchemeRsaSha256, + PR_ARRAY_SIZE(kSignatureSchemeRsaSha256)); + EnableOnlyStaticRsaCiphers(); + Connect(); + CheckKeys(ssl_kea_rsa, ssl_auth_rsa_decrypt); +} + +TEST_P(TlsConnectTls12Plus, SignatureAlgorithmNoOverlapEcdsa) { + Reset(TlsAgent::kServerEcdsa256); + client_->SetSignatureSchemes(kSignatureSchemeEcdsaSha384, + PR_ARRAY_SIZE(kSignatureSchemeEcdsaSha384)); + server_->SetSignatureSchemes(kSignatureSchemeEcdsaSha256, + PR_ARRAY_SIZE(kSignatureSchemeEcdsaSha256)); + ConnectExpectAlert(server_, kTlsAlertHandshakeFailure); + client_->CheckErrorCode(SSL_ERROR_NO_CYPHER_OVERLAP); + server_->CheckErrorCode(SSL_ERROR_UNSUPPORTED_SIGNATURE_ALGORITHM); +} + +// Pre 1.2, a mismatch on signature algorithms shouldn't affect anything. +TEST_P(TlsConnectPre12, SignatureAlgorithmNoOverlapEcdsa) { + Reset(TlsAgent::kServerEcdsa256); + client_->SetSignatureSchemes(kSignatureSchemeEcdsaSha384, + PR_ARRAY_SIZE(kSignatureSchemeEcdsaSha384)); + server_->SetSignatureSchemes(kSignatureSchemeEcdsaSha256, + PR_ARRAY_SIZE(kSignatureSchemeEcdsaSha256)); + Connect(); +} + +// The signature_algorithms extension is mandatory in TLS 1.3. +TEST_P(TlsConnectTls13, SignatureAlgorithmDrop) { + MakeTlsFilter<TlsExtensionDropper>(client_, ssl_signature_algorithms_xtn); + ConnectExpectAlert(server_, kTlsAlertMissingExtension); + client_->CheckErrorCode(SSL_ERROR_MISSING_EXTENSION_ALERT); + server_->CheckErrorCode(SSL_ERROR_MISSING_SIGNATURE_ALGORITHMS_EXTENSION); +} + +// TLS 1.2 has trouble detecting this sort of modification: it uses SHA1 and +// only fails when the Finished is checked. +TEST_P(TlsConnectTls12, SignatureAlgorithmDrop) { + MakeTlsFilter<TlsExtensionDropper>(client_, ssl_signature_algorithms_xtn); + ConnectExpectAlert(server_, kTlsAlertDecryptError); + client_->CheckErrorCode(SSL_ERROR_DECRYPT_ERROR_ALERT); + server_->CheckErrorCode(SSL_ERROR_BAD_HANDSHAKE_HASH_VALUE); +} + +TEST_P(TlsConnectTls13, UnsupportedSignatureSchemeAlert) { + EnsureTlsSetup(); + auto filter = + MakeTlsFilter<TlsReplaceSignatureSchemeFilter>(server_, ssl_sig_none); + filter->EnableDecryption(); + + ConnectExpectAlert(client_, kTlsAlertIllegalParameter); + server_->CheckErrorCode(SSL_ERROR_ILLEGAL_PARAMETER_ALERT); + client_->CheckErrorCode(SSL_ERROR_RX_MALFORMED_CERT_VERIFY); +} + +TEST_P(TlsConnectTls13, InconsistentSignatureSchemeAlert) { + EnsureTlsSetup(); + + // This won't work because we use an RSA cert by default. + auto filter = MakeTlsFilter<TlsReplaceSignatureSchemeFilter>( + server_, ssl_sig_ecdsa_secp256r1_sha256); + filter->EnableDecryption(); + + ConnectExpectAlert(client_, kTlsAlertIllegalParameter); + server_->CheckErrorCode(SSL_ERROR_ILLEGAL_PARAMETER_ALERT); + client_->CheckErrorCode(SSL_ERROR_INCORRECT_SIGNATURE_ALGORITHM); +} + +TEST_P(TlsConnectTls12, RequestClientAuthWithSha384) { + server_->SetSignatureSchemes(kSignatureSchemeRsaSha384, + PR_ARRAY_SIZE(kSignatureSchemeRsaSha384)); + server_->RequestClientAuth(false); + Connect(); +} + +class BeforeFinished : public TlsRecordFilter { + private: + enum HandshakeState { BEFORE_CCS, AFTER_CCS, DONE }; + + public: + BeforeFinished(const std::shared_ptr<TlsAgent>& server, + const std::shared_ptr<TlsAgent>& client, + VoidFunction before_ccs, VoidFunction before_finished) + : TlsRecordFilter(server), + client_(client), + before_ccs_(before_ccs), + before_finished_(before_finished), + state_(BEFORE_CCS) {} + + protected: + virtual PacketFilter::Action FilterRecord(const TlsRecordHeader& header, + const DataBuffer& body, + DataBuffer* out) { + switch (state_) { + case BEFORE_CCS: + // Awaken when we see the CCS. + if (header.content_type() == ssl_ct_change_cipher_spec) { + before_ccs_(); + + // Write the CCS out as a separate write, so that we can make + // progress. Ordinarily, libssl sends the CCS and Finished together, + // but that means that they both get processed together. + DataBuffer ccs; + header.Write(&ccs, 0, body); + agent()->SendDirect(ccs); + client_.lock()->Handshake(); + state_ = AFTER_CCS; + // Request that the original record be dropped by the filter. + return DROP; + } + break; + + case AFTER_CCS: + EXPECT_EQ(ssl_ct_handshake, header.content_type()); + // This could check that data contains a Finished message, but it's + // encrypted, so that's too much extra work. + + before_finished_(); + state_ = DONE; + break; + + case DONE: + break; + } + return KEEP; + } + + private: + std::weak_ptr<TlsAgent> client_; + VoidFunction before_ccs_; + VoidFunction before_finished_; + HandshakeState state_; +}; + +// Running code after the client has started processing the encrypted part of +// the server's first flight, but before the Finished is processed is very hard +// in TLS 1.3. These encrypted messages are sent in a single encrypted blob. +// The following test uses DTLS to make it possible to force the client to +// process the handshake in pieces. +// +// The first encrypted message from the server is dropped, and the MTU is +// reduced to just below the original message size so that the server sends two +// messages. The Finished message is then processed separately. +class BeforeFinished13 : public PacketFilter { + private: + enum HandshakeState { + INIT, + BEFORE_FIRST_FRAGMENT, + BEFORE_SECOND_FRAGMENT, + DONE + }; + + public: + BeforeFinished13(const std::shared_ptr<TlsAgent>& server, + const std::shared_ptr<TlsAgent>& client, + VoidFunction before_finished) + : server_(server), + client_(client), + before_finished_(before_finished), + records_(0) {} + + protected: + virtual PacketFilter::Action Filter(const DataBuffer& input, + DataBuffer* output) { + switch (++records_) { + case 1: + // Packet 1 is the server's entire first flight. Drop it. + EXPECT_EQ(SECSuccess, + SSLInt_SetMTU(server_.lock()->ssl_fd(), input.len() - 1)); + return DROP; + + // Packet 2 is the first part of the server's retransmitted first + // flight. Keep that. + + case 3: + // Packet 3 is the second part of the server's retransmitted first + // flight. Before passing that on, make sure that the client processes + // packet 2, then call the before_finished_() callback. + client_.lock()->Handshake(); + before_finished_(); + break; + + default: + break; + } + return KEEP; + } + + private: + std::weak_ptr<TlsAgent> server_; + std::weak_ptr<TlsAgent> client_; + VoidFunction before_finished_; + size_t records_; +}; + +static SECStatus AuthCompleteBlock(TlsAgent*, PRBool, PRBool) { + return SECWouldBlock; +} + +// This test uses an AuthCertificateCallback that blocks. A filter is used to +// split the server's first flight into two pieces. Before the second piece is +// processed by the client, SSL_AuthCertificateComplete() is called. +TEST_F(TlsConnectDatagram13, AuthCompleteBeforeFinished) { + client_->SetAuthCertificateCallback(AuthCompleteBlock); + MakeTlsFilter<BeforeFinished13>(server_, client_, [this]() { + EXPECT_EQ(SECSuccess, SSL_AuthCertificateComplete(client_->ssl_fd(), 0)); + }); + Connect(); +} + +// This test uses a simple AuthCertificateCallback. Due to the way that the +// entire server flight is processed, the call to SSL_AuthCertificateComplete +// will trigger after the Finished message is processed. +TEST_P(TlsConnectTls13, AuthCompleteAfterFinished) { + SetDeferredAuthCertificateCallback(client_, 0); // 0 = success. + Connect(); +} + +TEST_P(TlsConnectGenericPre13, ClientWriteBetweenCCSAndFinishedWithFalseStart) { + client_->EnableFalseStart(); + MakeTlsFilter<BeforeFinished>( + server_, client_, + [this]() { EXPECT_TRUE(client_->can_falsestart_hook_called()); }, + [this]() { + // Write something, which used to fail: bug 1235366. + client_->SendData(10); + }); + + Connect(); + server_->SendData(10); + Receive(10); +} + +TEST_P(TlsConnectGenericPre13, AuthCompleteBeforeFinishedWithFalseStart) { + client_->EnableFalseStart(); + client_->SetAuthCertificateCallback(AuthCompleteBlock); + MakeTlsFilter<BeforeFinished>( + server_, client_, + []() { + // Do nothing before CCS + }, + [this]() { + EXPECT_FALSE(client_->can_falsestart_hook_called()); + // AuthComplete before Finished still enables false start. + EXPECT_EQ(SECSuccess, + SSL_AuthCertificateComplete(client_->ssl_fd(), 0)); + EXPECT_TRUE(client_->can_falsestart_hook_called()); + client_->SendData(10); + }); + + Connect(); + server_->SendData(10); + Receive(10); +} + +class EnforceNoActivity : public PacketFilter { + protected: + PacketFilter::Action Filter(const DataBuffer& input, + DataBuffer* output) override { + std::cerr << "Unexpected packet: " << input << std::endl; + EXPECT_TRUE(false) << "should not send anything"; + return KEEP; + } +}; + +// In this test, we want to make sure that the server completes its handshake, +// but the client does not. Because the AuthCertificate callback blocks and we +// never call SSL_AuthCertificateComplete(), the client should never report that +// it has completed the handshake. Manually call Handshake(), alternating sides +// between client and server, until the desired state is reached. +TEST_P(TlsConnectGenericPre13, AuthCompleteDelayed) { + client_->SetAuthCertificateCallback(AuthCompleteBlock); + + StartConnect(); + client_->Handshake(); // Send ClientHello + server_->Handshake(); // Send ServerHello + client_->Handshake(); // Send ClientKeyExchange and Finished + server_->Handshake(); // Send Finished + // The server should now report that it is connected + EXPECT_EQ(TlsAgent::STATE_CONNECTED, server_->state()); + + // The client should send nothing from here on. + client_->SetFilter(std::make_shared<EnforceNoActivity>()); + client_->Handshake(); + EXPECT_EQ(TlsAgent::STATE_CONNECTING, client_->state()); + + // This should allow the handshake to complete now. + EXPECT_EQ(SECSuccess, SSL_AuthCertificateComplete(client_->ssl_fd(), 0)); + client_->Handshake(); // Transition to connected + EXPECT_EQ(TlsAgent::STATE_CONNECTED, client_->state()); + EXPECT_EQ(TlsAgent::STATE_CONNECTED, server_->state()); + + // Remove filter before closing or the close_notify alert will trigger it. + client_->ClearFilter(); +} + +TEST_P(TlsConnectGenericPre13, AuthCompleteFailDelayed) { + client_->SetAuthCertificateCallback(AuthCompleteBlock); + + StartConnect(); + client_->Handshake(); // Send ClientHello + server_->Handshake(); // Send ServerHello + client_->Handshake(); // Send ClientKeyExchange and Finished + server_->Handshake(); // Send Finished + // The server should now report that it is connected + EXPECT_EQ(TlsAgent::STATE_CONNECTED, server_->state()); + + // The client should send nothing from here on. + client_->SetFilter(std::make_shared<EnforceNoActivity>()); + client_->Handshake(); + EXPECT_EQ(TlsAgent::STATE_CONNECTING, client_->state()); + + // Report failure. + client_->ClearFilter(); + client_->ExpectSendAlert(kTlsAlertBadCertificate); + EXPECT_EQ(SECSuccess, SSL_AuthCertificateComplete(client_->ssl_fd(), + SSL_ERROR_BAD_CERTIFICATE)); + client_->Handshake(); // Fail + EXPECT_EQ(TlsAgent::STATE_ERROR, client_->state()); +} + +// TLS 1.3 handles a delayed AuthComplete callback differently since the +// shape of the handshake is different. +TEST_P(TlsConnectTls13, AuthCompleteDelayed) { + client_->SetAuthCertificateCallback(AuthCompleteBlock); + + StartConnect(); + client_->Handshake(); // Send ClientHello + server_->Handshake(); // Send ServerHello + EXPECT_EQ(TlsAgent::STATE_CONNECTING, client_->state()); + EXPECT_EQ(TlsAgent::STATE_CONNECTING, server_->state()); + + // The client will send nothing until AuthCertificateComplete is called. + client_->SetFilter(std::make_shared<EnforceNoActivity>()); + client_->Handshake(); + EXPECT_EQ(TlsAgent::STATE_CONNECTING, client_->state()); + + // This should allow the handshake to complete now. + client_->ClearFilter(); + EXPECT_EQ(SECSuccess, SSL_AuthCertificateComplete(client_->ssl_fd(), 0)); + client_->Handshake(); // Send Finished + server_->Handshake(); // Transition to connected and send NewSessionTicket + EXPECT_EQ(TlsAgent::STATE_CONNECTED, client_->state()); + EXPECT_EQ(TlsAgent::STATE_CONNECTED, server_->state()); +} + +TEST_P(TlsConnectTls13, AuthCompleteFailDelayed) { + client_->SetAuthCertificateCallback(AuthCompleteBlock); + + StartConnect(); + client_->Handshake(); // Send ClientHello + server_->Handshake(); // Send ServerHello + EXPECT_EQ(TlsAgent::STATE_CONNECTING, client_->state()); + EXPECT_EQ(TlsAgent::STATE_CONNECTING, server_->state()); + + // The client will send nothing until AuthCertificateComplete is called. + client_->SetFilter(std::make_shared<EnforceNoActivity>()); + client_->Handshake(); + EXPECT_EQ(TlsAgent::STATE_CONNECTING, client_->state()); + + // Report failure. + client_->ClearFilter(); + ExpectAlert(client_, kTlsAlertBadCertificate); + EXPECT_EQ(SECSuccess, SSL_AuthCertificateComplete(client_->ssl_fd(), + SSL_ERROR_BAD_CERTIFICATE)); + client_->Handshake(); // This should now fail. + server_->Handshake(); // Get the error. + EXPECT_EQ(TlsAgent::STATE_ERROR, client_->state()); + EXPECT_EQ(TlsAgent::STATE_ERROR, server_->state()); +} + +static SECStatus AuthCompleteFail(TlsAgent*, PRBool, PRBool) { + PORT_SetError(SSL_ERROR_BAD_CERTIFICATE); + return SECFailure; +} + +TEST_P(TlsConnectGeneric, AuthFailImmediate) { + client_->SetAuthCertificateCallback(AuthCompleteFail); + + StartConnect(); + ConnectExpectAlert(client_, kTlsAlertBadCertificate); + client_->CheckErrorCode(SSL_ERROR_BAD_CERTIFICATE); +} + +static const SSLExtraServerCertData ServerCertDataRsaPkcs1Decrypt = { + ssl_auth_rsa_decrypt, nullptr, nullptr, nullptr, nullptr, nullptr}; +static const SSLExtraServerCertData ServerCertDataRsaPkcs1Sign = { + ssl_auth_rsa_sign, nullptr, nullptr, nullptr, nullptr, nullptr}; +static const SSLExtraServerCertData ServerCertDataRsaPss = { + 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)); + EXPECT_FALSE(SSLInt_HasCertWithAuthType(ssl_fd, ssl_auth_rsa_pss)); + + // Configuring for only rsa_sign or rsa_decrypt should work. + EXPECT_TRUE(agent_->ConfigServerCert(TlsAgent::kServerRsa, false, + &ServerCertDataRsaPkcs1Decrypt)); + EXPECT_TRUE(agent_->ConfigServerCert(TlsAgent::kServerRsa, false, + &ServerCertDataRsaPkcs1Sign)); + EXPECT_FALSE(agent_->ConfigServerCert(TlsAgent::kServerRsa, false, + &ServerCertDataRsaPss)); +} + +// Test RSA cert with usage=[signature]. +TEST_F(TlsAgentStreamTestServer, ConfigureCertRsaPkcs1Sign) { + Reset(TlsAgent::kServerRsaSign); + + PRFileDesc* ssl_fd = agent_->ssl_fd(); + EXPECT_FALSE(SSLInt_HasCertWithAuthType(ssl_fd, ssl_auth_rsa_decrypt)); + EXPECT_TRUE(SSLInt_HasCertWithAuthType(ssl_fd, ssl_auth_rsa_sign)); + EXPECT_FALSE(SSLInt_HasCertWithAuthType(ssl_fd, ssl_auth_rsa_pss)); + + // Configuring for only rsa_decrypt should fail. + EXPECT_FALSE(agent_->ConfigServerCert(TlsAgent::kServerRsaSign, false, + &ServerCertDataRsaPkcs1Decrypt)); + + // Configuring for only rsa_sign should work. + EXPECT_TRUE(agent_->ConfigServerCert(TlsAgent::kServerRsaSign, false, + &ServerCertDataRsaPkcs1Sign)); + EXPECT_FALSE(agent_->ConfigServerCert(TlsAgent::kServerRsaSign, false, + &ServerCertDataRsaPss)); +} + +// Test RSA cert with usage=[encipherment]. +TEST_F(TlsAgentStreamTestServer, ConfigureCertRsaPkcs1KEX) { + Reset(TlsAgent::kServerRsaDecrypt); + + PRFileDesc* ssl_fd = agent_->ssl_fd(); + EXPECT_TRUE(SSLInt_HasCertWithAuthType(ssl_fd, ssl_auth_rsa_decrypt)); + EXPECT_FALSE(SSLInt_HasCertWithAuthType(ssl_fd, ssl_auth_rsa_sign)); + EXPECT_FALSE(SSLInt_HasCertWithAuthType(ssl_fd, ssl_auth_rsa_pss)); + + // Configuring for only rsa_sign or rsa_pss should fail. + EXPECT_FALSE(agent_->ConfigServerCert(TlsAgent::kServerRsaDecrypt, false, + &ServerCertDataRsaPkcs1Sign)); + EXPECT_FALSE(agent_->ConfigServerCert(TlsAgent::kServerRsaDecrypt, false, + &ServerCertDataRsaPss)); + + // Configuring for only rsa_decrypt should work. + EXPECT_TRUE(agent_->ConfigServerCert(TlsAgent::kServerRsaDecrypt, false, + &ServerCertDataRsaPkcs1Decrypt)); +} + +// Test configuring an RSA-PSS cert. +TEST_F(TlsAgentStreamTestServer, ConfigureCertRsaPss) { + Reset(TlsAgent::kServerRsaPss); + + PRFileDesc* ssl_fd = agent_->ssl_fd(); + EXPECT_FALSE(SSLInt_HasCertWithAuthType(ssl_fd, ssl_auth_rsa_decrypt)); + EXPECT_FALSE(SSLInt_HasCertWithAuthType(ssl_fd, ssl_auth_rsa_sign)); + EXPECT_TRUE(SSLInt_HasCertWithAuthType(ssl_fd, ssl_auth_rsa_pss)); + + // Configuring for only rsa_sign or rsa_decrypt should fail. + EXPECT_FALSE(agent_->ConfigServerCert(TlsAgent::kServerRsaPss, false, + &ServerCertDataRsaPkcs1Sign)); + EXPECT_FALSE(agent_->ConfigServerCert(TlsAgent::kServerRsaPss, false, + &ServerCertDataRsaPkcs1Decrypt)); + + // Configuring for only rsa_pss should work. + EXPECT_TRUE(agent_->ConfigServerCert(TlsAgent::kServerRsaPss, false, + &ServerCertDataRsaPss)); +} + +// A server should refuse to even start a handshake with +// misconfigured certificate and signature scheme. +TEST_P(TlsConnectTls12Plus, MisconfiguredCertScheme) { + Reset(TlsAgent::kServerDsa); + static const SSLSignatureScheme kScheme[] = {ssl_sig_ecdsa_secp256r1_sha256}; + server_->SetSignatureSchemes(kScheme, PR_ARRAY_SIZE(kScheme)); + ConnectExpectAlert(server_, kTlsAlertHandshakeFailure); + if (version_ < SSL_LIBRARY_VERSION_TLS_1_3) { + // TLS 1.2 disables cipher suites, which leads to a different error. + server_->CheckErrorCode(SSL_ERROR_NO_CYPHER_OVERLAP); + } else { + server_->CheckErrorCode(SSL_ERROR_NO_SUPPORTED_SIGNATURE_ALGORITHM); + } + client_->CheckErrorCode(SSL_ERROR_NO_CYPHER_OVERLAP); +} + +// In TLS 1.2, disabling an EC group causes ECDSA to be invalid. +TEST_P(TlsConnectTls12, Tls12CertDisabledGroup) { + Reset(TlsAgent::kServerEcdsa256); + static const std::vector<SSLNamedGroup> k25519 = {ssl_grp_ec_curve25519}; + server_->ConfigNamedGroups(k25519); + ConnectExpectAlert(server_, kTlsAlertHandshakeFailure); + server_->CheckErrorCode(SSL_ERROR_NO_CYPHER_OVERLAP); + client_->CheckErrorCode(SSL_ERROR_NO_CYPHER_OVERLAP); +} + +// In TLS 1.3, ECDSA configuration only depends on the signature scheme. +TEST_P(TlsConnectTls13, Tls13CertDisabledGroup) { + Reset(TlsAgent::kServerEcdsa256); + static const std::vector<SSLNamedGroup> k25519 = {ssl_grp_ec_curve25519}; + server_->ConfigNamedGroups(k25519); + Connect(); +} + +// A client should refuse to even start a handshake with only DSA. +TEST_P(TlsConnectTls13, Tls13DsaOnlyClient) { + static const SSLSignatureScheme kDsa[] = {ssl_sig_dsa_sha256}; + client_->SetSignatureSchemes(kDsa, PR_ARRAY_SIZE(kDsa)); + client_->StartConnect(); + client_->Handshake(); + EXPECT_EQ(TlsAgent::STATE_ERROR, client_->state()); + client_->CheckErrorCode(SSL_ERROR_NO_SUPPORTED_SIGNATURE_ALGORITHM); +} + +TEST_P(TlsConnectTls13, Tls13DsaOnlyServer) { + Reset(TlsAgent::kServerDsa); + static const SSLSignatureScheme kDsa[] = {ssl_sig_dsa_sha256}; + server_->SetSignatureSchemes(kDsa, PR_ARRAY_SIZE(kDsa)); + ConnectExpectAlert(server_, kTlsAlertHandshakeFailure); + server_->CheckErrorCode(SSL_ERROR_NO_SUPPORTED_SIGNATURE_ALGORITHM); + client_->CheckErrorCode(SSL_ERROR_NO_CYPHER_OVERLAP); +} + +TEST_P(TlsConnectTls13, Tls13Pkcs1OnlyClient) { + static const SSLSignatureScheme kPkcs1[] = {ssl_sig_rsa_pkcs1_sha256}; + client_->SetSignatureSchemes(kPkcs1, PR_ARRAY_SIZE(kPkcs1)); + client_->StartConnect(); + client_->Handshake(); + EXPECT_EQ(TlsAgent::STATE_ERROR, client_->state()); + client_->CheckErrorCode(SSL_ERROR_NO_SUPPORTED_SIGNATURE_ALGORITHM); +} + +TEST_P(TlsConnectTls13, Tls13Pkcs1OnlyServer) { + static const SSLSignatureScheme kPkcs1[] = {ssl_sig_rsa_pkcs1_sha256}; + server_->SetSignatureSchemes(kPkcs1, PR_ARRAY_SIZE(kPkcs1)); + ConnectExpectAlert(server_, kTlsAlertHandshakeFailure); + server_->CheckErrorCode(SSL_ERROR_NO_SUPPORTED_SIGNATURE_ALGORITHM); + client_->CheckErrorCode(SSL_ERROR_NO_CYPHER_OVERLAP); +} + +TEST_P(TlsConnectTls13, Tls13DsaIsNotAdvertisedClient) { + EnsureTlsSetup(); + static const SSLSignatureScheme kSchemes[] = {ssl_sig_dsa_sha256, + ssl_sig_rsa_pss_rsae_sha256}; + client_->SetSignatureSchemes(kSchemes, PR_ARRAY_SIZE(kSchemes)); + auto capture = + MakeTlsFilter<TlsExtensionCapture>(client_, ssl_signature_algorithms_xtn); + Connect(); + // We should only have the one signature algorithm advertised. + static const uint8_t kExpectedExt[] = {0, 2, ssl_sig_rsa_pss_rsae_sha256 >> 8, + ssl_sig_rsa_pss_rsae_sha256 & 0xff}; + ASSERT_EQ(DataBuffer(kExpectedExt, sizeof(kExpectedExt)), + capture->extension()); +} + +TEST_P(TlsConnectTls13, Tls13DsaIsNotAdvertisedServer) { + EnsureTlsSetup(); + static const SSLSignatureScheme kSchemes[] = {ssl_sig_dsa_sha256, + ssl_sig_rsa_pss_rsae_sha256}; + server_->SetSignatureSchemes(kSchemes, PR_ARRAY_SIZE(kSchemes)); + auto capture = MakeTlsFilter<TlsExtensionCapture>( + server_, ssl_signature_algorithms_xtn, true); + capture->SetHandshakeTypes({kTlsHandshakeCertificateRequest}); + capture->EnableDecryption(); + server_->RequestClientAuth(false); // So we get a CertificateRequest. + Connect(); + // We should only have the one signature algorithm advertised. + static const uint8_t kExpectedExt[] = {0, 2, ssl_sig_rsa_pss_rsae_sha256 >> 8, + ssl_sig_rsa_pss_rsae_sha256 & 0xff}; + ASSERT_EQ(DataBuffer(kExpectedExt, sizeof(kExpectedExt)), + capture->extension()); +} + +TEST_P(TlsConnectTls13, Tls13RsaPkcs1IsAdvertisedClient) { + EnsureTlsSetup(); + static const SSLSignatureScheme kSchemes[] = {ssl_sig_rsa_pkcs1_sha256, + ssl_sig_rsa_pss_rsae_sha256}; + client_->SetSignatureSchemes(kSchemes, PR_ARRAY_SIZE(kSchemes)); + auto capture = + MakeTlsFilter<TlsExtensionCapture>(client_, ssl_signature_algorithms_xtn); + Connect(); + // We should only have the one signature algorithm advertised. + static const uint8_t kExpectedExt[] = {0, + 4, + ssl_sig_rsa_pss_rsae_sha256 >> 8, + ssl_sig_rsa_pss_rsae_sha256 & 0xff, + ssl_sig_rsa_pkcs1_sha256 >> 8, + ssl_sig_rsa_pkcs1_sha256 & 0xff}; + ASSERT_EQ(DataBuffer(kExpectedExt, sizeof(kExpectedExt)), + capture->extension()); +} + +TEST_P(TlsConnectTls13, Tls13RsaPkcs1IsAdvertisedServer) { + EnsureTlsSetup(); + static const SSLSignatureScheme kSchemes[] = {ssl_sig_rsa_pkcs1_sha256, + ssl_sig_rsa_pss_rsae_sha256}; + server_->SetSignatureSchemes(kSchemes, PR_ARRAY_SIZE(kSchemes)); + auto capture = MakeTlsFilter<TlsExtensionCapture>( + server_, ssl_signature_algorithms_xtn, true); + capture->SetHandshakeTypes({kTlsHandshakeCertificateRequest}); + capture->EnableDecryption(); + server_->RequestClientAuth(false); // So we get a CertificateRequest. + Connect(); + // We should only have the one signature algorithm advertised. + static const uint8_t kExpectedExt[] = {0, + 4, + ssl_sig_rsa_pss_rsae_sha256 >> 8, + ssl_sig_rsa_pss_rsae_sha256 & 0xff, + ssl_sig_rsa_pkcs1_sha256 >> 8, + ssl_sig_rsa_pkcs1_sha256 & 0xff}; + ASSERT_EQ(DataBuffer(kExpectedExt, sizeof(kExpectedExt)), + capture->extension()); +} + +// variant, version, certificate, auth type, signature scheme +typedef std::tuple<SSLProtocolVariant, uint16_t, std::string, SSLAuthType, + SSLSignatureScheme> + SignatureSchemeProfile; + +class TlsSignatureSchemeConfiguration + : public TlsConnectTestBase, + public ::testing::WithParamInterface<SignatureSchemeProfile> { + public: + TlsSignatureSchemeConfiguration() + : TlsConnectTestBase(std::get<0>(GetParam()), std::get<1>(GetParam())), + certificate_(std::get<2>(GetParam())), + auth_type_(std::get<3>(GetParam())), + signature_scheme_(std::get<4>(GetParam())) {} + + protected: + void TestSignatureSchemeConfig(std::shared_ptr<TlsAgent>& configPeer) { + EnsureTlsSetup(); + configPeer->SetSignatureSchemes(&signature_scheme_, 1); + Connect(); + CheckKeys(ssl_kea_ecdh, ssl_grp_ec_curve25519, auth_type_, + signature_scheme_); + } + + std::string certificate_; + SSLAuthType auth_type_; + SSLSignatureScheme signature_scheme_; +}; + +TEST_P(TlsSignatureSchemeConfiguration, SignatureSchemeConfigServer) { + Reset(certificate_); + TestSignatureSchemeConfig(server_); +} + +TEST_P(TlsSignatureSchemeConfiguration, SignatureSchemeConfigClient) { + Reset(certificate_); + auto capture = + MakeTlsFilter<TlsExtensionCapture>(client_, ssl_signature_algorithms_xtn); + TestSignatureSchemeConfig(client_); + + const DataBuffer& ext = capture->extension(); + ASSERT_EQ(2U + 2U, ext.len()); + uint32_t v = 0; + ASSERT_TRUE(ext.Read(0, 2, &v)); + EXPECT_EQ(2U, v); + ASSERT_TRUE(ext.Read(2, 2, &v)); + EXPECT_EQ(signature_scheme_, static_cast<SSLSignatureScheme>(v)); +} + +TEST_P(TlsSignatureSchemeConfiguration, SignatureSchemeConfigBoth) { + Reset(certificate_); + EnsureTlsSetup(); + client_->SetSignatureSchemes(&signature_scheme_, 1); + server_->SetSignatureSchemes(&signature_scheme_, 1); + Connect(); + CheckKeys(ssl_kea_ecdh, ssl_grp_ec_curve25519, auth_type_, signature_scheme_); +} + +class Tls12CertificateRequestReplacer : public TlsHandshakeFilter { + public: + Tls12CertificateRequestReplacer(const std::shared_ptr<TlsAgent>& a, + SSLSignatureScheme scheme) + : TlsHandshakeFilter(a, {kTlsHandshakeCertificateRequest}), + scheme_(scheme) {} + + virtual PacketFilter::Action FilterHandshake(const HandshakeHeader& header, + const DataBuffer& input, + DataBuffer* output) { + uint32_t offset = 0; + + if (header.handshake_type() != ssl_hs_certificate_request) { + return KEEP; + } + + *output = input; + + uint32_t types_len = 0; + if (!output->Read(offset, 1, &types_len)) { + ADD_FAILURE(); + return KEEP; + } + offset += 1 + types_len; + uint32_t scheme_len = 0; + if (!output->Read(offset, 2, &scheme_len)) { + ADD_FAILURE(); + return KEEP; + } + DataBuffer schemes; + schemes.Write(0, 2, 2); + schemes.Write(2, scheme_, 2); + output->Write(offset, 2, schemes.len()); + output->Splice(schemes, offset + 2, scheme_len); + + return CHANGE; + } + + private: + SSLSignatureScheme scheme_; +}; + +// +// Test how policy interacts with client auth connections +// + +// TLS/DTLS version algorithm policy +typedef std::tuple<SSLProtocolVariant, uint16_t, SECOidTag, PRUint32> + PolicySignatureSchemeProfile; + +// Only TLS 1.2 handles client auth schemes inside +// the certificate request packet, so our failure tests for +// those kinds of connections only occur here. +class TlsConnectAuthWithPolicyTls12 + : public TlsConnectTestBase, + public ::testing::WithParamInterface<PolicySignatureSchemeProfile> { + public: + TlsConnectAuthWithPolicyTls12() + : TlsConnectTestBase(std::get<0>(GetParam()), std::get<1>(GetParam())) { + alg_ = std::get<2>(GetParam()); + policy_ = std::get<3>(GetParam()); + // use the algorithm to select which single scheme to deploy + // We use these schemes to force servers sending schemes the client + // didn't advertise to make sure the client will still filter these + // by policy and detect that no valid schemes were presented, rather + // than sending an empty client auth message. + switch (alg_) { + case SEC_OID_SHA256: + case SEC_OID_PKCS1_RSA_PSS_SIGNATURE: + scheme_ = ssl_sig_rsa_pss_pss_sha256; + break; + case SEC_OID_PKCS1_RSA_ENCRYPTION: + scheme_ = ssl_sig_rsa_pkcs1_sha256; + break; + case SEC_OID_ANSIX962_EC_PUBLIC_KEY: + scheme_ = ssl_sig_ecdsa_secp256r1_sha256; + break; + default: + ADD_FAILURE() << "need to update algorithm table in " + "TlsConnectAuthWithPolicyTls12"; + scheme_ = ssl_sig_none; + break; + } + } + + protected: + SECOidTag alg_; + PRUint32 policy_; + SSLSignatureScheme scheme_; +}; + +// Only TLS 1.2 and greater looks at schemes extensions on client auth +class TlsConnectAuthWithPolicyTls12Plus + : public TlsConnectTestBase, + public ::testing::WithParamInterface<PolicySignatureSchemeProfile> { + public: + TlsConnectAuthWithPolicyTls12Plus() + : TlsConnectTestBase(std::get<0>(GetParam()), std::get<1>(GetParam())) { + alg_ = std::get<2>(GetParam()); + policy_ = std::get<3>(GetParam()); + } + + protected: + SECOidTag alg_; + PRUint32 policy_; +}; + +// make sure we can turn single algorithms off by policy an still connect +// this is basically testing that we are properly filtering our schemes +// by policy before communicating them to the server, and that the +// server is respecting our choices +TEST_P(TlsConnectAuthWithPolicyTls12Plus, PolicySuccessTest) { + // in TLS 1.3, RSA PKCS1 is restricted. If we are also + // restricting RSA PSS by policy, we can't use the default + // RSA certificate as the server cert, switch to ECDSA + if ((version_ >= SSL_LIBRARY_VERSION_TLS_1_3) && + (alg_ == SEC_OID_PKCS1_RSA_PSS_SIGNATURE)) { + Reset(TlsAgent::kServerEcdsa256); + } + client_->SetPolicy(alg_, 0, policy_); // Disable policy for client + client_->SetupClientAuth(); + server_->RequestClientAuth(false); + Connect(); +} + +// make sure we fail if the server ignores our policy preference and +// requests client auth with a scheme we don't support +TEST_P(TlsConnectAuthWithPolicyTls12, PolicyFailureTest) { + client_->SetPolicy(alg_, 0, policy_); + client_->SetupClientAuth(); + server_->RequestClientAuth(false); + MakeTlsFilter<Tls12CertificateRequestReplacer>(server_, scheme_); + ConnectExpectAlert(client_, kTlsAlertHandshakeFailure); + client_->CheckErrorCode(SSL_ERROR_UNSUPPORTED_SIGNATURE_ALGORITHM); + server_->CheckErrorCode(SSL_ERROR_HANDSHAKE_FAILURE_ALERT); +} + +INSTANTIATE_TEST_SUITE_P( + SignaturesWithPolicyFail, TlsConnectAuthWithPolicyTls12, + ::testing::Combine(TlsConnectTestBase::kTlsVariantsAll, + TlsConnectTestBase::kTlsV12, + ::testing::Values(SEC_OID_SHA256, + SEC_OID_PKCS1_RSA_PSS_SIGNATURE, + SEC_OID_PKCS1_RSA_ENCRYPTION, + SEC_OID_ANSIX962_EC_PUBLIC_KEY), + ::testing::Values(NSS_USE_ALG_IN_SSL_KX, + NSS_USE_ALG_IN_ANY_SIGNATURE))); + +INSTANTIATE_TEST_SUITE_P( + SignaturesWithPolicySuccess, TlsConnectAuthWithPolicyTls12Plus, + ::testing::Combine(TlsConnectTestBase::kTlsVariantsAll, + TlsConnectTestBase::kTlsV12Plus, + ::testing::Values(SEC_OID_SHA256, + SEC_OID_PKCS1_RSA_PSS_SIGNATURE, + SEC_OID_PKCS1_RSA_ENCRYPTION, + SEC_OID_ANSIX962_EC_PUBLIC_KEY), + ::testing::Values(NSS_USE_ALG_IN_SSL_KX, + NSS_USE_ALG_IN_ANY_SIGNATURE))); + +INSTANTIATE_TEST_SUITE_P( + SignatureSchemeRsa, TlsSignatureSchemeConfiguration, + ::testing::Combine( + TlsConnectTestBase::kTlsVariantsAll, TlsConnectTestBase::kTlsV12, + ::testing::Values(TlsAgent::kServerRsaSign), + ::testing::Values(ssl_auth_rsa_sign), + ::testing::Values(ssl_sig_rsa_pkcs1_sha256, ssl_sig_rsa_pkcs1_sha384, + ssl_sig_rsa_pkcs1_sha512, ssl_sig_rsa_pss_rsae_sha256, + ssl_sig_rsa_pss_rsae_sha384))); +// RSASSA-PKCS1-v1_5 is not allowed to be used in TLS 1.3 +INSTANTIATE_TEST_SUITE_P( + SignatureSchemeRsaTls13, TlsSignatureSchemeConfiguration, + ::testing::Combine(TlsConnectTestBase::kTlsVariantsAll, + TlsConnectTestBase::kTlsV13, + ::testing::Values(TlsAgent::kServerRsaSign), + ::testing::Values(ssl_auth_rsa_sign), + ::testing::Values(ssl_sig_rsa_pss_rsae_sha256, + ssl_sig_rsa_pss_rsae_sha384))); +// PSS with SHA-512 needs a bigger key to work. +INSTANTIATE_TEST_SUITE_P( + SignatureSchemeBigRsa, TlsSignatureSchemeConfiguration, + ::testing::Combine(TlsConnectTestBase::kTlsVariantsAll, + TlsConnectTestBase::kTlsV12Plus, + ::testing::Values(TlsAgent::kRsa2048), + ::testing::Values(ssl_auth_rsa_sign), + ::testing::Values(ssl_sig_rsa_pss_rsae_sha512))); +INSTANTIATE_TEST_SUITE_P( + SignatureSchemeRsaSha1, TlsSignatureSchemeConfiguration, + ::testing::Combine(TlsConnectTestBase::kTlsVariantsAll, + TlsConnectTestBase::kTlsV12, + ::testing::Values(TlsAgent::kServerRsa), + ::testing::Values(ssl_auth_rsa_sign), + ::testing::Values(ssl_sig_rsa_pkcs1_sha1))); +INSTANTIATE_TEST_SUITE_P( + SignatureSchemeEcdsaP256, TlsSignatureSchemeConfiguration, + ::testing::Combine(TlsConnectTestBase::kTlsVariantsAll, + TlsConnectTestBase::kTlsV12Plus, + ::testing::Values(TlsAgent::kServerEcdsa256), + ::testing::Values(ssl_auth_ecdsa), + ::testing::Values(ssl_sig_ecdsa_secp256r1_sha256))); +INSTANTIATE_TEST_SUITE_P( + SignatureSchemeEcdsaP384, TlsSignatureSchemeConfiguration, + ::testing::Combine(TlsConnectTestBase::kTlsVariantsAll, + TlsConnectTestBase::kTlsV12Plus, + ::testing::Values(TlsAgent::kServerEcdsa384), + ::testing::Values(ssl_auth_ecdsa), + ::testing::Values(ssl_sig_ecdsa_secp384r1_sha384))); +INSTANTIATE_TEST_SUITE_P( + SignatureSchemeEcdsaP521, TlsSignatureSchemeConfiguration, + ::testing::Combine(TlsConnectTestBase::kTlsVariantsAll, + TlsConnectTestBase::kTlsV12Plus, + ::testing::Values(TlsAgent::kServerEcdsa521), + ::testing::Values(ssl_auth_ecdsa), + ::testing::Values(ssl_sig_ecdsa_secp521r1_sha512))); +INSTANTIATE_TEST_SUITE_P( + SignatureSchemeEcdsaSha1, TlsSignatureSchemeConfiguration, + ::testing::Combine(TlsConnectTestBase::kTlsVariantsAll, + TlsConnectTestBase::kTlsV12, + ::testing::Values(TlsAgent::kServerEcdsa256, + TlsAgent::kServerEcdsa384), + ::testing::Values(ssl_auth_ecdsa), + ::testing::Values(ssl_sig_ecdsa_sha1))); +} // namespace nss_test |