Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Bug 1295163 - Limit time-variation when accepting 0-RTT, r=ekr
--HG--
branch : NSS_TLS13_DRAFT19_BRANCH
extra : rebase_source : 6d7b1f33b0da396706a01dedc0142868d727daf4
  • Loading branch information
martinthomson committed May 22, 2017
1 parent 8fcd1d4 commit f077d65
Show file tree
Hide file tree
Showing 20 changed files with 351 additions and 95 deletions.
11 changes: 11 additions & 0 deletions gtests/ssl_gtest/libssl_internals.c
Expand Up @@ -380,3 +380,14 @@ 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;
}
1 change: 1 addition & 0 deletions gtests/ssl_gtest/libssl_internals.h
Expand Up @@ -51,5 +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);

#endif // ndef libssl_internals_h_
53 changes: 53 additions & 0 deletions gtests/ssl_gtest/ssl_0rtt_unittest.cc
Expand Up @@ -100,6 +100,59 @@ TEST_P(TlsConnectTls13, ZeroRttServerOnly) {
CheckKeys();
}

// 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.
TEST_P(TlsConnectTls13, ZeroRttRejectOldTicket) {
SetupForZeroRtt();
client_->Set0RttEnabled(true);
server_->Set0RttEnabled(true);
SSLInt_SetTicketAgeTolerance(server_->ssl_fd(), 1);
ExpectResumption(RESUME_TICKET);
ZeroRttSendReceive(true, false, []() {
PR_Sleep(PR_MillisecondsToInterval(10));
return true;
});
Handshake();
ExpectEarlyDataAccepted(false);
CheckConnected();
SendReceive();
}

// In this test, we falsely inflate the estimate of the RTT by delaying the
// ServerHello on the first handshake. This results in the server estimating a
// higher value of the ticket age than the client ultimately provides. Add a
// small tolerance for variation in ticket age and the ticket will appear to
// arrive prematurely, causing the server to reject early data.
TEST_P(TlsConnectTls13, ZeroRttRejectPrematureTicket) {
ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3);
server_->Set0RttEnabled(true);
client_->StartConnect();
server_->StartConnect();
client_->Handshake(); // ClientHello
server_->Handshake(); // ServerHello
PR_Sleep(PR_MillisecondsToInterval(10));
Handshake(); // Remainder of handshake
CheckConnected();
SendReceive();
CheckKeys();

Reset();
client_->Set0RttEnabled(true);
server_->Set0RttEnabled(true);
SSLInt_SetTicketAgeTolerance(server_->ssl_fd(), 1);
ExpectResumption(RESUME_TICKET);
ExpectEarlyDataAccepted(false);

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

TEST_P(TlsConnectTls13, TestTls13ZeroRttAlpn) {
EnableAlpn();
SetupForZeroRtt();
Expand Down
4 changes: 2 additions & 2 deletions gtests/ssl_gtest/ssl_fuzz_unittest.cc
Expand Up @@ -47,9 +47,9 @@ class TlsApplicationDataRecorder : public TlsRecordFilter {

// Ensure that ssl_Time() returns a constant value.
FUZZ_F(TlsFuzzTest, SSL_Time_Constant) {
PRUint32 now = ssl_Time();
PRUint32 now = ssl_TimeSec();
PR_Sleep(PR_SecondsToInterval(2));
EXPECT_EQ(ssl_Time(), now);
EXPECT_EQ(ssl_TimeSec(), now);
}

// Check that due to the deterministic PRNG we derive
Expand Down
9 changes: 1 addition & 8 deletions gtests/ssl_gtest/tls_connect.cc
Expand Up @@ -552,20 +552,13 @@ void TlsConnectTestBase::SendReceive() {
// Do a first connection so we can do 0-RTT on the second one.
void TlsConnectTestBase::SetupForZeroRtt() {
ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
client_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_1,
SSL_LIBRARY_VERSION_TLS_1_3);
server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_1,
SSL_LIBRARY_VERSION_TLS_1_3);
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();
client_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_1,
SSL_LIBRARY_VERSION_TLS_1_3);
server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_1,
SSL_LIBRARY_VERSION_TLS_1_3);
server_->StartConnect();
client_->StartConnect();
}
Expand Down
3 changes: 2 additions & 1 deletion lib/ssl/authcert.c
Expand Up @@ -17,6 +17,7 @@
#include "nss.h"
#include "ssl.h"
#include "pk11func.h" /* for PK11_ function calls */
#include "sslimpl.h"

