Bug 1323104 - TLS 1.3 experimental short headers. r=mt NSS_3_28_BRANCH
authorEKR <ekr@rtfm.com>
Sat, 10 Dec 2016 07:32:45 -1000
branchNSS_3_28_BRANCH
changeset 12977 377b0eb048f6e2ac356f60c6c5229ae242c51634
parent 12975 353d71c83d92a1819aa1372bc2a4d8d8ba807bea
child 12981 0288f1477edfd2aa8ec297b6959707e351c74b76
push id1886
push userekr@mozilla.com
push dateSun, 18 Dec 2016 05:27:58 +0000
reviewersmt
bugs1323104
Bug 1323104 - TLS 1.3 experimental short headers. r=mt Reviewers: mt Differential Revision: https://nss-review.dev.mozaws.net/D122
gtests/ssl_gtest/libssl_internals.c
gtests/ssl_gtest/libssl_internals.h
gtests/ssl_gtest/ssl_loopback_unittest.cc
gtests/ssl_gtest/tls_agent.cc
gtests/ssl_gtest/tls_agent.h
lib/ssl/ssl3con.c
lib/ssl/ssl3ext.c
lib/ssl/ssl3gthr.c
lib/ssl/ssl3prot.h
lib/ssl/sslimpl.h
lib/ssl/sslsock.c
lib/ssl/sslt.h
lib/ssl/tls13con.c
lib/ssl/tls13exthandle.c
lib/ssl/tls13exthandle.h
--- a/gtests/ssl_gtest/libssl_internals.c
+++ b/gtests/ssl_gtest/libssl_internals.c
@@ -308,8 +308,33 @@ SECStatus SSLInt_AdvanceWriteSeqByAWindo
 }
 
 SSLKEAType SSLInt_GetKEAType(SSLNamedGroup group) {
   const sslNamedGroupDef *groupDef = ssl_LookupNamedGroup(group);
   if (!groupDef) return ssl_kea_null;
 
   return groupDef->keaType;
 }
