Bug 1268745 - Limit use of the same symmetric key, r=ekr, sr=wtc
authorMartin Thomson <martin.thomson@gmail.com>
Fri, 19 Aug 2016 13:14:48 +1000
changeset 12496 a1b0b7023e1939d5d9da391094167ccdb38613d6
parent 12495 18af2e440fbf07a7348c6a168a5f9752c00ad5c2
child 12497 4200b26d80877f57b231cf9f4a96a4973d4c1881
push id1494
push usermartin.thomson@gmail.com
push dateWed, 24 Aug 2016 06:55:27 +0000
reviewersekr, wtc
bugs1268745
Bug 1268745 - Limit use of the same symmetric key, r=ekr, sr=wtc
external_tests/ssl_gtest/libssl_internals.c
external_tests/ssl_gtest/libssl_internals.h
external_tests/ssl_gtest/ssl_ciphersuite_unittest.cc
external_tests/ssl_gtest/ssl_loopback_unittest.cc
external_tests/ssl_gtest/tls_agent.cc
external_tests/ssl_gtest/tls_agent.h
lib/ssl/SSLerrs.h
lib/ssl/dtlscon.c
lib/ssl/ssl3con.c
lib/ssl/ssl3gthr.c
lib/ssl/sslerr.h
lib/ssl/sslimpl.h
lib/ssl/tls13con.c
--- a/external_tests/ssl_gtest/libssl_internals.c
+++ b/external_tests/ssl_gtest/libssl_internals.c
@@ -1,9 +1,9 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* This file contains functions for frobbing the internals of libssl */
 #include "libssl_internals.h"
 
@@ -217,8 +217,55 @@ PRBool SSLInt_SendAlert(PRFileDesc *fd, 
     return PR_FALSE;
   }
 
   SECStatus rv = SSL3_SendAlert(ss, level, type);
   if (rv != SECSuccess) return PR_FALSE;
 
   return PR_TRUE;
 }
+
+SECStatus SSLInt_AdvanceReadSeqNum(PRFileDesc *fd, PRUint64 to) {
+  PRUint64 epoch;
+  sslSocket *ss;
+  ssl3CipherSpec *spec;
+
+  ss = ssl_FindSocket(fd);
+  if (!ss) {
+    return SECFailure;
+  }
+  if (to >= (1ULL << 48)) {
+    return SECFailure;
+  }
+  ssl_GetSpecWriteLock(ss);
+  spec = ss->ssl3.crSpec;
+  epoch = spec->read_seq_num >> 48;
+  spec->read_seq_num = (epoch << 48) | to;
+
+  /* For DTLS, we need to fix the record sequence number.  For this, we can just
+   * scrub the entire structure on the assumption that the new sequence number
+   * is far enough past the last received sequence number. */
+  if (to <= spec->recvdRecords.right + DTLS_RECVD_RECORDS_WINDOW) {
+    return SECFailure;
+  }
+  dtls_RecordSetRecvd(&spec->recvdRecords, to);
+
+  ssl_ReleaseSpecWriteLock(ss);
+  return SECSuccess;
+}
+
+SECStatus SSLInt_AdvanceWriteSeqNum(PRFileDesc *fd, PRUint64 to) {
+  PRUint64 epoch;
+  sslSocket *ss;
+
+  ss = ssl_FindSocket(fd);
+  if (!ss) {
+    return SECFailure;
+  }
+  if (to >= (1ULL << 48)) {
+    return SECFailure;
+  }
+  ssl_GetSpecWriteLock(ss);
+  epoch = ss->ssl3.cwSpec->write_seq_num >> 48;
+  ss->ssl3.cwSpec->write_seq_num = (epoch << 48) | to;
+  ssl_ReleaseSpecWriteLock(ss);
+  return SECSuccess;
+}
--- a/external_tests/ssl_gtest/libssl_internals.h
+++ b/external_tests/ssl_gtest/libssl_internals.h
@@ -28,10 +28,12 @@ PRInt32 SSLInt_CountTls13CipherSpecs(PRF
 void SSLInt_ForceTimerExpiry(PRFileDesc *fd);
 SECStatus SSLInt_SetMTU(PRFileDesc *fd, PRUint16 mtu);
 PRBool SSLInt_CheckSecretsDestroyed(PRFileDesc *fd);
 PRBool SSLInt_DamageHsTrafficSecret(PRFileDesc *fd);
 PRBool SSLInt_DamageEarlyTrafficSecret(PRFileDesc *fd);
 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);
+SECStatus SSLInt_AdvanceWriteSeqNum(PRFileDesc *fd, PRUint64 to);
+SECStatus SSLInt_AdvanceReadSeqNum(PRFileDesc *fd, PRUint64 to);
 
 #endif  // ndef libssl_internals_h_
--- a/external_tests/ssl_gtest/ssl_ciphersuite_unittest.cc
+++ b/external_tests/ssl_gtest/ssl_ciphersuite_unittest.cc
@@ -6,40 +6,51 @@
 
 #include "ssl.h"
 #include <functional>
 #include <memory>
 #include "secerr.h"
 #include "sslerr.h"
 #include "sslproto.h"
 
+extern "C" {
+// This is not something that should make you happy.
+#include "libssl_internals.h"
+}
+
 #include "gtest_utils.h"
 #include "tls_connect.h"
