blob: a185104404294e4433233aa50afe821041f9f8a3 [file] [log] [blame]
/* -*- 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 "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 {
// This class tracks the maximum size of record that was sent, both cleartext
// and plain. It only tracks records that have an outer type of
// application_data or DTLSCiphertext. In TLS 1.3, this includes handshake
// messages.
class TlsRecordMaximum : public TlsRecordFilter {
public:
TlsRecordMaximum(const std::shared_ptr<TlsAgent>& a)
: TlsRecordFilter(a), max_ciphertext_(0), max_plaintext_(0) {}
size_t max_ciphertext() const { return max_ciphertext_; }
size_t max_plaintext() const { return max_plaintext_; }
protected:
PacketFilter::Action FilterRecord(const TlsRecordHeader& header,
const DataBuffer& record, size_t* offset,
DataBuffer* output) override {
std::cerr << "max: " << record << std::endl;
// Ignore unprotected packets.
if (!header.is_protected()) {
return KEEP;
}
max_ciphertext_ = (std::max)(max_ciphertext_, record.len());
return TlsRecordFilter::FilterRecord(header, record, offset, output);
}
PacketFilter::Action FilterRecord(const TlsRecordHeader& header,
const DataBuffer& data,
DataBuffer* changed) override {
max_plaintext_ = (std::max)(max_plaintext_, data.len());
return KEEP;
}
private:
size_t max_ciphertext_;
size_t max_plaintext_;
};
void CheckRecordSizes(const std::shared_ptr<TlsAgent>& agent,
const std::shared_ptr<TlsRecordMaximum>& record_max,
size_t config) {
uint16_t cipher_suite;
ASSERT_TRUE(agent->cipher_suite(&cipher_suite));
size_t expansion;
size_t iv;
switch (cipher_suite) {
case TLS_AES_128_GCM_SHA256:
case TLS_AES_256_GCM_SHA384:
case TLS_CHACHA20_POLY1305_SHA256:
expansion = 16;
iv = 0;
break;
case TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256:
expansion = 16;
iv = 8;
break;
case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA:
// Expansion is 20 for the MAC. Maximum block padding is 16. Maximum
// padding is added when the input plus the MAC is an exact multiple of
// the block size.
expansion = 20 + 16 - ((config + 20) % 16);
iv = 16;
break;
default:
ADD_FAILURE() << "No expansion set for ciphersuite "
<< agent->cipher_suite_name();
return;
}
switch (agent->version()) {
case SSL_LIBRARY_VERSION_TLS_1_3:
EXPECT_EQ(0U, iv) << "No IV for TLS 1.3";
// We only have decryption in TLS 1.3.
EXPECT_EQ(config - 1, record_max->max_plaintext())
<< "bad plaintext length for " << agent->role_str();
break;
case SSL_LIBRARY_VERSION_TLS_1_2:
case SSL_LIBRARY_VERSION_TLS_1_1:
expansion += iv;
break;
case SSL_LIBRARY_VERSION_TLS_1_0:
break;
default:
ADD_FAILURE() << "Unexpected version " << agent->version();
return;
}
EXPECT_EQ(config + expansion, record_max->max_ciphertext())
<< "bad ciphertext length for " << agent->role_str();
}
TEST_P(TlsConnectGeneric, RecordSizeMaximum) {
uint16_t max_record_size =
(version_ >= SSL_LIBRARY_VERSION_TLS_1_3) ? 16385 : 16384;
size_t send_size = (version_ >= SSL_LIBRARY_VERSION_TLS_1_3)
? max_record_size
: max_record_size + 1;
EnsureTlsSetup();
auto client_max = MakeTlsFilter<TlsRecordMaximum>(client_);
auto server_max = MakeTlsFilter<TlsRecordMaximum>(server_);
if (version_ >= SSL_LIBRARY_VERSION_TLS_1_3) {
client_max->EnableDecryption();
server_max->EnableDecryption();
}
Connect();
client_->SendData(send_size, send_size);
server_->SendData(send_size, send_size);
server_->ReadBytes(send_size);
client_->ReadBytes(send_size);
CheckRecordSizes(client_, client_max, max_record_size);
CheckRecordSizes(server_, server_max, max_record_size);
}
TEST_P(TlsConnectGeneric, RecordSizeMinimumClient) {
EnsureTlsSetup();
auto server_max = MakeTlsFilter<TlsRecordMaximum>(server_);
if (version_ >= SSL_LIBRARY_VERSION_TLS_1_3) {
server_max->EnableDecryption();
}
client_->SetOption(SSL_RECORD_SIZE_LIMIT, 64);
Connect();
SendReceive(127); // Big enough for one record, allowing for 1+N splitting.
CheckRecordSizes(server_, server_max, 64);
}
TEST_P(TlsConnectGeneric, RecordSizeMinimumServer) {
EnsureTlsSetup();
auto client_max = MakeTlsFilter<TlsRecordMaximum>(client_);
if (version_ >= SSL_LIBRARY_VERSION_TLS_1_3) {
client_max->EnableDecryption();
}
server_->SetOption(SSL_RECORD_SIZE_LIMIT, 64);
Connect();
SendReceive(127);
CheckRecordSizes(client_, client_max, 64);
}
TEST_P(TlsConnectGeneric, RecordSizeAsymmetric) {
EnsureTlsSetup();
auto client_max = MakeTlsFilter<TlsRecordMaximum>(client_);
auto server_max = MakeTlsFilter<TlsRecordMaximum>(server_);
if (version_ >= SSL_LIBRARY_VERSION_TLS_1_3) {
client_max->EnableDecryption();
server_max->EnableDecryption();
}
client_->SetOption(SSL_RECORD_SIZE_LIMIT, 64);
server_->SetOption(SSL_RECORD_SIZE_LIMIT, 100);
Connect();
SendReceive(127);
CheckRecordSizes(client_, client_max, 100);
CheckRecordSizes(server_, server_max, 64);
}
// This just modifies the encrypted payload so to include a few extra zeros.
class TlsRecordExpander : public TlsRecordFilter {
public:
TlsRecordExpander(const std::shared_ptr<TlsAgent>& a, size_t expansion)
: TlsRecordFilter(a), expansion_(expansion) {}
protected:
virtual PacketFilter::Action FilterRecord(const TlsRecordHeader& header,
const DataBuffer& data,
DataBuffer* changed) {
if (!header.is_protected()) {
// We're targeting application_data records. If the record is
// |!is_protected()|, we have two possibilities:
if (!decrypting()) {
// 1) We're not decrypting, in which this case this is truly an
// unencrypted record (Keep).
return KEEP;
}
if (header.content_type() != ssl_ct_application_data) {
// 2) We are decrypting, so is_protected() read the internal
// content_type. If the internal ct IS NOT application_data, then
// it's not our target (Keep).
return KEEP;
}
// Otherwise, the the internal ct IS application_data (Change).
}
changed->Allocate(data.len() + expansion_);
changed->Write(0, data.data(), data.len());
return CHANGE;
}
private:
size_t expansion_;
};
// Tweak the plaintext of server records so that they exceed the client's limit.
TEST_P(TlsConnectTls13, RecordSizePlaintextExceed) {
EnsureTlsSetup();
auto server_expand = MakeTlsFilter<TlsRecordExpander>(server_, 1);
server_expand->EnableDecryption();
client_->SetOption(SSL_RECORD_SIZE_LIMIT, 64);
Connect();
server_->SendData(100);
client_->ExpectReadWriteError();
ExpectAlert(client_, kTlsAlertRecordOverflow);
client_->ReadBytes(100);
EXPECT_EQ(SSL_ERROR_RX_RECORD_TOO_LONG, client_->error_code());
// Consume the alert at the server.
server_->Handshake();
server_->CheckErrorCode(SSL_ERROR_RECORD_OVERFLOW_ALERT);
}
// Tweak the ciphertext of server records so that they greatly exceed the limit.
// This requires a much larger expansion than for plaintext to trigger the
// guard, which runs before decryption (current allowance is 320 octets,
// see MAX_EXPANSION in ssl3con.c).
TEST_P(TlsConnectTls13, RecordSizeCiphertextExceed) {
EnsureTlsSetup();
client_->SetOption(SSL_RECORD_SIZE_LIMIT, 64);
Connect();
auto server_expand = MakeTlsFilter<TlsRecordExpander>(server_, 336);
server_->SendData(100);
client_->ExpectReadWriteError();
ExpectAlert(client_, kTlsAlertRecordOverflow);
client_->ReadBytes(100);
EXPECT_EQ(SSL_ERROR_RX_RECORD_TOO_LONG, client_->error_code());
// Consume the alert at the server.
server_->Handshake();
server_->CheckErrorCode(SSL_ERROR_RECORD_OVERFLOW_ALERT);
}
TEST_F(TlsConnectStreamTls13, ClientHelloF5Padding) {
EnsureTlsSetup();
ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
ScopedPK11SymKey key(
PK11_KeyGen(slot.get(), CKM_NSS_CHACHA20_POLY1305, nullptr, 32, nullptr));
auto filter =
MakeTlsFilter<TlsHandshakeRecorder>(client_, kTlsHandshakeClientHello);
// Add PSK with label long enough to push CH length into [256, 511].
std::vector<uint8_t> label(100);
EXPECT_EQ(SECSuccess,
SSL_AddExternalPsk(client_->ssl_fd(), key.get(), label.data(),
label.size(), ssl_hash_sha256));
StartConnect();
client_->Handshake();
// Filter removes the 4B handshake header.
EXPECT_EQ(508UL, filter->buffer().len());
}
// This indiscriminately adds padding to application data records.
class TlsRecordPadder : public TlsRecordFilter {
public:
TlsRecordPadder(const std::shared_ptr<TlsAgent>& a, size_t padding)
: TlsRecordFilter(a), padding_(padding) {}
protected:
PacketFilter::Action FilterRecord(const TlsRecordHeader& header,
const DataBuffer& record, size_t* offset,
DataBuffer* output) override {
if (!header.is_protected()) {
return KEEP;
}
uint16_t protection_epoch;
uint8_t inner_content_type;
DataBuffer plaintext;
TlsRecordHeader out_header;
if (!Unprotect(header, record, &protection_epoch, &inner_content_type,
&plaintext, &out_header)) {
return KEEP;
}
if (decrypting() && inner_content_type != ssl_ct_application_data) {
return KEEP;
}
DataBuffer ciphertext;
bool ok = Protect(spec(protection_epoch), out_header, inner_content_type,
plaintext, &ciphertext, &out_header, padding_);
EXPECT_TRUE(ok);
if (!ok) {
return KEEP;
}
*offset = out_header.Write(output, *offset, ciphertext);
return CHANGE;
}
private:
size_t padding_;
};
TEST_P(TlsConnectTls13, RecordSizeExceedPad) {
EnsureTlsSetup();
auto server_max = std::make_shared<TlsRecordMaximum>(server_);
auto server_expand = std::make_shared<TlsRecordPadder>(server_, 1);
server_->SetFilter(std::make_shared<ChainedPacketFilter>(
ChainedPacketFilterInit({server_max, server_expand})));
server_expand->EnableDecryption();
client_->SetOption(SSL_RECORD_SIZE_LIMIT, 64);
Connect();
server_->SendData(100);
client_->ExpectReadWriteError();
ExpectAlert(client_, kTlsAlertRecordOverflow);
client_->ReadBytes(100);
EXPECT_EQ(SSL_ERROR_RX_RECORD_TOO_LONG, client_->error_code());
// Consume the alert at the server.
server_->Handshake();
server_->CheckErrorCode(SSL_ERROR_RECORD_OVERFLOW_ALERT);
}
TEST_P(TlsConnectGeneric, RecordSizeBadValues) {
EnsureTlsSetup();
EXPECT_EQ(SECFailure,
SSL_OptionSet(client_->ssl_fd(), SSL_RECORD_SIZE_LIMIT, 63));
EXPECT_EQ(SECFailure,
SSL_OptionSet(client_->ssl_fd(), SSL_RECORD_SIZE_LIMIT, -1));
EXPECT_EQ(SECFailure,
SSL_OptionSet(server_->ssl_fd(), SSL_RECORD_SIZE_LIMIT, 16386));
Connect();
}
TEST_P(TlsConnectGeneric, RecordSizeGetValues) {
EnsureTlsSetup();
int v;
EXPECT_EQ(SECSuccess,
SSL_OptionGet(client_->ssl_fd(), SSL_RECORD_SIZE_LIMIT, &v));
EXPECT_EQ(16385, v);
client_->SetOption(SSL_RECORD_SIZE_LIMIT, 300);
EXPECT_EQ(SECSuccess,
SSL_OptionGet(client_->ssl_fd(), SSL_RECORD_SIZE_LIMIT, &v));
EXPECT_EQ(300, v);
Connect();
}
// The value of the extension is capped by the maximum version of the client.
TEST_P(TlsConnectGeneric, RecordSizeCapExtensionClient) {
EnsureTlsSetup();
client_->SetOption(SSL_RECORD_SIZE_LIMIT, 16385);
auto capture =
MakeTlsFilter<TlsExtensionCapture>(client_, ssl_record_size_limit_xtn);
if (version_ >= SSL_LIBRARY_VERSION_TLS_1_3) {
capture->EnableDecryption();
}
Connect();
uint64_t val = 0;
EXPECT_TRUE(capture->extension().Read(0, 2, &val));
if (version_ < SSL_LIBRARY_VERSION_TLS_1_3) {
EXPECT_EQ(16384U, val) << "Extension should be capped";
} else {
EXPECT_EQ(16385U, val);
}
}
// The value of the extension is capped by the maximum version of the server.
TEST_P(TlsConnectGeneric, RecordSizeCapExtensionServer) {
EnsureTlsSetup();
server_->SetOption(SSL_RECORD_SIZE_LIMIT, 16385);
auto capture =
MakeTlsFilter<TlsExtensionCapture>(server_, ssl_record_size_limit_xtn);
if (version_ >= SSL_LIBRARY_VERSION_TLS_1_3) {
capture->EnableDecryption();
}
Connect();
uint64_t val = 0;
EXPECT_TRUE(capture->extension().Read(0, 2, &val));
if (version_ < SSL_LIBRARY_VERSION_TLS_1_3) {
EXPECT_EQ(16384U, val) << "Extension should be capped";
} else {
EXPECT_EQ(16385U, val);
}
}
// Damage the client extension and the handshake fails, but the server
// doesn't generate a validation error.
TEST_P(TlsConnectGenericPre13, RecordSizeClientExtensionInvalid) {
EnsureTlsSetup();
client_->SetOption(SSL_RECORD_SIZE_LIMIT, 1000);
static const uint8_t v[] = {0xf4, 0x1f};
MakeTlsFilter<TlsExtensionReplacer>(client_, ssl_record_size_limit_xtn,
DataBuffer(v, sizeof(v)));
ConnectExpectAlert(server_, kTlsAlertDecryptError);
}
// Special handling for TLS 1.3, where the alert isn't read.
TEST_F(TlsConnectStreamTls13, RecordSizeClientExtensionInvalid) {
EnsureTlsSetup();
client_->SetOption(SSL_RECORD_SIZE_LIMIT, 1000);
static const uint8_t v[] = {0xf4, 0x1f};
MakeTlsFilter<TlsExtensionReplacer>(client_, ssl_record_size_limit_xtn,
DataBuffer(v, sizeof(v)));
client_->ExpectSendAlert(kTlsAlertBadRecordMac);
server_->ExpectSendAlert(kTlsAlertBadRecordMac);
ConnectExpectFail();
}
TEST_P(TlsConnectGeneric, RecordSizeServerExtensionInvalid) {
EnsureTlsSetup();
server_->SetOption(SSL_RECORD_SIZE_LIMIT, 1000);
static const uint8_t v[] = {0xf4, 0x1f};
auto replace = MakeTlsFilter<TlsExtensionReplacer>(
server_, ssl_record_size_limit_xtn, DataBuffer(v, sizeof(v)));
if (version_ >= SSL_LIBRARY_VERSION_TLS_1_3) {
replace->EnableDecryption();
}
ConnectExpectAlert(client_, kTlsAlertIllegalParameter);
}
TEST_P(TlsConnectGeneric, RecordSizeServerExtensionExtra) {
EnsureTlsSetup();
server_->SetOption(SSL_RECORD_SIZE_LIMIT, 1000);
static const uint8_t v[] = {0x01, 0x00, 0x00};
auto replace = MakeTlsFilter<TlsExtensionReplacer>(
server_, ssl_record_size_limit_xtn, DataBuffer(v, sizeof(v)));
if (version_ >= SSL_LIBRARY_VERSION_TLS_1_3) {
replace->EnableDecryption();
}
ConnectExpectAlert(client_, kTlsAlertDecodeError);
}
class RecordSizeDefaultsTest : public ::testing::Test {
public:
void SetUp() {
EXPECT_EQ(SECSuccess,
SSL_OptionGetDefault(SSL_RECORD_SIZE_LIMIT, &default_));
}
void TearDown() {
// Make sure to restore the default value at the end.
EXPECT_EQ(SECSuccess,
SSL_OptionSetDefault(SSL_RECORD_SIZE_LIMIT, default_));
}
private:
PRIntn default_ = 0;
};
TEST_F(RecordSizeDefaultsTest, RecordSizeBadValues) {
EXPECT_EQ(SECFailure, SSL_OptionSetDefault(SSL_RECORD_SIZE_LIMIT, 63));
EXPECT_EQ(SECFailure, SSL_OptionSetDefault(SSL_RECORD_SIZE_LIMIT, -1));
EXPECT_EQ(SECFailure, SSL_OptionSetDefault(SSL_RECORD_SIZE_LIMIT, 16386));
}
TEST_F(RecordSizeDefaultsTest, RecordSizeGetValue) {
int v;
EXPECT_EQ(SECSuccess, SSL_OptionGetDefault(SSL_RECORD_SIZE_LIMIT, &v));
EXPECT_EQ(16385, v);
EXPECT_EQ(SECSuccess, SSL_OptionSetDefault(SSL_RECORD_SIZE_LIMIT, 3000));
EXPECT_EQ(SECSuccess, SSL_OptionGetDefault(SSL_RECORD_SIZE_LIMIT, &v));
EXPECT_EQ(3000, v);
}
} // namespace nss_test