+
+SECStatus SSLInt_EnableShortHeaders(PRFileDesc *fd) {
+  sslSocket *ss;
+
+  ss = ssl_FindSocket(fd);
+  if (!ss) {
+    return SECFailure;
+  }
+
+  ss->opt.enableShortHeaders = PR_TRUE;
+  return SECSuccess;
+}
+
+SECStatus SSLInt_UsingShortHeaders(PRFileDesc *fd, PRBool *result) {
+  sslSocket *ss;
+
+  ss = ssl_FindSocket(fd);
+  if (!ss) {
+    return SECFailure;
+  }
+
+  *result = ss->ssl3.hs.shortHeaders;
+
+  return SECSuccess;
+}
--- a/gtests/ssl_gtest/libssl_internals.h
+++ b/gtests/ssl_gtest/libssl_internals.h
@@ -32,10 +32,12 @@ PRBool SSLInt_DamageEarlyTrafficSecret(P
 SECStatus SSLInt_Set0RttAlpn(PRFileDesc *fd, PRUint8 *data, unsigned int len);
 PRBool SSLInt_HasCertWithAuthType(PRFileDesc *fd, SSLAuthType authType);
 PRBool SSLInt_SendAlert(PRFileDesc *fd, uint8_t level, uint8_t type);
 PRBool SSLInt_SendNewSessionTicket(PRFileDesc *fd);
 SECStatus SSLInt_AdvanceWriteSeqNum(PRFileDesc *fd, PRUint64 to);
 SECStatus SSLInt_AdvanceReadSeqNum(PRFileDesc *fd, PRUint64 to);
 SECStatus SSLInt_AdvanceWriteSeqByAWindow(PRFileDesc *fd, PRInt32 extra);
 SSLKEAType SSLInt_GetKEAType(SSLNamedGroup group);
+SECStatus SSLInt_EnableShortHeaders(PRFileDesc *fd);
+SECStatus SSLInt_UsingShortHeaders(PRFileDesc *fd, PRBool *result);
 
 #endif  // ndef libssl_internals_h_
--- a/gtests/ssl_gtest/ssl_loopback_unittest.cc
+++ b/gtests/ssl_gtest/ssl_loopback_unittest.cc
@@ -205,16 +205,24 @@ TEST_P(TlsConnectTls13, UnknownAlert) {
 TEST_P(TlsConnectTls13, AlertWrongLevel) {
   Connect();
   SSLInt_SendAlert(server_->ssl_fd(), kTlsAlertWarning,
                    kTlsAlertUnexpectedMessage);
   client_->ExpectReadWriteError();
   client_->WaitForErrorCode(SSL_ERROR_HANDSHAKE_UNEXPECTED_ALERT, 2000);
 }
 
+TEST_F(TlsConnectStreamTls13, NegotiateShortHeaders) {
+  client_->SetShortHeadersEnabled();
+  server_->SetShortHeadersEnabled();
+  client_->ExpectShortHeaders();
+  server_->ExpectShortHeaders();
+  Connect();
+}
+
 INSTANTIATE_TEST_CASE_P(GenericStream, TlsConnectGeneric,
                         ::testing::Combine(TlsConnectTestBase::kTlsModesStream,
                                            TlsConnectTestBase::kTlsVAll));
 INSTANTIATE_TEST_CASE_P(
     GenericDatagram, TlsConnectGeneric,
     ::testing::Combine(TlsConnectTestBase::kTlsModesDatagram,
                        TlsConnectTestBase::kTlsV11Plus));
 
--- a/gtests/ssl_gtest/tls_agent.cc
+++ b/gtests/ssl_gtest/tls_agent.cc
@@ -63,17 +63,18 @@ TlsAgent::TlsAgent(const std::string& na
       auth_certificate_hook_called_(false),
       handshake_callback_called_(false),
       error_code_(0),
       send_ctr_(0),
       recv_ctr_(0),
       expect_readwrite_error_(false),
       handshake_callback_(),
       auth_certificate_callback_(),
-      sni_callback_() {
+      sni_callback_(),
+      expect_short_headers_(false) {
   memset(&info_, 0, sizeof(info_));
   memset(&csinfo_, 0, sizeof(csinfo_));
   SECStatus rv = SSL_VersionRangeGetDefault(
       mode_ == STREAM ? ssl_variant_stream : ssl_variant_datagram, &vrange_);
   EXPECT_EQ(SECSuccess, rv);
 }
 
 TlsAgent::~TlsAgent() {
@@ -360,16 +361,23 @@ void TlsAgent::SetSessionCacheEnabled(bo
 void TlsAgent::Set0RttEnabled(bool en) {
   EXPECT_TRUE(EnsureTlsSetup());
 
   SECStatus rv =
       SSL_OptionSet(ssl_fd_, SSL_ENABLE_0RTT_DATA, en ? PR_TRUE : PR_FALSE);
   EXPECT_EQ(SECSuccess, rv);
 }
 
+void TlsAgent::SetShortHeadersEnabled() {
+  EXPECT_TRUE(EnsureTlsSetup());
+
+  SECStatus rv = SSLInt_EnableShortHeaders(ssl_fd_);
+  EXPECT_EQ(SECSuccess, rv);
+}
+
 void TlsAgent::SetVersionRange(uint16_t minver, uint16_t maxver) {
   vrange_.min = minver;
   vrange_.max = maxver;
 
   if (ssl_fd_) {
     SECStatus rv = SSL_VersionRangeSet(ssl_fd_, &vrange_);
     EXPECT_EQ(SECSuccess, rv);
   }
@@ -383,16 +391,18 @@ void TlsAgent::GetVersionRange(uint16_t*
 void TlsAgent::SetExpectedVersion(uint16_t version) {
   expected_version_ = version;
 }
 
 void TlsAgent::SetServerKeyBits(uint16_t bits) { server_key_bits_ = bits; }
 
 void TlsAgent::ExpectReadWriteError() { expect_readwrite_error_ = true; }
 
+void TlsAgent::ExpectShortHeaders() { expect_short_headers_ = true; }
+
 void TlsAgent::SetSignatureSchemes(const SSLSignatureScheme* schemes,
                                    size_t count) {
   EXPECT_TRUE(EnsureTlsSetup());
   EXPECT_LE(count, SSL_SignatureMaxCount());
   EXPECT_EQ(SECSuccess,
             SSL_SignatureSchemePrefSet(ssl_fd_, schemes,
                                        static_cast<unsigned int>(count)));
   EXPECT_EQ(SECFailure, SSL_SignatureSchemePrefSet(ssl_fd_, schemes, 0))
@@ -653,16 +663,20 @@ void TlsAgent::Connected() {
     // by DTLS for retransmission.
     PRInt32 expected = ((mode_ == DGRAM) && (role_ == CLIENT)) ? 3 : 2;
     EXPECT_EQ(expected, cipherSuites);
     if (expected != cipherSuites) {
       SSLInt_PrintTls13CipherSpecs(ssl_fd_);
     }
   }
 
+  PRBool short_headers;
+  rv = SSLInt_UsingShortHeaders(ssl_fd_, &short_headers);
+  EXPECT_EQ(SECSuccess, rv);
+  EXPECT_EQ((PRBool)expect_short_headers_, short_headers);
   SetState(STATE_CONNECTED);
 }
 
 void TlsAgent::EnableExtendedMasterSecret() {
   ASSERT_TRUE(EnsureTlsSetup());
 
   SECStatus rv =
       SSL_OptionSet(ssl_fd_, SSL_ENABLE_EXTENDED_MASTER_SECRET, PR_TRUE);
--- a/gtests/ssl_gtest/tls_agent.h
+++ b/gtests/ssl_gtest/tls_agent.h
@@ -116,25 +116,27 @@ class TlsAgent : public PollTarget {
   void RequestClientAuth(bool requireAuth);
   bool GetClientAuthCredentials(CERTCertificate** cert,
                                 SECKEYPrivateKey** priv) const;
 
   void ConfigureSessionCache(SessionResumptionMode mode);
   void SetSessionTicketsEnabled(bool en);
   void SetSessionCacheEnabled(bool en);
   void Set0RttEnabled(bool en);
+  void SetShortHeadersEnabled();
   void SetVersionRange(uint16_t minver, uint16_t maxver);
   void GetVersionRange(uint16_t* minver, uint16_t* maxver);
   void CheckPreliminaryInfo();
   void ResetPreliminaryInfo();
   void SetExpectedVersion(uint16_t version);
   void SetServerKeyBits(uint16_t bits);
   void ExpectReadWriteError();
   void EnableFalseStart();
   void ExpectResumption();
+  void ExpectShortHeaders();
   void SetSignatureSchemes(const SSLSignatureScheme* schemes, size_t count);
   void EnableAlpn(const uint8_t* val, size_t len);
   void CheckAlpn(SSLNextProtoState expected_state,
                  const std::string& expected = "") const;
   void EnableSrtp();
   void CheckSrtp() const;
   void CheckErrorCode(int32_t expected) const;
   void WaitForErrorCode(int32_t expected, uint32_t delay) const;
@@ -356,16 +358,17 @@ class TlsAgent : public PollTarget {
   SSLVersionRange vrange_;
   PRErrorCode error_code_;
   size_t send_ctr_;
   size_t recv_ctr_;
   bool expect_readwrite_error_;
   HandshakeCallbackFunction handshake_callback_;
   AuthCertificateCallbackFunction auth_certificate_callback_;
   SniCallbackFunction sni_callback_;
+  bool expect_short_headers_;
 };
 
 inline std::ostream& operator<<(std::ostream& stream,
                                 const TlsAgent::State& state) {
   return stream << TlsAgent::state_str(state);
 }
 
 class TlsAgentTestBase : public ::testing::Test {
--- a/lib/ssl/ssl3con.c
+++ b/lib/ssl/ssl3con.c
@@ -2575,22 +2575,34 @@ ssl3_CompressMACEncryptRecord(ssl3Cipher
 }
 
 SECStatus
 ssl_ProtectRecord(sslSocket *ss, ssl3CipherSpec *cwSpec,
                   PRBool capRecordVersion, SSL3ContentType type,
                   const SSL3Opaque *pIn, PRUint32 contentLen, sslBuffer *wrBuf)
 {
     const ssl3BulkCipherDef *cipher_def = cwSpec->cipher_def;
-    PRUint16 headerLen = IS_DTLS(ss) ? DTLS_RECORD_HEADER_LENGTH : SSL3_RECORD_HEADER_LENGTH;
-    sslBuffer protBuf = { wrBuf->buf + headerLen, 0, wrBuf->space - headerLen };
+    PRUint16 headerLen;
+    sslBuffer protBuf;
     SSL3ProtocolVersion version = cwSpec->version;
     PRBool isTLS13;
+    PRUint8 *ptr = wrBuf->buf;
     SECStatus rv;
 
+    if (ss->ssl3.hs.shortHeaders) {
+        PORT_Assert(!IS_DTLS(ss));
+        PORT_Assert(ss->version >= SSL_LIBRARY_VERSION_TLS_1_3);
+        headerLen = TLS13_RECORD_HEADER_LENGTH_SHORT;
+    } else {
+        headerLen = IS_DTLS(ss) ? DTLS_RECORD_HEADER_LENGTH : SSL3_RECORD_HEADER_LENGTH;
+    }
+    protBuf.buf = wrBuf->buf + headerLen;
+    protBuf.len = 0;
+    protBuf.space = wrBuf->space - headerLen;
+
     PORT_Assert(cipher_def->max_records <= RECORD_SEQ_MAX);
     if ((cwSpec->write_seq_num & RECORD_SEQ_MAX) >= cipher_def->max_records) {
         SSL_TRC(3, ("%d: SSL[-]: write sequence number at limit 0x%0llx",
                     SSL_GETPID(), cwSpec->write_seq_num));
         PORT_SetError(SSL_ERROR_TOO_MANY_RECORDS);
         return SECFailure;
     }
 
@@ -2610,39 +2622,42 @@ ssl_ProtectRecord(sslSocket *ss, ssl3Cip
 #endif
     if (rv != SECSuccess) {
         return SECFailure; /* error was set */
     }
 
     PORT_Assert(protBuf.len <= MAX_FRAGMENT_LENGTH + (isTLS13 ? 256 : 1024));
     wrBuf->len = protBuf.len + headerLen;
 
+    if (ss->ssl3.hs.shortHeaders) {
+        PORT_Assert(!IS_DTLS(ss)); /* Decoder not yet implemented. */
+        (void)ssl_EncodeUintX(0x8000 | protBuf.len, 2, ptr);
+    } else {
 #ifndef UNSAFE_FUZZER_MODE
-    if (isTLS13 && cipher_def->calg != ssl_calg_null) {
-        wrBuf->buf[0] = content_application_data;
-    } else
+        if (isTLS13 && cipher_def->calg != ssl_calg_null) {
+            *ptr++ = content_application_data;
+        } else
 #endif
-    {
-        wrBuf->buf[0] = type;
-    }
-
-    if (IS_DTLS(ss)) {
-        version = isTLS13 ? SSL_LIBRARY_VERSION_TLS_1_1 : version;
-        version = dtls_TLSVersionToDTLSVersion(version);
-
-        (void)ssl_EncodeUintX(version, 2, &wrBuf->buf[1]);
-        (void)ssl_EncodeUintX(cwSpec->write_seq_num, 8, &wrBuf->buf[3]);
-        (void)ssl_EncodeUintX(protBuf.len, 2, &wrBuf->buf[11]);
-    } else {
-        if (capRecordVersion || isTLS13) {
-            version = PR_MIN(SSL_LIBRARY_VERSION_TLS_1_0, version);
-        }
-
-        (void)ssl_EncodeUintX(version, 2, &wrBuf->buf[1]);
-        (void)ssl_EncodeUintX(protBuf.len, 2, &wrBuf->buf[3]);
+        {
+            *ptr++ = type;
+        }
+
+        if (IS_DTLS(ss)) {
+            version = isTLS13 ? SSL_LIBRARY_VERSION_TLS_1_1 : version;
+            version = dtls_TLSVersionToDTLSVersion(version);
+
+            ptr = ssl_EncodeUintX(version, 2, ptr);
+            ptr = ssl_EncodeUintX(cwSpec->write_seq_num, 8, ptr);
+        } else {
+            if (capRecordVersion || isTLS13) {
+                version = PR_MIN(SSL_LIBRARY_VERSION_TLS_1_0, version);
+            }
+            ptr = ssl_EncodeUintX(version, 2, ptr);
+        }
+        (void)ssl_EncodeUintX(protBuf.len, 2, ptr);
     }
     ++cwSpec->write_seq_num;
 
     return SECSuccess;
 }
 
 /* Process the plain text before sending it.
  * Returns the number of bytes of plaintext that were successfully sent
--- a/lib/ssl/ssl3ext.c
+++ b/lib/ssl/ssl3ext.c
@@ -34,16 +34,17 @@ static const ssl3ExtensionHandler client
     { ssl_signature_algorithms_xtn, &ssl3_ServerHandleSigAlgsXtn },
     { ssl_extended_master_secret_xtn, &ssl3_HandleExtendedMasterSecretXtn },
     { ssl_signed_cert_timestamp_xtn, &ssl3_ServerHandleSignedCertTimestampXtn },
     { ssl_tls13_key_share_xtn, &tls13_ServerHandleKeyShareXtn },
     { ssl_tls13_pre_shared_key_xtn, &tls13_ServerHandlePreSharedKeyXtn },
     { ssl_tls13_early_data_xtn, &tls13_ServerHandleEarlyDataXtn },
     { ssl_tls13_psk_key_exchange_modes_xtn,
       &tls13_ServerHandlePskKeyExchangeModesXtn },
+    { ssl_tls13_short_header_xtn, &tls13_HandleShortHeaderXtn },
     { -1, NULL }
 };
 
 /* These two tables are used by the client, to handle server hello
  * extensions. */
 static const ssl3ExtensionHandler serverHelloHandlersTLS[] = {
     { ssl_server_name_xtn, &ssl3_HandleServerNameXtn },
     /* TODO: add a handler for ssl_ec_point_formats_xtn */
@@ -53,16 +54,17 @@ static const ssl3ExtensionHandler server
     { ssl_app_layer_protocol_xtn, &ssl3_ClientHandleAppProtoXtn },
     { ssl_use_srtp_xtn, &ssl3_ClientHandleUseSRTPXtn },
     { ssl_cert_status_xtn, &ssl3_ClientHandleStatusRequestXtn },
     { ssl_extended_master_secret_xtn, &ssl3_HandleExtendedMasterSecretXtn },
     { ssl_signed_cert_timestamp_xtn, &ssl3_ClientHandleSignedCertTimestampXtn },
     { ssl_tls13_key_share_xtn, &tls13_ClientHandleKeyShareXtn },
     { ssl_tls13_pre_shared_key_xtn, &tls13_ClientHandlePreSharedKeyXtn },
     { ssl_tls13_early_data_xtn, &tls13_ClientHandleEarlyDataXtn },
+    { ssl_tls13_short_header_xtn, &tls13_HandleShortHeaderXtn },
     { -1, NULL }
 };
 
 static const ssl3ExtensionHandler helloRetryRequestHandlers[] = {
     { ssl_tls13_key_share_xtn, tls13_ClientHandleKeyShareXtnHrr },
     { ssl_tls13_cookie_xtn, tls13_ClientHandleHrrCookie },
     { -1, NULL }
 };
@@ -110,16 +112,17 @@ static const ssl3HelloExtensionSender cl
       { ssl_signed_cert_timestamp_xtn, &ssl3_ClientSendSignedCertTimestampXtn },
       { ssl_tls13_key_share_xtn, &tls13_ClientSendKeyShareXtn },
       { ssl_tls13_early_data_xtn, &tls13_ClientSendEarlyDataXtn },
       /* Some servers (e.g. WebSphere Application Server 7.0 and Tomcat) will
        * time out or terminate the connection if the last extension in the
        * client hello is empty. They are not intolerant of TLS 1.2, so list
        * signature_algorithms at the end. See bug 1243641. */
       { ssl_tls13_supported_versions_xtn, &tls13_ClientSendSupportedVersionsXtn },
+      { ssl_tls13_short_header_xtn, &tls13_SendShortHeaderXtn },
       { ssl_signature_algorithms_xtn, &ssl3_ClientSendSigAlgsXtn },
       { ssl_tls13_cookie_xtn, &tls13_ClientSendHrrCookieXtn },
       { ssl_tls13_psk_key_exchange_modes_xtn,
         &tls13_ClientSendPskKeyExchangeModesXtn },
       /* The pre_shared_key extension MUST be last. */
       { ssl_tls13_pre_shared_key_xtn, &tls13_ClientSendPreSharedKeyXtn },
       /* any extra entries will appear as { 0, NULL }    */
     };
--- a/lib/ssl/ssl3gthr.c
+++ b/lib/ssl/ssl3gthr.c
@@ -92,17 +92,17 @@ ssl3_GatherData(sslSocket *ss, sslGather
     int nb;
     int err;
     int rv = 1;
     PRUint8 v2HdrLength = 0;
 
     PORT_Assert(ss->opt.noLocks || ssl_HaveRecvBufLock(ss));
     if (gs->state == GS_INIT) {
         gs->state = GS_HEADER;
-        gs->remainder = 5;
+        gs->remainder = ss->ssl3.hs.shortHeaders ? 2 : 5;
         gs->offset = 0;
         gs->writeOffset = 0;
         gs->readOffset = 0;
         gs->inbuf.len = 0;
     }
 
     lbp = gs->inbuf.buf;
     for (;;) {
@@ -144,20 +144,32 @@ ssl3_GatherData(sslSocket *ss, sslGather
         }
 
         /* have received entire record header, or entire record. */
         switch (gs->state) {
             case GS_HEADER:
                 /* Check for SSLv2 handshakes. Always assume SSLv3 on clients,
                  * support SSLv2 handshakes only when ssl2gs != NULL. */
                 if (!ssl2gs || ssl3_isLikelyV3Hello(gs->hdr)) {
-                    /* Should have an SSLv3 record header in gs->hdr. Extract
+                    /* Should have a non-SSLv2 record header in gs->hdr. Extract
                      * the length of the following encrypted data, and then
-                     * read in the rest of the SSL3 record into gs->inbuf. */
-                    gs->remainder = (gs->hdr[3] << 8) | gs->hdr[4];
+                     * read in the rest of the record into gs->inbuf. */
+                    if (ss->ssl3.hs.shortHeaders) {
+                        PRUint16 len = (gs->hdr[0] << 8) | gs->hdr[1];
+                        if (!(len & 0x8000)) {
+                            SSL_DBG(("%d: SSL3[%d]: incorrectly formatted header"));
+                            SSL3_SendAlert(ss, alert_fatal, illegal_parameter);
+                            gs->state = GS_INIT;
+                            PORT_SetError(SSL_ERROR_BAD_MAC_READ);
+                            return SECFailure;
+                        }
+                        gs->remainder = len & ~0x8000;
+                    } else {
+                        gs->remainder = (gs->hdr[3] << 8) | gs->hdr[4];
+                    }
                 } else {
                     /* Probably an SSLv2 record header. No need to handle any
                      * security escapes (gs->hdr[0] & 0x40) as we wouldn't get
                      * here if one was set. See ssl3_isLikelyV3Hello(). */
                     gs->remainder = ((gs->hdr[0] & 0x7f) << 8) | gs->hdr[1];
                     ssl2gs->isV2 = PR_TRUE;
                     v2HdrLength = 2;
 
@@ -453,18 +465,23 @@ ssl3_GatherCompleteHandshake(sslSocket *
                     return rv;
                 }
             } else {
                 /* decipher it, and handle it if it's a handshake.
                  * If it's application data, ss->gs.buf will not be empty upon return.
                  * If it's a change cipher spec, alert, or handshake message,
                  * ss->gs.buf.len will be 0 when ssl3_HandleRecord returns SECSuccess.
                  */
-                cText.type = (SSL3ContentType)ss->gs.hdr[0];
-                cText.version = (ss->gs.hdr[1] << 8) | ss->gs.hdr[2];
+                if (ss->ssl3.hs.shortHeaders) {
+                    cText.type = content_application_data;
+                    cText.version = SSL_LIBRARY_VERSION_TLS_1_0;
+                } else {
+                    cText.type = (SSL3ContentType)ss->gs.hdr[0];
+                    cText.version = (ss->gs.hdr[1] << 8) | ss->gs.hdr[2];
+                }
 
                 if (IS_DTLS(ss)) {
                     sslSequenceNumber seq_num;
 
                     cText.version = dtls_DTLSVersionToTLSVersion(cText.version);
                     /* DTLS sequence number */
                     PORT_Memcpy(&seq_num, &ss->gs.hdr[3], sizeof(seq_num));
                     cText.seq_num = PR_ntohll(seq_num);
--- a/lib/ssl/ssl3prot.h
+++ b/lib/ssl/ssl3prot.h
@@ -27,16 +27,17 @@ typedef PRUint16 ssl3CipherSuite;
 #define MAX_COMPRESSION_METHODS 10
 #define MAX_MAC_LENGTH 64
 #define MAX_PADDING_LENGTH 64
 #define MAX_KEY_LENGTH 64
 #define EXPORT_KEY_LENGTH 5
 #define SSL3_RANDOM_LENGTH 32
 
 #define SSL3_RECORD_HEADER_LENGTH 5
+#define TLS13_RECORD_HEADER_LENGTH_SHORT 2
 
 /* SSL3_RECORD_HEADER_LENGTH + epoch/sequence_number */
 #define DTLS_RECORD_HEADER_LENGTH 13
 
 #define MAX_FRAGMENT_LENGTH 16384
 
 typedef enum {
     content_change_cipher_spec = 20,
--- a/lib/ssl/sslimpl.h
+++ b/lib/ssl/sslimpl.h
@@ -288,16 +288,17 @@ typedef struct sslOptionsStr {
     unsigned int enableALPN : 1;
     unsigned int reuseServerECDHEKey : 1;
     unsigned int enableFallbackSCSV : 1;
     unsigned int enableServerDhe : 1;
     unsigned int enableExtendedMS : 1;
     unsigned int enableSignedCertTimestamps : 1;
     unsigned int requireDHENamedGroups : 1;
     unsigned int enable0RttData : 1;
+    unsigned int enableShortHeaders : 1;
 } sslOptions;
 
 typedef enum { sslHandshakingUndetermined = 0,
                sslHandshakingAsClient,
                sslHandshakingAsServer
 } sslHandshakingType;
 
 #define SSL_LOCK_RANK_SPEC 255
@@ -866,16 +867,17 @@ typedef struct SSL3HandshakeStateStr {
     sslZeroRttIgnore zeroRttIgnore; /* Are we ignoring 0-RTT? */
     ssl3CipherSuite zeroRttSuite;   /* The cipher suite we used for 0-RTT. */
     PRCList bufferedEarlyData;      /* Buffered TLS 1.3 early data
                                      * on server.*/
     PRBool helloRetry;              /* True if HelloRetryRequest has been sent
                                      * or received. */
     ssl3KEADef kea_def_mutable;     /* Used to hold the writable kea_def
                                      * we use for TLS 1.3 */
+    PRBool shortHeaders;            /* Assigned if we are doing short headers. */
 } SSL3HandshakeState;
 
 /*
 ** This is the "ssl3" struct, as in "ss->ssl3".
 ** note:
 ** usually,   crSpec == cwSpec and prSpec == pwSpec.
 ** Sometimes, crSpec == pwSpec and prSpec == cwSpec.
 ** But there are never more than 2 actual specs.
--- a/lib/ssl/sslsock.c
+++ b/lib/ssl/sslsock.c
@@ -73,17 +73,22 @@ static sslOptions ssl_defaults = {
     PR_FALSE,              /* enableNPN          */
     PR_TRUE,               /* enableALPN         */
     PR_TRUE,               /* reuseServerECDHEKey */
     PR_FALSE,              /* enableFallbackSCSV */
     PR_TRUE,               /* enableServerDhe */
     PR_FALSE,              /* enableExtendedMS    */
     PR_FALSE,              /* enableSignedCertTimestamps */
     PR_FALSE,              /* requireDHENamedGroups */
-    PR_FALSE               /* enable0RttData */
+    PR_FALSE,              /* enable0RttData */
+#ifdef NSS_ENABLE_TLS13_SHORT_HEADERS
+    PR_TRUE /* enableShortHeaders */
+#else
+    PR_FALSE /* enableShortHeaders */
+#endif
 };
 
 /*
  * default range of enabled SSL/TLS protocols
  */
 static SSLVersionRange versions_defaults_stream = {
     SSL_LIBRARY_VERSION_TLS_1_0,
     SSL_LIBRARY_VERSION_TLS_1_2
--- a/lib/ssl/sslt.h
+++ b/lib/ssl/sslt.h
@@ -383,27 +383,28 @@ typedef enum {
     ssl_tls13_key_share_xtn = 40,
     ssl_tls13_pre_shared_key_xtn = 41,
     ssl_tls13_early_data_xtn = 42,
     ssl_tls13_supported_versions_xtn = 43,
     ssl_tls13_cookie_xtn = 44,
     ssl_tls13_psk_key_exchange_modes_xtn = 45,
     ssl_tls13_ticket_early_data_info_xtn = 46,
     ssl_next_proto_nego_xtn = 13172,
-    ssl_renegotiation_info_xtn = 0xff01
+    ssl_renegotiation_info_xtn = 0xff01,
+    ssl_tls13_short_header_xtn = 0xff03
 } SSLExtensionType;
 
 /* This is the old name for the supported_groups extensions. */
 #define ssl_elliptic_curves_xtn ssl_supported_groups_xtn
 
 /* SSL_MAX_EXTENSIONS doesn't include ssl_padding_xtn.  It includes the maximum
  * number of extensions that are supported for any single message type.  That
  * is, a ClientHello; ServerHello and TLS 1.3 NewSessionTicket and
  * HelloRetryRequest extensions are smaller. */
-#define SSL_MAX_EXTENSIONS 18
+#define SSL_MAX_EXTENSIONS 19
 
 /* Deprecated */
 typedef enum {
     ssl_dhe_group_none = 0,
     ssl_ff_dhe_2048_group = 1,
     ssl_ff_dhe_3072_group = 2,
     ssl_ff_dhe_4096_group = 3,
     ssl_ff_dhe_6144_group = 4,
--- a/lib/ssl/tls13con.c
+++ b/lib/ssl/tls13con.c
@@ -1442,16 +1442,17 @@ tls13_HandleClientHelloPart2(sslSocket *
                                 kHkdfLabelEarlyTrafficSecret,
                                 NULL, /* Current running hash. */
                                 &ss->ssl3.hs.clientEarlyTrafficSecret);
         if (rv != SECSuccess) {
             FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error);
             return SECFailure;
         }
     }
+
     ssl_GetXmitBufLock(ss);
     rv = tls13_SendServerHelloSequence(ss);
     ssl_ReleaseXmitBufLock(ss);
     if (rv != SECSuccess) {
         FATAL_ERROR(ss, PORT_GetError(), handshake_failure);
         return SECFailure;
     }
 
@@ -1852,16 +1853,19 @@ tls13_SendEncryptedServerSequence(sslSoc
 
     rv = tls13_SetCipherSpec(ss, TrafficKeyHandshake,
                              CipherSpecWrite, PR_FALSE);
     if (rv != SECSuccess) {
         LOG_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE);
         return SECFailure;
     }
 
+    ss->ssl3.hs.shortHeaders = ssl3_ExtensionNegotiated(
+        ss, ssl_tls13_short_header_xtn);
+
     if (ss->ssl3.hs.zeroRttState == ssl_0rtt_accepted) {
         rv = ssl3_RegisterExtensionSender(ss, &ss->xtnData, ssl_tls13_early_data_xtn,
                                           tls13_ServerSendEarlyDataXtn);
         if (rv != SECSuccess) {
             return SECFailure; /* Error code set already. */
         }
     }
 
@@ -2058,16 +2062,19 @@ tls13_HandleServerHelloPart2(sslSocket *
     if (rv != SECSuccess) {
         return SECFailure;
     }
     rv = tls13_ComputeHandshakeSecrets(ss);
     if (rv != SECSuccess) {
         return SECFailure; /* error code is set. */
     }
 
+    ss->ssl3.hs.shortHeaders = ssl3_ExtensionNegotiated(
+        ss, ssl_tls13_short_header_xtn);
+
     rv = tls13_SetCipherSpec(ss, TrafficKeyHandshake,
                              CipherSpecRead, PR_FALSE);
     if (rv != SECSuccess) {
         FATAL_ERROR(ss, SSL_ERROR_INIT_CIPHER_SUITE_FAILURE, internal_error);
         return SECFailure;
     }
     TLS13_SET_HS_STATE(ss, wait_encrypted_extensions);
 
@@ -3990,17 +3997,18 @@ static const struct {
     { ssl_tls13_key_share_xtn, ExtensionSendClearOrHrr },
     { ssl_tls13_pre_shared_key_xtn, ExtensionSendClear },
     { ssl_tls13_early_data_xtn, ExtensionSendEncrypted },
     { ssl_next_proto_nego_xtn, ExtensionNotUsed },
     { ssl_renegotiation_info_xtn, ExtensionNotUsed },
     { ssl_signed_cert_timestamp_xtn, ExtensionSendCertificate },
     { ssl_cert_status_xtn, ExtensionSendCertificate },
     { ssl_tls13_ticket_early_data_info_xtn, ExtensionNewSessionTicket },
-    { ssl_tls13_cookie_xtn, ExtensionSendHrr }
+    { ssl_tls13_cookie_xtn, ExtensionSendHrr },
+    { ssl_tls13_short_header_xtn, ExtensionSendClear }
 };
 
 PRBool
 tls13_ExtensionAllowed(PRUint16 extension, SSL3HandshakeType message)
 {
     unsigned int i;
 
     PORT_Assert((message == client_hello) ||
@@ -4262,16 +4270,17 @@ tls13_MaybeDo0RTTHandshake(sslSocket *ss
 {
     SECStatus rv;
 
     /* Don't do anything if there is no early_data xtn, which means we're
      * not doing early data. */
     if (!ssl3_ClientExtensionAdvertised(ss, ssl_tls13_early_data_xtn)) {
         return SECSuccess;
     }
+
     ss->ssl3.hs.zeroRttState = ssl_0rtt_sent;
     ss->ssl3.hs.zeroRttSuite = ss->ssl3.hs.cipher_suite;
 
     SSL_TRC(3, ("%d: TLS13[%d]: in 0-RTT mode", SSL_GETPID(), ss->fd));
 
     /* Set the ALPN data as if it was negotiated. We check in the ServerHello
      * handler that the server negotiates the same value. */
     if (ss->sec.ci.sid->u.ssl3.alpnSelection.len) {
--- a/lib/ssl/tls13exthandle.c
+++ b/lib/ssl/tls13exthandle.c
@@ -1064,8 +1064,106 @@ tls13_ServerHandlePskKeyExchangeModesXtn
         return SECFailure;
     }
 
     /* Keep track of negotiated extensions. */
     xtnData->negotiated[xtnData->numNegotiated++] = ex_type;
 
     return SECSuccess;
 }
+
+PRInt32
+tls13_SendShortHeaderXtn(const sslSocket *ss,
+                         TLSExtensionData *xtnData,
+                         PRBool append, PRUint32 maxBytes)
+{
+    PRUint32 extension_len = 2 + 2; /* Type + length (0). */
+
+    if (!ss->opt.enableShortHeaders) {
+        return 0;
+    }
+
+    /* Presently this is incompatible with 0-RTT. We will fix if
+     * it becomes more than an experiment. */
+    if (ss->opt.enable0RttData) {
+        return 0;
+    }
+
+    if (IS_DTLS(ss)) {
+        return 0;
+    }
+
+    SSL_TRC(3, ("%d: TLS13[%d]: send short_header extension",
+                SSL_GETPID(), ss->fd));
+
+    if (maxBytes < extension_len) {
+        PORT_Assert(0);
+        return 0;
+    }
+
+    if (append) {
+        SECStatus rv;
+
+        rv = ssl3_ExtAppendHandshakeNumber(ss, ssl_tls13_short_header_xtn, 2);
+        if (rv != SECSuccess)
+            return -1;
+
+        rv = ssl3_ExtAppendHandshakeNumber(ss, 0, 2);
+        if (rv != SECSuccess)
+            return -1;
+
+        xtnData->advertised[xtnData->numAdvertised++] =
+            ssl_tls13_short_header_xtn;
+    }
+
+    return extension_len;
+}
+
+SECStatus
+tls13_HandleShortHeaderXtn(
+    const sslSocket *ss, TLSExtensionData *xtnData, PRUint16 ex_type,
+    SECItem *data)
+{
+    SSL_TRC(3, ("%d: TLS13[%d]: handle early_data extension",
+                SSL_GETPID(), ss->fd));
+
+    /* If we are doing < TLS 1.3, then ignore this. */
+    if (ss->version < SSL_LIBRARY_VERSION_TLS_1_3) {
+        return SECSuccess;
+    }
+
+    /* Presently this is incompatible with 0-RTT. We will fix if
+     * it becomes more than an experiment. */
+    if (ss->opt.enable0RttData) {
+        return SECSuccess;
+    }
+
+    if (IS_DTLS(ss)) {
+        PORT_SetError(SSL_ERROR_EXTENSION_DISALLOWED_FOR_VERSION);
+        return SECFailure;
+    }
+
+    if (data->len) {
+        PORT_SetError(SSL_ERROR_RX_MALFORMED_HANDSHAKE);
+        return SECFailure;
+    }
+
+    if (!ss->opt.enableShortHeaders) {
+        /* Ignore. */
+        return SECSuccess;
+    }
+
+    /* Keep track of negotiated extensions. */
+    xtnData->negotiated[xtnData->numNegotiated++] = ex_type;
+
+    if (ss->sec.isServer) {
+        SECStatus rv;
+
+        rv = ssl3_RegisterExtensionSender(ss, xtnData,
+                                          ssl_tls13_short_header_xtn,
+                                          tls13_SendShortHeaderXtn);
+        if (rv != SECSuccess) {
+            return SECFailure;
+        }
+    }
+
+    return SECSuccess;
+}
--- a/lib/ssl/tls13exthandle.h
+++ b/lib/ssl/tls13exthandle.h
@@ -59,10 +59,16 @@ PRInt32 tls13_ClientSendHrrCookieXtn(con
                                      PRBool append,
                                      PRUint32 maxBytes);
 PRInt32 tls13_ClientSendPskKeyExchangeModesXtn(const sslSocket *ss,
                                                TLSExtensionData *xtnData,
                                                PRBool append, PRUint32 maxBytes);
 SECStatus tls13_ServerHandlePskKeyExchangeModesXtn(const sslSocket *ss,
                                                    TLSExtensionData *xtnData,
                                                    PRUint16 ex_type, SECItem *data);
+PRInt32 tls13_SendShortHeaderXtn(const sslSocket *ss,
+                                 TLSExtensionData *xtnData,
+                                 PRBool append, PRUint32 maxBytes);
+SECStatus tls13_HandleShortHeaderXtn(
+    const sslSocket *ss, TLSExtensionData *xtnData, PRUint16 ex_type,
+    SECItem *data);
 
 #endif