Bug 1316231 - DTLS 1.3 ACKs. r=mt NSS_TLS13_DRAFT19_BRANCH
authorEKR <ekr@rtfm.com>
Mon, 04 Sep 2017 04:35:10 -0700
branchNSS_TLS13_DRAFT19_BRANCH
changeset 13559 65c4f36454948e967b6ec234692b609f598d0daf
parent 13545 32a679e3f6ba53f68b0287a5dea66a8d46436923
child 13572 c3ccbe0e91cf2ed741f649f1b7f10054cfafc2dc
push id2347
push userekr@mozilla.com
push dateMon, 04 Sep 2017 12:44:13 +0000
reviewersmt
bugs1316231
Bug 1316231 - DTLS 1.3 ACKs. r=mt
cpputil/databuffer.h
gtests/ssl_gtest/libssl_internals.c
gtests/ssl_gtest/libssl_internals.h
gtests/ssl_gtest/ssl_0rtt_unittest.cc
gtests/ssl_gtest/ssl_auth_unittest.cc
gtests/ssl_gtest/ssl_damage_unittest.cc
gtests/ssl_gtest/ssl_drop_unittest.cc
gtests/ssl_gtest/ssl_extension_unittest.cc
gtests/ssl_gtest/ssl_hrr_unittest.cc
gtests/ssl_gtest/ssl_loopback_unittest.cc
gtests/ssl_gtest/ssl_resumption_unittest.cc
gtests/ssl_gtest/ssl_skip_unittest.cc
gtests/ssl_gtest/ssl_version_unittest.cc
gtests/ssl_gtest/test_io.cc
gtests/ssl_gtest/test_io.h
gtests/ssl_gtest/tls_agent.cc
gtests/ssl_gtest/tls_agent.h
gtests/ssl_gtest/tls_connect.cc
gtests/ssl_gtest/tls_connect.h
gtests/ssl_gtest/tls_filter.cc
gtests/ssl_gtest/tls_filter.h
gtests/ssl_gtest/tls_protect.cc
gtests/ssl_gtest/tls_protect.h
lib/ssl/SSLerrs.h
lib/ssl/dtls13con.c
lib/ssl/dtls13con.h
lib/ssl/dtlscon.c
lib/ssl/dtlscon.h
lib/ssl/manifest.mn
lib/ssl/ssl.gyp
lib/ssl/ssl.h
lib/ssl/ssl3con.c
lib/ssl/ssl3gthr.c
lib/ssl/ssl3prot.h
lib/ssl/sslerr.h
lib/ssl/sslimpl.h
lib/ssl/sslsock.c
lib/ssl/tls13con.c
lib/ssl/tls13con.h
--- a/cpputil/databuffer.h
+++ b/cpputil/databuffer.h
@@ -95,29 +95,41 @@ class DataBuffer {
     assert(count <= sizeof(uint32_t));
     uint32_t nvalue = htonl(val);
     auto* addr = reinterpret_cast<const uint8_t*>(&nvalue);
     return Write(index, addr + sizeof(uint32_t) - count, count);
   }
 
   // This can't use the same trick as Write(), since we might be reading from a
   // smaller data source.
