summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEKR <ekr@rtfm.com>2016-02-07 16:21:26 -0800
committerEKR <ekr@rtfm.com>2016-02-07 16:21:26 -0800
commite239e24e04ee67efbbcc70b23eb7d73744e1ce69 (patch)
treeee508c9e25fa0394ad301b8e4dc3d1b2774ea14e
parent8bbc8aa7f7a4595b255c8fbe7e5ccc774a87488f (diff)
downloadnss-hg-e239e24e04ee67efbbcc70b23eb7d73744e1ce69.tar.gz
Bug 1257891 - TLS 1.3: Implement resumption-PSK. r=mtNSS_3_24_BETA1
-rw-r--r--external_tests/ssl_gtest/libssl_internals.c8
-rw-r--r--external_tests/ssl_gtest/libssl_internals.h2
-rw-r--r--external_tests/ssl_gtest/ssl_extension_unittest.cc152
-rw-r--r--external_tests/ssl_gtest/ssl_loopback_unittest.cc95
-rw-r--r--external_tests/ssl_gtest/tls_agent.cc5
-rw-r--r--external_tests/ssl_gtest/tls_agent.h3
-rw-r--r--external_tests/ssl_gtest/tls_connect.cc25
-rw-r--r--external_tests/ssl_gtest/tls_connect.h4
-rw-r--r--external_tests/ssl_gtest/tls_filter.cc133
-rw-r--r--external_tests/ssl_gtest/tls_filter.h35
-rw-r--r--external_tests/ssl_gtest/tls_parser.h2
-rw-r--r--lib/ssl/SSLerrs.h4
-rw-r--r--lib/ssl/ssl3con.c708
-rw-r--r--lib/ssl/ssl3ext.c1036
-rw-r--r--lib/ssl/ssl3prot.h3
-rw-r--r--lib/ssl/sslenum.c6
-rw-r--r--lib/ssl/sslerr.h1
-rw-r--r--lib/ssl/sslimpl.h20
-rw-r--r--lib/ssl/sslinfo.c21
-rw-r--r--lib/ssl/sslproto.h3
-rw-r--r--lib/ssl/sslt.h9
-rw-r--r--lib/ssl/tls13con.c612
-rw-r--r--lib/ssl/tls13con.h6
-rw-r--r--lib/ssl/tls13hkdf.c12
-rw-r--r--lib/util/secoid.c3
-rw-r--r--lib/util/secoidt.h2
26 files changed, 2000 insertions, 910 deletions
diff --git a/external_tests/ssl_gtest/libssl_internals.c b/external_tests/ssl_gtest/libssl_internals.c
index 7906ba6cd..0717f54f1 100644
--- a/external_tests/ssl_gtest/libssl_internals.c
+++ b/external_tests/ssl_gtest/libssl_internals.c
@@ -7,6 +7,7 @@
/* This file contains functions for frobbing the internals of libssl */
#include "libssl_internals.h"
+#include "nss.h"
#include "seccomon.h"
#include "ssl.h"
#include "sslimpl.h"
@@ -84,3 +85,10 @@ SSLInt_ExtensionNegotiated(PRFileDesc *fd, PRUint16 ext)
sslSocket *ss = ssl_FindSocket(fd);
return (PRBool)(ss && ssl3_ExtensionNegotiated(ss, ext));
}
+
+void
+SSLInt_ClearSessionTicketKey()
+{
+ ssl3_SessionTicketShutdown(NULL, NULL);
+ NSS_UnregisterShutdown(ssl3_SessionTicketShutdown, NULL);
+}
diff --git a/external_tests/ssl_gtest/libssl_internals.h b/external_tests/ssl_gtest/libssl_internals.h
index d6d67d4ef..e731e6f03 100644
--- a/external_tests/ssl_gtest/libssl_internals.h
+++ b/external_tests/ssl_gtest/libssl_internals.h
@@ -23,7 +23,7 @@ SECStatus SSLInt_UpdateSSLv2ClientRandom(PRFileDesc *fd,
uint8_t *msg, size_t msg_len);
PRBool SSLInt_ExtensionNegotiated(PRFileDesc *fd, PRUint16 ext);
-
+void SSLInt_ClearSessionTicketKey();
#endif
diff --git a/external_tests/ssl_gtest/ssl_extension_unittest.cc b/external_tests/ssl_gtest/ssl_extension_unittest.cc
index 3484aa75c..59d05ba8b 100644
--- a/external_tests/ssl_gtest/ssl_extension_unittest.cc
+++ b/external_tests/ssl_gtest/ssl_extension_unittest.cc
@@ -15,138 +15,6 @@
namespace nss_test {
-class TlsExtensionFilter : public TlsHandshakeFilter {
- protected:
- virtual PacketFilter::Action FilterHandshake(
- const HandshakeHeader& header,
- const DataBuffer& input, DataBuffer* output) {
- if (header.handshake_type() == kTlsHandshakeClientHello) {
- TlsParser parser(input);
- if (!FindClientHelloExtensions(&parser, header)) {
- return KEEP;
- }
- return FilterExtensions(&parser, input, output);
- }
- if (header.handshake_type() == kTlsHandshakeServerHello) {
- TlsParser parser(input);
- if (!FindServerHelloExtensions(&parser, header.version())) {
- return KEEP;
- }
- return FilterExtensions(&parser, input, output);
- }
- return KEEP;
- }
-
- virtual PacketFilter::Action FilterExtension(uint16_t extension_type,
- const DataBuffer& input,
- DataBuffer* output) = 0;
-
- public:
- static bool FindClientHelloExtensions(TlsParser* parser, const Versioned& header) {
- if (!parser->Skip(2 + 32)) { // version + random
- return false;
- }
- if (!parser->SkipVariable(1)) { // session ID
- return false;
- }
- if (header.is_dtls() && !parser->SkipVariable(1)) { // DTLS cookie
- return false;
- }
- if (!parser->SkipVariable(2)) { // cipher suites
- return false;
- }
- if (!parser->SkipVariable(1)) { // compression methods
- return false;
- }
- return true;
- }
-
- static bool FindServerHelloExtensions(TlsParser* parser, uint16_t version) {
- if (!parser->Skip(2 + 32)) { // version + random
- return false;
- }
- if (!parser->SkipVariable(1)) { // session ID
- return false;
- }
- if (!parser->Skip(2)) { // cipher suite
- return false;
- }
- if (NormalizeTlsVersion(version) <= SSL_LIBRARY_VERSION_TLS_1_2) {
- if (!parser->Skip(1)) { // compression method
- return false;
- }
- }
- return true;
- }
-
- private:
- PacketFilter::Action FilterExtensions(TlsParser* parser,
- const DataBuffer& input,
- DataBuffer* output) {
- size_t length_offset = parser->consumed();
- uint32_t all_extensions;
- if (!parser->Read(&all_extensions, 2)) {
- return KEEP; // no extensions, odd but OK
- }
- if (all_extensions != parser->remaining()) {
- return KEEP; // malformed
- }
-
- bool changed = false;
-
- // Write out the start of the message.
- output->Allocate(input.len());
- size_t offset = output->Write(0, input.data(), parser->consumed());
-
- while (parser->remaining()) {
- uint32_t extension_type;
- if (!parser->Read(&extension_type, 2)) {
- return KEEP; // malformed
- }
-
- DataBuffer extension;
- if (!parser->ReadVariable(&extension, 2)) {
- return KEEP; // malformed
- }
-
- DataBuffer filtered;
- PacketFilter::Action action = FilterExtension(extension_type, extension,
- &filtered);
- if (action == DROP) {
- changed = true;
- std::cerr << "extension drop: " << extension << std::endl;
- continue;
- }
-
- const DataBuffer* source = &extension;
- if (action == CHANGE) {
- EXPECT_GT(0x10000U, filtered.len());
- changed = true;
- std::cerr << "extension old: " << extension << std::endl;
- std::cerr << "extension new: " << filtered << std::endl;
- source = &filtered;
- }
-
- // Write out extension.
- offset = output->Write(offset, extension_type, 2);
- offset = output->Write(offset, source->len(), 2);
- offset = output->Write(offset, *source);
- }
- output->Truncate(offset);
-
- if (changed) {
- size_t newlen = output->len() - length_offset - 2;
- EXPECT_GT(0x10000U, newlen);
- if (newlen >= 0x10000) {
- return KEEP; // bad: size increased too much
- }
- output->Write(length_offset, newlen, 2);
- return CHANGE;
- }
- return KEEP;
- }
-};
-
class TlsExtensionTruncator : public TlsExtensionFilter {
public:
TlsExtensionTruncator(uint16_t extension, size_t length)
@@ -254,26 +122,6 @@ class TlsExtensionInjector : public TlsHandshakeFilter {
const DataBuffer data_;
};
-class TlsExtensionCapture : public TlsExtensionFilter {
- public:
- TlsExtensionCapture(uint16_t ext)
- : extension_(ext), data_() {}
-
- virtual PacketFilter::Action FilterExtension(
- uint16_t extension_type, const DataBuffer& input, DataBuffer* output) {
- if (extension_type == extension_) {
- data_.Assign(input);
- }
- return KEEP;
- }
-
- const DataBuffer& extension() const { return data_; }
-
- private:
- const uint16_t extension_;
- DataBuffer data_;
-};
-
class TlsExtensionTestBase : public TlsConnectTestBase {
protected:
TlsExtensionTestBase(Mode mode, uint16_t version)
diff --git a/external_tests/ssl_gtest/ssl_loopback_unittest.cc b/external_tests/ssl_gtest/ssl_loopback_unittest.cc
index 37de99b9c..73bf49bcb 100644
--- a/external_tests/ssl_gtest/ssl_loopback_unittest.cc
+++ b/external_tests/ssl_gtest/ssl_loopback_unittest.cc
@@ -161,62 +161,77 @@ TEST_P(TlsConnectGenericPre13, ConnectResumed) {
Connect();
}
-TEST_P(TlsConnectGenericPre13, ConnectClientCacheDisabled) {
+TEST_P(TlsConnectGeneric, ConnectClientCacheDisabled) {
ConfigureSessionCache(RESUME_NONE, RESUME_SESSIONID);
Connect();
+ SendReceive();
+
ResetRsa();
ExpectResumption(RESUME_NONE);
Connect();
+ SendReceive();
}
-TEST_P(TlsConnectGenericPre13, ConnectServerCacheDisabled) {
+TEST_P(TlsConnectGeneric, ConnectServerCacheDisabled) {
ConfigureSessionCache(RESUME_SESSIONID, RESUME_NONE);
Connect();
+ SendReceive();
+
ResetRsa();
ExpectResumption(RESUME_NONE);
Connect();
+ SendReceive();
}
-TEST_P(TlsConnectGenericPre13, ConnectSessionCacheDisabled) {
+TEST_P(TlsConnectGeneric, ConnectSessionCacheDisabled) {
ConfigureSessionCache(RESUME_NONE, RESUME_NONE);
Connect();
+ SendReceive();
+
ResetRsa();
ExpectResumption(RESUME_NONE);
Connect();
+ SendReceive();
}
-TEST_P(TlsConnectGenericPre13, ConnectResumeSupportBoth) {
+TEST_P(TlsConnectGeneric, ConnectResumeSupportBoth) {
// This prefers tickets.
ConfigureSessionCache(RESUME_BOTH, RESUME_BOTH);
Connect();
+ SendReceive();
ResetRsa();
ConfigureSessionCache(RESUME_BOTH, RESUME_BOTH);
ExpectResumption(RESUME_TICKET);
Connect();
+ SendReceive();
}
-TEST_P(TlsConnectGenericPre13, ConnectResumeClientTicketServerBoth) {
+TEST_P(TlsConnectGeneric, ConnectResumeClientTicketServerBoth) {
// This causes no resumption because the client needs the
// session cache to resume even with tickets.
ConfigureSessionCache(RESUME_TICKET, RESUME_BOTH);
Connect();
+ SendReceive();
ResetRsa();
ConfigureSessionCache(RESUME_TICKET, RESUME_BOTH);
ExpectResumption(RESUME_NONE);
Connect();
+ SendReceive();
}
-TEST_P(TlsConnectGenericPre13, ConnectResumeClientBothTicketServerTicket) {
+TEST_P(TlsConnectGeneric, ConnectResumeClientBothTicketServerTicket) {
// This causes a ticket resumption.
ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
Connect();
+ SendReceive();
ResetRsa();
ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
ExpectResumption(RESUME_TICKET);
Connect();
+ SendReceive();
}
TEST_P(TlsConnectGenericPre13, ConnectResumeClientServerTicketOnly) {
@@ -224,31 +239,37 @@ TEST_P(TlsConnectGenericPre13, ConnectResumeClientServerTicketOnly) {
// session cache to resume even with tickets.
ConfigureSessionCache(RESUME_TICKET, RESUME_TICKET);
Connect();
+ SendReceive();
ResetRsa();
ConfigureSessionCache(RESUME_TICKET, RESUME_TICKET);
ExpectResumption(RESUME_NONE);
Connect();
+ SendReceive();
}
TEST_P(TlsConnectGenericPre13, ConnectResumeClientBothServerNone) {
ConfigureSessionCache(RESUME_BOTH, RESUME_NONE);
Connect();
+ SendReceive();
ResetRsa();
ConfigureSessionCache(RESUME_BOTH, RESUME_NONE);
ExpectResumption(RESUME_NONE);
Connect();
+ SendReceive();
}
TEST_P(TlsConnectGenericPre13, ConnectResumeClientNoneServerBoth) {
ConfigureSessionCache(RESUME_NONE, RESUME_BOTH);
Connect();
+ SendReceive();
ResetRsa();
ConfigureSessionCache(RESUME_NONE, RESUME_BOTH);
ExpectResumption(RESUME_NONE);
Connect();
+ SendReceive();
}
TEST_P(TlsConnectGenericPre13, ConnectResumeWithHigherVersion) {
@@ -272,6 +293,20 @@ TEST_P(TlsConnectGenericPre13, ConnectResumeWithHigherVersion) {
Connect();
}
+TEST_P(TlsConnectGeneric, ConnectResumeClientBothTicketServerTicketForget) {
+ // This causes a ticket resumption.
+ ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
+ Connect();
+ SendReceive();
+
+ ResetRsa();
+ ClearServerCache();
+ ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
+ ExpectResumption(RESUME_NONE);
+ Connect();
+ SendReceive();
+}
+
TEST_P(TlsConnectGeneric, ClientAuth) {
client_->SetupClientAuth();
server_->RequestClientAuth(true);
@@ -902,6 +937,54 @@ TEST_F(TlsConnectTest, TestFallbackFromTls13) {
ConnectExpectFail();
ASSERT_EQ(SSL_ERROR_RX_MALFORMED_SERVER_HELLO, client_->error_code());
}
+
+// Test that two TLS resumptions work and produce the same ticket.
+// This will change after bug 1257047 is fixed.
+TEST_F(TlsConnectTest, TestTls13ResumptionTwice) {
+ 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);
+ Connect();
+ SendReceive(); // Need to read so that we absorb the session ticket.
+ CheckKeys(ssl_kea_ecdh, ssl_auth_rsa);
+
+ ResetRsa();
+ ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
+ TlsExtensionCapture *c1 =
+ new TlsExtensionCapture(kTlsExtensionPreSharedKey);
+ 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);
+ ExpectResumption(RESUME_TICKET);
+ Connect();
+ SendReceive();
+ CheckKeys(ssl_kea_ecdh, ssl_auth_rsa);
+ DataBuffer psk1(c1->extension());
+ ASSERT_GE(psk1.len(), 0UL);
+
+ ResetRsa();
+ ClearStats();
+ ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
+ TlsExtensionCapture *c2 =
+ new TlsExtensionCapture(kTlsExtensionPreSharedKey);
+ 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);
+ ExpectResumption(RESUME_TICKET);
+ Connect();
+ SendReceive();
+ CheckKeys(ssl_kea_ecdh, ssl_auth_rsa);
+ DataBuffer psk2(c2->extension());
+ ASSERT_GE(psk2.len(), 0UL);
+
+ // TODO(ekr@rtfm.com): This will change when we fix bug 1257047.
+ ASSERT_EQ(psk1, psk2);
+}
+
#endif
class BeforeFinished : public TlsRecordFilter {
diff --git a/external_tests/ssl_gtest/tls_agent.cc b/external_tests/ssl_gtest/tls_agent.cc
index 1205895fa..00a7d0dc7 100644
--- a/external_tests/ssl_gtest/tls_agent.cc
+++ b/external_tests/ssl_gtest/tls_agent.cc
@@ -418,10 +418,11 @@ void TlsAgent::CheckCallbacks() const {
EXPECT_TRUE(handshake_callback_called_);
}
- // These callbacks shouldn't fire if we are resuming.
+ // These callbacks shouldn't fire if we are resuming, except on TLS 1.3.
if (role_ == SERVER) {
PRBool have_sni = SSLInt_ExtensionNegotiated(ssl_fd_, ssl_server_name_xtn);
- EXPECT_EQ(!expect_resumption_ && have_sni, sni_hook_called_);
+ EXPECT_EQ(((!expect_resumption_ && have_sni) ||
+ expected_version_ >= SSL_LIBRARY_VERSION_TLS_1_3), sni_hook_called_);
} else {
EXPECT_EQ(!expect_resumption_, auth_certificate_hook_called_);
// Note that this isn't unconditionally called, even with false start on.
diff --git a/external_tests/ssl_gtest/tls_agent.h b/external_tests/ssl_gtest/tls_agent.h
index 3a8ad6cb7..1c824d6e6 100644
--- a/external_tests/ssl_gtest/tls_agent.h
+++ b/external_tests/ssl_gtest/tls_agent.h
@@ -238,7 +238,8 @@ class TlsAgent : public PollTarget {
TlsAgent* agent = reinterpret_cast<TlsAgent*>(arg);
agent->CheckPreliminaryInfo();
agent->sni_hook_called_ = true;
- return SSL_SNI_CURRENT_CONFIG_IS_USED;
+ EXPECT_EQ(1UL, srvNameArrSize);
+ return 0; // First configuration.
}
static SECStatus CanFalseStartCallback(PRFileDesc *fd, void *arg,
diff --git a/external_tests/ssl_gtest/tls_connect.cc b/external_tests/ssl_gtest/tls_connect.cc
index a5235f2db..6e37fc312 100644
--- a/external_tests/ssl_gtest/tls_connect.cc
+++ b/external_tests/ssl_gtest/tls_connect.cc
@@ -5,6 +5,9 @@
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "tls_connect.h"
+extern "C" {
+#include "libssl_internals.h"
+}
#include <iostream>
@@ -120,14 +123,22 @@ TlsConnectTestBase::TlsConnectTestBase(Mode mode, uint16_t version)
TlsConnectTestBase::~TlsConnectTestBase() {
}
-void TlsConnectTestBase::SetUp() {
- // Configure a fresh session cache.
- SSL_ConfigServerSessionIDCache(1024, 0, 0, g_working_dir_path.c_str());
-
+void TlsConnectTestBase::ClearStats() {
// Clear statistics.
SSL3Statistics* stats = SSL_GetStatistics();
memset(stats, 0, sizeof(*stats));
+}
+void TlsConnectTestBase::ClearServerCache() {
+ SSL_ShutdownServerSessionIDCache();
+ SSLInt_ClearSessionTicketKey();
+ SSL_ConfigServerSessionIDCache(1024, 0, 0, g_working_dir_path.c_str());
+}
+
+void TlsConnectTestBase::SetUp() {
+ SSL_ConfigServerSessionIDCache(1024, 0, 0, g_working_dir_path.c_str());
+ SSLInt_ClearSessionTicketKey();
+ ClearStats();
Init();
}
@@ -136,6 +147,7 @@ void TlsConnectTestBase::TearDown() {
delete server_;
SSL_ClearSessionCache();
+ SSLInt_ClearSessionTicketKey();
SSL_ShutdownServerSessionIDCache();
}
@@ -298,8 +310,11 @@ void TlsConnectTestBase::CheckResumption(SessionResumptionMode expected) {
EXPECT_EQ(stateless_ct, stats->hch_sid_stateless_resumes);
EXPECT_EQ(stateless_ct, stats->hsh_sid_stateless_resumes);
- if (resume_ct) {
+ if (resume_ct &&
+ client_->version() < SSL_LIBRARY_VERSION_TLS_1_3) {
// Check that the last two session ids match.
+ // TLS 1.3 doesn't do session id-based resumption. It's all
+ // tickets.
EXPECT_EQ(2U, session_ids_.size());
EXPECT_EQ(session_ids_[session_ids_.size()-1],
session_ids_[session_ids_.size()-2]);
diff --git a/external_tests/ssl_gtest/tls_connect.h b/external_tests/ssl_gtest/tls_connect.h
index 97c1cfe7d..ab9a5dc8b 100644
--- a/external_tests/ssl_gtest/tls_connect.h
+++ b/external_tests/ssl_gtest/tls_connect.h
@@ -46,6 +46,10 @@ class TlsConnectTestBase : public ::testing::Test {
// Initialize client and server.
void Init();
+ // Clear the statistics.
+ void ClearStats();
+ // Clear the server session cache.
+ void ClearServerCache();
// Re-initialize client and server with the default RSA cert.
void ResetRsa();
// Re-initialize client and server with an ECDSA cert on the server
diff --git a/external_tests/ssl_gtest/tls_filter.cc b/external_tests/ssl_gtest/tls_filter.cc
index c88a1203f..c1c736f53 100644
--- a/external_tests/ssl_gtest/tls_filter.cc
+++ b/external_tests/ssl_gtest/tls_filter.cc
@@ -5,6 +5,7 @@
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "tls_filter.h"
+#include "sslproto.h"
#include <iostream>
#include "gtest_utils.h"
@@ -271,4 +272,136 @@ PacketFilter::Action ChainedPacketFilter::Filter(const DataBuffer& input,
return changed ? CHANGE : KEEP;
}
+PacketFilter::Action TlsExtensionFilter::FilterHandshake(
+ const HandshakeHeader& header,
+ const DataBuffer& input, DataBuffer* output) {
+ if (header.handshake_type() == kTlsHandshakeClientHello) {
+ TlsParser parser(input);
+ if (!FindClientHelloExtensions(&parser, header)) {
+ return KEEP;
+ }
+ return FilterExtensions(&parser, input, output);
+ }
+ if (header.handshake_type() == kTlsHandshakeServerHello) {
+ TlsParser parser(input);
+ if (!FindServerHelloExtensions(&parser, header.version())) {
+ return KEEP;
+ }
+ return FilterExtensions(&parser, input, output);
+ }
+ return KEEP;
+}
+
+bool TlsExtensionFilter::FindClientHelloExtensions(TlsParser* parser,
+ const Versioned& header) {
+ if (!parser->Skip(2 + 32)) { // version + random
+ return false;
+ }
+ if (!parser->SkipVariable(1)) { // session ID
+ return false;
+ }
+ if (header.is_dtls() && !parser->SkipVariable(1)) { // DTLS cookie
+ return false;
+ }
+ if (!parser->SkipVariable(2)) { // cipher suites
+ return false;
+ }
+ if (!parser->SkipVariable(1)) { // compression methods
+ return false;
+ }
+ return true;
+}
+
+bool TlsExtensionFilter::FindServerHelloExtensions(TlsParser* parser,
+ uint16_t version) {
+ if (!parser->Skip(2 + 32)) { // version + random
+ return false;
+ }
+ if (!parser->SkipVariable(1)) { // session ID
+ return false;
+ }
+ if (!parser->Skip(2)) { // cipher suite
+ return false;
+ }
+ if (NormalizeTlsVersion(version) <= SSL_LIBRARY_VERSION_TLS_1_2) {
+ if (!parser->Skip(1)) { // compression method
+ return false;
+ }
+ }
+ return true;
+}
+
+PacketFilter::Action TlsExtensionFilter::FilterExtensions(
+ TlsParser* parser, const DataBuffer& input, DataBuffer* output) {
+ size_t length_offset = parser->consumed();
+ uint32_t all_extensions;
+ if (!parser->Read(&all_extensions, 2)) {
+ return KEEP; // no extensions, odd but OK
+ }
+ if (all_extensions != parser->remaining()) {
+ return KEEP; // malformed
+ }
+
+ bool changed = false;
+
+ // Write out the start of the message.
+ output->Allocate(input.len());
+ size_t offset = output->Write(0, input.data(), parser->consumed());
+
+ while (parser->remaining()) {
+ uint32_t extension_type;
+ if (!parser->Read(&extension_type, 2)) {
+ return KEEP; // malformed
+ }
+
+ DataBuffer extension;
+ if (!parser->ReadVariable(&extension, 2)) {
+ return KEEP; // malformed
+ }
+
+ DataBuffer filtered;
+ PacketFilter::Action action = FilterExtension(extension_type, extension,
+ &filtered);
+ if (action == DROP) {
+ changed = true;
+ std::cerr << "extension drop: " << extension << std::endl;
+ continue;
+ }
+
+ const DataBuffer* source = &extension;
+ if (action == CHANGE) {
+ EXPECT_GT(0x10000U, filtered.len());
+ changed = true;
+ std::cerr << "extension old: " << extension << std::endl;
+ std::cerr << "extension new: " << filtered << std::endl;
+ source = &filtered;
+ }
+
+ // Write out extension.
+ offset = output->Write(offset, extension_type, 2);
+ offset = output->Write(offset, source->len(), 2);
+ offset = output->Write(offset, *source);
+ }
+ output->Truncate(offset);
+
+ if (changed) {
+ size_t newlen = output->len() - length_offset - 2;
+ EXPECT_GT(0x10000U, newlen);
+ if (newlen >= 0x10000) {
+ return KEEP; // bad: size increased too much
+ }
+ output->Write(length_offset, newlen, 2);
+ return CHANGE;
+ }
+ return KEEP;
+}
+
+PacketFilter::Action TlsExtensionCapture::FilterExtension(
+ uint16_t extension_type, const DataBuffer& input, DataBuffer* output) {
+ if (extension_type == extension_) {
+ data_.Assign(input);
+ }
+ return KEEP;
+}
+
} // namespace nss_test
diff --git a/external_tests/ssl_gtest/tls_filter.h b/external_tests/ssl_gtest/tls_filter.h
index a1cf23765..13413276b 100644
--- a/external_tests/ssl_gtest/tls_filter.h
+++ b/external_tests/ssl_gtest/tls_filter.h
@@ -181,6 +181,41 @@ class ChainedPacketFilter : public PacketFilter {
std::vector<PacketFilter*> filters_;
};
+class TlsExtensionFilter : public TlsHandshakeFilter {
+ protected:
+ virtual PacketFilter::Action FilterHandshake(
+ const HandshakeHeader& header,
+ const DataBuffer& input, DataBuffer* output);
+
+ virtual PacketFilter::Action FilterExtension(uint16_t extension_type,
+ const DataBuffer& input,
+ DataBuffer* output) = 0;
+
+ public:
+ static bool FindClientHelloExtensions(TlsParser* parser,
+ const Versioned& header);
+ static bool FindServerHelloExtensions(TlsParser* parser, uint16_t version);
+
+ private:
+ PacketFilter::Action FilterExtensions(TlsParser* parser,
+ const DataBuffer& input,
+ DataBuffer* output);
+};
+
+class TlsExtensionCapture : public TlsExtensionFilter {
+ public:
+ TlsExtensionCapture(uint16_t ext)
+ : extension_(ext), data_() {}
+
+ virtual PacketFilter::Action FilterExtension(
+ uint16_t extension_type, const DataBuffer& input, DataBuffer* output);
+ const DataBuffer& extension() const { return data_; }
+
+ private:
+ const uint16_t extension_;
+ DataBuffer data_;
+};
+
} // namespace nss_test
#endif
diff --git a/external_tests/ssl_gtest/tls_parser.h b/external_tests/ssl_gtest/tls_parser.h
index da3f3a7ce..370985d97 100644
--- a/external_tests/ssl_gtest/tls_parser.h
+++ b/external_tests/ssl_gtest/tls_parser.h
@@ -42,6 +42,8 @@ const uint8_t kTlsAlertDecodeError = 50;
const uint8_t kTlsAlertUnsupportedExtension = 110;
const uint8_t kTlsAlertNoApplicationProtocol = 120;
+const uint8_t kTlsExtensionPreSharedKey = 41;
+
const uint8_t kTlsFakeChangeCipherSpec[] = {
kTlsChangeCipherSpecType, // Type
0xfe, 0xff, // Version
diff --git a/lib/ssl/SSLerrs.h b/lib/ssl/SSLerrs.h
index 75715d12c..6ae620b61 100644
--- a/lib/ssl/SSLerrs.h
+++ b/lib/ssl/SSLerrs.h
@@ -466,3 +466,7 @@ ER3(SSL_ERROR_EXTENSION_DISALLOWED_FOR_VERSION, (SSL_ERROR_BASE + 145),
ER3(SSL_ERROR_RX_MALFORMED_ENCRYPTED_EXTENSIONS, (SSL_ERROR_BASE + 146),
"SSL received a malformed Encrypted Extensions handshake message.")
+
+ER3(SSL_ERROR_RX_MALFORMED_PRE_SHARED_KEY, (SSL_ERROR_BASE + 147),
+ "SSL received an invalid PreSharedKey extension.")
+
diff --git a/lib/ssl/ssl3con.c b/lib/ssl/ssl3con.c
index 1e368cbdc..fa4e10988 100644
--- a/lib/ssl/ssl3con.c
+++ b/lib/ssl/ssl3con.c
@@ -58,6 +58,14 @@ static SECStatus ssl3_SendNextProto(sslSocket *ss);
static SECStatus ssl3_SendFinished(sslSocket *ss, PRInt32 flags);
static SECStatus ssl3_SendServerHelloDone(sslSocket *ss);
static SECStatus ssl3_SendServerKeyExchange(sslSocket *ss);
+static SECStatus ssl3_HandleClientHelloPart2(sslSocket *ss,
+ SECItem *suites,
+ SECItem *comps,
+ sslSessionID *sid,
+ PRBool canOfferSessionTicket);
+static SECStatus ssl3_HandleServerHelloPart2(sslSocket *ss,
+ const SECItem *sidBytes,
+ int *retErrCode);
static SECStatus ssl3_HandlePostHelloHandshakeMessage(sslSocket *ss,
SSL3Opaque *b,
PRUint32 length,
@@ -90,6 +98,11 @@ static SECStatus ssl3_AESGCMBypass(ssl3KeyMaterial *keys, PRBool doDecrypt,
static ssl3CipherSuiteCfg cipherSuites[ssl_V3_SUITES_IMPLEMENTED] = {
/* cipher_suite policy enabled isPresent */
+ /* ECDHE-PSK from [draft-mattsson-tls-ecdhe-psk-aead]. Only enabled if
+ * we are doing TLS 1.3 PSK-resumption.
+ */
+ { TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256, SSL_ALLOWED, PR_TRUE, PR_FALSE},
+
#ifndef NSS_DISABLE_ECC
{ TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, SSL_ALLOWED, PR_TRUE, PR_FALSE},
{ TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, SSL_ALLOWED, PR_TRUE, PR_FALSE},
@@ -318,6 +331,7 @@ static const ssl3KEADef kea_defs[] =
{kea_ecdh_rsa, kt_ecdh, ssl_sign_rsa, PR_FALSE, 0, PR_FALSE, PR_FALSE, SEC_OID_TLS_ECDH_RSA},
{kea_ecdhe_rsa, kt_ecdh, ssl_sign_rsa, PR_FALSE, 0, PR_FALSE, PR_TRUE, SEC_OID_TLS_ECDHE_RSA},
{kea_ecdh_anon, kt_ecdh, ssl_sign_null, PR_FALSE, 0, PR_FALSE, PR_TRUE, SEC_OID_TLS_ECDH_ANON},
+ {kea_ecdhe_psk, kt_ecdh, ssl_sign_psk, PR_FALSE, 0, PR_FALSE, PR_TRUE, SEC_OID_TLS_ECDHE_PSK}
#endif /* NSS_DISABLE_ECC */
};
@@ -460,6 +474,7 @@ static const ssl3CipherSuiteDef cipher_suite_defs[] =
{TLS_ECDH_anon_WITH_AES_128_CBC_SHA, cipher_aes_128, mac_sha, kea_ecdh_anon},
{TLS_ECDH_anon_WITH_AES_256_CBC_SHA, cipher_aes_256, mac_sha, kea_ecdh_anon},
#endif
+ {TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256, cipher_aes_128_gcm, mac_aead, kea_ecdhe_psk},
#endif /* NSS_DISABLE_ECC */
};
/* clang-format on */
@@ -594,6 +609,9 @@ ssl3_DecodeHandshakeType(int msgType)
case hello_verify_request:
rv = "hello_verify_request (3)";
break;
+ case new_session_ticket:
+ rv = "session_ticket (4)";
+ break;
case encrypted_extensions:
rv = "encrypted_extensions (8)";
break;
@@ -746,6 +764,9 @@ ssl3_CipherSuiteAllowedForVersionRange(
return vrange->max >= SSL_LIBRARY_VERSION_TLS_1_0 &&
vrange->min < SSL_LIBRARY_VERSION_TLS_1_3;
+ case TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256:
+ return vrange->max >= SSL_LIBRARY_VERSION_TLS_1_3;
+
default:
return vrange->min < SSL_LIBRARY_VERSION_TLS_1_3;
}
@@ -753,7 +774,7 @@ ssl3_CipherSuiteAllowedForVersionRange(
/* return pointer to ssl3CipherSuiteDef for suite, or NULL */
/* XXX This does a linear search. A binary search would be better. */
-static const ssl3CipherSuiteDef *
+const ssl3CipherSuiteDef *
ssl_LookupCipherSuiteDef(ssl3CipherSuite suite)
{
int cipher_suite_def_len =
@@ -907,10 +928,20 @@ config_match(ssl3CipherSuiteCfg *suite, int policy, PRBool enabled,
kea_defs[cipher_def->key_exchange_alg].exchKeyType == ssl_kea_dh)
return PR_FALSE;
- return (PRBool)(suite->enabled &&
- suite->isPresent &&
- suite->policy != SSL_NOT_ALLOWED &&
- suite->policy <= policy &&
+ if (!suite->enabled)
+ return PR_FALSE;
+
+ if ((suite->policy == SSL_NOT_ALLOWED) ||
+ (suite->policy > policy))
+ return PR_FALSE;
+
+ /* We only allow PSK for TLS 1.3 and only if there is resumption. */
+ if (kea_defs[cipher_def->key_exchange_alg].signKeyType ==
+ ssl_sign_psk) {
+ return tls13_AllowPskCipher(ss, cipher_def);
+ }
+
+ return (PRBool)(suite->isPresent &&
ssl3_CipherSuiteAllowedForVersionRange(
suite->cipher_suite, vrange));
}
@@ -5543,9 +5574,10 @@ ssl3_SendClientHello(sslSocket *ss, PRBool resending)
}
length = sizeof(SSL3ProtocolVersion) + SSL3_RANDOM_LENGTH +
- 1 + ((sid == NULL) ? 0 : sid->u.ssl3.sessionIDLength) +
- 2 + num_suites * sizeof(ssl3CipherSuite) +
- 1 + numCompressionMethods + total_exten_len;
+ 1 + (((sid == NULL) || sid->version >= SSL_LIBRARY_VERSION_TLS_1_3)
+ ? 0 : sid->u.ssl3.sessionIDLength) +
+ 2 + num_suites * sizeof(ssl3CipherSuite) +
+ 1 + numCompressionMethods + total_exten_len;
if (IS_DTLS(ss)) {
length += 1 + ss->ssl3.hs.cookieLen;
}
@@ -5611,7 +5643,7 @@ ssl3_SendClientHello(sslSocket *ss, PRBool resending)
return rv; /* err set by ssl3_AppendHandshake* */
}
- if (sid)
+ if (sid && sid->version < SSL_LIBRARY_VERSION_TLS_1_3)
rv = ssl3_AppendHandshakeVariable(
ss, sid->u.ssl3.sessionID, sid->u.ssl3.sessionIDLength, 1);
else
@@ -6021,8 +6053,8 @@ ssl_InitSymWrapKeysLock(void)
* If that fails, generate a new one, put the new one on disk,
* Put the new key in the in-memory array.
*/
-static PK11SymKey *
-getWrappingKey(sslSocket *ss,
+PK11SymKey *
+ssl3_GetWrappingKey(sslSocket *ss,
PK11SlotInfo *masterSecretSlot,
SSL3KEAType exchKeyType,
CK_MECHANISM_TYPE masterWrapMech,
@@ -6680,14 +6712,12 @@ done:
static SECStatus
ssl3_HandleServerHello(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
{
- sslSessionID *sid = ss->sec.ci.sid;
PRInt32 temp; /* allow for consume number failure */
PRBool suite_found = PR_FALSE;
int i;
int errCode = SSL_ERROR_RX_MALFORMED_SERVER_HELLO;
SECStatus rv;
SECItem sidBytes = { siBuffer, NULL, 0 };
- PRBool sid_match;
PRBool isTLS = PR_FALSE;
SSL3AlertDescription desc = illegal_parameter;
SSL3ProtocolVersion version;
@@ -6895,6 +6925,42 @@ ssl3_HandleServerHello(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
goto alert_loser;
}
}
+
+ if (ss->version >= SSL_LIBRARY_VERSION_TLS_1_3) {
+ rv = tls13_HandleServerHelloPart2(ss);
+ if (rv != SECSuccess) {
+ errCode = PORT_GetError();
+ goto loser;
+ }
+ } else {
+ rv = ssl3_HandleServerHelloPart2(ss, &sidBytes, &errCode);
+ if (rv != SECSuccess)
+ goto loser;
+ }
+
+ return SECSuccess;
+
+alert_loser:
+ (void)SSL3_SendAlert(ss, alert_fatal, desc);
+
+loser:
+ /* Clean up the temporary pointer to the handshake buffer. */
+ ss->xtnData.signedCertTimestamps.data = NULL;
+ ss->xtnData.signedCertTimestamps.len = 0;
+ ssl_MapLowLevelError(errCode);
+ return SECFailure;
+}
+
+static SECStatus
+ssl3_HandleServerHelloPart2(sslSocket *ss, const SECItem *sidBytes,
+ int *retErrCode)
+{
+ SSL3AlertDescription desc = handshake_failure;
+ int errCode = SSL_ERROR_RX_MALFORMED_SERVER_HELLO;
+ SECStatus rv;
+ PRBool sid_match;
+ sslSessionID *sid = ss->sec.ci.sid;
+
if ((ss->opt.requireSafeNegotiation ||
(ss->firstHsDone && (ss->peerRequestedProtection ||
ss->opt.enableRenegotiation ==
@@ -6921,10 +6987,11 @@ ssl3_HandleServerHello(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
* Attempt to restore the master secret to see if this is so...
* Don't consider failure to find a matching SID an error.
*/
- sid_match = (PRBool)(sidBytes.len > 0 &&
- sidBytes.len ==
+ sid_match = (PRBool)(sidBytes->len > 0 &&
+ sidBytes->len ==
sid->u.ssl3.sessionIDLength &&
- !PORT_Memcmp(sid->u.ssl3.sessionID, sidBytes.data, sidBytes.len));
+ !PORT_Memcmp(sid->u.ssl3.sessionID,
+ sidBytes->data, sidBytes->len));
if (sid_match &&
sid->version == ss->version &&
@@ -7088,8 +7155,8 @@ ssl3_HandleServerHello(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
}
sid->version = ss->version;
- sid->u.ssl3.sessionIDLength = sidBytes.len;
- PORT_Memcpy(sid->u.ssl3.sessionID, sidBytes.data, sidBytes.len);
+ sid->u.ssl3.sessionIDLength = sidBytes->len;
+ PORT_Memcpy(sid->u.ssl3.sessionID, sidBytes->data, sidBytes->len);
sid->u.ssl3.keys.extendedMasterSecretUsed =
ssl3_ExtensionNegotiated(ss, ssl_extended_master_secret_xtn);
@@ -7106,12 +7173,7 @@ ssl3_HandleServerHello(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
}
ss->ssl3.hs.isResuming = PR_FALSE;
- if (ss->version >= SSL_LIBRARY_VERSION_TLS_1_3) {
- rv = tls13_HandleServerKeyShare(ss);
- if (rv != SECSuccess)
- goto alert_loser;
- TLS13_SET_HS_STATE(ss, wait_encrypted_extensions);
- } else if (ss->ssl3.hs.kea_def->signKeyType != ssl_sign_null) {
+ if (ss->ssl3.hs.kea_def->signKeyType != ssl_sign_null) {
/* All current cipher suites other than those with ssl_sign_null (i.e.,
* (EC)DH_anon_* suites) require a certificate, so use that signal. */
ss->ssl3.hs.ws = wait_server_cert;
@@ -7128,10 +7190,7 @@ alert_loser:
(void)SSL3_SendAlert(ss, alert_fatal, desc);
loser:
- /* Clean up the temporary pointer to the handshake buffer. */
- ss->xtnData.signedCertTimestamps.data = NULL;
- ss->xtnData.signedCertTimestamps.len = 0;
- ssl_MapLowLevelError(errCode);
+ *retErrCode = errCode;
return SECFailure;
}
@@ -8222,6 +8281,217 @@ ssl3_KEAAllowsSessionTicket(SSL3KeyExchangeAlgorithm kea)
};
}
+/* Select a cipher suite.
+**
+** NOTE: This suite selection algorithm should be the same as the one in
+** ssl3_HandleV2ClientHello().
+**
+** If TLS 1.0 is enabled, we could handle the case where the client
+** offered TLS 1.1 but offered only export cipher suites by choosing TLS
+** 1.0 and selecting one of those export cipher suites. However, a secure
+** TLS 1.1 client should not have export cipher suites enabled at all,
+** and a TLS 1.1 client should definitely not be offering *only* export
+** cipher suites. Therefore, we refuse to negotiate export cipher suites
+** with any client that indicates support for TLS 1.1 or higher when we
+** (the server) have TLS 1.1 support enabled.
+*/
+SECStatus
+ssl3_NegotiateCipherSuite(sslSocket *ss, const SECItem *suites)
+{
+ ssl3CipherSuiteCfg *chosenSuite = NULL;
+ int j;
+ int i;
+
+ for (j = 0; j < ssl_V3_SUITES_IMPLEMENTED; j++) {
+ ssl3CipherSuiteCfg *suite = &ss->cipherSuites[j];
+ SSLVersionRange vrange = {ss->version, ss->version};
+ if (!config_match(suite, ss->ssl3.policy, PR_TRUE, &vrange, ss)) {
+ continue;
+ }
+ for (i = 0; i + 1 < suites->len; i += 2) {
+ PRUint16 suite_i = (suites->data[i] << 8) | suites->data[i + 1];
+ if (suite_i == suite->cipher_suite) {
+ chosenSuite = suite;
+ ss->ssl3.hs.cipher_suite = chosenSuite->cipher_suite;
+ ss->ssl3.hs.suite_def =
+ ssl_LookupCipherSuiteDef(ss->ssl3.hs.cipher_suite);
+ ss->ssl3.hs.kea_def =
+ &kea_defs[ss->ssl3.hs.suite_def->key_exchange_alg];
+ ss->ssl3.hs.preliminaryInfo |= ssl_preinfo_cipher_suite;
+ return SECSuccess;
+ }
+ }
+ }
+ return SECFailure;
+}
+
+/*
+ * Call the SNI config hook.
+ *
+ * Called from:
+ * ssl3_HandleClientHello
+ * tls13_HandleClientHelloPart2
+ */
+SECStatus
+ssl3_ServerCallSNICallback(sslSocket *ss)
+{
+ int errCode = SSL_ERROR_RX_MALFORMED_CLIENT_HELLO;
+ SSL3AlertDescription desc = illegal_parameter;
+ int ret = 0;
+
+ if (!ssl3_ExtensionNegotiated(ss, ssl_server_name_xtn)) {
+#ifndef SSL_SNI_ALLOW_NAME_CHANGE_2HS
+ if (ss->firstHsDone) {
+ /* Check that we don't have the name is current spec
+ * if this extension was not negotiated on the 2d hs. */
+ PRBool passed = PR_TRUE;
+ ssl_GetSpecReadLock(ss); /*******************************/
+ if (ss->ssl3.cwSpec->srvVirtName.data) {
+ passed = PR_FALSE;
+ }
+ ssl_ReleaseSpecReadLock(ss); /***************************/
+ if (!passed) {
+ errCode = SSL_ERROR_UNRECOGNIZED_NAME_ALERT;
+ desc = handshake_failure;
+ goto alert_loser;
+ }
+ }
+#endif
+ return SECSuccess;
+ }
+
+ if (ss->sniSocketConfig) do { /* not a loop */
+ PORT_Assert((ss->ssl3.hs.preliminaryInfo & ssl_preinfo_all) ==
+ ssl_preinfo_all);
+
+ ret = SSL_SNI_SEND_ALERT;
+ /* If extension is negotiated, the len of names should > 0. */
+ if (ss->xtnData.sniNameArrSize) {
+ /* Calling client callback to reconfigure the socket. */
+ ret = (SECStatus)(*ss->sniSocketConfig)(ss->fd,
+ ss->xtnData.sniNameArr,
+ ss->xtnData.sniNameArrSize,
+ ss->sniSocketConfigArg);
+ }
+ if (ret <= SSL_SNI_SEND_ALERT) {
+ /* Application does not know the name or was not able to
+ * properly reconfigure the socket. */
+ errCode = SSL_ERROR_UNRECOGNIZED_NAME_ALERT;
+ desc = unrecognized_name;
+ break;
+ } else if (ret == SSL_SNI_CURRENT_CONFIG_IS_USED) {
+ SECStatus rv = SECSuccess;
+ SECItem * cwsName, *pwsName;
+
+ ssl_GetSpecWriteLock(ss); /*******************************/
+ pwsName = &ss->ssl3.pwSpec->srvVirtName;
+ cwsName = &ss->ssl3.cwSpec->srvVirtName;
+#ifndef SSL_SNI_ALLOW_NAME_CHANGE_2HS
+ /* not allow name change on the 2d HS */
+ if (ss->firstHsDone) {
+ if (ssl3_ServerNameCompare(pwsName, cwsName)) {
+ ssl_ReleaseSpecWriteLock(ss); /******************/
+ errCode = SSL_ERROR_UNRECOGNIZED_NAME_ALERT;
+ desc = handshake_failure;
+ ret = SSL_SNI_SEND_ALERT;
+ break;
+ }
+ }
+#endif
+ if (pwsName->data) {
+ SECITEM_FreeItem(pwsName, PR_FALSE);
+ }
+ if (cwsName->data) {
+ rv = SECITEM_CopyItem(NULL, pwsName, cwsName);
+ }
+ ssl_ReleaseSpecWriteLock(ss); /**************************/
+ if (rv != SECSuccess) {
+ errCode = SSL_ERROR_INTERNAL_ERROR_ALERT;
+ desc = internal_error;
+ ret = SSL_SNI_SEND_ALERT;
+ break;
+ }
+ } else if ((unsigned int)ret < ss->xtnData.sniNameArrSize) {
+ /* Application has configured new socket info. Lets check it
+ * and save the name. */
+ SECStatus rv;
+ SECItem * name = &ss->xtnData.sniNameArr[ret];
+ int configedCiphers;
+ SECItem * pwsName;
+
+ /* get rid of the old name and save the newly picked. */
+ /* This code is protected by ssl3HandshakeLock. */
+ ssl_GetSpecWriteLock(ss); /*******************************/
+#ifndef SSL_SNI_ALLOW_NAME_CHANGE_2HS
+ /* not allow name change on the 2d HS */
+ if (ss->firstHsDone) {
+ SECItem *cwsName = &ss->ssl3.cwSpec->srvVirtName;
+ if (ssl3_ServerNameCompare(name, cwsName)) {
+ ssl_ReleaseSpecWriteLock(ss); /******************/
+ errCode = SSL_ERROR_UNRECOGNIZED_NAME_ALERT;
+ desc = handshake_failure;
+ ret = SSL_SNI_SEND_ALERT;
+ break;
+ }
+ }
+#endif
+ pwsName = &ss->ssl3.pwSpec->srvVirtName;
+ if (pwsName->data) {
+ SECITEM_FreeItem(pwsName, PR_FALSE);
+ }
+ rv = SECITEM_CopyItem(NULL, pwsName, name);
+ ssl_ReleaseSpecWriteLock(ss); /***************************/
+ if (rv != SECSuccess) {
+ errCode = SSL_ERROR_INTERNAL_ERROR_ALERT;
+ desc = internal_error;
+ ret = SSL_SNI_SEND_ALERT;
+ break;
+ }
+ configedCiphers = ssl3_config_match_init(ss);
+ if (configedCiphers <= 0) {
+ /* no ciphers are working/supported */
+ errCode = PORT_GetError();
+ desc = handshake_failure;
+ ret = SSL_SNI_SEND_ALERT;
+ break;
+ }
+ /* Need to tell the client that application has picked
+ * the name from the offered list and reconfigured the socket.
+ */
+ ssl3_RegisterServerHelloExtensionSender(ss, ssl_server_name_xtn,
+ ssl3_SendServerNameXtn);
+ } else {
+ /* Callback returned index outside of the boundary. */
+ PORT_Assert((unsigned int)ret < ss->xtnData.sniNameArrSize);
+ errCode = SSL_ERROR_INTERNAL_ERROR_ALERT;
+ desc = internal_error;
+ ret = SSL_SNI_SEND_ALERT;
+ break;
+ }
+ } while (0);
+ /* Free sniNameArr. The data that each SECItem in the array
+ * points into is the data from the input buffer "b". It will
+ * not be available outside the scope of this function or
+ * the callers (*HandleClientHelloPart2) and the callers
+ must not use it after this point. */
+ if (ss->xtnData.sniNameArr) {
+ PORT_Free(ss->xtnData.sniNameArr);
+ ss->xtnData.sniNameArr = NULL;
+ ss->xtnData.sniNameArrSize = 0;
+ }
+ if (ret <= SSL_SNI_SEND_ALERT) {
+ /* desc and errCode should be set. */
+ goto alert_loser;
+ }
+
+ return SECSuccess;
+
+alert_loser:
+ (void)SSL3_SendAlert(ss, alert_fatal, desc);
+ PORT_SetError(errCode);
+ return SECFailure;
+}
+
/* Called from ssl3_HandleHandshakeMessage() when it has deciphered a complete
* ssl3 Client Hello message.
* Caller must hold Handshake and RecvBuf locks.
@@ -8232,7 +8502,6 @@ ssl3_HandleClientHello(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
sslSessionID *sid = NULL;
PRInt32 tmp;
unsigned int i;
- int j;
SECStatus rv;
int errCode = SSL_ERROR_RX_MALFORMED_CLIENT_HELLO;
SSL3AlertDescription desc = illegal_parameter;
@@ -8242,10 +8511,8 @@ ssl3_HandleClientHello(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
SECItem cookieBytes = { siBuffer, NULL, 0 };
SECItem suites = { siBuffer, NULL, 0 };
SECItem comps = { siBuffer, NULL, 0 };
- PRBool haveSpecWriteLock = PR_FALSE;
- PRBool haveXmitBufLock = PR_FALSE;
PRBool canOfferSessionTicket = PR_FALSE;
- PRBool isTLS13 = PR_FALSE;
+ PRBool isTLS13;
SSL_TRC(3, ("%d: SSL3[%d]: handle client_hello handshake",
SSL_GETPID(), ss->fd));
@@ -8378,6 +8645,11 @@ ssl3_HandleClientHello(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
goto loser; /* malformed */
}
+ /* You can't resume TLS 1.3 like this, */
+ if (isTLS13 && sidBytes.len) {
+ goto alert_loser;
+ }
+
/* grab the client's cookie, if present. */
if (IS_DTLS(ss)) {
rv = ssl3_ConsumeHandshakeVariable(ss, &cookieBytes, 1, &b, &length);
@@ -8414,7 +8686,7 @@ ssl3_HandleClientHello(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
/* TLS 1.3 requires that compression be empty */
if (isTLS13) {
if (comps.len != 1 || comps.data[0] != ssl_compression_null) {
- goto loser;
+ goto alert_loser;
}
}
@@ -8441,6 +8713,7 @@ ssl3_HandleClientHello(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
goto loser; /* malformed */
}
}
+
if (!ssl3_ExtensionNegotiated(ss, ssl_renegotiation_info_xtn)) {
/* If we didn't receive an RI extension, look for the SCSV,
* and if found, treat it just like an empty RI extension
@@ -8473,13 +8746,15 @@ ssl3_HandleClientHello(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
goto alert_loser;
}
- /* We do stateful resumes only if either of the following
- * conditions are satisfied: (1) the client does not support the
- * session ticket extension, or (2) the client support the session
- * ticket extension, but sent an empty ticket.
+ /* We do stateful resumes only if we are in TLS < 1.3 and
+ * either of the following conditions are satisfied:
+ * (1) the client does not support the session ticket extension, or
+ * (2) the client support the session ticket extension, but sent an
+ * empty ticket.
*/
- if (!ssl3_ExtensionNegotiated(ss, ssl_session_ticket_xtn) ||
- ss->xtnData.emptySessionTicket) {
+ if ((ss->version < SSL_LIBRARY_VERSION_TLS_1_3) &&
+ (!ssl3_ExtensionNegotiated(ss, ssl_session_ticket_xtn) ||
+ ss->xtnData.emptySessionTicket)) {
if (sidBytes.len > 0 && !ss->opt.noCache) {
SSL_TRC(7, ("%d: SSL3[%d]: server, lookup client session-id for 0x%08x%08x%08x%08x",
SSL_GETPID(), ss->fd, ss->sec.ci.peer.pr_s6_addr32[0],
@@ -8497,6 +8772,7 @@ ssl3_HandleClientHello(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
} else if (ss->statelessResume) {
/* Fill in the client's session ID if doing a stateless resume.
* (When doing stateless resumes, server echos client's SessionID.)
+ * This branch also handles TLS 1.3 resumption-PSK.
*/
sid = ss->sec.ci.sid;
PORT_Assert(sid != NULL); /* Should have already been filled in.*/
@@ -8565,6 +8841,39 @@ ssl3_HandleClientHello(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
}
#endif
+ if (ss->version >= SSL_LIBRARY_VERSION_TLS_1_3) {
+ rv = tls13_HandleClientHelloPart2(ss, &suites, sid);
+ } else {
+ rv = ssl3_HandleClientHelloPart2(ss, &suites, &comps, sid,
+ canOfferSessionTicket);
+ }
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+ return SECSuccess;
+
+alert_loser:
+ (void)SSL3_SendAlert(ss, level, desc);
+ /* FALLTHRU */
+loser:
+ PORT_SetError(errCode);
+ return SECFailure;
+}
+
+static SECStatus ssl3_HandleClientHelloPart2(sslSocket *ss,
+ SECItem *suites,
+ SECItem *comps,
+ sslSessionID *sid,
+ PRBool canOfferSessionTicket)
+{
+ PRBool haveSpecWriteLock = PR_FALSE;
+ PRBool haveXmitBufLock = PR_FALSE;
+ int errCode = SSL_ERROR_RX_MALFORMED_CLIENT_HELLO;
+ SSL3AlertDescription desc = illegal_parameter;
+ SECStatus rv;
+ unsigned int i;
+ int j;
+
/* If we already have a session for this client, be sure to pick the
** same cipher suite and compression method we picked before.
** This is not a loop, despite appearances.
@@ -8581,11 +8890,11 @@ ssl3_HandleClientHello(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
break;
/* Check that the cached compression method is in the client's list */
- for (i = 0; i < comps.len; i++) {
- if (comps.data[i] == sid->u.ssl3.compression)
+ for (i = 0; i < comps->len; i++) {
+ if (comps->data[i] == sid->u.ssl3.compression)
break;
}
- if (i == comps.len)
+ if (i == comps->len)
break;
suite = ss->cipherSuites;
@@ -8610,8 +8919,8 @@ ssl3_HandleClientHello(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
break;
#endif
/* Double check that the cached cipher suite is in the client's list */
- for (i = 0; i + 1 < suites.len; i += 2) {
- PRUint16 suite_i = (suites.data[i] << 8) | suites.data[i + 1];
+ for (i = 0; i + 1 < suites->len; i += 2) {
+ PRUint16 suite_i = (suites->data[i] << 8) | suites->data[i + 1];
if (suite_i == suite->cipher_suite) {
ss->ssl3.hs.cipher_suite =
suite->cipher_suite;
@@ -8628,7 +8937,6 @@ ssl3_HandleClientHello(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
}
}
} while (0);
-
/* START A NEW SESSION */
#ifndef PARANOID
@@ -8641,43 +8949,12 @@ ssl3_HandleClientHello(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
}
#endif
- /* Select a cipher suite.
- **
- ** NOTE: This suite selection algorithm should be the same as the one in
- ** ssl3_HandleV2ClientHello().
- **
- ** If TLS 1.0 is enabled, we could handle the case where the client
- ** offered TLS 1.1 but offered only export cipher suites by choosing TLS
- ** 1.0 and selecting one of those export cipher suites. However, a secure
- ** TLS 1.1 client should not have export cipher suites enabled at all,
- ** and a TLS 1.1 client should definitely not be offering *only* export
- ** cipher suites. Therefore, we refuse to negotiate export cipher suites
- ** with any client that indicates support for TLS 1.1 or higher when we
- ** (the server) have TLS 1.1 support enabled.
- */
- for (j = 0; j < ssl_V3_SUITES_IMPLEMENTED; j++) {
- ssl3CipherSuiteCfg *suite = &ss->cipherSuites[j];
- SSLVersionRange vrange = { ss->version, ss->version };
- if (!config_match(suite, ss->ssl3.policy, PR_TRUE, &vrange, ss)) {
- continue;
- }
- for (i = 0; i + 1 < suites.len; i += 2) {
- PRUint16 suite_i = (suites.data[i] << 8) | suites.data[i + 1];
- if (suite_i == suite->cipher_suite) {
- ss->ssl3.hs.cipher_suite = suite->cipher_suite;
- ss->ssl3.hs.suite_def =
- ssl_LookupCipherSuiteDef(ss->ssl3.hs.cipher_suite);
- ss->ssl3.hs.kea_def =
- &kea_defs[ss->ssl3.hs.suite_def->key_exchange_alg];
- ss->ssl3.hs.preliminaryInfo |= ssl_preinfo_cipher_suite;
- goto suite_found;
- }
- }
+ rv = ssl3_NegotiateCipherSuite(ss, suites);
+ if (rv != SECSuccess) {
+ errCode = SSL_ERROR_NO_CYPHER_OVERLAP;
+ goto alert_loser;
}
- errCode = SSL_ERROR_NO_CYPHER_OVERLAP;
- goto alert_loser;
-suite_found:
if (canOfferSessionTicket)
canOfferSessionTicket = ssl3_KEAAllowsSessionTicket(
ss->ssl3.hs.suite_def->key_exchange_alg);
@@ -8688,11 +8965,11 @@ suite_found:
}
/* Select a compression algorithm. */
- for (i = 0; i < comps.len; i++) {
- if (!compressionEnabled(ss, comps.data[i]))
+ for (i = 0; i < comps->len; i++) {
+ if (!compressionEnabled(ss, comps->data[i]))
continue;
for (j = 0; j < compressionMethodsCount; j++) {
- if (comps.data[i] == compressions[j]) {
+ if (comps->data[i] == compressions[j]) {
ss->ssl3.hs.compression =
(SSLCompressionMethod)compressions[j];
goto compression_found;
@@ -8704,8 +8981,8 @@ suite_found:
goto alert_loser;
compression_found:
- suites.data = NULL;
- comps.data = NULL;
+ suites->data = NULL;
+ comps->data = NULL;
/* If there are any failures while processing the old sid,
* we don't consider them to be errors. Instead, We just behave
@@ -8774,9 +9051,9 @@ compression_found:
}
#endif
- wrapKey = getWrappingKey(ss, NULL, sid->u.ssl3.exchKeyType,
- sid->u.ssl3.masterWrapMech,
- ss->pkcs11PinArg);
+ wrapKey = ssl3_GetWrappingKey(ss, NULL, sid->u.ssl3.exchKeyType,
+ sid->u.ssl3.masterWrapMech,
+ ss->pkcs11PinArg);
if (!wrapKey) {
/* we have a SID cache entry, but no wrapping key for it??? */
break;
@@ -8931,159 +9208,11 @@ compression_found:
}
SSL_AtomicIncrementLong(&ssl3stats.hch_sid_cache_misses);
- if (ssl3_ExtensionNegotiated(ss, ssl_server_name_xtn)) {
- int ret = 0;
- if (ss->sniSocketConfig)
- do { /* not a loop */
- PORT_Assert((ss->ssl3.hs.preliminaryInfo & ssl_preinfo_all) ==
- ssl_preinfo_all);
-
- ret = SSL_SNI_SEND_ALERT;
- /* If extension is negotiated, the len of names should > 0. */
- if (ss->xtnData.sniNameArrSize) {
- /* Calling client callback to reconfigure the socket. */
- ret = (SECStatus)(*ss->sniSocketConfig)(ss->fd,
- ss->xtnData.sniNameArr,
- ss->xtnData.sniNameArrSize,
- ss->sniSocketConfigArg);
- }
- if (ret <= SSL_SNI_SEND_ALERT) {
- /* Application does not know the name or was not able to
- * properly reconfigure the socket. */
- errCode = SSL_ERROR_UNRECOGNIZED_NAME_ALERT;
- desc = unrecognized_name;
- break;
- } else if (ret == SSL_SNI_CURRENT_CONFIG_IS_USED) {
- SECStatus rv = SECSuccess;
- SECItem *cwsName, *pwsName;
-
- ssl_GetSpecWriteLock(ss); /*******************************/
- pwsName = &ss->ssl3.pwSpec->srvVirtName;
- cwsName = &ss->ssl3.cwSpec->srvVirtName;
-#ifndef SSL_SNI_ALLOW_NAME_CHANGE_2HS
- /* not allow name change on the 2d HS */
- if (ss->firstHsDone) {
- if (ssl3_ServerNameCompare(pwsName, cwsName)) {
- ssl_ReleaseSpecWriteLock(ss); /******************/
- errCode = SSL_ERROR_UNRECOGNIZED_NAME_ALERT;
- desc = handshake_failure;
- ret = SSL_SNI_SEND_ALERT;
- break;
- }
- }
-#endif
- if (pwsName->data) {
- SECITEM_FreeItem(pwsName, PR_FALSE);
- }
- if (cwsName->data) {
- rv = SECITEM_CopyItem(NULL, pwsName, cwsName);
- }
- ssl_ReleaseSpecWriteLock(ss); /**************************/
- if (rv != SECSuccess) {
- errCode = SSL_ERROR_INTERNAL_ERROR_ALERT;
- desc = internal_error;
- ret = SSL_SNI_SEND_ALERT;
- break;
- }
- } else if ((unsigned int)ret < ss->xtnData.sniNameArrSize) {
- /* Application has configured new socket info. Lets check it
- * and save the name. */
- SECStatus rv;
- SECItem *name = &ss->xtnData.sniNameArr[ret];
- int configedCiphers;
- SECItem *pwsName;
-
- /* get rid of the old name and save the newly picked. */
- /* This code is protected by ssl3HandshakeLock. */
- ssl_GetSpecWriteLock(ss); /*******************************/
-#ifndef SSL_SNI_ALLOW_NAME_CHANGE_2HS
- /* not allow name change on the 2d HS */
- if (ss->firstHsDone) {
- SECItem *cwsName = &ss->ssl3.cwSpec->srvVirtName;
- if (ssl3_ServerNameCompare(name, cwsName)) {
- ssl_ReleaseSpecWriteLock(ss); /******************/
- errCode = SSL_ERROR_UNRECOGNIZED_NAME_ALERT;
- desc = handshake_failure;
- ret = SSL_SNI_SEND_ALERT;
- break;
- }
- }
-#endif
- pwsName = &ss->ssl3.pwSpec->srvVirtName;
- if (pwsName->data) {
- SECITEM_FreeItem(pwsName, PR_FALSE);
- }
- rv = SECITEM_CopyItem(NULL, pwsName, name);
- ssl_ReleaseSpecWriteLock(ss); /***************************/
- if (rv != SECSuccess) {
- errCode = SSL_ERROR_INTERNAL_ERROR_ALERT;
- desc = internal_error;
- ret = SSL_SNI_SEND_ALERT;
- break;
- }
- configedCiphers = ssl3_config_match_init(ss);
- if (configedCiphers <= 0) {
- /* no ciphers are working/supported */
- errCode = PORT_GetError();
- desc = handshake_failure;
- ret = SSL_SNI_SEND_ALERT;
- break;
- }
- /* Need to tell the client that application has picked
- * the name from the offered list and reconfigured the socket.
- */
- ssl3_RegisterServerHelloExtensionSender(ss, ssl_server_name_xtn,
- ssl3_SendServerNameXtn);
- } else {
- /* Callback returned index outside of the boundary. */
- PORT_Assert((unsigned int)ret < ss->xtnData.sniNameArrSize);
- errCode = SSL_ERROR_INTERNAL_ERROR_ALERT;
- desc = internal_error;
- ret = SSL_SNI_SEND_ALERT;
- break;
- }
- } while (0);
- /* Free sniNameArr. The data that each SECItem in the array
- * points into is the data from the input buffer "b". It will
- * not be available outside the scope of this or it's child
- * functions.*/
- if (ss->xtnData.sniNameArr) {
- PORT_Free(ss->xtnData.sniNameArr);
- ss->xtnData.sniNameArr = NULL;
- ss->xtnData.sniNameArrSize = 0;
- }
- if (ret <= SSL_SNI_SEND_ALERT) {
- /* desc and errCode should be set. */
- goto alert_loser;
- }
- }
-#ifndef SSL_SNI_ALLOW_NAME_CHANGE_2HS
- else if (ss->firstHsDone) {
- /* Check that we don't have the name is current spec
- * if this extension was not negotiated on the 2d hs. */
- PRBool passed = PR_TRUE;
- ssl_GetSpecReadLock(ss); /*******************************/
- if (ss->ssl3.cwSpec->srvVirtName.data) {
- passed = PR_FALSE;
- }
- ssl_ReleaseSpecReadLock(ss); /***************************/
- if (!passed) {
- errCode = SSL_ERROR_UNRECOGNIZED_NAME_ALERT;
- desc = handshake_failure;
- goto alert_loser;
- }
- }
-#endif
-
- /* If this is TLS 1.3 we are expecting a ClientKeyShare
- * extension. Missing/absent extension cause failure
- * below. */
- if (isTLS13) {
- rv = tls13_HandleClientKeyShare(ss);
- if (rv != SECSuccess) {
- errCode = PORT_GetError();
- goto alert_loser;
- }
+ rv = ssl3_ServerCallSNICallback(ss);
+ if (rv != SECSuccess) {
+ /* The alert has already been sent. */
+ errCode = PORT_GetError();
+ goto loser;
}
sid = ssl3_NewSessionID(ss, PR_TRUE);
@@ -9098,11 +9227,7 @@ compression_found:
ss->ssl3.hs.isResuming = PR_FALSE;
ssl_GetXmitBufLock(ss);
- if (isTLS13) {
- rv = tls13_SendServerHelloSequence(ss);
- } else {
- rv = ssl3_SendServerHelloSequence(ss);
- }
+ rv = ssl3_SendServerHelloSequence(ss);
ssl_ReleaseXmitBufLock(ss);
if (rv != SECSuccess) {
errCode = PORT_GetError();
@@ -9121,8 +9246,8 @@ alert_loser:
ssl_ReleaseSpecWriteLock(ss);
haveSpecWriteLock = PR_FALSE;
}
- (void)SSL3_SendAlert(ss, level, desc);
-/* FALLTHRU */
+ (void)SSL3_SendAlert(ss, alert_fatal, desc);
+ /* FALLTHRU */
loser:
if (haveSpecWriteLock) {
ssl_ReleaseSpecWriteLock(ss);
@@ -11710,8 +11835,8 @@ ssl3_CacheWrappedMasterSecret(sslSocket *ss, sslSessionID *sid,
mechanism = PK11_GetBestWrapMechanism(symKeySlot);
if (mechanism != CKM_INVALID_MECHANISM) {
wrappingKey =
- getWrappingKey(ss, symKeySlot, effectiveExchKeyType,
- mechanism, pwArg);
+ ssl3_GetWrappingKey(ss, symKeySlot, effectiveExchKeyType,
+ mechanism, pwArg);
if (wrappingKey) {
mechanism = PK11_GetMechanism(wrappingKey); /* can't fail. */
}
@@ -11880,41 +12005,7 @@ xmit_loser:
}
if (sid->cached == never_cached && !ss->opt.noCache && ss->sec.cache) {
- /* fill in the sid */
- sid->u.ssl3.cipherSuite = ss->ssl3.hs.cipher_suite;
- sid->u.ssl3.compression = ss->ssl3.hs.compression;
- sid->u.ssl3.policy = ss->ssl3.policy;
-#ifndef NSS_DISABLE_ECC
- sid->u.ssl3.negotiatedECCurves = ss->ssl3.hs.negotiatedECCurves;
-#endif
- sid->u.ssl3.exchKeyType = effectiveExchKeyType;
- sid->version = ss->version;
- sid->authAlgorithm = ss->sec.authAlgorithm;
- 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->localCert = CERT_DupCertificate(ss->sec.localCert);
-
- ssl_GetSpecReadLock(ss); /*************************************/
-
- /* Copy the master secret (wrapped or unwrapped) into the sid */
- if (ss->ssl3.crSpec->msItem.len && ss->ssl3.crSpec->msItem.data) {
- sid->u.ssl3.keys.wrapped_master_secret_len =
- ss->ssl3.crSpec->msItem.len;
- memcpy(sid->u.ssl3.keys.wrapped_master_secret,
- ss->ssl3.crSpec->msItem.data, ss->ssl3.crSpec->msItem.len);
- sid->u.ssl3.masterValid = PR_TRUE;
- sid->u.ssl3.keys.msIsWrapped = PR_FALSE;
- rv = SECSuccess;
- } else {
- rv = ssl3_CacheWrappedMasterSecret(ss, ss->sec.ci.sid,
- ss->ssl3.crSpec,
- effectiveExchKeyType);
- sid->u.ssl3.keys.msIsWrapped = PR_TRUE;
- }
- ssl_ReleaseSpecReadLock(ss); /*************************************/
+ rv = ssl3_FillInCachedSID(ss, sid, effectiveExchKeyType);
/* If the wrap failed, we don't cache the sid.
* The connection continues normally however.
@@ -11937,6 +12028,53 @@ xmit_loser:
return rv;
}
+SECStatus
+ssl3_FillInCachedSID(sslSocket *ss, sslSessionID *sid,
+ SSL3KEAType effectiveExchKeyType)
+{
+ SECStatus rv;
+
+ /* fill in the sid */
+ sid->u.ssl3.cipherSuite =
+ ss->version >= SSL_LIBRARY_VERSION_TLS_1_3 ?
+ ss->ssl3.hs.origCipherSuite : ss->ssl3.hs.cipher_suite;
+ sid->u.ssl3.compression = ss->ssl3.hs.compression;
+ sid->u.ssl3.policy = ss->ssl3.policy;
+#ifndef NSS_DISABLE_ECC
+ sid->u.ssl3.negotiatedECCurves = ss->ssl3.hs.negotiatedECCurves;
+#endif
+ sid->u.ssl3.exchKeyType = effectiveExchKeyType;
+ sid->version = ss->version;
+ sid->authAlgorithm = ss->sec.authAlgorithm;
+ 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->localCert = CERT_DupCertificate(ss->sec.localCert);
+
+ ssl_GetSpecReadLock(ss); /*************************************/
+
+ /* Copy the master secret (wrapped or unwrapped) into the sid */
+ if (ss->ssl3.crSpec->msItem.len && ss->ssl3.crSpec->msItem.data) {
+ sid->u.ssl3.keys.wrapped_master_secret_len =
+ ss->ssl3.crSpec->msItem.len;
+ memcpy(sid->u.ssl3.keys.wrapped_master_secret,
+ ss->ssl3.crSpec->msItem.data, ss->ssl3.crSpec->msItem.len);
+ sid->u.ssl3.masterValid = PR_TRUE;
+ sid->u.ssl3.keys.msIsWrapped = PR_FALSE;
+ rv = SECSuccess;
+ } else {
+ rv = ssl3_CacheWrappedMasterSecret(ss, ss->sec.ci.sid,
+ ss->ssl3.crSpec,
+ effectiveExchKeyType);
+ sid->u.ssl3.keys.msIsWrapped = PR_TRUE;
+ }
+ ssl_ReleaseSpecReadLock(ss); /*************************************/
+
+ return rv;
+}
+
/* The return type is SECStatus instead of void because this function needs
* to have type sslRestartTarget.
*/
diff --git a/lib/ssl/ssl3ext.c b/lib/ssl/ssl3ext.c
index 600fce2ad..3e847e2bb 100644
--- a/lib/ssl/ssl3ext.c
+++ b/lib/ssl/ssl3ext.c
@@ -116,6 +116,15 @@ static SECStatus tls13_ClientHandleKeyShareXtn(sslSocket *ss,
static SECStatus tls13_ServerHandleKeyShareXtn(sslSocket *ss,
PRUint16 ex_type,
SECItem *data);
+static PRInt32 tls13_ClientSendPreSharedKeyXtn(sslSocket *ss, PRBool append,
+ PRUint32 maxBytes);
+static SECStatus tls13_ServerHandlePreSharedKeyXtn(sslSocket *ss,
+ PRUint16 ex_type,
+ SECItem *data);
+static SECStatus tls13_ClientHandlePreSharedKeyXtn(sslSocket *ss,
+ PRUint16 ex_type,
+ SECItem *data);
+
/*
* Write bytes. Using this function means the SECItem structure
@@ -160,7 +169,7 @@ ssl3_AppendNumberToItem(SECItem *item, PRUint32 num, PRInt32 lenSize)
return rv;
}
-static SECStatus
+SECStatus
ssl3_SessionTicketShutdown(void *appData, void *nssData)
{
if (session_ticket_enc_key_pkcs11) {
@@ -285,6 +294,7 @@ static const ssl3HelloExtensionHandler clientHelloHandlers[] = {
{ ssl_extended_master_secret_xtn, &ssl3_HandleExtendedMasterSecretXtn },
{ ssl_signed_cert_timestamp_xtn, &ssl3_ServerHandleSignedCertTimestampXtn },
{ ssl_tls13_key_share_xtn, &tls13_ServerHandleKeyShareXtn },
+ { ssl_tls13_pre_shared_key_xtn, &tls13_ServerHandlePreSharedKeyXtn },
{ -1, NULL }
};
@@ -302,6 +312,7 @@ static const ssl3HelloExtensionHandler serverHelloHandlersTLS[] = {
{ ssl_extended_master_secret_xtn, &ssl3_HandleExtendedMasterSecretXtn },
{ ssl_signed_cert_timestamp_xtn, &ssl3_ClientHandleSignedCertTimestampXtn },
{ ssl_tls13_key_share_xtn, &tls13_ClientHandleKeyShareXtn },
+ { ssl_tls13_pre_shared_key_xtn, &tls13_ClientHandlePreSharedKeyXtn },
{ -1, NULL }
};
@@ -315,10 +326,6 @@ static const ssl3HelloExtensionHandler serverHelloHandlersSSL3[] = {
* These static tables are for the formatting of client hello extensions.
* The server's table of hello senders is dynamic, in the socket struct,
* and sender functions are registered there.
- * NB: the order of these extensions can have an impact on compatibility. Some
- * servers (e.g. Tomcat) will terminate the connection if the last extension in
- * the client hello is empty (for example, the extended master secret
- * extension, if it were listed last). See bug 1243641.
*/
static const ssl3HelloExtensionSender clientHelloSendersTLS[SSL_MAX_EXTENSIONS] =
{
@@ -338,14 +345,15 @@ static const ssl3HelloExtensionSender clientHelloSendersTLS[SSL_MAX_EXTENSIONS]
{ ssl_tls13_draft_version_xtn, &ssl3_ClientSendDraftVersionXtn },
{ ssl_signed_cert_timestamp_xtn, &ssl3_ClientSendSignedCertTimestampXtn },
{ ssl_tls13_key_share_xtn, &tls13_ClientSendKeyShareXtn },
+ { ssl_tls13_pre_shared_key_xtn, &tls13_ClientSendPreSharedKeyXtn }
/* any extra entries will appear as { 0, NULL } */
};
-static const ssl3HelloExtensionSender clientHelloSendersSSL3[SSL_MAX_EXTENSIONS] =
- {
- { ssl_renegotiation_info_xtn, &ssl3_SendRenegotiationInfoXtn }
- /* any extra entries will appear as { 0, NULL } */
- };
+static const
+ssl3HelloExtensionSender clientHelloSendersSSL3[SSL_MAX_EXTENSIONS] = {
+ { ssl_renegotiation_info_xtn, &ssl3_SendRenegotiationInfoXtn }
+ /* any extra entries will appear as { 0, NULL } */
+};
static PRBool
arrayContainsExtension(const PRUint16 *array, PRUint32 len, PRUint16 ex_type)
@@ -366,7 +374,7 @@ ssl3_ExtensionNegotiated(sslSocket *ss, PRUint16 ex_type)
xtnData->numNegotiated, ex_type);
}
-static PRBool
+PRBool
ssl3_ClientExtensionAdvertised(sslSocket *ss, PRUint16 ex_type)
{
TLSExtensionData *xtnData = &ss->xtnData;
@@ -548,6 +556,13 @@ ssl3_SendSessionTicketXtn(
NewSessionTicket *session_ticket = NULL;
sslSessionID *sid = ss->sec.ci.sid;
+ /* Never send an extension with a ticket for TLS 1.3, but
+ * OK to send the empty one in case the server does 1.2. */
+ if (sid->cached == in_client_cache &&
+ sid->version >= SSL_LIBRARY_VERSION_TLS_1_3) {
+ return 0;
+ }
+
/* Ignore the SessionTicket extension if processing is disabled. */
if (!ss->opt.enableSessionTickets)
return 0;
@@ -1147,6 +1162,8 @@ ssl3_SendNewSessionTicket(sslSocket *ss)
PRUint32 srvNameLen = 0;
CK_MECHANISM_TYPE msWrapMech = 0; /* dummy default value,
* must be >= 0 */
+ ssl3CipherSpec *spec = ss->version >= SSL_LIBRARY_VERSION_TLS_1_3 ?
+ ss->ssl3.cwSpec : ss->ssl3.pwSpec;
SSL_TRC(3, ("%d: SSL3[%d]: send session_ticket handshake",
SSL_GETPID(), ss->fd));
@@ -1179,10 +1196,10 @@ ssl3_SendNewSessionTicket(sslSocket *ss)
if (rv != SECSuccess)
goto loser;
- if (ss->ssl3.pwSpec->msItem.len && ss->ssl3.pwSpec->msItem.data) {
+ if (spec->msItem.len && spec->msItem.data) {
/* The master secret is available unwrapped. */
- ms_item.data = ss->ssl3.pwSpec->msItem.data;
- ms_item.len = ss->ssl3.pwSpec->msItem.len;
+ ms_item.data = spec->msItem.data;
+ ms_item.len = spec->msItem.len;
ms_is_wrapped = PR_FALSE;
} else {
/* Extract the master secret wrapped. */
@@ -1196,7 +1213,7 @@ ssl3_SendNewSessionTicket(sslSocket *ss)
effectiveExchKeyType = ss->ssl3.hs.kea_def->exchKeyType;
}
- rv = ssl3_CacheWrappedMasterSecret(ss, &sid, ss->ssl3.pwSpec,
+ rv = ssl3_CacheWrappedMasterSecret(ss, &sid, spec,
effectiveExchKeyType);
if (rv == SECSuccess) {
if (sid.u.ssl3.keys.wrapped_master_secret_len > sizeof(wrapped_ms))
@@ -1213,7 +1230,7 @@ ssl3_SendNewSessionTicket(sslSocket *ss)
ms_is_wrapped = PR_TRUE;
}
/* Prep to send negotiated name */
- srvName = &ss->ssl3.pwSpec->srvVirtName;
+ srvName = &ss->sec.ci.sid->u.ssl3.srvName;
if (srvName->data && srvName->len) {
srvNameLen = 2 + srvName->len; /* len bytes + name len */
}
@@ -1373,6 +1390,7 @@ ssl3_SendNewSessionTicket(sslSocket *ss)
#ifndef NO_PKCS11_BYPASS
if (ss->opt.bypassPKCS11) {
PORT_Assert(aes_key);
+
aes_ctx = (AESContext *)aes_ctx_buf;
rv = AES_InitContext(aes_ctx, aes_key, aes_key_length, iv,
NSS_AES_CBC, 1, AES_BLOCK_SIZE);
@@ -1387,7 +1405,6 @@ ssl3_SendNewSessionTicket(sslSocket *ss)
} else
#endif
{
- PORT_Assert(aes_key_pkcs11);
aes_ctx_pkcs11 = PK11_CreateContextBySymKey(cipherMech,
CKA_ENCRYPT, aes_key_pkcs11, &ivItem);
if (!aes_ctx_pkcs11)
@@ -1409,8 +1426,6 @@ ssl3_SendNewSessionTicket(sslSocket *ss)
/* Compute MAC. */
#ifndef NO_PKCS11_BYPASS
if (ss->opt.bypassPKCS11) {
- PORT_Assert(mac_key);
-
hmac_ctx = (HMACContext *)hmac_ctx_buf;
hashObj = HASH_GetRawHashObject(HASH_AlgSHA256);
if (HMAC_Init(hmac_ctx, hashObj, mac_key,
@@ -1428,7 +1443,6 @@ ssl3_SendNewSessionTicket(sslSocket *ss)
#endif
{
SECItem macParam;
- PORT_Assert(mac_key_pkcs11);
macParam.data = NULL;
macParam.len = 0;
hmac_ctx_pkcs11 = PK11_CreateContextBySymKey(macMech,
@@ -1517,441 +1531,428 @@ ssl3_ClientHandleSessionTicketXtn(sslSocket *ss, PRUint16 ex_type,
return SECSuccess;
}
-SECStatus
-ssl3_ServerHandleSessionTicketXtn(sslSocket *ss, PRUint16 ex_type,
- SECItem *data)
+/* Generic ticket processing code, common to TLS 1.0-1.2 and
+ * TLS 1.3. */
+static SECStatus
+ssl3_ProcessSessionTicketCommon(sslSocket *ss, SECItem *data)
{
SECStatus rv;
SECItem *decrypted_state = NULL;
SessionTicket *parsed_session_ticket = NULL;
sslSessionID *sid = NULL;
SSL3Statistics *ssl3stats;
-
- /* Ignore the SessionTicket extension if processing is disabled. */
- if (!ss->opt.enableSessionTickets) {
- return SECSuccess;
+ PRUint32 i;
+ SECItem extension_data;
+ EncryptedSessionTicket enc_session_ticket;
+ unsigned char computed_mac[TLS_EX_SESS_TICKET_MAC_LENGTH];
+ unsigned int computed_mac_length;
+#ifndef NO_PKCS11_BYPASS
+ const SECHashObject *hashObj;
+ const unsigned char *aes_key = NULL;
+ const unsigned char *mac_key = NULL;
+ PRUint32 aes_key_length;
+ PRUint32 mac_key_length;
+ PRUint64 hmac_ctx_buf[MAX_MAC_CONTEXT_LLONGS];
+ HMACContext *hmac_ctx;
+ PRUint64 aes_ctx_buf[MAX_CIPHER_CONTEXT_LLONGS];
+ AESContext *aes_ctx;
+#endif
+ PK11SymKey *aes_key_pkcs11 = NULL;
+ PK11SymKey *mac_key_pkcs11 = NULL;
+ PK11Context *hmac_ctx_pkcs11;
+ CK_MECHANISM_TYPE macMech = CKM_SHA256_HMAC;
+ PK11Context *aes_ctx_pkcs11;
+ CK_MECHANISM_TYPE cipherMech = CKM_AES_CBC;
+ unsigned char *padding;
+ PRUint32 padding_length;
+ unsigned char *buffer;
+ unsigned int buffer_len;
+ PRInt32 temp;
+ SECItem cert_item;
+ PRInt8 nameType = TLS_STE_NO_SERVER_NAME;
+
+ /* Turn off stateless session resumption if the client sends a
+ * SessionTicket extension, even if the extension turns out to be
+ * malformed (ss->sec.ci.sid is non-NULL when doing session
+ * renegotiation.)
+ */
+ if (ss->sec.ci.sid != NULL) {
+ if (ss->sec.uncache)
+ ss->sec.uncache(ss->sec.ci.sid);
+ ssl_FreeSID(ss->sec.ci.sid);
+ ss->sec.ci.sid = NULL;
}
- /* Keep track of negotiated extensions. */
- ss->xtnData.negotiated[ss->xtnData.numNegotiated++] = ex_type;
+ extension_data.data = data->data; /* Keep a copy for future use. */
+ extension_data.len = data->len;
- /* Parse the received ticket sent in by the client. We are
- * lenient about some parse errors, falling back to a fullshake
- * instead of terminating the current connection.
- */
- if (data->len == 0) {
- ss->xtnData.emptySessionTicket = PR_TRUE;
- } else {
- PRUint32 i;
- SECItem extension_data;
- EncryptedSessionTicket enc_session_ticket;
- unsigned char computed_mac[TLS_EX_SESS_TICKET_MAC_LENGTH];
- unsigned int computed_mac_length;
+ if (ssl3_ParseEncryptedSessionTicket(ss, data, &enc_session_ticket) !=
+ SECSuccess) {
+ return SECSuccess; /* Pretend it isn't there */
+ }
+
+ /* Get session ticket keys. */
#ifndef NO_PKCS11_BYPASS
- const SECHashObject *hashObj;
- const unsigned char *aes_key = NULL;
- const unsigned char *mac_key = NULL;
- PRUint32 aes_key_length;
- PRUint32 mac_key_length;
- PRUint64 hmac_ctx_buf[MAX_MAC_CONTEXT_LLONGS];
- HMACContext *hmac_ctx;
- PRUint64 aes_ctx_buf[MAX_CIPHER_CONTEXT_LLONGS];
- AESContext *aes_ctx;
+ if (ss->opt.bypassPKCS11) {
+ rv = ssl3_GetSessionTicketKeys(&aes_key, &aes_key_length,
+ &mac_key, &mac_key_length);
+ } else
#endif
- PK11SymKey *aes_key_pkcs11 = NULL;
- PK11SymKey *mac_key_pkcs11 = NULL;
- PK11Context *hmac_ctx_pkcs11;
- CK_MECHANISM_TYPE macMech = CKM_SHA256_HMAC;
- PK11Context *aes_ctx_pkcs11;
- CK_MECHANISM_TYPE cipherMech = CKM_AES_CBC;
- unsigned char *padding;
- PRUint32 padding_length;
- unsigned char *buffer;
- unsigned int buffer_len;
- PRInt32 temp;
- SECItem cert_item;
- PRInt8 nameType = TLS_STE_NO_SERVER_NAME;
-
- /* Turn off stateless session resumption if the client sends a
- * SessionTicket extension, even if the extension turns out to be
- * malformed (ss->sec.ci.sid is non-NULL when doing session
- * renegotiation.)
- */
- if (ss->sec.ci.sid != NULL) {
- if (ss->sec.uncache)
- ss->sec.uncache(ss->sec.ci.sid);
- ssl_FreeSID(ss->sec.ci.sid);
- ss->sec.ci.sid = NULL;
- }
-
- extension_data.data = data->data; /* Keep a copy for future use. */
- extension_data.len = data->len;
+ {
+ rv = ssl3_GetSessionTicketKeysPKCS11(ss, &aes_key_pkcs11,
+ &mac_key_pkcs11);
+ }
+ if (rv != SECSuccess) {
+ SSL_DBG(("%d: SSL[%d]: Unable to get/generate session ticket keys.",
+ SSL_GETPID(), ss->fd));
+ goto loser;
+ }
- if (ssl3_ParseEncryptedSessionTicket(ss, data, &enc_session_ticket) !=
- SECSuccess) {
- return SECSuccess; /* Pretend it isn't there */
- }
+ /* If the ticket sent by the client was generated under a key different
+ * from the one we have, bypass ticket processing.
+ */
+ if (PORT_Memcmp(enc_session_ticket.key_name, key_name,
+ SESS_TICKET_KEY_NAME_LEN) != 0) {
+ SSL_DBG(("%d: SSL[%d]: Session ticket key_name sent mismatch.",
+ SSL_GETPID(), ss->fd));
+ goto no_ticket;
+ }
-/* Get session ticket keys. */
+ /* Verify the MAC on the ticket. MAC verification may also
+ * fail if the MAC key has been recently refreshed.
+ */
#ifndef NO_PKCS11_BYPASS
- if (ss->opt.bypassPKCS11) {
- rv = ssl3_GetSessionTicketKeys(&aes_key, &aes_key_length,
- &mac_key, &mac_key_length);
- } else
+ if (ss->opt.bypassPKCS11) {
+ PORT_Assert(mac_key);
+ hmac_ctx = (HMACContext *)hmac_ctx_buf;
+ hashObj = HASH_GetRawHashObject(HASH_AlgSHA256);
+ if (HMAC_Init(hmac_ctx, hashObj, mac_key,
+ sizeof(session_ticket_mac_key), PR_FALSE) != SECSuccess)
+ goto no_ticket;
+ HMAC_Begin(hmac_ctx);
+ HMAC_Update(hmac_ctx, extension_data.data,
+ extension_data.len - TLS_EX_SESS_TICKET_MAC_LENGTH);
+ if (HMAC_Finish(hmac_ctx, computed_mac, &computed_mac_length,
+ sizeof(computed_mac)) != SECSuccess)
+ goto no_ticket;
+ } else
#endif
- {
- rv = ssl3_GetSessionTicketKeysPKCS11(ss, &aes_key_pkcs11,
- &mac_key_pkcs11);
- }
- if (rv != SECSuccess) {
- SSL_DBG(("%d: SSL[%d]: Unable to get/generate session ticket keys.",
+ {
+ SECItem macParam;
+ PORT_Assert(mac_key_pkcs11);
+ macParam.data = NULL;
+ macParam.len = 0;
+ hmac_ctx_pkcs11 = PK11_CreateContextBySymKey(macMech,
+ CKA_SIGN, mac_key_pkcs11, &macParam);
+ if (!hmac_ctx_pkcs11) {
+ SSL_DBG(("%d: SSL[%d]: Unable to create HMAC context: %d.",
+ SSL_GETPID(), ss->fd, PORT_GetError()));
+ goto no_ticket;
+ } else {
+ SSL_DBG(("%d: SSL[%d]: Successfully created HMAC context.",
SSL_GETPID(), ss->fd));
- goto loser;
}
-
- /* If the ticket sent by the client was generated under a key different
- * from the one we have, bypass ticket processing.
- */
- if (PORT_Memcmp(enc_session_ticket.key_name, key_name,
- SESS_TICKET_KEY_NAME_LEN) != 0) {
- SSL_DBG(("%d: SSL[%d]: Session ticket key_name sent mismatch.",
- SSL_GETPID(), ss->fd));
+ rv = PK11_DigestBegin(hmac_ctx_pkcs11);
+ if (rv != SECSuccess) {
+ PK11_DestroyContext(hmac_ctx_pkcs11, PR_TRUE);
goto no_ticket;
}
-
- /* Verify the MAC on the ticket. MAC verification may also
- * fail if the MAC key has been recently refreshed.
- */
-#ifndef NO_PKCS11_BYPASS
- if (ss->opt.bypassPKCS11) {
- PORT_Assert(mac_key);
- hmac_ctx = (HMACContext *)hmac_ctx_buf;
- hashObj = HASH_GetRawHashObject(HASH_AlgSHA256);
- if (HMAC_Init(hmac_ctx, hashObj, mac_key,
- sizeof(session_ticket_mac_key), PR_FALSE) != SECSuccess)
- goto no_ticket;
- HMAC_Begin(hmac_ctx);
- HMAC_Update(hmac_ctx, extension_data.data,
- extension_data.len - TLS_EX_SESS_TICKET_MAC_LENGTH);
- if (HMAC_Finish(hmac_ctx, computed_mac, &computed_mac_length,
- sizeof(computed_mac)) != SECSuccess)
- goto no_ticket;
- } else
-#endif
- {
- SECItem macParam;
- PORT_Assert(mac_key_pkcs11);
- macParam.data = NULL;
- macParam.len = 0;
- hmac_ctx_pkcs11 = PK11_CreateContextBySymKey(macMech,
- CKA_SIGN, mac_key_pkcs11, &macParam);
- if (!hmac_ctx_pkcs11) {
- SSL_DBG(("%d: SSL[%d]: Unable to create HMAC context: %d.",
- SSL_GETPID(), ss->fd, PORT_GetError()));
- goto no_ticket;
- } else {
- SSL_DBG(("%d: SSL[%d]: Successfully created HMAC context.",
- SSL_GETPID(), ss->fd));
- }
- rv = PK11_DigestBegin(hmac_ctx_pkcs11);
- if (rv != SECSuccess) {
- PK11_DestroyContext(hmac_ctx_pkcs11, PR_TRUE);
- goto no_ticket;
- }
- rv = PK11_DigestOp(hmac_ctx_pkcs11, extension_data.data,
- extension_data.len -
- TLS_EX_SESS_TICKET_MAC_LENGTH);
- if (rv != SECSuccess) {
- PK11_DestroyContext(hmac_ctx_pkcs11, PR_TRUE);
- goto no_ticket;
- }
- rv = PK11_DigestFinal(hmac_ctx_pkcs11, computed_mac,
- &computed_mac_length, sizeof(computed_mac));
+ rv = PK11_DigestOp(hmac_ctx_pkcs11, extension_data.data,
+ extension_data.len -
+ TLS_EX_SESS_TICKET_MAC_LENGTH);
+ if (rv != SECSuccess) {
PK11_DestroyContext(hmac_ctx_pkcs11, PR_TRUE);
- if (rv != SECSuccess)
- goto no_ticket;
- }
- if (NSS_SecureMemcmp(computed_mac, enc_session_ticket.mac,
- computed_mac_length) !=
- 0) {
- SSL_DBG(("%d: SSL[%d]: Session ticket MAC mismatch.",
- SSL_GETPID(), ss->fd));
goto no_ticket;
}
+ rv = PK11_DigestFinal(hmac_ctx_pkcs11, computed_mac,
+ &computed_mac_length, sizeof(computed_mac));
+ PK11_DestroyContext(hmac_ctx_pkcs11, PR_TRUE);
+ if (rv != SECSuccess)
+ goto no_ticket;
+ }
+ if (NSS_SecureMemcmp(computed_mac, enc_session_ticket.mac,
+ computed_mac_length) !=
+ 0) {
+ SSL_DBG(("%d: SSL[%d]: Session ticket MAC mismatch.",
+ SSL_GETPID(), ss->fd));
+ goto no_ticket;
+ }
- /* We ignore key_name for now.
- * This is ok as MAC verification succeeded.
- */
+ /* We ignore key_name for now.
+ * This is ok as MAC verification succeeded.
+ */
- /* Decrypt the ticket. */
+ /* Decrypt the ticket. */
- /* Plaintext is shorter than the ciphertext due to padding. */
- decrypted_state = SECITEM_AllocItem(NULL, NULL,
- enc_session_ticket.encrypted_state.len);
+ /* Plaintext is shorter than the ciphertext due to padding. */
+ decrypted_state = SECITEM_AllocItem(NULL, NULL,
+ enc_session_ticket.encrypted_state.len);
#ifndef NO_PKCS11_BYPASS
- if (ss->opt.bypassPKCS11) {
- PORT_Assert(aes_key);
- aes_ctx = (AESContext *)aes_ctx_buf;
- rv = AES_InitContext(aes_ctx, aes_key,
- sizeof(session_ticket_enc_key), enc_session_ticket.iv,
- NSS_AES_CBC, 0, AES_BLOCK_SIZE);
- if (rv != SECSuccess) {
- SSL_DBG(("%d: SSL[%d]: Unable to create AES context.",
- SSL_GETPID(), ss->fd));
- goto no_ticket;
- }
-
- rv = AES_Decrypt(aes_ctx, decrypted_state->data,
- &decrypted_state->len, decrypted_state->len,
- enc_session_ticket.encrypted_state.data,
- enc_session_ticket.encrypted_state.len);
- if (rv != SECSuccess)
- goto no_ticket;
- } else
-#endif
- {
- SECItem ivItem;
- PORT_Assert(aes_key_pkcs11);
- ivItem.data = enc_session_ticket.iv;
- ivItem.len = AES_BLOCK_SIZE;
- aes_ctx_pkcs11 = PK11_CreateContextBySymKey(cipherMech,
- CKA_DECRYPT, aes_key_pkcs11, &ivItem);
- if (!aes_ctx_pkcs11) {
- SSL_DBG(("%d: SSL[%d]: Unable to create AES context.",
- SSL_GETPID(), ss->fd));
- goto no_ticket;
- }
-
- rv = PK11_CipherOp(aes_ctx_pkcs11, decrypted_state->data,
- (int *)&decrypted_state->len, decrypted_state->len,
- enc_session_ticket.encrypted_state.data,
- enc_session_ticket.encrypted_state.len);
- PK11_Finalize(aes_ctx_pkcs11);
- PK11_DestroyContext(aes_ctx_pkcs11, PR_TRUE);
- if (rv != SECSuccess)
- goto no_ticket;
- }
-
- /* Check padding. */
- padding_length =
- (PRUint32)decrypted_state->data[decrypted_state->len - 1];
- if (padding_length == 0 || padding_length > AES_BLOCK_SIZE)
+ if (ss->opt.bypassPKCS11) {
+ PORT_Assert(aes_key);
+ aes_ctx = (AESContext *)aes_ctx_buf;
+ rv = AES_InitContext(aes_ctx, aes_key,
+ sizeof(session_ticket_enc_key), enc_session_ticket.iv,
+ NSS_AES_CBC, 0, AES_BLOCK_SIZE);
+ if (rv != SECSuccess) {
+ SSL_DBG(("%d: SSL[%d]: Unable to create AES context.",
+ SSL_GETPID(), ss->fd));
goto no_ticket;
-
- padding = &decrypted_state->data[decrypted_state->len - padding_length];
- for (i = 0; i < padding_length; i++, padding++) {
- if (padding_length != (PRUint32)*padding)
- goto no_ticket;
- }
-
- /* Deserialize session state. */
- buffer = decrypted_state->data;
- buffer_len = decrypted_state->len;
-
- parsed_session_ticket = PORT_ZAlloc(sizeof(SessionTicket));
- if (parsed_session_ticket == NULL) {
- rv = SECFailure;
- goto loser;
}
- /* Read ticket_version and reject if the version is wrong */
- temp = ssl3_ConsumeHandshakeNumber(ss, 2, &buffer, &buffer_len);
- if (temp != TLS_EX_SESS_TICKET_VERSION)
- goto no_ticket;
-
- parsed_session_ticket->ticket_version = (SSL3ProtocolVersion)temp;
-
- /* Read SSLVersion. */
- temp = ssl3_ConsumeHandshakeNumber(ss, 2, &buffer, &buffer_len);
- if (temp < 0)
- goto no_ticket;
- parsed_session_ticket->ssl_version = (SSL3ProtocolVersion)temp;
-
- /* Read cipher_suite. */
- temp = ssl3_ConsumeHandshakeNumber(ss, 2, &buffer, &buffer_len);
- if (temp < 0)
- goto no_ticket;
- parsed_session_ticket->cipher_suite = (ssl3CipherSuite)temp;
-
- /* Read compression_method. */
- temp = ssl3_ConsumeHandshakeNumber(ss, 1, &buffer, &buffer_len);
- if (temp < 0)
- goto no_ticket;
- parsed_session_ticket->compression_method = (SSLCompressionMethod)temp;
-
- /* Read cipher spec parameters. */
- temp = ssl3_ConsumeHandshakeNumber(ss, 1, &buffer, &buffer_len);
- if (temp < 0)
- goto no_ticket;
- parsed_session_ticket->authAlgorithm = (SSLSignType)temp;
- temp = ssl3_ConsumeHandshakeNumber(ss, 4, &buffer, &buffer_len);
- if (temp < 0)
- goto no_ticket;
- parsed_session_ticket->authKeyBits = (PRUint32)temp;
- temp = ssl3_ConsumeHandshakeNumber(ss, 1, &buffer, &buffer_len);
- if (temp < 0)
+ rv = AES_Decrypt(aes_ctx, decrypted_state->data,
+ &decrypted_state->len, decrypted_state->len,
+ enc_session_ticket.encrypted_state.data,
+ enc_session_ticket.encrypted_state.len);
+ if (rv != SECSuccess)
goto no_ticket;
- parsed_session_ticket->keaType = (SSLKEAType)temp;
- temp = ssl3_ConsumeHandshakeNumber(ss, 4, &buffer, &buffer_len);
- if (temp < 0)
+ } else
+#endif
+ {
+ SECItem ivItem;
+ PORT_Assert(aes_key_pkcs11);
+ ivItem.data = enc_session_ticket.iv;
+ ivItem.len = AES_BLOCK_SIZE;
+ aes_ctx_pkcs11 = PK11_CreateContextBySymKey(cipherMech,
+ CKA_DECRYPT, aes_key_pkcs11, &ivItem);
+ if (!aes_ctx_pkcs11) {
+ SSL_DBG(("%d: SSL[%d]: Unable to create AES context.",
+ SSL_GETPID(), ss->fd));
goto no_ticket;
- parsed_session_ticket->keaKeyBits = (PRUint32)temp;
+ }
- /* Read wrapped master_secret. */
- temp = ssl3_ConsumeHandshakeNumber(ss, 1, &buffer, &buffer_len);
- if (temp < 0)
+ rv = PK11_CipherOp(aes_ctx_pkcs11, decrypted_state->data,
+ (int *)&decrypted_state->len, decrypted_state->len,
+ enc_session_ticket.encrypted_state.data,
+ enc_session_ticket.encrypted_state.len);
+ PK11_Finalize(aes_ctx_pkcs11);
+ PK11_DestroyContext(aes_ctx_pkcs11, PR_TRUE);
+ if (rv != SECSuccess)
goto no_ticket;
- parsed_session_ticket->ms_is_wrapped = (PRBool)temp;
+ }
- temp = ssl3_ConsumeHandshakeNumber(ss, 1, &buffer, &buffer_len);
- if (temp < 0)
- goto no_ticket;
- parsed_session_ticket->exchKeyType = (SSL3KEAType)temp;
+ /* Check padding. */
+ padding_length =
+ (PRUint32)decrypted_state->data[decrypted_state->len - 1];
+ if (padding_length == 0 || padding_length > AES_BLOCK_SIZE)
+ goto no_ticket;
- temp = ssl3_ConsumeHandshakeNumber(ss, 4, &buffer, &buffer_len);
- if (temp < 0)
+ padding = &decrypted_state->data[decrypted_state->len - padding_length];
+ for (i = 0; i < padding_length; i++, padding++) {
+ if (padding_length != (PRUint32)*padding)
goto no_ticket;
- parsed_session_ticket->msWrapMech = (CK_MECHANISM_TYPE)temp;
+ }
- temp = ssl3_ConsumeHandshakeNumber(ss, 2, &buffer, &buffer_len);
- if (temp < 0)
- goto no_ticket;
- parsed_session_ticket->ms_length = (PRUint16)temp;
- if (parsed_session_ticket->ms_length == 0 || /* sanity check MS. */
- parsed_session_ticket->ms_length >
- sizeof(parsed_session_ticket->master_secret))
- goto no_ticket;
+ /* Deserialize session state. */
+ buffer = decrypted_state->data;
+ buffer_len = decrypted_state->len;
- /* Allow for the wrapped master secret to be longer. */
- if (buffer_len < parsed_session_ticket->ms_length)
- goto no_ticket;
- PORT_Memcpy(parsed_session_ticket->master_secret, buffer,
- parsed_session_ticket->ms_length);
- buffer += parsed_session_ticket->ms_length;
- buffer_len -= parsed_session_ticket->ms_length;
+ parsed_session_ticket = PORT_ZAlloc(sizeof(SessionTicket));
+ if (parsed_session_ticket == NULL) {
+ rv = SECFailure;
+ goto loser;
+ }
- /* Read client_identity */
- temp = ssl3_ConsumeHandshakeNumber(ss, 1, &buffer, &buffer_len);
- if (temp < 0)
- goto no_ticket;
- parsed_session_ticket->client_identity.client_auth_type =
+ /* Read ticket_version and reject if the version is wrong */
+ temp = ssl3_ConsumeHandshakeNumber(ss, 2, &buffer, &buffer_len);
+ if (temp != TLS_EX_SESS_TICKET_VERSION)
+ goto no_ticket;
+
+ parsed_session_ticket->ticket_version = (SSL3ProtocolVersion)temp;
+
+ /* Read SSLVersion. */
+ temp = ssl3_ConsumeHandshakeNumber(ss, 2, &buffer, &buffer_len);
+ if (temp < 0)
+ goto no_ticket;
+ parsed_session_ticket->ssl_version = (SSL3ProtocolVersion)temp;
+
+ /* Read cipher_suite. */
+ temp = ssl3_ConsumeHandshakeNumber(ss, 2, &buffer, &buffer_len);
+ if (temp < 0)
+ goto no_ticket;
+ parsed_session_ticket->cipher_suite = (ssl3CipherSuite)temp;
+
+ /* Read compression_method. */
+ temp = ssl3_ConsumeHandshakeNumber(ss, 1, &buffer, &buffer_len);
+ if (temp < 0)
+ goto no_ticket;
+ parsed_session_ticket->compression_method = (SSLCompressionMethod)temp;
+
+ /* Read cipher spec parameters. */
+ temp = ssl3_ConsumeHandshakeNumber(ss, 1, &buffer, &buffer_len);
+ if (temp < 0)
+ goto no_ticket;
+ parsed_session_ticket->authAlgorithm = (SSLSignType)temp;
+ temp = ssl3_ConsumeHandshakeNumber(ss, 4, &buffer, &buffer_len);
+ if (temp < 0)
+ goto no_ticket;
+ parsed_session_ticket->authKeyBits = (PRUint32)temp;
+ temp = ssl3_ConsumeHandshakeNumber(ss, 1, &buffer, &buffer_len);
+ if (temp < 0)
+ goto no_ticket;
+ parsed_session_ticket->keaType = (SSLKEAType)temp;
+ temp = ssl3_ConsumeHandshakeNumber(ss, 4, &buffer, &buffer_len);
+ if (temp < 0)
+ goto no_ticket;
+ parsed_session_ticket->keaKeyBits = (PRUint32)temp;
+
+ /* Read wrapped master_secret. */
+ temp = ssl3_ConsumeHandshakeNumber(ss, 1, &buffer, &buffer_len);
+ if (temp < 0)
+ goto no_ticket;
+ parsed_session_ticket->ms_is_wrapped = (PRBool)temp;
+
+ temp = ssl3_ConsumeHandshakeNumber(ss, 1, &buffer, &buffer_len);
+ if (temp < 0)
+ goto no_ticket;
+ parsed_session_ticket->exchKeyType = (SSL3KEAType)temp;
+
+ temp = ssl3_ConsumeHandshakeNumber(ss, 4, &buffer, &buffer_len);
+ if (temp < 0)
+ goto no_ticket;
+ parsed_session_ticket->msWrapMech = (CK_MECHANISM_TYPE)temp;
+
+ temp = ssl3_ConsumeHandshakeNumber(ss, 2, &buffer, &buffer_len);
+ if (temp < 0)
+ goto no_ticket;
+ parsed_session_ticket->ms_length = (PRUint16)temp;
+ if (parsed_session_ticket->ms_length == 0 || /* sanity check MS. */
+ parsed_session_ticket->ms_length >
+ sizeof(parsed_session_ticket->master_secret))
+ goto no_ticket;
+
+ /* Allow for the wrapped master secret to be longer. */
+ if (buffer_len < parsed_session_ticket->ms_length)
+ goto no_ticket;
+ PORT_Memcpy(parsed_session_ticket->master_secret, buffer,
+ parsed_session_ticket->ms_length);
+ buffer += parsed_session_ticket->ms_length;
+ buffer_len -= parsed_session_ticket->ms_length;
+
+ /* Read client_identity */
+ temp = ssl3_ConsumeHandshakeNumber(ss, 1, &buffer, &buffer_len);
+ if (temp < 0)
+ goto no_ticket;
+ parsed_session_ticket->client_identity.client_auth_type =
(ClientAuthenticationType)temp;
- switch (parsed_session_ticket->client_identity.client_auth_type) {
- case CLIENT_AUTH_ANONYMOUS:
- break;
- case CLIENT_AUTH_CERTIFICATE:
- rv = ssl3_ConsumeHandshakeVariable(ss, &cert_item, 3,
- &buffer, &buffer_len);
- if (rv != SECSuccess)
- goto no_ticket;
- rv = SECITEM_CopyItem(NULL, &parsed_session_ticket->peer_cert,
- &cert_item);
- if (rv != SECSuccess)
- goto no_ticket;
- break;
- default:
- goto no_ticket;
- }
- /* Read timestamp. */
- temp = ssl3_ConsumeHandshakeNumber(ss, 4, &buffer, &buffer_len);
- if (temp < 0)
- goto no_ticket;
- parsed_session_ticket->timestamp = (PRUint32)temp;
-
- /* Read server name */
- nameType =
- ssl3_ConsumeHandshakeNumber(ss, 1, &buffer, &buffer_len);
- if (nameType != TLS_STE_NO_SERVER_NAME) {
- SECItem name_item;
- rv = ssl3_ConsumeHandshakeVariable(ss, &name_item, 2, &buffer,
- &buffer_len);
+ switch (parsed_session_ticket->client_identity.client_auth_type) {
+ case CLIENT_AUTH_ANONYMOUS:
+ break;
+ case CLIENT_AUTH_CERTIFICATE:
+ rv = ssl3_ConsumeHandshakeVariable(ss, &cert_item, 3,
+ &buffer, &buffer_len);
if (rv != SECSuccess)
goto no_ticket;
- rv = SECITEM_CopyItem(NULL, &parsed_session_ticket->srvName,
- &name_item);
+ rv = SECITEM_CopyItem(NULL, &parsed_session_ticket->peer_cert,
+ &cert_item);
if (rv != SECSuccess)
goto no_ticket;
- parsed_session_ticket->srvName.type = nameType;
- }
-
- /* Read extendedMasterSecretUsed */
- temp = ssl3_ConsumeHandshakeNumber(ss, 1, &buffer, &buffer_len);
- if (temp < 0)
+ break;
+ default:
goto no_ticket;
- PORT_Assert(temp == PR_TRUE || temp == PR_FALSE);
- parsed_session_ticket->extendedMasterSecretUsed = (PRBool)temp;
+ }
+ /* Read timestamp. */
+ temp = ssl3_ConsumeHandshakeNumber(ss, 4, &buffer, &buffer_len);
+ if (temp < 0)
+ goto no_ticket;
+ parsed_session_ticket->timestamp = (PRUint32)temp;
- /* Done parsing. Check that all bytes have been consumed. */
- if (buffer_len != padding_length)
+ /* Read server name */
+ nameType =
+ ssl3_ConsumeHandshakeNumber(ss, 1, &buffer, &buffer_len);
+ if (nameType != TLS_STE_NO_SERVER_NAME) {
+ SECItem name_item;
+ rv = ssl3_ConsumeHandshakeVariable(ss, &name_item, 2, &buffer,
+ &buffer_len);
+ if (rv != SECSuccess)
+ goto no_ticket;
+ rv = SECITEM_CopyItem(NULL, &parsed_session_ticket->srvName,
+ &name_item);
+ if (rv != SECSuccess)
goto no_ticket;
+ parsed_session_ticket->srvName.type = nameType;
+ }
- /* Use the ticket if it has not expired, otherwise free the allocated
- * memory since the ticket is of no use.
- */
- if (parsed_session_ticket->timestamp != 0 &&
- parsed_session_ticket->timestamp +
- TLS_EX_SESS_TICKET_LIFETIME_HINT >
- ssl_Time()) {
+ /* Read extendedMasterSecretUsed */
+ temp = ssl3_ConsumeHandshakeNumber(ss, 1, &buffer, &buffer_len);
+ if (temp < 0)
+ goto no_ticket;
+ PORT_Assert(temp == PR_TRUE || temp == PR_FALSE);
+ parsed_session_ticket->extendedMasterSecretUsed = (PRBool)temp;
- sid = ssl3_NewSessionID(ss, PR_TRUE);
- if (sid == NULL) {
- rv = SECFailure;
- goto loser;
- }
+ /* Done parsing. Check that all bytes have been consumed. */
+ if (buffer_len != padding_length)
+ goto no_ticket;
+
+ /* Use the ticket if it has not expired, otherwise free the allocated
+ * memory since the ticket is of no use.
+ */
+ if (parsed_session_ticket->timestamp != 0 &&
+ parsed_session_ticket->timestamp +
+ TLS_EX_SESS_TICKET_LIFETIME_HINT >
+ ssl_Time()) {
- /* Copy over parameters. */
- sid->version = parsed_session_ticket->ssl_version;
- sid->u.ssl3.cipherSuite = parsed_session_ticket->cipher_suite;
- sid->u.ssl3.compression = parsed_session_ticket->compression_method;
- sid->authAlgorithm = parsed_session_ticket->authAlgorithm;
- sid->authKeyBits = parsed_session_ticket->authKeyBits;
- sid->keaType = parsed_session_ticket->keaType;
- sid->keaKeyBits = parsed_session_ticket->keaKeyBits;
+ sid = ssl3_NewSessionID(ss, PR_TRUE);
+ if (sid == NULL) {
+ rv = SECFailure;
+ goto loser;
+ }
-/* Copy master secret. */
+ /* Copy over parameters. */
+ sid->version = parsed_session_ticket->ssl_version;
+ sid->u.ssl3.cipherSuite = parsed_session_ticket->cipher_suite;
+ sid->u.ssl3.compression = parsed_session_ticket->compression_method;
+ sid->authAlgorithm = parsed_session_ticket->authAlgorithm;
+ sid->authKeyBits = parsed_session_ticket->authKeyBits;
+ sid->keaType = parsed_session_ticket->keaType;
+ sid->keaKeyBits = parsed_session_ticket->keaKeyBits;
+ if (SECITEM_CopyItem(NULL, &sid->u.ssl3.locked.sessionTicket.ticket,
+ &extension_data) != SECSuccess)
+ goto no_ticket;
+
+ /* Copy master secret. */
#ifndef NO_PKCS11_BYPASS
- if (ss->opt.bypassPKCS11 &&
- parsed_session_ticket->ms_is_wrapped)
- goto no_ticket;
+ if (ss->opt.bypassPKCS11 &&
+ parsed_session_ticket->ms_is_wrapped)
+ goto no_ticket;
#endif
- if (parsed_session_ticket->ms_length >
- sizeof(sid->u.ssl3.keys.wrapped_master_secret))
- goto no_ticket;
- PORT_Memcpy(sid->u.ssl3.keys.wrapped_master_secret,
- parsed_session_ticket->master_secret,
- parsed_session_ticket->ms_length);
- sid->u.ssl3.keys.wrapped_master_secret_len =
+ if (parsed_session_ticket->ms_length >
+ sizeof(sid->u.ssl3.keys.wrapped_master_secret))
+ goto no_ticket;
+ PORT_Memcpy(sid->u.ssl3.keys.wrapped_master_secret,
+ parsed_session_ticket->master_secret,
+ parsed_session_ticket->ms_length);
+ sid->u.ssl3.keys.wrapped_master_secret_len =
parsed_session_ticket->ms_length;
- sid->u.ssl3.exchKeyType = parsed_session_ticket->exchKeyType;
- sid->u.ssl3.masterWrapMech = parsed_session_ticket->msWrapMech;
- sid->u.ssl3.keys.msIsWrapped =
+ sid->u.ssl3.exchKeyType = parsed_session_ticket->exchKeyType;
+ sid->u.ssl3.masterWrapMech = parsed_session_ticket->msWrapMech;
+ sid->u.ssl3.keys.msIsWrapped =
parsed_session_ticket->ms_is_wrapped;
- sid->u.ssl3.masterValid = PR_TRUE;
- sid->u.ssl3.keys.resumable = PR_TRUE;
- sid->u.ssl3.keys.extendedMasterSecretUsed = parsed_session_ticket->extendedMasterSecretUsed;
-
- /* Copy over client cert from session ticket if there is one. */
- if (parsed_session_ticket->peer_cert.data != NULL) {
- if (sid->peerCert != NULL)
- CERT_DestroyCertificate(sid->peerCert);
- sid->peerCert = CERT_NewTempCertificate(ss->dbHandle,
- &parsed_session_ticket->peer_cert, NULL, PR_FALSE, PR_TRUE);
- if (sid->peerCert == NULL) {
- rv = SECFailure;
- goto loser;
- }
- }
- if (parsed_session_ticket->srvName.data != NULL) {
- sid->u.ssl3.srvName = parsed_session_ticket->srvName;
+ sid->u.ssl3.masterValid = PR_TRUE;
+ sid->u.ssl3.keys.resumable = PR_TRUE;
+ sid->u.ssl3.keys.extendedMasterSecretUsed = parsed_session_ticket->extendedMasterSecretUsed;
+
+ /* Copy over client cert from session ticket if there is one. */
+ if (parsed_session_ticket->peer_cert.data != NULL) {
+ if (sid->peerCert != NULL)
+ CERT_DestroyCertificate(sid->peerCert);
+ sid->peerCert = CERT_NewTempCertificate(ss->dbHandle,
+ &parsed_session_ticket->peer_cert, NULL, PR_FALSE, PR_TRUE);
+ if (sid->peerCert == NULL) {
+ rv = SECFailure;
+ goto loser;
}
- ss->statelessResume = PR_TRUE;
- ss->sec.ci.sid = sid;
}
+ if (parsed_session_ticket->srvName.data != NULL) {
+ sid->u.ssl3.srvName = parsed_session_ticket->srvName;
+ }
+ ss->statelessResume = PR_TRUE;
+ ss->sec.ci.sid = sid;
}
if (0) {
- no_ticket:
+ no_ticket:
SSL_DBG(("%d: SSL[%d]: Session ticket parsing failed.",
SSL_GETPID(), ss->fd));
ssl3stats = SSL_GetStatistics();
@@ -1982,6 +1983,36 @@ loser:
return rv;
}
+SECStatus
+ssl3_ServerHandleSessionTicketXtn(sslSocket *ss, PRUint16 ex_type,
+ SECItem *data)
+{
+
+ /* Ignore the SessionTicket extension if processing is disabled. */
+ if (!ss->opt.enableSessionTickets) {
+ return SECSuccess;
+ }
+
+ /* If we are doing TLS 1.3, then ignore this. */
+ if (ss->version >= SSL_LIBRARY_VERSION_TLS_1_3) {
+ return SECSuccess;
+ }
+
+ /* Keep track of negotiated extensions. */
+ ss->xtnData.negotiated[ss->xtnData.numNegotiated++] = ex_type;
+
+ /* Parse the received ticket sent in by the client. We are
+ * lenient about some parse errors, falling back to a fullshake
+ * instead of terminating the current connection.
+ */
+ if (data->len == 0) {
+ ss->xtnData.emptySessionTicket = PR_TRUE;
+ return SECSuccess;
+ }
+
+ return ssl3_ProcessSessionTicketCommon(ss, data);
+}
+
/*
* Read bytes. Using this function means the SECItem structure
* cannot be freed. The caller is expected to call this function
@@ -3240,3 +3271,220 @@ tls13_ServerSendKeyShareXtn(sslSocket *ss, PRBool append,
loser:
return -1;
}
+
+/* Called by clients.
+ *
+ * opaque psk_identity<0..2^16-1>;
+ *
+ * struct {
+ * select (Role) {
+ * case client:
+ * psk_identity identities<2..2^16-1>;
+ *
+ * case server:
+ * psk_identity identity;
+ * }
+ * } PreSharedKeyExtension;
+ *
+ * Presently the only way to get a PSK is by resumption, so this is
+ * really a ticket label and there wll be at most one.
+ */
+static PRInt32
+tls13_ClientSendPreSharedKeyXtn(sslSocket * ss,
+ PRBool append,
+ PRUint32 maxBytes)
+{
+ PRInt32 extension_length;
+ NewSessionTicket *session_ticket = NULL;
+ sslSessionID *sid = ss->sec.ci.sid;
+
+ if (sid->cached == never_cached ||
+ sid->version < SSL_LIBRARY_VERSION_TLS_1_3) {
+ return 0;
+ }
+
+ /* The caller must be holding sid->u.ssl3.lock for reading. We cannot
+ * just acquire and release the lock within this function because the
+ * caller will call this function twice, and we need the inputs to be
+ * consistent between the two calls. Note that currently the caller
+ * will only be holding the lock when we are the client and when we're
+ * attempting to resume an existing session.
+ */
+ session_ticket = &sid->u.ssl3.locked.sessionTicket;
+ PORT_Assert(session_ticket && session_ticket->ticket.data);
+
+ /* In our first pass through, set the ticket to be verified if
+ * it is still valid. */
+ if (!append && (session_ticket->ticket_lifetime_hint == 0 ||
+ (session_ticket->ticket_lifetime_hint +
+ session_ticket->received_timestamp > ssl_Time()))) {
+ ss->xtnData.ticketTimestampVerified = PR_TRUE;
+ }
+
+ /* Ticket out of date so don't send PSK. */
+ if (!ss->xtnData.ticketTimestampVerified) {
+ return 0;
+ }
+
+ /* Type + length + vector length + identity length + ticket. */
+ extension_length = 2 + 2 + 2 + 2 +
+ session_ticket->ticket.len;
+
+ if (maxBytes < (PRUint32)extension_length) {
+ PORT_Assert(0);
+ return 0;
+ }
+
+ if (append) {
+ SECStatus rv;
+ /* extension_type */
+ rv = ssl3_AppendHandshakeNumber(ss, ssl_tls13_pre_shared_key_xtn, 2);
+ if (rv != SECSuccess)
+ goto loser;
+ rv = ssl3_AppendHandshakeNumber(ss, extension_length - 4, 2);
+ if (rv != SECSuccess)
+ goto loser;
+ rv = ssl3_AppendHandshakeNumber(ss, session_ticket->ticket.len + 2, 2);
+ if (rv != SECSuccess)
+ goto loser;
+ rv = ssl3_AppendHandshakeVariable(ss, session_ticket->ticket.data,
+ session_ticket->ticket.len, 2);
+ PRINT_BUF(50, (ss, "Sending PreSharedKey value",
+ session_ticket->ticket.data,
+ session_ticket->ticket.len));
+ ss->xtnData.ticketTimestampVerified = PR_FALSE;
+ ss->xtnData.sentSessionTicketInClientHello = PR_TRUE;
+ if (rv != SECSuccess)
+ goto loser;
+
+ ss->xtnData.advertised[ss->xtnData.numAdvertised++] =
+ ssl_tls13_pre_shared_key_xtn;
+ }
+ return extension_length;
+
+ loser:
+ ss->xtnData.ticketTimestampVerified = PR_FALSE;
+ return -1;
+}
+
+/* Handle a TLS 1.3 PreSharedKey Extension. We only accept PSKs
+ * that contain session tickets. */
+static SECStatus
+tls13_ServerHandlePreSharedKeyXtn(sslSocket *ss, PRUint16 ex_type,
+ SECItem *data)
+{
+ SECItem label;
+ PRInt32 len;
+ PRBool first = PR_TRUE;
+ SECStatus rv;
+
+ /* If we are doing < TLS 1.3, then ignore this. */
+ if (ss->version < SSL_LIBRARY_VERSION_TLS_1_3) {
+ return SECSuccess;
+ }
+
+ len = ssl3_ConsumeHandshakeNumber(ss, 2, &data->data, &data->len);
+ if (len < 0)
+ return SECFailure;
+
+ if (len != data->len) {
+ PORT_SetError(SSL_ERROR_MALFORMED_PRE_SHARED_KEY);
+ return SECFailure;
+ }
+
+ while (data->len) {
+ rv = ssl3_ConsumeHandshakeVariable(ss, &label, 2,
+ &data->data, &data->len);
+ if (rv != SECSuccess)
+ return rv;
+
+ if (first) {
+ first = PR_FALSE; /* Continue to read through the extension to check
+ * the format. */
+
+ PRINT_BUF(50, (ss, "Handling PreSharedKey value",
+ label.data, label.len));
+
+ rv = ssl3_ProcessSessionTicketCommon(ss, &label);
+ /* This only happens if we have an internal error, not
+ * a malformed ticket. Bogus tickets just don't resume
+ * and return SECSuccess. */
+ if (rv != SECSuccess)
+ return rv;
+ }
+ }
+
+ /* Keep track of negotiated extensions. Note that this does not
+ * mean we are resuming. */
+ ss->xtnData.negotiated[ss->xtnData.numNegotiated++] = ex_type;
+
+ return SECSuccess;
+}
+
+PRInt32
+tls13_ServerSendPreSharedKeyXtn(sslSocket * ss,
+ PRBool append,
+ PRUint32 maxBytes)
+{
+ SECItem *session_ticket =
+ &ss->sec.ci.sid->u.ssl3.locked.sessionTicket.ticket;
+ PRInt32 extension_length =
+ 2 + 2 + 2 + session_ticket->len; /* type + len +
+ inner_len + data */
+ SECStatus rv;
+
+ PORT_Assert(session_ticket->len);
+
+ if (append) {
+ rv = ssl3_AppendHandshakeNumber(ss, ssl_tls13_pre_shared_key_xtn, 2);
+ if (rv != SECSuccess)
+ return -1;
+
+ rv = ssl3_AppendHandshakeNumber(ss, session_ticket->len + 2, 2);
+ if (rv != SECSuccess)
+ return -1;
+
+ rv = ssl3_AppendHandshakeVariable(ss, session_ticket->data,
+ session_ticket->len, 2);
+ if (rv != SECSuccess)
+ return -1;
+ }
+
+ return extension_length;
+}
+
+/* Handle a TLS 1.3 PreSharedKey Extension. We only accept PSKs
+ * that contain session tickets. */
+static SECStatus
+tls13_ClientHandlePreSharedKeyXtn(sslSocket *ss, PRUint16 ex_type,
+ SECItem *data)
+{
+ PRInt32 len;
+
+ /* If we are doing < TLS 1.3, then ignore this. */
+ if (ss->version < SSL_LIBRARY_VERSION_TLS_1_3) {
+ return SECSuccess;
+ }
+
+ len = ssl3_ConsumeHandshakeNumber(ss, 2, &data->data, &data->len);
+ if (len < 0)
+ return SECFailure;
+
+ if (len != data->len) {
+ PORT_SetError(SSL_ERROR_MALFORMED_PRE_SHARED_KEY);
+ return SECFailure;
+ }
+
+ /* Just check for equality since we only sent one PSK label. */
+ if (SECITEM_CompareItem(
+ &ss->sec.ci.sid->u.ssl3.locked.sessionTicket.ticket,
+ data) != SECEqual) {
+ PORT_SetError(SSL_ERROR_MALFORMED_PRE_SHARED_KEY);
+ return SECFailure;
+ }
+
+ /* Keep track of negotiated extensions. */
+ ss->xtnData.negotiated[ss->xtnData.numNegotiated++] = ex_type;
+
+ return SECSuccess;
+}
diff --git a/lib/ssl/ssl3prot.h b/lib/ssl/ssl3prot.h
index e637d11ff..73a175aae 100644
--- a/lib/ssl/ssl3prot.h
+++ b/lib/ssl/ssl3prot.h
@@ -200,7 +200,8 @@ typedef enum {
kea_ecdhe_ecdsa,
kea_ecdh_rsa,
kea_ecdhe_rsa,
- kea_ecdh_anon
+ kea_ecdh_anon,
+ kea_ecdhe_psk,
} SSL3KeyExchangeAlgorithm;
typedef struct {
diff --git a/lib/ssl/sslenum.c b/lib/ssl/sslenum.c
index e9f526202..d080a14da 100644
--- a/lib/ssl/sslenum.c
+++ b/lib/ssl/sslenum.c
@@ -34,6 +34,8 @@
* algorithm: GCM, then HMAC-SHA1, then HMAC-SHA256, then HMAC-MD5.
* * Within message authentication algorithm sections, order by asymmetric
* signature algorithm: ECDSA, then RSA, then DSS.
+ * * As a special case, the PSK ciphers, which are only enabled when
+ * TLS 1.3 PSK-resumption is in use, come first.
*
* Exception: Because some servers ignore the high-order byte of the cipher
* suite ID, we must be careful about adding cipher suites with IDs larger
@@ -47,6 +49,10 @@
* the third one.
*/
const PRUint16 SSL_ImplementedCiphers[] = {
+ /* ECDHE-PSK from [draft-mattsson-tls-ecdhe-psk-aead]. */
+ TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256,
+
+
#ifndef NSS_DISABLE_ECC
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
diff --git a/lib/ssl/sslerr.h b/lib/ssl/sslerr.h
index 37d4d3b02..f7e3ebec0 100644
--- a/lib/ssl/sslerr.h
+++ b/lib/ssl/sslerr.h
@@ -230,6 +230,7 @@ typedef enum {
SSL_ERROR_KEY_EXCHANGE_FAILURE = (SSL_ERROR_BASE + 144),
SSL_ERROR_EXTENSION_DISALLOWED_FOR_VERSION = (SSL_ERROR_BASE + 145),
SSL_ERROR_RX_MALFORMED_ENCRYPTED_EXTENSIONS = (SSL_ERROR_BASE + 146),
+ SSL_ERROR_MALFORMED_PRE_SHARED_KEY = (SSL_ERROR_BASE + 147),
SSL_ERROR_END_OF_LIST /* let the c compiler determine the value of this. */
} SSLErrorCodes;
#endif /* NO_SECURITY_ERROR_ENUM */
diff --git a/lib/ssl/sslimpl.h b/lib/ssl/sslimpl.h
index 94c209dd4..f04f41391 100644
--- a/lib/ssl/sslimpl.h
+++ b/lib/ssl/sslimpl.h
@@ -276,7 +276,7 @@ typedef struct {
} ssl3CipherSuiteCfg;
#ifndef NSS_DISABLE_ECC
-#define ssl_V3_SUITES_IMPLEMENTED 67
+#define ssl_V3_SUITES_IMPLEMENTED 68
#else
#define ssl_V3_SUITES_IMPLEMENTED 41
#endif /* NSS_DISABLE_ECC */
@@ -949,6 +949,8 @@ typedef struct SSL3HandshakeStateStr {
* to Certificate */
PRUint8 certReqContextLen; /* Length of the context
* cannot be greater than 255. */
+ ssl3CipherSuite origCipherSuite; /* The cipher suite from the original
+ * connection if we are resuming. */
} SSL3HandshakeState;
/*
@@ -1813,6 +1815,7 @@ extern PRBool ssl_GetSessionTicketKeysPKCS11(SECKEYPrivateKey *svrPrivKey,
SECKEYPublicKey *svrPubKey, void *pwArg,
unsigned char *keyName, PK11SymKey **aesKey,
PK11SymKey **macKey);
+extern SECStatus ssl3_SessionTicketShutdown(void *appData, void *nssData);
/* Tell clients to consider tickets valid for this long. */
#define TLS_EX_SESS_TICKET_LIFETIME_HINT (2 * 24 * 60 * 60) /* 2 days */
@@ -1905,6 +1908,8 @@ extern SECStatus dtls_MaybeRetransmitHandshake(sslSocket *ss,
const SSL3Ciphertext *cText);
CK_MECHANISM_TYPE ssl3_Alg2Mech(SSLCipherAlgorithm calg);
+SECStatus ssl3_NegotiateCipherSuite(sslSocket *ss, const SECItem *suites);
+SECStatus ssl3_ServerCallSNICallback(sslSocket *ss);
SECStatus ssl3_SetupPendingCipherSpec(sslSocket *ss);
SECStatus ssl3_FlushHandshake(sslSocket *ss, PRInt32 flags);
SECStatus ssl3_SendCertificate(sslSocket *ss);
@@ -1942,6 +1947,19 @@ PK11SymKey *tls13_ComputeECDHSharedKey(sslSocket *ss,
SECKEYPrivateKey *myPrivKey,
SECKEYPublicKey *peerKey);
#endif
+SECStatus ssl3_FlushHandshake(sslSocket *ss, PRInt32 flags);
+PK11SymKey *ssl3_GetWrappingKey(sslSocket *ss,
+ PK11SlotInfo *masterSecretSlot,
+ SSL3KEAType exchKeyType,
+ CK_MECHANISM_TYPE masterWrapMech,
+ void *pwArg);
+PRInt32 tls13_ServerSendPreSharedKeyXtn(sslSocket * ss,
+ PRBool append,
+ PRUint32 maxBytes);
+PRBool ssl3_ClientExtensionAdvertised(sslSocket *ss, PRUint16 ex_type);
+SECStatus ssl3_FillInCachedSID(sslSocket *ss, sslSessionID *sid,
+ SSL3KEAType effectiveExchKeyType);
+const ssl3CipherSuiteDef *ssl_LookupCipherSuiteDef(ssl3CipherSuite suite);
/* Pull in TLS 1.3 functions */
#include "tls13con.h"
diff --git a/lib/ssl/sslinfo.c b/lib/ssl/sslinfo.c
index 3ca43a682..6a8fec144 100644
--- a/lib/ssl/sslinfo.c
+++ b/lib/ssl/sslinfo.c
@@ -56,7 +56,14 @@ SSL_GetChannelInfo(PRFileDesc *fd, SSLChannelInfo *info, PRUintn len)
* function should get it from cwSpec rather than from the "hs".
* See bug 275744 comment 69 and bug 766137.
*/
- inf.cipherSuite = ss->ssl3.hs.cipher_suite;
+ /* For TLS 1.3, we return the cipher suite of the original
+ * connection if there was one rather than the PSK cipher
+ * suite. This matches the original interface for resumption
+ * and is safe because we only enable the corresponding PSK
+ * cipher suite.
+ */
+ inf.cipherSuite = ss->version >= SSL_LIBRARY_VERSION_TLS_1_3 ?
+ ss->ssl3.hs.origCipherSuite : ss->ssl3.hs.cipher_suite;
inf.compressionMethod = ss->ssl3.cwSpec->compression_method;
ssl_ReleaseSpecReadLock(ss);
inf.compressionMethodName =
@@ -114,7 +121,14 @@ SSL_GetPreliminaryChannelInfo(PRFileDesc *fd,
inf.valuesSet = ss->ssl3.hs.preliminaryInfo;
inf.protocolVersion = ss->version;
- inf.cipherSuite = ss->ssl3.hs.cipher_suite;
+ /* For TLS 1.3, we return the cipher suite of the original
+ * connection if there was one rather than the PSK cipher
+ * suite. This matches the original interface for resumption
+ * and is safe because we only enable the corresponding PSK
+ * cipher suite.
+ */
+ inf.cipherSuite = ss->version >= SSL_LIBRARY_VERSION_TLS_1_3 ?
+ ss->ssl3.hs.origCipherSuite : ss->ssl3.hs.cipher_suite;
memcpy(info, &inf, inf.length);
return SECSuccess;
@@ -127,12 +141,14 @@ SSL_GetPreliminaryChannelInfo(PRFileDesc *fd,
#define S_RSA "RSA", ssl_auth_rsa
#define S_KEA "KEA", ssl_auth_kea
#define S_ECDSA "ECDSA", ssl_auth_ecdsa
+#define S_PSK "PSK", ssl_auth_psk
#define K_DHE "DHE", kt_dh
#define K_RSA "RSA", kt_rsa
#define K_KEA "KEA", kt_kea
#define K_ECDH "ECDH", kt_ecdh
#define K_ECDHE "ECDHE", kt_ecdh
+#define K_ECDHE_PSK "ECDHE-PSK", kt_ecdh
#define C_SEED "SEED", calg_seed
#define C_CAMELLIA "CAMELLIA", calg_camellia
@@ -215,6 +231,7 @@ static const SSLCipherSuiteInfo suiteInfo[] = {
/* ECC cipher suites */
{0,CS(TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256), S_RSA, K_ECDHE, C_AESGCM, B_128, M_AEAD_128, 1, 0, 0 },
{0,CS(TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256), S_ECDSA, K_ECDHE, C_AESGCM, B_128, M_AEAD_128, 1, 0, 0 },
+ {0,CS(TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256), S_PSK, K_ECDHE_PSK, C_AESGCM, B_128, M_AEAD_128, 1, 0, 0 },
{0,CS(TLS_ECDH_ECDSA_WITH_NULL_SHA), S_ECDSA, K_ECDH, C_NULL, B_0, M_SHA, 0, 0, 0 },
{0,CS(TLS_ECDH_ECDSA_WITH_RC4_128_SHA), S_ECDSA, K_ECDH, C_RC4, B_128, M_SHA, 0, 0, 0 },
diff --git a/lib/ssl/sslproto.h b/lib/ssl/sslproto.h
index 2788e4acb..4c668fcab 100644
--- a/lib/ssl/sslproto.h
+++ b/lib/ssl/sslproto.h
@@ -223,6 +223,9 @@
#define TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 0xCCA9
#define TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256 0xCCAA
+/* Experimental PSK support for [draft-mattsson-tls-ecdhe-psk-aead] */
+#define TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256 0xD001
+
/* Netscape "experimental" cipher suites. */
#define SSL_RSA_OLDFIPS_WITH_3DES_EDE_CBC_SHA 0xffe0
#define SSL_RSA_OLDFIPS_WITH_DES_CBC_SHA 0xffe1
diff --git a/lib/ssl/sslt.h b/lib/ssl/sslt.h
index bf722b5c0..098ce88e5 100644
--- a/lib/ssl/sslt.h
+++ b/lib/ssl/sslt.h
@@ -62,7 +62,8 @@ typedef enum {
ssl_sign_null = 0, /* "anonymous" in TLS */
ssl_sign_rsa = 1,
ssl_sign_dsa = 2,
- ssl_sign_ecdsa = 3
+ ssl_sign_ecdsa = 3,
+ ssl_sign_psk = 4
} SSLSignType;
/* Values of this enum match the HashAlgorithm enum from
@@ -89,7 +90,8 @@ typedef enum {
ssl_auth_rsa = 1,
ssl_auth_dsa = 2,
ssl_auth_kea = 3,
- ssl_auth_ecdsa = 4
+ ssl_auth_ecdsa = 4,
+ ssl_auth_psk = 5 /* Used for both PSK and (EC)DHE-PSK */
} SSLAuthType;
typedef enum {
@@ -248,12 +250,13 @@ typedef enum {
ssl_extended_master_secret_xtn = 23,
ssl_session_ticket_xtn = 35,
ssl_tls13_key_share_xtn = 40, /* unofficial TODO(ekr) */
+ ssl_tls13_pre_shared_key_xtn = 41, /* unofficial TODO(ekr) */
ssl_next_proto_nego_xtn = 13172,
ssl_renegotiation_info_xtn = 0xff01,
ssl_tls13_draft_version_xtn = 0xff02 /* experimental number */
} SSLExtensionType;
-#define SSL_MAX_EXTENSIONS 14 /* doesn't include ssl_padding_xtn. */
+#define SSL_MAX_EXTENSIONS 15 /* doesn't include ssl_padding_xtn. */
typedef enum {
ssl_dhe_group_none = 0,
diff --git a/lib/ssl/tls13con.c b/lib/ssl/tls13con.c
index 6578663ac..526e90a98 100644
--- a/lib/ssl/tls13con.c
+++ b/lib/ssl/tls13con.c
@@ -12,6 +12,7 @@
#include "keyhi.h"
#include "pk11func.h"
#include "secitem.h"
+#include "secmod.h"
#include "sslimpl.h"
#include "sslproto.h"
#include "sslerr.h"
@@ -65,6 +66,7 @@ static SECStatus tls13_HandleFinished(sslSocket *ss, SSL3Opaque *b, PRUint32 len
static SECStatus tls13_HandleNewSessionTicket(sslSocket *ss, SSL3Opaque *b,
PRUint32 length);
static SECStatus tls13_ComputeSecrets1(sslSocket *ss);
+static SECStatus tls13_ComputeSecrets2(sslSocket *ss);
static SECStatus tls13_ComputeFinished(
sslSocket *ss, const SSL3Hashes *hashes,
PRBool sending,
@@ -95,6 +97,10 @@ const char kServerFinishedLabel[] = "server finished";
const SSL3ProtocolVersion kTlsRecordVersion = SSL_LIBRARY_VERSION_TLS_1_0;
const SSL3ProtocolVersion kDtlsRecordVersion = SSL_LIBRARY_VERSION_TLS_1_1;
+/* Belt and suspenders in case we ever add a TLS 1.4. */
+PR_STATIC_ASSERT(SSL_LIBRARY_VERSION_MAX_SUPPORTED <=
+ SSL_LIBRARY_VERSION_TLS_1_3);
+
#define FATAL_ERROR(ss, prError, desc) \
do { \
SSL_TRC(3, ("%d: TLS13[%d]: fatal error %d in %s (%s:%d)", \
@@ -330,7 +336,283 @@ tls13_HandlePostHelloHandshakeMessage(sslSocket *ss, SSL3Opaque *b,
return SECFailure;
}
-/* Called from ssl3_HandleClientHello.
+static SECStatus
+tls13_RecoverWrappedSharedSecret(sslSocket *ss, sslSessionID *sid)
+{
+ PK11SymKey *wrapKey; /* wrapping key */
+ PK11SymKey *SS = NULL;
+ SECItem wrappedMS = {siBuffer, NULL, 0};
+ SECStatus rv;
+ PK11SlotInfo *slot = NULL;
+ SSL_TRC(3, ("%d: TLS13[%d]: recovering static secret (%s)",
+ SSL_GETPID(), ss->fd,
+ ss->sec.isServer ? "server" : "client"));
+ if (!sid->u.ssl3.keys.msIsWrapped) {
+ PORT_Assert(0); /* I think this can't happen. */
+ return SECFailure;
+ }
+
+ /* If we are the server, we compute the wrapping key, but if we
+ * are the client, it's coordinates are stored with the ticket. */
+ if (ss->sec.isServer) {
+ wrapKey = ssl3_GetWrappingKey(ss, NULL,
+ sid->u.ssl3.exchKeyType,
+ sid->u.ssl3.masterWrapMech,
+ ss->pkcs11PinArg);
+ } else {
+ slot = SECMOD_LookupSlot(sid->u.ssl3.masterModuleID,
+ sid->u.ssl3.masterSlotID);
+ if (!slot)
+ return SECFailure;
+
+ wrapKey = PK11_GetWrapKey(slot,
+ sid->u.ssl3.masterWrapIndex,
+ sid->u.ssl3.masterWrapMech,
+ sid->u.ssl3.masterWrapSeries,
+ ss->pkcs11PinArg);
+ }
+ if (!wrapKey) {
+ return SECFailure;
+ }
+
+ wrappedMS.data = sid->u.ssl3.keys.wrapped_master_secret;
+ wrappedMS.len = sid->u.ssl3.keys.wrapped_master_secret_len;
+
+ /* unwrap the "master secret" which becomes SS. */
+ PORT_Assert(tls13_GetHash(ss) == ssl_hash_sha256);
+ SS = PK11_UnwrapSymKeyWithFlags(wrapKey, sid->u.ssl3.masterWrapMech,
+ NULL, &wrappedMS,
+ CKM_SSL3_MASTER_KEY_DERIVE,
+ CKA_DERIVE, 32,
+ CKF_SIGN | CKF_VERIFY);
+ PK11_FreeSymKey(wrapKey);
+ if (!SS) {
+ return SECFailure;
+ }
+ PRINT_KEY(50, (ss, "Recovered static secret", SS));
+ rv = tls13_HkdfExtractSharedKey(ss, SS, StaticSharedSecret);
+ PK11_FreeSymKey(SS);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+
+ return SECSuccess;
+}
+
+static void
+tls13_RestoreCipherInfo(sslSocket *ss, sslSessionID *sid)
+{
+ /* Set these to match the cached value.
+ * TODO(ekr@rtfm.com): Make a version with the "true" values.
+ * Bug 1256137.
+ */
+ ss->sec.authAlgorithm = sid->authAlgorithm;
+ ss->sec.authKeyBits = sid->authKeyBits;
+ ss->sec.keaType = sid->keaType;
+ ss->sec.keaKeyBits = sid->keaKeyBits;
+ ss->ssl3.hs.origCipherSuite = sid->u.ssl3.cipherSuite;
+}
+
+PRBool
+tls13_AllowPskCipher(const sslSocket *ss, const ssl3CipherSuiteDef *cipher_def)
+{
+ if (ss->sec.isServer) {
+ if (!ss->statelessResume)
+ return PR_FALSE;
+ } else {
+ sslSessionID *sid = ss->sec.ci.sid;
+ const ssl3CipherSuiteDef *cached_cipher_def;
+
+ /* This is zero when called from ssl3_ConstructV2CipherSpecsHack.
+ * TODO(ekr@rtfm.com): remove when SSLv2 is removed. Bug 1228555.
+ */
+ if (!sid)
+ return PR_FALSE;
+
+ /* Verify that this was cached. */
+ if (sid->cached == never_cached)
+ return PR_FALSE;
+
+ /* Don't offer this if the session version < TLS 1.3 */
+ if (sid->version < SSL_LIBRARY_VERSION_TLS_1_3)
+ return PR_FALSE;
+ cached_cipher_def = ssl_LookupCipherSuiteDef(
+ sid->u.ssl3.cipherSuite);
+ PORT_Assert(cached_cipher_def);
+
+ /* Only offer a PSK cipher with the same symmetric parameters
+ * as we negotiated before. */
+ if (cached_cipher_def->bulk_cipher_alg !=
+ cipher_def->bulk_cipher_alg)
+ return PR_FALSE;
+ }
+ /* TODO(ekr@rtfm.com): Check the KDF code whenever we have
+ * adjustable KDFs. */
+ SSL_TRC(3, ("%d: TLS 1.3[%d]: Enabling cipher suite suite 0x%04x",
+ SSL_GETPID(), ss->fd,
+ cipher_def->cipher_suite));
+
+ return PR_TRUE;
+}
+
+/* Called from ssl3_HandleClientHello after we have parsed the
+ * ClientHello and are sure that we are going to do TLS 1.3
+ * or fail. */
+SECStatus
+tls13_HandleClientHelloPart2(sslSocket *ss,
+ const SECItem *suites,
+ sslSessionID *sid)
+{
+ PRBool haveSpecWriteLock = PR_FALSE;
+ PRBool haveXmitBufLock = PR_FALSE;
+ SECStatus rv;
+ SSL3Statistics *ssl3stats = SSL_GetStatistics();
+ int j;
+
+ /* Sanity check whether resumption-PSK is allowed. */
+ if (sid != NULL) {
+ PRBool resumeOK = PR_FALSE;
+
+ do {
+ if (sid->version != ss->version) {
+ break;
+ }
+ resumeOK = PR_TRUE;
+ } while(0);
+
+ if (!resumeOK) {
+ SSL_AtomicIncrementLong(& ssl3stats->hch_sid_cache_not_ok);
+ if (ss->sec.uncache)
+ ss->sec.uncache(sid);
+ ssl_FreeSID(sid);
+ sid = NULL;
+ ss->statelessResume = PR_FALSE;
+ }
+ }
+
+#ifndef PARANOID
+ /* Look for a matching cipher suite. */
+ j = ssl3_config_match_init(ss);
+ if (j <= 0) { /* no ciphers are working/supported by PK11 */
+ FATAL_ERROR(ss, PORT_GetError(), internal_error);
+ goto loser;
+ }
+#endif
+
+ rv = ssl3_NegotiateCipherSuite(ss, suites);
+ if (rv != SECSuccess) {
+ FATAL_ERROR(ss, SSL_ERROR_NO_CYPHER_OVERLAP, handshake_failure);
+ goto loser;
+ }
+
+ /* TODO(ekr@rtfm.com): Update this when we have pure PSK. */
+ if (ss->ssl3.hs.suite_def->key_exchange_alg != kea_ecdhe_psk) {
+ /* TODO(ekr@rtfm.com): Free resumeSID. */
+ ss->statelessResume = PR_FALSE;
+ }
+
+ if (ss->statelessResume) {
+ PORT_Assert(sid);
+
+ rv = tls13_RecoverWrappedSharedSecret(ss, sid);
+ if (rv != SECSuccess) {
+ FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error);
+ goto loser;
+ }
+
+ SSL_AtomicIncrementLong(& ssl3stats->hch_sid_cache_hits);
+ SSL_AtomicIncrementLong(& ssl3stats->hch_sid_stateless_resumes);
+ ss->ssl3.hs.isResuming = PR_TRUE;
+
+ tls13_RestoreCipherInfo(ss, sid);
+
+ /* server sids don't remember the server cert we previously sent,
+ ** but they do remember the kea type we originally used, so we
+ ** can locate it again, provided that the current ssl socket
+ ** has had its server certs configured the same as the previous one.
+ */
+ ss->sec.localCert =
+ CERT_DupCertificate(ss->serverCerts[sid->keaType].serverCert);
+
+ if (sid->peerCert != NULL) {
+ ss->sec.peerCert = CERT_DupCertificate(sid->peerCert);
+ }
+ ssl3_RegisterServerHelloExtensionSender(
+ ss, ssl_tls13_pre_shared_key_xtn, tls13_ServerSendPreSharedKeyXtn);
+ ss->sec.ci.sid = sid;
+ } else {
+ if (sid) { /* we had a sid, but it's no longer valid, free it */
+ SSL_AtomicIncrementLong(& ssl3stats->hch_sid_cache_not_ok);
+ if (ss->sec.uncache)
+ ss->sec.uncache(sid);
+ ssl_FreeSID(sid);
+ sid = NULL;
+ }
+ ss->ssl3.hs.origCipherSuite = ss->ssl3.hs.cipher_suite;
+ SSL_AtomicIncrementLong(& ssl3stats->hch_sid_cache_misses);
+ }
+
+ rv = ssl3_ServerCallSNICallback(ss);
+ if (rv != SECSuccess) {
+ goto loser; /* An alert has already been sent. */
+ }
+
+ if (sid) {
+ /* Check that the negotiated SID and the cached SID match. */
+ if (SECITEM_CompareItem(&sid->u.ssl3.srvName,
+ &ss->ssl3.pwSpec->srvVirtName) !=
+ SECEqual) {
+ FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_CLIENT_HELLO,
+ handshake_failure);
+ goto loser;
+ }
+ }
+
+ rv = ssl3_SetupPendingCipherSpec(ss);
+ if (rv != SECSuccess) {
+ FATAL_ERROR(ss, PORT_GetError(), internal_error);
+ goto loser;
+ }
+
+ /* If this is TLS 1.3 we are expecting a ClientKeyShare
+ * extension. Missing/absent extension cause failure
+ * below. */
+ rv = tls13_HandleClientKeyShare(ss);
+ if (rv != SECSuccess) {
+ goto loser; /* An alert was sent already. */
+ }
+
+ if (!sid) {
+ sid = ssl3_NewSessionID(ss, PR_TRUE);
+ if (sid == NULL) {
+ FATAL_ERROR(ss, PORT_GetError(), internal_error);
+ goto loser;
+ }
+ ss->sec.ci.sid = sid;
+ ss->ssl3.hs.isResuming = PR_FALSE;
+ }
+
+ ssl_GetXmitBufLock(ss);
+ rv = tls13_SendServerHelloSequence(ss);
+ ssl_ReleaseXmitBufLock(ss);
+ if (rv != SECSuccess) {
+ FATAL_ERROR(ss, PORT_GetError(), handshake_failure);
+ goto loser;
+ }
+
+ return SECSuccess;
+
+loser:
+ if (haveSpecWriteLock) {
+ ssl_ReleaseSpecWriteLock(ss);
+ }
+ if (haveXmitBufLock) {
+ ssl_ReleaseXmitBufLock(ss);
+ }
+ return SECFailure;
+}
+
+/* Called from tls13_HandleClientHello.
*
* Caller must hold Handshake and RecvBuf locks.
*/
@@ -348,10 +630,6 @@ tls13_HandleClientKeyShare(sslSocket *ss)
PORT_Assert(ss->opt.noLocks || ssl_HaveRecvBufLock(ss));
PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
- rv = ssl3_SetupPendingCipherSpec(ss);
- if (rv != SECSuccess)
- return SECFailure; /* Error code set below */
-
/* Figure out what group we expect */
switch (ss->ssl3.hs.kea_def->exchKeyType) {
#ifndef NSS_DISABLE_ECC
@@ -580,12 +858,14 @@ tls13_InitializeHandshakeEncryption(sslSocket *ss)
{
SECStatus rv;
- /* For all present cipher suites, SS = ES.
- * TODO(ekr@rtfm.com): Revisit for 0-RTT. */
- ss->ssl3.hs.xSS = PK11_ReferenceSymKey(ss->ssl3.hs.xES);
+ PORT_Assert(!!ss->ssl3.hs.xSS ==
+ (ss->ssl3.hs.kea_def->signKeyType == ssl_sign_psk));
if (!ss->ssl3.hs.xSS) {
- FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error);
- return SECFailure;
+ ss->ssl3.hs.xSS = PK11_ReferenceSymKey(ss->ssl3.hs.xES);
+ if (!ss->ssl3.hs.xSS) {
+ FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error);
+ return SECFailure;
+ }
}
/* Here we destroy the old cipher spec immediately; in DTLS, we have to
@@ -635,30 +915,32 @@ tls13_SendServerHelloSequence(sslSocket *ss)
return SECFailure; /* error code is set. */
}
}
- rv = ssl3_SendCertificate(ss);
- if (rv != SECSuccess) {
- return SECFailure; /* error code is set. */
- }
- rv = ssl3_SendCertificateStatus(ss);
- if (rv != SECSuccess) {
- return SECFailure; /* error code is set. */
- }
+ if (ss->ssl3.hs.kea_def->signKeyType != ssl_sign_psk) {
+ rv = ssl3_SendCertificate(ss);
+ if (rv != SECSuccess) {
+ return SECFailure; /* error code is set. */
+ }
+ rv = ssl3_SendCertificateStatus(ss);
+ if (rv != SECSuccess) {
+ return SECFailure; /* error code is set. */
+ }
- /* This was copied from: ssl3_SendCertificate.
- * TODO(ekr@rtfm.com): Verify that this selection logic is correct.
- * Bug 1237514.
- */
- if ((ss->ssl3.hs.kea_def->kea == kea_ecdhe_rsa) ||
- (ss->ssl3.hs.kea_def->kea == kea_dhe_rsa)) {
- certIndex = kt_rsa;
- } else {
- certIndex = ss->ssl3.hs.kea_def->exchKeyType;
- }
- rv = ssl3_SendCertificateVerify(ss, ss->serverCerts[certIndex].SERVERKEY);
- if (rv != SECSuccess) {
- return rv; /* err code is set. */
+ /* This was copied from: ssl3_SendCertificate.
+ * TODO(ekr@rtfm.com): Verify that this selection logic is correct.
+ * Bug 1237514.
+ */
+ if ((ss->ssl3.hs.kea_def->kea == kea_ecdhe_rsa) ||
+ (ss->ssl3.hs.kea_def->kea == kea_dhe_rsa)) {
+ certIndex = kt_rsa;
+ } else {
+ certIndex = ss->ssl3.hs.kea_def->exchKeyType;
+ }
+ rv = ssl3_SendCertificateVerify(ss,
+ ss->serverCerts[certIndex].SERVERKEY);
+ if (rv != SECSuccess) {
+ return rv; /* err code is set. */
+ }
}
-
/* Compute the rest of the secrets except for the resumption
* and exporter secret. */
rv = tls13_ComputeSecrets1(ss);
@@ -678,6 +960,101 @@ tls13_SendServerHelloSequence(sslSocket *ss)
return SECSuccess;
}
+SECStatus
+tls13_HandleServerHelloPart2(sslSocket *ss)
+{
+ SECStatus rv;
+ PRBool isPSK = ssl3_ExtensionNegotiated(ss, ssl_tls13_pre_shared_key_xtn);
+ sslSessionID *sid = ss->sec.ci.sid;
+ SSL3Statistics *ssl3stats = SSL_GetStatistics();
+
+ /* we need to call ssl3_SetupPendingCipherSpec here so we can check the
+ * key exchange algorithm. */
+ rv = ssl3_SetupPendingCipherSpec(ss);
+ if (rv != SECSuccess) {
+ FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error);
+ return SECFailure;
+ }
+
+ if (isPSK) {
+ PRBool cacheOK = PR_FALSE;
+ do {
+ if (ss->ssl3.hs.kea_def->signKeyType != ssl_sign_psk) {
+ FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_SERVER_HELLO,
+ illegal_parameter);
+ break;
+ }
+ rv = tls13_RecoverWrappedSharedSecret(ss, sid);
+ if (rv != SECSuccess) {
+ FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error);
+ break;
+ }
+ cacheOK = PR_TRUE;
+ } while (0);
+
+ if (!cacheOK) {
+ SSL_AtomicIncrementLong(&ssl3stats->hsh_sid_cache_not_ok);
+ if (ss->sec.uncache)
+ ss->sec.uncache(sid);
+ return SECFailure;
+ }
+
+ tls13_RestoreCipherInfo(ss, sid);
+
+ SSL_AtomicIncrementLong(&ssl3stats->hsh_sid_cache_hits);
+ SSL_AtomicIncrementLong(&ssl3stats->hsh_sid_stateless_resumes);
+ } else {
+ /* No PSK negotiated.*/
+ if (ss->ssl3.hs.kea_def->signKeyType == ssl_sign_psk) {
+ FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_SERVER_HELLO,
+ illegal_parameter);
+ return SECFailure;
+ }
+ if (ssl3_ClientExtensionAdvertised(ss, ssl_tls13_pre_shared_key_xtn)) {
+ SSL_AtomicIncrementLong(&ssl3stats->hsh_sid_cache_misses);
+ }
+
+ /* Copy Signed Certificate Timestamps, if any. */
+ if (ss->xtnData.signedCertTimestamps.data) {
+ rv = SECITEM_CopyItem(NULL, &sid->u.ssl3.signedCertTimestamps,
+ &ss->xtnData.signedCertTimestamps);
+ if (rv != SECSuccess) {
+ FATAL_ERROR(ss, SEC_ERROR_NO_MEMORY, internal_error);
+ return SECFailure;
+ }
+ /* Clean up the temporary pointer to the handshake buffer. */
+ ss->xtnData.signedCertTimestamps.data = NULL;
+ ss->xtnData.signedCertTimestamps.len = 0;
+ }
+ ss->ssl3.hs.origCipherSuite = ss->ssl3.hs.cipher_suite;
+
+ if (sid->cached == in_client_cache && (ss->sec.uncache)) {
+ /* If we tried to resume and failed, let's not try again. */
+ ss->sec.uncache(sid);
+ }
+ }
+
+
+ /* Discard current SID and make a new one, though it may eventually
+ * end up looking a lot like the old one.
+ */
+ ssl_FreeSID(sid);
+ ss->sec.ci.sid = sid = ssl3_NewSessionID(ss, PR_FALSE);
+ if (sid == NULL) {
+ FATAL_ERROR(ss, PORT_GetError(), internal_error);
+ return SECFailure;
+ }
+ sid->version = ss->version;
+ rv = tls13_HandleServerKeyShare(ss);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+ TLS13_SET_HS_STATE(ss, wait_encrypted_extensions);
+
+ return SECSuccess;
+}
+
+
/*
* Called from ssl3_HandleServerHello.
*
@@ -904,8 +1281,8 @@ tls13_AddContextToHashes(sslSocket *ss, SSL3Hashes *hashes /* IN/OUT */,
PORT_Assert(SECFailure);
PORT_Assert(!SECSuccess);
- PRINT_BUF(90, (ss, "TLS 1.3 hash without context", hashes->u.raw, hashes->len));
- PRINT_BUF(90, (ss, "Context string", context_string, strlen(context_string)));
+ PRINT_BUF(50, (ss, "TLS 1.3 hash without context", hashes->u.raw, hashes->len));
+ PRINT_BUF(50, (ss, "Context string", context_string, strlen(context_string)));
rv |= PK11_DigestBegin(ctx);
rv |= PK11_DigestOp(ctx, context_padding, sizeof(context_padding));
rv |= PK11_DigestOp(ctx, (unsigned char *)context_string,
@@ -914,7 +1291,7 @@ tls13_AddContextToHashes(sslSocket *ss, SSL3Hashes *hashes /* IN/OUT */,
/* Update the hash in-place */
rv |= PK11_DigestFinal(ctx, hashes->u.raw, &hashlength, sizeof(hashes->u.raw));
PK11_DestroyContext(ctx, PR_TRUE);
- PRINT_BUF(90, (NULL, "TLS 1.3 hash with context", hashes->u.raw, hashlength));
+ PRINT_BUF(50, (ss, "TLS 1.3 hash with context", hashes->u.raw, hashlength));
hashes->len = hashlength;
hashes->hashAlg = algorithm;
@@ -1016,7 +1393,7 @@ tls13_DeriveTrafficKeys(sslSocket *ss, ssl3CipherSpec *pwSpec,
ssl_MapLowLevelError(SSL_ERROR_SESSION_KEY_GEN_FAILURE);
return SECFailure;
}
- PRINT_BUF(60, (ss, "Deriving traffic keys. Session hash=", hashes.u.raw,
+ PRINT_BUF(50, (ss, "Deriving traffic keys. Session hash=", hashes.u.raw,
hashes.len));
switch (type) {
@@ -1145,15 +1522,15 @@ tls13_ComputeSecrets1(sslSocket *ss)
SECStatus rv;
PK11SymKey *mSS = NULL;
PK11SymKey *mES = NULL;
- PK11SymKey *masterSecret = NULL;
SSL3Hashes hashes;
+ ssl3CipherSpec *pwSpec = ss->ssl3.pwSpec;
rv = ssl3_SetupPendingCipherSpec(ss);
if (rv != SECSuccess) {
return rv; /* error code set below. */
}
- rv = ssl3_ComputeHandshakeHashes(ss, ss->ssl3.pwSpec, &hashes, 0);
+ rv = ssl3_ComputeHandshakeHashes(ss, pwSpec, &hashes, 0);
if (rv != SECSuccess) {
PORT_Assert(0); /* Should never fail */
ssl_MapLowLevelError(SSL_ERROR_SESSION_KEY_GEN_FAILURE);
@@ -1187,13 +1564,13 @@ tls13_ComputeSecrets1(sslSocket *ss)
rv = tls13_HkdfExtract(mSS, mES,
tls13_GetHash(ss),
- &masterSecret);
+ &pwSpec->master_secret);
if (rv != SECSuccess) {
goto loser;
}
- rv = tls13_HkdfExpandLabel(masterSecret,
+ rv = tls13_HkdfExpandLabel(pwSpec->master_secret,
tls13_GetHash(ss),
hashes.u.raw, hashes.len,
kHkdfLabelTrafficSecret,
@@ -1204,7 +1581,7 @@ tls13_ComputeSecrets1(sslSocket *ss)
goto loser;
}
- rv = tls13_HkdfExpandLabel(masterSecret,
+ rv = tls13_HkdfExpandLabel(pwSpec->master_secret,
tls13_GetHash(ss),
NULL, 0,
kHkdfLabelClientFinishedSecret,
@@ -1215,7 +1592,7 @@ tls13_ComputeSecrets1(sslSocket *ss)
goto loser;
}
- rv = tls13_HkdfExpandLabel(masterSecret,
+ rv = tls13_HkdfExpandLabel(pwSpec->master_secret,
tls13_GetHash(ss),
NULL, 0,
kHkdfLabelServerFinishedSecret,
@@ -1238,10 +1615,45 @@ loser:
if (mES) {
PK11_FreeSymKey(mES);
}
- if (masterSecret) {
- PK11_FreeSymKey(masterSecret);
+
+ return rv;
+}
+
+static SECStatus
+tls13_ComputeSecrets2(sslSocket *ss)
+{
+ SECStatus rv;
+ SSL3Hashes hashes;
+ ssl3CipherSpec *cwSpec = ss->ssl3.cwSpec;
+ PK11SymKey *resumptionMasterSecret = NULL;
+
+ rv = ssl3_ComputeHandshakeHashes(ss, cwSpec, &hashes, 0);
+ if (rv != SECSuccess) {
+ PORT_Assert(0); /* Should never fail */
+ ssl_MapLowLevelError(SSL_ERROR_SESSION_KEY_GEN_FAILURE);
+ return SECFailure;
+ }
+
+ rv = tls13_HkdfExpandLabel(cwSpec->master_secret,
+ tls13_GetHash(ss),
+ hashes.u.raw, hashes.len,
+ kHkdfLabelResumptionMasterSecret,
+ strlen(kHkdfLabelResumptionMasterSecret),
+ tls13_GetHkdfMechanism(ss),
+ hashes.len, &resumptionMasterSecret);
+ if (rv != SECSuccess) {
+ goto loser;
}
+ /* This is pretty gross. TLS 1.3 uses a number of master secrets.
+ * the master secret to generate the keys and then the resumption
+ * master secret for future connections. To make this work without
+ * refactoring too much of the SSLv3 code, we replace
+ * |pwSpec->master_secret| with the resumption master secret.
+ */
+ PK11_FreeSymKey(cwSpec->master_secret);
+ cwSpec->master_secret = resumptionMasterSecret;
+loser:
return rv;
}
@@ -1370,7 +1782,21 @@ tls13_HandleEncryptedExtensions(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
return SECFailure; /* Error code set below */
}
- TLS13_SET_HS_STATE(ss, wait_cert_request);
+ PORT_Assert(!ss->sec.isServer);
+
+ if (ss->ssl3.hs.kea_def->signKeyType == ssl_sign_psk) {
+ /* Compute the rest of the secrets except for the resumption
+ * and exporter secret. */
+ rv = tls13_ComputeSecrets1(ss);
+ if (rv != SECSuccess) {
+ FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error);
+ return SECFailure;
+ }
+ TLS13_SET_HS_STATE(ss, wait_finished);
+ } else {
+ TLS13_SET_HS_STATE(ss, wait_cert_request);
+ }
+
return SECSuccess;
}
@@ -1521,7 +1947,7 @@ tls13_ComputeFinished(sslSocket *ss, const SSL3Hashes *hashes, PRBool sending,
: ss->ssl3.hs.serverFinishedSecret;
PORT_Assert(secret);
- PRINT_BUF(90, (NULL, "Handshake hash", hashes->u.raw, hashes->len));
+ PRINT_BUF(50, (NULL, "Handshake hash", hashes->u.raw, hashes->len));
hmacCtx = PK11_CreateContextBySymKey(macAlg, CKA_SIGN,
secret, &param);
@@ -1674,7 +2100,6 @@ tls13_HandleFinished(sslSocket *ss, SSL3Opaque *b, PRUint32 length,
/* Server is now finished.
* Client sends second flight
*/
- /* TODO(ekr@rtfm.com): Send NewSession Ticket if server. */
if (ss->sec.isServer) {
/* Once we've receive the client's Finished, there is no need for
* retransmission; the retransmission timer was stopped when we received
@@ -1688,6 +2113,22 @@ tls13_HandleFinished(sslSocket *ss, SSL3Opaque *b, PRUint32 length,
}
rv = tls13_FinishHandshake(ss);
+ if (rv != SECSuccess) {
+ return SECFailure; /* Error code and alerts handled below */
+ }
+ ssl_GetXmitBufLock(ss);
+ if (ss->opt.enableSessionTickets &&
+ ss->ssl3.hs.kea_def->signKeyType != ssl_sign_psk) {
+ /* TODO(ekr@rtfm.com): Add support for new tickets in PSK. */
+ rv = ssl3_SendNewSessionTicket(ss);
+ if (rv != SECSuccess) {
+ ssl_ReleaseXmitBufLock(ss);
+ return SECFailure; /* Error code and alerts handled below */
+ }
+ rv = ssl3_FlushHandshake(ss, 0);
+ }
+ ssl_ReleaseXmitBufLock(ss);
+
} else {
if (ss->ssl3.hs.authCertificatePending) {
/* TODO(ekr@rtfm.com): Handle pending auth */
@@ -1703,8 +2144,6 @@ tls13_HandleFinished(sslSocket *ss, SSL3Opaque *b, PRUint32 length,
}
rv = tls13_SendClientSecondRound(ss);
- if (rv != SECSuccess)
- return SECFailure; /* Error code and alerts handled below */
}
return rv;
@@ -1713,10 +2152,16 @@ tls13_HandleFinished(sslSocket *ss, SSL3Opaque *b, PRUint32 length,
static SECStatus
tls13_FinishHandshake(sslSocket *ss)
{
+ SECStatus rv;
+
PORT_Assert(ss->opt.noLocks || ssl_HaveRecvBufLock(ss));
PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
PORT_Assert(ss->ssl3.hs.restartTarget == NULL);
+ rv = tls13_ComputeSecrets2(ss);
+ if (rv != SECSuccess)
+ return SECFailure;
+
/* The first handshake is now completed. */
ss->handshake = NULL;
@@ -1800,16 +2245,79 @@ static SECStatus
tls13_HandleNewSessionTicket(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
{
SECStatus rv;
+ PRInt32 tmp;
+ NewSessionTicket ticket;
+ SECItem data;
+
+ SSL_TRC(3, ("%d: TLS13[%d]: handle new session ticket message",
+ SSL_GETPID(), ss->fd));
rv = TLS13_CHECK_HS_STATE(ss, SSL_ERROR_RX_UNEXPECTED_NEW_SESSION_TICKET,
idle_handshake);
if (rv != SECSuccess) {
return SECFailure;
}
+ if (!ss->firstHsDone || ss->sec.isServer) {
+ FATAL_ERROR(ss, SSL_ERROR_RX_UNEXPECTED_NEW_SESSION_TICKET,
+ unexpected_message);
+ return SECFailure;
+ }
+
+ ticket.received_timestamp = ssl_Time();
+ tmp = ssl3_ConsumeHandshakeNumber(ss, 4, &b, &length);
+ if (tmp < 0) {
+ FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_NEW_SESSION_TICKET,
+ decode_error);
+ return SECFailure;
+ }
+ ticket.ticket_lifetime_hint = (PRUint32)tmp;
+ ticket.ticket.type = siBuffer;
+ rv = ssl3_ConsumeHandshakeVariable(ss, &data, 2, &b, &length);
+ if (rv != SECSuccess || length != 0 || !data.len) {
+ FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_NEW_SESSION_TICKET,
+ decode_error);
+ return SECFailure;
+ }
- UNIMPLEMENTED();
+ /* TODO(ekr@rtfm.com): Re-enable new tickets when PSK mode is
+ * in use. I believe this works, but I can't test it until the
+ * server side supports it. Bug 1257047.
+ */
+ if (!ss->opt.noCache && ss->sec.cache &&
+ ss->ssl3.hs.kea_def->signKeyType != ssl_sign_psk) {
+ SSL3KEAType effectiveExchKeyType;
+
+ /* Uncache so that we replace. */
+ (*ss->sec.uncache)(ss->sec.ci.sid);
+
+ rv = SECITEM_CopyItem(NULL, &ticket.ticket, &data);
+ if (rv != SECSuccess) {
+ FATAL_ERROR(ss, SEC_ERROR_NO_MEMORY, internal_error);
+ return SECFailure;
+ }
+ PRINT_BUF(50, (ss, "Caching session ticket",
+ ticket.ticket.data,
+ ticket.ticket.len));
+
+ ssl3_SetSIDSessionTicket(ss->sec.ci.sid, &ticket);
+ PORT_Assert(!ticket.ticket.data);
+
+ if (ss->ssl3.hs.kea_def->kea == kea_ecdhe_rsa ||
+ ss->ssl3.hs.kea_def->kea == kea_dhe_rsa) {
+ effectiveExchKeyType = kt_rsa;
+ } else {
+ effectiveExchKeyType = ss->ssl3.hs.kea_def->exchKeyType;
+ }
+
+ rv = ssl3_FillInCachedSID(ss, ss->sec.ci.sid, effectiveExchKeyType);
+ if (rv != SECSuccess)
+ return SECFailure;
+
+ /* Cache the session. */
+ ss->sec.ci.sid->cached = never_cached;
+ (*ss->sec.cache)(ss->sec.ci.sid);
+ }
- /* Ignore */
return SECSuccess;
}
@@ -1850,6 +2358,8 @@ static const struct {
ExtensionClientOnly },
{ ssl_tls13_key_share_xtn,
ExtensionSendClear },
+ { ssl_tls13_pre_shared_key_xtn,
+ ExtensionSendClear },
{ ssl_next_proto_nego_xtn,
ExtensionNotUsed },
{ ssl_renegotiation_info_xtn,
diff --git a/lib/ssl/tls13con.h b/lib/ssl/tls13con.h
index 79e8e517a..15397fc77 100644
--- a/lib/ssl/tls13con.h
+++ b/lib/ssl/tls13con.h
@@ -41,6 +41,12 @@ CK_MECHANISM_TYPE tls13_GetHkdfMechanism(sslSocket *ss);
void tls13_FatalError(sslSocket *ss, PRErrorCode prError,
SSL3AlertDescription desc);
SECStatus tls13_SetupClientHello(sslSocket *ss);
+PRBool tls13_AllowPskCipher(const sslSocket *ss,
+ const ssl3CipherSuiteDef *cipher_def);
+SECStatus tls13_HandleClientHelloPart2(sslSocket *ss,
+ const SECItem *suites,
+ sslSessionID *sid);
+SECStatus tls13_HandleServerHelloPart2(sslSocket *ss);
SECStatus tls13_HandlePostHelloHandshakeMessage(sslSocket *ss, SSL3Opaque *b,
PRUint32 length,
SSL3Hashes *hashesPtr);
diff --git a/lib/ssl/tls13hkdf.c b/lib/ssl/tls13hkdf.c
index febcc8b58..c3fec2953 100644
--- a/lib/ssl/tls13hkdf.c
+++ b/lib/ssl/tls13hkdf.c
@@ -82,7 +82,7 @@ tls13_HkdfExtract(PK11SymKey *ikm1, PK11SymKey *ikm2, SSLHashType baseHash,
CKA_DERIVE, kTlsHkdfInfo[baseHash].hashSize);
if (!prk)
return SECFailure;
- PRINT_KEY(60, (NULL, "HKDF Extract", prk));
+ PRINT_KEY(50, (NULL, "HKDF Extract", prk));
*prkp = prk;
return SECSuccess;
@@ -171,13 +171,13 @@ tls13_HkdfExpandLabel(PK11SymKey *prk, SSLHashType baseHash,
char labelStr[100];
PORT_Memcpy(labelStr, label, labelLen);
labelStr[labelLen] = 0;
- SSL_TRC(60, ("HKDF Expand: label=[TLS 1.3, ] + '%s',requested length=%d",
+ SSL_TRC(50, ("HKDF Expand: label=[TLS 1.3, ] + '%s',requested length=%d",
labelStr, keySize));
}
- PRINT_KEY(60, (NULL, "PRK", prk));
- PRINT_BUF(60, (NULL, "Hash", handshakeHash, handshakeHashLen));
- PRINT_BUF(60, (NULL, "Info", info, infoLen));
- PRINT_KEY(60, (NULL, "Derived key", derived));
+ PRINT_KEY(50, (NULL, "PRK", prk));
+ PRINT_BUF(50, (NULL, "Hash", handshakeHash, handshakeHashLen));
+ PRINT_BUF(50, (NULL, "Info", info, infoLen));
+ PRINT_KEY(50, (NULL, "Derived key", derived));
#endif
return SECSuccess;
diff --git a/lib/util/secoid.c b/lib/util/secoid.c
index 4f16ed386..b3b9bfebc 100644
--- a/lib/util/secoid.c
+++ b/lib/util/secoid.c
@@ -1712,6 +1712,9 @@ const static SECOidData oids[SEC_OID_TOTAL] = {
ODE( SEC_OID_CHACHA20_POLY1305,
"ChaCha20-Poly1305", CKM_NSS_CHACHA20_POLY1305, INVALID_CERT_EXTENSION ),
+ ODE( SEC_OID_TLS_ECDHE_PSK,
+ "TLS ECHDE-PSK key exchange", CKM_INVALID_MECHANISM, INVALID_CERT_EXTENSION ),
+
};
/* PRIVATE EXTENDED SECOID Table
diff --git a/lib/util/secoidt.h b/lib/util/secoidt.h
index d9386a75a..2f99d89f1 100644
--- a/lib/util/secoidt.h
+++ b/lib/util/secoidt.h
@@ -481,6 +481,8 @@ typedef enum {
SEC_OID_CHACHA20_POLY1305 = 346,
+ SEC_OID_TLS_ECDHE_PSK = 347,
+
SEC_OID_TOTAL
} SECOidTag;