Bug 713933: Make false start work with asynchronous certificate verification, r=bsmith
authorPatrick McManus <mcmanus@ducksong.com>
Sun, 30 Jun 2013 22:23:16 -0700
changeset 136995 f6bc026a0c368178b4d327bf05de785305161d72
parent 136994 89a5e4356ad1f7bc9d9d24f6409c6d963dde3ca4
child 136996 99e03f6249b918e6cff29abe41ae5f5ad1bde998
push idunknown
push userunknown
push dateunknown
reviewersbsmith
bugs713933
milestone25.0a1
Bug 713933: Make false start work with asynchronous certificate verification, r=bsmith
security/nss/coreconf/coreconf.dep
security/nss/lib/ssl/ssl.def
security/nss/lib/ssl/ssl.h
security/nss/lib/ssl/ssl3con.c
security/nss/lib/ssl/ssl3gthr.c
security/nss/lib/ssl/sslauth.c
security/nss/lib/ssl/sslimpl.h
security/nss/lib/ssl/sslinfo.c
security/nss/lib/ssl/sslreveal.c
security/nss/lib/ssl/sslsecur.c
security/nss/lib/ssl/sslsock.c
security/patches/README
security/patches/bug-658222-false-start.patch
--- a/security/nss/coreconf/coreconf.dep
+++ b/security/nss/coreconf/coreconf.dep
@@ -5,8 +5,9 @@
 
 /*
  * A dummy header file that is a dependency for all the object files.
  * Used to force a full recompilation of NSS in Mozilla's Tinderbox
  * depend builds.  See comments in rules.mk.
  */
 
 #error "Do not include this header file."
+
--- 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:
+;+*;
+;+};
--- 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.
 */
--- 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.
--- 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;
--- 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;
--- 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,
--- 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";
--- 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.
        */
--- 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);
     }
--- 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 */
--- a/security/patches/README
+++ b/security/patches/README
@@ -1,2 +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
+                              further modified to make it work for Chromium and
+			      other projects before being checked in.
new file mode 100644
--- /dev/null
+++ b/security/patches/bug-658222-false-start.patch
@@ -0,0 +1,1577 @@
+# 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": {