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 | 190 |
1 files changed, 167 insertions, 23 deletions
diff --git a/security/nss/gtests/ssl_gtest/ssl_resumption_unittest.cc b/security/nss/gtests/ssl_gtest/ssl_resumption_unittest.cc index 264bde67f..bfc3ccfeb 100644 --- a/security/nss/gtests/ssl_gtest/ssl_resumption_unittest.cc +++ b/security/nss/gtests/ssl_gtest/ssl_resumption_unittest.cc @@ -325,14 +325,17 @@ TEST_P(TlsConnectGeneric, ConnectResumeClientBothTicketServerTicketForget) { SendReceive(); } +// Tickets last two days maximum; this is a time longer than that. +static const PRTime kLongerThanTicketLifetime = + 3LL * 24 * 60 * 60 * PR_USEC_PER_SEC; + TEST_P(TlsConnectGenericResumption, ConnectWithExpiredTicketAtClient) { - SSLInt_SetTicketLifetime(1); // one second // This causes a ticket resumption. ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET); Connect(); SendReceive(); - WAIT_(false, 1000); + AdvanceTime(kLongerThanTicketLifetime); Reset(); ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET); @@ -354,7 +357,6 @@ TEST_P(TlsConnectGenericResumption, ConnectWithExpiredTicketAtClient) { } TEST_P(TlsConnectGeneric, ConnectWithExpiredTicketAtServer) { - SSLInt_SetTicketLifetime(1); // one second // This causes a ticket resumption. ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET); Connect(); @@ -373,7 +375,7 @@ TEST_P(TlsConnectGeneric, ConnectWithExpiredTicketAtServer) { EXPECT_TRUE(capture->captured()); EXPECT_LT(0U, capture->extension().len()); - WAIT_(false, 1000); // Let the ticket expire on the server. + AdvanceTime(kLongerThanTicketLifetime); Handshake(); CheckConnected(); @@ -421,6 +423,7 @@ static int32_t SwitchCertificates(TlsAgent* agent, const SECItem* srvNameArr, TEST_P(TlsConnectGeneric, ServerSNICertSwitch) { Connect(); ScopedCERTCertificate cert1(SSL_PeerCertificate(client_->ssl_fd())); + ASSERT_NE(nullptr, cert1.get()); Reset(); ConfigureSessionCache(RESUME_NONE, RESUME_NONE); @@ -429,6 +432,7 @@ TEST_P(TlsConnectGeneric, ServerSNICertSwitch) { Connect(); ScopedCERTCertificate cert2(SSL_PeerCertificate(client_->ssl_fd())); + ASSERT_NE(nullptr, cert2.get()); CheckKeys(); EXPECT_FALSE(SECITEM_ItemsAreEqual(&cert1->derCert, &cert2->derCert)); } @@ -437,6 +441,7 @@ TEST_P(TlsConnectGeneric, ServerSNICertTypeSwitch) { Reset(TlsAgent::kServerEcdsa256); Connect(); ScopedCERTCertificate cert1(SSL_PeerCertificate(client_->ssl_fd())); + ASSERT_NE(nullptr, cert1.get()); Reset(); ConfigureSessionCache(RESUME_NONE, RESUME_NONE); @@ -447,6 +452,7 @@ TEST_P(TlsConnectGeneric, ServerSNICertTypeSwitch) { Connect(); ScopedCERTCertificate cert2(SSL_PeerCertificate(client_->ssl_fd())); + ASSERT_NE(nullptr, cert2.get()); CheckKeys(ssl_kea_ecdh, ssl_auth_ecdsa); EXPECT_TRUE(SECITEM_ItemsAreEqual(&cert1->derCert, &cert2->derCert)); } @@ -531,6 +537,7 @@ TEST_P(TlsConnectTls13, TestTls13ResumeNoCertificateRequest) { Connect(); SendReceive(); // Need to read so that we absorb the session ticket. ScopedCERTCertificate cert1(SSL_LocalCertificate(client_->ssl_fd())); + ASSERT_NE(nullptr, cert1.get()); Reset(); ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET); @@ -546,6 +553,7 @@ TEST_P(TlsConnectTls13, TestTls13ResumeNoCertificateRequest) { // Sanity check whether the client certificate matches the one // decrypted from ticket. ScopedCERTCertificate cert2(SSL_PeerCertificate(server_->ssl_fd())); + ASSERT_NE(nullptr, cert2.get()); EXPECT_TRUE(SECITEM_ItemsAreEqual(&cert1->derCert, &cert2->derCert)); } @@ -561,6 +569,7 @@ TEST_P(TlsConnectTls13, WriteBeforeHandshakeCompleteOnResumption) { Connect(); SendReceive(); // Absorb the session ticket. ScopedCERTCertificate cert1(SSL_LocalCertificate(client_->ssl_fd())); + ASSERT_NE(nullptr, cert1.get()); Reset(); ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET); @@ -577,6 +586,7 @@ TEST_P(TlsConnectTls13, WriteBeforeHandshakeCompleteOnResumption) { // Check whether the client certificate matches the one from the ticket. ScopedCERTCertificate cert2(SSL_PeerCertificate(server_->ssl_fd())); + ASSERT_NE(nullptr, cert2.get()); EXPECT_TRUE(SECITEM_ItemsAreEqual(&cert1->derCert, &cert2->derCert)); } @@ -589,15 +599,17 @@ static uint16_t ChooseOneCipher(uint16_t version) { return TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA; } -static uint16_t ChooseAnotherCipher(uint16_t version) { +static uint16_t ChooseIncompatibleCipher(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) { +// Test that we don't resume when we can't negotiate the same cipher. Note that +// for TLS 1.3, resumption is allowed between compatible ciphers, that is those +// with the same KDF hash, but we choose an incompatible one here. +TEST_P(TlsConnectGenericResumption, ResumeClientIncompatibleCipher) { ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET); client_->EnableSingleCipher(ChooseOneCipher(version_)); Connect(); @@ -607,7 +619,7 @@ TEST_P(TlsConnectGenericResumption, TestResumeClientDifferentCipher) { Reset(); ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET); ExpectResumption(RESUME_NONE); - client_->EnableSingleCipher(ChooseAnotherCipher(version_)); + client_->EnableSingleCipher(ChooseIncompatibleCipher(version_)); uint16_t ticket_extension; if (version_ >= SSL_LIBRARY_VERSION_TLS_1_3) { ticket_extension = ssl_tls13_pre_shared_key_xtn; @@ -622,24 +634,24 @@ TEST_P(TlsConnectGenericResumption, TestResumeClientDifferentCipher) { } // Test that we don't resume when we can't negotiate the same cipher. -TEST_P(TlsConnectGenericResumption, TestResumeServerDifferentCipher) { +TEST_P(TlsConnectGenericResumption, ResumeServerIncompatibleCipher) { ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET); server_->EnableSingleCipher(ChooseOneCipher(version_)); Connect(); - SendReceive(); // Need to read so that we absorb the session ticket. + SendReceive(); // Absorb the session ticket. CheckKeys(); Reset(); ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET); ExpectResumption(RESUME_NONE); - server_->EnableSingleCipher(ChooseAnotherCipher(version_)); + server_->EnableSingleCipher(ChooseIncompatibleCipher(version_)); Connect(); CheckKeys(); } // Test that the client doesn't tolerate the server picking a different cipher // suite for resumption. -TEST_P(TlsConnectStream, TestResumptionOverrideCipher) { +TEST_P(TlsConnectStream, ResumptionOverrideCipher) { ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET); server_->EnableSingleCipher(ChooseOneCipher(version_)); Connect(); @@ -648,8 +660,8 @@ TEST_P(TlsConnectStream, TestResumptionOverrideCipher) { Reset(); ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET); - MakeTlsFilter<SelectedCipherSuiteReplacer>(server_, - ChooseAnotherCipher(version_)); + MakeTlsFilter<SelectedCipherSuiteReplacer>( + server_, ChooseIncompatibleCipher(version_)); if (version_ >= SSL_LIBRARY_VERSION_TLS_1_3) { client_->ExpectSendAlert(kTlsAlertIllegalParameter); @@ -668,6 +680,38 @@ TEST_P(TlsConnectStream, TestResumptionOverrideCipher) { } } +// In TLS 1.3, it is possible to resume with a different cipher if it has the +// same hash. +TEST_P(TlsConnectTls13, ResumeClientCompatibleCipher) { + ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET); + client_->EnableSingleCipher(TLS_AES_128_GCM_SHA256); + Connect(); + SendReceive(); // Absorb the session ticket. + CheckKeys(); + + Reset(); + ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET); + ExpectResumption(RESUME_TICKET); + client_->EnableSingleCipher(TLS_CHACHA20_POLY1305_SHA256); + Connect(); + CheckKeys(); +} + +TEST_P(TlsConnectTls13, ResumeServerCompatibleCipher) { + ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET); + server_->EnableSingleCipher(TLS_AES_128_GCM_SHA256); + Connect(); + SendReceive(); // Absorb the session ticket. + CheckKeys(); + + Reset(); + ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET); + ExpectResumption(RESUME_TICKET); + server_->EnableSingleCipher(TLS_CHACHA20_POLY1305_SHA256); + Connect(); + CheckKeys(); +} + class SelectedVersionReplacer : public TlsHandshakeFilter { public: SelectedVersionReplacer(const std::shared_ptr<TlsAgent>& a, uint16_t version) @@ -757,7 +801,7 @@ TEST_F(TlsConnectTest, TestTls13ResumptionTwice) { ASSERT_LT(0U, initialTicket.len()); ScopedCERTCertificate cert1(SSL_PeerCertificate(client_->ssl_fd())); - ASSERT_TRUE(!!cert1.get()); + ASSERT_NE(nullptr, cert1.get()); Reset(); ClearStats(); @@ -773,7 +817,7 @@ TEST_F(TlsConnectTest, TestTls13ResumptionTwice) { ASSERT_LT(0U, c2->extension().len()); ScopedCERTCertificate cert2(SSL_PeerCertificate(client_->ssl_fd())); - ASSERT_TRUE(!!cert2.get()); + ASSERT_NE(nullptr, 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. @@ -1109,7 +1153,7 @@ TEST_P(TlsConnectGenericResumption, ReConnectAgainTicket) { ssl_auth_rsa_sign, ssl_sig_rsa_pss_rsae_sha256); } -void CheckGetInfoResult(uint32_t alpnSize, uint32_t earlyDataSize, +void CheckGetInfoResult(PRTime now, uint32_t alpnSize, uint32_t earlyDataSize, ScopedCERTCertificate& cert, ScopedSSLResumptionTokenInfo& token) { ASSERT_TRUE(cert); @@ -1125,7 +1169,7 @@ void CheckGetInfoResult(uint32_t alpnSize, uint32_t earlyDataSize, ASSERT_EQ(earlyDataSize, token->maxEarlyDataSize); - ASSERT_LT(ssl_TimeUsec(), token->expirationTime); + ASSERT_LT(now, token->expirationTime); } // The client should generate a new, randomized session_id @@ -1174,8 +1218,9 @@ TEST_P(TlsConnectGenericResumptionToken, ConnectResumeGetInfo) { client_->GetTokenInfo(token); ScopedCERTCertificate cert( PK11_FindCertFromNickname(server_->name().c_str(), nullptr)); + ASSERT_NE(nullptr, cert.get()); - CheckGetInfoResult(0, 0, cert, token); + CheckGetInfoResult(now(), 0, 0, cert, token); Handshake(); CheckConnected(); @@ -1183,6 +1228,56 @@ TEST_P(TlsConnectGenericResumptionToken, ConnectResumeGetInfo) { SendReceive(); } +TEST_P(TlsConnectGenericResumptionToken, RefuseExpiredTicketClient) { + ConfigureSessionCache(RESUME_BOTH, RESUME_BOTH); + Connect(); + SendReceive(); + + // Move the clock to the expiration time of the ticket. + SSLResumptionTokenInfo tokenInfo = {0}; + ScopedSSLResumptionTokenInfo token(&tokenInfo); + client_->GetTokenInfo(token); + AdvanceTime(token->expirationTime - now()); + + Reset(); + ConfigureSessionCache(RESUME_BOTH, RESUME_BOTH); + ExpectResumption(RESUME_TICKET); + + StartConnect(); + ASSERT_EQ(SECFailure, + SSL_SetResumptionToken(client_->ssl_fd(), + client_->GetResumptionToken().data(), + client_->GetResumptionToken().size())); + EXPECT_EQ(SSL_ERROR_BAD_RESUMPTION_TOKEN_ERROR, PORT_GetError()); +} + +TEST_P(TlsConnectGenericResumptionToken, RefuseExpiredTicketServer) { + ConfigureSessionCache(RESUME_BOTH, RESUME_BOTH); + Connect(); + SendReceive(); + + Reset(); + ConfigureSessionCache(RESUME_BOTH, RESUME_BOTH); + ExpectResumption(RESUME_NONE); + + // Start the handshake and send the ClientHello. + StartConnect(); + ASSERT_EQ(SECSuccess, + SSL_SetResumptionToken(client_->ssl_fd(), + client_->GetResumptionToken().data(), + client_->GetResumptionToken().size())); + client_->Handshake(); + + // Move the clock to the expiration time of the ticket. + SSLResumptionTokenInfo tokenInfo = {0}; + ScopedSSLResumptionTokenInfo token(&tokenInfo); + client_->GetTokenInfo(token); + AdvanceTime(token->expirationTime - now()); + + Handshake(); + CheckConnected(); +} + TEST_P(TlsConnectGenericResumptionToken, ConnectResumeGetInfoAlpn) { EnableAlpn(); ConfigureSessionCache(RESUME_BOTH, RESUME_BOTH); @@ -1204,8 +1299,9 @@ TEST_P(TlsConnectGenericResumptionToken, ConnectResumeGetInfoAlpn) { client_->GetTokenInfo(token); ScopedCERTCertificate cert( PK11_FindCertFromNickname(server_->name().c_str(), nullptr)); + ASSERT_NE(nullptr, cert.get()); - CheckGetInfoResult(1, 0, cert, token); + CheckGetInfoResult(now(), 1, 0, cert, token); Handshake(); CheckConnected(); @@ -1216,7 +1312,7 @@ TEST_P(TlsConnectGenericResumptionToken, ConnectResumeGetInfoAlpn) { TEST_P(TlsConnectTls13ResumptionToken, ConnectResumeGetInfoZeroRtt) { EnableAlpn(); - SSLInt_RolloverAntiReplay(); + RolloverAntiReplay(); ConfigureSessionCache(RESUME_BOTH, RESUME_BOTH); server_->Set0RttEnabled(true); Connect(); @@ -1239,8 +1335,8 @@ TEST_P(TlsConnectTls13ResumptionToken, ConnectResumeGetInfoZeroRtt) { client_->GetTokenInfo(token); ScopedCERTCertificate cert( PK11_FindCertFromNickname(server_->name().c_str(), nullptr)); - - CheckGetInfoResult(1, 1024, cert, token); + ASSERT_NE(nullptr, cert.get()); + CheckGetInfoResult(now(), 1, 1024, cert, token); ZeroRttSendReceive(true, true); Handshake(); @@ -1272,6 +1368,54 @@ TEST_P(TlsConnectGenericResumption, ConnectResumeClientAuth) { SendReceive(); } +// Check that resumption is blocked if the server requires client auth. +TEST_P(TlsConnectGenericResumption, ClientAuthRequiredOnResumption) { + ConfigureSessionCache(RESUME_BOTH, RESUME_BOTH); + server_->RequestClientAuth(false); + Connect(); + SendReceive(); + + Reset(); + client_->SetupClientAuth(); + server_->RequestClientAuth(true); + ConfigureSessionCache(RESUME_BOTH, RESUME_BOTH); + ExpectResumption(RESUME_NONE); + Connect(); + SendReceive(); +} + +// Check that resumption is blocked if the server requires client auth and +// the client fails to provide a certificate. +TEST_P(TlsConnectGenericResumption, ClientAuthRequiredOnResumptionNoCert) { + ConfigureSessionCache(RESUME_BOTH, RESUME_BOTH); + server_->RequestClientAuth(false); + Connect(); + SendReceive(); + + Reset(); + server_->RequestClientAuth(true); + ConfigureSessionCache(RESUME_BOTH, RESUME_BOTH); + // Drive handshake manually because TLS 1.3 needs it. + StartConnect(); + client_->Handshake(); // CH + server_->Handshake(); // SH.. (no resumption) + client_->Handshake(); // ... + if (version_ >= SSL_LIBRARY_VERSION_TLS_1_3) { + // In TLS 1.3, the client thinks that everything is OK here. + ASSERT_EQ(TlsAgent::STATE_CONNECTED, client_->state()); + ExpectAlert(server_, kTlsAlertCertificateRequired); + server_->Handshake(); // Alert + client_->Handshake(); // Receive Alert + client_->CheckErrorCode(SSL_ERROR_RX_CERTIFICATE_REQUIRED_ALERT); + } else { + ExpectAlert(server_, kTlsAlertBadCertificate); + server_->Handshake(); // Alert + client_->Handshake(); // Receive Alert + client_->CheckErrorCode(SSL_ERROR_BAD_CERT_ALERT); + } + server_->CheckErrorCode(SSL_ERROR_NO_CERTIFICATE); +} + TEST_F(TlsConnectStreamTls13, ExternalTokenAfterHrr) { ConfigureSessionCache(RESUME_BOTH, RESUME_BOTH); Connect(); |