summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin Thomson <mt@lowentropy.net>2019-06-25 12:24:15 +1000
committerMartin Thomson <mt@lowentropy.net>2019-06-25 12:24:15 +1000
commitbcacf7b350e9e26db0e5c1d46a98f8b417e38e46 (patch)
treedc845c0324711d2ecc66d6c506db2de0e4088338
parentaf023085c60058746b5f15e7042bba598f318197 (diff)
downloadnss-hg-bcacf7b350e9e26db0e5c1d46a98f8b417e38e46.tar.gz
Bug 1558681 - Anti-replay contexts, r=jcj,kjacobs
Stop using a global anti-replay context and enable creating a context directly. This increases the overhead of managing anti-replay for applications marginally, but allows much greater flexibility in use of anti-replay mechanisms. In particular, it enables the testing of 0-RTT in a threaded environment. The comments in sslexp should be clear enough in explaining how this works. Basically, this is a new reference-counted object that can be created and tracked by applications. The only thing that I can see might be a problem with the API is that I haven't exposed a function to add a reference for use by applications. My thinking is that reference counting is an internal thing; it seems like applications won't need to worry about that. selfserv is updated to create a context and attach it to sockets. This shows that the management overhead is minor. The gtests have been tweaked to create a context during setup. The context is owned by the overall test framework and is passed to server instances after the sockets are initialized. Bonus changes: * ESNI keys are copied from the model socket when calling SSL_ReConfigFD(). * Some better tracing in the anti-replay functions. Neither of these seemed worth the overhead of a bug to fix. Differential Revision: https://phabricator.services.mozilla.com/D34660
-rw-r--r--cmd/selfserv/selfserv.c12
-rw-r--r--cpputil/scoped_ptrs_ssl.h8
-rw-r--r--gtests/ssl_gtest/ssl_0rtt_unittest.cc61
-rw-r--r--gtests/ssl_gtest/tls_agent.cc4
-rw-r--r--gtests/ssl_gtest/tls_agent.h2
-rw-r--r--gtests/ssl_gtest/tls_connect.cc14
-rw-r--r--gtests/ssl_gtest/tls_connect.h3
-rw-r--r--lib/ssl/sslexp.h36
-rw-r--r--lib/ssl/sslimpl.h3
-rw-r--r--lib/ssl/sslsock.c36
-rw-r--r--lib/ssl/tls13con.h14
-rw-r--r--lib/ssl/tls13replay.c180
12 files changed, 264 insertions, 109 deletions
diff --git a/cmd/selfserv/selfserv.c b/cmd/selfserv/selfserv.c
index 4b1adb028..533b3f053 100644
--- a/cmd/selfserv/selfserv.c
+++ b/cmd/selfserv/selfserv.c
@@ -805,6 +805,7 @@ PRBool enableSessionTickets = PR_FALSE;
PRBool failedToNegotiateName = PR_FALSE;
PRBool enableExtendedMasterSecret = PR_FALSE;
PRBool zeroRTT = PR_FALSE;
+SSLAntiReplayContext *antiReplay = NULL;
PRBool enableALPN = PR_FALSE;
PRBool enablePostHandshakeAuth = PR_FALSE;
SSLNamedGroup *enabledGroups = NULL;
@@ -1954,7 +1955,7 @@ server_main(
if (enabledVersions.max < SSL_LIBRARY_VERSION_TLS_1_3) {
errExit("You tried enabling 0RTT without enabling TLS 1.3!");
}
- rv = SSL_InitAntiReplay(PR_Now(), 10L * PR_USEC_PER_SEC, 7, 14);
+ rv = SSL_SetAntiReplayContext(model_sock, antiReplay);
if (rv != SECSuccess) {
errExit("error configuring anti-replay ");
}
@@ -2469,6 +2470,12 @@ main(int argc, char **argv)
case 'Z':
zeroRTT = PR_TRUE;
+ rv = SSL_CreateAntiReplayContext(PR_Now(), 10L * PR_USEC_PER_SEC, 7, 14, &antiReplay);
+ if (rv != SECSuccess) {
+ PL_DestroyOptState(optstate);
+ fprintf(stderr, "Unable to create anti-replay context for 0-RTT.\n");
+ exit(1);
+ }
break;
case 'Q':
@@ -2798,6 +2805,9 @@ cleanup:
if (enabledGroups) {
PORT_Free(enabledGroups);
}
+ if (antiReplay) {
+ SSL_ReleaseAntiReplayContext(antiReplay);
+ }
if (NSS_Shutdown() != SECSuccess) {
SECU_PrintError(progName, "NSS_Shutdown");
if (loggerThread) {
diff --git a/cpputil/scoped_ptrs_ssl.h b/cpputil/scoped_ptrs_ssl.h
index 581fe423a..474187540 100644
--- a/cpputil/scoped_ptrs_ssl.h
+++ b/cpputil/scoped_ptrs_ssl.h
@@ -11,10 +11,13 @@
#include "sslexp.h"
struct ScopedDeleteSSL {
+ void operator()(SSLAeadContext* ctx) { SSL_DestroyAead(ctx); }
+ void operator()(SSLAntiReplayContext* ctx) {
+ SSL_ReleaseAntiReplayContext(ctx);
+ }
void operator()(SSLResumptionTokenInfo* token) {
SSL_DestroyResumptionTokenInfo(token);
}
- void operator()(SSLAeadContext* ctx) { SSL_DestroyAead(ctx); }
};
template <class T>
@@ -29,8 +32,9 @@ struct ScopedMaybeDeleteSSL {
#define SCOPED(x) typedef std::unique_ptr<x, ScopedMaybeDeleteSSL<x> > Scoped##x
-SCOPED(SSLResumptionTokenInfo);
SCOPED(SSLAeadContext);
+SCOPED(SSLAntiReplayContext);
+SCOPED(SSLResumptionTokenInfo);
#undef SCOPED
diff --git a/gtests/ssl_gtest/ssl_0rtt_unittest.cc b/gtests/ssl_gtest/ssl_0rtt_unittest.cc
index 979351518..c9036291c 100644
--- a/gtests/ssl_gtest/ssl_0rtt_unittest.cc
+++ b/gtests/ssl_gtest/ssl_0rtt_unittest.cc
@@ -74,10 +74,11 @@ TEST_P(TlsConnectTls13, ZeroRttApplicationReject) {
}
TEST_P(TlsConnectTls13, ZeroRttApparentReplayAfterRestart) {
- // The test fixtures call SSL_InitAntiReplay() 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.
+ // The test fixtures enable anti-replay 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 that state and allows
+ // 0-RTT to work. Make the first connection manually to avoid that rollover
+ // and cause 0-RTT to be rejected.
ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3);
@@ -217,7 +218,7 @@ TEST_P(TlsConnectTls13, ZeroRttServerOnly) {
// delay exceeds half the anti-replay window.
TEST_P(TlsConnectTls13, ZeroRttRejectOldTicket) {
static const PRTime kWindow = 10 * PR_USEC_PER_SEC;
- EXPECT_EQ(SECSuccess, SSL_InitAntiReplay(now(), kWindow, 1, 3));
+ ResetAntiReplay(kWindow);
SetupForZeroRtt();
Reset();
@@ -242,7 +243,7 @@ TEST_P(TlsConnectTls13, ZeroRttRejectOldTicket) {
// arrive prematurely, causing the server to reject early data.
TEST_P(TlsConnectTls13, ZeroRttRejectPrematureTicket) {
static const PRTime kWindow = 10 * PR_USEC_PER_SEC;
- EXPECT_EQ(SECSuccess, SSL_InitAntiReplay(now(), kWindow, 1, 3));
+ ResetAntiReplay(kWindow);
ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3);
server_->Set0RttEnabled(true);
@@ -904,20 +905,21 @@ TEST_F(TlsConnectDatagram13, ZeroRttShortReadDtls) {
// that a small sleep results in rejection of early data. 0-RTT has a
// configurable timer, which makes it ideal for this.
TEST_F(TlsConnectStreamTls13, TimePassesByDefault) {
- // Set a tiny anti-replay window. This has to be at least 2 milliseconds to
- // have any chance of being relevant as that is the smallest window that we
- // can detect. Anything smaller rounds to zero.
- static const unsigned int kTinyWindowMs = 5;
- EXPECT_EQ(SECSuccess, SSL_InitAntiReplay(
- PR_Now(), kTinyWindowMs * PR_USEC_PER_MSEC, 1, 5));
-
// Calling EnsureTlsSetup() replaces the time function on client and server,
- // which we don't want, so initialize each directly.
+ // and sets up anti-replay, which we don't want, so initialize each directly.
client_->EnsureTlsSetup();
server_->EnsureTlsSetup();
- client_->StartConnect(); // Also avoid StartConnect().
+ // StartConnect() calls EnsureTlsSetup(), so avoid that too.
+ client_->StartConnect();
server_->StartConnect();
+ // Set a tiny anti-replay window. This has to be at least 2 milliseconds to
+ // have any chance of being relevant as that is the smallest window that we
+ // can detect. Anything smaller rounds to zero.
+ static const unsigned int kTinyWindowMs = 5;
+ ResetAntiReplay(static_cast<PRTime>(kTinyWindowMs * PR_USEC_PER_MSEC));
+ server_->SetAntiReplayContext(anti_replay_);
+
ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3);
server_->Set0RttEnabled(true);
@@ -951,6 +953,35 @@ TEST_F(TlsConnectStreamTls13, TimePassesByDefault) {
CheckConnected();
}
+// Test that SSL_CreateAntiReplayContext doesn't pass bad inputs.
+TEST_F(TlsConnectStreamTls13, BadAntiReplayArgs) {
+ SSLAntiReplayContext* p;
+ // Zero or negative window.
+ EXPECT_EQ(SECFailure, SSL_CreateAntiReplayContext(0, -1, 1, 1, &p));
+ EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError());
+ EXPECT_EQ(SECFailure, SSL_CreateAntiReplayContext(0, 0, 1, 1, &p));
+ EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError());
+ // Zero k.
+ EXPECT_EQ(SECFailure, SSL_CreateAntiReplayContext(0, 1, 0, 1, &p));
+ EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError());
+ // Zero bits.
+ EXPECT_EQ(SECFailure, SSL_CreateAntiReplayContext(0, 1, 1, 0, &p));
+ EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError());
+ EXPECT_EQ(SECFailure, SSL_CreateAntiReplayContext(0, 1, 1, 1, nullptr));
+ EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError());
+
+ // Prove that these parameters do work, even if they are useless..
+ EXPECT_EQ(SECSuccess, SSL_CreateAntiReplayContext(0, 1, 1, 1, &p));
+ ASSERT_NE(nullptr, p);
+ ScopedSSLAntiReplayContext ctx(p);
+
+ // The socket isn't a client or server until later, so configuring a client
+ // should work OK.
+ client_->EnsureTlsSetup();
+ EXPECT_EQ(SECSuccess, SSL_SetAntiReplayContext(client_->ssl_fd(), ctx.get()));
+ EXPECT_EQ(SECSuccess, SSL_SetAntiReplayContext(client_->ssl_fd(), nullptr));
+}
+
#ifndef NSS_DISABLE_TLS_1_3
INSTANTIATE_TEST_CASE_P(Tls13ZeroRttReplayTest, TlsZeroRttReplayTest,
TlsConnectTestBase::kTlsVariantsAll);
diff --git a/gtests/ssl_gtest/tls_agent.cc b/gtests/ssl_gtest/tls_agent.cc
index e726c6a61..697da6df2 100644
--- a/gtests/ssl_gtest/tls_agent.cc
+++ b/gtests/ssl_gtest/tls_agent.cc
@@ -251,6 +251,10 @@ bool TlsAgent::MaybeSetResumptionToken() {
return true;
}
+void TlsAgent::SetAntiReplayContext(ScopedSSLAntiReplayContext& ctx) {
+ EXPECT_EQ(SECSuccess, SSL_SetAntiReplayContext(ssl_fd_.get(), ctx.get()));
+}
+
void TlsAgent::SetupClientAuth() {
EXPECT_TRUE(EnsureTlsSetup());
ASSERT_EQ(CLIENT, role_);
diff --git a/gtests/ssl_gtest/tls_agent.h b/gtests/ssl_gtest/tls_agent.h
index a2ffe7711..0e1b3df6d 100644
--- a/gtests/ssl_gtest/tls_agent.h
+++ b/gtests/ssl_gtest/tls_agent.h
@@ -108,6 +108,8 @@ class TlsAgent : public PollTarget {
void PrepareForRenegotiate();
// Prepares for renegotiation, then actually triggers it.
void StartRenegotiate();
+ void SetAntiReplayContext(ScopedSSLAntiReplayContext& ctx);
+
static bool LoadCertificate(const std::string& name,
ScopedCERTCertificate* cert,
ScopedSECKEYPrivateKey* priv);
diff --git a/gtests/ssl_gtest/tls_connect.cc b/gtests/ssl_gtest/tls_connect.cc
index fa3376937..520950829 100644
--- a/gtests/ssl_gtest/tls_connect.cc
+++ b/gtests/ssl_gtest/tls_connect.cc
@@ -215,7 +215,7 @@ void TlsConnectTestBase::SetUp() {
SSL_ConfigServerSessionIDCache(1024, 0, 0, g_working_dir_path.c_str());
SSLInt_ClearSelfEncryptKey();
now_ = PR_Now();
- SSL_InitAntiReplay(now_, kAntiReplayWindow, 1, 3);
+ ResetAntiReplay(kAntiReplayWindow);
ClearStats();
SaveAlgorithmPolicy();
Init();
@@ -240,6 +240,14 @@ void TlsConnectTestBase::Init() {
}
}
+void TlsConnectTestBase::ResetAntiReplay(PRTime window) {
+ SSLAntiReplayContext* p_anti_replay;
+ EXPECT_EQ(SECSuccess,
+ SSL_CreateAntiReplayContext(now_, window, 1, 3, &p_anti_replay));
+ EXPECT_NE(nullptr, p_anti_replay);
+ anti_replay_.reset(p_anti_replay);
+}
+
void TlsConnectTestBase::Reset() {
// Take a copy of the names because they are about to disappear.
std::string server_name = server_->name();
@@ -258,7 +266,8 @@ void TlsConnectTestBase::Reset(const std::string& server_name,
server_->SkipVersionChecks();
}
- std::cerr << "Reset" << std::endl;
+ std::cerr << "Reset server:" << server_name << ", client:" << client_name
+ << std::endl;
Init();
}
@@ -290,6 +299,7 @@ void TlsConnectTestBase::EnsureTlsSetup() {
: nullptr));
EXPECT_TRUE(client_->EnsureTlsSetup(client_model_ ? client_model_->ssl_fd()
: nullptr));
+ server_->SetAntiReplayContext(anti_replay_);
EXPECT_EQ(SECSuccess, SSL_SetTimeFunc(client_->ssl_fd(),
TlsConnectTestBase::TimeFunc, &now_));
EXPECT_EQ(SECSuccess, SSL_SetTimeFunc(server_->ssl_fd(),
diff --git a/gtests/ssl_gtest/tls_connect.h b/gtests/ssl_gtest/tls_connect.h
index 6d74df972..4321828f0 100644
--- a/gtests/ssl_gtest/tls_connect.h
+++ b/gtests/ssl_gtest/tls_connect.h
@@ -134,6 +134,8 @@ class TlsConnectTestBase : public ::testing::Test {
// Move the DTLS timers for both endpoints to pop the next timer.
void ShiftDtlsTimers();
void AdvanceTime(PRTime time_shift);
+
+ void ResetAntiReplay(PRTime window);
void RolloverAntiReplay();
void SaveAlgorithmPolicy();
@@ -149,6 +151,7 @@ class TlsConnectTestBase : public ::testing::Test {
SessionResumptionMode expected_resumption_mode_;
uint8_t expected_resumptions_;
std::vector<std::vector<uint8_t>> session_ids_;
+ ScopedSSLAntiReplayContext anti_replay_;
// A simple value of "a", "b". Note that the preferred value of "a" is placed
// at the end, because the NSS API follows the now defunct NPN specification,
diff --git a/lib/ssl/sslexp.h b/lib/ssl/sslexp.h
index 7eb53cfee..96159f56b 100644
--- a/lib/ssl/sslexp.h
+++ b/lib/ssl/sslexp.h
@@ -159,11 +159,16 @@ typedef SECStatus(PR_CALLBACK *SSLExtensionHandler)(
handler, handlerArg))
/*
- * Initialize the anti-replay buffer for supporting 0-RTT in TLS 1.3 on servers.
+ * Create an anti-replay context for supporting 0-RTT in TLS 1.3 on servers.
*
- * To use 0-RTT on a server, you must call this function. Failing to call this
- * function will result in all 0-RTT being rejected. Connections will complete,
- * but early data will be rejected.
+ * To use 0-RTT on a server, you must create an anti-replay context using
+ * SSL_CreateAntiReplayContext and set that on the socket with
+ * SSL_SetAntiReplayContext. Failing to set a context on the server will result
+ * in all 0-RTT being rejected. Connections will complete, but early data will
+ * be rejected.
+ *
+ * Anti-replay contexts are reference counted and are released with
+ * SSL_ReleaseAntiReplayContext.
*
* NSS uses a Bloom filter to track the ClientHello messages that it receives
* (specifically, it uses the PSK binder). This function initializes a pair of
@@ -219,11 +224,23 @@ typedef SECStatus(PR_CALLBACK *SSLExtensionHandler)(
* Early data can be replayed at least once with every server instance that will
* accept tickets that are encrypted with the same key.
*/
-#define SSL_InitAntiReplay(now, window, k, bits) \
- SSL_EXPERIMENTAL_API("SSL_InitAntiReplay", \
- (PRTime _now, PRTime _window, \
- unsigned int _k, unsigned int _bits), \
- (now, window, k, bits))
+typedef struct SSLAntiReplayContextStr SSLAntiReplayContext;
+#define SSL_CreateAntiReplayContext(now, window, k, bits, ctx) \
+ SSL_EXPERIMENTAL_API("SSL_CreateAntiReplayContext", \
+ (PRTime _now, PRTime _window, \
+ unsigned int _k, unsigned int _bits, \
+ SSLAntiReplayContext **_ctx), \
+ (now, window, k, bits, ctx))
+
+#define SSL_SetAntiReplayContext(fd, ctx) \
+ SSL_EXPERIMENTAL_API("SSL_SetAntiReplayContext", \
+ (PRFileDesc * _fd, SSLAntiReplayContext * _ctx), \
+ (fd, ctx))
+
+#define SSL_ReleaseAntiReplayContext(ctx) \
+ SSL_EXPERIMENTAL_API("SSL_ReleaseAntiReplayContext", \
+ (SSLAntiReplayContext * _ctx), \
+ (ctx))
/*
* This function allows a server application to generate a session ticket that
@@ -743,6 +760,7 @@ typedef PRTime(PR_CALLBACK *SSLTimeFunc)(void *arg);
/* Deprecated experimental APIs */
#define SSL_UseAltServerHelloType(fd, enable) SSL_DEPRECATED_EXPERIMENTAL_API
#define SSL_SetupAntiReplay(a, b, c) SSL_DEPRECATED_EXPERIMENTAL_API
+#define SSL_InitAntiReplay(a, b, c) SSL_DEPRECATED_EXPERIMENTAL_API
SEC_END_PROTOS
diff --git a/lib/ssl/sslimpl.h b/lib/ssl/sslimpl.h
index 8c472580b..aa6f804ff 100644
--- a/lib/ssl/sslimpl.h
+++ b/lib/ssl/sslimpl.h
@@ -1094,6 +1094,9 @@ struct sslSocketStr {
/* The information from the ESNI keys record
* (also the private key for the server). */
sslEsniKeys *esniKeys;
+
+ /* Anti-replay for TLS 1.3 0-RTT. */
+ SSLAntiReplayContext *antiReplay;
};
struct sslSelfEncryptKeysStr {
diff --git a/lib/ssl/sslsock.c b/lib/ssl/sslsock.c
index 802ef6714..b53075be0 100644
--- a/lib/ssl/sslsock.c
+++ b/lib/ssl/sslsock.c
@@ -372,6 +372,13 @@ ssl_DupSocket(sslSocket *os)
goto loser;
}
}
+ if (os->antiReplay) {
+ ss->antiReplay = tls13_RefAntiReplayContext(os->antiReplay);
+ PORT_Assert(ss->antiReplay); /* Can't fail. */
+ if (!ss->antiReplay) {
+ goto loser;
+ }
+ }
/* Create security data */
rv = ssl_CopySecurityInfo(ss, os);
@@ -460,6 +467,7 @@ ssl_DestroySocketContents(sslSocket *ss)
ssl_ClearPRCList(&ss->ssl3.hs.dtlsRcvdHandshake, NULL);
tls13_DestroyESNIKeys(ss->esniKeys);
+ tls13_ReleaseAntiReplayContext(ss->antiReplay);
}
/*
@@ -2292,6 +2300,29 @@ SSL_ReconfigFD(PRFileDesc *model, PRFileDesc *fd)
}
}
+ /* Copy ESNI. */
+ tls13_DestroyESNIKeys(ss->esniKeys);
+ ss->esniKeys = NULL;
+ if (sm->esniKeys) {
+ ss->esniKeys = tls13_CopyESNIKeys(sm->esniKeys);
+ if (!ss->esniKeys) {
+ return NULL;
+ }
+ }
+
+ /* Copy anti-replay context. */
+ if (ss->antiReplay) {
+ tls13_ReleaseAntiReplayContext(ss->antiReplay);
+ ss->antiReplay = NULL;
+ }
+ if (sm->antiReplay) {
+ ss->antiReplay = tls13_RefAntiReplayContext(sm->antiReplay);
+ PORT_Assert(ss->antiReplay);
+ if (!ss->antiReplay) {
+ return NULL;
+ }
+ }
+
if (sm->authCertificate)
ss->authCertificate = sm->authCertificate;
if (sm->authCertificateArg)
@@ -3988,6 +4019,7 @@ ssl_NewSocket(PRBool makeLocks, SSLProtocolVariant protocolVariant)
dtls_InitTimers(ss);
ss->esniKeys = NULL;
+ ss->antiReplay = NULL;
if (makeLocks) {
rv = ssl_MakeLocks(ss);
@@ -4056,6 +4088,7 @@ struct {
#ifndef SSL_DISABLE_EXPERIMENTAL_API
EXP(AeadDecrypt),
EXP(AeadEncrypt),
+ EXP(CreateAntiReplayContext),
EXP(DestroyAead),
EXP(DestroyResumptionTokenInfo),
EXP(EnableESNI),
@@ -4064,7 +4097,6 @@ struct {
EXP(GetExtensionSupport),
EXP(GetResumptionTokenInfo),
EXP(HelloRetryRequestCallback),
- EXP(InitAntiReplay),
EXP(InstallExtensionHooks),
EXP(HkdfExtract),
EXP(HkdfExpandLabel),
@@ -4073,9 +4105,11 @@ struct {
EXP(MakeAead),
EXP(RecordLayerData),
EXP(RecordLayerWriteCallback),
+ EXP(ReleaseAntiReplayContext),
EXP(SecretCallback),
EXP(SendCertificateRequest),
EXP(SendSessionTicket),
+ EXP(SetAntiReplayContext),
EXP(SetESNIKeyPair),
EXP(SetMaxEarlyDataSize),
EXP(SetResumptionTokenCallback),
diff --git a/lib/ssl/tls13con.h b/lib/ssl/tls13con.h
index d634f4b84..bd309419f 100644
--- a/lib/ssl/tls13con.h
+++ b/lib/ssl/tls13con.h
@@ -115,10 +115,16 @@ SECStatus tls13_NegotiateVersion(sslSocket *ss,
PRBool tls13_ShouldRequestClientAuth(sslSocket *ss);
PRBool tls13_IsReplay(const sslSocket *ss, const sslSessionID *sid);
-void tls13_AntiReplayRollover(PRTime now);
-
-SECStatus SSLExp_InitAntiReplay(PRTime now, PRTime window, unsigned int k,
- unsigned int bits);
+void tls13_AntiReplayRollover(SSLAntiReplayContext *ctx, PRTime now);
+SSLAntiReplayContext *tls13_RefAntiReplayContext(SSLAntiReplayContext *ctx);
+void tls13_ReleaseAntiReplayContext(SSLAntiReplayContext *ctx);
+
+SECStatus SSLExp_CreateAntiReplayContext(
+ PRTime now, PRTime window, unsigned int k, unsigned int bits,
+ SSLAntiReplayContext **ctx);
+SECStatus SSLExp_SetAntiReplayContext(PRFileDesc *fd,
+ SSLAntiReplayContext *ctx);
+SECStatus SSLExp_ReleaseAntiReplayContext(SSLAntiReplayContext *ctx);
SECStatus SSLExp_HelloRetryRequestCallback(PRFileDesc *fd,
SSLHelloRetryRequestCallback cb,
diff --git a/lib/ssl/tls13replay.c b/lib/ssl/tls13replay.c
index 2770be761..ac44811f0 100644
--- a/lib/ssl/tls13replay.c
+++ b/lib/ssl/tls13replay.c
@@ -9,7 +9,6 @@
#include "nss.h" /* for NSS_RegisterShutdown */
#include "nssilock.h" /* for PZMonitor */
#include "pk11pub.h"
-#include "prinit.h" /* for PR_CallOnce */
#include "prmon.h"
#include "prtime.h"
#include "secerr.h"
@@ -18,10 +17,10 @@
#include "sslimpl.h"
#include "tls13hkdf.h"
-static struct {
- /* Used to ensure that we only initialize the cleanup function once. */
- PRCallOnceType init;
- /* Used to serialize access to the filters. */
+struct SSLAntiReplayContextStr {
+ /* The number of outstanding references to this context. */
+ PRInt32 refCount;
+ /* Used to serialize access. */
PZMonitor *lock;
/* The filters, use of which alternates. */
sslBloomFilter filters[2];
@@ -33,44 +32,55 @@ static struct {
PRTime window;
/* This key ensures that the bloom filter index is unpredictable. */
PK11SymKey *key;
-} ssl_anti_replay;
+};
-/* Clear the current state and free any resources we allocated. The signature
- * here is odd to allow this to be called during shutdown. */
-static SECStatus
-tls13_AntiReplayReset(void *appData, void *nssData)
+void
+tls13_ReleaseAntiReplayContext(SSLAntiReplayContext *ctx)
{
- if (ssl_anti_replay.key) {
- PK11_FreeSymKey(ssl_anti_replay.key);
- ssl_anti_replay.key = NULL;
+ if (!ctx) {
+ return;
}
- if (ssl_anti_replay.lock) {
- PZ_DestroyMonitor(ssl_anti_replay.lock);
- ssl_anti_replay.lock = NULL;
+ if (PR_ATOMIC_DECREMENT(&ctx->refCount) >= 1) {
+ return;
}
- sslBloom_Destroy(&ssl_anti_replay.filters[0]);
- sslBloom_Destroy(&ssl_anti_replay.filters[1]);
+
+ if (ctx->lock) {
+ PZ_DestroyMonitor(ctx->lock);
+ ctx->lock = NULL;
+ }
+ PK11_FreeSymKey(ctx->key);
+ ctx->key = NULL;
+ sslBloom_Destroy(&ctx->filters[0]);
+ sslBloom_Destroy(&ctx->filters[1]);
+}
+
+/* Clear the current state and free any resources we allocated. The signature
+ * here is odd to allow this to be called during shutdown. */
+SECStatus
+SSLExp_ReleaseAntiReplayContext(SSLAntiReplayContext *ctx)
+{
+ tls13_ReleaseAntiReplayContext(ctx);
return SECSuccess;
}
-static PRStatus
-tls13_AntiReplayInit(void)
+SSLAntiReplayContext *
+tls13_RefAntiReplayContext(SSLAntiReplayContext *ctx)
{
- SECStatus rv = NSS_RegisterShutdown(tls13_AntiReplayReset, NULL);
- if (rv != SECSuccess) {
- return PR_FAILURE;
- }
- return PR_SUCCESS;
+ PORT_Assert(ctx);
+ PR_ATOMIC_INCREMENT(&ctx->refCount);
+ return ctx;
}
static SECStatus
-tls13_AntiReplayKeyGen()
+tls13_AntiReplayKeyGen(SSLAntiReplayContext *ctx)
{
PRUint8 buf[32];
SECItem keyItem = { siBuffer, buf, sizeof(buf) };
PK11SlotInfo *slot;
SECStatus rv;
+ PORT_Assert(ctx);
+
slot = PK11_GetInternalSlot();
if (!slot) {
PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
@@ -81,10 +91,10 @@ tls13_AntiReplayKeyGen()
goto loser;
}
- ssl_anti_replay.key = PK11_ImportSymKey(slot, CKM_NSS_HKDF_SHA256,
- PK11_OriginUnwrap, CKA_DERIVE,
- &keyItem, NULL);
- if (!ssl_anti_replay.key) {
+ ctx->key = PK11_ImportSymKey(slot, CKM_NSS_HKDF_SHA256,
+ PK11_OriginUnwrap, CKA_DERIVE,
+ &keyItem, NULL);
+ if (!ctx->key) {
goto loser;
}
@@ -100,20 +110,18 @@ loser:
#define SSL_MAX_BLOOM_FILTER_SIZE 64
/*
- * The structures created by this function can be called concurrently on
- * multiple threads if the server is multi-threaded. A monitor is used to
- * ensure that only one thread can access the structures that change over time,
- * but no such guarantee is provided for configuration data.
- *
- * Functions that read from static configuration data depend on there being a
- * memory barrier between the setup and use of this function.
+ * The context created by this function can be called concurrently on multiple
+ * threads if the server is multi-threaded. A monitor is used to ensure that
+ * only one thread can access the structures that change over time, but no such
+ * guarantee is provided for configuration data.
*/
SECStatus
-SSLExp_InitAntiReplay(PRTime now, PRTime window, unsigned int k, unsigned int bits)
+SSLExp_CreateAntiReplayContext(PRTime now, PRTime window, unsigned int k,
+ unsigned int bits, SSLAntiReplayContext **pctx)
{
SECStatus rv;
- if (k == 0 || bits == 0) {
+ if (window <= 0 || k == 0 || bits == 0 || pctx == NULL) {
PORT_SetError(SEC_ERROR_INVALID_ARGS);
return SECFailure;
}
@@ -122,54 +130,69 @@ SSLExp_InitAntiReplay(PRTime now, PRTime window, unsigned int k, unsigned int bi
return SECFailure;
}
- if (PR_SUCCESS != PR_CallOnce(&ssl_anti_replay.init,
- tls13_AntiReplayInit)) {
- PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
- return SECFailure;
+ SSLAntiReplayContext *ctx = PORT_ZNew(SSLAntiReplayContext);
+ if (!ctx) {
+ return SECFailure; /* Code already set. */
}
- (void)tls13_AntiReplayReset(NULL, NULL);
-
- ssl_anti_replay.lock = PZ_NewMonitor(nssILockSSL);
- if (!ssl_anti_replay.lock) {
+ ctx->refCount = 1;
+ ctx->lock = PZ_NewMonitor(nssILockSSL);
+ if (!ctx->lock) {
goto loser; /* Code already set. */
}
- rv = tls13_AntiReplayKeyGen();
+ rv = tls13_AntiReplayKeyGen(ctx);
if (rv != SECSuccess) {
goto loser; /* Code already set. */
}
- rv = sslBloom_Init(&ssl_anti_replay.filters[0], k, bits);
+ rv = sslBloom_Init(&ctx->filters[0], k, bits);
if (rv != SECSuccess) {
goto loser; /* Code already set. */
}
- rv = sslBloom_Init(&ssl_anti_replay.filters[1], k, bits);
+ rv = sslBloom_Init(&ctx->filters[1], k, bits);
if (rv != SECSuccess) {
goto loser; /* Code already set. */
}
/* When starting out, ensure that 0-RTT is not accepted until the window is
* updated. A ClientHello might have been accepted prior to a restart. */
- sslBloom_Fill(&ssl_anti_replay.filters[1]);
+ sslBloom_Fill(&ctx->filters[1]);
- ssl_anti_replay.current = 0;
- ssl_anti_replay.nextUpdate = now + window;
- ssl_anti_replay.window = window;
+ ctx->current = 0;
+ ctx->nextUpdate = now + window;
+ ctx->window = window;
+ *pctx = ctx;
return SECSuccess;
loser:
- (void)tls13_AntiReplayReset(NULL, NULL);
+ tls13_ReleaseAntiReplayContext(ctx);
return SECFailure;
}
+SECStatus
+SSLExp_SetAntiReplayContext(PRFileDesc *fd, SSLAntiReplayContext *ctx)
+{
+ sslSocket *ss = ssl_FindSocket(fd);
+ if (!ss) {
+ return SECFailure; /* Code already set. */
+ }
+ tls13_ReleaseAntiReplayContext(ss->antiReplay);
+ if (ctx != NULL) {
+ ss->antiReplay = tls13_RefAntiReplayContext(ctx);
+ } else {
+ ss->antiReplay = NULL;
+ }
+ return SECSuccess;
+}
+
static void
-tls13_AntiReplayUpdate(PRTime now)
+tls13_AntiReplayUpdate(SSLAntiReplayContext *ctx, PRTime now)
{
- PR_ASSERT_CURRENT_THREAD_IN_MONITOR(ssl_anti_replay.lock);
- if (now >= ssl_anti_replay.nextUpdate) {
- ssl_anti_replay.current ^= 1;
- ssl_anti_replay.nextUpdate = now + ssl_anti_replay.window;
- sslBloom_Zero(ssl_anti_replay.filters + ssl_anti_replay.current);
+ PR_ASSERT_CURRENT_THREAD_IN_MONITOR(ctx->lock);
+ if (now >= ctx->nextUpdate) {
+ ctx->current ^= 1;
+ ctx->nextUpdate = now + ctx->window;
+ sslBloom_Zero(ctx->filters + ctx->current);
}
}
@@ -207,7 +230,10 @@ tls13_InWindow(const sslSocket *ss, const sslSessionID *sid)
* prevent the same 0-RTT attempt from being accepted during window 1 and
* later window 3.
*/
- return PR_ABS(timeDelta) < (ssl_anti_replay.window / (PR_USEC_PER_MSEC * 2));
+ PRInt32 allowance = ss->antiReplay->window / (PR_USEC_PER_MSEC * 2);
+ SSL_TRC(10, ("%d: TLS13[%d]: replay check time delta=%d, allow=%d",
+ SSL_GETPID(), ss->fd, timeDelta, allowance));
+ return PR_ABS(timeDelta) < allowance;
}
/* Checks for a duplicate in the two filters we have. Performs maintenance on
@@ -222,12 +248,13 @@ tls13_IsReplay(const sslSocket *ss, const sslSessionID *sid)
unsigned int size;
PRUint8 index;
SECStatus rv;
- static const char *label = "tls13 anti-replay";
+ static const char *label = "anti-replay";
PRUint8 buf[SSL_MAX_BLOOM_FILTER_SIZE];
+ SSLAntiReplayContext *ctx = ss->antiReplay;
- /* If SSL_SetupAntiReplay hasn't been called, then treat all attempts at
- * 0-RTT as a replay. */
- if (!ssl_anti_replay.init.initialized) {
+ /* If SSL_SetAntiReplayContext hasn't been called with a valid context, then
+ * treat all attempts at 0-RTT as a replay. */
+ if (ctx == NULL) {
return PR_TRUE;
}
@@ -235,10 +262,9 @@ tls13_IsReplay(const sslSocket *ss, const sslSessionID *sid)
return PR_TRUE;
}
- size = ssl_anti_replay.filters[0].k *
- (ssl_anti_replay.filters[0].bits + 7) / 8;
+ size = ctx->filters[0].k * (ctx->filters[0].bits + 7) / 8;
PORT_Assert(size <= SSL_MAX_BLOOM_FILTER_SIZE);
- rv = tls13_HkdfExpandLabelRaw(ssl_anti_replay.key, ssl_hash_sha256,
+ rv = tls13_HkdfExpandLabelRaw(ctx->key, ssl_hash_sha256,
ss->xtnData.pskBinder.data,
ss->xtnData.pskBinder.len,
label, strlen(label),
@@ -247,15 +273,19 @@ tls13_IsReplay(const sslSocket *ss, const sslSessionID *sid)
return PR_TRUE;
}
- PZ_EnterMonitor(ssl_anti_replay.lock);
- tls13_AntiReplayUpdate(ssl_Time(ss));
+ PZ_EnterMonitor(ctx->lock);
+ tls13_AntiReplayUpdate(ctx, ssl_Time(ss));
- index = ssl_anti_replay.current;
- replay = sslBloom_Add(&ssl_anti_replay.filters[index], buf);
+ index = ctx->current;
+ replay = sslBloom_Add(&ctx->filters[index], buf);
+ SSL_TRC(10, ("%d: TLS13[%d]: replay check current window: %s",
+ SSL_GETPID(), ss->fd, replay ? "replay" : "ok"));
if (!replay) {
- replay = sslBloom_Check(&ssl_anti_replay.filters[index ^ 1], buf);
+ replay = sslBloom_Check(&ctx->filters[index ^ 1], buf);
+ SSL_TRC(10, ("%d: TLS13[%d]: replay check previous window: %s",
+ SSL_GETPID(), ss->fd, replay ? "replay" : "ok"));
}
- PZ_ExitMonitor(ssl_anti_replay.lock);
+ PZ_ExitMonitor(ctx->lock);
return replay;
}