diff options
Diffstat (limited to 'security/nss/gtests/ssl_gtest')
57 files changed, 2818 insertions, 292 deletions
diff --git a/security/nss/gtests/ssl_gtest/bloomfilter_unittest.cc b/security/nss/gtests/ssl_gtest/bloomfilter_unittest.cc index 553d47459..6efe06ec7 100644 --- a/security/nss/gtests/ssl_gtest/bloomfilter_unittest.cc +++ b/security/nss/gtests/ssl_gtest/bloomfilter_unittest.cc @@ -1,4 +1,5 @@ /* -*- 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/. */ diff --git a/security/nss/gtests/ssl_gtest/gtest_utils.h b/security/nss/gtests/ssl_gtest/gtest_utils.h index 8e61228e6..2344c3cea 100644 --- a/security/nss/gtests/ssl_gtest/gtest_utils.h +++ b/security/nss/gtests/ssl_gtest/gtest_utils.h @@ -1,4 +1,5 @@ /* -*- 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/. */ diff --git a/security/nss/gtests/ssl_gtest/libssl_internals.c b/security/nss/gtests/ssl_gtest/libssl_internals.c index a986c6c83..854eca07f 100644 --- a/security/nss/gtests/ssl_gtest/libssl_internals.c +++ b/security/nss/gtests/ssl_gtest/libssl_internals.c @@ -1,4 +1,5 @@ /* -*- 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/. */ @@ -8,8 +9,56 @@ #include "nss.h" #include "pk11pub.h" +#include "pk11priv.h" #include "seccomon.h" #include "selfencrypt.h" +#include "secmodti.h" +#include "sslproto.h" + +SECStatus SSLInt_RemoveServerCertificates(PRFileDesc *fd) { + if (!fd) { + return SECFailure; + } + sslSocket *ss = ssl_FindSocket(fd); + if (!ss) { + return SECFailure; + } + + PRCList *cursor; + while (!PR_CLIST_IS_EMPTY(&ss->serverCerts)) { + cursor = PR_LIST_TAIL(&ss->serverCerts); + PR_REMOVE_LINK(cursor); + ssl_FreeServerCert((sslServerCert *)cursor); + } + return SECSuccess; +} + +SECStatus SSLInt_SetDCAdvertisedSigSchemes(PRFileDesc *fd, + const SSLSignatureScheme *schemes, + uint32_t num_sig_schemes) { + if (!fd) { + return SECFailure; + } + sslSocket *ss = ssl_FindSocket(fd); + if (!ss) { + return SECFailure; + } + + // Alloc and copy, libssl will free. + SSLSignatureScheme *dc_schemes = + PORT_ZNewArray(SSLSignatureScheme, num_sig_schemes); + if (!dc_schemes) { + return SECFailure; + } + memcpy(dc_schemes, schemes, sizeof(SSLSignatureScheme) * num_sig_schemes); + + if (ss->xtnData.delegCredSigSchemesAdvertised) { + PORT_Free(ss->xtnData.delegCredSigSchemesAdvertised); + } + ss->xtnData.delegCredSigSchemesAdvertised = dc_schemes; + ss->xtnData.numDelegCredSigSchemesAdvertised = num_sig_schemes; + return SECSuccess; +} SECStatus SSLInt_TweakChannelInfoForDC(PRFileDesc *fd, PRBool changeAuthKeyBits, PRBool changeScheme) { @@ -96,6 +145,8 @@ PRBool SSLInt_ExtensionNegotiated(PRFileDesc *fd, PRUint16 ext) { return (PRBool)(ss && ssl3_ExtensionNegotiated(ss, ext)); } +// Tests should not use this function directly, because the keys may +// still be in cache. Instead, use TlsConnectTestBase::ClearServerCache. void SSLInt_ClearSelfEncryptKey() { ssl_ResetSelfEncryptKeys(); } sslSelfEncryptKeys *ssl_GetSelfEncryptKeysInt(); @@ -303,6 +354,9 @@ SECStatus SSLInt_AdvanceReadSeqNum(PRFileDesc *fd, PRUint64 to) { SECStatus SSLInt_AdvanceWriteSeqNum(PRFileDesc *fd, PRUint64 to) { sslSocket *ss; + ssl3CipherSpec *spec; + PK11Context *pk11ctxt; + const ssl3BulkCipherDef *cipher_def; ss = ssl_FindSocket(fd); if (!ss) { @@ -313,7 +367,43 @@ SECStatus SSLInt_AdvanceWriteSeqNum(PRFileDesc *fd, PRUint64 to) { return SECFailure; } ssl_GetSpecWriteLock(ss); - ss->ssl3.cwSpec->nextSeqNum = to; + spec = ss->ssl3.cwSpec; + cipher_def = spec->cipherDef; + spec->nextSeqNum = to; + if (cipher_def->type != type_aead) { + ssl_ReleaseSpecWriteLock(ss); + return SECSuccess; + } + /* If we are using aead, we need to advance the counter in the + * internal IV generator as well. + * This could be in the token or software. */ + pk11ctxt = spec->cipherContext; + /* If counter is in the token, we need to switch it to software, + * since we don't have access to the internal state of the token. We do + * that by turning on the simulated message interface, then setting up the + * software IV generator */ + if (pk11ctxt->ivCounter == 0) { + _PK11_ContextSetAEADSimulation(pk11ctxt); + pk11ctxt->ivLen = cipher_def->iv_size + cipher_def->explicit_nonce_size; + pk11ctxt->ivMaxCount = PR_UINT64(0xffffffffffffffff); + if ((cipher_def->explicit_nonce_size == 0) || + (spec->version >= SSL_LIBRARY_VERSION_TLS_1_3)) { + pk11ctxt->ivFixedBits = + (pk11ctxt->ivLen - sizeof(sslSequenceNumber)) * BPB; + pk11ctxt->ivGen = CKG_GENERATE_COUNTER_XOR; + } else { + pk11ctxt->ivFixedBits = cipher_def->iv_size * BPB; + pk11ctxt->ivGen = CKG_GENERATE_COUNTER; + } + /* DTLS included the epoch in the fixed portion of the IV */ + if (IS_DTLS(ss)) { + pk11ctxt->ivFixedBits += 2 * BPB; + } + } + /* now we can update the internal counter (either we are already using + * the software IV generator, or we just switched to it above */ + pk11ctxt->ivCounter = to; + ssl_ReleaseSpecWriteLock(ss); return SECSuccess; } @@ -332,6 +422,24 @@ SECStatus SSLInt_AdvanceWriteSeqByAWindow(PRFileDesc *fd, PRInt32 extra) { return SSLInt_AdvanceWriteSeqNum(fd, to); } +SECStatus SSLInt_AdvanceDtls13DecryptFailures(PRFileDesc *fd, PRUint64 to) { + sslSocket *ss = ssl_FindSocket(fd); + if (!ss) { + return SECFailure; + } + + ssl_GetSpecWriteLock(ss); + ssl3CipherSpec *spec = ss->ssl3.crSpec; + if (spec->cipherDef->type != type_aead) { + ssl_ReleaseSpecWriteLock(ss); + return SECFailure; + } + + spec->deprotectionFailures = to; + ssl_ReleaseSpecWriteLock(ss); + return SECSuccess; +} + SSLKEAType SSLInt_GetKEAType(SSLNamedGroup group) { const sslNamedGroupDef *groupDef = ssl_LookupNamedGroup(group); if (!groupDef) return ssl_kea_null; diff --git a/security/nss/gtests/ssl_gtest/libssl_internals.h b/security/nss/gtests/ssl_gtest/libssl_internals.h index 9cdd9ec72..4b076c2ee 100644 --- a/security/nss/gtests/ssl_gtest/libssl_internals.h +++ b/security/nss/gtests/ssl_gtest/libssl_internals.h @@ -1,4 +1,5 @@ /* -*- 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/. */ @@ -35,6 +36,7 @@ PRBool SSLInt_DamageEarlyTrafficSecret(PRFileDesc *fd); SECStatus SSLInt_Set0RttAlpn(PRFileDesc *fd, PRUint8 *data, unsigned int len); PRBool SSLInt_HasCertWithAuthType(PRFileDesc *fd, SSLAuthType authType); PRBool SSLInt_SendAlert(PRFileDesc *fd, uint8_t level, uint8_t type); +SECStatus SSLInt_AdvanceDtls13DecryptFailures(PRFileDesc *fd, PRUint64 to); SECStatus SSLInt_AdvanceWriteSeqNum(PRFileDesc *fd, PRUint64 to); SECStatus SSLInt_AdvanceReadSeqNum(PRFileDesc *fd, PRUint64 to); SECStatus SSLInt_AdvanceWriteSeqByAWindow(PRFileDesc *fd, PRInt32 extra); @@ -43,5 +45,9 @@ SECStatus SSLInt_HasPendingHandshakeData(PRFileDesc *fd, PRBool *pending); SECStatus SSLInt_SetSocketMaxEarlyDataSize(PRFileDesc *fd, uint32_t size); SECStatus SSLInt_TweakChannelInfoForDC(PRFileDesc *fd, PRBool changeAuthKeyBits, PRBool changeScheme); +SECStatus SSLInt_SetDCAdvertisedSigSchemes(PRFileDesc *fd, + const SSLSignatureScheme *schemes, + uint32_t num_sig_schemes); +SECStatus SSLInt_RemoveServerCertificates(PRFileDesc *fd); #endif // ndef libssl_internals_h_ diff --git a/security/nss/gtests/ssl_gtest/manifest.mn b/security/nss/gtests/ssl_gtest/manifest.mn index ed1128f7c..2cfa7cdd2 100644 --- a/security/nss/gtests/ssl_gtest/manifest.mn +++ b/security/nss/gtests/ssl_gtest/manifest.mn @@ -14,6 +14,7 @@ CSRCS = \ CPPSRCS = \ bloomfilter_unittest.cc \ ssl_0rtt_unittest.cc \ + ssl_aead_unittest.cc \ ssl_agent_unittest.cc \ ssl_auth_unittest.cc \ ssl_cert_ext_unittest.cc \ @@ -35,8 +36,8 @@ CPPSRCS = \ ssl_hrr_unittest.cc \ ssl_keyupdate_unittest.cc \ ssl_loopback_unittest.cc \ + ssl_masking_unittest.cc \ ssl_misc_unittest.cc \ - ssl_primitive_unittest.cc \ ssl_record_unittest.cc \ ssl_recordsep_unittest.cc \ ssl_recordsize_unittest.cc \ @@ -55,6 +56,7 @@ CPPSRCS = \ tls_hkdf_unittest.cc \ tls_filter.cc \ tls_protect.cc \ + tls_psk_unittest.cc \ tls_subcerts_unittest.cc \ tls_esni_unittest.cc \ $(SSLKEYLOGFILE_FILES) \ diff --git a/security/nss/gtests/ssl_gtest/rsa8193.h b/security/nss/gtests/ssl_gtest/rsa8193.h index f3c79518f..1ac8503bc 100644 --- a/security/nss/gtests/ssl_gtest/rsa8193.h +++ b/security/nss/gtests/ssl_gtest/rsa8193.h @@ -1,4 +1,5 @@ /* -*- 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/. */ diff --git a/security/nss/gtests/ssl_gtest/selfencrypt_unittest.cc b/security/nss/gtests/ssl_gtest/selfencrypt_unittest.cc index 82319b099..0c62c4cac 100644 --- a/security/nss/gtests/ssl_gtest/selfencrypt_unittest.cc +++ b/security/nss/gtests/ssl_gtest/selfencrypt_unittest.cc @@ -1,4 +1,5 @@ /* -*- 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/. */ diff --git a/security/nss/gtests/ssl_gtest/ssl_0rtt_unittest.cc b/security/nss/gtests/ssl_gtest/ssl_0rtt_unittest.cc index 941df7020..f873d265b 100644 --- a/security/nss/gtests/ssl_gtest/ssl_0rtt_unittest.cc +++ b/security/nss/gtests/ssl_gtest/ssl_0rtt_unittest.cc @@ -1,4 +1,5 @@ /* -*- 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/. */ @@ -14,6 +15,7 @@ extern "C" { #include "libssl_internals.h" } +#include "cpputil.h" #include "gtest_utils.h" #include "nss_scoped_ptrs.h" #include "tls_connect.h" @@ -116,16 +118,12 @@ class TlsZeroRttReplayTest : public TlsConnectTls13 { }; protected: - void RunTest(bool rollover) { - // Run the initial handshake - SetupForZeroRtt(); - + void RunTest(bool rollover, const ScopedPK11SymKey& epsk) { // Now run a true 0-RTT handshake, but capture the first packet. auto first_packet = std::make_shared<SaveFirstPacket>(); client_->SetFilter(first_packet); client_->Set0RttEnabled(true); server_->Set0RttEnabled(true); - ExpectResumption(RESUME_TICKET); ZeroRttSendReceive(true, true); Handshake(); EXPECT_LT(0U, first_packet->packet().len()); @@ -141,6 +139,11 @@ class TlsZeroRttReplayTest : public TlsConnectTls13 { Reset(); server_->StartConnect(); server_->Set0RttEnabled(true); + server_->SetAntiReplayContext(anti_replay_); + if (epsk) { + AddPsk(epsk, std::string("foo"), ssl_hash_sha256, + TLS_CHACHA20_POLY1305_SHA256); + } // Capture the early_data extension, which should not appear. auto early_data_ext = @@ -153,11 +156,41 @@ class TlsZeroRttReplayTest : public TlsConnectTls13 { server_->Handshake(); EXPECT_FALSE(early_data_ext->captured()); } + + void RunResPskTest(bool rollover) { + // Run the initial handshake + SetupForZeroRtt(); + ExpectResumption(RESUME_TICKET); + RunTest(rollover, ScopedPK11SymKey(nullptr)); + } + + void RunExtPskTest(bool rollover) { + ScopedPK11SlotInfo slot(PK11_GetInternalSlot()); + ASSERT_NE(nullptr, slot); + + const std::vector<uint8_t> kPskDummyVal(16, 0xFF); + SECItem psk_item = {siBuffer, toUcharPtr(kPskDummyVal.data()), + static_cast<unsigned int>(kPskDummyVal.size())}; + PK11SymKey* key = + PK11_ImportSymKey(slot.get(), CKM_HKDF_KEY_GEN, PK11_OriginUnwrap, + CKA_DERIVE, &psk_item, NULL); + ASSERT_NE(nullptr, key); + ScopedPK11SymKey scoped_psk(key); + RolloverAntiReplay(); + AddPsk(scoped_psk, std::string("foo"), ssl_hash_sha256, + TLS_CHACHA20_POLY1305_SHA256); + StartConnect(); + RunTest(rollover, scoped_psk); + } }; -TEST_P(TlsZeroRttReplayTest, ZeroRttReplay) { RunTest(false); } +TEST_P(TlsZeroRttReplayTest, ResPskZeroRttReplay) { RunResPskTest(false); } -TEST_P(TlsZeroRttReplayTest, ZeroRttReplayAfterRollover) { RunTest(true); } +TEST_P(TlsZeroRttReplayTest, ExtPskZeroRttReplay) { RunExtPskTest(false); } + +TEST_P(TlsZeroRttReplayTest, ZeroRttReplayAfterRollover) { + RunResPskTest(true); +} // Test that we don't try to send 0-RTT data when the server sent // us a ticket without the 0-RTT flags. @@ -178,6 +211,106 @@ TEST_P(TlsConnectTls13, ZeroRttOptionsSetLate) { SendReceive(); } +// Make sure that a session ticket sent well after the original handshake +// can be used for 0-RTT. +// Stream because DTLS doesn't support SSL_SendSessionTicket. +TEST_F(TlsConnectStreamTls13, ZeroRttUsingLateTicket) { + // Use a small-ish anti-replay window. + ResetAntiReplay(100 * PR_USEC_PER_MSEC); + RolloverAntiReplay(); + + ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET); + server_->Set0RttEnabled(true); + Connect(); + CheckKeys(); + + // Now move time forward 30s and send a ticket. + AdvanceTime(30 * PR_USEC_PER_SEC); + EXPECT_EQ(SECSuccess, SSL_SendSessionTicket(server_->ssl_fd(), NULL, 0)); + SendReceive(); + Reset(); + StartConnect(); + + client_->Set0RttEnabled(true); + server_->Set0RttEnabled(true); + ExpectResumption(RESUME_TICKET); + ZeroRttSendReceive(true, true); + Handshake(); + ExpectEarlyDataAccepted(true); + CheckConnected(); + SendReceive(); +} + +// Check that post-handshake authentication with a long RTT doesn't +// make things worse. +TEST_F(TlsConnectStreamTls13, ZeroRttUsingLateTicketPha) { + // Use a small-ish anti-replay window. + ResetAntiReplay(100 * PR_USEC_PER_MSEC); + RolloverAntiReplay(); + + ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET); + server_->Set0RttEnabled(true); + client_->SetupClientAuth(); + client_->SetOption(SSL_ENABLE_POST_HANDSHAKE_AUTH, PR_TRUE); + Connect(); + CheckKeys(); + + // Add post-handshake authentication, with some added delays. + AdvanceTime(10 * PR_USEC_PER_SEC); + EXPECT_EQ(SECSuccess, SSL_SendCertificateRequest(server_->ssl_fd())); + AdvanceTime(10 * PR_USEC_PER_SEC); + server_->SendData(50); + client_->ReadBytes(50); + client_->SendData(50); + server_->ReadBytes(50); + + AdvanceTime(10 * PR_USEC_PER_SEC); + EXPECT_EQ(SECSuccess, SSL_SendSessionTicket(server_->ssl_fd(), NULL, 0)); + server_->SendData(100); + client_->ReadBytes(100); + Reset(); + StartConnect(); + + client_->Set0RttEnabled(true); + server_->Set0RttEnabled(true); + ExpectResumption(RESUME_TICKET); + ZeroRttSendReceive(true, true); + Handshake(); + ExpectEarlyDataAccepted(true); + CheckConnected(); + SendReceive(); +} + +// Same, but with client authentication on the first connection. +TEST_F(TlsConnectStreamTls13, ZeroRttUsingLateTicketClientAuth) { + // Use a small-ish anti-replay window. + ResetAntiReplay(100 * PR_USEC_PER_MSEC); + RolloverAntiReplay(); + + ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET); + client_->SetupClientAuth(); + server_->RequestClientAuth(true); + server_->Set0RttEnabled(true); + Connect(); + CheckKeys(); + + // Now move time forward 30s and send a ticket. + AdvanceTime(30 * PR_USEC_PER_SEC); + EXPECT_EQ(SECSuccess, SSL_SendSessionTicket(server_->ssl_fd(), NULL, 0)); + SendReceive(); + Reset(); + StartConnect(); + + client_->Set0RttEnabled(true); + server_->Set0RttEnabled(true); + ExpectResumption(RESUME_TICKET); + ZeroRttSendReceive(true, true); + Handshake(); + ExpectEarlyDataAccepted(true); + CheckConnected(); + SendReceive(); +} + TEST_P(TlsConnectTls13, ZeroRttServerForgetTicket) { SetupForZeroRtt(); client_->Set0RttEnabled(true); @@ -476,15 +609,6 @@ TEST_P(TlsConnectTls13, TestTls13ZeroRttDowngradeEarlyData) { client_->CheckErrorCode(SSL_ERROR_DOWNGRADE_WITH_EARLY_DATA); } -static void CheckEarlyDataLimit(const std::shared_ptr<TlsAgent>& agent, - size_t expected_size) { - SSLPreliminaryChannelInfo preinfo; - SECStatus rv = - SSL_GetPreliminaryChannelInfo(agent->ssl_fd(), &preinfo, sizeof(preinfo)); - EXPECT_EQ(SECSuccess, rv); - EXPECT_EQ(expected_size, static_cast<size_t>(preinfo.maxEarlyDataSize)); -} - TEST_P(TlsConnectTls13, SendTooMuchEarlyData) { EnsureTlsSetup(); const char* big_message = "0123456789abcdef"; @@ -1022,6 +1146,35 @@ TEST_P(TlsConnectTls13, ZeroRttDifferentIncompatibleCipher) { SendReceive(); } +// The client failing to provide EndOfEarlyData results in failure. +// After 0-RTT working perfectly, things fall apart later. +// The server is unable to detect the change in keys, so it fails decryption. +// The client thinks everything has worked until it gets the alert. +TEST_F(TlsConnectStreamTls13, SuppressEndOfEarlyDataClientOnly) { + SetupForZeroRtt(); + client_->Set0RttEnabled(true); + server_->Set0RttEnabled(true); + client_->SetOption(SSL_SUPPRESS_END_OF_EARLY_DATA, true); + ExpectResumption(RESUME_TICKET); + ZeroRttSendReceive(true, true); + ExpectAlert(server_, kTlsAlertBadRecordMac); + Handshake(); + EXPECT_EQ(TlsAgent::STATE_CONNECTED, client_->state()); + EXPECT_EQ(TlsAgent::STATE_ERROR, server_->state()); + server_->CheckErrorCode(SSL_ERROR_BAD_MAC_READ); + client_->Handshake(); + EXPECT_EQ(TlsAgent::STATE_ERROR, client_->state()); + client_->CheckErrorCode(SSL_ERROR_BAD_MAC_ALERT); +} + +TEST_P(TlsConnectGeneric, SuppressEndOfEarlyDataNoZeroRtt) { + EnsureTlsSetup(); + client_->SetOption(SSL_SUPPRESS_END_OF_EARLY_DATA, true); + server_->SetOption(SSL_SUPPRESS_END_OF_EARLY_DATA, true); + Connect(); + SendReceive(); +} + #ifndef NSS_DISABLE_TLS_1_3 INSTANTIATE_TEST_CASE_P(Tls13ZeroRttReplayTest, TlsZeroRttReplayTest, TlsConnectTestBase::kTlsVariantsAll); diff --git a/security/nss/gtests/ssl_gtest/ssl_primitive_unittest.cc b/security/nss/gtests/ssl_gtest/ssl_aead_unittest.cc index 79509e558..d94683be3 100644 --- a/security/nss/gtests/ssl_gtest/ssl_primitive_unittest.cc +++ b/security/nss/gtests/ssl_gtest/ssl_aead_unittest.cc @@ -1,4 +1,5 @@ /* -*- 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/. */ @@ -53,7 +54,7 @@ class AeadTest : public ::testing::Test { ASSERT_GE(kMaxSize, ciphertext_len); ASSERT_LT(0U, ciphertext_len); - uint8_t output[kMaxSize]; + uint8_t output[kMaxSize] = {0}; unsigned int output_len = 0; EXPECT_EQ(SECSuccess, SSL_AeadEncrypt(ctx.get(), 0, kAad, sizeof(kAad), kPlaintext, sizeof(kPlaintext), @@ -180,7 +181,7 @@ TEST_F(AeadTest, AeadNoPointer) { } TEST_F(AeadTest, AeadAes128Gcm) { - SSLAeadContext *ctxInit; + SSLAeadContext *ctxInit = nullptr; ASSERT_EQ(SECSuccess, SSL_MakeAead(SSL_LIBRARY_VERSION_TLS_1_3, TLS_AES_128_GCM_SHA256, secret_.get(), kLabel, strlen(kLabel), &ctxInit)); @@ -202,7 +203,7 @@ TEST_F(AeadTest, AeadAes256Gcm) { } TEST_F(AeadTest, AeadChaCha20Poly1305) { - SSLAeadContext *ctxInit; + SSLAeadContext *ctxInit = nullptr; ASSERT_EQ( SECSuccess, SSL_MakeAead(SSL_LIBRARY_VERSION_TLS_1_3, TLS_CHACHA20_POLY1305_SHA256, diff --git a/security/nss/gtests/ssl_gtest/ssl_agent_unittest.cc b/security/nss/gtests/ssl_gtest/ssl_agent_unittest.cc index fd776f6e4..c3455d130 100644 --- a/security/nss/gtests/ssl_gtest/ssl_agent_unittest.cc +++ b/security/nss/gtests/ssl_gtest/ssl_agent_unittest.cc @@ -1,4 +1,5 @@ /* -*- 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/. */ diff --git a/security/nss/gtests/ssl_gtest/ssl_auth_unittest.cc b/security/nss/gtests/ssl_gtest/ssl_auth_unittest.cc index 0454db8cd..5c6eee7b6 100644 --- a/security/nss/gtests/ssl_gtest/ssl_auth_unittest.cc +++ b/security/nss/gtests/ssl_gtest/ssl_auth_unittest.cc @@ -1,4 +1,5 @@ /* -*- 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/. */ @@ -213,8 +214,7 @@ TEST_F(TlsConnectStreamTls13, PostHandshakeAuth) { MakeTlsFilter<TlsCertificateRequestContextRecorder>( client_, kTlsHandshakeCertificate); client_->SetupClientAuth(); - EXPECT_EQ(SECSuccess, SSL_OptionSet(client_->ssl_fd(), - SSL_ENABLE_POST_HANDSHAKE_AUTH, PR_TRUE)); + client_->SetOption(SSL_ENABLE_POST_HANDSHAKE_AUTH, PR_TRUE); size_t called = 0; server_->SetAuthCertificateCallback( [&called](TlsAgent*, PRBool, PRBool) -> SECStatus { @@ -252,6 +252,47 @@ TEST_F(TlsConnectStreamTls13, PostHandshakeAuth) { EXPECT_TRUE(SECITEM_ItemsAreEqual(&cert1->derCert, &cert2->derCert)); } +TEST_F(TlsConnectStreamTls13, 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(); + 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_->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)); +} + static SECStatus GetClientAuthDataHook(void* self, PRFileDesc* fd, CERTDistNames* caNames, CERTCertificate** clientCert, @@ -874,6 +915,104 @@ TEST_P(TlsConnectTls12, ClientAuthNoSigAlgs) { 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(TlsConnectTls12Plus, ClientAuthDisjointSchemes) { + EnsureTlsSetup(); + client_->SetupClientAuth(); + 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_F(TlsConnectStreamTls13, 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(); + 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[] = { @@ -1191,7 +1330,7 @@ TEST_F(TlsConnectDatagram13, AuthCompleteBeforeFinished) { // 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_F(TlsConnectDatagram13, AuthCompleteAfterFinished) { +TEST_P(TlsConnectTls13, AuthCompleteAfterFinished) { SetDeferredAuthCertificateCallback(client_, 0); // 0 = success. Connect(); } @@ -1550,6 +1689,47 @@ TEST_P(TlsConnectTls13, Tls13DsaIsNotAdvertisedServer) { 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> diff --git a/security/nss/gtests/ssl_gtest/ssl_cert_ext_unittest.cc b/security/nss/gtests/ssl_gtest/ssl_cert_ext_unittest.cc index bd66e3465..26e5fb502 100644 --- a/security/nss/gtests/ssl_gtest/ssl_cert_ext_unittest.cc +++ b/security/nss/gtests/ssl_gtest/ssl_cert_ext_unittest.cc @@ -1,4 +1,5 @@ /* -*- 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/. */ diff --git a/security/nss/gtests/ssl_gtest/ssl_cipherorder_unittest.cc b/security/nss/gtests/ssl_gtest/ssl_cipherorder_unittest.cc index 46d934868..1e4f817e9 100644 --- a/security/nss/gtests/ssl_gtest/ssl_cipherorder_unittest.cc +++ b/security/nss/gtests/ssl_gtest/ssl_cipherorder_unittest.cc @@ -1,4 +1,5 @@ /* -*- 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/. */ diff --git a/security/nss/gtests/ssl_gtest/ssl_ciphersuite_unittest.cc b/security/nss/gtests/ssl_gtest/ssl_ciphersuite_unittest.cc index ba7b775f0..86cb02d73 100644 --- a/security/nss/gtests/ssl_gtest/ssl_ciphersuite_unittest.cc +++ b/security/nss/gtests/ssl_gtest/ssl_ciphersuite_unittest.cc @@ -1,4 +1,5 @@ /* -*- 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/. */ @@ -262,6 +263,7 @@ TEST_P(TlsCipherSuiteTest, ResumeCipherSuite) { TEST_P(TlsCipherSuiteTest, ReadLimit) { SetupCertificate(); EnableSingleCipher(); + TlsSendCipherSpecCapturer capturer(client_); ConnectAndCheckCipherSuite(); if (version_ < SSL_LIBRARY_VERSION_TLS_1_3) { uint64_t last = last_safe_write(); @@ -294,9 +296,31 @@ TEST_P(TlsCipherSuiteTest, ReadLimit) { } else { epoch = 0; } - TlsAgentTestBase::MakeRecord(variant_, ssl_ct_application_data, version_, - payload, sizeof(payload), &record, - (epoch << 48) | record_limit()); + + uint64_t seqno = (epoch << 48) | record_limit(); + + // DTLS 1.3 masks the sequence number + if (variant_ == ssl_variant_datagram && + version_ >= SSL_LIBRARY_VERSION_TLS_1_3) { + auto spec = capturer.spec(1); + ASSERT_NE(nullptr, spec.get()); + ASSERT_EQ(3, spec->epoch()); + + DataBuffer pt, ct; + uint8_t dtls13_ctype = kCtDtlsCiphertext | kCtDtlsCiphertext16bSeqno | + kCtDtlsCiphertextLengthPresent; + TlsRecordHeader hdr(variant_, version_, dtls13_ctype, seqno); + pt.Assign(payload, sizeof(payload)); + TlsRecordHeader out_hdr; + spec->Protect(hdr, pt, &ct, &out_hdr); + + auto rv = out_hdr.Write(&record, 0, ct); + EXPECT_EQ(out_hdr.header_length() + ct.len(), rv); + } else { + TlsAgentTestBase::MakeRecord(variant_, ssl_ct_application_data, version_, + payload, sizeof(payload), &record, seqno); + } + client_->SendDirect(record); server_->ExpectReadWriteError(); server_->ReadBytes(); diff --git a/security/nss/gtests/ssl_gtest/ssl_custext_unittest.cc b/security/nss/gtests/ssl_gtest/ssl_custext_unittest.cc index 8ee60defe..68c789a38 100644 --- a/security/nss/gtests/ssl_gtest/ssl_custext_unittest.cc +++ b/security/nss/gtests/ssl_gtest/ssl_custext_unittest.cc @@ -1,4 +1,5 @@ /* -*- 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/. */ diff --git a/security/nss/gtests/ssl_gtest/ssl_damage_unittest.cc b/security/nss/gtests/ssl_gtest/ssl_damage_unittest.cc index a146ffc2f..9cbe9566f 100644 --- a/security/nss/gtests/ssl_gtest/ssl_damage_unittest.cc +++ b/security/nss/gtests/ssl_gtest/ssl_damage_unittest.cc @@ -1,4 +1,5 @@ /* -*- 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/. */ diff --git a/security/nss/gtests/ssl_gtest/ssl_debug_env_unittest.cc b/security/nss/gtests/ssl_gtest/ssl_debug_env_unittest.cc index 48b813a2f..59ec3d393 100644 --- a/security/nss/gtests/ssl_gtest/ssl_debug_env_unittest.cc +++ b/security/nss/gtests/ssl_gtest/ssl_debug_env_unittest.cc @@ -1,4 +1,5 @@ /* -*- 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/. */ diff --git a/security/nss/gtests/ssl_gtest/ssl_dhe_unittest.cc b/security/nss/gtests/ssl_gtest/ssl_dhe_unittest.cc index 60ad017e3..0fe88ea88 100644 --- a/security/nss/gtests/ssl_gtest/ssl_dhe_unittest.cc +++ b/security/nss/gtests/ssl_gtest/ssl_dhe_unittest.cc @@ -1,4 +1,5 @@ /* -*- 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/. */ diff --git a/security/nss/gtests/ssl_gtest/ssl_drop_unittest.cc b/security/nss/gtests/ssl_gtest/ssl_drop_unittest.cc index 7e2f9aded..05b38e381 100644 --- a/security/nss/gtests/ssl_gtest/ssl_drop_unittest.cc +++ b/security/nss/gtests/ssl_gtest/ssl_drop_unittest.cc @@ -1,4 +1,5 @@ /* -*- 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/. */ @@ -618,55 +619,6 @@ TEST_P(TlsDropDatagram13, ReorderServerEE) { // The client sends an out of order non-handshake message // but with the handshake key. -class TlsSendCipherSpecCapturer { - public: - TlsSendCipherSpecCapturer(const std::shared_ptr<TlsAgent>& agent) - : agent_(agent), send_cipher_specs_() { - EXPECT_EQ(SECSuccess, - SSL_SecretCallback(agent_->ssl_fd(), SecretCallback, this)); - } - - std::shared_ptr<TlsCipherSpec> spec(size_t i) { - if (i >= send_cipher_specs_.size()) { - return nullptr; - } - return send_cipher_specs_[i]; - } - - private: - static void SecretCallback(PRFileDesc* fd, PRUint16 epoch, - SSLSecretDirection dir, PK11SymKey* secret, - void* arg) { - auto self = static_cast<TlsSendCipherSpecCapturer*>(arg); - std::cerr << self->agent_->role_str() << ": capture " << dir - << " secret for epoch " << epoch << std::endl; - - if (dir == ssl_secret_read) { - return; - } - - SSLPreliminaryChannelInfo preinfo; - EXPECT_EQ(SECSuccess, - SSL_GetPreliminaryChannelInfo(self->agent_->ssl_fd(), &preinfo, - sizeof(preinfo))); - EXPECT_EQ(sizeof(preinfo), preinfo.length); - EXPECT_TRUE(preinfo.valuesSet & ssl_preinfo_cipher_suite); - - SSLCipherSuiteInfo cipherinfo; - EXPECT_EQ(SECSuccess, - SSL_GetCipherSuiteInfo(preinfo.cipherSuite, &cipherinfo, - sizeof(cipherinfo))); - EXPECT_EQ(sizeof(cipherinfo), cipherinfo.length); - - auto spec = std::make_shared<TlsCipherSpec>(true, epoch); - EXPECT_TRUE(spec->SetKeys(&cipherinfo, secret)); - self->send_cipher_specs_.push_back(spec); - } - - std::shared_ptr<TlsAgent> agent_; - std::vector<std::shared_ptr<TlsCipherSpec>> send_cipher_specs_; -}; - TEST_F(TlsConnectDatagram13, SendOutOfOrderAppWithHandshakeKey) { StartConnect(); // Capturing secrets means that we can't use decrypting filters on the client. @@ -683,8 +635,10 @@ TEST_F(TlsConnectDatagram13, SendOutOfOrderAppWithHandshakeKey) { auto spec = capturer.spec(0); ASSERT_NE(nullptr, spec.get()); ASSERT_EQ(2, spec->epoch()); - ASSERT_TRUE(client_->SendEncryptedRecord(spec, 0x0002000000000002, - ssl_ct_application_data, + + uint8_t dtls13_ct = kCtDtlsCiphertext | kCtDtlsCiphertext16bSeqno | + kCtDtlsCiphertextLengthPresent; + ASSERT_TRUE(client_->SendEncryptedRecord(spec, 0x0002000000000002, dtls13_ct, DataBuffer(buf, sizeof(buf)))); // Now have the server consume the bogus message. @@ -843,7 +797,7 @@ static void GetCipherAndLimit(uint16_t version, uint16_t* cipher, // a reasonable amount of time. *cipher = TLS_CHACHA20_POLY1305_SHA256; // Assume that we are starting with an expected sequence number of 0. - *limit = (1ULL << 29) - 1; + *limit = (1ULL << 15) - 1; } } @@ -865,14 +819,14 @@ TEST_P(TlsConnectDatagram, MissLotsOfPackets) { SendReceive(); } -// Send a sequence number of 0xfffffffd and it should be interpreted as that +// Send a sequence number of 0xfffd and it should be interpreted as that // (and not -3 or UINT64_MAX - 2). TEST_F(TlsConnectDatagram13, UnderflowSequenceNumber) { Connect(); // This is only valid if short headers are disabled. client_->SetOption(SSL_ENABLE_DTLS_SHORT_HEADER, PR_FALSE); EXPECT_EQ(SECSuccess, - SSLInt_AdvanceWriteSeqNum(client_->ssl_fd(), (1ULL << 30) - 3)); + SSLInt_AdvanceWriteSeqNum(client_->ssl_fd(), (1ULL << 16) - 3)); SendReceive(); } @@ -917,9 +871,13 @@ class TlsReplaceFirstRecordWithJunk : public TlsRecordFilter { return KEEP; } replaced_ = true; - TlsRecordHeader out_header(header.variant(), header.version(), - ssl_ct_application_data, - header.sequence_number()); + + uint8_t dtls13_ct = kCtDtlsCiphertext | kCtDtlsCiphertext16bSeqno | + kCtDtlsCiphertextLengthPresent; + TlsRecordHeader out_header( + header.variant(), header.version(), + is_dtls13() ? dtls13_ct : ssl_ct_application_data, + header.sequence_number()); static const uint8_t junk[] = {1, 2, 3, 4}; *offset = out_header.Write(output, *offset, DataBuffer(junk, sizeof(junk))); diff --git a/security/nss/gtests/ssl_gtest/ssl_ecdh_unittest.cc b/security/nss/gtests/ssl_gtest/ssl_ecdh_unittest.cc index b2759ef5d..e62e002f3 100644 --- a/security/nss/gtests/ssl_gtest/ssl_ecdh_unittest.cc +++ b/security/nss/gtests/ssl_gtest/ssl_ecdh_unittest.cc @@ -1,4 +1,5 @@ /* -*- 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/. */ diff --git a/security/nss/gtests/ssl_gtest/ssl_ems_unittest.cc b/security/nss/gtests/ssl_gtest/ssl_ems_unittest.cc index dc2b92881..39b2d5873 100644 --- a/security/nss/gtests/ssl_gtest/ssl_ems_unittest.cc +++ b/security/nss/gtests/ssl_gtest/ssl_ems_unittest.cc @@ -1,4 +1,5 @@ /* -*- 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/. */ diff --git a/security/nss/gtests/ssl_gtest/ssl_exporter_unittest.cc b/security/nss/gtests/ssl_gtest/ssl_exporter_unittest.cc index ac75aa1b1..26ed6bc0e 100644 --- a/security/nss/gtests/ssl_gtest/ssl_exporter_unittest.cc +++ b/security/nss/gtests/ssl_gtest/ssl_exporter_unittest.cc @@ -1,4 +1,5 @@ /* -*- 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/. */ @@ -147,4 +148,41 @@ TEST_P(TlsConnectTls13, EarlyExporter) { SendReceive(); } +TEST_P(TlsConnectTls13, EarlyExporterExternalPsk) { + RolloverAntiReplay(); + ScopedPK11SlotInfo slot(PK11_GetInternalSlot()); + ASSERT_TRUE(!!slot); + ScopedPK11SymKey scoped_psk( + PK11_KeyGen(slot.get(), CKM_HKDF_KEY_GEN, nullptr, 16, nullptr)); + AddPsk(scoped_psk, std::string("foo"), ssl_hash_sha256, + TLS_CHACHA20_POLY1305_SHA256); + StartConnect(); + client_->Set0RttEnabled(true); + server_->Set0RttEnabled(true); + client_->Handshake(); // Send ClientHello. + uint8_t client_value[10] = {0}; + RegularExporterShouldFail(client_.get(), nullptr, 0); + + EXPECT_EQ(SECSuccess, + SSL_ExportEarlyKeyingMaterial( + client_->ssl_fd(), kExporterLabel, strlen(kExporterLabel), + kExporterContext, sizeof(kExporterContext), client_value, + sizeof(client_value))); + + server_->SetSniCallback(RegularExporterShouldFail); + server_->Handshake(); // Handle ClientHello. + uint8_t server_value[10] = {0}; + EXPECT_EQ(SECSuccess, + SSL_ExportEarlyKeyingMaterial( + server_->ssl_fd(), kExporterLabel, strlen(kExporterLabel), + kExporterContext, sizeof(kExporterContext), server_value, + sizeof(server_value))); + EXPECT_EQ(0, memcmp(client_value, server_value, sizeof(client_value))); + + Handshake(); + ExpectEarlyDataAccepted(true); + CheckConnected(); + SendReceive(); +} + } // namespace nss_test diff --git a/security/nss/gtests/ssl_gtest/ssl_extension_unittest.cc b/security/nss/gtests/ssl_gtest/ssl_extension_unittest.cc index 87b8e4ace..fb995953f 100644 --- a/security/nss/gtests/ssl_gtest/ssl_extension_unittest.cc +++ b/security/nss/gtests/ssl_gtest/ssl_extension_unittest.cc @@ -1,4 +1,5 @@ /* -*- 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/. */ @@ -19,6 +20,45 @@ namespace nss_test { +class Dtls13LegacyCookieInjector : public TlsHandshakeFilter { + public: + Dtls13LegacyCookieInjector(const std::shared_ptr<TlsAgent>& a) + : TlsHandshakeFilter(a, {kTlsHandshakeClientHello}) {} + + virtual PacketFilter::Action FilterHandshake(const HandshakeHeader& header, + const DataBuffer& input, + DataBuffer* output) { + const uint8_t cookie_bytes[] = {0x03, 0x0A, 0x0B, 0x0C}; + uint32_t offset = 2 /* version */ + 32 /* random */; + + if (agent()->variant() != ssl_variant_datagram) { + ADD_FAILURE(); + return KEEP; + } + + if (header.handshake_type() != ssl_hs_client_hello) { + return KEEP; + } + + DataBuffer cookie(cookie_bytes, sizeof(cookie_bytes)); + *output = input; + + // Add the SID length (if any) to locate the cookie. + uint32_t sid_len = 0; + if (!output->Read(offset, 1, &sid_len)) { + ADD_FAILURE(); + return KEEP; + } + offset += 1 + sid_len; + output->Splice(cookie, offset, 1); + + return CHANGE; + } + + private: + DataBuffer cookie_; +}; + class TlsExtensionTruncator : public TlsExtensionFilter { public: TlsExtensionTruncator(const std::shared_ptr<TlsAgent>& a, uint16_t extension, @@ -188,8 +228,27 @@ class TlsExtensionTest13 } void ConnectWithReplacementVersionList(uint16_t version) { - DataBuffer versions_buf; + // Convert the version encoding for DTLS, if needed. + if (variant_ == ssl_variant_datagram) { + switch (version) { +#ifdef DTLS_1_3_DRAFT_VERSION + case SSL_LIBRARY_VERSION_TLS_1_3: + version = 0x7f00 | DTLS_1_3_DRAFT_VERSION; + break; +#endif + case SSL_LIBRARY_VERSION_TLS_1_2: + version = SSL_LIBRARY_VERSION_DTLS_1_2_WIRE; + break; + case SSL_LIBRARY_VERSION_TLS_1_1: + /* TLS_1_1 maps to DTLS_1_0, see sslproto.h. */ + version = SSL_LIBRARY_VERSION_DTLS_1_0_WIRE; + break; + default: + PORT_Assert(0); + } + } + DataBuffer versions_buf; size_t index = versions_buf.Write(0, 2, 1); versions_buf.Write(index, version, 2); MakeTlsFilter<TlsExtensionReplacer>( @@ -887,6 +946,26 @@ TEST_F(TlsExtensionTest13Stream, ResumeIncorrectBinderValue) { server_->CheckErrorCode(SSL_ERROR_BAD_HANDSHAKE_HASH_VALUE); } +// Do the same with an External PSK. +TEST_P(TlsConnectTls13, TestTls13PskInvalidBinderValue) { + ScopedPK11SlotInfo slot(PK11_GetInternalSlot()); + ASSERT_TRUE(!!slot); + ScopedPK11SymKey key( + PK11_KeyGen(slot.get(), CKM_HKDF_KEY_GEN, nullptr, 16, nullptr)); + ASSERT_TRUE(!!key); + AddPsk(key, std::string("foo"), ssl_hash_sha256); + StartConnect(); + ASSERT_TRUE(client_->MaybeSetResumptionToken()); + + MakeTlsFilter<TlsPreSharedKeyReplacer>( + client_, [](TlsPreSharedKeyReplacer* r) { + r->binders_[0].Write(0, r->binders_[0].data()[0] ^ 0xff, 1); + }); + ConnectExpectAlert(server_, kTlsAlertDecryptError); + client_->CheckErrorCode(SSL_ERROR_DECRYPT_ERROR_ALERT); + server_->CheckErrorCode(SSL_ERROR_BAD_HANDSHAKE_HASH_VALUE); +} + // Extend the binder by one. TEST_F(TlsExtensionTest13Stream, ResumeIncorrectBinderLength) { SetupForResume(); @@ -1226,6 +1305,14 @@ TEST_P(TlsConnectStream, IncludePadding) { EXPECT_TRUE(capture->captured()); } +TEST_F(TlsConnectDatagram13, Dtls13RejectLegacyCookie) { + EnsureTlsSetup(); + MakeTlsFilter<Dtls13LegacyCookieInjector>(client_); + ConnectExpectAlert(server_, kTlsAlertIllegalParameter); + server_->CheckErrorCode(SSL_ERROR_RX_MALFORMED_CLIENT_HELLO); + client_->CheckErrorCode(SSL_ERROR_ILLEGAL_PARAMETER_ALERT); +} + INSTANTIATE_TEST_CASE_P( ExtensionStream, TlsExtensionTestGeneric, ::testing::Combine(TlsConnectTestBase::kTlsVariantsStream, diff --git a/security/nss/gtests/ssl_gtest/ssl_fragment_unittest.cc b/security/nss/gtests/ssl_gtest/ssl_fragment_unittest.cc index 004d3fe9f..375281263 100644 --- a/security/nss/gtests/ssl_gtest/ssl_fragment_unittest.cc +++ b/security/nss/gtests/ssl_gtest/ssl_fragment_unittest.cc @@ -1,4 +1,5 @@ /* -*- 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/. */ diff --git a/security/nss/gtests/ssl_gtest/ssl_gather_unittest.cc b/security/nss/gtests/ssl_gtest/ssl_gather_unittest.cc index 4ab5e1797..745432951 100644 --- a/security/nss/gtests/ssl_gtest/ssl_gather_unittest.cc +++ b/security/nss/gtests/ssl_gtest/ssl_gather_unittest.cc @@ -1,4 +1,5 @@ /* -*- 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/. */ diff --git a/security/nss/gtests/ssl_gtest/ssl_gtest.gyp b/security/nss/gtests/ssl_gtest/ssl_gtest.gyp index 6cff0fc9d..5491a0725 100644 --- a/security/nss/gtests/ssl_gtest/ssl_gtest.gyp +++ b/security/nss/gtests/ssl_gtest/ssl_gtest.gyp @@ -15,6 +15,7 @@ 'libssl_internals.c', 'selfencrypt_unittest.cc', 'ssl_0rtt_unittest.cc', + 'ssl_aead_unittest.cc', 'ssl_agent_unittest.cc', 'ssl_auth_unittest.cc', 'ssl_cert_ext_unittest.cc', @@ -36,8 +37,8 @@ 'ssl_hrr_unittest.cc', 'ssl_keyupdate_unittest.cc', 'ssl_loopback_unittest.cc', + 'ssl_masking_unittest.cc', 'ssl_misc_unittest.cc', - 'ssl_primitive_unittest.cc', 'ssl_record_unittest.cc', 'ssl_recordsep_unittest.cc', 'ssl_recordsize_unittest.cc', @@ -56,6 +57,7 @@ 'tls_hkdf_unittest.cc', 'tls_esni_unittest.cc', 'tls_protect.cc', + 'tls_psk_unittest.cc', 'tls_subcerts_unittest.cc' ], 'dependencies': [ @@ -103,6 +105,18 @@ 'NSS_ALLOW_SSLKEYLOGFILE', ], }], + # ssl_gtest fuzz defines should only be determined by the 'fuzz_tls' + # flag (so as to match lib/ssl). If gtest.gypi added the define due + # to '--fuzz' only, remove it. + ['fuzz_tls==1', { + 'defines': [ + 'UNSAFE_FUZZER_MODE', + ], + }, { + 'defines!': [ + 'UNSAFE_FUZZER_MODE', + ], + }], ], } ], diff --git a/security/nss/gtests/ssl_gtest/ssl_hrr_unittest.cc b/security/nss/gtests/ssl_gtest/ssl_hrr_unittest.cc index ec6c95d60..56b7a2bf0 100644 --- a/security/nss/gtests/ssl_gtest/ssl_hrr_unittest.cc +++ b/security/nss/gtests/ssl_gtest/ssl_hrr_unittest.cc @@ -1,4 +1,5 @@ /* -*- 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/. */ @@ -1054,6 +1055,41 @@ TEST_F(TlsConnectTest, Select12AfterHelloRetryRequest) { EXPECT_EQ(SSL_ERROR_RX_MALFORMED_SERVER_HELLO, client_->error_code()); } +// This class increments the low byte of the first Handshake.message_seq +// field in every handshake record. +class MessageSeqIncrementer : public TlsRecordFilter { + public: + MessageSeqIncrementer(const std::shared_ptr<TlsAgent>& a) + : TlsRecordFilter(a) {} + + protected: + PacketFilter::Action FilterRecord(const TlsRecordHeader& header, + const DataBuffer& data, + DataBuffer* changed) override { + if (header.content_type() != ssl_ct_handshake) { + return KEEP; + } + + *changed = data; + // struct { uint8 msg_type; uint24 length; uint16 message_seq; ... } + // Handshake; + changed->data()[5]++; + EXPECT_NE(0, changed->data()[5]); // Check for overflow. + return CHANGE; + } +}; + +// A server that receives a ClientHello with message_seq == 1 +// assumes that this is after a stateless HelloRetryRequest. +// However, it should reject the ClientHello if it lacks a cookie. +TEST_F(TlsConnectDatagram13, MessageSeq1ClientHello) { + EnsureTlsSetup(); + MakeTlsFilter<MessageSeqIncrementer>(client_); + ConnectExpectAlert(server_, kTlsAlertMissingExtension); + EXPECT_EQ(SSL_ERROR_MISSING_COOKIE_EXTENSION, server_->error_code()); + EXPECT_EQ(SSL_ERROR_MISSING_EXTENSION_ALERT, client_->error_code()); +} + class HelloRetryRequestAgentTest : public TlsAgentTestClient { protected: void SetUp() override { @@ -1177,6 +1213,114 @@ TEST_P(TlsConnectStreamPre13, HrrRandomOnTls10) { server_->CheckErrorCode(SSL_ERROR_ILLEGAL_PARAMETER_ALERT); } +TEST_F(TlsConnectStreamTls13, HrrThenTls12) { + StartConnect(); + size_t cb_called = 0; + EXPECT_EQ(SECSuccess, SSL_HelloRetryRequestCallback(server_->ssl_fd(), + RetryHello, &cb_called)); + server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_2, + SSL_LIBRARY_VERSION_TLS_1_3); + + client_->Handshake(); // Send CH (1.3) + server_->Handshake(); // Send HRR. + EXPECT_EQ(1U, cb_called); + + // Replace the client with a new TLS 1.2 client. Don't call Init(), since + // it will artifically limit the server's vrange. + client_.reset( + new TlsAgent(client_->name(), TlsAgent::CLIENT, ssl_variant_stream)); + client_->SetPeer(server_); + server_->SetPeer(client_); + client_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_2, + SSL_LIBRARY_VERSION_TLS_1_2); + + client_->StartConnect(); + client_->Handshake(); // Send CH (1.2) + ExpectAlert(server_, kTlsAlertProtocolVersion); + server_->Handshake(); + server_->CheckErrorCode(SSL_ERROR_UNSUPPORTED_VERSION); + client_->Handshake(); + client_->CheckErrorCode(SSL_ERROR_PROTOCOL_VERSION_ALERT); +} + +TEST_F(TlsConnectStreamTls13, ZeroRttHrrThenTls12) { + SetupForZeroRtt(); + + client_->Set0RttEnabled(true); + size_t cb_called = 0; + EXPECT_EQ(SECSuccess, SSL_HelloRetryRequestCallback(server_->ssl_fd(), + RetryHello, &cb_called)); + server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_2, + SSL_LIBRARY_VERSION_TLS_1_3); + + client_->Handshake(); // Send CH (1.3) + ZeroRttSendReceive(true, false); + server_->Handshake(); // Send HRR. + EXPECT_EQ(1U, cb_called); + + // Replace the client with a new TLS 1.2 client. Don't call Init(), since + // it will artifically limit the server's vrange. + client_.reset( + new TlsAgent(client_->name(), TlsAgent::CLIENT, ssl_variant_stream)); + client_->SetPeer(server_); + server_->SetPeer(client_); + client_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_2, + SSL_LIBRARY_VERSION_TLS_1_2); + + client_->StartConnect(); + client_->Handshake(); // Send CH (1.2) + ExpectAlert(server_, kTlsAlertProtocolVersion); + server_->Handshake(); + server_->CheckErrorCode(SSL_ERROR_UNSUPPORTED_VERSION); + client_->Handshake(); + client_->CheckErrorCode(SSL_ERROR_PROTOCOL_VERSION_ALERT); + + // Try to write something + server_->Handshake(); + client_->ExpectReadWriteError(); + client_->SendData(1); + uint8_t buf[1]; + EXPECT_EQ(-1, PR_Read(server_->ssl_fd(), buf, sizeof(buf))); + EXPECT_EQ(SSL_ERROR_HANDSHAKE_FAILED, PR_GetError()); +} + +TEST_F(TlsConnectStreamTls13, HrrThenTls12SupportedVersions) { + SetupForZeroRtt(); + client_->Set0RttEnabled(true); + size_t cb_called = 0; + EXPECT_EQ(SECSuccess, SSL_HelloRetryRequestCallback(server_->ssl_fd(), + RetryHello, &cb_called)); + server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_2, + SSL_LIBRARY_VERSION_TLS_1_3); + + client_->Handshake(); // Send CH (1.3) + ZeroRttSendReceive(true, false); + server_->Handshake(); // Send HRR. + EXPECT_EQ(1U, cb_called); + + // Replace the client with a new TLS 1.2 client. Don't call Init(), since + // it will artifically limit the server's vrange. + client_.reset( + new TlsAgent(client_->name(), TlsAgent::CLIENT, ssl_variant_stream)); + client_->SetPeer(server_); + server_->SetPeer(client_); + client_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_1, + SSL_LIBRARY_VERSION_TLS_1_2); + // Negotiate via supported_versions + static const uint8_t tls12[] = {0x02, 0x03, 0x03}; + auto replacer = MakeTlsFilter<TlsExtensionInjector>( + client_, ssl_tls13_supported_versions_xtn, + DataBuffer(tls12, sizeof(tls12))); + + client_->StartConnect(); + client_->Handshake(); // Send CH (1.2) + ExpectAlert(server_, kTlsAlertProtocolVersion); + server_->Handshake(); + server_->CheckErrorCode(SSL_ERROR_UNSUPPORTED_VERSION); + client_->Handshake(); + client_->CheckErrorCode(SSL_ERROR_PROTOCOL_VERSION_ALERT); +} + INSTANTIATE_TEST_CASE_P(HelloRetryRequestAgentTests, HelloRetryRequestAgentTest, ::testing::Combine(TlsConnectTestBase::kTlsVariantsAll, TlsConnectTestBase::kTlsV13)); diff --git a/security/nss/gtests/ssl_gtest/ssl_keylog_unittest.cc b/security/nss/gtests/ssl_gtest/ssl_keylog_unittest.cc index c07eec1da..4713e52a2 100644 --- a/security/nss/gtests/ssl_gtest/ssl_keylog_unittest.cc +++ b/security/nss/gtests/ssl_gtest/ssl_keylog_unittest.cc @@ -1,4 +1,5 @@ /* -*- 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/. */ diff --git a/security/nss/gtests/ssl_gtest/ssl_keyupdate_unittest.cc b/security/nss/gtests/ssl_gtest/ssl_keyupdate_unittest.cc index e0af9a641..b921d2c1e 100644 --- a/security/nss/gtests/ssl_gtest/ssl_keyupdate_unittest.cc +++ b/security/nss/gtests/ssl_gtest/ssl_keyupdate_unittest.cc @@ -1,4 +1,5 @@ /* -*- 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/. */ diff --git a/security/nss/gtests/ssl_gtest/ssl_loopback_unittest.cc b/security/nss/gtests/ssl_gtest/ssl_loopback_unittest.cc index 214e53a21..12c2496a6 100644 --- a/security/nss/gtests/ssl_gtest/ssl_loopback_unittest.cc +++ b/security/nss/gtests/ssl_gtest/ssl_loopback_unittest.cc @@ -1,4 +1,5 @@ /* -*- 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/. */ diff --git a/security/nss/gtests/ssl_gtest/ssl_masking_unittest.cc b/security/nss/gtests/ssl_gtest/ssl_masking_unittest.cc new file mode 100644 index 000000000..cf0553cbb --- /dev/null +++ b/security/nss/gtests/ssl_gtest/ssl_masking_unittest.cc @@ -0,0 +1,350 @@ +/* -*- 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 <memory> + +#include "keyhi.h" +#include "pk11pub.h" +#include "secerr.h" +#include "ssl.h" +#include "sslerr.h" +#include "sslexp.h" +#include "sslproto.h" + +#include "gtest_utils.h" +#include "nss_scoped_ptrs.h" +#include "scoped_ptrs_ssl.h" +#include "tls_connect.h" + +namespace nss_test { + +// From tls_hkdf_unittest.cc: +extern size_t GetHashLength(SSLHashType ht); + +const std::string kLabel = "sn"; + +class MaskingTest : public ::testing::Test { + public: + MaskingTest() : slot_(PK11_GetInternalSlot()) {} + + void InitSecret(SSLHashType hash_type) { + ScopedPK11SlotInfo slot(PK11_GetInternalSlot()); + PK11SymKey *s = PK11_KeyGen(slot_.get(), CKM_GENERIC_SECRET_KEY_GEN, + nullptr, AES_128_KEY_LENGTH, nullptr); + ASSERT_NE(nullptr, s); + secret_.reset(s); + } + + void SetUp() override { + InitSecret(ssl_hash_sha256); + PORT_SetError(0); + } + + protected: + ScopedPK11SymKey secret_; + ScopedPK11SlotInfo slot_; + // Should have 4B ctr, 12B nonce for ChaCha, or >=16B ciphertext for AES. + // Use the same default size for mask output. + static const int kSampleSize = 16; + static const int kMaskSize = 16; + void CreateMask(PRUint16 ciphersuite, SSLProtocolVariant variant, + std::string label, const std::vector<uint8_t> &sample, + std::vector<uint8_t> *out_mask) { + ASSERT_NE(nullptr, out_mask); + SSLMaskingContext *ctx_init = nullptr; + EXPECT_EQ(SECSuccess, + SSL_CreateVariantMaskingContext( + SSL_LIBRARY_VERSION_TLS_1_3, ciphersuite, variant, + secret_.get(), label.c_str(), label.size(), &ctx_init)); + ASSERT_NE(nullptr, ctx_init); + ScopedSSLMaskingContext ctx(ctx_init); + + EXPECT_EQ(SECSuccess, + SSL_CreateMask(ctx.get(), sample.data(), sample.size(), + out_mask->data(), out_mask->size())); + bool all_zeros = std::all_of(out_mask->begin(), out_mask->end(), + [](uint8_t v) { return v == 0; }); + + // If out_mask is short, |all_zeros| will be (expectedly) true often enough + // to fail tests. + // In this case, just retry to make sure we're not outputting zeros + // continuously. + if (all_zeros && out_mask->size() < 3) { + unsigned int tries = 2; + std::vector<uint8_t> tmp_sample = sample; + std::vector<uint8_t> tmp_mask(out_mask->size()); + while (tries--) { + tmp_sample.data()[0]++; // Tweak something to get a new mask. + EXPECT_EQ(SECSuccess, SSL_CreateMask(ctx.get(), tmp_sample.data(), + tmp_sample.size(), tmp_mask.data(), + tmp_mask.size())); + bool retry_zero = std::all_of(tmp_mask.begin(), tmp_mask.end(), + [](uint8_t v) { return v == 0; }); + if (!retry_zero) { + all_zeros = false; + break; + } + } + } + EXPECT_FALSE(all_zeros); + } +}; + +class SuiteTest : public MaskingTest, + public ::testing::WithParamInterface<uint16_t> { + public: + SuiteTest() : ciphersuite_(GetParam()) {} + void CreateMask(std::string label, const std::vector<uint8_t> &sample, + std::vector<uint8_t> *out_mask) { + MaskingTest::CreateMask(ciphersuite_, ssl_variant_datagram, label, sample, + out_mask); + } + + protected: + const uint16_t ciphersuite_; +}; + +class VariantTest : public MaskingTest, + public ::testing::WithParamInterface<SSLProtocolVariant> { + public: + VariantTest() : variant_(GetParam()) {} + void CreateMask(uint16_t ciphersuite, std::string label, + const std::vector<uint8_t> &sample, + std::vector<uint8_t> *out_mask) { + MaskingTest::CreateMask(ciphersuite, variant_, label, sample, out_mask); + } + + protected: + const SSLProtocolVariant variant_; +}; + +class VariantSuiteTest : public MaskingTest, + public ::testing::WithParamInterface< + std::tuple<SSLProtocolVariant, uint16_t>> { + public: + VariantSuiteTest() + : variant_(std::get<0>(GetParam())), + ciphersuite_(std::get<1>(GetParam())) {} + void CreateMask(std::string label, const std::vector<uint8_t> &sample, + std::vector<uint8_t> *out_mask) { + MaskingTest::CreateMask(ciphersuite_, variant_, label, sample, out_mask); + } + + protected: + const SSLProtocolVariant variant_; + const uint16_t ciphersuite_; +}; + +TEST_P(VariantSuiteTest, MaskContextNoLabel) { + std::vector<uint8_t> sample(kSampleSize); + std::vector<uint8_t> mask(kMaskSize); + CreateMask(std::string(""), sample, &mask); +} + +TEST_P(VariantSuiteTest, MaskNoSample) { + std::vector<uint8_t> mask(kMaskSize); + SSLMaskingContext *ctx_init = nullptr; + EXPECT_EQ(SECSuccess, + SSL_CreateVariantMaskingContext( + SSL_LIBRARY_VERSION_TLS_1_3, ciphersuite_, variant_, + secret_.get(), kLabel.c_str(), kLabel.size(), &ctx_init)); + ASSERT_NE(nullptr, ctx_init); + ScopedSSLMaskingContext ctx(ctx_init); + + EXPECT_EQ(SECFailure, + SSL_CreateMask(ctx.get(), nullptr, 0, mask.data(), mask.size())); + EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError()); + + EXPECT_EQ(SECFailure, SSL_CreateMask(ctx.get(), nullptr, mask.size(), + mask.data(), mask.size())); + EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError()); +} + +TEST_P(VariantSuiteTest, MaskShortSample) { + std::vector<uint8_t> sample(kSampleSize); + std::vector<uint8_t> mask(kMaskSize); + SSLMaskingContext *ctx_init = nullptr; + EXPECT_EQ(SECSuccess, + SSL_CreateVariantMaskingContext( + SSL_LIBRARY_VERSION_TLS_1_3, ciphersuite_, variant_, + secret_.get(), kLabel.c_str(), kLabel.size(), &ctx_init)); + ASSERT_NE(nullptr, ctx_init); + ScopedSSLMaskingContext ctx(ctx_init); + + EXPECT_EQ(SECFailure, + SSL_CreateMask(ctx.get(), sample.data(), sample.size() - 1, + mask.data(), mask.size())); + EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError()); +} + +TEST_P(VariantSuiteTest, MaskContextUnsupportedMech) { + std::vector<uint8_t> sample(kSampleSize); + std::vector<uint8_t> mask(kMaskSize); + SSLMaskingContext *ctx_init = nullptr; + EXPECT_EQ(SECFailure, + SSL_CreateVariantMaskingContext( + SSL_LIBRARY_VERSION_TLS_1_3, TLS_RSA_WITH_AES_128_CBC_SHA256, + variant_, secret_.get(), nullptr, 0, &ctx_init)); + EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError()); + EXPECT_EQ(nullptr, ctx_init); +} + +TEST_P(VariantSuiteTest, MaskContextUnsupportedVersion) { + std::vector<uint8_t> sample(kSampleSize); + std::vector<uint8_t> mask(kMaskSize); + SSLMaskingContext *ctx_init = nullptr; + EXPECT_EQ(SECFailure, SSL_CreateVariantMaskingContext( + SSL_LIBRARY_VERSION_TLS_1_2, ciphersuite_, variant_, + secret_.get(), nullptr, 0, &ctx_init)); + EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError()); + EXPECT_EQ(nullptr, ctx_init); +} + +TEST_P(VariantSuiteTest, MaskMaxLength) { + uint32_t max_mask_len = kMaskSize; + if (ciphersuite_ == TLS_CHACHA20_POLY1305_SHA256) { + // Internal limitation for ChaCha20 masks. + max_mask_len = 128; + } + + std::vector<uint8_t> sample(kSampleSize); + std::vector<uint8_t> mask(max_mask_len + 1); + SSLMaskingContext *ctx_init = nullptr; + EXPECT_EQ(SECSuccess, + SSL_CreateVariantMaskingContext( + SSL_LIBRARY_VERSION_TLS_1_3, ciphersuite_, variant_, + secret_.get(), kLabel.c_str(), kLabel.size(), &ctx_init)); + ASSERT_NE(nullptr, ctx_init); + ScopedSSLMaskingContext ctx(ctx_init); + + EXPECT_EQ(SECSuccess, SSL_CreateMask(ctx.get(), sample.data(), sample.size(), + mask.data(), mask.size() - 1)); + EXPECT_EQ(SECFailure, SSL_CreateMask(ctx.get(), sample.data(), sample.size(), + mask.data(), mask.size())); + EXPECT_EQ(SEC_ERROR_OUTPUT_LEN, PORT_GetError()); +} + +TEST_P(VariantSuiteTest, MaskMinLength) { + std::vector<uint8_t> sample(kSampleSize); + std::vector<uint8_t> mask(1); // Don't pass a null + + SSLMaskingContext *ctx_init = nullptr; + EXPECT_EQ(SECSuccess, + SSL_CreateVariantMaskingContext( + SSL_LIBRARY_VERSION_TLS_1_3, ciphersuite_, variant_, + secret_.get(), kLabel.c_str(), kLabel.size(), &ctx_init)); + ASSERT_NE(nullptr, ctx_init); + ScopedSSLMaskingContext ctx(ctx_init); + EXPECT_EQ(SECFailure, SSL_CreateMask(ctx.get(), sample.data(), sample.size(), + mask.data(), 0)); + EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError()); + EXPECT_EQ(SECSuccess, SSL_CreateMask(ctx.get(), sample.data(), sample.size(), + mask.data(), 1)); +} + +TEST_P(VariantSuiteTest, MaskRotateLabel) { + std::vector<uint8_t> sample(kSampleSize); + std::vector<uint8_t> mask1(kMaskSize); + std::vector<uint8_t> mask2(kMaskSize); + EXPECT_EQ(SECSuccess, PK11_GenerateRandomOnSlot(slot_.get(), sample.data(), + sample.size())); + + CreateMask(kLabel, sample, &mask1); + CreateMask(std::string("sn1"), sample, &mask2); + EXPECT_FALSE(mask1 == mask2); +} + +TEST_P(VariantSuiteTest, MaskRotateSample) { + std::vector<uint8_t> sample(kSampleSize); + std::vector<uint8_t> mask1(kMaskSize); + std::vector<uint8_t> mask2(kMaskSize); + + EXPECT_EQ(SECSuccess, PK11_GenerateRandomOnSlot(slot_.get(), sample.data(), + sample.size())); + CreateMask(kLabel, sample, &mask1); + + EXPECT_EQ(SECSuccess, PK11_GenerateRandomOnSlot(slot_.get(), sample.data(), + sample.size())); + CreateMask(kLabel, sample, &mask2); + EXPECT_FALSE(mask1 == mask2); +} + +TEST_P(VariantSuiteTest, MaskRederive) { + std::vector<uint8_t> sample(kSampleSize); + std::vector<uint8_t> mask1(kMaskSize); + std::vector<uint8_t> mask2(kMaskSize); + + SECStatus rv = + PK11_GenerateRandomOnSlot(slot_.get(), sample.data(), sample.size()); + EXPECT_EQ(SECSuccess, rv); + + // Check that re-using inputs with a new context produces the same mask. + CreateMask(kLabel, sample, &mask1); + CreateMask(kLabel, sample, &mask2); + EXPECT_TRUE(mask1 == mask2); +} + +TEST_P(SuiteTest, MaskTlsVariantKeySeparation) { + std::vector<uint8_t> sample(kSampleSize); + std::vector<uint8_t> tls_mask(kMaskSize); + std::vector<uint8_t> dtls_mask(kMaskSize); + SSLMaskingContext *stream_ctx_init = nullptr; + SSLMaskingContext *datagram_ctx_init = nullptr; + + // Init + EXPECT_EQ(SECSuccess, SSL_CreateVariantMaskingContext( + SSL_LIBRARY_VERSION_TLS_1_3, ciphersuite_, + ssl_variant_stream, secret_.get(), kLabel.c_str(), + kLabel.size(), &stream_ctx_init)); + ASSERT_NE(nullptr, stream_ctx_init); + EXPECT_EQ(SECSuccess, SSL_CreateVariantMaskingContext( + SSL_LIBRARY_VERSION_TLS_1_3, ciphersuite_, + ssl_variant_datagram, secret_.get(), kLabel.c_str(), + kLabel.size(), &datagram_ctx_init)); + ASSERT_NE(nullptr, datagram_ctx_init); + ScopedSSLMaskingContext tls_ctx(stream_ctx_init); + ScopedSSLMaskingContext dtls_ctx(datagram_ctx_init); + + // Derive + EXPECT_EQ(SECSuccess, + SSL_CreateMask(tls_ctx.get(), sample.data(), sample.size(), + tls_mask.data(), tls_mask.size())); + + EXPECT_EQ(SECSuccess, + SSL_CreateMask(dtls_ctx.get(), sample.data(), sample.size(), + dtls_mask.data(), dtls_mask.size())); + EXPECT_NE(tls_mask, dtls_mask); +} + +TEST_P(VariantTest, MaskChaChaRederiveOddSizes) { + // Non-block-aligned. + std::vector<uint8_t> sample(27); + std::vector<uint8_t> mask1(26); + std::vector<uint8_t> mask2(25); + EXPECT_EQ(SECSuccess, PK11_GenerateRandomOnSlot(slot_.get(), sample.data(), + sample.size())); + CreateMask(TLS_CHACHA20_POLY1305_SHA256, kLabel, sample, &mask1); + CreateMask(TLS_CHACHA20_POLY1305_SHA256, kLabel, sample, &mask2); + mask1.pop_back(); + EXPECT_TRUE(mask1 == mask2); +} + +static const uint16_t kMaskingCiphersuites[] = {TLS_CHACHA20_POLY1305_SHA256, + TLS_AES_128_GCM_SHA256, + TLS_AES_256_GCM_SHA384}; +::testing::internal::ParamGenerator<uint16_t> kMaskingCiphersuiteParams = + ::testing::ValuesIn(kMaskingCiphersuites); + +INSTANTIATE_TEST_CASE_P(GenericMasking, SuiteTest, kMaskingCiphersuiteParams); + +INSTANTIATE_TEST_CASE_P(GenericMasking, VariantTest, + TlsConnectTestBase::kTlsVariantsAll); + +INSTANTIATE_TEST_CASE_P(GenericMasking, VariantSuiteTest, + ::testing::Combine(TlsConnectTestBase::kTlsVariantsAll, + kMaskingCiphersuiteParams)); + +} // namespace nss_test diff --git a/security/nss/gtests/ssl_gtest/ssl_misc_unittest.cc b/security/nss/gtests/ssl_gtest/ssl_misc_unittest.cc index f9bc4f421..2b1b92dcd 100644 --- a/security/nss/gtests/ssl_gtest/ssl_misc_unittest.cc +++ b/security/nss/gtests/ssl_gtest/ssl_misc_unittest.cc @@ -1,4 +1,5 @@ /* -*- 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/. */ diff --git a/security/nss/gtests/ssl_gtest/ssl_record_unittest.cc b/security/nss/gtests/ssl_gtest/ssl_record_unittest.cc index dab0812e0..7c0dbca3b 100644 --- a/security/nss/gtests/ssl_gtest/ssl_record_unittest.cc +++ b/security/nss/gtests/ssl_gtest/ssl_record_unittest.cc @@ -1,4 +1,5 @@ /* -*- 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/. */ @@ -184,12 +185,42 @@ TEST_F(TlsConnectStreamTls13, TooLargeRecord) { class ShortHeaderChecker : public PacketFilter { public: PacketFilter::Action Filter(const DataBuffer& input, DataBuffer* output) { - // The first octet should be 0b001xxxxx. - EXPECT_EQ(1, input.data()[0] >> 5); + // The first octet should be 0b001000xx. + EXPECT_EQ(kCtDtlsCiphertext, (input.data()[0] & ~0x3)); return KEEP; } }; +TEST_F(TlsConnectDatagram13, AeadLimit) { + Connect(); + EXPECT_EQ(SECSuccess, SSLInt_AdvanceDtls13DecryptFailures(server_->ssl_fd(), + (1ULL << 36) - 2)); + SendReceive(50); + + // Expect this to increment the counter. We should still be able to talk. + client_->SetFilter(std::make_shared<TlsRecordLastByteDamager>(client_)); + client_->SendData(10); + server_->ReadBytes(10); + client_->ClearFilter(); + client_->ResetSentBytes(50); + SendReceive(60); + + // Expect alert when the limit is hit. + client_->SetFilter(std::make_shared<TlsRecordLastByteDamager>(client_)); + client_->SendData(10); + ExpectAlert(server_, kTlsAlertBadRecordMac); + + // Check the error on both endpoints. + uint8_t buf[10]; + PRInt32 rv = PR_Read(server_->ssl_fd(), buf, sizeof(buf)); + EXPECT_EQ(-1, rv); + EXPECT_EQ(SSL_ERROR_BAD_MAC_READ, PORT_GetError()); + + rv = PR_Read(client_->ssl_fd(), buf, sizeof(buf)); + EXPECT_EQ(-1, rv); + EXPECT_EQ(SSL_ERROR_BAD_MAC_ALERT, PORT_GetError()); +} + TEST_F(TlsConnectDatagram13, ShortHeadersClient) { Connect(); client_->SetOption(SSL_ENABLE_DTLS_SHORT_HEADER, PR_TRUE); @@ -204,6 +235,35 @@ TEST_F(TlsConnectDatagram13, ShortHeadersServer) { SendReceive(); } +// Send a DTLSCiphertext header with a 2B sequence number, and no length. +TEST_F(TlsConnectDatagram13, DtlsAlternateShortHeader) { + StartConnect(); + TlsSendCipherSpecCapturer capturer(client_); + Connect(); + SendReceive(50); + + uint8_t buf[] = {0x32, 0x33, 0x34}; + auto spec = capturer.spec(1); + ASSERT_NE(nullptr, spec.get()); + ASSERT_EQ(3, spec->epoch()); + + uint8_t dtls13_ct = kCtDtlsCiphertext | kCtDtlsCiphertext16bSeqno; + TlsRecordHeader header(variant_, SSL_LIBRARY_VERSION_TLS_1_3, dtls13_ct, + 0x0003000000000001); + TlsRecordHeader out_header(header); + DataBuffer msg(buf, sizeof(buf)); + msg.Write(msg.len(), ssl_ct_application_data, 1); + DataBuffer ciphertext; + EXPECT_TRUE(spec->Protect(header, msg, &ciphertext, &out_header)); + + DataBuffer record; + auto rv = out_header.Write(&record, 0, ciphertext); + EXPECT_EQ(out_header.header_length() + ciphertext.len(), rv); + client_->SendDirect(record); + + server_->ReadBytes(3); +} + TEST_F(TlsConnectStreamTls13, UnencryptedFinishedMessage) { StartConnect(); client_->Handshake(); // Send ClientHello diff --git a/security/nss/gtests/ssl_gtest/ssl_recordsep_unittest.cc b/security/nss/gtests/ssl_gtest/ssl_recordsep_unittest.cc index 4333f5c74..8051b58d0 100644 --- a/security/nss/gtests/ssl_gtest/ssl_recordsep_unittest.cc +++ b/security/nss/gtests/ssl_gtest/ssl_recordsep_unittest.cc @@ -1,4 +1,5 @@ /* -*- 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/. */ @@ -276,7 +277,8 @@ class StagedRecords { // Now there should be staged data. EXPECT_FALSE(data_.empty()); if (g_ssl_gtest_verbose) { - std::cerr << role_ << ": forward " << data_ << std::endl; + std::cerr << role_ << ": forward epoch " << epoch_ << " " << data_ + << std::endl; } EXPECT_EQ(SECSuccess, SSL_RecordLayerData(peer->ssl_fd(), epoch_, content_type_, @@ -364,14 +366,24 @@ TEST_P(TlsConnectStream, ReplaceRecordLayer) { server_stage.ForwardAll(client_, TlsAgent::STATE_CONNECTING); // This processes the ClientHello and stages the first server flight. client_stage.ForwardAll(server_, TlsAgent::STATE_CONNECTING); + + // In TLS 1.3, this is 0-RTT; in <TLS 1.3, this is application data. + // Neither is acceptable. + RefuseApplicationData(client_, 1); RefuseApplicationData(server_, 1); + if (version_ >= SSL_LIBRARY_VERSION_TLS_1_3) { + // Application data in handshake is never acceptable. + RefuseApplicationData(client_, 2); + RefuseApplicationData(server_, 2); + // Don't accept real data until the handshake is done. + RefuseApplicationData(client_, 3); + RefuseApplicationData(server_, 3); // Process the server flight and the client is done. server_stage.ForwardAll(client_, TlsAgent::STATE_CONNECTED); client_stage.ForwardAll(server_, TlsAgent::STATE_CONNECTED); } else { server_stage.ForwardAll(client_, TlsAgent::STATE_CONNECTING); - RefuseApplicationData(client_, 1); client_stage.ForwardAll(server_, TlsAgent::STATE_CONNECTED); server_stage.ForwardAll(client_, TlsAgent::STATE_CONNECTED); } @@ -382,6 +394,52 @@ TEST_P(TlsConnectStream, ReplaceRecordLayer) { SendForwardReceive(server_, server_stage, client_); } +TEST_F(TlsConnectStreamTls13, ReplaceRecordLayerZeroRtt) { + SetupForZeroRtt(); + + client_->Set0RttEnabled(true); + server_->Set0RttEnabled(true); + StartConnect(); + client_->SetServerKeyBits(server_->server_key_bits()); + + BadPrSocket bad_layer_client(client_); + BadPrSocket bad_layer_server(server_); + + StagedRecords client_stage(client_); + StagedRecords server_stage(server_); + + ExpectResumption(RESUME_TICKET); + + // Send ClientHello + server_stage.ForwardAll(client_, TlsAgent::STATE_CONNECTING); + + // The client can never accept 0-RTT. + RefuseApplicationData(client_, 1); + + // Send some 0-RTT data, which get staged in `client_stage`. + const char* kMsg = "EarlyData"; + const PRInt32 kMsgLen = static_cast<PRInt32>(strlen(kMsg)); + PRInt32 rv = PR_Write(client_->ssl_fd(), kMsg, kMsgLen); + EXPECT_EQ(kMsgLen, rv); + + client_stage.ForwardAll(server_, TlsAgent::STATE_CONNECTING); + + // The server should now have 0-RTT to read. + std::vector<uint8_t> buf(kMsgLen); + rv = PR_Read(server_->ssl_fd(), buf.data(), kMsgLen); + EXPECT_EQ(kMsgLen, rv); + + // The handshake should happily finish. + server_stage.ForwardAll(client_, TlsAgent::STATE_CONNECTED); + client_stage.ForwardAll(server_, TlsAgent::STATE_CONNECTED); + ExpectEarlyDataAccepted(true); + CheckConnected(); + + // Reading and writing application data should work. + SendForwardReceive(client_, client_stage, server_); + SendForwardReceive(server_, server_stage, client_); +} + static SECStatus AuthCompleteBlock(TlsAgent*, PRBool, PRBool) { return SECWouldBlock; } @@ -573,4 +631,49 @@ TEST_F(TlsConnectDatagram13, ForwardDataDtls) { EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError()); } +TEST_F(TlsConnectStreamTls13, SuppressEndOfEarlyData) { + SetupForZeroRtt(); + + client_->Set0RttEnabled(true); + server_->Set0RttEnabled(true); + client_->SetOption(SSL_SUPPRESS_END_OF_EARLY_DATA, true); + server_->SetOption(SSL_SUPPRESS_END_OF_EARLY_DATA, true); + StartConnect(); + client_->SetServerKeyBits(server_->server_key_bits()); + + BadPrSocket bad_layer_client(client_); + BadPrSocket bad_layer_server(server_); + + StagedRecords client_stage(client_); + StagedRecords server_stage(server_); + + ExpectResumption(RESUME_TICKET); + + // Send ClientHello + server_stage.ForwardAll(client_, TlsAgent::STATE_CONNECTING); + + // Send some 0-RTT data, which get staged in `client_stage`. + const char* kMsg = "ABCDEF"; + const PRInt32 kMsgLen = static_cast<PRInt32>(strlen(kMsg)); + PRInt32 rv = PR_Write(client_->ssl_fd(), kMsg, kMsgLen); + EXPECT_EQ(kMsgLen, rv); + + client_stage.ForwardAll(server_, TlsAgent::STATE_CONNECTING); + + // The server should now have 0-RTT to read. + std::vector<uint8_t> buf(kMsgLen); + rv = PR_Read(server_->ssl_fd(), buf.data(), kMsgLen); + EXPECT_EQ(kMsgLen, rv); + + // The handshake should happily finish, without the end of the early data. + server_stage.ForwardAll(client_, TlsAgent::STATE_CONNECTED); + client_stage.ForwardAll(server_, TlsAgent::STATE_CONNECTED); + ExpectEarlyDataAccepted(true); + CheckConnected(); + + // Reading and writing application data should work. + SendForwardReceive(client_, client_stage, server_); + SendForwardReceive(server_, server_stage, client_); +} + } // namespace nss_test diff --git a/security/nss/gtests/ssl_gtest/ssl_recordsize_unittest.cc b/security/nss/gtests/ssl_gtest/ssl_recordsize_unittest.cc index 8989ee801..8926b5551 100644 --- a/security/nss/gtests/ssl_gtest/ssl_recordsize_unittest.cc +++ b/security/nss/gtests/ssl_gtest/ssl_recordsize_unittest.cc @@ -1,4 +1,5 @@ /* -*- 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/. */ @@ -18,7 +19,8 @@ namespace nss_test { // This class tracks the maximum size of record that was sent, both cleartext // and plain. It only tracks records that have an outer type of -// application_data. In TLS 1.3, this includes handshake messages. +// application_data or DTLSCiphertext. In TLS 1.3, this includes handshake +// messages. class TlsRecordMaximum : public TlsRecordFilter { public: TlsRecordMaximum(const std::shared_ptr<TlsAgent>& a) @@ -33,7 +35,7 @@ class TlsRecordMaximum : public TlsRecordFilter { DataBuffer* output) override { std::cerr << "max: " << record << std::endl; // Ignore unprotected packets. - if (header.content_type() != ssl_ct_application_data) { + if (!header.is_protected()) { return KEEP; } @@ -194,9 +196,23 @@ class TlsRecordExpander : public TlsRecordFilter { virtual PacketFilter::Action FilterRecord(const TlsRecordHeader& header, const DataBuffer& data, DataBuffer* changed) { - if (header.content_type() != ssl_ct_application_data) { - return KEEP; + if (!header.is_protected()) { + // We're targeting application_data records. If the record is + // |!is_protected()|, we have two possibilities: + if (!decrypting()) { + // 1) We're not decrypting, in which this case this is truly an + // unencrypted record (Keep). + return KEEP; + } + if (header.content_type() != ssl_ct_application_data) { + // 2) We are decrypting, so is_protected() read the internal + // content_type. If the internal ct IS NOT application_data, then + // it's not our target (Keep). + return KEEP; + } + // Otherwise, the the internal ct IS application_data (Change). } + changed->Allocate(data.len() + expansion_); changed->Write(0, data.data(), data.len()); return CHANGE; @@ -260,30 +276,31 @@ class TlsRecordPadder : public TlsRecordFilter { PacketFilter::Action FilterRecord(const TlsRecordHeader& header, const DataBuffer& record, size_t* offset, DataBuffer* output) override { - if (header.content_type() != ssl_ct_application_data) { + if (!header.is_protected()) { return KEEP; } uint16_t protection_epoch; uint8_t inner_content_type; DataBuffer plaintext; + TlsRecordHeader out_header; if (!Unprotect(header, record, &protection_epoch, &inner_content_type, - &plaintext)) { + &plaintext, &out_header)) { return KEEP; } - if (inner_content_type != ssl_ct_application_data) { + if (decrypting() && inner_content_type != ssl_ct_application_data) { return KEEP; } DataBuffer ciphertext; - bool ok = Protect(spec(protection_epoch), header, inner_content_type, - plaintext, &ciphertext, padding_); + bool ok = Protect(spec(protection_epoch), out_header, inner_content_type, + plaintext, &ciphertext, &out_header, padding_); EXPECT_TRUE(ok); if (!ok) { return KEEP; } - *offset = header.Write(output, *offset, ciphertext); + *offset = out_header.Write(output, *offset, ciphertext); return CHANGE; } diff --git a/security/nss/gtests/ssl_gtest/ssl_renegotiation_unittest.cc b/security/nss/gtests/ssl_gtest/ssl_renegotiation_unittest.cc index d1c718163..072a1836c 100644 --- a/security/nss/gtests/ssl_gtest/ssl_renegotiation_unittest.cc +++ b/security/nss/gtests/ssl_gtest/ssl_renegotiation_unittest.cc @@ -1,4 +1,5 @@ /* -*- 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/. */ diff --git a/security/nss/gtests/ssl_gtest/ssl_resumption_unittest.cc b/security/nss/gtests/ssl_gtest/ssl_resumption_unittest.cc index 38b0a4e79..c41240d8e 100644 --- a/security/nss/gtests/ssl_gtest/ssl_resumption_unittest.cc +++ b/security/nss/gtests/ssl_gtest/ssl_resumption_unittest.cc @@ -1,4 +1,5 @@ /* -*- 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/. */ @@ -836,8 +837,8 @@ TEST_F(TlsConnectTest, TestTls13ResumptionDuplicateNST) { Connect(); // Clear the session ticket keys to invalidate the old ticket. - SSLInt_ClearSelfEncryptKey(); - SSL_SendSessionTicket(server_->ssl_fd(), NULL, 0); + ClearServerCache(); + EXPECT_EQ(SECSuccess, SSL_SendSessionTicket(server_->ssl_fd(), NULL, 0)); SendReceive(); // Need to read so that we absorb the session tickets. CheckKeys(); @@ -884,7 +885,7 @@ TEST_F(TlsConnectTest, TestTls13ResumptionDuplicateNSTWithToken) { Connect(); // Clear the session ticket keys to invalidate the old ticket. - SSLInt_ClearSelfEncryptKey(); + ClearServerCache(); nst_capture->Reset(); uint8_t token[] = {0x20, 0x20, 0xff, 0x00}; EXPECT_EQ(SECSuccess, @@ -914,8 +915,7 @@ 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)); + server_->SetOption(SSL_ENABLE_SESSION_TICKETS, PR_FALSE); auto nst_capture = MakeTlsFilter<TlsHandshakeRecorder>(server_, ssl_hs_new_session_ticket); @@ -943,6 +943,50 @@ TEST_F(TlsConnectTest, SendSessionTicketWithTicketsDisabled) { NstTicketMatchesPskIdentity(nst_capture->buffer(), psk_capture->extension()); } +// Successfully send a session ticket after resuming and then use it. +TEST_F(TlsConnectTest, SendTicketAfterResumption) { + 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); + + // We need to capture just one ticket, so + // disable automatic sending of tickets at the server. + // ConfigureSessionCache enables this option, so revert that. + server_->SetOption(SSL_ENABLE_SESSION_TICKETS, PR_FALSE); + auto nst_capture = + MakeTlsFilter<TlsHandshakeRecorder>(server_, ssl_hs_new_session_ticket); + nst_capture->EnableDecryption(); + Connect(); + + ClearServerCache(); + EXPECT_EQ(SECSuccess, SSL_SendSessionTicket(server_->ssl_fd(), NULL, 0)); + SendReceive(); + + // Reset stats so that the counters for resumptions match up. + ClearStats(); + // Resume again and ensure that we get the same ticket. + 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); @@ -1004,7 +1048,8 @@ TEST_F(TlsConnectStreamTls13, ExternalResumptionUseSecondTicket) { state->invoked++; return SECSuccess; }; - SSL_SetResumptionTokenCallback(client_->ssl_fd(), cb, &ticket_state); + EXPECT_EQ(SECSuccess, SSL_SetResumptionTokenCallback(client_->ssl_fd(), cb, + &ticket_state)); Connect(); EXPECT_EQ(SECSuccess, SSL_SendSessionTicket(server_->ssl_fd(), nullptr, 0)); @@ -1445,4 +1490,34 @@ TEST_F(TlsConnectStreamTls13, ExternalTokenAfterHrr) { SendReceive(); } +TEST_F(TlsConnectStreamTls13, ExternalTokenWithPeerId) { + ConfigureSessionCache(RESUME_BOTH, RESUME_BOTH); + ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3); + EXPECT_EQ(SECSuccess, SSL_SetSockPeerID(client_->ssl_fd(), "testPeerId")); + std::vector<uint8_t> ticket_state; + auto cb = [](PRFileDesc* fd, const PRUint8* ticket, unsigned int ticket_len, + void* arg) -> SECStatus { + EXPECT_NE(0U, ticket_len); + EXPECT_NE(nullptr, ticket); + auto ticket_state_ = reinterpret_cast<std::vector<uint8_t>*>(arg); + ticket_state_->assign(ticket, ticket + ticket_len); + return SECSuccess; + }; + EXPECT_EQ(SECSuccess, SSL_SetResumptionTokenCallback(client_->ssl_fd(), cb, + &ticket_state)); + + Connect(); + SendReceive(); + EXPECT_NE(0U, ticket_state.size()); + + Reset(); + ConfigureSessionCache(RESUME_BOTH, RESUME_BOTH); + EXPECT_EQ(SECSuccess, SSL_SetSockPeerID(client_->ssl_fd(), "testPeerId")); + client_->SetResumptionToken(ticket_state); + ASSERT_TRUE(client_->MaybeSetResumptionToken()); + ExpectResumption(RESUME_TICKET); + Connect(); + SendReceive(); +} + } // namespace nss_test diff --git a/security/nss/gtests/ssl_gtest/ssl_skip_unittest.cc b/security/nss/gtests/ssl_gtest/ssl_skip_unittest.cc index 8ffc6f37f..3ed42e86b 100644 --- a/security/nss/gtests/ssl_gtest/ssl_skip_unittest.cc +++ b/security/nss/gtests/ssl_gtest/ssl_skip_unittest.cc @@ -1,4 +1,5 @@ /* -*- 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/. */ diff --git a/security/nss/gtests/ssl_gtest/ssl_staticrsa_unittest.cc b/security/nss/gtests/ssl_gtest/ssl_staticrsa_unittest.cc index b06c8d943..abddaa5b6 100644 --- a/security/nss/gtests/ssl_gtest/ssl_staticrsa_unittest.cc +++ b/security/nss/gtests/ssl_gtest/ssl_staticrsa_unittest.cc @@ -1,4 +1,5 @@ /* -*- 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/. */ diff --git a/security/nss/gtests/ssl_gtest/ssl_tls13compat_unittest.cc b/security/nss/gtests/ssl_gtest/ssl_tls13compat_unittest.cc index 769413cc9..645f84ff0 100644 --- a/security/nss/gtests/ssl_gtest/ssl_tls13compat_unittest.cc +++ b/security/nss/gtests/ssl_gtest/ssl_tls13compat_unittest.cc @@ -1,4 +1,5 @@ /* -*- 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/. */ @@ -347,6 +348,85 @@ TEST_F(TlsConnectStreamTls13, ChangeCipherSpecBeforeClientHelloTwice) { client_->CheckErrorCode(SSL_ERROR_HANDSHAKE_UNEXPECTED_ALERT); } +// The server accepts a ChangeCipherSpec even if the client advertises +// an empty session ID. +TEST_F(TlsConnectStreamTls13, ChangeCipherSpecAfterClientHelloEmptySid) { + EnsureTlsSetup(); + ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3); + + StartConnect(); + client_->Handshake(); // Send ClientHello + client_->SendDirect(DataBuffer(kCannedCcs, sizeof(kCannedCcs))); // Send CCS + + Handshake(); + CheckConnected(); +} + +// The server rejects multiple ChangeCipherSpec even if the client +// indicates compatibility mode with non-empty session ID. +TEST_F(Tls13CompatTest, ChangeCipherSpecAfterClientHelloTwice) { + EnsureTlsSetup(); + ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3); + EnableCompatMode(); + + StartConnect(); + client_->Handshake(); // Send ClientHello + // Send CCS twice in a row + client_->SendDirect(DataBuffer(kCannedCcs, sizeof(kCannedCcs))); + client_->SendDirect(DataBuffer(kCannedCcs, sizeof(kCannedCcs))); + + server_->ExpectSendAlert(kTlsAlertUnexpectedMessage); + server_->Handshake(); // Consume ClientHello and CCS. + server_->CheckErrorCode(SSL_ERROR_RX_MALFORMED_CHANGE_CIPHER); +} + +// The client accepts a ChangeCipherSpec even if it advertises an empty +// session ID. +TEST_F(TlsConnectStreamTls13, ChangeCipherSpecAfterServerHelloEmptySid) { + EnsureTlsSetup(); + ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3); + + // To replace Finished with a CCS below + auto filter = MakeTlsFilter<TlsHandshakeDropper>(server_); + filter->SetHandshakeTypes({kTlsHandshakeFinished}); + filter->EnableDecryption(); + + StartConnect(); + client_->Handshake(); // Send ClientHello + server_->Handshake(); // Consume ClientHello, and + // send ServerHello..CertificateVerify + // Send CCS + server_->SendDirect(DataBuffer(kCannedCcs, sizeof(kCannedCcs))); + + // No alert is sent from the client. As Finished is dropped, we + // can't use Handshake() and CheckConnected(). + client_->Handshake(); +} + +// The client rejects multiple ChangeCipherSpec in a row even if the +// client indicates compatibility mode with non-empty session ID. +TEST_F(Tls13CompatTest, ChangeCipherSpecAfterServerHelloTwice) { + EnsureTlsSetup(); + ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3); + EnableCompatMode(); + + // To replace Finished with a CCS below + auto filter = MakeTlsFilter<TlsHandshakeDropper>(server_); + filter->SetHandshakeTypes({kTlsHandshakeFinished}); + filter->EnableDecryption(); + + StartConnect(); + client_->Handshake(); // Send ClientHello + server_->Handshake(); // Consume ClientHello, and + // send ServerHello..CertificateVerify + // the ServerHello is followed by CCS + // Send another CCS + server_->SendDirect(DataBuffer(kCannedCcs, sizeof(kCannedCcs))); + client_->ExpectSendAlert(kTlsAlertUnexpectedMessage); + client_->Handshake(); // Consume ClientHello and CCS + client_->CheckErrorCode(SSL_ERROR_RX_MALFORMED_CHANGE_CIPHER); +} + // If we negotiate 1.2, we abort. TEST_F(TlsConnectStreamTls13, ChangeCipherSpecBeforeClientHello12) { EnsureTlsSetup(); @@ -383,14 +463,16 @@ TEST_F(TlsConnectDatagram13, CompatModeDtlsClient) { ASSERT_EQ(2U, client_records->count()); // CH, Fin EXPECT_EQ(ssl_ct_handshake, client_records->record(0).header.content_type()); - EXPECT_EQ(ssl_ct_application_data, - client_records->record(1).header.content_type()); + EXPECT_EQ(kCtDtlsCiphertext, + (client_records->record(1).header.content_type() & + kCtDtlsCiphertextMask)); ASSERT_EQ(6U, server_records->count()); // SH, EE, CT, CV, Fin, Ack EXPECT_EQ(ssl_ct_handshake, server_records->record(0).header.content_type()); for (size_t i = 1; i < server_records->count(); ++i) { - EXPECT_EQ(ssl_ct_application_data, - server_records->record(i).header.content_type()); + EXPECT_EQ(kCtDtlsCiphertext, + (server_records->record(i).header.content_type() & + kCtDtlsCiphertextMask)); } } @@ -439,8 +521,9 @@ TEST_F(TlsConnectDatagram13, CompatModeDtlsServer) { ASSERT_EQ(5U, server_records->count()); // SH, EE, CT, CV, Fin EXPECT_EQ(ssl_ct_handshake, server_records->record(0).header.content_type()); for (size_t i = 1; i < server_records->count(); ++i) { - EXPECT_EQ(ssl_ct_application_data, - server_records->record(i).header.content_type()); + EXPECT_EQ(kCtDtlsCiphertext, + (server_records->record(i).header.content_type() & + kCtDtlsCiphertextMask)); } uint32_t session_id_len = 0; diff --git a/security/nss/gtests/ssl_gtest/ssl_v2_client_hello_unittest.cc b/security/nss/gtests/ssl_gtest/ssl_v2_client_hello_unittest.cc index 657dd70e6..cafbcce68 100644 --- a/security/nss/gtests/ssl_gtest/ssl_v2_client_hello_unittest.cc +++ b/security/nss/gtests/ssl_gtest/ssl_v2_client_hello_unittest.cc @@ -1,4 +1,5 @@ /* -*- 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/. */ diff --git a/security/nss/gtests/ssl_gtest/ssl_version_unittest.cc b/security/nss/gtests/ssl_gtest/ssl_version_unittest.cc index c473fcbcf..419a4052b 100644 --- a/security/nss/gtests/ssl_gtest/ssl_version_unittest.cc +++ b/security/nss/gtests/ssl_gtest/ssl_version_unittest.cc @@ -1,4 +1,5 @@ /* -*- 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/. */ @@ -101,6 +102,61 @@ TEST_F(TlsConnectTest, TestDisableDowngradeDetection) { server_->CheckErrorCode(SSL_ERROR_BAD_HANDSHAKE_HASH_VALUE); } +typedef std::tuple<SSLProtocolVariant, + uint16_t, // client version + uint16_t> // server version + TlsDowngradeProfile; + +class TlsDowngradeTest + : public TlsConnectTestBase, + public ::testing::WithParamInterface<TlsDowngradeProfile> { + public: + TlsDowngradeTest() + : TlsConnectTestBase(std::get<0>(GetParam()), std::get<1>(GetParam())), + c_ver(std::get<1>(GetParam())), + s_ver(std::get<2>(GetParam())) {} + + protected: + const uint16_t c_ver; + const uint16_t s_ver; +}; + +TEST_P(TlsDowngradeTest, TlsDowngradeSentinelTest) { + static const uint8_t tls12_downgrade_random[] = {0x44, 0x4F, 0x57, 0x4E, + 0x47, 0x52, 0x44, 0x01}; + static const uint8_t tls1_downgrade_random[] = {0x44, 0x4F, 0x57, 0x4E, + 0x47, 0x52, 0x44, 0x00}; + static const size_t kRandomLen = 32; + + if (c_ver > s_ver) { + return; + } + + client_->SetVersionRange(c_ver, c_ver); + server_->SetVersionRange(c_ver, s_ver); + + auto sh = MakeTlsFilter<TlsHandshakeRecorder>(server_, ssl_hs_server_hello); + Connect(); + ASSERT_TRUE(sh->buffer().len() > (kRandomLen + 2)); + + const uint8_t* downgrade_sentinel = + sh->buffer().data() + 2 + kRandomLen - sizeof(tls1_downgrade_random); + if (c_ver < s_ver) { + if (c_ver == SSL_LIBRARY_VERSION_TLS_1_2) { + EXPECT_EQ(0, memcmp(downgrade_sentinel, tls12_downgrade_random, + sizeof(tls12_downgrade_random))); + } else { + EXPECT_EQ(0, memcmp(downgrade_sentinel, tls1_downgrade_random, + sizeof(tls1_downgrade_random))); + } + } else { + EXPECT_NE(0, memcmp(downgrade_sentinel, tls12_downgrade_random, + sizeof(tls12_downgrade_random))); + EXPECT_NE(0, memcmp(downgrade_sentinel, tls1_downgrade_random, + sizeof(tls1_downgrade_random))); + } +} + // TLS 1.1 clients do not check the random values, so we should // instead get a handshake failure alert from the server. TEST_F(TlsConnectTest, TestDowngradeDetectionToTls10) { @@ -279,4 +335,82 @@ TEST_F(TlsConnectStreamTls13, Ssl30ClientHelloWithSupportedVersions) { ConnectExpectAlert(server_, kTlsAlertProtocolVersion); } +// Verify the client sends only DTLS versions in supported_versions +TEST_F(DtlsConnectTest, DtlsSupportedVersionsEncoding) { + client_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_1, + SSL_LIBRARY_VERSION_TLS_1_3); + server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_1, + SSL_LIBRARY_VERSION_TLS_1_3); + auto capture = MakeTlsFilter<TlsExtensionCapture>( + client_, ssl_tls13_supported_versions_xtn); + Connect(); + + ASSERT_EQ(7U, capture->extension().len()); + uint32_t version = 0; + ASSERT_TRUE(capture->extension().Read(1, 2, &version)); + EXPECT_EQ(0x7f00 | DTLS_1_3_DRAFT_VERSION, static_cast<int>(version)); + ASSERT_TRUE(capture->extension().Read(3, 2, &version)); + EXPECT_EQ(SSL_LIBRARY_VERSION_DTLS_1_2_WIRE, static_cast<int>(version)); + ASSERT_TRUE(capture->extension().Read(5, 2, &version)); + EXPECT_EQ(SSL_LIBRARY_VERSION_DTLS_1_0_WIRE, static_cast<int>(version)); +} + +// Verify the DTLS 1.3 supported_versions interop workaround. +TEST_F(DtlsConnectTest, Dtls13VersionWorkaround) { + static const uint16_t kExpectVersionsWorkaround[] = { + 0x7f00 | DTLS_1_3_DRAFT_VERSION, SSL_LIBRARY_VERSION_DTLS_1_2_WIRE, + SSL_LIBRARY_VERSION_TLS_1_2, SSL_LIBRARY_VERSION_DTLS_1_0_WIRE, + SSL_LIBRARY_VERSION_TLS_1_1}; + const int min_ver = SSL_LIBRARY_VERSION_TLS_1_1, + max_ver = SSL_LIBRARY_VERSION_TLS_1_3; + + // Toggle the workaround, then verify both encodings are present. + EnsureTlsSetup(); + SSL_SetDtls13VersionWorkaround(client_->ssl_fd(), PR_TRUE); + SSL_SetDtls13VersionWorkaround(client_->ssl_fd(), PR_FALSE); + SSL_SetDtls13VersionWorkaround(client_->ssl_fd(), PR_TRUE); + client_->SetVersionRange(min_ver, max_ver); + server_->SetVersionRange(min_ver, max_ver); + auto capture = MakeTlsFilter<TlsExtensionCapture>( + client_, ssl_tls13_supported_versions_xtn); + Connect(); + + uint32_t version = 0; + size_t off = 1; + ASSERT_EQ(1 + sizeof(kExpectVersionsWorkaround), capture->extension().len()); + for (unsigned int i = 0; i < PR_ARRAY_SIZE(kExpectVersionsWorkaround); i++) { + ASSERT_TRUE(capture->extension().Read(off, 2, &version)); + EXPECT_EQ(kExpectVersionsWorkaround[i], static_cast<uint16_t>(version)); + off += 2; + } +} + +// Verify the client sends only TLS versions in supported_versions +TEST_F(TlsConnectTest, TlsSupportedVersionsEncoding) { + client_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_0, + SSL_LIBRARY_VERSION_TLS_1_3); + server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_0, + SSL_LIBRARY_VERSION_TLS_1_3); + auto capture = MakeTlsFilter<TlsExtensionCapture>( + client_, ssl_tls13_supported_versions_xtn); + Connect(); + + ASSERT_EQ(9U, capture->extension().len()); + uint32_t version = 0; + ASSERT_TRUE(capture->extension().Read(1, 2, &version)); + EXPECT_EQ(SSL_LIBRARY_VERSION_TLS_1_3, static_cast<int>(version)); + ASSERT_TRUE(capture->extension().Read(3, 2, &version)); + EXPECT_EQ(SSL_LIBRARY_VERSION_TLS_1_2, static_cast<int>(version)); + ASSERT_TRUE(capture->extension().Read(5, 2, &version)); + EXPECT_EQ(SSL_LIBRARY_VERSION_TLS_1_1, static_cast<int>(version)); + ASSERT_TRUE(capture->extension().Read(7, 2, &version)); + EXPECT_EQ(SSL_LIBRARY_VERSION_TLS_1_0, static_cast<int>(version)); +} + +INSTANTIATE_TEST_CASE_P( + TlsDowngradeSentinelTest, TlsDowngradeTest, + ::testing::Combine(TlsConnectTestBase::kTlsVariantsStream, + TlsConnectTestBase::kTlsVAll, + TlsConnectTestBase::kTlsV12Plus)); + } // namespace nss_test diff --git a/security/nss/gtests/ssl_gtest/ssl_versionpolicy_unittest.cc b/security/nss/gtests/ssl_gtest/ssl_versionpolicy_unittest.cc index b3acdf9c8..44e685414 100644 --- a/security/nss/gtests/ssl_gtest/ssl_versionpolicy_unittest.cc +++ b/security/nss/gtests/ssl_gtest/ssl_versionpolicy_unittest.cc @@ -1,4 +1,5 @@ /* -*- 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/. */ diff --git a/security/nss/gtests/ssl_gtest/test_io.cc b/security/nss/gtests/ssl_gtest/test_io.cc index c6de3dfe8..4a7f91459 100644 --- a/security/nss/gtests/ssl_gtest/test_io.cc +++ b/security/nss/gtests/ssl_gtest/test_io.cc @@ -1,4 +1,5 @@ /* -*- 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/. */ diff --git a/security/nss/gtests/ssl_gtest/test_io.h b/security/nss/gtests/ssl_gtest/test_io.h index 97093e7f0..e262fb123 100644 --- a/security/nss/gtests/ssl_gtest/test_io.h +++ b/security/nss/gtests/ssl_gtest/test_io.h @@ -1,4 +1,5 @@ /* -*- 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/. */ diff --git a/security/nss/gtests/ssl_gtest/tls_agent.cc b/security/nss/gtests/ssl_gtest/tls_agent.cc index 2ea70cae3..2eafc5bcb 100644 --- a/security/nss/gtests/ssl_gtest/tls_agent.cc +++ b/security/nss/gtests/ssl_gtest/tls_agent.cc @@ -1,4 +1,5 @@ /* -*- 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/. */ @@ -48,6 +49,7 @@ const std::string TlsAgent::kServerEcdhEcdsa = "ecdh_ecdsa"; const std::string TlsAgent::kServerDsa = "dsa"; const std::string TlsAgent::kDelegatorEcdsa256 = "delegator_ecdsa256"; const std::string TlsAgent::kDelegatorRsae2048 = "delegator_rsae2048"; +const std::string TlsAgent::kDelegatorRsaPss2048 = "delegator_rsa_pss2048"; static const uint8_t kCannedTls13ServerHello[] = { 0x03, 0x03, 0x9c, 0xbc, 0x14, 0x9b, 0x0e, 0x2e, 0xfa, 0x0d, 0xf3, @@ -71,8 +73,8 @@ TlsAgent::TlsAgent(const std::string& nm, Role rl, SSLProtocolVariant var) falsestart_enabled_(false), expected_version_(0), expected_cipher_suite_(0), - expect_resumption_(false), expect_client_auth_(false), + expect_psk_(ssl_psk_none), can_falsestart_hook_called_(false), sni_hook_called_(false), auth_certificate_hook_called_(false), @@ -299,7 +301,7 @@ bool TlsAgent::MaybeSetResumptionToken() { // rv is SECFailure with error set to SSL_ERROR_BAD_RESUMPTION_TOKEN_ERROR // if the resumption token was bad (expired/malformed/etc.). - if (expect_resumption_) { + if (expect_psk_ == ssl_psk_resume) { // Only in case we expect resumption this has to be successful. We might // not expect resumption due to some reason but the token is totally fine. EXPECT_EQ(SECSuccess, rv); @@ -307,8 +309,8 @@ bool TlsAgent::MaybeSetResumptionToken() { if (rv != SECSuccess) { EXPECT_EQ(SSL_ERROR_BAD_RESUMPTION_TOKEN_ERROR, PORT_GetError()); resumption_token_.clear(); - EXPECT_FALSE(expect_resumption_); - if (expect_resumption_) return false; + EXPECT_FALSE(expect_psk_ == ssl_psk_resume); + if (expect_psk_ == ssl_psk_resume) return false; } } @@ -632,7 +634,9 @@ void TlsAgent::CheckAuthType(SSLAuthType auth, SSLSignatureScheme sig_scheme) const { EXPECT_EQ(STATE_CONNECTED, state_); EXPECT_EQ(auth, info_.authType); - EXPECT_EQ(server_key_bits_, info_.authKeyBits); + if (auth != ssl_auth_psk) { + EXPECT_EQ(server_key_bits_, info_.authKeyBits); + } if (expected_version_ < SSL_LIBRARY_VERSION_TLS_1_2) { switch (auth) { case ssl_auth_rsa_sign: @@ -683,13 +687,31 @@ void TlsAgent::EnableFalseStart() { SetOption(SSL_ENABLE_FALSE_START, PR_TRUE); } -void TlsAgent::ExpectResumption() { expect_resumption_ = true; } +void TlsAgent::ExpectPsk() { expect_psk_ = ssl_psk_external; } + +void TlsAgent::ExpectResumption() { expect_psk_ = ssl_psk_resume; } void TlsAgent::EnableAlpn(const uint8_t* val, size_t len) { EXPECT_TRUE(EnsureTlsSetup()); EXPECT_EQ(SECSuccess, SSL_SetNextProtoNego(ssl_fd(), val, len)); } +void TlsAgent::AddPsk(const ScopedPK11SymKey& psk, std::string label, + SSLHashType hash, uint16_t zeroRttSuite) { + EXPECT_TRUE(EnsureTlsSetup()); + EXPECT_EQ(SECSuccess, SSL_AddExternalPsk0Rtt( + ssl_fd(), psk.get(), + reinterpret_cast<const uint8_t*>(label.data()), + label.length(), hash, zeroRttSuite, 1000)); +} + +void TlsAgent::RemovePsk(std::string label) { + EXPECT_EQ(SECSuccess, + SSL_RemoveExternalPsk( + ssl_fd(), reinterpret_cast<const uint8_t*>(label.data()), + label.length())); +} + void TlsAgent::CheckAlpn(SSLNextProtoState expected_state, const std::string& expected) const { SSLNextProtoState alpn_state; @@ -819,22 +841,22 @@ void TlsAgent::CheckPreliminaryInfo() { void TlsAgent::CheckCallbacks() const { // If false start happens, the handshake is reported as being complete at the // point that false start happens. - if (expect_resumption_ || !falsestart_enabled_) { + if (expect_psk_ == ssl_psk_resume || !falsestart_enabled_) { EXPECT_TRUE(handshake_callback_called_); } // These callbacks shouldn't fire if we are resuming, except on TLS 1.3. if (role_ == SERVER) { PRBool have_sni = SSLInt_ExtensionNegotiated(ssl_fd(), ssl_server_name_xtn); - EXPECT_EQ(((!expect_resumption_ && have_sni) || + EXPECT_EQ(((expect_psk_ != ssl_psk_resume && have_sni) || expected_version_ >= SSL_LIBRARY_VERSION_TLS_1_3), sni_hook_called_); } else { - EXPECT_EQ(!expect_resumption_, auth_certificate_hook_called_); + EXPECT_EQ(expect_psk_ == ssl_psk_none, auth_certificate_hook_called_); // Note that this isn't unconditionally called, even with false start on. // But the callback is only skipped if a cipher that is ridiculously weak // (80 bits) is chosen. Don't test that: plan to remove bad ciphers. - EXPECT_EQ(falsestart_enabled_ && !expect_resumption_, + EXPECT_EQ(falsestart_enabled_ && expect_psk_ != ssl_psk_resume, can_falsestart_hook_called_); } } @@ -870,7 +892,7 @@ void TlsAgent::ValidateCipherSpecs() { } else { // For DTLS 1.1 and 1.2, the last endpoint to send maintains a cipher spec // until the holddown timer runs down. - if (expect_resumption_) { + if (expect_psk_ == ssl_psk_resume) { if (role_ == CLIENT) { expected = 3; } @@ -908,7 +930,8 @@ void TlsAgent::Connected() { EXPECT_EQ(SECSuccess, rv); EXPECT_EQ(sizeof(info_), info_.length); - EXPECT_EQ(expect_resumption_, info_.resumed == PR_TRUE); + EXPECT_EQ(expect_psk_ == ssl_psk_resume, info_.resumed == PR_TRUE); + EXPECT_EQ(expect_psk_, info_.pskType); // Preliminary values are exposed through callbacks during the handshake. // If either expected values were set or the callbacks were called, check @@ -1063,21 +1086,28 @@ void TlsAgent::SendBuffer(const DataBuffer& buf) { bool TlsAgent::SendEncryptedRecord(const std::shared_ptr<TlsCipherSpec>& spec, uint64_t seq, uint8_t ct, const DataBuffer& buf) { - LOGV("Encrypting " << buf.len() << " bytes"); // Ensure that we are doing TLS 1.3. EXPECT_GE(expected_version_, SSL_LIBRARY_VERSION_TLS_1_3); - TlsRecordHeader header(variant_, expected_version_, ssl_ct_application_data, - seq); + if (variant_ != ssl_variant_datagram) { + ADD_FAILURE(); + return false; + } + + LOGV("Encrypting " << buf.len() << " bytes"); + uint8_t dtls13_ct = kCtDtlsCiphertext | kCtDtlsCiphertext16bSeqno | + kCtDtlsCiphertextLengthPresent; + TlsRecordHeader header(variant_, expected_version_, dtls13_ct, seq); + TlsRecordHeader out_header(header); DataBuffer padded = buf; padded.Write(padded.len(), ct, 1); DataBuffer ciphertext; - if (!spec->Protect(header, padded, &ciphertext)) { + if (!spec->Protect(header, padded, &ciphertext, &out_header)) { return false; } DataBuffer record; - auto rv = header.Write(&record, 0, ciphertext); - EXPECT_EQ(header.header_length() + ciphertext.len(), rv); + auto rv = out_header.Write(&record, 0, ciphertext); + EXPECT_EQ(out_header.header_length() + ciphertext.len(), rv); SendDirect(record); return true; } @@ -1124,7 +1154,7 @@ void TlsAgent::ReadBytes(size_t amount) { } } -void TlsAgent::ResetSentBytes() { send_ctr_ = 0; } +void TlsAgent::ResetSentBytes(size_t bytes) { send_ctr_ = bytes; } void TlsAgent::SetOption(int32_t option, int value) { ASSERT_TRUE(EnsureTlsSetup()); @@ -1201,16 +1231,26 @@ void TlsAgentTestBase::MakeRecord(SSLProtocolVariant variant, uint8_t type, uint16_t version, const uint8_t* buf, size_t len, DataBuffer* out, uint64_t sequence_number) { + // Fixup the content type for DTLSCiphertext + if (variant == ssl_variant_datagram && + version >= SSL_LIBRARY_VERSION_TLS_1_3 && + type == ssl_ct_application_data) { + type = kCtDtlsCiphertext | kCtDtlsCiphertext16bSeqno | + kCtDtlsCiphertextLengthPresent; + } + size_t index = 0; - index = out->Write(index, type, 1); if (variant == ssl_variant_stream) { + index = out->Write(index, type, 1); index = out->Write(index, version, 2); } else if (version >= SSL_LIBRARY_VERSION_TLS_1_3 && - type == ssl_ct_application_data) { + (type & kCtDtlsCiphertextMask) == kCtDtlsCiphertext) { uint32_t epoch = (sequence_number >> 48) & 0x3; - uint32_t seqno = sequence_number & ((1ULL << 30) - 1); - index = out->Write(index, (epoch << 30) | seqno, 4); + index = out->Write(index, type | epoch, 1); + uint32_t seqno = sequence_number & ((1ULL << 16) - 1); + index = out->Write(index, seqno, 2); } else { + index = out->Write(index, type, 1); index = out->Write(index, TlsVersionToDtlsVersion(version), 2); index = out->Write(index, sequence_number >> 32, 4); index = out->Write(index, sequence_number & PR_UINT32_MAX, 4); diff --git a/security/nss/gtests/ssl_gtest/tls_agent.h b/security/nss/gtests/ssl_gtest/tls_agent.h index 5385a6173..f9bb26aee 100644 --- a/security/nss/gtests/ssl_gtest/tls_agent.h +++ b/security/nss/gtests/ssl_gtest/tls_agent.h @@ -1,4 +1,5 @@ /* -*- 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/. */ @@ -8,6 +9,7 @@ #include "prio.h" #include "ssl.h" +#include "sslproto.h" #include <functional> #include <iostream> @@ -75,8 +77,9 @@ class TlsAgent : public PollTarget { static const std::string kServerEcdhEcdsa; static const std::string kServerEcdhRsa; static const std::string kServerDsa; - static const std::string kDelegatorEcdsa256; // draft-ietf-tls-subcerts - static const std::string kDelegatorRsae2048; // draft-ietf-tls-subcerts + static const std::string kDelegatorEcdsa256; // draft-ietf-tls-subcerts + static const std::string kDelegatorRsae2048; // draft-ietf-tls-subcerts + static const std::string kDelegatorRsaPss2048; // draft-ietf-tls-subcerts TlsAgent(const std::string& name, Role role, SSLProtocolVariant variant); virtual ~TlsAgent(); @@ -155,6 +158,7 @@ class TlsAgent : public PollTarget { void SetServerKeyBits(uint16_t bits); void ExpectReadWriteError(); void EnableFalseStart(); + void ExpectPsk(); void ExpectResumption(); void SkipVersionChecks(); void SetSignatureSchemes(const SSLSignatureScheme* schemes, size_t count); @@ -174,8 +178,11 @@ class TlsAgent : public PollTarget { // Send data directly to the underlying socket, skipping the TLS layer. void SendDirect(const DataBuffer& buf); void SendRecordDirect(const TlsRecord& record); + void AddPsk(const ScopedPK11SymKey& psk, std::string label, SSLHashType hash, + uint16_t zeroRttSuite = TLS_NULL_WITH_NULL_NULL); + void RemovePsk(std::string label); void ReadBytes(size_t max = 16384U); - void ResetSentBytes(); // Hack to test drops. + void ResetSentBytes(size_t bytes = 0); // Hack to test drops. void EnableExtendedMasterSecret(); void CheckExtendedMasterSecret(bool expected); void CheckEarlyDataAccepted(bool expected); @@ -246,6 +253,8 @@ class TlsAgent : public PollTarget { return true; } + void expected_cipher_suite(uint16_t suite) { expected_cipher_suite_ = suite; } + std::string cipher_suite_name() const { if (state_ != STATE_CONNECTED) return "UNKNOWN"; @@ -416,8 +425,8 @@ class TlsAgent : public PollTarget { bool falsestart_enabled_; uint16_t expected_version_; uint16_t expected_cipher_suite_; - bool expect_resumption_; bool expect_client_auth_; + SSLPskType expect_psk_; bool can_falsestart_hook_called_; bool sni_hook_called_; bool auth_certificate_hook_called_; diff --git a/security/nss/gtests/ssl_gtest/tls_connect.cc b/security/nss/gtests/ssl_gtest/tls_connect.cc index 8da5b57ac..9b7f9b6d8 100644 --- a/security/nss/gtests/ssl_gtest/tls_connect.cc +++ b/security/nss/gtests/ssl_gtest/tls_connect.cc @@ -1,4 +1,5 @@ /* -*- 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/. */ @@ -106,7 +107,7 @@ std::string VersionString(uint16_t version) { } // The default anti-replay window for tests. Tests that rely on a different -// value call SSL_InitAntiReplay directly. +// value call ResetAntiReplay directly. static PRTime kAntiReplayWindow = 100 * PR_USEC_PER_SEC; TlsConnectTestBase::TlsConnectTestBase(SSLProtocolVariant variant, @@ -400,6 +401,15 @@ void TlsConnectTestBase::CheckConnected() { server_->CheckSecretsDestroyed(); } +void TlsConnectTestBase::CheckEarlyDataLimit( + const std::shared_ptr<TlsAgent>& agent, size_t expected_size) { + SSLPreliminaryChannelInfo preinfo; + SECStatus rv = + SSL_GetPreliminaryChannelInfo(agent->ssl_fd(), &preinfo, sizeof(preinfo)); + EXPECT_EQ(SECSuccess, rv); + EXPECT_EQ(expected_size, static_cast<size_t>(preinfo.maxEarlyDataSize)); +} + void TlsConnectTestBase::CheckKeys(SSLKEAType kea_type, SSLNamedGroup kea_group, SSLAuthType auth_type, SSLSignatureScheme sig_scheme) const { @@ -519,6 +529,14 @@ void TlsConnectTestBase::SetExpectedVersion(uint16_t version) { server_->SetExpectedVersion(version); } +void TlsConnectTestBase::AddPsk(const ScopedPK11SymKey& psk, std::string label, + SSLHashType hash, uint16_t zeroRttSuite) { + client_->AddPsk(psk, label, hash, zeroRttSuite); + server_->AddPsk(psk, label, hash, zeroRttSuite); + client_->ExpectPsk(); + server_->ExpectPsk(); +} + void TlsConnectTestBase::DisableAllCiphers() { EnsureTlsSetup(); client_->DisableAllCiphers(); @@ -755,7 +773,7 @@ void TlsConnectTestBase::ZeroRttSendReceive( << "Unexpected error: " << PORT_ErrorToName(PORT_GetError()); } - // Do a second read. this should fail. + // Do a second read. This should fail. rv = PR_Read(server_->ssl_fd(), buf.data(), k0RttDataLen); EXPECT_EQ(SECFailure, rv); EXPECT_EQ(PR_WOULD_BLOCK_ERROR, PORT_GetError()); diff --git a/security/nss/gtests/ssl_gtest/tls_connect.h b/security/nss/gtests/ssl_gtest/tls_connect.h index 55ba571ef..3a43d6bca 100644 --- a/security/nss/gtests/ssl_gtest/tls_connect.h +++ b/security/nss/gtests/ssl_gtest/tls_connect.h @@ -1,4 +1,5 @@ /* -*- 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/. */ @@ -79,6 +80,8 @@ class TlsConnectTestBase : public ::testing::Test { void ConnectExpectAlert(std::shared_ptr<TlsAgent>& sender, uint8_t alert); void ConnectExpectFailOneSide(TlsAgent::Role failingSide); void ConnectWithCipherSuite(uint16_t cipher_suite); + void CheckEarlyDataLimit(const std::shared_ptr<TlsAgent>& agent, + size_t expected_size); // Check that the keys used in the handshake match expectations. void CheckKeys(SSLKEAType kea_type, SSLNamedGroup kea_group, SSLAuthType auth_type, SSLSignatureScheme sig_scheme) const; @@ -119,6 +122,9 @@ class TlsConnectTestBase : public ::testing::Test { void EnableSrtp(); void CheckSrtp() const; void SendReceive(size_t total = 50); + void AddPsk(const ScopedPK11SymKey& psk, std::string label, SSLHashType hash, + uint16_t zeroRttSuite = TLS_NULL_WITH_NULL_NULL); + void RemovePsk(std::string label); void SetupForZeroRtt(); void SetupForResume(); void ZeroRttSendReceive( diff --git a/security/nss/gtests/ssl_gtest/tls_esni_unittest.cc b/security/nss/gtests/ssl_gtest/tls_esni_unittest.cc index dfc4f4e7f..0a02d0683 100644 --- a/security/nss/gtests/ssl_gtest/tls_esni_unittest.cc +++ b/security/nss/gtests/ssl_gtest/tls_esni_unittest.cc @@ -1,4 +1,5 @@ /* -*- 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/. */ @@ -313,12 +314,20 @@ TEST_P(TlsConnectTls13, ConnectEsniHrr) { MakeTlsFilter<TlsExtensionCapture>(client_, ssl_server_name_xtn); auto filter2 = MakeTlsFilter<TlsExtensionCapture>(client_, ssl_server_name_xtn, true); + auto efilter = + MakeTlsFilter<TlsExtensionCapture>(client_, ssl_tls13_encrypted_sni_xtn); + auto efilter2 = MakeTlsFilter<TlsExtensionCapture>( + client_, ssl_tls13_encrypted_sni_xtn, true); + client_->SetFilter(std::make_shared<ChainedPacketFilter>( - ChainedPacketFilterInit({filter, filter2}))); + ChainedPacketFilterInit({filter, filter2, efilter, efilter2}))); server_->SetSniCallback(SniCallback); Connect(); CheckSniExtension(filter->extension()); CheckSniExtension(filter2->extension()); + ASSERT_TRUE(efilter->captured()); + ASSERT_TRUE(efilter2->captured()); + ASSERT_NE(efilter->extension(), efilter2->extension()); EXPECT_NE(0UL, hrr_capture->buffer().len()); } diff --git a/security/nss/gtests/ssl_gtest/tls_filter.cc b/security/nss/gtests/ssl_gtest/tls_filter.cc index 8be2a70b7..074a8bf29 100644 --- a/security/nss/gtests/ssl_gtest/tls_filter.cc +++ b/security/nss/gtests/ssl_gtest/tls_filter.cc @@ -1,4 +1,5 @@ /* -*- 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/. */ @@ -119,6 +120,10 @@ bool TlsRecordFilter::is_dtls13() const { info.canSendEarlyData; } +bool TlsRecordFilter::is_dtls13_ciphertext(uint8_t ct) const { + return is_dtls13() && (ct & kCtDtlsCiphertextMask) == kCtDtlsCiphertext; +} + // Gets the cipher spec that matches the specified epoch. TlsCipherSpec& TlsRecordFilter::spec(uint16_t write_epoch) { for (auto& sp : cipher_specs_) { @@ -195,23 +200,24 @@ PacketFilter::Action TlsRecordFilter::FilterRecord( uint8_t inner_content_type; DataBuffer plaintext; uint16_t protection_epoch = 0; + TlsRecordHeader out_header(header); if (!Unprotect(header, record, &protection_epoch, &inner_content_type, - &plaintext)) { + &plaintext, &out_header)) { std::cerr << agent()->role_str() << ": unprotect failed: " << header << ":" << record << std::endl; return KEEP; } auto& protection_spec = spec(protection_epoch); - TlsRecordHeader real_header(header.variant(), header.version(), - inner_content_type, header.sequence_number()); + TlsRecordHeader real_header(out_header.variant(), out_header.version(), + inner_content_type, out_header.sequence_number()); PacketFilter::Action action = FilterRecord(real_header, plaintext, &filtered); // In stream mode, even if something doesn't change we need to re-encrypt if // previous packets were dropped. if (action == KEEP) { - if (header.is_dtls() || !protection_spec.record_dropped()) { + if (out_header.is_dtls() || !protection_spec.record_dropped()) { // Count every outgoing packet. protection_spec.RecordProtected(); return KEEP; @@ -220,7 +226,7 @@ PacketFilter::Action TlsRecordFilter::FilterRecord( } if (action == DROP) { - std::cerr << "record drop: " << header << ":" << record << std::endl; + std::cerr << "record drop: " << out_header << ":" << record << std::endl; protection_spec.RecordDropped(); return DROP; } @@ -232,17 +238,15 @@ PacketFilter::Action TlsRecordFilter::FilterRecord( } uint64_t seq_num = protection_spec.next_out_seqno(); - if (!decrypting_ && header.is_dtls()) { + if (!decrypting_ && out_header.is_dtls()) { // Copy over the epoch, which isn't tracked when not decrypting. - seq_num |= header.sequence_number() & (0xffffULL << 48); + seq_num |= out_header.sequence_number() & (0xffffULL << 48); } - - TlsRecordHeader out_header(header.variant(), header.version(), - header.content_type(), seq_num); + out_header.sequence_number(seq_num); DataBuffer ciphertext; bool rv = Protect(protection_spec, out_header, inner_content_type, filtered, - &ciphertext); + &ciphertext, &out_header); if (!rv) { return KEEP; } @@ -261,19 +265,72 @@ size_t TlsRecordHeader::header_length() const { return WriteHeader(&buf, 0, 0); } -uint64_t TlsRecordHeader::RecoverSequenceNumber(uint64_t expected, +bool TlsRecordHeader::MaskSequenceNumber() { + return MaskSequenceNumber(sn_mask()); +} + +bool TlsRecordHeader::MaskSequenceNumber(const DataBuffer& mask_buf) { + if (mask_buf.empty()) { + return false; + } + + DataBuffer mask; + if (is_dtls13_ciphertext()) { + uint64_t seqno = sequence_number(); + uint8_t len = content_type() & kCtDtlsCiphertext16bSeqno ? 2 : 1; + uint16_t seqno_bitmask = (1 << len * 8) - 1; + DataBuffer val; + if (val.Write(0, seqno & seqno_bitmask, len) != len) { + return false; + } + +#ifdef UNSAFE_FUZZER_MODE + // Use a null mask. + mask.Allocate(mask_buf.len()); +#endif + mask.Append(mask_buf); + val.data()[0] ^= mask.data()[0]; + if (len == 2 && mask.len() > 1) { + val.data()[1] ^= mask.data()[1]; + } + uint32_t tmp; + if (!val.Read(0, len, &tmp)) { + return false; + } + + seqno = (seqno & ~seqno_bitmask) | tmp; + seqno_is_masked_ = !seqno_is_masked_; + if (!seqno_is_masked_) { + seqno = ParseSequenceNumber(guess_seqno_, seqno, len * 8, 2); + } + sequence_number_ = seqno; + + // Now update the header bytes + if (header_.len() > 1) { + header_.data()[1] ^= mask.data()[0]; + if ((content_type() & kCtDtlsCiphertext16bSeqno) && header().len() > 2) { + header_.data()[2] ^= mask.data()[1]; + } + } + } + + sn_mask_ = mask; + return true; +} + +uint64_t TlsRecordHeader::RecoverSequenceNumber(uint64_t guess_seqno, uint32_t partial, size_t partial_bits) { EXPECT_GE(32U, partial_bits); uint64_t mask = (1ULL << partial_bits) - 1; // First we determine the highest possible value. This is half the - // expressible range above the expected value, less 1. + // expressible range above the expected value (|guess_seqno|), less 1. // // We subtract the extra 1 from the cap so that when given a choice between // the equidistant expected+N and expected-N we want to chose the lower. With // 0-RTT, we sometimes have to recover an epoch of 1 when we expect an epoch // of 3 and with 2 partial bits, the alternative result of 5 is wrong. - uint64_t cap = expected + (1ULL << (partial_bits - 1)) - 1; + uint64_t cap = guess_seqno + (1ULL << (partial_bits - 1)) - 1; // Add the partial piece in. e.g., xxxx789a and 1234 becomes xxxx1234. uint64_t seq_no = (cap & ~mask) | partial; // If the partial value is higher than the same partial piece from the cap, @@ -285,15 +342,18 @@ uint64_t TlsRecordHeader::RecoverSequenceNumber(uint64_t expected, } // Determine the full epoch and sequence number from an expected and raw value. -// The expected and output values are packed as they are in DTLS 1.2 and -// earlier: with 16 bits of epoch and 48 bits of sequence number. -uint64_t TlsRecordHeader::ParseSequenceNumber(uint64_t expected, uint32_t raw, +// The expected, raw, and output values are packed as they are in DTLS 1.2 and +// earlier: with 16 bits of epoch and 48 bits of sequence number. The raw value +// is packed this way (even before recovery) so that we don't need to track a +// moving value between two calls (one to recover the epoch, and one after +// unmasking to recover the sequence number). +uint64_t TlsRecordHeader::ParseSequenceNumber(uint64_t expected, uint64_t raw, size_t seq_no_bits, size_t epoch_bits) { uint64_t epoch_mask = (1ULL << epoch_bits) - 1; - uint64_t epoch = RecoverSequenceNumber( - expected >> 48, (raw >> seq_no_bits) & epoch_mask, epoch_bits); - if (epoch > (expected >> 48)) { + uint64_t ep = RecoverSequenceNumber(expected >> 48, (raw >> 48) & epoch_mask, + epoch_bits); + if (ep > (expected >> 48)) { // If the epoch has changed, reset the expected sequence number. expected = 0; } else { @@ -301,9 +361,12 @@ uint64_t TlsRecordHeader::ParseSequenceNumber(uint64_t expected, uint32_t raw, expected &= (1ULL << 48) - 1; } uint64_t seq_no_mask = (1ULL << seq_no_bits) - 1; - uint64_t seq_no = - RecoverSequenceNumber(expected, raw & seq_no_mask, seq_no_bits); - return (epoch << 48) | seq_no; + uint64_t seq_no = (raw & seq_no_mask); + if (!seqno_is_masked_) { + seq_no = RecoverSequenceNumber(expected, seq_no, seq_no_bits); + } + + return (ep << 48) | seq_no; } bool TlsRecordHeader::Parse(bool is_dtls13, uint64_t seqno, TlsParser* parser, @@ -319,38 +382,47 @@ bool TlsRecordHeader::Parse(bool is_dtls13, uint64_t seqno, TlsParser* parser, version_ = SSL_LIBRARY_VERSION_TLS_1_3; #ifndef UNSAFE_FUZZER_MODE - // Deal with the 7 octet header. - if (content_type_ == ssl_ct_application_data) { + // Deal with the DTLSCipherText header. + if (is_dtls13_ciphertext()) { + uint8_t seq_no_bytes = + (content_type_ & kCtDtlsCiphertext16bSeqno) ? 2 : 1; uint32_t tmp; - if (!parser->Read(&tmp, 4)) { - return false; - } - sequence_number_ = ParseSequenceNumber(seqno, tmp, 30, 2); - if (!parser->ReadFromMark(&header_, parser->consumed() + 2 - mark, - mark)) { + + if (!parser->Read(&tmp, seq_no_bytes)) { return false; } - return parser->ReadVariable(body, 2); - } - // The short, 2 octet header. - if ((content_type_ & 0xe0) == 0x20) { - uint32_t tmp; - if (!parser->Read(&tmp, 1)) { - return false; + // Store the guess if masked. If and when seqno_bytesenceNumber is called, + // the value will be unmasked and recovered. This assumes we only call + // Parse() on headers containing masked values. + seqno_is_masked_ = true; + guess_seqno_ = seqno; + uint64_t ep = content_type_ & 0x03; + sequence_number_ = (ep << 48) | tmp; + + // Recover the full epoch. Note the sequence number portion holds the + // masked value until a call to Mask() reveals it (as indicated by + // |seqno_is_masked_|). + sequence_number_ = + ParseSequenceNumber(seqno, sequence_number_, seq_no_bytes * 8, 2); + + uint32_t len_bytes = + (content_type_ & kCtDtlsCiphertextLengthPresent) ? 2 : 0; + if (len_bytes) { + if (!parser->Read(&tmp, 2)) { + return false; + } } - // Need to use the low 5 bits of the first octet too. - tmp |= (content_type_ & 0x1f) << 8; - content_type_ = ssl_ct_application_data; - sequence_number_ = ParseSequenceNumber(seqno, tmp, 12, 1); if (!parser->ReadFromMark(&header_, parser->consumed() - mark, mark)) { return false; } - return parser->Read(body, parser->remaining()); + + return len_bytes ? parser->Read(body, tmp) + : parser->Read(body, parser->remaining()); } - // The full 13 octet header can only be used for a few types. + // The full DTLSPlainText header can only be used for a few types. EXPECT_TRUE(content_type_ == ssl_ct_alert || content_type_ == ssl_ct_handshake || content_type_ == ssl_ct_ack); @@ -388,15 +460,20 @@ bool TlsRecordHeader::Parse(bool is_dtls13, uint64_t seqno, TlsParser* parser, size_t TlsRecordHeader::WriteHeader(DataBuffer* buffer, size_t offset, size_t body_len) const { - offset = buffer->Write(offset, content_type_, 1); - if (is_dtls() && version_ >= SSL_LIBRARY_VERSION_TLS_1_3 && - content_type() == ssl_ct_application_data) { + if (is_dtls13_ciphertext()) { + uint8_t seq_no_bytes = (content_type_ & kCtDtlsCiphertext16bSeqno) ? 2 : 1; // application_data records in TLS 1.3 have a different header format. - // Always use the long header here for simplicity. uint32_t e = (sequence_number_ >> 48) & 0x3; - uint32_t seqno = sequence_number_ & ((1ULL << 30) - 1); - offset = buffer->Write(offset, (e << 30) | seqno, 4); + uint32_t seqno = sequence_number_ & ((1ULL << seq_no_bytes * 8) - 1); + uint8_t new_content_type_ = content_type_ | e; + offset = buffer->Write(offset, new_content_type_, 1); + offset = buffer->Write(offset, seqno, seq_no_bytes); + + if (content_type_ & kCtDtlsCiphertextLengthPresent) { + offset = buffer->Write(offset, body_len, 2); + } } else { + offset = buffer->Write(offset, content_type_, 1); uint16_t v = is_dtls() ? TlsVersionToDtlsVersion(version_) : version_; offset = buffer->Write(offset, v, 2); if (is_dtls()) { @@ -404,8 +481,9 @@ size_t TlsRecordHeader::WriteHeader(DataBuffer* buffer, size_t offset, offset = buffer->Write(offset, sequence_number_ >> 32, 4); offset = buffer->Write(offset, sequence_number_ & 0xffffffff, 4); } + offset = buffer->Write(offset, body_len, 2); } - offset = buffer->Write(offset, body_len, 2); + return offset; } @@ -420,8 +498,9 @@ bool TlsRecordFilter::Unprotect(const TlsRecordHeader& header, const DataBuffer& ciphertext, uint16_t* protection_epoch, uint8_t* inner_content_type, - DataBuffer* plaintext) { - if (!decrypting_ || header.content_type() != ssl_ct_application_data) { + DataBuffer* plaintext, + TlsRecordHeader* out_header) { + if (!decrypting_ || !header.is_protected()) { // Maintain the epoch and sequence number for plaintext records. uint16_t ep = 0; if (agent()->variant() == ssl_variant_datagram) { @@ -437,7 +516,7 @@ bool TlsRecordFilter::Unprotect(const TlsRecordHeader& header, uint16_t ep = 0; if (agent()->variant() == ssl_variant_datagram) { ep = static_cast<uint16_t>(header.sequence_number() >> 48); - if (!spec(ep).Unprotect(header, ciphertext, plaintext)) { + if (!spec(ep).Unprotect(header, ciphertext, plaintext, out_header)) { return false; } } else { @@ -445,7 +524,8 @@ bool TlsRecordFilter::Unprotect(const TlsRecordHeader& header, // can't just use the newest keys because the same flight of messages can // contain multiple epochs. So... trial decrypt! for (size_t i = cipher_specs_.size() - 1; i > 0; --i) { - if (cipher_specs_[i].Unprotect(header, ciphertext, plaintext)) { + if (cipher_specs_[i].Unprotect(header, ciphertext, plaintext, + out_header)) { ep = cipher_specs_[i].epoch(); break; } @@ -480,7 +560,8 @@ bool TlsRecordFilter::Protect(TlsCipherSpec& protection_spec, const TlsRecordHeader& header, uint8_t inner_content_type, const DataBuffer& plaintext, - DataBuffer* ciphertext, size_t padding) { + DataBuffer* ciphertext, + TlsRecordHeader* out_header, size_t padding) { if (!protection_spec.is_protected()) { // Not protected, just keep the sequence numbers updated. protection_spec.RecordProtected(); @@ -493,7 +574,7 @@ bool TlsRecordFilter::Protect(TlsCipherSpec& protection_spec, size_t offset = padded.Write(0, plaintext.data(), plaintext.len()); padded.Write(offset, inner_content_type, 1); - bool ok = protection_spec.Protect(header, padded, ciphertext); + bool ok = protection_spec.Protect(header, padded, ciphertext, out_header); if (!ok) { ADD_FAILURE() << "protect fail"; } else if (g_ssl_gtest_verbose) { @@ -1066,15 +1147,14 @@ PacketFilter::Action SelectedCipherSuiteReplacer::FilterHandshake( *output = input; uint32_t temp = 0; EXPECT_TRUE(input.Read(0, 2, &temp)); - // Cipher suite is after version(2) and random(32). + EXPECT_EQ(header.version(), NormalizeTlsVersion(temp)); + // Cipher suite is after version(2), random(32) + // and [legacy_]session_id(<0..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; - } + EXPECT_TRUE(input.Read(pos, 1, &temp)); + pos += 1 + temp; + output->Write(pos, static_cast<uint32_t>(cipher_suite_), 2); return CHANGE; } - } // namespace nss_test diff --git a/security/nss/gtests/ssl_gtest/tls_filter.h b/security/nss/gtests/ssl_gtest/tls_filter.h index 93e5bad02..5300075ea 100644 --- a/security/nss/gtests/ssl_gtest/tls_filter.h +++ b/security/nss/gtests/ssl_gtest/tls_filter.h @@ -1,4 +1,5 @@ /* -*- 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/. */ @@ -11,6 +12,7 @@ #include <set> #include <vector> #include "sslt.h" +#include "sslproto.h" #include "test_io.h" #include "tls_agent.h" #include "tls_parser.h" @@ -24,6 +26,59 @@ namespace nss_test { class TlsCipherSpec; +class TlsSendCipherSpecCapturer { + public: + TlsSendCipherSpecCapturer(const std::shared_ptr<TlsAgent>& agent) + : agent_(agent), send_cipher_specs_() { + EXPECT_EQ(SECSuccess, + SSL_SecretCallback(agent_->ssl_fd(), SecretCallback, this)); + } + + std::shared_ptr<TlsCipherSpec> spec(size_t i) { + if (i >= send_cipher_specs_.size()) { + return nullptr; + } + return send_cipher_specs_[i]; + } + + private: + static void SecretCallback(PRFileDesc* fd, PRUint16 epoch, + SSLSecretDirection dir, PK11SymKey* secret, + void* arg) { + auto self = static_cast<TlsSendCipherSpecCapturer*>(arg); + std::cerr << self->agent_->role_str() << ": capture " << dir + << " secret for epoch " << epoch << std::endl; + + if (dir == ssl_secret_read) { + return; + } + + SSLPreliminaryChannelInfo preinfo; + EXPECT_EQ(SECSuccess, + SSL_GetPreliminaryChannelInfo(self->agent_->ssl_fd(), &preinfo, + sizeof(preinfo))); + EXPECT_EQ(sizeof(preinfo), preinfo.length); + EXPECT_TRUE(preinfo.valuesSet & ssl_preinfo_cipher_suite); + + // Check the version: + EXPECT_TRUE(preinfo.valuesSet & ssl_preinfo_version); + ASSERT_GE(SSL_LIBRARY_VERSION_TLS_1_3, preinfo.protocolVersion); + + SSLCipherSuiteInfo cipherinfo; + EXPECT_EQ(SECSuccess, + SSL_GetCipherSuiteInfo(preinfo.cipherSuite, &cipherinfo, + sizeof(cipherinfo))); + EXPECT_EQ(sizeof(cipherinfo), cipherinfo.length); + + auto spec = std::make_shared<TlsCipherSpec>(true, epoch); + EXPECT_TRUE(spec->SetKeys(&cipherinfo, secret)); + self->send_cipher_specs_.push_back(spec); + } + + std::shared_ptr<TlsAgent> agent_; + std::vector<std::shared_ptr<TlsCipherSpec>> send_cipher_specs_; +}; + class TlsVersioned { public: TlsVersioned() : variant_(ssl_variant_stream), version_(0) {} @@ -44,22 +99,57 @@ class TlsVersioned { class TlsRecordHeader : public TlsVersioned { public: TlsRecordHeader() - : TlsVersioned(), content_type_(0), sequence_number_(0), header_() {} + : TlsVersioned(), + content_type_(0), + guess_seqno_(0), + seqno_is_masked_(false), + sequence_number_(0), + header_() {} TlsRecordHeader(SSLProtocolVariant var, uint16_t ver, uint8_t ct, uint64_t seqno) : TlsVersioned(var, ver), content_type_(ct), + guess_seqno_(0), + seqno_is_masked_(false), sequence_number_(seqno), - header_() {} + header_(), + sn_mask_() {} + + bool is_protected() const { + // *TLS < 1.3 + if (version() < SSL_LIBRARY_VERSION_TLS_1_3 && + content_type() == ssl_ct_application_data) { + return true; + } + + // TLS 1.3 + if (!is_dtls() && version() >= SSL_LIBRARY_VERSION_TLS_1_3 && + content_type() == ssl_ct_application_data) { + return true; + } + + // DTLS 1.3 + return is_dtls13_ciphertext(); + } uint8_t content_type() const { return content_type_; } - uint64_t sequence_number() const { return sequence_number_; } uint16_t epoch() const { return static_cast<uint16_t>(sequence_number_ >> 48); } + uint64_t sequence_number() const { return sequence_number_; } + void sequence_number(uint64_t seqno) { sequence_number_ = seqno; } + const DataBuffer& sn_mask() const { return sn_mask_; } + bool is_dtls13_ciphertext() const { + return is_dtls() && (version() >= SSL_LIBRARY_VERSION_TLS_1_3) && + (content_type() & kCtDtlsCiphertextMask) == kCtDtlsCiphertext; + } + size_t header_length() const; const DataBuffer& header() const { return header_; } + bool MaskSequenceNumber(); + bool MaskSequenceNumber(const DataBuffer& mask_buf); + // Parse the header; return true if successful; body in an outparam if OK. bool Parse(bool is_dtls13, uint64_t sequence_number, TlsParser* parser, DataBuffer* body); @@ -69,14 +159,17 @@ class TlsRecordHeader : public TlsVersioned { size_t WriteHeader(DataBuffer* buffer, size_t offset, size_t body_len) const; private: - static uint64_t RecoverSequenceNumber(uint64_t expected, uint32_t partial, + static uint64_t RecoverSequenceNumber(uint64_t guess_seqno, uint32_t partial, size_t partial_bits); - static uint64_t ParseSequenceNumber(uint64_t expected, uint32_t raw, - size_t seq_no_bits, size_t epoch_bits); + uint64_t ParseSequenceNumber(uint64_t expected, uint64_t raw, + size_t seq_no_bits, size_t epoch_bits); uint8_t content_type_; + uint64_t guess_seqno_; + bool seqno_is_masked_; uint64_t sequence_number_; DataBuffer header_; + DataBuffer sn_mask_; }; struct TlsRecord { @@ -110,12 +203,14 @@ class TlsRecordFilter : public PacketFilter { // Enabling it for lower version tests will cause undefined // behavior. void EnableDecryption(); + bool decrypting() const { return decrypting_; }; bool Unprotect(const TlsRecordHeader& header, const DataBuffer& cipherText, uint16_t* protection_epoch, uint8_t* inner_content_type, - DataBuffer* plaintext); + DataBuffer* plaintext, TlsRecordHeader* out_header); bool Protect(TlsCipherSpec& protection_spec, const TlsRecordHeader& header, uint8_t inner_content_type, const DataBuffer& plaintext, - DataBuffer* ciphertext, size_t padding = 0); + DataBuffer* ciphertext, TlsRecordHeader* out_header, + size_t padding = 0); protected: // There are two filter functions which can be overriden. Both are @@ -140,6 +235,7 @@ class TlsRecordFilter : public PacketFilter { } bool is_dtls13() const; + bool is_dtls13_ciphertext(uint8_t ct) const; TlsCipherSpec& spec(uint16_t epoch); private: @@ -470,8 +566,9 @@ class TlsEncryptedHandshakeMessageReplacer : public TlsRecordFilter { uint16_t protection_epoch = 0; uint8_t inner_content_type; DataBuffer plaintext; + TlsRecordHeader out_header; if (!Unprotect(header, record, &protection_epoch, &inner_content_type, - &plaintext) || + &plaintext, &out_header) || !plaintext.len()) { return KEEP; } @@ -500,12 +597,12 @@ class TlsEncryptedHandshakeMessageReplacer : public TlsRecordFilter { } DataBuffer ciphertext; - bool ok = Protect(spec(protection_epoch), header, inner_content_type, - plaintext, &ciphertext, 0); + bool ok = Protect(spec(protection_epoch), out_header, inner_content_type, + plaintext, &ciphertext, &out_header); if (!ok) { return KEEP; } - *offset = header.Write(output, *offset, ciphertext); + *offset = out_header.Write(output, *offset, ciphertext); return CHANGE; } diff --git a/security/nss/gtests/ssl_gtest/tls_hkdf_unittest.cc b/security/nss/gtests/ssl_gtest/tls_hkdf_unittest.cc index 03db44dfb..6f55c9265 100644 --- a/security/nss/gtests/ssl_gtest/tls_hkdf_unittest.cc +++ b/security/nss/gtests/ssl_gtest/tls_hkdf_unittest.cc @@ -1,4 +1,5 @@ /* -*- 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/. */ @@ -67,18 +68,6 @@ size_t GetHashLength(SSLHashType hash) { return 0; } -CK_MECHANISM_TYPE GetHkdfMech(SSLHashType hash) { - switch (hash) { - case ssl_hash_sha256: - return CKM_NSS_HKDF_SHA256; - case ssl_hash_sha384: - return CKM_NSS_HKDF_SHA384; - default: - ADD_FAILURE() << "Unknown hash: " << hash; - } - return CKM_INVALID_MECHANISM; -} - PRUint16 GetSomeCipherSuiteForHash(SSLHashType hash) { switch (hash) { case ssl_hash_sha256: @@ -172,7 +161,7 @@ class TlsHkdfTest : public ::testing::Test, ScopedPK11SymKey prkk(prk); DumpKey("Output", prkk); - VerifyKey(prkk, GetHkdfMech(base_hash), expected); + VerifyKey(prkk, CKM_HKDF_DERIVE, expected); // Now test the public wrapper. PRUint16 cs = GetSomeCipherSuiteForHash(base_hash); @@ -180,7 +169,7 @@ class TlsHkdfTest : public ::testing::Test, ikmk2.get(), &prk); ASSERT_EQ(SECSuccess, rv); ASSERT_NE(nullptr, prk); - VerifyKey(ScopedPK11SymKey(prk), GetHkdfMech(base_hash), expected); + VerifyKey(ScopedPK11SymKey(prk), CKM_HKDF_DERIVE, expected); } void HkdfExpandLabel(ScopedPK11SymKey* prk, SSLHashType base_hash, @@ -191,9 +180,9 @@ class TlsHkdfTest : public ::testing::Test, std::vector<uint8_t> output(expected.len()); - SECStatus rv = tls13_HkdfExpandLabelRaw(prk->get(), base_hash, session_hash, - session_hash_len, label, label_len, - &output[0], output.size()); + SECStatus rv = tls13_HkdfExpandLabelRaw( + prk->get(), base_hash, session_hash, session_hash_len, label, label_len, + ssl_variant_stream, &output[0], output.size()); ASSERT_EQ(SECSuccess, rv); DumpData("Output", &output[0], output.size()); EXPECT_EQ(0, memcmp(expected.data(), &output[0], expected.len())); @@ -206,7 +195,7 @@ class TlsHkdfTest : public ::testing::Test, &secret); EXPECT_EQ(SECSuccess, rv); ASSERT_NE(nullptr, prk); - VerifyKey(ScopedPK11SymKey(secret), GetHkdfMech(base_hash), expected); + VerifyKey(ScopedPK11SymKey(secret), CKM_HKDF_DERIVE, expected); // Verify that a key can be created with a different key type and size. rv = SSL_HkdfExpandLabelWithMech( diff --git a/security/nss/gtests/ssl_gtest/tls_protect.cc b/security/nss/gtests/ssl_gtest/tls_protect.cc index 6c87d0a05..6187660a5 100644 --- a/security/nss/gtests/ssl_gtest/tls_protect.cc +++ b/security/nss/gtests/ssl_gtest/tls_protect.cc @@ -1,4 +1,5 @@ /* -*- 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/. */ @@ -24,39 +25,68 @@ TlsCipherSpec::TlsCipherSpec(bool dtls, uint16_t epoc) bool TlsCipherSpec::SetKeys(SSLCipherSuiteInfo* cipherinfo, PK11SymKey* secret) { - SSLAeadContext* ctx; - SECStatus rv = SSL_MakeAead(SSL_LIBRARY_VERSION_TLS_1_3, - cipherinfo->cipherSuite, secret, "", - 0, // Use the default labels. - &ctx); + SSLAeadContext* aead_ctx; + SSLProtocolVariant variant = + dtls_ ? ssl_variant_datagram : ssl_variant_stream; + SECStatus rv = + SSL_MakeVariantAead(SSL_LIBRARY_VERSION_TLS_1_3, cipherinfo->cipherSuite, + variant, secret, "", 0, // Use the default labels. + &aead_ctx); + if (rv != SECSuccess) { + return false; + } + aead_.reset(aead_ctx); + + SSLMaskingContext* mask_ctx; + const char kHkdfPurposeSn[] = "sn"; + rv = SSL_CreateVariantMaskingContext( + SSL_LIBRARY_VERSION_TLS_1_3, cipherinfo->cipherSuite, variant, secret, + kHkdfPurposeSn, strlen(kHkdfPurposeSn), &mask_ctx); if (rv != SECSuccess) { return false; } - aead_.reset(ctx); + mask_.reset(mask_ctx); return true; } bool TlsCipherSpec::Unprotect(const TlsRecordHeader& header, const DataBuffer& ciphertext, - DataBuffer* plaintext) { - if (aead_ == nullptr) { + DataBuffer* plaintext, + TlsRecordHeader* out_header) { + if (!aead_ || !out_header) { return false; } + *out_header = header; + // Make space. plaintext->Allocate(ciphertext.len()); - auto header_bytes = header.header(); unsigned int len; - uint64_t seqno; - if (dtls_) { - seqno = header.sequence_number(); - } else { - seqno = in_seqno_; + uint64_t seqno = dtls_ ? header.sequence_number() : in_seqno_; + SECStatus rv; + + if (header.is_dtls13_ciphertext()) { + if (!mask_ || !out_header) { + return false; + } + PORT_Assert(ciphertext.len() >= 16); + DataBuffer mask(2); + rv = SSL_CreateMask(mask_.get(), ciphertext.data(), ciphertext.len(), + mask.data(), mask.len()); + if (rv != SECSuccess) { + return false; + } + + if (!out_header->MaskSequenceNumber(mask)) { + return false; + } + seqno = out_header->sequence_number(); } - SECStatus rv = - SSL_AeadDecrypt(aead_.get(), seqno, header_bytes.data(), - header_bytes.len(), ciphertext.data(), ciphertext.len(), - plaintext->data(), &len, plaintext->len()); + + auto header_bytes = out_header->header(); + rv = SSL_AeadDecrypt(aead_.get(), seqno, header_bytes.data(), + header_bytes.len(), ciphertext.data(), ciphertext.len(), + plaintext->data(), &len, plaintext->len()); if (rv != SECSuccess) { return false; } @@ -68,11 +98,14 @@ bool TlsCipherSpec::Unprotect(const TlsRecordHeader& header, } bool TlsCipherSpec::Protect(const TlsRecordHeader& header, - const DataBuffer& plaintext, - DataBuffer* ciphertext) { - if (aead_ == nullptr) { + const DataBuffer& plaintext, DataBuffer* ciphertext, + TlsRecordHeader* out_header) { + if (!aead_ || !out_header) { return false; } + + *out_header = header; + // Make a padded buffer. ciphertext->Allocate(plaintext.len() + 32); // Room for any plausible auth tag @@ -80,12 +113,7 @@ bool TlsCipherSpec::Protect(const TlsRecordHeader& header, DataBuffer header_bytes; (void)header.WriteHeader(&header_bytes, 0, plaintext.len() + 16); - uint64_t seqno; - if (dtls_) { - seqno = header.sequence_number(); - } else { - seqno = out_seqno_; - } + uint64_t seqno = dtls_ ? header.sequence_number() : out_seqno_; SECStatus rv = SSL_AeadEncrypt(aead_.get(), seqno, header_bytes.data(), @@ -95,6 +123,22 @@ bool TlsCipherSpec::Protect(const TlsRecordHeader& header, return false; } + if (header.is_dtls13_ciphertext()) { + if (!mask_ || !out_header) { + return false; + } + PORT_Assert(ciphertext->len() >= 16); + DataBuffer mask(2); + rv = SSL_CreateMask(mask_.get(), ciphertext->data(), ciphertext->len(), + mask.data(), mask.len()); + if (rv != SECSuccess) { + return false; + } + if (!out_header->MaskSequenceNumber(mask)) { + return false; + } + } + RecordProtected(); ciphertext->Truncate(len); diff --git a/security/nss/gtests/ssl_gtest/tls_protect.h b/security/nss/gtests/ssl_gtest/tls_protect.h index 08b3483aa..d7ea2aa12 100644 --- a/security/nss/gtests/ssl_gtest/tls_protect.h +++ b/security/nss/gtests/ssl_gtest/tls_protect.h @@ -1,4 +1,5 @@ /* -*- 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/. */ @@ -26,9 +27,9 @@ class TlsCipherSpec { bool SetKeys(SSLCipherSuiteInfo* cipherinfo, PK11SymKey* secret); bool Protect(const TlsRecordHeader& header, const DataBuffer& plaintext, - DataBuffer* ciphertext); + DataBuffer* ciphertext, TlsRecordHeader* out_header); bool Unprotect(const TlsRecordHeader& header, const DataBuffer& ciphertext, - DataBuffer* plaintext); + DataBuffer* plaintext, TlsRecordHeader* out_header); uint16_t epoch() const { return epoch_; } uint64_t next_in_seqno() const { return in_seqno_; } @@ -51,6 +52,7 @@ class TlsCipherSpec { uint64_t out_seqno_; bool record_dropped_ = false; ScopedSSLAeadContext aead_; + ScopedSSLMaskingContext mask_; }; } // namespace nss_test diff --git a/security/nss/gtests/ssl_gtest/tls_psk_unittest.cc b/security/nss/gtests/ssl_gtest/tls_psk_unittest.cc new file mode 100644 index 000000000..c75297bc8 --- /dev/null +++ b/security/nss/gtests/ssl_gtest/tls_psk_unittest.cc @@ -0,0 +1,514 @@ +/* -*- 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 "sslproto.h" + +#include "gtest_utils.h" +#include "tls_connect.h" + +namespace nss_test { + +class Tls13PskTest : public TlsConnectTestBase, + public ::testing::WithParamInterface< + std::tuple<SSLProtocolVariant, uint16_t>> { + public: + Tls13PskTest() + : TlsConnectTestBase(std::get<0>(GetParam()), + SSL_LIBRARY_VERSION_TLS_1_3), + suite_(std::get<1>(GetParam())) {} + + void SetUp() override { + TlsConnectTestBase::SetUp(); + scoped_psk_.reset(GetPsk()); + ASSERT_TRUE(!!scoped_psk_); + } + + private: + PK11SymKey* GetPsk() { + ScopedPK11SlotInfo slot(PK11_GetInternalSlot()); + if (!slot) { + ADD_FAILURE(); + return nullptr; + } + + SECItem psk_item; + psk_item.type = siBuffer; + psk_item.len = sizeof(kPskDummyVal_); + psk_item.data = const_cast<uint8_t*>(kPskDummyVal_); + + PK11SymKey* key = + PK11_ImportSymKey(slot.get(), CKM_HKDF_KEY_GEN, PK11_OriginUnwrap, + CKA_DERIVE, &psk_item, NULL); + if (!key) { + ADD_FAILURE(); + } + return key; + } + + protected: + ScopedPK11SymKey scoped_psk_; + const uint16_t suite_; + const uint8_t kPskDummyVal_[16] = {0x01, 0x02, 0x03, 0x04, 0x05, + 0x06, 0x07, 0x08, 0x09, 0x0a, + 0x0b, 0x0c, 0x0d, 0x0e, 0x0f}; + const std::string kPskDummyLabel_ = "NSS PSK GTEST label"; + const SSLHashType kPskHash_ = ssl_hash_sha384; +}; + +// TLS 1.3 PSK connection test. +TEST_P(Tls13PskTest, NormalExternal) { + AddPsk(scoped_psk_, kPskDummyLabel_, kPskHash_); + Connect(); + SendReceive(); + CheckKeys(ssl_kea_ecdh, ssl_grp_ec_curve25519, ssl_auth_psk, ssl_sig_none); + client_->RemovePsk(kPskDummyLabel_); + server_->RemovePsk(kPskDummyLabel_); + + // Removing it again should fail. + EXPECT_EQ(SECFailure, SSL_RemoveExternalPsk(client_->ssl_fd(), + reinterpret_cast<const uint8_t*>( + kPskDummyLabel_.data()), + kPskDummyLabel_.length())); + EXPECT_EQ(SECFailure, SSL_RemoveExternalPsk(server_->ssl_fd(), + reinterpret_cast<const uint8_t*>( + kPskDummyLabel_.data()), + kPskDummyLabel_.length())); +} + +TEST_P(Tls13PskTest, KeyTooLarge) { + ScopedPK11SlotInfo slot(PK11_GetInternalSlot()); + ASSERT_TRUE(!!slot); + ScopedPK11SymKey scoped_psk(PK11_KeyGen( + slot.get(), CKM_GENERIC_SECRET_KEY_GEN, nullptr, 128, nullptr)); + AddPsk(scoped_psk_, kPskDummyLabel_, kPskHash_); + Connect(); + SendReceive(); + CheckKeys(ssl_kea_ecdh, ssl_grp_ec_curve25519, ssl_auth_psk, ssl_sig_none); +} + +// Attempt to use a PSK with the wrong PRF hash. +// "Clients MUST verify that...the server selected a cipher suite +// indicating a Hash associated with the PSK" +TEST_P(Tls13PskTest, ClientVerifyHashType) { + AddPsk(scoped_psk_, kPskDummyLabel_, kPskHash_); + MakeTlsFilter<SelectedCipherSuiteReplacer>(server_, + TLS_CHACHA20_POLY1305_SHA256); + client_->ExpectSendAlert(kTlsAlertIllegalParameter); + if (variant_ == ssl_variant_stream) { + server_->ExpectSendAlert(kTlsAlertUnexpectedMessage); + ConnectExpectFail(); + EXPECT_EQ(SSL_ERROR_RX_UNEXPECTED_RECORD_TYPE, server_->error_code()); + } else { + ConnectExpectFailOneSide(TlsAgent::CLIENT); + } + EXPECT_EQ(SSL_ERROR_RX_MALFORMED_SERVER_HELLO, client_->error_code()); +} + +// Different EPSKs (by label) on each endpoint. Expect cert auth. +TEST_P(Tls13PskTest, LabelMismatch) { + client_->AddPsk(scoped_psk_, std::string("foo"), kPskHash_); + server_->AddPsk(scoped_psk_, std::string("bar"), kPskHash_); + Connect(); + CheckKeys(ssl_kea_ecdh, ssl_auth_rsa_sign); +} + +SSLHelloRetryRequestAction RetryFirstHello( + PRBool firstHello, const PRUint8* clientToken, unsigned int clientTokenLen, + PRUint8* appToken, unsigned int* appTokenLen, unsigned int appTokenMax, + void* arg) { + auto* called = reinterpret_cast<size_t*>(arg); + ++*called; + EXPECT_EQ(0U, clientTokenLen); + EXPECT_EQ(*called, firstHello ? 1U : 2U); + return firstHello ? ssl_hello_retry_request : ssl_hello_retry_accept; +} + +// Test resumption PSK with HRR. +TEST_P(Tls13PskTest, ResPskRetryStateless) { + ConfigureSelfEncrypt(); + ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET); + Connect(); + SendReceive(); // Need to read so that we absorb the session ticket. + CheckKeys(); + + Reset(); + StartConnect(); + size_t cb_called = 0; + EXPECT_EQ(SECSuccess, SSL_HelloRetryRequestCallback( + server_->ssl_fd(), RetryFirstHello, &cb_called)); + ExpectResumption(RESUME_TICKET); + Handshake(); + CheckConnected(); + EXPECT_EQ(2U, cb_called); + CheckKeys(ssl_kea_ecdh, ssl_auth_rsa_sign); + SendReceive(); +} + +// Test external PSK with HRR. +TEST_P(Tls13PskTest, ExtPskRetryStateless) { + AddPsk(scoped_psk_, kPskDummyLabel_, kPskHash_); + size_t cb_called = 0; + EXPECT_EQ(SECSuccess, SSL_HelloRetryRequestCallback( + server_->ssl_fd(), RetryFirstHello, &cb_called)); + StartConnect(); + client_->Handshake(); + server_->Handshake(); + EXPECT_EQ(1U, cb_called); + auto replacement = std::make_shared<TlsAgent>( + server_->name(), TlsAgent::SERVER, server_->variant()); + server_ = replacement; + server_->SetVersionRange(version_, version_); + client_->SetPeer(server_); + server_->SetPeer(client_); + server_->AddPsk(scoped_psk_, kPskDummyLabel_, kPskHash_); + server_->ExpectPsk(); + server_->StartConnect(); + Handshake(); + CheckConnected(); + SendReceive(); + CheckKeys(ssl_kea_ecdh, ssl_grp_ec_curve25519, ssl_auth_psk, ssl_sig_none); +} + +// Server not configured with PSK and sends a certificate instead of +// a selected_identity. Client should attempt certificate authentication. +TEST_P(Tls13PskTest, ClientOnly) { + client_->AddPsk(scoped_psk_, kPskDummyLabel_, kPskHash_); + Connect(); + CheckKeys(ssl_kea_ecdh, ssl_auth_rsa_sign); +} + +// Set a PSK, remove psk_key_exchange_modes. +TEST_P(Tls13PskTest, DropKexModes) { + AddPsk(scoped_psk_, kPskDummyLabel_, kPskHash_); + StartConnect(); + MakeTlsFilter<TlsExtensionDropper>(client_, + ssl_tls13_psk_key_exchange_modes_xtn); + ConnectExpectAlert(server_, kTlsAlertMissingExtension); + client_->CheckErrorCode(SSL_ERROR_MISSING_EXTENSION_ALERT); + server_->CheckErrorCode(SSL_ERROR_MISSING_PSK_KEY_EXCHANGE_MODES); +} + +// "Clients MUST verify that...a server "key_share" extension is present +// if required by the ClientHello "psk_key_exchange_modes" extension." +// As we don't support PSK without DH, it is always required. +TEST_P(Tls13PskTest, DropRequiredKeyShare) { + AddPsk(scoped_psk_, kPskDummyLabel_, kPskHash_); + StartConnect(); + MakeTlsFilter<TlsExtensionDropper>(server_, ssl_tls13_key_share_xtn); + client_->ExpectSendAlert(kTlsAlertMissingExtension); + if (variant_ == ssl_variant_stream) { + server_->ExpectSendAlert(kTlsAlertUnexpectedMessage); + ConnectExpectFail(); + } else { + ConnectExpectFailOneSide(TlsAgent::CLIENT); + } + client_->CheckErrorCode(SSL_ERROR_MISSING_KEY_SHARE); +} + +// "Clients MUST verify that...the server's selected_identity is +// within the range supplied by the client". We send one OfferedPsk. +TEST_P(Tls13PskTest, InvalidSelectedIdentity) { + static const uint8_t selected_identity[] = {0x00, 0x01}; + DataBuffer buf(selected_identity, sizeof(selected_identity)); + AddPsk(scoped_psk_, kPskDummyLabel_, kPskHash_); + StartConnect(); + MakeTlsFilter<TlsExtensionReplacer>(server_, ssl_tls13_pre_shared_key_xtn, + buf); + client_->ExpectSendAlert(kTlsAlertIllegalParameter); + if (variant_ == ssl_variant_stream) { + server_->ExpectSendAlert(kTlsAlertUnexpectedMessage); + ConnectExpectFail(); + } else { + ConnectExpectFailOneSide(TlsAgent::CLIENT); + } + client_->CheckErrorCode(SSL_ERROR_MALFORMED_PRE_SHARED_KEY); +} + +// Resume-eligible reconnect with an EPSK configured. +// Expect the EPSK to be used. +TEST_P(Tls13PskTest, PreferEpsk) { + ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET); + Connect(); + SendReceive(); // Need to read so that we absorb the session ticket. + CheckKeys(); + + Reset(); + AddPsk(scoped_psk_, kPskDummyLabel_, kPskHash_); + ExpectResumption(RESUME_NONE); + StartConnect(); + Handshake(); + CheckConnected(); + SendReceive(); + CheckKeys(ssl_kea_ecdh, ssl_grp_ec_curve25519, ssl_auth_psk, ssl_sig_none); +} + +// Enable resumption, but connect (initially) with an EPSK. +// Expect no session ticket. +TEST_P(Tls13PskTest, SuppressNewSessionTicket) { + AddPsk(scoped_psk_, kPskDummyLabel_, kPskHash_); + ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET); + auto nst_capture = + MakeTlsFilter<TlsHandshakeRecorder>(server_, ssl_hs_new_session_ticket); + nst_capture->EnableDecryption(); + Connect(); + SendReceive(); + CheckKeys(ssl_kea_ecdh, ssl_grp_ec_curve25519, ssl_auth_psk, ssl_sig_none); + EXPECT_EQ(SECFailure, SSL_SendSessionTicket(server_->ssl_fd(), nullptr, 0)); + EXPECT_EQ(0U, nst_capture->buffer().len()); + if (variant_ == ssl_variant_stream) { + EXPECT_EQ(SSL_ERROR_FEATURE_DISABLED, PORT_GetError()); + } else { + EXPECT_EQ(SSL_ERROR_FEATURE_NOT_SUPPORTED_FOR_VERSION, PORT_GetError()); + } + + Reset(); + ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET); + AddPsk(scoped_psk_, kPskDummyLabel_, kPskHash_); + ExpectResumption(RESUME_NONE); + Connect(); + SendReceive(); + CheckKeys(ssl_kea_ecdh, ssl_grp_ec_curve25519, ssl_auth_psk, ssl_sig_none); +} + +TEST_P(Tls13PskTest, BadConfigValues) { + EXPECT_TRUE(client_->EnsureTlsSetup()); + std::vector<uint8_t> label{'L', 'A', 'B', 'E', 'L'}; + EXPECT_EQ(SECFailure, + SSL_AddExternalPsk(client_->ssl_fd(), nullptr, label.data(), + label.size(), kPskHash_)); + EXPECT_EQ(SECFailure, SSL_AddExternalPsk(client_->ssl_fd(), scoped_psk_.get(), + nullptr, label.size(), kPskHash_)); + + EXPECT_EQ(SECFailure, SSL_AddExternalPsk(client_->ssl_fd(), scoped_psk_.get(), + label.data(), 0, kPskHash_)); + EXPECT_EQ(SECSuccess, + SSL_AddExternalPsk(client_->ssl_fd(), scoped_psk_.get(), + label.data(), label.size(), ssl_hash_sha256)); + + EXPECT_EQ(SECFailure, + SSL_RemoveExternalPsk(client_->ssl_fd(), nullptr, label.size())); + + EXPECT_EQ(SECFailure, + SSL_RemoveExternalPsk(client_->ssl_fd(), label.data(), 0)); + + EXPECT_EQ(SECSuccess, SSL_RemoveExternalPsk(client_->ssl_fd(), label.data(), + label.size())); +} + +// If the server has an EPSK configured with a ciphersuite not supported +// by the client, it should use certificate authentication. +TEST_P(Tls13PskTest, FallbackUnsupportedCiphersuite) { + client_->AddPsk(scoped_psk_, kPskDummyLabel_, ssl_hash_sha256, + TLS_AES_128_GCM_SHA256); + server_->AddPsk(scoped_psk_, kPskDummyLabel_, ssl_hash_sha256, + TLS_CHACHA20_POLY1305_SHA256); + + client_->EnableSingleCipher(TLS_AES_128_GCM_SHA256); + Connect(); + SendReceive(); + CheckKeys(ssl_kea_ecdh, ssl_auth_rsa_sign); +} + +// That fallback should not occur if there is no cipher overlap. +TEST_P(Tls13PskTest, ExplicitSuiteNoOverlap) { + client_->AddPsk(scoped_psk_, kPskDummyLabel_, ssl_hash_sha256, + TLS_AES_128_GCM_SHA256); + server_->AddPsk(scoped_psk_, kPskDummyLabel_, ssl_hash_sha256, + TLS_CHACHA20_POLY1305_SHA256); + + client_->EnableSingleCipher(TLS_AES_128_GCM_SHA256); + server_->EnableSingleCipher(TLS_CHACHA20_POLY1305_SHA256); + ConnectExpectAlert(server_, kTlsAlertHandshakeFailure); + server_->CheckErrorCode(SSL_ERROR_NO_CYPHER_OVERLAP); + client_->CheckErrorCode(SSL_ERROR_NO_CYPHER_OVERLAP); +} + +TEST_P(Tls13PskTest, SuppressHandshakeCertReq) { + AddPsk(scoped_psk_, kPskDummyLabel_, kPskHash_); + server_->SetOption(SSL_REQUEST_CERTIFICATE, PR_TRUE); + server_->SetOption(SSL_REQUIRE_CERTIFICATE, PR_TRUE); + const std::set<uint8_t> hs_types = {ssl_hs_certificate, + ssl_hs_certificate_request}; + auto cr_cert_capture = MakeTlsFilter<TlsHandshakeRecorder>(server_, hs_types); + cr_cert_capture->EnableDecryption(); + + Connect(); + SendReceive(); + CheckKeys(ssl_kea_ecdh, ssl_grp_ec_curve25519, ssl_auth_psk, ssl_sig_none); + EXPECT_EQ(0U, cr_cert_capture->buffer().len()); +} + +TEST_P(Tls13PskTest, DisallowClientConfigWithoutServerCert) { + AddPsk(scoped_psk_, kPskDummyLabel_, kPskHash_); + server_->SetOption(SSL_REQUEST_CERTIFICATE, PR_TRUE); + server_->SetOption(SSL_REQUIRE_CERTIFICATE, PR_TRUE); + const std::set<uint8_t> hs_types = {ssl_hs_certificate, + ssl_hs_certificate_request}; + auto cr_cert_capture = MakeTlsFilter<TlsHandshakeRecorder>(server_, hs_types); + cr_cert_capture->EnableDecryption(); + + EXPECT_EQ(SECSuccess, SSLInt_RemoveServerCertificates(server_->ssl_fd())); + + ConnectExpectAlert(server_, kTlsAlertHandshakeFailure); + server_->CheckErrorCode(SSL_ERROR_NO_CERTIFICATE); + client_->CheckErrorCode(SSL_ERROR_NO_CYPHER_OVERLAP); + EXPECT_EQ(0U, cr_cert_capture->buffer().len()); +} + +TEST_F(TlsConnectStreamTls13, ClientRejectHandshakeCertReq) { + // Stream only, as the filter doesn't support DTLS 1.3 yet. + ScopedPK11SlotInfo slot(PK11_GetInternalSlot()); + ASSERT_TRUE(!!slot); + ScopedPK11SymKey scoped_psk(PK11_KeyGen( + slot.get(), CKM_GENERIC_SECRET_KEY_GEN, nullptr, 32, nullptr)); + AddPsk(scoped_psk, std::string("foo"), ssl_hash_sha256); + // Inject a CR after EE. This would be legal if not for ssl_auth_psk. + auto filter = MakeTlsFilter<TlsEncryptedHandshakeMessageReplacer>( + server_, kTlsHandshakeFinished, kTlsHandshakeCertificateRequest); + filter->EnableDecryption(); + + ExpectAlert(client_, kTlsAlertUnexpectedMessage); + ConnectExpectFail(); + client_->CheckErrorCode(SSL_ERROR_RX_UNEXPECTED_CERT_REQUEST); + server_->CheckErrorCode(SSL_ERROR_HANDSHAKE_UNEXPECTED_ALERT); +} + +TEST_F(TlsConnectStreamTls13, RejectPha) { + // Stream only, as the filter doesn't support DTLS 1.3 yet. + ScopedPK11SlotInfo slot(PK11_GetInternalSlot()); + ASSERT_TRUE(!!slot); + ScopedPK11SymKey scoped_psk(PK11_KeyGen( + slot.get(), CKM_GENERIC_SECRET_KEY_GEN, nullptr, 32, nullptr)); + AddPsk(scoped_psk, std::string("foo"), ssl_hash_sha256); + server_->SetOption(SSL_ENABLE_POST_HANDSHAKE_AUTH, PR_TRUE); + auto kuToCr = MakeTlsFilter<TlsEncryptedHandshakeMessageReplacer>( + server_, kTlsHandshakeKeyUpdate, kTlsHandshakeCertificateRequest); + kuToCr->EnableDecryption(); + Connect(); + + // Make sure the direct path is blocked. + EXPECT_EQ(SECFailure, SSL_SendCertificateRequest(server_->ssl_fd())); + EXPECT_EQ(SSL_ERROR_FEATURE_DISABLED, PORT_GetError()); + + // Inject a PHA CR. Since this is not allowed, send KeyUpdate + // and change the message type. + EXPECT_EQ(SECSuccess, SSL_KeyUpdate(server_->ssl_fd(), PR_TRUE)); + ExpectAlert(client_, kTlsAlertUnexpectedMessage); + client_->Handshake(); // Eat the CR. + server_->Handshake(); + client_->CheckErrorCode(SSL_ERROR_RX_UNEXPECTED_CERT_REQUEST); + server_->CheckErrorCode(SSL_ERROR_HANDSHAKE_UNEXPECTED_ALERT); +} + +class Tls13PskTestWithCiphers : public Tls13PskTest {}; + +TEST_P(Tls13PskTestWithCiphers, 0RttCiphers) { + RolloverAntiReplay(); + AddPsk(scoped_psk_, kPskDummyLabel_, tls13_GetHashForCipherSuite(suite_), + suite_); + StartConnect(); + client_->Set0RttEnabled(true); + server_->Set0RttEnabled(true); + ZeroRttSendReceive(true, true); + Handshake(); + ExpectEarlyDataAccepted(true); + CheckConnected(); + SendReceive(); + CheckKeys(ssl_kea_ecdh, ssl_grp_ec_curve25519, ssl_auth_psk, ssl_sig_none); +} + +TEST_P(Tls13PskTestWithCiphers, 0RttMaxEarlyData) { + EnsureTlsSetup(); + RolloverAntiReplay(); + const char* big_message = "0123456789abcdef"; + const size_t short_size = strlen(big_message) - 1; + const PRInt32 short_length = static_cast<PRInt32>(short_size); + + // Set up the PSK + EXPECT_EQ(SECSuccess, + SSL_AddExternalPsk0Rtt( + client_->ssl_fd(), scoped_psk_.get(), + reinterpret_cast<const uint8_t*>(kPskDummyLabel_.data()), + kPskDummyLabel_.length(), tls13_GetHashForCipherSuite(suite_), + suite_, short_length)); + EXPECT_EQ(SECSuccess, + SSL_AddExternalPsk0Rtt( + server_->ssl_fd(), scoped_psk_.get(), + reinterpret_cast<const uint8_t*>(kPskDummyLabel_.data()), + kPskDummyLabel_.length(), tls13_GetHashForCipherSuite(suite_), + suite_, short_length)); + client_->ExpectPsk(); + server_->ExpectPsk(); + client_->expected_cipher_suite(suite_); + server_->expected_cipher_suite(suite_); + StartConnect(); + client_->Set0RttEnabled(true); + server_->Set0RttEnabled(true); + client_->Handshake(); + CheckEarlyDataLimit(client_, short_size); + + PRInt32 sent; + // Writing more than the limit will succeed in TLS, but fail in DTLS. + if (variant_ == ssl_variant_stream) { + sent = PR_Write(client_->ssl_fd(), big_message, + static_cast<PRInt32>(strlen(big_message))); + } else { + sent = PR_Write(client_->ssl_fd(), big_message, + static_cast<PRInt32>(strlen(big_message))); + EXPECT_GE(0, sent); + EXPECT_EQ(PR_WOULD_BLOCK_ERROR, PORT_GetError()); + + // Try an exact-sized write now. + sent = PR_Write(client_->ssl_fd(), big_message, short_length); + } + EXPECT_EQ(short_length, sent); + + // Even a single octet write should now fail. + sent = PR_Write(client_->ssl_fd(), big_message, 1); + EXPECT_GE(0, sent); + EXPECT_EQ(PR_WOULD_BLOCK_ERROR, PORT_GetError()); + + // Process the ClientHello and read 0-RTT. + server_->Handshake(); + CheckEarlyDataLimit(server_, short_size); + + std::vector<uint8_t> buf(short_size + 1); + PRInt32 read = PR_Read(server_->ssl_fd(), buf.data(), buf.capacity()); + EXPECT_EQ(short_length, read); + EXPECT_EQ(0, memcmp(big_message, buf.data(), short_size)); + + // Second read fails. + read = PR_Read(server_->ssl_fd(), buf.data(), buf.capacity()); + EXPECT_EQ(SECFailure, read); + EXPECT_EQ(PR_WOULD_BLOCK_ERROR, PORT_GetError()); + + Handshake(); + ExpectEarlyDataAccepted(true); + CheckConnected(); + SendReceive(); +} + +static const uint16_t k0RttCipherDefs[] = {TLS_CHACHA20_POLY1305_SHA256, + TLS_AES_128_GCM_SHA256, + TLS_AES_256_GCM_SHA384}; + +static const uint16_t kDefaultSuite[] = {TLS_CHACHA20_POLY1305_SHA256}; + +INSTANTIATE_TEST_CASE_P(Tls13PskTest, Tls13PskTest, + ::testing::Combine(TlsConnectTestBase::kTlsVariantsAll, + ::testing::ValuesIn(kDefaultSuite))); + +INSTANTIATE_TEST_CASE_P( + Tls13PskTestWithCiphers, Tls13PskTestWithCiphers, + ::testing::Combine(TlsConnectTestBase::kTlsVariantsAll, + ::testing::ValuesIn(k0RttCipherDefs))); + +} // namespace nss_test diff --git a/security/nss/gtests/ssl_gtest/tls_subcerts_unittest.cc b/security/nss/gtests/ssl_gtest/tls_subcerts_unittest.cc index f0c65b852..77bb41a0b 100644 --- a/security/nss/gtests/ssl_gtest/tls_subcerts_unittest.cc +++ b/security/nss/gtests/ssl_gtest/tls_subcerts_unittest.cc @@ -1,4 +1,5 @@ /* -*- 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/. */ @@ -17,9 +18,10 @@ namespace nss_test { const std::string kEcdsaDelegatorId = TlsAgent::kDelegatorEcdsa256; const std::string kRsaeDelegatorId = TlsAgent::kDelegatorRsae2048; +const std::string kPssDelegatorId = TlsAgent::kDelegatorRsaPss2048; const std::string kDCId = TlsAgent::kServerEcdsa256; const SSLSignatureScheme kDCScheme = ssl_sig_ecdsa_secp256r1_sha256; -const PRUint32 kDCValidFor = 60 * 60 * 24 * 7 /* 1 week (seconds */; +const PRUint32 kDCValidFor = 60 * 60 * 24 * 7 /* 1 week (seconds) */; static void CheckPreliminaryPeerDelegCred( const std::shared_ptr<TlsAgent>& client, bool expected, @@ -121,6 +123,23 @@ TEST_P(TlsConnectTls13, DCConnectEcdsaP256) { EXPECT_EQ(ssl_sig_ecdsa_secp256r1_sha256, client_->info().signatureScheme); } +// Connected with ECDSA-P384. +TEST_P(TlsConnectTls13, DCConnectEcdsaP483) { + Reset(kEcdsaDelegatorId); + client_->EnableDelegatedCredentials(); + server_->AddDelegatedCredential(TlsAgent::kServerEcdsa384, + ssl_sig_ecdsa_secp384r1_sha384, kDCValidFor, + now()); + + auto cfilter = MakeTlsFilter<TlsExtensionCapture>( + client_, ssl_delegated_credentials_xtn); + Connect(); + + EXPECT_TRUE(cfilter->captured()); + CheckPeerDelegCred(client_, true, 384); + EXPECT_EQ(ssl_sig_ecdsa_secp384r1_sha384, client_->info().signatureScheme); +} + // Connected with ECDSA-P521. TEST_P(TlsConnectTls13, DCConnectEcdsaP521) { Reset(kEcdsaDelegatorId); @@ -139,12 +158,19 @@ TEST_P(TlsConnectTls13, DCConnectEcdsaP521) { EXPECT_EQ(ssl_sig_ecdsa_secp521r1_sha512, client_->info().signatureScheme); } -// Connected with RSA-PSS, using an RSAE DC SPKI. -TEST_P(TlsConnectTls13, DCConnectRsaPssRsae) { +// Connected with RSA-PSS, using a PSS SPKI and ECDSA delegation cert. +TEST_P(TlsConnectTls13, DCConnectRsaPssEcdsa) { Reset(kEcdsaDelegatorId); + + // Need to enable PSS-PSS, which is not on by default. + static const SSLSignatureScheme kSchemes[] = {ssl_sig_ecdsa_secp256r1_sha256, + ssl_sig_rsa_pss_pss_sha256}; + client_->SetSignatureSchemes(kSchemes, PR_ARRAY_SIZE(kSchemes)); + server_->SetSignatureSchemes(kSchemes, PR_ARRAY_SIZE(kSchemes)); + client_->EnableDelegatedCredentials(); server_->AddDelegatedCredential( - TlsAgent::kServerRsaPss, ssl_sig_rsa_pss_rsae_sha256, kDCValidFor, now()); + TlsAgent::kServerRsaPss, ssl_sig_rsa_pss_pss_sha256, kDCValidFor, now()); auto cfilter = MakeTlsFilter<TlsExtensionCapture>( client_, ssl_delegated_credentials_xtn); @@ -152,14 +178,15 @@ TEST_P(TlsConnectTls13, DCConnectRsaPssRsae) { EXPECT_TRUE(cfilter->captured()); CheckPeerDelegCred(client_, true, 1024); - EXPECT_EQ(ssl_sig_rsa_pss_rsae_sha256, client_->info().signatureScheme); + EXPECT_EQ(ssl_sig_rsa_pss_pss_sha256, client_->info().signatureScheme); } -// Connected with RSA-PSS, using a RSAE Delegator SPKI. -TEST_P(TlsConnectTls13, DCConnectRsaeDelegator) { - Reset(kRsaeDelegatorId); +// Connected with RSA-PSS, using a PSS SPKI and PSS delegation cert. +TEST_P(TlsConnectTls13, DCConnectRsaPssRsaPss) { + Reset(kPssDelegatorId); - static const SSLSignatureScheme kSchemes[] = {ssl_sig_rsa_pss_rsae_sha256, + // Need to enable PSS-PSS, which is not on by default. + static const SSLSignatureScheme kSchemes[] = {ssl_sig_ecdsa_secp256r1_sha256, ssl_sig_rsa_pss_pss_sha256}; client_->SetSignatureSchemes(kSchemes, PR_ARRAY_SIZE(kSchemes)); server_->SetSignatureSchemes(kSchemes, PR_ARRAY_SIZE(kSchemes)); @@ -177,9 +204,9 @@ TEST_P(TlsConnectTls13, DCConnectRsaeDelegator) { EXPECT_EQ(ssl_sig_rsa_pss_pss_sha256, client_->info().signatureScheme); } -// Connected with RSA-PSS, using a PSS SPKI. -TEST_P(TlsConnectTls13, DCConnectRsaPssPss) { - Reset(kEcdsaDelegatorId); +// Connected with ECDSA-P256 using a PSS delegation cert. +TEST_P(TlsConnectTls13, DCConnectEcdsaP256RsaPss) { + Reset(kPssDelegatorId); // Need to enable PSS-PSS, which is not on by default. static const SSLSignatureScheme kSchemes[] = {ssl_sig_ecdsa_secp256r1_sha256, @@ -188,16 +215,130 @@ TEST_P(TlsConnectTls13, DCConnectRsaPssPss) { server_->SetSignatureSchemes(kSchemes, PR_ARRAY_SIZE(kSchemes)); client_->EnableDelegatedCredentials(); + server_->AddDelegatedCredential(TlsAgent::kServerEcdsa256, + ssl_sig_ecdsa_secp256r1_sha256, kDCValidFor, + now()); + + auto cfilter = MakeTlsFilter<TlsExtensionCapture>( + client_, ssl_delegated_credentials_xtn); + Connect(); + + EXPECT_TRUE(cfilter->captured()); + CheckPeerDelegCred(client_, true, 256); + EXPECT_EQ(ssl_sig_ecdsa_secp256r1_sha256, client_->info().signatureScheme); +} + +// Simulate the client receiving a DC containing algorithms not advertised. +// Do this by tweaking the client's supported sigSchemes after the CH. +TEST_P(TlsConnectTls13, DCReceiveUnadvertisedScheme) { + Reset(kEcdsaDelegatorId); + static const SSLSignatureScheme kClientSchemes[] = { + ssl_sig_ecdsa_secp256r1_sha256, ssl_sig_ecdsa_secp384r1_sha384}; + static const SSLSignatureScheme kServerSchemes[] = { + ssl_sig_ecdsa_secp384r1_sha384, ssl_sig_ecdsa_secp256r1_sha256}; + static const SSLSignatureScheme kEcdsaP256Only[] = { + ssl_sig_ecdsa_secp256r1_sha256}; + client_->SetSignatureSchemes(kClientSchemes, PR_ARRAY_SIZE(kClientSchemes)); + server_->SetSignatureSchemes(kServerSchemes, PR_ARRAY_SIZE(kServerSchemes)); + client_->EnableDelegatedCredentials(); + server_->AddDelegatedCredential(TlsAgent::kServerEcdsa384, + ssl_sig_ecdsa_secp384r1_sha384, kDCValidFor, + now()); + StartConnect(); + client_->Handshake(); // CH with P256/P384. + server_->Handshake(); // Respond with P384 DC. + // Tell the client it only advertised P256. + SECStatus rv = SSLInt_SetDCAdvertisedSigSchemes( + client_->ssl_fd(), kEcdsaP256Only, PR_ARRAY_SIZE(kEcdsaP256Only)); + EXPECT_EQ(SECSuccess, rv); + ExpectAlert(client_, kTlsAlertIllegalParameter); + Handshake(); + client_->CheckErrorCode(SSL_ERROR_UNSUPPORTED_SIGNATURE_ALGORITHM); + server_->CheckErrorCode(SSL_ERROR_ILLEGAL_PARAMETER_ALERT); +} + +// Server schemes includes only RSAE schemes. Connection should succeed +// without delegation. +TEST_P(TlsConnectTls13, DCConnectServerRsaeOnly) { + Reset(kRsaeDelegatorId); + static const SSLSignatureScheme kClientSchemes[] = { + ssl_sig_rsa_pss_rsae_sha256, ssl_sig_rsa_pss_pss_sha256}; + static const SSLSignatureScheme kServerSchemes[] = { + ssl_sig_rsa_pss_rsae_sha256}; + client_->SetSignatureSchemes(kClientSchemes, PR_ARRAY_SIZE(kClientSchemes)); + server_->SetSignatureSchemes(kServerSchemes, PR_ARRAY_SIZE(kServerSchemes)); + client_->EnableDelegatedCredentials(); + Connect(); + + CheckPeerDelegCred(client_, false); +} + +// Connect with an RSA-PSS DC SPKI, and an RSAE Delegator SPKI. +TEST_P(TlsConnectTls13, DCConnectRsaeDelegator) { + Reset(kRsaeDelegatorId); + + static const SSLSignatureScheme kSchemes[] = {ssl_sig_rsa_pss_rsae_sha256, + ssl_sig_rsa_pss_pss_sha256}; + client_->SetSignatureSchemes(kSchemes, PR_ARRAY_SIZE(kSchemes)); + server_->SetSignatureSchemes(kSchemes, PR_ARRAY_SIZE(kSchemes)); + + client_->EnableDelegatedCredentials(); server_->AddDelegatedCredential( TlsAgent::kServerRsaPss, ssl_sig_rsa_pss_pss_sha256, kDCValidFor, now()); + ConnectExpectAlert(client_, kTlsAlertIllegalParameter); + server_->CheckErrorCode(SSL_ERROR_ILLEGAL_PARAMETER_ALERT); + client_->CheckErrorCode(SSL_ERROR_UNSUPPORTED_SIGNATURE_ALGORITHM); +} +// Client schemes includes only RSAE schemes. Connection should succeed +// without delegation, and no DC extension should be present in the CH. +TEST_P(TlsConnectTls13, DCConnectClientRsaeOnly) { + Reset(kRsaeDelegatorId); + static const SSLSignatureScheme kClientSchemes[] = { + ssl_sig_rsa_pss_rsae_sha256}; + static const SSLSignatureScheme kServerSchemes[] = { + ssl_sig_rsa_pss_rsae_sha256, ssl_sig_rsa_pss_pss_sha256}; + client_->SetSignatureSchemes(kClientSchemes, PR_ARRAY_SIZE(kClientSchemes)); + server_->SetSignatureSchemes(kServerSchemes, PR_ARRAY_SIZE(kServerSchemes)); + client_->EnableDelegatedCredentials(); auto cfilter = MakeTlsFilter<TlsExtensionCapture>( client_, ssl_delegated_credentials_xtn); Connect(); + EXPECT_FALSE(cfilter->captured()); + CheckPeerDelegCred(client_, false); +} - EXPECT_TRUE(cfilter->captured()); - CheckPeerDelegCred(client_, true, 1024); - EXPECT_EQ(ssl_sig_rsa_pss_pss_sha256, client_->info().signatureScheme); +// Test fallback. DC extension will not advertise RSAE schemes. +// The server will attempt to set one, but decline to after seeing +// the client-advertised schemes does not include it. Expect non- +// delegated success. +TEST_P(TlsConnectTls13, DCConnectRsaeDcSpki) { + Reset(kRsaeDelegatorId); + + static const SSLSignatureScheme kSchemes[] = {ssl_sig_rsa_pss_rsae_sha256, + ssl_sig_rsa_pss_pss_sha256}; + client_->SetSignatureSchemes(kSchemes, PR_ARRAY_SIZE(kSchemes)); + server_->SetSignatureSchemes(kSchemes, PR_ARRAY_SIZE(kSchemes)); + client_->EnableDelegatedCredentials(); + + EnsureTlsSetup(); + ScopedSECKEYPublicKey pub; + ScopedSECKEYPrivateKey priv; + EXPECT_TRUE( + TlsAgent::LoadKeyPairFromCert(TlsAgent::kDelegatorRsae2048, &pub, &priv)); + + StackSECItem dc; + server_->DelegateCredential(server_->name(), pub, ssl_sig_rsa_pss_rsae_sha256, + kDCValidFor, now(), &dc); + + SSLExtraServerCertData extra_data = {ssl_auth_null, nullptr, nullptr, + nullptr, &dc, priv.get()}; + EXPECT_TRUE(server_->ConfigServerCert(server_->name(), true, &extra_data)); + auto sfilter = MakeTlsFilter<TlsExtensionCapture>( + server_, ssl_delegated_credentials_xtn); + Connect(); + EXPECT_FALSE(sfilter->captured()); + CheckPeerDelegCred(client_, false); } // Generate a weak key. We can't do this in the fixture because certutil @@ -243,8 +384,12 @@ static void GenerateWeakRsaKey(ScopedSECKEYPrivateKey& priv, // Fail to connect with a weak RSA key. TEST_P(TlsConnectTls13, DCWeakKey) { - Reset(kEcdsaDelegatorId); + Reset(kPssDelegatorId); EnsureTlsSetup(); + static const SSLSignatureScheme kSchemes[] = {ssl_sig_rsa_pss_rsae_sha256, + ssl_sig_rsa_pss_pss_sha256}; + client_->SetSignatureSchemes(kSchemes, PR_ARRAY_SIZE(kSchemes)); + server_->SetSignatureSchemes(kSchemes, PR_ARRAY_SIZE(kSchemes)); ScopedSECKEYPrivateKey dc_priv; ScopedSECKEYPublicKey dc_pub; @@ -253,14 +398,14 @@ TEST_P(TlsConnectTls13, DCWeakKey) { // Construct a DC. StackSECItem dc; - TlsAgent::DelegateCredential(kEcdsaDelegatorId, dc_pub, - ssl_sig_rsa_pss_rsae_sha256, kDCValidFor, now(), + TlsAgent::DelegateCredential(kPssDelegatorId, dc_pub, + ssl_sig_rsa_pss_pss_sha256, kDCValidFor, now(), &dc); // Configure the DC on the server. SSLExtraServerCertData extra_data = {ssl_auth_null, nullptr, nullptr, nullptr, &dc, dc_priv.get()}; - EXPECT_TRUE(server_->ConfigServerCert(kEcdsaDelegatorId, true, &extra_data)); + EXPECT_TRUE(server_->ConfigServerCert(kPssDelegatorId, true, &extra_data)); client_->EnableDelegatedCredentials(); @@ -313,8 +458,8 @@ TEST_P(TlsConnectTls13, DCAbortBadSignature) { now(), &dc); ASSERT_TRUE(dc.data != nullptr); - // Flip the first bit of the DC so that the signature is invalid. - dc.data[0] ^= 0x01; + // Flip the last bit of the DC so that the signature is invalid. + dc.data[dc.len - 1] ^= 0x01; SSLExtraServerCertData extra_data = {ssl_auth_null, nullptr, nullptr, nullptr, &dc, priv.get()}; @@ -338,6 +483,17 @@ TEST_P(TlsConnectTls13, DCAbortExpired) { server_->CheckErrorCode(SSL_ERROR_ILLEGAL_PARAMETER_ALERT); } +// Aborted due to remaining TTL > max validity period. +TEST_P(TlsConnectTls13, DCAbortExcessiveTTL) { + Reset(kEcdsaDelegatorId); + server_->AddDelegatedCredential(kDCId, kDCScheme, + kDCValidFor + 1 /* seconds */, now()); + client_->EnableDelegatedCredentials(); + ConnectExpectAlert(client_, kTlsAlertIllegalParameter); + client_->CheckErrorCode(SSL_ERROR_DC_INAPPROPRIATE_VALIDITY_PERIOD); + server_->CheckErrorCode(SSL_ERROR_ILLEGAL_PARAMETER_ALERT); +} + // Aborted because of invalid key usage. TEST_P(TlsConnectTls13, DCAbortBadKeyUsage) { // The sever does not have the delegationUsage extension. @@ -528,20 +684,19 @@ TEST_F(DCDelegation, DCDelegations) { EXPECT_EQ(SSL_ERROR_INCORRECT_SIGNATURE_ALGORITHM, PORT_GetError()); // Using different PSS hashes should be OK. - EXPECT_EQ(SECSuccess, - SSL_DelegateCredential(cert.get(), priv.get(), pub_rsa.get(), - ssl_sig_rsa_pss_rsae_sha256, kDCValidFor, - now, &dc)); - // Make sure to reset |dc| after each success. - dc.Reset(); EXPECT_EQ(SECSuccess, SSL_DelegateCredential( cert.get(), priv.get(), pub_rsa.get(), ssl_sig_rsa_pss_pss_sha256, kDCValidFor, now, &dc)); + // Make sure to reset |dc| after each success. dc.Reset(); EXPECT_EQ(SECSuccess, SSL_DelegateCredential( cert.get(), priv.get(), pub_rsa.get(), ssl_sig_rsa_pss_pss_sha384, kDCValidFor, now, &dc)); dc.Reset(); + EXPECT_EQ(SECSuccess, SSL_DelegateCredential( + cert.get(), priv.get(), pub_rsa.get(), + ssl_sig_rsa_pss_pss_sha512, kDCValidFor, now, &dc)); + dc.Reset(); ScopedSECKEYPublicKey pub_ecdsa; ScopedSECKEYPrivateKey priv_ecdsa; |