802 lines
28 KiB
C++
802 lines
28 KiB
C++
/* -*- 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
|