summaryrefslogtreecommitdiff
path: root/nss/gtests/ssl_gtest/ssl_dhe_unittest.cc
diff options
context:
space:
mode:
Diffstat (limited to 'nss/gtests/ssl_gtest/ssl_dhe_unittest.cc')
-rw-r--r--nss/gtests/ssl_gtest/ssl_dhe_unittest.cc609
1 files changed, 609 insertions, 0 deletions
diff --git a/nss/gtests/ssl_gtest/ssl_dhe_unittest.cc b/nss/gtests/ssl_gtest/ssl_dhe_unittest.cc
new file mode 100644
index 0000000..82d5558
--- /dev/null
+++ b/nss/gtests/ssl_gtest/ssl_dhe_unittest.cc
@@ -0,0 +1,609 @@
+/* -*- 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 "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<PacketFilter*> 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<PacketFilter*> 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<std::string, 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())) {
+ 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<TlsDheSkeChangeY::ChangeYTo> kAllY =
+ ::testing::ValuesIn(kAllYArr);
+static const bool kTrueFalseArr[] = {true, false};
+static ::testing::internal::ParamGenerator<bool> 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<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_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);
+
+ 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<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);
+
+ 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<SSLNamedGroup> 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