Bug 713933: Add the NSS patch for this bug (rather than the PSM patch
authorWan-Teh Chang <wtc@google.com>
Thu, 01 Aug 2013 15:49:16 -0700
changeset 153297 eccb3b98dadbb9a31e03992e55a0bbaa7afb2962
parent 153296 82c3a4ad5d693f23b92d6ed566ce503b652519e4
child 153298 914a4d53edfb45db018036b6488f142678e8ce33
push id2859
push userakeybl@mozilla.com
push dateMon, 16 Sep 2013 19:14:59 +0000
treeherdermozilla-beta@87d3c51cd2bf [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs713933, 658222
milestone25.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 713933: Add the NSS patch for this bug (rather than the PSM patch for bug 658222) to security/patches.
security/patches/README
security/patches/bug-658222-false-start.patch
security/patches/bug-713933-false-start.patch
--- a/security/patches/README
+++ b/security/patches/README
@@ -1,6 +1,6 @@
 This directory contains patches that were added locally
 on top of the NSS release.
 
-bug-658222-false-start.patch: First iteration of false start. The patch will be
+bug-713933-false-start.patch: First iteration of false start. The patch will be
                               further modified to make it work for Chromium and
 			      other projects before being checked in.
deleted file mode 100644
--- a/security/patches/bug-658222-false-start.patch
+++ /dev/null
@@ -1,1577 +0,0 @@
-# HG changeset patch
-# User Patrick McManus <mcmanus@ducksong.com>
-# Date 1372453108 14400
-# Node ID 7587e41b0d6760166e1ab7ec4737fa2523038281
-# Parent  bd363921496c4a219851de041249455e91fe3147
-bug 658222 - Enable TLS False Start (PSM) r=bsmith
-
-diff --git a/netwerk/base/public/security-prefs.js b/netwerk/base/public/security-prefs.js
---- a/netwerk/base/public/security-prefs.js
-+++ b/netwerk/base/public/security-prefs.js
-@@ -7,18 +7,20 @@ pref("security.tls.version.max", 1);
- pref("security.enable_tls_session_tickets", true);
- pref("security.enable_md5_signatures", false);
- 
- pref("security.ssl.allow_unrestricted_renego_everywhere__temporarily_available_pref", false);
- pref("security.ssl.renego_unrestricted_hosts", "");
- pref("security.ssl.treat_unsafe_negotiation_as_broken", false);
- pref("security.ssl.require_safe_negotiation",  false);
- pref("security.ssl.warn_missing_rfc5746",  1);
--pref("security.ssl.enable_false_start", false);
- pref("security.ssl.enable_ocsp_stapling", true);
-+pref("security.ssl.enable_false_start", true);
-+pref("security.ssl.false_start.require-npn", true);
-+pref("security.ssl.false_start.require-forward-secrecy", false);
- 
- pref("security.ssl3.rsa_rc4_128_md5", true);
- pref("security.ssl3.rsa_rc4_128_sha", true);
- pref("security.ssl3.rsa_fips_des_ede3_sha", true);
- pref("security.ssl3.rsa_des_ede3_sha", true);
- pref("security.ssl3.dhe_rsa_camellia_256_sha", true);
- pref("security.ssl3.dhe_dss_camellia_256_sha", true);
- pref("security.ssl3.rsa_camellia_256_sha", true);
-diff --git a/netwerk/protocol/http/ConnectionDiagnostics.cpp b/netwerk/protocol/http/ConnectionDiagnostics.cpp
---- a/netwerk/protocol/http/ConnectionDiagnostics.cpp
-+++ b/netwerk/protocol/http/ConnectionDiagnostics.cpp
-@@ -122,18 +122,18 @@ nsHttpConnectionMgr::nsHalfOpenSocket::P
-                    !!mSocketTransport.get(), !!mBackupTransport.get());
- }
- 
- void
- nsHttpConnection::PrintDiagnostics(nsCString &log)
- {
-   log.AppendPrintf("    CanDirectlyActivate = %d\n", CanDirectlyActivate());
- 
--  log.AppendPrintf("    npncomplete = %d  setupNPNCalled = %d\n",
--                   mNPNComplete, mSetupNPNCalled);
-+  log.AppendPrintf("    npncomplete = %d  setupSSLCalled = %d\n",
-+                   mNPNComplete, mSetupSSLCalled);
- 
-   log.AppendPrintf("    spdyVersion = %d  reportedSpdy = %d everspdy = %d\n",
-                    mUsingSpdyVersion, mReportedSpdy, mEverUsedSpdy);
- 
-   log.AppendPrintf("    iskeepalive = %d  dontReuse = %d isReused = %d\n",
-                    IsKeepAlive(), mDontReuse, mIsReused);
- 
-   log.AppendPrintf("    mTransaction = %d mSpdySession = %d\n",
-diff --git a/netwerk/protocol/http/nsHttp.h b/netwerk/protocol/http/nsHttp.h
---- a/netwerk/protocol/http/nsHttp.h
-+++ b/netwerk/protocol/http/nsHttp.h
-@@ -53,16 +53,21 @@ typedef uint8_t nsHttpVersion;
- // such as HTTP upgrade which are nonsensical for SPDY, it is not the
- // SPDY configuration variable.
- #define NS_HTTP_DISALLOW_SPDY        (1<<7)
- 
- // a transaction with this flag loads without respect to whether the load
- // group is currently blocking on some resources
- #define NS_HTTP_LOAD_UNBLOCKED       (1<<8)
- 
-+// These flags allow a transaction to use TLS false start with
-+// weaker security profiles based on past history
-+#define NS_HTTP_ALLOW_RSA_FALSESTART (1<<9)
-+#define NS_HTTP_ALLOW_RC4_FALSESTART (1<<10)
-+
- //-----------------------------------------------------------------------------
- // some default values
- //-----------------------------------------------------------------------------
- 
- #define NS_HTTP_DEFAULT_PORT  80
- #define NS_HTTPS_DEFAULT_PORT 443
- 
- #define NS_HTTP_HEADER_SEPS ", \t"
-diff --git a/netwerk/protocol/http/nsHttpChannel.cpp b/netwerk/protocol/http/nsHttpChannel.cpp
---- a/netwerk/protocol/http/nsHttpChannel.cpp
-+++ b/netwerk/protocol/http/nsHttpChannel.cpp
-@@ -34,16 +34,24 @@
- #include "nsError.h"
- #include "nsAlgorithm.h"
- #include "GeckoProfiler.h"
- #include "nsIConsoleService.h"
- #include "base/compiler_specific.h"
- #include "NullHttpTransaction.h"
- #include "mozilla/Attributes.h"
- #include "mozilla/VisualEventTracer.h"
-+#include "nsISSLSocketControl.h"
-+#include "sslt.h"
-+#include "nsContentUtils.h"
-+#include "nsIPermissionManager.h"
-+#include "nsIPrincipal.h"
-+#include "nsIScriptSecurityManager.h"
-+#include "nsISSLStatus.h"
-+#include "nsISSLStatusProvider.h"
- 
- namespace mozilla { namespace net {
- 
- namespace {
- 
- // Device IDs for various cache types
- const char kDiskDeviceID[] = "disk";
- const char kMemoryDeviceID[] = "memory";
-@@ -385,16 +393,17 @@ nsHttpChannel::Connect()
-         }
-     }
- 
-     // ensure that we are using a valid hostname
-     if (!net_IsValidHostName(nsDependentCString(mConnectionInfo->Host())))
-         return NS_ERROR_UNKNOWN_HOST;
- 
-     // Consider opening a TCP connection right away
-+    RetrieveSSLOptions();
-     SpeculativeConnect();
- 
-     // Don't allow resuming when cache must be used
-     if (mResuming && (mLoadFlags & LOAD_ONLY_FROM_CACHE)) {
-         LOG(("Resuming from cache is not supported yet"));
-         return NS_ERROR_DOCUMENT_NOT_CACHED;
-     }
- 
-@@ -521,18 +530,19 @@ nsHttpChannel::SpeculativeConnect()
-     nsCOMPtr<nsIInterfaceRequestor> callbacks;
-     NS_NewNotificationCallbacksAggregation(mCallbacks, mLoadGroup,
-                                            getter_AddRefs(callbacks));
-     if (!callbacks)
-         return;
- 
-     mConnectionInfo->SetAnonymous((mLoadFlags & LOAD_ANONYMOUS) != 0);
-     mConnectionInfo->SetPrivate(mPrivateBrowsing);
--    gHttpHandler->SpeculativeConnect(mConnectionInfo,
--                                     callbacks);
-+    gHttpHandler->SpeculativeConnect(
-+        mConnectionInfo, callbacks,
-+        mCaps & (NS_HTTP_ALLOW_RSA_FALSESTART | NS_HTTP_ALLOW_RC4_FALSESTART | NS_HTTP_DISALLOW_SPDY));
- }
- 
- void
- nsHttpChannel::DoNotifyListenerCleanup()
- {
-     // We don't need this info anymore
-     CleanRedirectCacheChainIfNecessary();
- }
-@@ -687,16 +697,47 @@ nsHttpChannel::SetupTransactionLoadGroup
- 
-     // Set the load group connection scope on the transaction
-     nsCOMPtr<nsILoadGroupConnectionInfo> ci;
-     rootLoadGroup->GetConnectionInfo(getter_AddRefs(ci));
-     if (ci)
-         mTransaction->SetLoadGroupConnectionInfo(ci);
- }
- 
-+void
-+nsHttpChannel::RetrieveSSLOptions()
-+{
-+    if (!IsHTTPS() || mPrivateBrowsing)
-+        return;
-+
-+    nsIPrincipal *principal = GetPrincipal();
-+    if (!principal)
-+        return;
-+
-+    nsCOMPtr<nsIPermissionManager> permMgr =
-+        do_GetService(NS_PERMISSIONMANAGER_CONTRACTID);
-+    if (!permMgr)
-+        return;
-+
-+    uint32_t perm;
-+    nsresult rv = permMgr->TestPermissionFromPrincipal(principal,
-+                                                       "falsestart-rsa", &perm);
-+    if (NS_SUCCEEDED(rv) && perm == nsIPermissionManager::ALLOW_ACTION) {
-+        LOG(("nsHttpChannel::RetrieveSSLOptions [this=%p] "
-+             "falsestart-rsa permission found\n", this));
-+        mCaps |= NS_HTTP_ALLOW_RSA_FALSESTART;
-+    }
-+    rv = permMgr->TestPermissionFromPrincipal(principal, "falsestart-rc4", &perm);
-+    if (NS_SUCCEEDED(rv) && perm == nsIPermissionManager::ALLOW_ACTION) {
-+        LOG(("nsHttpChannel::RetrieveSSLOptions [this=%p] "
-+             "falsestart-rc4 permission found\n", this));
-+        mCaps |= NS_HTTP_ALLOW_RC4_FALSESTART;
-+    }
-+}
-+
- nsresult
- nsHttpChannel::SetupTransaction()
- {
-     LOG(("nsHttpChannel::SetupTransaction [this=%p]\n", this));
- 
-     NS_ENSURE_TRUE(!mTransaction, NS_ERROR_ALREADY_INITIALIZED);
- 
-     nsresult rv;
-@@ -1154,16 +1195,99 @@ nsHttpChannel::ProcessSTSHeader()
-     if (NS_FAILED(rv)) {
-         LOG(("STS: Failed to parse STS header, continuing load.\n"));
-         return NS_OK;
-     }
- 
-     return NS_OK;
- }
- 
-+bool
-+nsHttpChannel::IsHTTPS()
-+{
-+    bool isHttps;
-+    if (NS_FAILED(mURI->SchemeIs("https", &isHttps)) || !isHttps)
-+        return false;
-+    return true;
-+}
-+
-+void
-+nsHttpChannel::ProcessSSLInformation()
-+{
-+    // If this is HTTPS, record any use of RSA so that Key Exchange Algorithm
-+    // can be whitelisted for TLS False Start in future sessions. We could
-+    // do the same for DH but its rarity doesn't justify the lookup.
-+    // Also do the same for RC4 symmetric ciphers.
-+
-+    if (mCanceled || NS_FAILED(mStatus) || !mSecurityInfo ||
-+        !IsHTTPS() || mPrivateBrowsing)
-+        return;
-+
-+    nsCOMPtr<nsISSLSocketControl> ssl = do_QueryInterface(mSecurityInfo);
-+    nsCOMPtr<nsISSLStatusProvider> statusProvider =
-+        do_QueryInterface(mSecurityInfo);
-+    if (!ssl || !statusProvider)
-+        return;
-+    nsCOMPtr<nsISSLStatus> sslstat;
-+    statusProvider->GetSSLStatus(getter_AddRefs(sslstat));
-+    if (!sslstat)
-+        return;
-+
-+    // If certificate exceptions are being used don't record this information
-+    // in the permission manager.
-+    bool trustCheck;
-+    if (NS_FAILED(sslstat->GetIsDomainMismatch(&trustCheck)) || trustCheck)
-+        return;
-+    if (NS_FAILED(sslstat->GetIsNotValidAtThisTime(&trustCheck)) || trustCheck)
-+        return;
-+    if (NS_FAILED(sslstat->GetIsUntrusted(&trustCheck)) || trustCheck)
-+        return;
-+
-+    int16_t kea = ssl->GetKEAUsed();
-+    int16_t symcipher = ssl->GetSymmetricCipherUsed();
-+
-+    nsIPrincipal *principal = GetPrincipal();
-+    if (!principal)
-+        return;
-+
-+    // set a permission manager flag that future transactions can
-+    // use via RetrieveSSLOptions(()
-+
-+    nsCOMPtr<nsIPermissionManager> permMgr =
-+        do_GetService(NS_PERMISSIONMANAGER_CONTRACTID);
-+    if (!permMgr)
-+        return;
-+
-+    // Allow this to stand for a week
-+    int64_t expireTime = (PR_Now() / PR_USEC_PER_MSEC) +
-+        (86400 * 7 * PR_MSEC_PER_SEC);
-+
-+    if (kea == ssl_kea_rsa) {
-+        permMgr->AddFromPrincipal(principal, "falsestart-rsa",
-+                                  nsIPermissionManager::ALLOW_ACTION,
-+                                  nsIPermissionManager::EXPIRE_TIME,
-+                                  expireTime);
-+        LOG(("nsHttpChannel::ProcessSSLInformation [this=%p] "
-+             "falsestart-rsa permission granted for this host\n", this));
-+    } else {
-+        permMgr->RemoveFromPrincipal(principal, "falsestart-rsa");
-+    }
-+
-+    if (symcipher == ssl_calg_rc4) {
-+        permMgr->AddFromPrincipal(principal, "falsestart-rc4",
-+                                  nsIPermissionManager::ALLOW_ACTION,
-+                                  nsIPermissionManager::EXPIRE_TIME,
-+                                  expireTime);
-+        LOG(("nsHttpChannel::ProcessSSLInformation [this=%p] "
-+             "falsestart-rc4 permission granted for this host\n", this));
-+    } else {
-+        permMgr->RemoveFromPrincipal(principal, "falsestart-rc4");
-+    }
-+}
-+
- nsresult
- nsHttpChannel::ProcessResponse()
- {
-     nsresult rv;
-     uint32_t httpStatus = mResponseHead->Status();
- 
-     // Gather data on whether the transaction and page (if this is
-     // the initial page load) is being loaded with SSL.
-@@ -1186,16 +1310,18 @@ nsHttpChannel::ProcessResponse()
-     } else {
-         // Given a successful connection, process any STS data that's relevant.
-         rv = ProcessSTSHeader();
-         MOZ_ASSERT(NS_SUCCEEDED(rv), "ProcessSTSHeader failed, continuing load.");
-     }
- 
-     MOZ_ASSERT(!mCachedContentIsValid);
- 
-+    ProcessSSLInformation();
-+
-     // notify "http-on-examine-response" observers
-     gHttpHandler->OnExamineResponse(this);
- 
-     SetCookie(mResponseHead->PeekHeader(nsHttp::Set_Cookie));
- 
-     // handle unused username and password in url (see bug 232567)
-     if (httpStatus != 401 && httpStatus != 407) {
-         if (!mAuthRetryPending)
-@@ -6020,16 +6146,40 @@ nsHttpChannel::ShouldSkipCache()
- 
-     LOG(("Cache dampener installed because service lock held too long [%fms]\n",
-          timeLocked));
-     cacheSkippedUntil = TimeStamp::Now() + TimeDuration::FromSeconds(60);
-     gHttpHandler->SetCacheSkippedUntil(cacheSkippedUntil);
-     return true;
- }
- 
-+nsIPrincipal *
-+nsHttpChannel::GetPrincipal()
-+{
-+    if (mPrincipal)
-+        return mPrincipal;
-+
-+    nsIScriptSecurityManager *securityManager =
-+        nsContentUtils::GetSecurityManager();
-+
-+    if (!securityManager)
-+        return nullptr;
-+
-+    securityManager->GetChannelPrincipal(this, getter_AddRefs(mPrincipal));
-+    if (!mPrincipal)
-+        return nullptr;
-+
-+    // principals with unknown app ids do not work with the permission manager
-+    if (mPrincipal->GetUnknownAppId())
-+        mPrincipal = nullptr;
-+
-+    return mPrincipal;
-+}
-+
-+
- NS_IMETHODIMP
- nsHttpChannel::SetLoadGroup(nsILoadGroup *aLoadGroup)
- {
-     MOZ_ASSERT(NS_IsMainThread(), "Wrong thread.");
- 
-     nsresult rv = HttpBaseChannel::SetLoadGroup(aLoadGroup);
-     if (NS_SUCCEEDED(rv)) {
-         UpdateAggregateCallbacks();
-diff --git a/netwerk/protocol/http/nsHttpChannel.h b/netwerk/protocol/http/nsHttpChannel.h
---- a/netwerk/protocol/http/nsHttpChannel.h
-+++ b/netwerk/protocol/http/nsHttpChannel.h
-@@ -29,16 +29,17 @@
- #include "nsITimedChannel.h"
- #include "nsIFile.h"
- #include "nsDNSPrefetch.h"
- #include "TimingStruct.h"
- #include "AutoClose.h"
- #include "mozilla/Telemetry.h"
- 
- class nsAHttpConnection;
-+class nsIPrincipal;
- 
- namespace mozilla { namespace net {
- 
- class HttpCacheQuery;
- 
- //-----------------------------------------------------------------------------
- // nsHttpChannel
- //-----------------------------------------------------------------------------
-@@ -169,16 +170,19 @@ private:
-     nsresult AsyncProcessRedirection(uint32_t httpStatus);
-     nsresult ContinueProcessRedirection(nsresult);
-     nsresult ContinueProcessRedirectionAfterFallback(nsresult);
-     nsresult ProcessFailedProxyConnect(uint32_t httpStatus);
-     nsresult ProcessFallback(bool *waitingForRedirectCallback);
-     nsresult ContinueProcessFallback(nsresult);
-     void     HandleAsyncAbort();
-     nsresult EnsureAssocReq();
-+    void     ProcessSSLInformation();
-+    bool     IsHTTPS();
-+    void     RetrieveSSLOptions();
- 
-     nsresult ContinueOnStartRequest1(nsresult);
-     nsresult ContinueOnStartRequest2(nsresult);
-     nsresult ContinueOnStartRequest3(nsresult);
- 
-     // redirection specific methods
-     void     HandleAsyncRedirect();
-     void     HandleAsyncAPIRedirect();
-@@ -368,13 +372,17 @@ private:
-     void PushRedirectAsyncFunc(nsContinueRedirectionFunc func);
-     void PopRedirectAsyncFunc(nsContinueRedirectionFunc func);
- 
- protected:
-     virtual void DoNotifyListenerCleanup();
- 
- private: // cache telemetry
-     bool mDidReval;
-+
-+private:
-+    nsIPrincipal *GetPrincipal();
-+    nsCOMPtr<nsIPrincipal> mPrincipal;
- };
- 
- } } // namespace mozilla::net
- 
- #endif // nsHttpChannel_h__
-diff --git a/netwerk/protocol/http/nsHttpConnection.cpp b/netwerk/protocol/http/nsHttpConnection.cpp
---- a/netwerk/protocol/http/nsHttpConnection.cpp
-+++ b/netwerk/protocol/http/nsHttpConnection.cpp
-@@ -12,16 +12,17 @@
- #include "nsHttpRequestHead.h"
- #include "nsHttpResponseHead.h"
- #include "nsHttpHandler.h"
- #include "nsIOService.h"
- #include "nsISocketTransportService.h"
- #include "nsISocketTransport.h"
- #include "nsIServiceManager.h"
- #include "nsISSLSocketControl.h"
-+#include "sslt.h"
- #include "nsStringStream.h"
- #include "netCore.h"
- #include "nsNetCID.h"
- #include "nsProxyRelease.h"
- #include "nsPreloadedStream.h"
- #include "ASpdySession.h"
- #include "mozilla/Telemetry.h"
- #include "nsISupportsPriority.h"
-@@ -59,21 +60,22 @@ nsHttpConnection::nsHttpConnection()
-     , mCompletedProxyConnect(false)
-     , mLastTransactionExpectedNoContent(false)
-     , mIdleMonitoring(false)
-     , mProxyConnectInProgress(false)
-     , mHttp1xTransactionCount(0)
-     , mRemainingConnectionUses(0xffffffff)
-     , mClassification(nsAHttpTransaction::CLASS_GENERAL)
-     , mNPNComplete(false)
--    , mSetupNPNCalled(false)
-+    , mSetupSSLCalled(false)
-     , mUsingSpdyVersion(0)
-     , mPriority(nsISupportsPriority::PRIORITY_NORMAL)
-     , mReportedSpdy(false)
-     , mEverUsedSpdy(false)
-+    , mTransactionCaps(0)
- {
-     LOG(("Creating nsHttpConnection @%x\n", this));
- 
-     // grab a reference to the handler to ensure that it doesn't go away.
-     nsHttpHandler *handler = gHttpHandler;
-     NS_ADDREF(handler);
- }
- 
-@@ -231,18 +233,16 @@ nsHttpConnection::StartSpdy(uint8_t spdy
-     mSupportsPipelining = false; // dont use http/1 pipelines with spdy
-     mTransaction = mSpdySession;
-     mIdleTimeout = gHttpHandler->SpdyTimeout();
- }
- 
- bool
- nsHttpConnection::EnsureNPNComplete()
- {
--    // NPN is only used by SPDY right now.
--    //
-     // If for some reason the components to check on NPN aren't available,
-     // this function will just return true to continue on and disable SPDY
- 
-     MOZ_ASSERT(mSocketTransport);
-     if (!mSocketTransport) {
-         // this cannot happen
-         mNPNComplete = true;
-         return true;
-@@ -303,32 +303,33 @@ nsresult
- nsHttpConnection::Activate(nsAHttpTransaction *trans, uint32_t caps, int32_t pri)
- {
-     nsresult rv;
- 
-     MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
-     LOG(("nsHttpConnection::Activate [this=%p trans=%x caps=%x]\n",
-          this, trans, caps));
- 
-+    mTransactionCaps = caps;
-     mPriority = pri;
-     if (mTransaction && mUsingSpdyVersion)
-         return AddTransaction(trans, pri);
- 
-     NS_ENSURE_ARG_POINTER(trans);
-     NS_ENSURE_TRUE(!mTransaction, NS_ERROR_IN_PROGRESS);
- 
-     // reset the read timers to wash away any idle time
-     mLastReadTime = PR_IntervalNow();
- 
-     // Update security callbacks
-     nsCOMPtr<nsIInterfaceRequestor> callbacks;
-     trans->GetSecurityCallbacks(getter_AddRefs(callbacks));
-     SetSecurityCallbacks(callbacks);
- 
--    SetupNPN(caps); // only for spdy
-+    SetupSSL(caps);
- 
-     // take ownership of the transaction
-     mTransaction = trans;
- 
-     MOZ_ASSERT(!mIdleMonitoring, "Activating a connection with an Idle Monitor");
-     mIdleMonitoring = false;
- 
-     // set mKeepAlive according to what will be requested
-@@ -355,65 +356,80 @@ failed_activation:
-     if (NS_FAILED(rv)) {
-         mTransaction = nullptr;
-     }
- 
-     return rv;
- }
- 
- void
--nsHttpConnection::SetupNPN(uint32_t caps)
-+nsHttpConnection::SetupSSL(uint32_t caps)
- {
--    if (mSetupNPNCalled)                                /* do only once */
-+    LOG(("nsHttpConnection::SetupSSL %p caps=0x%X\n", this, caps));
-+
-+    if (mSetupSSLCalled) // do only once
-         return;
--    mSetupNPNCalled = true;
-+    mSetupSSLCalled = true;
- 
--    // Setup NPN Negotiation if necessary (only for SPDY)
--    if (!mNPNComplete) {
-+    if (mNPNComplete)
-+        return;
- 
--        mNPNComplete = true;
-+    // we flip this back to false if SetNPNList succeeds at the end
-+    // of this function
-+    mNPNComplete = true;
- 
--        if (mConnInfo->UsingSSL()) {
--            LOG(("nsHttpConnection::SetupNPN Setting up "
--                 "Next Protocol Negotiation"));
--            nsCOMPtr<nsISupports> securityInfo;
--            nsresult rv =
--                mSocketTransport->GetSecurityInfo(getter_AddRefs(securityInfo));
--            if (NS_FAILED(rv))
--                return;
-+    if (!mConnInfo->UsingSSL())
-+        return;
- 
--            nsCOMPtr<nsISSLSocketControl> ssl =
--                do_QueryInterface(securityInfo, &rv);
--            if (NS_FAILED(rv))
--                return;
-+    LOG(("nsHttpConnection::SetupSSL Setting up "
-+         "Next Protocol Negotiation"));
-+    nsCOMPtr<nsISupports> securityInfo;
-+    nsresult rv =
-+        mSocketTransport->GetSecurityInfo(getter_AddRefs(securityInfo));
-+    if (NS_FAILED(rv))
-+        return;
- 
--            nsTArray<nsCString> protocolArray;
-+    nsCOMPtr<nsISSLSocketControl> ssl = do_QueryInterface(securityInfo, &rv);
-+    if (NS_FAILED(rv))
-+        return;
- 
--            // The first protocol is used as the fallback if none of the
--            // protocols supported overlap with the server's list.
--            // In the case of overlap, matching priority is driven by
--            // the order of the server's advertisement.
--            protocolArray.AppendElement(NS_LITERAL_CSTRING("http/1.1"));
-+    if (caps & NS_HTTP_ALLOW_RSA_FALSESTART) {
-+        LOG(("nsHttpConnection::SetupSSL %p "
-+             ">= RSA Key Exchange Expected\n", this));
-+        ssl->SetKEAExpected(ssl_kea_rsa);
-+    }
- 
--            if (gHttpHandler->IsSpdyEnabled() &&
--                !(caps & NS_HTTP_DISALLOW_SPDY)) {
--                LOG(("nsHttpConnection::SetupNPN Allow SPDY NPN selection"));
--                if (gHttpHandler->SpdyInfo()->ProtocolEnabled(0))
--                    protocolArray.AppendElement(
--                        gHttpHandler->SpdyInfo()->VersionString[0]);
--                if (gHttpHandler->SpdyInfo()->ProtocolEnabled(1))
--                    protocolArray.AppendElement(
--                        gHttpHandler->SpdyInfo()->VersionString[1]);
--            }
-+    if (caps & NS_HTTP_ALLOW_RC4_FALSESTART) {
-+        LOG(("nsHttpConnection::SetupSSL %p "
-+             ">= RC4 Key Exchange Expected\n", this));
-+        ssl->SetSymmetricCipherExpected(ssl_calg_rc4);
-+    }
- 
--            if (NS_SUCCEEDED(ssl->SetNPNList(protocolArray))) {
--                LOG(("nsHttpConnection::Init Setting up SPDY Negotiation OK"));
--                mNPNComplete = false;
--            }
--        }
-+    nsTArray<nsCString> protocolArray;
-+
-+    // The first protocol is used as the fallback if none of the
-+    // protocols supported overlap with the server's list.
-+    // In the case of overlap, matching priority is driven by
-+    // the order of the server's advertisement.
-+    protocolArray.AppendElement(NS_LITERAL_CSTRING("http/1.1"));
-+
-+    if (gHttpHandler->IsSpdyEnabled() &&
-+        !(caps & NS_HTTP_DISALLOW_SPDY)) {
-+        LOG(("nsHttpConnection::SetupSSL Allow SPDY NPN selection"));
-+        if (gHttpHandler->SpdyInfo()->ProtocolEnabled(0))
-+            protocolArray.AppendElement(
-+                gHttpHandler->SpdyInfo()->VersionString[0]);
-+        if (gHttpHandler->SpdyInfo()->ProtocolEnabled(1))
-+            protocolArray.AppendElement(
-+                gHttpHandler->SpdyInfo()->VersionString[1]);
-+    }
-+
-+    if (NS_SUCCEEDED(ssl->SetNPNList(protocolArray))) {
-+        LOG(("nsHttpConnection::Init Setting up SPDY Negotiation OK"));
-+        mNPNComplete = false;
-     }
- }
- 
- nsresult
- nsHttpConnection::AddTransaction(nsAHttpTransaction *httpTransaction,
-                                  int32_t priority)
- {
-     LOG(("nsHttpConnection::AddTransaction for SPDY"));
-@@ -591,17 +607,17 @@ nsHttpConnection::TimeToLive()
- bool
- nsHttpConnection::IsAlive()
- {
-     if (!mSocketTransport)
-         return false;
- 
-     // SocketTransport::IsAlive can run the SSL state machine, so make sure
-     // the NPN options are set before that happens.
--    SetupNPN(0);
-+    SetupSSL(mTransactionCaps);
- 
-     bool alive;
-     nsresult rv = mSocketTransport->IsAlive(&alive);
-     if (NS_FAILED(rv))
-         alive = false;
- 
- //#define TEST_RESTART_LOGIC
- #ifdef TEST_RESTART_LOGIC
-diff --git a/netwerk/protocol/http/nsHttpConnection.h b/netwerk/protocol/http/nsHttpConnection.h
---- a/netwerk/protocol/http/nsHttpConnection.h
-+++ b/netwerk/protocol/http/nsHttpConnection.h
-@@ -153,16 +153,17 @@ public:
-     // When the connection is active this is called every second
-     void  ReadTimeoutTick();
- 
-     int64_t BytesWritten() { return mTotalBytesWritten; }
- 
-     void    SetSecurityCallbacks(nsIInterfaceRequestor* aCallbacks);
-     void    PrintDiagnostics(nsCString &log);
- 
-+    void    SetTransactionCaps(uint32_t aCaps) { mTransactionCaps = aCaps; }
- private:
-     // called to cause the underlying socket to start speaking SSL
-     nsresult ProxyStartSSL();
- 
-     nsresult OnTransactionDone(nsresult reason);
-     nsresult OnSocketWritable();
-     nsresult OnSocketReadable();
- 
-@@ -170,17 +171,17 @@ private:
- 
-     PRIntervalTime IdleTime();
-     bool     IsAlive();
-     bool     SupportsPipelining(nsHttpResponseHead *);
- 
-     // Makes certain the SSL handshake is complete and NPN negotiation
-     // has had a chance to happen
-     bool     EnsureNPNComplete();
--    void     SetupNPN(uint32_t caps);
-+    void     SetupSSL(uint32_t caps);
- 
-     // Start the Spdy transaction handler when NPN indicates spdy/*
-     void     StartSpdy(uint8_t versionLevel);
- 
-     // Directly Add a transaction to an active connection for SPDY
-     nsresult AddTransaction(nsAHttpTransaction *, int32_t);
- 
- private:
-@@ -235,22 +236,25 @@ private:
-     // transactions (including the current one) that the server expects to allow
-     // on this persistent connection.
-     uint32_t                        mRemainingConnectionUses;
- 
-     nsAHttpTransaction::Classifier  mClassification;
- 
-     // SPDY related
-     bool                            mNPNComplete;
--    bool                            mSetupNPNCalled;
-+    bool                            mSetupSSLCalled;
- 
-     // version level in use, 0 if unused
-     uint8_t                         mUsingSpdyVersion;
- 
-     nsRefPtr<mozilla::net::ASpdySession> mSpdySession;
-     int32_t                         mPriority;
-     bool                            mReportedSpdy;
- 
-     // mUsingSpdyVersion is cleared when mSpdySession is freed, this is permanent
-     bool                            mEverUsedSpdy;
-+
-+    // The capabailities associated with the most recent transaction
-+    uint32_t                        mTransactionCaps;
- };
- 
- #endif // nsHttpConnection_h__
-diff --git a/netwerk/protocol/http/nsHttpConnectionMgr.cpp b/netwerk/protocol/http/nsHttpConnectionMgr.cpp
---- a/netwerk/protocol/http/nsHttpConnectionMgr.cpp
-+++ b/netwerk/protocol/http/nsHttpConnectionMgr.cpp
-@@ -321,27 +321,28 @@ nsHttpConnectionMgr::DoShiftReloadConnec
-                             0, connInfo);
-     if (NS_SUCCEEDED(rv))
-         connInfo.forget();
-     return rv;
- }
- 
- nsresult
- nsHttpConnectionMgr::SpeculativeConnect(nsHttpConnectionInfo *ci,
--                                        nsIInterfaceRequestor *callbacks)
-+                                        nsIInterfaceRequestor *callbacks,
-+                                        uint32_t caps)
- {
-     LOG(("nsHttpConnectionMgr::SpeculativeConnect [ci=%s]\n",
-          ci->HashKey().get()));
- 
-     // Wrap up the callbacks and the target to ensure they're released on the target
-     // thread properly.
-     nsCOMPtr<nsIInterfaceRequestor> wrappedCallbacks;
-     NS_NewInterfaceRequestorAggregation(callbacks, nullptr, getter_AddRefs(wrappedCallbacks));
- 
--    uint32_t caps = ci->GetAnonymous() ? NS_HTTP_LOAD_ANONYMOUS : 0;
-+    caps |= ci->GetAnonymous() ? NS_HTTP_LOAD_ANONYMOUS : 0;
-     nsRefPtr<NullHttpTransaction> trans =
-         new NullHttpTransaction(ci, wrappedCallbacks, caps);
- 
-     nsresult rv =
-         PostEvent(&nsHttpConnectionMgr::OnMsgSpeculativeConnect, 0, trans);
-     if (NS_SUCCEEDED(rv))
-         trans.forget();
-     return rv;
-@@ -2809,16 +2810,20 @@ nsHalfOpenSocket::OnOutputStreamReady(ns
- 
-     CancelBackupTimer();
- 
-     // assign the new socket to the http connection
-     nsRefPtr<nsHttpConnection> conn = new nsHttpConnection();
-     LOG(("nsHalfOpenSocket::OnOutputStreamReady "
-          "Created new nshttpconnection %p\n", conn.get()));
- 
-+    // Some capabilities are needed before a transaciton actually gets
-+    // scheduled (e.g. how to negotiate false start)
-+    conn->SetTransactionCaps(mTransaction->Caps());
-+
-     NetAddr peeraddr;
-     nsCOMPtr<nsIInterfaceRequestor> callbacks;
-     mTransaction->GetSecurityCallbacks(getter_AddRefs(callbacks));
-     if (out == mStreamOut) {
-         TimeDuration rtt = TimeStamp::Now() - mPrimarySynStarted;
-         rv = conn->Init(mEnt->mConnInfo,
-                         gHttpHandler->ConnMgr()->mMaxRequestDelay,
-                         mSocketTransport, mStreamIn, mStreamOut,
-diff --git a/netwerk/protocol/http/nsHttpConnectionMgr.h b/netwerk/protocol/http/nsHttpConnectionMgr.h
---- a/netwerk/protocol/http/nsHttpConnectionMgr.h
-+++ b/netwerk/protocol/http/nsHttpConnectionMgr.h
-@@ -108,17 +108,18 @@ public:
- 
-     // called to indicate a transaction for the connectionInfo is likely coming
-     // soon. The connection manager may use this information to start a TCP
-     // and/or SSL level handshake for that resource immediately so that it is
-     // ready when the transaction is submitted. No obligation is taken on by the
-     // connection manager, nor is the submitter obligated to actually submit a
-     // real transaction for this connectionInfo.
-     nsresult SpeculativeConnect(nsHttpConnectionInfo *,
--                                nsIInterfaceRequestor *);
-+                                nsIInterfaceRequestor *,
-+                                uint32_t caps = 0);
- 
-     // called when a connection is done processing a transaction.  if the
-     // connection can be reused then it will be added to the idle list, else
-     // it will be closed.
-     nsresult ReclaimConnection(nsHttpConnection *conn);
- 
-     // called by the main thread to execute the taketransport() logic on the
-     // socket thread after a 101 response has been received and the socket
-diff --git a/netwerk/protocol/http/nsHttpHandler.h b/netwerk/protocol/http/nsHttpHandler.h
---- a/netwerk/protocol/http/nsHttpHandler.h
-+++ b/netwerk/protocol/http/nsHttpHandler.h
-@@ -176,19 +176,20 @@ public:
-     }
- 
-     nsresult GetSocketThreadTarget(nsIEventTarget **target)
-     {
-         return mConnMgr->GetSocketThreadTarget(target);
-     }
- 
-     nsresult SpeculativeConnect(nsHttpConnectionInfo *ci,
--                                nsIInterfaceRequestor *callbacks)
-+                                nsIInterfaceRequestor *callbacks,
-+                                uint32_t caps = 0)
-     {
--        return mConnMgr->SpeculativeConnect(ci, callbacks);
-+        return mConnMgr->SpeculativeConnect(ci, callbacks, caps);
-     }
- 
-     //
-     // The HTTP handler caches pointers to specific XPCOM services, and
-     // provides the following helper routines for accessing those services:
-     //
-     nsresult GetStreamConverterService(nsIStreamConverterService **);
-     nsresult GetIOService(nsIIOService** service);
-diff --git a/netwerk/socket/nsISSLSocketControl.idl b/netwerk/socket/nsISSLSocketControl.idl
---- a/netwerk/socket/nsISSLSocketControl.idl
-+++ b/netwerk/socket/nsISSLSocketControl.idl
-@@ -9,17 +9,17 @@
- interface nsIInterfaceRequestor;
- 
- %{C++
- #include "nsTArray.h"
- class nsCString;
- %}
- [ref] native nsCStringTArrayRef(nsTArray<nsCString>);
- 
--[scriptable, uuid(bb2bb490-3ba4-4254-b8f5-8b43c7b714ea)]
-+[scriptable, builtinclass, uuid(c5eb9af4-238c-4fc6-bdec-d5ab5e7dce68)]
- interface nsISSLSocketControl : nsISupports {
-     attribute nsIInterfaceRequestor     notificationCallbacks;
- 
-     void proxyStartSSL();
-     void StartTLS();
- 
-     /* NPN (Next Protocol Negotiation) is a mechanism for
-        negotiating the protocol to be spoken inside the SSL
-@@ -47,14 +47,30 @@ interface nsISSLSocketControl : nsISuppo
-      * a desired NPN negotiated protocol of npnProtocol can use the socket
-      * associated with this object instead of making a new one.
-      */
-     boolean joinConnection(
-       in ACString npnProtocol, /* e.g. "spdy/2" */
-       in ACString hostname,
-       in long port);
- 
-+    /* The Key Exchange Algorithm and Symmetric Cipher
-+       is used when determining whether or not to do false start.
-+       After a handshake is complete it can be read from *Used,
-+       before a handshake is started it may be set through *Expected.
-+       The values correspond to the SSLKEAType and SSLCipherAlgorithm
-+       enums in NSS or the *_UNKNOWN constant defined below.
-+    */
-+
-+    [infallible] readonly attribute short KEAUsed;
-+    [infallible] attribute short KEAExpected;
-+    [infallible] readonly attribute short SymmetricCipherUsed;
-+    [infallible] attribute short SymmetricCipherExpected;
-+
-+    const short KEY_EXCHANGE_UNKNOWN = -1;
-+    const short SYMMETRIC_CIPHER_UNKNOWN = -1;
-+
-     /*
-      * The original flags from the socket provider.
-      */
-     readonly attribute uint32_t providerFlags;
- };
- 
-diff --git a/security/manager/ssl/src/nsNSSCallbacks.cpp b/security/manager/ssl/src/nsNSSCallbacks.cpp
---- a/security/manager/ssl/src/nsNSSCallbacks.cpp
-+++ b/security/manager/ssl/src/nsNSSCallbacks.cpp
-@@ -819,31 +819,214 @@ PK11PasswordPrompt(PK11SlotInfo* slot, P
- {
-   RefPtr<PK11PasswordPromptRunnable> runnable(
-     new PK11PasswordPromptRunnable(slot,
-                                    static_cast<nsIInterfaceRequestor*>(arg)));
-   runnable->DispatchToMainThreadAndWait();
-   return runnable->mResult;
- }
- 
-+// call with shutdown prevention lock held
-+static void
-+PreliminaryHandshakeDone(PRFileDesc* fd)
-+{
-+  nsNSSSocketInfo* infoObject = (nsNSSSocketInfo*) fd->higher->secret;
-+  if (!infoObject)
-+    return;
-+
-+  if (infoObject->IsPreliminaryHandshakeDone())
-+    return;
-+
-+  infoObject->SetPreliminaryHandshakeDone();
-+  infoObject->SetFirstServerHelloReceived();
-+
-+  // Get the NPN value.
-+  SSLNextProtoState state;
-+  unsigned char npnbuf[256];
-+  unsigned int npnlen;
-+
-+  if (SSL_GetNextProto(fd, &state, npnbuf, &npnlen, 256) == SECSuccess) {
-+    if (state == SSL_NEXT_PROTO_NEGOTIATED) {
-+      infoObject->SetNegotiatedNPN(reinterpret_cast<char *>(npnbuf), npnlen);
-+    }
-+    else {
-+      infoObject->SetNegotiatedNPN(nullptr, 0);
-+    }
-+    mozilla::Telemetry::Accumulate(Telemetry::SSL_NPN_TYPE, state);
-+  }
-+  else {
-+    infoObject->SetNegotiatedNPN(nullptr, 0);
-+  }
-+}
-+
-+SECStatus
-+CanFalseStartCallback(PRFileDesc* fd, void* client_data, PRBool *canFalseStart)
-+{
-+  *canFalseStart = false;
-+
-+  nsNSSShutDownPreventionLock locker;
-+
-+  nsNSSSocketInfo* infoObject = (nsNSSSocketInfo*) fd->higher->secret;
-+  if (!infoObject) {
-+    PR_SetError(PR_INVALID_STATE_ERROR, 0);
-+    return SECFailure;
-+  }
-+
-+  if (infoObject->isAlreadyShutDown()) {
-+    MOZ_NOT_REACHED("SSL socket used after NSS shut down");
-+    PR_SetError(PR_INVALID_STATE_ERROR, 0);
-+    return SECFailure;
-+  }
-+
-+  PreliminaryHandshakeDone(fd);
-+
-+  SSLChannelInfo channelInfo;
-+  if (SSL_GetChannelInfo(fd, &channelInfo, sizeof(channelInfo)) != SECSuccess) {
-+    return SECSuccess;
-+  }
-+
-+  SSLCipherSuiteInfo cipherInfo;
-+  if (SSL_GetCipherSuiteInfo(channelInfo.cipherSuite, &cipherInfo,
-+                             sizeof (cipherInfo)) != SECSuccess) {
-+    PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("CanFalseStartCallback [%p] failed - "
-+                                      " KEA %d\n", fd,
-+                                      static_cast<int32_t>(cipherInfo.keaType)));
-+    return SECSuccess;
-+  }
-+
-+  if (channelInfo.protocolVersion < SSL_LIBRARY_VERSION_TLS_1_0) {
-+    PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("CanFalseStartCallback [%p] failed - "
-+                                      "SSL Version must be >= TLS1 %x\n", fd,
-+                                      static_cast<int32_t>(channelInfo.protocolVersion)));
-+    return SECSuccess;
-+  }
-+
-+  // never do false start without one of these key exchange algorithms
-+  if (cipherInfo.keaType != ssl_kea_rsa &&
-+      cipherInfo.keaType != ssl_kea_dh &&
-+      cipherInfo.keaType != ssl_kea_ecdh) {
-+    PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("CanFalseStartCallback [%p] failed - "
-+                                      "unsupported KEA %d\n", fd,
-+                                      static_cast<int32_t>(cipherInfo.keaType)));
-+    return SECSuccess;
-+  }
-+
-+  // never do false start without at least 80 bits of key material. This should
-+  // be redundant to an NSS precondition
-+  if (cipherInfo.effectiveKeyBits < 80) {
-+    MOZ_NOT_REACHED("NSS is not enforcing the precondition that the effective "
-+                    "key size must be >= 80 bits for false start");
-+    PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("CanFalseStartCallback [%p] failed - "
-+                                      "key too small %d\n", fd,
-+                                      static_cast<int32_t>(cipherInfo.effectiveKeyBits)));
-+    PR_SetError(PR_INVALID_STATE_ERROR, 0);
-+    return SECFailure;
-+  }
-+
-+  // XXX: An attacker can choose which protocols are advertised in the
-+  // NPN extension. TODO(Bug 861311): We should restrict the ability
-+  // of an attacker leverage this capability by restricting false start
-+  // to the same protocol we previously saw for the server, after the
-+  // first successful connection to the server.
-+
-+  // Enforce NPN to do false start if policy requires it. Do this as an
-+  // indicator if server compatibility.
-+  nsSSLIOLayerHelpers& helpers = infoObject->SharedState().IOLayerHelpers();
-+  if (helpers.mFalseStartRequireNPN) {
-+    nsAutoCString negotiatedNPN;
-+    if (NS_FAILED(infoObject->GetNegotiatedNPN(negotiatedNPN)) ||
-+        !negotiatedNPN.Length()) {
-+      PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("CanFalseStartCallback [%p] failed - "
-+                                        "NPN cannot be verified\n", fd));
-+      return SECSuccess;
-+    }
-+  }
-+
-+  // If we're not using eliptical curve kea then make sure we've seen the
-+  // same kea from this host in the past, to limit the potential for downgrade
-+  // attacks.
-+  if (cipherInfo.keaType != ssl_kea_ecdh) {
-+
-+    if (helpers.mFalseStartRequireForwardSecrecy) {
-+      PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("CanFalseStartCallback [%p] failed - "
-+                                        "KEA used is %d, but "
-+                                        "require-forward-secrecy configured.\n",
-+                                        fd, static_cast<int32_t>(cipherInfo.keaType)));
-+      return SECSuccess;
-+    }
-+
-+    int16_t expected = infoObject->GetKEAExpected();
-+    if (cipherInfo.keaType != expected) {
-+      PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("CanFalseStartCallback [%p] failed - "
-+                                        "KEA used is %d, expected %d\n", fd,
-+                                        static_cast<int32_t>(cipherInfo.keaType),
-+                                        static_cast<int32_t>(expected)));
-+      return SECSuccess;
-+    }
-+
-+    // whitelist the expected key exchange algorithms that are
-+    // acceptable for false start when seen before.
-+    if (expected != ssl_kea_rsa && expected != ssl_kea_dh &&
-+        expected != ssl_kea_ecdh) {
-+      PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("CanFalseStartCallback [%p] failed - "
-+                                        "KEA used, %d, "
-+                                        "is not supported with False Start.\n",
-+                                        fd, static_cast<int32_t>(expected)));
-+      return SECSuccess;
-+    }
-+  }
-+
-+  // If we're not using AES then verify that this is the historically expected
-+  // symmetrical cipher for this host, to limit potential for downgrade attacks.
-+  if (cipherInfo.symCipher != ssl_calg_aes) {
-+    int16_t expected = infoObject->GetSymmetricCipherExpected();
-+    if (cipherInfo.symCipher != expected) {
-+      PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("CanFalseStartCallback [%p] failed - "
-+                                        "Symmetric cipher used is %d, expected %d\n",
-+                                        fd, static_cast<int32_t>(cipherInfo.symCipher),
-+                                        static_cast<int32_t>(expected)));
-+      return SECSuccess;
-+    }
-+
-+    // whitelist the expected ciphers that are
-+    // acceptable for false start when seen before.
-+    if ((expected != ssl_calg_rc4) && (expected != ssl_calg_3des) &&
-+        (expected != ssl_calg_aes)) {
-+      PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("CanFalseStartCallback [%p] failed - "
-+                                        "Symmetric cipher used, %d, "
-+                                        "is not supported with False Start.\n",
-+                                        fd, static_cast<int32_t>(expected)));
-+      return SECSuccess;
-+    }
-+  }
-+
-+  infoObject->NoteTimeUntilReady();
-+  *canFalseStart = true;
-+  PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("CanFalseStartCallback [%p] ok\n", fd));
-+  return SECSuccess;
-+}
-+
- void HandshakeCallback(PRFileDesc* fd, void* client_data) {
-   nsNSSShutDownPreventionLock locker;
-   int32_t sslStatus;
-   char* cipherName = nullptr;
-   int32_t keyLength;
-   int32_t encryptBits;
- 
-   nsNSSSocketInfo* infoObject = (nsNSSSocketInfo*) fd->higher->secret;
- 
-   // certificate validation sets FirstServerHelloReceived, so if that flag
--  // is absent at handshake time we have a resumed session.
-+  // is absent at handshake time we have a resumed session. Check this before
-+  // PreliminaryHandshakeDone() because that function also sets that flag.
-   bool isResumedSession = !(infoObject->GetFirstServerHelloReceived());
- 
--  // This is the first callback on resumption handshakes
--  infoObject->SetFirstServerHelloReceived();
-+  // Do the bookkeeping that needs to be done after the
-+  // server's ServerHello...ServerHelloDone have been processed, but that doesn't
-+  // need the handshake to be completed.
-+  PreliminaryHandshakeDone(fd);
- 
-   // If the handshake completed, then we know the site is TLS tolerant (if this
-   // was a TLS connection).
-   nsSSLIOLayerHelpers& ioLayerHelpers = infoObject->SharedState().IOLayerHelpers();
-   ioLayerHelpers.rememberTolerantSite(infoObject);
- 
-   if (SECSuccess != SSL_SecurityStatus(fd, &sslStatus, &cipherName, &keyLength,
-                                        &encryptBits, nullptr, nullptr)) {
-@@ -925,47 +1108,35 @@ void HandshakeCallback(PRFileDesc* fd, v
-     }
-   }
- 
-   status->mHaveKeyLengthAndCipher = true;
-   status->mKeyLength = keyLength;
-   status->mSecretKeyLength = encryptBits;
-   status->mCipherName.Assign(cipherName);
- 
--  // Get the NPN value.
--  SSLNextProtoState state;
--  unsigned char npnbuf[256];
--  unsigned int npnlen;
--    
--  if (SSL_GetNextProto(fd, &state, npnbuf, &npnlen, 256) == SECSuccess) {
--    if (state == SSL_NEXT_PROTO_NEGOTIATED)
--      infoObject->SetNegotiatedNPN(reinterpret_cast<char *>(npnbuf), npnlen);
--    else
--      infoObject->SetNegotiatedNPN(nullptr, 0);
--    mozilla::Telemetry::Accumulate(Telemetry::SSL_NPN_TYPE, state);
--  }
--  else
--    infoObject->SetNegotiatedNPN(nullptr, 0);
--
-   SSLChannelInfo channelInfo;
-   if (SSL_GetChannelInfo(fd, &channelInfo, sizeof(channelInfo)) == SECSuccess) {
-     // Get the protocol version for telemetry
-     // 0=ssl3, 1=tls1, 2=tls1.1, 3=tls1.2
-     unsigned int versionEnum = channelInfo.protocolVersion & 0xFF;
-     Telemetry::Accumulate(Telemetry::SSL_HANDSHAKE_VERSION, versionEnum);
- 
-     SSLCipherSuiteInfo cipherInfo;
-     if (SSL_GetCipherSuiteInfo(channelInfo.cipherSuite, &cipherInfo,
--                                sizeof (cipherInfo)) == SECSuccess) {
-+                               sizeof (cipherInfo)) == SECSuccess) {
-       // keyExchange null=0, rsa=1, dh=2, fortezza=3, ecdh=4
-       Telemetry::Accumulate(Telemetry::SSL_KEY_EXCHANGE_ALGORITHM,
-                             cipherInfo.keaType);
-+      infoObject->SetKEAUsed(cipherInfo.keaType);
-+      infoObject->SetSymmetricCipherUsed(cipherInfo.symCipher);
-     }
--      
-   }
-+
-+  infoObject->NoteTimeUntilReady();
-   infoObject->SetHandshakeCompleted(isResumedSession);
- 
-   PORT_Free(cipherName);
- }
- 
- struct OCSPDefaultResponders {
-     const char *issuerName_string;
-     CERTName *issuerName;
-diff --git a/security/manager/ssl/src/nsNSSCallbacks.h b/security/manager/ssl/src/nsNSSCallbacks.h
---- a/security/manager/ssl/src/nsNSSCallbacks.h
-+++ b/security/manager/ssl/src/nsNSSCallbacks.h
-@@ -18,16 +18,18 @@
- #include "nsString.h"
- 
- class nsILoadGroup;
- 
- char*
- PK11PasswordPrompt(PK11SlotInfo *slot, PRBool retry, void* arg);
- 
- void HandshakeCallback(PRFileDesc *fd, void *client_data);
-+SECStatus CanFalseStartCallback(PRFileDesc* fd, void* client_data,
-+                                PRBool *canFalseStart);
- 
- SECStatus RegisterMyOCSPAIAInfoCallback();
- SECStatus UnregisterMyOCSPAIAInfoCallback();
- 
- class nsHTTPListener MOZ_FINAL : public nsIStreamLoaderObserver
- {
- private:
-   // For XPCOM implementations that are not a base class for some other
-diff --git a/security/manager/ssl/src/nsNSSIOLayer.cpp b/security/manager/ssl/src/nsNSSIOLayer.cpp
---- a/security/manager/ssl/src/nsNSSIOLayer.cpp
-+++ b/security/manager/ssl/src/nsNSSIOLayer.cpp
-@@ -78,22 +78,28 @@ nsNSSSocketInfo::nsNSSSocketInfo(SharedS
-     mForSTARTTLS(false),
-     mSSL3Enabled(false),
-     mTLSEnabled(false),
-     mHandshakePending(true),
-     mHasCleartextPhase(false),
-     mHandshakeInProgress(false),
-     mAllowTLSIntoleranceTimeout(true),
-     mRememberClientAuthCertificate(false),
-+    mPreliminaryHandshakeDone(false),
-     mHandshakeStartTime(0),
-     mFirstServerHelloReceived(false),
-     mNPNCompleted(false),
-     mHandshakeCompleted(false),
-     mJoined(false),
-     mSentClientCert(false),
-+    mNotedTimeUntilReady(false),
-+    mKEAUsed(nsISSLSocketControl::KEY_EXCHANGE_UNKNOWN),
-+    mKEAExpected(nsISSLSocketControl::KEY_EXCHANGE_UNKNOWN),
-+    mSymmetricCipherUsed(nsISSLSocketControl::SYMMETRIC_CIPHER_UNKNOWN),
-+    mSymmetricCipherExpected(nsISSLSocketControl::SYMMETRIC_CIPHER_UNKNOWN),
-     mProviderFlags(providerFlags),
-     mSocketCreationTimestamp(TimeStamp::Now()),
-     mPlaintextBytesRead(0)
- {
- }
- 
- NS_IMPL_ISUPPORTS_INHERITED2(nsNSSSocketInfo, TransportSecurityInfo,
-                              nsISSLSocketControl,
-@@ -101,16 +107,58 @@ NS_IMPL_ISUPPORTS_INHERITED2(nsNSSSocket
- 
- NS_IMETHODIMP
- nsNSSSocketInfo::GetProviderFlags(uint32_t* aProviderFlags)
- {
-   *aProviderFlags = mProviderFlags;
-   return NS_OK;
- }
- 
-+NS_IMETHODIMP
-+nsNSSSocketInfo::GetKEAUsed(int16_t *aKea)
-+{
-+  *aKea = mKEAUsed;
-+  return NS_OK;
-+}
-+
-+NS_IMETHODIMP
-+nsNSSSocketInfo::GetKEAExpected(int16_t *aKea)
-+{
-+  *aKea = mKEAExpected;
-+  return NS_OK;
-+}
-+
-+NS_IMETHODIMP
-+nsNSSSocketInfo::SetKEAExpected(int16_t aKea)
-+{
-+  mKEAExpected = aKea;
-+  return NS_OK;
-+}
-+
-+NS_IMETHODIMP
-+nsNSSSocketInfo::GetSymmetricCipherUsed(int16_t *aSymmetricCipher)
-+{
-+  *aSymmetricCipher = mSymmetricCipherUsed;
-+  return NS_OK;
-+}
-+
-+NS_IMETHODIMP
-+nsNSSSocketInfo::GetSymmetricCipherExpected(int16_t *aSymmetricCipher)
-+{
-+  *aSymmetricCipher = mSymmetricCipherExpected;
-+  return NS_OK;
-+}
-+
-+NS_IMETHODIMP
-+nsNSSSocketInfo::SetSymmetricCipherExpected(int16_t aSymmetricCipher)
-+{
-+  mSymmetricCipherExpected = aSymmetricCipher;
-+  return NS_OK;
-+}
-+
- nsresult
- nsNSSSocketInfo::GetHandshakePending(bool *aHandshakePending)
- {
-   *aHandshakePending = mHandshakePending;
-   return NS_OK;
- }
- 
- nsresult
-@@ -193,21 +241,36 @@ getSecureBrowserUI(nsIInterfaceRequestor
-     if (docShell) {
-       (void) docShell->GetSecurityUI(result);
-     }
-   }
- }
- #endif
- 
- void
-+nsNSSSocketInfo::NoteTimeUntilReady()
-+{
-+  if (mNotedTimeUntilReady)
-+    return;
-+
-+  mNotedTimeUntilReady = true;
-+
-+  // This will include TCP and proxy tunnel wait time
-+  Telemetry::AccumulateTimeDelta(Telemetry::SSL_TIME_UNTIL_READY,
-+                                 mSocketCreationTimestamp, TimeStamp::Now());
-+  PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
-+         ("[%p] nsNSSSocketInfo::NoteTimeUntilReady\n", mFd));
-+}
-+
-+void
- nsNSSSocketInfo::SetHandshakeCompleted(bool aResumedSession)
- {
-   if (!mHandshakeCompleted) {
-     // This will include TCP and proxy tunnel wait time
--    Telemetry::AccumulateTimeDelta(Telemetry::SSL_TIME_UNTIL_READY,
-+    Telemetry::AccumulateTimeDelta(Telemetry::SSL_TIME_UNTIL_HANDSHAKE_FINISHED,
-                                    mSocketCreationTimestamp, TimeStamp::Now());
- 
-     // If the handshake is completed for the first time from just 1 callback
-     // that means that TLS session resumption must have been used.
-     Telemetry::Accumulate(Telemetry::SSL_RESUMED_SESSION, aResumedSession);
- 
-     // Remove the plain text layer as it is not needed anymore.
-     // The plain text layer is not always present - so its not a fatal error
-@@ -215,16 +278,19 @@ nsNSSSocketInfo::SetHandshakeCompleted(b
-     PRFileDesc* poppedPlaintext =
-       PR_GetIdentitiesLayer(mFd, nsSSLIOLayerHelpers::nsSSLPlaintextLayerIdentity);
-     if (poppedPlaintext) {
-       PR_PopIOLayer(mFd, nsSSLIOLayerHelpers::nsSSLPlaintextLayerIdentity);
-       poppedPlaintext->dtor(poppedPlaintext);
-     }
- 
-     mHandshakeCompleted = true;
-+
-+    PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
-+           ("[%p] nsNSSSocketInfo::SetHandshakeCompleted\n", (void*)mFd));
-   }
- }
- 
- void
- nsNSSSocketInfo::SetNegotiatedNPN(const char *value, uint32_t length)
- {
-   if (!value)
-     mNegotiatedNPN.Truncate();
-@@ -1090,16 +1156,18 @@ nsSSLIOLayerPoll(PRFileDesc * fd, int16_
- 
- nsSSLIOLayerHelpers::nsSSLIOLayerHelpers()
- : mutex(nullptr)
- , mTLSIntolerantSites(nullptr)
- , mTLSTolerantSites(nullptr)
- , mRenegoUnrestrictedSites(nullptr)
- , mTreatUnsafeNegotiationAsBroken(false)
- , mWarnLevelMissingRFC5746(1)
-+, mFalseStartRequireNPN(true)
-+, mFalseStartRequireForwardSecrecy(false)
- {
- }
- 
- static int _PSM_InvalidInt(void)
- {
-     PR_ASSERT(!"I/O method is invalid");
-     PR_SetError(PR_INVALID_METHOD_ERROR, 0);
-     return -1;
-@@ -1280,16 +1348,22 @@ PrefObserver::Observe(nsISupports *aSubj
-     } else if (prefName.Equals("security.ssl.treat_unsafe_negotiation_as_broken")) {
-       bool enabled;
-       Preferences::GetBool("security.ssl.treat_unsafe_negotiation_as_broken", &enabled);
-       mOwner->setTreatUnsafeNegotiationAsBroken(enabled);
-     } else if (prefName.Equals("security.ssl.warn_missing_rfc5746")) {
-       int32_t warnLevel = 1;
-       Preferences::GetInt("security.ssl.warn_missing_rfc5746", &warnLevel);
-       mOwner->setWarnLevelMissingRFC5746(warnLevel);
-+    } else if (prefName.Equals("security.ssl.false_start.require-npn")) {
-+      Preferences::GetBool("security.ssl.false_start.require-npn",
-+                           &mOwner->mFalseStartRequireNPN);
-+    } else if (prefName.Equals("security.ssl.false_start.require-forward-secrecy")) {
-+      Preferences::GetBool("security.ssl.false_start.require-forward-secrecy",
-+                           &mOwner->mFalseStartRequireForwardSecrecy);
-     }
-   }
-   return NS_OK;
- }
- 
- static int32_t PlaintextRecv(PRFileDesc *fd, void *buf, int32_t amount,
-                              int flags, PRIntervalTime timeout)
- {
-@@ -1307,16 +1381,18 @@ static int32_t PlaintextRecv(PRFileDesc 
-   return bytesRead;
- }
- 
- nsSSLIOLayerHelpers::~nsSSLIOLayerHelpers()
- {
-   Preferences::RemoveObserver(mPrefObserver, "security.ssl.renego_unrestricted_hosts");
-   Preferences::RemoveObserver(mPrefObserver, "security.ssl.treat_unsafe_negotiation_as_broken");
-   Preferences::RemoveObserver(mPrefObserver, "security.ssl.warn_missing_rfc5746");
-+  Preferences::RemoveObserver(mPrefObserver, "security.ssl.false_start.require-npn");
-+  Preferences::RemoveObserver(mPrefObserver, "security.ssl.false_start.require-forward-secrecy");
- }
- 
- nsresult nsSSLIOLayerHelpers::Init()
- {
-   if (!nsSSLIOLayerInitialized) {
-     nsSSLIOLayerInitialized = true;
-     nsSSLIOLayerIdentity = PR_GetUniqueIdentity("NSS layer");
-     nsSSLIOLayerMethods  = *PR_GetDefaultIOMethods();
-@@ -1381,24 +1457,32 @@ nsresult nsSSLIOLayerHelpers::Init()
-   bool enabled = false;
-   Preferences::GetBool("security.ssl.treat_unsafe_negotiation_as_broken", &enabled);
-   setTreatUnsafeNegotiationAsBroken(enabled);
- 
-   int32_t warnLevel = 1;
-   Preferences::GetInt("security.ssl.warn_missing_rfc5746", &warnLevel);
-   setWarnLevelMissingRFC5746(warnLevel);
- 
-+  Preferences::GetBool("security.ssl.false_start.require-npn",
-+                       &mFalseStartRequireNPN);
-+  Preferences::GetBool("security.ssl.false_start.require-forward-secrecy",
-+                       &mFalseStartRequireForwardSecrecy);
-+
-   mPrefObserver = new PrefObserver(this);
-   Preferences::AddStrongObserver(mPrefObserver,
-                                  "security.ssl.renego_unrestricted_hosts");
-   Preferences::AddStrongObserver(mPrefObserver,
-                                  "security.ssl.treat_unsafe_negotiation_as_broken");
-   Preferences::AddStrongObserver(mPrefObserver,
-                                  "security.ssl.warn_missing_rfc5746");
--
-+  Preferences::AddStrongObserver(mPrefObserver,
-+                                 "security.ssl.false_start.require-npn");
-+  Preferences::AddStrongObserver(mPrefObserver,
-+                                 "security.ssl.false_start.require-forward-secrecy");
-   return NS_OK;
- }
- 
- void nsSSLIOLayerHelpers::clearStoredData()
- {
-   mRenegoUnrestrictedSites->Clear();
-   mTLSTolerantSites->Clear();
-   mTLSIntolerantSites->Clear();
-@@ -2474,16 +2558,17 @@ nsSSLIOLayerImportFD(PRFileDesc *fd,
-   nsNSSShutDownPreventionLock locker;
-   PRFileDesc* sslSock = SSL_ImportFD(nullptr, fd);
-   if (!sslSock) {
-     NS_ASSERTION(false, "NSS: Error importing socket");
-     return nullptr;
-   }
-   SSL_SetPKCS11PinArg(sslSock, (nsIInterfaceRequestor*)infoObject);
-   SSL_HandshakeCallback(sslSock, HandshakeCallback, infoObject);
-+  SSL_SetCanFalseStartCallback(sslSock, CanFalseStartCallback, infoObject);
- 
-   // Disable this hook if we connect anonymously. See bug 466080.
-   uint32_t flags = 0;
-   infoObject->GetProviderFlags(&flags);
-   if (flags & nsISocketProvider::ANONYMOUS_CONNECT) {
-       SSL_GetClientAuthDataHook(sslSock, nullptr, infoObject);
-   } else {
-       SSL_GetClientAuthDataHook(sslSock, 
-diff --git a/security/manager/ssl/src/nsNSSIOLayer.h b/security/manager/ssl/src/nsNSSIOLayer.h
---- a/security/manager/ssl/src/nsNSSIOLayer.h
-+++ b/security/manager/ssl/src/nsNSSIOLayer.h
-@@ -56,16 +56,17 @@ public:
- 
-   void SetAllowTLSIntoleranceTimeout(bool aAllow);
- 
-   PRStatus CloseSocketAndDestroy(
-                 const nsNSSShutDownPreventionLock & proofOfLock);
-   
-   void SetNegotiatedNPN(const char *value, uint32_t length);
-   void SetHandshakeCompleted(bool aResumedSession);
-+  void NoteTimeUntilReady();
- 
-   bool GetJoined() { return mJoined; }
-   void SetSentClientCert() { mSentClientCert = true; }
- 
-   uint32_t GetProviderFlags() const { return mProviderFlags; }
- 
-   mozilla::psm::SharedSSLState& SharedState();
- 
-@@ -88,40 +89,73 @@ public:
-   }
- 
-   bool IsSSL3Enabled() const { return mSSL3Enabled; }
-   void SetSSL3Enabled(bool enabled) { mSSL3Enabled = enabled; }
-   bool IsTLSEnabled() const { return mTLSEnabled; }
-   void SetTLSEnabled(bool enabled) { mTLSEnabled = enabled; }
- 
-   void AddPlaintextBytesRead(uint64_t val) { mPlaintextBytesRead += val; }
-+
-+  bool IsPreliminaryHandshakeDone() const { return mPreliminaryHandshakeDone; }
-+  void SetPreliminaryHandshakeDone() { mPreliminaryHandshakeDone = true; }
-+
-+  void SetKEAUsed(PRUint16 kea) { mKEAUsed = kea; }
-+  inline int16_t GetKEAExpected() // infallible in nsISSLSocketControl
-+  {
-+    int16_t result;
-+    mozilla::DebugOnly<nsresult> rv = GetKEAExpected(&result);
-+    MOZ_ASSERT(NS_SUCCEEDED(rv));
-+    return result;
-+  }
-+  void SetSymmetricCipherUsed(PRUint16 symmetricCipher)
-+  {
-+    mSymmetricCipherUsed = symmetricCipher;
-+  }
-+  inline int16_t GetSymmetricCipherExpected() // infallible in nsISSLSocketControl
-+  {
-+    int16_t result;
-+    mozilla::DebugOnly<nsresult> rv = GetSymmetricCipherExpected(&result);
-+    MOZ_ASSERT(NS_SUCCEEDED(rv));
-+    return result;
-+  }
-+
- private:
-   PRFileDesc* mFd;
- 
-   CertVerificationState mCertVerificationState;
- 
-   mozilla::psm::SharedSSLState& mSharedState;
-   bool mForSTARTTLS;
-   bool mSSL3Enabled;
-   bool mTLSEnabled;
-   bool mHandshakePending;
-   bool mHasCleartextPhase;
-   bool mHandshakeInProgress;
-   bool mAllowTLSIntoleranceTimeout;
-   bool mRememberClientAuthCertificate;
-+  bool mPreliminaryHandshakeDone; // after false start items are complete
-   PRIntervalTime mHandshakeStartTime;
-   bool mFirstServerHelloReceived;
- 
-   nsresult ActivateSSL();
- 
-   nsCString mNegotiatedNPN;
-   bool      mNPNCompleted;
-   bool      mHandshakeCompleted;
-   bool      mJoined;
-   bool      mSentClientCert;
-+  bool      mNotedTimeUntilReady;
-+
-+  // mKEA* and mSymmetricCipher* are used in false start detetermination
-+  // values are from nsISSLSocketControl
-+  PRInt16 mKEAUsed;
-+  PRInt16 mKEAExpected;
-+  PRInt16 mSymmetricCipherUsed;
-+  PRInt16 mSymmetricCipherExpected;
- 
-   uint32_t mProviderFlags;
-   mozilla::TimeStamp mSocketCreationTimestamp;
-   uint64_t mPlaintextBytesRead;
- };
- 
- class nsSSLIOLayerHelpers
- {
-@@ -159,16 +193,19 @@ public:
-   void addIntolerantSite(const nsCString &str);
-   void removeIntolerantSite(const nsCString &str);
-   bool isKnownAsIntolerantSite(const nsCString &str);
- 
-   void setRenegoUnrestrictedSites(const nsCString &str);
-   bool isRenegoUnrestrictedSite(const nsCString &str);
- 
-   void clearStoredData();
-+
-+  bool mFalseStartRequireNPN;
-+  bool mFalseStartRequireForwardSecrecy;
- private:
-   nsCOMPtr<nsIObserver> mPrefObserver;
- };
- 
- nsresult nsSSLIOLayerNewSocket(int32_t family,
-                                const char *host,
-                                int32_t port,
-                                const char *proxyHost,
-diff --git a/toolkit/components/telemetry/Histograms.json b/toolkit/components/telemetry/Histograms.json
---- a/toolkit/components/telemetry/Histograms.json
-+++ b/toolkit/components/telemetry/Histograms.json
-@@ -838,16 +838,22 @@
-   },
-   "SSL_TIME_UNTIL_READY": {
-     "kind": "exponential",
-     "high": "60000",
-     "n_buckets": 200,
-     "extended_statistics_ok": true,
-     "description": "ms of SSL wait time including TCP and proxy tunneling"
-   },
-+  "SSL_TIME_UNTIL_HANDSHAKE_FINISHED": {
-+    "kind": "exponential",
-+    "high": "60000",
-+    "n_buckets": 200,
-+    "description": "ms of SSL wait time for full handshake including TCP and proxy tunneling"
-+  },
-   "SSL_BYTES_BEFORE_CERT_CALLBACK": {
-     "kind": "exponential",
-     "high": "32000",
-     "n_buckets": 64,
-     "extended_statistics_ok": true,
-     "description": "plaintext bytes read before a server certificate authenticated"
-   },
-   "SSL_NPN_TYPE": {
new file mode 100644
--- /dev/null
+++ b/security/patches/bug-713933-false-start.patch
@@ -0,0 +1,836 @@
+# HG changeset patch
+# User Patrick McManus <mcmanus@ducksong.com>
+# Date 1372656196 25200
+# Node ID f6bc026a0c368178b4d327bf05de785305161d72
+# Parent  89a5e4356ad1f7bc9d9d24f6409c6d963dde3ca4
+Bug 713933: Make false start work with asynchronous certificate verification, r=bsmith
+
+diff --git a/security/nss/lib/ssl/ssl.def b/security/nss/lib/ssl/ssl.def
+--- a/security/nss/lib/ssl/ssl.def
++++ b/security/nss/lib/ssl/ssl.def
+@@ -158,8 +158,15 @@ SSL_SetSRTPCiphers;
+ ;+};
+ ;+NSS_3.15 {      # NSS 3.15 release
+ ;+    global:
+ SSL_PeerStapledOCSPResponses;
+ SSL_SetStapledOCSPResponses;
+ ;+    local:
+ ;+*;
+ ;+};
++;+NSS_3.15.2 {    # NSS 3.15.2 release
++;+    global:
++SSL_SetCanFalseStartCallback;
++SSL_DefaultCanFalseStart;
++;+    local:
++;+*;
++;+};
+diff --git a/security/nss/lib/ssl/ssl.h b/security/nss/lib/ssl/ssl.h
+--- a/security/nss/lib/ssl/ssl.h
++++ b/security/nss/lib/ssl/ssl.h
+@@ -116,24 +116,32 @@ SSL_IMPORT PRFileDesc *DTLS_ImportFD(PRF
+ #define SSL_REQUIRE_SAFE_NEGOTIATION   21 /* Peer must send Signaling       */
+ 					  /* Cipher Suite Value (SCSV) or   */
+                                           /* Renegotiation  Info (RI)       */
+ 					  /* extension in ALL handshakes.   */
+                                           /* default: off                   */
+ #define SSL_ENABLE_FALSE_START         22 /* Enable SSL false start (off by */
+                                           /* default, applies only to       */
+                                           /* clients). False start is a     */
+-/* mode where an SSL client will start sending application data before      */
+-/* verifying the server's Finished message. This means that we could end up */
+-/* sending data to an imposter. However, the data will be encrypted and     */
+-/* only the true server can derive the session key. Thus, so long as the    */
+-/* cipher isn't broken this is safe. Because of this, False Start will only */
+-/* occur on RSA or DH ciphersuites where the cipher's key length is >= 80   */
+-/* bits. The advantage of False Start is that it saves a round trip for     */
+-/* client-speaks-first protocols when performing a full handshake.          */
++/* mode where an SSL client will start sending application data before
++ * verifying the server's Finished message. This means that we could end up
++ * sending data to an imposter. However, the data will be encrypted and
++ * only the true server can derive the session key. Thus, so long as the
++ * cipher isn't broken this is safe. The advantage of false start is that
++ * it saves a round trip for client-speaks-first protocols when performing a
++ * full handshake.
++ *
++ * See SSL_DefaultCanFalseStart for the default criteria that NSS uses to
++ * determine whether to false start or not. See SSL_SetCanFalseStartCallback
++ * for how to change that criteria. In addition to those criteria, false start
++ * will only be done when the server selects a cipher suite with an effective
++ * key length of 80 bits or more (including RC4-128). Also, see
++ * SSL_HandshakeCallback for a description on how false start affects when the
++ * handshake callback gets called.
++ */
+ 
+ /* For SSL 3.0 and TLS 1.0, by default we prevent chosen plaintext attacks
+  * on SSL CBC mode cipher suites (see RFC 4346 Section F.3) by splitting
+  * non-empty application_data records into two records; the first record has
+  * only the first byte of plaintext, and the second has the rest.
+  *
+  * This only prevents the attack in the sending direction; the connection may
+  * still be vulnerable to such attacks if the peer does not implement a similar
+@@ -648,24 +656,69 @@ SSL_IMPORT SECStatus SSL_SetMaxServerCac
+ /* called in child to inherit SID Cache variables. 
+  * If envString is NULL, this function will use the value of the environment
+  * variable "SSL_INHERITANCE", otherwise the string value passed in will be 
+  * used.
+  */
+ SSL_IMPORT SECStatus SSL_InheritMPServerSIDCache(const char * envString);
+ 
+ /*
+-** Set the callback on a particular socket that gets called when we finish
+-** performing a handshake.
++** Set the callback that normally gets called when the TLS handshake
++** is complete. If false start is not enabled, then the handshake callback is
++** called after verifying the peer's Finished message and before sending
++** outgoing application data and before processing incoming application data.
++**
++** If false start is enabled and there is a custom CanFalseStartCallback
++** callback set, then the handshake callback gets called after the peer's
++** Finished message has been verified, which may be after application data is
++** sent.
++**
++** If false start is enabled and there is not a custom CanFalseStartCallback
++** callback established with SSL_SetCanFalseStartCallback then the handshake
++** callback gets called before any application data is sent, which may be
++** before the peer's Finished message has been verified.
+ */
+ typedef void (PR_CALLBACK *SSLHandshakeCallback)(PRFileDesc *fd,
+                                                  void *client_data);
+ SSL_IMPORT SECStatus SSL_HandshakeCallback(PRFileDesc *fd, 
+ 			          SSLHandshakeCallback cb, void *client_data);
+ 
++/* Applications that wish to customize TLS false start should set this callback
++** function. NSS will invoke the functon to determine if a particular
++** connection should use false start or not. SECSuccess indicates that the
++** callback completed successfully, and if so *canFalseStart indicates if false
++** start can be used. If the callback does not return SECSuccess then the
++** handshake will be canceled.
++**
++** Applications that do not set the callback will use an internal set of
++** criteria to determine if the connection should false start. If
++** the callback is set false start will never be used without invoking the
++** callback function, but some connections (e.g. resumed connections) will
++** never use false start and therefore will not invoke the callback.
++**
++** NSS's internal criteria for this connection can be evaluated by calling
++** SSL_DefaultCanFalseStart() from the custom callback.
++**
++** See the description of SSL_HandshakeCallback for important information on
++** how registering a custom false start callback affects when the handshake
++** callback gets called.
++**/
++typedef SECStatus (PR_CALLBACK *SSLCanFalseStartCallback)(
++    PRFileDesc *fd, void *arg, PRBool *canFalseStart);
++
++SSL_IMPORT SECStatus SSL_SetCanFalseStartCallback(
++    PRFileDesc *fd, SSLCanFalseStartCallback callback, void *arg);
++
++/* A utility function that can be called from a custom CanFalseStartCallback
++** function to determine what NSS would have done for this connection if the
++** custom callback was not implemented.
++**/
++SSL_IMPORT SECStatus SSL_DefaultCanFalseStart(PRFileDesc *fd,
++                                              PRBool *canFalseStart);
++
+ /*
+ ** For the server, request a new handshake.  For the client, begin a new
+ ** handshake.  If flushCache is non-zero, the SSL3 cache entry will be 
+ ** flushed first, ensuring that a full SSL handshake will be done.
+ ** If flushCache is zero, and an SSL connection is established, it will 
+ ** do the much faster session restart handshake.  This will change the 
+ ** session keys without doing another private key operation.
+ */
+diff --git a/security/nss/lib/ssl/ssl3con.c b/security/nss/lib/ssl/ssl3con.c
+--- a/security/nss/lib/ssl/ssl3con.c
++++ b/security/nss/lib/ssl/ssl3con.c
+@@ -6664,45 +6664,61 @@ loser:
+     PORT_SetError(errCode);
+     rv = SECFailure;
+ done:
+     if (arena != NULL)
+     	PORT_FreeArena(arena, PR_FALSE);
+     return rv;
+ }
+ 
+-PRBool
+-ssl3_CanFalseStart(sslSocket *ss) {
+-    PRBool rv;
++static SECStatus
++ssl3_CheckFalseStart(sslSocket *ss)
++{
++    SECStatus rv;
++    PRBool maybeFalseStart = PR_TRUE;
+ 
+     PORT_Assert( ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss) );
+-
+-    /* XXX: does not take into account whether we are waiting for
+-     * SSL_AuthCertificateComplete or SSL_RestartHandshakeAfterCertReq. If/when
+-     * that is done, this function could return different results each time it
+-     * would be called.
+-     */
++    PORT_Assert( !ss->ssl3.hs.authCertificatePending );
++
++    /* An attacker can control the selected ciphersuite so we only wish to
++     * do False Start in the case that the selected ciphersuite is
++     * sufficiently strong that the attack can gain no advantage.
++     * Therefore we always require an 80-bit cipher. */
+ 
+     ssl_GetSpecReadLock(ss);
+-    rv = ss->opt.enableFalseStart &&
+-	 !ss->sec.isServer &&
+-	 !ss->ssl3.hs.isResuming &&
+-	 ss->ssl3.cwSpec &&
+-
+-	 /* An attacker can control the selected ciphersuite so we only wish to
+-	  * do False Start in the case that the selected ciphersuite is
+-	  * sufficiently strong that the attack can gain no advantage.
+-	  * Therefore we require an 80-bit cipher and a forward-secret key
+-	  * exchange. */
+-	 ss->ssl3.cwSpec->cipher_def->secret_key_size >= 10 &&
+-	(ss->ssl3.hs.kea_def->kea == kea_dhe_dss ||
+-	 ss->ssl3.hs.kea_def->kea == kea_dhe_rsa ||
+-	 ss->ssl3.hs.kea_def->kea == kea_ecdhe_ecdsa ||
+-	 ss->ssl3.hs.kea_def->kea == kea_ecdhe_rsa);
++    if (ss->ssl3.cwSpec->cipher_def->secret_key_size < 10) {
++	ss->ssl3.hs.canFalseStart = PR_FALSE;
++	maybeFalseStart = PR_FALSE;
++    }
+     ssl_ReleaseSpecReadLock(ss);
++    if (!maybeFalseStart) {
++	return SECSuccess;
++    }
++
++    if (!ss->canFalseStartCallback) {
++	rv = SSL_DefaultCanFalseStart(ss->fd, &ss->ssl3.hs.canFalseStart);
++
++	if (rv == SECSuccess &&
++            ss->ssl3.hs.canFalseStart && ss->handshakeCallback) {
++	    /* Call the handshake callback here for backwards compatibility
++	     * with applications that were using false start before
++	     * canFalseStartCallback was added.
++	     */
++	    (ss->handshakeCallback)(ss->fd, ss->handshakeCallbackData);
++	}
++    } else {
++	rv = (ss->canFalseStartCallback)(ss->fd,
++ 					 ss->canFalseStartCallbackData,
++					 &ss->ssl3.hs.canFalseStart);
++    }
++
++    if (rv != SECSuccess) {
++	ss->ssl3.hs.canFalseStart = PR_FALSE;
++    }
++
+     return rv;
+ }
+ 
+ static SECStatus ssl3_SendClientSecondRound(sslSocket *ss);
+ 
+ /* Called from ssl3_HandleHandshakeMessage() when it has deciphered a complete
+  * ssl3 Server Hello Done message.
+  * Caller must hold Handshake and RecvBuf locks.
+@@ -6722,16 +6738,17 @@ ssl3_HandleServerHelloDone(sslSocket *ss
+         ws != wait_server_cert &&
+ 	ws != wait_server_key  &&
+ 	ws != wait_cert_request) {
+ 	SSL3_SendAlert(ss, alert_fatal, unexpected_message);
+ 	PORT_SetError(SSL_ERROR_RX_UNEXPECTED_HELLO_DONE);
+ 	return SECFailure;
+     }
+ 
++    ss->enoughFirstHsDone = PR_TRUE;
+     rv = ssl3_SendClientSecondRound(ss);
+ 
+     return rv;
+ }
+ 
+ /* Called from ssl3_HandleServerHelloDone and ssl3_AuthCertificateComplete.
+  *
+  * Caller must hold Handshake and RecvBuf locks.
+@@ -6820,35 +6837,47 @@ ssl3_SendClientSecondRound(sslSocket *ss
+     /* XXX: If the server's certificate hasn't been authenticated by this
+      * point, then we may be leaking this NPN message to an attacker.
+      */
+     if (!ss->firstHsDone) {
+ 	rv = ssl3_SendNextProto(ss);
+ 	if (rv != SECSuccess) {
+ 	    goto loser;	/* err code was set. */
+ 	}
++
++	if (ss->opt.enableFalseStart) {
++	    if (!ss->ssl3.hs.authCertificatePending) {
++		rv = ssl3_CheckFalseStart(ss);
++		if (rv != SECSuccess) {
++		    goto loser;
++		}
++	    } else {
++		/* The certificate authentication and the server's Finished
++		 * message are going to race each other. If the certificate
++		 * authentication wins, then we will try to false start. If the
++		 * server's Finished message wins, then ssl3_HandleFinished will
++		 * reset restartTarget to ssl3_FinishHandshake.
++		 */
++		ss->ssl3.hs.restartTarget = ssl3_CheckFalseStart;
++	    }
++	}
+     }
+ 
+     rv = ssl3_SendFinished(ss, 0);
+     if (rv != SECSuccess) {
+ 	goto loser;	/* err code was set. */
+     }
+ 
+     ssl_ReleaseXmitBufLock(ss);		/*******************************/
+ 
+     if (ssl3_ExtensionNegotiated(ss, ssl_session_ticket_xtn))
+ 	ss->ssl3.hs.ws = wait_new_session_ticket;
+     else
+ 	ss->ssl3.hs.ws = wait_change_cipher;
+ 
+-    /* Do the handshake callback for sslv3 here, if we can false start. */
+-    if (ss->handshakeCallback != NULL && ssl3_CanFalseStart(ss)) {
+-	(ss->handshakeCallback)(ss->fd, ss->handshakeCallbackData);
+-    }
+-
+     return SECSuccess;
+ 
+ loser:
+     ssl_ReleaseXmitBufLock(ss);
+     return rv;
+ }
+ 
+ /*
+@@ -9411,23 +9440,16 @@ ssl3_AuthCertificate(sslSocket *ss)
+ 	    if (ss->sec.isServer) {
+ 		errCode = SSL_ERROR_FEATURE_NOT_SUPPORTED_FOR_SERVERS;
+ 		rv = SECFailure;
+ 		goto loser;
+ 	    }
+ 
+ 	    ss->ssl3.hs.authCertificatePending = PR_TRUE;
+ 	    rv = SECSuccess;
+-
+-	    /* XXX: Async cert validation and False Start don't work together
+-	     * safely yet; if we leave False Start enabled, we may end up false
+-	     * starting (sending application data) before we
+-	     * SSL_AuthCertificateComplete has been called.
+-	     */
+-	    ss->opt.enableFalseStart = PR_FALSE;
+ 	}
+ 
+ 	if (rv != SECSuccess) {
+ 	    ssl3_SendAlertForCertError(ss, errCode);
+ 	    goto loser;
+ 	}
+     }
+ 
+@@ -10065,16 +10087,21 @@ xmit_loser:
+ 	ssl_ReleaseSpecReadLock(ss);  /*************************************/
+ 
+ 	/* If the wrap failed, we don't cache the sid.
+ 	 * The connection continues normally however.
+ 	 */
+ 	ss->ssl3.hs.cacheSID = rv == SECSuccess;
+     }
+ 
++    /* Cancel false start check since we already completed the handshake */
++    if (ss->ssl3.hs.restartTarget == ssl3_CheckFalseStart) {
++	ss->ssl3.hs.restartTarget = NULL;
++    }
++
+     if (ss->ssl3.hs.authCertificatePending) {
+ 	if (ss->ssl3.hs.restartTarget) {
+ 	    PR_NOT_REACHED("ssl3_HandleFinished: unexpected restartTarget");
+ 	    PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ 	    return SECFailure;
+ 	}
+ 
+ 	ss->ssl3.hs.restartTarget = ssl3_FinishHandshake;
+@@ -10083,33 +10110,41 @@ xmit_loser:
+ 
+     rv = ssl3_FinishHandshake(ss);
+     return rv;
+ }
+ 
+ SECStatus
+ ssl3_FinishHandshake(sslSocket * ss)
+ {
++    PRBool falseStarted;
++
+     PORT_Assert( ss->opt.noLocks || ssl_HaveRecvBufLock(ss) );
+     PORT_Assert( ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss) );
+     PORT_Assert( ss->ssl3.hs.restartTarget == NULL );
+ 
+     /* The first handshake is now completed. */
+     ss->handshake           = NULL;
+     ss->firstHsDone         = PR_TRUE;
++    ss->enoughFirstHsDone   = PR_TRUE;
+ 
+     if (ss->ssl3.hs.cacheSID) {
+ 	(*ss->sec.cache)(ss->sec.ci.sid);
+ 	ss->ssl3.hs.cacheSID = PR_FALSE;
+     }
+ 
+     ss->ssl3.hs.ws = idle_handshake;
+-
+-    /* Do the handshake callback for sslv3 here, if we cannot false start. */
+-    if (ss->handshakeCallback != NULL && !ssl3_CanFalseStart(ss)) {
++    falseStarted = ss->ssl3.hs.canFalseStart;
++    ss->ssl3.hs.canFalseStart = PR_FALSE; /* False Start phase is complete */
++
++    /* Call the handshake callback for sslv3 here, unless we called it already
++     * for the case where false start was done without a canFalseStartCallback.
++     */
++    if (ss->handshakeCallback != NULL &&
++	!(falseStarted && !ss->canFalseStartCallback)) {
+ 	(ss->handshakeCallback)(ss->fd, ss->handshakeCallbackData);
+     }
+ 
+     return SECSuccess;
+ }
+ 
+ /* Called from ssl3_HandleHandshake() when it has gathered a complete ssl3
+  * hanshake message.
+diff --git a/security/nss/lib/ssl/ssl3gthr.c b/security/nss/lib/ssl/ssl3gthr.c
+--- a/security/nss/lib/ssl/ssl3gthr.c
++++ b/security/nss/lib/ssl/ssl3gthr.c
+@@ -369,19 +369,17 @@ ssl3_GatherCompleteHandshake(sslSocket *
+ 	    return ss->recvdCloseNotify ? 0 : rv;
+ 	}
+ 
+ 	/* If we kicked off a false start in ssl3_HandleServerHelloDone, break
+ 	 * out of this loop early without finishing the handshake.
+ 	 */
+ 	if (ss->opt.enableFalseStart) {
+ 	    ssl_GetSSL3HandshakeLock(ss);
+-	    canFalseStart = (ss->ssl3.hs.ws == wait_change_cipher ||
+-			     ss->ssl3.hs.ws == wait_new_session_ticket) &&
+-		            ssl3_CanFalseStart(ss);
++	    canFalseStart = ss->ssl3.hs.canFalseStart;
+ 	    ssl_ReleaseSSL3HandshakeLock(ss);
+ 	}
+     } while (ss->ssl3.hs.ws != idle_handshake &&
+              !canFalseStart &&
+              ss->gs.buf.len == 0);
+ 
+     ss->gs.readOffset = 0;
+     ss->gs.writeOffset = ss->gs.buf.len;
+diff --git a/security/nss/lib/ssl/sslauth.c b/security/nss/lib/ssl/sslauth.c
+--- a/security/nss/lib/ssl/sslauth.c
++++ b/security/nss/lib/ssl/sslauth.c
+@@ -55,17 +55,16 @@ SSL_LocalCertificate(PRFileDesc *fd)
+ /* NEED LOCKS IN HERE.  */
+ SECStatus
+ SSL_SecurityStatus(PRFileDesc *fd, int *op, char **cp, int *kp0, int *kp1,
+ 		   char **ip, char **sp)
+ {
+     sslSocket *ss;
+     const char *cipherName;
+     PRBool isDes = PR_FALSE;
+-    PRBool enoughFirstHsDone = PR_FALSE;
+ 
+     ss = ssl_FindSocket(fd);
+     if (!ss) {
+ 	SSL_DBG(("%d: SSL[%d]: bad socket in SecurityStatus",
+ 		 SSL_GETPID(), fd));
+ 	return SECFailure;
+     }
+ 
+@@ -73,24 +72,17 @@ SSL_SecurityStatus(PRFileDesc *fd, int *
+     if (kp0) *kp0 = 0;
+     if (kp1) *kp1 = 0;
+     if (ip) *ip = 0;
+     if (sp) *sp = 0;
+     if (op) {
+ 	*op = SSL_SECURITY_STATUS_OFF;
+     }
+ 
+-    if (ss->firstHsDone) {
+-	enoughFirstHsDone = PR_TRUE;
+-    } else if (ss->version >= SSL_LIBRARY_VERSION_3_0 &&
+-	       ssl3_CanFalseStart(ss)) {
+-	enoughFirstHsDone = PR_TRUE;
+-    }
+-
+-    if (ss->opt.useSecurity && enoughFirstHsDone) {
++    if (ss->opt.useSecurity && ss->enoughFirstHsDone) {
+ 	if (ss->version < SSL_LIBRARY_VERSION_3_0) {
+ 	    cipherName = ssl_cipherName[ss->sec.cipherType];
+ 	} else {
+ 	    cipherName = ssl3_cipherName[ss->sec.cipherType];
+ 	}
+ 	PORT_Assert(cipherName);
+ 	if (cipherName) {
+             if (PORT_Strstr(cipherName, "DES")) isDes = PR_TRUE;
+diff --git a/security/nss/lib/ssl/sslimpl.h b/security/nss/lib/ssl/sslimpl.h
+--- a/security/nss/lib/ssl/sslimpl.h
++++ b/security/nss/lib/ssl/sslimpl.h
+@@ -863,16 +863,18 @@ const ssl3CipherSuiteDef *suite_def;
+ 					    * in progress. */
+     unsigned char         cookie[32];      /* The cookie */
+     unsigned char         cookieLen;       /* The length of the cookie */
+     PRIntervalTime        rtTimerStarted;  /* When the timer was started */
+     DTLSTimerCb           rtTimerCb;       /* The function to call on expiry */
+     PRUint32              rtTimeoutMs;     /* The length of the current timeout
+ 					    * used for backoff (in ms) */
+     PRUint32              rtRetries;       /* The retry counter */
++    PRBool                canFalseStart;   /* Can/did we False Start */
++
+ } SSL3HandshakeState;
+ 
+ 
+ 
+ /*
+ ** This is the "ssl3" struct, as in "ss->ssl3".
+ ** note:
+ ** usually,   crSpec == cwSpec and prSpec == pwSpec.  
+@@ -1111,16 +1113,20 @@ struct sslSocketStr {
+     sslOptions       opt;
+     /* Enabled version range */
+     SSLVersionRange  vrange;
+ 
+     /* State flags */
+     unsigned long    clientAuthRequested;
+     unsigned long    delayDisabled;       /* Nagle delay disabled */
+     unsigned long    firstHsDone;         /* first handshake is complete. */
++    unsigned long    enoughFirstHsDone;   /* enough of the handshake is done
++					   * for callbacks to be able to
++					   * retrieve channel security
++					   * parameters from callback functions. */
+     unsigned long    handshakeBegun;     
+     unsigned long    lastWriteBlocked;   
+     unsigned long    recvdCloseNotify;    /* received SSL EOF. */
+     unsigned long    TCPconnected;       
+     unsigned long    appDataBuffered;
+     unsigned long    peerRequestedProtection; /* from old renegotiation */
+ 
+     /* version of the protocol to use */
+@@ -1151,16 +1157,18 @@ const unsigned char *  preferredCipher;
+     SSLGetClientAuthData      getClientAuthData;
+     void                     *getClientAuthDataArg;
+     SSLSNISocketConfig        sniSocketConfig;
+     void                     *sniSocketConfigArg;
+     SSLBadCertHandler         handleBadCert;
+     void                     *badCertArg;
+     SSLHandshakeCallback      handshakeCallback;
+     void                     *handshakeCallbackData;
++    SSLCanFalseStartCallback  canFalseStartCallback;
++    void                     *canFalseStartCallbackData;
+     void                     *pkcs11PinArg;
+     SSLNextProtoCallback      nextProtoCallback;
+     void                     *nextProtoArg;
+ 
+     PRIntervalTime            rTimeout; /* timeout for NSPR I/O */
+     PRIntervalTime            wTimeout; /* timeout for NSPR I/O */
+     PRIntervalTime            cTimeout; /* timeout for NSPR I/O */
+ 
+@@ -1353,17 +1361,16 @@ extern int       ssl3_SendApplicationDat
+ extern PRBool    ssl_FdIsBlocking(PRFileDesc *fd);
+ 
+ extern PRBool    ssl_SocketIsBlocking(sslSocket *ss);
+ 
+ extern void      ssl3_SetAlwaysBlock(sslSocket *ss);
+ 
+ extern SECStatus ssl_EnableNagleDelay(sslSocket *ss, PRBool enabled);
+ 
+-extern PRBool    ssl3_CanFalseStart(sslSocket *ss);
+ extern SECStatus
+ ssl3_CompressMACEncryptRecord(ssl3CipherSpec *   cwSpec,
+ 		              PRBool             isServer,
+ 			      PRBool             isDTLS,
+ 			      PRBool             capRecordVersion,
+                               SSL3ContentType    type,
+ 		              const SSL3Opaque * pIn,
+ 		              PRUint32           contentLen,
+diff --git a/security/nss/lib/ssl/sslinfo.c b/security/nss/lib/ssl/sslinfo.c
+--- a/security/nss/lib/ssl/sslinfo.c
++++ b/security/nss/lib/ssl/sslinfo.c
+@@ -21,41 +21,33 @@ ssl_GetCompressionMethodName(SSLCompress
+ }
+ 
+ SECStatus 
+ SSL_GetChannelInfo(PRFileDesc *fd, SSLChannelInfo *info, PRUintn len)
+ {
+     sslSocket *      ss;
+     SSLChannelInfo   inf;
+     sslSessionID *   sid;
+-    PRBool           enoughFirstHsDone = PR_FALSE;
+ 
+     if (!info || len < sizeof inf.length) { 
+ 	PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ 	return SECFailure;
+     }
+ 
+     ss = ssl_FindSocket(fd);
+     if (!ss) {
+ 	SSL_DBG(("%d: SSL[%d]: bad socket in SSL_GetChannelInfo",
+ 		 SSL_GETPID(), fd));
+ 	return SECFailure;
+     }
+ 
+     memset(&inf, 0, sizeof inf);
+     inf.length = PR_MIN(sizeof inf, len);
+ 
+-    if (ss->firstHsDone) {
+-	enoughFirstHsDone = PR_TRUE;
+-    } else if (ss->version >= SSL_LIBRARY_VERSION_3_0 &&
+-	       ssl3_CanFalseStart(ss)) {
+-	enoughFirstHsDone = PR_TRUE;
+-    }
+-
+-    if (ss->opt.useSecurity && enoughFirstHsDone) {
++    if (ss->opt.useSecurity && ss->enoughFirstHsDone) {
+         sid = ss->sec.ci.sid;
+ 	inf.protocolVersion  = ss->version;
+ 	inf.authKeyBits      = ss->sec.authKeyBits;
+ 	inf.keaKeyBits       = ss->sec.keaKeyBits;
+ 	if (ss->version < SSL_LIBRARY_VERSION_3_0) { /* SSL2 */
+ 	    inf.cipherSuite           = ss->sec.cipherType | 0xff00;
+ 	    inf.compressionMethod     = ssl_compression_null;
+ 	    inf.compressionMethodName = "N/A";
+diff --git a/security/nss/lib/ssl/sslreveal.c b/security/nss/lib/ssl/sslreveal.c
+--- a/security/nss/lib/ssl/sslreveal.c
++++ b/security/nss/lib/ssl/sslreveal.c
+@@ -72,40 +72,33 @@ SSL_RevealURL(PRFileDesc * fd)
+ 
+ SECStatus
+ SSL_HandshakeNegotiatedExtension(PRFileDesc * socket, 
+                                  SSLExtensionType extId,
+                                  PRBool *pYes)
+ {
+   /* some decisions derived from SSL_GetChannelInfo */
+   sslSocket * sslsocket = NULL;
+-  PRBool enoughFirstHsDone = PR_FALSE;
+ 
+   if (!pYes) {
+     PORT_SetError(SEC_ERROR_INVALID_ARGS);
+     return SECFailure;
+   }
+ 
+   sslsocket = ssl_FindSocket(socket);
+   if (!sslsocket) {
+     SSL_DBG(("%d: SSL[%d]: bad socket in HandshakeNegotiatedExtension",
+              SSL_GETPID(), socket));
+     return SECFailure;
+   }
+ 
+   *pYes = PR_FALSE;
+ 
+-  if (sslsocket->firstHsDone) {
+-    enoughFirstHsDone = PR_TRUE;
+-  } else if (sslsocket->ssl3.initialized && ssl3_CanFalseStart(sslsocket)) {
+-    enoughFirstHsDone = PR_TRUE;
+-  }
+-
+   /* according to public API SSL_GetChannelInfo, this doesn't need a lock */
+-  if (sslsocket->opt.useSecurity && enoughFirstHsDone) {
++  if (sslsocket->opt.useSecurity && sslsocket->enoughFirstHsDone) {
+     if (sslsocket->ssl3.initialized) { /* SSL3 and TLS */
+       /* now we know this socket went through ssl3_InitState() and
+        * ss->xtnData got initialized, which is the only member accessed by
+        * ssl3_ExtensionNegotiated();
+        * Member xtnData appears to get accessed in functions that handle
+        * the handshake (hello messages and extension sending),
+        * therefore the handshake lock should be sufficient.
+        */
+diff --git a/security/nss/lib/ssl/sslsecur.c b/security/nss/lib/ssl/sslsecur.c
+--- a/security/nss/lib/ssl/sslsecur.c
++++ b/security/nss/lib/ssl/sslsecur.c
+@@ -103,20 +103,22 @@ ssl_Do1stHandshake(sslSocket *ss)
+ 
+ 	    SSL_TRC(3, ("%d: SSL[%d]: handshake is completed",
+ 			SSL_GETPID(), ss->fd));
+             /* call handshake callback for ssl v2 */
+ 	    /* for v3 this is done in ssl3_HandleFinished() */
+ 	    if ((ss->handshakeCallback != NULL) && /* has callback */
+ 		(!ss->firstHsDone) &&              /* only first time */
+ 		(ss->version < SSL_LIBRARY_VERSION_3_0)) {  /* not ssl3 */
+-		ss->firstHsDone     = PR_TRUE;
++		ss->firstHsDone = PR_TRUE;
++		ss->enoughFirstHsDone = PR_TRUE;
+ 		(ss->handshakeCallback)(ss->fd, ss->handshakeCallbackData);
+ 	    }
+-	    ss->firstHsDone         = PR_TRUE;
++	    ss->firstHsDone = PR_TRUE;
++	    ss->enoughFirstHsDone = PR_TRUE;
+ 	    ss->gs.writeOffset = 0;
+ 	    ss->gs.readOffset  = 0;
+ 	    break;
+ 	}
+ 	rv = (*ss->handshake)(ss);
+ 	++loopCount;
+     /* This code must continue to loop on SECWouldBlock, 
+      * or any positive value.	See XXX_1 comments.
+@@ -201,31 +203,34 @@ SSL_ResetHandshake(PRFileDesc *s, PRBool
+ 
+     SSL_LOCK_READER(ss);
+     SSL_LOCK_WRITER(ss);
+ 
+     /* Reset handshake state */
+     ssl_Get1stHandshakeLock(ss);
+ 
+     ss->firstHsDone = PR_FALSE;
++    ss->enoughFirstHsDone = PR_FALSE;
+     if ( asServer ) {
+ 	ss->handshake = ssl2_BeginServerHandshake;
+ 	ss->handshaking = sslHandshakingAsServer;
+     } else {
+ 	ss->handshake = ssl2_BeginClientHandshake;
+ 	ss->handshaking = sslHandshakingAsClient;
+     }
+     ss->nextHandshake       = 0;
+     ss->securityHandshake   = 0;
+ 
+     ssl_GetRecvBufLock(ss);
+     status = ssl_InitGather(&ss->gs);
+     ssl_ReleaseRecvBufLock(ss);
+ 
+     ssl_GetSSL3HandshakeLock(ss);
++    ss->ssl3.hs.canFalseStart = PR_FALSE;
++    ss->ssl3.hs.restartTarget = NULL;
+ 
+     /*
+     ** Blow away old security state and get a fresh setup.
+     */
+     ssl_GetXmitBufLock(ss); 
+     ssl_ResetSecurityInfo(&ss->sec, PR_TRUE);
+     status = ssl_CreateSecurityInfo(ss);
+     ssl_ReleaseXmitBufLock(ss); 
+@@ -326,16 +331,84 @@ SSL_HandshakeCallback(PRFileDesc *fd, SS
+     ss->handshakeCallbackData = client_data;
+ 
+     ssl_ReleaseSSL3HandshakeLock(ss);
+     ssl_Release1stHandshakeLock(ss);
+ 
+     return SECSuccess;
+ }
+ 
++/* Register an application callback to be called when false start may happen.
++** Acquires and releases HandshakeLock.
++*/
++SECStatus
++SSL_SetCanFalseStartCallback(PRFileDesc *fd, SSLCanFalseStartCallback cb,
++			     void *client_data)
++{
++    sslSocket *ss;
++
++    ss = ssl_FindSocket(fd);
++    if (!ss) {
++	SSL_DBG(("%d: SSL[%d]: bad socket in SSL_SetCanFalseStartCallback",
++		 SSL_GETPID(), fd));
++	return SECFailure;
++    }
++
++    if (!ss->opt.useSecurity) {
++	PORT_SetError(SEC_ERROR_INVALID_ARGS);
++	return SECFailure;
++    }
++
++    ssl_Get1stHandshakeLock(ss);
++    ssl_GetSSL3HandshakeLock(ss);
++
++    ss->canFalseStartCallback     = cb;
++    ss->canFalseStartCallbackData = client_data;
++
++    ssl_ReleaseSSL3HandshakeLock(ss);
++    ssl_Release1stHandshakeLock(ss);
++
++    return SECSuccess;
++}
++
++/* A utility function that can be called from a custom CanFalseStartCallback
++** function to determine what NSS would have done for this connection if the
++** custom callback was not implemented.
++*/
++SECStatus
++SSL_DefaultCanFalseStart(PRFileDesc *fd, PRBool *canFalseStart)
++{
++    sslSocket *ss;
++    *canFalseStart = PR_FALSE;
++    ss = ssl_FindSocket(fd);
++    if (!ss) {
++	SSL_DBG(("%d: SSL[%d]: bad socket in SSL_DefaultCanFalseStart",
++		 SSL_GETPID(), fd));
++	return SECFailure;
++    }
++
++    if (!ss->ssl3.initialized) {
++	PORT_SetError(SEC_ERROR_NOT_INITIALIZED);
++	return SECFailure;
++    }
++
++    if (ss->version <= SSL_LIBRARY_VERSION_3_0) {
++	PORT_SetError(SSL_ERROR_FEATURE_NOT_SUPPORTED_FOR_VERSION);
++	return SECFailure;
++    }
++
++    /* Require a forward-secret key exchange. */
++    *canFalseStart = ss->ssl3.hs.kea_def->kea == kea_dhe_dss ||
++		     ss->ssl3.hs.kea_def->kea == kea_dhe_rsa ||
++		     ss->ssl3.hs.kea_def->kea == kea_ecdhe_ecdsa ||
++		     ss->ssl3.hs.kea_def->kea == kea_ecdhe_rsa;
++
++    return SECSuccess;
++}
++
+ /* Try to make progress on an SSL handshake by attempting to read the 
+ ** next handshake from the peer, and sending any responses.
+ ** For non-blocking sockets, returns PR_ERROR_WOULD_BLOCK  if it cannot 
+ ** read the next handshake from the underlying socket.
+ ** For SSLv2, returns when handshake is complete or fatal error occurs.
+ ** For SSLv3, returns when handshake is complete, or application data has
+ ** arrived that must be taken by application before handshake can continue, 
+ ** or a fatal error occurs.
+@@ -1190,22 +1263,17 @@ ssl_SecureSend(sslSocket *ss, const unsi
+     if (len > 0) 
+     	ss->writerThread = PR_GetCurrentThread();
+     /* If any of these is non-zero, the initial handshake is not done. */
+     if (!ss->firstHsDone) {
+ 	PRBool canFalseStart = PR_FALSE;
+ 	ssl_Get1stHandshakeLock(ss);
+ 	if (ss->version >= SSL_LIBRARY_VERSION_3_0) {
+ 	    ssl_GetSSL3HandshakeLock(ss);
+-	    if ((ss->ssl3.hs.ws == wait_change_cipher ||
+-		ss->ssl3.hs.ws == wait_finished ||
+-		ss->ssl3.hs.ws == wait_new_session_ticket) &&
+-		ssl3_CanFalseStart(ss)) {
+-		canFalseStart = PR_TRUE;
+-	    }
++	    canFalseStart = ss->ssl3.hs.canFalseStart;
+ 	    ssl_ReleaseSSL3HandshakeLock(ss);
+ 	}
+ 	if (!canFalseStart &&
+ 	    (ss->handshake || ss->nextHandshake || ss->securityHandshake)) {
+ 	    rv = ssl_Do1stHandshake(ss);
+ 	}
+ 	ssl_Release1stHandshakeLock(ss);
+     }
+diff --git a/security/nss/lib/ssl/sslsock.c b/security/nss/lib/ssl/sslsock.c
+--- a/security/nss/lib/ssl/sslsock.c
++++ b/security/nss/lib/ssl/sslsock.c
+@@ -2336,19 +2336,23 @@ ssl_Poll(PRFileDesc *fd, PRInt16 how_fla
+ 		    ** The code should select on write, not read.
+ 		    */
+ 		    new_flags ^=  PR_POLL_READ;	   /* don't select on read. */
+ 		    new_flags |=  PR_POLL_WRITE;   /* do    select on write. */
+ 		}
+ 	    } else if (new_flags & PR_POLL_WRITE) {
+ 		    /* The caller is trying to write, but the handshake is 
+ 		    ** blocked waiting for data to read, and the first 
+-		    ** handshake has been sent.  so do NOT to poll on write.
++		    ** handshake has been sent.  So do NOT to poll on write
++		    ** unless we did false start.
+ 		    */
+-		    new_flags ^=  PR_POLL_WRITE;   /* don't select on write. */
++		    if (!(ss->version >= SSL_LIBRARY_VERSION_3_0 &&
++			ss->ssl3.hs.canFalseStart)) {
++			new_flags ^=  PR_POLL_WRITE;   /* don't select on write. */
++		    }
+ 		    new_flags |=  PR_POLL_READ;	   /* do    select on read. */
+ 	    }
+ 	}
+     } else if ((new_flags & PR_POLL_READ) && (SSL_DataPending(fd) > 0)) {
+ 	*p_out_flags = PR_POLL_READ;	/* it's ready already. */
+ 	return new_flags;
+     } else if ((ss->lastWriteBlocked) && (how_flags & PR_POLL_READ) &&
+ 	       (ss->pendingBuf.len != 0)) { /* write data waiting to be sent */