diff options
author | EKR <ekr@rtfm.com> | 2016-02-07 16:21:26 -0800 |
---|---|---|
committer | EKR <ekr@rtfm.com> | 2016-02-07 16:21:26 -0800 |
commit | e239e24e04ee67efbbcc70b23eb7d73744e1ce69 (patch) | |
tree | ee508c9e25fa0394ad301b8e4dc3d1b2774ea14e | |
parent | 8bbc8aa7f7a4595b255c8fbe7e5ccc774a87488f (diff) | |
download | nss-hg-e239e24e04ee67efbbcc70b23eb7d73744e1ce69.tar.gz |
Bug 1257891 - TLS 1.3: Implement resumption-PSK. r=mtNSS_3_24_BETA1
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, ¶m); @@ -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; |