| /* -*- 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 <functional> |
| #include <memory> |
| #include "secerr.h" |
| #include "ssl.h" |
| #include "sslerr.h" |
| #include "sslexp.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 "scoped_ptrs_ssl.h" |
| #include "tls_connect.h" |
| #include "tls_filter.h" |
| #include "tls_parser.h" |
| #include "tls_protect.h" |
| |
| namespace nss_test { |
| |
| class TlsServerKeyExchangeEcdhe { |
| public: |
| bool Parse(const DataBuffer& buffer) { |
| TlsParser parser(buffer); |
| |
| uint8_t curve_type; |
| if (!parser.Read(&curve_type)) { |
| return false; |
| } |
| |
| if (curve_type != 3) { // named_curve |
| return false; |
| } |
| |
| uint32_t named_curve; |
| if (!parser.Read(&named_curve, 2)) { |
| return false; |
| } |
| |
| return parser.ReadVariable(&public_key_, 1); |
| } |
| |
| DataBuffer public_key_; |
| }; |
| |
| TEST_P(TlsConnectGenericPre13, ConnectResumed) { |
| ConfigureSessionCache(RESUME_SESSIONID, RESUME_SESSIONID); |
| Connect(); |
| |
| Reset(); |
| ExpectResumption(RESUME_SESSIONID); |
| Connect(); |
| } |
| |
| TEST_P(TlsConnectGenericResumption, ConnectClientCacheDisabled) { |
| ConfigureSessionCache(RESUME_NONE, RESUME_SESSIONID); |
| Connect(); |
| SendReceive(); |
| |
| Reset(); |
| ExpectResumption(RESUME_NONE); |
| Connect(); |
| SendReceive(); |
| } |
| |
| TEST_P(TlsConnectGenericResumption, ConnectServerCacheDisabled) { |
| ConfigureSessionCache(RESUME_SESSIONID, RESUME_NONE); |
| Connect(); |
| SendReceive(); |
| |
| Reset(); |
| ExpectResumption(RESUME_NONE); |
| Connect(); |
| SendReceive(); |
| } |
| |
| TEST_P(TlsConnectGenericResumption, ConnectSessionCacheDisabled) { |
| ConfigureSessionCache(RESUME_NONE, RESUME_NONE); |
| Connect(); |
| SendReceive(); |
| |
| Reset(); |
| ExpectResumption(RESUME_NONE); |
| Connect(); |
| SendReceive(); |
| } |
| |
| TEST_P(TlsConnectGenericResumption, ConnectResumeSupportBoth) { |
| // This prefers tickets. |
| ConfigureSessionCache(RESUME_BOTH, RESUME_BOTH); |
| Connect(); |
| SendReceive(); |
| |
| Reset(); |
| ConfigureSessionCache(RESUME_BOTH, RESUME_BOTH); |
| ExpectResumption(RESUME_TICKET); |
| Connect(); |
| SendReceive(); |
| } |
| |
| TEST_P(TlsConnectGenericResumption, ConnectResumeClientTicketServerBoth) { |
| // This causes no resumption because the client needs the |
| // session cache to resume even with tickets. |
| ConfigureSessionCache(RESUME_TICKET, RESUME_BOTH); |
| Connect(); |
| SendReceive(); |
| |
| Reset(); |
| ConfigureSessionCache(RESUME_TICKET, RESUME_BOTH); |
| ExpectResumption(RESUME_NONE); |
| Connect(); |
| SendReceive(); |
| } |
| |
| TEST_P(TlsConnectGenericResumption, ConnectResumeClientBothTicketServerTicket) { |
| // This causes a ticket resumption. |
| ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET); |
| Connect(); |
| SendReceive(); |
| |
| Reset(); |
| ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET); |
| ExpectResumption(RESUME_TICKET); |
| Connect(); |
| SendReceive(); |
| } |
| |
| TEST_P(TlsConnectGenericResumption, ConnectResumeClientServerTicketOnly) { |
| // This causes no resumption because the client needs the |
| // session cache to resume even with tickets. |
| ConfigureSessionCache(RESUME_TICKET, RESUME_TICKET); |
| Connect(); |
| SendReceive(); |
| |
| Reset(); |
| ConfigureSessionCache(RESUME_TICKET, RESUME_TICKET); |
| ExpectResumption(RESUME_NONE); |
| Connect(); |
| SendReceive(); |
| } |
| |
| TEST_P(TlsConnectGenericResumption, ConnectResumeClientBothServerNone) { |
| ConfigureSessionCache(RESUME_BOTH, RESUME_NONE); |
| Connect(); |
| SendReceive(); |
| |
| Reset(); |
| ConfigureSessionCache(RESUME_BOTH, RESUME_NONE); |
| ExpectResumption(RESUME_NONE); |
| Connect(); |
| SendReceive(); |
| } |
| |
| TEST_P(TlsConnectGenericResumption, ConnectResumeClientNoneServerBoth) { |
| ConfigureSessionCache(RESUME_NONE, RESUME_BOTH); |
| Connect(); |
| SendReceive(); |
| |
| Reset(); |
| ConfigureSessionCache(RESUME_NONE, RESUME_BOTH); |
| ExpectResumption(RESUME_NONE); |
| Connect(); |
| SendReceive(); |
| } |
| |
| TEST_P(TlsConnectGenericPre13, ResumeWithHigherVersionTls13) { |
| uint16_t lower_version = version_; |
| ConfigureSessionCache(RESUME_BOTH, RESUME_BOTH); |
| Connect(); |
| SendReceive(); |
| CheckKeys(); |
| |
| Reset(); |
| ConfigureSessionCache(RESUME_BOTH, RESUME_BOTH); |
| EnsureTlsSetup(); |
| auto psk_ext = std::make_shared<TlsExtensionCapture>( |
| client_, ssl_tls13_pre_shared_key_xtn); |
| auto ticket_ext = |
| std::make_shared<TlsExtensionCapture>(client_, ssl_session_ticket_xtn); |
| client_->SetFilter(std::make_shared<ChainedPacketFilter>( |
| ChainedPacketFilterInit({psk_ext, ticket_ext}))); |
| SetExpectedVersion(SSL_LIBRARY_VERSION_TLS_1_3); |
| client_->SetVersionRange(lower_version, SSL_LIBRARY_VERSION_TLS_1_3); |
| server_->SetVersionRange(lower_version, SSL_LIBRARY_VERSION_TLS_1_3); |
| ExpectResumption(RESUME_NONE); |
| Connect(); |
| |
| // The client shouldn't have sent a PSK, though it will send a ticket. |
| EXPECT_FALSE(psk_ext->captured()); |
| EXPECT_TRUE(ticket_ext->captured()); |
| } |
| |
| class CaptureSessionId : public TlsHandshakeFilter { |
| public: |
| CaptureSessionId(const std::shared_ptr<TlsAgent>& a) |
| : TlsHandshakeFilter( |
| a, {kTlsHandshakeClientHello, kTlsHandshakeServerHello}), |
| sid_() {} |
| |
| const DataBuffer& sid() const { return sid_; } |
| |
| protected: |
| PacketFilter::Action FilterHandshake(const HandshakeHeader& header, |
| const DataBuffer& input, |
| DataBuffer* output) override { |
| // The session_id is in the same place in both Hello messages: |
| size_t offset = 2 + 32; // Version(2) + Random(32) |
| uint32_t len = 0; |
| EXPECT_TRUE(input.Read(offset, 1, &len)); |
| offset++; |
| if (input.len() < offset + len) { |
| ADD_FAILURE() << "session_id overflows the Hello message"; |
| return KEEP; |
| } |
| sid_.Assign(input.data() + offset, len); |
| return KEEP; |
| } |
| |
| private: |
| DataBuffer sid_; |
| }; |
| |
| // Attempting to resume from TLS 1.2 when 1.3 is possible should not result in |
| // resumption, though it will appear to be TLS 1.3 compatibility mode if the |
| // server uses a session ID. |
| TEST_P(TlsConnectGenericPre13, ResumeWithHigherVersionTls13SessionId) { |
| uint16_t lower_version = version_; |
| ConfigureSessionCache(RESUME_SESSIONID, RESUME_SESSIONID); |
| auto original_sid = MakeTlsFilter<CaptureSessionId>(server_); |
| Connect(); |
| CheckKeys(); |
| EXPECT_EQ(32U, original_sid->sid().len()); |
| |
| // The client should now attempt to resume with the session ID from the last |
| // connection. This looks like compatibility mode, we just want to ensure |
| // that we get TLS 1.3 rather than 1.2 (and no resumption). |
| Reset(); |
| auto client_sid = MakeTlsFilter<CaptureSessionId>(client_); |
| auto server_sid = MakeTlsFilter<CaptureSessionId>(server_); |
| ConfigureSessionCache(RESUME_SESSIONID, RESUME_SESSIONID); |
| SetExpectedVersion(SSL_LIBRARY_VERSION_TLS_1_3); |
| client_->SetVersionRange(lower_version, SSL_LIBRARY_VERSION_TLS_1_3); |
| server_->SetVersionRange(lower_version, SSL_LIBRARY_VERSION_TLS_1_3); |
| ExpectResumption(RESUME_NONE); |
| |
| Connect(); |
| SendReceive(); |
| |
| EXPECT_EQ(client_sid->sid(), original_sid->sid()); |
| if (variant_ == ssl_variant_stream) { |
| EXPECT_EQ(client_sid->sid(), server_sid->sid()); |
| } else { |
| // DTLS servers don't echo the session ID. |
| EXPECT_EQ(0U, server_sid->sid().len()); |
| } |
| } |
| |
| TEST_P(TlsConnectPre12, ResumeWithHigherVersionTls12) { |
| uint16_t lower_version = version_; |
| ConfigureSessionCache(RESUME_BOTH, RESUME_BOTH); |
| Connect(); |
| |
| Reset(); |
| ConfigureSessionCache(RESUME_BOTH, RESUME_BOTH); |
| EnsureTlsSetup(); |
| SetExpectedVersion(SSL_LIBRARY_VERSION_TLS_1_3); |
| client_->SetVersionRange(lower_version, SSL_LIBRARY_VERSION_TLS_1_3); |
| server_->SetVersionRange(lower_version, SSL_LIBRARY_VERSION_TLS_1_3); |
| ExpectResumption(RESUME_NONE); |
| Connect(); |
| } |
| |
| TEST_P(TlsConnectGenericPre13, ResumeWithLowerVersionFromTls13) { |
| uint16_t original_version = version_; |
| ConfigureSessionCache(RESUME_BOTH, RESUME_BOTH); |
| ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3); |
| Connect(); |
| SendReceive(); |
| CheckKeys(); |
| |
| Reset(); |
| ConfigureSessionCache(RESUME_BOTH, RESUME_BOTH); |
| ConfigureVersion(original_version); |
| ExpectResumption(RESUME_NONE); |
| Connect(); |
| SendReceive(); |
| } |
| |
| TEST_P(TlsConnectPre12, ResumeWithLowerVersionFromTls12) { |
| uint16_t original_version = version_; |
| ConfigureSessionCache(RESUME_BOTH, RESUME_BOTH); |
| ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_2); |
| Connect(); |
| SendReceive(); |
| CheckKeys(); |
| |
| Reset(); |
| ConfigureSessionCache(RESUME_BOTH, RESUME_BOTH); |
| ConfigureVersion(original_version); |
| ExpectResumption(RESUME_NONE); |
| Connect(); |
| SendReceive(); |
| } |
| |
| TEST_P(TlsConnectGeneric, ConnectResumeClientBothTicketServerTicketForget) { |
| // This causes a ticket resumption. |
| ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET); |
| Connect(); |
| SendReceive(); |
| |
| Reset(); |
| ClearServerCache(); |
| ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET); |
| ExpectResumption(RESUME_NONE); |
| Connect(); |
| SendReceive(); |
| } |
| |
| TEST_P(TlsConnectGenericResumption, ConnectWithExpiredTicketAtClient) { |
| SSLInt_SetTicketLifetime(1); // one second |
| // This causes a ticket resumption. |
| ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET); |
| Connect(); |
| SendReceive(); |
| |
| WAIT_(false, 1000); |
| |
| Reset(); |
| ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET); |
| ExpectResumption(RESUME_NONE); |
| |
| // TLS 1.3 uses the pre-shared key extension instead. |
| SSLExtensionType xtn = (version_ >= SSL_LIBRARY_VERSION_TLS_1_3) |
| ? ssl_tls13_pre_shared_key_xtn |
| : ssl_session_ticket_xtn; |
| auto capture = MakeTlsFilter<TlsExtensionCapture>(client_, xtn); |
| Connect(); |
| |
| if (version_ >= SSL_LIBRARY_VERSION_TLS_1_3) { |
| EXPECT_FALSE(capture->captured()); |
| } else { |
| EXPECT_TRUE(capture->captured()); |
| EXPECT_EQ(0U, capture->extension().len()); |
| } |
| } |
| |
| TEST_P(TlsConnectGeneric, ConnectWithExpiredTicketAtServer) { |
| SSLInt_SetTicketLifetime(1); // one second |
| // This causes a ticket resumption. |
| ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET); |
| Connect(); |
| SendReceive(); |
| |
| Reset(); |
| ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET); |
| ExpectResumption(RESUME_NONE); |
| |
| SSLExtensionType xtn = (version_ >= SSL_LIBRARY_VERSION_TLS_1_3) |
| ? ssl_tls13_pre_shared_key_xtn |
| : ssl_session_ticket_xtn; |
| auto capture = MakeTlsFilter<TlsExtensionCapture>(client_, xtn); |
| StartConnect(); |
| client_->Handshake(); |
| EXPECT_TRUE(capture->captured()); |
| EXPECT_LT(0U, capture->extension().len()); |
| |
| WAIT_(false, 1000); // Let the ticket expire on the server. |
| |
| Handshake(); |
| CheckConnected(); |
| } |
| |
| TEST_P(TlsConnectGeneric, ConnectResumeCorruptTicket) { |
| // This causes a ticket resumption. |
| ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET); |
| Connect(); |
| SendReceive(); |
| |
| Reset(); |
| static const uint8_t kHmacKey1Buf[32] = {0}; |
| static const DataBuffer kHmacKey1(kHmacKey1Buf, sizeof(kHmacKey1Buf)); |
| |
| SECItem key_item = {siBuffer, const_cast<uint8_t*>(kHmacKey1Buf), |
| sizeof(kHmacKey1Buf)}; |
| |
| ScopedPK11SlotInfo slot(PK11_GetInternalSlot()); |
| PK11SymKey* hmac_key = |
| PK11_ImportSymKey(slot.get(), CKM_SHA256_HMAC, PK11_OriginUnwrap, |
| CKA_SIGN, &key_item, nullptr); |
| ASSERT_NE(nullptr, hmac_key); |
| SSLInt_SetSelfEncryptMacKey(hmac_key); |
| ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET); |
| if (version_ >= SSL_LIBRARY_VERSION_TLS_1_3) { |
| ExpectResumption(RESUME_NONE); |
| Connect(); |
| } else { |
| ConnectExpectAlert(server_, illegal_parameter); |
| server_->CheckErrorCode(SSL_ERROR_RX_MALFORMED_CLIENT_HELLO); |
| } |
| } |
| |
| // This callback switches out the "server" cert used on the server with |
| // the "client" certificate, which should be the same type. |
| static int32_t SwitchCertificates(TlsAgent* agent, const SECItem* srvNameArr, |
| uint32_t srvNameArrSize) { |
| bool ok = agent->ConfigServerCert("client"); |
| if (!ok) return SSL_SNI_SEND_ALERT; |
| |
| return 0; // first config |
| }; |
| |
| TEST_P(TlsConnectGeneric, ServerSNICertSwitch) { |
| Connect(); |
| ScopedCERTCertificate cert1(SSL_PeerCertificate(client_->ssl_fd())); |
| |
| Reset(); |
| ConfigureSessionCache(RESUME_NONE, RESUME_NONE); |
| |
| server_->SetSniCallback(SwitchCertificates); |
| |
| Connect(); |
| ScopedCERTCertificate cert2(SSL_PeerCertificate(client_->ssl_fd())); |
| CheckKeys(); |
| EXPECT_FALSE(SECITEM_ItemsAreEqual(&cert1->derCert, &cert2->derCert)); |
| } |
| |
| TEST_P(TlsConnectGeneric, ServerSNICertTypeSwitch) { |
| Reset(TlsAgent::kServerEcdsa256); |
| Connect(); |
| ScopedCERTCertificate cert1(SSL_PeerCertificate(client_->ssl_fd())); |
| |
| Reset(); |
| ConfigureSessionCache(RESUME_NONE, RESUME_NONE); |
| |
| // Because we configure an RSA certificate here, it only adds a second, unused |
| // certificate, which has no effect on what the server uses. |
| server_->SetSniCallback(SwitchCertificates); |
| |
| Connect(); |
| ScopedCERTCertificate cert2(SSL_PeerCertificate(client_->ssl_fd())); |
| CheckKeys(ssl_kea_ecdh, ssl_auth_ecdsa); |
| EXPECT_TRUE(SECITEM_ItemsAreEqual(&cert1->derCert, &cert2->derCert)); |
| } |
| |
| // Prior to TLS 1.3, we were not fully ephemeral; though 1.3 fixes that |
| TEST_P(TlsConnectGenericPre13, ConnectEcdheTwiceReuseKey) { |
| auto filter = MakeTlsFilter<TlsHandshakeRecorder>( |
| server_, kTlsHandshakeServerKeyExchange); |
| Connect(); |
| CheckKeys(); |
| TlsServerKeyExchangeEcdhe dhe1; |
| EXPECT_TRUE(dhe1.Parse(filter->buffer())); |
| |
| // Restart |
| Reset(); |
| auto filter2 = MakeTlsFilter<TlsHandshakeRecorder>( |
| server_, kTlsHandshakeServerKeyExchange); |
| ConfigureSessionCache(RESUME_NONE, RESUME_NONE); |
| Connect(); |
| CheckKeys(); |
| |
| TlsServerKeyExchangeEcdhe dhe2; |
| EXPECT_TRUE(dhe2.Parse(filter2->buffer())); |
| |
| // Make sure they are the same. |
| EXPECT_EQ(dhe1.public_key_.len(), dhe2.public_key_.len()); |
| EXPECT_TRUE(!memcmp(dhe1.public_key_.data(), dhe2.public_key_.data(), |
| dhe1.public_key_.len())); |
| } |
| |
| // This test parses the ServerKeyExchange, which isn't in 1.3 |
| TEST_P(TlsConnectGenericPre13, ConnectEcdheTwiceNewKey) { |
| server_->SetOption(SSL_REUSE_SERVER_ECDHE_KEY, PR_FALSE); |
| auto filter = MakeTlsFilter<TlsHandshakeRecorder>( |
| server_, kTlsHandshakeServerKeyExchange); |
| Connect(); |
| CheckKeys(); |
| TlsServerKeyExchangeEcdhe dhe1; |
| EXPECT_TRUE(dhe1.Parse(filter->buffer())); |
| |
| // Restart |
| Reset(); |
| server_->SetOption(SSL_REUSE_SERVER_ECDHE_KEY, PR_FALSE); |
| auto filter2 = MakeTlsFilter<TlsHandshakeRecorder>( |
| server_, kTlsHandshakeServerKeyExchange); |
| ConfigureSessionCache(RESUME_NONE, RESUME_NONE); |
| Connect(); |
| CheckKeys(); |
| |
| TlsServerKeyExchangeEcdhe dhe2; |
| EXPECT_TRUE(dhe2.Parse(filter2->buffer())); |
| |
| // Make sure they are different. |
| EXPECT_FALSE((dhe1.public_key_.len() == dhe2.public_key_.len()) && |
| (!memcmp(dhe1.public_key_.data(), dhe2.public_key_.data(), |
| dhe1.public_key_.len()))); |
| } |
| |
| // Verify that TLS 1.3 reports an accurate group on resumption. |
| TEST_P(TlsConnectTls13, TestTls13ResumeDifferentGroup) { |
| ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET); |
| Connect(); |
| SendReceive(); // Need to read so that we absorb the session ticket. |
| CheckKeys(); |
| |
| Reset(); |
| ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET); |
| ExpectResumption(RESUME_TICKET); |
| client_->ConfigNamedGroups(kFFDHEGroups); |
| server_->ConfigNamedGroups(kFFDHEGroups); |
| Connect(); |
| CheckKeys(ssl_kea_dh, ssl_grp_ffdhe_2048, ssl_auth_rsa_sign, |
| ssl_sig_rsa_pss_rsae_sha256); |
| } |
| |
| // Verify that TLS 1.3 server doesn't request certificate in the main |
| // handshake, after resumption. |
| TEST_P(TlsConnectTls13, TestTls13ResumeNoCertificateRequest) { |
| ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET); |
| client_->SetupClientAuth(); |
| server_->RequestClientAuth(true); |
| Connect(); |
| SendReceive(); // Need to read so that we absorb the session ticket. |
| ScopedCERTCertificate cert1(SSL_LocalCertificate(client_->ssl_fd())); |
| |
| Reset(); |
| ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET); |
| ExpectResumption(RESUME_TICKET); |
| server_->RequestClientAuth(false); |
| auto cr_capture = |
| MakeTlsFilter<TlsHandshakeRecorder>(server_, ssl_hs_certificate_request); |
| cr_capture->EnableDecryption(); |
| Connect(); |
| SendReceive(); |
| EXPECT_EQ(0U, cr_capture->buffer().len()) << "expect nothing captured yet"; |
| |
| // Sanity check whether the client certificate matches the one |
| // decrypted from ticket. |
| ScopedCERTCertificate cert2(SSL_PeerCertificate(server_->ssl_fd())); |
| EXPECT_TRUE(SECITEM_ItemsAreEqual(&cert1->derCert, &cert2->derCert)); |
| } |
| |
| // Here we test that 0.5 RTT is available at the server when resuming, even if |
| // configured to request a client certificate. The resumed handshake relies on |
| // the authentication from the original handshake, so no certificate is |
| // requested this time around. The server can write before the handshake |
| // completes because the PSK binder is sufficient authentication for the client. |
| TEST_P(TlsConnectTls13, WriteBeforeHandshakeCompleteOnResumption) { |
| ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET); |
| client_->SetupClientAuth(); |
| server_->RequestClientAuth(true); |
| Connect(); |
| SendReceive(); // Absorb the session ticket. |
| ScopedCERTCertificate cert1(SSL_LocalCertificate(client_->ssl_fd())); |
| |
| Reset(); |
| ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET); |
| ExpectResumption(RESUME_TICKET); |
| server_->RequestClientAuth(false); |
| StartConnect(); |
| client_->Handshake(); // ClientHello |
| server_->Handshake(); // ServerHello |
| |
| server_->SendData(10); |
| client_->ReadBytes(10); // Client should emit the Finished as a side-effect. |
| server_->Handshake(); // Server consumes the Finished. |
| CheckConnected(); |
| |
| // Check whether the client certificate matches the one from the ticket. |
| ScopedCERTCertificate cert2(SSL_PeerCertificate(server_->ssl_fd())); |
| EXPECT_TRUE(SECITEM_ItemsAreEqual(&cert1->derCert, &cert2->derCert)); |
| } |
| |
| // We need to enable different cipher suites at different times in the following |
| // tests. Those cipher suites need to be suited to the version. |
| static uint16_t ChooseOneCipher(uint16_t version) { |
| if (version >= SSL_LIBRARY_VERSION_TLS_1_3) { |
| return TLS_AES_128_GCM_SHA256; |
| } |
| return TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA; |
| } |
| |
| static uint16_t ChooseAnotherCipher(uint16_t version) { |
| if (version >= SSL_LIBRARY_VERSION_TLS_1_3) { |
| return TLS_AES_256_GCM_SHA384; |
| } |
| return TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA; |
| } |
| |
| // Test that we don't resume when we can't negotiate the same cipher. |
| TEST_P(TlsConnectGenericResumption, TestResumeClientDifferentCipher) { |
| ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET); |
| client_->EnableSingleCipher(ChooseOneCipher(version_)); |
| Connect(); |
| SendReceive(); |
| CheckKeys(ssl_kea_ecdh, ssl_auth_rsa_sign); |
| |
| Reset(); |
| ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET); |
| ExpectResumption(RESUME_NONE); |
| client_->EnableSingleCipher(ChooseAnotherCipher(version_)); |
| uint16_t ticket_extension; |
| if (version_ >= SSL_LIBRARY_VERSION_TLS_1_3) { |
| ticket_extension = ssl_tls13_pre_shared_key_xtn; |
| } else { |
| ticket_extension = ssl_session_ticket_xtn; |
| } |
| auto ticket_capture = |
| MakeTlsFilter<TlsExtensionCapture>(client_, ticket_extension); |
| Connect(); |
| CheckKeys(ssl_kea_ecdh, ssl_auth_rsa_sign); |
| EXPECT_EQ(0U, ticket_capture->extension().len()); |
| } |
| |
| // Test that we don't resume when we can't negotiate the same cipher. |
| TEST_P(TlsConnectGenericResumption, TestResumeServerDifferentCipher) { |
| ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET); |
| server_->EnableSingleCipher(ChooseOneCipher(version_)); |
| Connect(); |
| SendReceive(); // Need to read so that we absorb the session ticket. |
| CheckKeys(); |
| |
| Reset(); |
| ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET); |
| ExpectResumption(RESUME_NONE); |
| server_->EnableSingleCipher(ChooseAnotherCipher(version_)); |
| Connect(); |
| CheckKeys(); |
| } |
| |
| // Test that the client doesn't tolerate the server picking a different cipher |
| // suite for resumption. |
| TEST_P(TlsConnectStream, TestResumptionOverrideCipher) { |
| ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET); |
| server_->EnableSingleCipher(ChooseOneCipher(version_)); |
| Connect(); |
| SendReceive(); |
| CheckKeys(ssl_kea_ecdh, ssl_auth_rsa_sign); |
| |
| Reset(); |
| ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET); |
| MakeTlsFilter<SelectedCipherSuiteReplacer>(server_, |
| ChooseAnotherCipher(version_)); |
| |
| if (version_ >= SSL_LIBRARY_VERSION_TLS_1_3) { |
| client_->ExpectSendAlert(kTlsAlertIllegalParameter); |
| server_->ExpectSendAlert(kTlsAlertUnexpectedMessage); |
| } else { |
| ExpectAlert(client_, kTlsAlertHandshakeFailure); |
| } |
| ConnectExpectFail(); |
| client_->CheckErrorCode(SSL_ERROR_RX_MALFORMED_SERVER_HELLO); |
| if (version_ >= SSL_LIBRARY_VERSION_TLS_1_3) { |
| // The reason this test is stream only: the server is unable to decrypt |
| // the alert that the client sends, see bug 1304603. |
| server_->CheckErrorCode(SSL_ERROR_RX_UNEXPECTED_RECORD_TYPE); |
| } else { |
| server_->CheckErrorCode(SSL_ERROR_HANDSHAKE_FAILURE_ALERT); |
| } |
| } |
| |
| class SelectedVersionReplacer : public TlsHandshakeFilter { |
| public: |
| SelectedVersionReplacer(const std::shared_ptr<TlsAgent>& a, uint16_t version) |
| : TlsHandshakeFilter(a, {kTlsHandshakeServerHello}), version_(version) {} |
| |
| protected: |
| PacketFilter::Action FilterHandshake(const HandshakeHeader& header, |
| const DataBuffer& input, |
| DataBuffer* output) override { |
| *output = input; |
| output->Write(0, static_cast<uint32_t>(version_), 2); |
| return CHANGE; |
| } |
| |
| private: |
| uint16_t version_; |
| }; |
| |
| // Test how the client handles the case where the server picks a |
| // lower version number on resumption. |
| TEST_P(TlsConnectGenericPre13, TestResumptionOverrideVersion) { |
| uint16_t override_version = 0; |
| if (variant_ == ssl_variant_stream) { |
| switch (version_) { |
| case SSL_LIBRARY_VERSION_TLS_1_0: |
| return; // Skip the test. |
| case SSL_LIBRARY_VERSION_TLS_1_1: |
| override_version = SSL_LIBRARY_VERSION_TLS_1_0; |
| break; |
| case SSL_LIBRARY_VERSION_TLS_1_2: |
| override_version = SSL_LIBRARY_VERSION_TLS_1_1; |
| break; |
| default: |
| ASSERT_TRUE(false) << "unknown version"; |
| } |
| } else { |
| if (version_ == SSL_LIBRARY_VERSION_TLS_1_2) { |
| override_version = SSL_LIBRARY_VERSION_DTLS_1_0_WIRE; |
| } else { |
| ASSERT_EQ(SSL_LIBRARY_VERSION_TLS_1_1, version_); |
| return; // Skip the test. |
| } |
| } |
| |
| ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET); |
| // Need to use a cipher that is plausible for the lower version. |
| server_->EnableSingleCipher(TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA); |
| Connect(); |
| CheckKeys(ssl_kea_ecdh, ssl_auth_rsa_sign); |
| |
| Reset(); |
| ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET); |
| // Enable the lower version on the client. |
| client_->SetVersionRange(version_ - 1, version_); |
| server_->EnableSingleCipher(TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA); |
| MakeTlsFilter<SelectedVersionReplacer>(server_, override_version); |
| |
| ConnectExpectAlert(client_, kTlsAlertHandshakeFailure); |
| client_->CheckErrorCode(SSL_ERROR_RX_MALFORMED_SERVER_HELLO); |
| server_->CheckErrorCode(SSL_ERROR_HANDSHAKE_FAILURE_ALERT); |
| } |
| |
| // Test that two TLS resumptions work and produce the same ticket. |
| // This will change after bug 1257047 is fixed. |
| TEST_F(TlsConnectTest, TestTls13ResumptionTwice) { |
| ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET); |
| ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3); |
| |
| Connect(); |
| SendReceive(); // Need to read so that we absorb the session ticket. |
| CheckKeys(); |
| uint16_t original_suite; |
| EXPECT_TRUE(client_->cipher_suite(&original_suite)); |
| |
| Reset(); |
| ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET); |
| ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3); |
| ExpectResumption(RESUME_TICKET); |
| auto c1 = |
| MakeTlsFilter<TlsExtensionCapture>(client_, ssl_tls13_pre_shared_key_xtn); |
| Connect(); |
| SendReceive(); |
| CheckKeys(ssl_kea_ecdh, ssl_grp_ec_curve25519, ssl_auth_rsa_sign, |
| ssl_sig_rsa_pss_rsae_sha256); |
| // The filter will go away when we reset, so save the captured extension. |
| DataBuffer initialTicket(c1->extension()); |
| ASSERT_LT(0U, initialTicket.len()); |
| |
| ScopedCERTCertificate cert1(SSL_PeerCertificate(client_->ssl_fd())); |
| ASSERT_TRUE(!!cert1.get()); |
| |
| Reset(); |
| ClearStats(); |
| ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET); |
| ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3); |
| auto c2 = |
| MakeTlsFilter<TlsExtensionCapture>(client_, ssl_tls13_pre_shared_key_xtn); |
| ExpectResumption(RESUME_TICKET); |
| Connect(); |
| SendReceive(); |
| CheckKeys(ssl_kea_ecdh, ssl_grp_ec_curve25519, ssl_auth_rsa_sign, |
| ssl_sig_rsa_pss_rsae_sha256); |
| ASSERT_LT(0U, c2->extension().len()); |
| |
| ScopedCERTCertificate cert2(SSL_PeerCertificate(client_->ssl_fd())); |
| ASSERT_TRUE(!!cert2.get()); |
| |
| // Check that the cipher suite is reported the same on both sides, though in |
| // TLS 1.3 resumption actually negotiates a different cipher suite. |
| uint16_t resumed_suite; |
| EXPECT_TRUE(server_->cipher_suite(&resumed_suite)); |
| EXPECT_EQ(original_suite, resumed_suite); |
| EXPECT_TRUE(client_->cipher_suite(&resumed_suite)); |
| EXPECT_EQ(original_suite, resumed_suite); |
| |
| ASSERT_NE(initialTicket, c2->extension()); |
| } |
| |
| // Check that resumption works after receiving two NST messages. |
| TEST_F(TlsConnectTest, TestTls13ResumptionDuplicateNST) { |
| ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET); |
| ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3); |
| Connect(); |
| |
| // Clear the session ticket keys to invalidate the old ticket. |
| SSLInt_ClearSelfEncryptKey(); |
| SSL_SendSessionTicket(server_->ssl_fd(), NULL, 0); |
| |
| 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); |
| Connect(); |
| SendReceive(); |
| } |
| |
| // Check that the value captured in a NewSessionTicket message matches the value |
| // captured from a pre_shared_key extension. |
| void NstTicketMatchesPskIdentity(const DataBuffer& nst, const DataBuffer& psk) { |
| uint32_t len; |
| |
| size_t offset = 4 + 4; // Skip ticket_lifetime and ticket_age_add. |
| ASSERT_TRUE(nst.Read(offset, 1, &len)); |
| offset += 1 + len; // Skip ticket_nonce. |
| |
| ASSERT_TRUE(nst.Read(offset, 2, &len)); |
| offset += 2; // Skip the ticket length. |
| ASSERT_LE(offset + len, nst.len()); |
| DataBuffer nst_ticket(nst.data() + offset, static_cast<size_t>(len)); |
| |
| offset = 2; // Skip the identities length. |
| ASSERT_TRUE(psk.Read(offset, 2, &len)); |
| offset += 2; // Skip the identity length. |
| ASSERT_LE(offset + len, psk.len()); |
| DataBuffer psk_ticket(psk.data() + offset, static_cast<size_t>(len)); |
| |
| EXPECT_EQ(nst_ticket, psk_ticket); |
| } |
| |
| TEST_F(TlsConnectTest, TestTls13ResumptionDuplicateNSTWithToken) { |
| ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET); |
| ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3); |
| |
| auto nst_capture = |
| MakeTlsFilter<TlsHandshakeRecorder>(server_, ssl_hs_new_session_ticket); |
| nst_capture->EnableDecryption(); |
| Connect(); |
| |
| // Clear the session ticket keys to invalidate the old ticket. |
| SSLInt_ClearSelfEncryptKey(); |
| nst_capture->Reset(); |
| uint8_t token[] = {0x20, 0x20, 0xff, 0x00}; |
| EXPECT_EQ(SECSuccess, |
| SSL_SendSessionTicket(server_->ssl_fd(), token, sizeof(token))); |
| |
| SendReceive(); // Need to read so that we absorb the session tickets. |
| CheckKeys(); |
| EXPECT_LT(0U, nst_capture->buffer().len()); |
| |
| // Resume the connection. |
| Reset(); |
| ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET); |
| ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3); |
| ExpectResumption(RESUME_TICKET); |
| |
| auto psk_capture = |
| MakeTlsFilter<TlsExtensionCapture>(client_, ssl_tls13_pre_shared_key_xtn); |
| Connect(); |
| SendReceive(); |
| |
| NstTicketMatchesPskIdentity(nst_capture->buffer(), psk_capture->extension()); |
| } |
| |
| // Disable SSL_ENABLE_SESSION_TICKETS but ensure that tickets can still be sent |
| // by invoking SSL_SendSessionTicket directly (and that the ticket is usable). |
| TEST_F(TlsConnectTest, SendSessionTicketWithTicketsDisabled) { |
| ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET); |
| ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3); |
| |
| EXPECT_EQ(SECSuccess, SSL_OptionSet(server_->ssl_fd(), |
| SSL_ENABLE_SESSION_TICKETS, PR_FALSE)); |
| |
| auto nst_capture = |
| MakeTlsFilter<TlsHandshakeRecorder>(server_, ssl_hs_new_session_ticket); |
| nst_capture->EnableDecryption(); |
| Connect(); |
| |
| EXPECT_EQ(0U, nst_capture->buffer().len()) << "expect nothing captured yet"; |
| |
| EXPECT_EQ(SECSuccess, SSL_SendSessionTicket(server_->ssl_fd(), NULL, 0)); |
| EXPECT_LT(0U, nst_capture->buffer().len()) << "should capture now"; |
| |
| SendReceive(); // Ensure that the client reads the ticket. |
| |
| // Resume the connection. |
| Reset(); |
| ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET); |
| ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3); |
| ExpectResumption(RESUME_TICKET); |
| |
| auto psk_capture = |
| MakeTlsFilter<TlsExtensionCapture>(client_, ssl_tls13_pre_shared_key_xtn); |
| Connect(); |
| SendReceive(); |
| |
| NstTicketMatchesPskIdentity(nst_capture->buffer(), psk_capture->extension()); |
| } |
| |
| // Test calling SSL_SendSessionTicket in inappropriate conditions. |
| TEST_F(TlsConnectTest, SendSessionTicketInappropriate) { |
| ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET); |
| ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_2); |
| |
| EXPECT_EQ(SECFailure, SSL_SendSessionTicket(client_->ssl_fd(), NULL, 0)) |
| << "clients can't send tickets"; |
| EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError()); |
| |
| StartConnect(); |
| |
| EXPECT_EQ(SECFailure, SSL_SendSessionTicket(server_->ssl_fd(), NULL, 0)) |
| << "no ticket before the handshake has started"; |
| EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError()); |
| Handshake(); |
| EXPECT_EQ(SECFailure, SSL_SendSessionTicket(server_->ssl_fd(), NULL, 0)) |
| << "no special tickets in TLS 1.2"; |
| EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError()); |
| } |
| |
| TEST_F(TlsConnectTest, SendSessionTicketMassiveToken) { |
| ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET); |
| ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3); |
| Connect(); |
| // It should be safe to set length with a NULL token because the length should |
| // be checked before reading token. |
| EXPECT_EQ(SECFailure, SSL_SendSessionTicket(server_->ssl_fd(), NULL, 0x1ffff)) |
| << "this is clearly too big"; |
| EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError()); |
| |
| static const uint8_t big_token[0xffff] = {1}; |
| EXPECT_EQ(SECFailure, SSL_SendSessionTicket(server_->ssl_fd(), big_token, |
| sizeof(big_token))) |
| << "this is too big, but that's not immediately obvious"; |
| EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError()); |
| } |
| |
| TEST_F(TlsConnectDatagram13, SendSessionTicketDtls) { |
| ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET); |
| ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3); |
| Connect(); |
| EXPECT_EQ(SECFailure, SSL_SendSessionTicket(server_->ssl_fd(), NULL, 0)) |
| << "no extra tickets in DTLS until we have Ack support"; |
| EXPECT_EQ(SSL_ERROR_FEATURE_NOT_SUPPORTED_FOR_VERSION, PORT_GetError()); |
| } |
| |
| TEST_F(TlsConnectStreamTls13, ExternalResumptionUseSecondTicket) { |
| ConfigureSessionCache(RESUME_BOTH, RESUME_BOTH); |
| ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3); |
| |
| struct ResumptionTicketState { |
| std::vector<uint8_t> ticket; |
| size_t invoked = 0; |
| } ticket_state; |
| auto cb = [](PRFileDesc* fd, const PRUint8* ticket, unsigned int ticket_len, |
| void* arg) -> SECStatus { |
| auto state = reinterpret_cast<ResumptionTicketState*>(arg); |
| state->ticket.assign(ticket, ticket + ticket_len); |
| state->invoked++; |
| return SECSuccess; |
| }; |
| SSL_SetResumptionTokenCallback(client_->ssl_fd(), cb, &ticket_state); |
| |
| Connect(); |
| EXPECT_EQ(SECSuccess, SSL_SendSessionTicket(server_->ssl_fd(), nullptr, 0)); |
| SendReceive(); |
| EXPECT_EQ(2U, ticket_state.invoked); |
| |
| Reset(); |
| ConfigureSessionCache(RESUME_BOTH, RESUME_BOTH); |
| client_->SetResumptionToken(ticket_state.ticket); |
| ExpectResumption(RESUME_TICKET); |
| Connect(); |
| SendReceive(); |
| } |
| |
| TEST_F(TlsConnectTest, TestTls13ResumptionDowngrade) { |
| 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(); |
| |
| // Try resuming the connection. This will fail resuming the 1.3 session |
| // from before, but will successfully establish a 1.2 connection. |
| Reset(); |
| ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET); |
| 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); |
| Connect(); |
| |
| // Renegotiate to ensure we don't carryover any state |
| // from the 1.3 resumption attempt. |
| client_->SetExpectedVersion(SSL_LIBRARY_VERSION_TLS_1_2); |
| client_->PrepareForRenegotiate(); |
| server_->StartRenegotiate(); |
| Handshake(); |
| |
| SendReceive(); |
| CheckKeys(); |
| } |
| |
| TEST_F(TlsConnectTest, TestTls13ResumptionForcedDowngrade) { |
| 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(); |
| |
| // Try resuming the connection. |
| Reset(); |
| ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3); |
| ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET); |
| // Enable the lower version on the client. |
| client_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_2, |
| SSL_LIBRARY_VERSION_TLS_1_3); |
| |
| // Add filters that set downgrade SH.version to 1.2 and the cipher suite |
| // to one that works with 1.2, so that we don't run into early sanity checks. |
| // We will eventually fail the (sid.version == SH.version) check. |
| std::vector<std::shared_ptr<PacketFilter>> filters; |
| filters.push_back(std::make_shared<SelectedCipherSuiteReplacer>( |
| server_, TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256)); |
| filters.push_back(std::make_shared<SelectedVersionReplacer>( |
| server_, SSL_LIBRARY_VERSION_TLS_1_2)); |
| |
| // Drop a bunch of extensions so that we get past the SH processing. The |
| // version extension says TLS 1.3, which is counter to our goal, the others |
| // are not permitted in TLS 1.2 handshakes. |
| filters.push_back(std::make_shared<TlsExtensionDropper>( |
| server_, ssl_tls13_supported_versions_xtn)); |
| filters.push_back( |
| std::make_shared<TlsExtensionDropper>(server_, ssl_tls13_key_share_xtn)); |
| filters.push_back(std::make_shared<TlsExtensionDropper>( |
| server_, ssl_tls13_pre_shared_key_xtn)); |
| server_->SetFilter(std::make_shared<ChainedPacketFilter>(filters)); |
| |
| // The client here generates an unexpected_message alert when it receives an |
| // encrypted handshake message from the server (EncryptedExtension). The |
| // client expects to receive an unencrypted TLS 1.2 Certificate message. |
| // The server can't decrypt the alert. |
| client_->ExpectSendAlert(kTlsAlertUnexpectedMessage); |
| server_->ExpectSendAlert(kTlsAlertUnexpectedMessage); // Server can't read |
| ConnectExpectFail(); |
| client_->CheckErrorCode(SSL_ERROR_RX_UNEXPECTED_APPLICATION_DATA); |
| server_->CheckErrorCode(SSL_ERROR_RX_UNEXPECTED_RECORD_TYPE); |
| } |
| |
| TEST_P(TlsConnectGenericResumption, ReConnectTicket) { |
| ConfigureSessionCache(RESUME_BOTH, RESUME_BOTH); |
| server_->EnableSingleCipher(ChooseOneCipher(version_)); |
| Connect(); |
| SendReceive(); |
| CheckKeys(ssl_kea_ecdh, ssl_grp_ec_curve25519, ssl_auth_rsa_sign, |
| ssl_sig_rsa_pss_rsae_sha256); |
| // Resume |
| Reset(); |
| ConfigureSessionCache(RESUME_BOTH, RESUME_BOTH); |
| ExpectResumption(RESUME_TICKET); |
| Connect(); |
| // Only the client knows this. |
| CheckKeysResumption(ssl_kea_ecdh, ssl_grp_none, ssl_grp_ec_curve25519, |
| ssl_auth_rsa_sign, ssl_sig_rsa_pss_rsae_sha256); |
| } |
| |
| TEST_P(TlsConnectGenericPre13, ReConnectCache) { |
| ConfigureSessionCache(RESUME_SESSIONID, RESUME_SESSIONID); |
| server_->EnableSingleCipher(ChooseOneCipher(version_)); |
| Connect(); |
| SendReceive(); |
| CheckKeys(ssl_kea_ecdh, ssl_grp_ec_curve25519, ssl_auth_rsa_sign, |
| ssl_sig_rsa_pss_rsae_sha256); |
| // Resume |
| Reset(); |
| ExpectResumption(RESUME_SESSIONID); |
| Connect(); |
| CheckKeysResumption(ssl_kea_ecdh, ssl_grp_none, ssl_grp_ec_curve25519, |
| ssl_auth_rsa_sign, ssl_sig_rsa_pss_rsae_sha256); |
| } |
| |
| TEST_P(TlsConnectGenericResumption, ReConnectAgainTicket) { |
| ConfigureSessionCache(RESUME_BOTH, RESUME_BOTH); |
| server_->EnableSingleCipher(ChooseOneCipher(version_)); |
| Connect(); |
| SendReceive(); |
| CheckKeys(ssl_kea_ecdh, ssl_grp_ec_curve25519, ssl_auth_rsa_sign, |
| ssl_sig_rsa_pss_rsae_sha256); |
| // Resume |
| Reset(); |
| ConfigureSessionCache(RESUME_BOTH, RESUME_BOTH); |
| ExpectResumption(RESUME_TICKET); |
| Connect(); |
| // Only the client knows this. |
| CheckKeysResumption(ssl_kea_ecdh, ssl_grp_none, ssl_grp_ec_curve25519, |
| ssl_auth_rsa_sign, ssl_sig_rsa_pss_rsae_sha256); |
| // Resume connection again |
| Reset(); |
| ConfigureSessionCache(RESUME_BOTH, RESUME_BOTH); |
| ExpectResumption(RESUME_TICKET, 2); |
| Connect(); |
| // Only the client knows this. |
| CheckKeysResumption(ssl_kea_ecdh, ssl_grp_none, ssl_grp_ec_curve25519, |
| ssl_auth_rsa_sign, ssl_sig_rsa_pss_rsae_sha256); |
| } |
| |
| void CheckGetInfoResult(uint32_t alpnSize, uint32_t earlyDataSize, |
| ScopedCERTCertificate& cert, |
| ScopedSSLResumptionTokenInfo& token) { |
| ASSERT_TRUE(cert); |
| ASSERT_TRUE(token->peerCert); |
| |
| // Check that the server cert is the correct one. |
| ASSERT_EQ(cert->derCert.len, token->peerCert->derCert.len); |
| EXPECT_EQ(0, memcmp(cert->derCert.data, token->peerCert->derCert.data, |
| cert->derCert.len)); |
| |
| ASSERT_EQ(alpnSize, token->alpnSelectionLen); |
| EXPECT_EQ(0, memcmp("a", token->alpnSelection, token->alpnSelectionLen)); |
| |
| ASSERT_EQ(earlyDataSize, token->maxEarlyDataSize); |
| |
| ASSERT_LT(ssl_TimeUsec(), token->expirationTime); |
| } |
| |
| // The client should generate a new, randomized session_id |
| // when resuming using an external token. |
| TEST_P(TlsConnectGenericResumptionToken, CheckSessionId) { |
| ConfigureSessionCache(RESUME_BOTH, RESUME_BOTH); |
| auto original_sid = MakeTlsFilter<CaptureSessionId>(client_); |
| Connect(); |
| SendReceive(); |
| |
| Reset(); |
| ConfigureSessionCache(RESUME_BOTH, RESUME_BOTH); |
| ExpectResumption(RESUME_TICKET); |
| |
| StartConnect(); |
| ASSERT_TRUE(client_->MaybeSetResumptionToken()); |
| auto resumed_sid = MakeTlsFilter<CaptureSessionId>(client_); |
| |
| Handshake(); |
| CheckConnected(); |
| SendReceive(); |
| |
| if (version_ < SSL_LIBRARY_VERSION_TLS_1_3) { |
| EXPECT_NE(resumed_sid->sid(), original_sid->sid()); |
| EXPECT_EQ(32U, resumed_sid->sid().len()); |
| } else { |
| EXPECT_EQ(0U, resumed_sid->sid().len()); |
| } |
| } |
| |
| TEST_P(TlsConnectGenericResumptionToken, ConnectResumeGetInfo) { |
| ConfigureSessionCache(RESUME_BOTH, RESUME_BOTH); |
| Connect(); |
| SendReceive(); |
| |
| Reset(); |
| ConfigureSessionCache(RESUME_BOTH, RESUME_BOTH); |
| ExpectResumption(RESUME_TICKET); |
| |
| StartConnect(); |
| ASSERT_TRUE(client_->MaybeSetResumptionToken()); |
| |
| // Get resumption token infos |
| SSLResumptionTokenInfo tokenInfo = {0}; |
| ScopedSSLResumptionTokenInfo token(&tokenInfo); |
| client_->GetTokenInfo(token); |
| ScopedCERTCertificate cert( |
| PK11_FindCertFromNickname(server_->name().c_str(), nullptr)); |
| |
| CheckGetInfoResult(0, 0, cert, token); |
| |
| Handshake(); |
| CheckConnected(); |
| |
| SendReceive(); |
| } |
| |
| TEST_P(TlsConnectGenericResumptionToken, ConnectResumeGetInfoAlpn) { |
| EnableAlpn(); |
| ConfigureSessionCache(RESUME_BOTH, RESUME_BOTH); |
| Connect(); |
| CheckAlpn("a"); |
| SendReceive(); |
| |
| Reset(); |
| EnableAlpn(); |
| ConfigureSessionCache(RESUME_BOTH, RESUME_BOTH); |
| ExpectResumption(RESUME_TICKET); |
| |
| StartConnect(); |
| ASSERT_TRUE(client_->MaybeSetResumptionToken()); |
| |
| // Get resumption token infos |
| SSLResumptionTokenInfo tokenInfo = {0}; |
| ScopedSSLResumptionTokenInfo token(&tokenInfo); |
| client_->GetTokenInfo(token); |
| ScopedCERTCertificate cert( |
| PK11_FindCertFromNickname(server_->name().c_str(), nullptr)); |
| |
| CheckGetInfoResult(1, 0, cert, token); |
| |
| Handshake(); |
| CheckConnected(); |
| CheckAlpn("a"); |
| |
| SendReceive(); |
| } |
| |
| TEST_P(TlsConnectTls13ResumptionToken, ConnectResumeGetInfoZeroRtt) { |
| EnableAlpn(); |
| SSLInt_RolloverAntiReplay(); |
| ConfigureSessionCache(RESUME_BOTH, RESUME_BOTH); |
| server_->Set0RttEnabled(true); |
| Connect(); |
| CheckAlpn("a"); |
| SendReceive(); |
| |
| Reset(); |
| EnableAlpn(); |
| ConfigureSessionCache(RESUME_BOTH, RESUME_BOTH); |
| ExpectResumption(RESUME_TICKET); |
| |
| StartConnect(); |
| server_->Set0RttEnabled(true); |
| client_->Set0RttEnabled(true); |
| ASSERT_TRUE(client_->MaybeSetResumptionToken()); |
| |
| // Get resumption token infos |
| SSLResumptionTokenInfo tokenInfo = {0}; |
| ScopedSSLResumptionTokenInfo token(&tokenInfo); |
| client_->GetTokenInfo(token); |
| ScopedCERTCertificate cert( |
| PK11_FindCertFromNickname(server_->name().c_str(), nullptr)); |
| |
| CheckGetInfoResult(1, 1024, cert, token); |
| |
| ZeroRttSendReceive(true, true); |
| Handshake(); |
| ExpectEarlyDataAccepted(true); |
| CheckConnected(); |
| CheckAlpn("a"); |
| |
| SendReceive(); |
| } |
| |
| // Resumption on sessions with client authentication only works with internal |
| // caching. |
| TEST_P(TlsConnectGenericResumption, ConnectResumeClientAuth) { |
| ConfigureSessionCache(RESUME_BOTH, RESUME_BOTH); |
| client_->SetupClientAuth(); |
| server_->RequestClientAuth(true); |
| Connect(); |
| SendReceive(); |
| EXPECT_FALSE(client_->resumption_callback_called()); |
| |
| Reset(); |
| ConfigureSessionCache(RESUME_BOTH, RESUME_BOTH); |
| if (use_external_cache()) { |
| ExpectResumption(RESUME_NONE); |
| } else { |
| ExpectResumption(RESUME_TICKET); |
| } |
| Connect(); |
| SendReceive(); |
| } |
| |
| TEST_F(TlsConnectStreamTls13, ExternalTokenAfterHrr) { |
| ConfigureSessionCache(RESUME_BOTH, RESUME_BOTH); |
| Connect(); |
| SendReceive(); |
| |
| Reset(); |
| ConfigureSessionCache(RESUME_BOTH, RESUME_BOTH); |
| ExpectResumption(RESUME_TICKET); |
| |
| static const std::vector<SSLNamedGroup> groups = {ssl_grp_ec_secp384r1, |
| ssl_grp_ec_secp521r1}; |
| server_->ConfigNamedGroups(groups); |
| |
| StartConnect(); |
| ASSERT_TRUE(client_->MaybeSetResumptionToken()); |
| |
| client_->Handshake(); // Send ClientHello. |
| server_->Handshake(); // Process ClientHello, send HelloRetryRequest. |
| |
| auto& token = client_->GetResumptionToken(); |
| SECStatus rv = |
| SSL_SetResumptionToken(client_->ssl_fd(), token.data(), token.size()); |
| ASSERT_EQ(SECFailure, rv); |
| ASSERT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError()); |
| |
| Handshake(); |
| CheckConnected(); |
| SendReceive(); |
| } |
| |
| } // namespace nss_test |