Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Bug 1343036 - Early data size limit, r=franziskus,ekr
--HG--
extra : rebase_source : 534c4b9c7eb8e00d24b4a838000cc6a48c12a42b
extra : amend_source : 98c7feaa2b8e9f701385de9305884e37c652b22d
  • Loading branch information
martinthomson committed Mar 6, 2017
1 parent b990eae commit cc62cf3
Show file tree
Hide file tree
Showing 14 changed files with 269 additions and 81 deletions.
54 changes: 39 additions & 15 deletions gtests/ssl_gtest/libssl_internals.c
Expand Up @@ -33,15 +33,8 @@ SECStatus SSLInt_UpdateSSLv2ClientRandom(PRFileDesc *fd, uint8_t *rnd,
return SECFailure;
}

SECStatus rv = ssl3_InitState(ss);
if (rv != SECSuccess) {
return rv;
}

rv = ssl3_RestartHandshakeHashes(ss);
if (rv != SECSuccess) {
return rv;
}
ssl3_InitState(ss);
ssl3_RestartHandshakeHashes(ss);

// Ensure we don't overrun hs.client_random.
rnd_len = PR_MIN(SSL3_RANDOM_LENGTH, rnd_len);
Expand All @@ -66,11 +59,11 @@ void SSLInt_ClearSessionTicketKey() { ssl_ResetSessionTicketKeys(); }

