Skip to content

Commit

Permalink
Bug 1295163 - Bloom filter for anti-replay, r=ekr
Browse files Browse the repository at this point in the history
--HG--
branch : NSS_TLS13_DRAFT19_BRANCH
extra : rebase_source : 8b48c282f4121d5e9727b8533766967c5a311276
extra : histedit_source : 3e4fbdd835ef0082a655d6935369629e74f98e36%2C45d73a4721d890565ad3e9417d829debc5127623
  • Loading branch information
martinthomson committed Jul 5, 2017
1 parent f077d65 commit e9c5d52
Show file tree
Hide file tree
Showing 18 changed files with 694 additions and 28 deletions.
108 changes: 108 additions & 0 deletions gtests/ssl_gtest/bloomfilter_unittest.cc
@@ -0,0 +1,108 @@
/* -*- 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/. */

extern "C" {
#include "sslbloom.h"
}

#include "gtest_utils.h"

namespace nss_test {

// Some random-ish inputs to test with. These don't result in collisions in any
// of the configurations that are tested below.
static const uint8_t kHashes1[] = {
0x79, 0x53, 0xb8, 0xdd, 0x6b, 0x98, 0xce, 0x00, 0xb7, 0xdc, 0xe8,
0x03, 0x70, 0x8c, 0xe3, 0xac, 0x06, 0x8b, 0x22, 0xfd, 0x0e, 0x34,
0x48, 0xe6, 0xe5, 0xe0, 0x8a, 0xd6, 0x16, 0x18, 0xe5, 0x48};
static const uint8_t kHashes2[] = {
0xc6, 0xdd, 0x6e, 0xc4, 0x76, 0xb8, 0x55, 0xf2, 0xa4, 0xfc, 0x59,
0x04, 0xa4, 0x90, 0xdc, 0xa7, 0xa7, 0x0d, 0x94, 0x8f, 0xc2, 0xdc,
0x15, 0x6d, 0x48, 0x93, 0x9d, 0x05, 0xbb, 0x9a, 0xbc, 0xc1};

typedef struct {
unsigned int k;
unsigned int bits;
} BloomFilterConfig;

class BloomFilterTest
: public ::testing::Test,
public ::testing::WithParamInterface<BloomFilterConfig> {
public:
BloomFilterTest() : filter_() {}

void SetUp() { Init(); }

void TearDown() { sslBloom_Destroy(&filter_); }

protected:
void Init() {
if (filter_.filter) {
sslBloom_Destroy(&filter_);
}
ASSERT_EQ(SECSuccess,
sslBloom_Init(&filter_, GetParam().k, GetParam().bits));
}

bool Check(const uint8_t* hashes) {
return sslBloom_Check(&filter_, hashes) ? true : false;
}

void Add(const uint8_t* hashes, bool expect_collision = false) {
EXPECT_EQ(expect_collision, sslBloom_Add(&filter_, hashes) ? true : false);
EXPECT_TRUE(Check(hashes));
}

sslBloomFilter filter_;
};

TEST_P(BloomFilterTest, InitOnly) {}

TEST_P(BloomFilterTest, AddToEmpty) {
EXPECT_FALSE(Check(kHashes1));
Add(kHashes1);
}

TEST_P(BloomFilterTest, AddTwo) {
Add(kHashes1);
Add(kHashes2);
}

TEST_P(BloomFilterTest, AddOneTwice) {
Add(kHashes1);
Add(kHashes1, true);
}

TEST_P(BloomFilterTest, Zero) {
Add(kHashes1);
sslBloom_Zero(&filter_);
EXPECT_FALSE(Check(kHashes1));
EXPECT_FALSE(Check(kHashes2));
}

TEST_P(BloomFilterTest, Fill) {
sslBloom_Fill(&filter_);
EXPECT_TRUE(Check(kHashes1));
EXPECT_TRUE(Check(kHashes2));
}

static const BloomFilterConfig kBloomFilterConfigurations[] = {
{1, 1}, // 1 hash, 1 bit input - high chance of collision.
{1, 2}, // 1 hash, 2 bits - smaller than the basic unit size.
{1, 3}, // 1 hash, 3 bits - same as basic unit size.
{1, 4}, // 1 hash, 4 bits - 2 octets each.
{3, 10}, // 3 hashes over a reasonable number of bits.
{3, 3}, // Test that we can read multiple bits.
{4, 15}, // A credible filter.
{2, 18}, // A moderately large allocation.
{16, 16}, // Insane, use all of the bits from the hashes.
{16, 9}, // This also uses all of the bits from the hashes.
};

INSTANTIATE_TEST_CASE_P(BloomFilterConfigurations, BloomFilterTest,
::testing::ValuesIn(kBloomFilterConfigurations));

} // namespace nspr_test
11 changes: 2 additions & 9 deletions gtests/ssl_gtest/libssl_internals.c
Expand Up @@ -381,13 +381,6 @@ SECStatus SSLInt_SetSocketMaxEarlyDataSize(PRFileDesc *fd, uint32_t size) {
return SECSuccess;
}

