Bug 1264578 - NSS support for http with TLS 1.3. r=keeler
authorDragana Damjanovic <dd.mozilla@gmail.com>
Fri, 19 Aug 2016 05:01:00 -0400
changeset 310729 a1fcbe1fc10c17fb0b7c6e31a46ffc0d8f7f18ef
parent 310728 16bcc176a1439cc6ba5b70cd62d0d049243c41b8
child 310730 9238fb3e770e0166514a6352a6dac1ec5e3a305b
push id30593
push userryanvm@gmail.com
push dateTue, 23 Aug 2016 14:05:29 +0000
treeherdermozilla-central@052656fc513c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskeeler
bugs1264578
milestone51.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 1264578 - NSS support for http with TLS 1.3. r=keeler
netwerk/base/security-prefs.js
netwerk/socket/nsISSLSocketControl.idl
security/manager/ssl/nsNSSCallbacks.cpp
security/manager/ssl/nsNSSComponent.cpp
security/manager/ssl/nsNSSIOLayer.cpp
security/manager/ssl/nsNSSIOLayer.h
--- a/netwerk/base/security-prefs.js
+++ b/netwerk/base/security-prefs.js
@@ -2,16 +2,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 pref("security.tls.version.min", 1);
 pref("security.tls.version.max", 3);
 pref("security.tls.version.fallback-limit", 3);
 pref("security.tls.insecure_fallback_hosts", "");
 pref("security.tls.unrestricted_rc4_fallback", false);
+pref("security.tls.enable_0rtt_data", false);
 
 pref("security.ssl.treat_unsafe_negotiation_as_broken", false);
 pref("security.ssl.require_safe_negotiation",  false);
 pref("security.ssl.enable_ocsp_stapling", true);
 pref("security.ssl.enable_false_start", true);
 pref("security.ssl.false_start.require-npn", false);
 pref("security.ssl.enable_npn", true);
 pref("security.ssl.enable_alpn", true);
--- a/netwerk/socket/nsISSLSocketControl.idl
+++ b/netwerk/socket/nsISSLSocketControl.idl
@@ -39,16 +39,32 @@ interface nsISSLSocketControl : nsISuppo
      * implement NPN.
      *
      * If negotiatedNPN is read before NPN has progressed to the point
      * where this information is available NS_ERROR_NOT_CONNECTED is
      * raised.
      */
     readonly attribute ACString negotiatedNPN;
 
