summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin Thomson <martin.thomson@gmail.com>2018-10-23 15:35:13 +1100
committerMartin Thomson <martin.thomson@gmail.com>2018-10-23 15:35:13 +1100
commita9b4d3dbe96c8889dc331fe39490402cb40e5f56 (patch)
treea4c88d6cc94168208d49baeebeefd26a9aa0db86
parentcc253da3e8d632ea0f8268516788de77b7a60d9b (diff)
downloadnss-hg-a9b4d3dbe96c8889dc331fe39490402cb40e5f56.tar.gz
Bug 1471126 - Provide extra information needed to use record layer separation, r=ekr
This started as an attempt to remove the cipher spec update callback we use for testing. Using the new, public secrets interface should be better for that. In doing so, it became apparent that we needed more interfaces to NSS to support the use of these secrets. In particular: 1. We need to know what the KDF hash function is for a given cipher suite. This allows users of the secret to use the right hash function. 2. We need to know what cipher spec was picked when sending 0-RTT. NSS currently doesn't expose that information. (When receiving 0-RTT you can safely assume that the negotiated cipher suite is good to use.) 3. We need to know what epoch NSS is currently using. Otherwise, we can't be sure which epoch to feed it. Data from a good epoch is saved, whereas data from a bad epoch is lost, so applications need to know. So this patch adds these functions to the appropriate info functions and uses that information in tests to remove and re-add protection. The test changes are considerable. The main effect of the changes is to rely on the new functions for managing secrets, rather than the old interface. But with the changes in the other CLs for this bug, secrets appear before they are used, which complicates things considerably. For that, I've moved more logic into the TlsCipherSpec class, which now tracks per-epoch state, like sequence numbers and record drops. Trial decryption (yep) is used to identify the right cipher spec every time when decrypting, so tests are no longer tolerant of failures to decrypt. It's no longer possible to have a test enable decryption and pass when decryption fails; this is particularly true for some parameterized tests that assumed it was OK to enable decryption even for TLS 1.2 and earlier.
-rw-r--r--automation/abi-check/expected-report-libssl3.so.txt20
-rw-r--r--gtests/ssl_gtest/libssl_internals.c46
-rw-r--r--gtests/ssl_gtest/libssl_internals.h10
-rw-r--r--gtests/ssl_gtest/ssl_auth_unittest.cc11
-rw-r--r--gtests/ssl_gtest/ssl_damage_unittest.cc5
-rw-r--r--gtests/ssl_gtest/ssl_drop_unittest.cc172
-rw-r--r--gtests/ssl_gtest/ssl_recordsep_unittest.cc25
-rw-r--r--gtests/ssl_gtest/ssl_recordsize_unittest.cc44
-rw-r--r--gtests/ssl_gtest/test_io.cc5
-rw-r--r--gtests/ssl_gtest/tls_agent.cc3
-rw-r--r--gtests/ssl_gtest/tls_filter.cc227
-rw-r--r--gtests/ssl_gtest/tls_filter.h34
-rw-r--r--gtests/ssl_gtest/tls_protect.cc151
-rw-r--r--gtests/ssl_gtest/tls_protect.h53
-rw-r--r--lib/ssl/ssl3gthr.c20
-rw-r--r--lib/ssl/sslexp.h14
-rw-r--r--lib/ssl/sslimpl.h7
-rw-r--r--lib/ssl/sslinfo.c163
-rw-r--r--lib/ssl/sslsock.c1
-rw-r--r--lib/ssl/sslt.h16
-rw-r--r--lib/ssl/tls13con.c17
21 files changed, 621 insertions, 423 deletions
diff --git a/automation/abi-check/expected-report-libssl3.so.txt b/automation/abi-check/expected-report-libssl3.so.txt
index e69de29bb..8ef488de0 100644
--- a/automation/abi-check/expected-report-libssl3.so.txt
+++ b/automation/abi-check/expected-report-libssl3.so.txt
@@ -0,0 +1,20 @@
+
+2 functions with some indirect sub-type change:
+
+ [C]'function SECStatus SSL_GetCipherSuiteInfo(PRUint16, SSLCipherSuiteInfo*, PRUintn)' at sslinfo.c:326:1 has some indirect sub-type changes:
+ parameter 2 of type 'SSLCipherSuiteInfo*' has sub-type changes:
+ in pointed to type 'typedef SSLCipherSuiteInfo' at sslt.h:433:1:
+ underlying type 'struct SSLCipherSuiteInfoStr' at sslt.h:366:1 changed:
+ type size changed from 768 to 832 (in bits)
+ 1 data member insertion:
+ 'SSLHashType SSLCipherSuiteInfoStr::kdfHash', at offset 768 (in bits) at sslt.h:429:1
+
+ [C]'function SECStatus SSL_GetPreliminaryChannelInfo(PRFileDesc*, SSLPreliminaryChannelInfo*, PRUintn)' at sslinfo.c:111:1 has some indirect sub-type changes:
+ parameter 2 of type 'SSLPreliminaryChannelInfo*' has sub-type changes:
+ in pointed to type 'typedef SSLPreliminaryChannelInfo' at sslt.h:379:1:
+ underlying type 'struct SSLPreliminaryChannelInfoStr' at sslt.h:333:1 changed:
+ type size changed from 160 to 192 (in bits)
+ 1 data member insertion:
+ 'PRUint16 SSLPreliminaryChannelInfoStr::zeroRttCipherSuite', at offset 160 (in bits) at sslt.h:375:1
+
+
diff --git a/gtests/ssl_gtest/libssl_internals.c b/gtests/ssl_gtest/libssl_internals.c
index 81ba7349e..2a9803daa 100644
--- a/gtests/ssl_gtest/libssl_internals.c
+++ b/gtests/ssl_gtest/libssl_internals.c
@@ -297,38 +297,6 @@ SSLKEAType SSLInt_GetKEAType(SSLNamedGroup group) {
return groupDef->keaType;
}
-SECStatus SSLInt_SetCipherSpecChangeFunc(PRFileDesc *fd,
- sslCipherSpecChangedFunc func,
- void *arg) {
- sslSocket *ss;
-
- ss = ssl_FindSocket(fd);
- if (!ss) {
- return SECFailure;
- }
-
- ss->ssl3.changedCipherSpecFunc = func;
- ss->ssl3.changedCipherSpecArg = arg;
-
- return SECSuccess;
-}
-
-PK11SymKey *SSLInt_CipherSpecToKey(const ssl3CipherSpec *spec) {
- return spec->keyMaterial.key;
-}
-
-SSLCipherAlgorithm SSLInt_CipherSpecToAlgorithm(const ssl3CipherSpec *spec) {
- return spec->cipherDef->calg;
-}
-
-const PRUint8 *SSLInt_CipherSpecToIv(const ssl3CipherSpec *spec) {
- return spec->keyMaterial.iv;
-}
-
-PRUint16 SSLInt_CipherSpecToEpoch(const ssl3CipherSpec *spec) {
- return spec->epoch;
-}
-
void SSLInt_SetTicketLifetime(uint32_t lifetime) {
ssl_ticket_lifetime = lifetime;
}
@@ -360,20 +328,6 @@ void SSLInt_RolloverAntiReplay(void) {
tls13_AntiReplayRollover(ssl_TimeUsec());
}
-SECStatus SSLInt_GetEpochs(PRFileDesc *fd, PRUint16 *readEpoch,
- PRUint16 *writeEpoch) {
- sslSocket *ss = ssl_FindSocket(fd);
- if (!ss || !readEpoch || !writeEpoch) {
- return SECFailure;
- }
-
- ssl_GetSpecReadLock(ss);
- *readEpoch = ss->ssl3.crSpec->epoch;
- *writeEpoch = ss->ssl3.cwSpec->epoch;
- ssl_ReleaseSpecReadLock(ss);
- return SECSuccess;
-}
-
SECStatus SSLInt_HasPendingHandshakeData(PRFileDesc *fd, PRBool *pending) {
sslSocket *ss = ssl_FindSocket(fd);
if (!ss) {
diff --git a/gtests/ssl_gtest/libssl_internals.h b/gtests/ssl_gtest/libssl_internals.h
index e61d7c942..8c78de28f 100644
--- a/gtests/ssl_gtest/libssl_internals.h
+++ b/gtests/ssl_gtest/libssl_internals.h
@@ -39,17 +39,7 @@ SECStatus SSLInt_AdvanceWriteSeqNum(PRFileDesc *fd, PRUint64 to);
SECStatus SSLInt_AdvanceReadSeqNum(PRFileDesc *fd, PRUint64 to);
SECStatus SSLInt_AdvanceWriteSeqByAWindow(PRFileDesc *fd, PRInt32 extra);
SSLKEAType SSLInt_GetKEAType(SSLNamedGroup group);
-SECStatus SSLInt_GetEpochs(PRFileDesc *fd, PRUint16 *readEpoch,
- PRUint16 *writeEpoch);
SECStatus SSLInt_HasPendingHandshakeData(PRFileDesc *fd, PRBool *pending);
-
-SECStatus SSLInt_SetCipherSpecChangeFunc(PRFileDesc *fd,
- sslCipherSpecChangedFunc func,
- void *arg);
-PRUint16 SSLInt_CipherSpecToEpoch(const ssl3CipherSpec *spec);
-PK11SymKey *SSLInt_CipherSpecToKey(const ssl3CipherSpec *spec);
-SSLCipherAlgorithm SSLInt_CipherSpecToAlgorithm(const ssl3CipherSpec *spec);
-const PRUint8 *SSLInt_CipherSpecToIv(const ssl3CipherSpec *spec);
void SSLInt_SetTicketLifetime(uint32_t lifetime);
SECStatus SSLInt_SetSocketMaxEarlyDataSize(PRFileDesc *fd, uint32_t size);
void SSLInt_RolloverAntiReplay(void);
diff --git a/gtests/ssl_gtest/ssl_auth_unittest.cc b/gtests/ssl_gtest/ssl_auth_unittest.cc
index 3a52ac20c..7e6a1c661 100644
--- a/gtests/ssl_gtest/ssl_auth_unittest.cc
+++ b/gtests/ssl_gtest/ssl_auth_unittest.cc
@@ -273,9 +273,7 @@ class TlsReplaceSignatureSchemeFilter : public TlsHandshakeFilter {
TlsReplaceSignatureSchemeFilter(const std::shared_ptr<TlsAgent>& a,
SSLSignatureScheme scheme)
: TlsHandshakeFilter(a, {kTlsHandshakeCertificateVerify}),
- scheme_(scheme) {
- EnableDecryption();
- }
+ scheme_(scheme) {}
protected:
virtual PacketFilter::Action FilterHandshake(const HandshakeHeader& header,
@@ -552,7 +550,9 @@ TEST_P(TlsConnectTls12, SignatureAlgorithmDrop) {
TEST_P(TlsConnectTls13, UnsupportedSignatureSchemeAlert) {
EnsureTlsSetup();
- MakeTlsFilter<TlsReplaceSignatureSchemeFilter>(server_, ssl_sig_none);
+ auto filter =
+ MakeTlsFilter<TlsReplaceSignatureSchemeFilter>(server_, ssl_sig_none);
+ filter->EnableDecryption();
ConnectExpectAlert(client_, kTlsAlertIllegalParameter);
server_->CheckErrorCode(SSL_ERROR_ILLEGAL_PARAMETER_ALERT);
@@ -563,8 +563,9 @@ TEST_P(TlsConnectTls13, InconsistentSignatureSchemeAlert) {
EnsureTlsSetup();
// This won't work because we use an RSA cert by default.
- MakeTlsFilter<TlsReplaceSignatureSchemeFilter>(
+ auto filter = MakeTlsFilter<TlsReplaceSignatureSchemeFilter>(
server_, ssl_sig_ecdsa_secp256r1_sha256);
+ filter->EnableDecryption();
ConnectExpectAlert(client_, kTlsAlertIllegalParameter);
server_->CheckErrorCode(SSL_ERROR_ILLEGAL_PARAMETER_ALERT);
diff --git a/gtests/ssl_gtest/ssl_damage_unittest.cc b/gtests/ssl_gtest/ssl_damage_unittest.cc
index 0723c9bee..9cbe9566f 100644
--- a/gtests/ssl_gtest/ssl_damage_unittest.cc
+++ b/gtests/ssl_gtest/ssl_damage_unittest.cc
@@ -62,7 +62,6 @@ TEST_P(TlsConnectGenericPre13, DamageServerSignature) {
EnsureTlsSetup();
auto filter = MakeTlsFilter<TlsLastByteDamager>(
server_, kTlsHandshakeServerKeyExchange);
- filter->EnableDecryption();
ExpectAlert(client_, kTlsAlertDecryptError);
ConnectExpectFail();
client_->CheckErrorCode(SEC_ERROR_BAD_SIGNATURE);
@@ -84,7 +83,9 @@ TEST_P(TlsConnectGeneric, DamageClientSignature) {
server_->RequestClientAuth(true);
auto filter = MakeTlsFilter<TlsLastByteDamager>(
client_, kTlsHandshakeCertificateVerify);
- filter->EnableDecryption();
+ if (version_ >= SSL_LIBRARY_VERSION_TLS_1_3) {
+ filter->EnableDecryption();
+ }
server_->ExpectSendAlert(kTlsAlertDecryptError);
// Do these handshakes by hand to avoid race condition on
// the client processing the server's alert.
diff --git a/gtests/ssl_gtest/ssl_drop_unittest.cc b/gtests/ssl_gtest/ssl_drop_unittest.cc
index f25efc77a..b441b5c10 100644
--- a/gtests/ssl_gtest/ssl_drop_unittest.cc
+++ b/gtests/ssl_gtest/ssl_drop_unittest.cc
@@ -66,6 +66,38 @@ TEST_P(TlsConnectDatagramPre13, DropServerSecondFlightThrice) {
Connect();
}
+static void CheckAcks(const std::shared_ptr<TlsRecordRecorder>& acks,
+ size_t index, std::vector<uint64_t> expected) {
+ ASSERT_LT(index, acks->count());
+ const DataBuffer& buf = acks->record(index).buffer;
+ size_t offset = 2;
+ uint64_t len;
+
+ EXPECT_EQ(2 + expected.size() * 8, buf.len());
+ ASSERT_TRUE(buf.Read(0, 2, &len));
+ ASSERT_EQ(static_cast<size_t>(len + 2), buf.len());
+ if ((2 + expected.size() * 8) != buf.len()) {
+ while (offset < buf.len()) {
+ uint64_t ack;
+ ASSERT_TRUE(buf.Read(offset, 8, &ack));
+ offset += 8;
+ std::cerr << "Ack=0x" << std::hex << ack << std::dec << std::endl;
+ }
+ return;
+ }
+
+ for (size_t i = 0; i < expected.size(); ++i) {
+ uint64_t a = expected[i];
+ uint64_t ack;
+ ASSERT_TRUE(buf.Read(offset, 8, &ack));
+ offset += 8;
+ if (a != ack) {
+ ADD_FAILURE() << "Wrong ack " << i << " expected=0x" << std::hex << a
+ << " got=0x" << ack << std::dec;
+ }
+ }
+}
+
class TlsDropDatagram13 : public TlsConnectDatagram13,
public ::testing::WithParamInterface<bool> {
public:
@@ -139,37 +171,6 @@ class TlsDropDatagram13 : public TlsConnectDatagram13,
std::shared_ptr<PacketFilter> chain_;
};
- void CheckAcks(const DropAckChain& chain, size_t index,
- std::vector<uint64_t> acks) {
- const DataBuffer& buf = chain.ack_->record(index).buffer;
- size_t offset = 2;
- uint64_t len;
-
- EXPECT_EQ(2 + acks.size() * 8, buf.len());
- ASSERT_TRUE(buf.Read(0, 2, &len));
- ASSERT_EQ(static_cast<size_t>(len + 2), buf.len());
- if ((2 + acks.size() * 8) != buf.len()) {
- while (offset < buf.len()) {
- uint64_t ack;
- ASSERT_TRUE(buf.Read(offset, 8, &ack));
- offset += 8;
- std::cerr << "Ack=0x" << std::hex << ack << std::dec << std::endl;
- }
- return;
- }
-
- for (size_t i = 0; i < acks.size(); ++i) {
- uint64_t a = acks[i];
- uint64_t ack;
- ASSERT_TRUE(buf.Read(offset, 8, &ack));
- offset += 8;
- if (a != ack) {
- ADD_FAILURE() << "Wrong ack " << i << " expected=0x" << std::hex << a
- << " got=0x" << ack << std::dec;
- }
- }
- }
-
void CheckedHandshakeSendReceive() {
Handshake();
CheckPostHandshake();
@@ -199,7 +200,7 @@ TEST_P(TlsDropDatagram13, DropClientFirstFlightOnce) {
client_->Handshake();
server_->Handshake();
CheckedHandshakeSendReceive();
- CheckAcks(server_filters_, 0, {0x0002000000000000ULL});
+ CheckAcks(server_filters_.ack_, 0, {0x0002000000000000ULL});
}
TEST_P(TlsDropDatagram13, DropServerFirstFlightOnce) {
@@ -210,7 +211,7 @@ TEST_P(TlsDropDatagram13, DropServerFirstFlightOnce) {
server_->Handshake();
server_filters_.drop_->Disable();
CheckedHandshakeSendReceive();
- CheckAcks(server_filters_, 0, {0x0002000000000000ULL});
+ CheckAcks(server_filters_.ack_, 0, {0x0002000000000000ULL});
}
// Dropping the server's first record also does not produce
@@ -223,7 +224,7 @@ TEST_P(TlsDropDatagram13, DropServerFirstRecordOnce) {
server_->Handshake();
Handshake();
CheckedHandshakeSendReceive();
- CheckAcks(server_filters_, 0, {0x0002000000000000ULL});
+ CheckAcks(server_filters_.ack_, 0, {0x0002000000000000ULL});
}
// Dropping the second packet of the server's flight should
@@ -236,8 +237,8 @@ TEST_P(TlsDropDatagram13, DropServerSecondRecordOnce) {
HandshakeAndAck(client_);
expected_client_acks_ = 1;
CheckedHandshakeSendReceive();
- CheckAcks(client_filters_, 0, {0}); // ServerHello
- CheckAcks(server_filters_, 0, {0x0002000000000000ULL});
+ CheckAcks(client_filters_.ack_, 0, {0}); // ServerHello
+ CheckAcks(server_filters_.ack_, 0, {0x0002000000000000ULL});
}
// Drop the server ACK and verify that the client retransmits
@@ -265,8 +266,8 @@ TEST_P(TlsDropDatagram13, DropServerAckOnce) {
EXPECT_EQ(PR_WOULD_BLOCK_ERROR, PORT_GetError());
CheckPostHandshake();
// There should be two copies of the finished ACK
- CheckAcks(server_filters_, 0, {0x0002000000000000ULL});
- CheckAcks(server_filters_, 1, {0x0002000000000000ULL});
+ CheckAcks(server_filters_.ack_, 0, {0x0002000000000000ULL});
+ CheckAcks(server_filters_.ack_, 1, {0x0002000000000000ULL});
}
// Drop the client certificate verify.
@@ -281,10 +282,10 @@ TEST_P(TlsDropDatagram13, DropClientCertVerify) {
expected_server_acks_ = 2;
CheckedHandshakeSendReceive();
// Ack of the Cert.
- CheckAcks(server_filters_, 0, {0x0002000000000000ULL});
+ CheckAcks(server_filters_.ack_, 0, {0x0002000000000000ULL});
// Ack of the whole client handshake.
CheckAcks(
- server_filters_, 1,
+ server_filters_.ack_, 1,
{0x0002000000000000ULL, // CH (we drop everything after this on client)
0x0002000000000003ULL, // CT (2)
0x0002000000000004ULL}); // FIN (2)
@@ -310,11 +311,11 @@ TEST_P(TlsDropDatagram13, DropFirstHalfOfServerCertificate) {
// as the previous CT1).
EXPECT_EQ(ct1_size, server_filters_.record(0).buffer.len());
CheckedHandshakeSendReceive();
- CheckAcks(client_filters_, 0,
+ CheckAcks(client_filters_.ack_, 0,
{0, // SH
0x0002000000000000ULL, // EE
0x0002000000000002ULL}); // CT2
- CheckAcks(server_filters_, 0, {0x0002000000000000ULL});
+ CheckAcks(server_filters_.ack_, 0, {0x0002000000000000ULL});
}
// Shrink the MTU down so that certs get split and drop the second piece.
@@ -336,13 +337,13 @@ TEST_P(TlsDropDatagram13, DropSecondHalfOfServerCertificate) {
// Check that the first record is CT1
EXPECT_EQ(ct1_size, server_filters_.record(0).buffer.len());
CheckedHandshakeSendReceive();
- CheckAcks(client_filters_, 0,
+ CheckAcks(client_filters_.ack_, 0,
{
0, // SH
0x0002000000000000ULL, // EE
0x0002000000000001ULL, // CT1
});
- CheckAcks(server_filters_, 0, {0x0002000000000000ULL});
+ CheckAcks(server_filters_.ack_, 0, {0x0002000000000000ULL});
}
// In this test, the Certificate message is sent four times, we drop all or part
@@ -392,18 +393,18 @@ class TlsFragmentationAndRecoveryTest : public TlsDropDatagram13 {
0, // SH
0x0002000000000000ULL // EE
};
- CheckAcks(client_filters_, 0, client_acks);
+ CheckAcks(client_filters_.ack_, 0, client_acks);
// And from the second attempt for the half was kept (we delayed this ACK).
client_acks.push_back(0x0002000000000000ULL + second_flight_count +
~dropped_half % 2);
- CheckAcks(client_filters_, 1, client_acks);
+ CheckAcks(client_filters_.ack_, 1, client_acks);
// And the third attempt where the first and last thirds got through.
client_acks.push_back(0x0002000000000000ULL + second_flight_count +
third_flight_count - 1);
client_acks.push_back(0x0002000000000000ULL + second_flight_count +
third_flight_count + 1);
- CheckAcks(client_filters_, 2, client_acks);
- CheckAcks(server_filters_, 0, {0x0002000000000000ULL});
+ CheckAcks(client_filters_.ack_, 2, client_acks);
+ CheckAcks(server_filters_.ack_, 0, {0x0002000000000000ULL});
}
private:
@@ -548,7 +549,7 @@ TEST_P(TlsDropDatagram13, NoDropsDuringZeroRtt) {
CheckConnected();
SendReceive();
EXPECT_EQ(0U, client_filters_.ack_->count());
- CheckAcks(server_filters_, 0,
+ CheckAcks(server_filters_.ack_, 0,
{0x0001000000000001ULL, // EOED
0x0002000000000000ULL}); // Finished
}
@@ -567,8 +568,8 @@ TEST_P(TlsDropDatagram13, DropEEDuringZeroRtt) {
ExpectEarlyDataAccepted(true);
CheckConnected();
SendReceive();
- CheckAcks(client_filters_, 0, {0});
- CheckAcks(server_filters_, 0,
+ CheckAcks(client_filters_.ack_, 0, {0});
+ CheckAcks(server_filters_.ack_, 0,
{0x0001000000000002ULL, // EOED
0x0002000000000000ULL}); // Finished
}
@@ -608,22 +609,22 @@ TEST_P(TlsDropDatagram13, ReorderServerEE) {
expected_client_acks_ = 1;
HandshakeAndAck(client_);
CheckedHandshakeSendReceive();
- CheckAcks(client_filters_, 0,
+ CheckAcks(client_filters_.ack_, 0,
{
0, // SH
0x0002000000000000, // EE
});
- CheckAcks(server_filters_, 0, {0x0002000000000000ULL});
+ CheckAcks(server_filters_.ack_, 0, {0x0002000000000000ULL});
}
// The client sends an out of order non-handshake message
// but with the handshake key.
class TlsSendCipherSpecCapturer {
public:
- TlsSendCipherSpecCapturer(std::shared_ptr<TlsAgent>& agent)
- : send_cipher_specs_() {
- SSLInt_SetCipherSpecChangeFunc(agent->ssl_fd(), CipherSpecChanged,
- (void*)this);
+ TlsSendCipherSpecCapturer(const std::shared_ptr<TlsAgent>& agent)
+ : agent_(agent), send_cipher_specs_() {
+ EXPECT_EQ(SECSuccess,
+ SSL_SecretCallback(agent_->ssl_fd(), SecretCallback, this));
}
std::shared_ptr<TlsCipherSpec> spec(size_t i) {
@@ -634,28 +635,42 @@ class TlsSendCipherSpecCapturer {
}
private:
- static void CipherSpecChanged(void* arg, PRBool sending,
- ssl3CipherSpec* newSpec) {
- if (!sending) {
+ static void SecretCallback(PRFileDesc* fd, PRUint16 epoch,
+ SSLSecretDirection dir, PK11SymKey* secret,
+ void* arg) {
+ auto self = static_cast<TlsSendCipherSpecCapturer*>(arg);
+ std::cerr << self->agent_->role_str() << ": capture " << dir
+ << " secret for epoch " << epoch << std::endl;
+
+ if (dir == ssl_secret_read) {
return;
}
- auto self = static_cast<TlsSendCipherSpecCapturer*>(arg);
-
- auto spec = std::make_shared<TlsCipherSpec>();
- bool ret = spec->Init(SSLInt_CipherSpecToEpoch(newSpec),
- SSLInt_CipherSpecToAlgorithm(newSpec),
- SSLInt_CipherSpecToKey(newSpec),
- SSLInt_CipherSpecToIv(newSpec));
- EXPECT_EQ(true, ret);
+ SSLPreliminaryChannelInfo preinfo;
+ EXPECT_EQ(SECSuccess,
+ SSL_GetPreliminaryChannelInfo(self->agent_->ssl_fd(), &preinfo,
+ sizeof(preinfo)));
+ EXPECT_EQ(sizeof(preinfo), preinfo.length);
+ EXPECT_TRUE(preinfo.valuesSet & ssl_preinfo_cipher_suite);
+
+ SSLCipherSuiteInfo cipherinfo;
+ EXPECT_EQ(SECSuccess,
+ SSL_GetCipherSuiteInfo(preinfo.cipherSuite, &cipherinfo,
+ sizeof(cipherinfo)));
+ EXPECT_EQ(sizeof(cipherinfo), cipherinfo.length);
+
+ auto spec = std::make_shared<TlsCipherSpec>(true, epoch);
+ EXPECT_TRUE(spec->SetKeys(&cipherinfo, secret));
self->send_cipher_specs_.push_back(spec);
}
+ std::shared_ptr<TlsAgent> agent_;
std::vector<std::shared_ptr<TlsCipherSpec>> send_cipher_specs_;
};
-TEST_P(TlsDropDatagram13, SendOutOfOrderAppWithHandshakeKey) {
+TEST_F(TlsConnectDatagram13, SendOutOfOrderAppWithHandshakeKey) {
StartConnect();
+ // Capturing secrets means that we can't use decrypting filters on the client.
TlsSendCipherSpecCapturer capturer(client_);
client_->Handshake();
server_->Handshake();
@@ -680,9 +695,12 @@ TEST_P(TlsDropDatagram13, SendOutOfOrderAppWithHandshakeKey) {
EXPECT_EQ(SSL_ERROR_RX_UNKNOWN_RECORD_TYPE, PORT_GetError());
}
-TEST_P(TlsDropDatagram13, SendOutOfOrderHsNonsenseWithHandshakeKey) {
+TEST_F(TlsConnectDatagram13, SendOutOfOrderHsNonsenseWithHandshakeKey) {
StartConnect();
TlsSendCipherSpecCapturer capturer(client_);
+ auto acks = MakeTlsFilter<TlsRecordRecorder>(server_, ssl_ct_ack);
+ acks->EnableDecryption();
+
client_->Handshake();
server_->Handshake();
client_->Handshake();
@@ -699,10 +717,10 @@ TEST_P(TlsDropDatagram13, SendOutOfOrderHsNonsenseWithHandshakeKey) {
ssl_ct_handshake,
DataBuffer(buf, sizeof(buf))));
server_->Handshake();
- EXPECT_EQ(2UL, server_filters_.ack_->count());
+ EXPECT_EQ(2UL, acks->count());
// The server acknowledges client Finished twice.
- CheckAcks(server_filters_, 0, {0x0002000000000000ULL});
- CheckAcks(server_filters_, 1, {0x0002000000000000ULL});
+ CheckAcks(acks, 0, {0x0002000000000000ULL});
+ CheckAcks(acks, 1, {0x0002000000000000ULL});
}
// Shrink the MTU down so that certs get split and then swap the first and
@@ -726,7 +744,7 @@ TEST_P(TlsReorderDatagram13, ReorderServerCertificate) {
ShiftDtlsTimers();
CheckedHandshakeSendReceive();
EXPECT_EQ(2UL, server_filters_.records_->count()); // ACK + Data
- CheckAcks(server_filters_, 0, {0x0002000000000000ULL});
+ CheckAcks(server_filters_.ack_, 0, {0x0002000000000000ULL});
}
TEST_P(TlsReorderDatagram13, DataAfterEOEDDuringZeroRtt) {
@@ -761,7 +779,8 @@ TEST_P(TlsReorderDatagram13, DataAfterEOEDDuringZeroRtt) {
CheckConnected();
EXPECT_EQ(0U, client_filters_.ack_->count());
// Acknowledgements for EOED and Finished.
- CheckAcks(server_filters_, 0, {0x0001000000000002ULL, 0x0002000000000000ULL});
+ CheckAcks(server_filters_.ack_, 0,
+ {0x0001000000000002ULL, 0x0002000000000000ULL});
uint8_t buf[8];
rv = PR_Read(server_->ssl_fd(), buf, sizeof(buf));
EXPECT_EQ(-1, rv);
@@ -800,7 +819,8 @@ TEST_P(TlsReorderDatagram13, DataAfterFinDuringZeroRtt) {
CheckConnected();
EXPECT_EQ(0U, client_filters_.ack_->count());
// Acknowledgements for EOED and Finished.
- CheckAcks(server_filters_, 0, {0x0001000000000002ULL, 0x0002000000000000ULL});
+ CheckAcks(server_filters_.ack_, 0,
+ {0x0001000000000002ULL, 0x0002000000000000ULL});
uint8_t buf[8];
rv = PR_Read(server_->ssl_fd(), buf, sizeof(buf));
EXPECT_EQ(-1, rv);
diff --git a/gtests/ssl_gtest/ssl_recordsep_unittest.cc b/gtests/ssl_gtest/ssl_recordsep_unittest.cc
index cb3cb02a4..c54593ec8 100644
--- a/gtests/ssl_gtest/ssl_recordsep_unittest.cc
+++ b/gtests/ssl_gtest/ssl_recordsep_unittest.cc
@@ -50,8 +50,8 @@ class HandshakeSecretTracker {
void SecretUpdated(PRUint16 epoch, SSLSecretDirection dir,
PK11SymKey* secret) {
if (g_ssl_gtest_verbose) {
- std::cerr << agent_->role_str() << ": secret callback for "
- << dir << " epoch " << epoch << std::endl;
+ std::cerr << agent_->role_str() << ": secret callback for " << dir
+ << " epoch " << epoch << std::endl;
}
EXPECT_TRUE(secret);
@@ -493,30 +493,25 @@ TEST_P(TlsConnectStream, ForwardDataFromWrongEpoch) {
TEST_F(TlsConnectStreamTls13, ForwardInvalidData) {
const uint8_t data[1] = {0};
- // NULL fd.
- EXPECT_EQ(SECFailure,
- SSL_RecordLayerData(nullptr, 0,
- ssl_ct_application_data, nullptr, 1));
- EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError());
-
+ EnsureTlsSetup();
// Zero-length data.
- EXPECT_EQ(SECFailure,
- SSL_RecordLayerData(client_->ssl_fd(), 0,
- ssl_ct_application_data, data, 0));
+ EXPECT_EQ(SECFailure, SSL_RecordLayerData(client_->ssl_fd(), 0,
+ ssl_ct_application_data, data, 0));
EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError());
// NULL data.
EXPECT_EQ(SECFailure,
- SSL_RecordLayerData(client_->ssl_fd(), 0,
- ssl_ct_application_data, nullptr, 1));
+ SSL_RecordLayerData(client_->ssl_fd(), 0, ssl_ct_application_data,
+ nullptr, 1));
EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError());
}
TEST_F(TlsConnectDatagram13, ForwardDataDtls) {
+ EnsureTlsSetup();
const uint8_t data[1] = {0};
EXPECT_EQ(SECFailure,
- SSL_RecordLayerData(client_->ssl_fd(), 0,
- ssl_ct_application_data, data, sizeof(data)));
+ SSL_RecordLayerData(client_->ssl_fd(), 0, ssl_ct_application_data,
+ data, sizeof(data)));
EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError());
}
diff --git a/gtests/ssl_gtest/ssl_recordsize_unittest.cc b/gtests/ssl_gtest/ssl_recordsize_unittest.cc
index c9149bcd9..566899420 100644
--- a/gtests/ssl_gtest/ssl_recordsize_unittest.cc
+++ b/gtests/ssl_gtest/ssl_recordsize_unittest.cc
@@ -123,9 +123,11 @@ TEST_P(TlsConnectGeneric, RecordSizeMaximum) {
EnsureTlsSetup();
auto client_max = MakeTlsFilter<TlsRecordMaximum>(client_);
- client_max->EnableDecryption();
auto server_max = MakeTlsFilter<TlsRecordMaximum>(server_);
- server_max->EnableDecryption();
+ if (version_ >= SSL_LIBRARY_VERSION_TLS_1_3) {
+ client_max->EnableDecryption();
+ server_max->EnableDecryption();
+ }
Connect();
client_->SendData(send_size, send_size);
@@ -140,7 +142,9 @@ TEST_P(TlsConnectGeneric, RecordSizeMaximum) {
TEST_P(TlsConnectGeneric, RecordSizeMinimumClient) {
EnsureTlsSetup();
auto server_max = MakeTlsFilter<TlsRecordMaximum>(server_);
- server_max->EnableDecryption();
+ if (version_ >= SSL_LIBRARY_VERSION_TLS_1_3) {
+ server_max->EnableDecryption();
+ }
client_->SetOption(SSL_RECORD_SIZE_LIMIT, 64);
Connect();
@@ -152,7 +156,9 @@ TEST_P(TlsConnectGeneric, RecordSizeMinimumClient) {
TEST_P(TlsConnectGeneric, RecordSizeMinimumServer) {
EnsureTlsSetup();
auto client_max = MakeTlsFilter<TlsRecordMaximum>(client_);
- client_max->EnableDecryption();
+ if (version_ >= SSL_LIBRARY_VERSION_TLS_1_3) {
+ client_max->EnableDecryption();
+ }
server_->SetOption(SSL_RECORD_SIZE_LIMIT, 64);
Connect();
@@ -164,9 +170,11 @@ TEST_P(TlsConnectGeneric, RecordSizeMinimumServer) {
TEST_P(TlsConnectGeneric, RecordSizeAsymmetric) {
EnsureTlsSetup();
auto client_max = MakeTlsFilter<TlsRecordMaximum>(client_);
- client_max->EnableDecryption();
auto server_max = MakeTlsFilter<TlsRecordMaximum>(server_);
- server_max->EnableDecryption();
+ 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);
@@ -256,9 +264,11 @@ class TlsRecordPadder : public TlsRecordFilter {
return KEEP;
}
+ uint16_t protection_epoch;
uint8_t inner_content_type;
DataBuffer plaintext;
- if (!Unprotect(header, record, &inner_content_type, &plaintext)) {
+ if (!Unprotect(header, record, &protection_epoch, &inner_content_type,
+ &plaintext)) {
return KEEP;
}
@@ -267,8 +277,8 @@ class TlsRecordPadder : public TlsRecordFilter {
}
DataBuffer ciphertext;
- bool ok =
- Protect(header, inner_content_type, plaintext, &ciphertext, padding_);
+ bool ok = Protect(spec(protection_epoch), header, inner_content_type,
+ plaintext, &ciphertext, padding_);
EXPECT_TRUE(ok);
if (!ok) {
return KEEP;
@@ -334,7 +344,9 @@ TEST_P(TlsConnectGeneric, RecordSizeCapExtensionClient) {
client_->SetOption(SSL_RECORD_SIZE_LIMIT, 16385);
auto capture =
MakeTlsFilter<TlsExtensionCapture>(client_, ssl_record_size_limit_xtn);
- capture->EnableDecryption();
+ if (version_ >= SSL_LIBRARY_VERSION_TLS_1_3) {
+ capture->EnableDecryption();
+ }
Connect();
uint64_t val = 0;
@@ -352,7 +364,9 @@ TEST_P(TlsConnectGeneric, RecordSizeCapExtensionServer) {
server_->SetOption(SSL_RECORD_SIZE_LIMIT, 16385);
auto capture =
MakeTlsFilter<TlsExtensionCapture>(server_, ssl_record_size_limit_xtn);
- capture->EnableDecryption();
+ if (version_ >= SSL_LIBRARY_VERSION_TLS_1_3) {
+ capture->EnableDecryption();
+ }
Connect();
uint64_t val = 0;
@@ -393,7 +407,9 @@ TEST_P(TlsConnectGeneric, RecordSizeServerExtensionInvalid) {
static const uint8_t v[] = {0xf4, 0x1f};
auto replace = MakeTlsFilter<TlsExtensionReplacer>(
server_, ssl_record_size_limit_xtn, DataBuffer(v, sizeof(v)));
- replace->EnableDecryption();
+ if (version_ >= SSL_LIBRARY_VERSION_TLS_1_3) {
+ replace->EnableDecryption();
+ }
ConnectExpectAlert(client_, kTlsAlertIllegalParameter);
}
@@ -403,7 +419,9 @@ TEST_P(TlsConnectGeneric, RecordSizeServerExtensionExtra) {
static const uint8_t v[] = {0x01, 0x00, 0x00};
auto replace = MakeTlsFilter<TlsExtensionReplacer>(
server_, ssl_record_size_limit_xtn, DataBuffer(v, sizeof(v)));
- replace->EnableDecryption();
+ if (version_ >= SSL_LIBRARY_VERSION_TLS_1_3) {
+ replace->EnableDecryption();
+ }
ConnectExpectAlert(client_, kTlsAlertDecodeError);
}
diff --git a/gtests/ssl_gtest/test_io.cc b/gtests/ssl_gtest/test_io.cc
index 1ac752bfd..4a7f91459 100644
--- a/gtests/ssl_gtest/test_io.cc
+++ b/gtests/ssl_gtest/test_io.cc
@@ -139,19 +139,18 @@ int32_t DummyPrSocket::Write(PRFileDesc *f, const void *buf, int32_t length) {
DataBuffer filtered;
PacketFilter::Action action = PacketFilter::KEEP;
if (filter_) {
+ LOGV("Original packet: " << packet);
action = filter_->Process(packet, &filtered);
}
switch (action) {
case PacketFilter::CHANGE:
- LOG("Original packet: " << packet);
LOG("Filtered packet: " << filtered);
dst->PacketReceived(filtered);
break;
case PacketFilter::DROP:
- LOG("Droppped packet: " << packet);
+ LOG("Drop packet");
break;
case PacketFilter::KEEP:
- LOGV("Packet: " << packet);
dst->PacketReceived(packet);
break;
}
diff --git a/gtests/ssl_gtest/tls_agent.cc b/gtests/ssl_gtest/tls_agent.cc
index 8b6b36fa7..e726c6a61 100644
--- a/gtests/ssl_gtest/tls_agent.cc
+++ b/gtests/ssl_gtest/tls_agent.cc
@@ -644,7 +644,8 @@ void TlsAgent::CheckEpochs(uint16_t expected_read,
uint16_t expected_write) const {
uint16_t read_epoch = 0;
uint16_t write_epoch = 0;
- EXPECT_EQ(SECSuccess, SSLInt_GetEpochs(ssl_fd(), &read_epoch, &write_epoch));
+ EXPECT_EQ(SECSuccess,
+ SSL_GetCurrentEpoch(ssl_fd(), &read_epoch, &write_epoch));
EXPECT_EQ(expected_read, read_epoch) << role_str() << " read epoch";
EXPECT_EQ(expected_write, write_epoch) << role_str() << " write epoch";
}
diff --git a/gtests/ssl_gtest/tls_filter.cc b/gtests/ssl_gtest/tls_filter.cc
index 25ad606fc..b2917274b 100644
--- a/gtests/ssl_gtest/tls_filter.cc
+++ b/gtests/ssl_gtest/tls_filter.cc
@@ -45,40 +45,65 @@ void TlsVersioned::WriteStream(std::ostream& stream) const {
}
}
+TlsRecordFilter::TlsRecordFilter(const std::shared_ptr<TlsAgent>& a)
+ : agent_(a) {
+ cipher_specs_.emplace_back(a->variant() == ssl_variant_datagram, 0);
+}
+
void TlsRecordFilter::EnableDecryption() {
- SSLInt_SetCipherSpecChangeFunc(agent()->ssl_fd(), CipherSpecChanged,
- (void*)this);
+ EXPECT_EQ(SECSuccess,
+ SSL_SecretCallback(agent()->ssl_fd(), SecretCallback, this));
+ decrypting_ = true;
}
-void TlsRecordFilter::CipherSpecChanged(void* arg, PRBool sending,
- ssl3CipherSpec* newSpec) {
+void TlsRecordFilter::SecretCallback(PRFileDesc* fd, PRUint16 epoch,
+ SSLSecretDirection dir, PK11SymKey* secret,
+ void* arg) {
TlsRecordFilter* self = static_cast<TlsRecordFilter*>(arg);
- PRBool isServer = self->agent()->role() == TlsAgent::SERVER;
-
if (g_ssl_gtest_verbose) {
- std::cerr << (isServer ? "server" : "client") << ": "
- << (sending ? "send" : "receive")
- << " cipher spec changed: " << newSpec->epoch << " ("
- << newSpec->phase << ")" << std::endl;
+ std::cerr << self->agent()->role_str() << ": " << dir
+ << " secret changed for epoch " << epoch << std::endl;
}
- if (!sending) {
+
+ if (dir == ssl_secret_read) {
return;
}
- uint64_t seq_no;
- if (self->agent()->variant() == ssl_variant_datagram) {
- seq_no = static_cast<uint64_t>(SSLInt_CipherSpecToEpoch(newSpec)) << 48;
+ for (auto& spec : self->cipher_specs_) {
+ ASSERT_NE(spec.epoch(), epoch) << "duplicate spec for epoch " << epoch;
+ }
+
+ SSLPreliminaryChannelInfo preinfo;
+ EXPECT_EQ(SECSuccess,
+ SSL_GetPreliminaryChannelInfo(self->agent()->ssl_fd(), &preinfo,
+ sizeof(preinfo)));
+ EXPECT_EQ(sizeof(preinfo), preinfo.length);
+
+ // Check the version.
+ if (preinfo.valuesSet & ssl_preinfo_version) {
+ EXPECT_EQ(SSL_LIBRARY_VERSION_TLS_1_3, preinfo.protocolVersion);
+ } else {
+ EXPECT_EQ(1U, epoch);
+ }
+
+ uint16_t suite;
+ if (epoch == 1) {
+ // 0-RTT
+ EXPECT_TRUE(preinfo.valuesSet & ssl_preinfo_0rtt_cipher_suite);
+ suite = preinfo.zeroRttCipherSuite;
} else {
- seq_no = 0;
+ EXPECT_TRUE(preinfo.valuesSet & ssl_preinfo_cipher_suite);
+ suite = preinfo.cipherSuite;
}
- self->in_sequence_number_ = seq_no;
- self->out_sequence_number_ = seq_no;
- self->dropped_record_ = false;
- self->cipher_spec_.reset(new TlsCipherSpec());
- bool ret = self->cipher_spec_->Init(
- SSLInt_CipherSpecToEpoch(newSpec), SSLInt_CipherSpecToAlgorithm(newSpec),
- SSLInt_CipherSpecToKey(newSpec), SSLInt_CipherSpecToIv(newSpec));
- EXPECT_EQ(true, ret);
+
+ SSLCipherSuiteInfo cipherinfo;
+ EXPECT_EQ(SECSuccess,
+ SSL_GetCipherSuiteInfo(suite, &cipherinfo, sizeof(cipherinfo)));
+ EXPECT_EQ(sizeof(cipherinfo), cipherinfo.length);
+
+ bool is_dtls = self->agent()->variant() == ssl_variant_datagram;
+ self->cipher_specs_.emplace_back(is_dtls, epoch);
+ EXPECT_TRUE(self->cipher_specs_.back().SetKeys(&cipherinfo, secret));
}
bool TlsRecordFilter::is_dtls13() const {
@@ -95,6 +120,23 @@ bool TlsRecordFilter::is_dtls13() const {
info.canSendEarlyData;
}
+// Gets the cipher spec that matches the specified epoch.
+TlsCipherSpec& TlsRecordFilter::spec(uint16_t write_epoch) {
+ for (auto& sp : cipher_specs_) {
+ if (sp.epoch() == write_epoch) {
+ return sp;
+ }
+ }
+
+ // If we aren't decrypting, provide a cipher spec that does nothing other than
+ // count sequence numbers.
+ EXPECT_FALSE(decrypting_) << "No spec available for epoch " << write_epoch;
+ ;
+ bool is_dtls = agent()->variant() == ssl_variant_datagram;
+ cipher_specs_.emplace_back(is_dtls, write_epoch);
+ return cipher_specs_.back();
+}
+
PacketFilter::Action TlsRecordFilter::Filter(const DataBuffer& input,
DataBuffer* output) {
// Disable during shutdown.
@@ -108,34 +150,28 @@ PacketFilter::Action TlsRecordFilter::Filter(const DataBuffer& input,
output->Allocate(input.len());
TlsParser parser(input);
+ // This uses the current write spec for the purposes of parsing the epoch and
+ // sequence number from the header. This might be wrong because we can
+ // receive records from older specs, but guessing is good enough:
+ // - In DTLS, parsing the sequence number corrects any errors.
+ // - In TLS, we don't use the sequence number unless decrypting, where we use
+ // trial decryption to get the right epoch.
+ uint16_t write_epoch = 0;
+ SECStatus rv = SSL_GetCurrentEpoch(agent()->ssl_fd(), nullptr, &write_epoch);
+ if (rv != SECSuccess) {
+ ADD_FAILURE() << "unable to read epoch";
+ return KEEP;
+ }
+ uint64_t guess_seqno = static_cast<uint64_t>(write_epoch) << 48;
+
while (parser.remaining()) {
TlsRecordHeader header;
DataBuffer record;
-
- if (!header.Parse(is_dtls13(), in_sequence_number_, &parser, &record)) {
+ if (!header.Parse(is_dtls13(), guess_seqno, &parser, &record)) {
ADD_FAILURE() << "not a valid record";
return KEEP;
}
- // Track the sequence number, which is necessary for stream mode when
- // decrypting and for TLS 1.3 datagram to recover the sequence number.
- //
- // We reset the counter when the cipher spec changes, but that notification
- // appears before a record is sent. If multiple records are sent with
- // different cipher specs, this would fail. This filters out cleartext
- // records, so we don't get confused by handshake messages that are sent at
- // the same time as encrypted records. Sequence numbers are therefore
- // likely to be incorrect for cleartext records.
- //
- // This isn't perfectly robust: if there is a change from an active cipher
- // spec to another active cipher spec (KeyUpdate for instance) AND writes
- // are consolidated across that change, this code could use the wrong
- // sequence numbers when re-encrypting records with the old keys.
- if (header.content_type() == ssl_ct_application_data) {
- in_sequence_number_ =
- (std::max)(in_sequence_number_, header.sequence_number() + 1);
- }
-
if (FilterRecord(header, record, &offset, output) != KEEP) {
changed = true;
} else {
@@ -159,14 +195,16 @@ PacketFilter::Action TlsRecordFilter::FilterRecord(
DataBuffer filtered;
uint8_t inner_content_type;
DataBuffer plaintext;
+ uint16_t protection_epoch = 0;
- if (!Unprotect(header, record, &inner_content_type, &plaintext)) {
- if (g_ssl_gtest_verbose) {
- std::cerr << "unprotect failed: " << header << ":" << record << std::endl;
- }
+ if (!Unprotect(header, record, &protection_epoch, &inner_content_type,
+ &plaintext)) {
+ std::cerr << agent()->role_str() << ": unprotect failed: " << header << ":"
+ << record << std::endl;
return KEEP;
}
+ auto& protection_spec = spec(protection_epoch);
TlsRecordHeader real_header(header.variant(), header.version(),
inner_content_type, header.sequence_number());
@@ -174,7 +212,9 @@ PacketFilter::Action TlsRecordFilter::FilterRecord(
// In stream mode, even if something doesn't change we need to re-encrypt if
// previous packets were dropped.
if (action == KEEP) {
- if (header.is_dtls() || !dropped_record_) {
+ if (header.is_dtls() || !protection_spec.record_dropped()) {
+ // Count every outgoing packet.
+ protection_spec.RecordProtected();
return KEEP;
}
filtered = plaintext;
@@ -182,7 +222,7 @@ PacketFilter::Action TlsRecordFilter::FilterRecord(
if (action == DROP) {
std::cerr << "record drop: " << header << ":" << record << std::endl;
- dropped_record_ = true;
+ protection_spec.RecordDropped();
return DROP;
}
@@ -192,19 +232,18 @@ PacketFilter::Action TlsRecordFilter::FilterRecord(
std::cerr << "record new: " << filtered << std::endl;
}
- uint64_t seq_num;
- if (header.is_dtls() || !cipher_spec_ ||
- header.content_type() != ssl_ct_application_data) {
- seq_num = header.sequence_number();
- } else {
- seq_num = out_sequence_number_++;
+ uint64_t seq_num = protection_spec.next_out_seqno();
+ if (!decrypting_ && header.is_dtls()) {
+ // Copy over the epoch, which isn't tracked when not decrypting.
+ seq_num |= header.sequence_number() & (0xffffULL << 48);
}
+
TlsRecordHeader out_header(header.variant(), header.version(),
header.content_type(), seq_num);
DataBuffer ciphertext;
- bool rv = Protect(out_header, inner_content_type, filtered, &ciphertext);
- EXPECT_TRUE(rv);
+ bool rv = Protect(protection_spec, out_header, inner_content_type, filtered,
+ &ciphertext);
if (!rv) {
return KEEP;
}
@@ -227,15 +266,20 @@ uint64_t TlsRecordHeader::RecoverSequenceNumber(uint64_t expected,
uint32_t partial,
size_t partial_bits) {
EXPECT_GE(32U, partial_bits);
- uint64_t mask = (1 << partial_bits) - 1;
+ uint64_t mask = (1ULL << partial_bits) - 1;
// First we determine the highest possible value. This is half the
- // expressible range above the expected value.
- uint64_t cap = expected + (1ULL << (partial_bits - 1));
+ // expressible range above the expected value, less 1.
+ //
+ // We subtract the extra 1 from the cap so that when given a choice between
+ // the equidistant expected+N and expected-N we want to chose the lower. With
+ // 0-RTT, we sometimes have to recover an epoch of 1 when we expect an epoch
+ // of 3 and with 2 partial bits, the alternative result of 5 is wrong.
+ uint64_t cap = expected + (1ULL << (partial_bits - 1)) - 1;
// Add the partial piece in. e.g., xxxx789a and 1234 becomes xxxx1234.
uint64_t seq_no = (cap & ~mask) | partial;
// If the partial value is higher than the same partial piece from the cap,
// then the real value has to be lower. e.g., xxxx1234 can't become xxxx5678.
- if (partial > (cap & mask)) {
+ if (partial > (cap & mask) && (seq_no >= (1ULL << partial_bits))) {
seq_no -= 1ULL << partial_bits;
}
return seq_no;
@@ -375,16 +419,41 @@ size_t TlsRecordHeader::Write(DataBuffer* buffer, size_t offset,
bool TlsRecordFilter::Unprotect(const TlsRecordHeader& header,
const DataBuffer& ciphertext,
+ uint16_t* protection_epoch,
uint8_t* inner_content_type,
DataBuffer* plaintext) {
- if (!cipher_spec_ || header.content_type() != ssl_ct_application_data) {
+ if (!decrypting_ || header.content_type() != ssl_ct_application_data) {
+ // Maintain the epoch and sequence number for plaintext records.
+ uint16_t ep = 0;
+ if (agent()->variant() == ssl_variant_datagram) {
+ ep = static_cast<uint16_t>(header.sequence_number() >> 48);
+ }
+ spec(ep).RecordUnprotected(header.sequence_number());
+ *protection_epoch = ep;
*inner_content_type = header.content_type();
*plaintext = ciphertext;
return true;
}
- if (!cipher_spec_->Unprotect(header, ciphertext, plaintext)) {
- return false;
+ uint16_t ep = 0;
+ if (agent()->variant() == ssl_variant_datagram) {
+ ep = static_cast<uint16_t>(header.sequence_number() >> 48);
+ if (!spec(ep).Unprotect(header, ciphertext, plaintext)) {
+ return false;
+ }
+ } else {
+ // In TLS, records aren't clearly labelled with their epoch, and we
+ // can't just use the newest keys because the same flight of messages can
+ // contain multiple epochs. So... trial decrypt!
+ for (size_t i = cipher_specs_.size() - 1; i > 0; --i) {
+ if (cipher_specs_[i].Unprotect(header, ciphertext, plaintext)) {
+ ep = cipher_specs_[i].epoch();
+ break;
+ }
+ }
+ if (!ep) {
+ return false;
+ }
}
size_t len = plaintext->len();
@@ -396,33 +465,45 @@ bool TlsRecordFilter::Unprotect(const TlsRecordHeader& header,
return false;
}
+ *protection_epoch = ep;
*inner_content_type = plaintext->data()[len - 1];
plaintext->Truncate(len - 1);
if (g_ssl_gtest_verbose) {
- std::cerr << "unprotect: " << std::hex << header.sequence_number()
- << std::dec << " type=" << static_cast<int>(*inner_content_type)
+ std::cerr << agent()->role_str() << ": unprotect: epoch=" << ep
+ << " seq=" << std::hex << header.sequence_number() << std::dec
<< " " << *plaintext << std::endl;
}
return true;
}
-bool TlsRecordFilter::Protect(const TlsRecordHeader& header,
+bool TlsRecordFilter::Protect(TlsCipherSpec& protection_spec,
+ const TlsRecordHeader& header,
uint8_t inner_content_type,
const DataBuffer& plaintext,
DataBuffer* ciphertext, size_t padding) {
- if (!cipher_spec_ || header.content_type() != ssl_ct_application_data) {
+ if (!protection_spec.is_protected()) {
+ // Not protected, just keep the sequence numbers updated.
+ protection_spec.RecordProtected();
*ciphertext = plaintext;
return true;
}
- if (g_ssl_gtest_verbose) {
- std::cerr << "protect: " << header.sequence_number() << std::endl;
- }
+
DataBuffer padded;
padded.Allocate(plaintext.len() + 1 + padding);
size_t offset = padded.Write(0, plaintext.data(), plaintext.len());
padded.Write(offset, inner_content_type, 1);
- return cipher_spec_->Protect(header, padded, ciphertext);
+
+ bool ok = protection_spec.Protect(header, padded, ciphertext);
+ if (!ok) {
+ ADD_FAILURE() << "protect fail";
+ } else if (g_ssl_gtest_verbose) {
+ std::cerr << agent()->role_str()
+ << ": protect: epoch=" << protection_spec.epoch()
+ << " seq=" << std::hex << header.sequence_number() << std::dec
+ << " " << *ciphertext << std::endl;
+ }
+ return ok;
}
bool IsHelloRetry(const DataBuffer& body) {
diff --git a/gtests/ssl_gtest/tls_filter.h b/gtests/ssl_gtest/tls_filter.h
index 2b6e88645..9c75ae960 100644
--- a/gtests/ssl_gtest/tls_filter.h
+++ b/gtests/ssl_gtest/tls_filter.h
@@ -97,13 +97,7 @@ inline std::shared_ptr<T> MakeTlsFilter(const std::shared_ptr<TlsAgent>& agent,
// Abstract filter that operates on entire (D)TLS records.
class TlsRecordFilter : public PacketFilter {
public:
- TlsRecordFilter(const std::shared_ptr<TlsAgent>& a)
- : agent_(a),
- count_(0),
- cipher_spec_(),
- dropped_record_(false),
- in_sequence_number_(0),
- out_sequence_number_(0) {}
+ TlsRecordFilter(const std::shared_ptr<TlsAgent>& a);
std::shared_ptr<TlsAgent> agent() const { return agent_.lock(); }
@@ -118,10 +112,11 @@ class TlsRecordFilter : public PacketFilter {
// behavior.
void EnableDecryption();
bool Unprotect(const TlsRecordHeader& header, const DataBuffer& cipherText,
- uint8_t* inner_content_type, DataBuffer* plaintext);
- bool Protect(const TlsRecordHeader& header, uint8_t inner_content_type,
- const DataBuffer& plaintext, DataBuffer* ciphertext,
- size_t padding = 0);
+ uint16_t* protection_epoch, uint8_t* inner_content_type,
+ DataBuffer* plaintext);
+ bool Protect(TlsCipherSpec& protection_spec, const TlsRecordHeader& header,
+ uint8_t inner_content_type, const DataBuffer& plaintext,
+ DataBuffer* ciphertext, size_t padding = 0);
protected:
// There are two filter functions which can be overriden. Both are
@@ -146,20 +141,17 @@ class TlsRecordFilter : public PacketFilter {
}
bool is_dtls13() const;
+ TlsCipherSpec& spec(uint16_t epoch);
private:
- static void CipherSpecChanged(void* arg, PRBool sending,
- ssl3CipherSpec* newSpec);
+ static void SecretCallback(PRFileDesc* fd, PRUint16 epoch,
+ SSLSecretDirection dir, PK11SymKey* secret,
+ void* arg);
std::weak_ptr<TlsAgent> agent_;
- size_t count_;
- std::unique_ptr<TlsCipherSpec> cipher_spec_;
- // Whether we dropped a record since the cipher spec changed.
- bool dropped_record_;
- // The sequence number we use for reading records as they are written.
- uint64_t in_sequence_number_;
- // The sequence number we use for writing modified records.
- uint64_t out_sequence_number_;
+ size_t count_ = 0;
+ std::vector<TlsCipherSpec> cipher_specs_;
+ bool decrypting_ = false;
};
inline std::ostream& operator<<(std::ostream& stream, const TlsVersioned& v) {
diff --git a/gtests/ssl_gtest/tls_protect.cc b/gtests/ssl_gtest/tls_protect.cc
index c715a36a6..c92b5ccf7 100644
--- a/gtests/ssl_gtest/tls_protect.cc
+++ b/gtests/ssl_gtest/tls_protect.cc
@@ -7,6 +7,9 @@
#include "tls_protect.h"
#include "tls_filter.h"
+// Do this to avoid having to re-implement HKDF.
+#include "tls13hkdf.h"
+
namespace nss_test {
AeadCipher::~AeadCipher() {
@@ -15,32 +18,37 @@ AeadCipher::~AeadCipher() {
}
}
-bool AeadCipher::Init(PK11SymKey *key, const uint8_t *iv) {
- key_ = PK11_ReferenceSymKey(key);
+bool AeadCipher::Init(PK11SymKey* key, const uint8_t* iv) {
+ key_ = key;
if (!key_) return false;
memcpy(iv_, iv, sizeof(iv_));
+ if (g_ssl_gtest_verbose) {
+ EXPECT_EQ(SECSuccess, PK11_ExtractKeyValue(key_));
+ SECItem* raw_key = PK11_GetKeyData(key_);
+ std::cerr << "key: " << DataBuffer(raw_key->data, raw_key->len)
+ << std::endl;
+ std::cerr << "iv: " << DataBuffer(iv_, 12) << std::endl;
+ }
return true;
}
-void AeadCipher::FormatNonce(uint64_t seq, uint8_t *nonce) {
+void AeadCipher::FormatNonce(uint64_t seq, uint8_t* nonce) {
memcpy(nonce, iv_, 12);
for (size_t i = 0; i < 8; ++i) {
nonce[12 - (i + 1)] ^= seq & 0xff;
seq >>= 8;
}
-
- DataBuffer d(nonce, 12);
}
-bool AeadCipher::AeadInner(bool decrypt, void *params, size_t param_length,
- const uint8_t *in, size_t inlen, uint8_t *out,
- size_t *outlen, size_t maxlen) {
+bool AeadCipher::AeadInner(bool decrypt, void* params, size_t param_length,
+ const uint8_t* in, size_t inlen, uint8_t* out,
+ size_t* outlen, size_t maxlen) {
SECStatus rv;
unsigned int uoutlen = 0;
SECItem param = {
- siBuffer, static_cast<unsigned char *>(params),
+ siBuffer, static_cast<unsigned char*>(params),
static_cast<unsigned int>(param_length),
};
@@ -54,28 +62,28 @@ bool AeadCipher::AeadInner(bool decrypt, void *params, size_t param_length,
return rv == SECSuccess;
}
-bool AeadCipherAesGcm::Aead(bool decrypt, const uint8_t *hdr, size_t hdr_len,
- uint64_t seq, const uint8_t *in, size_t inlen,
- uint8_t *out, size_t *outlen, size_t maxlen) {
+bool AeadCipherAesGcm::Aead(bool decrypt, const uint8_t* hdr, size_t hdr_len,
+ uint64_t seq, const uint8_t* in, size_t inlen,
+ uint8_t* out, size_t* outlen, size_t maxlen) {
CK_GCM_PARAMS aeadParams;
unsigned char nonce[12];
memset(&aeadParams, 0, sizeof(aeadParams));
aeadParams.pIv = nonce;
aeadParams.ulIvLen = sizeof(nonce);
- aeadParams.pAAD = const_cast<uint8_t *>(hdr);
+ aeadParams.pAAD = const_cast<uint8_t*>(hdr);
aeadParams.ulAADLen = hdr_len;
aeadParams.ulTagBits = 128;
FormatNonce(seq, nonce);
- return AeadInner(decrypt, (unsigned char *)&aeadParams, sizeof(aeadParams),
- in, inlen, out, outlen, maxlen);
+ return AeadInner(decrypt, (unsigned char*)&aeadParams, sizeof(aeadParams), in,
+ inlen, out, outlen, maxlen);
}
-bool AeadCipherChacha20Poly1305::Aead(bool decrypt, const uint8_t *hdr,
+bool AeadCipherChacha20Poly1305::Aead(bool decrypt, const uint8_t* hdr,
size_t hdr_len, uint64_t seq,
- const uint8_t *in, size_t inlen,
- uint8_t *out, size_t *outlen,
+ const uint8_t* in, size_t inlen,
+ uint8_t* out, size_t* outlen,
size_t maxlen) {
CK_NSS_AEAD_PARAMS aeadParams;
unsigned char nonce[12];
@@ -83,67 +91,126 @@ bool AeadCipherChacha20Poly1305::Aead(bool decrypt, const uint8_t *hdr,
memset(&aeadParams, 0, sizeof(aeadParams));
aeadParams.pNonce = nonce;
aeadParams.ulNonceLen = sizeof(nonce);
- aeadParams.pAAD = const_cast<uint8_t *>(hdr);
+ aeadParams.pAAD = const_cast<uint8_t*>(hdr);
aeadParams.ulAADLen = hdr_len;
aeadParams.ulTagLen = 16;
FormatNonce(seq, nonce);
- return AeadInner(decrypt, (unsigned char *)&aeadParams, sizeof(aeadParams),
- in, inlen, out, outlen, maxlen);
+ return AeadInner(decrypt, (unsigned char*)&aeadParams, sizeof(aeadParams), in,
+ inlen, out, outlen, maxlen);
+}
+
+static uint64_t FirstSeqno(bool dtls, uint16_t epoc) {
+ if (dtls) {
+ return static_cast<uint64_t>(epoc) << 48;
+ }
+ return 0;
}
-bool TlsCipherSpec::Init(uint16_t epoc, SSLCipherAlgorithm cipher,
- PK11SymKey *key, const uint8_t *iv) {
- epoch_ = epoc;
- switch (cipher) {
+TlsCipherSpec::TlsCipherSpec(bool dtls, uint16_t epoc)
+ : dtls_(dtls),
+ epoch_(epoc),
+ in_seqno_(FirstSeqno(dtls, epoc)),
+ out_seqno_(FirstSeqno(dtls, epoc)) {}
+
+bool TlsCipherSpec::SetKeys(SSLCipherSuiteInfo* cipherinfo,
+ PK11SymKey* secret) {
+ CK_MECHANISM_TYPE mech;
+ switch (cipherinfo->symCipher) {
case ssl_calg_aes_gcm:
aead_.reset(new AeadCipherAesGcm());
+ mech = CKM_AES_GCM;
break;
case ssl_calg_chacha20:
aead_.reset(new AeadCipherChacha20Poly1305());
+ mech = CKM_NSS_CHACHA20_POLY1305;
break;
default:
return false;
}
+ PK11SymKey* key;
+ const std::string kPurposeKey = "key";
+ SECStatus rv = tls13_HkdfExpandLabel(
+ secret, cipherinfo->kdfHash, NULL, 0, kPurposeKey.c_str(),
+ kPurposeKey.length(), mech, cipherinfo->symKeyBits / 8, &key);
+ if (rv != SECSuccess) {
+ ADD_FAILURE() << "unable to derive key for epoch " << epoch_;
+ return false;
+ }
+
+ // No constant for IV length, but everything we know of uses 12.
+ uint8_t iv[12];
+ const std::string kPurposeIv = "iv";
+ rv = tls13_HkdfExpandLabelRaw(secret, cipherinfo->kdfHash, NULL, 0,
+ kPurposeIv.c_str(), kPurposeIv.length(), iv,
+ sizeof(iv));
+ if (rv != SECSuccess) {
+ ADD_FAILURE() << "unable to derive IV for epoch " << epoch_;
+ return false;
+ }
+
return aead_->Init(key, iv);
}
-bool TlsCipherSpec::Unprotect(const TlsRecordHeader &header,
- const DataBuffer &ciphertext,
- DataBuffer *plaintext) {
+bool TlsCipherSpec::Unprotect(const TlsRecordHeader& header,
+ const DataBuffer& ciphertext,
+ DataBuffer* plaintext) {
+ if (aead_ == nullptr) {
+ return false;
+ }
// Make space.
plaintext->Allocate(ciphertext.len());
auto header_bytes = header.header();
size_t len;
- bool ret =
- aead_->Aead(true, header_bytes.data(), header_bytes.len(),
- header.sequence_number(), ciphertext.data(), ciphertext.len(),
- plaintext->data(), &len, plaintext->len());
- if (!ret) return false;
+ uint64_t seqno;
+ if (dtls_) {
+ seqno = header.sequence_number();
+ } else {
+ seqno = in_seqno_;
+ }
+ bool ret = aead_->Aead(true, header_bytes.data(), header_bytes.len(), seqno,
+ ciphertext.data(), ciphertext.len(), plaintext->data(),
+ &len, plaintext->len());
+ if (!ret) {
+ return false;
+ }
+ RecordUnprotected(seqno);
plaintext->Truncate(len);
return true;
}
-bool TlsCipherSpec::Protect(const TlsRecordHeader &header,
- const DataBuffer &plaintext,
- DataBuffer *ciphertext) {
+bool TlsCipherSpec::Protect(const TlsRecordHeader& header,
+ const DataBuffer& plaintext,
+ DataBuffer* ciphertext) {
+ if (aead_ == nullptr) {
+ return false;
+ }
// Make a padded buffer.
-
ciphertext->Allocate(plaintext.len() +
32); // Room for any plausible auth tag
size_t len;
DataBuffer header_bytes;
(void)header.WriteHeader(&header_bytes, 0, plaintext.len() + 16);
- bool ret =
- aead_->Aead(false, header_bytes.data(), header_bytes.len(),
- header.sequence_number(), plaintext.data(), plaintext.len(),
- ciphertext->data(), &len, ciphertext->len());
- if (!ret) return false;
+ uint64_t seqno;
+ if (dtls_) {
+ seqno = header.sequence_number();
+ } else {
+ seqno = out_seqno_;
+ }
+
+ bool ret = aead_->Aead(false, header_bytes.data(), header_bytes.len(), seqno,
+ plaintext.data(), plaintext.len(), ciphertext->data(),
+ &len, ciphertext->len());
+ if (!ret) {
+ return false;
+ }
+
+ RecordProtected();
ciphertext->Truncate(len);
return true;
diff --git a/gtests/ssl_gtest/tls_protect.h b/gtests/ssl_gtest/tls_protect.h
index 6f129a4eb..ded515dbb 100644
--- a/gtests/ssl_gtest/tls_protect.h
+++ b/gtests/ssl_gtest/tls_protect.h
@@ -22,19 +22,19 @@ class AeadCipher {
AeadCipher(CK_MECHANISM_TYPE mech) : mech_(mech), key_(nullptr) {}
virtual ~AeadCipher();
- bool Init(PK11SymKey *key, const uint8_t *iv);
- virtual bool Aead(bool decrypt, const uint8_t *hdr, size_t hdr_len,
- uint64_t seq, const uint8_t *in, size_t inlen, uint8_t *out,
- size_t *outlen, size_t maxlen) = 0;
+ bool Init(PK11SymKey* key, const uint8_t* iv);
+ virtual bool Aead(bool decrypt, const uint8_t* hdr, size_t hdr_len,
+ uint64_t seq, const uint8_t* in, size_t inlen, uint8_t* out,
+ size_t* outlen, size_t maxlen) = 0;
protected:
- void FormatNonce(uint64_t seq, uint8_t *nonce);
- bool AeadInner(bool decrypt, void *params, size_t param_length,
- const uint8_t *in, size_t inlen, uint8_t *out, size_t *outlen,
+ void FormatNonce(uint64_t seq, uint8_t* nonce);
+ bool AeadInner(bool decrypt, void* params, size_t param_length,
+ const uint8_t* in, size_t inlen, uint8_t* out, size_t* outlen,
size_t maxlen);
CK_MECHANISM_TYPE mech_;
- PK11SymKey *key_;
+ PK11SymKey* key_;
uint8_t iv_[12];
};
@@ -43,8 +43,8 @@ class AeadCipherChacha20Poly1305 : public AeadCipher {
AeadCipherChacha20Poly1305() : AeadCipher(CKM_NSS_CHACHA20_POLY1305) {}
protected:
- bool Aead(bool decrypt, const uint8_t *hdr, size_t hdr_len, uint64_t seq,
- const uint8_t *in, size_t inlen, uint8_t *out, size_t *outlen,
+ bool Aead(bool decrypt, const uint8_t* hdr, size_t hdr_len, uint64_t seq,
+ const uint8_t* in, size_t inlen, uint8_t* out, size_t* outlen,
size_t maxlen);
};
@@ -53,27 +53,42 @@ class AeadCipherAesGcm : public AeadCipher {
AeadCipherAesGcm() : AeadCipher(CKM_AES_GCM) {}
protected:
- bool Aead(bool decrypt, const uint8_t *hdr, size_t hdr_len, uint64_t seq,
- const uint8_t *in, size_t inlen, uint8_t *out, size_t *outlen,
+ bool Aead(bool decrypt, const uint8_t* hdr, size_t hdr_len, uint64_t seq,
+ const uint8_t* in, size_t inlen, uint8_t* out, size_t* outlen,
size_t maxlen);
};
// Our analog of ssl3CipherSpec
class TlsCipherSpec {
public:
- TlsCipherSpec() : epoch_(0), aead_() {}
+ TlsCipherSpec(bool dtls, uint16_t epoc);
+ bool SetKeys(SSLCipherSuiteInfo* cipherinfo, PK11SymKey* secret);
- bool Init(uint16_t epoch, SSLCipherAlgorithm cipher, PK11SymKey *key,
- const uint8_t *iv);
+ bool Protect(const TlsRecordHeader& header, const DataBuffer& plaintext,
+ DataBuffer* ciphertext);
+ bool Unprotect(const TlsRecordHeader& header, const DataBuffer& ciphertext,
+ DataBuffer* plaintext);
- bool Protect(const TlsRecordHeader &header, const DataBuffer &plaintext,
- DataBuffer *ciphertext);
- bool Unprotect(const TlsRecordHeader &header, const DataBuffer &ciphertext,
- DataBuffer *plaintext);
uint16_t epoch() const { return epoch_; }
+ uint64_t next_in_seqno() const { return in_seqno_; }
+ void RecordUnprotected(uint64_t seqno) {
+ // Reordering happens, so don't let this go backwards.
+ in_seqno_ = (std::max)(in_seqno_, seqno + 1);
+ }
+ uint64_t next_out_seqno() { return out_seqno_; }
+ void RecordProtected() { out_seqno_++; }
+
+ void RecordDropped() { record_dropped_ = true; }
+ bool record_dropped() const { return record_dropped_; }
+
+ bool is_protected() const { return aead_ != nullptr; }
private:
+ bool dtls_;
uint16_t epoch_;
+ uint64_t in_seqno_;
+ uint64_t out_seqno_;
+ bool record_dropped_ = false;
std::unique_ptr<AeadCipher> aead_;
};
diff --git a/lib/ssl/ssl3gthr.c b/lib/ssl/ssl3gthr.c
index e7ca65a36..5f8322b7f 100644
--- a/lib/ssl/ssl3gthr.c
+++ b/lib/ssl/ssl3gthr.c
@@ -680,3 +680,23 @@ early_loser:
ssl_Release1stHandshakeLock(ss);
return SECFailure;
}
+
+SECStatus
+SSLExp_GetCurrentEpoch(PRFileDesc *fd, PRUint16 *readEpoch,
+ PRUint16 *writeEpoch)
+{
+ sslSocket *ss = ssl_FindSocket(fd);
+ if (!ss) {
+ return SECFailure;
+ }
+
+ ssl_GetSpecReadLock(ss);
+ if (readEpoch) {
+ *readEpoch = ss->ssl3.crSpec->epoch;
+ }
+ if (writeEpoch) {
+ *writeEpoch = ss->ssl3.cwSpec->epoch;
+ }
+ ssl_ReleaseSpecReadLock(ss);
+ return SECSuccess;
+}
diff --git a/lib/ssl/sslexp.h b/lib/ssl/sslexp.h
index 8965f5606..a342618e5 100644
--- a/lib/ssl/sslexp.h
+++ b/lib/ssl/sslexp.h
@@ -561,7 +561,7 @@ typedef void(PR_CALLBACK *SSLSecretCallback)(
*
* Prior versions of TLS use epoch 1 and higher for application data.
*
- * This API is not supported for DTLS sockets.
+ * This API is not supported for DTLS.
*/
typedef SECStatus(PR_CALLBACK *SSLRecordWriteCallback)(
PRFileDesc *fd, PRUint16 epoch, SSLContentType contentType,
@@ -593,6 +593,18 @@ typedef SECStatus(PR_CALLBACK *SSLRecordWriteCallback)(
const PRUint8 *_data, unsigned int _len), \
(fd, epoch, ct, data, len))
+/*
+ * SSL_GetCurrentEpoch() returns the read and write epochs that the socket is
+ * currently using. NULL values for readEpoch or writeEpoch are ignored.
+ *
+ * See SSL_RecordLayerWriteCallback() for details on epochs.
+ */
+#define SSL_GetCurrentEpoch(fd, readEpoch, writeEpoch) \
+ SSL_EXPERIMENTAL_API("SSL_GetCurrentEpoch", \
+ (PRFileDesc * _fd, PRUint16 * _readEpoch, \
+ PRUint16 * _writeEpoch), \
+ (fd, readEpoch, writeEpoch))
+
/* Deprecated experimental APIs */
#define SSL_UseAltServerHelloType(fd, enable) SSL_DEPRECATED_EXPERIMENTAL_API
diff --git a/lib/ssl/sslimpl.h b/lib/ssl/sslimpl.h
index f89b2fe83..f3c63fa9f 100644
--- a/lib/ssl/sslimpl.h
+++ b/lib/ssl/sslimpl.h
@@ -742,11 +742,6 @@ struct ssl3StateStr {
* update is initiated locally. */
PRBool peerRequestedKeyUpdate;
- /* Internal callback for when we do a cipher suite change. Used for
- * debugging in TLS 1.3. This can only be set by non-public functions. */
- sslCipherSpecChangedFunc changedCipherSpecFunc;
- void *changedCipherSpecArg;
-
CERTCertificate *clientCertificate; /* used by client */
SECKEYPrivateKey *clientPrivateKey; /* used by client */
CERTCertificateList *clientCertChain; /* used by client */
@@ -1752,6 +1747,8 @@ SECStatus SSLExp_RecordLayerWriteCallback(PRFileDesc *fd,
SECStatus SSLExp_RecordLayerData(PRFileDesc *fd, PRUint16 epoch,
SSLContentType contentType,
const PRUint8 *data, unsigned int len);
+SECStatus SSLExp_GetCurrentEpoch(PRFileDesc *fd, PRUint16 *readEpoch,
+ PRUint16 *writeEpoch);
#define SSLResumptionTokenVersion 2
diff --git a/lib/ssl/sslinfo.c b/lib/ssl/sslinfo.c
index 4e58c5ae7..9eb22f081 100644
--- a/lib/ssl/sslinfo.c
+++ b/lib/ssl/sslinfo.c
@@ -150,6 +150,7 @@ SSL_GetPreliminaryChannelInfo(PRFileDesc *fd,
} else {
inf.maxEarlyDataSize = 0;
}
+ inf.zeroRttCipherSuite = ss->ssl3.hs.zeroRttSuite;
memcpy(info, &inf, inf.length);
return SECSuccess;
@@ -234,89 +235,89 @@ SSL_GetPreliminaryChannelInfo(PRFileDesc *fd,
static const SSLCipherSuiteInfo suiteInfo[] = {
/* <------ Cipher suite --------------------> <auth> <KEA> <bulk cipher> <MAC> <FIPS> */
- { 0, CS_(TLS_AES_128_GCM_SHA256), S_ANY, K_ANY, C_AESGCM, B_128, M_AEAD_128, F_FIPS_STD, A_ANY },
- { 0, CS_(TLS_CHACHA20_POLY1305_SHA256), S_ANY, K_ANY, C_CHACHA20, B_256, M_AEAD_128, F_NFIPS_STD, A_ANY },
- { 0, CS_(TLS_AES_256_GCM_SHA384), S_ANY, K_ANY, C_AESGCM, B_256, M_AEAD_128, F_NFIPS_STD, A_ANY },
-
- { 0, CS(RSA_WITH_AES_128_GCM_SHA256), S_RSA, K_RSA, C_AESGCM, B_128, M_AEAD_128, F_FIPS_STD, A_RSAD },
- { 0, CS(DHE_RSA_WITH_CHACHA20_POLY1305_SHA256), S_RSA, K_DHE, C_CHACHA20, B_256, M_AEAD_128, F_NFIPS_STD, A_RSAS },
-
- { 0, CS(DHE_RSA_WITH_CAMELLIA_256_CBC_SHA), S_RSA, K_DHE, C_CAMELLIA, B_256, M_SHA, F_NFIPS_STD, A_RSAS },
- { 0, CS(DHE_DSS_WITH_CAMELLIA_256_CBC_SHA), S_DSA, K_DHE, C_CAMELLIA, B_256, M_SHA, F_NFIPS_STD, A_DSA },
- { 0, CS(DHE_RSA_WITH_AES_256_CBC_SHA256), S_RSA, K_DHE, C_AES, B_256, M_SHA256, F_FIPS_STD, A_RSAS },
- { 0, CS(DHE_RSA_WITH_AES_256_CBC_SHA), S_RSA, K_DHE, C_AES, B_256, M_SHA, F_FIPS_STD, A_RSAS },
- { 0, CS(DHE_DSS_WITH_AES_256_CBC_SHA), S_DSA, K_DHE, C_AES, B_256, M_SHA, F_FIPS_STD, A_DSA },
- { 0, CS(DHE_DSS_WITH_AES_256_CBC_SHA256), S_DSA, K_DHE, C_AES, B_256, M_SHA256, F_FIPS_STD, A_DSA },
- { 0, CS(RSA_WITH_CAMELLIA_256_CBC_SHA), S_RSA, K_RSA, C_CAMELLIA, B_256, M_SHA, F_NFIPS_STD, A_RSAD },
- { 0, CS(RSA_WITH_AES_256_CBC_SHA256), S_RSA, K_RSA, C_AES, B_256, M_SHA256, F_FIPS_STD, A_RSAD },
- { 0, CS(RSA_WITH_AES_256_CBC_SHA), S_RSA, K_RSA, C_AES, B_256, M_SHA, F_FIPS_STD, A_RSAD },
-
- { 0, CS(DHE_RSA_WITH_CAMELLIA_128_CBC_SHA), S_RSA, K_DHE, C_CAMELLIA, B_128, M_SHA, F_NFIPS_STD, A_RSAS },
- { 0, CS(DHE_DSS_WITH_CAMELLIA_128_CBC_SHA), S_DSA, K_DHE, C_CAMELLIA, B_128, M_SHA, F_NFIPS_STD, A_DSA },
- { 0, CS(DHE_DSS_WITH_RC4_128_SHA), S_DSA, K_DHE, C_RC4, B_128, M_SHA, F_NFIPS_STD, A_DSA },
- { 0, CS(DHE_RSA_WITH_AES_128_CBC_SHA256), S_RSA, K_DHE, C_AES, B_128, M_SHA256, F_FIPS_STD, A_RSAS },
- { 0, CS(DHE_RSA_WITH_AES_128_GCM_SHA256), S_RSA, K_DHE, C_AESGCM, B_128, M_AEAD_128, F_FIPS_STD, A_RSAS },
- { 0, CS(DHE_RSA_WITH_AES_128_CBC_SHA), S_RSA, K_DHE, C_AES, B_128, M_SHA, F_FIPS_STD, A_RSAS },
- { 0, CS(DHE_DSS_WITH_AES_128_GCM_SHA256), S_DSA, K_DHE, C_AESGCM, B_128, M_AEAD_128, F_FIPS_STD, A_DSA },
- { 0, CS(DHE_DSS_WITH_AES_128_CBC_SHA), S_DSA, K_DHE, C_AES, B_128, M_SHA, F_FIPS_STD, A_DSA },
- { 0, CS(DHE_DSS_WITH_AES_128_CBC_SHA256), S_DSA, K_DHE, C_AES, B_128, M_SHA256, F_FIPS_STD, A_DSA },
- { 0, CS(RSA_WITH_SEED_CBC_SHA), S_RSA, K_RSA, C_SEED, B_128, M_SHA, F_FIPS_STD, A_RSAD },
- { 0, CS(RSA_WITH_CAMELLIA_128_CBC_SHA), S_RSA, K_RSA, C_CAMELLIA, B_128, M_SHA, F_NFIPS_STD, A_RSAD },
- { 0, CS(RSA_WITH_RC4_128_SHA), S_RSA, K_RSA, C_RC4, B_128, M_SHA, F_NFIPS_STD, A_RSAD },
- { 0, CS(RSA_WITH_RC4_128_MD5), S_RSA, K_RSA, C_RC4, B_128, M_MD5, F_NFIPS_STD, A_RSAD },
- { 0, CS(RSA_WITH_AES_128_CBC_SHA256), S_RSA, K_RSA, C_AES, B_128, M_SHA256, F_FIPS_STD, A_RSAD },
- { 0, CS(RSA_WITH_AES_128_CBC_SHA), S_RSA, K_RSA, C_AES, B_128, M_SHA, F_FIPS_STD, A_RSAD },
-
- { 0, CS(DHE_RSA_WITH_3DES_EDE_CBC_SHA), S_RSA, K_DHE, C_3DES, B_3DES, M_SHA, F_FIPS_STD, A_RSAS },
- { 0, CS(DHE_DSS_WITH_3DES_EDE_CBC_SHA), S_DSA, K_DHE, C_3DES, B_3DES, M_SHA, F_FIPS_STD, A_DSA },
- { 0, CS(RSA_WITH_3DES_EDE_CBC_SHA), S_RSA, K_RSA, C_3DES, B_3DES, M_SHA, F_FIPS_STD, A_RSAD },
-
- { 0, CS(DHE_RSA_WITH_DES_CBC_SHA), S_RSA, K_DHE, C_DES, B_DES, M_SHA, F_NFIPS_STD, A_RSAS },
- { 0, CS(DHE_DSS_WITH_DES_CBC_SHA), S_DSA, K_DHE, C_DES, B_DES, M_SHA, F_NFIPS_STD, A_DSA },
- { 0, CS(RSA_WITH_DES_CBC_SHA), S_RSA, K_RSA, C_DES, B_DES, M_SHA, F_NFIPS_STD, A_RSAD },
-
- { 0, CS(RSA_WITH_NULL_SHA256), S_RSA, K_RSA, C_NULL, B_0, M_SHA256, F_EXPORT, A_RSAD },
- { 0, CS(RSA_WITH_NULL_SHA), S_RSA, K_RSA, C_NULL, B_0, M_SHA, F_EXPORT, A_RSAD },
- { 0, CS(RSA_WITH_NULL_MD5), S_RSA, K_RSA, C_NULL, B_0, M_MD5, F_EXPORT, A_RSAD },
+ { 0, CS_(TLS_AES_128_GCM_SHA256), S_ANY, K_ANY, C_AESGCM, B_128, M_AEAD_128, F_FIPS_STD, A_ANY, ssl_hash_sha256 },
+ { 0, CS_(TLS_CHACHA20_POLY1305_SHA256), S_ANY, K_ANY, C_CHACHA20, B_256, M_AEAD_128, F_NFIPS_STD, A_ANY, ssl_hash_sha256 },
+ { 0, CS_(TLS_AES_256_GCM_SHA384), S_ANY, K_ANY, C_AESGCM, B_256, M_AEAD_128, F_NFIPS_STD, A_ANY, ssl_hash_sha384 },
+
+ { 0, CS(RSA_WITH_AES_128_GCM_SHA256), S_RSA, K_RSA, C_AESGCM, B_128, M_AEAD_128, F_FIPS_STD, A_RSAD, ssl_hash_sha256 },
+ { 0, CS(DHE_RSA_WITH_CHACHA20_POLY1305_SHA256), S_RSA, K_DHE, C_CHACHA20, B_256, M_AEAD_128, F_NFIPS_STD, A_RSAS, ssl_hash_sha256 },
+
+ { 0, CS(DHE_RSA_WITH_CAMELLIA_256_CBC_SHA), S_RSA, K_DHE, C_CAMELLIA, B_256, M_SHA, F_NFIPS_STD, A_RSAS, ssl_hash_none },
+ { 0, CS(DHE_DSS_WITH_CAMELLIA_256_CBC_SHA), S_DSA, K_DHE, C_CAMELLIA, B_256, M_SHA, F_NFIPS_STD, A_DSA, ssl_hash_none },
+ { 0, CS(DHE_RSA_WITH_AES_256_CBC_SHA256), S_RSA, K_DHE, C_AES, B_256, M_SHA256, F_FIPS_STD, A_RSAS, ssl_hash_sha256 },
+ { 0, CS(DHE_RSA_WITH_AES_256_CBC_SHA), S_RSA, K_DHE, C_AES, B_256, M_SHA, F_FIPS_STD, A_RSAS, ssl_hash_none },
+ { 0, CS(DHE_DSS_WITH_AES_256_CBC_SHA), S_DSA, K_DHE, C_AES, B_256, M_SHA, F_FIPS_STD, A_DSA, ssl_hash_none },
+ { 0, CS(DHE_DSS_WITH_AES_256_CBC_SHA256), S_DSA, K_DHE, C_AES, B_256, M_SHA256, F_FIPS_STD, A_DSA, ssl_hash_sha256 },
+ { 0, CS(RSA_WITH_CAMELLIA_256_CBC_SHA), S_RSA, K_RSA, C_CAMELLIA, B_256, M_SHA, F_NFIPS_STD, A_RSAD, ssl_hash_none },
+ { 0, CS(RSA_WITH_AES_256_CBC_SHA256), S_RSA, K_RSA, C_AES, B_256, M_SHA256, F_FIPS_STD, A_RSAD, ssl_hash_sha256 },
+ { 0, CS(RSA_WITH_AES_256_CBC_SHA), S_RSA, K_RSA, C_AES, B_256, M_SHA, F_FIPS_STD, A_RSAD, ssl_hash_none },
+
+ { 0, CS(DHE_RSA_WITH_CAMELLIA_128_CBC_SHA), S_RSA, K_DHE, C_CAMELLIA, B_128, M_SHA, F_NFIPS_STD, A_RSAS, ssl_hash_none },
+ { 0, CS(DHE_DSS_WITH_CAMELLIA_128_CBC_SHA), S_DSA, K_DHE, C_CAMELLIA, B_128, M_SHA, F_NFIPS_STD, A_DSA, ssl_hash_none },
+ { 0, CS(DHE_DSS_WITH_RC4_128_SHA), S_DSA, K_DHE, C_RC4, B_128, M_SHA, F_NFIPS_STD, A_DSA, ssl_hash_none },
+ { 0, CS(DHE_RSA_WITH_AES_128_CBC_SHA256), S_RSA, K_DHE, C_AES, B_128, M_SHA256, F_FIPS_STD, A_RSAS, ssl_hash_sha256 },
+ { 0, CS(DHE_RSA_WITH_AES_128_GCM_SHA256), S_RSA, K_DHE, C_AESGCM, B_128, M_AEAD_128, F_FIPS_STD, A_RSAS, ssl_hash_sha256 },
+ { 0, CS(DHE_RSA_WITH_AES_128_CBC_SHA), S_RSA, K_DHE, C_AES, B_128, M_SHA, F_FIPS_STD, A_RSAS, ssl_hash_none },
+ { 0, CS(DHE_DSS_WITH_AES_128_GCM_SHA256), S_DSA, K_DHE, C_AESGCM, B_128, M_AEAD_128, F_FIPS_STD, A_DSA, ssl_hash_sha256 },
+ { 0, CS(DHE_DSS_WITH_AES_128_CBC_SHA), S_DSA, K_DHE, C_AES, B_128, M_SHA, F_FIPS_STD, A_DSA, ssl_hash_none },
+ { 0, CS(DHE_DSS_WITH_AES_128_CBC_SHA256), S_DSA, K_DHE, C_AES, B_128, M_SHA256, F_FIPS_STD, A_DSA, ssl_hash_sha256 },
+ { 0, CS(RSA_WITH_SEED_CBC_SHA), S_RSA, K_RSA, C_SEED, B_128, M_SHA, F_FIPS_STD, A_RSAD, ssl_hash_none },
+ { 0, CS(RSA_WITH_CAMELLIA_128_CBC_SHA), S_RSA, K_RSA, C_CAMELLIA, B_128, M_SHA, F_NFIPS_STD, A_RSAD, ssl_hash_none },
+ { 0, CS(RSA_WITH_RC4_128_SHA), S_RSA, K_RSA, C_RC4, B_128, M_SHA, F_NFIPS_STD, A_RSAD, ssl_hash_none },
+ { 0, CS(RSA_WITH_RC4_128_MD5), S_RSA, K_RSA, C_RC4, B_128, M_MD5, F_NFIPS_STD, A_RSAD, ssl_hash_none },
+ { 0, CS(RSA_WITH_AES_128_CBC_SHA256), S_RSA, K_RSA, C_AES, B_128, M_SHA256, F_FIPS_STD, A_RSAD, ssl_hash_sha256 },
+ { 0, CS(RSA_WITH_AES_128_CBC_SHA), S_RSA, K_RSA, C_AES, B_128, M_SHA, F_FIPS_STD, A_RSAD, ssl_hash_none },
+
+ { 0, CS(DHE_RSA_WITH_3DES_EDE_CBC_SHA), S_RSA, K_DHE, C_3DES, B_3DES, M_SHA, F_FIPS_STD, A_RSAS, ssl_hash_none },
+ { 0, CS(DHE_DSS_WITH_3DES_EDE_CBC_SHA), S_DSA, K_DHE, C_3DES, B_3DES, M_SHA, F_FIPS_STD, A_DSA, ssl_hash_none },
+ { 0, CS(RSA_WITH_3DES_EDE_CBC_SHA), S_RSA, K_RSA, C_3DES, B_3DES, M_SHA, F_FIPS_STD, A_RSAD, ssl_hash_none },
+
+ { 0, CS(DHE_RSA_WITH_DES_CBC_SHA), S_RSA, K_DHE, C_DES, B_DES, M_SHA, F_NFIPS_STD, A_RSAS, ssl_hash_none },
+ { 0, CS(DHE_DSS_WITH_DES_CBC_SHA), S_DSA, K_DHE, C_DES, B_DES, M_SHA, F_NFIPS_STD, A_DSA, ssl_hash_none },
+ { 0, CS(RSA_WITH_DES_CBC_SHA), S_RSA, K_RSA, C_DES, B_DES, M_SHA, F_NFIPS_STD, A_RSAD, ssl_hash_none },
+
+ { 0, CS(RSA_WITH_NULL_SHA256), S_RSA, K_RSA, C_NULL, B_0, M_SHA256, F_EXPORT, A_RSAD, ssl_hash_sha256 },
+ { 0, CS(RSA_WITH_NULL_SHA), S_RSA, K_RSA, C_NULL, B_0, M_SHA, F_EXPORT, A_RSAD, ssl_hash_none },
+ { 0, CS(RSA_WITH_NULL_MD5), S_RSA, K_RSA, C_NULL, B_0, M_MD5, F_EXPORT, A_RSAD, ssl_hash_none },
/* ECC cipher suites */
- { 0, CS(ECDHE_RSA_WITH_AES_128_GCM_SHA256), S_RSA, K_ECDHE, C_AESGCM, B_128, M_AEAD_128, F_FIPS_STD, A_RSAS },
- { 0, CS(ECDHE_ECDSA_WITH_AES_128_GCM_SHA256), S_ECDSA, K_ECDHE, C_AESGCM, B_128, M_AEAD_128, F_FIPS_STD, A_ECDSA },
- { 0, CS(ECDH_ECDSA_WITH_NULL_SHA), S_ECDSA, K_ECDH, C_NULL, B_0, M_SHA, F_NFIPS_STD, A_ECDH_E },
- { 0, CS(ECDH_ECDSA_WITH_RC4_128_SHA), S_ECDSA, K_ECDH, C_RC4, B_128, M_SHA, F_NFIPS_STD, A_ECDH_E },
- { 0, CS(ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA), S_ECDSA, K_ECDH, C_3DES, B_3DES, M_SHA, F_FIPS_STD, A_ECDH_E },
- { 0, CS(ECDH_ECDSA_WITH_AES_128_CBC_SHA), S_ECDSA, K_ECDH, C_AES, B_128, M_SHA, F_FIPS_STD, A_ECDH_E },
- { 0, CS(ECDH_ECDSA_WITH_AES_256_CBC_SHA), S_ECDSA, K_ECDH, C_AES, B_256, M_SHA, F_FIPS_STD, A_ECDH_E },
-
- { 0, CS(ECDHE_ECDSA_WITH_NULL_SHA), S_ECDSA, K_ECDHE, C_NULL, B_0, M_SHA, F_NFIPS_STD, A_ECDSA },
- { 0, CS(ECDHE_ECDSA_WITH_RC4_128_SHA), S_ECDSA, K_ECDHE, C_RC4, B_128, M_SHA, F_NFIPS_STD, A_ECDSA },
- { 0, CS(ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA), S_ECDSA, K_ECDHE, C_3DES, B_3DES, M_SHA, F_FIPS_STD, A_ECDSA },
- { 0, CS(ECDHE_ECDSA_WITH_AES_128_CBC_SHA), S_ECDSA, K_ECDHE, C_AES, B_128, M_SHA, F_FIPS_STD, A_ECDSA },
- { 0, CS(ECDHE_ECDSA_WITH_AES_128_CBC_SHA256), S_ECDSA, K_ECDHE, C_AES, B_128, M_SHA256, F_FIPS_STD, A_ECDSA },
- { 0, CS(ECDHE_ECDSA_WITH_AES_256_CBC_SHA), S_ECDSA, K_ECDHE, C_AES, B_256, M_SHA, F_FIPS_STD, A_ECDSA },
- { 0, CS(ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256), S_ECDSA, K_ECDHE, C_CHACHA20, B_256, M_AEAD_128, F_NFIPS_STD, A_ECDSA },
-
- { 0, CS(ECDH_RSA_WITH_NULL_SHA), S_RSA, K_ECDH, C_NULL, B_0, M_SHA, F_NFIPS_STD, A_ECDH_R },
- { 0, CS(ECDH_RSA_WITH_RC4_128_SHA), S_RSA, K_ECDH, C_RC4, B_128, M_SHA, F_NFIPS_STD, A_ECDH_R },
- { 0, CS(ECDH_RSA_WITH_3DES_EDE_CBC_SHA), S_RSA, K_ECDH, C_3DES, B_3DES, M_SHA, F_FIPS_STD, A_ECDH_R },
- { 0, CS(ECDH_RSA_WITH_AES_128_CBC_SHA), S_RSA, K_ECDH, C_AES, B_128, M_SHA, F_FIPS_STD, A_ECDH_R },
- { 0, CS(ECDH_RSA_WITH_AES_256_CBC_SHA), S_RSA, K_ECDH, C_AES, B_256, M_SHA, F_FIPS_STD, A_ECDH_R },
-
- { 0, CS(ECDHE_RSA_WITH_NULL_SHA), S_RSA, K_ECDHE, C_NULL, B_0, M_SHA, F_NFIPS_STD, A_RSAS },
- { 0, CS(ECDHE_RSA_WITH_RC4_128_SHA), S_RSA, K_ECDHE, C_RC4, B_128, M_SHA, F_NFIPS_STD, A_RSAS },
- { 0, CS(ECDHE_RSA_WITH_3DES_EDE_CBC_SHA), S_RSA, K_ECDHE, C_3DES, B_3DES, M_SHA, F_FIPS_STD, A_RSAS },
- { 0, CS(ECDHE_RSA_WITH_AES_128_CBC_SHA), S_RSA, K_ECDHE, C_AES, B_128, M_SHA, F_FIPS_STD, A_RSAS },
- { 0, CS(ECDHE_RSA_WITH_AES_128_CBC_SHA256), S_RSA, K_ECDHE, C_AES, B_128, M_SHA256, F_FIPS_STD, A_RSAS },
- { 0, CS(ECDHE_RSA_WITH_AES_256_CBC_SHA), S_RSA, K_ECDHE, C_AES, B_256, M_SHA, F_FIPS_STD, A_RSAS },
- { 0, CS(ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256), S_RSA, K_ECDHE, C_CHACHA20, B_256, M_AEAD_128, F_NFIPS_STD, A_RSAS },
- { 0, CS(ECDHE_RSA_WITH_AES_256_CBC_SHA384), S_RSA, K_ECDHE, C_AES, B_256, M_SHA384, F_FIPS_STD, A_RSAS },
- { 0, CS(ECDHE_ECDSA_WITH_AES_256_CBC_SHA384), S_ECDSA, K_ECDHE, C_AES, B_256, M_SHA384, F_FIPS_STD, A_ECDSA },
- { 0, CS(ECDHE_ECDSA_WITH_AES_256_GCM_SHA384), S_ECDSA, K_ECDHE, C_AESGCM, B_256, M_AEAD_128, F_FIPS_STD, A_ECDSA },
- { 0, CS(ECDHE_RSA_WITH_AES_256_GCM_SHA384), S_RSA, K_ECDHE, C_AESGCM, B_256, M_AEAD_128, F_FIPS_STD, A_RSAS },
-
- { 0, CS(DHE_DSS_WITH_AES_256_GCM_SHA384), S_DSA, K_DHE, C_AESGCM, B_256, M_AEAD_128, F_FIPS_STD, A_DSA },
- { 0, CS(DHE_RSA_WITH_AES_256_GCM_SHA384), S_RSA, K_DHE, C_AESGCM, B_256, M_AEAD_128, F_FIPS_STD, A_RSAS },
- { 0, CS(RSA_WITH_AES_256_GCM_SHA384), S_RSA, K_RSA, C_AESGCM, B_256, M_AEAD_128, F_FIPS_STD, A_RSAD },
+ { 0, CS(ECDHE_RSA_WITH_AES_128_GCM_SHA256), S_RSA, K_ECDHE, C_AESGCM, B_128, M_AEAD_128, F_FIPS_STD, A_RSAS, ssl_hash_sha256 },
+ { 0, CS(ECDHE_ECDSA_WITH_AES_128_GCM_SHA256), S_ECDSA, K_ECDHE, C_AESGCM, B_128, M_AEAD_128, F_FIPS_STD, A_ECDSA, ssl_hash_sha256 },
+ { 0, CS(ECDH_ECDSA_WITH_NULL_SHA), S_ECDSA, K_ECDH, C_NULL, B_0, M_SHA, F_NFIPS_STD, A_ECDH_E, ssl_hash_none },
+ { 0, CS(ECDH_ECDSA_WITH_RC4_128_SHA), S_ECDSA, K_ECDH, C_RC4, B_128, M_SHA, F_NFIPS_STD, A_ECDH_E, ssl_hash_none },
+ { 0, CS(ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA), S_ECDSA, K_ECDH, C_3DES, B_3DES, M_SHA, F_FIPS_STD, A_ECDH_E, ssl_hash_none },
+ { 0, CS(ECDH_ECDSA_WITH_AES_128_CBC_SHA), S_ECDSA, K_ECDH, C_AES, B_128, M_SHA, F_FIPS_STD, A_ECDH_E, ssl_hash_none },
+ { 0, CS(ECDH_ECDSA_WITH_AES_256_CBC_SHA), S_ECDSA, K_ECDH, C_AES, B_256, M_SHA, F_FIPS_STD, A_ECDH_E, ssl_hash_none },
+
+ { 0, CS(ECDHE_ECDSA_WITH_NULL_SHA), S_ECDSA, K_ECDHE, C_NULL, B_0, M_SHA, F_NFIPS_STD, A_ECDSA, ssl_hash_none },
+ { 0, CS(ECDHE_ECDSA_WITH_RC4_128_SHA), S_ECDSA, K_ECDHE, C_RC4, B_128, M_SHA, F_NFIPS_STD, A_ECDSA, ssl_hash_none },
+ { 0, CS(ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA), S_ECDSA, K_ECDHE, C_3DES, B_3DES, M_SHA, F_FIPS_STD, A_ECDSA, ssl_hash_none },
+ { 0, CS(ECDHE_ECDSA_WITH_AES_128_CBC_SHA), S_ECDSA, K_ECDHE, C_AES, B_128, M_SHA, F_FIPS_STD, A_ECDSA, ssl_hash_none },
+ { 0, CS(ECDHE_ECDSA_WITH_AES_128_CBC_SHA256), S_ECDSA, K_ECDHE, C_AES, B_128, M_SHA256, F_FIPS_STD, A_ECDSA, ssl_hash_sha256 },
+ { 0, CS(ECDHE_ECDSA_WITH_AES_256_CBC_SHA), S_ECDSA, K_ECDHE, C_AES, B_256, M_SHA, F_FIPS_STD, A_ECDSA, ssl_hash_none },
+ { 0, CS(ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256), S_ECDSA, K_ECDHE, C_CHACHA20, B_256, M_AEAD_128, F_NFIPS_STD, A_ECDSA, ssl_hash_sha256 },
+
+ { 0, CS(ECDH_RSA_WITH_NULL_SHA), S_RSA, K_ECDH, C_NULL, B_0, M_SHA, F_NFIPS_STD, A_ECDH_R, ssl_hash_none },
+ { 0, CS(ECDH_RSA_WITH_RC4_128_SHA), S_RSA, K_ECDH, C_RC4, B_128, M_SHA, F_NFIPS_STD, A_ECDH_R, ssl_hash_none },
+ { 0, CS(ECDH_RSA_WITH_3DES_EDE_CBC_SHA), S_RSA, K_ECDH, C_3DES, B_3DES, M_SHA, F_FIPS_STD, A_ECDH_R, ssl_hash_none },
+ { 0, CS(ECDH_RSA_WITH_AES_128_CBC_SHA), S_RSA, K_ECDH, C_AES, B_128, M_SHA, F_FIPS_STD, A_ECDH_R, ssl_hash_none },
+ { 0, CS(ECDH_RSA_WITH_AES_256_CBC_SHA), S_RSA, K_ECDH, C_AES, B_256, M_SHA, F_FIPS_STD, A_ECDH_R, ssl_hash_none },
+
+ { 0, CS(ECDHE_RSA_WITH_NULL_SHA), S_RSA, K_ECDHE, C_NULL, B_0, M_SHA, F_NFIPS_STD, A_RSAS, ssl_hash_none },
+ { 0, CS(ECDHE_RSA_WITH_RC4_128_SHA), S_RSA, K_ECDHE, C_RC4, B_128, M_SHA, F_NFIPS_STD, A_RSAS, ssl_hash_none },
+ { 0, CS(ECDHE_RSA_WITH_3DES_EDE_CBC_SHA), S_RSA, K_ECDHE, C_3DES, B_3DES, M_SHA, F_FIPS_STD, A_RSAS, ssl_hash_none },
+ { 0, CS(ECDHE_RSA_WITH_AES_128_CBC_SHA), S_RSA, K_ECDHE, C_AES, B_128, M_SHA, F_FIPS_STD, A_RSAS, ssl_hash_none },
+ { 0, CS(ECDHE_RSA_WITH_AES_128_CBC_SHA256), S_RSA, K_ECDHE, C_AES, B_128, M_SHA256, F_FIPS_STD, A_RSAS, ssl_hash_sha256 },
+ { 0, CS(ECDHE_RSA_WITH_AES_256_CBC_SHA), S_RSA, K_ECDHE, C_AES, B_256, M_SHA, F_FIPS_STD, A_RSAS, ssl_hash_none },
+ { 0, CS(ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256), S_RSA, K_ECDHE, C_CHACHA20, B_256, M_AEAD_128, F_NFIPS_STD, A_RSAS, ssl_hash_sha256 },
+ { 0, CS(ECDHE_RSA_WITH_AES_256_CBC_SHA384), S_RSA, K_ECDHE, C_AES, B_256, M_SHA384, F_FIPS_STD, A_RSAS, ssl_hash_sha384 },
+ { 0, CS(ECDHE_ECDSA_WITH_AES_256_CBC_SHA384), S_ECDSA, K_ECDHE, C_AES, B_256, M_SHA384, F_FIPS_STD, A_ECDSA, ssl_hash_sha384 },
+ { 0, CS(ECDHE_ECDSA_WITH_AES_256_GCM_SHA384), S_ECDSA, K_ECDHE, C_AESGCM, B_256, M_AEAD_128, F_FIPS_STD, A_ECDSA, ssl_hash_sha384 },
+ { 0, CS(ECDHE_RSA_WITH_AES_256_GCM_SHA384), S_RSA, K_ECDHE, C_AESGCM, B_256, M_AEAD_128, F_FIPS_STD, A_RSAS, ssl_hash_sha384 },
+
+ { 0, CS(DHE_DSS_WITH_AES_256_GCM_SHA384), S_DSA, K_DHE, C_AESGCM, B_256, M_AEAD_128, F_FIPS_STD, A_DSA, ssl_hash_sha384 },
+ { 0, CS(DHE_RSA_WITH_AES_256_GCM_SHA384), S_RSA, K_DHE, C_AESGCM, B_256, M_AEAD_128, F_FIPS_STD, A_RSAS, ssl_hash_sha384 },
+ { 0, CS(RSA_WITH_AES_256_GCM_SHA384), S_RSA, K_RSA, C_AESGCM, B_256, M_AEAD_128, F_FIPS_STD, A_RSAD, ssl_hash_sha384 },
};
#define NUM_SUITEINFOS ((sizeof suiteInfo) / (sizeof suiteInfo[0]))
diff --git a/lib/ssl/sslsock.c b/lib/ssl/sslsock.c
index 4d4b7e1e6..3c2a5c655 100644
--- a/lib/ssl/sslsock.c
+++ b/lib/ssl/sslsock.c
@@ -4029,6 +4029,7 @@ struct {
EXP(DestroyResumptionTokenInfo),
EXP(EnableESNI),
EXP(EncodeESNIKeys),
+ EXP(GetCurrentEpoch),
EXP(GetExtensionSupport),
EXP(GetResumptionTokenInfo),
EXP(HelloRetryRequestCallback),
diff --git a/lib/ssl/sslt.h b/lib/ssl/sslt.h
index 038925338..f489f882d 100644
--- a/lib/ssl/sslt.h
+++ b/lib/ssl/sslt.h
@@ -333,6 +333,9 @@ typedef struct SSLChannelInfoStr {
/* Preliminary channel info */
#define ssl_preinfo_version (1U << 0)
#define ssl_preinfo_cipher_suite (1U << 1)
+#define ssl_preinfo_0rtt_cipher_suite (1U << 2)
+/* ssl_preinfo_all doesn't contain ssl_preinfo_0rtt_cipher_suite because that
+ * field is only set if 0-RTT is sent (client) or accepted (server). */
#define ssl_preinfo_all (ssl_preinfo_version | ssl_preinfo_cipher_suite)
typedef struct SSLPreliminaryChannelInfoStr {
@@ -364,6 +367,13 @@ typedef struct SSLPreliminaryChannelInfoStr {
* resume this session. */
PRUint32 maxEarlyDataSize;
+ /* The following fields were added in NSS 3.39. */
+ /* This reports the cipher suite used for 0-RTT if it sent or accepted. For
+ * a client, this is set earlier than |cipherSuite|, and will match that
+ * value if 0-RTT is accepted by the server. The server only sets this
+ * after accepting 0-RTT, so this will contain the same value. */
+ PRUint16 zeroRttCipherSuite;
+
/* When adding new fields to this structure, please document the
* NSS version in which they were added. */
} SSLPreliminaryChannelInfo;
@@ -412,6 +422,12 @@ typedef struct SSLCipherSuiteInfoStr {
* this instead of |authAlgorithm|. */
SSLAuthType authType;
+ /* The following fields were added in NSS 3.39. */
+ /* This reports the hash function used in the TLS KDF, or HKDF for TLS 1.3.
+ * For suites defined for versions of TLS earlier than TLS 1.2, this reports
+ * ssl_hash_none. */
+ SSLHashType kdfHash;
+
/* When adding new fields to this structure, please document the
* NSS version in which they were added. */
} SSLCipherSuiteInfo;
diff --git a/lib/ssl/tls13con.c b/lib/ssl/tls13con.c
index cbf149372..898b36728 100644
--- a/lib/ssl/tls13con.c
+++ b/lib/ssl/tls13con.c
@@ -1112,7 +1112,7 @@ tls13_ComputeHandshakeSecrets(sslSocket *ss)
if (ss->secretCallback) {
SSLSecretDirection dir =
- ss->sec.isServer ? ssl_secret_read : ssl_secret_write;
+ ss->sec.isServer ? ssl_secret_read : ssl_secret_write;
ss->secretCallback(ss->fd, (PRUint16)TrafficKeyHandshake, dir,
ss->ssl3.hs.clientHsTrafficSecret,
ss->secretCallbackArg);
@@ -1174,7 +1174,7 @@ tls13_ComputeApplicationSecrets(sslSocket *ss)
if (ss->secretCallback) {
SSLSecretDirection dir =
- ss->sec.isServer ? ssl_secret_read : ssl_secret_write;
+ ss->sec.isServer ? ssl_secret_read : ssl_secret_write;
ss->secretCallback(ss->fd, (PRUint16)TrafficKeyApplicationData,
dir, ss->ssl3.hs.clientTrafficSecret,
ss->secretCallbackArg);
@@ -1330,6 +1330,8 @@ tls13_NegotiateZeroRtt(sslSocket *ss, const sslSessionID *sid)
PORT_Assert(ss->statelessResume);
ss->ssl3.hs.zeroRttState = ssl_0rtt_accepted;
ss->ssl3.hs.zeroRttIgnore = ssl_0rtt_ignore_none;
+ ss->ssl3.hs.zeroRttSuite = ss->ssl3.hs.cipher_suite;
+ ss->ssl3.hs.preliminaryInfo |= ssl_preinfo_0rtt_cipher_suite;
}
/* Check if the offered group is acceptable. */
@@ -3428,11 +3430,6 @@ tls13_SetCipherSpec(sslSocket *ss, PRUint16 epoch,
SSL_TRC(3, ("%d: TLS13[%d]: %s installed key for epoch=%d (%s) dir=%s",
SSL_GETPID(), ss->fd, SSL_ROLE(ss), spec->epoch,
spec->phase, SPEC_DIR(spec)));
-
- if (ss->ssl3.changedCipherSpecFunc) {
- ss->ssl3.changedCipherSpecFunc(ss->ssl3.changedCipherSpecArg,
- direction == ssl_secret_write, spec);
- }
return SECSuccess;
loser:
@@ -5231,6 +5228,9 @@ tls13_MaybeDo0RTTHandshake(sslSocket *ss)
ss->ssl3.hs.zeroRttState = ssl_0rtt_sent;
ss->ssl3.hs.zeroRttSuite = ss->ssl3.hs.cipher_suite;
+ /* Note: Reset the preliminary info here rather than just add 0-RTT. We are
+ * only guessing what might happen at this point.*/
+ ss->ssl3.hs.preliminaryInfo = ssl_preinfo_0rtt_cipher_suite;
SSL_TRC(3, ("%d: TLS13[%d]: in 0-RTT mode", SSL_GETPID(), ss->fd));
@@ -5259,9 +5259,6 @@ tls13_MaybeDo0RTTHandshake(sslSocket *ss)
}
}
- /* Cipher suite already set in tls13_SetupClientHello. */
- ss->ssl3.hs.preliminaryInfo = 0;
-
rv = tls13_DeriveEarlySecrets(ss);
if (rv != SECSuccess) {
return SECFailure;