SECStatus SSLInt_SetTicketAgeTolerance(PRFileDesc *fd, PRUint16 tolerance) {
sslSocket *ss = ssl_FindSocket(fd);

if (!ss) {
return SECFailure;
}

ss->opt.ticketAgeTolerance = tolerance;
return SECSuccess;
void SSLInt_RolloverAntiReplay(void) {
tls13_AntiReplayRollover(ssl_TimeUsec());
}
2 changes: 1 addition & 1 deletion gtests/ssl_gtest/libssl_internals.h
Expand Up @@ -51,6 +51,6 @@ unsigned char *SSLInt_CipherSpecToIv(PRBool isServer, ssl3CipherSpec *spec);
void SSLInt_SetTicketLifetime(uint32_t lifetime);
void SSLInt_SetMaxEarlyDataSize(uint32_t size);
SECStatus SSLInt_SetSocketMaxEarlyDataSize(PRFileDesc *fd, uint32_t size);
SECStatus SSLInt_SetTicketAgeTolerance(PRFileDesc *fd, PRUint16 tolerance);
void SSLInt_RolloverAntiReplay(void);

#endif // ndef libssl_internals_h_
1 change: 1 addition & 0 deletions gtests/ssl_gtest/manifest.mn
Expand Up @@ -12,6 +12,7 @@ CSRCS = \
$(NULL)

CPPSRCS = \
bloomfilter_unittest.cc \
ssl_0rtt_unittest.cc \
ssl_agent_unittest.cc \
ssl_auth_unittest.cc \
Expand Down
100 changes: 97 additions & 3 deletions gtests/ssl_gtest/ssl_0rtt_unittest.cc
Expand Up @@ -45,6 +45,95 @@ TEST_P(TlsConnectTls13, ZeroRttServerRejectByOption) {
SendReceive();
}

TEST_P(TlsConnectTls13, ZeroRttApparentReplayAfterRestart) {
// The test fixtures call SSL_SetupAntiReplay() in SetUp(). This results in
// 0-RTT being rejected until at least one window passes. SetupFor0Rtt()
// forces a rollover of the anti-replay filters, which clears this state.
// Here, we do the setup manually here without that forced rollover.

ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3);
server_->Set0RttEnabled(true); // So we signal that we allow 0-RTT.
Connect();
SendReceive(); // Need to read so that we absorb the session ticket.
CheckKeys();

Reset();
server_->StartConnect();
client_->StartConnect();
client_->Set0RttEnabled(true);
server_->Set0RttEnabled(true);
ExpectResumption(RESUME_TICKET);
ZeroRttSendReceive(true, false);
Handshake();
CheckConnected();
SendReceive();
}

class TlsZeroRttReplayTest : public TlsConnectTls13 {
private:
class SaveFirstPacket : public PacketFilter {
public:
PacketFilter::Action Filter(const DataBuffer& input,
DataBuffer* output) override {
if (!packet_.len() && input.len()) {
packet_ = input;
}
return KEEP;
}

const DataBuffer& packet() const { return packet_; }

private:
DataBuffer packet_;
};

protected:
void RunTest(bool rollover) {
// Run the initial handshake
SetupForZeroRtt();

// Now run a true 0-RTT handshake, but capture the first packet.
auto first_packet = std::make_shared<SaveFirstPacket>();
client_->SetPacketFilter(first_packet);
client_->Set0RttEnabled(true);
server_->Set0RttEnabled(true);
ExpectResumption(RESUME_TICKET);
ZeroRttSendReceive(true, true);
Handshake();
EXPECT_LT(0U, first_packet->packet().len());
ExpectEarlyDataAccepted(true);
CheckConnected();
SendReceive();

if (rollover) {
SSLInt_RolloverAntiReplay();
}

// Now replay that packet against the server.
Reset();
server_->StartConnect();
server_->Set0RttEnabled(true);

// Capture the early_data extension, which should not appear.
auto early_data_ext =
std::make_shared<TlsExtensionCapture>(ssl_tls13_early_data_xtn);
server_->SetPacketFilter(early_data_ext);
early_data_ext->EnableDecryption();

// Finally, replay the ClientHello and force the server to consume it. Stop
// after the server sends its first flight; the client will not be able to
// complete this handshake.
server_->adapter()->PacketReceived(first_packet->packet());
server_->Handshake();
EXPECT_FALSE(early_data_ext->captured());
}
};

TEST_P(TlsZeroRttReplayTest, ZeroRttReplay) { RunTest(false); }

TEST_P(TlsZeroRttReplayTest, ZeroRttReplayAfterRollover) { RunTest(true); }

