/* -*- 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 #include #include #include "secerr.h" #include "ssl.h" #include "sslerr.h" #include "sslproto.h" #include "gtest_utils.h" #include "scoped_ptrs.h" #include "tls_connect.h" #include "tls_filter.h" #include "tls_parser.h" namespace nss_test { TEST_P(TlsConnectGeneric, ConnectDhe) { EnableOnlyDheCiphers(); Connect(); CheckKeys(ssl_kea_dh, ssl_grp_ffdhe_2048, ssl_auth_rsa_sign, ssl_sig_rsa_pss_sha256); } TEST_P(TlsConnectTls13, SharesForBothEcdheAndDhe) { EnsureTlsSetup(); client_->ConfigNamedGroups(kAllDHEGroups); auto groups_capture = new TlsExtensionCapture(ssl_supported_groups_xtn); auto shares_capture = new TlsExtensionCapture(ssl_tls13_key_share_xtn); std::vector captures; captures.push_back(groups_capture); captures.push_back(shares_capture); client_->SetPacketFilter(new ChainedPacketFilter(captures)); Connect(); CheckKeys(); bool ec, dh; auto track_group_type = [&ec, &dh](SSLNamedGroup group) { if ((group & 0xff00U) == 0x100U) { dh = true; } else { ec = true; } }; CheckGroups(groups_capture->extension(), track_group_type); CheckShares(shares_capture->extension(), track_group_type); EXPECT_TRUE(ec) << "Should include an EC group and share"; EXPECT_TRUE(dh) << "Should include an FFDHE group and share"; } TEST_P(TlsConnectGeneric, ConnectFfdheClient) { EnableOnlyDheCiphers(); EXPECT_EQ(SECSuccess, SSL_OptionSet(client_->ssl_fd(), SSL_REQUIRE_DH_NAMED_GROUPS, PR_TRUE)); auto groups_capture = new TlsExtensionCapture(ssl_supported_groups_xtn); auto shares_capture = new TlsExtensionCapture(ssl_tls13_key_share_xtn); std::vector captures; captures.push_back(groups_capture); captures.push_back(shares_capture); client_->SetPacketFilter(new ChainedPacketFilter(captures)); Connect(); CheckKeys(ssl_kea_dh, ssl_auth_rsa_sign); auto is_ffdhe = [](SSLNamedGroup group) { // The group has to be in this range. EXPECT_LE(ssl_grp_ffdhe_2048, group); EXPECT_GE(ssl_grp_ffdhe_8192, group); }; CheckGroups(groups_capture->extension(), is_ffdhe); if (version_ == SSL_LIBRARY_VERSION_TLS_1_3) { CheckShares(shares_capture->extension(), is_ffdhe); } else { EXPECT_EQ(0U, shares_capture->extension().len()); } } // Requiring the FFDHE extension on the server alone means that clients won't be // able to connect using a DHE suite. They should still connect in TLS 1.3, // because the client automatically sends the supported groups extension. TEST_P(TlsConnectGenericPre13, ConnectFfdheServer) { EnableOnlyDheCiphers(); EXPECT_EQ(SECSuccess, SSL_OptionSet(server_->ssl_fd(), SSL_REQUIRE_DH_NAMED_GROUPS, PR_TRUE)); if (version_ >= SSL_LIBRARY_VERSION_TLS_1_3) { Connect(); CheckKeys(ssl_kea_dh, ssl_auth_rsa_sign); } else { ConnectExpectFail(); client_->CheckErrorCode(SSL_ERROR_NO_CYPHER_OVERLAP); server_->CheckErrorCode(SSL_ERROR_NO_CYPHER_OVERLAP); } } class TlsDheServerKeyExchangeDamager : public TlsHandshakeFilter { public: TlsDheServerKeyExchangeDamager() {} virtual PacketFilter::Action FilterHandshake( const TlsHandshakeFilter::HandshakeHeader& header, const DataBuffer& input, DataBuffer* output) { if (header.handshake_type() != kTlsHandshakeServerKeyExchange) { return KEEP; } // Damage the first octet of dh_p. Anything other than the known prime will // be rejected as "weak" when we have SSL_REQUIRE_DH_NAMED_GROUPS enabled. *output = input; output->data()[3] ^= 73; return CHANGE; } }; // Changing the prime in the server's key share results in an error. This will // invalidate the signature over the ServerKeyShare. That's ok, NSS won't check // the signature until everything else has been checked. TEST_P(TlsConnectGenericPre13, DamageServerKeyShare) { EnableOnlyDheCiphers(); EXPECT_EQ(SECSuccess, SSL_OptionSet(client_->ssl_fd(), SSL_REQUIRE_DH_NAMED_GROUPS, PR_TRUE)); server_->SetPacketFilter(new TlsDheServerKeyExchangeDamager()); ConnectExpectFail(); client_->CheckErrorCode(SSL_ERROR_WEAK_SERVER_EPHEMERAL_DH_KEY); server_->CheckErrorCode(SSL_ERROR_ILLEGAL_PARAMETER_ALERT); } class TlsDheSkeChangeY : public TlsHandshakeFilter { public: enum ChangeYTo { kYZero, kYOne, kYPMinusOne, kYGreaterThanP, kYTooLarge, kYZeroPad }; TlsDheSkeChangeY(ChangeYTo change) : change_Y_(change) {} protected: void ChangeY(const DataBuffer& input, DataBuffer* output, size_t offset, const DataBuffer& prime) { static const uint8_t kExtraZero = 0; static const uint8_t kTooLargeExtra = 1; uint32_t dh_Ys_len; EXPECT_TRUE(input.Read(offset, 2, &dh_Ys_len)); EXPECT_LT(offset + dh_Ys_len, input.len()); offset += 2; // This isn't generally true, but our code pads. EXPECT_EQ(prime.len(), dh_Ys_len) << "Length of dh_Ys must equal length of dh_p"; *output = input; switch (change_Y_) { case kYZero: memset(output->data() + offset, 0, prime.len()); break; case kYOne: memset(output->data() + offset, 0, prime.len() - 1); output->Write(offset + prime.len() - 1, 1U, 1); break; case kYPMinusOne: output->Write(offset, prime); EXPECT_TRUE(output->data()[offset + prime.len() - 1] & 0x01) << "P must at least be odd"; --output->data()[offset + prime.len() - 1]; break; case kYGreaterThanP: // Set the first 32 octets of Y to 0xff, except the first which we set // to p[0]. This will make Y > p. That is, unless p is Mersenne, or // improbably large (but still the same bit length). We currently only // use a fixed prime that isn't a problem for this code. EXPECT_LT(0, prime.data()[0]) << "dh_p should not be zero-padded"; offset = output->Write(offset, prime.data()[0], 1); memset(output->data() + offset, 0xff, 31); break; case kYTooLarge: // Increase the dh_Ys length. output->Write(offset - 2, prime.len() + sizeof(kTooLargeExtra), 2); // Then insert the octet. output->Splice(&kTooLargeExtra, sizeof(kTooLargeExtra), offset); break; case kYZeroPad: output->Write(offset - 2, prime.len() + sizeof(kExtraZero), 2); output->Splice(&kExtraZero, sizeof(kExtraZero), offset); break; } } private: ChangeYTo change_Y_; }; class TlsDheSkeChangeYServer : public TlsDheSkeChangeY { public: TlsDheSkeChangeYServer(ChangeYTo change, bool modify) : TlsDheSkeChangeY(change), modify_(modify), p_() {} const DataBuffer& prime() const { return p_; } protected: virtual PacketFilter::Action FilterHandshake( const TlsHandshakeFilter::HandshakeHeader& header, const DataBuffer& input, DataBuffer* output) override { if (header.handshake_type() != kTlsHandshakeServerKeyExchange) { return KEEP; } size_t offset = 2; // Read dh_p uint32_t dh_len = 0; EXPECT_TRUE(input.Read(0, 2, &dh_len)); EXPECT_GT(input.len(), offset + dh_len); p_.Assign(input.data() + offset, dh_len); offset += dh_len; // Skip dh_g to find dh_Ys EXPECT_TRUE(input.Read(offset, 2, &dh_len)); offset += 2 + dh_len; if (modify_) { ChangeY(input, output, offset, p_); return CHANGE; } return KEEP; } private: bool modify_; DataBuffer p_; }; class TlsDheSkeChangeYClient : public TlsDheSkeChangeY { public: TlsDheSkeChangeYClient(ChangeYTo change, const TlsDheSkeChangeYServer* server_filter) : TlsDheSkeChangeY(change), server_filter_(server_filter) {} protected: virtual PacketFilter::Action FilterHandshake( const TlsHandshakeFilter::HandshakeHeader& header, const DataBuffer& input, DataBuffer* output) override { if (header.handshake_type() != kTlsHandshakeClientKeyExchange) { return KEEP; } ChangeY(input, output, 0, server_filter_->prime()); return CHANGE; } private: const TlsDheSkeChangeYServer* server_filter_; }; /* This matrix includes: mode (stream/datagram), TLS version, what change to * make to dh_Ys, whether the client will be configured to require DH named * groups. Test all combinations. */ typedef std::tuple DamageDHYProfile; class TlsDamageDHYTest : public TlsConnectTestBase, public ::testing::WithParamInterface { public: TlsDamageDHYTest() : TlsConnectTestBase(std::get<0>(GetParam()), std::get<1>(GetParam())) {} }; TEST_P(TlsDamageDHYTest, DamageServerY) { EnableOnlyDheCiphers(); if (std::get<3>(GetParam())) { EXPECT_EQ(SECSuccess, SSL_OptionSet(client_->ssl_fd(), SSL_REQUIRE_DH_NAMED_GROUPS, PR_TRUE)); } TlsDheSkeChangeY::ChangeYTo change = std::get<2>(GetParam()); server_->SetPacketFilter(new TlsDheSkeChangeYServer(change, true)); ConnectExpectFail(); if (change == TlsDheSkeChangeY::kYZeroPad) { // Zero padding Y only manifests in a signature failure. // In TLS 1.0 and 1.1, the client reports a device error. if (version_ < SSL_LIBRARY_VERSION_TLS_1_2) { client_->CheckErrorCode(SEC_ERROR_PKCS11_DEVICE_ERROR); } else { client_->CheckErrorCode(SEC_ERROR_BAD_SIGNATURE); } server_->CheckErrorCode(SSL_ERROR_DECRYPT_ERROR_ALERT); } else { client_->CheckErrorCode(SSL_ERROR_RX_MALFORMED_DHE_KEY_SHARE); server_->CheckErrorCode(SSL_ERROR_ILLEGAL_PARAMETER_ALERT); } } TEST_P(TlsDamageDHYTest, DamageClientY) { EnableOnlyDheCiphers(); if (std::get<3>(GetParam())) { EXPECT_EQ(SECSuccess, SSL_OptionSet(client_->ssl_fd(), SSL_REQUIRE_DH_NAMED_GROUPS, PR_TRUE)); } // The filter on the server is required to capture the prime. TlsDheSkeChangeYServer* server_filter = new TlsDheSkeChangeYServer(TlsDheSkeChangeY::kYZero, false); server_->SetPacketFilter(server_filter); // The client filter does the damage. TlsDheSkeChangeY::ChangeYTo change = std::get<2>(GetParam()); client_->SetPacketFilter(new TlsDheSkeChangeYClient(change, server_filter)); ConnectExpectFail(); if (change == TlsDheSkeChangeY::kYZeroPad) { // Zero padding Y only manifests in a finished error. client_->CheckErrorCode(SSL_ERROR_DECRYPT_ERROR_ALERT); server_->CheckErrorCode(SSL_ERROR_BAD_HANDSHAKE_HASH_VALUE); } else { client_->CheckErrorCode(SSL_ERROR_HANDSHAKE_FAILURE_ALERT); server_->CheckErrorCode(SSL_ERROR_RX_MALFORMED_DHE_KEY_SHARE); } } static const TlsDheSkeChangeY::ChangeYTo kAllYArr[] = { TlsDheSkeChangeY::kYZero, TlsDheSkeChangeY::kYOne, TlsDheSkeChangeY::kYPMinusOne, TlsDheSkeChangeY::kYGreaterThanP, TlsDheSkeChangeY::kYTooLarge, TlsDheSkeChangeY::kYZeroPad}; static ::testing::internal::ParamGenerator kAllY = ::testing::ValuesIn(kAllYArr); static const bool kTrueFalseArr[] = {true, false}; static ::testing::internal::ParamGenerator kTrueFalse = ::testing::ValuesIn(kTrueFalseArr); INSTANTIATE_TEST_CASE_P(DamageYStream, TlsDamageDHYTest, ::testing::Combine(TlsConnectTestBase::kTlsModesStream, TlsConnectTestBase::kTlsV10ToV12, kAllY, kTrueFalse)); INSTANTIATE_TEST_CASE_P( DamageYDatagram, TlsDamageDHYTest, ::testing::Combine(TlsConnectTestBase::kTlsModesDatagram, TlsConnectTestBase::kTlsV11V12, kAllY, kTrueFalse)); class TlsDheSkeMakePEven : public TlsHandshakeFilter { public: virtual PacketFilter::Action FilterHandshake( const TlsHandshakeFilter::HandshakeHeader& header, const DataBuffer& input, DataBuffer* output) { if (header.handshake_type() != kTlsHandshakeServerKeyExchange) { return KEEP; } // Find the end of dh_p uint32_t dh_len = 0; EXPECT_TRUE(input.Read(0, 2, &dh_len)); EXPECT_GT(input.len(), 2 + dh_len) << "enough space for dh_p"; size_t offset = 2 + dh_len - 1; EXPECT_TRUE((input.data()[offset] & 0x01) == 0x01) << "p should be odd"; *output = input; output->data()[offset] &= 0xfe; return CHANGE; } }; // Even without requiring named groups, an even value for p is bad news. TEST_P(TlsConnectGenericPre13, MakeDhePEven) { EnableOnlyDheCiphers(); server_->SetPacketFilter(new TlsDheSkeMakePEven()); ConnectExpectFail(); client_->CheckErrorCode(SSL_ERROR_RX_MALFORMED_DHE_KEY_SHARE); server_->CheckErrorCode(SSL_ERROR_ILLEGAL_PARAMETER_ALERT); } class TlsDheSkeZeroPadP : public TlsHandshakeFilter { public: virtual PacketFilter::Action FilterHandshake( const TlsHandshakeFilter::HandshakeHeader& header, const DataBuffer& input, DataBuffer* output) { if (header.handshake_type() != kTlsHandshakeServerKeyExchange) { return KEEP; } *output = input; uint32_t dh_len = 0; EXPECT_TRUE(input.Read(0, 2, &dh_len)); static const uint8_t kZeroPad = 0; output->Write(0, dh_len + sizeof(kZeroPad), 2); // increment the length output->Splice(&kZeroPad, sizeof(kZeroPad), 2); // insert a zero return CHANGE; } }; // Zero padding only causes signature failure. TEST_P(TlsConnectGenericPre13, PadDheP) { EnableOnlyDheCiphers(); server_->SetPacketFilter(new TlsDheSkeZeroPadP()); ConnectExpectFail(); // In TLS 1.0 and 1.1, the client reports a device error. if (version_ < SSL_LIBRARY_VERSION_TLS_1_2) { client_->CheckErrorCode(SEC_ERROR_PKCS11_DEVICE_ERROR); } else { client_->CheckErrorCode(SEC_ERROR_BAD_SIGNATURE); } server_->CheckErrorCode(SSL_ERROR_DECRYPT_ERROR_ALERT); } // The server should not pick the weak DH group if the client includes FFDHE // named groups in the supported_groups extension. The server then picks a // commonly-supported named DH group and this connects. // // Note: This test case can take ages to generate the weak DH key. TEST_P(TlsConnectGenericPre13, WeakDHGroup) { EnableOnlyDheCiphers(); EXPECT_EQ(SECSuccess, SSL_OptionSet(client_->ssl_fd(), SSL_REQUIRE_DH_NAMED_GROUPS, PR_TRUE)); EXPECT_EQ(SECSuccess, SSL_EnableWeakDHEPrimeGroup(server_->ssl_fd(), PR_TRUE)); Connect(); } TEST_P(TlsConnectGeneric, Ffdhe3072) { EnableOnlyDheCiphers(); static const std::vector groups = {ssl_grp_ffdhe_3072}; client_->ConfigNamedGroups(groups); Connect(); } // Even though the client doesn't have DHE groups enabled the server assumes it // does. Because the client doesn't require named groups it accepts FF3072 as // custom group. TEST_P(TlsConnectGenericPre13, NamedGroupMismatchPre13) { EnableOnlyDheCiphers(); static const std::vector server_groups = {ssl_grp_ffdhe_3072}; static const std::vector client_groups = { ssl_grp_ec_secp256r1}; server_->ConfigNamedGroups(server_groups); client_->ConfigNamedGroups(client_groups); Connect(); CheckKeys(ssl_kea_dh, ssl_grp_ffdhe_custom, ssl_auth_rsa_sign, ssl_sig_rsa_pss_sha256); } // Same test but for TLS 1.3. This has to fail. TEST_P(TlsConnectTls13, NamedGroupMismatch13) { EnableOnlyDheCiphers(); static const std::vector server_groups = {ssl_grp_ffdhe_3072}; static const std::vector client_groups = { ssl_grp_ec_secp256r1}; server_->ConfigNamedGroups(server_groups); client_->ConfigNamedGroups(client_groups); ConnectExpectFail(); server_->CheckErrorCode(SSL_ERROR_NO_CYPHER_OVERLAP); client_->CheckErrorCode(SSL_ERROR_NO_CYPHER_OVERLAP); } // Even though the client doesn't have DHE groups enabled the server assumes it // does. The client requires named groups and thus does not accept FF3072 as // custom group in contrast to the previous test. TEST_P(TlsConnectGenericPre13, RequireNamedGroupsMismatchPre13) { EnableOnlyDheCiphers(); EXPECT_EQ(SECSuccess, SSL_OptionSet(client_->ssl_fd(), SSL_REQUIRE_DH_NAMED_GROUPS, PR_TRUE)); static const std::vector server_groups = {ssl_grp_ffdhe_3072}; static const std::vector client_groups = {ssl_grp_ec_secp256r1, ssl_grp_ffdhe_2048}; server_->ConfigNamedGroups(server_groups); client_->ConfigNamedGroups(client_groups); ConnectExpectFail(); server_->CheckErrorCode(SSL_ERROR_NO_CYPHER_OVERLAP); client_->CheckErrorCode(SSL_ERROR_NO_CYPHER_OVERLAP); } TEST_P(TlsConnectGenericPre13, PreferredFfdhe) { EnableOnlyDheCiphers(); static const SSLDHEGroupType groups[] = {ssl_ff_dhe_3072_group, ssl_ff_dhe_2048_group}; EXPECT_EQ(SECSuccess, SSL_DHEGroupPrefSet(server_->ssl_fd(), groups, PR_ARRAY_SIZE(groups))); Connect(); client_->CheckKEA(ssl_kea_dh, ssl_grp_ffdhe_3072, 3072); server_->CheckKEA(ssl_kea_dh, ssl_grp_ffdhe_3072, 3072); client_->CheckAuthType(ssl_auth_rsa_sign, ssl_sig_rsa_pss_sha256); server_->CheckAuthType(ssl_auth_rsa_sign, ssl_sig_rsa_pss_sha256); } TEST_P(TlsConnectGenericPre13, MismatchDHE) { EnableOnlyDheCiphers(); EXPECT_EQ(SECSuccess, SSL_OptionSet(client_->ssl_fd(), SSL_REQUIRE_DH_NAMED_GROUPS, PR_TRUE)); static const SSLDHEGroupType serverGroups[] = {ssl_ff_dhe_3072_group}; EXPECT_EQ(SECSuccess, SSL_DHEGroupPrefSet(server_->ssl_fd(), serverGroups, PR_ARRAY_SIZE(serverGroups))); static const SSLDHEGroupType clientGroups[] = {ssl_ff_dhe_2048_group}; EXPECT_EQ(SECSuccess, SSL_DHEGroupPrefSet(client_->ssl_fd(), clientGroups, PR_ARRAY_SIZE(clientGroups))); ConnectExpectFail(); server_->CheckErrorCode(SSL_ERROR_NO_CYPHER_OVERLAP); client_->CheckErrorCode(SSL_ERROR_NO_CYPHER_OVERLAP); } TEST_P(TlsConnectTls13, ResumeFfdhe) { EnableOnlyDheCiphers(); ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET); Connect(); SendReceive(); // Need to read so that we absorb the session ticket. CheckKeys(ssl_kea_dh, ssl_auth_rsa_sign); Reset(); ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET); EnableOnlyDheCiphers(); TlsExtensionCapture* clientCapture = new TlsExtensionCapture(ssl_tls13_pre_shared_key_xtn); client_->SetPacketFilter(clientCapture); TlsExtensionCapture* serverCapture = new TlsExtensionCapture(ssl_tls13_pre_shared_key_xtn); server_->SetPacketFilter(serverCapture); ExpectResumption(RESUME_TICKET); Connect(); CheckKeys(ssl_kea_dh, ssl_grp_ffdhe_2048, ssl_auth_rsa_sign, ssl_sig_none); ASSERT_LT(0UL, clientCapture->extension().len()); ASSERT_LT(0UL, serverCapture->extension().len()); } class TlsDheSkeChangeSignature : public TlsHandshakeFilter { public: TlsDheSkeChangeSignature(uint16_t version, const uint8_t* data, size_t len) : version_(version), data_(data), len_(len) {} protected: virtual PacketFilter::Action FilterHandshake(const HandshakeHeader& header, const DataBuffer& input, DataBuffer* output) { if (header.handshake_type() != kTlsHandshakeServerKeyExchange) { return KEEP; } TlsParser parser(input); EXPECT_TRUE(parser.SkipVariable(2)); // dh_p EXPECT_TRUE(parser.SkipVariable(2)); // dh_g EXPECT_TRUE(parser.SkipVariable(2)); // dh_Ys // Copy DH params to output. size_t offset = output->Write(0, input.data(), parser.consumed()); if (version_ == SSL_LIBRARY_VERSION_TLS_1_2) { // Write signature algorithm. offset = output->Write(offset, ssl_sig_dsa_sha256, 2); } // Write new signature. offset = output->Write(offset, len_, 2); offset = output->Write(offset, data_, len_); return CHANGE; } private: uint16_t version_; const uint8_t* data_; size_t len_; }; TEST_P(TlsConnectGenericPre13, InvalidDERSignatureFfdhe) { const uint8_t kBogusDheSignature[] = { 0x30, 0x69, 0x3c, 0x02, 0x1c, 0x7d, 0x0b, 0x2f, 0x64, 0x00, 0x27, 0xae, 0xcf, 0x1e, 0x28, 0x08, 0x6a, 0x7f, 0xb1, 0xbd, 0x78, 0xb5, 0x3b, 0x8c, 0x8f, 0x59, 0xed, 0x8f, 0xee, 0x78, 0xeb, 0x2c, 0xe9, 0x02, 0x1c, 0x6d, 0x7f, 0x3c, 0x0f, 0xf4, 0x44, 0x35, 0x0b, 0xb2, 0x6d, 0xdc, 0xb8, 0x21, 0x87, 0xdd, 0x0d, 0xb9, 0x46, 0x09, 0x3e, 0xef, 0x81, 0x5b, 0x37, 0x09, 0x39, 0xeb}; Reset(TlsAgent::kServerDsa); const std::vector client_groups = {ssl_grp_ffdhe_2048}; client_->ConfigNamedGroups(client_groups); server_->SetPacketFilter(new TlsDheSkeChangeSignature( version_, kBogusDheSignature, sizeof(kBogusDheSignature))); ConnectExpectFail(); client_->CheckErrorCode(SSL_ERROR_BAD_HANDSHAKE_HASH_VALUE); } } // namespace nss_test