+    /* For 0RTT we need to know the alpn protocol selected for the last tls
+     * session. This function will return a value if applicable or an error
+     * NS_ERROR_NOT_AVAILABLE.
+     */
+    ACString getAlpnEarlySelection();
+
+    /* If 0RTT handshake was applied and some data has been sent, as soon as
+     * the handshake finishes this attribute will be set to appropriate value.
+     */
+    readonly attribute bool earlyDataAccepted;
+
+    /* When 0RTT is performed, PR_Write will not drive the handshake forward.
+     * It must be forced by calling this function.
+     */
+    void driveHandshake();
+
     /* Determine if a potential SSL connection to hostname:port with
      * 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);
--- a/security/manager/ssl/nsNSSCallbacks.cpp
+++ b/security/manager/ssl/nsNSSCallbacks.cpp
@@ -833,16 +833,17 @@ PreliminaryHandshakeDone(PRFileDesc* fd)
 {
   nsNSSSocketInfo* infoObject = (nsNSSSocketInfo*) fd->higher->secret;
   if (!infoObject)
     return;
 
   SSLChannelInfo channelInfo;
   if (SSL_GetChannelInfo(fd, &channelInfo, sizeof(channelInfo)) == SECSuccess) {
     infoObject->SetSSLVersionUsed(channelInfo.protocolVersion);
+    infoObject->SetEarlyDataAccepted(channelInfo.earlyDataAccepted);
 
     SSLCipherSuiteInfo cipherInfo;
     if (SSL_GetCipherSuiteInfo(channelInfo.cipherSuite, &cipherInfo,
                                sizeof cipherInfo) == SECSuccess) {
       /* Set the SSL Status information */
       RefPtr<nsSSLStatus> status(infoObject->SSLStatus());
       if (!status) {
         status = new nsSSLStatus();
--- a/security/manager/ssl/nsNSSComponent.cpp
+++ b/security/manager/ssl/nsNSSComponent.cpp
@@ -1364,16 +1364,17 @@ nsNSSComponent::FillTLSVersionRange(SSLV
   rangeOut.max = (uint16_t) maxFromPrefs;
 }
 
 static const int32_t OCSP_ENABLED_DEFAULT = 1;
 static const bool REQUIRE_SAFE_NEGOTIATION_DEFAULT = false;
 static const bool FALSE_START_ENABLED_DEFAULT = true;
 static const bool NPN_ENABLED_DEFAULT = true;
 static const bool ALPN_ENABLED_DEFAULT = false;
+static const bool ENABLED_0RTT_DATA_DEFAULT = false;
 
 static void
 ConfigureTLSSessionIdentifiers()
 {
   bool disableSessionIdentifiers =
     Preferences::GetBool("security.ssl.disable_session_identifiers", false);
   SSL_OptionSetDefault(SSL_ENABLE_SESSION_TICKETS, !disableSessionIdentifiers);
   SSL_OptionSetDefault(SSL_NO_CACHE, disableSessionIdentifiers);
@@ -1771,16 +1772,20 @@ nsNSSComponent::InitializeNSS()
   // preferences are true.
   SSL_OptionSetDefault(SSL_ENABLE_NPN,
                        Preferences::GetBool("security.ssl.enable_npn",
                                             NPN_ENABLED_DEFAULT));
   SSL_OptionSetDefault(SSL_ENABLE_ALPN,
                        Preferences::GetBool("security.ssl.enable_alpn",
                                             ALPN_ENABLED_DEFAULT));
 
+  SSL_OptionSetDefault(SSL_ENABLE_0RTT_DATA,
+                       Preferences::GetBool("security.tls.enable_0rtt_data",
+                                            ENABLED_0RTT_DATA_DEFAULT));
+
   if (NS_FAILED(InitializeCipherSuite())) {
     MOZ_LOG(gPIPNSSLog, LogLevel::Error, ("Unable to initialize cipher suite settings\n"));
     return NS_ERROR_FAILURE;
   }
 
   // TLSServerSocket may be run with the session cache enabled. It is necessary
   // to call this once before that can happen. This specifies a maximum of 1000
   // cache entries (the default number of cache entries is 10000, which seems a
@@ -1956,16 +1961,20 @@ nsNSSComponent::Observe(nsISupports* aSu
     } else if (prefName.EqualsLiteral("security.ssl.enable_npn")) {
       SSL_OptionSetDefault(SSL_ENABLE_NPN,
                            Preferences::GetBool("security.ssl.enable_npn",
                                                 NPN_ENABLED_DEFAULT));
     } else if (prefName.EqualsLiteral("security.ssl.enable_alpn")) {
       SSL_OptionSetDefault(SSL_ENABLE_ALPN,
                            Preferences::GetBool("security.ssl.enable_alpn",
                                                 ALPN_ENABLED_DEFAULT));
+    } else if (prefName.EqualsLiteral("security.tls.enable_0rtt_data")) {
+      SSL_OptionSetDefault(SSL_ENABLE_0RTT_DATA,
+                           Preferences::GetBool("security.tls.enable_0rtt_data",
+                                                ENABLED_0RTT_DATA_DEFAULT));
     } else if (prefName.Equals("security.ssl.disable_session_identifiers")) {
       ConfigureTLSSessionIdentifiers();
     } else if (prefName.EqualsLiteral("security.OCSP.enabled") ||
                prefName.EqualsLiteral("security.OCSP.require") ||
                prefName.EqualsLiteral("security.OCSP.GET.enabled") ||
                prefName.EqualsLiteral("security.pki.cert_short_lifetime_in_days") ||
                prefName.EqualsLiteral("security.ssl.enable_ocsp_stapling") ||
                prefName.EqualsLiteral("security.ssl.enable_ocsp_must_staple") ||
--- a/security/manager/ssl/nsNSSIOLayer.cpp
+++ b/security/manager/ssl/nsNSSIOLayer.cpp
@@ -53,16 +53,18 @@ using namespace mozilla::psm;
                        //DEBUG_SSL_VERBOSE to dump SSL
                        //read/write buffer to a log.
                        //Uses PR_LOG except on Mac where
                        //we always write out to our own
                        //file.
 
 namespace {
 
+#define MAX_ALPN_LENGTH 255
+
 void
 getSiteKey(const nsACString& hostName, uint16_t port,
            /*out*/ nsCSubstring& key)
 {
   key = hostName;
   key.AppendASCII(":");
   key.AppendInt(port);
 }
@@ -82,16 +84,17 @@ nsNSSSocketInfo::nsNSSSocketInfo(SharedS
   : mFd(nullptr),
     mCertVerificationState(before_cert_verification),
     mSharedState(aState),
     mForSTARTTLS(false),
     mHandshakePending(true),
     mRememberClientAuthCertificate(false),
     mPreliminaryHandshakeDone(false),
     mNPNCompleted(false),
+    mEarlyDataAccepted(false),
     mFalseStartCallbackCalled(false),
     mFalseStarted(false),
     mIsFullHandshake(false),
     mHandshakeCompleted(false),
     mJoined(false),
     mSentClientCert(false),
     mNotedTimeUntilReady(false),
     mFailedVerification(false),
@@ -303,16 +306,81 @@ nsNSSSocketInfo::GetNegotiatedNPN(nsACSt
   if (!mNPNCompleted)
     return NS_ERROR_NOT_CONNECTED;
 
   aNegotiatedNPN = mNegotiatedNPN;
   return NS_OK;
 }
 
 NS_IMETHODIMP
+nsNSSSocketInfo::GetAlpnEarlySelection(nsACString& aAlpnSelected)
+{
+  nsNSSShutDownPreventionLock locker;
+  if (isAlreadyShutDown() || isPK11LoggedOut()) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+  SSLNextProtoState alpnState;
+  unsigned char chosenAlpn[MAX_ALPN_LENGTH];
+  unsigned int chosenAlpnLen;
+  SECStatus rv = SSL_GetNextProto(mFd, &alpnState, chosenAlpn, &chosenAlpnLen,
+                                  AssertedCast<unsigned int>(ArrayLength(chosenAlpn)));
+
+  if (rv != SECSuccess || alpnState != SSL_NEXT_PROTO_EARLY_VALUE ||
+      chosenAlpnLen == 0) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
+  aAlpnSelected.Assign(BitwiseCast<char*, unsigned char*>(chosenAlpn),
+                       chosenAlpnLen);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNSSSocketInfo::GetEarlyDataAccepted(bool* aAccepted)
+{
+  *aAccepted = mEarlyDataAccepted;
+  return NS_OK;
+}
+
+void
+nsNSSSocketInfo::SetEarlyDataAccepted(bool aAccepted)
+{
+  mEarlyDataAccepted = aAccepted;
+}
+
+NS_IMETHODIMP
+nsNSSSocketInfo::DriveHandshake()
+{
+  nsNSSShutDownPreventionLock locker;
+  if (isAlreadyShutDown() || isPK11LoggedOut()) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+  if (!mFd) {
+    return NS_ERROR_FAILURE;
+  }
+  PRErrorCode errorCode = GetErrorCode();
+  if (errorCode) {
+    return GetXPCOMFromNSSError(errorCode);
+  }
+
+  SECStatus rv = SSL_ForceHandshake(mFd);
+
+  if (rv != SECSuccess) {
+    errorCode = PR_GetError();
+    if (errorCode == PR_WOULD_BLOCK_ERROR) {
+      return NS_BASE_STREAM_WOULD_BLOCK;
+    }
+
+    SetCanceled(errorCode, PlainErrorMessage);
+    return GetXPCOMFromNSSError(errorCode);
+  }
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 nsNSSSocketInfo::IsAcceptableForHost(const nsACString& hostname, bool* _retval)
 {
   // If this is the same hostname then the certicate status does not
   // need to be considered. They are joinable.
   if (hostname.Equals(GetHostName())) {
     *_retval = true;
     return NS_OK;
   }
--- a/security/manager/ssl/nsNSSIOLayer.h
+++ b/security/manager/ssl/nsNSSIOLayer.h
@@ -48,16 +48,17 @@ public:
 
   void SetTLSVersionRange(SSLVersionRange range) { mTLSVersionRange = range; }
   SSLVersionRange GetTLSVersionRange() const { return mTLSVersionRange; };
 
   PRStatus CloseSocketAndDestroy(
                 const nsNSSShutDownPreventionLock& proofOfLock);
 
   void SetNegotiatedNPN(const char* value, uint32_t length);
+  void SetEarlyDataAccepted(bool aAccepted);
 
   void SetHandshakeCompleted();
   void NoteTimeUntilReady();
 
 
   void SetFalseStartCallbackCalled() { mFalseStartCallbackCalled = true; }
   void SetFalseStarted() { mFalseStarted = true; }
 
@@ -127,16 +128,17 @@ private:
   bool mHandshakePending;
   bool mRememberClientAuthCertificate;
   bool mPreliminaryHandshakeDone; // after false start items are complete
 
   nsresult ActivateSSL();
 
   nsCString mNegotiatedNPN;
   bool      mNPNCompleted;
+  bool      mEarlyDataAccepted;
   bool      mFalseStartCallbackCalled;
   bool      mFalseStarted;
   bool      mIsFullHandshake;
   bool      mHandshakeCompleted;
   bool      mJoined;
   bool      mSentClientCert;
   bool      mNotedTimeUntilReady;
   bool      mFailedVerification;