SECStatus SSLInt_SetMTU(PRFileDesc *fd, PRUint16 mtu) {
sslSocket *ss = ssl_FindSocket(fd);
if (ss) {
ss->ssl3.mtu = mtu;
return SECSuccess;
if (!ss) {
return SECFailure;
}
return SECFailure;
ss->ssl3.mtu = mtu;
return SECSuccess;
}

PRInt32 SSLInt_CountTls13CipherSpecs(PRFileDesc *fd) {
Expand Down Expand Up @@ -194,7 +187,9 @@ SECStatus SSLInt_Set0RttAlpn(PRFileDesc *fd, PRUint8 *data, unsigned int len) {
if (ss->xtnData.nextProto.data) {
SECITEM_FreeItem(&ss->xtnData.nextProto, PR_FALSE);
}
if (!SECITEM_AllocItem(NULL, &ss->xtnData.nextProto, len)) return SECFailure;
if (!SECITEM_AllocItem(NULL, &ss->xtnData.nextProto, len)) {
return SECFailure;
}
PORT_Memcpy(ss->xtnData.nextProto.data, data, len);

return SECSuccess;
Expand Down Expand Up @@ -251,6 +246,7 @@ SECStatus SSLInt_AdvanceReadSeqNum(PRFileDesc *fd, PRUint64 to) {
return SECFailure;
}
if (to >= (1ULL << 48)) {
PORT_SetError(SEC_ERROR_INVALID_ARGS);
return SECFailure;
}
ssl_GetSpecWriteLock(ss);
Expand All @@ -262,6 +258,7 @@ SECStatus SSLInt_AdvanceReadSeqNum(PRFileDesc *fd, PRUint64 to) {
* scrub the entire structure on the assumption that the new sequence number
* is far enough past the last received sequence number. */
if (to <= spec->recvdRecords.right + DTLS_RECVD_RECORDS_WINDOW) {
PORT_SetError(SEC_ERROR_INVALID_ARGS);
return SECFailure;
}
dtls_RecordSetRecvd(&spec->recvdRecords, to);
Expand All @@ -279,6 +276,7 @@ SECStatus SSLInt_AdvanceWriteSeqNum(PRFileDesc *fd, PRUint64 to) {
return SECFailure;
}
if (to >= (1ULL << 48)) {
PORT_SetError(SEC_ERROR_INVALID_ARGS);
return SECFailure;
}
ssl_GetSpecWriteLock(ss);
Expand Down Expand Up @@ -364,10 +362,36 @@ SECStatus SSLInt_UsingShortHeaders(PRFileDesc *fd, PRBool *result) {
}

*result = ss->ssl3.hs.shortHeaders;

return SECSuccess;
}

void SSLInt_SetTicketLifetime(uint32_t lifetime) {
ssl_ticket_lifetime = lifetime;
}

void SSLInt_SetMaxEarlyDataSize(uint32_t size) {
ssl_max_early_data_size = size;
}

SECStatus SSLInt_SetSocketMaxEarlyDataSize(PRFileDesc *fd, uint32_t size) {
sslSocket *ss;

ss = ssl_FindSocket(fd);
if (!ss) {
return SECFailure;
}

/* This only works when resuming. */
if (!ss->statelessResume) {
PORT_SetError(SEC_INTERNAL_ONLY);
return SECFailure;
}

/* Modifying both specs allows this to be used on either peer. */
ssl_GetSpecWriteLock(ss);
ss->ssl3.crSpec->earlyDataRemaining = size;
ss->ssl3.cwSpec->earlyDataRemaining = size;
ssl_ReleaseSpecWriteLock(ss);

return SECSuccess;
}
2 changes: 2 additions & 0 deletions gtests/ssl_gtest/libssl_internals.h
Expand Up @@ -50,5 +50,7 @@ unsigned char *SSLInt_CipherSpecToIv(PRBool isServer, ssl3CipherSpec *spec);
SECStatus SSLInt_EnableShortHeaders(PRFileDesc *fd);
SECStatus SSLInt_UsingShortHeaders(PRFileDesc *fd, PRBool *result);
void SSLInt_SetTicketLifetime(uint32_t lifetime);
void SSLInt_SetMaxEarlyDataSize(uint32_t size);
SECStatus SSLInt_SetSocketMaxEarlyDataSize(PRFileDesc *fd, uint32_t size);

#endif // ndef libssl_internals_h_
106 changes: 106 additions & 0 deletions gtests/ssl_gtest/ssl_0rtt_unittest.cc
Expand Up @@ -282,4 +282,110 @@ TEST_P(TlsConnectTls13, TestTls13ZeroRttDowngradeEarlyData) {
}
}

static void CheckEarlyDataLimit(const std::shared_ptr<TlsAgent>& agent,
size_t expected_size) {
SSLPreliminaryChannelInfo preinfo;
SECStatus rv =
SSL_GetPreliminaryChannelInfo(agent->ssl_fd(), &preinfo, sizeof(preinfo));
EXPECT_EQ(SECSuccess, rv);
EXPECT_EQ(expected_size, static_cast<size_t>(preinfo.maxEarlyDataSize));
}

TEST_P(TlsConnectTls13, SendTooMuchEarlyData) {
const char* big_message = "0123456789abcdef";
const size_t short_size = strlen(big_message) - 1;
const PRInt32 short_length = static_cast<PRInt32>(short_size);
SSLInt_SetMaxEarlyDataSize(static_cast<PRUint32>(short_size));
SetupForZeroRtt();

client_->Set0RttEnabled(true);
server_->Set0RttEnabled(true);
ExpectResumption(RESUME_TICKET);
client_->SetExpectedAlertSentCount(1);
server_->SetExpectedAlertReceivedCount(1);

client_->Handshake();
CheckEarlyDataLimit(client_, short_size);

PRInt32 sent;
// Writing more than the limit will succeed in TLS, but fail in DTLS.
if (mode_ == STREAM) {
sent = PR_Write(client_->ssl_fd(), big_message,
static_cast<PRInt32>(strlen(big_message)));
} else {
sent = PR_Write(client_->ssl_fd(), big_message,
static_cast<PRInt32>(strlen(big_message)));
EXPECT_GE(0, sent);
EXPECT_EQ(PR_WOULD_BLOCK_ERROR, PORT_GetError());

// Try an exact-sized write now.
sent = PR_Write(client_->ssl_fd(), big_message, short_length);
}
EXPECT_EQ(short_length, sent);

// Even a single octet write should now fail.
sent = PR_Write(client_->ssl_fd(), big_message, 1);
EXPECT_GE(0, sent);
EXPECT_EQ(PR_WOULD_BLOCK_ERROR, PORT_GetError());

// Process the ClientHello and read 0-RTT.
server_->Handshake();
CheckEarlyDataLimit(server_, short_size);

std::vector<uint8_t> buf(short_size + 1);
PRInt32 read = PR_Read(server_->ssl_fd(), buf.data(), buf.capacity());
EXPECT_EQ(short_length, read);
EXPECT_EQ(0, memcmp(big_message, buf.data(), short_size));

// Second read fails.
read = PR_Read(server_->ssl_fd(), buf.data(), buf.capacity());
EXPECT_EQ(SECFailure, read);
EXPECT_EQ(PR_WOULD_BLOCK_ERROR, PORT_GetError());

Handshake();
ExpectEarlyDataAccepted(true);
CheckConnected();
SendReceive();
}

TEST_P(TlsConnectTls13, ReceiveTooMuchEarlyData) {
const size_t limit = 5;
SSLInt_SetMaxEarlyDataSize(limit);
SetupForZeroRtt();

client_->Set0RttEnabled(true);
server_->Set0RttEnabled(true);
ExpectResumption(RESUME_TICKET);

client_->Handshake(); // Send ClientHello
CheckEarlyDataLimit(client_, limit);

// Lift the limit on the client.
EXPECT_EQ(SECSuccess,
SSLInt_SetSocketMaxEarlyDataSize(client_->ssl_fd(), 1000));

// Send message
const char* message = "0123456789abcdef";
const PRInt32 message_len = static_cast<PRInt32>(strlen(message));
EXPECT_EQ(message_len, PR_Write(client_->ssl_fd(), message, message_len));

server_->Handshake(); // Process ClientHello, send server flight.
server_->Handshake(); // Just to make sure that we don't read ahead.
CheckEarlyDataLimit(server_, limit);

// Attempt to read early data.
std::vector<uint8_t> buf(strlen(message) + 1);
EXPECT_GT(0, PR_Read(server_->ssl_fd(), buf.data(), buf.capacity()));
if (mode_ == STREAM) {
// This error isn't fatal for DTLS.
server_->CheckErrorCode(SSL_ERROR_TOO_MUCH_EARLY_DATA);
}

client_->Handshake(); // Process the handshake.
client_->Handshake(); // Process the alert.
if (mode_ == STREAM) {
client_->CheckErrorCode(SSL_ERROR_HANDSHAKE_UNEXPECTED_ALERT);
}
}

} // namespace nss_test
1 change: 1 addition & 0 deletions gtests/ssl_gtest/tls_connect.cc
Expand Up @@ -175,6 +175,7 @@ void TlsConnectTestBase::SetUp() {
SSL_ConfigServerSessionIDCache(1024, 0, 0, g_working_dir_path.c_str());
SSLInt_ClearSessionTicketKey();
SSLInt_SetTicketLifetime(30);
SSLInt_SetMaxEarlyDataSize(1024);
ClearStats();
Init();
}
Expand Down
3 changes: 3 additions & 0 deletions lib/ssl/SSLerrs.h
Expand Up @@ -508,3 +508,6 @@ ER3(SSL_ERROR_MISSING_PSK_KEY_EXCHANGE_MODES, (SSL_ERROR_BASE + 159),

ER3(SSL_ERROR_DOWNGRADE_WITH_EARLY_DATA, (SSL_ERROR_BASE + 160),
"SSL got a pre-TLS 1.3 version even though we sent early data.")

ER3(SSL_ERROR_TOO_MUCH_EARLY_DATA, (SSL_ERROR_BASE + 161),
"SSL received more early data than permitted.")
51 changes: 11 additions & 40 deletions lib/ssl/ssl3con.c
Expand Up @@ -2726,10 +2726,7 @@ ssl3_SendRecord(sslSocket *ss,
** trying to send an alert.
*/
PR_ASSERT(type == content_alert);
rv = ssl3_InitState(ss);
if (rv != SECSuccess) {
return SECFailure; /* ssl3_InitState has set the error code. */
}
ssl3_InitState(ss);
}

/* check for Token Presence */
Expand Down Expand Up @@ -2935,6 +2932,7 @@ ssl3_SendApplicationData(sslSocket *ss, const unsigned char *in,
ssl_GetXmitBufLock(ss);
}
toSend = PR_MIN(len - totalSent, MAX_FRAGMENT_LENGTH);

/*
* Note that the 0 epoch is OK because flags will never require
* its use, as guaranteed by the PORT_Assert above.
Expand Down Expand Up @@ -4101,11 +4099,9 @@ ssl3_InitHandshakeHashes(sslSocket *ss)
return SECSuccess;
}

SECStatus
void
ssl3_RestartHandshakeHashes(sslSocket *ss)
{
SECStatus rv = SECSuccess;

SSL_TRC(30, ("%d: SSL3[%d]: reset handshake hashes",
SSL_GETPID(), ss->fd));
ss->ssl3.hs.hashType = handshake_hash_unknown;
Expand All @@ -4118,7 +4114,6 @@ ssl3_RestartHandshakeHashes(sslSocket *ss)
PK11_DestroyContext(ss->ssl3.hs.sha, PR_TRUE);
ss->ssl3.hs.sha = NULL;
}
return rv;
}

/*
Expand Down Expand Up @@ -5024,15 +5019,8 @@ ssl3_SendClientHello(sslSocket *ss, sslClientHelloType type)
if (ss->ssl3.hs.helloRetry) {
PORT_Assert(type == client_hello_retry);
} else {
rv = ssl3_InitState(ss);
if (rv != SECSuccess) {
return rv; /* ssl3_InitState has set the error code. */
}

rv = ssl3_RestartHandshakeHashes(ss);
if (rv != SECSuccess) {
return rv;
}
ssl3_InitState(ss);
ssl3_RestartHandshakeHashes(ss);
}

