From bd199f7fc2115695a834ad27ab4d5e233e3e4c8c Mon Sep 17 00:00:00 2001 From: Martin Thomson Date: Fri, 20 Mar 2015 14:33:51 -0700 Subject: [PATCH] Bug 1086145 - Improving handshake test coverage, r=wtc --HG-- extra : rebase_source : ff9160ed9fd8c30942150392bb75bd9260134328 --- external_tests/README | 12 +- external_tests/ssl_gtest/manifest.mn | 1 + .../ssl_gtest/ssl_extension_unittest.cc | 20 +-- .../ssl_gtest/ssl_loopback_unittest.cc | 101 +++++------ external_tests/ssl_gtest/ssl_skip_unittest.cc | 167 ++++++++++++++++++ external_tests/ssl_gtest/tls_agent.cc | 80 +++++---- external_tests/ssl_gtest/tls_agent.h | 12 +- external_tests/ssl_gtest/tls_connect.cc | 67 ++++--- external_tests/ssl_gtest/tls_connect.h | 12 +- external_tests/ssl_gtest/tls_filter.cc | 26 +-- external_tests/ssl_gtest/tls_filter.h | 5 +- external_tests/ssl_gtest/tls_parser.h | 25 +-- tests/cert/cert.sh | 21 ++- 13 files changed, 379 insertions(+), 170 deletions(-) create mode 100644 external_tests/ssl_gtest/ssl_skip_unittest.cc diff --git a/external_tests/README b/external_tests/README index 97f44e63c4..45d3b1f706 100644 --- a/external_tests/README +++ b/external_tests/README @@ -21,11 +21,11 @@ You should be able to run the unit tests manually as: ssl_gtest -d ${SSLGTESTDIR} -Where $SSLGTESTDIR the directory created by ./all.sh or a manually -created directory with a database containing a certificate called -server (with its private keys) +Where $SSLGTESTDIR is a directory with a database containing: + - an RSA certificate called server (with its private key) + - an ECDSA certificate called ecdsa (with its private key) +A directory like this is created by ./all.sh and can be found +in a directory named something like -There is a very trivial set of tests that demonstrate some -of the features. - + tests_results/security/${hostname}.${NUMBER}/ssl_gtests diff --git a/external_tests/ssl_gtest/manifest.mn b/external_tests/ssl_gtest/manifest.mn index 9b8669b3ee..ee9f1c2abf 100644 --- a/external_tests/ssl_gtest/manifest.mn +++ b/external_tests/ssl_gtest/manifest.mn @@ -9,6 +9,7 @@ MODULE = nss CPPSRCS = \ ssl_loopback_unittest.cc \ ssl_extension_unittest.cc \ + ssl_skip_unittest.cc \ ssl_gtest.cc \ test_io.cc \ tls_agent.cc \ diff --git a/external_tests/ssl_gtest/ssl_extension_unittest.cc b/external_tests/ssl_gtest/ssl_extension_unittest.cc index a11f905436..478cc00824 100644 --- a/external_tests/ssl_gtest/ssl_extension_unittest.cc +++ b/external_tests/ssl_gtest/ssl_extension_unittest.cc @@ -268,8 +268,8 @@ class TlsExtensionTestBase : public TlsConnectTestBase { client_->SetPacketFilter(filter); } ConnectExpectFail(); - ASSERT_EQ(kTlsAlertFatal, alert_recorder->level()); - ASSERT_EQ(alert, alert_recorder->description()); + EXPECT_EQ(kTlsAlertFatal, alert_recorder->level()); + EXPECT_EQ(alert, alert_recorder->description()); } void ServerHelloErrorTest(PacketFilter* filter, @@ -280,8 +280,8 @@ class TlsExtensionTestBase : public TlsConnectTestBase { server_->SetPacketFilter(filter); } ConnectExpectFail(); - ASSERT_EQ(kTlsAlertFatal, alert_recorder->level()); - ASSERT_EQ(alert, alert_recorder->description()); + EXPECT_EQ(kTlsAlertFatal, alert_recorder->level()); + EXPECT_EQ(alert, alert_recorder->description()); } static void InitSimpleSni(DataBuffer* extension) { @@ -494,7 +494,7 @@ TEST_P(TlsExtensionTest12Plus, DISABLED_SignatureAlgorithmsSigUnsupported) { } TEST_P(TlsExtensionTestGeneric, SupportedCurvesShort) { - EnableSomeECDHECiphers(); + EnableSomeEcdheCiphers(); const uint8_t val[] = { 0x00, 0x01, 0x00 }; DataBuffer extension(val, sizeof(val)); ClientHelloErrorTest(new TlsExtensionReplacer(ssl_elliptic_curves_xtn, @@ -502,7 +502,7 @@ TEST_P(TlsExtensionTestGeneric, SupportedCurvesShort) { } TEST_P(TlsExtensionTestGeneric, SupportedCurvesBadLength) { - EnableSomeECDHECiphers(); + EnableSomeEcdheCiphers(); const uint8_t val[] = { 0x09, 0x99, 0x00, 0x00 }; DataBuffer extension(val, sizeof(val)); ClientHelloErrorTest(new TlsExtensionReplacer(ssl_elliptic_curves_xtn, @@ -510,7 +510,7 @@ TEST_P(TlsExtensionTestGeneric, SupportedCurvesBadLength) { } TEST_P(TlsExtensionTestGeneric, SupportedCurvesTrailingData) { - EnableSomeECDHECiphers(); + EnableSomeEcdheCiphers(); const uint8_t val[] = { 0x00, 0x02, 0x00, 0x00, 0x00 }; DataBuffer extension(val, sizeof(val)); ClientHelloErrorTest(new TlsExtensionReplacer(ssl_elliptic_curves_xtn, @@ -518,7 +518,7 @@ TEST_P(TlsExtensionTestGeneric, SupportedCurvesTrailingData) { } TEST_P(TlsExtensionTestGeneric, SupportedPointsEmpty) { - EnableSomeECDHECiphers(); + EnableSomeEcdheCiphers(); const uint8_t val[] = { 0x00 }; DataBuffer extension(val, sizeof(val)); ClientHelloErrorTest(new TlsExtensionReplacer(ssl_ec_point_formats_xtn, @@ -526,7 +526,7 @@ TEST_P(TlsExtensionTestGeneric, SupportedPointsEmpty) { } TEST_P(TlsExtensionTestGeneric, SupportedPointsBadLength) { - EnableSomeECDHECiphers(); + EnableSomeEcdheCiphers(); const uint8_t val[] = { 0x99, 0x00, 0x00 }; DataBuffer extension(val, sizeof(val)); ClientHelloErrorTest(new TlsExtensionReplacer(ssl_ec_point_formats_xtn, @@ -534,7 +534,7 @@ TEST_P(TlsExtensionTestGeneric, SupportedPointsBadLength) { } TEST_P(TlsExtensionTestGeneric, SupportedPointsTrailingData) { - EnableSomeECDHECiphers(); + EnableSomeEcdheCiphers(); const uint8_t val[] = { 0x01, 0x00, 0x00 }; DataBuffer extension(val, sizeof(val)); ClientHelloErrorTest(new TlsExtensionReplacer(ssl_ec_point_formats_xtn, diff --git a/external_tests/ssl_gtest/ssl_loopback_unittest.cc b/external_tests/ssl_gtest/ssl_loopback_unittest.cc index ae77119eb8..8b3b84bf85 100644 --- a/external_tests/ssl_gtest/ssl_loopback_unittest.cc +++ b/external_tests/ssl_gtest/ssl_loopback_unittest.cc @@ -15,7 +15,7 @@ namespace nss_test { -class TlsServerKeyExchangeECDHE { +class TlsServerKeyExchangeEcdhe { public: bool Parse(const DataBuffer& buffer) { TlsParser parser(buffer); @@ -45,13 +45,14 @@ TEST_P(TlsConnectGeneric, SetupOnly) {} TEST_P(TlsConnectGeneric, Connect) { Connect(); client_->CheckVersion(std::get<1>(GetParam())); + client_->CheckAuthType(ssl_auth_rsa); } TEST_P(TlsConnectGeneric, ConnectResumed) { ConfigureSessionCache(RESUME_SESSIONID, RESUME_SESSIONID); Connect(); - Reset(); + ResetRsa(); Connect(); CheckResumption(RESUME_SESSIONID); } @@ -59,7 +60,7 @@ TEST_P(TlsConnectGeneric, ConnectResumed) { TEST_P(TlsConnectGeneric, ConnectClientCacheDisabled) { ConfigureSessionCache(RESUME_NONE, RESUME_SESSIONID); Connect(); - Reset(); + ResetRsa(); Connect(); CheckResumption(RESUME_NONE); } @@ -67,7 +68,7 @@ TEST_P(TlsConnectGeneric, ConnectClientCacheDisabled) { TEST_P(TlsConnectGeneric, ConnectServerCacheDisabled) { ConfigureSessionCache(RESUME_SESSIONID, RESUME_NONE); Connect(); - Reset(); + ResetRsa(); Connect(); CheckResumption(RESUME_NONE); } @@ -75,7 +76,7 @@ TEST_P(TlsConnectGeneric, ConnectServerCacheDisabled) { TEST_P(TlsConnectGeneric, ConnectSessionCacheDisabled) { ConfigureSessionCache(RESUME_NONE, RESUME_NONE); Connect(); - Reset(); + ResetRsa(); Connect(); CheckResumption(RESUME_NONE); } @@ -85,7 +86,7 @@ TEST_P(TlsConnectGeneric, ConnectResumeSupportBoth) { ConfigureSessionCache(RESUME_BOTH, RESUME_BOTH); Connect(); - Reset(); + ResetRsa(); ConfigureSessionCache(RESUME_BOTH, RESUME_BOTH); Connect(); CheckResumption(RESUME_TICKET); @@ -97,7 +98,7 @@ TEST_P(TlsConnectGeneric, ConnectResumeClientTicketServerBoth) { ConfigureSessionCache(RESUME_TICKET, RESUME_BOTH); Connect(); - Reset(); + ResetRsa(); ConfigureSessionCache(RESUME_TICKET, RESUME_BOTH); Connect(); CheckResumption(RESUME_NONE); @@ -108,7 +109,7 @@ TEST_P(TlsConnectGeneric, ConnectResumeClientBothTicketServerTicket) { ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET); Connect(); - Reset(); + ResetRsa(); ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET); Connect(); CheckResumption(RESUME_TICKET); @@ -120,7 +121,7 @@ TEST_P(TlsConnectGeneric, ConnectClientServerTicketOnly) { ConfigureSessionCache(RESUME_TICKET, RESUME_TICKET); Connect(); - Reset(); + ResetRsa(); ConfigureSessionCache(RESUME_TICKET, RESUME_TICKET); Connect(); CheckResumption(RESUME_NONE); @@ -130,7 +131,7 @@ TEST_P(TlsConnectGeneric, ConnectClientBothServerNone) { ConfigureSessionCache(RESUME_BOTH, RESUME_NONE); Connect(); - Reset(); + ResetRsa(); ConfigureSessionCache(RESUME_BOTH, RESUME_NONE); Connect(); CheckResumption(RESUME_NONE); @@ -140,35 +141,12 @@ TEST_P(TlsConnectGeneric, ConnectClientNoneServerBoth) { ConfigureSessionCache(RESUME_NONE, RESUME_BOTH); Connect(); - Reset(); + ResetRsa(); ConfigureSessionCache(RESUME_NONE, RESUME_BOTH); Connect(); CheckResumption(RESUME_NONE); } -TEST_P(TlsConnectGeneric, ConnectTLS_1_1_Only) { - EnsureTlsSetup(); - client_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_1, - SSL_LIBRARY_VERSION_TLS_1_1); - - server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_1, - SSL_LIBRARY_VERSION_TLS_1_1); - - Connect(); - - client_->CheckVersion(SSL_LIBRARY_VERSION_TLS_1_1); -} - -TEST_P(TlsConnectGeneric, ConnectTLS_1_2_Only) { - EnsureTlsSetup(); - client_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_2, - SSL_LIBRARY_VERSION_TLS_1_2); - server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_2, - SSL_LIBRARY_VERSION_TLS_1_2); - Connect(); - client_->CheckVersion(SSL_LIBRARY_VERSION_TLS_1_2); -} - TEST_P(TlsConnectGeneric, ResumeWithHigherVersion) { EnsureTlsSetup(); ConfigureSessionCache(RESUME_SESSIONID, RESUME_SESSIONID); @@ -178,7 +156,7 @@ TEST_P(TlsConnectGeneric, ResumeWithHigherVersion) { SSL_LIBRARY_VERSION_TLS_1_1); Connect(); - Reset(); + ResetRsa(); EnsureTlsSetup(); client_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_1, SSL_LIBRARY_VERSION_TLS_1_2); @@ -196,65 +174,72 @@ TEST_P(TlsConnectGeneric, ConnectAlpn) { server_->CheckAlpn(SSL_NEXT_PROTO_NEGOTIATED, "a"); } +TEST_P(TlsConnectGeneric, ConnectEcdsa) { + ResetEcdsa(); + Connect(); + client_->CheckVersion(std::get<1>(GetParam())); + client_->CheckAuthType(ssl_auth_ecdsa); +} + TEST_P(TlsConnectDatagram, ConnectSrtp) { EnableSrtp(); Connect(); CheckSrtp(); } -TEST_P(TlsConnectStream, ConnectECDHE) { - EnableSomeECDHECiphers(); +TEST_P(TlsConnectStream, ConnectEcdhe) { + EnableSomeEcdheCiphers(); Connect(); client_->CheckKEAType(ssl_kea_ecdh); } -TEST_P(TlsConnectStream, ConnectECDHETwiceReuseKey) { - EnableSomeECDHECiphers(); +TEST_P(TlsConnectStream, ConnectEcdheTwiceReuseKey) { + EnableSomeEcdheCiphers(); TlsInspectorRecordHandshakeMessage* i1 = new TlsInspectorRecordHandshakeMessage(kTlsHandshakeServerKeyExchange); server_->SetPacketFilter(i1); Connect(); client_->CheckKEAType(ssl_kea_ecdh); - TlsServerKeyExchangeECDHE dhe1; - ASSERT_TRUE(dhe1.Parse(i1->buffer())); + TlsServerKeyExchangeEcdhe dhe1; + EXPECT_TRUE(dhe1.Parse(i1->buffer())); // Restart - Reset(); + ResetRsa(); TlsInspectorRecordHandshakeMessage* i2 = new TlsInspectorRecordHandshakeMessage(kTlsHandshakeServerKeyExchange); server_->SetPacketFilter(i2); - EnableSomeECDHECiphers(); + EnableSomeEcdheCiphers(); ConfigureSessionCache(RESUME_NONE, RESUME_NONE); Connect(); client_->CheckKEAType(ssl_kea_ecdh); - TlsServerKeyExchangeECDHE dhe2; - ASSERT_TRUE(dhe2.Parse(i2->buffer())); + TlsServerKeyExchangeEcdhe dhe2; + EXPECT_TRUE(dhe2.Parse(i2->buffer())); // Make sure they are the same. - ASSERT_EQ(dhe1.public_key_.len(), dhe2.public_key_.len()); - ASSERT_TRUE(!memcmp(dhe1.public_key_.data(), dhe2.public_key_.data(), + EXPECT_EQ(dhe1.public_key_.len(), dhe2.public_key_.len()); + EXPECT_TRUE(!memcmp(dhe1.public_key_.data(), dhe2.public_key_.data(), dhe1.public_key_.len())); } -TEST_P(TlsConnectStream, ConnectECDHETwiceNewKey) { - EnableSomeECDHECiphers(); +TEST_P(TlsConnectStream, ConnectEcdheTwiceNewKey) { + EnableSomeEcdheCiphers(); SECStatus rv = SSL_OptionSet(server_->ssl_fd(), SSL_REUSE_SERVER_ECDHE_KEY, PR_FALSE); - ASSERT_EQ(SECSuccess, rv); + EXPECT_EQ(SECSuccess, rv); TlsInspectorRecordHandshakeMessage* i1 = new TlsInspectorRecordHandshakeMessage(kTlsHandshakeServerKeyExchange); server_->SetPacketFilter(i1); Connect(); client_->CheckKEAType(ssl_kea_ecdh); - TlsServerKeyExchangeECDHE dhe1; - ASSERT_TRUE(dhe1.Parse(i1->buffer())); + TlsServerKeyExchangeEcdhe dhe1; + EXPECT_TRUE(dhe1.Parse(i1->buffer())); // Restart - Reset(); - EnableSomeECDHECiphers(); + ResetRsa(); + EnableSomeEcdheCiphers(); rv = SSL_OptionSet(server_->ssl_fd(), SSL_REUSE_SERVER_ECDHE_KEY, PR_FALSE); - ASSERT_EQ(SECSuccess, rv); + EXPECT_EQ(SECSuccess, rv); TlsInspectorRecordHandshakeMessage* i2 = new TlsInspectorRecordHandshakeMessage(kTlsHandshakeServerKeyExchange); server_->SetPacketFilter(i2); @@ -262,11 +247,11 @@ TEST_P(TlsConnectStream, ConnectECDHETwiceNewKey) { Connect(); client_->CheckKEAType(ssl_kea_ecdh); - TlsServerKeyExchangeECDHE dhe2; - ASSERT_TRUE(dhe2.Parse(i2->buffer())); + TlsServerKeyExchangeEcdhe dhe2; + EXPECT_TRUE(dhe2.Parse(i2->buffer())); // Make sure they are different. - ASSERT_FALSE((dhe1.public_key_.len() == dhe2.public_key_.len()) && + EXPECT_FALSE((dhe1.public_key_.len() == dhe2.public_key_.len()) && (!memcmp(dhe1.public_key_.data(), dhe2.public_key_.data(), dhe1.public_key_.len()))); } diff --git a/external_tests/ssl_gtest/ssl_skip_unittest.cc b/external_tests/ssl_gtest/ssl_skip_unittest.cc new file mode 100644 index 0000000000..5f6be9f239 --- /dev/null +++ b/external_tests/ssl_gtest/ssl_skip_unittest.cc @@ -0,0 +1,167 @@ +/* -*- 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 "sslerr.h" + +#include "tls_parser.h" +#include "tls_filter.h" +#include "tls_connect.h" + +/* + * The tests in this file test that the TLS state machine is robust against + * attacks that alter the order of handshake messages. + * + * See for a description of the problems + * that this sort of attack can enable. + */ +namespace nss_test { + +class TlsHandshakeSkipFilter : public TlsRecordFilter { + public: + // A TLS record filter that skips handshake messages of the identified type. + TlsHandshakeSkipFilter(uint8_t handshake_type) + : handshake_type_(handshake_type), + skipped_(false) {} + + protected: + // Takes a record; if it is a handshake record, it removes the first handshake + // message that is of handshake_type_ type. + virtual bool FilterRecord(uint8_t content_type, uint16_t version, + const DataBuffer& input, DataBuffer* output) { + if (content_type != kTlsHandshakeType) { + return false; + } + + size_t output_offset = 0U; + output->Allocate(input.len()); + + TlsParser parser(input); + while (parser.remaining()) { + size_t start = parser.consumed(); + uint8_t handshake_type; + if (!parser.Read(&handshake_type)) { + return false; + } + uint32_t length; + if (!TlsHandshakeFilter::ReadLength(&parser, version, &length)) { + return false; + } + + if (!parser.Skip(length)) { + return false; + } + + if (skipped_ || handshake_type != handshake_type_) { + size_t entire_length = parser.consumed() - start; + output->Write(output_offset, input.data() + start, + entire_length); + // DTLS sequence numbers need to be rewritten + if (skipped_ && IsDtls(version)) { + output->data()[start + 5] -= 1; + } + output_offset += entire_length; + } else { + std::cerr << "Dropping handshake: " + << static_cast(handshake_type_) << std::endl; + // We only need to report that the output contains changed data if we + // drop a handshake message. But once we've skipped one message, we + // have to modify all subsequent handshake messages so that they include + // the correct DTLS sequence numbers. + skipped_ = true; + } + } + output->Truncate(output_offset); + return skipped_; + } + + private: + // The type of handshake message to drop. + uint8_t handshake_type_; + // Whether this filter has ever skipped a handshake message. Track this so + // that sequence numbers on DTLS handshake messages can be rewritten in + // subsequent calls. + bool skipped_; +}; + +class TlsSkipTest + : public TlsConnectTestBase, + public ::testing::WithParamInterface> { + + protected: + TlsSkipTest() + : TlsConnectTestBase(TlsConnectTestBase::ToMode(std::get<0>(GetParam())), + std::get<1>(GetParam())) {} + + void ServerSkipTest(PacketFilter* filter, + uint8_t alert = kTlsAlertUnexpectedMessage) { + auto alert_recorder = new TlsAlertRecorder(); + client_->SetPacketFilter(alert_recorder); + if (filter) { + server_->SetPacketFilter(filter); + } + ConnectExpectFail(); + EXPECT_EQ(kTlsAlertFatal, alert_recorder->level()); + EXPECT_EQ(alert, alert_recorder->description()); + } +}; + +TEST_P(TlsSkipTest, SkipCertificate) { + ServerSkipTest(new TlsHandshakeSkipFilter(kTlsHandshakeCertificate)); + client_->CheckErrorCode(SSL_ERROR_RX_UNEXPECTED_HELLO_DONE); +} + +TEST_P(TlsSkipTest, SkipCertificateEcdhe) { + EnableSomeEcdheCiphers(); + ServerSkipTest(new TlsHandshakeSkipFilter(kTlsHandshakeCertificate)); + client_->CheckErrorCode(SSL_ERROR_RX_UNEXPECTED_SERVER_KEY_EXCH); +} + +TEST_P(TlsSkipTest, SkipCertificateEcdsa) { + ResetEcdsa(); + ServerSkipTest(new TlsHandshakeSkipFilter(kTlsHandshakeCertificate)); + client_->CheckErrorCode(SSL_ERROR_RX_UNEXPECTED_SERVER_KEY_EXCH); +} + +TEST_P(TlsSkipTest, SkipServerKeyExchange) { + // Have to enable some ephemeral suites, or ServerKeyExchange doesn't appear. + EnableSomeEcdheCiphers(); + ServerSkipTest(new TlsHandshakeSkipFilter(kTlsHandshakeServerKeyExchange)); + client_->CheckErrorCode(SSL_ERROR_RX_UNEXPECTED_HELLO_DONE); +} + +TEST_P(TlsSkipTest, SkipServerKeyExchangeEcdsa) { + ResetEcdsa(); + ServerSkipTest(new TlsHandshakeSkipFilter(kTlsHandshakeServerKeyExchange)); + client_->CheckErrorCode(SSL_ERROR_RX_UNEXPECTED_HELLO_DONE); +} + +TEST_P(TlsSkipTest, SkipCertAndKeyExch) { + auto chain = new ChainedPacketFilter(); + chain->Add(new TlsHandshakeSkipFilter(kTlsHandshakeCertificate)); + chain->Add(new TlsHandshakeSkipFilter(kTlsHandshakeServerKeyExchange)); + ServerSkipTest(chain); + client_->CheckErrorCode(SSL_ERROR_RX_UNEXPECTED_HELLO_DONE); +} + +TEST_P(TlsSkipTest, SkipCertAndKeyExchEcdsa) { + ResetEcdsa(); + auto chain = new ChainedPacketFilter(); + chain->Add(new TlsHandshakeSkipFilter(kTlsHandshakeCertificate)); + chain->Add(new TlsHandshakeSkipFilter(kTlsHandshakeServerKeyExchange)); + ServerSkipTest(chain); + client_->CheckErrorCode(SSL_ERROR_RX_UNEXPECTED_HELLO_DONE); +} + +INSTANTIATE_TEST_CASE_P(SkipTls10, TlsSkipTest, + ::testing::Combine( + TlsConnectTestBase::kTlsModesStream, + TlsConnectTestBase::kTlsV10)); +INSTANTIATE_TEST_CASE_P(SkipVariants, TlsSkipTest, + ::testing::Combine( + TlsConnectTestBase::kTlsModesAll, + TlsConnectTestBase::kTlsV11V12)); + +} // namespace nss_test diff --git a/external_tests/ssl_gtest/tls_agent.cc b/external_tests/ssl_gtest/tls_agent.cc index 76e924574d..7357412049 100644 --- a/external_tests/ssl_gtest/tls_agent.cc +++ b/external_tests/ssl_gtest/tls_agent.cc @@ -42,7 +42,7 @@ bool TlsAgent::EnsureTlsSetup() { EXPECT_NE(nullptr, priv); if (!priv) return false; // Leak cert. - SECStatus rv = SSL_ConfigSecureServer(ssl_fd_, cert, priv, kt_rsa); + SECStatus rv = SSL_ConfigSecureServer(ssl_fd_, cert, priv, kea_); EXPECT_EQ(SECSuccess, rv); if (rv != SECSuccess) return false; // Leak cert and key. @@ -71,40 +71,42 @@ bool TlsAgent::EnsureTlsSetup() { } void TlsAgent::StartConnect() { - ASSERT_TRUE(EnsureTlsSetup()); + EXPECT_TRUE(EnsureTlsSetup()); SECStatus rv; rv = SSL_ResetHandshake(ssl_fd_, role_ == SERVER ? PR_TRUE : PR_FALSE); - ASSERT_EQ(SECSuccess, rv); + EXPECT_EQ(SECSuccess, rv); SetState(CONNECTING); } -void TlsAgent::EnableSomeECDHECiphers() { - ASSERT_TRUE(EnsureTlsSetup()); +void TlsAgent::EnableSomeEcdheCiphers() { + EXPECT_TRUE(EnsureTlsSetup()); - const uint32_t EnabledCiphers[] = {TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, - TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA}; + const uint32_t EcdheCiphers[] = {TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, + TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA}; - for (size_t i = 0; i < PR_ARRAY_SIZE(EnabledCiphers); ++i) { - SECStatus rv = SSL_CipherPrefSet(ssl_fd_, EnabledCiphers[i], PR_TRUE); - ASSERT_EQ(SECSuccess, rv); + for (size_t i = 0; i < PR_ARRAY_SIZE(EcdheCiphers); ++i) { + SECStatus rv = SSL_CipherPrefSet(ssl_fd_, EcdheCiphers[i], PR_TRUE); + EXPECT_EQ(SECSuccess, rv); } } void TlsAgent::SetSessionTicketsEnabled(bool en) { - ASSERT_TRUE(EnsureTlsSetup()); + EXPECT_TRUE(EnsureTlsSetup()); SECStatus rv = SSL_OptionSet(ssl_fd_, SSL_ENABLE_SESSION_TICKETS, en ? PR_TRUE : PR_FALSE); - ASSERT_EQ(SECSuccess, rv); + EXPECT_EQ(SECSuccess, rv); } void TlsAgent::SetSessionCacheEnabled(bool en) { - ASSERT_TRUE(EnsureTlsSetup()); + EXPECT_TRUE(EnsureTlsSetup()); SECStatus rv = SSL_OptionSet(ssl_fd_, SSL_NO_CACHE, en ? PR_FALSE : PR_TRUE); - ASSERT_EQ(SECSuccess, rv); + EXPECT_EQ(SECSuccess, rv); } void TlsAgent::SetVersionRange(uint16_t minver, uint16_t maxver) { @@ -113,25 +115,30 @@ void TlsAgent::SetVersionRange(uint16_t minver, uint16_t maxver) { if (ssl_fd_) { SECStatus rv = SSL_VersionRangeSet(ssl_fd_, &vrange_); - ASSERT_EQ(SECSuccess, rv); + EXPECT_EQ(SECSuccess, rv); } } void TlsAgent::CheckKEAType(SSLKEAType type) const { - ASSERT_EQ(CONNECTED, state_); - ASSERT_EQ(type, csinfo_.keaType); + EXPECT_EQ(CONNECTED, state_); + EXPECT_EQ(type, csinfo_.keaType); +} + +void TlsAgent::CheckAuthType(SSLAuthType type) const { + EXPECT_EQ(CONNECTED, state_); + EXPECT_EQ(type, csinfo_.authAlgorithm); } void TlsAgent::CheckVersion(uint16_t version) const { - ASSERT_EQ(CONNECTED, state_); - ASSERT_EQ(version, info_.protocolVersion); + EXPECT_EQ(CONNECTED, state_); + EXPECT_EQ(version, info_.protocolVersion); } void TlsAgent::EnableAlpn(const uint8_t* val, size_t len) { - ASSERT_TRUE(EnsureTlsSetup()); + EXPECT_TRUE(EnsureTlsSetup()); - ASSERT_EQ(SECSuccess, SSL_OptionSet(ssl_fd_, SSL_ENABLE_ALPN, PR_TRUE)); - ASSERT_EQ(SECSuccess, SSL_SetNextProtoNego(ssl_fd_, val, len)); + EXPECT_EQ(SECSuccess, SSL_OptionSet(ssl_fd_, SSL_ENABLE_ALPN, PR_TRUE)); + EXPECT_EQ(SECSuccess, SSL_SetNextProtoNego(ssl_fd_, val, len)); } void TlsAgent::CheckAlpn(SSLNextProtoState expected_state, @@ -142,37 +149,41 @@ void TlsAgent::CheckAlpn(SSLNextProtoState expected_state, SECStatus rv = SSL_GetNextProto(ssl_fd_, &state, reinterpret_cast(chosen), &chosen_len, sizeof(chosen)); - ASSERT_EQ(SECSuccess, rv); - ASSERT_EQ(expected_state, state); - ASSERT_EQ(expected, std::string(chosen, chosen_len)); + EXPECT_EQ(SECSuccess, rv); + EXPECT_EQ(expected_state, state); + EXPECT_EQ(expected, std::string(chosen, chosen_len)); } void TlsAgent::EnableSrtp() { - ASSERT_TRUE(EnsureTlsSetup()); + EXPECT_TRUE(EnsureTlsSetup()); const uint16_t ciphers[] = { SRTP_AES128_CM_HMAC_SHA1_80, SRTP_AES128_CM_HMAC_SHA1_32 }; - ASSERT_EQ(SECSuccess, SSL_SetSRTPCiphers(ssl_fd_, ciphers, + EXPECT_EQ(SECSuccess, SSL_SetSRTPCiphers(ssl_fd_, ciphers, PR_ARRAY_SIZE(ciphers))); } void TlsAgent::CheckSrtp() { uint16_t actual; - ASSERT_EQ(SECSuccess, SSL_GetSRTPCipher(ssl_fd_, &actual)); - ASSERT_EQ(SRTP_AES128_CM_HMAC_SHA1_80, actual); + EXPECT_EQ(SECSuccess, SSL_GetSRTPCipher(ssl_fd_, &actual)); + EXPECT_EQ(SRTP_AES128_CM_HMAC_SHA1_80, actual); } +void TlsAgent::CheckErrorCode(int32_t expected) const { + EXPECT_EQ(ERROR, state_); + EXPECT_EQ(expected, error_code_); +} void TlsAgent::Handshake() { SECStatus rv = SSL_ForceHandshake(ssl_fd_); if (rv == SECSuccess) { LOG("Handshake success"); SECStatus rv = SSL_GetChannelInfo(ssl_fd_, &info_, sizeof(info_)); - ASSERT_EQ(SECSuccess, rv); + EXPECT_EQ(SECSuccess, rv); rv = SSL_GetCipherSuiteInfo(info_.cipherSuite, &csinfo_, sizeof(csinfo_)); - ASSERT_EQ(SECSuccess, rv); + EXPECT_EQ(SECSuccess, rv); SetState(CONNECTED); return; @@ -192,25 +203,26 @@ void TlsAgent::Handshake() { case SSL_ERROR_RX_MALFORMED_HANDSHAKE: default: LOG("Handshake failed with error " << err); + error_code_ = err; SetState(ERROR); return; } } void TlsAgent::ConfigureSessionCache(SessionResumptionMode mode) { - ASSERT_TRUE(EnsureTlsSetup()); + EXPECT_TRUE(EnsureTlsSetup()); SECStatus rv = SSL_OptionSet(ssl_fd_, SSL_NO_CACHE, mode & RESUME_SESSIONID ? PR_FALSE : PR_TRUE); - ASSERT_EQ(SECSuccess, rv); + EXPECT_EQ(SECSuccess, rv); rv = SSL_OptionSet(ssl_fd_, SSL_ENABLE_SESSION_TICKETS, mode & RESUME_TICKET ? PR_TRUE : PR_FALSE); - ASSERT_EQ(SECSuccess, rv); + EXPECT_EQ(SECSuccess, rv); } diff --git a/external_tests/ssl_gtest/tls_agent.h b/external_tests/ssl_gtest/tls_agent.h index dd2a5e01b4..b3afeca405 100644 --- a/external_tests/ssl_gtest/tls_agent.h +++ b/external_tests/ssl_gtest/tls_agent.h @@ -33,14 +33,16 @@ class TlsAgent : public PollTarget { enum Role { CLIENT, SERVER }; enum State { INIT, CONNECTING, CONNECTED, ERROR }; - TlsAgent(const std::string& name, Role role, Mode mode) + TlsAgent(const std::string& name, Role role, Mode mode, SSLKEAType kea) : name_(name), mode_(mode), + kea_(kea), pr_fd_(nullptr), adapter_(nullptr), ssl_fd_(nullptr), role_(role), - state_(INIT) { + state_(INIT), + error_code_(0) { memset(&info_, 0, sizeof(info_)); memset(&csinfo_, 0, sizeof(csinfo_)); SECStatus rv = SSL_VersionRangeGetDefault(mode_ == STREAM ? @@ -78,10 +80,11 @@ class TlsAgent : public PollTarget { void StartConnect(); void CheckKEAType(SSLKEAType type) const; + void CheckAuthType(SSLAuthType type) const; void CheckVersion(uint16_t version) const; void Handshake(); - void EnableSomeECDHECiphers(); + void EnableSomeEcdheCiphers(); bool EnsureTlsSetup(); void ConfigureSessionCache(SessionResumptionMode mode); @@ -93,6 +96,7 @@ class TlsAgent : public PollTarget { const std::string& expected); void EnableSrtp(); void CheckSrtp(); + void CheckErrorCode(int32_t expected) const; State state() const { return state_; } @@ -172,6 +176,7 @@ class TlsAgent : public PollTarget { const std::string name_; Mode mode_; + SSLKEAType kea_; PRFileDesc* pr_fd_; DummyPrSocket* adapter_; PRFileDesc* ssl_fd_; @@ -180,6 +185,7 @@ class TlsAgent : public PollTarget { SSLChannelInfo info_; SSLCipherSuiteInfo csinfo_; SSLVersionRange vrange_; + int32_t error_code_; }; } // namespace nss_test diff --git a/external_tests/ssl_gtest/tls_connect.cc b/external_tests/ssl_gtest/tls_connect.cc index 6101b271a7..5200c39ee0 100644 --- a/external_tests/ssl_gtest/tls_connect.cc +++ b/external_tests/ssl_gtest/tls_connect.cc @@ -52,8 +52,8 @@ static std::string VersionString(uint16_t version) { TlsConnectTestBase::TlsConnectTestBase(Mode mode, uint16_t version) : mode_(mode), - client_(new TlsAgent("client", TlsAgent::CLIENT, mode_)), - server_(new TlsAgent("server", TlsAgent::SERVER, mode_)), + client_(new TlsAgent("client", TlsAgent::CLIENT, mode_, ssl_kea_rsa)), + server_(new TlsAgent("server", TlsAgent::SERVER, mode_, ssl_kea_rsa)), version_(version), session_ids_() { std::cerr << "Version: " << mode_ << " " << VersionString(version_) << std::endl; @@ -84,8 +84,8 @@ void TlsConnectTestBase::TearDown() { } void TlsConnectTestBase::Init() { - ASSERT_TRUE(client_->Init()); - ASSERT_TRUE(server_->Init()); + EXPECT_TRUE(client_->Init()); + EXPECT_TRUE(server_->Init()); client_->SetPeer(server_); server_->SetPeer(client_); @@ -96,19 +96,28 @@ void TlsConnectTestBase::Init() { } } -void TlsConnectTestBase::Reset() { +void TlsConnectTestBase::Reset(const std::string& server_name, SSLKEAType kea) { delete client_; delete server_; - client_ = new TlsAgent("client", TlsAgent::CLIENT, mode_); - server_ = new TlsAgent("server", TlsAgent::SERVER, mode_); + client_ = new TlsAgent("client", TlsAgent::CLIENT, mode_, kea); + server_ = new TlsAgent(server_name, TlsAgent::SERVER, mode_, kea); Init(); } +void TlsConnectTestBase::ResetRsa() { + Reset("server", ssl_kea_rsa); +} + +void TlsConnectTestBase::ResetEcdsa() { + Reset("ecdsa", ssl_kea_ecdh); + EnableSomeEcdheCiphers(); +} + void TlsConnectTestBase::EnsureTlsSetup() { - ASSERT_TRUE(client_->EnsureTlsSetup()); - ASSERT_TRUE(server_->EnsureTlsSetup()); + EXPECT_TRUE(client_->EnsureTlsSetup()); + EXPECT_TRUE(server_->EnsureTlsSetup()); } void TlsConnectTestBase::Handshake() { @@ -127,20 +136,20 @@ void TlsConnectTestBase::Connect() { Handshake(); // Check the version is as expected - ASSERT_EQ(client_->version(), server_->version()); - ASSERT_EQ(std::min(client_->max_version(), + EXPECT_EQ(client_->version(), server_->version()); + EXPECT_EQ(std::min(client_->max_version(), server_->max_version()), client_->version()); - ASSERT_EQ(TlsAgent::CONNECTED, client_->state()); - ASSERT_EQ(TlsAgent::CONNECTED, server_->state()); + EXPECT_EQ(TlsAgent::CONNECTED, client_->state()); + EXPECT_EQ(TlsAgent::CONNECTED, server_->state()); int16_t cipher_suite1, cipher_suite2; bool ret = client_->cipher_suite(&cipher_suite1); - ASSERT_TRUE(ret); + EXPECT_TRUE(ret); ret = server_->cipher_suite(&cipher_suite2); - ASSERT_TRUE(ret); - ASSERT_EQ(cipher_suite1, cipher_suite2); + EXPECT_TRUE(ret); + EXPECT_EQ(cipher_suite1, cipher_suite2); std::cerr << "Connected with version " << client_->version() << " cipher suite " << client_->cipher_suite_name() @@ -148,10 +157,10 @@ void TlsConnectTestBase::Connect() { // Check and store session ids. std::vector sid_c1 = client_->session_id(); - ASSERT_EQ(32U, sid_c1.size()); + EXPECT_EQ(32U, sid_c1.size()); std::vector sid_s1 = server_->session_id(); - ASSERT_EQ(32U, sid_s1.size()); - ASSERT_EQ(sid_c1, sid_s1); + EXPECT_EQ(32U, sid_s1.size()); + EXPECT_EQ(sid_c1, sid_s1); session_ids_.push_back(sid_c1); } @@ -162,9 +171,9 @@ void TlsConnectTestBase::ConnectExpectFail() { ASSERT_EQ(TlsAgent::ERROR, server_->state()); } -void TlsConnectTestBase::EnableSomeECDHECiphers() { - client_->EnableSomeECDHECiphers(); - server_->EnableSomeECDHECiphers(); +void TlsConnectTestBase::EnableSomeEcdheCiphers() { + client_->EnableSomeEcdheCiphers(); + server_->EnableSomeEcdheCiphers(); } @@ -175,22 +184,22 @@ void TlsConnectTestBase::ConfigureSessionCache(SessionResumptionMode client, } void TlsConnectTestBase::CheckResumption(SessionResumptionMode expected) { - ASSERT_NE(RESUME_BOTH, expected); + EXPECT_NE(RESUME_BOTH, expected); int resume_ct = expected ? 1 : 0; int stateless_ct = (expected & RESUME_TICKET) ? 1 : 0; SSL3Statistics* stats = SSL_GetStatistics(); - ASSERT_EQ(resume_ct, stats->hch_sid_cache_hits); - ASSERT_EQ(resume_ct, stats->hsh_sid_cache_hits); + EXPECT_EQ(resume_ct, stats->hch_sid_cache_hits); + EXPECT_EQ(resume_ct, stats->hsh_sid_cache_hits); - ASSERT_EQ(stateless_ct, stats->hch_sid_stateless_resumes); - ASSERT_EQ(stateless_ct, stats->hsh_sid_stateless_resumes); + EXPECT_EQ(stateless_ct, stats->hch_sid_stateless_resumes); + EXPECT_EQ(stateless_ct, stats->hsh_sid_stateless_resumes); if (resume_ct) { // Check that the last two session ids match. - ASSERT_GE(2U, session_ids_.size()); - ASSERT_EQ(session_ids_[session_ids_.size()-1], + EXPECT_GE(2U, session_ids_.size()); + EXPECT_EQ(session_ids_[session_ids_.size()-1], session_ids_[session_ids_.size()-2]); } } diff --git a/external_tests/ssl_gtest/tls_connect.h b/external_tests/ssl_gtest/tls_connect.h index a981399f68..ae8605d474 100644 --- a/external_tests/ssl_gtest/tls_connect.h +++ b/external_tests/ssl_gtest/tls_connect.h @@ -39,8 +39,11 @@ class TlsConnectTestBase : public ::testing::Test { // Initialize client and server. void Init(); - // Re-initialize client and server. - void Reset(); + // Re-initialize client and server with the default RSA cert. + void ResetRsa(); + // Re-initialize client and server with an ECDSA cert on the server + // and some ECDHE suites. + void ResetEcdsa(); // Make sure TLS is configured for a connection. void EnsureTlsSetup(); @@ -51,7 +54,7 @@ class TlsConnectTestBase : public ::testing::Test { // Connect and expect it to fail. void ConnectExpectFail(); - void EnableSomeECDHECiphers(); + void EnableSomeEcdheCiphers(); void ConfigureSessionCache(SessionResumptionMode client, SessionResumptionMode server); void CheckResumption(SessionResumptionMode expected); @@ -65,6 +68,9 @@ class TlsConnectTestBase : public ::testing::Test { TlsAgent* server_; uint16_t version_; std::vector> session_ids_; + + private: + void Reset(const std::string& server_name, SSLKEAType kea); }; // A TLS-only test base. diff --git a/external_tests/ssl_gtest/tls_filter.cc b/external_tests/ssl_gtest/tls_filter.cc index 4ed74e4aa5..2430cfefda 100644 --- a/external_tests/ssl_gtest/tls_filter.cc +++ b/external_tests/ssl_gtest/tls_filter.cc @@ -67,7 +67,7 @@ size_t TlsRecordFilter::ApplyFilter(uint8_t content_type, uint16_t version, filtered.len() < 0x10000) { *changed = true; std::cerr << "record old: " << record << std::endl; - std::cerr << "record old: " << filtered << std::endl; + std::cerr << "record new: " << filtered << std::endl; source = &filtered; } @@ -96,11 +96,7 @@ bool TlsHandshakeFilter::FilterRecord(uint8_t content_type, uint16_t version, return false; // malformed } uint32_t length; - if (!parser.Read(&length, 3)) { - return false; // malformed - } - - if (IsDtls(version) && !CheckDtls(parser, length)) { + if (!ReadLength(&parser, version, &length)) { return false; } @@ -125,24 +121,32 @@ bool TlsHandshakeFilter::FilterRecord(uint8_t content_type, uint16_t version, return changed; } -bool TlsHandshakeFilter::CheckDtls(TlsParser& parser, size_t length) { +bool TlsHandshakeFilter::ReadLength(TlsParser* parser, uint16_t version, uint32_t *length) { + if (!parser->Read(length, 3)) { + return false; // malformed + } + + if (!IsDtls(version)) { + return true; // nothing left to do + } + // Read and check DTLS parameters - if (!parser.Skip(2)) { // sequence number + if (!parser->Skip(2)) { // sequence number return false; } uint32_t fragment_offset; - if (!parser.Read(&fragment_offset, 3)) { + if (!parser->Read(&fragment_offset, 3)) { return false; } uint32_t fragment_length; - if (!parser.Read(&fragment_length, 3)) { + if (!parser->Read(&fragment_length, 3)) { return false; } // All current tests where we are using this code don't fragment. - return (fragment_offset == 0 && fragment_length == length); + return (fragment_offset == 0 && fragment_length == *length); } size_t TlsHandshakeFilter::ApplyFilter( diff --git a/external_tests/ssl_gtest/tls_filter.h b/external_tests/ssl_gtest/tls_filter.h index 7ebd2c4822..49593b3638 100644 --- a/external_tests/ssl_gtest/tls_filter.h +++ b/external_tests/ssl_gtest/tls_filter.h @@ -43,6 +43,10 @@ class TlsHandshakeFilter : public TlsRecordFilter { public: TlsHandshakeFilter() {} + // Reads the length from the record header. + // This also reads the DTLS fragment information and checks it. + static bool ReadLength(TlsParser* parser, uint16_t version, uint32_t *length); + protected: virtual bool FilterRecord(uint8_t content_type, uint16_t version, const DataBuffer& input, DataBuffer* output); @@ -50,7 +54,6 @@ class TlsHandshakeFilter : public TlsRecordFilter { const DataBuffer& input, DataBuffer* output) = 0; private: - bool CheckDtls(TlsParser& parser, size_t length); size_t ApplyFilter(uint16_t version, uint8_t handshake_type, const DataBuffer& record, DataBuffer* output, size_t length_offset, size_t value_offset, bool* changed); diff --git a/external_tests/ssl_gtest/tls_parser.h b/external_tests/ssl_gtest/tls_parser.h index 9ac4bdabea..3e6ac24c68 100644 --- a/external_tests/ssl_gtest/tls_parser.h +++ b/external_tests/ssl_gtest/tls_parser.h @@ -15,23 +15,24 @@ namespace nss_test { -const uint8_t kTlsChangeCipherSpecType = 0x14; -const uint8_t kTlsAlertType = 0x15; -const uint8_t kTlsHandshakeType = 0x16; +const uint8_t kTlsChangeCipherSpecType = 20; +const uint8_t kTlsAlertType = 21; +const uint8_t kTlsHandshakeType = 22; -const uint8_t kTlsHandshakeClientHello = 0x01; -const uint8_t kTlsHandshakeServerHello = 0x02; -const uint8_t kTlsHandshakeCertificate = 0x0b; -const uint8_t kTlsHandshakeServerKeyExchange = 0x0c; +const uint8_t kTlsHandshakeClientHello = 1; +const uint8_t kTlsHandshakeServerHello = 2; +const uint8_t kTlsHandshakeCertificate = 11; +const uint8_t kTlsHandshakeServerKeyExchange = 12; const uint8_t kTlsAlertWarning = 1; const uint8_t kTlsAlertFatal = 2; -const uint8_t kTlsAlertHandshakeFailure = 0x28; -const uint8_t kTlsAlertIllegalParameter = 0x2f; -const uint8_t kTlsAlertDecodeError = 0x32; -const uint8_t kTlsAlertUnsupportedExtension = 0x6e; -const uint8_t kTlsAlertNoApplicationProtocol = 0x78; +const uint8_t kTlsAlertUnexpectedMessage = 10; +const uint8_t kTlsAlertHandshakeFailure = 40; +const uint8_t kTlsAlertIllegalParameter = 47; +const uint8_t kTlsAlertDecodeError = 50; +const uint8_t kTlsAlertUnsupportedExtension = 110; +const uint8_t kTlsAlertNoApplicationProtocol = 120; const uint8_t kTlsFakeChangeCipherSpec[] = { kTlsChangeCipherSpecType, // Type diff --git a/tests/cert/cert.sh b/tests/cert/cert.sh index 9f424ff5cc..93316257d2 100755 --- a/tests/cert/cert.sh +++ b/tests/cert/cert.sh @@ -958,8 +958,23 @@ cert_ssl_gtests() certu -N -d "${SSLGTESTDIR}" --empty-password 2>&1 # the ssl server used here is special: is a self-signed server # certificate with name server. - echo "$SCRIPTNAME: Creating server cert for ssl_gtests" - certu -S -z ${R_NOISE_FILE} -g 2048 -d ${SSLGTESTDIR} -n server -s "CN=server" -t C,C,C -x -m 1 -w -2 -v 120 -Z SHA256 -1 -2 <