diff options
Diffstat (limited to 'security/nss/gtests/ssl_gtest/ssl_resumption_unittest.cc')
-rw-r--r-- | security/nss/gtests/ssl_gtest/ssl_resumption_unittest.cc | 274 |
1 files changed, 224 insertions, 50 deletions
diff --git a/security/nss/gtests/ssl_gtest/ssl_resumption_unittest.cc b/security/nss/gtests/ssl_gtest/ssl_resumption_unittest.cc index ce0e3ca8d..a413caf2c 100644 --- a/security/nss/gtests/ssl_gtest/ssl_resumption_unittest.cc +++ b/security/nss/gtests/ssl_gtest/ssl_resumption_unittest.cc @@ -9,6 +9,7 @@ #include "secerr.h" #include "ssl.h" #include "sslerr.h" +#include "sslexp.h" #include "sslproto.h" extern "C" { @@ -246,8 +247,7 @@ TEST_P(TlsConnectGeneric, ConnectWithExpiredTicketAtServer) { : ssl_session_ticket_xtn; auto capture = std::make_shared<TlsExtensionCapture>(xtn); client_->SetPacketFilter(capture); - client_->StartConnect(); - server_->StartConnect(); + StartConnect(); client_->Handshake(); EXPECT_TRUE(capture->captured()); EXPECT_LT(0U, capture->extension().len()); @@ -355,10 +355,7 @@ TEST_P(TlsConnectGenericPre13, ConnectEcdheTwiceReuseKey) { // This test parses the ServerKeyExchange, which isn't in 1.3 TEST_P(TlsConnectGenericPre13, ConnectEcdheTwiceNewKey) { - server_->EnsureTlsSetup(); - SECStatus rv = - SSL_OptionSet(server_->ssl_fd(), SSL_REUSE_SERVER_ECDHE_KEY, PR_FALSE); - EXPECT_EQ(SECSuccess, rv); + server_->SetOption(SSL_REUSE_SERVER_ECDHE_KEY, PR_FALSE); auto i1 = std::make_shared<TlsInspectorRecordHandshakeMessage>( kTlsHandshakeServerKeyExchange); server_->SetPacketFilter(i1); @@ -369,9 +366,7 @@ TEST_P(TlsConnectGenericPre13, ConnectEcdheTwiceNewKey) { // Restart Reset(); - server_->EnsureTlsSetup(); - rv = SSL_OptionSet(server_->ssl_fd(), SSL_REUSE_SERVER_ECDHE_KEY, PR_FALSE); - EXPECT_EQ(SECSuccess, rv); + server_->SetOption(SSL_REUSE_SERVER_ECDHE_KEY, PR_FALSE); auto i2 = std::make_shared<TlsInspectorRecordHandshakeMessage>( kTlsHandshakeServerKeyExchange); server_->SetPacketFilter(i2); @@ -401,7 +396,8 @@ TEST_P(TlsConnectTls13, TestTls13ResumeDifferentGroup) { client_->ConfigNamedGroups(kFFDHEGroups); server_->ConfigNamedGroups(kFFDHEGroups); Connect(); - CheckKeys(ssl_kea_dh, ssl_grp_ffdhe_2048, ssl_auth_rsa_sign, ssl_sig_none); + CheckKeys(ssl_kea_dh, ssl_grp_ffdhe_2048, ssl_auth_rsa_sign, + ssl_sig_rsa_pss_sha256); } // We need to enable different cipher suites at different times in the following @@ -461,36 +457,6 @@ TEST_P(TlsConnectGeneric, TestResumeServerDifferentCipher) { CheckKeys(); } -class SelectedCipherSuiteReplacer : public TlsHandshakeFilter { - public: - SelectedCipherSuiteReplacer(uint16_t suite) : cipher_suite_(suite) {} - - protected: - PacketFilter::Action FilterHandshake(const HandshakeHeader& header, - const DataBuffer& input, - DataBuffer* output) override { - if (header.handshake_type() != kTlsHandshakeServerHello) { - return KEEP; - } - - *output = input; - uint32_t temp = 0; - EXPECT_TRUE(input.Read(0, 2, &temp)); - // Cipher suite is after version(2) and random(32). - size_t pos = 34; - if (temp < SSL_LIBRARY_VERSION_TLS_1_3) { - // In old versions, we have to skip a session_id too. - EXPECT_TRUE(input.Read(pos, 1, &temp)); - pos += 1 + temp; - } - output->Write(pos, static_cast<uint32_t>(cipher_suite_), 2); - return CHANGE; - } - - private: - uint16_t cipher_suite_; -}; - // Test that the client doesn't tolerate the server picking a different cipher // suite for resumption. TEST_P(TlsConnectStream, TestResumptionOverrideCipher) { @@ -524,16 +490,13 @@ TEST_P(TlsConnectStream, TestResumptionOverrideCipher) { class SelectedVersionReplacer : public TlsHandshakeFilter { public: - SelectedVersionReplacer(uint16_t version) : version_(version) {} + SelectedVersionReplacer(uint16_t version) + : TlsHandshakeFilter({kTlsHandshakeServerHello}), version_(version) {} protected: PacketFilter::Action FilterHandshake(const HandshakeHeader& header, const DataBuffer& input, DataBuffer* output) override { - if (header.handshake_type() != kTlsHandshakeServerHello) { - return KEEP; - } - *output = input; output->Write(0, static_cast<uint32_t>(version_), 2); return CHANGE; @@ -609,7 +572,7 @@ TEST_F(TlsConnectTest, TestTls13ResumptionTwice) { Connect(); SendReceive(); CheckKeys(ssl_kea_ecdh, ssl_grp_ec_curve25519, ssl_auth_rsa_sign, - ssl_sig_none); + ssl_sig_rsa_pss_sha256); // The filter will go away when we reset, so save the captured extension. DataBuffer initialTicket(c1->extension()); ASSERT_LT(0U, initialTicket.len()); @@ -627,7 +590,7 @@ TEST_F(TlsConnectTest, TestTls13ResumptionTwice) { Connect(); SendReceive(); CheckKeys(ssl_kea_ecdh, ssl_grp_ec_curve25519, ssl_auth_rsa_sign, - ssl_sig_none); + ssl_sig_rsa_pss_sha256); ASSERT_LT(0U, c2->extension().len()); ScopedCERTCertificate cert2(SSL_PeerCertificate(client_->ssl_fd())); @@ -652,18 +615,158 @@ TEST_F(TlsConnectTest, TestTls13ResumptionDuplicateNST) { // Clear the session ticket keys to invalidate the old ticket. SSLInt_ClearSelfEncryptKey(); - SSLInt_SendNewSessionTicket(server_->ssl_fd()); + 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 = std::make_shared<TlsInspectorRecordHandshakeMessage>( + ssl_hs_new_session_ticket); + server_->SetTlsRecordFilter(nst_capture); + 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 = + std::make_shared<TlsExtensionCapture>(ssl_tls13_pre_shared_key_xtn); + client_->SetPacketFilter(psk_capture); 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 = std::make_shared<TlsInspectorRecordHandshakeMessage>( + ssl_hs_new_session_ticket); + server_->SetTlsRecordFilter(nst_capture); + 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 = + std::make_shared<TlsExtensionCapture>(ssl_tls13_pre_shared_key_xtn); + client_->SetPacketFilter(psk_capture); + 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(TlsConnectTest, TestTls13ResumptionDowngrade) { @@ -719,13 +822,84 @@ TEST_F(TlsConnectTest, TestTls13ResumptionForcedDowngrade) { TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256)); filters.push_back( std::make_shared<SelectedVersionReplacer>(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>(ssl_tls13_supported_versions_xtn)); + filters.push_back( + std::make_shared<TlsExtensionDropper>(ssl_tls13_key_share_xtn)); + filters.push_back( + std::make_shared<TlsExtensionDropper>(ssl_tls13_pre_shared_key_xtn)); server_->SetPacketFilter(std::make_shared<ChainedPacketFilter>(filters)); - client_->ExpectSendAlert(kTlsAlertDecodeError); + // 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(kTlsAlertBadRecordMac); // Server can't read ConnectExpectFail(); - client_->CheckErrorCode(SSL_ERROR_RX_MALFORMED_SERVER_HELLO); + client_->CheckErrorCode(SSL_ERROR_RX_UNEXPECTED_APPLICATION_DATA); server_->CheckErrorCode(SSL_ERROR_BAD_MAC_READ); } +TEST_P(TlsConnectGeneric, 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_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_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_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_sha256); +} + +TEST_P(TlsConnectGeneric, 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_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_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_sha256); +} + } // namespace nss_test |