+#include "tls_parser.h"
 
 namespace nss_test {
 
 // mode, version, cipher suite
 typedef std::tuple<std::string, uint16_t, uint16_t> CipherSuiteProfile;
 
 class TlsCipherSuiteTestBase : public TlsConnectTestBase {
  public:
   TlsCipherSuiteTestBase(std::string mode, uint16_t version,
                          uint16_t cipher_suite)
       : TlsConnectTestBase(TlsConnectTestBase::ToMode(mode), version),
-        cipher_suite_(cipher_suite) {}
+        cipher_suite_(cipher_suite),
+        csinfo_({0}) {
+    SECStatus rv =
+        SSL_GetCipherSuiteInfo(cipher_suite_, &csinfo_, sizeof(csinfo_));
+    EXPECT_EQ(SECSuccess, rv);
+    if (rv == SECSuccess) {
+      std::cerr << "Cipher suite: " << csinfo_.cipherSuiteName << std::endl;
+    }
+  }
 
  protected:
   uint16_t cipher_suite_;
+  SSLCipherSuiteInfo csinfo_;
 
   void SetupCertificate() {
-    SSLCipherSuiteInfo csinfo;
-    EXPECT_EQ(SECSuccess,
-              SSL_GetCipherSuiteInfo(cipher_suite_, &csinfo, sizeof(csinfo)));
-    std::cerr << "Cipher suite: " << csinfo.cipherSuiteName << std::endl;
-    switch (csinfo.authType) {
+    switch (csinfo_.authType) {
       case ssl_auth_rsa_sign:
         Reset(TlsAgent::kServerRsaSign);
         break;
       case ssl_auth_rsa_decrypt:
         Reset(TlsAgent::kServerRsaDecrypt);
         break;
       case ssl_auth_ecdsa:
         Reset(TlsAgent::kServerEcdsa256);
@@ -96,72 +107,62 @@ class TlsResumptionTest
     : public TlsCipherSuiteTestBase,
       public ::testing::WithParamInterface<CipherSuiteProfile> {
  public:
   TlsResumptionTest()
       : TlsCipherSuiteTestBase(std::get<0>(GetParam()), std::get<1>(GetParam()),
                                std::get<2>(GetParam())) {}
 
   bool SkipIfCipherSuiteIsDSA() {
-    SSLCipherSuiteInfo csinfo;
-    SECStatus rv =
-        SSL_GetCipherSuiteInfo(cipher_suite_, &csinfo, sizeof(csinfo));
-    if (rv != SECSuccess) {
-      EXPECT_TRUE(false) << "Can't get cipher suite info";
-      return false;
-    }
-    bool isDSA = csinfo.authType == ssl_auth_dsa;
+    bool isDSA = csinfo_.authType == ssl_auth_dsa;
     if (isDSA) {
-      std::cerr << "Skipping DSA suite: " << csinfo.cipherSuiteName
+      std::cerr << "Skipping DSA suite: " << csinfo_.cipherSuiteName
                 << std::endl;
     }
     return isDSA;
   }
 
   void EnablePskCipherSuite() {
-    SSLCipherSuiteInfo targetInfo;
-    ASSERT_EQ(SECSuccess, SSL_GetCipherSuiteInfo(cipher_suite_, &targetInfo,
-                                                 sizeof(targetInfo)));
     SSLKEAType targetKea;
-    switch (targetInfo.keaType) {
+    switch (csinfo_.keaType) {
       case ssl_kea_ecdh:
         targetKea = ssl_kea_ecdh_psk;
         break;
       case ssl_kea_dh:
         targetKea = ssl_kea_dh_psk;
         break;
       default:
         EXPECT_TRUE(false) << "Unsupported KEA type for "
-                           << targetInfo.cipherSuiteName;
+                           << csinfo_.cipherSuiteName;
         return;
     }
 
     size_t count = SSL_GetNumImplementedCiphers();
     const uint16_t *ciphers = SSL_GetImplementedCiphers();
     bool found = false;
     for (size_t i = 0; i < count; ++i) {
       SSLCipherSuiteInfo candidateInfo;
       ASSERT_EQ(SECSuccess, SSL_GetCipherSuiteInfo(ciphers[i], &candidateInfo,
                                                    sizeof(candidateInfo)));
       if (candidateInfo.authType == ssl_auth_psk &&
           candidateInfo.keaType == targetKea &&
-          candidateInfo.symCipher == targetInfo.symCipher &&
-          candidateInfo.macAlgorithm == targetInfo.macAlgorithm) {
+          candidateInfo.symCipher == csinfo_.symCipher &&
+          candidateInfo.macAlgorithm == csinfo_.macAlgorithm) {
         // We aren't able to check that the PRF hash is the same.  This is OK
         // because there are (currently) no suites that have different PRF
         // hashes but also use the same symmetric cipher.
         EXPECT_EQ(SECSuccess,
                   SSL_CipherPrefSet(client_->ssl_fd(), ciphers[i], PR_TRUE));
         EXPECT_EQ(SECSuccess,
                   SSL_CipherPrefSet(server_->ssl_fd(), ciphers[i], PR_TRUE));
         found = true;
       }
     }
     EXPECT_TRUE(found) << "Can't find matching PSK cipher for "
-                       << targetInfo.cipherSuiteName;
+                       << csinfo_.cipherSuiteName;
   }
 };
 
 TEST_P(TlsResumptionTest, ResumeCipherSuite) {
   if (SkipIfCipherSuiteIsDSA()) {
     return;  // Tickets don't work with DSA (bug 1174677).
   }
 
@@ -182,30 +183,131 @@ TEST_P(TlsResumptionTest, ResumeCipherSu
   if (version_ == SSL_LIBRARY_VERSION_TLS_1_3) {
     EnablePskCipherSuite();
   }
 
   ExpectResumption(RESUME_TICKET);
   ConnectAndCheckCipherSuite();
 }
 
+class TlsCipherLimitTest
+    : public TlsCipherSuiteTestBase,
+      public ::testing::WithParamInterface<CipherSuiteProfile> {
+ public:
+  TlsCipherLimitTest()
+      : TlsCipherSuiteTestBase(std::get<0>(GetParam()), std::get<1>(GetParam()),
+                               std::get<2>(GetParam())) {}
+
+ protected:
+  // Get the expected limit on the number of records that can be sent for the
+  // cipher suite.
+  uint64_t record_limit() const {
+    switch (csinfo_.symCipher) {
+      case ssl_calg_rc4:
+      case ssl_calg_3des:
+        return 1ULL << 20;
+      case ssl_calg_aes:
+      case ssl_calg_aes_gcm:
+        return 0x5aULL << 28;
+      case ssl_calg_null:
+      case ssl_calg_chacha20:
+        return (1ULL << 48) - 1;
+      case ssl_calg_rc2:
+      case ssl_calg_des:
+      case ssl_calg_idea:
+      case ssl_calg_fortezza:
+      case ssl_calg_camellia:
+      case ssl_calg_seed:
+        break;
+    }
+    EXPECT_TRUE(false) << "No limit for " << csinfo_.cipherSuiteName;
+    return 1ULL < 48;
+  }
+
+  uint64_t last_safe_write() const {
+    uint64_t limit = record_limit() - 1;
+    if (version_ < SSL_LIBRARY_VERSION_TLS_1_1 &&
+        (csinfo_.symCipher == ssl_calg_3des ||
+         csinfo_.symCipher == ssl_calg_aes)) {
+      // 1/n-1 record splitting needs space for two records.
+      limit--;
+    }
+    return limit;
+  }
+};
+
+// This only works for stream ciphers because we modify the sequence number -
+// which is included explicitly in the DTLS record header - and that trips a
+// different error code.  Note that the message that the client sends would not
+// decrypt (the nonce/IV wouldn't match), but the record limit is hit before
+// attempting to decrypt a record.
+TEST_P(TlsCipherLimitTest, ReadLimit) {
+  SetupCertificate();
+  EnableSingleCipher();
+  ConnectAndCheckCipherSuite();
+  EXPECT_EQ(SECSuccess,
+            SSLInt_AdvanceWriteSeqNum(client_->ssl_fd(), last_safe_write()));
+  EXPECT_EQ(SECSuccess,
+            SSLInt_AdvanceReadSeqNum(server_->ssl_fd(), last_safe_write()));
+
+  client_->SendData(10, 10);
+  server_->ReadBytes();  // This should be OK.
+
+  // The payload needs to be big enough to pass for encrypted.  In the extreme
+  // case (TLS 1.3), this means 1 for payload, 1 for content type and 16 for
+  // authentication tag.
+  static const uint8_t payload[18] = {6};
+  DataBuffer record;
+  uint64_t epoch = 0;
+  if (mode_ == DGRAM) {
+    epoch++;
+    if (version_ == SSL_LIBRARY_VERSION_TLS_1_3) {
+      epoch++;
+    }
+  }
+  TlsAgentTestBase::MakeRecord(mode_, kTlsApplicationDataType, version_,
+                               payload, sizeof(payload), &record,
+                               (epoch << 48) | record_limit());
+  server_->adapter()->PacketReceived(record);
+  server_->ExpectReadWriteError();
+  server_->ReadBytes();
+  EXPECT_EQ(SSL_ERROR_TOO_MANY_RECORDS, server_->error_code());
+}
+
+TEST_P(TlsCipherLimitTest, WriteLimit) {
+  SetupCertificate();
+  EnableSingleCipher();
+  ConnectAndCheckCipherSuite();
+  EXPECT_EQ(SECSuccess,
+            SSLInt_AdvanceWriteSeqNum(client_->ssl_fd(), last_safe_write()));
+  client_->SendData(10, 10);
+  client_->ExpectReadWriteError();
+  client_->SendData(10, 10);
+  EXPECT_EQ(SSL_ERROR_TOO_MANY_RECORDS, client_->error_code());
+}
+
 // This awful macro makes the test instantiations easier to read.
 #define INSTANTIATE_CIPHER_TEST_P(name, modes, versions, ...)      \
   static const uint16_t k##name##CiphersArr[] = {__VA_ARGS__};     \
   static const ::testing::internal::ParamGenerator<uint16_t>       \
       k##name##Ciphers = ::testing::ValuesIn(k##name##CiphersArr); \
   INSTANTIATE_TEST_CASE_P(                                         \
       CipherSuite##name, TlsCipherSuiteTest,                       \
       ::testing::Combine(TlsConnectTestBase::kTlsModes##modes,     \
                          TlsConnectTestBase::kTls##versions,       \
                          k##name##Ciphers));                       \
   INSTANTIATE_TEST_CASE_P(                                         \
       Resume##name, TlsResumptionTest,                             \
       ::testing::Combine(TlsConnectTestBase::kTlsModes##modes,     \
                          TlsConnectTestBase::kTls##versions,       \
+                         k##name##Ciphers));                       \
+  INSTANTIATE_TEST_CASE_P(                                         \
+      Limit##name, TlsCipherLimitTest,                             \
+      ::testing::Combine(TlsConnectTestBase::kTlsModes##modes,     \
+                         TlsConnectTestBase::kTls##versions,       \
                          k##name##Ciphers))
 
 INSTANTIATE_CIPHER_TEST_P(RC4, Stream, V10ToV12, TLS_RSA_WITH_RC4_128_SHA,
                           TLS_ECDH_ECDSA_WITH_RC4_128_SHA,
                           TLS_ECDHE_ECDSA_WITH_RC4_128_SHA,
                           TLS_ECDH_RSA_WITH_RC4_128_SHA,
                           TLS_ECDHE_RSA_WITH_RC4_128_SHA);
 INSTANTIATE_CIPHER_TEST_P(AEAD12, All, V12, TLS_RSA_WITH_AES_128_GCM_SHA256,
--- a/external_tests/ssl_gtest/ssl_loopback_unittest.cc
+++ b/external_tests/ssl_gtest/ssl_loopback_unittest.cc
@@ -90,22 +90,21 @@ TEST_P(TlsConnectGeneric, ConnectSendRec
 // The next two tests takes advantage of the fact that we
 // automatically read the first 1024 bytes, so if
 // we provide 1200 bytes, they overrun the read buffer
 // provided by the calling test.
 
 // DTLS should return an error.
 TEST_P(TlsConnectDatagram, ShortRead) {
   Connect();
-  client_->SetExpectedReadError(true);
+  client_->ExpectReadWriteError();
   server_->SendData(1200, 1200);
   client_->WaitForErrorCode(SSL_ERROR_RX_SHORT_DTLS_READ, 2000);
 
   // Now send and receive another packet.
-  client_->SetExpectedReadError(false);
   server_->ResetSentBytes();  // Reset the counter.
   SendReceive();
 }
 
 // TLS should get the write in two chunks.
 TEST_P(TlsConnectStream, ShortRead) {
   // This test behaves oddly with TLS 1.0 because of 1/n+1 splitting,
   // so skip in that case.
@@ -178,25 +177,25 @@ TEST_P(TlsConnectStreamPre13, ServerFini
   client_->CheckErrorCode(SSL_ERROR_RX_UNEXPECTED_CHANGE_CIPHER);
   EXPECT_EQ(TlsAgent::STATE_CONNECTED, server_->state());
 }
 
 TEST_P(TlsConnectTls13, UnknownAlert) {
   Connect();
   SSLInt_SendAlert(server_->ssl_fd(), kTlsAlertWarning,
                    0xff);  // Unknown value.
-  client_->SetExpectedReadError(true);
+  client_->ExpectReadWriteError();
   client_->WaitForErrorCode(SSL_ERROR_RX_UNKNOWN_ALERT, 2000);
 }
 
 TEST_P(TlsConnectTls13, AlertWrongLevel) {
   Connect();
   SSLInt_SendAlert(server_->ssl_fd(), kTlsAlertWarning,
                    kTlsAlertUnexpectedMessage);
-  client_->SetExpectedReadError(true);
+  client_->ExpectReadWriteError();
   client_->WaitForErrorCode(SSL_ERROR_HANDSHAKE_UNEXPECTED_ALERT, 2000);
 }
 
 INSTANTIATE_TEST_CASE_P(GenericStream, TlsConnectGeneric,
                         ::testing::Combine(TlsConnectTestBase::kTlsModesStream,
                                            TlsConnectTestBase::kTlsVAll));
 INSTANTIATE_TEST_CASE_P(
     GenericDatagram, TlsConnectGeneric,
--- a/external_tests/ssl_gtest/tls_agent.cc
+++ b/external_tests/ssl_gtest/tls_agent.cc
@@ -58,17 +58,17 @@ TlsAgent::TlsAgent(const std::string& na
       expect_client_auth_(false),
       can_falsestart_hook_called_(false),
       sni_hook_called_(false),
       auth_certificate_hook_called_(false),
       handshake_callback_called_(false),
       error_code_(0),
       send_ctr_(0),
       recv_ctr_(0),
-      expected_read_error_(false),
+      expect_readwrite_error_(false),
       handshake_callback_(),
       auth_certificate_callback_(),
       sni_callback_() {
   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);
@@ -335,17 +335,17 @@ 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::SetExpectedReadError(bool err) { expected_read_error_ = err; }
+void TlsAgent::ExpectReadWriteError() { expect_readwrite_error_ = true; }
 
 void TlsAgent::SetSignatureAlgorithms(const SSLSignatureAndHashAlg* algorithms,
                                       size_t count) {
   EXPECT_TRUE(EnsureTlsSetup());
   EXPECT_LE(count, SSL_SignatureMaxCount());
   EXPECT_EQ(SECSuccess, SSL_SignaturePrefSet(ssl_fd_, algorithms,
                                              static_cast<unsigned int>(count)));
   EXPECT_EQ(SECFailure, SSL_SignaturePrefSet(ssl_fd_, algorithms, 0))
@@ -482,26 +482,26 @@ void TlsAgent::CheckSrtp() const {
   uint16_t actual;
   EXPECT_EQ(SECSuccess, SSL_GetSRTPCipher(ssl_fd_, &actual));
   EXPECT_EQ(SRTP_AES128_CM_HMAC_SHA1_80, actual);
 }
 
 void TlsAgent::CheckErrorCode(int32_t expected) const {
   EXPECT_EQ(STATE_ERROR, state_);
   EXPECT_EQ(expected, error_code_)
-      << "Got error code " << PORT_ErrorToString(error_code_) << " expecting "
-      << PORT_ErrorToString(expected) << std::endl;
+      << "Got error code " << PORT_ErrorToName(error_code_) << " expecting "
+      << PORT_ErrorToName(expected) << std::endl;
 }
 
 void TlsAgent::WaitForErrorCode(int32_t expected, uint32_t delay) const {
   ASSERT_EQ(0, error_code_);
   WAIT_(error_code_ != 0, delay);
-  EXPECT_EQ(expected, error_code_) << "Got error code " << error_code_
-                                   << " expecting "
-                                   << PORT_ErrorToString(expected) << std::endl;
+  EXPECT_EQ(expected, error_code_)
+      << "Got error code " << PORT_ErrorToName(error_code_) << " expecting "
+      << PORT_ErrorToName(expected) << std::endl;
 }
 
 void TlsAgent::CheckPreliminaryInfo() {
   SSLPreliminaryChannelInfo info;
   EXPECT_EQ(SECSuccess,
             SSL_GetPreliminaryChannelInfo(ssl_fd_, &info, sizeof(info)));
   EXPECT_EQ(sizeof(info), info.length);
   EXPECT_TRUE(info.valuesSet & ssl_preinfo_version);
@@ -682,59 +682,68 @@ void TlsAgent::StartRenegotiate() {
   EXPECT_EQ(SECSuccess, rv);
 }
 
 void TlsAgent::SendDirect(const DataBuffer& buf) {
   LOG("Send Direct " << buf);
   adapter_->peer()->PacketReceived(buf);
 }
 
+static bool ErrorIsNonFatal(PRErrorCode code) {
+  return code == PR_WOULD_BLOCK_ERROR || code == SSL_ERROR_RX_SHORT_DTLS_READ;
+}
+
 void TlsAgent::SendData(size_t bytes, size_t blocksize) {
   uint8_t block[4096];
 
   ASSERT_LT(blocksize, sizeof(block));
 
   while (bytes) {
     size_t tosend = std::min(blocksize, bytes);
 
     for (size_t i = 0; i < tosend; ++i) {
       block[i] = 0xff & send_ctr_;
       ++send_ctr_;
     }
 
     LOGV("Writing " << tosend << " bytes");
     int32_t rv = PR_Write(ssl_fd_, block, tosend);
-    ASSERT_EQ(tosend, static_cast<size_t>(rv));
+    if (expect_readwrite_error_) {
+      EXPECT_GT(0, rv);
+      EXPECT_NE(PR_WOULD_BLOCK_ERROR, error_code_);
+      error_code_ = PR_GetError();
+      expect_readwrite_error_ = false;
+    } else {
+      ASSERT_EQ(tosend, static_cast<size_t>(rv));
+    }
 
     bytes -= tosend;
   }
 }
 
-static bool ErrorIsNonFatal(PRErrorCode code) {
-  return code == PR_WOULD_BLOCK_ERROR || code == SSL_ERROR_RX_SHORT_DTLS_READ;
-}
-
 void TlsAgent::ReadBytes() {
   uint8_t block[1024];
 
   int32_t rv = PR_Read(ssl_fd_, block, sizeof(block));
   LOGV("ReadBytes " << rv);
   int32_t err;
 
   if (rv >= 0) {
     size_t count = static_cast<size_t>(rv);
     for (size_t i = 0; i < count; ++i) {
       ASSERT_EQ(recv_ctr_ & 0xff, block[i]);
       recv_ctr_++;
     }
   } else {
     err = PR_GetError();
-    LOG("Read error " << err << ": " << PORT_ErrorToString(err));
-    if (err != PR_WOULD_BLOCK_ERROR && expected_read_error_) {
+    LOG("Read error " << PORT_ErrorToName(err) << ": "
+                      << PORT_ErrorToString(err));
+    if (err != PR_WOULD_BLOCK_ERROR && expect_readwrite_error_) {
       error_code_ = err;
+      expect_readwrite_error_ = false;
     }
   }
 
   // If closed, then don't bother waiting around.
   if (rv > 0 || (rv < 0 && ErrorIsNonFatal(err))) {
     LOGV("Re-arming");
     Poller::Instance()->Wait(READABLE_EVENT, adapter_, this,
                              &TlsAgent::ReadableCallback);
@@ -799,42 +808,46 @@ void TlsAgentTestBase::ProcessMessage(co
 
   ASSERT_EQ(expected_state, agent_->state());
 
   if (expected_state == TlsAgent::STATE_ERROR) {
     ASSERT_EQ(error_code, agent_->error_code());
   }
 }
 
-void TlsAgentTestBase::MakeRecord(uint8_t type, uint16_t version,
+void TlsAgentTestBase::MakeRecord(Mode mode, uint8_t type, uint16_t version,
                                   const uint8_t* buf, size_t len,
-                                  DataBuffer* out, uint32_t seq_num) {
+                                  DataBuffer* out, uint64_t seq_num) {
   size_t index = 0;
   index = out->Write(index, type, 1);
-  ;  // Content Type
   index = out->Write(
-      index, mode_ == STREAM ? version : TlsVersionToDtlsVersion(version),
-      2);  // Version
-  if (mode_ == DGRAM) {
-    index = out->Write(index, 0U, 4);
-    index = out->Write(index, seq_num, 4);
+      index, mode == STREAM ? version : TlsVersionToDtlsVersion(version), 2);
+  if (mode == DGRAM) {
+    index = out->Write(index, seq_num >> 32, 4);
+    index = out->Write(index, seq_num & PR_UINT32_MAX, 4);
   }
-  index = out->Write(index, len, 2);  // Length
+  index = out->Write(index, len, 2);
   out->Write(index, buf, len);
 }
 
+void TlsAgentTestBase::MakeRecord(uint8_t type, uint16_t version,
+                                  const uint8_t* buf, size_t len,
+                                  DataBuffer* out, uint64_t seq_num) {
+  MakeRecord(mode_, type, version, buf, len, out, seq_num);
+}
+
 void TlsAgentTestBase::MakeHandshakeMessage(uint8_t hs_type,
                                             const uint8_t* data, size_t hs_len,
-                                            DataBuffer* out, uint32_t seq_num) {
+                                            DataBuffer* out, uint64_t seq_num) {
   return MakeHandshakeMessageFragment(hs_type, data, hs_len, out, seq_num, 0,
                                       0);
 }
 void TlsAgentTestBase::MakeHandshakeMessageFragment(
     uint8_t hs_type, const uint8_t* data, size_t hs_len, DataBuffer* out,
-    uint32_t seq_num, uint32_t fragment_offset, uint32_t fragment_length) {
+    uint64_t seq_num, uint32_t fragment_offset, uint32_t fragment_length) {
   size_t index = 0;
   if (!fragment_length) fragment_length = hs_len;
   index = out->Write(index, hs_type, 1);  // Handshake record type.
   index = out->Write(index, hs_len, 3);   // Handshake length
   if (mode_ == DGRAM) {
     index = out->Write(index, seq_num, 2);
     index = out->Write(index, fragment_offset, 3);
     index = out->Write(index, fragment_length, 3);
--- a/external_tests/ssl_gtest/tls_agent.h
+++ b/external_tests/ssl_gtest/tls_agent.h
@@ -111,17 +111,17 @@ class TlsAgent : public PollTarget {
   void SetSessionCacheEnabled(bool en);
   void Set0RttEnabled(bool en);
   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 SetExpectedReadError(bool err);
+  void ExpectReadWriteError();
   void EnableFalseStart();
   void ExpectResumption();
   void SetSignatureAlgorithms(const SSLSignatureAndHashAlg* algorithms,
                               size_t count);
   void EnableAlpn(const uint8_t* val, size_t len);
   void CheckAlpn(SSLNextProtoState expected_state,
                  const std::string& expected = "") const;
   void EnableSrtp();
@@ -186,17 +186,17 @@ class TlsAgent : public PollTarget {
   }
 
   std::vector<uint8_t> session_id() const {
     return std::vector<uint8_t>(info_.sessionID,
                                 info_.sessionID + info_.sessionIDLength);
   }
 
   size_t received_bytes() const { return recv_ctr_; }
-  int32_t error_code() const { return error_code_; }
+  PRErrorCode error_code() const { return error_code_; }
 
   bool can_falsestart_hook_called() const {
     return can_falsestart_hook_called_;
   }
 
   void SetHandshakeCallback(HandshakeCallbackFunction handshake_callback) {
     handshake_callback_ = handshake_callback;
   }
@@ -326,20 +326,20 @@ class TlsAgent : public PollTarget {
   bool expect_client_auth_;
   bool can_falsestart_hook_called_;
   bool sni_hook_called_;
   bool auth_certificate_hook_called_;
   bool handshake_callback_called_;
   SSLChannelInfo info_;
   SSLCipherSuiteInfo csinfo_;
   SSLVersionRange vrange_;
-  int32_t error_code_;
+  PRErrorCode error_code_;
   size_t send_ctr_;
   size_t recv_ctr_;
-  bool expected_read_error_;
+  bool expect_readwrite_error_;
   HandshakeCallbackFunction handshake_callback_;
   AuthCertificateCallbackFunction auth_certificate_callback_;
   SniCallbackFunction sni_callback_;
 };
 
 class TlsAgentTestBase : public ::testing::Test {
  public:
   static ::testing::internal::ParamGenerator<std::string> kTlsRolesAll;
@@ -351,25 +351,28 @@ class TlsAgentTestBase : public ::testin
       PR_Close(fd_);
     }
   }
 
   void SetUp();
   void TearDown();
 
   void MakeRecord(uint8_t type, uint16_t version, const uint8_t* buf,
-                  size_t len, DataBuffer* out, uint32_t seq_num = 0);
+                  size_t len, DataBuffer* out, uint64_t seq_num = 0);
+  static void MakeRecord(Mode mode, uint8_t type, uint16_t version,
+                         const uint8_t* buf, size_t len, DataBuffer* out,
+                         uint64_t seq_num = 0);
   void MakeHandshakeMessage(uint8_t hs_type, const uint8_t* data, size_t hs_len,
-                            DataBuffer* out, uint32_t seq_num = 0);
+                            DataBuffer* out, uint64_t seq_num = 0);
   void MakeHandshakeMessageFragment(uint8_t hs_type, const uint8_t* data,
                                     size_t hs_len, DataBuffer* out,
-                                    uint32_t seq_num, uint32_t fragment_offset,
+                                    uint64_t seq_num, uint32_t fragment_offset,
                                     uint32_t fragment_length);
-  void MakeTrivialHandshakeRecord(uint8_t hs_type, size_t hs_len,
-                                  DataBuffer* out);
+  static void MakeTrivialHandshakeRecord(uint8_t hs_type, size_t hs_len,
+                                         DataBuffer* out);
   static inline TlsAgent::Role ToRole(const std::string& str) {
     return str == "CLIENT" ? TlsAgent::CLIENT : TlsAgent::SERVER;
   }
 
   static inline Mode ToMode(const std::string& str) {
     return str == "TLS" ? STREAM : DGRAM;
   }
 
--- a/lib/ssl/SSLerrs.h
+++ b/lib/ssl/SSLerrs.h
@@ -479,8 +479,11 @@ ER3(SSL_ERROR_END_OF_EARLY_DATA_ALERT, (
 ER3(SSL_ERROR_MISSING_ALPN_EXTENSION, (SSL_ERROR_BASE + 150),
     "SSL didn't receive an expected ALPN extension.")
 
 ER3(SSL_ERROR_RX_UNEXPECTED_EXTENSION, (SSL_ERROR_BASE + 151),
     "SSL received an unexpected extension.")
 
 ER3(SSL_ERROR_MISSING_SUPPORTED_GROUPS, (SSL_ERROR_BASE + 152),
     "SSL expected a supported groups extension.")
+
+ER3(SSL_ERROR_TOO_MANY_RECORDS, (SSL_ERROR_BASE + 153),
+    "SSL sent or received too many records with the same symmetric key.")
--- a/lib/ssl/dtlscon.c
+++ b/lib/ssl/dtlscon.c
@@ -1,8 +1,9 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /*
  * DTLS Protocol
  */
 
@@ -1071,17 +1072,17 @@ dtls_InitRecvdRecords(DTLSRecvdRecords *
  * Has this DTLS record been received? Return values are:
  * -1 -- out of range to the left
  *  0 -- not received yet
  *  1 -- replay
  *
  *  Called from: ssl3_HandleRecord()
  */
 int
-dtls_RecordGetRecvd(const DTLSRecvdRecords *records, PRUint64 seq)
+dtls_RecordGetRecvd(const DTLSRecvdRecords *records, sslSequenceNumber seq)
 {
     PRUint64 offset;
 
     /* Out of range to the left */
     if (seq < records->left) {
         return -1;
     }
 
@@ -1096,27 +1097,27 @@ dtls_RecordGetRecvd(const DTLSRecvdRecor
     return !!(records->data[offset / 8] & (1 << (offset % 8)));
 }
 
 /* Update the DTLS anti-replay window
  *
  * Called from ssl3_HandleRecord()
  */
 void
-dtls_RecordSetRecvd(DTLSRecvdRecords *records, PRUint64 seq)
+dtls_RecordSetRecvd(DTLSRecvdRecords *records, sslSequenceNumber seq)
 {
     PRUint64 offset;
 
     if (seq < records->left)
         return;
 
     if (seq > records->right) {
-        PRUint64 new_left;
-        PRUint64 new_right;
-        PRUint64 right;
+        sslSequenceNumber new_left;
+        sslSequenceNumber new_right;
+        sslSequenceNumber right;
 
         /* Slide to the right; this is the tricky part
          *
          * 1. new_top is set to have room for seq, on the
          *    next byte boundary by setting the right 8
          *    bits of seq
          * 2. new_left is set to compensate.
          * 3. Zero all bits between top and new_top. Since
@@ -1180,31 +1181,30 @@ DTLS_GetHandshakeTimeout(PRFileDesc *soc
  * consistent with the guidance of RFC 6347 Sections 4.1 and 4.2.4.1.
  *
  * If the packet is not relevant, this function returns PR_FALSE.
  * If the packet is relevant, this function returns PR_TRUE
  * and sets |*seqNum| to the packet sequence number.
  */
 PRBool
 dtls_IsRelevant(sslSocket *ss, const ssl3CipherSpec *crSpec,
-                const SSL3Ciphertext *cText, PRUint64 *seqNum)
+                const SSL3Ciphertext *cText, sslSequenceNumber *seqNum)
 {
-    DTLSEpoch epoch = cText->seq_num.high >> 16;
-    PRUint64 dtls_seq_num;
+    DTLSEpoch epoch;
+    sslSequenceNumber dtls_seq_num;
 
+    epoch = cText->seq_num >> 48;
     if (crSpec->epoch != epoch) {
         SSL_DBG(("%d: SSL3[%d]: dtls_IsRelevant, received packet "
                  "from irrelevant epoch %d",
                  SSL_GETPID(), ss->fd, epoch));
         return PR_FALSE;
     }
 
-    dtls_seq_num = (((PRUint64)(cText->seq_num.high & 0xffff)) << 32) |
-                   ((PRUint64)cText->seq_num.low);
-
+    dtls_seq_num = cText->seq_num & RECORD_SEQ_MAX;
     if (dtls_RecordGetRecvd(&crSpec->recvdRecords, dtls_seq_num) != 0) {
         SSL_DBG(("%d: SSL3[%d]: dtls_IsRelevant, rejecting "
                  "potentially replayed packet",
                  SSL_GETPID(), ss->fd));
         return PR_FALSE;
     }
 
     *seqNum = dtls_seq_num;
@@ -1223,17 +1223,17 @@ dtls_IsRelevant(sslSocket *ss, const ssl
  * Note: This isn't an issue in earlier versions because the second-to-last
  * flight (sent by the server) includes the Finished message, which is not
  * dropped because it has the same epoch that the client currently expects.
  */
 SECStatus
 dtls_MaybeRetransmitHandshake(sslSocket *ss, const SSL3Ciphertext *cText)
 {
     SECStatus rv = SECSuccess;
-    DTLSEpoch messageEpoch = cText->seq_num.high >> 16;
+    DTLSEpoch messageEpoch = cText->seq_num >> 48;
 
     if (!ss->sec.isServer && ss->version >= SSL_LIBRARY_VERSION_TLS_1_3 &&
         messageEpoch == 0 && cText->type == content_handshake) {
         ssl_GetSSL3HandshakeLock(ss);
         if (ss->ssl3.hs.rtTimerCb == dtls_FinishedTimerCb &&
             ss->ssl3.hs.ws == idle_handshake) {
             rv = dtls_RetransmitDetected(ss);
         }
--- a/lib/ssl/ssl3con.c
+++ b/lib/ssl/ssl3con.c
@@ -285,65 +285,73 @@ static const /*SSL3ClientCertificateType
 #define EXPORT_RSA_KEY_LENGTH 64 /* bytes */
 
 /* This global item is used only in servers.  It is is initialized by
 ** SSL_ConfigSecureServer(), and is used in ssl3_SendCertificateRequest().
 */
 CERTDistNames *ssl3_server_ca_list = NULL;
 static SSL3Statistics ssl3stats;
 
-/* indexed by SSL3BulkCipher */
+/* Record protection algorithms, indexed by SSL3BulkCipher.
+ *
+ * The |max_records| field (|mr| below) is set to a number that is higher than
+ * recommended in some literature (esp. TLS 1.3) because we currently abort the
+ * connection when this limit is reached and we want to ensure that we only
+ * rarely hit this limit.  See bug 1268745 for details.
+ */
+#define MR_MAX RECORD_SEQ_MAX /* 2^48-1 */
+#define MR_128 (0x5aULL<<28)  /* For AES and similar. */
+#define MR_LOW (1ULL<<20)     /* For weak ciphers. */
 /* clang-format off */
 static const ssl3BulkCipherDef bulk_cipher_defs[] = {
     /*                                    |--------- Lengths ---------| */
-    /* cipher             calg            k  s  type         i  b  t  n */
-    /*                                    e  e               v  l  a  o */
-    /* oid                short_name      y  c               |  o  g  n */
-    /*                                    |  r               |  c  |  c */
-    /*                                    |  e               |  k  |  e */
-    /*                                    |  t               |  |  |  | */
+    /* cipher             calg            :  s                        : */
+    /*                                    :  e                  b     n */
+    /* oid                short_name mr   :                     l     o */
+    /*                                    k  r                  o  t  n */
+    /*                                    e  e               i  c  a  c */
+    /*                                    y  t  type         v  k  g  e */
     {cipher_null,         calg_null,      0, 0, type_stream, 0, 0, 0, 0,
-     SEC_OID_NULL_CIPHER, "NULL"},
+     SEC_OID_NULL_CIPHER, "NULL", MR_MAX},
     {cipher_rc4,          calg_rc4,      16,16, type_stream, 0, 0, 0, 0,
-     SEC_OID_RC4,         "RC4"},
+     SEC_OID_RC4,         "RC4", MR_LOW},
     {cipher_rc4_40,       calg_rc4,      16, 5, type_stream, 0, 0, 0, 0,
-     SEC_OID_RC4_40,      "RC4-40"},
+     SEC_OID_RC4_40,      "RC4-40", MR_LOW},
     {cipher_rc4_56,       calg_rc4,      16, 7, type_stream, 0, 0, 0, 0,
-     SEC_OID_RC4_56,      "RC4-56"},
+     SEC_OID_RC4_56,      "RC4-56", MR_LOW},
     {cipher_rc2,          calg_rc2,      16,16, type_block,  8, 8, 0, 0,
-     SEC_OID_RC2_CBC,     "RC2-CBC"},
+     SEC_OID_RC2_CBC,     "RC2-CBC", MR_LOW},
     {cipher_rc2_40,       calg_rc2,      16, 5, type_block,  8, 8, 0, 0,
-     SEC_OID_RC2_40_CBC,  "RC2-CBC-40"},
+     SEC_OID_RC2_40_CBC,  "RC2-CBC-40", MR_LOW},
     {cipher_des,          calg_des,       8, 8, type_block,  8, 8, 0, 0,
-     SEC_OID_DES_CBC,     "DES-CBC"},
+     SEC_OID_DES_CBC,     "DES-CBC", MR_LOW},
     {cipher_3des,         calg_3des,     24,24, type_block,  8, 8, 0, 0,
-     SEC_OID_DES_EDE3_CBC, "3DES-EDE-CBC"},
-    {cipher_des40,        calg_des,       8, 5, type_block,  8, 8, 0, 0,
-     SEC_OID_DES_40_CBC,  "DES-CBC-40"},
-    {cipher_idea,         calg_idea,     16,16, type_block,  8, 8, 0, 0,
-     SEC_OID_IDEA_CBC,    "IDEA-CBC"},
+     SEC_OID_DES_EDE3_CBC, "3DES-EDE-CBC", MR_LOW},
     {cipher_aes_128,      calg_aes,      16,16, type_block, 16,16, 0, 0,
-     SEC_OID_AES_128_CBC, "AES-128"},
+     SEC_OID_AES_128_CBC, "AES-128", MR_128},
     {cipher_aes_256,      calg_aes,      32,32, type_block, 16,16, 0, 0,
-     SEC_OID_AES_256_CBC, "AES-256"},
+     SEC_OID_AES_256_CBC, "AES-256", MR_128},
     {cipher_camellia_128, calg_camellia, 16,16, type_block, 16,16, 0, 0,
-     SEC_OID_CAMELLIA_128_CBC, "Camellia-128"},
+     SEC_OID_CAMELLIA_128_CBC, "Camellia-128", MR_128},
     {cipher_camellia_256, calg_camellia, 32,32, type_block, 16,16, 0, 0,
-     SEC_OID_CAMELLIA_256_CBC, "Camellia-256"},
+     SEC_OID_CAMELLIA_256_CBC, "Camellia-256", MR_128},
     {cipher_seed,         calg_seed,     16,16, type_block, 16,16, 0, 0,
-     SEC_OID_SEED_CBC,    "SEED-CBC"},
+     SEC_OID_SEED_CBC,    "SEED-CBC", MR_128},
     {cipher_aes_128_gcm,  calg_aes_gcm,  16,16, type_aead,   4, 0,16, 8,
-     SEC_OID_AES_128_GCM, "AES-128-GCM"},
+     SEC_OID_AES_128_GCM, "AES-128-GCM", MR_128},
     {cipher_aes_256_gcm,  calg_aes_gcm,  32,32, type_aead,   4, 0,16, 8,
-     SEC_OID_AES_256_GCM, "AES-256-GCM"},
+     SEC_OID_AES_256_GCM, "AES-256-GCM", MR_128},
     {cipher_chacha20,     calg_chacha20, 32,32, type_aead,  12, 0,16, 0,
-     SEC_OID_CHACHA20_POLY1305, "ChaCha20-Poly1305"},
+     SEC_OID_CHACHA20_POLY1305, "ChaCha20-Poly1305", MR_MAX},
     {cipher_missing,      calg_null,      0, 0, type_stream, 0, 0, 0, 0,
-     SEC_OID_UNKNOWN,     "missing"},
+     SEC_OID_UNKNOWN,     "missing", 0U},
 };
+#undef MR_MAX
+#undef MR_128
+#undef MR_LOW
 
 static const ssl3KEADef kea_defs[] =
 { /* indexed by SSL3KeyExchangeAlgorithm */
     /* kea            exchKeyType signKeyType authKeyType, is_limited limit tls_keygen ephemeral  oid */
     {kea_null,           ssl_kea_null, nullKey, ssl_auth_null, PR_FALSE,   0, PR_FALSE, PR_FALSE, 0},
     {kea_rsa,            ssl_kea_rsa,  nullKey, ssl_auth_rsa_decrypt,  PR_FALSE,   0, PR_FALSE, PR_FALSE, SEC_OID_TLS_RSA},
     /* note: export suites abuse RSA, but these will be removed soon */
     {kea_rsa_export,     ssl_kea_rsa,  rsaKey, ssl_auth_rsa_sign, PR_TRUE,  512, PR_FALSE, PR_FALSE, SEC_OID_TLS_RSA_EXPORT},
@@ -378,73 +386,39 @@ static const ssl3CipherSuiteDef cipher_s
     {TLS_RSA_WITH_NULL_MD5,         cipher_null,   mac_md5, kea_rsa, ssl_hash_none},
     {TLS_RSA_WITH_NULL_SHA,         cipher_null,   mac_sha, kea_rsa, ssl_hash_none},
     {TLS_RSA_WITH_NULL_SHA256,      cipher_null,   hmac_sha256, kea_rsa, ssl_hash_sha256},
     {TLS_RSA_EXPORT_WITH_RC4_40_MD5,cipher_rc4_40, mac_md5, kea_rsa_export, ssl_hash_none},
     {TLS_RSA_WITH_RC4_128_MD5,      cipher_rc4,    mac_md5, kea_rsa, ssl_hash_none},
     {TLS_RSA_WITH_RC4_128_SHA,      cipher_rc4,    mac_sha, kea_rsa, ssl_hash_none},
     {TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5,
                                     cipher_rc2_40, mac_md5, kea_rsa_export, ssl_hash_none},
-#if 0 /* not implemented */
-    {TLS_RSA_WITH_IDEA_CBC_SHA,     cipher_idea,   mac_sha, kea_rsa, ssl_hash_none},
-    {TLS_RSA_EXPORT_WITH_DES40_CBC_SHA,
-                                    cipher_des40,  mac_sha, kea_rsa_export, ssl_hash_none},
-#endif
     {TLS_RSA_WITH_DES_CBC_SHA,      cipher_des,    mac_sha, kea_rsa, ssl_hash_none},
     {TLS_RSA_WITH_3DES_EDE_CBC_SHA, cipher_3des,   mac_sha, kea_rsa, ssl_hash_none},
     {TLS_DHE_DSS_WITH_DES_CBC_SHA,  cipher_des,    mac_sha, kea_dhe_dss, ssl_hash_none},
     {TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA,
                                     cipher_3des,   mac_sha, kea_dhe_dss, ssl_hash_none},
     {TLS_DHE_DSS_WITH_RC4_128_SHA,  cipher_rc4,    mac_sha, kea_dhe_dss, ssl_hash_none},
-#if 0 /* not implemented */
-    {TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA,
-                                    cipher_des40,  mac_sha, kea_dh_dss_export, ssl_hash_none},
-    {TLS_DH_DSS_DES_CBC_SHA,        cipher_des,    mac_sha, kea_dh_dss, ssl_hash_none},
-    {TLS_DH_DSS_3DES_CBC_SHA,       cipher_3des,   mac_sha, kea_dh_dss, ssl_hash_none},
-    {TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA,
-                                    cipher_des40,  mac_sha, kea_dh_rsa_export, ssl_hash_none},
-    {TLS_DH_RSA_DES_CBC_SHA,        cipher_des,    mac_sha, kea_dh_rsa, ssl_hash_none},
-    {TLS_DH_RSA_3DES_CBC_SHA,       cipher_3des,   mac_sha, kea_dh_rsa, ssl_hash_none},
-    {TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA,
-                                    cipher_des40,  mac_sha, kea_dh_dss_export, ssl_hash_none},
-    {TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA,
-                                    cipher_des40,  mac_sha, kea_dh_rsa_export, ssl_hash_none},
-#endif
     {TLS_DHE_RSA_WITH_DES_CBC_SHA,  cipher_des,    mac_sha, kea_dhe_rsa, ssl_hash_none},
     {TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA,
                                     cipher_3des,   mac_sha, kea_dhe_rsa, ssl_hash_none},
-#if 0
-    {SSL_DH_ANON_EXPORT_RC4_40_MD5, cipher_rc4_40, mac_md5, kea_dh_anon_export, ssl_hash_none},
-    {TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA,
-                                    cipher_des40,  mac_sha, kea_dh_anon_export, ssl_hash_none},
-    {TLS_DH_anon_WITH_DES_CBC_SHA,  cipher_des,    mac_sha, kea_dh_anon, ssl_hash_none},
-    {TLS_DH_anon_WITH_3DES_CBC_SHA, cipher_3des,   mac_sha, kea_dh_anon, ssl_hash_none},
-#endif
 
 
 /* New TLS cipher suites */
     {TLS_RSA_WITH_AES_128_CBC_SHA,      cipher_aes_128, mac_sha, kea_rsa, ssl_hash_none},
     {TLS_RSA_WITH_AES_128_CBC_SHA256,   cipher_aes_128, hmac_sha256, kea_rsa, ssl_hash_sha256},
     {TLS_DHE_DSS_WITH_AES_128_CBC_SHA,  cipher_aes_128, mac_sha, kea_dhe_dss, ssl_hash_none},
     {TLS_DHE_RSA_WITH_AES_128_CBC_SHA,  cipher_aes_128, mac_sha, kea_dhe_rsa, ssl_hash_none},
     {TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, cipher_aes_128, hmac_sha256, kea_dhe_rsa, ssl_hash_sha256},
     {TLS_RSA_WITH_AES_256_CBC_SHA,      cipher_aes_256, mac_sha, kea_rsa, ssl_hash_none},
     {TLS_RSA_WITH_AES_256_CBC_SHA256,   cipher_aes_256, hmac_sha256, kea_rsa, ssl_hash_sha256},
     {TLS_DHE_DSS_WITH_AES_256_CBC_SHA,  cipher_aes_256, mac_sha, kea_dhe_dss, ssl_hash_none},
     {TLS_DHE_RSA_WITH_AES_256_CBC_SHA,  cipher_aes_256, mac_sha, kea_dhe_rsa, ssl_hash_none},
     {TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, cipher_aes_256, hmac_sha256, kea_dhe_rsa, ssl_hash_sha256},
     {TLS_DHE_RSA_WITH_AES_256_GCM_SHA384, cipher_aes_256_gcm, mac_aead, kea_dhe_rsa, ssl_hash_sha384},
-#if 0
-    {TLS_DH_DSS_WITH_AES_128_CBC_SHA,   cipher_aes_128, mac_sha, kea_dh_dss, ssl_hash_none},
-    {TLS_DH_RSA_WITH_AES_128_CBC_SHA,   cipher_aes_128, mac_sha, kea_dh_rsa, ssl_hash_none},
-    {TLS_DH_anon_WITH_AES_128_CBC_SHA,  cipher_aes_128, mac_sha, kea_dh_anon, ssl_hash_none},
-    {TLS_DH_DSS_WITH_AES_256_CBC_SHA,   cipher_aes_256, mac_sha, kea_dh_dss, ssl_hash_none},
-    {TLS_DH_RSA_WITH_AES_256_CBC_SHA,   cipher_aes_256, mac_sha, kea_dh_rsa, ssl_hash_none},
-    {TLS_DH_anon_WITH_AES_256_CBC_SHA,  cipher_aes_256, mac_sha, kea_dh_anon, ssl_hash_none},
-#endif
 
     {TLS_RSA_WITH_SEED_CBC_SHA,     cipher_seed,   mac_sha, kea_rsa, ssl_hash_none},
 
     {TLS_RSA_WITH_CAMELLIA_128_CBC_SHA, cipher_camellia_128, mac_sha, kea_rsa, ssl_hash_none},
     {TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA,
      cipher_camellia_128, mac_sha, kea_dhe_dss, ssl_hash_none},
     {TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA,
      cipher_camellia_128, mac_sha, kea_dhe_rsa, ssl_hash_none},
@@ -1569,24 +1543,16 @@ ssl3_ComputeDHKeyHash(sslSocket *ss, SSL
         }
     }
 
     if (hashBuf != buf && hashBuf != NULL)
         PORT_Free(hashBuf);
     return rv;
 }
 
-void
-ssl3_BumpSequenceNumber(SSL3SequenceNumber *num)
-{
-    num->low++;
-    if (num->low == 0)
-        num->high++;
-}
-
 /* Called twice, only from ssl3_DestroyCipherSpec (immediately below). */
 static void
 ssl3_CleanupKeyMaterial(ssl3KeyMaterial *mat)
 {
     if (mat->write_key != NULL) {
         PK11_FreeSymKey(mat->write_key);
         mat->write_key = NULL;
     }
@@ -2071,31 +2037,31 @@ ssl3_ParamFromIV(CK_MECHANISM_TYPE mtype
  * pseudo-header defintiion to use should be decided based on the version of
  * the protocol that was negotiated when the cipher spec became current, NOT
  * based on the version value in the record itself, and the decision is passed
  * to this function as the |includesVersion| argument. But, the |version|
  * argument should be the record's version value.
  */
 static unsigned int
 ssl3_BuildRecordPseudoHeader(unsigned char *out,
-                             SSL3SequenceNumber seq_num,
+                             sslSequenceNumber seq_num,
                              SSL3ContentType type,
                              PRBool includesVersion,
                              SSL3ProtocolVersion version,
                              PRBool isDTLS,
                              int length)
 {
-    out[0] = (unsigned char)(seq_num.high >> 24);
-    out[1] = (unsigned char)(seq_num.high >> 16);
-    out[2] = (unsigned char)(seq_num.high >> 8);
-    out[3] = (unsigned char)(seq_num.high >> 0);
-    out[4] = (unsigned char)(seq_num.low >> 24);
-    out[5] = (unsigned char)(seq_num.low >> 16);
-    out[6] = (unsigned char)(seq_num.low >> 8);
-    out[7] = (unsigned char)(seq_num.low >> 0);
+    out[0] = (unsigned char)(seq_num >> 56);
+    out[1] = (unsigned char)(seq_num >> 48);
+    out[2] = (unsigned char)(seq_num >> 40);
+    out[3] = (unsigned char)(seq_num >> 32);
+    out[4] = (unsigned char)(seq_num >> 24);
+    out[5] = (unsigned char)(seq_num >> 16);
+    out[6] = (unsigned char)(seq_num >> 8);
+    out[7] = (unsigned char)(seq_num >> 0);
     out[8] = type;
 
     /* SSL3 MAC doesn't include the record's version field. */
     if (!includesVersion) {
         out[9] = MSB(length);
         out[10] = LSB(length);
         return 11;
     }
@@ -2594,35 +2560,34 @@ ssl3_InitPendingCipherSpec(sslSocket *ss
         rv = SECFailure;
     }
     if (rv != SECSuccess) {
         goto done;
     }
 
     /* Generic behaviors -- common to all crypto methods */
     if (!IS_DTLS(ss)) {
-        pwSpec->read_seq_num.high = pwSpec->write_seq_num.high = 0;
+        pwSpec->read_seq_num = pwSpec->write_seq_num = 0;
     } else {
         if (cwSpec->epoch == PR_UINT16_MAX) {
             /* The problem here is that we have rehandshaked too many
              * times (you are not allowed to wrap the epoch). The
              * spec says you should be discarding the connection
              * and start over, so not much we can do here. */
             PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
             rv = SECFailure;
             goto done;
         }
         /* The sequence number has the high 16 bits as the epoch. */
         pwSpec->epoch = cwSpec->epoch + 1;
-        pwSpec->read_seq_num.high = pwSpec->write_seq_num.high =
-            pwSpec->epoch << 16;
+        pwSpec->read_seq_num = pwSpec->write_seq_num =
+            (sslSequenceNumber)pwSpec->epoch << 48;
 
         dtls_InitRecvdRecords(&pwSpec->recvdRecords);
     }
-    pwSpec->read_seq_num.low = pwSpec->write_seq_num.low = 0;
 
 done:
     ssl_ReleaseSpecWriteLock(ss); /******************************/
     if (rv != SECSuccess)
         ssl_MapLowLevelError(SSL_ERROR_SESSION_KEY_GEN_FAILURE);
     return rv;
 }
 
@@ -2925,20 +2890,29 @@ ssl3_CompressMACEncryptRecord(ssl3Cipher
     PRUint32 macLen = 0;
     PRUint32 fragLen;
     PRUint32 p1Len, p2Len, oddLen = 0;
     PRUint16 headerLen;
     unsigned int ivLen = 0;
     int cipherBytes = 0;
     unsigned char pseudoHeader[13];
     unsigned int pseudoHeaderLen;
+    PRUint8 *b;
 
     cipher_def = cwSpec->cipher_def;
     headerLen = isDTLS ? DTLS_RECORD_HEADER_LENGTH : SSL3_RECORD_HEADER_LENGTH;
 
+    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;
+    }
+
     if (cipher_def->type == type_block &&
         cwSpec->version >= SSL_LIBRARY_VERSION_TLS_1_1) {
         /* Prepend the per-record explicit IV using technique 2b from
          * RFC 4346 section 6.2.3.2: The IV is a cryptographically
          * strong random number XORed with the CBC residue from the previous
          * record.
          */
         ivLen = cipher_def->iv_size;
@@ -3085,47 +3059,36 @@ ssl3_CompressMACEncryptRecord(ssl3Cipher
             }
             cipherBytes += cipherBytesPart2;
         }
     }
 
     PORT_Assert(cipherBytes <= MAX_FRAGMENT_LENGTH + 1024);
 
     wrBuf->len = cipherBytes + headerLen;
-    wrBuf->buf[0] = type;
+    b = &wrBuf->buf[0];
+    b = ssl_EncodeUintX(type, 1, b);
     if (isDTLS) {
         SSL3ProtocolVersion version;
 
         version = dtls_TLSVersionToDTLSVersion(cwSpec->version);
-        wrBuf->buf[1] = MSB(version);
-        wrBuf->buf[2] = LSB(version);
-        wrBuf->buf[3] = (unsigned char)(cwSpec->write_seq_num.high >> 24);
-        wrBuf->buf[4] = (unsigned char)(cwSpec->write_seq_num.high >> 16);
-        wrBuf->buf[5] = (unsigned char)(cwSpec->write_seq_num.high >> 8);
-        wrBuf->buf[6] = (unsigned char)(cwSpec->write_seq_num.high >> 0);
-        wrBuf->buf[7] = (unsigned char)(cwSpec->write_seq_num.low >> 24);
-        wrBuf->buf[8] = (unsigned char)(cwSpec->write_seq_num.low >> 16);
-        wrBuf->buf[9] = (unsigned char)(cwSpec->write_seq_num.low >> 8);
-        wrBuf->buf[10] = (unsigned char)(cwSpec->write_seq_num.low >> 0);
-        wrBuf->buf[11] = MSB(cipherBytes);
-        wrBuf->buf[12] = LSB(cipherBytes);
+        b = ssl_EncodeUintX(version, 2, b);
+        b = ssl_EncodeUintX(cwSpec->write_seq_num, 8, b);
     } else {
         SSL3ProtocolVersion version = cwSpec->version;
 
         if (capRecordVersion || version >= SSL_LIBRARY_VERSION_TLS_1_3) {
             version = PR_MIN(SSL_LIBRARY_VERSION_TLS_1_0, version);
         }
 
-        wrBuf->buf[1] = MSB(version);
-        wrBuf->buf[2] = LSB(version);
-        wrBuf->buf[3] = MSB(cipherBytes);
-        wrBuf->buf[4] = LSB(cipherBytes);
-    }
-
-    ssl3_BumpSequenceNumber(&cwSpec->write_seq_num);
+        b = ssl_EncodeUintX(version, 2, b);
+    }
+    b = ssl_EncodeUintX(cipherBytes, 2, b);
+
+    ++cwSpec->write_seq_num;
 
     return SECSuccess;
 }
 
 /* Process the plain text before sending it.
  * Returns the number of bytes of plaintext that were successfully sent
  *  plus the number of bytes of plaintext that were copied into the
  *  output (write) buffer.
@@ -4975,26 +4938,26 @@ ssl3_ConsumeHandshakeVariable(sslSocket 
         i->data = *b;
         i->len = count;
         *b += count;
         *length -= count;
     }
     return SECSuccess;
 }
 
-/* Helper function to encode a uint32 into a buffer */
+/* Helper function to encode an unsigned integer into a buffer. */
 PRUint8 *
-ssl_EncodeUintX(PRUint32 value, unsigned int bytes, PRUint8 *to)
-{
-    PRUint32 encoded;
-
-    PORT_Assert(bytes > 0 && bytes <= 4);
-
-    encoded = PR_htonl(value);
-    memcpy(to, ((unsigned char *)(&encoded)) + (4 - bytes), bytes);
+ssl_EncodeUintX(PRUint64 value, unsigned int bytes, PRUint8 *to)
+{
+    PRUint64 encoded;
+
+    PORT_Assert(bytes > 0 && bytes <= sizeof(encoded));
+
+    encoded = PR_htonll(value);
+    memcpy(to, ((unsigned char *)(&encoded)) + (sizeof(encoded) - bytes), bytes);
     return to + bytes;
 }
 
 /* ssl3_TLSHashAlgorithmToOID converts a TLS hash identifier into an OID value.
  * If the hash is not recognised, SEC_OID_UNKNOWN is returned.
  *
  * See https://tools.ietf.org/html/rfc5246#section-7.4.1.4.1 */
 SECOidTag
@@ -13517,17 +13480,17 @@ ssl3_UnprotectRecord(sslSocket *ss, SSL3
  * lock around any calls to functions that handle records other than
  * Application Data records.
  */
 SECStatus
 ssl3_HandleRecord(sslSocket *ss, SSL3Ciphertext *cText, sslBuffer *databuf)
 {
     SECStatus rv;
     PRBool isTLS;
-    PRUint64 dtls_seq_num = 0;
+    sslSequenceNumber seq_num = 0;
     ssl3CipherSpec *crSpec;
     SSL3ContentType rType;
     sslBuffer *plaintext;
     sslBuffer temp_buf;
     SSL3AlertDescription alert;
     PORT_Assert(ss->opt.noLocks || ssl_HaveRecvBufLock(ss));
 
     if (!ss->ssl3.initialized) {
@@ -13556,22 +13519,31 @@ ssl3_HandleRecord(sslSocket *ss, SSL3Cip
         goto process_it;
     }
 
     ssl_GetSpecReadLock(ss); /******************************************/
     crSpec = ss->ssl3.crSpec;
     isTLS = (PRBool)(crSpec->version > SSL_LIBRARY_VERSION_3_0);
 
     if (IS_DTLS(ss)) {
-        if (!dtls_IsRelevant(ss, crSpec, cText, &dtls_seq_num)) {
+        if (!dtls_IsRelevant(ss, crSpec, cText, &seq_num)) {
             ssl_ReleaseSpecReadLock(ss); /*****************************/
             databuf->len = 0;            /* Needed to ensure data not left around */
             /* Drop the packet, but first see if retransmission is needed. */
             return dtls_MaybeRetransmitHandshake(ss, cText);
         }
+    } else {
+        seq_num = crSpec->read_seq_num + 1;
+    }
+    if (seq_num >= crSpec->cipher_def->max_records) {
+        ssl_ReleaseSpecReadLock(ss); /*****************************/
+        SSL_TRC(3, ("%d: SSL[%d]: read sequence number at limit 0x%0llx",
+                    SSL_GETPID(), ss->fd, seq_num));
+        PORT_SetError(SSL_ERROR_TOO_MANY_RECORDS);
+        return SECFailure;
     }
 
     /* If we will be decompressing the buffer we need to decrypt somewhere
      * other than into databuf */
     if (crSpec->decompressor) {
         temp_buf.buf = NULL;
         temp_buf.space = 0;
         plaintext = &temp_buf;
@@ -13618,20 +13590,19 @@ ssl3_HandleRecord(sslSocket *ss, SSL3Cip
             /* Reset the error code in case SSL3_SendAlert called
              * PORT_SetError(). */
             PORT_SetError(errCode);
             return SECFailure;
         }
     }
 
     /* SECSuccess */
-    if (!IS_DTLS(ss)) {
-        ssl3_BumpSequenceNumber(&crSpec->read_seq_num);
-    } else {
-        dtls_RecordSetRecvd(&crSpec->recvdRecords, dtls_seq_num);
+    crSpec->read_seq_num = seq_num;
+    if (IS_DTLS(ss)) {
+        dtls_RecordSetRecvd(&crSpec->recvdRecords, seq_num);
     }
 
     ssl_ReleaseSpecReadLock(ss); /*****************************************/
 
     /*
      * The decrypted data is now in plaintext.
      */
     rType = cText->type; /* This must go after decryption because TLS 1.3
@@ -13793,23 +13764,20 @@ ssl3_InitCipherSpec(ssl3CipherSpec *spec
     spec->client.write_key = NULL;
     spec->client.write_mac_key = NULL;
     spec->client.write_mac_context = NULL;
 
     spec->server.write_key = NULL;
     spec->server.write_mac_key = NULL;
     spec->server.write_mac_context = NULL;
 
-    spec->write_seq_num.high = 0;
-    spec->write_seq_num.low = 0;
-
-    spec->read_seq_num.high = 0;
-    spec->read_seq_num.low = 0;
-
+    spec->write_seq_num = 0;
+    spec->read_seq_num = 0;
     spec->epoch = 0;
+
     spec->refCt = 128; /* Arbitrarily high number to prevent
                         * non-TLS 1.3 cipherSpecs from being
                         * GCed. This will be overwritten with
                         * a valid refCt for TLS 1.3. */
     dtls_InitRecvdRecords(&spec->recvdRecords);
 }
 
 /* Called from: ssl3_SendRecord
--- a/lib/ssl/ssl3gthr.c
+++ b/lib/ssl/ssl3gthr.c
@@ -457,28 +457,22 @@ ssl3_GatherCompleteHandshake(sslSocket *
                  * 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 (IS_DTLS(ss)) {
-                    int i;
+                    sslSequenceNumber seq_num;
 
                     cText.version = dtls_DTLSVersionToTLSVersion(cText.version);
                     /* DTLS sequence number */
-                    cText.seq_num.high = 0;
-                    cText.seq_num.low = 0;
-                    for (i = 0; i < 4; i++) {
-                        cText.seq_num.high <<= 8;
-                        cText.seq_num.low <<= 8;
-                        cText.seq_num.high |= ss->gs.hdr[3 + i];
-                        cText.seq_num.low |= ss->gs.hdr[7 + i];
-                    }
+                    PORT_Memcpy(&seq_num, &ss->gs.hdr[3], sizeof(seq_num));
+                    cText.seq_num = PR_ntohll(seq_num);
                 }
 
                 cText.buf = &ss->gs.inbuf;
                 rv = ssl3_HandleRecord(ss, &cText, &ss->gs.buf);
             }
         }
         if (rv < 0) {
             return ss->recvdCloseNotify ? 0 : rv;
--- a/lib/ssl/sslerr.h
+++ b/lib/ssl/sslerr.h
@@ -231,15 +231,16 @@ typedef enum {
     SSL_ERROR_EXTENSION_DISALLOWED_FOR_VERSION = (SSL_ERROR_BASE + 145),
     SSL_ERROR_RX_MALFORMED_ENCRYPTED_EXTENSIONS = (SSL_ERROR_BASE + 146),
     SSL_ERROR_MALFORMED_PRE_SHARED_KEY = (SSL_ERROR_BASE + 147),
     SSL_ERROR_MALFORMED_EARLY_DATA = (SSL_ERROR_BASE + 148),
     SSL_ERROR_END_OF_EARLY_DATA_ALERT = (SSL_ERROR_BASE + 149),
     SSL_ERROR_MISSING_ALPN_EXTENSION = (SSL_ERROR_BASE + 150),
     SSL_ERROR_RX_UNEXPECTED_EXTENSION = (SSL_ERROR_BASE + 151),
     SSL_ERROR_MISSING_SUPPORTED_GROUPS_EXTENSION = (SSL_ERROR_BASE + 152),
+    SSL_ERROR_TOO_MANY_RECORDS = (SSL_ERROR_BASE + 153),
     SSL_ERROR_END_OF_LIST   /* let the c compiler determine the value of this. */
 } SSLErrorCodes;
 #endif /* NO_SECURITY_ERROR_ENUM */
 
 /* clang-format on */
 
 #endif /* __SSL_ERR_H_ */
--- a/lib/ssl/sslimpl.h
+++ b/lib/ssl/sslimpl.h
@@ -432,18 +432,16 @@ typedef enum {
     cipher_null,
     cipher_rc4,
     cipher_rc4_40,
     cipher_rc4_56,
     cipher_rc2,
     cipher_rc2_40,
     cipher_des,
     cipher_3des,
-    cipher_des40,
-    cipher_idea,
     cipher_aes_128,
     cipher_aes_256,
     cipher_camellia_128,
     cipher_camellia_256,
     cipher_seed,
     cipher_aes_128_gcm,
     cipher_aes_256_gcm,
     cipher_chacha20,
@@ -452,24 +450,17 @@ typedef enum {
 } SSL3BulkCipher;
 
 typedef enum { type_stream,
                type_block,
                type_aead } CipherType;
 
 #define MAX_IV_LENGTH 24
 
-/*
- * Do not depend upon 64 bit arithmetic in the underlying machine.
- */
-typedef struct {
-    PRUint32 high;
-    PRUint32 low;
-} SSL3SequenceNumber;
-
+typedef PRUint64 sslSequenceNumber;
 typedef PRUint16 DTLSEpoch;
 
 typedef void (*DTLSTimerCb)(sslSocket *);
 
 /* 400 is large enough for MD5, SHA-1, and SHA-256.
  * For SHA-384 support, increase it to 712. */
 #define MAX_MAC_CONTEXT_BYTES 712
 #define MAX_MAC_CONTEXT_LLONGS (MAX_MAC_CONTEXT_BYTES / 8)
@@ -523,20 +514,21 @@ typedef SECStatus (*SSLDestroy)(void *co
 /* The DTLS anti-replay window. Defined here because we need it in
  * the cipher spec. Note that this is a ring buffer but left and
  * right represent the true window, with modular arithmetic used to
  * map them onto the buffer.
  */
 #define DTLS_RECVD_RECORDS_WINDOW 1024 /* Packets; approximate   \
                                         * Must be divisible by 8 \
                                         */
+#define RECORD_SEQ_MAX ((1ULL<<48)-1)
 typedef struct DTLSRecvdRecordsStr {
     unsigned char data[DTLS_RECVD_RECORDS_WINDOW / 8];
-    PRUint64 left;
-    PRUint64 right;
+    sslSequenceNumber left;
+    sslSequenceNumber right;
 } DTLSRecvdRecords;
 
 /*
 ** These are the "specs" in the "ssl3" struct.
 ** Access to the pointers to these specs, and all the specs' contents
 ** (direct and indirect) is protected by the reader/writer lock ss->specLock.
 */
 typedef struct {
@@ -555,18 +547,18 @@ typedef struct {
     SSLCompressor decompressor; /* and uncompress because zconf.h   */
                                 /* may define them as macros.       */
     SSLDestroy destroyCompressContext;
     void *compressContext;
     SSLDestroy destroyDecompressContext;
     void *decompressContext;
     PRBool bypassCiphers; /* did double bypass (at least) */
     PK11SymKey *master_secret;
-    SSL3SequenceNumber write_seq_num;
-    SSL3SequenceNumber read_seq_num;
+    sslSequenceNumber write_seq_num;
+    sslSequenceNumber read_seq_num;
     SSL3ProtocolVersion version;
     ssl3KeyMaterial client;
     ssl3KeyMaterial server;
     SECItem msItem;
     unsigned char key_block[NUM_MIXERS * HASH_LENGTH_MAX];
     unsigned char raw_master_secret[56];
     DTLSEpoch epoch;
     DTLSRecvdRecords recvdRecords;
@@ -736,16 +728,19 @@ struct ssl3BulkCipherDefStr {
     unsigned int secret_key_size;
     CipherType type;
     unsigned int iv_size;
     unsigned int block_size;
     unsigned int tag_size;            /* for AEAD ciphers. */
     unsigned int explicit_nonce_size; /* for AEAD ciphers. */
     SECOidTag oid;
     const char *short_name;
+    /* The maximum number of records that can be sent/received with the same
+      * symmetric key before the connection will be terminated. */
+    PRUint64 max_records;
 };
 
 /*
 ** There are tables of these, all const.
 */
 struct ssl3MACDefStr {
     SSL3MACAlgorithm mac;
     CK_MECHANISM_TYPE mmech;
@@ -1068,17 +1063,17 @@ struct ssl3StateStr {
 /* Ethernet MTU but without subtracting the headers,
  * so slightly larger than expected */
 #define DTLS_MAX_MTU 1500U
 #define IS_DTLS(ss) (ss->protocolVariant == ssl_variant_datagram)
 
 typedef struct {
     SSL3ContentType type;
     SSL3ProtocolVersion version;
-    SSL3SequenceNumber seq_num; /* DTLS only */
+    sslSequenceNumber seq_num; /* DTLS only */
     sslBuffer *buf;
 } SSL3Ciphertext;
 
 struct sslKeyPairStr {
     SECKEYPrivateKey *privKey;
     SECKEYPublicKey *pubKey;
     PRInt32 refCount; /* use PR_Atomic calls for this. */
 };
@@ -1793,17 +1788,17 @@ extern SECStatus ssl3_AppendSignatureAnd
     sslSocket *ss, const SSLSignatureAndHashAlg *sigAndHash);
 extern SECStatus ssl3_ConsumeHandshake(sslSocket *ss, void *v, PRInt32 bytes,
                                        SSL3Opaque **b, PRUint32 *length);
 extern PRInt32 ssl3_ConsumeHandshakeNumber(sslSocket *ss, PRInt32 bytes,
                                            SSL3Opaque **b, PRUint32 *length);
 extern SECStatus ssl3_ConsumeHandshakeVariable(sslSocket *ss, SECItem *i,
                                                PRInt32 bytes, SSL3Opaque **b,
                                                PRUint32 *length);
-extern PRUint8 *ssl_EncodeUintX(PRUint32 value, unsigned int bytes,
+extern PRUint8 *ssl_EncodeUintX(PRUint64 value, unsigned int bytes,
                                 PRUint8 *to);
 extern PRBool ssl_IsSupportedSignatureScheme(SignatureScheme scheme);
 extern SECStatus ssl_CheckSignatureSchemeConsistency(
     sslSocket *ss, SignatureScheme scheme, CERTCertificate *cert);
 extern SECStatus ssl_ParseSignatureSchemes(sslSocket *ss, PLArenaPool *arena,
                                            SignatureScheme **schemesOut,
                                            unsigned int *numSchemesOut,
                                            unsigned char **b,
@@ -1931,25 +1926,28 @@ extern SECStatus dtls_CompressMACEncrypt
                                                PRUint32 contentLen,
                                                sslBuffer *wrBuf);
 SECStatus ssl3_DisableNonDTLSSuites(sslSocket *ss);
 extern SECStatus dtls_StartHolddownTimer(sslSocket *ss);
 extern void dtls_CheckTimer(sslSocket *ss);
 extern void dtls_CancelTimer(sslSocket *ss);
 extern void dtls_SetMTU(sslSocket *ss, PRUint16 advertised);
 extern void dtls_InitRecvdRecords(DTLSRecvdRecords *records);
-extern int dtls_RecordGetRecvd(const DTLSRecvdRecords *records, PRUint64 seq);
-extern void dtls_RecordSetRecvd(DTLSRecvdRecords *records, PRUint64 seq);
+extern int dtls_RecordGetRecvd(const DTLSRecvdRecords *records,
+                               sslSequenceNumber seq);
+extern void dtls_RecordSetRecvd(DTLSRecvdRecords *records,
+                                sslSequenceNumber seq);
 extern void dtls_RehandshakeCleanup(sslSocket *ss);
 extern SSL3ProtocolVersion
 dtls_TLSVersionToDTLSVersion(SSL3ProtocolVersion tlsv);
 extern SSL3ProtocolVersion
 dtls_DTLSVersionToTLSVersion(SSL3ProtocolVersion dtlsv);
 extern PRBool dtls_IsRelevant(sslSocket *ss, const ssl3CipherSpec *crSpec,
-                              const SSL3Ciphertext *cText, PRUint64 *seqNum);
+                              const SSL3Ciphertext *cText,
+                              sslSequenceNumber *seqNum);
 extern SECStatus dtls_MaybeRetransmitHandshake(sslSocket *ss,
                                                const SSL3Ciphertext *cText);
 
 CK_MECHANISM_TYPE ssl3_Alg2Mech(SSLCipherAlgorithm calg);
 SECStatus ssl3_NegotiateCipherSuite(sslSocket *ss, const SECItem *suites);
 SECStatus ssl3_ServerCallSNICallback(sslSocket *ss);
 SECStatus ssl3_SetupPendingCipherSpec(sslSocket *ss);
 SECStatus ssl3_FlushHandshake(sslSocket *ss, PRInt32 flags);
@@ -1970,17 +1968,16 @@ SECStatus ssl3_ParseCertificateRequestCA
 SECStatus ssl3_CompleteHandleCertificateRequest(
     sslSocket *ss, const SignatureScheme *signatureSchemes,
     unsigned int signatureSchemeCount, CERTDistNames *ca_list);
 SECStatus ssl3_SendServerHello(sslSocket *ss);
 SECStatus ssl3_ComputeHandshakeHashes(sslSocket *ss,
                                       ssl3CipherSpec *spec,
                                       SSL3Hashes *hashes,
                                       PRUint32 sender);
-void ssl3_BumpSequenceNumber(SSL3SequenceNumber *num);
 PRInt32 tls13_ServerSendKeyShareXtn(sslSocket *ss, PRBool append,
                                     PRUint32 maxBytes);
 SECStatus ssl_CreateECDHEphemeralKeyPair(const namedGroupDef *ecGroup,
                                          sslEphemeralKeyPair **keyPair);
 SECStatus ssl3_FlushHandshake(sslSocket *ss, PRInt32 flags);
 PK11SymKey *ssl3_GetWrappingKey(sslSocket *ss,
                                 PK11SlotInfo *masterSecretSlot,
                                 const sslServerCert *serverCert,
--- a/lib/ssl/tls13con.c
+++ b/lib/ssl/tls13con.c
@@ -2036,25 +2036,24 @@ tls13_SetCipherSpec(sslSocket *ss, Traff
      * it in both TLS and DTLS. */
     if ((*specp)->epoch == PR_UINT16_MAX) {
         ssl_ReleaseSpecWriteLock(ss);
         return SECFailure;
     }
     spec->epoch = (*specp)->epoch + 1;
 
     if (!IS_DTLS(ss)) {
-        spec->read_seq_num.high = spec->write_seq_num.high = 0;
+        spec->read_seq_num = spec->write_seq_num = 0;
     } else {
         /* The sequence number has the high 16 bits as the epoch. */
-        spec->read_seq_num.high = spec->write_seq_num.high =
-            spec->epoch << 16;
+        spec->read_seq_num = spec->write_seq_num =
+            (sslSequenceNumber)spec->epoch << 48;
 
         dtls_InitRecvdRecords(&spec->recvdRecords);
     }
-    spec->read_seq_num.low = spec->write_seq_num.low = 0;
 
     /* Now that we've set almost everything up, finally cut over. */
     ssl_GetSpecWriteLock(ss);
     tls13_CipherSpecRelease(*specp); /* May delete old cipher. */
     *specp = spec;                   /* Overwrite. */
     ssl_ReleaseSpecWriteLock(ss);
 
     SSL_TRC(3, ("%d: TLS13[%d]: %s installed key for phase='%s' dir=%s",
@@ -3224,23 +3223,22 @@ tls13_ExtensionAllowed(PRUint16 extensio
 
 /* TLS 1.3 doesn't actually have additional data but the aead function
  * signature overloads additional data to carry the record sequence
  * number and that's what we put here. The TLS 1.3 AEAD functions
  * just use this input as the sequence number and not as additional
  * data. */
 static void
 tls13_FormatAdditionalData(PRUint8 *aad, unsigned int length,
-                           SSL3SequenceNumber seqNum)
+                           sslSequenceNumber seqNum)
 {
     PRUint8 *ptr = aad;
 
     PORT_Assert(length == 8);
-    ptr = ssl_EncodeUintX(seqNum.high, 4, ptr);
-    ptr = ssl_EncodeUintX(seqNum.low, 4, ptr);
+    ptr = ssl_EncodeUintX(seqNum, 8, ptr);
     PORT_Assert((ptr - aad) == length);
 }
 
 SECStatus
 tls13_ProtectRecord(sslSocket *ss,
                     ssl3CipherSpec *cwSpec,
                     SSL3ContentType type,
                     const SSL3Opaque *pIn,
@@ -3248,20 +3246,27 @@ tls13_ProtectRecord(sslSocket *ss,
                     sslBuffer *wrBuf)
 {
     const ssl3BulkCipherDef *cipher_def = cwSpec->cipher_def;
     SECStatus rv;
     PRUint16 headerLen;
     int cipherBytes = 0;
     const int tagLen = cipher_def->tag_size;
 
-    SSL_TRC(3, ("%d: TLS13[%d]: spec=%d phase=%s protect record of length %u, seq=0x%0x%0x",
-                SSL_GETPID(), ss->fd, cwSpec, cwSpec->phase, contentLen,
-                cwSpec->write_seq_num.high,
-                cwSpec->write_seq_num.low));
+    SSL_TRC(3, ("%d: TLS13[%d]: spec=%d (%s) protect record 0x%0llx len=%u",
+                SSL_GETPID(), ss->fd, cwSpec, cwSpec->phase,
+                cwSpec->write_seq_num, contentLen));
+
+    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: TLS13[%d]: write sequence number at limit 0x%0llx",
+                    SSL_GETPID(), ss->fd, cwSpec->write_seq_num));
+        PORT_SetError(SSL_ERROR_TOO_MANY_RECORDS);
+        return SECFailure;
+    }
 
     headerLen = IS_DTLS(ss) ? DTLS_RECORD_HEADER_LENGTH : SSL3_RECORD_HEADER_LENGTH;
 
     if (headerLen + contentLen + 1 + tagLen > wrBuf->space) {
         PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
         return SECFailure;
     }
 
@@ -3277,18 +3282,17 @@ tls13_ProtectRecord(sslSocket *ss,
         PORT_Assert(cipher_def->type == type_aead);
 
         /* Add the content type at the end. */
         wrBuf->buf[headerLen + contentLen] = type;
 
         /* Stomp the content type to be application_data */
         type = content_application_data;
 
-        tls13_FormatAdditionalData(aad, sizeof(aad),
-                                   cwSpec->write_seq_num);
+        tls13_FormatAdditionalData(aad, sizeof(aad), cwSpec->write_seq_num);
         cipherBytes = contentLen + 1; /* Room for the content type on the end. */
         rv = cwSpec->aead(
             ss->sec.isServer ? &cwSpec->server : &cwSpec->client,
             PR_FALSE,                               /* do encrypt */
             wrBuf->buf + headerLen,                 /* output  */
             &cipherBytes,                           /* out len */
             wrBuf->space - headerLen,               /* max out */
             wrBuf->buf + headerLen, contentLen + 1, /* input   */
@@ -3303,24 +3307,23 @@ tls13_ProtectRecord(sslSocket *ss,
 
     wrBuf->len = cipherBytes + headerLen;
     wrBuf->buf[0] = type;
 
     if (IS_DTLS(ss)) {
         (void)ssl_EncodeUintX(
             dtls_TLSVersionToDTLSVersion(kDtlsRecordVersion), 2,
             &wrBuf->buf[1]);
-        (void)ssl_EncodeUintX(cwSpec->write_seq_num.high, 4, &wrBuf->buf[3]);
-        (void)ssl_EncodeUintX(cwSpec->write_seq_num.low, 4, &wrBuf->buf[7]);
+        (void)ssl_EncodeUintX(cwSpec->write_seq_num, 8, &wrBuf->buf[3]);
         (void)ssl_EncodeUintX(cipherBytes, 2, &wrBuf->buf[11]);
     } else {
         (void)ssl_EncodeUintX(kTlsRecordVersion, 2, &wrBuf->buf[1]);
         (void)ssl_EncodeUintX(cipherBytes, 2, &wrBuf->buf[3]);
     }
-    ssl3_BumpSequenceNumber(&cwSpec->write_seq_num);
+    ++cwSpec->write_seq_num;
 
     return SECSuccess;
 }
 
 /* Unprotect a TLS 1.3 record and leave the result in plaintext.
  *
  * Called by ssl3_HandleRecord. Caller must hold the spec read lock.
  * Therefore, we MUST not call SSL3_SendAlert().
@@ -3335,20 +3338,19 @@ tls13_UnprotectRecord(sslSocket *ss, SSL
 {
     ssl3CipherSpec *crSpec = ss->ssl3.crSpec;
     const ssl3BulkCipherDef *cipher_def = crSpec->cipher_def;
     PRUint8 aad[8];
     SECStatus rv;
 
     *alert = bad_record_mac; /* Default alert for most issues. */
 
-    SSL_TRC(3, ("%d: TLS13[%d]: spec=%d phase=%s unprotect record of length %u seq=0x%0x%0x",
-                SSL_GETPID(), ss->fd, crSpec, crSpec->phase, cText->buf->len,
-                crSpec->read_seq_num.high,
-                crSpec->read_seq_num.low));
+    SSL_TRC(3, ("%d: TLS13[%d]: spec=%d (%s) unprotect record 0x%0llx len=%u",
+                SSL_GETPID(), ss->fd, crSpec, crSpec->phase,
+                crSpec->read_seq_num, cText->buf->len));
 
     /* We can perform this test in variable time because the record's total
      * length and the ciphersuite are both public knowledge. */
     if (cText->buf->len < cipher_def->tag_size) {
         SSL_TRC(3,
                 ("%d: TLS13[%d]: record too short to contain valid AEAD data",
                  SSL_GETPID(), ss->fd));
         PORT_SetError(SSL_ERROR_BAD_MAC_READ);