diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
commit | 36d22d82aa202bb199967e9512281e9a53db42c9 (patch) | |
tree | 105e8c98ddea1c1e4784a60a5a6410fa416be2de /security/nss/gtests/ssl_gtest/ssl_dhe_unittest.cc | |
parent | Initial commit. (diff) | |
download | firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip |
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'security/nss/gtests/ssl_gtest/ssl_dhe_unittest.cc')
-rw-r--r-- | security/nss/gtests/ssl_gtest/ssl_dhe_unittest.cc | 802 |
1 files changed, 802 insertions, 0 deletions
diff --git a/security/nss/gtests/ssl_gtest/ssl_dhe_unittest.cc b/security/nss/gtests/ssl_gtest/ssl_dhe_unittest.cc new file mode 100644 index 0000000000..09beb2a6d9 --- /dev/null +++ b/security/nss/gtests/ssl_gtest/ssl_dhe_unittest.cc @@ -0,0 +1,802 @@ +/* -*- 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 <set> +#include "secerr.h" +#include "ssl.h" +#include "sslerr.h" +#include "sslproto.h" + +#include "gtest_utils.h" +#include "nss_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_rsae_sha256); +} + +TEST_P(TlsConnectTls13, SharesForBothEcdheAndDhe) { + EnsureTlsSetup(); + client_->ConfigNamedGroups(kAllDHEGroups); + + auto groups_capture = + std::make_shared<TlsExtensionCapture>(client_, ssl_supported_groups_xtn); + auto shares_capture = + std::make_shared<TlsExtensionCapture>(client_, ssl_tls13_key_share_xtn); + std::vector<std::shared_ptr<PacketFilter>> captures = {groups_capture, + shares_capture}; + client_->SetFilter(std::make_shared<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(); + client_->SetOption(SSL_REQUIRE_DH_NAMED_GROUPS, PR_TRUE); + auto groups_capture = + std::make_shared<TlsExtensionCapture>(client_, ssl_supported_groups_xtn); + auto shares_capture = + std::make_shared<TlsExtensionCapture>(client_, ssl_tls13_key_share_xtn); + std::vector<std::shared_ptr<PacketFilter>> captures = {groups_capture, + shares_capture}; + client_->SetFilter(std::make_shared<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(); + server_->SetOption(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 { + ConnectExpectAlert(server_, kTlsAlertHandshakeFailure); + client_->CheckErrorCode(SSL_ERROR_NO_CYPHER_OVERLAP); + server_->CheckErrorCode(SSL_ERROR_NO_CYPHER_OVERLAP); + } +} + +class TlsDheServerKeyExchangeDamager : public TlsHandshakeFilter { + public: + TlsDheServerKeyExchangeDamager(const std::shared_ptr<TlsAgent>& a) + : TlsHandshakeFilter(a, {kTlsHandshakeServerKeyExchange}) {} + virtual PacketFilter::Action FilterHandshake( + const TlsHandshakeFilter::HandshakeHeader& header, + const DataBuffer& input, DataBuffer* output) { + // 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(); + client_->SetOption(SSL_REQUIRE_DH_NAMED_GROUPS, PR_TRUE); + MakeTlsFilter<TlsDheServerKeyExchangeDamager>(server_); + + ConnectExpectAlert(client_, kTlsAlertIllegalParameter); + + 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(const std::shared_ptr<TlsAgent>& a, uint8_t handshake_type, + ChangeYTo change) + : TlsHandshakeFilter(a, {handshake_type}), 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(const std::shared_ptr<TlsAgent>& a, ChangeYTo change, + bool modify) + : TlsDheSkeChangeY(a, kTlsHandshakeServerKeyExchange, 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 { + 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( + const std::shared_ptr<TlsAgent>& a, ChangeYTo change, + std::shared_ptr<const TlsDheSkeChangeYServer> server_filter) + : TlsDheSkeChangeY(a, kTlsHandshakeClientKeyExchange, change), + server_filter_(server_filter) {} + + protected: + virtual PacketFilter::Action FilterHandshake( + const TlsHandshakeFilter::HandshakeHeader& header, + const DataBuffer& input, DataBuffer* output) override { + ChangeY(input, output, 0, server_filter_->prime()); + return CHANGE; + } + + private: + std::shared_ptr<const TlsDheSkeChangeYServer> server_filter_; +}; + +/* This matrix includes: variant (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<SSLProtocolVariant, uint16_t, TlsDheSkeChangeY::ChangeYTo, + bool> + DamageDHYProfile; +class TlsDamageDHYTest + : public TlsConnectTestBase, + public ::testing::WithParamInterface<DamageDHYProfile> { + public: + TlsDamageDHYTest() + : TlsConnectTestBase(std::get<0>(GetParam()), std::get<1>(GetParam())) {} +}; + +TEST_P(TlsDamageDHYTest, DamageServerY) { + EnableOnlyDheCiphers(); + if (std::get<3>(GetParam())) { + client_->SetOption(SSL_REQUIRE_DH_NAMED_GROUPS, PR_TRUE); + } + TlsDheSkeChangeY::ChangeYTo change = std::get<2>(GetParam()); + MakeTlsFilter<TlsDheSkeChangeYServer>(server_, change, true); + + if (change == TlsDheSkeChangeY::kYZeroPad) { + ExpectAlert(client_, kTlsAlertDecryptError); + } else { + ExpectAlert(client_, kTlsAlertIllegalParameter); + } + 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())) { + client_->SetOption(SSL_REQUIRE_DH_NAMED_GROUPS, PR_TRUE); + } + // The filter on the server is required to capture the prime. + auto server_filter = MakeTlsFilter<TlsDheSkeChangeYServer>( + server_, TlsDheSkeChangeY::kYZero, false); + + // The client filter does the damage. + TlsDheSkeChangeY::ChangeYTo change = std::get<2>(GetParam()); + MakeTlsFilter<TlsDheSkeChangeYClient>(client_, change, server_filter); + + if (change == TlsDheSkeChangeY::kYZeroPad) { + ExpectAlert(server_, kTlsAlertDecryptError); + } else { + ExpectAlert(server_, kTlsAlertHandshakeFailure); + } + 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<TlsDheSkeChangeY::ChangeYTo> kAllY = + ::testing::ValuesIn(kAllYArr); +static const bool kTrueFalseArr[] = {true, false}; +static ::testing::internal::ParamGenerator<bool> kTrueFalse = + ::testing::ValuesIn(kTrueFalseArr); + +INSTANTIATE_TEST_SUITE_P( + DamageYStream, TlsDamageDHYTest, + ::testing::Combine(TlsConnectTestBase::kTlsVariantsStream, + TlsConnectTestBase::kTlsV10ToV12, kAllY, kTrueFalse)); +INSTANTIATE_TEST_SUITE_P( + DamageYDatagram, TlsDamageDHYTest, + ::testing::Combine(TlsConnectTestBase::kTlsVariantsDatagram, + TlsConnectTestBase::kTlsV11V12, kAllY, kTrueFalse)); + +class TlsDheSkeMakePEven : public TlsHandshakeFilter { + public: + TlsDheSkeMakePEven(const std::shared_ptr<TlsAgent>& a) + : TlsHandshakeFilter(a, {kTlsHandshakeServerKeyExchange}) {} + + virtual PacketFilter::Action FilterHandshake( + const TlsHandshakeFilter::HandshakeHeader& header, + const DataBuffer& input, DataBuffer* output) { + // 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(); + MakeTlsFilter<TlsDheSkeMakePEven>(server_); + + ConnectExpectAlert(client_, kTlsAlertIllegalParameter); + + client_->CheckErrorCode(SSL_ERROR_RX_MALFORMED_DHE_KEY_SHARE); + server_->CheckErrorCode(SSL_ERROR_ILLEGAL_PARAMETER_ALERT); +} + +class TlsDheSkeZeroPadP : public TlsHandshakeFilter { + public: + TlsDheSkeZeroPadP(const std::shared_ptr<TlsAgent>& a) + : TlsHandshakeFilter(a, {kTlsHandshakeServerKeyExchange}) {} + + virtual PacketFilter::Action FilterHandshake( + const TlsHandshakeFilter::HandshakeHeader& header, + const DataBuffer& input, DataBuffer* output) { + *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(); + MakeTlsFilter<TlsDheSkeZeroPadP>(server_); + + ConnectExpectAlert(client_, kTlsAlertDecryptError); + + // 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(); + client_->SetOption(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<SSLNamedGroup> 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<SSLNamedGroup> server_groups = {ssl_grp_ffdhe_3072}; + static const std::vector<SSLNamedGroup> 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_rsae_sha256); +} + +// Same test but for TLS 1.3. This has to fail. +TEST_P(TlsConnectTls13, NamedGroupMismatch13) { + EnableOnlyDheCiphers(); + static const std::vector<SSLNamedGroup> server_groups = {ssl_grp_ffdhe_3072}; + static const std::vector<SSLNamedGroup> client_groups = { + ssl_grp_ec_secp256r1}; + server_->ConfigNamedGroups(server_groups); + client_->ConfigNamedGroups(client_groups); + + ConnectExpectAlert(server_, kTlsAlertHandshakeFailure); + server_->CheckErrorCode(SSL_ERROR_NO_CYPHER_OVERLAP); + client_->CheckErrorCode(SSL_ERROR_NO_CYPHER_OVERLAP); +} + +// Replace the key share in the server key exchange message with one that's +// larger than 8192 bits. +class TooLongDHEServerKEXFilter : public TlsHandshakeFilter { + public: + TooLongDHEServerKEXFilter(const std::shared_ptr<TlsAgent>& server) + : TlsHandshakeFilter(server, {kTlsHandshakeServerKeyExchange}) {} + + protected: + virtual PacketFilter::Action FilterHandshake(const HandshakeHeader& header, + const DataBuffer& input, + DataBuffer* output) { + // Replace the server key exchange message very large DH shares that are + // not supported by NSS. + const uint32_t share_len = 0x401; + const uint8_t zero_share[share_len] = {0x80}; + size_t offset = 0; + // Write dh_p. + offset = output->Write(offset, share_len, 2); + offset = output->Write(offset, zero_share, share_len); + // Write dh_g. + offset = output->Write(offset, share_len, 2); + offset = output->Write(offset, zero_share, share_len); + // Write dh_Y. + offset = output->Write(offset, share_len, 2); + offset = output->Write(offset, zero_share, share_len); + + return CHANGE; + } +}; + +TEST_P(TlsConnectGenericPre13, TooBigDHGroup) { + EnableOnlyDheCiphers(); + MakeTlsFilter<TooLongDHEServerKEXFilter>(server_); + client_->SetOption(SSL_REQUIRE_DH_NAMED_GROUPS, PR_FALSE); + ConnectExpectAlert(client_, kTlsAlertIllegalParameter); + server_->CheckErrorCode(SSL_ERROR_ILLEGAL_PARAMETER_ALERT); + client_->CheckErrorCode(SSL_ERROR_DH_KEY_TOO_LONG); +} + +// 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(); + client_->SetOption(SSL_REQUIRE_DH_NAMED_GROUPS, PR_TRUE); + static const std::vector<SSLNamedGroup> server_groups = {ssl_grp_ffdhe_3072}; + static const std::vector<SSLNamedGroup> client_groups = {ssl_grp_ec_secp256r1, + ssl_grp_ffdhe_2048}; + server_->ConfigNamedGroups(server_groups); + client_->ConfigNamedGroups(client_groups); + + ConnectExpectAlert(server_, kTlsAlertHandshakeFailure); + 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_rsae_sha256); + server_->CheckAuthType(ssl_auth_rsa_sign, ssl_sig_rsa_pss_rsae_sha256); +} + +TEST_P(TlsConnectGenericPre13, MismatchDHE) { + EnableOnlyDheCiphers(); + client_->SetOption(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))); + + ConnectExpectAlert(server_, kTlsAlertHandshakeFailure); + 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_grp_ffdhe_2048, ssl_auth_rsa_sign, + ssl_sig_rsa_pss_rsae_sha256); + + Reset(); + ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET); + EnableOnlyDheCiphers(); + auto clientCapture = + MakeTlsFilter<TlsExtensionCapture>(client_, ssl_tls13_pre_shared_key_xtn); + auto serverCapture = + MakeTlsFilter<TlsExtensionCapture>(server_, ssl_tls13_pre_shared_key_xtn); + ExpectResumption(RESUME_TICKET); + Connect(); + CheckKeys(ssl_kea_dh, ssl_grp_ffdhe_2048, ssl_auth_rsa_sign, + ssl_sig_rsa_pss_rsae_sha256); + ASSERT_LT(0UL, clientCapture->extension().len()); + ASSERT_LT(0UL, serverCapture->extension().len()); +} + +class TlsDheSkeChangeSignature : public TlsHandshakeFilter { + public: + TlsDheSkeChangeSignature(const std::shared_ptr<TlsAgent>& a, uint16_t version, + const uint8_t* data, size_t len) + : TlsHandshakeFilter(a, {kTlsHandshakeServerKeyExchange}), + version_(version), + data_(data), + len_(len) {} + + protected: + virtual PacketFilter::Action FilterHandshake(const HandshakeHeader& header, + const DataBuffer& input, + DataBuffer* output) { + 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<SSLNamedGroup> client_groups = {ssl_grp_ffdhe_2048}; + client_->ConfigNamedGroups(client_groups); + + MakeTlsFilter<TlsDheSkeChangeSignature>(server_, version_, kBogusDheSignature, + sizeof(kBogusDheSignature)); + + ConnectExpectAlert(client_, kTlsAlertDecryptError); + client_->CheckErrorCode(SSL_ERROR_BAD_HANDSHAKE_HASH_VALUE); +} + +TEST_P(TlsConnectTls12, ConnectInconsistentSigAlgDHE) { + EnableOnlyDheCiphers(); + + MakeTlsFilter<DHEServerKEXSigAlgReplacer>(server_, + ssl_sig_ecdsa_secp256r1_sha256); + ConnectExpectAlert(client_, kTlsAlertIllegalParameter); +} + +static void CheckSkeSigScheme( + std::shared_ptr<TlsHandshakeRecorder>& capture_ske, + uint16_t expected_scheme) { + TlsParser parser(capture_ske->buffer()); + EXPECT_TRUE(parser.SkipVariable(2)) << " read dh_p"; + EXPECT_TRUE(parser.SkipVariable(2)) << " read dh_q"; + EXPECT_TRUE(parser.SkipVariable(2)) << " read dh_Ys"; + + uint32_t tmp; + EXPECT_TRUE(parser.Read(&tmp, 2)) << " read sig_scheme"; + EXPECT_EQ(expected_scheme, static_cast<uint16_t>(tmp)); +} + +TEST_P(TlsConnectTls12, ConnectSigAlgEnabledByPolicyDhe) { + EnableOnlyDheCiphers(); + + const std::vector<SSLSignatureScheme> schemes = {ssl_sig_rsa_pkcs1_sha1, + ssl_sig_rsa_pkcs1_sha384}; + + EnsureTlsSetup(); + client_->SetSignatureSchemes(schemes.data(), schemes.size()); + server_->SetSignatureSchemes(schemes.data(), schemes.size()); + auto capture_ske = MakeTlsFilter<TlsHandshakeRecorder>( + server_, kTlsHandshakeServerKeyExchange); + + StartConnect(); + client_->Handshake(); // Send ClientHello + + // Enable SHA-1 by policy. + SECStatus rv = NSS_SetAlgorithmPolicy(SEC_OID_SHA1, NSS_USE_ALG_IN_SSL_KX, 0); + ASSERT_EQ(SECSuccess, rv); + rv = NSS_SetAlgorithmPolicy(SEC_OID_APPLY_SSL_POLICY, NSS_USE_POLICY_IN_SSL, + 0); + ASSERT_EQ(SECSuccess, rv); + + Handshake(); // Remainder of handshake + // The server should now report that it is connected + EXPECT_EQ(TlsAgent::STATE_CONNECTED, server_->state()); + + CheckSkeSigScheme(capture_ske, ssl_sig_rsa_pkcs1_sha1); +} + +TEST_P(TlsConnectTls12, ConnectSigAlgDisabledByPolicyDhe) { + EnableOnlyDheCiphers(); + + const std::vector<SSLSignatureScheme> schemes = {ssl_sig_rsa_pkcs1_sha1, + ssl_sig_rsa_pkcs1_sha384}; + + EnsureTlsSetup(); + client_->SetSignatureSchemes(schemes.data(), schemes.size()); + server_->SetSignatureSchemes(schemes.data(), schemes.size()); + auto capture_ske = MakeTlsFilter<TlsHandshakeRecorder>( + server_, kTlsHandshakeServerKeyExchange); + + StartConnect(); + client_->Handshake(); // Send ClientHello + + // Disable SHA-1 by policy after sending ClientHello so that CH + // includes SHA-1 signature scheme. + SECStatus rv = NSS_SetAlgorithmPolicy(SEC_OID_SHA1, 0, NSS_USE_ALG_IN_SSL_KX); + ASSERT_EQ(SECSuccess, rv); + rv = NSS_SetAlgorithmPolicy(SEC_OID_APPLY_SSL_POLICY, NSS_USE_POLICY_IN_SSL, + 0); + ASSERT_EQ(SECSuccess, rv); + + Handshake(); // Remainder of handshake + // The server should now report that it is connected + EXPECT_EQ(TlsAgent::STATE_CONNECTED, server_->state()); + + CheckSkeSigScheme(capture_ske, ssl_sig_rsa_pkcs1_sha384); +} + +TEST_P(TlsConnectPre12, ConnectSigAlgDisabledWeakGroupByOption3072DhePre12) { + EnableOnlyDheCiphers(); + + // explicitly enable the weak groups + EXPECT_EQ(SECSuccess, + SSL_EnableWeakDHEPrimeGroup(server_->ssl_fd(), PR_TRUE)); + EXPECT_EQ(SECSuccess, + SSL_EnableWeakDHEPrimeGroup(client_->ssl_fd(), PR_TRUE)); + server_->SetNssOption(NSS_DH_MIN_KEY_SIZE, 3072); + Connect(); + client_->CheckKEA(ssl_kea_dh, ssl_grp_ffdhe_3072, 3072); + server_->CheckKEA(ssl_kea_dh, ssl_grp_ffdhe_3072, 3072); +} + +TEST_P(TlsConnectPre12, ConnectSigAlgDisabledWeakGroupByOption2048DhePre12) { + EnableOnlyDheCiphers(); + + // explicitly enable the weak groups + EXPECT_EQ(SECSuccess, + SSL_EnableWeakDHEPrimeGroup(server_->ssl_fd(), PR_TRUE)); + EXPECT_EQ(SECSuccess, + SSL_EnableWeakDHEPrimeGroup(client_->ssl_fd(), PR_TRUE)); + server_->SetNssOption(NSS_DH_MIN_KEY_SIZE, 2048); + Connect(); + client_->CheckKEA(ssl_kea_dh, ssl_grp_ffdhe_2048, 2048); + server_->CheckKEA(ssl_kea_dh, ssl_grp_ffdhe_2048, 2048); +} + +TEST_P(TlsConnectPre12, ConnectSigAlgDisabledByPolicyDhePre12) { + EnableOnlyDheCiphers(); + + EnsureTlsSetup(); + StartConnect(); + client_->Handshake(); // Send ClientHello + + // Disable SHA-1 by policy. This will cause the connection fail as + // TLS 1.1 or earlier uses combined SHA-1 + MD5 signature. + SECStatus rv = NSS_SetAlgorithmPolicy(SEC_OID_SHA1, 0, NSS_USE_ALG_IN_SSL_KX); + ASSERT_EQ(SECSuccess, rv); + rv = NSS_SetAlgorithmPolicy(SEC_OID_APPLY_SSL_POLICY, NSS_USE_POLICY_IN_SSL, + 0); + ASSERT_EQ(SECSuccess, rv); + + server_->ExpectSendAlert(kTlsAlertHandshakeFailure); + client_->ExpectReceiveAlert(kTlsAlertHandshakeFailure); + + // Remainder of handshake + Handshake(); + + server_->CheckErrorCode(SSL_ERROR_UNSUPPORTED_HASH_ALGORITHM); +} + +TEST_P(TlsConnectTls12, ConnectSigAlgDisablePreferredGroupByOption3072Dhe) { + EnableOnlyDheCiphers(); + static const SSLDHEGroupType dhe_groups[] = { + ssl_ff_dhe_2048_group, // first in the lists is the preferred group + ssl_ff_dhe_3072_group}; + + server_->SetNssOption(NSS_DH_MIN_KEY_SIZE, 3072); + EXPECT_EQ(SECSuccess, SSL_DHEGroupPrefSet(server_->ssl_fd(), &dhe_groups[0], + PR_ARRAY_SIZE(dhe_groups))); + Connect(); + // our option size should override the preferred group + client_->CheckKEA(ssl_kea_dh, ssl_grp_ffdhe_3072, 3072); + server_->CheckKEA(ssl_kea_dh, ssl_grp_ffdhe_3072, 3072); +} + +TEST_P(TlsConnectTls12, ConnectSigAlgDisableGroupByOption3072Dhe) { + EnableOnlyDheCiphers(); + + server_->SetNssOption(NSS_DH_MIN_KEY_SIZE, 3072); + Connect(); + client_->CheckKEA(ssl_kea_dh, ssl_grp_ffdhe_3072, 3072); + server_->CheckKEA(ssl_kea_dh, ssl_grp_ffdhe_3072, 3072); +} + +} // namespace nss_test |