summaryrefslogtreecommitdiffstats
path: root/security/nss/gtests/ssl_gtest/ssl_fuzz_unittest.cc
diff options
context:
space:
mode:
Diffstat (limited to 'security/nss/gtests/ssl_gtest/ssl_fuzz_unittest.cc')
-rw-r--r--security/nss/gtests/ssl_gtest/ssl_fuzz_unittest.cc252
1 files changed, 252 insertions, 0 deletions
diff --git a/security/nss/gtests/ssl_gtest/ssl_fuzz_unittest.cc b/security/nss/gtests/ssl_gtest/ssl_fuzz_unittest.cc
new file mode 100644
index 0000000000..ef6f7602cf
--- /dev/null
+++ b/security/nss/gtests/ssl_gtest/ssl_fuzz_unittest.cc
@@ -0,0 +1,252 @@
+/* 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 "blapi.h"
+#include "ssl.h"
+#include "sslimpl.h"
+#include "tls_connect.h"
+
+#include "gtest/gtest.h"
+
+namespace nss_test {
+
+#ifdef UNSAFE_FUZZER_MODE
+#define FUZZ_F(c, f) TEST_F(c, Fuzz_##f)
+#define FUZZ_P(c, f) TEST_P(c, Fuzz_##f)
+#else
+#define FUZZ_F(c, f) TEST_F(c, DISABLED_Fuzz_##f)
+#define FUZZ_P(c, f) TEST_P(c, DISABLED_Fuzz_##f)
+#endif
+
+const uint8_t kShortEmptyFinished[8] = {0};
+const uint8_t kLongEmptyFinished[128] = {0};
+
+class TlsFuzzTest : public TlsConnectGeneric {};
+
+// Record the application data stream.
+class TlsApplicationDataRecorder : public TlsRecordFilter {
+ public:
+ TlsApplicationDataRecorder(const std::shared_ptr<TlsAgent>& a)
+ : TlsRecordFilter(a), buffer_() {}
+
+ virtual PacketFilter::Action FilterRecord(const TlsRecordHeader& header,
+ const DataBuffer& input,
+ DataBuffer* output) {
+ if (header.content_type() == ssl_ct_application_data) {
+ buffer_.Append(input);
+ }
+
+ return KEEP;
+ }
+
+ const DataBuffer& buffer() const { return buffer_; }
+
+ private:
+ DataBuffer buffer_;
+};
+
+// Check that due to the deterministic PRNG we derive
+// the same master secret in two consecutive TLS sessions.
+FUZZ_P(TlsFuzzTest, DeterministicExporter) {
+ const char kLabel[] = "label";
+ std::vector<unsigned char> out1(32), out2(32);
+
+ // Make sure we have RSA blinding params.
+ Connect();
+
+ Reset();
+ ConfigureSessionCache(RESUME_NONE, RESUME_NONE);
+
+ // Reset the RNG state.
+ EXPECT_EQ(SECSuccess, RNG_RandomUpdate(NULL, 0));
+ Connect();
+
+ // Export a key derived from the MS and nonces.
+ SECStatus rv =
+ SSL_ExportKeyingMaterial(client_->ssl_fd(), kLabel, strlen(kLabel), false,
+ NULL, 0, out1.data(), out1.size());
+ EXPECT_EQ(SECSuccess, rv);
+
+ Reset();
+ ConfigureSessionCache(RESUME_NONE, RESUME_NONE);
+
+ // Reset the RNG state.
+ EXPECT_EQ(SECSuccess, RNG_RandomUpdate(NULL, 0));
+ Connect();
+
+ // Export another key derived from the MS and nonces.
+ rv = SSL_ExportKeyingMaterial(client_->ssl_fd(), kLabel, strlen(kLabel),
+ false, NULL, 0, out2.data(), out2.size());
+ EXPECT_EQ(SECSuccess, rv);
+
+ // The two exported keys should be the same.
+ EXPECT_EQ(out1, out2);
+}
+
+// Check that due to the deterministic RNG two consecutive
+// TLS sessions will have the exact same transcript.
+FUZZ_P(TlsFuzzTest, DeterministicTranscript) {
+ // Make sure we have RSA blinding params.
+ Connect();
+
+ // Connect a few times and compare the transcripts byte-by-byte.
+ DataBuffer last;
+ for (size_t i = 0; i < 5; i++) {
+ Reset();
+ ConfigureSessionCache(RESUME_NONE, RESUME_NONE);
+
+ DataBuffer buffer;
+ MakeTlsFilter<TlsConversationRecorder>(client_, buffer);
+ MakeTlsFilter<TlsConversationRecorder>(server_, buffer);
+
+ // Reset the RNG state.
+ EXPECT_EQ(SECSuccess, RNG_RandomUpdate(NULL, 0));
+ Connect();
+
+ // Ensure the filters go away before |buffer| does.
+ client_->ClearFilter();
+ server_->ClearFilter();
+
+ if (last.len() > 0) {
+ EXPECT_EQ(last, buffer);
+ }
+
+ last = buffer;
+ }
+}
+
+// Check that we can establish and use a connection
+// with all supported TLS versions, STREAM and DGRAM.
+// Check that records are NOT encrypted.
+// Check that records don't have a MAC.
+FUZZ_P(TlsFuzzTest, ConnectSendReceive_NullCipher) {
+ // Set up app data filters.
+ auto client_recorder = MakeTlsFilter<TlsApplicationDataRecorder>(client_);
+ auto server_recorder = MakeTlsFilter<TlsApplicationDataRecorder>(server_);
+
+ Connect();
+
+ // Construct the plaintext.
+ DataBuffer buf;
+ buf.Allocate(50);
+ for (size_t i = 0; i < buf.len(); ++i) {
+ buf.data()[i] = i & 0xff;
+ }
+
+ // Send/Receive data.
+ client_->SendBuffer(buf);
+ server_->SendBuffer(buf);
+ Receive(buf.len());
+
+ // Check for plaintext on the wire.
+ EXPECT_EQ(buf, client_recorder->buffer());
+ EXPECT_EQ(buf, server_recorder->buffer());
+}
+
+// Check that an invalid Finished message doesn't abort the connection.
+FUZZ_P(TlsFuzzTest, BogusClientFinished) {
+ EnsureTlsSetup();
+
+ MakeTlsFilter<TlsInspectorReplaceHandshakeMessage>(
+ client_, kTlsHandshakeFinished,
+ DataBuffer(kShortEmptyFinished, sizeof(kShortEmptyFinished)));
+ Connect();
+ SendReceive();
+}
+
+// Check that an invalid Finished message doesn't abort the connection.
+FUZZ_P(TlsFuzzTest, BogusServerFinished) {
+ EnsureTlsSetup();
+
+ MakeTlsFilter<TlsInspectorReplaceHandshakeMessage>(
+ server_, kTlsHandshakeFinished,
+ DataBuffer(kLongEmptyFinished, sizeof(kLongEmptyFinished)));
+ Connect();
+ SendReceive();
+}
+
+// Check that an invalid server auth signature doesn't abort the connection.
+FUZZ_P(TlsFuzzTest, BogusServerAuthSignature) {
+ EnsureTlsSetup();
+ uint8_t msg_type = version_ == SSL_LIBRARY_VERSION_TLS_1_3
+ ? kTlsHandshakeCertificateVerify
+ : kTlsHandshakeServerKeyExchange;
+ MakeTlsFilter<TlsLastByteDamager>(server_, msg_type);
+ Connect();
+ SendReceive();
+}
+
+// Check that an invalid client auth signature doesn't abort the connection.
+FUZZ_P(TlsFuzzTest, BogusClientAuthSignature) {
+ EnsureTlsSetup();
+ client_->SetupClientAuth();
+ server_->RequestClientAuth(true);
+ MakeTlsFilter<TlsLastByteDamager>(client_, kTlsHandshakeCertificateVerify);
+ Connect();
+}
+
+// Check that session ticket resumption works.
+FUZZ_P(TlsFuzzTest, SessionTicketResumption) {
+ ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
+ Connect();
+ SendReceive();
+
+ Reset();
+ ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
+ ExpectResumption(RESUME_TICKET);
+ Connect();
+ SendReceive();
+}
+
+// Check that session tickets are not encrypted.
+FUZZ_P(TlsFuzzTest, UnencryptedSessionTickets) {
+ ConfigureSessionCache(RESUME_TICKET, RESUME_TICKET);
+
+ auto filter = MakeTlsFilter<TlsHandshakeRecorder>(
+ server_, kTlsHandshakeNewSessionTicket);
+ Connect();
+
+ std::cerr << "ticket" << filter->buffer() << std::endl;
+ size_t offset = 4; // Skip lifetime.
+
+ if (version_ == SSL_LIBRARY_VERSION_TLS_1_3) {
+ offset += 4; // Skip ticket_age_add.
+ uint32_t nonce_len = 0;
+ EXPECT_TRUE(filter->buffer().Read(offset, 1, &nonce_len));
+ offset += 1 + nonce_len;
+ }
+
+ offset += 2; // Skip the ticket length.
+
+ // This bit parses the contents of the ticket, which would ordinarily be
+ // encrypted. Start by checking that we have the right version. This needs
+ // to be updated every time that TLS_EX_SESS_TICKET_VERSION is changed. But
+ // we don't use the #define. That way, any time that code is updated, this
+ // test will fail unless it is manually checked.
+ uint32_t ticket_version;
+ EXPECT_TRUE(filter->buffer().Read(offset, 2, &ticket_version));
+ EXPECT_EQ(0x010aU, ticket_version);
+ offset += 2;
+
+ // Check the protocol version number.
+ uint32_t tls_version = 0;
+ EXPECT_TRUE(filter->buffer().Read(offset, sizeof(version_), &tls_version));
+ EXPECT_EQ(version_, static_cast<decltype(version_)>(tls_version));
+ offset += sizeof(version_);
+
+ // Check the cipher suite.
+ uint32_t suite = 0;
+ EXPECT_TRUE(filter->buffer().Read(offset, 2, &suite));
+ client_->CheckCipherSuite(static_cast<uint16_t>(suite));
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ FuzzStream, TlsFuzzTest,
+ ::testing::Combine(TlsConnectTestBase::kTlsVariantsStream,
+ TlsConnectTestBase::kTlsVAll));
+INSTANTIATE_TEST_SUITE_P(
+ FuzzDatagram, TlsFuzzTest,
+ ::testing::Combine(TlsConnectTestBase::kTlsVariantsDatagram,
+ TlsConnectTestBase::kTlsV11Plus));
+} // namespace nss_test