/* These must be reset every handshake. */
Expand Down Expand Up @@ -9164,16 +9152,8 @@ ssl3_HandleV2ClientHello(sslSocket *ss, unsigned char *buffer, int length,
goto loser;
}

rv = ssl3_InitState(ss);
if (rv != SECSuccess) {
ssl_ReleaseSSL3HandshakeLock(ss);
return rv; /* ssl3_InitState has set the error code. */
}
rv = ssl3_RestartHandshakeHashes(ss);
if (rv != SECSuccess) {
ssl_ReleaseSSL3HandshakeLock(ss);
return rv;
}
ssl3_InitState(ss);
ssl3_RestartHandshakeHashes(ss);

if (ss->ssl3.hs.ws != wait_client_hello) {
desc = unexpected_message;
Expand Down Expand Up @@ -11795,10 +11775,7 @@ ssl3_HandleHandshakeMessage(sslSocket *ss, SSL3Opaque *b, PRUint32 length,
/* Start new handshake hashes when we start a new handshake. Unless this is
* TLS 1.3 and we sent a HelloRetryRequest. */
if (ss->ssl3.hs.msg_type == client_hello && !ss->ssl3.hs.helloRetry) {
rv = ssl3_RestartHandshakeHashes(ss);
if (rv != SECSuccess) {
return rv;
}
ssl3_RestartHandshakeHashes(ss);
}
/* We should not include hello_request and hello_verify_request messages
* in the handshake hashes */
Expand Down Expand Up @@ -12590,11 +12567,8 @@ ssl3_HandleRecord(sslSocket *ss, SSL3Ciphertext *cText, sslBuffer *databuf)

if (!ss->ssl3.initialized) {
ssl_GetSSL3HandshakeLock(ss);
rv = ssl3_InitState(ss);
ssl3_InitState(ss);
ssl_ReleaseSSL3HandshakeLock(ss);
if (rv != SECSuccess) {
return rv; /* ssl3_InitState has set the error code. */
}
}

/* check for Token Presence */
Expand Down Expand Up @@ -12913,16 +12887,14 @@ ssl3_InitCipherSpec(ssl3CipherSpec *spec)
** ssl3_HandleRecord()
**
** This function should perhaps acquire and release the SpecWriteLock.
**
**
*/
SECStatus
void
ssl3_InitState(sslSocket *ss)
{
PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));

if (ss->ssl3.initialized)
return SECSuccess; /* Function should be idempotent */
return; /* Function should be idempotent */

ss->ssl3.policy = SSL_ALLOWED;

Expand Down Expand Up @@ -12977,7 +12949,6 @@ ssl3_InitState(sslSocket *ss)
ssl_FilterSupportedGroups(ss);

ss->ssl3.initialized = PR_TRUE;
return SECSuccess;
}

/* record the export policy for this cipher suite */
Expand Down

0 comments on commit cc62cf3

Please sign in to comment.