diff options
author | EKR <ekr@rtfm.com> | 2015-01-10 21:25:38 +0000 |
---|---|---|
committer | EKR <ekr@rtfm.com> | 2015-01-10 21:25:38 +0000 |
commit | 8c9285302f210624f914e6501a3825ac08581855 (patch) | |
tree | d8f50658bb99b944bdcb230309ca7d34d4de1219 | |
parent | 6b09a4887a8b9e97e689eea32ecf744982da937a (diff) | |
download | nss-hg-8c9285302f210624f914e6501a3825ac08581855.tar.gz |
Bug 1117022 - Implement draft-ietf-tls-session-hash [libssl]. r=mt
27 files changed, 938 insertions, 198 deletions
diff --git a/cmd/selfserv/selfserv.c b/cmd/selfserv/selfserv.c index 81fb43d95..549fda53e 100644 --- a/cmd/selfserv/selfserv.c +++ b/cmd/selfserv/selfserv.c @@ -428,10 +428,11 @@ printSecurityInfo(PRFileDesc *fd) suite.macBits, suite.macAlgorithmName); FPRINTF(stderr, "selfserv: Server Auth: %d-bit %s, Key Exchange: %d-bit %s\n" - " Compression: %s\n", + " Compression: %s, Extended Master Secret: %s\n", channel.authKeyBits, suite.authAlgorithmName, channel.keaKeyBits, suite.keaTypeName, - channel.compressionMethodName); + channel.compressionMethodName, + channel.extendedMasterSecretUsed ? "Yes": "No"); } } if (verbose) { @@ -837,6 +838,8 @@ PRBool testbypass = PR_FALSE; PRBool enableSessionTickets = PR_FALSE; PRBool enableCompression = PR_FALSE; PRBool failedToNegotiateName = PR_FALSE; +PRBool enableExtendedMasterSecret = PR_FALSE; + static char *virtServerNameArray[MAX_VIRT_SERVER_NAME_ARRAY_INDEX]; static int virtServerNameIndex = 1; @@ -1942,6 +1945,13 @@ server_main( } } + if (enableExtendedMasterSecret) { + rv = SSL_OptionSet(model_sock, SSL_ENABLE_EXTENDED_MASTER_SECRET, PR_TRUE); + if (rv != SECSuccess) { + errExit("error enabling extended master secret "); + } + } + for (kea = kt_rsa; kea < kt_kea_size; kea++) { if (cert[kea] != NULL) { secStatus = SSL_ConfigSecureServer(model_sock, @@ -2218,7 +2228,7 @@ main(int argc, char **argv) ** numbers, then capital letters, then lower case, alphabetical. */ optstate = PL_CreateOptState(argc, argv, - "2:A:BC:DEH:L:M:NP:RS:T:U:V:W:Ya:bc:d:e:f:g:hi:jk:lmn:op:qrst:uvw:xyz"); + "2:A:BC:DEGH:L:M:NP:RS:T:U:V:W:Ya:bc:d:e:f:g:hi:jk:lmn:op:qrst:uvw:xyz"); while ((status = PL_GetNextOpt(optstate)) == PL_OPT_OK) { ++optionsFound; switch(optstate->option) { @@ -2234,6 +2244,8 @@ main(int argc, char **argv) case 'E': disableStepDown = PR_TRUE; break; case 'H': configureDHE = (PORT_Atoi(optstate->value) != 0); break; + case 'G': enableExtendedMasterSecret = PR_TRUE; break; + case 'I': /* reserved for OCSP multi-stapling */ break; case 'L': diff --git a/cmd/tstclnt/tstclnt.c b/cmd/tstclnt/tstclnt.c index ddfadafd5..93a702220 100644 --- a/cmd/tstclnt/tstclnt.c +++ b/cmd/tstclnt/tstclnt.c @@ -129,10 +129,11 @@ void printSecurityInfo(PRFileDesc *fd) suite.macBits, suite.macAlgorithmName); FPRINTF(stderr, "tstclnt: Server Auth: %d-bit %s, Key Exchange: %d-bit %s\n" - " Compression: %s\n", + " Compression: %s, Extended Master Secret: %s\n", channel.authKeyBits, suite.authAlgorithmName, channel.keaKeyBits, suite.keaTypeName, - channel.compressionMethodName); + channel.compressionMethodName, + channel.extendedMasterSecretUsed ? "Yes": "No"); } } cert = SSL_RevealCert(fd); @@ -231,6 +232,7 @@ static void PrintParameterUsage(void) fprintf(stderr, "%-20s Enable compression.\n", "-z"); fprintf(stderr, "%-20s Enable false start.\n", "-g"); fprintf(stderr, "%-20s Enable the cert_status extension (OCSP stapling).\n", "-T"); + fprintf(stderr, "%-20s Enable the extended master secret extension (session hash).\n", "-G"); fprintf(stderr, "%-20s Require fresh revocation info from side channel.\n" "%-20s -F once means: require for server cert only\n" "%-20s -F twice means: require for intermediates, too\n" @@ -919,6 +921,7 @@ int main(int argc, char **argv) int enableFalseStart = 0; int enableCertStatus = 0; int forceFallbackSCSV = 0; + int enableExtendedMasterSecret = 0; PRSocketOptionData opt; PRNetAddr addr; PRPollDesc pollset[2]; @@ -967,7 +970,7 @@ int main(int argc, char **argv) SSL_VersionRangeGetSupported(ssl_variant_stream, &enabledVersions); optstate = PL_CreateOptState(argc, argv, - "46BCDFKM:OR:STV:W:Ya:bc:d:fgh:m:n:op:qr:st:uvw:xz"); + "46BCDFGKM:OR:STV:W:Ya:bc:d:fgh:m:n:op:qr:st:uvw:xz"); while ((optstatus = PL_GetNextOpt(optstate)) == PL_OPT_OK) { switch (optstate->option) { case '?': @@ -989,6 +992,8 @@ int main(int argc, char **argv) serverCertAuth.testFreshStatusFromSideChannel = PR_TRUE; break; + case 'G': enableExtendedMasterSecret = PR_TRUE; break; + case 'I': /* reserved for OCSP multi-stapling */ break; case 'O': serverCertAuth.shouldPause = PR_FALSE; break; @@ -1386,6 +1391,15 @@ int main(int argc, char **argv) return 1; } + /* enable extended master secret mode */ + if (enableExtendedMasterSecret) { + rv = SSL_OptionSet(s, SSL_ENABLE_EXTENDED_MASTER_SECRET, PR_TRUE); + if (rv != SECSuccess) { + SECU_PrintError(progName, "error enabling extended master secret"); + return 1; + } + } + SSL_SetPKCS11PinArg(s, &pwdata); serverCertAuth.dbHandle = CERT_GetDefaultCertDB(); diff --git a/coreconf/rules.mk b/coreconf/rules.mk index 0a891ebc7..34b742a7f 100644 --- a/coreconf/rules.mk +++ b/coreconf/rules.mk @@ -424,12 +424,12 @@ $(OBJDIR)/$(PROG_PREFIX)%$(OBJ_SUFFIX): %.S $(OBJDIR)/$(PROG_PREFIX)%: %.cpp @$(MAKE_OBJDIR) ifdef USE_NT_C_SYNTAX - $(CCC) -Fo$@ -c $(CFLAGS) $(call core_abspath,$<) + $(CCC) -Fo$@ -c $(CFLAGS) $(CXXFLAGS) $(call core_abspath,$<) else ifdef NEED_ABSOLUTE_PATH - $(CCC) -o $@ -c $(CFLAGS) $(call core_abspath,$<) + $(CCC) -o $@ -c $(CFLAGS) $(CXXFLAGS) $(call core_abspath,$<) else - $(CCC) -o $@ -c $(CFLAGS) $< + $(CCC) -o $@ -c $(CFLAGS) $(CXXFLAGS) $< endif endif @@ -440,16 +440,16 @@ $(OBJDIR)/$(PROG_PREFIX)%$(OBJ_SUFFIX): %.cc $(MAKE_OBJDIR) ifdef STRICT_CPLUSPLUS_SUFFIX echo "#line 1 \"$<\"" | cat - $< > $(OBJDIR)/t_$*.cc - $(CCC) -o $@ -c $(CFLAGS) $(OBJDIR)/t_$*.cc + $(CCC) -o $@ -c $(CFLAGS) $(CXXFLAGS) $(OBJDIR)/t_$*.cc rm -f $(OBJDIR)/t_$*.cc else ifdef USE_NT_C_SYNTAX - $(CCC) -Fo$@ -c $(CFLAGS) $(call core_abspath,$<) + $(CCC) -Fo$@ -c $(CFLAGS) $(CXXFLAGS) $(call core_abspath,$<) else ifdef NEED_ABSOLUTE_PATH - $(CCC) -o $@ -c $(CFLAGS) $(call core_abspath,$<) + $(CCC) -o $@ -c $(CFLAGS) $(CXXFLAGS) $(call core_abspath,$<) else - $(CCC) -o $@ -c $(CFLAGS) $< + $(CCC) -o $@ -c $(CFLAGS) $(CXXFLAGS) $< endif endif endif #STRICT_CPLUSPLUS_SUFFIX @@ -458,22 +458,22 @@ $(OBJDIR)/$(PROG_PREFIX)%$(OBJ_SUFFIX): %.cpp @$(MAKE_OBJDIR) ifdef STRICT_CPLUSPLUS_SUFFIX echo "#line 1 \"$<\"" | cat - $< > $(OBJDIR)/t_$*.cc - $(CCC) -o $@ -c $(CFLAGS) $(OBJDIR)/t_$*.cc + $(CCC) -o $@ -c $(CFLAGS) $(CXXFLAGS) $(OBJDIR)/t_$*.cc rm -f $(OBJDIR)/t_$*.cc else ifdef USE_NT_C_SYNTAX - $(CCC) -Fo$@ -c $(CFLAGS) $(call core_abspath,$<) + $(CCC) -Fo$@ -c $(CFLAGS) $(CXXFLAGS) $(call core_abspath,$<) else ifdef NEED_ABSOLUTE_PATH - $(CCC) -o $@ -c $(CFLAGS) $(call core_abspath,$<) + $(CCC) -o $@ -c $(CFLAGS) $(CXXFLAGS) $(call core_abspath,$<) else - $(CCC) -o $@ -c $(CFLAGS) $< + $(CCC) -o $@ -c $(CFLAGS) $(CXXFLAGS) $< endif endif endif #STRICT_CPLUSPLUS_SUFFIX %.i: %.cpp - $(CCC) -C -E $(CFLAGS) $< > $@ + $(CCC) -C -E $(CFLAGS) $(CXXFLAGS) $< > $@ %.i: %.c ifeq (,$(filter-out WIN%,$(OS_TARGET))) diff --git a/external_tests/ssl_gtest/Makefile b/external_tests/ssl_gtest/Makefile index 61965a20a..7f36bffe4 100644 --- a/external_tests/ssl_gtest/Makefile +++ b/external_tests/ssl_gtest/Makefile @@ -43,6 +43,9 @@ include $(CORE_DEPTH)/coreconf/rules.mk ####################################################################### MKPROG = $(CCC) +CXXFLAGS += -std=c++0x +CFLAGS += -I$(CORE_DEPTH)/lib/ssl + include ../../cmd/platrules.mk ifeq (WINNT,$(OS_ARCH)) @@ -55,6 +58,4 @@ ifeq (WINNT,$(OS_ARCH)) # Linking to winsock to get htonl OS_LIBS += Ws2_32.lib -else - CFLAGS += -std=c++0x endif diff --git a/external_tests/ssl_gtest/libssl_internals.c b/external_tests/ssl_gtest/libssl_internals.c new file mode 100644 index 000000000..db83ef694 --- /dev/null +++ b/external_tests/ssl_gtest/libssl_internals.c @@ -0,0 +1,26 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* This file contains functions for frobbing the internals of libssl */ +#include "libssl_internals.h" + +#include "seccomon.h" +#include "ssl.h" +#include "sslimpl.h" + +SECStatus +SSLInt_IncrementClientHandshakeVersion(PRFileDesc *fd) +{ + sslSocket *ss = (sslSocket *)fd->secret; + + if (!ss) { + return SECFailure; + } + + ++ss->clientHelloVersion; + + return SECSuccess; +} diff --git a/external_tests/ssl_gtest/libssl_internals.h b/external_tests/ssl_gtest/libssl_internals.h new file mode 100644 index 000000000..db6d0af62 --- /dev/null +++ b/external_tests/ssl_gtest/libssl_internals.h @@ -0,0 +1,17 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef libssl_internals_h_ +#define libssl_internals_h_ + +#include "prio.h" +#include "seccomon.h" + +SECStatus SSLInt_IncrementClientHandshakeVersion(PRFileDesc *fd); + +#endif + + diff --git a/external_tests/ssl_gtest/manifest.mn b/external_tests/ssl_gtest/manifest.mn index 212aa39d6..ac5f3845e 100644 --- a/external_tests/ssl_gtest/manifest.mn +++ b/external_tests/ssl_gtest/manifest.mn @@ -6,6 +6,11 @@ CORE_DEPTH = ../.. DEPTH = ../.. MODULE = nss +# These sources have access to libssl internals +CSRCS = \ + libssl_internals.c \ + $(NULL) + CPPSRCS = \ ssl_loopback_unittest.cc \ ssl_extension_unittest.cc \ diff --git a/external_tests/ssl_gtest/ssl_loopback_unittest.cc b/external_tests/ssl_gtest/ssl_loopback_unittest.cc index 09303e715..8d9e40399 100644 --- a/external_tests/ssl_gtest/ssl_loopback_unittest.cc +++ b/external_tests/ssl_gtest/ssl_loopback_unittest.cc @@ -7,9 +7,13 @@ #include "ssl.h" #include "sslerr.h" #include "sslproto.h" - #include <memory> +extern "C" { +// This is not something that should make you happy. +#include "libssl_internals.h" +} + #include "tls_parser.h" #include "tls_filter.h" #include "tls_connect.h" @@ -17,6 +21,46 @@ namespace nss_test { +uint8_t kBogusClientKeyExchange[] = { + 0x01, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +}; + +// When we see the ClientKeyExchange from |client|, increment the +// ClientHelloVersion on |server|. +class TlsInspectorClientHelloVersionChanger : public TlsHandshakeFilter { + public: + TlsInspectorClientHelloVersionChanger(TlsAgent* server) : server_(server) {} + + virtual bool FilterHandshake(uint16_t version, uint8_t handshake_type, + const DataBuffer& input, DataBuffer* output) { + if (handshake_type == kTlsHandshakeClientKeyExchange) { + EXPECT_EQ( + SECSuccess, + SSLInt_IncrementClientHandshakeVersion(server_->ssl_fd())); + } + return false; + } + + private: + TlsAgent* server_; +}; + class TlsServerKeyExchangeEcdhe { public: bool Parse(const DataBuffer& buffer) { @@ -317,6 +361,53 @@ TEST_P(TlsConnectStream, ConnectAndServerRenegotiate) { CheckConnected(); } +TEST_P(TlsConnectStream, ConnectStaticRSA) { + DisableDheCiphers(); + Connect(); + client_->CheckKEAType(ssl_kea_rsa); +} + +// Test that a totally bogus EPMS is handled correctly. +// This test is stream so we can catch the bad_record_mac alert. +TEST_P(TlsConnectStream, ConnectStaticRSABogusCKE) { + DisableDheCiphers(); + TlsInspectorReplaceHandshakeMessage* i1 = + new TlsInspectorReplaceHandshakeMessage(kTlsHandshakeClientKeyExchange, + DataBuffer( + kBogusClientKeyExchange, + sizeof(kBogusClientKeyExchange))); + client_->SetPacketFilter(i1); + auto alert_recorder = new TlsAlertRecorder(); + server_->SetPacketFilter(alert_recorder); + ConnectExpectFail(); + EXPECT_EQ(kTlsAlertFatal, alert_recorder->level()); + EXPECT_EQ(kTlsAlertBadRecordMac, alert_recorder->description()); +} + +// Test that a PMS with a bogus version number is handled correctly. +// This test is stream so we can catch the bad_record_mac alert. +TEST_P(TlsConnectStream, ConnectStaticRSABogusPMSVersionDetect) { + DisableDheCiphers(); + client_->SetPacketFilter(new TlsInspectorClientHelloVersionChanger( + server_)); + auto alert_recorder = new TlsAlertRecorder(); + server_->SetPacketFilter(alert_recorder); + ConnectExpectFail(); + EXPECT_EQ(kTlsAlertFatal, alert_recorder->level()); + EXPECT_EQ(kTlsAlertBadRecordMac, alert_recorder->description()); +} + +// Test that a PMS with a bogus version number is ignored when +// rollback detection is disabled. This is a positive control for +// ConnectStaticRSABogusPMSVersionDetect. +TEST_P(TlsConnectGeneric, ConnectStaticRSABogusPMSVersionIgnore) { + DisableDheCiphers(); + client_->SetPacketFilter(new TlsInspectorClientHelloVersionChanger( + server_)); + server_->DisableRollbackDetection(); + Connect(); +} + TEST_P(TlsConnectStream, ConnectEcdhe) { EnableSomeEcdheCiphers(); Connect(); @@ -429,6 +520,126 @@ TEST_P(TlsConnectStream, ShortRead) { ASSERT_EQ(1200U, client_->received_bytes()); } +TEST_P(TlsConnectGeneric, ConnectExtendedMasterSecret) { + EnableExtendedMasterSecret(); + Connect(); + ResetRsa(); + ExpectResumption(RESUME_SESSIONID); + EnableExtendedMasterSecret(); + Connect(); +} + + +TEST_P(TlsConnectGeneric, ConnectExtendedMasterSecretStaticRSA) { + DisableDheCiphers(); + EnableExtendedMasterSecret(); + Connect(); +} + +// This test is stream so we can catch the bad_record_mac alert. +TEST_P(TlsConnectStream, ConnectExtendedMasterSecretStaticRSABogusCKE) { + DisableDheCiphers(); + EnableExtendedMasterSecret(); + TlsInspectorReplaceHandshakeMessage* inspect = + new TlsInspectorReplaceHandshakeMessage(kTlsHandshakeClientKeyExchange, + DataBuffer( + kBogusClientKeyExchange, + sizeof(kBogusClientKeyExchange))); + client_->SetPacketFilter(inspect); + auto alert_recorder = new TlsAlertRecorder(); + server_->SetPacketFilter(alert_recorder); + ConnectExpectFail(); + EXPECT_EQ(kTlsAlertFatal, alert_recorder->level()); + EXPECT_EQ(kTlsAlertBadRecordMac, alert_recorder->description()); +} + +// This test is stream so we can catch the bad_record_mac alert. +TEST_P(TlsConnectStream, ConnectExtendedMasterSecretStaticRSABogusPMSVersionDetect) { + DisableDheCiphers(); + EnableExtendedMasterSecret(); + client_->SetPacketFilter(new TlsInspectorClientHelloVersionChanger( + server_)); + auto alert_recorder = new TlsAlertRecorder(); + server_->SetPacketFilter(alert_recorder); + ConnectExpectFail(); + EXPECT_EQ(kTlsAlertFatal, alert_recorder->level()); + EXPECT_EQ(kTlsAlertBadRecordMac, alert_recorder->description()); +} + +TEST_P(TlsConnectStream, ConnectExtendedMasterSecretStaticRSABogusPMSVersionIgnore) { + DisableDheCiphers(); + EnableExtendedMasterSecret(); + client_->SetPacketFilter(new TlsInspectorClientHelloVersionChanger( + server_)); + server_->DisableRollbackDetection(); + Connect(); +} + +TEST_P(TlsConnectGeneric, ConnectExtendedMasterSecretECDHE) { + EnableExtendedMasterSecret(); + EnableSomeEcdheCiphers(); + Connect(); + + ResetRsa(); + EnableExtendedMasterSecret(); + EnableSomeEcdheCiphers(); + ExpectResumption(RESUME_SESSIONID); + Connect(); +} + +TEST_P(TlsConnectGeneric, ConnectExtendedMasterSecretTicket) { + ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET); + EnableExtendedMasterSecret(); + Connect(); + + ResetRsa(); + ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET); + + EnableExtendedMasterSecret(); + ExpectResumption(RESUME_TICKET); + Connect(); +} + +TEST_P(TlsConnectGeneric, + ConnectExtendedMasterSecretClientOnly) { + client_->EnableExtendedMasterSecret(); + ExpectExtendedMasterSecret(false); + Connect(); +} + +TEST_P(TlsConnectGeneric, + ConnectExtendedMasterSecretServerOnly) { + server_->EnableExtendedMasterSecret(); + ExpectExtendedMasterSecret(false); + Connect(); +} + +TEST_P(TlsConnectGeneric, + ConnectExtendedMasterSecretResumeWithout) { + EnableExtendedMasterSecret(); + Connect(); + + ResetRsa(); + server_->EnableExtendedMasterSecret(); + auto alert_recorder = new TlsAlertRecorder(); + server_->SetPacketFilter(alert_recorder); + ConnectExpectFail(); + EXPECT_EQ(kTlsAlertFatal, alert_recorder->level()); + EXPECT_EQ(kTlsAlertHandshakeFailure, alert_recorder->description()); +} + +TEST_P(TlsConnectGeneric, + ConnectNormalResumeWithExtendedMasterSecret) { + ConfigureSessionCache(RESUME_SESSIONID, RESUME_SESSIONID); + ExpectExtendedMasterSecret(false); + Connect(); + + ResetRsa(); + EnableExtendedMasterSecret(); + ExpectResumption(RESUME_NONE); + Connect(); +} + INSTANTIATE_TEST_CASE_P(VariantsStream10, TlsConnectGeneric, ::testing::Combine( TlsConnectTestBase::kTlsModesStream, diff --git a/external_tests/ssl_gtest/tls_agent.cc b/external_tests/ssl_gtest/tls_agent.cc index f6fb3ae47..3fb9374eb 100644 --- a/external_tests/ssl_gtest/tls_agent.cc +++ b/external_tests/ssl_gtest/tls_agent.cc @@ -408,6 +408,31 @@ void TlsAgent::Connected() { SetState(STATE_CONNECTED); } +void TlsAgent::EnableExtendedMasterSecret() { + ASSERT_TRUE(EnsureTlsSetup()); + + SECStatus rv = SSL_OptionSet(ssl_fd_, + SSL_ENABLE_EXTENDED_MASTER_SECRET, + PR_TRUE); + + ASSERT_EQ(SECSuccess, rv); +} + +void TlsAgent::CheckExtendedMasterSecret(bool expected) { + ASSERT_EQ(expected, info_.extendedMasterSecretUsed) + << "unexpected extended master secret state for " << name_; +} + +void TlsAgent::DisableRollbackDetection() { + ASSERT_TRUE(EnsureTlsSetup()); + + SECStatus rv = SSL_OptionSet(ssl_fd_, + SSL_ROLLBACK_DETECTION, + PR_FALSE); + + ASSERT_EQ(SECSuccess, rv); +} + void TlsAgent::Handshake() { SECStatus rv = SSL_ForceHandshake(ssl_fd_); if (rv == SECSuccess) { diff --git a/external_tests/ssl_gtest/tls_agent.h b/external_tests/ssl_gtest/tls_agent.h index 5e5901528..db0d25abe 100644 --- a/external_tests/ssl_gtest/tls_agent.h +++ b/external_tests/ssl_gtest/tls_agent.h @@ -91,6 +91,9 @@ class TlsAgent : public PollTarget { void SendData(size_t bytes, size_t blocksize = 1024); void ReadBytes(); void ResetSentBytes(); // Hack to test drops. + void EnableExtendedMasterSecret(); + void CheckExtendedMasterSecret(bool expected); + void DisableRollbackDetection(); State state() const { return state_; } diff --git a/external_tests/ssl_gtest/tls_connect.cc b/external_tests/ssl_gtest/tls_connect.cc index 3e8a77d7d..536940c31 100644 --- a/external_tests/ssl_gtest/tls_connect.cc +++ b/external_tests/ssl_gtest/tls_connect.cc @@ -59,7 +59,8 @@ TlsConnectTestBase::TlsConnectTestBase(Mode mode, uint16_t version) server_(new TlsAgent("server", TlsAgent::SERVER, mode_, ssl_kea_rsa)), version_(version), expected_resumption_mode_(RESUME_NONE), - session_ids_() { + session_ids_(), + expect_extended_master_secret_(false) { std::cerr << "Version: " << mode_ << " " << VersionString(version_) << std::endl; } @@ -139,6 +140,12 @@ void TlsConnectTestBase::Handshake() { 5000); } +void TlsConnectTestBase::EnableExtendedMasterSecret() { + client_->EnableExtendedMasterSecret(); + server_->EnableExtendedMasterSecret(); + ExpectExtendedMasterSecret(true); +} + void TlsConnectTestBase::Connect() { server_->StartConnect(); client_->StartConnect(); @@ -176,6 +183,8 @@ void TlsConnectTestBase::CheckConnected() { session_ids_.push_back(sid_c1); CheckResumption(expected_resumption_mode_); + // Check whether the extended master secret extension was negotiated. + CheckExtendedMasterSecret(); } void TlsConnectTestBase::ConnectExpectFail() { @@ -258,6 +267,15 @@ void TlsConnectTestBase::SendReceive() { ASSERT_EQ(50U, server_->received_bytes()); } +void TlsConnectTestBase::ExpectExtendedMasterSecret(bool expected) { + expect_extended_master_secret_ = expected; +} + +void TlsConnectTestBase::CheckExtendedMasterSecret() { + client_->CheckExtendedMasterSecret(expect_extended_master_secret_); + server_->CheckExtendedMasterSecret(expect_extended_master_secret_); +} + TlsConnectGeneric::TlsConnectGeneric() : TlsConnectTestBase(TlsConnectTestBase::ToMode(std::get<0>(GetParam())), std::get<1>(GetParam())) {} diff --git a/external_tests/ssl_gtest/tls_connect.h b/external_tests/ssl_gtest/tls_connect.h index 90c72024c..3a4f2568c 100644 --- a/external_tests/ssl_gtest/tls_connect.h +++ b/external_tests/ssl_gtest/tls_connect.h @@ -62,15 +62,16 @@ class TlsConnectTestBase : public ::testing::Test { void ExpectResumption(SessionResumptionMode expected); void EnableSomeEcdheCiphers(); void DisableDheCiphers(); + void EnableExtendedMasterSecret(); void ConfigureSessionCache(SessionResumptionMode client, SessionResumptionMode server); void EnableAlpn(); void EnableSrtp(); void CheckSrtp() const; void SendReceive(); + void ExpectExtendedMasterSecret(bool expected); protected: - Mode mode_; TlsAgent* client_; TlsAgent* server_; @@ -81,6 +82,9 @@ class TlsConnectTestBase : public ::testing::Test { private: void Reset(const std::string& server_name, SSLKEAType kea); void CheckResumption(SessionResumptionMode expected); + void CheckExtendedMasterSecret(); + + bool expect_extended_master_secret_; }; // A TLS-only test base. diff --git a/external_tests/ssl_gtest/tls_filter.cc b/external_tests/ssl_gtest/tls_filter.cc index 2430cfefd..07654eed7 100644 --- a/external_tests/ssl_gtest/tls_filter.cc +++ b/external_tests/ssl_gtest/tls_filter.cc @@ -187,6 +187,18 @@ bool TlsInspectorRecordHandshakeMessage::FilterHandshake( return false; } + +bool TlsInspectorReplaceHandshakeMessage::FilterHandshake( + uint16_t version, uint8_t handshake_type, + const DataBuffer& input, DataBuffer* output) { + if (handshake_type == handshake_type_) { + *output = buffer_; + return true; + } + + return false; +} + bool TlsAlertRecorder::FilterRecord(uint8_t content_type, uint16_t version, const DataBuffer& input, DataBuffer* output) { if (level_ == kTlsAlertFatal) { // already fatal diff --git a/external_tests/ssl_gtest/tls_filter.h b/external_tests/ssl_gtest/tls_filter.h index 49593b363..1eec64b26 100644 --- a/external_tests/ssl_gtest/tls_filter.h +++ b/external_tests/ssl_gtest/tls_filter.h @@ -75,6 +75,21 @@ class TlsInspectorRecordHandshakeMessage : public TlsHandshakeFilter { DataBuffer buffer_; }; +// Replace all instances of a handshake message. +class TlsInspectorReplaceHandshakeMessage : public TlsHandshakeFilter { + public: + TlsInspectorReplaceHandshakeMessage(uint8_t handshake_type, + const DataBuffer& replacement) + : handshake_type_(handshake_type), buffer_(replacement) {} + + virtual bool FilterHandshake(uint16_t version, uint8_t handshake_type, + const DataBuffer& input, DataBuffer* output); + + private: + uint8_t handshake_type_; + DataBuffer buffer_; +}; + // Records an alert. If an alert has already been recorded, it won't save the // new alert unless the old alert is a warning and the new one is fatal. class TlsAlertRecorder : public TlsRecordFilter { diff --git a/external_tests/ssl_gtest/tls_parser.h b/external_tests/ssl_gtest/tls_parser.h index a17933047..866f5fe12 100644 --- a/external_tests/ssl_gtest/tls_parser.h +++ b/external_tests/ssl_gtest/tls_parser.h @@ -27,11 +27,13 @@ const uint8_t kTlsHandshakeClientHello = 1; const uint8_t kTlsHandshakeServerHello = 2; const uint8_t kTlsHandshakeCertificate = 11; const uint8_t kTlsHandshakeServerKeyExchange = 12; +const uint8_t kTlsHandshakeClientKeyExchange = 16; const uint8_t kTlsAlertWarning = 1; const uint8_t kTlsAlertFatal = 2; const uint8_t kTlsAlertUnexpectedMessage = 10; +const uint8_t kTlsAlertBadRecordMac = 20; const uint8_t kTlsAlertHandshakeFailure = 40; const uint8_t kTlsAlertIllegalParameter = 47; const uint8_t kTlsAlertDecodeError = 50; diff --git a/lib/ssl/SSLerrs.h b/lib/ssl/SSLerrs.h index da5616441..602839680 100644 --- a/lib/ssl/SSLerrs.h +++ b/lib/ssl/SSLerrs.h @@ -434,3 +434,9 @@ ER3(SSL_ERROR_NO_SUPPORTED_SIGNATURE_ALGORITHM, (SSL_ERROR_BASE + 134), ER3(SSL_ERROR_UNSUPPORTED_SIGNATURE_ALGORITHM, (SSL_ERROR_BASE + 135), "The peer used an unsupported combination of signature and hash algorithm.") + +ER3(SSL_ERROR_MISSING_EXTENDED_MASTER_SECRET, (SSL_ERROR_BASE + 136), +"The peer tried to resume without a correct extended_master_secret extension") + +ER3(SSL_ERROR_UNEXPECTED_EXTENDED_MASTER_SECRET, (SSL_ERROR_BASE + 137), +"The peer tried to resume with an unexpected extended_master_secret extension") diff --git a/lib/ssl/derive.c b/lib/ssl/derive.c index b7c38c30b..8b58b800d 100644 --- a/lib/ssl/derive.c +++ b/lib/ssl/derive.c @@ -431,7 +431,7 @@ key_and_mac_derive_fail: * so isRSA is always true. */ SECStatus -ssl3_MasterKeyDeriveBypass( +ssl3_MasterSecretDeriveBypass( ssl3CipherSpec * pwSpec, const unsigned char * cr, const unsigned char * sr, diff --git a/lib/ssl/ssl.h b/lib/ssl/ssl.h index 40f8476d5..2a527693b 100644 --- a/lib/ssl/ssl.h +++ b/lib/ssl/ssl.h @@ -196,6 +196,14 @@ SSL_IMPORT PRFileDesc *DTLS_ImportFD(PRFileDesc *model, PRFileDesc *fd); */ #define SSL_ENABLE_SERVER_DHE 29 +/* Use draft-ietf-tls-session-hash. Controls whether we offer the + * extended_master_secret extension which, when accepted, hashes + * the handshake transcript into the master secret. This option is + * disabled by default. + */ +#define SSL_ENABLE_EXTENDED_MASTER_SECRET 30 + + #ifdef SSL_DEPRECATED_FUNCTION /* Old deprecated function names */ SSL_IMPORT SECStatus SSL_Enable(PRFileDesc *fd, int option, PRBool on); diff --git a/lib/ssl/ssl3con.c b/lib/ssl/ssl3con.c index 717ece17a..877f1fcb0 100644 --- a/lib/ssl/ssl3con.c +++ b/lib/ssl/ssl3con.c @@ -62,6 +62,10 @@ static SECStatus ssl3_UpdateHandshakeHashes( sslSocket *ss, const unsigned char *b, unsigned int l); static SECOidTag ssl3_TLSHashAlgorithmToOID(SSLHashType hashFunc); +static SECStatus ssl3_ComputeHandshakeHashes(sslSocket *ss, + ssl3CipherSpec *spec, + SSL3Hashes *hashes, + PRUint32 sender); static SECStatus ssl3_FlushHandshakeMessages(sslSocket *ss, PRInt32 flags); static SECStatus Null_Cipher(void *ctx, unsigned char *output, int *outputLen, @@ -2177,7 +2181,11 @@ fail: * Sets error code, but caller probably should override to disambiguate. * NULL pms means re-use old master_secret. * - * This code is common to the bypass and PKCS11 execution paths. + * This code is common to the bypass and PKCS11 execution paths. For + * the bypass case, pms is NULL. If the old master secret is reused, + * pms is NULL and the master secret is already in either + * pwSpec->msItem.len (the bypass case) or pwSpec->master_secret. + * * For the bypass case, pms is NULL. */ SECStatus @@ -3583,13 +3591,70 @@ ssl3_HandleChangeCipherSpecs(sslSocket *ss, sslBuffer *buf) return SECSuccess; } -/* This method uses PKCS11 to derive the MS from the PMS, where PMS -** is a PKCS11 symkey. This is used in all cases except the -** "triple bypass" with RSA key exchange. -** Called from ssl3_InitPendingCipherSpec. prSpec is pwSpec. +/* This method completes the derivation of the MS from the PMS. +** +** 1. Derive the MS, if possible, else return an error. +** +** 2. Check the version if |pms_version| is non-zero and if wrong, +** return an error. +** +** 3. If |msp| is nonzero, return MS in |*msp|. + +** Called from: +** ssl3_ComputeMasterSecretInt +** tls_ComputeExtendedMasterSecretInt */ static SECStatus -ssl3_DeriveMasterSecret(sslSocket *ss, PK11SymKey *pms) +ssl3_ComputeMasterSecretFinish(sslSocket *ss, + CK_MECHANISM_TYPE master_derive, + CK_MECHANISM_TYPE key_derive, + CK_VERSION *pms_version, + SECItem *params, CK_FLAGS keyFlags, + PK11SymKey *pms, PK11SymKey **msp) +{ + PK11SymKey *ms = NULL; + + ms = PK11_DeriveWithFlags(pms, master_derive, + params, key_derive, + CKA_DERIVE, 0, keyFlags); + if (!ms) { + ssl_MapLowLevelError(SSL_ERROR_SESSION_KEY_GEN_FAILURE); + return SECFailure; + } + + if (pms_version && ss->opt.detectRollBack) { + SSL3ProtocolVersion client_version; + client_version = pms_version->major << 8 | pms_version->minor; + + if (IS_DTLS(ss)) { + client_version = dtls_DTLSVersionToTLSVersion(client_version); + } + + if (client_version != ss->clientHelloVersion) { + /* Destroy MS. Version roll-back detected. */ + PK11_FreeSymKey(ms); + ssl_MapLowLevelError(SSL_ERROR_SESSION_KEY_GEN_FAILURE); + return SECFailure; + } + } + + if (msp) { + *msp = ms; + } else { + PK11_FreeSymKey(ms); + } + + return SECSuccess; +} + +/* Compute the ordinary (pre draft-ietf-tls-session-hash) master + ** secret and return it in |*msp|. + ** + ** Called from: ssl3_ComputeMasterSecret + */ +static SECStatus +ssl3_ComputeMasterSecretInt(sslSocket *ss, PK11SymKey *pms, + PK11SymKey **msp) { ssl3CipherSpec * pwSpec = ss->ssl3.pwSpec; const ssl3KEADef *kea_def= ss->ssl3.hs.kea_def; @@ -3599,26 +3664,23 @@ ssl3_DeriveMasterSecret(sslSocket *ss, PK11SymKey *pms) (pwSpec->version > SSL_LIBRARY_VERSION_3_0)); PRBool isTLS12= (PRBool)(isTLS && pwSpec->version >= SSL_LIBRARY_VERSION_TLS_1_2); - /* + /* * Whenever isDH is true, we need to use CKM_TLS_MASTER_KEY_DERIVE_DH * which, unlike CKM_TLS_MASTER_KEY_DERIVE, converts arbitrary size - * data into a 48-byte value. + * data into a 48-byte value, and does not expect to return the version. */ PRBool isDH = (PRBool) ((ss->ssl3.hs.kea_def->exchKeyType == kt_dh) || (ss->ssl3.hs.kea_def->exchKeyType == kt_ecdh)); - SECStatus rv = SECFailure; CK_MECHANISM_TYPE master_derive; CK_MECHANISM_TYPE key_derive; SECItem params; CK_FLAGS keyFlags; CK_VERSION pms_version; + CK_VERSION *pms_version_ptr = NULL; /* master_params may be used as a CK_SSL3_MASTER_KEY_DERIVE_PARAMS */ CK_TLS12_MASTER_KEY_DERIVE_PARAMS master_params; unsigned int master_params_len; - PORT_Assert( ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss)); - PORT_Assert( ss->opt.noLocks || ssl_HaveSpecWriteLock(ss)); - PORT_Assert(ss->ssl3.prSpec == ss->ssl3.pwSpec); if (isTLS12) { if(isDH) master_derive = CKM_TLS12_MASTER_KEY_DERIVE_DH; else master_derive = CKM_TLS12_MASTER_KEY_DERIVE; @@ -3636,93 +3698,142 @@ ssl3_DeriveMasterSecret(sslSocket *ss, PK11SymKey *pms) keyFlags = 0; } - if (pms || !pwSpec->master_secret) { - if (isDH) { - master_params.pVersion = NULL; - } else { - master_params.pVersion = &pms_version; - } - master_params.RandomInfo.pClientRandom = cr; - master_params.RandomInfo.ulClientRandomLen = SSL3_RANDOM_LENGTH; - master_params.RandomInfo.pServerRandom = sr; - master_params.RandomInfo.ulServerRandomLen = SSL3_RANDOM_LENGTH; - if (isTLS12) { - master_params.prfHashMechanism = CKM_SHA256; - master_params_len = sizeof(CK_TLS12_MASTER_KEY_DERIVE_PARAMS); - } else { - master_params_len = sizeof(CK_SSL3_MASTER_KEY_DERIVE_PARAMS); - } + if (!isDH) { + pms_version_ptr = &pms_version; + } - params.data = (unsigned char *) &master_params; - params.len = master_params_len; + master_params.pVersion = pms_version_ptr; + master_params.RandomInfo.pClientRandom = cr; + master_params.RandomInfo.ulClientRandomLen = SSL3_RANDOM_LENGTH; + master_params.RandomInfo.pServerRandom = sr; + master_params.RandomInfo.ulServerRandomLen = SSL3_RANDOM_LENGTH; + if (isTLS12) { + master_params.prfHashMechanism = CKM_SHA256; + master_params_len = sizeof(CK_TLS12_MASTER_KEY_DERIVE_PARAMS); + } else { + /* prfHashMechanism is not relevant with this PRF */ + master_params_len = sizeof(CK_SSL3_MASTER_KEY_DERIVE_PARAMS); } - if (pms != NULL) { -#if defined(TRACE) - if (ssl_trace >= 100) { - SECStatus extractRV = PK11_ExtractKeyValue(pms); - if (extractRV == SECSuccess) { - SECItem * keyData = PK11_GetKeyData(pms); - if (keyData && keyData->data && keyData->len) { - ssl_PrintBuf(ss, "Pre-Master Secret", - keyData->data, keyData->len); - } - } - } -#endif - pwSpec->master_secret = PK11_DeriveWithFlags(pms, master_derive, - ¶ms, key_derive, CKA_DERIVE, 0, keyFlags); - if (!isDH && pwSpec->master_secret && ss->opt.detectRollBack) { - SSL3ProtocolVersion client_version; - client_version = pms_version.major << 8 | pms_version.minor; + params.data = (unsigned char *) &master_params; + params.len = master_params_len; - if (IS_DTLS(ss)) { - client_version = dtls_DTLSVersionToTLSVersion(client_version); - } + return ssl3_ComputeMasterSecretFinish(ss, master_derive, key_derive, + pms_version_ptr, ¶ms, + keyFlags, pms, msp); +} - if (client_version != ss->clientHelloVersion) { - /* Destroy it. Version roll-back detected. */ - PK11_FreeSymKey(pwSpec->master_secret); - pwSpec->master_secret = NULL; - } - } - if (pwSpec->master_secret == NULL) { - /* Generate a faux master secret in the same slot as the old one. */ - PK11SlotInfo * slot = PK11_GetSlotFromKey((PK11SymKey *)pms); - PK11SymKey * fpms = ssl3_GenerateRSAPMS(ss, pwSpec, slot); +/* Compute the draft-ietf-tls-session-hash master +** secret and return it in |*msp|. +** +** Called from: ssl3_ComputeMasterSecret +*/ +static SECStatus +tls_ComputeExtendedMasterSecretInt(sslSocket *ss, PK11SymKey *pms, + PK11SymKey **msp) +{ + ssl3CipherSpec *pwSpec = ss->ssl3.pwSpec; + CK_NSS_TLS_EXTENDED_MASTER_KEY_DERIVE_PARAMS extended_master_params; + SSL3Hashes hashes; + /* + * Determine whether to use the DH/ECDH or RSA derivation modes. + */ + /* + * TODO(ekr@rtfm.com): Verify that the slot can handle this key expansion + * mode. Bug 1198298 */ + PRBool isDH = (PRBool) ((ss->ssl3.hs.kea_def->exchKeyType == kt_dh) || + (ss->ssl3.hs.kea_def->exchKeyType == kt_ecdh)); + CK_MECHANISM_TYPE master_derive; + CK_MECHANISM_TYPE key_derive; + SECItem params; + const CK_FLAGS keyFlags = CKF_SIGN | CKF_VERIFY; + CK_VERSION pms_version; + CK_VERSION *pms_version_ptr = NULL; + SECStatus rv; - PK11_FreeSlot(slot); - if (fpms != NULL) { - pwSpec->master_secret = PK11_DeriveWithFlags(fpms, - master_derive, ¶ms, key_derive, - CKA_DERIVE, 0, keyFlags); - PK11_FreeSymKey(fpms); - } - } + rv = ssl3_ComputeHandshakeHashes(ss, pwSpec, &hashes, 0); + if (rv != SECSuccess) { + PORT_Assert(0); /* Should never fail */ + ssl_MapLowLevelError(SSL_ERROR_SESSION_KEY_GEN_FAILURE); + return SECFailure; } - if (pwSpec->master_secret == NULL) { - /* Generate a faux master secret from the internal slot. */ - PK11SlotInfo * slot = PK11_GetInternalSlot(); - PK11SymKey * fpms = ssl3_GenerateRSAPMS(ss, pwSpec, slot); - PK11_FreeSlot(slot); - if (fpms != NULL) { - pwSpec->master_secret = PK11_DeriveWithFlags(fpms, - master_derive, ¶ms, key_derive, - CKA_DERIVE, 0, keyFlags); - if (pwSpec->master_secret == NULL) { - pwSpec->master_secret = fpms; /* use the fpms as the master. */ - fpms = NULL; - } - } - if (fpms) { - PK11_FreeSymKey(fpms); - } + if (isDH) { + master_derive = CKM_NSS_TLS_EXTENDED_MASTER_KEY_DERIVE_DH; + } else { + master_derive = CKM_NSS_TLS_EXTENDED_MASTER_KEY_DERIVE; + pms_version_ptr = &pms_version; } - if (pwSpec->master_secret == NULL) { - ssl_MapLowLevelError(SSL_ERROR_SESSION_KEY_GEN_FAILURE); - return rv; + + if (pwSpec->version >= SSL_LIBRARY_VERSION_TLS_1_2) { + /* TLS 1.2 */ + extended_master_params.prfHashMechanism = CKM_SHA256; + key_derive = CKM_TLS12_KEY_AND_MAC_DERIVE; + } else { + /* TLS < 1.2 */ + extended_master_params.prfHashMechanism = CKM_TLS_PRF; + key_derive = CKM_TLS_KEY_AND_MAC_DERIVE; + } + + extended_master_params.pVersion = pms_version_ptr; + extended_master_params.pSessionHash = hashes.u.raw; + extended_master_params.ulSessionHashLen = hashes.len; + + params.data = (unsigned char *) &extended_master_params; + params.len = sizeof extended_master_params; + + return ssl3_ComputeMasterSecretFinish(ss, master_derive, key_derive, + pms_version_ptr, ¶ms, + keyFlags, pms, msp); +} + + +/* Wrapper method to compute the master secret and return it in |*msp|. +** +** Called from ssl3_ComputeMasterSecret +*/ +static SECStatus +ssl3_ComputeMasterSecret(sslSocket *ss, PK11SymKey *pms, + PK11SymKey **msp) +{ + PORT_Assert(pms != NULL); + PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss)); + PORT_Assert(ss->ssl3.prSpec == ss->ssl3.pwSpec); + + if (ssl3_ExtensionNegotiated(ss, ssl_extended_master_secret_xtn)) { + return tls_ComputeExtendedMasterSecretInt(ss, pms, msp); + } else { + return ssl3_ComputeMasterSecretInt(ss, pms, msp); + } +} + +/* This method uses PKCS11 to derive the MS from the PMS, where PMS +** is a PKCS11 symkey. We call ssl3_ComputeMasterSecret to do the +** computations and then modify the pwSpec->state as a side effect. +** +** This is used in all cases except the "triple bypass" with RSA key +** exchange. +** +** Called from ssl3_InitPendingCipherSpec. prSpec is pwSpec. +*/ +static SECStatus +ssl3_DeriveMasterSecret(sslSocket *ss, PK11SymKey *pms) +{ + SECStatus rv; + PK11SymKey* ms = NULL; + ssl3CipherSpec *pwSpec = ss->ssl3.pwSpec; + + PORT_Assert( ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss)); + PORT_Assert( ss->opt.noLocks || ssl_HaveSpecWriteLock(ss)); + PORT_Assert(ss->ssl3.prSpec == ss->ssl3.pwSpec); + + if (pms) { + rv = ssl3_ComputeMasterSecret(ss, pms, &ms); + pwSpec->master_secret = ms; + if (rv != SECSuccess) + return rv; } + #ifndef NO_PKCS11_BYPASS if (ss->opt.bypassPKCS11) { SECItem * keydata; @@ -3733,7 +3844,7 @@ ssl3_DeriveMasterSecret(sslSocket *ss, PK11SymKey *pms) rv = PK11_ExtractKeyValue(pwSpec->master_secret); if (rv != SECSuccess) { return rv; - } + } /* This returns the address of the secItem inside the key struct, * not a copy or a reference. So, there's no need to free it. */ @@ -3748,10 +3859,10 @@ ssl3_DeriveMasterSecret(sslSocket *ss, PK11SymKey *pms) } } #endif + return SECSuccess; } - /* * Derive encryption and MAC Keys (and IVs) from master secret * Sets a useful error code when returning SECFailure. @@ -4584,11 +4695,6 @@ ssl3_ComputeHandshakeHashes(sslSocket * ss, /* compute them without PKCS11 */ PRUint64 sha_cx[MAX_MAC_CONTEXT_LLONGS]; - if (!spec->msItem.data) { - PORT_SetError(SSL_ERROR_RX_UNEXPECTED_HANDSHAKE); - return SECFailure; - } - ss->ssl3.hs.sha_clone(sha_cx, ss->ssl3.hs.sha_cx); ss->ssl3.hs.sha_obj->end(sha_cx, hashes->u.raw, &hashes->len, sizeof(hashes->u.raw)); @@ -4607,15 +4713,15 @@ ssl3_ComputeHandshakeHashes(sslSocket * ss, #define md5cx ((MD5Context *)md5_cx) #define shacx ((SHA1Context *)sha_cx) - if (!spec->msItem.data) { - PORT_SetError(SSL_ERROR_RX_UNEXPECTED_HANDSHAKE); - return SECFailure; - } - MD5_Clone (md5cx, (MD5Context *)ss->ssl3.hs.md5_cx); SHA1_Clone(shacx, (SHA1Context *)ss->ssl3.hs.sha_cx); if (!isTLS) { + if (!spec->msItem.data) { + PORT_SetError(SSL_ERROR_RX_UNEXPECTED_HANDSHAKE); + return SECFailure; + } + /* compute hashes for SSL3. */ unsigned char s[4]; @@ -4691,11 +4797,6 @@ ssl3_ComputeHandshakeHashes(sslSocket * ss, unsigned char stackBuf[1024]; unsigned char *stateBuf = NULL; - if (!spec->master_secret) { - PORT_SetError(SSL_ERROR_RX_UNEXPECTED_HANDSHAKE); - return SECFailure; - } - h = ss->ssl3.hs.sha; stateBuf = PK11_SaveContextAlloc(h, stackBuf, sizeof(stackBuf), &stateLen); @@ -4735,11 +4836,6 @@ tls12_loser: unsigned char md5StackBuf[256]; unsigned char shaStackBuf[512]; - if (!spec->master_secret) { - PORT_SetError(SSL_ERROR_RX_UNEXPECTED_HANDSHAKE); - return SECFailure; - } - md5StateBuf = PK11_SaveContextAlloc(ss->ssl3.hs.md5, md5StackBuf, sizeof md5StackBuf, &md5StateLen); if (md5StateBuf == NULL) { @@ -4757,6 +4853,11 @@ tls12_loser: sha = ss->ssl3.hs.sha; if (!isTLS) { + if (!spec->master_secret) { + PORT_SetError(SSL_ERROR_RX_UNEXPECTED_HANDSHAKE); + return SECFailure; + } + /* compute hashes for SSL3. */ unsigned char s[4]; @@ -6005,14 +6106,6 @@ sendRSAClientKeyExchange(sslSocket * ss, SECKEYPublicKey * svrPubKey) } } - rv = ssl3_InitPendingCipherSpec(ss, pms); - PK11_FreeSymKey(pms); pms = NULL; - - if (rv != SECSuccess) { - ssl_MapLowLevelError(SSL_ERROR_CLIENT_KEY_EXCHANGE_FAILURE); - goto loser; - } - rv = ssl3_AppendHandshakeHeader(ss, client_key_exchange, isTLS ? enc_pms.len + 2 : enc_pms.len); if (rv != SECSuccess) { @@ -6027,6 +6120,15 @@ sendRSAClientKeyExchange(sslSocket * ss, SECKEYPublicKey * svrPubKey) goto loser; /* err set by ssl3_AppendHandshake* */ } + rv = ssl3_InitPendingCipherSpec(ss, pms); + PK11_FreeSymKey(pms); + pms = NULL; + + if (rv != SECSuccess) { + ssl_MapLowLevelError(SSL_ERROR_CLIENT_KEY_EXCHANGE_FAILURE); + goto loser; + } + rv = SECSuccess; loser: @@ -6096,14 +6198,6 @@ sendDHClientKeyExchange(sslSocket * ss, SECKEYPublicKey * svrPubKey) SECKEY_DestroyPrivateKey(privKey); privKey = NULL; - rv = ssl3_InitPendingCipherSpec(ss, pms); - PK11_FreeSymKey(pms); pms = NULL; - - if (rv != SECSuccess) { - ssl_MapLowLevelError(SSL_ERROR_CLIENT_KEY_EXCHANGE_FAILURE); - goto loser; - } - rv = ssl3_AppendHandshakeHeader(ss, client_key_exchange, pubKey->u.dh.publicValue.len + 2); if (rv != SECSuccess) { @@ -6119,8 +6213,16 @@ sendDHClientKeyExchange(sslSocket * ss, SECKEYPublicKey * svrPubKey) goto loser; /* err set by ssl3_AppendHandshake* */ } - rv = SECSuccess; + rv = ssl3_InitPendingCipherSpec(ss, pms); + PK11_FreeSymKey(pms); + pms = NULL; + if (rv != SECSuccess) { + ssl_MapLowLevelError(SSL_ERROR_CLIENT_KEY_EXCHANGE_FAILURE); + goto loser; + } + + rv = SECSuccess; loser: @@ -6517,6 +6619,32 @@ ssl3_HandleServerHello(sslSocket *ss, SSL3Opaque *b, PRUint32 length) SECItem wrappedMS; /* wrapped master secret. */ + /* [draft-ietf-tls-session-hash-06; Section 5.3] + * + * o If the original session did not use the "extended_master_secret" + * extension but the new ServerHello contains the extension, the + * client MUST abort the handshake. + */ + if (!sid->u.ssl3.keys.extendedMasterSecretUsed && + ssl3_ExtensionNegotiated(ss, ssl_extended_master_secret_xtn)) { + errCode = SSL_ERROR_UNEXPECTED_EXTENDED_MASTER_SECRET; + goto alert_loser; + } + + /* + * o If the original session used an extended master secret but the new + * ServerHello does not contain the "extended_master_secret" + * extension, the client SHOULD abort the handshake. + * + * TODO(ekr@rtfm.com): Add option to refuse to resume when EMS is not + * used at all (bug 1176526). + */ + if (sid->u.ssl3.keys.extendedMasterSecretUsed && + !ssl3_ExtensionNegotiated(ss, ssl_extended_master_secret_xtn)) { + errCode = SSL_ERROR_MISSING_EXTENDED_MASTER_SECRET; + goto alert_loser; + } + ss->sec.authAlgorithm = sid->authAlgorithm; ss->sec.authKeyBits = sid->authKeyBits; ss->sec.keaType = sid->keaType; @@ -6618,7 +6746,7 @@ ssl3_HandleServerHello(sslSocket *ss, SSL3Opaque *b, PRUint32 length) ss->sec.peerCert = CERT_DupCertificate(sid->peerCert); } - /* NULL value for PMS signifies re-use of the old MS */ + /* NULL value for PMS because we are reusing the old MS */ rv = ssl3_InitPendingCipherSpec(ss, NULL); if (rv != SECSuccess) { goto alert_loser; /* err code was set */ @@ -6647,6 +6775,9 @@ ssl3_HandleServerHello(sslSocket *ss, SSL3Opaque *b, PRUint32 length) 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); + ss->ssl3.hs.isResuming = PR_FALSE; if (ss->ssl3.hs.kea_def->signKeyType != sign_null) { /* All current cipher suites other than those with sign_null (i.e., @@ -7584,6 +7715,7 @@ ssl3_NewSessionID(sslSocket *ss, PRBool is_server) sid->u.ssl3.policy = SSL_ALLOWED; sid->u.ssl3.clientWriteKey = NULL; sid->u.ssl3.serverWriteKey = NULL; + sid->u.ssl3.keys.extendedMasterSecretUsed = PR_FALSE; if (is_server) { SECStatus rv; @@ -8144,6 +8276,8 @@ compression_found: /* If there are any failures while processing the old sid, * we don't consider them to be errors. Instead, We just behave * as if the client had sent us no sid to begin with, and make a new one. + * The exception here is attempts to resume extended_master_secret + * sessions without the extension, which causes an alert. */ if (sid != NULL) do { ssl3CipherSpec *pwSpec; @@ -8155,6 +8289,30 @@ compression_found: break; /* not an error */ } + /* [draft-ietf-tls-session-hash-06; Section 5.3] + * o If the original session did not use the "extended_master_secret" + * extension but the new ClientHello contains the extension, then the + * server MUST NOT perform the abbreviated handshake. Instead, it + * SHOULD continue with a full handshake (as described in + * Section 5.2) to negotiate a new session. + * + * o If the original session used the "extended_master_secret" + * extension but the new ClientHello does not contain the extension, + * the server MUST abort the abbreviated handshake. + */ + if (ssl3_ExtensionNegotiated(ss, ssl_extended_master_secret_xtn)) { + if (!sid->u.ssl3.keys.extendedMasterSecretUsed) { + break; /* not an error */ + } + } else { + if (sid->u.ssl3.keys.extendedMasterSecretUsed) { + /* Note: we do not destroy the session */ + desc = handshake_failure; + errCode = SSL_ERROR_MISSING_EXTENDED_MASTER_SECRET; + goto alert_loser; + } + } + if (ss->sec.ci.sid) { if (ss->sec.uncache) ss->sec.uncache(ss->sec.ci.sid); @@ -8295,7 +8453,7 @@ compression_found: haveSpecWriteLock = PR_FALSE; } - /* NULL value for PMS signifies re-use of the old MS */ + /* NULL value for PMS because we are re-using the old MS */ rv = ssl3_InitPendingCipherSpec(ss, NULL); if (rv != SECSuccess) { errCode = PORT_GetError(); @@ -8486,6 +8644,8 @@ compression_found: } ss->sec.ci.sid = sid; + sid->u.ssl3.keys.extendedMasterSecretUsed = + ssl3_ExtensionNegotiated(ss, ssl_extended_master_secret_xtn); ss->ssl3.hs.isResuming = PR_FALSE; ssl_GetXmitBufLock(ss); rv = ssl3_SendServerHelloSequence(ss); @@ -9496,7 +9656,6 @@ ssl3_HandleRSAClientKeyExchange(sslSocket *ss, PRUint32 length, SECKEYPrivateKey *serverKey) { - PK11SymKey * pms; #ifndef NO_PKCS11_BYPASS unsigned char * cr = (unsigned char *)&ss->ssl3.hs.client_random; unsigned char * sr = (unsigned char *)&ss->ssl3.hs.server_random; @@ -9541,6 +9700,13 @@ ssl3_HandleRSAClientKeyExchange(sslSocket *ss, #ifndef NO_PKCS11_BYPASS if (ss->opt.bypassPKCS11) { + /* We have not implemented a tls_ExtendedMasterKeyDeriveBypass + * and will not negotiate this extension in bypass mode. This + * assert just double-checks that. + */ + PORT_Assert( + !ssl3_ExtensionNegotiated(ss, ssl_extended_master_secret_xtn)); + /* TRIPLE BYPASS, get PMS directly from RSA decryption. * Use PK11_PrivDecryptPKCS1 to decrypt the PMS to a buffer, * then, check for version rollback attack, then @@ -9568,8 +9734,8 @@ ssl3_HandleRSAClientKeyExchange(sslSocket *ss, } } /* have PMS, build MS without PKCS11 */ - rv = ssl3_MasterKeyDeriveBypass(pwSpec, cr, sr, &pmsItem, isTLS, - PR_TRUE); + rv = ssl3_MasterSecretDeriveBypass(pwSpec, cr, sr, &pmsItem, isTLS, + PR_TRUE); if (rv != SECSuccess) { pwSpec->msItem.data = pwSpec->raw_master_secret; pwSpec->msItem.len = SSL3_MASTER_SECRET_LENGTH; @@ -9579,46 +9745,101 @@ ssl3_HandleRSAClientKeyExchange(sslSocket *ss, } else #endif { + PK11SymKey *tmpPms[2] = {NULL, NULL}; + PK11SlotInfo *slot; + int useFauxPms = 0; +#define currentPms tmpPms[!useFauxPms] +#define unusedPms tmpPms[useFauxPms] +#define realPms tmpPms[1] +#define fauxPms tmpPms[0] + #ifndef NO_PKCS11_BYPASS double_bypass: #endif - /* - * unwrap pms out of the incoming buffer - * Note: CKM_SSL3_MASTER_KEY_DERIVE is NOT the mechanism used to do - * the unwrap. Rather, it is the mechanism with which the - * unwrapped pms will be used. - */ - pms = PK11_PubUnwrapSymKey(serverKey, &enc_pms, - CKM_SSL3_MASTER_KEY_DERIVE, CKA_DERIVE, 0); - if (pms != NULL) { - PRINT_BUF(60, (ss, "decrypted premaster secret:", - PK11_GetKeyData(pms)->data, - PK11_GetKeyData(pms)->len)); - } else { - /* unwrap failed. Generate a bogus PMS and carry on. */ - PK11SlotInfo * slot = PK11_GetSlotFromPrivateKey(serverKey); - ssl_GetSpecWriteLock(ss); - pms = ssl3_GenerateRSAPMS(ss, ss->ssl3.prSpec, slot); - ssl_ReleaseSpecWriteLock(ss); - PK11_FreeSlot(slot); - } + /* + * Get as close to algorithm 2 from RFC 5246; Section 7.4.7.1 + * as we can within the constraints of the PKCS#11 interface. + * + * 1. Unconditionally generate a bogus PMS (what RFC 5246 + * calls R). + * 2. Attempt the RSA decryption to recover the PMS (what + * RFC 5246 calls M). + * 3. Set PMS = (M == NULL) ? R : M + * 4. Use ssl3_ComputeMasterSecret(PMS) to attempt to derive + * the MS from PMS. This includes performing the version + * check and length check. + * 5. If either the initial RSA decryption failed or + * ssl3_ComputeMasterSecret(PMS) failed, then discard + * M and set PMS = R. Else, discard R and set PMS = M. + * + * We do two derivations here because we can't rely on having + * a function that only performs the PMS version and length + * check. The only redundant cost is that this runs the PRF, + * which isn't necessary here. + */ - if (pms == NULL) { - /* last gasp. */ + /* Generate the bogus PMS (R) */ + slot = PK11_GetSlotFromPrivateKey(serverKey); + if (!PK11_DoesMechanism(slot, CKM_SSL3_MASTER_KEY_DERIVE)) { + slot = PK11_GetBestSlot(CKM_SSL3_MASTER_KEY_DERIVE, NULL); + if (!slot) { + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + } + + ssl_GetSpecWriteLock(ss); + fauxPms = ssl3_GenerateRSAPMS(ss, ss->ssl3.prSpec, slot); + ssl_ReleaseSpecWriteLock(ss); + PK11_FreeSlot(slot); + + if (fauxPms == NULL) { ssl_MapLowLevelError(SSL_ERROR_CLIENT_KEY_EXCHANGE_FAILURE); return SECFailure; } + /* + * unwrap pms out of the incoming buffer + * Note: CKM_SSL3_MASTER_KEY_DERIVE is NOT the mechanism used to do + * the unwrap. Rather, it is the mechanism with which the + * unwrapped pms will be used. + */ + realPms = PK11_PubUnwrapSymKey(serverKey, &enc_pms, + CKM_SSL3_MASTER_KEY_DERIVE, CKA_DERIVE, 0); + /* Temporarily use the PMS if unwrapping the real PMS fails. */ + useFauxPms |= (realPms == NULL); + + /* Attempt to derive the MS from the PMS. This is the only way to + * check the version field in the RSA PMS. If this fails, we + * then use the faux PMS in place of the PMS. Note that this + * operation should never fail if we are using the faux PMS + * since it is correctly formatted. */ + rv = ssl3_ComputeMasterSecret(ss, currentPms, NULL); + + /* If we succeeded, then select the true PMS and discard the + * FPMS. Else, select the FPMS and select the true PMS */ + useFauxPms |= (rv != SECSuccess); + + if (unusedPms) { + PK11_FreeSymKey(unusedPms); + } + /* This step will derive the MS from the PMS, among other things. */ - rv = ssl3_InitPendingCipherSpec(ss, pms); - PK11_FreeSymKey(pms); + rv = ssl3_InitPendingCipherSpec(ss, currentPms); + PK11_FreeSymKey(currentPms); } if (rv != SECSuccess) { SEND_ALERT return SECFailure; /* error code set by ssl3_InitPendingCipherSpec */ } + +#undef currentPms +#undef unusedPms +#undef realPms +#undef fauxPms + return SECSuccess; } diff --git a/lib/ssl/ssl3ecc.c b/lib/ssl/ssl3ecc.c index 5dbca1658..539ddbbab 100644 --- a/lib/ssl/ssl3ecc.c +++ b/lib/ssl/ssl3ecc.c @@ -319,14 +319,6 @@ ssl3_SendECDHClientKeyExchange(sslSocket * ss, SECKEYPublicKey * svrPubKey) SECKEY_DestroyPrivateKey(privKey); privKey = NULL; - rv = ssl3_InitPendingCipherSpec(ss, pms); - PK11_FreeSymKey(pms); pms = NULL; - - if (rv != SECSuccess) { - ssl_MapLowLevelError(SSL_ERROR_CLIENT_KEY_EXCHANGE_FAILURE); - goto loser; - } - rv = ssl3_AppendHandshakeHeader(ss, client_key_exchange, pubKey->u.ec.publicValue.len + 1); if (rv != SECSuccess) { @@ -343,6 +335,14 @@ ssl3_SendECDHClientKeyExchange(sslSocket * ss, SECKEYPublicKey * svrPubKey) goto loser; /* err set by ssl3_AppendHandshake* */ } + rv = ssl3_InitPendingCipherSpec(ss, pms); + PK11_FreeSymKey(pms); pms = NULL; + + if (rv != SECSuccess) { + ssl_MapLowLevelError(SSL_ERROR_CLIENT_KEY_EXCHANGE_FAILURE); + goto loser; + } + rv = SECSuccess; loser: diff --git a/lib/ssl/ssl3ext.c b/lib/ssl/ssl3ext.c index c45f29542..07d792944 100644 --- a/lib/ssl/ssl3ext.c +++ b/lib/ssl/ssl3ext.c @@ -91,6 +91,12 @@ static PRInt32 ssl3_ClientSendDraftVersionXtn(sslSocket *ss, PRBool append, PRUint32 maxBytes); static SECStatus ssl3_ServerHandleDraftVersionXtn(sslSocket *ss, PRUint16 ex_type, SECItem *data); +static PRInt32 ssl3_SendExtendedMasterSecretXtn(sslSocket *ss, PRBool append, + PRUint32 maxBytes); +static SECStatus ssl3_HandleExtendedMasterSecretXtn(sslSocket *ss, + PRUint16 ex_type, + SECItem *data); + /* * Write bytes. Using this function means the SECItem structure @@ -256,6 +262,7 @@ static const ssl3HelloExtensionHandler clientHelloHandlers[] = { { ssl_cert_status_xtn, &ssl3_ServerHandleStatusRequestXtn }, { ssl_signature_algorithms_xtn, &ssl3_ServerHandleSigAlgsXtn }, { ssl_tls13_draft_version_xtn, &ssl3_ServerHandleDraftVersionXtn }, + { ssl_extended_master_secret_xtn, &ssl3_HandleExtendedMasterSecretXtn }, { -1, NULL } }; @@ -270,6 +277,7 @@ static const ssl3HelloExtensionHandler serverHelloHandlersTLS[] = { { ssl_app_layer_protocol_xtn, &ssl3_ClientHandleAppProtoXtn }, { ssl_use_srtp_xtn, &ssl3_ClientHandleUseSRTPXtn }, { ssl_cert_status_xtn, &ssl3_ClientHandleStatusRequestXtn }, + { ssl_extended_master_secret_xtn, &ssl3_HandleExtendedMasterSecretXtn }, { -1, NULL } }; @@ -299,6 +307,7 @@ ssl3HelloExtensionSender clientHelloSendersTLS[SSL_MAX_EXTENSIONS] = { { ssl_cert_status_xtn, &ssl3_ClientSendStatusRequestXtn }, { ssl_signature_algorithms_xtn, &ssl3_ClientSendSigAlgsXtn }, { ssl_tls13_draft_version_xtn, &ssl3_ClientSendDraftVersionXtn }, + { ssl_extended_master_secret_xtn, &ssl3_SendExtendedMasterSecretXtn}, /* any extra entries will appear as { 0, NULL } */ }; @@ -1182,6 +1191,7 @@ ssl3_SendNewSessionTicket(sslSocket *ss) + cert_length /* cert */ + 1 /* server name type */ + srvNameLen /* name len + length field */ + + 1 /* extendedMasterSecretUsed */ + sizeof(ticket.ticket_lifetime_hint); padding_length = AES_BLOCK_SIZE - (ciphertext_length % AES_BLOCK_SIZE); @@ -1280,6 +1290,11 @@ ssl3_SendNewSessionTicket(sslSocket *ss) if (rv != SECSuccess) goto loser; } + /* extendedMasterSecretUsed */ + rv = ssl3_AppendNumberToItem( + &plaintext, ss->sec.ci.sid->u.ssl3.keys.extendedMasterSecretUsed, 1); + if (rv != SECSuccess) goto loser; + PORT_Assert(plaintext.len == padding_length); for (i = 0; i < padding_length; i++) plaintext.data[i] = (unsigned char)padding_length; @@ -1637,9 +1652,10 @@ ssl3_ServerHandleSessionTicketXtn(sslSocket *ss, PRUint16 ex_type, goto loser; } - /* Read ticket_version (which is ignored for now.) */ + /* Read ticket_version and reject if the version is wrong */ temp = ssl3_ConsumeHandshakeNumber(ss, 2, &buffer, &buffer_len); - if (temp < 0) goto no_ticket; + if (temp != TLS_EX_SESS_TICKET_VERSION) goto no_ticket; + parsed_session_ticket->ticket_version = (SSL3ProtocolVersion)temp; /* Read SSLVersion. */ @@ -1740,6 +1756,13 @@ ssl3_ServerHandleSessionTicketXtn(sslSocket *ss, PRUint16 ex_type, parsed_session_ticket->srvName.type = nameType; } + /* 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; + /* Done parsing. Check that all bytes have been consumed. */ if (buffer_len != padding_length) goto no_ticket; @@ -1786,6 +1809,8 @@ ssl3_ServerHandleSessionTicketXtn(sslSocket *ss, PRUint16 ex_type, 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) { @@ -2559,3 +2584,90 @@ ssl3_ServerHandleDraftVersionXtn(sslSocket * ss, PRUint16 ex_type, return SECSuccess; } + +static PRInt32 +ssl3_SendExtendedMasterSecretXtn(sslSocket * ss, PRBool append, + PRUint32 maxBytes) +{ + PRInt32 extension_length; + + if (!ss->opt.enableExtendedMS) { + return 0; + } + +#ifndef NO_PKCS11_BYPASS + /* Extended MS can only be used w/o bypass mode */ + if (ss->opt.bypassPKCS11) { + PORT_Assert(0); + PORT_SetError(PR_NOT_IMPLEMENTED_ERROR); + return -1; + } +#endif + + /* Always send the extension in this function, since the + * client always sends it and this function is only called on + * the server if we negotiated the extension. */ + extension_length = 4; /* Type + length (0) */ + if (maxBytes < extension_length) { + PORT_Assert(0); + return 0; + } + + if (append) { + SECStatus rv; + rv = ssl3_AppendHandshakeNumber(ss, ssl_extended_master_secret_xtn, 2); + if (rv != SECSuccess) + goto loser; + rv = ssl3_AppendHandshakeNumber(ss, 0, 2); + if (rv != SECSuccess) + goto loser; + ss->xtnData.advertised[ss->xtnData.numAdvertised++] = + ssl_extended_master_secret_xtn; + } + + return extension_length; + +loser: + return -1; +} + + +static SECStatus +ssl3_HandleExtendedMasterSecretXtn(sslSocket * ss, PRUint16 ex_type, + SECItem *data) +{ + if (ss->version < SSL_LIBRARY_VERSION_TLS_1_0) { + return SECSuccess; + } + + if (!ss->opt.enableExtendedMS) { + return SECSuccess; + } + +#ifndef NO_PKCS11_BYPASS + /* Extended MS can only be used w/o bypass mode */ + if (ss->opt.bypassPKCS11) { + PORT_Assert(0); + PORT_SetError(PR_NOT_IMPLEMENTED_ERROR); + return SECFailure; + } +#endif + + if (data->len != 0) { + SSL_TRC(30, ("%d: SSL3[%d]: Bogus extended master secret extension", + SSL_GETPID(), ss->fd)); + return SECFailure; + } + + SSL_DBG(("%d: SSL[%d]: Negotiated extended master secret extension.", + SSL_GETPID(), ss->fd)); + + /* Keep track of negotiated extensions. */ + ss->xtnData.negotiated[ss->xtnData.numNegotiated++] = ex_type; + + if (ss->sec.isServer) { + return ssl3_RegisterServerHelloExtensionSender( + ss, ex_type, ssl3_SendExtendedMasterSecretXtn); + } + return SECSuccess; +} diff --git a/lib/ssl/sslerr.h b/lib/ssl/sslerr.h index 4e905438e..192a10758 100644 --- a/lib/ssl/sslerr.h +++ b/lib/ssl/sslerr.h @@ -205,6 +205,9 @@ SSL_ERROR_RX_SHORT_DTLS_READ = (SSL_ERROR_BASE + 133), SSL_ERROR_NO_SUPPORTED_SIGNATURE_ALGORITHM = (SSL_ERROR_BASE + 134), SSL_ERROR_UNSUPPORTED_SIGNATURE_ALGORITHM = (SSL_ERROR_BASE + 135), +SSL_ERROR_MISSING_EXTENDED_MASTER_SECRET = (SSL_ERROR_BASE + 136), +SSL_ERROR_UNEXPECTED_EXTENDED_MASTER_SECRET = (SSL_ERROR_BASE + 137), + 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 e155a0800..41b51d8a8 100644 --- a/lib/ssl/sslimpl.h +++ b/lib/ssl/sslimpl.h @@ -346,6 +346,7 @@ typedef struct sslOptionsStr { unsigned int reuseServerECDHEKey : 1; /* 28 */ unsigned int enableFallbackSCSV : 1; /* 29 */ unsigned int enableServerDhe : 1; /* 30 */ + unsigned int enableExtendedMS : 1; /* 31 */ } sslOptions; typedef enum { sslHandshakingUndetermined = 0, @@ -518,6 +519,7 @@ typedef struct { PRUint16 wrapped_master_secret_len; PRUint8 msIsWrapped; PRUint8 resumable; + PRUint8 extendedMasterSecretUsed; } ssl3SidKeys; /* 52 bytes */ typedef struct { @@ -1070,6 +1072,7 @@ typedef struct SessionTicketStr { CK_MECHANISM_TYPE msWrapMech; PRUint16 ms_length; SSL3Opaque master_secret[48]; + PRBool extendedMasterSecretUsed; ClientIdentity client_identity; SECItem peer_cert; PRUint32 timestamp; @@ -1595,7 +1598,7 @@ extern PRBool ssl3_VersionIsSupported(SSLProtocolVariant protocolVariant, extern SECStatus ssl3_KeyAndMacDeriveBypass(ssl3CipherSpec * pwSpec, const unsigned char * cr, const unsigned char * sr, PRBool isTLS, PRBool isExport); -extern SECStatus ssl3_MasterKeyDeriveBypass( ssl3CipherSpec * pwSpec, +extern SECStatus ssl3_MasterSecretDeriveBypass( ssl3CipherSpec * pwSpec, const unsigned char * cr, const unsigned char * sr, const SECItem * pms, PRBool isTLS, PRBool isRSA); @@ -1846,7 +1849,7 @@ extern PRBool ssl_GetSessionTicketKeysPKCS11(SECKEYPrivateKey *svrPrivKey, /* Tell clients to consider tickets valid for this long. */ #define TLS_EX_SESS_TICKET_LIFETIME_HINT (2 * 24 * 60 * 60) /* 2 days */ -#define TLS_EX_SESS_TICKET_VERSION (0x0100) +#define TLS_EX_SESS_TICKET_VERSION (0x0101) extern SECStatus ssl3_ValidateNextProtoNego(const unsigned char* data, unsigned int length); diff --git a/lib/ssl/sslinfo.c b/lib/ssl/sslinfo.c index d2df8c2ec..216ab0fa0 100644 --- a/lib/ssl/sslinfo.c +++ b/lib/ssl/sslinfo.c @@ -67,6 +67,8 @@ SSL_GetChannelInfo(PRFileDesc *fd, SSLChannelInfo *info, PRUintn len) inf.creationTime = sid->creationTime; inf.lastAccessTime = sid->lastAccessTime; inf.expirationTime = sid->expirationTime; + inf.extendedMasterSecretUsed = sid->u.ssl3.keys.extendedMasterSecretUsed; + if (ss->version < SSL_LIBRARY_VERSION_3_0) { /* SSL2 */ inf.sessionIDLength = SSL2_SESSIONID_BYTES; memcpy(inf.sessionID, sid->u.ssl2.sessionID, diff --git a/lib/ssl/sslsnce.c b/lib/ssl/sslsnce.c index 6d908c86e..16bfbe49b 100644 --- a/lib/ssl/sslsnce.c +++ b/lib/ssl/sslsnce.c @@ -120,14 +120,14 @@ struct sidCacheEntryStr { /* 2 */ ssl3CipherSuite cipherSuite; /* 2 */ PRUint16 compression; /* SSLCompressionMethod */ -/* 52 */ ssl3SidKeys keys; /* keys, wrapped as needed. */ +/* 54 */ ssl3SidKeys keys; /* keys, wrapped as needed. */ /* 4 */ PRUint32 masterWrapMech; /* 4 */ SSL3KEAType exchKeyType; /* 4 */ PRInt32 certIndex; /* 4 */ PRInt32 srvNameIndex; /* 32 */ PRUint8 srvNameHash[SHA256_LENGTH]; /* SHA256 name hash */ -/*104 */} ssl3; +/*108 */} ssl3; /* force sizeof(sidCacheEntry) to be a multiple of cache line size */ struct { /*120 */ PRUint8 filler[120]; /* 72+120==192, a multiple of 16 */ @@ -507,7 +507,6 @@ ConvertFromSID(sidCacheEntry *to, sslSessionID *from) to->sessionIDLength = from->u.ssl3.sessionIDLength; to->u.ssl3.certIndex = -1; to->u.ssl3.srvNameIndex = -1; - PORT_Memcpy(to->sessionID, from->u.ssl3.sessionID, to->sessionIDLength); @@ -637,7 +636,7 @@ ConvertToSID(sidCacheEntry * from, to->authKeyBits = from->authKeyBits; to->keaType = from->keaType; to->keaKeyBits = from->keaKeyBits; - + return to; loser: diff --git a/lib/ssl/sslsock.c b/lib/ssl/sslsock.c index 2ab9d2d52..f73500925 100644 --- a/lib/ssl/sslsock.c +++ b/lib/ssl/sslsock.c @@ -85,6 +85,7 @@ static sslOptions ssl_defaults = { PR_TRUE, /* reuseServerECDHEKey */ PR_FALSE, /* enableFallbackSCSV */ PR_TRUE, /* enableServerDhe */ + PR_FALSE /* enableExtendedMS */ }; /* @@ -825,6 +826,10 @@ SSL_OptionSet(PRFileDesc *fd, PRInt32 which, PRBool on) ss->opt.enableServerDhe = on; break; + case SSL_ENABLE_EXTENDED_MASTER_SECRET: + ss->opt.enableExtendedMS = on; + break; + default: PORT_SetError(SEC_ERROR_INVALID_ARGS); rv = SECFailure; @@ -901,6 +906,8 @@ SSL_OptionGet(PRFileDesc *fd, PRInt32 which, PRBool *pOn) on = ss->opt.reuseServerECDHEKey; break; case SSL_ENABLE_FALLBACK_SCSV: on = ss->opt.enableFallbackSCSV; break; case SSL_ENABLE_SERVER_DHE: on = ss->opt.enableServerDhe; break; + case SSL_ENABLE_EXTENDED_MASTER_SECRET: + on = ss->opt.enableExtendedMS; break; default: PORT_SetError(SEC_ERROR_INVALID_ARGS); @@ -973,6 +980,9 @@ SSL_OptionGetDefault(PRInt32 which, PRBool *pOn) case SSL_ENABLE_SERVER_DHE: on = ssl_defaults.enableServerDhe; break; + case SSL_ENABLE_EXTENDED_MASTER_SECRET: + on = ssl_defaults.enableExtendedMS; + break; default: PORT_SetError(SEC_ERROR_INVALID_ARGS); @@ -1160,6 +1170,10 @@ SSL_OptionSetDefault(PRInt32 which, PRBool on) ssl_defaults.enableServerDhe = on; break; + case SSL_ENABLE_EXTENDED_MASTER_SECRET: + ssl_defaults.enableExtendedMS = on; + break; + default: PORT_SetError(SEC_ERROR_INVALID_ARGS); return SECFailure; diff --git a/lib/ssl/sslt.h b/lib/ssl/sslt.h index f9d83c851..cd742bbb2 100644 --- a/lib/ssl/sslt.h +++ b/lib/ssl/sslt.h @@ -145,6 +145,12 @@ typedef struct SSLChannelInfoStr { /* compression method info */ const char * compressionMethodName; SSLCompressionMethod compressionMethod; + + /* The following fields are added in NSS 3.21. + * This field only has meaning in TLS < 1.3 and will be set to + * PR_FALSE in TLS 1.3. + */ + PRBool extendedMasterSecretUsed; } SSLChannelInfo; /* Preliminary channel info */ @@ -229,13 +235,14 @@ typedef enum { ssl_use_srtp_xtn = 14, ssl_app_layer_protocol_xtn = 16, ssl_padding_xtn = 21, + ssl_extended_master_secret_xtn = 23, ssl_session_ticket_xtn = 35, ssl_next_proto_nego_xtn = 13172, ssl_renegotiation_info_xtn = 0xff01, ssl_tls13_draft_version_xtn = 0xff02 /* experimental number */ } SSLExtensionType; -#define SSL_MAX_EXTENSIONS 11 /* doesn't include ssl_padding_xtn. */ +#define SSL_MAX_EXTENSIONS 12 /* doesn't include ssl_padding_xtn. */ typedef enum { ssl_dhe_group_none = 0, |