/*
* This callback used by SSL to pull client sertificate upon
Expand Down Expand Up @@ -63,7 +64,7 @@ NSS_GetClientAuthData(void *arg,
if (!cert)
continue;
/* Only check unexpired certs */
if (CERT_CheckCertValidTimes(cert, PR_Now(), PR_TRUE) !=
if (CERT_CheckCertValidTimes(cert, ssl_TimeUsec(), PR_TRUE) !=
secCertTimeValid) {
CERT_DestroyCertificate(cert);
continue;
Expand Down
9 changes: 4 additions & 5 deletions lib/ssl/ssl3con.c
Expand Up @@ -2949,9 +2949,8 @@ ssl3_FlushHandshake(sslSocket *ss, PRInt32 flags)
{
if (IS_DTLS(ss)) {
return dtls_FlushHandshakeMessages(ss, flags);
} else {
return ssl3_FlushHandshakeMessages(ss, flags);
}
return ssl3_FlushHandshakeMessages(ss, flags);
}

/* Attempt to send the content of sendBuf buffer in an SSL handshake record.
Expand Down Expand Up @@ -10196,7 +10195,7 @@ ssl3_HandleNewSessionTicket(sslSocket *ss, PRUint8 *b, PRUint32 length)
* until it has verified the server's Finished message." See the comment in
* ssl3_FinishHandshake for more details.
*/
ss->ssl3.hs.newSessionTicket.received_timestamp = PR_Now();
ss->ssl3.hs.newSessionTicket.received_timestamp = ssl_TimeUsec();
if (length < 4) {
(void)SSL3_SendAlert(ss, alert_fatal, decode_error);
PORT_SetError(SSL_ERROR_RX_MALFORMED_NEW_SESSION_TICKET);
Expand Down Expand Up @@ -11488,8 +11487,8 @@ ssl3_FillInCachedSID(sslSocket *ss, sslSessionID *sid, PK11SymKey *secret)
sid->authKeyBits = ss->sec.authKeyBits;
sid->keaType = ss->sec.keaType;
sid->keaKeyBits = ss->sec.keaKeyBits;
sid->lastAccessTime = sid->creationTime = ssl_Time();
sid->expirationTime = sid->creationTime + ssl3_sid_timeout;
sid->lastAccessTime = sid->creationTime = ssl_TimeUsec();
sid->expirationTime = sid->creationTime + ssl3_sid_timeout * PR_USEC_PER_SEC;
sid->localCert = CERT_DupCertificate(ss->sec.localCert);
if (ss->sec.isServer) {
sid->namedCurve = ss->sec.serverCert->namedCurve;
Expand Down
85 changes: 85 additions & 0 deletions lib/ssl/ssl3encode.c
@@ -0,0 +1,85 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is PRIVATE to SSL.
*
* 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 "prnetdb.h"
#include "seccomon.h"
#include "secerr.h"
#include "ssl3encode.h"

SECStatus
ssl3_AppendToItem(SECItem *item, const unsigned char *buf, PRUint32 bytes)
{
if (bytes > item->len) {
PORT_SetError(SEC_ERROR_INVALID_ARGS);
return SECFailure;
}

PORT_Memcpy(item->data, buf, bytes);
item->data += bytes;
item->len -= bytes;
return SECSuccess;
}

SECStatus
ssl3_AppendNumberToItem(SECItem *item, PRUint64 num, PRInt32 lenSize)
{
SECStatus rv;
PRUint8 b[sizeof(num)];

ssl_EncodeUintX(num, lenSize, b);
rv = ssl3_AppendToItem(item, &b[0], lenSize);
return rv;
}

SECStatus
ssl3_ConsumeFromItem(SECItem *item, unsigned char **buf, PRUint32 bytes)
{
if (bytes > item->len) {
PORT_SetError(SEC_ERROR_BAD_DATA);
return SECFailure;
}

*buf = item->data;
item->data += bytes;
item->len -= bytes;
return SECSuccess;
}

SECStatus
ssl3_ConsumeNumberFromItem(SECItem *item, PRUint32 *num, PRUint32 bytes)
{
int i;

if (bytes > item->len || bytes > sizeof(*num)) {
PORT_SetError(SEC_ERROR_BAD_DATA);
return SECFailure;
}

*num = 0;
for (i = 0; i < bytes; i++) {
*num = (*num << 8) + item->data[i];
}

item->data += bytes;
item->len -= bytes;

return SECSuccess;
}

/* Helper function to encode an unsigned integer into a buffer. */
PRUint8 *
ssl_EncodeUintX(PRUint64 value, unsigned int bytes, PRUint8 *to)
{
PRUint64 encoded;

PORT_Assert(bytes > 0 && bytes <= sizeof(encoded));

encoded = PR_htonll(value);
memcpy(to, ((unsigned char *)(&encoded)) + (sizeof(encoded) - bytes), bytes);
return to + bytes;
}
26 changes: 26 additions & 0 deletions lib/ssl/ssl3encode.h
@@ -0,0 +1,26 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is PRIVATE to SSL.
*
* 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/. */

#ifndef __ssl3encode_h_
#define __ssl3encode_h_

#include "seccomon.h"

/* All of these functions modify the underlying SECItem, and so should
* be performed on a shallow copy.*/
SECStatus ssl3_AppendToItem(SECItem *item,
const unsigned char *buf, PRUint32 bytes);
SECStatus ssl3_AppendNumberToItem(SECItem *item,
PRUint64 num, PRInt32 lenSize);
SECStatus ssl3_ConsumeFromItem(SECItem *item,
unsigned char **buf, PRUint32 bytes);
SECStatus ssl3_ConsumeNumberFromItem(SECItem *item,
PRUint32 *num, PRUint32 bytes);
PRUint8 *ssl_EncodeUintX(PRUint64 value, unsigned int bytes, PRUint8 *to);

#endif
2 changes: 2 additions & 0 deletions lib/ssl/ssl3ext.h
Expand Up @@ -90,6 +90,8 @@ struct TLSExtensionDataStr {
SECItem pskBinder; /* The PSK binder for the first PSK (TLS 1.3) */
unsigned int pskBinderPrefixLen; /* The length of the binder input. */
PRCList remoteKeyShares; /* The other side's public keys (TLS 1.3) */
/* This is used when deciding whether to accept early data. */
PRUint32 ticketAge;
};

typedef struct TLSExtensionStr {
Expand Down

0 comments on commit f077d65

Please sign in to comment.