// Test that we don't try to send 0-RTT data when the server sent
// us a ticket without the 0-RTT flags.
TEST_P(TlsConnectTls13, ZeroRttOptionsSetLate) {
Expand Down Expand Up @@ -102,12 +191,15 @@ TEST_P(TlsConnectTls13, ZeroRttServerOnly) {

// A small sleep after sending the ClientHello means that the ticket age that
// arrives at the server is too low. With a small tolerance for variation in
// ticket age, the server then rejects early data.
// ticket age (which is determined by the |window| parameter that is passed to
// SSL_SetupAntiReplay()), the server then rejects early data.
TEST_P(TlsConnectTls13, ZeroRttRejectOldTicket) {
SetupForZeroRtt();
client_->Set0RttEnabled(true);
server_->Set0RttEnabled(true);
SSLInt_SetTicketAgeTolerance(server_->ssl_fd(), 1);
EXPECT_EQ(SECSuccess, SSL_SetupAntiReplay(1, 1, 3));
SSLInt_RolloverAntiReplay(); // Make sure to flush replay state.
SSLInt_RolloverAntiReplay();
ExpectResumption(RESUME_TICKET);
ZeroRttSendReceive(true, false, []() {
PR_Sleep(PR_MillisecondsToInterval(10));
Expand Down Expand Up @@ -141,7 +233,9 @@ TEST_P(TlsConnectTls13, ZeroRttRejectPrematureTicket) {
Reset();
client_->Set0RttEnabled(true);
server_->Set0RttEnabled(true);
SSLInt_SetTicketAgeTolerance(server_->ssl_fd(), 1);
EXPECT_EQ(SECSuccess, SSL_SetupAntiReplay(1, 1, 3));
SSLInt_RolloverAntiReplay(); // Make sure to flush replay state.
SSLInt_RolloverAntiReplay();
ExpectResumption(RESUME_TICKET);
ExpectEarlyDataAccepted(false);

Expand Down
1 change: 1 addition & 0 deletions gtests/ssl_gtest/ssl_gtest.gyp
Expand Up @@ -11,6 +11,7 @@
'target_name': 'ssl_gtest',
'type': 'executable',
'sources': [
'bloomfilter_unittest.cc',
'libssl_internals.c',
'selfencrypt_unittest.cc',
'ssl_0rtt_unittest.cc',
Expand Down
4 changes: 4 additions & 0 deletions gtests/ssl_gtest/tls_connect.cc
Expand Up @@ -181,6 +181,7 @@ void TlsConnectTestBase::SetUp() {
SSLInt_ClearSelfEncryptKey();
SSLInt_SetTicketLifetime(30);
SSLInt_SetMaxEarlyDataSize(1024);
SSL_SetupAntiReplay(1 * PR_USEC_PER_SEC, 1, 3);
ClearStats();
Init();
}
Expand Down Expand Up @@ -551,6 +552,9 @@ void TlsConnectTestBase::SendReceive() {

// Do a first connection so we can do 0-RTT on the second one.
void TlsConnectTestBase::SetupForZeroRtt() {
// If we don't do this, then all 0-RTT attempts will be rejected.
SSLInt_RolloverAntiReplay();

ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3);
server_->Set0RttEnabled(true); // So we signal that we allow 0-RTT.
Expand Down
2 changes: 2 additions & 0 deletions lib/ssl/manifest.mn
Expand Up @@ -24,6 +24,7 @@ CSRCS = \
ssl3con.c \
ssl3gthr.c \
sslauth.c \
sslbloom.c \
sslcon.c \
ssldef.c \
sslencode.c \
Expand All @@ -49,6 +50,7 @@ CSRCS = \
tls13con.c \
tls13exthandle.c \
tls13hkdf.c \
tls13replay.c \
sslcert.c \
sslgrp.c \
$(NULL)
Expand Down
2 changes: 2 additions & 0 deletions lib/ssl/ssl.gyp
Expand Up @@ -21,6 +21,7 @@
'ssl3exthandle.c',
'ssl3gthr.c',
'sslauth.c',
'sslbloom.c',
'sslcert.c',
'sslcon.c',
'ssldef.c',
Expand All @@ -42,6 +43,7 @@
'tls13con.c',
'tls13exthandle.c',
'tls13hkdf.c',
'tls13replay.c',
],
'conditions': [
[ 'OS=="win"', {
Expand Down
1 change: 1 addition & 0 deletions lib/ssl/ssl.h
Expand Up @@ -1376,6 +1376,7 @@ SSL_IMPORT SECStatus SSL_AuthCertificateComplete(PRFileDesc *fd,
PRErrorCode error);

/*
<<<<<<< dest
* SSL_GetExtensionSupport() returns whether NSS supports a particular TLS extension.
*
* - ssl_ext_none indicates that NSS does not support the extension and
Expand Down

0 comments on commit e9c5d52

Please sign in to comment.