-  bool Read(size_t index, size_t count, uint32_t* val) const {
-    assert(count < sizeof(uint32_t));
+  bool Read(size_t index, size_t count, uint64_t* val) const {
+    assert(count <= sizeof(uint64_t));
     assert(val);
     if ((index > len()) || (count > (len() - index))) {
       return false;
     }
     *val = 0;
     for (size_t i = 0; i < count; ++i) {
       *val = (*val << 8) | data()[index + i];
     }
     return true;
   }
 
+  // Overload because we have a lot of places where we are doing uint32_t
+  bool Read(size_t index, size_t count, uint32_t* val) const {
+    assert(count <= sizeof(uint32_t));
+    uint64_t tmp;
+
+    if (!Read(index, count, &tmp)) {
+      return false;
+    }
+    *val = tmp & 0xffffffff;
+    return true;
+  }
+
   // Starting at |index|, remove |remove| bytes and replace them with the
   // contents of |buf|.
   void Splice(const DataBuffer& buf, size_t index, size_t remove = 0) {
     Splice(buf.data(), buf.len(), index, remove);
   }
 
   void Splice(const uint8_t* ins, size_t ins_len, size_t index,
               size_t remove = 0) {
--- a/gtests/ssl_gtest/libssl_internals.c
+++ b/gtests/ssl_gtest/libssl_internals.c
@@ -87,45 +87,47 @@ PRInt32 SSLInt_CountTls13CipherSpecs(PRF
 
   for (cur_p = PR_NEXT_LINK(&ss->ssl3.hs.cipherSpecs);
        cur_p != &ss->ssl3.hs.cipherSpecs; cur_p = PR_NEXT_LINK(cur_p)) {
     ++ct;
   }
   return ct;
 }
 
-void SSLInt_PrintTls13CipherSpecs(PRFileDesc *fd) {
+void SSLInt_PrintTls13CipherSpecs(const char *label, PRFileDesc *fd) {
   PRCList *cur_p;
 
   sslSocket *ss = ssl_FindSocket(fd);
   if (!ss) {
     return;
   }
 
-  fprintf(stderr, "Cipher specs\n");
+  fprintf(stderr, "Cipher specs for %s\n", label);
   for (cur_p = PR_NEXT_LINK(&ss->ssl3.hs.cipherSpecs);
        cur_p != &ss->ssl3.hs.cipherSpecs; cur_p = PR_NEXT_LINK(cur_p)) {
     ssl3CipherSpec *spec = (ssl3CipherSpec *)cur_p;
-    fprintf(stderr, "  %s\n", spec->phase);
+    fprintf(stderr, "  %s %s refct=%d\n", spec->phase,
+            spec->direction == CipherSpecRead ? "read" : "write", spec->refCt);
   }
 }
 
-/* Force a timer expiry by backdating when the timer was started.
- * We could set the remaining time to 0 but then backoff would not
- * work properly if we decide to test it. */
-void SSLInt_ForceTimerExpiry(PRFileDesc *fd) {
+/* Force a retransmission timer expiry by backdating when the timer
+ * was started. We could set the remaining time to 0 but then backoff
+ * would not work properly if we decide to test it. */
+void SSLInt_ForceRtTimerExpiry(PRFileDesc *fd) {
   sslSocket *ss = ssl_FindSocket(fd);
   if (!ss) {
     return;
   }
 
-  if (!ss->ssl3.hs.rtTimerCb) return;
+  if (!ss->ssl3.hs.rtTimer->cb) return;
 
-  ss->ssl3.hs.rtTimerStarted =
-      PR_IntervalNow() - PR_MillisecondsToInterval(ss->ssl3.hs.rtTimeoutMs + 1);
+  ss->ssl3.hs.rtTimer->started =
+      PR_IntervalNow() -
+      PR_MillisecondsToInterval(ss->ssl3.hs.rtTimer->timeout + 1);
 }
 
 #define CHECK_SECRET(secret)                  \
   if (ss->ssl3.hs.secret) {                   \
     fprintf(stderr, "%s != NULL\n", #secret); \
     return PR_FALSE;                          \
   }
 
@@ -325,16 +327,20 @@ SSLCipherAlgorithm SSLInt_CipherSpecToAl
                                                 ssl3CipherSpec *spec) {
   return spec->cipher_def->calg;
 }
 
 unsigned char *SSLInt_CipherSpecToIv(PRBool isServer, ssl3CipherSpec *spec) {
   return GetKeyingMaterial(isServer, spec)->write_iv;
 }
 
+PRUint16 SSLInt_CipherSpecToEpoch(PRBool isServer, ssl3CipherSpec *spec) {
+  return spec->epoch;
+}
+
 void SSLInt_SetTicketLifetime(uint32_t lifetime) {
   ssl_ticket_lifetime = lifetime;
 }
 
 void SSLInt_SetMaxEarlyDataSize(uint32_t size) {
   ssl_max_early_data_size = size;
 }
 
--- a/gtests/ssl_gtest/libssl_internals.h
+++ b/gtests/ssl_gtest/libssl_internals.h
@@ -20,18 +20,18 @@ SECStatus SSLInt_IncrementClientHandshak
 SECStatus SSLInt_UpdateSSLv2ClientRandom(PRFileDesc *fd, uint8_t *rnd,
                                          size_t rnd_len, uint8_t *msg,
                                          size_t msg_len);
 
 PRBool SSLInt_ExtensionNegotiated(PRFileDesc *fd, PRUint16 ext);
 void SSLInt_ClearSelfEncryptKey();
 void SSLInt_SetSelfEncryptMacKey(PK11SymKey *key);
 PRInt32 SSLInt_CountTls13CipherSpecs(PRFileDesc *fd);
-void SSLInt_PrintTls13CipherSpecs(PRFileDesc *fd);
-void SSLInt_ForceTimerExpiry(PRFileDesc *fd);
+void SSLInt_PrintTls13CipherSpecs(const char *label, PRFileDesc *fd);
+void SSLInt_ForceRtTimerExpiry(PRFileDesc *fd);
 SECStatus SSLInt_SetMTU(PRFileDesc *fd, PRUint16 mtu);
 PRBool SSLInt_CheckSecretsDestroyed(PRFileDesc *fd);
 PRBool SSLInt_DamageClientHsTrafficSecret(PRFileDesc *fd);
 PRBool SSLInt_DamageServerHsTrafficSecret(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);
@@ -42,14 +42,15 @@ SSLKEAType SSLInt_GetKEAType(SSLNamedGro
 
 SECStatus SSLInt_SetCipherSpecChangeFunc(PRFileDesc *fd,
                                          sslCipherSpecChangedFunc func,
                                          void *arg);
 PK11SymKey *SSLInt_CipherSpecToKey(PRBool isServer, ssl3CipherSpec *spec);
 SSLCipherAlgorithm SSLInt_CipherSpecToAlgorithm(PRBool isServer,
                                                 ssl3CipherSpec *spec);
 unsigned char *SSLInt_CipherSpecToIv(PRBool isServer, ssl3CipherSpec *spec);
+PRUint16 SSLInt_CipherSpecToEpoch(PRBool isServer, ssl3CipherSpec *spec);
 void SSLInt_SetTicketLifetime(uint32_t lifetime);
 void SSLInt_SetMaxEarlyDataSize(uint32_t size);
 SECStatus SSLInt_SetSocketMaxEarlyDataSize(PRFileDesc *fd, uint32_t size);
 void SSLInt_RolloverAntiReplay(void);
 
 #endif  // ndef libssl_internals_h_
--- a/gtests/ssl_gtest/ssl_0rtt_unittest.cc
+++ b/gtests/ssl_gtest/ssl_0rtt_unittest.cc
@@ -54,18 +54,17 @@ TEST_P(TlsConnectTls13, ZeroRttApparentR
   ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
   ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3);
   server_->Set0RttEnabled(true);  // So we signal that we allow 0-RTT.
   Connect();
   SendReceive();  // Need to read so that we absorb the session ticket.
   CheckKeys();
 
   Reset();
-  server_->StartConnect();
-  client_->StartConnect();
+  StartConnect();
   client_->Set0RttEnabled(true);
   server_->Set0RttEnabled(true);
   ExpectResumption(RESUME_TICKET);
   ZeroRttSendReceive(true, false);
   Handshake();
   CheckConnected();
   SendReceive();
 }
@@ -136,18 +135,17 @@ TEST_P(TlsZeroRttReplayTest, ZeroRttRepl
 // Test that we don't try to send 0-RTT data when the server sent
 // us a ticket without the 0-RTT flags.
 TEST_P(TlsConnectTls13, ZeroRttOptionsSetLate) {
   ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
   Connect();
   SendReceive();  // Need to read so that we absorb the session ticket.
   CheckKeys(ssl_kea_ecdh, ssl_auth_rsa_sign);
   Reset();
-  server_->StartConnect();
-  client_->StartConnect();
+  StartConnect();
   // Now turn on 0-RTT but too late for the ticket.
   client_->Set0RttEnabled(true);
   server_->Set0RttEnabled(true);
   ExpectResumption(RESUME_TICKET);
   ZeroRttSendReceive(false, false);
   Handshake();
   CheckConnected();
   SendReceive();
@@ -164,18 +162,17 @@ TEST_P(TlsConnectTls13, ZeroRttServerFor
   Handshake();
   CheckConnected();
   SendReceive();
 }
 
 TEST_P(TlsConnectTls13, ZeroRttServerOnly) {
   ExpectResumption(RESUME_NONE);
   server_->Set0RttEnabled(true);
-  client_->StartConnect();
-  server_->StartConnect();
+  StartConnect();
 
   // Client sends ordinary ClientHello.
   client_->Handshake();
 
   // Verify that the server doesn't get data.
   uint8_t buf[100];
   PRInt32 rv = PR_Read(server_->ssl_fd(), buf, sizeof(buf));
   EXPECT_EQ(SECFailure, rv);
@@ -214,37 +211,34 @@ TEST_P(TlsConnectTls13, ZeroRttRejectOld
 // ServerHello on the first handshake.  This results in the server estimating a
 // higher value of the ticket age than the client ultimately provides.  Add a
 // small tolerance for variation in ticket age and the ticket will appear to
 // arrive prematurely, causing the server to reject early data.
 TEST_P(TlsConnectTls13, ZeroRttRejectPrematureTicket) {
   ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
   ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3);
   server_->Set0RttEnabled(true);
-  client_->StartConnect();
-  server_->StartConnect();
+  StartConnect();
   client_->Handshake();  // ClientHello
   server_->Handshake();  // ServerHello
   PR_Sleep(PR_MillisecondsToInterval(10));
   Handshake();  // Remainder of handshake
   CheckConnected();
   SendReceive();
   CheckKeys();
 
   Reset();
   client_->Set0RttEnabled(true);
   server_->Set0RttEnabled(true);
   EXPECT_EQ(SECSuccess, SSL_SetupAntiReplay(1, 1, 3));
   SSLInt_RolloverAntiReplay();  // Make sure to flush replay state.
   SSLInt_RolloverAntiReplay();
   ExpectResumption(RESUME_TICKET);
   ExpectEarlyDataAccepted(false);
-
-  server_->StartConnect();
-  client_->StartConnect();
+  StartConnect();
   ZeroRttSendReceive(true, false);
   Handshake();
   CheckConnected();
   SendReceive();
 }
 
 TEST_P(TlsConnectTls13, TestTls13ZeroRttAlpn) {
   EnableAlpn();
@@ -378,19 +372,17 @@ TEST_P(TlsConnectTls13, TestTls13ZeroRtt
   CheckKeys();
 
   Reset();
   ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
   client_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_2,
                            SSL_LIBRARY_VERSION_TLS_1_3);
   server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_2,
                            SSL_LIBRARY_VERSION_TLS_1_2);
-  client_->StartConnect();
-  server_->StartConnect();
-
+  StartConnect();
   // We will send the early data xtn without sending actual early data. Thus
   // a 1.2 server shouldn't fail until the client sends an alert because the
   // client sends end_of_early_data only after reading the server's flight.
   client_->Set0RttEnabled(true);
 
   client_->ExpectSendAlert(kTlsAlertIllegalParameter);
   if (variant_ == ssl_variant_stream) {
     server_->ExpectSendAlert(kTlsAlertUnexpectedMessage);
@@ -421,19 +413,17 @@ TEST_P(TlsConnectTls13, TestTls13ZeroRtt
   CheckKeys();
 
   Reset();
   ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
   client_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_2,
                            SSL_LIBRARY_VERSION_TLS_1_3);
   server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_2,
                            SSL_LIBRARY_VERSION_TLS_1_2);
-  client_->StartConnect();
-  server_->StartConnect();
-
+  StartConnect();
   // Send the early data xtn in the CH, followed by early app data. The server
   // will fail right after sending its flight, when receiving the early data.
   client_->Set0RttEnabled(true);
   ZeroRttSendReceive(true, false, [this]() {
     client_->ExpectSendAlert(kTlsAlertIllegalParameter);
     if (variant_ == ssl_variant_stream) {
       server_->ExpectSendAlert(kTlsAlertUnexpectedMessage);
     }
--- a/gtests/ssl_gtest/ssl_auth_unittest.cc
+++ b/gtests/ssl_gtest/ssl_auth_unittest.cc
@@ -576,18 +576,17 @@ class EnforceNoActivity : public PacketF
 // In this test, we want to make sure that the server completes its handshake,
 // but the client does not.  Because the AuthCertificate callback blocks and we
 // never call SSL_AuthCertificateComplete(), the client should never report that
 // it has completed the handshake.  Manually call Handshake(), alternating sides
 // between client and server, until the desired state is reached.
 TEST_P(TlsConnectGenericPre13, AuthCompleteDelayed) {
   client_->SetAuthCertificateCallback(AuthCompleteBlock);
 
-  server_->StartConnect();
-  client_->StartConnect();
+  StartConnect();
   client_->Handshake();  // Send ClientHello
   server_->Handshake();  // Send ServerHello
   client_->Handshake();  // Send ClientKeyExchange and Finished
   server_->Handshake();  // Send Finished
   // The server should now report that it is connected
   EXPECT_EQ(TlsAgent::STATE_CONNECTED, server_->state());
 
   // The client should send nothing from here on.
@@ -605,18 +604,17 @@ TEST_P(TlsConnectGenericPre13, AuthCompl
   client_->DeletePacketFilter();
 }
 
 // TLS 1.3 handles a delayed AuthComplete callback differently since the
 // shape of the handshake is different.
 TEST_P(TlsConnectTls13, AuthCompleteDelayed) {
   client_->SetAuthCertificateCallback(AuthCompleteBlock);
 
-  server_->StartConnect();
-  client_->StartConnect();
+  StartConnect();
   client_->Handshake();  // Send ClientHello
   server_->Handshake();  // Send ServerHello
   EXPECT_EQ(TlsAgent::STATE_CONNECTING, client_->state());
   EXPECT_EQ(TlsAgent::STATE_CONNECTING, server_->state());
 
   // The client will send nothing until AuthCertificateComplete is called.
   client_->SetPacketFilter(std::make_shared<EnforceNoActivity>());
   client_->Handshake();
--- a/gtests/ssl_gtest/ssl_damage_unittest.cc
+++ b/gtests/ssl_gtest/ssl_damage_unittest.cc
@@ -24,18 +24,17 @@ extern "C" {
 
 namespace nss_test {
 
 TEST_F(TlsConnectTest, DamageSecretHandleClientFinished) {
   client_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_1,
                            SSL_LIBRARY_VERSION_TLS_1_3);
   server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_1,
                            SSL_LIBRARY_VERSION_TLS_1_3);
-  server_->StartConnect();
-  client_->StartConnect();
+  StartConnect();
   client_->Handshake();
   server_->Handshake();
   std::cerr << "Damaging HS secret" << std::endl;
   SSLInt_DamageClientHsTrafficSecret(server_->ssl_fd());
   client_->Handshake();
   // The client thinks it has connected.
   EXPECT_EQ(TlsAgent::STATE_CONNECTED, client_->state());
 
@@ -84,18 +83,17 @@ TEST_P(TlsConnectGeneric, DamageClientSi
   client_->SetupClientAuth();
   server_->RequestClientAuth(true);
   auto filter =
       std::make_shared<TlsLastByteDamager>(kTlsHandshakeCertificateVerify);
   client_->SetTlsRecordFilter(filter);
   server_->ExpectSendAlert(kTlsAlertDecryptError);
   // Do these handshakes by hand to avoid race condition on
   // the client processing the server's alert.
-  client_->StartConnect();
-  server_->StartConnect();
+  StartConnect();
   client_->Handshake();
   server_->Handshake();
   client_->Handshake();
   server_->Handshake();
   EXPECT_EQ(version_ >= SSL_LIBRARY_VERSION_TLS_1_3
                 ? TlsAgent::STATE_CONNECTED
                 : TlsAgent::STATE_CONNECTING,
             client_->state());
--- a/gtests/ssl_gtest/ssl_drop_unittest.cc
+++ b/gtests/ssl_gtest/ssl_drop_unittest.cc
@@ -1,75 +1,616 @@
 /* -*- 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/. */
 
 #include "secerr.h"
 #include "ssl.h"
+#include "sslexp.h"
 
 extern "C" {
 // This is not something that should make you happy.
 #include "libssl_internals.h"
 }
 
 #include "gtest_utils.h"
 #include "scoped_ptrs.h"
 #include "tls_connect.h"
 #include "tls_filter.h"
 #include "tls_parser.h"
 
 namespace nss_test {
 
-TEST_P(TlsConnectDatagram, DropClientFirstFlightOnce) {
+TEST_P(TlsConnectDatagramPre13, DropClientFirstFlightOnce) {
   client_->SetPacketFilter(std::make_shared<SelectiveDropFilter>(0x1));
   Connect();
   SendReceive();
 }
 
-TEST_P(TlsConnectDatagram, DropServerFirstFlightOnce) {
+TEST_P(TlsConnectDatagramPre13, DropServerFirstFlightOnce) {
   server_->SetPacketFilter(std::make_shared<SelectiveDropFilter>(0x1));
   Connect();
   SendReceive();
 }
 
 // This drops the first transmission from both the client and server of all
 // flights that they send.  Note: In DTLS 1.3, the shorter handshake means that
 // this will also drop some application data, so we can't call SendReceive().
-TEST_P(TlsConnectDatagram, DropAllFirstTransmissions) {
+TEST_P(TlsConnectDatagramPre13, DropAllFirstTransmissions) {
   client_->SetPacketFilter(std::make_shared<SelectiveDropFilter>(0x15));
   server_->SetPacketFilter(std::make_shared<SelectiveDropFilter>(0x5));
   Connect();
 }
 
 // This drops the server's first flight three times.
-TEST_P(TlsConnectDatagram, DropServerFirstFlightThrice) {
+TEST_P(TlsConnectDatagramPre13, DropServerFirstFlightThrice) {
   server_->SetPacketFilter(std::make_shared<SelectiveDropFilter>(0x7));
   Connect();
 }
 
 // This drops the client's second flight once
-TEST_P(TlsConnectDatagram, DropClientSecondFlightOnce) {
+TEST_P(TlsConnectDatagramPre13, DropClientSecondFlightOnce) {
   client_->SetPacketFilter(std::make_shared<SelectiveDropFilter>(0x2));
   Connect();
 }
 
 // This drops the client's second flight three times.
-TEST_P(TlsConnectDatagram, DropClientSecondFlightThrice) {
+TEST_P(TlsConnectDatagramPre13, DropClientSecondFlightThrice) {
   client_->SetPacketFilter(std::make_shared<SelectiveDropFilter>(0xe));
   Connect();
 }
 
 // This drops the server's second flight three times.
-TEST_P(TlsConnectDatagram, DropServerSecondFlightThrice) {
+TEST_P(TlsConnectDatagramPre13, DropServerSecondFlightThrice) {
   server_->SetPacketFilter(std::make_shared<SelectiveDropFilter>(0xe));
   Connect();
 }
 
+class TlsDropDatagram13 : public TlsConnectDatagram13 {
+ public:
+  TlsDropDatagram13()
+      : client_filters_(),
+        server_filters_(),
+        expected_client_acks_(0),
+        expected_server_acks_(1) {}
+
+  void SetUp() {
+    TlsConnectDatagram13::SetUp();
+    ConfigureSessionCache(RESUME_NONE, RESUME_NONE);
+    SetFilters();
+  }
+
+  void SetFilters() {
+    EnsureTlsSetup();
+    client_->SetPacketFilter(client_filters_.chain_);
+    client_filters_.ack_->SetAgent(client_.get());
+    client_filters_.ack_->EnableDecryption();
+    server_->SetPacketFilter(server_filters_.chain_);
+    server_filters_.ack_->SetAgent(server_.get());
+    server_filters_.ack_->EnableDecryption();
+  }
+
+  void WaitTimeout(const std::shared_ptr<TlsAgent>& agent, uint32_t minTo) {
+    PRIntervalTime timeout;
+    ASSERT_EQ(SECSuccess, DTLS_GetHandshakeTimeout(agent->ssl_fd(), &timeout));
+    ASSERT_GE(PR_MillisecondsToInterval(minTo), timeout);
+    PR_Sleep(timeout);
+  }
+
+  void HandshakeAndAck(const std::shared_ptr<TlsAgent>& agent) {
+    agent->Handshake();  // Read flight.
+    WaitTimeout(agent, DTLS_RETRANSMIT_INITIAL_MS);
+    agent->Handshake();  // Generate ACK.
+  }
+
+  void ShrinkPostServerHelloMtu() {
+    // Abuse the custom extension mechanism to modify the MTU so that the
+    // Certificate message is split into two pieces.
+    ASSERT_EQ(
+        SECSuccess,
+        SSL_InstallExtensionHooks(
+            server_->ssl_fd(), 1,
+            [](PRFileDesc* fd, SSLHandshakeType message, PRUint8* data,
+               unsigned int* len, unsigned int maxLen, void* arg) -> PRBool {
+              SSLInt_SetMTU(fd, 384);  // Splits the certificate.
+              return PR_FALSE;
+            },
+            nullptr,
+            [](PRFileDesc* fd, SSLHandshakeType message, const PRUint8* data,
+               unsigned int len, SSLAlertDescription* alert,
+               void* arg) -> SECStatus { return SECSuccess; },
+            nullptr));
+  }
+
+ protected:
+  class DropAckChain {
+   public:
+    DropAckChain()
+        : records_(std::make_shared<TlsRecordRecorder>()),
+          ack_(std::make_shared<TlsRecordRecorder>(content_ack)),
+          drop_(std::make_shared<SelectiveRecordDropFilter>(0, false)),
+          chain_(std::make_shared<ChainedPacketFilter>(
+              ChainedPacketFilterInit({records_, ack_, drop_}))) {}
+
+    const TlsRecord& record(size_t i) const { return records_->record(i); }
+
+    std::shared_ptr<TlsRecordRecorder> records_;
+    std::shared_ptr<TlsRecordRecorder> ack_;
+    std::shared_ptr<SelectiveRecordDropFilter> drop_;
+    std::shared_ptr<PacketFilter> chain_;
+  };
+
+  void CheckAcks(const DropAckChain& chain, size_t index,
+                 std::vector<uint64_t> acks) {
+    const DataBuffer& buf = chain.ack_->record(index).buffer;
+    size_t offset = 0;
+
+    EXPECT_EQ(acks.size() * 8, buf.len());
+    if ((acks.size() * 8) != buf.len()) {
+      while (offset < buf.len()) {
+        uint64_t ack;
+        ASSERT_TRUE(buf.Read(offset, 8, &ack));
+        offset += 8;
+        std::cerr << "Ack=0x" << std::hex << ack << std::dec << std::endl;
+      }
+      return;
+    }
+
+    for (size_t i = 0; i < acks.size(); ++i) {
+      uint64_t a = acks[i];
+      uint64_t ack;
+      ASSERT_TRUE(buf.Read(offset, 8, &ack));
+      offset += 8;
+      if (a != ack) {
+        ADD_FAILURE() << "Wrong ack " << i << " expected=0x" << std::hex << a
+                      << " got=0x" << ack << std::dec;
+      }
+    }
+  }
+
+  void CheckedHandshakeSendReceive() {
+    Handshake();
+    CheckPostHandshake();
+  }
+
+  void CheckPostHandshake() {
+    CheckConnected();
+    SendReceive();
+    EXPECT_EQ(expected_client_acks_, client_filters_.ack_->count());
+    EXPECT_EQ(expected_server_acks_, server_filters_.ack_->count());
+  }
+
+ protected:
+  DropAckChain client_filters_;
+  DropAckChain server_filters_;
+  size_t expected_client_acks_;
+  size_t expected_server_acks_;
+};
+
+// All of these tests produce a minimum one ACK, from the server
+// to the client upon receiving the client Finished.
+// Dropping complete first and second flights does not produce
+// ACKs
+TEST_F(TlsDropDatagram13, DropClientFirstFlightOnce) {
+  client_filters_.drop_->Enable(1);
+  StartConnect();
+  client_->Handshake();
+  server_->Handshake();
+  CheckedHandshakeSendReceive();
+  CheckAcks(server_filters_, 0, {0x0002000000000000ULL});
+}
+
+TEST_F(TlsDropDatagram13, DropServerFirstFlightOnce) {
+  server_filters_.drop_->Enable(0xff);
+  StartConnect();
+  client_->Handshake();
+  // Send the first flight, all dropped.
+  server_->Handshake();
+  server_filters_.drop_->Disable();
+  CheckedHandshakeSendReceive();
+  CheckAcks(server_filters_, 0, {0x0002000000000000ULL});
+}
+
+// Dropping the server's first record also does not produce
+// an ACK because the next record is ignored.
+// TODO(ekr@rtfm.com): We should generate an empty ACK.
+TEST_F(TlsDropDatagram13, DropServerFirstRecordOnce) {
+  server_filters_.drop_->Enable(1);
+  StartConnect();
+  client_->Handshake();
+  server_->Handshake();
+  Handshake();
+  CheckedHandshakeSendReceive();
+  CheckAcks(server_filters_, 0, {0x0002000000000000ULL});
+}
+
+// Dropping the second packet of the server's flight should
+// produce an ACK.
+TEST_F(TlsDropDatagram13, DropServerSecondRecordOnce) {
+  server_filters_.drop_->Enable(2);
+  StartConnect();
+  client_->Handshake();
+  server_->Handshake();
+  HandshakeAndAck(client_);
+  expected_client_acks_ = 1;
+  CheckedHandshakeSendReceive();
+  CheckAcks(client_filters_, 0, {0});
+  CheckAcks(server_filters_, 0, {0x0002000000000000ULL});
+}
+
+// Drop the server ACK and verify that the client retransmits
+// the ClientHello.
+TEST_F(TlsDropDatagram13, DropServerAckOnce) {
+  StartConnect();
+  client_->Handshake();
+  server_->Handshake();
+  // At this point the server has sent it's first flight,
+  // so make it drop the ACK.
+  server_filters_.drop_->Enable(1);
+  client_->Handshake();  // Send the client Finished.
+  server_->Handshake();  // Receive the Finished and send the ACK.
+  EXPECT_EQ(TlsAgent::STATE_CONNECTED, client_->state());
+  EXPECT_EQ(TlsAgent::STATE_CONNECTED, server_->state());
+  // Wait for the DTLS timeout to make sure we retransmit the
+  // Finished.
+  WaitTimeout(client_, DTLS_RETRANSMIT_INITIAL_MS * 2);
+  client_->Handshake();  // Retransmit the Finished.
+  server_->Handshake();  // Read the Finished and send an ACK.
+  uint8_t buf[1];
+  PRInt32 rv = PR_Read(client_->ssl_fd(), buf, sizeof(buf));
+  expected_server_acks_ = 2;
+  EXPECT_GT(0, rv);
+  EXPECT_EQ(PR_WOULD_BLOCK_ERROR, PORT_GetError());
+  CheckPostHandshake();
+  // There should be two copies of the finished ACK
+  CheckAcks(server_filters_, 0, {0x0002000000000000ULL});
+  CheckAcks(server_filters_, 0, {0x0002000000000000ULL});
+}
+
+// Drop the client certificate verify.
+TEST_F(TlsDropDatagram13, DropClientCertVerify) {
+  StartConnect();
+  client_->SetupClientAuth();
+  server_->RequestClientAuth(true);
+  client_->Handshake();
+  server_->Handshake();
+  // Have the client drop Cert Verify
+  client_filters_.drop_->Enable(2);
+  expected_server_acks_ = 2;
+  CheckedHandshakeSendReceive();
+  // Ack of the Cert.
+  CheckAcks(server_filters_, 0, {0x0002000000000000ULL});
+  // Ack of the whole client handshake.
+  CheckAcks(
+      server_filters_, 1,
+      {0x0002000000000000ULL,  // CH (we drop everything after this on client)
+       0x0002000000000003ULL,  // CT (2)
+       0x0002000000000004ULL}  // FIN (2)
+      );
+}
+
+// Shrink the MTU down so that certs get split and drop the first piece.
+TEST_F(TlsDropDatagram13, DropFirstHalfOfServerCertificate) {
+  server_filters_.drop_->Enable(4);
+  StartConnect();
+  ShrinkPostServerHelloMtu();
+  client_->Handshake();
+  server_->Handshake();
+  // Check that things got split.
+  EXPECT_EQ(6UL,
+            server_filters_.records_->count());  // SH, EE, CT1, CT2, CV, FIN
+  size_t ct1_size = server_filters_.record(2).buffer.len();
+  server_filters_.records_->Clear();
+  expected_client_acks_ = 1;
+  HandshakeAndAck(client_);
+  server_->Handshake();                               // Retransmit
+  EXPECT_EQ(3UL, server_filters_.records_->count());  // CT2, CV, FIN
+  // Check that the first record is CT1 (which is identical to the same
+  // as the previous CT1).
+  EXPECT_EQ(ct1_size, server_filters_.record(0).buffer.len());
+  CheckedHandshakeSendReceive();
+  CheckAcks(client_filters_, 0, {0,                      // SH
+                                 0x0002000000000000ULL,  // EE
+                                 0x0002000000000002ULL}  // CT2
+            );
+  CheckAcks(server_filters_, 0, {0x0002000000000000ULL});
+}
+
+// Shrink the MTU down so that certs get split and drop the second piece.
+TEST_F(TlsDropDatagram13, DropSecondHalfOfServerCertificate) {
+  server_filters_.drop_->Enable(8);
+  StartConnect();
+  ShrinkPostServerHelloMtu();
+  client_->Handshake();
+  server_->Handshake();
+  // Check that things got split.
+  EXPECT_EQ(6UL,
+            server_filters_.records_->count());  // SH, EE, CT1, CT2, CV, FIN
+  size_t ct1_size = server_filters_.record(3).buffer.len();
+  server_filters_.records_->Clear();
+  expected_client_acks_ = 1;
+  HandshakeAndAck(client_);
+  server_->Handshake();                               // Retransmit
+  EXPECT_EQ(3UL, server_filters_.records_->count());  // CT1, CV, FIN
+  // Check that the first record is CT1
+  EXPECT_EQ(ct1_size, server_filters_.record(0).buffer.len());
+  CheckedHandshakeSendReceive();
+  CheckAcks(client_filters_, 0, {
+                                    0,                      // SH
+                                    0x0002000000000000ULL,  // EE
+                                    0x0002000000000001ULL,  // CT1
+                                });
+  CheckAcks(server_filters_, 0, {0x0002000000000000ULL});
+}
+
+TEST_F(TlsDropDatagram13, NoDropsDuringZeroRtt) {
+  SetupForZeroRtt();
+  SetFilters();
+  std::cerr << "Starting second handshake" << std::endl;
+  client_->Set0RttEnabled(true);
+  server_->Set0RttEnabled(true);
+  ExpectResumption(RESUME_TICKET);
+  ZeroRttSendReceive(true, true);
+  Handshake();
+  ExpectEarlyDataAccepted(true);
+  CheckConnected();
+  SendReceive();
+  CheckAcks(server_filters_, 0, {0x0002000000000000ULL});
+}
+
+TEST_F(TlsDropDatagram13, DropEEDuringZeroRtt) {
+  SetupForZeroRtt();
+  SetFilters();
+  std::cerr << "Starting second handshake" << std::endl;
+  client_->Set0RttEnabled(true);
+  server_->Set0RttEnabled(true);
+  ExpectResumption(RESUME_TICKET);
+  server_filters_.drop_->Enable(2);
+  ZeroRttSendReceive(true, true);
+  HandshakeAndAck(client_);
+  Handshake();
+  ExpectEarlyDataAccepted(true);
+  CheckConnected();
+  SendReceive();
+  CheckAcks(client_filters_, 0, {0});
+  CheckAcks(server_filters_, 0, {0x0002000000000000ULL});
+}
+
+class TlsReorderDatagram13 : public TlsDropDatagram13 {
+ public:
+  TlsReorderDatagram13() {}
+
+  // Send records from the records buffer in the given order.
+  void ReSend(TlsAgent::Role side, std::vector<size_t> indices) {
+    std::shared_ptr<TlsAgent> agent;
+    std::shared_ptr<TlsRecordRecorder> records;
+
+    if (side == TlsAgent::CLIENT) {
+      agent = client_;
+      records = client_filters_.records_;
+    } else {
+      agent = server_;
+      records = server_filters_.records_;
+    }
+
+    for (auto i : indices) {
+      agent->SendRecordDirect(records->record(i));
+    }
+  }
+};
+
+// Reorder the server records so that EE comes at the end
+// of the flight and will still produce an ACK.
+TEST_F(TlsDropDatagram13, ReorderServerEE) {
+  server_filters_.drop_->Enable(2);
+  StartConnect();
+  client_->Handshake();
+  server_->Handshake();
+  // We dropped EE, now reinject.
+  server_->SendRecordDirect(server_filters_.record(1));
+  expected_client_acks_ = 1;
+  HandshakeAndAck(client_);
+  CheckedHandshakeSendReceive();
+  CheckAcks(client_filters_, 0, {
+                                    0,                   // SH
+                                    0x0002000000000000,  // EE
+                                });
+  CheckAcks(server_filters_, 0, {0x0002000000000000ULL});
+}
+
+// The client sends an out of order non-handshake message
+// but with the handshake key.
+class TlsSendCipherSpecCapturer {
+ public:
+  TlsSendCipherSpecCapturer(std::shared_ptr<TlsAgent>& agent)
+      : is_server_(agent->role() == TlsAgent::SERVER), send_cipher_specs_() {
+    SSLInt_SetCipherSpecChangeFunc(agent->ssl_fd(), CipherSpecChanged,
+                                   (void*)this);
+  }
+
+  std::shared_ptr<TlsCipherSpec> spec(size_t i) {
+    if (i >= send_cipher_specs_.size()) {
+      return nullptr;
+    }
+    return send_cipher_specs_[i];
+  }
+
+ private:
+  static void CipherSpecChanged(void* arg, PRBool sending,
+                                ssl3CipherSpec* newSpec) {
+    if (!sending) {
+      return;
+    }
+
+    auto self = static_cast<TlsSendCipherSpecCapturer*>(arg);
+
+    auto spec = std::make_shared<TlsCipherSpec>();
+    bool ret =
+        spec->Init(SSLInt_CipherSpecToEpoch(self->is_server_, newSpec),
+                   SSLInt_CipherSpecToAlgorithm(self->is_server_, newSpec),
+                   SSLInt_CipherSpecToKey(self->is_server_, newSpec),
+                   SSLInt_CipherSpecToIv(self->is_server_, newSpec));
+    EXPECT_EQ(true, ret);
+    self->send_cipher_specs_.push_back(spec);
+  }
+
+  bool is_server_;
+  std::vector<std::shared_ptr<TlsCipherSpec>> send_cipher_specs_;
+};
+
+TEST_F(TlsDropDatagram13, SendOutOfOrderAppWithHandshakeKey) {
+  StartConnect();
+  TlsSendCipherSpecCapturer capturer(client_);
+  client_->Handshake();
+  server_->Handshake();
+  client_->Handshake();
+  EXPECT_EQ(TlsAgent::STATE_CONNECTED, client_->state());
+  server_->Handshake();
+  EXPECT_EQ(TlsAgent::STATE_CONNECTED, server_->state());
+  // After the client sends Finished, inject an app data record
+  // with the handshake key. This should produce an alert.
+  uint8_t buf[] = {'a', 'b', 'c'};
+  auto spec = capturer.spec(0);
+  ASSERT_NE(nullptr, spec.get());
+  ASSERT_EQ(2, spec->epoch());
+  ASSERT_TRUE(client_->SendEncryptedRecord(spec, 0xfeff, 0x0002000000000002,
+                                           kTlsApplicationDataType,
+                                           DataBuffer(buf, sizeof(buf))));
+
+  // Now have the server consume the bogus message.
+  server_->ExpectSendAlert(illegal_parameter, kTlsAlertFatal);
+  server_->Handshake();
+  EXPECT_EQ(TlsAgent::STATE_ERROR, server_->state());
+  EXPECT_EQ(SSL_ERROR_RX_UNKNOWN_RECORD_TYPE, PORT_GetError());
+}
+
+TEST_F(TlsDropDatagram13, SendOutOfOrderHsNonsenseWithHandshakeKey) {
+  StartConnect();
+  TlsSendCipherSpecCapturer capturer(client_);
+  client_->Handshake();
+  server_->Handshake();
+  client_->Handshake();
+  EXPECT_EQ(TlsAgent::STATE_CONNECTED, client_->state());
+  server_->Handshake();
+  EXPECT_EQ(TlsAgent::STATE_CONNECTED, server_->state());
+  // Inject a new bogus handshake record, which the server responds
+  // to by just ACKing the original one (we ignore the contents).
+  uint8_t buf[] = {'a', 'b', 'c'};
+  auto spec = capturer.spec(0);
+  ASSERT_NE(nullptr, spec.get());
+  ASSERT_EQ(2, spec->epoch());
+  ASSERT_TRUE(client_->SendEncryptedRecord(spec, 0xfeff, 0x0002000000000002,
+                                           kTlsHandshakeType,
+                                           DataBuffer(buf, sizeof(buf))));
+  server_->Handshake();
+  EXPECT_EQ(2UL, server_filters_.ack_->count());
+  CheckAcks(server_filters_, 0, {0x0002000000000000ULL});
+  CheckAcks(server_filters_, 1, {0x0002000000000000ULL});
+}
+
+// Shrink the MTU down so that certs get split and then swap the first and
+// second pieces of the server certificate.
+TEST_F(TlsReorderDatagram13, ReorderServerCertificate) {
+  StartConnect();
+  ShrinkPostServerHelloMtu();
+  client_->Handshake();
+  // Drop the entire handshake flight so we can reorder.
+  server_filters_.drop_->Enable(255);
+  server_->Handshake();
+  // Check that things got split.
+  EXPECT_EQ(6UL,
+            server_filters_.records_->count());  // CH, EE, CT1, CT2, CV, FIN
+  // Now re-send things in a different order.
+  ReSend(TlsAgent::SERVER, std::vector<size_t>{0, 1, 3, 2, 4, 5});
+  // Clear.
+  server_filters_.drop_->Disable();
+  server_filters_.records_->Clear();
+  // Wait for client to send ACK.
+  WaitTimeout(client_, DTLS_RETRANSMIT_INITIAL_MS);
+  CheckedHandshakeSendReceive();
+  EXPECT_EQ(2UL, server_filters_.records_->count());  // ACK + Data
+  CheckAcks(server_filters_, 0, {0x0002000000000000ULL});
+}
+
+TEST_F(TlsReorderDatagram13, DataAfterEOEDDuringZeroRtt) {
+  SetupForZeroRtt();
+  SetFilters();
+  std::cerr << "Starting second handshake" << std::endl;
+  client_->Set0RttEnabled(true);
+  server_->Set0RttEnabled(true);
+  ExpectResumption(RESUME_TICKET);
+  // Send the client's first flight of zero RTT data.
+  ZeroRttSendReceive(true, true);
+  // Now send another client application data record but
+  // capture it.
+  client_filters_.records_->Clear();
+  client_filters_.drop_->Enable(255);
+  const char* k0RttData = "123456";
+  const PRInt32 k0RttDataLen = static_cast<PRInt32>(strlen(k0RttData));
+  PRInt32 rv =
+      PR_Write(client_->ssl_fd(), k0RttData, k0RttDataLen);  // 0-RTT write.
+  EXPECT_EQ(k0RttDataLen, rv);
+  EXPECT_EQ(1UL, client_filters_.records_->count());  // data
+  server_->Handshake();
+  client_->Handshake();
+  ExpectEarlyDataAccepted(true);
+  // The server still hasn't received anything at this point.
+  EXPECT_EQ(3UL, client_filters_.records_->count());  // data, EOED, FIN
+  EXPECT_EQ(TlsAgent::STATE_CONNECTED, client_->state());
+  EXPECT_EQ(TlsAgent::STATE_CONNECTING, server_->state());
+  // Now re-send the client's messages: EOED, data, FIN
+  ReSend(TlsAgent::CLIENT, std::vector<size_t>({1, 0, 2}));
+  server_->Handshake();
+  CheckConnected();
+  CheckAcks(server_filters_, 0, {0x0002000000000000ULL});
+  uint8_t buf[8];
+  rv = PR_Read(server_->ssl_fd(), buf, sizeof(buf));
+  EXPECT_EQ(-1, rv);
+  EXPECT_EQ(PR_WOULD_BLOCK_ERROR, PORT_GetError());
+}
+
+TEST_F(TlsReorderDatagram13, DataAfterFinDuringZeroRtt) {
+  SetupForZeroRtt();
+  SetFilters();
+  std::cerr << "Starting second handshake" << std::endl;
+  client_->Set0RttEnabled(true);
+  server_->Set0RttEnabled(true);
+  ExpectResumption(RESUME_TICKET);
+  // Send the client's first flight of zero RTT data.
+  ZeroRttSendReceive(true, true);
+  // Now send another client application data record but
+  // capture it.
+  client_filters_.records_->Clear();
+  client_filters_.drop_->Enable(255);
+  const char* k0RttData = "123456";
+  const PRInt32 k0RttDataLen = static_cast<PRInt32>(strlen(k0RttData));
+  PRInt32 rv =
+      PR_Write(client_->ssl_fd(), k0RttData, k0RttDataLen);  // 0-RTT write.
+  EXPECT_EQ(k0RttDataLen, rv);
+  EXPECT_EQ(1UL, client_filters_.records_->count());  // data
+  server_->Handshake();
+  client_->Handshake();
+  ExpectEarlyDataAccepted(true);
+  // The server still hasn't received anything at this point.
+  EXPECT_EQ(3UL, client_filters_.records_->count());  // EOED, FIN, Data
+  EXPECT_EQ(TlsAgent::STATE_CONNECTED, client_->state());
+  EXPECT_EQ(TlsAgent::STATE_CONNECTING, server_->state());
+  // Now re-send the client's messages: EOED, FIN, Data
+  ReSend(TlsAgent::CLIENT, std::vector<size_t>({1, 2, 0}));
+  server_->Handshake();
+  CheckConnected();
+  CheckAcks(server_filters_, 0, {0x0002000000000000ULL});
+  uint8_t buf[8];
+  rv = PR_Read(server_->ssl_fd(), buf, sizeof(buf));
+  EXPECT_EQ(-1, rv);
+  EXPECT_EQ(PR_WOULD_BLOCK_ERROR, PORT_GetError());
+}
+
 static void GetCipherAndLimit(uint16_t version, uint16_t* cipher,
                               uint64_t* limit = nullptr) {
   uint64_t l;
   if (!limit) limit = &l;
 
   if (version < SSL_LIBRARY_VERSION_TLS_1_2) {
     *cipher = TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA;
     *limit = 0x5aULL << 28;
@@ -106,17 +647,16 @@ class TlsConnectDatagram12Plus : public 
 
 // This simulates missing a window's worth of packets.
 TEST_P(TlsConnectDatagram12Plus, MissAWindow) {
   EnsureTlsSetup();
   uint16_t cipher;
   GetCipherAndLimit(version_, &cipher);
   server_->EnableSingleCipher(cipher);
   Connect();
-
   EXPECT_EQ(SECSuccess, SSLInt_AdvanceWriteSeqByAWindow(client_->ssl_fd(), 0));
   SendReceive();
 }
 
 TEST_P(TlsConnectDatagram12Plus, MissAWindowAndOne) {
   EnsureTlsSetup();
   uint16_t cipher;
   GetCipherAndLimit(version_, &cipher);
@@ -124,10 +664,12 @@ TEST_P(TlsConnectDatagram12Plus, MissAWi
   Connect();
 
   EXPECT_EQ(SECSuccess, SSLInt_AdvanceWriteSeqByAWindow(client_->ssl_fd(), 1));
   SendReceive();
 }
 
 INSTANTIATE_TEST_CASE_P(Datagram12Plus, TlsConnectDatagram12Plus,
                         TlsConnectTestBase::kTlsV12Plus);
+INSTANTIATE_TEST_CASE_P(DatagramPre13, TlsConnectDatagramPre13,
+                        TlsConnectTestBase::kTlsV11V12);
 
 }  // namespace nss_test
--- a/gtests/ssl_gtest/ssl_extension_unittest.cc
+++ b/gtests/ssl_gtest/ssl_extension_unittest.cc
@@ -153,18 +153,17 @@ class TlsExtensionTestBase : public TlsC
                                    PRInt32 server_error) {
     static const std::vector<SSLNamedGroup> client_groups = {
         ssl_grp_ec_secp384r1, ssl_grp_ec_curve25519};
     static const std::vector<SSLNamedGroup> server_groups = {
         ssl_grp_ec_curve25519, ssl_grp_ec_secp384r1};
     client_->ConfigNamedGroups(client_groups);
     server_->ConfigNamedGroups(server_groups);
     EnsureTlsSetup();
-    client_->StartConnect();
-    server_->StartConnect();
+    StartConnect();
     client_->Handshake();  // Send ClientHello
     server_->Handshake();  // Send HRR.
     client_->SetPacketFilter(std::make_shared<TlsExtensionDropper>(type));
     Handshake();
     client_->CheckErrorCode(client_error);
     server_->CheckErrorCode(server_error);
   }
 };
@@ -993,18 +992,17 @@ class TlsBogusExtensionTest13 : public T
       ConnectExpectAlert(client_, kTlsAlertUnsupportedExtension);
       return;
     }
 
     FailWithAlert(kTlsAlertUnsupportedExtension);
   }
 
   void FailWithAlert(uint8_t alert) {
-    client_->StartConnect();
-    server_->StartConnect();
+    StartConnect();
     client_->Handshake();  // ClientHello
     server_->Handshake();  // ServerHello
 
     client_->ExpectSendAlert(alert);
     client_->Handshake();
     if (variant_ == ssl_variant_stream) {
       server_->ExpectSendAlert(kTlsAlertBadRecordMac);
     }
--- a/gtests/ssl_gtest/ssl_hrr_unittest.cc
+++ b/gtests/ssl_gtest/ssl_hrr_unittest.cc
@@ -353,18 +353,17 @@ TEST_P(TlsConnectTls13, RetryCallbackRet
                                                       capture_key_share};
   server_->SetPacketFilter(std::make_shared<ChainedPacketFilter>(chain));
 
   size_t cb_called = 0;
   EXPECT_EQ(SECSuccess, SSL_HelloRetryRequestCallback(server_->ssl_fd(),
                                                       RetryHello, &cb_called));
 
   // Do the first message exchange.
-  client_->StartConnect();
-  server_->StartConnect();
+  StartConnect();
   client_->Handshake();
   server_->Handshake();
 
   EXPECT_EQ(1U, cb_called) << "callback should be called once here";
   EXPECT_LT(0U, capture_hrr->buffer().len()) << "HelloRetryRequest expected";
   EXPECT_FALSE(capture_key_share->captured())
       << "no key_share extension expected";
 
@@ -744,18 +743,17 @@ TEST_F(TlsConnectTest, Select12AfterHell
   server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_2,
                            SSL_LIBRARY_VERSION_TLS_1_3);
   static const std::vector<SSLNamedGroup> client_groups = {
       ssl_grp_ec_secp256r1, ssl_grp_ec_secp521r1};
   client_->ConfigNamedGroups(client_groups);
   static const std::vector<SSLNamedGroup> server_groups = {
       ssl_grp_ec_secp384r1, ssl_grp_ec_secp521r1};
   server_->ConfigNamedGroups(server_groups);
-  client_->StartConnect();
-  server_->StartConnect();
+  StartConnect();
 
   client_->Handshake();
   server_->Handshake();
 
   // Here we replace the TLS server with one that does TLS 1.2 only.
   // This will happily send the client a TLS 1.2 ServerHello.
   server_.reset(new TlsAgent(server_->name(), TlsAgent::SERVER, variant_));
   client_->SetPeer(server_);
--- a/gtests/ssl_gtest/ssl_loopback_unittest.cc
+++ b/gtests/ssl_gtest/ssl_loopback_unittest.cc
@@ -120,18 +120,17 @@ TEST_P(TlsConnectGenericPre13, CaptureAl
 }
 
 // In TLS 1.3, the server can't read the client alert.
 TEST_P(TlsConnectTls13, CaptureAlertClient) {
   server_->SetPacketFilter(std::make_shared<HelloTruncator>());
   auto alert_recorder = std::make_shared<TlsAlertRecorder>();
   client_->SetPacketFilter(alert_recorder);
 
-  server_->StartConnect();
-  client_->StartConnect();
+  StartConnect();
 
   client_->Handshake();
   client_->ExpectSendAlert(kTlsAlertDecodeError);
   server_->Handshake();
   client_->Handshake();
   if (variant_ == ssl_variant_stream) {
     // DTLS just drops the alert it can't decrypt.
     server_->ExpectSendAlert(kTlsAlertBadRecordMac);
@@ -334,18 +333,18 @@ TEST_P(TlsConnectGeneric, ConnectWithCom
                 variant_ != ssl_variant_datagram,
             client_->is_compressed());
   SendReceive();
 }
 
 TEST_P(TlsConnectDatagram, TestDtlsHolddownExpiry) {
   Connect();
   std::cerr << "Expiring holddown timer\n";
-  SSLInt_ForceTimerExpiry(client_->ssl_fd());
-  SSLInt_ForceTimerExpiry(server_->ssl_fd());
+  SSLInt_ForceRtTimerExpiry(client_->ssl_fd());
+  SSLInt_ForceRtTimerExpiry(server_->ssl_fd());
   SendReceive();
   if (version_ >= SSL_LIBRARY_VERSION_TLS_1_3) {
     // One for send, one for receive.
     EXPECT_EQ(2, SSLInt_CountTls13CipherSpecs(client_->ssl_fd()));
   }
 }
 
 class TlsPreCCSHeaderInjector : public TlsRecordFilter {
@@ -370,18 +369,17 @@ TEST_P(TlsConnectStreamPre13, ClientFini
   client_->SetPacketFilter(std::make_shared<TlsPreCCSHeaderInjector>());
   ConnectExpectAlert(server_, kTlsAlertUnexpectedMessage);
   client_->CheckErrorCode(SSL_ERROR_HANDSHAKE_UNEXPECTED_ALERT);
   server_->CheckErrorCode(SSL_ERROR_RX_UNEXPECTED_CHANGE_CIPHER);
 }
 
 TEST_P(TlsConnectStreamPre13, ServerFinishedHeaderBeforeCCS) {
   server_->SetPacketFilter(std::make_shared<TlsPreCCSHeaderInjector>());
-  client_->StartConnect();
-  server_->StartConnect();
+  StartConnect();
   ExpectAlert(client_, kTlsAlertUnexpectedMessage);
   Handshake();
   EXPECT_EQ(TlsAgent::STATE_ERROR, client_->state());
   client_->CheckErrorCode(SSL_ERROR_RX_UNEXPECTED_CHANGE_CIPHER);
   EXPECT_EQ(TlsAgent::STATE_CONNECTED, server_->state());
   server_->Handshake();  // Make sure alert is consumed.
 }
 
@@ -402,18 +400,17 @@ TEST_P(TlsConnectTls13, AlertWrongLevel)
   SSLInt_SendAlert(server_->ssl_fd(), kTlsAlertWarning,
                    kTlsAlertUnexpectedMessage);
   client_->ExpectReadWriteError();
   client_->WaitForErrorCode(SSL_ERROR_HANDSHAKE_UNEXPECTED_ALERT, 2000);
 }
 
 TEST_F(TlsConnectStreamTls13, Tls13FailedWriteSecondFlight) {
   EnsureTlsSetup();
-  client_->StartConnect();
-  server_->StartConnect();
+  StartConnect();
   client_->Handshake();
   server_->Handshake();  // Send first flight.
   client_->adapter()->CloseWrites();
   client_->Handshake();  // This will get an error, but shouldn't crash.
   client_->CheckErrorCode(SSL_ERROR_SOCKET_WRITE_FAILURE);
 }
 
 TEST_F(TlsConnectTest, ConnectSSLv3) {
--- a/gtests/ssl_gtest/ssl_resumption_unittest.cc
+++ b/gtests/ssl_gtest/ssl_resumption_unittest.cc
@@ -242,18 +242,17 @@ TEST_P(TlsConnectGeneric, ConnectWithExp
   ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
   ExpectResumption(RESUME_NONE);
 
   SSLExtensionType xtn = (version_ >= SSL_LIBRARY_VERSION_TLS_1_3)
                              ? ssl_tls13_pre_shared_key_xtn
                              : ssl_session_ticket_xtn;
   auto capture = std::make_shared<TlsExtensionCapture>(xtn);
   client_->SetPacketFilter(capture);
-  client_->StartConnect();
-  server_->StartConnect();
+  StartConnect();
   client_->Handshake();
   EXPECT_TRUE(capture->captured());
   EXPECT_LT(0U, capture->extension().len());
 
   WAIT_(false, 1000);  // Let the ticket expire on the server.
 
   Handshake();
   CheckConnected();
@@ -735,18 +734,17 @@ TEST_F(TlsConnectTest, SendSessionTicket
 TEST_F(TlsConnectTest, SendSessionTicketInappropriate) {
   ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
   ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_2);
 
   EXPECT_EQ(SECFailure, SSL_SendSessionTicket(client_->ssl_fd(), NULL, 0))
       << "clients can't send tickets";
   EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError());
 
-  server_->StartConnect();
-  client_->StartConnect();
+  StartConnect();
 
   EXPECT_EQ(SECFailure, SSL_SendSessionTicket(server_->ssl_fd(), NULL, 0))
       << "no ticket before the handshake has started";
   EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError());
   Handshake();
   EXPECT_EQ(SECFailure, SSL_SendSessionTicket(server_->ssl_fd(), NULL, 0))
       << "no special tickets in TLS 1.2";
   EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError());
--- a/gtests/ssl_gtest/ssl_skip_unittest.cc
+++ b/gtests/ssl_gtest/ssl_skip_unittest.cc
@@ -155,21 +155,20 @@ TEST_P(TlsSkipTest, SkipServerKeyExchang
 TEST_P(TlsSkipTest, SkipServerKeyExchangeEcdsa) {
   Reset(TlsAgent::kServerEcdsa256);
   ServerSkipTest(
       std::make_shared<TlsHandshakeSkipFilter>(kTlsHandshakeServerKeyExchange));
   client_->CheckErrorCode(SSL_ERROR_RX_UNEXPECTED_HELLO_DONE);
 }
 
 TEST_P(TlsSkipTest, SkipCertAndKeyExch) {
-  auto chain = std::make_shared<ChainedPacketFilter>();
-  chain->Add(
-      std::make_shared<TlsHandshakeSkipFilter>(kTlsHandshakeCertificate));
-  chain->Add(
-      std::make_shared<TlsHandshakeSkipFilter>(kTlsHandshakeServerKeyExchange));
+  auto chain = std::make_shared<ChainedPacketFilter>(ChainedPacketFilterInit{
+      std::make_shared<TlsHandshakeSkipFilter>(kTlsHandshakeCertificate),
+      std::make_shared<TlsHandshakeSkipFilter>(
+          kTlsHandshakeServerKeyExchange)});
   ServerSkipTest(chain);
   client_->CheckErrorCode(SSL_ERROR_RX_UNEXPECTED_HELLO_DONE);
 }
 
 TEST_P(TlsSkipTest, SkipCertAndKeyExchEcdsa) {
   Reset(TlsAgent::kServerEcdsa256);
   auto chain = std::make_shared<ChainedPacketFilter>();
   chain->Add(
--- a/gtests/ssl_gtest/ssl_version_unittest.cc
+++ b/gtests/ssl_gtest/ssl_version_unittest.cc
@@ -249,18 +249,17 @@ TEST_F(TlsConnectTest, Tls13RejectsRehan
   SECStatus rv = SSL_ReHandshake(server_->ssl_fd(), PR_TRUE);
   EXPECT_EQ(SECFailure, rv);
   EXPECT_EQ(SSL_ERROR_RENEGOTIATION_NOT_ALLOWED, PORT_GetError());
 }
 
 TEST_P(TlsConnectGeneric, AlertBeforeServerHello) {
   EnsureTlsSetup();
   client_->ExpectReceiveAlert(kTlsAlertUnrecognizedName, kTlsAlertWarning);
-  client_->StartConnect();
-  server_->StartConnect();
+  StartConnect();
   client_->Handshake();  // Send ClientHello.
   static const uint8_t kWarningAlert[] = {kTlsAlertWarning,
                                           kTlsAlertUnrecognizedName};
   DataBuffer alert;
   TlsAgentTestBase::MakeRecord(variant_, kTlsAlertType,
                                SSL_LIBRARY_VERSION_TLS_1_0, kWarningAlert,
                                PR_ARRAY_SIZE(kWarningAlert), &alert);
   client_->adapter()->PacketReceived(alert);
--- a/gtests/ssl_gtest/test_io.cc
+++ b/gtests/ssl_gtest/test_io.cc
@@ -104,17 +104,17 @@ int32_t DummyPrSocket::Write(PRFileDesc 
     return -1;
   }
 
   DataBuffer packet(static_cast<const uint8_t *>(buf),
                     static_cast<size_t>(length));
   DataBuffer filtered;
   PacketFilter::Action action = PacketFilter::KEEP;
   if (filter_) {
-    action = filter_->Filter(packet, &filtered);
+    action = filter_->Process(packet, &filtered);
   }
   switch (action) {
     case PacketFilter::CHANGE:
       LOG("Original packet: " << packet);
       LOG("Filtered packet: " << filtered);
       peer->PacketReceived(filtered);
       break;
     case PacketFilter::DROP:
--- a/gtests/ssl_gtest/test_io.h
+++ b/gtests/ssl_gtest/test_io.h
@@ -28,26 +28,38 @@ class DummyPrSocket;  // Fwd decl.
 // Allow us to inspect a packet before it is written.
 class PacketFilter {
  public:
   enum Action {
     KEEP,    // keep the original packet unmodified
     CHANGE,  // change the packet to a different value
     DROP     // drop the packet
   };
+  PacketFilter(bool enabled = true) : enabled_(enabled) {}
+  virtual ~PacketFilter() {}
 
-  virtual ~PacketFilter() {}
+  virtual Action Process(const DataBuffer& input, DataBuffer* output) {
+    if (!enabled_) {
+      return KEEP;
+    }
+    return Filter(input, output);
+  }
+  void Enable() { enabled_ = true; }
+  void Disable() { enabled_ = false; }
 
   // The packet filter takes input and has the option of mutating it.
   //
   // A filter that modifies the data places the modified data in *output and
   // returns CHANGE.  A filter that does not modify data returns LEAVE, in which
   // case the value in *output is ignored.  A Filter can return DROP, in which
   // case the packet is dropped (and *output is ignored).
   virtual Action Filter(const DataBuffer& input, DataBuffer* output) = 0;
+
+ private:
+  bool enabled_;
 };
 
 class DummyPrSocket : public DummyIOLayerMethods {
  public:
   DummyPrSocket(const std::string& name, SSLProtocolVariant variant)
       : name_(name),
         variant_(variant),
         peer_(),
--- a/gtests/ssl_gtest/tls_agent.cc
+++ b/gtests/ssl_gtest/tls_agent.cc
@@ -748,22 +748,29 @@ void TlsAgent::Connected() {
   EXPECT_EQ(sizeof(csinfo_), csinfo_.length);
 
   if (expected_version_ >= SSL_LIBRARY_VERSION_TLS_1_3) {
     PRInt32 cipherSuites = SSLInt_CountTls13CipherSpecs(ssl_fd());
     // We use one ciphersuite in each direction.
     PRInt32 expected = 2;
     // For DTLS, the client retains the cipher spec for early data and the
     // handshake so that it can retransmit EndOfEarlyData and its final flight.
-    if (variant_ == ssl_variant_datagram && role_ == CLIENT) {
-      expected = info_.earlyDataAccepted ? 4 : 3;
+    // It also retains the handshake read cipher spec so that it can
+    // read ACKs from the server. The server retains the handshake read
+    // cipher spec so it can read the client's retransmitted Finished.
+    if (variant_ == ssl_variant_datagram) {
+      if (role_ == CLIENT) {
+        expected = info_.earlyDataAccepted ? 5 : 4;
+      } else {
+        expected = 3;
+      }
     }
     EXPECT_EQ(expected, cipherSuites);
     if (expected != cipherSuites) {
-      SSLInt_PrintTls13CipherSpecs(ssl_fd());
+      SSLInt_PrintTls13CipherSpecs(role_str().c_str(), ssl_fd());
     }
   }
 
   SetState(STATE_CONNECTED);
 }
 
 void TlsAgent::EnableExtendedMasterSecret() {
   ASSERT_TRUE(EnsureTlsSetup());
@@ -871,16 +878,24 @@ void TlsAgent::SendDirect(const DataBuff
   auto peer = adapter_->peer().lock();
   if (peer) {
     peer->PacketReceived(buf);
   } else {
     LOG("Send Direct peer absent");
   }
 }
 
+void TlsAgent::SendRecordDirect(const TlsRecord& record) {
+  DataBuffer buf;
+
+  auto rv = record.header.Write(&buf, 0, record.buffer);
+  EXPECT_EQ(record.header.header_length() + record.buffer.len(), rv);
+  SendDirect(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));
@@ -906,16 +921,37 @@ void TlsAgent::SendBuffer(const DataBuff
     EXPECT_NE(PR_WOULD_BLOCK_ERROR, error_code_);
     error_code_ = PR_GetError();
     expect_readwrite_error_ = false;
   } else {
     ASSERT_EQ(buf.len(), static_cast<size_t>(rv));
   }
 }
 
+bool TlsAgent::SendEncryptedRecord(const std::shared_ptr<TlsCipherSpec>& spec,
+                                   uint16_t wireVersion, uint64_t seq,
+                                   uint8_t ct, const DataBuffer& buf) {
+  LOGV("Writing " << buf.len() << " bytes");
+  // Ensure we are a TLS 1.3 cipher agent.
+  EXPECT_GE(expected_version_, SSL_LIBRARY_VERSION_TLS_1_3);
+  TlsRecordHeader header(wireVersion, kTlsApplicationDataType, seq);
+  DataBuffer padded = buf;
+  padded.Write(padded.len(), ct, 1);
+  DataBuffer ciphertext;
+  if (!spec->Protect(header, padded, &ciphertext)) {
+    return false;
+  }
+
+  DataBuffer record;
+  auto rv = header.Write(&record, 0, ciphertext);
+  EXPECT_EQ(header.header_length() + ciphertext.len(), rv);
+  SendDirect(record);
+  return true;
+}
+
 void TlsAgent::ReadBytes(size_t amount) {
   uint8_t block[16384];
 
   int32_t rv = PR_Read(ssl_fd(), block, (std::min)(amount, sizeof(block)));
   LOGV("ReadBytes " << rv);
   int32_t err;
 
   if (rv >= 0) {
--- a/gtests/ssl_gtest/tls_agent.h
+++ b/gtests/ssl_gtest/tls_agent.h
@@ -145,18 +145,22 @@ class TlsAgent : public PollTarget {
                  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;
   // Send data on the socket, encrypting it.
   void SendData(size_t bytes, size_t blocksize = 1024);
   void SendBuffer(const DataBuffer& buf);
+  bool SendEncryptedRecord(const std::shared_ptr<TlsCipherSpec>& spec,
+                           uint16_t wireVersion, uint64_t seq, uint8_t ct,
+                           const DataBuffer& buf);
   // Send data directly to the underlying socket, skipping the TLS layer.
   void SendDirect(const DataBuffer& buf);
+  void SendRecordDirect(const TlsRecord& record);
   void ReadBytes(size_t max = 16384U);
   void ResetSentBytes();  // Hack to test drops.
   void EnableExtendedMasterSecret();
   void CheckExtendedMasterSecret(bool expected);
   void CheckEarlyDataAccepted(bool expected);
   void DisableRollbackDetection();
   void EnableCompression();
   void SetDowngradeCheckVersion(uint16_t version);
--- a/gtests/ssl_gtest/tls_connect.cc
+++ b/gtests/ssl_gtest/tls_connect.cc
@@ -257,32 +257,50 @@ void TlsConnectTestBase::EnableExtendedM
 
 void TlsConnectTestBase::Connect() {
   server_->StartConnect(server_model_ ? server_model_->ssl_fd() : nullptr);
   client_->StartConnect(client_model_ ? client_model_->ssl_fd() : nullptr);
   Handshake();
   CheckConnected();
 }
 
+void TlsConnectTestBase::StartConnect() {
+  server_->StartConnect(server_model_ ? server_model_->ssl_fd() : nullptr);
+  client_->StartConnect(client_model_ ? client_model_->ssl_fd() : nullptr);
+}
+
 void TlsConnectTestBase::ConnectWithCipherSuite(uint16_t cipher_suite) {
   EnsureTlsSetup();
   client_->EnableSingleCipher(cipher_suite);
 
   Connect();
   SendReceive();
 
   // Check that we used the right cipher suite.
   uint16_t actual;
   EXPECT_TRUE(client_->cipher_suite(&actual));
   EXPECT_EQ(cipher_suite, actual);
   EXPECT_TRUE(server_->cipher_suite(&actual));
   EXPECT_EQ(cipher_suite, actual);
 }
 
 void TlsConnectTestBase::CheckConnected() {
+  // Have the client read handshake twice to make sure we get the
+  // NST and the ACK.
+  if (client_->version() >= SSL_LIBRARY_VERSION_TLS_1_3 &&
+      variant_ == ssl_variant_datagram) {
+    client_->Handshake();
+    client_->Handshake();
+    auto suites = SSLInt_CountTls13CipherSpecs(client_->ssl_fd());
+    // Verify that we dropped the client's retransmission cipher suites.
+    EXPECT_EQ(2, suites) << "Client has the wrong number of suites";
+    if (suites != 2) {
+      SSLInt_PrintTls13CipherSpecs("client", client_->ssl_fd());
+    }
+  }
   EXPECT_EQ(client_->version(), server_->version());
   if (!skip_version_checks_) {
     // Check the version is as expected
     EXPECT_EQ(std::min(client_->max_version(), server_->max_version()),
               client_->version());
   }
 
   EXPECT_EQ(TlsAgent::STATE_CONNECTED, client_->state());
@@ -372,18 +390,17 @@ void TlsConnectTestBase::CheckKeys(SSLKE
   CheckKeys(kea_type, group, auth_type, scheme);
 }
 
 void TlsConnectTestBase::CheckKeys() const {
   CheckKeys(ssl_kea_ecdh, ssl_auth_rsa_sign);
 }
 
 void TlsConnectTestBase::ConnectExpectFail() {
-  server_->StartConnect();
-  client_->StartConnect();
+  StartConnect();
   Handshake();
   ASSERT_EQ(TlsAgent::STATE_ERROR, client_->state());
   ASSERT_EQ(TlsAgent::STATE_ERROR, server_->state());
 }
 
 void TlsConnectTestBase::ExpectAlert(std::shared_ptr<TlsAgent>& sender,
                                      uint8_t alert) {
   EnsureTlsSetup();
@@ -394,18 +411,17 @@ void TlsConnectTestBase::ExpectAlert(std
 
 void TlsConnectTestBase::ConnectExpectAlert(std::shared_ptr<TlsAgent>& sender,
                                             uint8_t alert) {
   ExpectAlert(sender, alert);
   ConnectExpectFail();
 }
 
 void TlsConnectTestBase::ConnectExpectFailOneSide(TlsAgent::Role failing_side) {
-  server_->StartConnect();
-  client_->StartConnect();
+  StartConnect();
   client_->SetServerKeyBits(server_->server_key_bits());
   client_->Handshake();
   server_->Handshake();
 
   auto failing_agent = server_;
   if (failing_side == TlsAgent::CLIENT) {
     failing_agent = client_;
   }
@@ -562,18 +578,17 @@ void TlsConnectTestBase::SetupForZeroRtt
   ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
   ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3);
   server_->Set0RttEnabled(true);  // So we signal that we allow 0-RTT.
   Connect();
   SendReceive();  // Need to read so that we absorb the session ticket.
   CheckKeys();
 
   Reset();
-  server_->StartConnect();
-  client_->StartConnect();
+  StartConnect();
 }
 
 // Do a first connection so we can do resumption
 void TlsConnectTestBase::SetupForResume() {
   EnsureTlsSetup();
   ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
   Connect();
   SendReceive();  // Need to read so that we absorb the session ticket.
@@ -594,17 +609,17 @@ void TlsConnectTestBase::ZeroRttSendRece
   }
   PRInt32 rv =
       PR_Write(client_->ssl_fd(), k0RttData, k0RttDataLen);  // 0-RTT write.
   if (expect_writable) {
     EXPECT_EQ(k0RttDataLen, rv);
   } else {
     EXPECT_EQ(SECFailure, rv);
   }
-  server_->Handshake();  // Consume ClientHello, EE, Finished.
+  server_->Handshake();  // Consume ClientHello
 
   std::vector<uint8_t> buf(k0RttDataLen);
   rv = PR_Read(server_->ssl_fd(), buf.data(), k0RttDataLen);  // 0-RTT read
   if (expect_readable) {
     std::cerr << "0-RTT read " << rv << " bytes\n";
     EXPECT_EQ(k0RttDataLen, rv);
   } else {
     EXPECT_EQ(SECFailure, rv);
--- a/gtests/ssl_gtest/tls_connect.h
+++ b/gtests/ssl_gtest/tls_connect.h
@@ -57,16 +57,18 @@ class TlsConnectTestBase : public ::test
   // Make sure TLS is configured for a connection.
   void EnsureTlsSetup();
   // Reset and keep the same certificate names
   void Reset();
   // Reset, and update the certificate names on both peers
   void Reset(const std::string& server_name,
              const std::string& client_name = "client");
 
+  // Set up
+  void StartConnect();
   // Run the handshake.
   void Handshake();
   // Connect and check that it works.
   void Connect();
   // Check that the connection was successfully established.
   void CheckConnected();
   // Connect and expect it to fail.
   void ConnectExpectFail();
@@ -240,16 +242,21 @@ class TlsConnectStreamTls13 : public Tls
 };
 
 class TlsConnectDatagram13 : public TlsConnectTestBase {
  public:
   TlsConnectDatagram13()
       : TlsConnectTestBase(ssl_variant_datagram, SSL_LIBRARY_VERSION_TLS_1_3) {}
 };
 
+class TlsConnectDatagramPre13 : public TlsConnectDatagram {
+ public:
+  TlsConnectDatagramPre13() {}
+};
+
 // A variant that is used only with Pre13.
 class TlsConnectGenericPre13 : public TlsConnectGeneric {};
 
 class TlsKeyExchangeTest : public TlsConnectGeneric {
  protected:
   std::shared_ptr<TlsExtensionCapture> groups_capture_;
   std::shared_ptr<TlsExtensionCapture> shares_capture_;
   std::shared_ptr<TlsExtensionCapture> shares_capture2_;
--- a/gtests/ssl_gtest/tls_filter.cc
+++ b/gtests/ssl_gtest/tls_filter.cc
@@ -65,17 +65,18 @@ void TlsRecordFilter::CipherSpecChanged(
     return;
   }
 
   self->in_sequence_number_ = 0;
   self->out_sequence_number_ = 0;
   self->dropped_record_ = false;
   self->cipher_spec_.reset(new TlsCipherSpec());
   bool ret =
-      self->cipher_spec_->Init(SSLInt_CipherSpecToAlgorithm(isServer, newSpec),
+      self->cipher_spec_->Init(SSLInt_CipherSpecToEpoch(isServer, newSpec),
+                               SSLInt_CipherSpecToAlgorithm(isServer, newSpec),
                                SSLInt_CipherSpecToKey(isServer, newSpec),
                                SSLInt_CipherSpecToIv(isServer, newSpec));
   EXPECT_EQ(true, ret);
 }
 
 PacketFilter::Action TlsRecordFilter::Filter(const DataBuffer& input,
                                              DataBuffer* output) {
   bool changed = false;
@@ -142,17 +143,17 @@ PacketFilter::Action TlsRecordFilter::Fi
   if (action == KEEP) {
     if (header.is_dtls() || !dropped_record_) {
       return KEEP;
     }
     filtered = plaintext;
   }
 
   if (action == DROP) {
-    std::cerr << "record drop: " << record << std::endl;
+    std::cerr << "record drop: " << header << ":" << record << std::endl;
     dropped_record_ = true;
     return DROP;
   }
 
   EXPECT_GT(0x10000U, filtered.len());
   if (action != KEEP) {
     std::cerr << "record old: " << plaintext << std::endl;
     std::cerr << "record new: " << filtered << std::endl;
@@ -229,17 +230,19 @@ bool TlsRecordFilter::Unprotect(const Tl
     *inner_content_type = header.content_type();
     *plaintext = ciphertext;
     return true;
   }
 
   if (g_ssl_gtest_verbose) {
     std::cerr << "unprotect: " << header.sequence_number() << std::endl;
   }
-  if (!cipher_spec_->Unprotect(header, ciphertext, plaintext)) return false;
+  if (!cipher_spec_->Unprotect(header, ciphertext, plaintext)) {
+    return false;
+  }
 
   size_t len = plaintext->len();
   while (len > 0 && !plaintext->data()[len - 1]) {
     --len;
   }
   if (!len) {
     // Bogus padding.
     return false;
@@ -402,16 +405,25 @@ PacketFilter::Action TlsInspectorReplace
   if (header.handshake_type() == handshake_type_) {
     *output = buffer_;
     return CHANGE;
   }
 
   return KEEP;
 }
 
+PacketFilter::Action TlsRecordRecorder::FilterRecord(
+    const TlsRecordHeader& header, const DataBuffer& input,
+    DataBuffer* output) {
+  if (!filter_ || (header.content_type() == ct_)) {
+    records_.push_back({header, input});
+  }
+  return KEEP;
+}
+
 PacketFilter::Action TlsConversationRecorder::FilterRecord(
     const TlsRecordHeader& header, const DataBuffer& input,
     DataBuffer* output) {
   buffer_.Append(input);
   return KEEP;
 }
 
 PacketFilter::Action TlsHeaderRecorder::FilterRecord(
@@ -428,17 +440,17 @@ const TlsRecordHeader* TlsHeaderRecorder
   return &headers_[index];
 }
 
 PacketFilter::Action ChainedPacketFilter::Filter(const DataBuffer& input,
                                                  DataBuffer* output) {
   DataBuffer in(input);
   bool changed = false;
   for (auto it = filters_.begin(); it != filters_.end(); ++it) {
-    PacketFilter::Action action = (*it)->Filter(in, output);
+    PacketFilter::Action action = (*it)->Process(in, output);
     if (action == DROP) {
       return DROP;
     }
 
     if (action == CHANGE) {
       in = *output;
       changed = true;
     }
@@ -725,16 +737,25 @@ PacketFilter::Action TlsInspectorClientH
 PacketFilter::Action SelectiveDropFilter::Filter(const DataBuffer& input,
                                                  DataBuffer* output) {
   if (counter_ >= 32) {
     return KEEP;
   }
   return ((1 << counter_++) & pattern_) ? DROP : KEEP;
 }
 
+PacketFilter::Action SelectiveRecordDropFilter::FilterRecord(
+    const TlsRecordHeader& header, const DataBuffer& data,
+    DataBuffer* changed) {
+  if (counter_ >= 32) {
+    return KEEP;
+  }
+  return ((1 << counter_++) & pattern_) ? DROP : KEEP;
+}
+
 PacketFilter::Action TlsInspectorClientHelloVersionSetter::FilterHandshake(
     const HandshakeHeader& header, const DataBuffer& input,
     DataBuffer* output) {
   if (header.handshake_type() == kTlsHandshakeClientHello) {
     *output = input;
     output->Write(0, version_, 2);
     return CHANGE;
   }
--- a/gtests/ssl_gtest/tls_filter.h
+++ b/gtests/ssl_gtest/tls_filter.h
@@ -45,29 +45,34 @@ class TlsRecordHeader : public TlsVersio
   TlsRecordHeader(uint16_t version, uint8_t content_type,
                   uint64_t sequence_number)
       : TlsVersioned(version),
         content_type_(content_type),
         sequence_number_(sequence_number) {}
 
   uint8_t content_type() const { return content_type_; }
   uint64_t sequence_number() const { return sequence_number_; }
-  size_t header_length() const { return is_dtls() ? 11 : 3; }
+  size_t header_length() const { return is_dtls() ? 13 : 5; }
 
   // Parse the header; return true if successful; body in an outparam if OK.
   bool Parse(uint64_t sequence_number, TlsParser* parser, DataBuffer* body);
   // Write the header and body to a buffer at the given offset.
   // Return the offset of the end of the write.
   size_t Write(DataBuffer* buffer, size_t offset, const DataBuffer& body) const;
 
  private:
   uint8_t content_type_;
   uint64_t sequence_number_;
 };
 
+struct TlsRecord {
+  const TlsRecordHeader header;
+  const DataBuffer buffer;
+};
+
 // Abstract filter that operates on entire (D)TLS records.
 class TlsRecordFilter : public PacketFilter {
  public:
   TlsRecordFilter()
       : agent_(nullptr),
         count_(0),
         cipher_spec_(),
         dropped_record_(false),
@@ -124,22 +129,23 @@ class TlsRecordFilter : public PacketFil
   // Whether we dropped a record since the cipher spec changed.
   bool dropped_record_;
   // The sequence number we use for reading records as they are written.
   uint64_t in_sequence_number_;
   // The sequence number we use for writing modified records.
   uint64_t out_sequence_number_;
 };
 
-inline std::ostream& operator<<(std::ostream& stream, TlsVersioned v) {
+inline std::ostream& operator<<(std::ostream& stream, const TlsVersioned& v) {
   v.WriteStream(stream);
   return stream;
 }
 
-inline std::ostream& operator<<(std::ostream& stream, TlsRecordHeader& hdr) {
+inline std::ostream& operator<<(std::ostream& stream,
+                                const TlsRecordHeader& hdr) {
   hdr.WriteStream(stream);
   stream << ' ';
   switch (hdr.content_type()) {
     case kTlsChangeCipherSpecType:
       stream << "CCS";
       break;
     case kTlsAlertType:
       stream << "Alert";
@@ -230,16 +236,39 @@ class TlsInspectorReplaceHandshakeMessag
                                                const DataBuffer& input,
                                                DataBuffer* output);
 
  private:
   uint8_t handshake_type_;
   DataBuffer buffer_;
 };
 
+// Make a copy of each record of a given type.
+class TlsRecordRecorder : public TlsRecordFilter {
+ public:
+  TlsRecordRecorder(uint8_t ct) : filter_(true), ct_(ct), records_() {}
+  TlsRecordRecorder()
+      : filter_(false),
+        ct_(content_handshake),  // dummy (<optional> is C++14)
+        records_() {}
+  virtual PacketFilter::Action FilterRecord(const TlsRecordHeader& header,
+                                            const DataBuffer& input,
+                                            DataBuffer* output);
+
+  size_t count() const { return records_.size(); }
+  void Clear() { records_.clear(); }
+
+  const TlsRecord& record(size_t i) const { return records_[i]; }
+
+ private:
+  bool filter_;
+  uint8_t ct_;
+  std::vector<TlsRecord> records_;
+};
+
 // Make a copy of the complete conversation.
 class TlsConversationRecorder : public TlsRecordFilter {
  public:
   TlsConversationRecorder(DataBuffer& buffer) : buffer_(buffer) {}
 
   virtual PacketFilter::Action FilterRecord(const TlsRecordHeader& header,
                                             const DataBuffer& input,
                                             DataBuffer* output);
@@ -256,21 +285,25 @@ class TlsHeaderRecorder : public TlsReco
                                             DataBuffer* output);
   const TlsRecordHeader* header(size_t index);
 
  private:
   std::vector<TlsRecordHeader> headers_;
 };
 
 // Runs multiple packet filters in series.
+typedef std::initializer_list<std::shared_ptr<PacketFilter>>
+    ChainedPacketFilterInit;
+
 class ChainedPacketFilter : public PacketFilter {
  public:
   ChainedPacketFilter() {}
   ChainedPacketFilter(const std::vector<std::shared_ptr<PacketFilter>> filters)
       : filters_(filters.begin(), filters.end()) {}
+  ChainedPacketFilter(ChainedPacketFilterInit il) : filters_(il) {}
   virtual ~ChainedPacketFilter() {}
 
   virtual PacketFilter::Action Filter(const DataBuffer& input,
                                       DataBuffer* output);
 
   // Takes ownership of the filter.
   void Add(std::shared_ptr<PacketFilter> filter) { filters_.push_back(filter); }
 
@@ -419,16 +452,44 @@ class SelectiveDropFilter : public Packe
   virtual PacketFilter::Action Filter(const DataBuffer& input,
                                       DataBuffer* output) override;
 
  private:
   const uint32_t pattern_;
   uint8_t counter_;
 };
 
+// This class selectively drops complete records. The difference from
+// SelectiveDropFilter is that if multiple DTLS records are in the same
+// datagram, we just drop one.
+class SelectiveRecordDropFilter : public TlsRecordFilter {
+ public:
+  SelectiveRecordDropFilter(uint32_t pattern, bool enabled = true)
+      : pattern_(pattern), counter_(0) {
+    if (!enabled) {
+      Disable();
+    }
+  }
+
+  void Enable(uint32_t pattern) {
+    std::cerr << "Enable " << this << std::endl;
+    PacketFilter::Enable();
+    pattern_ = pattern;
+  }
+
+ protected:
+  PacketFilter::Action FilterRecord(const TlsRecordHeader& header,
+                                    const DataBuffer& data,
+                                    DataBuffer* changed) override;
+
+ private:
+  uint32_t pattern_;
+  uint8_t counter_;
+};
+
 // Set the version number in the ClientHello.
 class TlsInspectorClientHelloVersionSetter : public TlsHandshakeFilter {
  public:
   TlsInspectorClientHelloVersionSetter(uint16_t version) : version_(version) {}
 
   virtual PacketFilter::Action FilterHandshake(const HandshakeHeader& header,
                                                const DataBuffer& input,
                                                DataBuffer* output);
--- a/gtests/ssl_gtest/tls_protect.cc
+++ b/gtests/ssl_gtest/tls_protect.cc
@@ -27,17 +27,16 @@ void AeadCipher::FormatNonce(uint64_t se
   memcpy(nonce, iv_, 12);
 
   for (size_t i = 0; i < 8; ++i) {
     nonce[12 - (i + 1)] ^= seq & 0xff;
     seq >>= 8;
   }
 
   DataBuffer d(nonce, 12);
-  std::cerr << "Nonce " << d << std::endl;
 }
 
 bool AeadCipher::AeadInner(bool decrypt, void *params, size_t param_length,
                            const uint8_t *in, size_t inlen, uint8_t *out,
                            size_t *outlen, size_t maxlen) {
   SECStatus rv;
   unsigned int uoutlen = 0;
   SECItem param = {
@@ -87,18 +86,19 @@ bool AeadCipherChacha20Poly1305::Aead(bo
   aeadParams.ulAADLen = 0;
   aeadParams.ulTagLen = 16;
 
   FormatNonce(seq, nonce);
   return AeadInner(decrypt, (unsigned char *)&aeadParams, sizeof(aeadParams),
                    in, inlen, out, outlen, maxlen);
 }
 
-bool TlsCipherSpec::Init(SSLCipherAlgorithm cipher, PK11SymKey *key,
-                         const uint8_t *iv) {
+bool TlsCipherSpec::Init(uint16_t epoch, SSLCipherAlgorithm cipher,
+                         PK11SymKey *key, const uint8_t *iv) {
+  epoch_ = epoch;
   switch (cipher) {
     case ssl_calg_aes_gcm:
       aead_.reset(new AeadCipherAesGcm());
       break;
     case ssl_calg_chacha20:
       aead_.reset(new AeadCipherChacha20Poly1305());
       break;
     default:
--- a/gtests/ssl_gtest/tls_protect.h
+++ b/gtests/ssl_gtest/tls_protect.h
@@ -53,24 +53,27 @@ class AeadCipherAesGcm : public AeadCiph
  protected:
   bool Aead(bool decrypt, uint64_t seq, const uint8_t *in, size_t inlen,
             uint8_t *out, size_t *outlen, size_t maxlen);
 };
 
 // Our analog of ssl3CipherSpec
 class TlsCipherSpec {
  public:
-  TlsCipherSpec() : aead_() {}
+  TlsCipherSpec() : epoch_(0), aead_() {}
 
-  bool Init(SSLCipherAlgorithm cipher, PK11SymKey *key, const uint8_t *iv);
+  bool Init(uint16_t epoch, SSLCipherAlgorithm cipher, PK11SymKey *key,
+            const uint8_t *iv);
 
   bool Protect(const TlsRecordHeader &header, const DataBuffer &plaintext,
                DataBuffer *ciphertext);
   bool Unprotect(const TlsRecordHeader &header, const DataBuffer &ciphertext,
                  DataBuffer *plaintext);
+  uint16_t epoch() const { return epoch_; }
 
  private:
+  uint16_t epoch_;
   std::unique_ptr<AeadCipher> aead_;
 };
 
 }  // namespace nss_test
 
 #endif
--- a/lib/ssl/SSLerrs.h
+++ b/lib/ssl/SSLerrs.h
@@ -520,8 +520,11 @@ ER3(SSL_ERROR_RX_MALFORMED_END_OF_EARLY_
 ER3(SSL_ERROR_UNSUPPORTED_EXPERIMENTAL_API, (SSL_ERROR_BASE + 164),
     "An experimental API was called, but not supported.")
 
 ER3(SSL_ERROR_APPLICATION_ABORT, (SSL_ERROR_BASE + 165),
     "SSL handshake aborted by the application.")
 
 ER3(SSL_ERROR_APP_CALLBACK_ERROR, (SSL_ERROR_BASE + 166),
     "An application callback produced an invalid response.")
+
+ER3(SSL_ERROR_NO_TIMERS_ERROR, (SSL_ERROR_BASE + 167),
+    "No timers are currently running.")
new file mode 100644
--- /dev/null
+++ b/lib/ssl/dtls13con.c
@@ -0,0 +1,414 @@
+/* -*- 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 1.3 Protocol
+ */
+
+#include "ssl.h"
+#include "sslimpl.h"
+#include "sslproto.h"
+
+/* DTLS 1.3 Record map for ACK processing.
+ * This represents a single fragment, so a record which includes
+ * multiple fragments will have one entry for each fragment on the
+ * sender. We use the same structure on the receiver for convenience
+ * but the only value we actually use is |record|.
+ */
+typedef struct DTLSHandshakeRecordEntryStr {
+    PRCList link;
+    PRUint16 messageSeq;      /* The handshake message sequence */
+    PRUint32 offset;          /* The offset into the handshake message. */
+    PRUint32 length;          /* The length of the fragment. */
+    sslSequenceNumber record; /* The record */
+    PRBool acked;             /* Has this packet been acked. */
+} DTLSHandshakeRecordEntry;
+
+SECStatus
+dtls13_RememberFragment(sslSocket *ss,
+                        PRCList *list,
+                        PRUint32 sequence,
+                        PRUint32 offset,
+                        PRUint32 length,
+                        sslSequenceNumber record)
+{
+    DTLSHandshakeRecordEntry *entry;
+    PORT_Assert(IS_DTLS(ss));
+    PORT_Assert(tls13_MaybeTls13(ss));
+    PORT_Assert(sequence != 0xffff);
+    SSL_TRC(20, ("%d: SSL3[%d]: %s remembering %s record=%llx msg=%d offset=%d",
+                 SSL_GETPID(), ss->fd,
+                 SSL_ROLE(ss),
+                 list == &ss->ssl3.hs.dtlsSentHandshake ? "sent" : "received",
+                 record, sequence, offset));
+
+    entry = PORT_ZAlloc(sizeof(DTLSHandshakeRecordEntry));
+    if (!entry) {
+        return SECFailure;
+    }
+
+    entry->messageSeq = sequence;
+    entry->offset = offset;
+    entry->length = length;
+    entry->record = record;
+    entry->acked = PR_FALSE;
+
+    PR_APPEND_LINK(&entry->link, list);
+
+    return SECSuccess;
+}
+
+SECStatus
+dtls13_SendAck(sslSocket *ss)
+{
+    sslBuffer buf = { NULL, 0, 0 };
+    SECStatus rv = SECSuccess;
+    PRCList *cursor;
+    PRInt32 sent;
+
+    SSL_TRC(10, ("%d: SSL3[%d]: Sending ACK",
+                 SSL_GETPID(), ss->fd));
+
+    for (cursor = PR_LIST_HEAD(&ss->ssl3.hs.dtlsRcvdHandshake);
+         cursor != &ss->ssl3.hs.dtlsRcvdHandshake;
+         cursor = PR_NEXT_LINK(cursor)) {
+        DTLSHandshakeRecordEntry *entry = (DTLSHandshakeRecordEntry *)cursor;
+
+        SSL_TRC(10, ("%d: SSL3[%d]: ACK for record=%llx",
+                     SSL_GETPID(), ss->fd, entry->record));
+        rv = sslBuffer_AppendNumber(&buf, entry->record, 8);
+        if (rv != SECSuccess) {
+            goto loser;
+        }
+    }
+
+    ssl_GetXmitBufLock(ss);
+    sent = ssl3_SendRecord(ss, NULL, content_ack,
+                           buf.buf, buf.len, 0);
+    ssl_ReleaseXmitBufLock(ss);
+    if (sent != buf.len) {
+        rv = SECFailure;
+        if (sent != -1) {
+            PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+        }
+    }
+
+loser:
+    sslBuffer_Clear(&buf);
+    return rv;
+}
+
+void
+dtls13_SendAckCb(sslSocket *ss)
+{
+    if (!IS_DTLS(ss)) {
+        return;
+    }
+    (void)dtls13_SendAck(ss);
+}
+
+/* Check to see if all of a message was ACKed. */
+PRBool
+dtls13_FragmentWasAcked(sslSocket *ss, PRUint16 msgSeq, PRUint32 offset,
+                        PRUint32 len)
+{
+    PRCList *cursor;
+    PORT_Assert(msgSeq != 0xffff);
+    PORT_Assert(tls13_MaybeTls13(ss));
+
+    for (cursor = PR_LIST_HEAD(&ss->ssl3.hs.dtlsSentHandshake);
+         cursor != &ss->ssl3.hs.dtlsSentHandshake;
+         cursor = PR_NEXT_LINK(cursor)) {
+        DTLSHandshakeRecordEntry *entry = (DTLSHandshakeRecordEntry *)cursor;
+        if (!entry->acked) {
+            continue;
+        }
+        if (msgSeq != entry->messageSeq) {
+            continue;
+        }
+        if (offset < entry->offset)
+            continue;
+        if ((offset + len) > (entry->offset + entry->length))
+            continue;
+        return PR_TRUE;
+    }
+    return PR_FALSE;
+}
+
+ssl3CipherSpec *
+dtls13_FindCipherSpecByEpoch(sslSocket *ss, CipherSpecDirection direction,
+                             DTLSEpoch epoch)
+{
+    PRCList *cur_p;
+    PORT_Assert(ss->version >= SSL_LIBRARY_VERSION_TLS_1_3);
+    for (cur_p = PR_LIST_HEAD(&ss->ssl3.hs.cipherSpecs);
+         cur_p != &ss->ssl3.hs.cipherSpecs;
+         cur_p = PR_NEXT_LINK(cur_p)) {
+        ssl3CipherSpec *spec = (ssl3CipherSpec *)cur_p;
+
+        if (spec->epoch != epoch) {
+            continue;
+        }
+        if (direction != spec->direction) {
+            continue;
+        }
+        return spec;
+    }
+    return NULL;
+}
+
+SECStatus
+dtls13_SetupAcks(sslSocket *ss)
+{
+    if (ss->version < SSL_LIBRARY_VERSION_TLS_1_3) {
+        return SECSuccess;
+    }
+
+    if (ss->ssl3.hs.endOfFlight) {
+        dtls_CancelTimer(ss, ss->ssl3.hs.ackTimer);
+
+        if (ss->ssl3.hs.ws == idle_handshake && ss->sec.isServer) {
+            SSL_TRC(10, ("%d: SSL3[%d]: dtls_HandleHandshake, sending ACK",
+                         SSL_GETPID(), ss->fd));
+            return dtls13_SendAck(ss);
+        }
+        return SECSuccess;
+    }
+
+    /* We need to send an ACK. */
+    if (!ss->ssl3.hs.ackTimer->cb) {
+        /* We're not armed, so arm. */
+        SSL_TRC(10, ("%d: SSL3[%d]: dtls_HandleHandshake, arming ack timer",
+                     SSL_GETPID(), ss->fd));
+        return dtls_StartTimer(ss, ss->ssl3.hs.ackTimer,
+                               DTLS_RETRANSMIT_INITIAL_MS / 4,
+                               dtls13_SendAckCb);
+    }
+    /* The ack timer is already armed, so just return. */
+    return SECSuccess;
+}
+
+/*
+ * Special case processing for out-of-epoch records.
+ * This can only handle ACKs for now and everything else generates
+ * an error. In future, may also handle KeyUpdate.
+ *
+ * The error checking here is as follows:
+ *
+ * - If it's not encrypted, out of epoch stuff is just discarded.
+ * - If it's encrypted, out of epoch stuff causes an error.
+ */
+SECStatus
+dtls13_HandleOutOfEpochRecord(sslSocket *ss, const ssl3CipherSpec *spec,
+                              SSL3ContentType rType,
+                              sslBuffer *databuf)
+{
+    SECStatus rv;
+    sslBuffer buf = *databuf;
+
+    databuf->len = 0; /* Discard data whatever happens. */
+    PORT_Assert(IS_DTLS(ss));
+    PORT_Assert(ss->version >= SSL_LIBRARY_VERSION_TLS_1_3);
+    /* Can't happen, but double check. */
+    if (!IS_DTLS(ss) || (ss->version < SSL_LIBRARY_VERSION_TLS_1_3)) {
+        tls13_FatalError(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error);
+        return SECFailure;
+    }
+    SSL_TRC(10, ("%d: DTLS13[%d]: handle out of epoch record: type=%d", SSL_GETPID(),
+                 ss->fd, rType));
+
+    if (rType == content_ack) {
+        ssl_GetSSL3HandshakeLock(ss);
+        rv = dtls13_HandleAck(ss, &buf);
+        ssl_ReleaseSSL3HandshakeLock(ss);
+        PORT_Assert(databuf->len == 0);
+        return rv;
+    }
+
+    switch (spec->epoch) {
+        case TrafficKeyClearText:
+            /* Drop. */
+            return SECSuccess;
+
+        case TrafficKeyHandshake:
+            /* Drop out of order handshake messages, but if we are the
+             * server, we might have processed the client's Finished and
+             * moved on to application data keys, but the client has
+             * retransmitted Finished (e.g., because our ACK got lost.)
+             * We just retransmit the previous Finished to let the client
+             * complete. */
+            if (rType == content_handshake) {
+                if ((ss->sec.isServer) &&
+                    (ss->ssl3.hs.ws == idle_handshake)) {
+                    PORT_Assert(dtls_TimerActive(ss, ss->ssl3.hs.hdTimer));
+                    return dtls13_SendAck(ss);
+                }
+                return SECSuccess;
+            }
+
+            /* This isn't a handshake record, so shouldn't be encrypted
+             * under the handshake key. */
+            break;
+
+        default:
+            /* Any other epoch is forbidden. */
+            break;
+    }
+
+    SSL_TRC(10, ("%d: SSL3[%d]: unexpected out of epoch record type %d", SSL_GETPID(),
+                 ss->fd, rType));
+
+    (void)SSL3_SendAlert(ss, alert_fatal, illegal_parameter);
+    PORT_SetError(SSL_ERROR_RX_UNKNOWN_RECORD_TYPE);
+    return SECFailure;
+}
+
+/* Store the null cipher spec with the right refct. */
+SECStatus
+dtls13_SaveNullCipherSpec(sslSocket *ss, const ssl3CipherSpec *crSpec)
+{
+    ssl3CipherSpec *spec;
+    extern const char kKeyPhaseCleartext[];
+    PORT_Assert(IS_DTLS(ss));
+
+    spec = PORT_ZNew(ssl3CipherSpec);
+    if (!spec) {
+        PORT_SetError(SEC_ERROR_NO_MEMORY);
+        return SECFailure;
+    }
+    spec->refCt = 1;
+    spec->cipher_def = crSpec->cipher_def;
+    spec->mac_def = crSpec->mac_def;
+    spec->decode = crSpec->decode;
+    spec->epoch = crSpec->epoch;
+    PORT_Memcpy(&spec->recvdRecords, &crSpec->recvdRecords,
+                sizeof(spec->recvdRecords));
+    spec->direction = CipherSpecRead;
+    spec->phase = kKeyPhaseCleartext;
+    spec->read_seq_num = crSpec->write_seq_num;
+    spec->refCt = 1;
+
+    PR_APPEND_LINK(&spec->link, &ss->ssl3.hs.cipherSpecs);
+
+    return SECSuccess;
+}
+
+void
+dtls13_ReleaseReadCipherSpec(sslSocket *ss, DTLSEpoch epoch)
+{
+    ssl3CipherSpec *spec;
+    if (!IS_DTLS(ss)) {
+        return;
+    }
+
+    SSL_TRC(10, ("%d: SSL3[%d]: releasing read cipher spec for epoch %d",
+                 SSL_GETPID(), ss->fd, epoch));
+
+    spec = dtls13_FindCipherSpecByEpoch(ss, CipherSpecRead, epoch);
+    if (!spec) {
+        return;
+    }
+
+    tls13_CipherSpecRelease(spec);
+}
+
+SECStatus
+dtls13_HandleAck(sslSocket *ss, sslBuffer *databuf)
+{
+    PRUint8 *b = databuf->buf;
+    PRUint32 l = databuf->len;
+    SECStatus rv;
+    PRBool messagesSent;
+
+    /* Ensure we don't loop. */
+    databuf->len = 0;
+
+    PORT_Assert(IS_DTLS(ss));
+    if (!tls13_MaybeTls13(ss)) {
+        tls13_FatalError(ss, SSL_ERROR_RX_UNKNOWN_RECORD_TYPE, illegal_parameter);
+        return SECSuccess;
+    }
+
+    SSL_TRC(10, ("%d: SSL3[%d]: Handling ACK", SSL_GETPID(), ss->fd));
+    while (l > 0) {
+        PRUint64 seq;
+        PRCList *cursor;
+
+        rv = ssl3_ConsumeHandshakeNumber64(ss, &seq, 8, &b, &l);
+        if (rv != SECSuccess) {
+            return SECFailure;
+        }
+
+        for (cursor = PR_LIST_HEAD(&ss->ssl3.hs.dtlsSentHandshake);
+             cursor != &ss->ssl3.hs.dtlsSentHandshake;
+             cursor = PR_NEXT_LINK(cursor)) {
+            DTLSHandshakeRecordEntry *entry = (DTLSHandshakeRecordEntry *)cursor;
+
+            if (entry->record == seq) {
+                SSL_TRC(10, (
+                                "%d: SSL3[%d]: Marking record=%llx message %d offset %d length=%d as ACKed",
+                                SSL_GETPID(), ss->fd,
+                                seq, entry->messageSeq, entry->offset, entry->length));
+                entry->acked = PR_TRUE;
+            }
+        }
+    }
+
+    /* Try to flush. */
+    rv = dtls_TransmitMessageFlight(ss, &messagesSent);
+    if (rv != SECSuccess) {
+        return SECFailure;
+    }
+
+    /* Reset the retransmit timer. */
+    if (ss->ssl3.hs.rtTimer->cb) {
+        (void)dtls_RestartTimer(ss, ss->ssl3.hs.rtTimer);
+    }
+
+    /* If no messages were sent, then clean up. */
+    if (!messagesSent) {
+        SSL_TRC(10, (
+                        "%d: SSL3[%d]: No more unacked handshake messages, cancelling retransmits",
+                        SSL_GETPID(), ss->fd));
+
+        dtls_CancelTimer(ss, ss->ssl3.hs.rtTimer);
+        ssl_ClearPRCList(&ss->ssl3.hs.dtlsSentHandshake, NULL);
+        dtls_FreeHandshakeMessages(&ss->ssl3.hs.lastMessageFlight);
+        /* If the handshake is finished, and we're the client then
+         * also clean up the handshake read cipher spec. Any ACKs
+         * we receive will be with the application data cipher spec.
+         * The server needs to keep the handshake cipher spec around
+         * for the holddown period to process retransmitted Finisheds.
+         */
+        if (!ss->sec.isServer && (ss->ssl3.hs.ws == idle_handshake)) {
+            dtls13_ReleaseReadCipherSpec(ss, TrafficKeyHandshake);
+        }
+    }
+    return SECSuccess;
+}
+
+/* Clean up the read timer for the handshake cipher suites on the
+ * server.
+ *
+ * In DTLS 1.3, the client speaks last (Finished), and will retransmit
+ * until the server ACKs that message (using application data cipher
+ * suites). I.e.,
+ *
+ * - The client uses the retransmit timer and retransmits using the
+ *   saved write handshake cipher suite.
+ * - The server keeps the saved read handshake cipher suite around
+ *   for the holddown period in case it needs to read the Finished.
+ *
+ * After the holddown period, the server assumes the client is happy
+ * and discards the handshake read cipher suite.
+ */
+void
+dtls13_HolddownTimerCb(sslSocket *ss)
+{
+    SSL_TRC(10, ("%d: SSL3[%d]: holddown timer fired",
+                 SSL_GETPID(), ss->fd));
+    dtls13_ReleaseReadCipherSpec(ss, TrafficKeyHandshake);
+    ssl_ClearPRCList(&ss->ssl3.hs.dtlsRcvdHandshake, NULL);
+}
new file mode 100644
--- /dev/null
+++ b/lib/ssl/dtls13con.h
@@ -0,0 +1,32 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is PRIVATE to SSL.
+ *
+ * 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/. */
+
+#ifndef __dtls13con_h_
+#define __dtls13con_h_
+
+SECStatus dtls13_RememberFragment(sslSocket *ss, PRCList *list,
+                                  PRUint32 sequence, PRUint32 offset, PRUint32 length,
+                                  sslSequenceNumber record);
+PRBool dtls13_FragmentWasAcked(sslSocket *ss, PRUint16 msgSeq, PRUint32 offset,
+                               PRUint32 len);
+ssl3CipherSpec *dtls13_FindCipherSpecByEpoch(sslSocket *ss, CipherSpecDirection direction,
+                                             DTLSEpoch epoch);
+SECStatus dtls13_SetupAcks(sslSocket *ss);
+SECStatus dtls13_HandleOutOfEpochRecord(sslSocket *ss, const ssl3CipherSpec *spec,
+                                        SSL3ContentType rType,
+                                        sslBuffer *databuf);
+SECStatus dtls13_HandleAck(sslSocket *ss, sslBuffer *databuf);
+SECStatus dtls13_SaveNullCipherSpec(sslSocket *ss, const ssl3CipherSpec *crSpec);
+void dtls13_ReleaseReadCipherSpec(sslSocket *ss, DTLSEpoch epoch);
+
+SECStatus dtls13_SendAck(sslSocket *ss);
+void dtls13_SendAckCb(sslSocket *ss);
+void dtls13_HolddownTimerCb(sslSocket *ss);
+void dtls_ReceivedFirstMessageInFlight(sslSocket *ss);
+
+#endif
--- a/lib/ssl/dtlscon.c
+++ b/lib/ssl/dtlscon.c
@@ -5,36 +5,40 @@
 
 /*
  * DTLS Protocol
  */
 
 #include "ssl.h"
 #include "sslimpl.h"
 #include "sslproto.h"
+#include "dtls13con.h"
 
 #ifndef PR_ARRAY_SIZE
 #define PR_ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
 #endif
 
-static SECStatus dtls_TransmitMessageFlight(sslSocket *ss);
 static SECStatus dtls_StartRetransmitTimer(sslSocket *ss);
 static void dtls_RetransmitTimerExpiredCb(sslSocket *ss);
 static SECStatus dtls_SendSavedWriteData(sslSocket *ss);
 static void dtls_FinishedTimerCb(sslSocket *ss);
+static void dtls_CancelAllTimers(sslSocket *ss);
 
 /* -28 adjusts for the IP/UDP header */
 static const PRUint16 COMMON_MTU_VALUES[] = {
     1500 - 28, /* Ethernet MTU */
     1280 - 28, /* IPv6 minimum MTU */
     576 - 28,  /* Common assumption */
     256 - 28   /* We're in serious trouble now */
 };
 
 #define DTLS_COOKIE_BYTES 32
+/* Maximum DTLS expansion = header + IV + max CBC padding +
+ * maximum MAC. */
+#define DTLS_MAX_EXPANSION (DTLS_RECORD_HEADER_LENGTH + 16 + 16 + 32)
 
 /* List copied from ssl3con.c:cipherSuites */
 static const ssl3CipherSuite nonDTLSSuites[] = {
     TLS_ECDHE_ECDSA_WITH_RC4_128_SHA,
     TLS_ECDHE_RSA_WITH_RC4_128_SHA,
     TLS_DHE_DSS_WITH_RC4_128_SHA,
     TLS_ECDH_RSA_WITH_RC4_128_SHA,
     TLS_ECDH_ECDSA_WITH_RC4_128_SHA,
@@ -179,83 +183,73 @@ dtls_FreeHandshakeMessages(PRCList *list
     }
 }
 
 /* Called by dtls_HandleHandshake() and dtls_MaybeRetransmitHandshake() if a
  * handshake message retransmission is detected. */
 static SECStatus
 dtls_RetransmitDetected(sslSocket *ss)
 {
+    dtlsTimer *timer = ss->ssl3.hs.rtTimer;
     SECStatus rv = SECSuccess;
 
     PORT_Assert(ss->opt.noLocks || ssl_HaveRecvBufLock(ss));
     PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
 
-    if (ss->ssl3.hs.rtTimerCb == dtls_RetransmitTimerExpiredCb) {
+    if (timer->cb == dtls_RetransmitTimerExpiredCb) {
         /* Check to see if we retransmitted recently. If so,
          * suppress the triggered retransmit. This avoids
          * retransmit wars after packet loss.
          * This is not in RFC 5346 but it should be.
          */
-        if ((PR_IntervalNow() - ss->ssl3.hs.rtTimerStarted) >
-            (ss->ssl3.hs.rtTimeoutMs / 4)) {
+        if ((PR_IntervalNow() - timer->started) >
+            (timer->timeout / 4)) {
             SSL_TRC(30,
                     ("%d: SSL3[%d]: Shortcutting retransmit timer",
                      SSL_GETPID(), ss->fd));
 
             /* Cancel the timer and call the CB,
              * which re-arms the timer */
-            dtls_CancelTimer(ss);
+            dtls_CancelTimer(ss, ss->ssl3.hs.rtTimer);
             dtls_RetransmitTimerExpiredCb(ss);
         } else {
             SSL_TRC(30,
                     ("%d: SSL3[%d]: Ignoring retransmission: "
                      "last retransmission %dms ago, suppressed for %dms",
                      SSL_GETPID(), ss->fd,
-                     PR_IntervalNow() - ss->ssl3.hs.rtTimerStarted,
-                     ss->ssl3.hs.rtTimeoutMs / 4));
+                     PR_IntervalNow() - timer->started,
+                     timer->timeout / 4));
         }
 
-    } else if (ss->ssl3.hs.rtTimerCb == dtls_FinishedTimerCb) {
+    } else if (timer->cb == dtls_FinishedTimerCb) {
         SSL_TRC(30, ("%d: SSL3[%d]: Retransmit detected in holddown",
                      SSL_GETPID(), ss->fd));
         /* Retransmit the messages and re-arm the timer
          * Note that we are not backing off the timer here.
          * The spec isn't clear and my reasoning is that this
          * may be a re-ordered packet rather than slowness,
          * so let's be aggressive. */
-        dtls_CancelTimer(ss);
-        rv = dtls_TransmitMessageFlight(ss);
+        dtls_CancelTimer(ss, ss->ssl3.hs.rtTimer);
+        rv = dtls_TransmitMessageFlight(ss, NULL);
         if (rv == SECSuccess) {
             rv = dtls_StartHolddownTimer(ss);
         }
 
     } else {
-        PORT_Assert(ss->ssl3.hs.rtTimerCb == NULL);
+        PORT_Assert(timer->cb == NULL);
         /* ... and ignore it. */
     }
     return rv;
 }
 
 static SECStatus
 dtls_HandleHandshakeMessage(sslSocket *ss, PRUint8 *data, PRBool last)
 {
-
-    /* At this point we are advancing our state machine, so we can free our last
-     * flight of messages. */
-    dtls_FreeHandshakeMessages(&ss->ssl3.hs.lastMessageFlight);
     ss->ssl3.hs.recvdHighWater = -1;
 
-    /* Reset the timer to the initial value if the retry counter
-     * is 0, per Sec. 4.2.4.1 */
-    dtls_CancelTimer(ss);
-    if (ss->ssl3.hs.rtRetries == 0) {
-        ss->ssl3.hs.rtTimeoutMs = DTLS_RETRANSMIT_INITIAL_MS;
-    }
-
     return ssl3_HandleHandshakeMessage(ss, data, ss->ssl3.hs.msg_len,
                                        last);
 }
 
 /* Called only from ssl3_HandleRecord, for each (deciphered) DTLS record.
  * origBuf is the decrypted ssl record content and is expected to contain
  * complete handshake records
  * Caller must hold the handshake and RecvBuf locks.
@@ -278,32 +272,37 @@ dtls_HandleHandshake(sslSocket *ss, sslB
     /* XXX OK for now.
      * This doesn't work properly with asynchronous certificate validation.
      * because that returns a WOULDBLOCK error. The current DTLS
      * applications do not need asynchronous validation, but in the
      * future we will need to add this.
      */
     sslBuffer buf = *origBuf;
     SECStatus rv = SECSuccess;
+    PRBool discarded = PR_FALSE;
+    DTLSEpoch savedEpoch = ss->ssl3.crSpec->epoch;
+    sslSequenceNumber savedSeqNum = ss->ssl3.crSpec->read_seq_num;
+
+    ss->ssl3.hs.endOfFlight = PR_FALSE;
 
     PORT_Assert(ss->opt.noLocks || ssl_HaveRecvBufLock(ss));
     PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
 
     while (buf.len > 0) {
         PRUint8 type;
         PRUint32 message_length;
         PRUint16 message_seq;
         PRUint32 fragment_offset;
         PRUint32 fragment_length;
         PRUint32 offset;
 
         if (buf.len < 12) {
             PORT_SetError(SSL_ERROR_RX_MALFORMED_HANDSHAKE);
             rv = SECFailure;
-            break;
+            goto loser;
         }
 
         /* Parse the header */
         type = buf.buf[0];
         message_length = (buf.buf[1] << 16) | (buf.buf[2] << 8) | buf.buf[3];
         message_seq = (buf.buf[4] << 8) | buf.buf[5];
         fragment_offset = (buf.buf[6] << 16) | (buf.buf[7] << 8) | buf.buf[8];
         fragment_length = (buf.buf[9] << 16) | (buf.buf[10] << 8) | buf.buf[11];
@@ -318,24 +317,24 @@ dtls_HandleHandshake(sslSocket *ss, sslB
 
         buf.buf += 12;
         buf.len -= 12;
 
         /* This fragment must be complete */
         if (buf.len < fragment_length) {
             PORT_SetError(SSL_ERROR_RX_MALFORMED_HANDSHAKE);
             rv = SECFailure;
-            break;
+            goto loser;
         }
 
         /* Sanity check the packet contents */
         if ((fragment_length + fragment_offset) > message_length) {
             PORT_SetError(SSL_ERROR_RX_MALFORMED_HANDSHAKE);
             rv = SECFailure;
-            break;
+            goto loser;
         }
 
         /* If we're a server and we receive what appears to be a retried
          * ClientHello, and we are expecting a ClientHello, move the receive
          * sequence number forward.  This allows for a retried ClientHello if we
          * send a stateless HelloRetryRequest. */
         if (message_seq > ss->ssl3.hs.recvMessageSeq &&
             message_seq == 1 &&
@@ -361,50 +360,53 @@ dtls_HandleHandshake(sslSocket *ss, sslB
             (fragment_length == message_length)) {
             /* Complete next message. Process immediately */
             ss->ssl3.hs.msg_type = (SSLHandshakeType)type;
             ss->ssl3.hs.msg_len = message_length;
 
             rv = dtls_HandleHandshakeMessage(ss, buf.buf,
                                              buf.len == fragment_length);
             if (rv == SECFailure) {
-                break; /* Discard the remainder of the record. */
+                goto loser;
             }
         } else {
             if (message_seq < ss->ssl3.hs.recvMessageSeq) {
                 /* Case 3: we do an immediate retransmit if we're
                  * in a waiting state. */
                 rv = dtls_RetransmitDetected(ss);
-                break;
+                goto loser;
             } else if (message_seq > ss->ssl3.hs.recvMessageSeq) {
                 /* Case 2
                  *
                  * Ignore this message. This means we don't handle out of
                  * order complete messages that well, but we're still
                  * compliant and this probably does not happen often
                  *
                  * XXX OK for now. Maybe do something smarter at some point?
                  */
+                SSL_TRC(10, ("%d: SSL3[%d]: dtls_HandleHandshake, discarding handshake message",
+                             SSL_GETPID(), ss->fd));
+                discarded = PR_TRUE;
             } else {
                 /* Case 1
                  *
                  * Buffer the fragment for reassembly
                  */
                 /* Make room for the message */
                 if (ss->ssl3.hs.recvdHighWater == -1) {
                     PRUint32 map_length = OFFSET_BYTE(message_length) + 1;
 
                     rv = sslBuffer_Grow(&ss->ssl3.hs.msg_body, message_length);
                     if (rv != SECSuccess)
-                        break;
+                        goto loser;
                     /* Make room for the fragment map */
                     rv = sslBuffer_Grow(&ss->ssl3.hs.recvdFragments,
                                         map_length);
                     if (rv != SECSuccess)
-                        break;
+                        goto loser;
 
                     /* Reset the reassembly map */
                     ss->ssl3.hs.recvdHighWater = 0;
                     PORT_Memset(ss->ssl3.hs.recvdFragments.buf, 0,
                                 ss->ssl3.hs.recvdFragments.space);
                     ss->ssl3.hs.msg_type = (SSLHandshakeType)type;
                     ss->ssl3.hs.msg_len = message_length;
                 }
@@ -412,17 +414,17 @@ dtls_HandleHandshake(sslSocket *ss, sslB
                 /* If we have a message length mismatch, abandon the reassembly
                  * in progress and hope that the next retransmit will give us
                  * something sane
                  */
                 if (message_length != ss->ssl3.hs.msg_len) {
                     ss->ssl3.hs.recvdHighWater = -1;
                     PORT_SetError(SSL_ERROR_RX_MALFORMED_HANDSHAKE);
                     rv = SECFailure;
-                    break;
+                    goto loser;
                 }
 
                 /* Now copy this fragment into the buffer */
                 PORT_Assert((fragment_offset + fragment_length) <=
                             ss->ssl3.hs.msg_body.space);
                 PORT_Memcpy(ss->ssl3.hs.msg_body.buf + fragment_offset,
                             buf.buf, fragment_length);
 
@@ -466,26 +468,48 @@ dtls_HandleHandshake(sslSocket *ss, sslB
                 }
 
                 /* If we have all the bytes, then we are good to go */
                 if (ss->ssl3.hs.recvdHighWater == ss->ssl3.hs.msg_len) {
                     rv = dtls_HandleHandshakeMessage(ss, ss->ssl3.hs.msg_body.buf,
                                                      buf.len == fragment_length);
 
                     if (rv == SECFailure) {
-                        break; /* Discard the rest of the record. */
+                        goto loser;
                     }
                 }
             }
         }
 
         buf.buf += fragment_length;
         buf.len -= fragment_length;
     }
 
+    // This should never happen, but belt and suspenders.
+    if (rv == SECFailure) {
+        PORT_Assert(0);
+        goto loser;
+    }
+
+    /* If we processed all the fragments in this message, then mark it as remembered.
+     * TODO(ekr@rtfm.com): Store out of order messages for DTLS 1.3 so ACKs work
+     * better. Bug 1392620.*/
+    if (!discarded && tls13_MaybeTls13(ss)) {
+        rv = dtls13_RememberFragment(
+            ss, &ss->ssl3.hs.dtlsRcvdHandshake,
+            0, 0, 0,
+            (((sslSequenceNumber)savedEpoch) << 48) | savedSeqNum);
+    }
+    if (rv != SECSuccess) {
+        goto loser;
+    }
+
+    rv = dtls13_SetupAcks(ss);
+
+loser:
     origBuf->len = 0; /* So ssl3_GatherAppDataRecord will keep looping. */
 
     /* XXX OK for now. In future handle rv == SECWouldBlock safely in order
      * to deal with asynchronous certificate verification */
     return rv;
 }
 
 /* Enqueue a message (either handshake or CCS)
@@ -562,121 +586,161 @@ dtls_FlushHandshakeMessages(sslSocket *s
     PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
     PORT_Assert(ss->opt.noLocks || ssl_HaveXmitBufLock(ss));
 
     rv = dtls_StageHandshakeMessage(ss);
     if (rv != SECSuccess)
         return rv;
 
     if (!(flags & ssl_SEND_FLAG_FORCE_INTO_BUFFER)) {
-        rv = dtls_TransmitMessageFlight(ss);
+        rv = dtls_TransmitMessageFlight(ss, NULL);
         if (rv != SECSuccess) {
             return rv;
         }
 
         if (!(flags & ssl_SEND_FLAG_NO_RETRANSMIT)) {
             rv = dtls_StartRetransmitTimer(ss);
+        } else {
+            PORT_Assert(ss->version < SSL_LIBRARY_VERSION_TLS_1_3);
         }
     }
 
     return rv;
 }
 
 /* The callback for when the retransmit timer expires
  *
  * Called from:
  *              dtls_CheckTimer()
  *              dtls_HandleHandshake()
  */
 static void
 dtls_RetransmitTimerExpiredCb(sslSocket *ss)
 {
     SECStatus rv;
-
+    dtlsTimer *timer = ss->ssl3.hs.rtTimer;
     ss->ssl3.hs.rtRetries++;
 
     if (!(ss->ssl3.hs.rtRetries % 3)) {
         /* If one of the messages was potentially greater than > MTU,
          * then downgrade. Do this every time we have retransmitted a
          * message twice, per RFC 6347 Sec. 4.1.1 */
         dtls_SetMTU(ss, ss->ssl3.hs.maxMessageSent - 1);
     }
 
-    rv = dtls_TransmitMessageFlight(ss);
+    rv = dtls_TransmitMessageFlight(ss, NULL);
     if (rv == SECSuccess) {
         /* Re-arm the timer */
-        ss->ssl3.hs.rtTimeoutMs *= 2;
-        if (ss->ssl3.hs.rtTimeoutMs > DTLS_RETRANSMIT_MAX_MS) {
-            ss->ssl3.hs.rtTimeoutMs = DTLS_RETRANSMIT_MAX_MS;
+        timer->timeout *= 2;
+        if (timer->timeout > DTLS_RETRANSMIT_MAX_MS) {
+            timer->timeout = DTLS_RETRANSMIT_MAX_MS;
         }
 
-        ss->ssl3.hs.rtTimerStarted = PR_IntervalNow();
-        ss->ssl3.hs.rtTimerCb = dtls_RetransmitTimerExpiredCb;
+        timer->started = PR_IntervalNow();
+        timer->cb = dtls_RetransmitTimerExpiredCb;
 
         SSL_TRC(30,
                 ("%d: SSL3[%d]: Retransmit #%d, next in %d",
                  SSL_GETPID(), ss->fd,
-                 ss->ssl3.hs.rtRetries, ss->ssl3.hs.rtTimeoutMs));
+                 ss->ssl3.hs.rtRetries, timer->timeout));
     }
     /* else: OK for now. In future maybe signal the stack that we couldn't
      * transmit. For now, let the read handle any real network errors */
 }
 
 /* Transmit a flight of handshake messages, stuffing them
  * into as few records as seems reasonable
  *
  * Called from:
  *             dtls_FlushHandshake()
  *             dtls_RetransmitTimerExpiredCb()
  */
-static SECStatus
-dtls_TransmitMessageFlight(sslSocket *ss)
+SECStatus
+dtls_TransmitMessageFlight(sslSocket *ss, PRBool *messagesSent)
 {
     SECStatus rv = SECSuccess;
     PRCList *msg_p;
     PRUint16 room_left = ss->ssl3.mtu;
     PRInt32 sent;
+    PRBool maybeDtls13 = tls13_MaybeTls13(ss);
+
+    if (messagesSent) {
+        *messagesSent = PR_FALSE;
+    }
+
+    SSL_TRC(10, ("%d: SSL3[%d]: dtls_TransmitMessageFlight",
+                 SSL_GETPID(), ss->fd));
 
     ssl_GetXmitBufLock(ss);
     ssl_GetSpecReadLock(ss);
 
     /* DTLS does not buffer its handshake messages in
      * ss->pendingBuf, but rather in the lastMessageFlight
      * structure. This is just a sanity check that
      * some programming error hasn't inadvertantly
      * stuffed something in ss->pendingBuf
      */
     PORT_Assert(!ss->pendingBuf.len);
     for (msg_p = PR_LIST_HEAD(&ss->ssl3.hs.lastMessageFlight);
          msg_p != &ss->ssl3.hs.lastMessageFlight;
          msg_p = PR_NEXT_LINK(msg_p)) {
         DTLSQueuedMessage *msg = (DTLSQueuedMessage *)msg_p;
+        PRUint16 msgSeq = 0xffff; /* Compiler can't tell it's not used
+                                   * uninitialized. */
+
+        if (maybeDtls13) {
+            msgSeq = (msg->data[4] << 8) | msg->data[5];
+        }
 
         /* The logic here is:
          *
          * 1. If this is a message that will not fit into the remaining
          *    space, then flush.
          * 2. If the message will now fit into the remaining space,
          *    encrypt, buffer, and loop.
          * 3. If the message will not fit, then fragment.
          *
          * At the end of the function, flush.
          */
-        if ((msg->len + SSL3_BUFFER_FUDGE) > room_left) {
+        if ((msg->len + DTLS_MAX_EXPANSION) > room_left) {
             /* The message will not fit into the remaining space, so flush */
             rv = dtls_SendSavedWriteData(ss);
             if (rv != SECSuccess)
                 break;
 
             room_left = ss->ssl3.mtu;
         }
 
-        if ((msg->len + SSL3_BUFFER_FUDGE) <= room_left) {
+        if ((msg->len + DTLS_MAX_EXPANSION) <= room_left) {
+            if (ss->version >= SSL_LIBRARY_VERSION_TLS_1_3) {
+                if (dtls13_FragmentWasAcked(ss, msgSeq, 0, msg->len)) {
+                    SSL_TRC(10, (
+                                    "%d: SSL3[%d]: message %d offset %d length=%d was ACKed; skipping",
+                                    SSL_GETPID(), ss->fd,
+                                    msgSeq, 0, msg->len));
+                    continue;
+                }
+            }
             /* The message will fit, so encrypt and then continue with the
              * next packet */
+
+            if (maybeDtls13) {
+                /* Record that we are sending first, because encrypting
+                 * increments the sequence number. */
+                if (messagesSent) {
+                    *messagesSent = PR_TRUE;
+                }
+                rv = dtls13_RememberFragment(ss,
+                                             &ss->ssl3.hs.dtlsSentHandshake,
+                                             msgSeq, 0, msg->len,
+                                             msg->cwSpec->write_seq_num);
+                if (rv != SECSuccess) {
+                    break;
+                }
+            }
             sent = ssl3_SendRecord(ss, msg->cwSpec, msg->type,
                                    msg->data, msg->len,
                                    ssl_SEND_FLAG_FORCE_INTO_BUFFER);
             if (sent != msg->len) {
                 rv = SECFailure;
                 if (sent != -1) {
                     PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
                 }
@@ -709,29 +773,42 @@ dtls_TransmitMessageFlight(sslSocket *ss
 
             while ((fragment_offset + 12) < msg->len) {
                 PRUint32 fragment_len;
                 const unsigned char *content = msg->data + 12;
                 PRUint32 content_len = msg->len - 12;
 
                 /* The reason we use 8 here is that that's the length of
                  * the new DTLS data that we add to the header */
-                fragment_len = PR_MIN((PRUint32)room_left - (SSL3_BUFFER_FUDGE + 8),
+                fragment_len = PR_MIN((PRUint32)room_left - (DTLS_MAX_EXPANSION),
                                       content_len - fragment_offset);
                 PORT_Assert(fragment_len < DTLS_MAX_MTU - 12);
                 /* Make totally sure that we are within the buffer.
                  * Note that the only way that fragment len could get
                  * adjusted here is if
                  *
                  * (a) we are in release mode so the PORT_Assert is compiled out
                  * (b) either the MTU table is inconsistent with DTLS_MAX_MTU
                  * or ss->ssl3.mtu has become corrupt.
                  */
                 fragment_len = PR_MIN(fragment_len, DTLS_MAX_MTU - 12);
 
+                if (ss->version >= SSL_LIBRARY_VERSION_TLS_1_3) {
+                    /* Now see if we want to send this fragment. */
+                    if (dtls13_FragmentWasAcked(ss, msgSeq, fragment_offset,
+                                                fragment_len)) {
+                        SSL_TRC(10, (
+                                        "%d: SSL3[%d]: message %d offset %d length=%d was ACKed; skipping",
+                                        SSL_GETPID(), ss->fd,
+                                        msgSeq, fragment_offset, fragment_len));
+                        fragment_offset += fragment_len;
+                        continue;
+                    }
+                }
+
                 /* Construct an appropriate-sized fragment */
                 /* Type, length, sequence */
                 PORT_Memcpy(fragment, msg->data, 6);
 
                 /* Offset */
                 fragment[6] = (fragment_offset >> 16) & 0xff;
                 fragment[7] = (fragment_offset >> 8) & 0xff;
                 fragment[8] = (fragment_offset)&0xff;
@@ -743,27 +820,41 @@ dtls_TransmitMessageFlight(sslSocket *ss
 
                 PORT_Memcpy(fragment + 12, content + fragment_offset,
                             fragment_len);
 
                 /*
                  *  Send the record. We do this in two stages
                  * 1. Encrypt
                  */
+
+                /* Record that we are sending first, because encrypting
+                 * increments the sequence number. */
+                if (maybeDtls13) {
+                    if (messagesSent) {
+                        *messagesSent = PR_TRUE;
+                    }
+                    rv = dtls13_RememberFragment(ss,
+                                                 &ss->ssl3.hs.dtlsSentHandshake,
+                                                 msgSeq, fragment_offset, fragment_len,
+                                                 msg->cwSpec->write_seq_num);
+                    if (rv != SECSuccess) {
+                        break;
+                    }
+                }
                 sent = ssl3_SendRecord(ss, msg->cwSpec, msg->type,
                                        fragment, fragment_len + 12,
                                        ssl_SEND_FLAG_FORCE_INTO_BUFFER);
                 if (sent != (fragment_len + 12)) {
                     rv = SECFailure;
                     if (sent != -1) {
                         PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
                     }
                     break;
                 }
-
                 /* 2. Flush */
                 rv = dtls_SendSavedWriteData(ss);
                 if (rv != SECSuccess)
                     break;
 
                 fragment_offset += fragment_len;
             }
         }
@@ -805,81 +896,144 @@ dtls_SendSavedWriteData(sslSocket *ss)
     /* Update the largest message sent so we can adjust the MTU
      * estimate if necessary */
     if (sent > ss->ssl3.hs.maxMessageSent)
         ss->ssl3.hs.maxMessageSent = sent;
 
     return SECSuccess;
 }
 
-static SECStatus
-dtls_StartTimer(sslSocket *ss, PRUint32 time, DTLSTimerCb cb)
+void
+dtls_InitTimers(sslSocket *ss)
 {
-    PORT_Assert(ss->ssl3.hs.rtTimerCb == NULL);
+    unsigned int i;
+    dtlsTimer **timers[PR_ARRAY_SIZE(ss->ssl3.hs.timers)] = {
+        &ss->ssl3.hs.rtTimer,
+        &ss->ssl3.hs.ackTimer,
+        &ss->ssl3.hs.hdTimer
+    };
+    static const char *timerLabels[] = {
+        "retransmit", "ack", "holddown"
+    };
 
-    ss->ssl3.hs.rtRetries = 0;
-    ss->ssl3.hs.rtTimerStarted = PR_IntervalNow();
-    ss->ssl3.hs.rtTimeoutMs = time;
-    ss->ssl3.hs.rtTimerCb = cb;
+    PORT_Assert(PR_ARRAY_SIZE(timers) == PR_ARRAY_SIZE(timerLabels));
+    for (i = 0; i < PR_ARRAY_SIZE(ss->ssl3.hs.timers); ++i) {
+        *timers[i] = &ss->ssl3.hs.timers[i];
+        ss->ssl3.hs.timers[i].label = timerLabels[i];
+    }
+}
+
+SECStatus
+dtls_StartTimer(sslSocket *ss, dtlsTimer *timer, PRUint32 time, DTLSTimerCb cb)
+{
+    PORT_Assert(timer->cb == NULL);
+
+    SSL_TRC(10, ("%d: SSL3[%d]: %s dtls_StartTimer %s timeout=%d",
+                 SSL_GETPID(), ss->fd, SSL_ROLE(ss), timer->label, time));
+
+    timer->started = PR_IntervalNow();
+    timer->timeout = time;
+    timer->cb = cb;
     return SECSuccess;
 }
 
+SECStatus
+dtls_RestartTimer(sslSocket *ss, dtlsTimer *timer)
+{
+    timer->started = PR_IntervalNow();
+    return SECSuccess;
+}
+
+PRBool
+dtls_TimerActive(sslSocket *ss, dtlsTimer *timer)
+{
+    return timer->cb != NULL;
+}
 /* Start a timer for retransmission. */
 static SECStatus
 dtls_StartRetransmitTimer(sslSocket *ss)
 {
-    return dtls_StartTimer(ss, DTLS_RETRANSMIT_INITIAL_MS,
+    ss->ssl3.hs.rtRetries = 0;
+    return dtls_StartTimer(ss, ss->ssl3.hs.rtTimer,
+                           DTLS_RETRANSMIT_INITIAL_MS,
                            dtls_RetransmitTimerExpiredCb);
 }
 
 /* Start a timer for holding an old cipher spec. */
 SECStatus
 dtls_StartHolddownTimer(sslSocket *ss)
 {
-    return dtls_StartTimer(ss, DTLS_RETRANSMIT_FINISHED_MS,
+    ss->ssl3.hs.rtRetries = 0;
+    return dtls_StartTimer(ss, ss->ssl3.hs.rtTimer,
+                           DTLS_RETRANSMIT_FINISHED_MS,
                            dtls_FinishedTimerCb);
 }
 
 /* Cancel a pending timer
  *
  * Called from:
  *              dtls_HandleHandshake()
  *              dtls_CheckTimer()
  */
 void
-dtls_CancelTimer(sslSocket *ss)
+dtls_CancelTimer(sslSocket *ss, dtlsTimer *timer)
 {
+    SSL_TRC(30, ("%d: SSL3[%d]: %s dtls_CancelTimer %s",
+                 SSL_GETPID(), ss->fd, SSL_ROLE(ss),
+                 timer->label));
+
     PORT_Assert(ss->opt.noLocks || ssl_HaveRecvBufLock(ss));
 
-    ss->ssl3.hs.rtTimerCb = NULL;
+    timer->cb = NULL;
+}
+
+static void
+dtls_CancelAllTimers(sslSocket *ss)
+{
+    unsigned int i;
+
+    for (i = 0; i < PR_ARRAY_SIZE(ss->ssl3.hs.timers); ++i) {
+        dtls_CancelTimer(ss, &ss->ssl3.hs.timers[i]);
+    }
 }
 
 /* Check the pending timer and fire the callback if it expired
  *
  * Called from ssl3_GatherCompleteHandshake()
  */
 void
 dtls_CheckTimer(sslSocket *ss)
 {
+    unsigned int i;
+    SSL_TRC(30, ("%d: SSL3[%d]: dtls_CheckTimer (%s)",
+                 SSL_GETPID(), ss->fd, ss->sec.isServer ? "server" : "client"));
+
     ssl_GetSSL3HandshakeLock(ss);
-    if (!ss->ssl3.hs.rtTimerCb) {
-        ssl_ReleaseSSL3HandshakeLock(ss);
-        return;
-    }
+
+    for (i = 0; i < PR_ARRAY_SIZE(ss->ssl3.hs.timers); ++i) {
+        dtlsTimer *timer = &ss->ssl3.hs.timers[i];
+        if (!timer->cb) {
+            continue;
+        }
 
-    if ((PR_IntervalNow() - ss->ssl3.hs.rtTimerStarted) >
-        PR_MillisecondsToInterval(ss->ssl3.hs.rtTimeoutMs)) {
-        /* Timer has expired */
-        DTLSTimerCb cb = ss->ssl3.hs.rtTimerCb;
+        if ((PR_IntervalNow() - timer->started) >=
+            PR_MillisecondsToInterval(timer->timeout)) {
+            /* Timer has expired */
+            DTLSTimerCb cb = timer->cb;
 
-        /* Cancel the timer so that we can call the CB safely */
-        dtls_CancelTimer(ss);
+            SSL_TRC(10, ("%d: SSL3[%d]: %s firing timer %s",
+                         SSL_GETPID(), ss->fd, SSL_ROLE(ss),
+                         timer->label));
 
-        /* Now call the CB */
-        cb(ss);
+            /* Cancel the timer so that we can call the CB safely */
+            dtls_CancelTimer(ss, timer);
+
+            /* Now call the CB */
+            cb(ss);
+        }
     }
     ssl_ReleaseSSL3HandshakeLock(ss);
 }
 
 /* The callback to fire when the holddown timer for the Finished
  * message expires and we can delete it
  *
  * Called from dtls_CheckTimer()
@@ -904,17 +1058,17 @@ dtls_FinishedTimerCb(sslSocket *ss)
 void
 dtls_RehandshakeCleanup(sslSocket *ss)
 {
     /* Skip this if we are handling a second ClientHello. */
     if (ss->ssl3.hs.helloRetry) {
         return;
     }
     PORT_Assert((ss->version < SSL_LIBRARY_VERSION_TLS_1_3));
-    dtls_CancelTimer(ss);
+    dtls_CancelAllTimers(ss);
     ssl3_DestroyCipherSpec(ss->ssl3.pwSpec, PR_FALSE);
     ss->ssl3.hs.sendMessageSeq = 0;
     ss->ssl3.hs.recvMessageSeq = 0;
 }
 
 /* Set the MTU to the next step less than or equal to the
  * advertised value. Also used to downgrade the MTU by
  * doing dtls_SetMTU(ss, biggest packet set).
@@ -968,16 +1122,18 @@ dtls_HandleHelloVerifyRequest(sslSocket 
     PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
 
     if (ss->ssl3.hs.ws != wait_server_hello) {
         errCode = SSL_ERROR_RX_UNEXPECTED_HELLO_VERIFY_REQUEST;
         desc = unexpected_message;
         goto alert_loser;
     }
 
+    dtls_ReceivedFirstMessageInFlight(ss);
+
     /* The version.
      *
      * RFC 4347 required that you verify that the server versions
      * match (Section 4.2.1) in the HelloVerifyRequest and the
      * ServerHello.
      *
      * RFC 6347 suggests (SHOULD) that servers always use 1.0 in
      * HelloVerifyRequest and allows the versions not to match,
@@ -1112,37 +1268,63 @@ dtls_RecordSetRecvd(DTLSRecvdRecords *re
 
     records->data[offset / 8] |= (1 << (offset % 8));
 }
 
 SECStatus
 DTLS_GetHandshakeTimeout(PRFileDesc *socket, PRIntervalTime *timeout)
 {
     sslSocket *ss = NULL;
-    PRIntervalTime elapsed;
-    PRIntervalTime desired;
+    PRBool found = PR_FALSE;
+    PRIntervalTime now = PR_IntervalNow();
+    PRIntervalTime to;
+    unsigned int i;
+
+    *timeout = PR_INTERVAL_NO_TIMEOUT;
 
     ss = ssl_FindSocket(socket);
 
-    if (!ss)
+    if (!ss) {
+        PORT_SetError(SEC_ERROR_INVALID_ARGS);
         return SECFailure;
+    }
 
-    if (!IS_DTLS(ss))
-        return SECFailure;
-
-    if (!ss->ssl3.hs.rtTimerCb)
+    if (!IS_DTLS(ss)) {
+        PORT_SetError(SEC_ERROR_INVALID_ARGS);
         return SECFailure;
+    }
 
-    elapsed = PR_IntervalNow() - ss->ssl3.hs.rtTimerStarted;
-    desired = PR_MillisecondsToInterval(ss->ssl3.hs.rtTimeoutMs);
-    if (elapsed > desired) {
-        /* Timer expired */
-        *timeout = PR_INTERVAL_NO_WAIT;
-    } else {
-        *timeout = desired - elapsed;
+    for (i = 0; i < PR_ARRAY_SIZE(ss->ssl3.hs.timers); ++i) {
+        PRIntervalTime elapsed;
+        PRIntervalTime desired;
+        dtlsTimer *timer = &ss->ssl3.hs.timers[i];
+
+        if (!timer->cb) {
+            continue;
+        }
+        found = PR_TRUE;
+
+        elapsed = now - timer->started;
+        desired = PR_MillisecondsToInterval(timer->timeout);
+        if (elapsed > desired) {
+            /* Timer expired */
+            *timeout = PR_INTERVAL_NO_WAIT;
+            return SECSuccess;
+        } else {
+            to = desired - elapsed;
+        }
+
+        if (*timeout > to) {
+            *timeout = to;
+        }
+    }
+
+    if (!found) {
+        PORT_SetError(SSL_ERROR_NO_TIMERS_FOUND);
+        return SECFailure;
     }
 
     return SECSuccess;
 }
 
 /*
  * DTLS relevance checks:
  * Note that this code currently ignores all out-of-epoch packets,
@@ -1151,72 +1333,53 @@ DTLS_GetHandshakeTimeout(PRFileDesc *soc
  * seems like a good tradeoff for implementation effort and is
  * 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 SSL3Ciphertext *cText,
-                PRBool *sameEpoch, PRUint64 *seqNum)
+dtls_IsRelevant(sslSocket *ss, const ssl3CipherSpec *spec,
+                const SSL3Ciphertext *cText,
+                sslSequenceNumber *seqNum)
 {
-    const ssl3CipherSpec *crSpec = ss->ssl3.crSpec;
-    DTLSEpoch epoch;
     sslSequenceNumber dtls_seq_num;
 
-    epoch = cText->seq_num >> 48;
-    *sameEpoch = crSpec->epoch == epoch;
-    if (!*sameEpoch) {
-        SSL_DBG(("%d: SSL3[%d]: dtls_IsRelevant, received packet "
-                 "from irrelevant epoch %d",
-                 SSL_GETPID(), ss->fd, epoch));
-        return PR_FALSE;
-    }
-
     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));
+    if (dtls_RecordGetRecvd(&spec->recvdRecords, dtls_seq_num) != 0) {
+        SSL_TRC(10, ("%d: SSL3[%d]: dtls_IsRelevant, rejecting "
+                     "potentially replayed packet",
+                     SSL_GETPID(), ss->fd));
         return PR_FALSE;
     }
 
     *seqNum = dtls_seq_num;
+
     return PR_TRUE;
 }
 
-/* In TLS 1.3, a client that receives a retransmission of the server's first
- * flight will reject that message and discard it (see dtls_IsRelevant() above).
- * However, we need to trigger retransmission to prevent loss of the client's
- * last flight from causing the connection to fail.
- *
- * This only triggers for a retransmitted ServerHello.  Other (encrypted)
- * handshake messages do not trigger retransmission, so we are a little more
- * exposed to loss than is ideal.
- *
- * 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,
-                              PRBool sameEpoch)
+void
+dtls_ReceivedFirstMessageInFlight(sslSocket *ss)
 {
-    SECStatus rv = SECSuccess;
-    DTLSEpoch messageEpoch = cText->seq_num >> 48;
+    if (!IS_DTLS(ss))
+        return;
 
-    /* Drop messages from other epochs if we are ignoring things. */
-    if (!sameEpoch && ss->ssl3.hs.zeroRttIgnore != ssl_0rtt_ignore_none) {
-        return SECSuccess;
+    /* At this point we are advancing our state machine, so we can free our last
+     * flight of messages. */
+    if (ss->ssl3.hs.ws != idle_handshake ||
+        ss->version >= SSL_LIBRARY_VERSION_TLS_1_3) {
+        /* We need to keep our last flight around in DTLS 1.2 and below,
+         * so we can retransmit it in response to other people's
+         * retransmits. */
+        dtls_FreeHandshakeMessages(&ss->ssl3.hs.lastMessageFlight);
+
+        /* Reset the timer to the initial value if the retry counter
+         * is 0, per RFC 6347, Sec. 4.2.4.1 */
+        dtls_CancelTimer(ss, ss->ssl3.hs.rtTimer);
+        if (ss->ssl3.hs.rtRetries == 0) {
+            ss->ssl3.hs.rtTimer->timeout = DTLS_RETRANSMIT_INITIAL_MS;
+        }
     }
 
-    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);
-        }
-        ssl_ReleaseSSL3HandshakeLock(ss);
-    }
-    return rv;
+    /* Empty the ACK queue (TLS 1.3 only). */
+    ssl_ClearPRCList(&ss->ssl3.hs.dtlsRcvdHandshake, NULL);
 }
new file mode 100644
--- /dev/null
+++ b/lib/ssl/dtlscon.h
@@ -0,0 +1,47 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is PRIVATE to SSL.
+ *
+ * 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/. */
+
+#ifndef __dtlscon_h_
+#define __dtlscon_h_
+
+extern void dtls_FreeHandshakeMessage(DTLSQueuedMessage *msg);
+extern void dtls_FreeHandshakeMessages(PRCList *lst);
+SECStatus dtls_TransmitMessageFlight(sslSocket *ss,
+                                     PRBool *messagesSent);
+void dtls_InitTimers(sslSocket *ss);
+SECStatus dtls_StartTimer(sslSocket *ss, dtlsTimer *timer,
+                          PRUint32 time, DTLSTimerCb cb);
+SECStatus dtls_RestartTimer(sslSocket *ss, dtlsTimer *timer);
+PRBool dtls_TimerActive(sslSocket *ss, dtlsTimer *timer);
+extern SECStatus dtls_HandleHandshake(sslSocket *ss, sslBuffer *origBuf);
+extern SECStatus dtls_HandleHelloVerifyRequest(sslSocket *ss,
+                                               PRUint8 *b, PRUint32 length);
+extern SECStatus dtls_StageHandshakeMessage(sslSocket *ss);
+extern SECStatus dtls_QueueMessage(sslSocket *ss, SSL3ContentType type,
+                                   const PRUint8 *pIn, PRInt32 nIn);
+extern SECStatus dtls_FlushHandshakeMessages(sslSocket *ss, PRInt32 flags);
+SECStatus ssl3_DisableNonDTLSSuites(sslSocket *ss);
+extern SECStatus dtls_StartHolddownTimer(sslSocket *ss);
+extern void dtls_CheckTimer(sslSocket *ss);
+extern void dtls_CancelTimer(sslSocket *ss, dtlsTimer *timer);
+extern void dtls_SetMTU(sslSocket *ss, PRUint16 advertised);
+extern void dtls_InitRecvdRecords(DTLSRecvdRecords *records);
+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 *spec,
+                              const SSL3Ciphertext *cText,
+                              sslSequenceNumber *seqNum);
+void dtls_ReceivedFirstMessageInFlight(sslSocket *ss);
+#endif
--- a/lib/ssl/manifest.mn
+++ b/lib/ssl/manifest.mn
@@ -15,16 +15,17 @@ EXPORTS = \
         preenc.h \
         $(NULL)
 
 MODULE = nss
 MAPFILE = $(OBJDIR)/ssl.def
 
 CSRCS = \
         dtlscon.c \
+        dtls13con.c \
         prelib.c \
         ssl3con.c \
         ssl3gthr.c \
         sslauth.c \
         sslbloom.c \
         sslcon.c \
         ssldef.c \
         sslencode.c \
--- a/lib/ssl/ssl.gyp
+++ b/lib/ssl/ssl.gyp
@@ -8,16 +8,17 @@
   'targets': [
     {
       'target_name': 'ssl',
       'type': 'static_library',
       'sources': [
         'authcert.c',
         'cmpcert.c',
         'dtlscon.c',
+        'dtls13con.c',
         'prelib.c',
         'selfencrypt.c',
         'ssl3con.c',
         'ssl3ecc.c',
         'ssl3ext.c',
         'ssl3exthandle.c',
         'ssl3gthr.c',
         'sslauth.c',
--- a/lib/ssl/ssl.h
+++ b/lib/ssl/ssl.h
@@ -238,16 +238,19 @@ SSL_IMPORT PRFileDesc *DTLS_ImportFD(PRF
  * time that SSL_ForceHandshake() returns a PR_WOULD_BLOCK_ERROR, use
  * PR_Read() to read all available data.  If PR_Read() is called
  * multiple times, this will result in the handshake completing, but the
  * handshake callback will occur after early data has all been read.
  *
  * WARNING: 0-RTT data has different anti-replay and PFS properties than
  * the rest of the TLS data. See [draft-ietf-tls-tls13; Section 8]
  * for more details.
+ *
+ * Note: when DTLS 1.3 is in use, any 0-RTT data received after EndOfEarlyData
+ * (e.g., because of reordering) is discarded.
  */
 #define SSL_ENABLE_0RTT_DATA 33
 
 #ifdef SSL_DEPRECATED_FUNCTION
 /* Old deprecated function names */
 SSL_IMPORT SECStatus SSL_Enable(PRFileDesc *fd, int option, PRBool on);
 SSL_IMPORT SECStatus SSL_EnableDefault(int option, PRBool on);
 #endif
--- a/lib/ssl/ssl3con.c
+++ b/lib/ssl/ssl3con.c
@@ -622,16 +622,19 @@ ssl3_DecodeContentType(int msgType)
             rv = "alert      (21)";
             break;
         case content_handshake:
             rv = "handshake  (22)";
             break;
         case content_application_data:
             rv = "application_data (23)";
             break;
+        case content_ack:
+            rv = "ack (25)";
+            break;
         default:
             sprintf(line, "*UNKNOWN* record type! (%d)", msgType);
             rv = line;
     }
     return rv;
 }
 
 #endif
@@ -4309,39 +4312,64 @@ ssl3_ConsumeHandshake(sslSocket *ss, voi
 /* Read up the next "bytes" number of bytes from the (decrypted) input
  * stream "b" (which is *length bytes long), and interpret them as an
  * integer in network byte order.  Sets *num to the received value.
  * Reduces *length by bytes.  Advances *b by bytes.
  *
  * On error, an alert has been sent, and a generic error code has been set.
  */
 SECStatus
-ssl3_ConsumeHandshakeNumber(sslSocket *ss, PRUint32 *num, PRUint32 bytes,
-                            PRUint8 **b, PRUint32 *length)
+ssl3_ConsumeHandshakeNumber64(sslSocket *ss, PRUint64 *num, PRUint32 bytes,
+                              PRUint8 **b, PRUint32 *length)
 {
     PRUint8 *buf = *b;
     int i;
 
     PORT_Assert(ss->opt.noLocks || ssl_HaveRecvBufLock(ss));
     PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
 
     *num = 0;
-    if (bytes > *length || bytes > sizeof(*num)) {
+    if (bytes > sizeof(*num)) {
+        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+        return SECFailure;
+    }
+
+    if (bytes > *length) {
         return ssl3_DecodeError(ss);
     }
     PRINT_BUF(60, (ss, "consume bytes:", *b, bytes));
 
     for (i = 0; i < bytes; i++) {
         *num = (*num << 8) + buf[i];
     }
     *b += bytes;
     *length -= bytes;
     return SECSuccess;
 }
 
+SECStatus
+ssl3_ConsumeHandshakeNumber(sslSocket *ss, PRUint32 *num, PRUint32 bytes,
+                            PRUint8 **b, PRUint32 *length)
+{
+    PRUint64 num64;
+    SECStatus rv;
+
+    PORT_Assert(bytes <= sizeof(*num));
+    if (bytes > sizeof(*num)) {
+        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+        return SECFailure;
+    }
+    rv = ssl3_ConsumeHandshakeNumber64(ss, &num64, bytes, b, length);
+    if (rv != SECSuccess) {
+        return SECFailure;
+    }
+    *num = num64 & 0xffffffff;
+    return SECSuccess;
+}
+
 /* Read in two values from the incoming decrypted byte stream "b", which is
  * *length bytes long.  The first value is a number whose size is "bytes"
  * bytes long.  The second value is a byte-string whose size is the value
  * of the first number received.  The latter byte-string, and its length,
  * is returned in the SECItem i.
  *
  * Returns SECFailure (-1) on failure.
  * On error, an alert has been sent, and a generic error code has been set.
@@ -6763,16 +6791,18 @@ ssl3_HandleServerHello(sslSocket *ss, PR
         } else {
             rv = ssl3_HandleExtensions(ss, &extensions.data,
                                        &extensions.len, ssl_hs_server_hello);
             if (rv != SECSuccess)
                 goto alert_loser;
         }
     }
 
+    dtls_ReceivedFirstMessageInFlight(ss);
+
     if (ss->version >= SSL_LIBRARY_VERSION_TLS_1_3) {
         rv = tls13_HandleServerHelloPart2(ss);
         if (rv != SECSuccess) {
             errCode = PORT_GetError();
             goto loser;
         }
     } else {
         rv = ssl3_HandleServerHelloPart2(ss, &sidBytes, &errCode);
@@ -8599,16 +8629,18 @@ ssl3_HandleClientHello(sslSocket *ss, PR
     /* Look for a matching cipher suite. */
     j = ssl3_config_match_init(ss);
     if (j <= 0) {                  /* no ciphers are working/supported by PK11 */
         errCode = PORT_GetError(); /* error code is already set. */
         goto alert_loser;
     }
 #endif
 
+    dtls_ReceivedFirstMessageInFlight(ss);
+
     if (ss->version >= SSL_LIBRARY_VERSION_TLS_1_3) {
         rv = tls13_HandleClientHelloPart2(ss, &suites, sid, savedMsg, savedLen);
     } else {
         rv = ssl3_HandleClientHelloPart2(ss, &suites, &comps, sid,
                                          savedMsg, savedLen);
     }
     if (rv != SECSuccess) {
         errCode = PORT_GetError();
@@ -10603,16 +10635,20 @@ ssl3_HandleCertificate(sslSocket *ss, PR
 
     if ((ss->sec.isServer && ss->ssl3.hs.ws != wait_client_cert) ||
         (!ss->sec.isServer && ss->ssl3.hs.ws != wait_server_cert)) {
         (void)SSL3_SendAlert(ss, alert_fatal, unexpected_message);
         PORT_SetError(SSL_ERROR_RX_UNEXPECTED_CERTIFICATE);
         return SECFailure;
     }
 
+    if (ss->sec.isServer) {
+        dtls_ReceivedFirstMessageInFlight(ss);
+    }
+
     return ssl3_CompleteHandleCertificate(ss, b, length);
 }
 
 /* Called from ssl3_HandleCertificate
  */
 SECStatus
 ssl3_CompleteHandleCertificate(sslSocket *ss, PRUint8 *b, PRUint32 length)
 {
@@ -11368,16 +11404,20 @@ ssl3_HandleFinished(sslSocket *ss, PRUin
                 SSL_GETPID(), ss->fd));
 
     if (ss->ssl3.hs.ws != wait_finished) {
         SSL3_SendAlert(ss, alert_fatal, unexpected_message);
         PORT_SetError(SSL_ERROR_RX_UNEXPECTED_FINISHED);
         return SECFailure;
     }
 
+    if (!ss->sec.isServer || !ss->opt.requestCertificate) {
+        dtls_ReceivedFirstMessageInFlight(ss);
+    }
+
     rv = ssl3_ComputeHandshakeHashes(ss, ss->ssl3.crSpec, &hashes,
                                      isServer ? sender_client : sender_server);
     if (rv != SECSuccess) {
         PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
         return SECFailure;
     }
 
     rv = ssl_HashHandshakeMessage(ss, ssl_hs_finished, b, length);
@@ -12236,56 +12276,57 @@ ssl_CBCExtractMAC(sslBuffer *plaintext,
  * 1. Set |*alert| to the alert to be sent.
  * 2. Call PORT_SetError() with an appropriate code.
  *
  * Called by ssl3_HandleRecord. Caller must hold the spec read lock.
  * Therefore, we MUST not call SSL3_SendAlert().
  *
  */
 static SECStatus
-ssl3_UnprotectRecord(sslSocket *ss, SSL3Ciphertext *cText, sslBuffer *plaintext,
+ssl3_UnprotectRecord(sslSocket *ss,
+                     ssl3CipherSpec *spec,
+                     SSL3Ciphertext *cText, sslBuffer *plaintext,
                      SSL3AlertDescription *alert)
 {
-    ssl3CipherSpec *crSpec = ss->ssl3.crSpec;
-    const ssl3BulkCipherDef *cipher_def = crSpec->cipher_def;
+    const ssl3BulkCipherDef *cipher_def = spec->cipher_def;
     PRBool isTLS;
     unsigned int good;
     unsigned int ivLen = 0;
     SSL3ContentType rType;
     unsigned int minLength;
     unsigned int originalLen = 0;
     unsigned char header[13];
     unsigned int headerLen;
     PRUint8 hash[MAX_MAC_LENGTH];
     PRUint8 givenHashBuf[MAX_MAC_LENGTH];
     PRUint8 *givenHash;
     unsigned int hashBytes = MAX_MAC_LENGTH + 1;
     SECStatus rv;
 
     good = ~0U;
-    minLength = crSpec->mac_size;
+    minLength = spec->mac_size;
     if (cipher_def->type == type_block) {
         /* CBC records have a padding length byte at the end. */
         minLength++;
-        if (crSpec->version >= SSL_LIBRARY_VERSION_TLS_1_1) {
+        if (spec->version >= SSL_LIBRARY_VERSION_TLS_1_1) {
             /* With >= TLS 1.1, CBC records have an explicit IV. */
             minLength += cipher_def->iv_size;
         }
     } else if (cipher_def->type == type_aead) {
         minLength = cipher_def->explicit_nonce_size + cipher_def->tag_size;
     }
 
     /* 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 < minLength) {
         goto decrypt_loser;
     }
 
     if (cipher_def->type == type_block &&
-        crSpec->version >= SSL_LIBRARY_VERSION_TLS_1_1) {
+        spec->version >= SSL_LIBRARY_VERSION_TLS_1_1) {
         /* Consume the per-record explicit IV. RFC 4346 Section 6.2.3.2 states
          * "The receiver decrypts the entire GenericBlockCipher structure and
          * then discards the first cipher block corresponding to the IV
          * component." Instead, we decrypt the first cipher block and then
          * discard it before decrypting the rest.
          */
         PRUint8 iv[MAX_IV_LENGTH];
         int decoded;
@@ -12298,26 +12339,26 @@ ssl3_UnprotectRecord(sslSocket *ss, SSL3
         }
 
         PRINT_BUF(80, (ss, "IV (ciphertext):", cText->buf->buf, ivLen));
 
         /* The decryption result is garbage, but since we just throw away
          * the block it doesn't matter.  The decryption of the next block
          * depends only on the ciphertext of the IV block.
          */
-        rv = crSpec->decode(crSpec->decodeContext, iv, &decoded,
-                            sizeof(iv), cText->buf->buf, ivLen);
+        rv = spec->decode(spec->decodeContext, iv, &decoded,
+                          sizeof(iv), cText->buf->buf, ivLen);
 
         good &= SECStatusToMask(rv);
     }
 
     PRINT_BUF(80, (ss, "ciphertext:", cText->buf->buf + ivLen,
                    cText->buf->len - ivLen));
 
-    isTLS = (PRBool)(crSpec->version > SSL_LIBRARY_VERSION_3_0);
+    isTLS = (PRBool)(spec->version > SSL_LIBRARY_VERSION_3_0);
 
     if (isTLS && cText->buf->len - ivLen > (MAX_FRAGMENT_LENGTH + 2048)) {
         *alert = record_overflow;
         PORT_SetError(SSL_ERROR_RX_RECORD_TOO_LONG);
         return SECFailure;
     }
 
     rType = cText->type;
@@ -12325,21 +12366,21 @@ ssl3_UnprotectRecord(sslSocket *ss, SSL3
         /* XXX For many AEAD ciphers, the plaintext is shorter than the
          * ciphertext by a fixed byte count, but it is not true in general.
          * Each AEAD cipher should provide a function that returns the
          * plaintext length for a given ciphertext. */
         unsigned int decryptedLen =
             cText->buf->len - cipher_def->explicit_nonce_size -
             cipher_def->tag_size;
         headerLen = ssl3_BuildRecordPseudoHeader(
-            header, IS_DTLS(ss) ? cText->seq_num : crSpec->read_seq_num,
+            header, IS_DTLS(ss) ? cText->seq_num : spec->read_seq_num,
             rType, isTLS, cText->version, IS_DTLS(ss), decryptedLen);
         PORT_Assert(headerLen <= sizeof(header));
-        rv = crSpec->aead(
-            ss->sec.isServer ? &crSpec->client : &crSpec->server,
+        rv = spec->aead(
+            ss->sec.isServer ? &spec->client : &spec->server,
             PR_TRUE,                /* do decrypt */
             plaintext->buf,         /* out */
             (int *)&plaintext->len, /* outlen */
             plaintext->space,       /* maxout */
             cText->buf->buf,        /* in */
             cText->buf->len,        /* inlen */
             header, headerLen);
         if (rv != SECSuccess) {
@@ -12347,94 +12388,242 @@ ssl3_UnprotectRecord(sslSocket *ss, SSL3
         }
     } else {
         if (cipher_def->type == type_block &&
             ((cText->buf->len - ivLen) % cipher_def->block_size) != 0) {
             goto decrypt_loser;
         }
 
         /* decrypt from cText buf to plaintext. */
-        rv = crSpec->decode(
-            crSpec->decodeContext, plaintext->buf, (int *)&plaintext->len,
+        rv = spec->decode(
+            spec->decodeContext, plaintext->buf, (int *)&plaintext->len,
             plaintext->space, cText->buf->buf + ivLen, cText->buf->len - ivLen);
         if (rv != SECSuccess) {
             goto decrypt_loser;
         }
 
         PRINT_BUF(80, (ss, "cleartext:", plaintext->buf, plaintext->len));
 
         originalLen = plaintext->len;
 
         /* If it's a block cipher, check and strip the padding. */
         if (cipher_def->type == type_block) {
             const unsigned int blockSize = cipher_def->block_size;
-            const unsigned int macSize = crSpec->mac_size;
+            const unsigned int macSize = spec->mac_size;
 
             if (!isTLS) {
                 good &= SECStatusToMask(ssl_RemoveSSLv3CBCPadding(
                     plaintext, blockSize, macSize));
             } else {
                 good &= SECStatusToMask(ssl_RemoveTLSCBCPadding(
                     plaintext, macSize));
             }
         }
 
         /* compute the MAC */
         headerLen = ssl3_BuildRecordPseudoHeader(
-            header, IS_DTLS(ss) ? cText->seq_num : crSpec->read_seq_num,
+            header, IS_DTLS(ss) ? cText->seq_num : spec->read_seq_num,
             rType, isTLS, cText->version, IS_DTLS(ss),
-            plaintext->len - crSpec->mac_size);
+            plaintext->len - spec->mac_size);
         PORT_Assert(headerLen <= sizeof(header));
         if (cipher_def->type == type_block) {
             rv = ssl3_ComputeRecordMACConstantTime(
-                crSpec, (PRBool)(!ss->sec.isServer), header, headerLen,
+                spec, (PRBool)(!ss->sec.isServer), header, headerLen,
                 plaintext->buf, plaintext->len, originalLen,
                 hash, &hashBytes);
 
             ssl_CBCExtractMAC(plaintext, originalLen, givenHashBuf,
-                              crSpec->mac_size);
+                              spec->mac_size);
             givenHash = givenHashBuf;
 
             /* plaintext->len will always have enough space to remove the MAC
              * because in ssl_Remove{SSLv3|TLS}CBCPadding we only adjust
              * plaintext->len if the result has enough space for the MAC and we
              * tested the unadjusted size against minLength, above. */
-            plaintext->len -= crSpec->mac_size;
+            plaintext->len -= spec->mac_size;
         } else {
             /* This is safe because we checked the minLength above. */
-            plaintext->len -= crSpec->mac_size;
+            plaintext->len -= spec->mac_size;
 
             rv = ssl3_ComputeRecordMAC(
-                crSpec, (PRBool)(!ss->sec.isServer), header, headerLen,
+                spec, (PRBool)(!ss->sec.isServer), header, headerLen,
                 plaintext->buf, plaintext->len, hash, &hashBytes);
 
             /* We can read the MAC directly from the record because its location
              * is public when a stream cipher is used. */
             givenHash = plaintext->buf + plaintext->len;
         }
 
         good &= SECStatusToMask(rv);
 
-        if (hashBytes != (unsigned)crSpec->mac_size ||
-            NSS_SecureMemcmp(givenHash, hash, crSpec->mac_size) != 0) {
+        if (hashBytes != (unsigned)spec->mac_size ||
+            NSS_SecureMemcmp(givenHash, hash, spec->mac_size) != 0) {
             /* We're allowed to leak whether or not the MAC check was correct */
             good = 0;
         }
     }
 
     if (good == 0) {
     decrypt_loser:
         /* always log mac error, in case attacker can read server logs. */
         PORT_SetError(SSL_ERROR_BAD_MAC_READ);
         *alert = bad_record_mac;
         return SECFailure;
     }
     return SECSuccess;
 }
 
+static SECStatus
+ssl3_Decompress(sslSocket *ss, ssl3CipherSpec *crSpec,
+                sslBuffer *plaintext, /* Compressed */
+                sslBuffer *databuf /* Uncompressed, outparam */)
+{
+    SECStatus rv;
+
+    PORT_Assert(ss->version < SSL_LIBRARY_VERSION_TLS_1_3);
+
+    if (databuf->space < plaintext->len + SSL3_COMPRESSION_MAX_EXPANSION) {
+        rv = sslBuffer_Grow(
+            databuf, plaintext->len + SSL3_COMPRESSION_MAX_EXPANSION);
+        if (rv != SECSuccess) {
+            SSL_DBG(("%d: SSL3[%d]: HandleRecord, tried to get %d bytes",
+                     SSL_GETPID(), ss->fd,
+                     plaintext->len +
+                         SSL3_COMPRESSION_MAX_EXPANSION));
+            /* sslBuffer_Grow has set a memory error code. */
+            /* Perhaps we should send an alert. (but we have no memory!) */
+            return SECFailure;
+        }
+    }
+
+    rv = crSpec->decompressor(crSpec->decompressContext,
+                              databuf->buf,
+                              (int *)&databuf->len,
+                              databuf->space,
+                              plaintext->buf,
+                              plaintext->len);
+
+    if (rv != SECSuccess) {
+        int err = ssl_MapLowLevelError(SSL_ERROR_DECOMPRESSION_FAILURE);
+        SSL3_SendAlert(ss, alert_fatal,
+                       (crSpec->version > SSL_LIBRARY_VERSION_3_0) ? decompression_failure : bad_record_mac);
+
+        /* There appears to be a bug with (at least) Apache + OpenSSL where
+         * resumed SSLv3 connections don't actually use compression. See
+         * comments 93-95 of
+         * https://bugzilla.mozilla.org/show_bug.cgi?id=275744
+         *
+         * So, if we get a decompression error, and the record appears to
+         * be already uncompressed, then we return a more specific error
+         * code to hopefully save somebody some debugging time in the
+         * future.
+         */
+        if (plaintext->len >= 4) {
+            unsigned int len = ((unsigned int)plaintext->buf[1] << 16) |
+                               ((unsigned int)plaintext->buf[2] << 8) |
+                               (unsigned int)plaintext->buf[3];
+            if (len == plaintext->len - 4) {
+                /* This appears to be uncompressed already */
+                err = SSL_ERROR_RX_UNEXPECTED_UNCOMPRESSED_RECORD;
+            }
+        }
+
+        PORT_SetError(err);
+        return SECFailure;
+    }
+
+    return SECSuccess;
+}
+
+static SECStatus
+ssl3_HandleNonApplicationData(sslSocket *ss, SSL3ContentType rType,
+                              sslBuffer *databuf)
+{
+    SECStatus rv;
+
+    ssl_GetSSL3HandshakeLock(ss);
+
+    /* Special case: allow alt content type for TLS 1.3 ServerHello. */
+    if ((rType == content_alt_handshake) &&
+        (ss->vrange.max >= SSL_LIBRARY_VERSION_TLS_1_3) &&
+        (ss->ssl3.hs.ws == wait_server_hello) &&
+        (ss->opt.enableAltHandshaketype) &&
+        (!IS_DTLS(ss))) {
+        rType = content_handshake;
+    }
+
+    /* All the functions called in this switch MUST set error code if
+    ** they return SECFailure or SECWouldBlock.
+    */
+    switch (rType) {
+        case content_change_cipher_spec:
+            rv = ssl3_HandleChangeCipherSpecs(ss, databuf);
+            break;
+        case content_alert:
+            rv = ssl3_HandleAlert(ss, databuf);
+            break;
+        case content_handshake:
+            if (!IS_DTLS(ss)) {
+                rv = ssl3_HandleHandshake(ss, databuf);
+            } else {
+                rv = dtls_HandleHandshake(ss, databuf);
+            }
+            break;
+        case content_ack:
+            if (IS_DTLS(ss) && tls13_MaybeTls13(ss)) {
+                rv = dtls13_HandleAck(ss, databuf);
+                break;
+            }
+        /* Fall through. */
+        default:
+            SSL_DBG(("%d: SSL3[%d]: bogus content type=%d",
+                     SSL_GETPID(), ss->fd, rType));
+            PORT_SetError(SSL_ERROR_RX_UNKNOWN_RECORD_TYPE);
+            ssl3_DecodeError(ss);
+            rv = SECFailure;
+            break;
+    }
+
+    ssl_ReleaseSSL3HandshakeLock(ss);
+    return rv;
+}
+
+/* Find the cipher spec to use for a given record. For TLS, this
+ * is the current cipherspec. For DTLS, we look up by epoch.
+ * In DTLS < 1.3 this just means the current epoch or nothing,
+ * but in DTLS >= 1.3, we keep multiple reading cipherspecs.
+ * Returns NULL if no appropriate cipher spec is found.
+ */
+static ssl3CipherSpec *
+ssl3_GetCipherSpec(sslSocket *ss, sslSequenceNumber seq)
+{
+    ssl3CipherSpec *crSpec = ss->ssl3.crSpec;
+    ssl3CipherSpec *newSpec = NULL;
+    DTLSEpoch epoch = seq >> 48;
+
+    if (!IS_DTLS(ss)) {
+        return crSpec;
+    }
+    if (crSpec->epoch == epoch) {
+        return crSpec;
+    }
+    if (ss->version >= SSL_LIBRARY_VERSION_TLS_1_3) {
+        /* Try to find the cipher spec. */
+        newSpec = dtls13_FindCipherSpecByEpoch(ss,
+                                               CipherSpecRead,
+                                               epoch);
+        if (newSpec != NULL) {
+            return newSpec;
+        }
+    }
+    SSL_TRC(10, ("%d: DTLS[%d]: Couldn't find cipherspec from epoch %d",
+                 SSL_GETPID(), ss->fd, epoch));
+    return NULL;
+}
+
 /* if cText is non-null, then decipher, check MAC, and decompress the
  * SSL record from cText->buf (typically gs->inbuf)
  * into databuf (typically gs->buf), and any previous contents of databuf
  * is lost.  Then handle databuf according to its SSL record type,
  * unless it's an application record.
  *
  * If cText is NULL, then the ciphertext has previously been deciphered and
  * checked, and is already sitting in databuf.  It is processed as an SSL
@@ -12453,17 +12642,18 @@ ssl3_UnprotectRecord(sslSocket *ss, SSL3
  * Application Data records.
  */
 SECStatus
 ssl3_HandleRecord(sslSocket *ss, SSL3Ciphertext *cText, sslBuffer *databuf)
 {
     SECStatus rv;
     PRBool isTLS;
     sslSequenceNumber seq_num = 0;
-    ssl3CipherSpec *crSpec;
+    ssl3CipherSpec *spec = NULL;
+    PRBool outOfOrderSpec = PR_FALSE;
     SSL3ContentType rType;
     sslBuffer *plaintext;
     sslBuffer temp_buf = SSL_BUFFER_EMPTY; /* for decompression */
     SSL3AlertDescription alert = internal_error;
     PORT_Assert(ss->opt.noLocks || ssl_HaveRecvBufLock(ss));
 
     if (!ss->ssl3.initialized) {
         ssl_GetSSL3HandshakeLock(ss);
@@ -12479,47 +12669,58 @@ ssl3_HandleRecord(sslSocket *ss, SSL3Cip
 
     /* cText is NULL when we're called from ssl3_RestartHandshakeAfterXXX().
      * This implies that databuf holds a previously deciphered SSL Handshake
      * message.
      */
     if (cText == NULL) {
         SSL_DBG(("%d: SSL3[%d]: HandleRecord, resuming handshake",
                  SSL_GETPID(), ss->fd));
-        rType = content_handshake;
-        goto process_it;
+        return ssl3_HandleNonApplicationData(ss, content_handshake,
+                                             databuf);
     }
 
     ssl_GetSpecReadLock(ss); /******************************************/
-    crSpec = ss->ssl3.crSpec;
-    isTLS = (PRBool)(crSpec->version > SSL_LIBRARY_VERSION_3_0);
-
+    spec = ssl3_GetCipherSpec(ss, cText->seq_num);
+    if (!spec) {
+        PORT_Assert(IS_DTLS(ss));
+        ssl_ReleaseSpecReadLock(ss); /*****************************/
+        databuf->len = 0;            /* Needed to ensure data not left around */
+        return SECSuccess;
+    }
+    if (spec != ss->ssl3.crSpec) {
+        PORT_Assert(IS_DTLS(ss));
+        SSL_TRC(3, ("%d: DTLS[%d]: Handling out-of-epoch record from epoch=",
+                    SSL_GETPID(), ss->fd, spec->epoch));
+        outOfOrderSpec = PR_TRUE;
+    }
+    isTLS = (PRBool)(spec->version > SSL_LIBRARY_VERSION_3_0);
     if (IS_DTLS(ss)) {
-        PRBool sameEpoch;
-        if (!dtls_IsRelevant(ss, cText, &sameEpoch, &seq_num)) {
+        if (!dtls_IsRelevant(ss, spec, cText, &seq_num)) {
             ssl_ReleaseSpecReadLock(ss); /*****************************/
             databuf->len = 0;            /* Needed to ensure data not left around */
 
-            /* Maybe retransmit if needed. */
-            return dtls_MaybeRetransmitHandshake(ss, cText, sameEpoch);
+            return SECSuccess;
         }
     } else {
-        seq_num = crSpec->read_seq_num + 1;
-    }
-    if (seq_num >= crSpec->cipher_def->max_records) {
+        PORT_Assert(spec == ss->ssl3.crSpec);
+        seq_num = spec->read_seq_num + 1;
+    }
+
+    if (seq_num >= spec->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) {
+    if (spec->decompressor) {
         plaintext = &temp_buf;
     } else {
         plaintext = databuf;
     }
     plaintext->len = 0; /* filled in by Unprotect call below. */
 
     /* We're waiting for another ClientHello, which will appear unencrypted.
      * Use the content type to tell whether this is should be discarded.
@@ -12548,22 +12749,22 @@ ssl3_HandleRecord(sslSocket *ss, SSL3Cip
 
 #ifdef UNSAFE_FUZZER_MODE
     rv = Null_Cipher(NULL, plaintext->buf, (int *)&plaintext->len,
                      plaintext->space, cText->buf->buf, cText->buf->len);
 #else
     /* IMPORTANT: Unprotect functions MUST NOT send alerts
      * because we still hold the spec read lock. Instead, if they
      * return SECFailure, they set *alert to the alert to be sent. */
-    if (crSpec->version < SSL_LIBRARY_VERSION_TLS_1_3 ||
-        crSpec->cipher_def->calg == ssl_calg_null) {
+    if (spec->version < SSL_LIBRARY_VERSION_TLS_1_3 ||
+        spec->cipher_def->calg == ssl_calg_null) {
         /* Unencrypted TLS 1.3 records use the pre-TLS 1.3 format. */
-        rv = ssl3_UnprotectRecord(ss, cText, plaintext, &alert);
+        rv = ssl3_UnprotectRecord(ss, spec, cText, plaintext, &alert);
     } else {
-        rv = tls13_UnprotectRecord(ss, cText, plaintext, &alert);
+        rv = tls13_UnprotectRecord(ss, spec, cText, plaintext, &alert);
     }
 #endif
 
     if (rv != SECSuccess) {
         ssl_ReleaseSpecReadLock(ss); /***************************/
 
         SSL_DBG(("%d: SSL3[%d]: decryption failed", SSL_GETPID(), ss->fd));
 
@@ -12582,91 +12783,51 @@ ssl3_HandleRecord(sslSocket *ss, SSL3Cip
             /* Reset the error code in case SSL3_SendAlert called
              * PORT_SetError(). */
             PORT_SetError(errCode);
             return SECFailure;
         }
     }
 
     /* SECSuccess */
-    crSpec->read_seq_num = seq_num;
+    spec->read_seq_num = seq_num;
     if (IS_DTLS(ss)) {
-        dtls_RecordSetRecvd(&crSpec->recvdRecords, seq_num);
+        dtls_RecordSetRecvd(&spec->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
                           * has encrypted content types. */
 
+    /* IMPORTANT: We are in DTLS 1.3 mode and we have processed something
+     * from the wrong epoch. Divert to a divert processing function to make
+     * sure we don't accidentally use the data unsafely. */
+    if (outOfOrderSpec) {
+        PORT_Assert(IS_DTLS(ss) && ss->version >= SSL_LIBRARY_VERSION_TLS_1_3);
+        return dtls13_HandleOutOfEpochRecord(ss, spec, rType, databuf);
+    }
+
     /* possibly decompress the record. If we aren't using compression then
      * plaintext == databuf and so the uncompressed data is already in
      * databuf. */
-    if (crSpec->decompressor) {
-        if (databuf->space < plaintext->len + SSL3_COMPRESSION_MAX_EXPANSION) {
-            rv = sslBuffer_Grow(
-                databuf, plaintext->len + SSL3_COMPRESSION_MAX_EXPANSION);
-            if (rv != SECSuccess) {
-                SSL_DBG(("%d: SSL3[%d]: HandleRecord, tried to get %d bytes",
-                         SSL_GETPID(), ss->fd,
-                         plaintext->len +
-                             SSL3_COMPRESSION_MAX_EXPANSION));
-                /* sslBuffer_Grow has set a memory error code. */
-                /* Perhaps we should send an alert. (but we have no memory!) */
-                sslBuffer_Clear(&temp_buf);
-                return SECFailure;
-            }
-        }
-
-        rv = crSpec->decompressor(crSpec->decompressContext,
-                                  databuf->buf,
-                                  (int *)&databuf->len,
-                                  databuf->space,
-                                  plaintext->buf,
-                                  plaintext->len);
-
-        if (rv != SECSuccess) {
-            int err = ssl_MapLowLevelError(SSL_ERROR_DECOMPRESSION_FAILURE);
-            SSL3_SendAlert(ss, alert_fatal,
-                           isTLS ? decompression_failure
-                                 : bad_record_mac);
-
-            /* There appears to be a bug with (at least) Apache + OpenSSL where
-             * resumed SSLv3 connections don't actually use compression. See
-             * comments 93-95 of
-             * https://bugzilla.mozilla.org/show_bug.cgi?id=275744
-             *
-             * So, if we get a decompression error, and the record appears to
-             * be already uncompressed, then we return a more specific error
-             * code to hopefully save somebody some debugging time in the
-             * future.
-             */
-            if (plaintext->len >= 4) {
-                unsigned int len = ((unsigned int)plaintext->buf[1] << 16) |
-                                   ((unsigned int)plaintext->buf[2] << 8) |
-                                   (unsigned int)plaintext->buf[3];
-                if (len == plaintext->len - 4) {
-                    /* This appears to be uncompressed already */
-                    err = SSL_ERROR_RX_UNEXPECTED_UNCOMPRESSED_RECORD;
-                }
-            }
-
-            sslBuffer_Clear(&temp_buf);
-            PORT_SetError(err);
+    if (spec->decompressor) {
+        rv = ssl3_Decompress(ss, spec, plaintext, databuf);
+        sslBuffer_Clear(&temp_buf);
+        if (rv != SECSuccess) {
             return SECFailure;
         }
-
-        sslBuffer_Clear(&temp_buf);
     }
 
     /*
-    ** Having completed the decompression, check the length again.
+    ** Check the length again (this is checking the post-decompressed
+    ** limit).
     */
     if (isTLS && databuf->len > MAX_FRAGMENT_LENGTH) {
         SSL3_SendAlert(ss, alert_fatal, record_overflow);
         PORT_SetError(SSL_ERROR_RX_RECORD_TOO_LONG);
         return SECFailure;
     }
 
     /* Application data records are processed by the caller of this
@@ -12680,63 +12841,17 @@ ssl3_HandleRecord(sslSocket *ss, SSL3Cip
             ss->ssl3.hs.zeroRttState == ssl_0rtt_accepted) {
             return tls13_HandleEarlyApplicationData(ss, databuf);
         }
         (void)SSL3_SendAlert(ss, alert_fatal, unexpected_message);
         PORT_SetError(SSL_ERROR_RX_UNEXPECTED_APPLICATION_DATA);
         return SECFailure;
     }
 
-/* It's a record that must be handled by ssl itself, not the application.
-    */
-process_it:
-    /* XXX  Get the xmit lock here.  Odds are very high that we'll be xmiting
-     * data ang getting the xmit lock here prevents deadlocks.
-     */
-    ssl_GetSSL3HandshakeLock(ss);
-
-    /* Special case: allow alt content type for TLS 1.3 ServerHello. */
-    if ((rType == content_alt_handshake) &&
-        (ss->vrange.max >= SSL_LIBRARY_VERSION_TLS_1_3) &&
-        (ss->ssl3.hs.ws == wait_server_hello) &&
-        (ss->opt.enableAltHandshaketype) &&
-        (!IS_DTLS(ss))) {
-        rType = content_handshake;
-    }
-    /* All the functions called in this switch MUST set error code if
-    ** they return SECFailure or SECWouldBlock.
-    */
-    switch (rType) {
-        case content_change_cipher_spec:
-            rv = ssl3_HandleChangeCipherSpecs(ss, databuf);
-            break;
-        case content_alert:
-            rv = ssl3_HandleAlert(ss, databuf);
-            break;
-        case content_handshake:
-            if (!IS_DTLS(ss)) {
-                rv = ssl3_HandleHandshake(ss, databuf);
-            } else {
-                rv = dtls_HandleHandshake(ss, databuf);
-            }
-            break;
-        /*
-        case content_application_data is handled before this switch
-        */
-        default:
-            SSL_DBG(("%d: SSL3[%d]: bogus content type=%d",
-                     SSL_GETPID(), ss->fd, cText->type));
-            PORT_SetError(SSL_ERROR_RX_UNKNOWN_RECORD_TYPE);
-            ssl3_DecodeError(ss);
-            rv = SECFailure;
-            break;
-    }
-
-    ssl_ReleaseSSL3HandshakeLock(ss);
-    return rv;
+    return ssl3_HandleNonApplicationData(ss, rType, databuf);
 }
 
 /*
  * Initialization functions
  */
 
 void
 ssl_InitSecState(sslSecurityInfo *sec)
@@ -12820,17 +12935,17 @@ ssl3_InitState(sslSocket *ss)
     ss->ssl3.hs.preliminaryInfo = 0;
     ss->ssl3.hs.ws = (ss->sec.isServer) ? wait_client_hello : wait_server_hello;
 
     ssl3_ResetExtensionData(&ss->xtnData, ss);
     PR_INIT_CLIST(&ss->ssl3.hs.remoteExtensions);
     if (IS_DTLS(ss)) {
         ss->ssl3.hs.sendMessageSeq = 0;
         ss->ssl3.hs.recvMessageSeq = 0;
-        ss->ssl3.hs.rtTimeoutMs = DTLS_RETRANSMIT_INITIAL_MS;
+        ss->ssl3.hs.rtTimer->timeout = DTLS_RETRANSMIT_INITIAL_MS;
         ss->ssl3.hs.rtRetries = 0;
         ss->ssl3.hs.recvdHighWater = -1;
         PR_INIT_CLIST(&ss->ssl3.hs.lastMessageFlight);
         dtls_SetMTU(ss, 0); /* Set the MTU to the highest plateau */
     }
 
     ss->ssl3.hs.currentSecret = NULL;
     ss->ssl3.hs.resumptionMasterSecret = NULL;
--- a/lib/ssl/ssl3gthr.c
+++ b/lib/ssl/ssl3gthr.c
@@ -345,16 +345,19 @@ dtls_GatherData(sslSocket *ss, sslGather
     /* OK, we have at least one complete packet, copy into inbuf */
     if (gs->remainder > gs->inbuf.space) {
         err = sslBuffer_Grow(&gs->inbuf, gs->remainder);
         if (err) { /* realloc has set error code to no mem. */
             return err;
         }
     }
 
+    SSL_TRC(20, ("%d: SSL3[%d]: dtls gathered record type=%d len=%d",
+                 SSL_GETPID(), ss->fd, gs->hdr[0], gs->inbuf.len));
+
     memcpy(gs->inbuf.buf, gs->dtlsPacket.buf + gs->dtlsPacketOffset,
            gs->remainder);
     gs->inbuf.len = gs->remainder;
     gs->offset = gs->remainder;
     gs->dtlsPacketOffset += gs->remainder;
     gs->state = GS_INIT;
 
     return 1;
@@ -550,18 +553,19 @@ ssl3_GatherCompleteHandshake(sslSocket *
              * delivered to the application before the handshake completes. */
             ssl_ReleaseSSL3HandshakeLock(ss);
             PORT_SetError(PR_WOULD_BLOCK_ERROR);
             return SECWouldBlock;
         }
         ssl_ReleaseSSL3HandshakeLock(ss);
     } while (keepGoing);
 
-    /* Service the DTLS timer so that the holddown timer eventually fires. */
-    if (IS_DTLS(ss)) {
+    /* Service the DTLS timer so that the post-handshake timers
+     * fire. */
+    if (IS_DTLS(ss) && (ss->ssl3.hs.ws == idle_handshake)) {
         dtls_CheckTimer(ss);
     }
     ss->gs.readOffset = 0;
     ss->gs.writeOffset = ss->gs.buf.len;
     return 1;
 }
 
 /* Repeatedly gather in a record and when complete, Handle that record.
--- a/lib/ssl/ssl3prot.h
+++ b/lib/ssl/ssl3prot.h
@@ -36,17 +36,18 @@ typedef PRUint16 ssl3CipherSuite;
 
 #define MAX_FRAGMENT_LENGTH 16384
 
 typedef enum {
     content_change_cipher_spec = 20,
     content_alert = 21,
     content_handshake = 22,
     content_application_data = 23,
-    content_alt_handshake = 24
+    content_alt_handshake = 24,
+    content_ack = 25
 } SSL3ContentType;
 
 typedef struct {
     SSL3ContentType type;
     SSL3ProtocolVersion version;
     PRUint16 length;
     SECItem fragment;
 } SSL3Plaintext;
--- a/lib/ssl/sslerr.h
+++ b/lib/ssl/sslerr.h
@@ -249,16 +249,17 @@ typedef enum {
     SSL_ERROR_TOO_MUCH_EARLY_DATA = (SSL_ERROR_BASE + 161),
     SSL_ERROR_RX_UNEXPECTED_END_OF_EARLY_DATA = (SSL_ERROR_BASE + 162),
     SSL_ERROR_RX_MALFORMED_END_OF_EARLY_DATA = (SSL_ERROR_BASE + 163),
 
     SSL_ERROR_UNSUPPORTED_EXPERIMENTAL_API = (SSL_ERROR_BASE + 164),
 
     SSL_ERROR_APPLICATION_ABORT = (SSL_ERROR_BASE + 165),
     SSL_ERROR_APP_CALLBACK_ERROR = (SSL_ERROR_BASE + 166),
+    SSL_ERROR_NO_TIMERS_FOUND = (SSL_ERROR_BASE + 167),
 
     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
@@ -403,18 +403,16 @@ typedef enum { type_stream,
                type_block,
                type_aead } CipherType;
 
 #define MAX_IV_LENGTH 24
 
 typedef PRUint64 sslSequenceNumber;
 typedef PRUint16 DTLSEpoch;
 
-typedef void (*DTLSTimerCb)(sslSocket *);
-
 typedef struct {
     PRUint8 wrapped_master_secret[48];
     PRUint16 wrapped_master_secret_len;
     PRUint8 msIsWrapped;
     PRUint8 resumable;
     PRUint8 extendedMasterSecretUsed;
 } ssl3SidKeys; /* 52 bytes */
 
@@ -462,16 +460,21 @@ typedef SECStatus (*SSLDestroy)(void *co
 PR_STATIC_ASSERT(DTLS_RECVD_RECORDS_WINDOW % 8 == 0);
 
 typedef struct DTLSRecvdRecordsStr {
     unsigned char data[DTLS_RECVD_RECORDS_WINDOW / 8];
     sslSequenceNumber left;
     sslSequenceNumber right;
 } DTLSRecvdRecords;
 
+typedef enum {
+    CipherSpecRead,
+    CipherSpecWrite,
+} CipherSpecDirection;
+
 /*
 ** 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.
 */
 struct ssl3CipherSpecStr {
     PRCList link;
     const ssl3BulkCipherDef *cipher_def;
@@ -499,16 +502,17 @@ struct ssl3CipherSpecStr {
     SECItem msItem;
     DTLSEpoch epoch;
     DTLSRecvdRecords recvdRecords;
     /* The number of 0-RTT bytes that can be sent or received in TLS 1.3. This
      * will be zero for everything but 0-RTT. */
     PRUint32 earlyDataRemaining;
 
     PRUint8 refCt;
+    CipherSpecDirection direction;
     const char *phase;
 };
 
 typedef enum { never_cached,
                in_client_cache,
                in_server_cache,
                invalid_cache /* no longer in any cache. */
 } Cached;
@@ -751,16 +755,26 @@ typedef struct TLS13EarlyDataStr {
 
 typedef enum {
     handshake_hash_unknown = 0,
     handshake_hash_combo = 1,  /* The MD5/SHA-1 combination */
     handshake_hash_single = 2, /* A single hash */
     handshake_hash_record
 } SSL3HandshakeHashType;
 
+// A DTLS Timer.
+typedef void (*DTLSTimerCb)(sslSocket *);
+
+typedef struct {
+    const char *label;
+    DTLSTimerCb cb;
+    PRIntervalTime started;
+    PRUint32 timeout;
+} dtlsTimer;
+
 /*
 ** This is the "hs" member of the "ssl3" struct.
 ** This entire struct is protected by ssl3HandshakeLock
 */
 typedef struct SSL3HandshakeStateStr {
     SSL3Random server_random;
     SSL3Random client_random;
     SSL3WaitState ws; /* May also contain SSL3WaitState | 0x80 for TLS 1.3 */
@@ -816,35 +830,35 @@ typedef struct SSL3HandshakeStateStr {
     PRBool canFalseStart; /* Can/did we False Start */
     /* Which preliminaryinfo values have been set. */
     PRUint32 preliminaryInfo;
 
     /* Parsed extensions */
     PRCList remoteExtensions; /* Parsed incoming extensions */
 
     /* This group of values is used for DTLS */
-    PRUint16 sendMessageSeq;       /* The sending message sequence
+    PRUint16 sendMessageSeq;   /* The sending message sequence
                                     * number */
-    PRCList lastMessageFlight;     /* The last message flight we
+    PRCList lastMessageFlight; /* The last message flight we
                                     * sent */
-    PRUint16 maxMessageSent;       /* The largest message we sent */
-    PRUint16 recvMessageSeq;       /* The receiving message sequence
+    PRUint16 maxMessageSent;   /* The largest message we sent */
+    PRUint16 recvMessageSeq;   /* The receiving message sequence
                                     * number */
-    sslBuffer recvdFragments;      /* The fragments we have received in
+    sslBuffer recvdFragments;  /* The fragments we have received in
                                     * a bitmask */
-    PRInt32 recvdHighWater;        /* The high water mark for fragments
+    PRInt32 recvdHighWater;    /* The high water mark for fragments
                                     * received. -1 means no reassembly
                                     * in progress. */
-    SECItem cookie;                /* The Hello(Retry|Verify)Request cookie. */
-    PRIntervalTime rtTimerStarted; /* When the timer was started */
-    DTLSTimerCb rtTimerCb;         /* The function to call on expiry */
-    PRUint32 rtTimeoutMs;          /* The length of the current timeout
-                                    * used for backoff (in ms) */
-    PRUint32 rtRetries;            /* The retry counter */
-    SECItem srvVirtName;           /* for server: name that was negotiated
+    SECItem cookie;            /* The Hello(Retry|Verify)Request cookie. */
+    dtlsTimer timers[3];       /* Holder for timers. */
+    dtlsTimer *rtTimer;        /* Retransmit timer. */
+    dtlsTimer *ackTimer;       /* Ack timer (DTLS 1.3 only). */
+    dtlsTimer *hdTimer;        /* Read cipher holddown timer (DLTS 1.3 only) */
+    PRUint32 rtRetries;        /* The retry counter */
+    SECItem srvVirtName;       /* for server: name that was negotiated
                                     * with a client. For client - is
                                     * always set to NULL.*/
 
     /* This group of values is used for TLS 1.3 and above */
     PK11SymKey *currentSecret;            /* The secret down the "left hand side"
                                            * of the TLS 1.3 key schedule. */
     PK11SymKey *resumptionMasterSecret;   /* The resumption PSK. */
     PK11SymKey *dheSecret;                /* The (EC)DHE shared secret. */
@@ -866,16 +880,22 @@ typedef struct SSL3HandshakeStateStr {
     PRBool helloRetry;                    /* True if HelloRetryRequest has been sent
                                            * or received. */
     PRBool clientCertRequested;           /* True if CertificateRequest received. */
     ssl3KEADef kea_def_mutable;           /* Used to hold the writable kea_def
                                            * we use for TLS 1.3 */
     PRTime serverHelloTime;               /* Time the ServerHello flight was sent. */
     PRUint16 ticketNonce;                 /* A counter we use for tickets. */
     PRBool altHandshakeType;              /* Alternative ServerHello content type. */
+    PRBool endOfFlight;                   /* Processed a full flight (DTLS 1.3). */
+
+    /* The following lists contain DTLSHandshakeRecordEntry */
+    PRCList dtlsSentHandshake; /* Used to map records to handshake fragments. */
+    PRCList dtlsRcvdHandshake; /* Handshake records we have received
+                                           * used to generate ACKs. */
 } SSL3HandshakeState;
 
 #define SSL_ASSERT_HASHES_EMPTY(ss)                                  \
     do {                                                             \
         PORT_Assert(ss->ssl3.hs.hashType == handshake_hash_unknown); \
         PORT_Assert(ss->ssl3.hs.messages.len == 0);                  \
     } while (0)
 
@@ -1375,16 +1395,19 @@ SECStatus ssl_HashHandshakeMessage(sslSo
  */
 extern PRBool ssl3_WaitingForServerSecondRound(sslSocket *ss);
 
 extern PRInt32 ssl3_SendRecord(sslSocket *ss, ssl3CipherSpec *cwSpec,
                                SSL3ContentType type,
                                const PRUint8 *pIn, PRInt32 nIn,
                                PRInt32 flags);
 
+/* Clear any PRCList, optionally calling f on the value. */
+void ssl_ClearPRCList(PRCList *list, void (*f)(void *));
+
 #ifdef NSS_SSL_ENABLE_ZLIB
 /*
  * The DEFLATE algorithm can result in an expansion of 0.1% + 12 bytes. For a
  * maximum TLS record payload of 2**14 bytes, that's 29 bytes.
  */
 #define SSL3_COMPRESSION_MAX_EXPANSION 29
 #else /* !NSS_SSL_ENABLE_ZLIB */
 #define SSL3_COMPRESSION_MAX_EXPANSION 0
@@ -1673,16 +1696,19 @@ extern void ssl3_DestroyCipherSpec(ssl3C
 extern SECStatus ssl3_InitPendingCipherSpec(sslSocket *ss, PK11SymKey *pms);
 extern SECStatus ssl3_AppendSignatureAndHashAlgorithm(
     sslSocket *ss, const SSLSignatureAndHashAlg *sigAndHash);
 extern SECStatus ssl3_ConsumeHandshake(sslSocket *ss, void *v, PRUint32 bytes,
                                        PRUint8 **b, PRUint32 *length);
 extern SECStatus ssl3_ConsumeHandshakeNumber(sslSocket *ss, PRUint32 *num,
                                              PRUint32 bytes, PRUint8 **b,
                                              PRUint32 *length);
+extern SECStatus ssl3_ConsumeHandshakeNumber64(sslSocket *ss, PRUint64 *num,
+                                               PRUint32 bytes, PRUint8 **b,
+                                               PRUint32 *length);
 extern SECStatus ssl3_ConsumeHandshakeVariable(sslSocket *ss, SECItem *i,
                                                PRUint32 bytes, PRUint8 **b,
                                                PRUint32 *length);
 extern PRBool ssl_IsSupportedSignatureScheme(SSLSignatureScheme scheme);
 extern SECStatus ssl_CheckSignatureSchemeConsistency(
     sslSocket *ss, SSLSignatureScheme scheme, CERTCertificate *cert);
 extern SECStatus ssl_ParseSignatureSchemes(const sslSocket *ss, PLArenaPool *arena,
                                            SSLSignatureScheme **schemesOut,
@@ -1749,48 +1775,16 @@ extern SECStatus SSL3_ShutdownServerCach
 extern SECStatus ssl_InitSymWrapKeysLock(void);
 
 extern SECStatus ssl_FreeSymWrapKeysLock(void);
 
 extern SECStatus ssl_InitSessionCacheLocks(PRBool lazyInit);
 
 extern SECStatus ssl_FreeSessionCacheLocks(void);
 
-/**************** DTLS-specific functions **************/
-extern void dtls_FreeHandshakeMessage(DTLSQueuedMessage *msg);
-extern void dtls_FreeHandshakeMessages(PRCList *lst);
-
-extern SECStatus dtls_HandleHandshake(sslSocket *ss, sslBuffer *origBuf);
-extern SECStatus dtls_HandleHelloVerifyRequest(sslSocket *ss,
-                                               PRUint8 *b, PRUint32 length);
-extern SECStatus dtls_StageHandshakeMessage(sslSocket *ss);
-extern SECStatus dtls_QueueMessage(sslSocket *ss, SSL3ContentType type,
-                                   const PRUint8 *pIn, PRInt32 nIn);
-extern SECStatus dtls_FlushHandshakeMessages(sslSocket *ss, PRInt32 flags);
-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,
-                               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 SSL3Ciphertext *cText,
-                              PRBool *sameEpoch, PRUint64 *seqNum);
-extern SECStatus dtls_MaybeRetransmitHandshake(sslSocket *ss,
-                                               const SSL3Ciphertext *cText,
-                                               PRBool sameEpoch);
-
 CK_MECHANISM_TYPE ssl3_Alg2Mech(SSLCipherAlgorithm calg);
 SECStatus ssl3_NegotiateCipherSuite(sslSocket *ss, const SECItem *suites,
                                     PRBool initHashes);
 SECStatus ssl3_InitHandshakeHashes(sslSocket *ss);
 SECStatus ssl3_ServerCallSNICallback(sslSocket *ss);
 SECStatus ssl3_SetupPendingCipherSpec(sslSocket *ss);
 SECStatus ssl3_FlushHandshake(sslSocket *ss, PRInt32 flags);
 SECStatus ssl3_CompleteHandleCertificate(sslSocket *ss,
@@ -1841,18 +1835,22 @@ SECStatus ssl_PickSignatureScheme(sslSoc
                                   unsigned int peerSchemeCount,
                                   PRBool requireSha1);
 SECOidTag ssl3_HashTypeToOID(SSLHashType hashType);
 SSLHashType ssl_SignatureSchemeToHashType(SSLSignatureScheme scheme);
 KeyType ssl_SignatureSchemeToKeyType(SSLSignatureScheme scheme);
 
 SECStatus ssl3_SetupCipherSuite(sslSocket *ss, PRBool initHashes);
 
+/* Pull in DTLS functions */
+#include "dtlscon.h"
+
 /* Pull in TLS 1.3 functions */
 #include "tls13con.h"
+#include "dtls13con.h"
 
 /********************** misc calls *********************/
 
 #ifdef DEBUG
 extern void ssl3_CheckCipherSuiteOrderConsistency();
 #endif
 
 extern int ssl_MapLowLevelError(int hiLevelError);
--- a/lib/ssl/sslsock.c
+++ b/lib/ssl/sslsock.c
@@ -428,25 +428,24 @@ ssl_DestroySocketContents(sslSocket *ss)
     /* Clean up server certificates and sundries. */
     while (!PR_CLIST_IS_EMPTY(&ss->serverCerts)) {
         cursor = PR_LIST_TAIL(&ss->serverCerts);
         PR_REMOVE_LINK(cursor);
         ssl_FreeServerCert((sslServerCert *)cursor);
     }
 
     /* Remove extension handlers. */
-    while (!PR_CLIST_IS_EMPTY(&ss->extensionHooks)) {
-        cursor = PR_LIST_TAIL(&ss->extensionHooks);
-        PR_REMOVE_LINK(cursor);
-        PORT_Free(cursor);
-    }
+    ssl_ClearPRCList(&ss->extensionHooks, NULL);
 
     ssl_FreeEphemeralKeyPairs(ss);
     SECITEM_FreeItem(&ss->opt.nextProtoNego, PR_FALSE);
     ssl3_FreeSniNameArray(&ss->xtnData);
+
+    ssl_ClearPRCList(&ss->ssl3.hs.dtlsSentHandshake, NULL);
+    ssl_ClearPRCList(&ss->ssl3.hs.dtlsRcvdHandshake, NULL);
 }
 
 /*
  * free an sslSocket struct, and all the stuff that hangs off of it
  */
 void
 ssl_FreeSocket(sslSocket *ss)
 {
@@ -3777,17 +3776,16 @@ ssl_FreeEphemeralKeyPairs(sslSocket *ss)
 ** Create a newsocket structure for a file descriptor.
 */
 static sslSocket *
 ssl_NewSocket(PRBool makeLocks, SSLProtocolVariant protocolVariant)
 {
     SECStatus rv;
     sslSocket *ss;
     int i;
-
     ssl_SetDefaultsFromEnvironment();
 
     if (ssl_force_locks)
         makeLocks = PR_TRUE;
 
     /* Make a new socket and get it ready */
     ss = (sslSocket *)PORT_ZAlloc(sizeof(sslSocket));
     if (!ss) {
@@ -3837,16 +3835,20 @@ ssl_NewSocket(PRBool makeLocks, SSLProto
         ss->namedGroupPreferences[i] = &ssl_named_groups[i];
     }
     ss->additionalShares = 0;
     PR_INIT_CLIST(&ss->ssl3.hs.remoteExtensions);
     PR_INIT_CLIST(&ss->ssl3.hs.lastMessageFlight);
     PR_INIT_CLIST(&ss->ssl3.hs.cipherSpecs);
     PR_INIT_CLIST(&ss->ssl3.hs.bufferedEarlyData);
     ssl3_InitExtensionData(&ss->xtnData, ss);
+    PR_INIT_CLIST(&ss->ssl3.hs.dtlsSentHandshake);
+    PR_INIT_CLIST(&ss->ssl3.hs.dtlsRcvdHandshake);
+    dtls_InitTimers(ss);
+
     if (makeLocks) {
         rv = ssl_MakeLocks(ss);
         if (rv != SECSuccess)
             goto loser;
     }
     rv = ssl_CreateSecurityInfo(ss);
     if (rv != SECSuccess)
         goto loser;
@@ -3922,8 +3924,24 @@ SSL_GetExperimentalAPI(const char *name)
     for (i = 0; i < PR_ARRAY_SIZE(ssl_experimental_functions); ++i) {
         if (strcmp(name, ssl_experimental_functions[i].name) == 0) {
             return ssl_experimental_functions[i].function;
         }
     }
     PORT_SetError(SSL_ERROR_UNSUPPORTED_EXPERIMENTAL_API);
     return NULL;
 }
+
+void
+ssl_ClearPRCList(PRCList *list, void (*f)(void *))
+{
+    PRCList *cursor;
+
+    while (!PR_CLIST_IS_EMPTY(list)) {
+        cursor = PR_LIST_TAIL(list);
+
+        PR_REMOVE_LINK(cursor);
+        if (f) {
+            f(cursor);
+        }
+        PORT_Free(cursor);
+    }
+}
--- a/lib/ssl/tls13con.c
+++ b/lib/ssl/tls13con.c
@@ -19,28 +19,16 @@
 #include "sslerr.h"
 #include "ssl3exthandle.h"
 #include "tls13hkdf.h"
 #include "tls13con.h"
 #include "tls13err.h"
 #include "tls13exthandle.h"
 #include "tls13hashstate.h"
 
-typedef enum {
-    TrafficKeyClearText = 0,
-    TrafficKeyEarlyApplicationData = 1,
-    TrafficKeyHandshake = 2,
-    TrafficKeyApplicationData = 3
-} TrafficKeyType;
-
-typedef enum {
-    CipherSpecRead,
-    CipherSpecWrite,
-} CipherSpecDirection;
-
 static SECStatus tls13_SetCipherSpec(sslSocket *ss, TrafficKeyType type,
                                      CipherSpecDirection install,
                                      PRBool deleteSecret);
 static SECStatus tls13_AESGCM(
     ssl3KeyMaterial *keys,
     PRBool doDecrypt,
     unsigned char *out, int *outlen, int maxout,
     const unsigned char *in, int inlen,
@@ -125,16 +113,17 @@ const char kHkdfLabelEarlyExporterSecret
 const char kHkdfLabelHandshakeTrafficSecret[] = "hs traffic";
 const char kHkdfLabelApplicationTrafficSecret[] = "ap traffic";
 const char kHkdfLabelFinishedSecret[] = "finished";
 const char kHkdfLabelResumptionMasterSecret[] = "res master";
 const char kHkdfLabelExporterMasterSecret[] = "exp master";
 const char kHkdfLabelResumption[] = "resumption";
 const char kHkdfPurposeKey[] = "key";
 const char kHkdfPurposeIv[] = "iv";
+const char kKeyPhaseCleartext[] = "clear";
 
 #define TRAFFIC_SECRET(ss, dir, name) ((ss->sec.isServer ^            \
                                         (dir == CipherSpecWrite))     \
                                            ? ss->ssl3.hs.client##name \
                                            : ss->ssl3.hs.server##name)
 
 const SSL3ProtocolVersion kTlsRecordVersion = SSL_LIBRARY_VERSION_TLS_1_0;
 const SSL3ProtocolVersion kDtlsRecordVersion = SSL_LIBRARY_VERSION_TLS_1_1;
@@ -1334,16 +1323,18 @@ tls13_HandleClientHelloPart2(sslSocket *
     SECStatus rv;
     SSL3Statistics *ssl3stats = SSL_GetStatistics();
     const sslNamedGroupDef *requestedGroup = NULL;
     TLS13KeyShareEntry *clientShare = NULL;
     ssl3CipherSuite previousCipherSuite = 0;
     const sslNamedGroupDef *previousGroup = NULL;
     PRBool hrr = PR_FALSE;
 
+    ss->ssl3.hs.endOfFlight = PR_TRUE;
+
     if (ssl3_ExtensionNegotiated(ss, ssl_tls13_early_data_xtn)) {
         ss->ssl3.hs.zeroRttState = ssl_0rtt_sent;
     }
 
 #ifndef PARANOID
     /* Look for a matching cipher suite. */
     if (ssl3_config_match_init(ss) <= 0) {
         /* no ciphers are working/supported by PK11 */
@@ -1941,16 +1932,18 @@ tls13_HandleHelloRetryRequest(sslSocket 
         tls13_CipherSpecRelease(ss->ssl3.cwSpec);
         ss->ssl3.cwSpec = ss->ssl3.crSpec;
         PORT_Assert(ss->ssl3.cwSpec->cipher_def->cipher == cipher_null);
         ssl_ReleaseSpecWriteLock(ss);
     } else {
         PORT_Assert(ss->ssl3.hs.zeroRttState == ssl_0rtt_none);
     }
 
+    dtls_ReceivedFirstMessageInFlight(ss);
+
     /* Version. */
     rv = ssl_ClientReadVersion(ss, &b, &length, &version);
     if (rv != SECSuccess) {
         return SECFailure; /* alert already sent */
     }
     if (version > ss->vrange.max || version < SSL_LIBRARY_VERSION_TLS_1_3) {
         FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_HELLO_RETRY_REQUEST,
                     protocol_version);
@@ -2195,16 +2188,23 @@ tls13_SendServerHelloSequence(sslSocket 
             LOG_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE);
             return SECFailure;
         }
         TLS13_SET_HS_STATE(ss, wait_end_of_early_data);
     } else {
         PORT_Assert(ss->ssl3.hs.zeroRttState == ssl_0rtt_none ||
                     ss->ssl3.hs.zeroRttState == ssl_0rtt_ignored);
 
+        if (IS_DTLS(ss)) {
+            /* Store the cleartext cipherSpec so we can decrypt ACKs. */
+            dtls13_SaveNullCipherSpec(ss, ss->ssl3.crSpec);
+            if (rv != SECSuccess) {
+                return SECFailure;
+            }
+        }
         rv = tls13_SetCipherSpec(ss,
                                  TrafficKeyHandshake,
                                  CipherSpecRead, PR_FALSE);
         if (rv != SECSuccess) {
             LOG_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE);
             return SECFailure;
         }
         TLS13_SET_HS_STATE(ss,
@@ -2574,16 +2574,21 @@ tls13_HandleCertificate(sslSocket *ss, P
                                   wait_client_cert);
     } else {
         rv = TLS13_CHECK_HS_STATE(ss, SSL_ERROR_RX_UNEXPECTED_CERTIFICATE,
                                   wait_cert_request, wait_server_cert);
     }
     if (rv != SECSuccess)
         return SECFailure;
 
+    /* We can ignore any other cleartext from the client. */
+    if (ss->sec.isServer) {
+        dtls13_ReleaseReadCipherSpec(ss, TrafficKeyClearText);
+        dtls_ReceivedFirstMessageInFlight(ss);
+    }
     /* Process the context string */
     rv = ssl3_ConsumeHandshakeVariable(ss, &context, 1, &b, &length);
     if (rv != SECSuccess)
         return SECFailure;
 
     if (context.len) {
         /* The context string MUST be empty */
         FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_CERTIFICATE, illegal_parameter);
@@ -2676,18 +2681,16 @@ tls13_HandleCertificate(sslSocket *ss, P
 void
 tls13_CipherSpecAddRef(ssl3CipherSpec *spec)
 {
     ++spec->refCt;
     SSL_TRC(10, ("%d: TLS13[-]: Increment ref ct for spec %d. new ct = %d",
                  SSL_GETPID(), spec, spec->refCt));
 }
 
-/* This function is never called on a spec which is on the
- * cipherSpecs list. */
 void
 tls13_CipherSpecRelease(ssl3CipherSpec *spec)
 {
     PORT_Assert(spec->refCt > 0);
     --spec->refCt;
     SSL_TRC(10, ("%d: TLS13[-]: decrement refct for spec %d. phase=%s new ct = %d",
                  SSL_GETPID(), spec, spec->phase, spec->refCt));
     if (!spec->refCt) {
@@ -3014,16 +3017,23 @@ tls13_SetCipherSpec(sslSocket *ss, Traff
 
     /* Create the new spec. */
     spec = PORT_ZNew(ssl3CipherSpec);
     if (!spec) {
         PORT_SetError(SEC_ERROR_NO_MEMORY);
         return SECFailure;
     }
     spec->refCt = 1;
+    /* We want to keep read cipher specs around longer because
+     * there are cases where we might get either epoch N or
+     * epoch N+1. */
+    if (IS_DTLS(ss) && direction == CipherSpecRead) {
+        ++spec->refCt;
+    }
+    spec->direction = direction;
     PR_APPEND_LINK(&spec->link, &ss->ssl3.hs.cipherSpecs);
     ss->ssl3.pwSpec = ss->ssl3.prSpec = spec;
 
     rv = tls13_SetupPendingCipherSpec(ss);
     if (rv != SECSuccess)
         return SECFailure;
 
     switch (spec->cipher_def->calg) {
@@ -3844,134 +3854,138 @@ tls13_VerifyFinished(sslSocket *ss, SSLH
         return SECFailure;
 #endif
     }
 
     return SECSuccess;
 }
 
 static SECStatus
-tls13_ClientHandleFinished(sslSocket *ss, PRUint8 *b, PRUint32 length)
+tls13_CommonHandleFinished(sslSocket *ss, PK11SymKey *key,
+                           PRUint8 *b, PRUint32 length)
 {
     SECStatus rv;
     SSL3Hashes hashes;
 
-    PORT_Assert(ss->opt.noLocks || ssl_HaveRecvBufLock(ss));
-    PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
-
-    SSL_TRC(3, ("%d: TLS13[%d]: client handle finished handshake",
-                SSL_GETPID(), ss->fd));
-
     rv = TLS13_CHECK_HS_STATE(ss, SSL_ERROR_RX_UNEXPECTED_FINISHED,
                               wait_finished);
     if (rv != SECSuccess) {
         return SECFailure;
     }
+    ss->ssl3.hs.endOfFlight = PR_TRUE;
 
     rv = tls13_ComputeHandshakeHashes(ss, &hashes);
     if (rv != SECSuccess) {
         LOG_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE);
         return SECFailure;
     }
 
     rv = ssl_HashHandshakeMessage(ss, ssl_hs_finished, b, length);
     if (rv != SECSuccess) {
         PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
         return SECFailure;
     }
 
-    rv = tls13_VerifyFinished(ss, ssl_hs_finished,
-                              ss->ssl3.hs.serverHsTrafficSecret,
-                              b, length, &hashes);
-    if (rv != SECSuccess)
+    return tls13_VerifyFinished(ss, ssl_hs_finished,
+                                key, b, length, &hashes);
+}
+
+static SECStatus
+tls13_ClientHandleFinished(sslSocket *ss, PRUint8 *b, PRUint32 length)
+{
+    SECStatus rv;
+
+    PORT_Assert(ss->opt.noLocks || ssl_HaveRecvBufLock(ss));
+    PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
+
+    SSL_TRC(3, ("%d: TLS13[%d]: client handle finished handshake",
+                SSL_GETPID(), ss->fd));
+
+    rv = tls13_CommonHandleFinished(ss, ss->ssl3.hs.serverHsTrafficSecret,
+                                    b, length);
+    if (rv != SECSuccess) {
         return SECFailure;
+    }
 
     return tls13_SendClientSecondRound(ss);
 }
 
 static SECStatus
 tls13_ServerHandleFinished(sslSocket *ss, PRUint8 *b, PRUint32 length)
 {
     SECStatus rv;
-    PK11SymKey *secret;
-    SSL3Hashes hashes;
 
     PORT_Assert(ss->opt.noLocks || ssl_HaveRecvBufLock(ss));
     PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
 
     SSL_TRC(3, ("%d: TLS13[%d]: server handle finished handshake",
                 SSL_GETPID(), ss->fd));
 
-    rv = TLS13_CHECK_HS_STATE(ss, SSL_ERROR_RX_UNEXPECTED_FINISHED, wait_finished);
+    rv = tls13_CommonHandleFinished(ss, ss->ssl3.hs.clientHsTrafficSecret,
+                                    b, length);
     if (rv != SECSuccess) {
         return SECFailure;
     }
 
-    if (TLS13_IN_HS_STATE(ss, wait_finished)) {
-        secret = ss->ssl3.hs.clientHsTrafficSecret;
-    } else {
-        secret = ss->ssl3.hs.clientEarlyTrafficSecret;
-    }
-
-    rv = tls13_ComputeHandshakeHashes(ss, &hashes);
-    if (rv != SECSuccess) {
-        LOG_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE);
-        return SECFailure;
-    }
-
-    rv = ssl_HashHandshakeMessage(ss, ssl_hs_finished, b, length);
-    if (rv != SECSuccess) {
-        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
-        return SECFailure;
-    }
-
-    rv = tls13_VerifyFinished(ss, ssl_hs_finished, secret, b, length, &hashes);
-    if (rv != SECSuccess)
-        return SECFailure;
+    if (!ss->opt.requestCertificate &&
+        (ss->ssl3.hs.zeroRttState != ssl_0rtt_done)) {
+        dtls_ReceivedFirstMessageInFlight(ss);
+    }
 
     rv = tls13_SetCipherSpec(ss, TrafficKeyApplicationData,
                              CipherSpecRead, PR_TRUE);
     if (rv != SECSuccess) {
         FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error);
         return SECFailure;
     }
 
-    rv = tls13_FinishHandshake(ss);
+    if (IS_DTLS(ss)) {
+        dtls13_ReleaseReadCipherSpec(ss, TrafficKeyClearText);
+        /* We need to keep the handshake cipher spec so we can
+         * read re-transmitted client Finished. */
+        rv = dtls_StartTimer(ss, ss->ssl3.hs.hdTimer,
+                             DTLS_RETRANSMIT_FINISHED_MS,
+                             dtls13_HolddownTimerCb);
+        if (rv != SECSuccess) {
+            return SECFailure;
+        }
+    }
+
+    rv = tls13_ComputeFinalSecrets(ss);
     if (rv != SECSuccess) {
-        return SECFailure; /* Error code and alerts handled below */
-    }
+        return SECFailure;
+    }
+
     ssl_GetXmitBufLock(ss);
     if (ss->opt.enableSessionTickets) {
         rv = tls13_SendNewSessionTicket(ss, NULL, 0);
         if (rv != SECSuccess) {
-            ssl_ReleaseXmitBufLock(ss);
-            return SECFailure; /* Error code and alerts handled below */
+            goto loser;
         }
         rv = ssl3_FlushHandshake(ss, 0);
+        if (rv != SECSuccess) {
+            goto loser;
+        }
     }
     ssl_ReleaseXmitBufLock(ss);
-    if (rv != SECSuccess)
-        return SECFailure;
-
-    return SECSuccess;
+
+    return tls13_FinishHandshake(ss);
+
+loser:
+    ssl_ReleaseXmitBufLock(ss);
+    return SECFailure;
 }
 
 static SECStatus
 tls13_FinishHandshake(sslSocket *ss)
 {
-    SECStatus rv;
-
     PORT_Assert(ss->opt.noLocks || ssl_HaveRecvBufLock(ss));
     PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
     PORT_Assert(ss->ssl3.hs.restartTarget == NULL);
 
-    rv = tls13_ComputeFinalSecrets(ss);
-    if (rv != SECSuccess)
-        return SECFailure;
-
     /* The first handshake is now completed. */
     ss->handshake = NULL;
 
     /* Don't need this. */
     PK11_FreeSymKey(ss->ssl3.hs.clientHsTrafficSecret);
     ss->ssl3.hs.clientHsTrafficSecret = NULL;
     PK11_FreeSymKey(ss->ssl3.hs.serverHsTrafficSecret);
     ss->ssl3.hs.serverHsTrafficSecret = NULL;
@@ -4027,29 +4041,24 @@ tls13_SendClientSecondFlight(sslSocket *
             return SECFailure; /* err is set. */
         }
     }
 
     rv = tls13_SendFinished(ss, ss->ssl3.hs.clientHsTrafficSecret);
     if (rv != SECSuccess) {
         return SECFailure; /* err code was set. */
     }
-    rv = ssl3_FlushHandshake(ss, IS_DTLS(ss) ? ssl_SEND_FLAG_NO_RETRANSMIT : 0);
+    rv = ssl3_FlushHandshake(ss, 0);
     if (rv != SECSuccess) {
         /* No point in sending an alert here because we're not going to
          * be able to send it if we couldn't flush the handshake. */
         *sendAlert = no_alert;
         return SECFailure;
     }
 
-    rv = dtls_StartHolddownTimer(ss);
-    if (rv != SECSuccess) {
-        return SECFailure; /* err code was set. */
-    }
-
     return SECSuccess;
 }
 
 static SECStatus
 tls13_SendClientSecondRound(sslSocket *ss)
 {
     SECStatus rv;
     PRBool sendClientCert;
@@ -4121,16 +4130,21 @@ tls13_SendClientSecondRound(sslSocket *s
     }
     rv = tls13_SetCipherSpec(ss, TrafficKeyApplicationData,
                              CipherSpecWrite, PR_TRUE);
     if (rv != SECSuccess) {
         PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
         return SECFailure;
     }
 
+    rv = tls13_ComputeFinalSecrets(ss);
+    if (rv != SECSuccess) {
+        return SECFailure;
+    }
+
     /* The handshake is now finished */
     return tls13_FinishHandshake(ss);
 }
 
 /*
  *  enum { (65535) } TicketExtensionType;
  *
  *  struct {
@@ -4610,29 +4624,30 @@ tls13_ProtectRecord(sslSocket *ss,
  * Called by ssl3_HandleRecord. Caller must hold the spec read lock.
  * Therefore, we MUST not call SSL3_SendAlert().
  *
  * If SECFailure is returned, we:
  * 1. Set |*alert| to the alert to be sent.
  * 2. Call PORT_SetError() witn an appropriate code.
  */
 SECStatus
-tls13_UnprotectRecord(sslSocket *ss, SSL3Ciphertext *cText, sslBuffer *plaintext,
+tls13_UnprotectRecord(sslSocket *ss,
+                      ssl3CipherSpec *spec,
+                      SSL3Ciphertext *cText, sslBuffer *plaintext,
                       SSL3AlertDescription *alert)
 {
-    ssl3CipherSpec *crSpec = ss->ssl3.crSpec;
-    const ssl3BulkCipherDef *cipher_def = crSpec->cipher_def;
+    const ssl3BulkCipherDef *cipher_def = spec->cipher_def;
     PRUint8 aad[8];
     SECStatus rv;
 
     *alert = bad_record_mac; /* Default alert for most issues. */
 
     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));
+                SSL_GETPID(), ss->fd, spec, spec->phase,
+                spec->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);
@@ -4658,22 +4673,22 @@ tls13_UnprotectRecord(sslSocket *ss, SSL
                  SSL_GETPID(), ss->fd));
         return SECFailure;
     }
 
     /* Decrypt */
     PORT_Assert(cipher_def->type == type_aead);
     rv = tls13_FormatAdditionalData(aad, sizeof(aad),
                                     IS_DTLS(ss) ? cText->seq_num
-                                                : crSpec->read_seq_num);
+                                                : spec->read_seq_num);
     if (rv != SECSuccess) {
         return SECFailure;
     }
-    rv = crSpec->aead(
-        ss->sec.isServer ? &crSpec->client : &crSpec->server,
+    rv = spec->aead(
+        ss->sec.isServer ? &spec->client : &spec->server,
         PR_TRUE,                /* do decrypt */
         plaintext->buf,         /* out */
         (int *)&plaintext->len, /* outlen */
         plaintext->space,       /* maxout */
         cText->buf->buf,        /* in */
         cText->buf->len,        /* inlen */
         aad, sizeof(aad));
     if (rv != SECSuccess) {
@@ -4702,24 +4717,24 @@ tls13_UnprotectRecord(sslSocket *ss, SSL
         return SECFailure;
     }
 
     /* Record the type. */
     cText->type = plaintext->buf[plaintext->len - 1];
     --plaintext->len;
 
     /* Check that we haven't received too much 0-RTT data. */
-    if (crSpec->epoch == TrafficKeyEarlyApplicationData &&
+    if (spec->epoch == TrafficKeyEarlyApplicationData &&
         cText->type == content_application_data) {
-        if (plaintext->len > crSpec->earlyDataRemaining) {
+        if (plaintext->len > spec->earlyDataRemaining) {
             *alert = unexpected_message;
             PORT_SetError(SSL_ERROR_TOO_MUCH_EARLY_DATA);
             return SECFailure;
         }
-        crSpec->earlyDataRemaining -= plaintext->len;
+        spec->earlyDataRemaining -= plaintext->len;
     }
 
     SSL_TRC(10,
             ("%d: TLS13[%d]: %s received record of length=%d type=%d",
              SSL_GETPID(), ss->fd, SSL_ROLE(ss),
              plaintext->len, cText->type));
 
     return SECSuccess;
@@ -4847,16 +4862,21 @@ tls13_HandleEndOfEarlyData(sslSocket *ss
     PORT_Assert(ss->version >= SSL_LIBRARY_VERSION_TLS_1_3);
 
     rv = TLS13_CHECK_HS_STATE(ss, SSL_ERROR_RX_UNEXPECTED_END_OF_EARLY_DATA,
                               wait_end_of_early_data);
     if (rv != SECSuccess) {
         return SECFailure;
     }
 
+    /* We shouldn't be getting any more application data, and if we do,
+     * because of reordering, we drop it. */
+    dtls13_ReleaseReadCipherSpec(ss, TrafficKeyEarlyApplicationData);
+    dtls_ReceivedFirstMessageInFlight(ss);
+
     PORT_Assert(ss->ssl3.hs.zeroRttState == ssl_0rtt_accepted);
 
     if (length) {
         FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_END_OF_EARLY_DATA, decode_error);
         return SECFailure;
     }
 
     rv = tls13_SetCipherSpec(ss, TrafficKeyHandshake,
@@ -4985,8 +5005,27 @@ SSLExp_UseAltServerHelloType(PRFileDesc 
         PORT_SetError(SEC_ERROR_INVALID_ARGS);
         return SECFailure;
     }
 
     ss->opt.enableAltHandshaketype = enable;
 
     return SECSuccess;
 }
+
+/* This is TLS 1.3 or might negotiate to it. */
+PRBool
+tls13_MaybeTls13(sslSocket *ss)
+{
+    if (ss->version >= SSL_LIBRARY_VERSION_TLS_1_3) {
+        return PR_TRUE;
+    }
+
+    if (ss->vrange.max < SSL_LIBRARY_VERSION_TLS_1_3) {
+        return PR_FALSE;
+    }
+
+    if (!(ss->ssl3.hs.preliminaryInfo & ssl_preinfo_version)) {
+        return PR_TRUE;
+    }
+
+    return PR_FALSE;
+}
--- a/lib/ssl/tls13con.h
+++ b/lib/ssl/tls13con.h
@@ -7,30 +7,33 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef __tls13con_h_
 #define __tls13con_h_
 
 #include "sslexp.h"
 
 typedef enum {
-    StaticSharedSecret,
-    EphemeralSharedSecret
-} SharedSecretType;
+    TrafficKeyClearText = 0,
+    TrafficKeyEarlyApplicationData = 1,
+    TrafficKeyHandshake = 2,
+    TrafficKeyApplicationData = 3
+} TrafficKeyType;
 
 typedef enum {
     tls13_extension_allowed,
     tls13_extension_disallowed,
     tls13_extension_unknown
 } tls13ExtensionStatus;
 
 #define TLS13_MAX_FINISHED_SIZE 64
 
 SECStatus tls13_UnprotectRecord(
-    sslSocket *ss, SSL3Ciphertext *cText, sslBuffer *plaintext,
+    sslSocket *ss, ssl3CipherSpec *spec,
+    SSL3Ciphertext *cText, sslBuffer *plaintext,
     SSL3AlertDescription *alert);
 
 #if defined(WIN32)
 #define __func__ __FUNCTION__
 #endif
 
 void tls13_SetHsState(sslSocket *ss, SSL3WaitState ws,
                       const char *func, const char *file, int line);
@@ -110,10 +113,11 @@ PRBool tls13_IsReplay(const sslSocket *s
 void tls13_AntiReplayRollover(PRTime now);
 SECStatus SSLExp_SetupAntiReplay(PRTime window, unsigned int k,
                                  unsigned int bits);
 
 SECStatus SSLExp_HelloRetryRequestCallback(PRFileDesc *fd,
                                            SSLHelloRetryRequestCallback cb,
                                            void *arg);
 SECStatus SSLExp_UseAltServerHelloType(PRFileDesc *fd, PRBool enable);
+PRBool tls13_MaybeTls13(sslSocket *ss);
 
 #endif /* __tls13con_h_ */