Bug 1420060 - land NSS ceb8b9290b35 UPGRADE_NSS_RELEASE, r=me
authorFranziskus Kiefer <franziskuskiefer@gmail.com>
Fri, 24 Nov 2017 09:00:26 +0100
changeset 393600 0c40802096cdbadf1ca5910b93eb4db29cfc54b0
parent 393599 f9b391e62608afe47cb5d9269a4afee6472bb1d4
child 393601 45e4387bc585d3187d5fd945c2115e75195b0bfa
push id32967
push useraciure@mozilla.com
push dateFri, 24 Nov 2017 22:04:51 +0000
treeherdermozilla-central@4ce67352b2a9 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersme
bugs1420060
milestone59.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1420060 - land NSS ceb8b9290b35 UPGRADE_NSS_RELEASE, r=me MozReview-Commit-ID: KprUV50uNDs
security/nss/TAG-INFO
security/nss/automation/abi-check/expected-report-libnss3.so.txt
security/nss/automation/abi-check/expected-report-libssl3.so.txt
security/nss/automation/abi-check/previous-nss-release
security/nss/automation/taskcluster/docker-hacl/Dockerfile
security/nss/automation/taskcluster/scripts/run_hacl.sh
security/nss/automation/taskcluster/windows/releng.manifest
security/nss/automation/taskcluster/windows/setup.sh
security/nss/automation/taskcluster/windows/setup32.sh
security/nss/automation/taskcluster/windows/setup64.sh
security/nss/build.sh
security/nss/cmd/certcgi/certcgi.c
security/nss/cmd/certutil/keystuff.c
security/nss/cmd/crlutil/crlgen.c
security/nss/cmd/fipstest/fipstest.c
security/nss/cmd/fipstest/runtest.sh
security/nss/cmd/lib/secutil.c
security/nss/cmd/libpkix/pkix/util/test_list2.c
security/nss/cmd/modutil/install-ds.c
security/nss/cmd/modutil/pk11.c
security/nss/cmd/multinit/multinit.c
security/nss/cmd/pk11mode/pk11mode.c
security/nss/cmd/rsaperf/rsaperf.c
security/nss/cmd/rsapoptst/rsapoptst.c
security/nss/cmd/selfserv/selfserv.c
security/nss/cmd/signtool/javascript.c
security/nss/cmd/signtool/signtool.c
security/nss/cmd/smimetools/cmsutil.c
security/nss/cmd/ssltap/ssltap.c
security/nss/coreconf/config.gypi
security/nss/coreconf/config.mk
security/nss/coreconf/coreconf.dep
security/nss/cpputil/databuffer.cc
security/nss/cpputil/databuffer.h
security/nss/cpputil/tls_parser.h
security/nss/fuzz/tls_mutators.cc
security/nss/gtests/common/util.h
security/nss/gtests/freebl_gtest/freebl_gtest.gyp
security/nss/gtests/freebl_gtest/rsa_unittest.cc
security/nss/gtests/nss_bogo_shim/Makefile
security/nss/gtests/nss_bogo_shim/config.json
security/nss/gtests/pk11_gtest/manifest.mn
security/nss/gtests/pk11_gtest/pk11_encrypt_derive_unittest.cc
security/nss/gtests/pk11_gtest/pk11_gtest.gyp
security/nss/gtests/ssl_gtest/Makefile
security/nss/gtests/ssl_gtest/bloomfilter_unittest.cc
security/nss/gtests/ssl_gtest/libssl_internals.c
security/nss/gtests/ssl_gtest/libssl_internals.h
security/nss/gtests/ssl_gtest/manifest.mn
security/nss/gtests/ssl_gtest/ssl_0rtt_unittest.cc
security/nss/gtests/ssl_gtest/ssl_agent_unittest.cc
security/nss/gtests/ssl_gtest/ssl_alths_unittest.cc
security/nss/gtests/ssl_gtest/ssl_auth_unittest.cc
security/nss/gtests/ssl_gtest/ssl_ciphersuite_unittest.cc
security/nss/gtests/ssl_gtest/ssl_custext_unittest.cc
security/nss/gtests/ssl_gtest/ssl_damage_unittest.cc
security/nss/gtests/ssl_gtest/ssl_dhe_unittest.cc
security/nss/gtests/ssl_gtest/ssl_drop_unittest.cc
security/nss/gtests/ssl_gtest/ssl_ecdh_unittest.cc
security/nss/gtests/ssl_gtest/ssl_exporter_unittest.cc
security/nss/gtests/ssl_gtest/ssl_extension_unittest.cc
security/nss/gtests/ssl_gtest/ssl_fragment_unittest.cc
security/nss/gtests/ssl_gtest/ssl_fuzz_unittest.cc
security/nss/gtests/ssl_gtest/ssl_gtest.gyp
security/nss/gtests/ssl_gtest/ssl_hrr_unittest.cc
security/nss/gtests/ssl_gtest/ssl_loopback_unittest.cc
security/nss/gtests/ssl_gtest/ssl_record_unittest.cc
security/nss/gtests/ssl_gtest/ssl_renegotiation_unittest.cc
security/nss/gtests/ssl_gtest/ssl_resumption_unittest.cc
security/nss/gtests/ssl_gtest/ssl_skip_unittest.cc
security/nss/gtests/ssl_gtest/ssl_tls13compat_unittest.cc
security/nss/gtests/ssl_gtest/ssl_version_unittest.cc
security/nss/gtests/ssl_gtest/test_io.cc
security/nss/gtests/ssl_gtest/test_io.h
security/nss/gtests/ssl_gtest/tls_agent.cc
security/nss/gtests/ssl_gtest/tls_agent.h
security/nss/gtests/ssl_gtest/tls_connect.cc
security/nss/gtests/ssl_gtest/tls_connect.h
security/nss/gtests/ssl_gtest/tls_filter.cc
security/nss/gtests/ssl_gtest/tls_filter.h
security/nss/gtests/ssl_gtest/tls_hkdf_unittest.cc
security/nss/gtests/ssl_gtest/tls_protect.cc
security/nss/gtests/ssl_gtest/tls_protect.h
security/nss/help.txt
security/nss/lib/certdb/alg1485.c
security/nss/lib/certdb/crl.c
security/nss/lib/ckfw/builtins/certdata.txt
security/nss/lib/ckfw/builtins/nssckbi.h
security/nss/lib/ckfw/capi/cfind.c
security/nss/lib/cryptohi/seckey.c
security/nss/lib/cryptohi/secsign.c
security/nss/lib/dbm/src/hash.c
security/nss/lib/freebl/Makefile
security/nss/lib/freebl/chacha20.c
security/nss/lib/freebl/ecl/curve25519_64.c
security/nss/lib/freebl/fipsfreebl.c
security/nss/lib/freebl/freebl.gyp
security/nss/lib/freebl/freebl_base.gypi
security/nss/lib/freebl/mpi/README
security/nss/lib/freebl/mpi/mpi-config.h
security/nss/lib/freebl/mpi/mpi.c
security/nss/lib/freebl/poly1305.h
security/nss/lib/freebl/rsa.c
security/nss/lib/freebl/verified/FStar.c
security/nss/lib/freebl/verified/FStar.h
security/nss/lib/freebl/verified/Hacl_Chacha20.c
security/nss/lib/freebl/verified/Hacl_Chacha20.h
security/nss/lib/freebl/verified/Hacl_Curve25519.c
security/nss/lib/freebl/verified/Hacl_Curve25519.h
security/nss/lib/freebl/verified/fstar_uint128.h
security/nss/lib/freebl/verified/hacl_curve25519_64.c
security/nss/lib/freebl/verified/hacl_curve25519_64.h
security/nss/lib/freebl/verified/kremlib.h
security/nss/lib/freebl/verified/kremlib_base.h
security/nss/lib/freebl/verified/specs/Spec.CTR.fst
security/nss/lib/freebl/verified/specs/Spec.Chacha20.fst
security/nss/lib/nss/nss.h
security/nss/lib/pk11wrap/pk11load.c
security/nss/lib/pk11wrap/pk11merge.c
security/nss/lib/pk11wrap/pk11pars.c
security/nss/lib/pk11wrap/pk11pbe.c
security/nss/lib/pk11wrap/pk11pk12.c
security/nss/lib/pk11wrap/pk11util.c
security/nss/lib/pkcs12/p12local.c
security/nss/lib/pkcs7/p7create.c
security/nss/lib/smime/cmsdecode.c
security/nss/lib/smime/cmsencode.c
security/nss/lib/softoken/pkcs11.c
security/nss/lib/softoken/pkcs11c.c
security/nss/lib/softoken/softkver.h
security/nss/lib/softoken/softoknt.h
security/nss/lib/ssl/SSLerrs.h
security/nss/lib/ssl/authcert.c
security/nss/lib/ssl/config.mk
security/nss/lib/ssl/dtls13con.c
security/nss/lib/ssl/dtls13con.h
security/nss/lib/ssl/dtlscon.c
security/nss/lib/ssl/dtlscon.h
security/nss/lib/ssl/manifest.mn
security/nss/lib/ssl/selfencrypt.c
security/nss/lib/ssl/selfencrypt.h
security/nss/lib/ssl/ssl.gyp
security/nss/lib/ssl/ssl.h
security/nss/lib/ssl/ssl3con.c
security/nss/lib/ssl/ssl3ecc.c
security/nss/lib/ssl/ssl3encode.c
security/nss/lib/ssl/ssl3encode.h
security/nss/lib/ssl/ssl3ext.c
security/nss/lib/ssl/ssl3ext.h
security/nss/lib/ssl/ssl3exthandle.c
security/nss/lib/ssl/ssl3exthandle.h
security/nss/lib/ssl/ssl3gthr.c
security/nss/lib/ssl/ssl3prot.h
security/nss/lib/ssl/sslbloom.c
security/nss/lib/ssl/sslbloom.h
security/nss/lib/ssl/sslcert.c
security/nss/lib/ssl/sslencode.c
security/nss/lib/ssl/sslencode.h
security/nss/lib/ssl/sslerr.h
security/nss/lib/ssl/sslexp.h
security/nss/lib/ssl/sslimpl.h
security/nss/lib/ssl/sslinfo.c
security/nss/lib/ssl/sslnonce.c
security/nss/lib/ssl/sslreveal.c
security/nss/lib/ssl/sslsecur.c
security/nss/lib/ssl/sslsnce.c
security/nss/lib/ssl/sslsock.c
security/nss/lib/ssl/sslspec.c
security/nss/lib/ssl/sslspec.h
security/nss/lib/ssl/sslt.h
security/nss/lib/ssl/tls13con.c
security/nss/lib/ssl/tls13con.h
security/nss/lib/ssl/tls13err.h
security/nss/lib/ssl/tls13exthandle.c
security/nss/lib/ssl/tls13exthandle.h
security/nss/lib/ssl/tls13hashstate.c
security/nss/lib/ssl/tls13hashstate.h
security/nss/lib/ssl/tls13hkdf.c
security/nss/lib/ssl/tls13replay.c
security/nss/lib/util/nssrwlk.c
security/nss/lib/util/nssutil.h
security/nss/lib/util/pkcs11uri.c
security/nss/lib/util/quickder.c
security/nss/lib/util/secasn1d.c
security/nss/lib/util/secport.c
security/nss/tests/all.sh
security/nss/tests/cert/cert.sh
security/nss/tests/ssl/ssl.sh
security/nss/tests/ssl_gtests/ssl_gtests.sh
security/nss/tests/tools/TestOldAES128CA.p12
security/nss/tests/tools/tools.sh
--- a/security/nss/TAG-INFO
+++ b/security/nss/TAG-INFO
@@ -1,1 +1,1 @@
-NSS_3_34_BETA5
+ceb8b9290b35
--- a/security/nss/automation/abi-check/expected-report-libnss3.so.txt
+++ b/security/nss/automation/abi-check/expected-report-libnss3.so.txt
@@ -1,11 +0,0 @@
-Functions changes summary: 0 Removed, 0 Changed, 4 Added functions
-Variables changes summary: 0 Removed, 0 Changed, 0 Added variable
-
-4 Added functions:
-
-  'function SECItem* SEC_CreateSignatureAlgorithmParameters(SECItem*, SECOidTag, SECOidTag, const SECItem*, const SECKEYPrivateKey*)'    {SEC_CreateSignatureAlgorithmParameters@@NSS_3.34}
-  'function SECStatus SEC_DerSignDataWithAlgorithmID(SECItem*, const unsigned char*, int, SECKEYPrivateKey*, SECAlgorithmID*)'    {SEC_DerSignDataWithAlgorithmID@@NSS_3.34}
-  'function SECStatus SEC_SignDataWithAlgorithmID(SECItem*, const unsigned char*, int, SECKEYPrivateKey*, SECAlgorithmID*)'    {SEC_SignDataWithAlgorithmID@@NSS_3.34}
-  'function void SGN_NewContextWithAlgorithmID(SECAlgorithmID*, SECKEYPrivateKey*)'    {SGN_NewContextWithAlgorithmID@@NSS_3.34}
-
-
--- a/security/nss/automation/abi-check/expected-report-libssl3.so.txt
+++ b/security/nss/automation/abi-check/expected-report-libssl3.so.txt
@@ -1,15 +0,0 @@
-Functions changes summary: 0 Removed, 1 Changed, 0 Added function
-Variables changes summary: 0 Removed, 0 Changed, 0 Added variable
-
-1 function with some indirect sub-type change:
-
-  [C]'function SECStatus SSL_GetChannelInfo(SSLChannelInfo*, PRUintn)' at sslinfo.c:26:1 has some indirect sub-type changes:
-    parameter 1 of type 'SSLChannelInfo*' has sub-type changes:
-      in pointed to type 'typedef SSLChannelInfo' at sslt.h:288:1:
-        underlying type 'struct SSLChannelInfoStr' at sslt.h:229:1 changed:
-          type size changed from 896 to 960 bits
-          2 data member insertions:
-            'SSLNamedGroup SSLChannelInfoStr::originalKeaGroup', at offset 864 (in bits) at sslt.h:281:1
-            'PRBool SSLChannelInfoStr::resumed', at offset 896 (in bits) at sslt.h:284:1
-
-
--- a/security/nss/automation/abi-check/previous-nss-release
+++ b/security/nss/automation/abi-check/previous-nss-release
@@ -1,1 +1,1 @@
-NSS_3_33_BRANCH
+NSS_3_34_BRANCH
--- a/security/nss/automation/taskcluster/docker-hacl/Dockerfile
+++ b/security/nss/automation/taskcluster/docker-hacl/Dockerfile
@@ -1,70 +1,67 @@
 FROM ubuntu:xenial
 
 MAINTAINER Franziskus Kiefer <franziskuskiefer@gmail.com>
 # Based on the HACL* image from Benjamin Beurdouche and
 # the original F* formula with Daniel Fabian
 
-# Pinned versions of HaCl* (F* and KreMLin are pinned as submodules)
+# Pinned versions of HACL* (F* and KreMLin are pinned as submodules)
 ENV haclrepo https://github.com/mitls/hacl-star.git
 
 # Define versions of dependencies
 ENV opamv 4.04.2
-ENV z3v 4.5.1.1f29cebd4df6-x64-ubuntu-14.04
-ENV haclversion 0030539598cde15d1a0e5f93b32e121f7b7b5a1c
-ENV haclbranch production-nss
+ENV haclversion daa7e159f0adf252b5e6962967bc0f27dbac243b
 
 # Install required packages and set versions
 RUN apt-get -qq update
-RUN apt-get install --yes sudo libssl-dev libsqlite3-dev g++-5 gcc-5 m4 make opam pkg-config python libgmp3-dev cmake curl libtool-bin autoconf
+RUN apt-get install --yes sudo libssl-dev libsqlite3-dev g++-5 gcc-5 m4 make opam pkg-config python libgmp3-dev cmake curl libtool-bin autoconf wget
 RUN update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-5 200
 RUN update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-5 200
 
 # Create user
 RUN useradd -ms /bin/bash worker
 RUN echo "worker ALL=(ALL:ALL) NOPASSWD:ALL" >> /etc/sudoers
 WORKDIR /home/worker
 
 # Add build and test scripts.
 ADD bin /home/worker/bin
 RUN chmod +x /home/worker/bin/*
 USER worker
 
-# Add "known-good" version of Z3
-RUN curl -LO https://github.com/FStarLang/binaries/raw/master/z3-tested/z3-${z3v}.zip
-RUN unzip z3-${z3v}.zip
-RUN rm z3-${z3v}.zip
-RUN mv z3-${z3v} z3
-ENV PATH "/home/worker/z3/bin:$PATH"
-
 # Prepare build (OCaml packages)
 ENV OPAMYES true
 RUN opam init
 RUN echo ". /home/worker/.opam/opam-init/init.sh > /dev/null 2> /dev/null || true" >> .bashrc
 RUN opam switch -v ${opamv}
 RUN opam install ocamlfind batteries sqlite3 fileutils yojson ppx_deriving_yojson zarith pprint menhir ulex process fix wasm stdint
 
-# Get the HaCl* code
+# Get the HACL* code
 RUN git clone ${haclrepo} hacl-star
 RUN git -C hacl-star checkout ${haclversion}
 
 # Prepare submodules, and build, verify, test, and extract c code
-# This caches the extracted c code (pins the HaCl* version). All we need to do
+# This caches the extracted c code (pins the HACL* version). All we need to do
 # on CI now is comparing the code in this docker image with the one in NSS.
-RUN opam config exec -- make -C hacl-star nss -j$(nproc)
+RUN opam config exec -- make -C hacl-star prepare -j$(nproc)
+ENV PATH "/home/worker/hacl-star/dependencies/z3/bin:$PATH"
+RUN make -C hacl-star verify-nss -j$(nproc)
+RUN make -C hacl-star -f Makefile.build snapshots/nss -j$(nproc)
+RUN KOPTS="-funroll-loops 5" make -C hacl-star/code/curve25519 test -j$(nproc)
+RUN make -C hacl-star/code/salsa-family test -j$(nproc)
 
 # Get clang-format-3.9
 RUN curl -LO http://releases.llvm.org/3.9.1/clang+llvm-3.9.1-x86_64-linux-gnu-ubuntu-16.04.tar.xz
 RUN curl -LO http://releases.llvm.org/3.9.1/clang+llvm-3.9.1-x86_64-linux-gnu-ubuntu-16.04.tar.xz.sig
 # Verify the signature.
 RUN gpg --keyserver pool.sks-keyservers.net --recv-keys B6C8F98282B944E3B0D5C2530FC3042E345AD05D
 RUN gpg --verify *.tar.xz.sig
 # Install into /usr/local/.
 RUN sudo tar xJvf *.tar.xz -C /usr/local --strip-components=1
 # Cleanup.
 RUN rm *.tar.xz*
 
 # Cleanup
 RUN rm -rf ~/.ccache ~/.cache
+RUN rm -rf /home/worker/hacl-star/dependencies
 RUN sudo apt-get autoremove -y
 RUN sudo apt-get clean
 RUN sudo apt-get autoclean
--- a/security/nss/automation/taskcluster/scripts/run_hacl.sh
+++ b/security/nss/automation/taskcluster/scripts/run_hacl.sh
@@ -8,17 +8,24 @@ fi
 
 set -e -x -v
 
 # The docker image this is running in has the HACL* and NSS sources.
 # The extracted C code from HACL* is already generated and the HACL* tests were
 # successfully executed.
 
 # Format the extracted C code.
-cd ~/hacl-star/snapshots/nss-production
+cd ~/hacl-star/snapshots/nss
 cp ~/nss/.clang-format .
 find . -type f -name '*.[ch]' -exec clang-format -i {} \+
 
 # These diff commands will return 1 if there are differences and stop the script.
 files=($(find ~/nss/lib/freebl/verified/ -type f -name '*.[ch]'))
 for f in "${files[@]}"; do
     diff $f $(basename "$f")
 done
+
+# Check that the specs didn't change either.
+cd ~/hacl-star/specs
+files=($(find ~/nss/lib/freebl/verified/specs -type f))
+for f in "${files[@]}"; do
+    diff $f $(basename "$f")
+done
--- a/security/nss/automation/taskcluster/windows/releng.manifest
+++ b/security/nss/automation/taskcluster/windows/releng.manifest
@@ -1,15 +1,15 @@
 [
   {
-    "version": "Visual Studio 2015 Update 3 14.0.25425.01 / SDK 10.0.14393.0",
-    "size": 326656969,
-    "digest": "babc414ffc0457d27f5a1ed24a8e4873afbe2f1c1a4075469a27c005e1babc3b2a788f643f825efedff95b79686664c67ec4340ed535487168a3482e68559bc7",
+    "version": "Visual Studio 2017 15.4.2 / SDK 10.0.15063.0",
+    "size": 303146863,
+    "digest": "18700889e6b5e81613b9cf57ce4e0d46a6ee45bb4c5c33bae2604a5275326128775b8a032a1eb178c5db973746d565340c4e36d98375789e1d5bd836ab16ba58",
     "algorithm": "sha512",
-    "filename": "vs2015u3.zip",
+    "filename": "vs2017_15.4.2.zip",
     "unpack": true
   },
   {
     "version": "Ninja 1.7.1",
     "size": 184821,
     "digest": "e4f9a1ae624a2630e75264ba37d396d9c7407d6e6aea3763056210ba6e1387908bd31cf4037a6a3661a418e86c4d2761e0c333e6a3bd0d66549d2b0d72d3f43b",
     "algorithm": "sha512",
     "filename": "ninja171.zip",
--- a/security/nss/automation/taskcluster/windows/setup.sh
+++ b/security/nss/automation/taskcluster/windows/setup.sh
@@ -1,18 +1,18 @@
 #!/usr/bin/env bash
 
 set -v -e -x
 
-export VSPATH="$(pwd)/vs2015u3"
+export VSPATH="$(pwd)/vs2017_15.4.2"
 export NINJA_PATH="$(pwd)/ninja/bin"
 
 export WINDOWSSDKDIR="${VSPATH}/SDK"
 export VS90COMNTOOLS="${VSPATH}/VC"
-export INCLUDE="${VSPATH}/VC/include:${VSPATH}/SDK/Include/10.0.14393.0/ucrt:${VSPATH}/SDK/Include/10.0.14393.0/shared:${VSPATH}/SDK/Include/10.0.14393.0/um"
+export INCLUDE="${VSPATH}/VC/include:${VSPATH}/SDK/Include/10.0.15063.0/ucrt:${VSPATH}/SDK/Include/10.0.15063.0/shared:${VSPATH}/SDK/Include/10.0.15063.0/um"
 
 # Usage: hg_clone repo dir [revision=@]
 hg_clone() {
     repo=$1
     dir=$2
     rev=${3:-@}
     for i in 0 2 5; do
         sleep $i
--- a/security/nss/automation/taskcluster/windows/setup32.sh
+++ b/security/nss/automation/taskcluster/windows/setup32.sh
@@ -1,10 +1,10 @@
 #!/usr/bin/env bash
 
 set -v -e -x
 
 source $(dirname $0)/setup.sh
 
-export WIN32_REDIST_DIR="${VSPATH}/VC/redist/x86/Microsoft.VC140.CRT"
+export WIN32_REDIST_DIR="${VSPATH}/VC/redist/x86/Microsoft.VC141.CRT"
 export WIN_UCRT_REDIST_DIR="${VSPATH}/SDK/Redist/ucrt/DLLs/x86"
-export PATH="${NINJA_PATH}:${VSPATH}/VC/bin/amd64_x86:${VSPATH}/VC/bin/amd64:${VSPATH}/VC/bin:${VSPATH}/SDK/bin/x86:${VSPATH}/SDK/bin/x64:${VSPATH}/VC/redist/x86/Microsoft.VC140.CRT:${VSPATH}/VC/redist/x64/Microsoft.VC140.CRT:${VSPATH}/SDK/Redist/ucrt/DLLs/x86:${VSPATH}/SDK/Redist/ucrt/DLLs/x64:${PATH}"
-export LIB="${VSPATH}/VC/lib:${VSPATH}/SDK/lib/10.0.14393.0/ucrt/x86:${VSPATH}/SDK/lib/10.0.14393.0/um/x86"
+export PATH="${NINJA_PATH}:${VSPATH}/VC/bin/Hostx64/x86:${VSPATH}/VC/bin/Hostx64/x64:${VSPATH}/VC/Hostx86/x86:${VSPATH}/SDK/bin/10.0.15063.0/x64:${VSPATH}/VC/redist/x86/Microsoft.VC141.CRT:${VSPATH}/SDK/Redist/ucrt/DLLs/x86:${PATH}"
+export LIB="${VSPATH}/VC/lib/x86:${VSPATH}/SDK/lib/10.0.15063.0/ucrt/x86:${VSPATH}/SDK/lib/10.0.15063.0/um/x86"
--- a/security/nss/automation/taskcluster/windows/setup64.sh
+++ b/security/nss/automation/taskcluster/windows/setup64.sh
@@ -1,10 +1,10 @@
 #!/usr/bin/env bash
 
 set -v -e -x
 
 source $(dirname $0)/setup.sh
 
-export WIN32_REDIST_DIR="${VSPATH}/VC/redist/x64/Microsoft.VC140.CRT"
+export WIN32_REDIST_DIR="${VSPATH}/VC/redist/x64/Microsoft.VC141.CRT"
 export WIN_UCRT_REDIST_DIR="${VSPATH}/SDK/Redist/ucrt/DLLs/x64"
-export PATH="${NINJA_PATH}:${VSPATH}/VC/bin/amd64:${VSPATH}/VC/bin:${VSPATH}/SDK/bin/x64:${VSPATH}/VC/redist/x64/Microsoft.VC140.CRT:${VSPATH}/SDK/Redist/ucrt/DLLs/x64:${PATH}"
-export LIB="${VSPATH}/VC/lib/amd64:${VSPATH}/SDK/lib/10.0.14393.0/ucrt/x64:${VSPATH}/SDK/lib/10.0.14393.0/um/x64"
+export PATH="${NINJA_PATH}:${VSPATH}/VC/bin/Hostx64/x64:${VSPATH}/VC/bin/Hostx86/x86:${VSPATH}/SDK/bin/10.0.15063.0/x64:${VSPATH}/VC/redist/x64/Microsoft.VC141.CRT:${VSPATH}/SDK/Redist/ucrt/DLLs/x64:${PATH}"
+export LIB="${VSPATH}/VC/lib/x64:${VSPATH}/SDK/lib/10.0.15063.0/ucrt/x64:${VSPATH}/SDK/lib/10.0.15063.0/um/x64"
--- a/security/nss/build.sh
+++ b/security/nss/build.sh
@@ -63,16 +63,17 @@ if [ "$arch" = "x64" -o "$arch" = "aarch
 elif [ "$arch" = "arm" ]; then
     armhf=1
 fi
 
 # parse command line arguments
 while [ $# -gt 0 ]; do
     case $1 in
         -c) clean=1 ;;
+        -cc) clean_only=1 ;;
         --gyp|-g) rebuild_gyp=1 ;;
         --nspr) nspr_clean; rebuild_nspr=1 ;;
         -j) ninja_params+=(-j "$2"); shift ;;
         -v) ninja_params+=(-v); verbose=1 ;;
         --test) gyp_params+=(-Dtest_build=1) ;;
         --clang) export CC=clang; export CCC=clang++; export CXX=clang++ ;;
         --gcc) export CC=gcc; export CCC=g++; export CXX=g++ ;;
         --fuzz) fuzz=1 ;;
@@ -119,20 +120,25 @@ fi
 # set paths
 target_dir="$cwd"/out/$target
 mkdir -p "$target_dir"
 dist_dir="$cwd"/../dist
 dist_dir=$(mkdir -p "$dist_dir"; cd "$dist_dir"; pwd -P)
 gyp_params+=(-Dnss_dist_dir="$dist_dir")
 
 # -c = clean first
-if [ "$clean" = 1 ]; then
+if [ "$clean" = 1 -o "$clean_only" = 1 ]; then
     nspr_clean
     rm -rf "$cwd"/out
     rm -rf "$dist_dir"
+    # -cc = only clean, don't build
+    if [ "$clean_only" = 1 ]; then
+        echo "Cleaned"
+        exit 0
+    fi
 fi
 
 # This saves a canonical representation of arguments that we are passing to gyp
 # or the NSPR build so that we can work out if a rebuild is needed.
 # Caveat: This can fail for arguments that are position-dependent.
 # e.g., "-e 2 -f 1" and "-e 1 -f 2" canonicalize the same.
 check_config()
 {
--- a/security/nss/cmd/certcgi/certcgi.c
+++ b/security/nss/cmd/certcgi/certcgi.c
@@ -228,18 +228,17 @@ make_datastruct(char *data, int len)
     datastruct = current = (Pair *)PORT_Alloc(fields * sizeof(Pair));
     if (datastruct == NULL) {
         error_allocate();
     }
     while (curr_pos - data < len) {
         if (remaining == 1) {
             remaining += fields;
             fields = fields * 2;
-            datastruct = (Pair *)PORT_Realloc(datastruct, fields *
-                                                              sizeof(Pair));
+            datastruct = (Pair *)PORT_Realloc(datastruct, fields * sizeof(Pair));
             if (datastruct == NULL) {
                 error_allocate();
             }
             current = datastruct + (fields - remaining);
         }
         *current = make_pair(curr_pos);
         while (*curr_pos != '&') {
             ++curr_pos;
--- a/security/nss/cmd/certutil/keystuff.c
+++ b/security/nss/cmd/certutil/keystuff.c
@@ -571,18 +571,17 @@ CERTUTIL_GeneratePrivateKey(KeyType keyt
         default:
             return NULL;
     }
 
     fprintf(stderr, "\n\n");
     fprintf(stderr, "Generating key.  This may take a few moments...\n\n");
 
     privKey = PK11_GenerateKeyPairWithOpFlags(slot, mechanism, params, pubkeyp,
-                                              attrFlags, opFlagsOn, opFlagsOn |
-                                                                        opFlagsOff,
+                                              attrFlags, opFlagsOn, opFlagsOn | opFlagsOff,
                                               pwdata /*wincx*/);
     /* free up the params */
     switch (keytype) {
         case dsaKey:
             if (dsaparams)
                 CERTUTIL_DestroyParamsPQG(dsaparams);
             break;
         case ecKey:
--- a/security/nss/cmd/crlutil/crlgen.c
+++ b/security/nss/cmd/crlutil/crlgen.c
@@ -611,18 +611,17 @@ crlgen_CreateInvalidityDate(PLArenaPool 
     length = PORT_Strlen(dataArr[2]);
 
     encodedItem->type = siGeneralizedTime;
     encodedItem->data = PORT_ArenaAlloc(arena, length);
     if (!encodedItem->data) {
         goto loser;
     }
 
-    PORT_Memcpy(encodedItem->data, dataArr[2], (encodedItem->len = length) *
-                                                   sizeof(char));
+    PORT_Memcpy(encodedItem->data, dataArr[2], (encodedItem->len = length) * sizeof(char));
 
     *extCode = SEC_OID_X509_INVALID_DATE;
     return encodedItem;
 
 loser:
     if (mark) {
         PORT_ArenaRelease(arena, mark);
     }
--- a/security/nss/cmd/fipstest/fipstest.c
+++ b/security/nss/cmd/fipstest/fipstest.c
@@ -5917,18 +5917,17 @@ tls(char *reqfn)
             crv = NSC_CreateObject(session, create_template,
                                    create_template_count, &pms_handle);
             if (crv != CKR_OK) {
                 fprintf(stderr, "NSC_CreateObject failed crv=0x%x\n",
                         (unsigned int)crv);
                 goto loser;
             }
             crv = NSC_DeriveKey(session, &master_mech, pms_handle,
-                                derive_template, derive_template_count -
-                                                     1,
+                                derive_template, derive_template_count - 1,
                                 &master_handle);
             if (crv != CKR_OK) {
                 fprintf(stderr, "NSC_DeriveKey(master) failed crv=0x%x\n",
                         (unsigned int)crv);
                 goto loser;
             }
             crv = NSC_GetAttributeValue(session, master_handle,
                                         &master_template, 1);
--- a/security/nss/cmd/fipstest/runtest.sh
+++ b/security/nss/cmd/fipstest/runtest.sh
@@ -2,16 +2,13 @@
 # 
 # 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/.
 #
 TESTDIR=${1-.}
 COMMAND=${2-run}
 TESTS="aes aesgcm dsa ecdsa hmac tls rng rsa sha tdea"
-if [ ${NSS_ENABLE_ECC}x = 1x ]; then
-   TESTS=${TESTS} ecdsa
-fi
 for i in $TESTS
 do
     echo "********************Running $i tests"
     sh ./${i}.sh ${TESTDIR} ${COMMAND}
 done
--- a/security/nss/cmd/lib/secutil.c
+++ b/security/nss/cmd/lib/secutil.c
@@ -235,17 +235,18 @@ SECU_GetModulePassword(PK11SlotInfo *slo
                     PK11_GetTokenName(slot));
             return SECU_GetPasswordString(NULL, prompt);
         case PW_FROMFILE:
             return SECU_FilePasswd(slot, retry, pwdata->data);
         case PW_EXTERNAL:
             sprintf(prompt,
                     "Press Enter, then enter PIN for \"%s\" on external device.\n",
                     PK11_GetTokenName(slot));
-            (void)SECU_GetPasswordString(NULL, prompt);
+            char *pw = SECU_GetPasswordString(NULL, prompt);
+            PORT_Free(pw);
         /* Fall Through */
         case PW_PLAINTEXT:
             return PL_strdup(pwdata->data);
         default:
             break;
     }
 
     PR_fprintf(PR_STDERR, "Password check failed:  No password found.\n");
@@ -1187,17 +1188,17 @@ secu_PrintRSAPSSParams(FILE *out, SECIte
                 SECU_Indent(out, level + 1);
                 fprintf(out, "Invalid mask generation algorithm parameters\n");
             }
         }
         if (!param.saltLength.data) {
             SECU_Indent(out, level + 1);
             fprintf(out, "Salt length: default, %i (0x%2X)\n", 20, 20);
         } else {
-            SECU_PrintInteger(out, &param.saltLength, "Salt Length", level + 1);
+            SECU_PrintInteger(out, &param.saltLength, "Salt length", level + 1);
         }
     } else {
         SECU_Indent(out, level + 1);
         fprintf(out, "Invalid RSA-PSS parameters\n");
     }
     PORT_FreeArena(pool, PR_FALSE);
 }
 
--- a/security/nss/cmd/libpkix/pkix/util/test_list2.c
+++ b/security/nss/cmd/libpkix/pkix/util/test_list2.c
@@ -73,26 +73,24 @@ test_list2(int argc, char *argv[])
     }
     PKIX_TEST_DECREF_BC(testString);
 
     subTest("Performing Bubble Sort");
 
     for (i = 0; i < size; i++)
         for (j = 9; j > i; j--) {
             PKIX_TEST_EXPECT_NO_ERROR(PKIX_List_GetItem(list, j, &obj, plContext));
-            PKIX_TEST_EXPECT_NO_ERROR(PKIX_List_GetItem(list, j -
-                                                                  1,
+            PKIX_TEST_EXPECT_NO_ERROR(PKIX_List_GetItem(list, j - 1,
                                                         &obj2, plContext));
 
             PKIX_TEST_EXPECT_NO_ERROR(PKIX_PL_Object_Compare(obj, obj2, &cmpResult, plContext));
             if (cmpResult < 0) {
                 /* Exchange the items */
                 PKIX_TEST_EXPECT_NO_ERROR(PKIX_List_SetItem(list, j, obj2, plContext));
-                PKIX_TEST_EXPECT_NO_ERROR(PKIX_List_SetItem(list, j -
-                                                                      1,
+                PKIX_TEST_EXPECT_NO_ERROR(PKIX_List_SetItem(list, j - 1,
                                                             obj, plContext));
             }
             /* DecRef objects */
             PKIX_TEST_DECREF_BC(obj);
             PKIX_TEST_DECREF_BC(obj2);
         }
 
     subTest("Outputting Sorted List");
--- a/security/nss/cmd/modutil/install-ds.c
+++ b/security/nss/cmd/modutil/install-ds.c
@@ -970,18 +970,17 @@ Pk11Install_Platform_Print(Pk11Install_P
     PAD(pad);
     if (_this->usesEquiv) {
         printf("Uses equiv, which points to:\n");
         Pk11Install_Platform_Print(_this->equiv, pad + PADINC);
     } else {
         printf("Doesn't use equiv\n");
     }
     PAD(pad);
-    printf("Module File: %s\n", _this->moduleFile ? _this->moduleFile
-                                                  : "<NULL>");
+    printf("Module File: %s\n", _this->moduleFile ? _this->moduleFile : "<NULL>");
     PAD(pad);
     printf("mechFlags: %lx\n", _this->mechFlags);
     PAD(pad);
     printf("cipherFlags: %lx\n", _this->cipherFlags);
     PAD(pad);
     printf("Files:\n");
     for (i = 0; i < _this->numFiles; i++) {
         Pk11Install_File_Print(&_this->files[i], pad + PADINC);
--- a/security/nss/cmd/modutil/pk11.c
+++ b/security/nss/cmd/modutil/pk11.c
@@ -723,17 +723,17 @@ ChangePW(char *tokenName, char *pwFile, 
     if (!PK11_NeedUserInit(slot)) {
         if (pwFile) {
             oldpw = SECU_FilePasswd(NULL, PR_FALSE, pwFile);
             if (PK11_CheckUserPassword(slot, oldpw) != SECSuccess) {
                 PR_fprintf(PR_STDERR, errStrings[BAD_PW_ERR]);
                 ret = BAD_PW_ERR;
                 goto loser;
             }
-        } else {
+        } else if (PK11_NeedLogin(slot)) {
             for (matching = PR_FALSE; !matching;) {
                 oldpw = SECU_GetPasswordString(NULL, "Enter old password: ");
                 if (PK11_CheckUserPassword(slot, oldpw) == SECSuccess) {
                     matching = PR_TRUE;
                 } else {
                     PR_fprintf(PR_STDOUT, msgStrings[BAD_PW_MSG]);
                 }
             }
--- a/security/nss/cmd/multinit/multinit.c
+++ b/security/nss/cmd/multinit/multinit.c
@@ -497,18 +497,17 @@ do_list_certs(const char *progName, int 
 
     for (node = CERT_LIST_HEAD(sorted); !CERT_LIST_END(node, sorted);
          node = CERT_LIST_NEXT(node)) {
         CERTCertificate *cert = node->cert;
         char *commonName;
 
         SECU_PrintCertNickname(node, stderr);
         if (log) {
-            fprintf(stderr, "*	Slot=%s*\n", cert->slot ? PK11_GetTokenName(cert->slot)
-                                                        : "none");
+            fprintf(stderr, "*	Slot=%s*\n", cert->slot ? PK11_GetTokenName(cert->slot) : "none");
             fprintf(stderr, "*	Nickname=%s*\n", cert->nickname);
             fprintf(stderr, "*	Subject=<%s>*\n", cert->subjectName);
             fprintf(stderr, "*	Issuer=<%s>*\n", cert->issuerName);
             fprintf(stderr, "*	SN=");
             for (i = 0; i < cert->serialNumber.len; i++) {
                 if (i != 0)
                     fprintf(stderr, ":");
                 fprintf(stderr, "%02x", cert->serialNumber.data[0]);
--- a/security/nss/cmd/pk11mode/pk11mode.c
+++ b/security/nss/cmd/pk11mode/pk11mode.c
@@ -2164,46 +2164,32 @@ PKM_Mechanism(CK_FUNCTION_LIST_PTR pFunc
         if (!mechName)
             mechName = "Unknown mechanism";
         PKM_LogIt("    [%lu]: CK_MECHANISM_TYPE = %s 0x%08lX\n", (i + 1),
                   mechName,
                   pMechanismList[i]);
         PKM_LogIt("    ulMinKeySize = %lu\n", minfo.ulMinKeySize);
         PKM_LogIt("    ulMaxKeySize = %lu\n", minfo.ulMaxKeySize);
         PKM_LogIt("    flags = 0x%08x\n", minfo.flags);
-        PKM_LogIt("        -> HW = %s\n", minfo.flags & CKF_HW ? "TRUE"
-                                                               : "FALSE");
-        PKM_LogIt("        -> ENCRYPT = %s\n", minfo.flags & CKF_ENCRYPT ? "TRUE"
-                                                                         : "FALSE");
-        PKM_LogIt("        -> DECRYPT = %s\n", minfo.flags & CKF_DECRYPT ? "TRUE"
-                                                                         : "FALSE");
-        PKM_LogIt("        -> DIGEST = %s\n", minfo.flags & CKF_DIGEST ? "TRUE"
-                                                                       : "FALSE");
-        PKM_LogIt("        -> SIGN = %s\n", minfo.flags & CKF_SIGN ? "TRUE"
-                                                                   : "FALSE");
-        PKM_LogIt("        -> SIGN_RECOVER = %s\n", minfo.flags &
-                                                            CKF_SIGN_RECOVER
-                                                        ? "TRUE"
-                                                        : "FALSE");
-        PKM_LogIt("        -> VERIFY = %s\n", minfo.flags & CKF_VERIFY ? "TRUE"
-                                                                       : "FALSE");
+        PKM_LogIt("        -> HW = %s\n", minfo.flags & CKF_HW ? "TRUE" : "FALSE");
+        PKM_LogIt("        -> ENCRYPT = %s\n", minfo.flags & CKF_ENCRYPT ? "TRUE" : "FALSE");
+        PKM_LogIt("        -> DECRYPT = %s\n", minfo.flags & CKF_DECRYPT ? "TRUE" : "FALSE");
+        PKM_LogIt("        -> DIGEST = %s\n", minfo.flags & CKF_DIGEST ? "TRUE" : "FALSE");
+        PKM_LogIt("        -> SIGN = %s\n", minfo.flags & CKF_SIGN ? "TRUE" : "FALSE");
+        PKM_LogIt("        -> SIGN_RECOVER = %s\n", minfo.flags & CKF_SIGN_RECOVER ? "TRUE" : "FALSE");
+        PKM_LogIt("        -> VERIFY = %s\n", minfo.flags & CKF_VERIFY ? "TRUE" : "FALSE");
         PKM_LogIt("        -> VERIFY_RECOVER = %s\n",
                   minfo.flags & CKF_VERIFY_RECOVER ? "TRUE" : "FALSE");
-        PKM_LogIt("        -> GENERATE = %s\n", minfo.flags & CKF_GENERATE ? "TRUE"
-                                                                           : "FALSE");
+        PKM_LogIt("        -> GENERATE = %s\n", minfo.flags & CKF_GENERATE ? "TRUE" : "FALSE");
         PKM_LogIt("        -> GENERATE_KEY_PAIR = %s\n",
                   minfo.flags & CKF_GENERATE_KEY_PAIR ? "TRUE" : "FALSE");
-        PKM_LogIt("        -> WRAP = %s\n", minfo.flags & CKF_WRAP ? "TRUE"
-                                                                   : "FALSE");
-        PKM_LogIt("        -> UNWRAP = %s\n", minfo.flags & CKF_UNWRAP ? "TRUE"
-                                                                       : "FALSE");
-        PKM_LogIt("        -> DERIVE = %s\n", minfo.flags & CKF_DERIVE ? "TRUE"
-                                                                       : "FALSE");
-        PKM_LogIt("        -> EXTENSION = %s\n", minfo.flags & CKF_EXTENSION ? "TRUE"
-                                                                             : "FALSE");
+        PKM_LogIt("        -> WRAP = %s\n", minfo.flags & CKF_WRAP ? "TRUE" : "FALSE");
+        PKM_LogIt("        -> UNWRAP = %s\n", minfo.flags & CKF_UNWRAP ? "TRUE" : "FALSE");
+        PKM_LogIt("        -> DERIVE = %s\n", minfo.flags & CKF_DERIVE ? "TRUE" : "FALSE");
+        PKM_LogIt("        -> EXTENSION = %s\n", minfo.flags & CKF_EXTENSION ? "TRUE" : "FALSE");
 
         PKM_LogIt("\n");
     }
 
     return crv;
 }
 
 CK_RV
@@ -3599,34 +3585,22 @@ PKM_FindAllObjects(CK_FUNCTION_LIST_PTR 
         return crv;
     }
 
     PKM_LogIt("    SESSION INFO:\n");
     PKM_LogIt("        slotID = %lu\n", sinfo.slotID);
     PKM_LogIt("        state = %lu\n", sinfo.state);
     PKM_LogIt("        flags = 0x%08x\n", sinfo.flags);
 #ifdef CKF_EXCLUSIVE_SESSION
-    PKM_LogIt("            -> EXCLUSIVE SESSION = %s\n", sinfo.flags &
-                                                                 CKF_EXCLUSIVE_SESSION
-                                                             ? "TRUE"
-                                                             : "FALSE");
+    PKM_LogIt("            -> EXCLUSIVE SESSION = %s\n", sinfo.flags & CKF_EXCLUSIVE_SESSION ? "TRUE" : "FALSE");
 #endif /* CKF_EXCLUSIVE_SESSION */
-    PKM_LogIt("            -> RW SESSION = %s\n", sinfo.flags &
-                                                          CKF_RW_SESSION
-                                                      ? "TRUE"
-                                                      : "FALSE");
-    PKM_LogIt("            -> SERIAL SESSION = %s\n", sinfo.flags &
-                                                              CKF_SERIAL_SESSION
-                                                          ? "TRUE"
-                                                          : "FALSE");
+    PKM_LogIt("            -> RW SESSION = %s\n", sinfo.flags & CKF_RW_SESSION ? "TRUE" : "FALSE");
+    PKM_LogIt("            -> SERIAL SESSION = %s\n", sinfo.flags & CKF_SERIAL_SESSION ? "TRUE" : "FALSE");
 #ifdef CKF_INSERTION_CALLBACK
-    PKM_LogIt("            -> INSERTION CALLBACK = %s\n", sinfo.flags &
-                                                                  CKF_INSERTION_CALLBACK
-                                                              ? "TRUE"
-                                                              : "FALSE");
+    PKM_LogIt("            -> INSERTION CALLBACK = %s\n", sinfo.flags & CKF_INSERTION_CALLBACK ? "TRUE" : "FALSE");
 #endif /* CKF_INSERTION_CALLBACK */
     PKM_LogIt("        ulDeviceError = %lu\n", sinfo.ulDeviceError);
     PKM_LogIt("\n");
 
     crv = pFunctionList->C_FindObjectsInit(h, NULL, 0);
     if (CKR_OK != crv) {
         PKM_LogIt("C_FindObjectsInit(%lu, NULL, 0) returned "
                   "0x%08X, %-26s\n",
--- a/security/nss/cmd/rsaperf/rsaperf.c
+++ b/security/nss/cmd/rsaperf/rsaperf.c
@@ -666,18 +666,17 @@ main(int argc, char **argv)
     }
     PORT_Free(runDataArr);
     PORT_Free(threadsArr);
 
     TimingEnd(timeCtx, PR_Now());
 
     printf("%ld iterations in %s\n",
            iters, TimingGenerateString(timeCtx));
-    printf("%.2f operations/s .\n", ((double)(iters) * (double)1000000.0) /
-                                        (double)timeCtx->interval);
+    printf("%.2f operations/s .\n", ((double)(iters) * (double)1000000.0) / (double)timeCtx->interval);
     TimingDivide(timeCtx, iters);
     printf("one operation every %s\n", TimingGenerateString(timeCtx));
 
     if (pubHighKey) {
         SECKEY_DestroyPublicKey(pubHighKey);
     }
 
     if (privHighKey) {
--- a/security/nss/cmd/rsapoptst/rsapoptst.c
+++ b/security/nss/cmd/rsapoptst/rsapoptst.c
@@ -211,17 +211,17 @@ rsaKeysAreEqual(PK11ObjectType srcType, 
     memcpy(srcTemplate, rsaTemplate, RSA_SIZE);
     memcpy(destTemplate, rsaTemplate, RSA_SIZE);
 
     rv = readKey(srcType, src, srcTemplate, 0, RSA_ATTRIBUTES);
     if (rv != SECSuccess) {
         printf("Could read source key\n");
         return PR_FALSE;
     }
-    readKey(destType, dest, destTemplate, 0, RSA_ATTRIBUTES);
+    rv = readKey(destType, dest, destTemplate, 0, RSA_ATTRIBUTES);
     if (rv != SECSuccess) {
         printf("Could read dest key\n");
         return PR_FALSE;
     }
 
     for (i = 0; i < RSA_ATTRIBUTES; i++) {
         if (srcTemplate[i].type == CKA_ID) {
             continue; /* we purposefully make the CKA_ID different */
--- a/security/nss/cmd/selfserv/selfserv.c
+++ b/security/nss/cmd/selfserv/selfserv.c
@@ -33,16 +33,17 @@
 #include "prnetdb.h"
 #include "prclist.h"
 #include "plgetopt.h"
 #include "pk11func.h"
 #include "secitem.h"
 #include "nss.h"
 #include "ssl.h"
 #include "sslproto.h"
+#include "sslexp.h"
 #include "cert.h"
 #include "certt.h"
 #include "ocsp.h"
 
 #ifndef PORT_Sprintf
 #define PORT_Sprintf sprintf
 #endif
 
@@ -1948,16 +1949,20 @@ server_main(
             errExit("SSL_OptionSet SSL_NO_CACHE");
         }
     }
 
     if (zeroRTT) {
         if (enabledVersions.max < SSL_LIBRARY_VERSION_TLS_1_3) {
             errExit("You tried enabling 0RTT without enabling TLS 1.3!");
         }
+        rv = SSL_SetupAntiReplay(10 * PR_USEC_PER_SEC, 7, 14);
+        if (rv != SECSuccess) {
+            errExit("error configuring anti-replay ");
+        }
         rv = SSL_OptionSet(model_sock, SSL_ENABLE_0RTT_DATA, PR_TRUE);
         if (rv != SECSuccess) {
             errExit("error enabling 0RTT ");
         }
     }
 
     if (enableALPN) {
         PRUint8 alpnVal[] = { 0x08,
@@ -2544,16 +2549,24 @@ main(int argc, char **argv)
     }
 
     envString = PR_GetEnvSecure(envVarName);
     tmp = PR_GetEnvSecure("TMP");
     if (!tmp)
         tmp = PR_GetEnvSecure("TMPDIR");
     if (!tmp)
         tmp = PR_GetEnvSecure("TEMP");
+
+    /* Call the NSS initialization routines */
+    rv = NSS_Initialize(dir, certPrefix, certPrefix, SECMOD_DB, NSS_INIT_READONLY);
+    if (rv != SECSuccess) {
+        fputs("NSS_Init failed.\n", stderr);
+        exit(8);
+    }
+
     if (envString) {
         /* we're one of the children in a multi-process server. */
         listen_sock = PR_GetInheritedFD(inheritableSockName);
         if (!listen_sock)
             errExit("PR_GetInheritedFD");
 #ifndef WINNT
         /* we can't do this on NT because it breaks NSPR and
     PR_Accept will fail on the socket in the child process if
@@ -2598,23 +2611,16 @@ main(int argc, char **argv)
     lm = PR_NewLogModule("TestCase");
 
     if (fileName)
         readBigFile(fileName);
 
     /* set our password function */
     PK11_SetPasswordFunc(SECU_GetModulePassword);
 
-    /* Call the NSS initialization routines */
-    rv = NSS_Initialize(dir, certPrefix, certPrefix, SECMOD_DB, NSS_INIT_READONLY);
-    if (rv != SECSuccess) {
-        fputs("NSS_Init failed.\n", stderr);
-        exit(8);
-    }
-
     /* all SSL3 cipher suites are enabled by default. */
     if (cipherString) {
         char *cstringSaved = cipherString;
         int ndx;
 
         /* disable all the ciphers, then enable the ones we want. */
         disableAllSSLCiphers();
 
--- a/security/nss/cmd/signtool/javascript.c
+++ b/security/nss/cmd/signtool/javascript.c
@@ -1110,18 +1110,17 @@ extract_js(char *filename)
     }
 
     state = TEXT_HTML_STATE;
 
     fb = FB_Create(fd);
 
     textStart = 0;
     startLine = 0;
-    while (linenum = FB_GetLineNum(fb), (curchar = FB_GetChar(fb)) !=
-                                            EOF) {
+    while (linenum = FB_GetLineNum(fb), (curchar = FB_GetChar(fb)) != EOF) {
         switch (state) {
             case TEXT_HTML_STATE:
                 if (curchar == '<') {
                     /*
                      * Found a tag
                      */
                     /* Save the text so far to a new text item */
                     curOffset = FB_GetPointer(fb) - 2;
--- a/security/nss/cmd/signtool/signtool.c
+++ b/security/nss/cmd/signtool/signtool.c
@@ -1028,19 +1028,17 @@ main(int argc, char *argv[])
 
         if (!leaveArc) {
             RemoveAllArc(jartree);
         }
 
         if (errorCount > 0 || warningCount > 0) {
             PR_fprintf(outputFD, "%d error%s, %d warning%s.\n",
                        errorCount,
-                       errorCount == 1 ? "" : "s", warningCount, warningCount == 1
-                                                                     ? ""
-                                                                     : "s");
+                       errorCount == 1 ? "" : "s", warningCount, warningCount == 1 ? "" : "s");
         } else {
             PR_fprintf(outputFD, "Directory %s signed successfully.\n",
                        jartree);
         }
     } else if (jartree) {
         SignArchive(jartree, keyName, zipfile, javascript, metafile,
                     install_script, optimize, !noRecurse);
     } else
--- a/security/nss/cmd/smimetools/cmsutil.c
+++ b/security/nss/cmd/smimetools/cmsutil.c
@@ -1567,20 +1567,17 @@ main(int argc, char **argv)
             fprintf(stderr, "%s: cannot create encoder context.\n", progName);
             exit(1);
         }
         if (cms_verbose) {
             fprintf(stderr, "input len [%d]\n", input.len);
             {
                 unsigned int j;
                 for (j = 0; j < input.len; j++)
-                    fprintf(stderr, "%2x%c", input.data[j], (j > 0 &&
-                                                             j % 35 == 0)
-                                                                ? '\n'
-                                                                : ' ');
+                    fprintf(stderr, "%2x%c", input.data[j], (j > 0 && j % 35 == 0) ? '\n' : ' ');
             }
         }
         if (input.len > 0) { /* skip if certs-only (or other zero content) */
             rv = NSS_CMSEncoder_Update(ecx, (char *)input.data, input.len);
             if (rv) {
                 fprintf(stderr,
                         "%s: failed to add data to encoder.\n", progName);
                 exit(1);
--- a/security/nss/cmd/ssltap/ssltap.c
+++ b/security/nss/cmd/ssltap/ssltap.c
@@ -1632,18 +1632,17 @@ print_ssl3_handshake(unsigned char *reco
                                certlength, certlength);
                     certbytesread +=
                         certlength + 3;
                     if (certbytesread <=
                         certslength) {
                         PR_snprintf(certFileName, sizeof certFileName, "cert.%03d",
                                     ++certFileNumber);
                         cfd =
-                            PR_Open(certFileName, PR_WRONLY |
-                                                      PR_CREATE_FILE | PR_TRUNCATE,
+                            PR_Open(certFileName, PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE,
                                     0664);
                         if (!cfd) {
                             PR_fprintf(PR_STDOUT,
                                        "               data = { couldn't save file '%s' }\n",
                                        certFileName);
                         } else {
                             PR_Write(cfd, (hsdata +
                                            pos),
@@ -1717,18 +1716,17 @@ print_ssl3_handshake(unsigned char *reco
                             PORT_Free(ca_name);
                         } else {
                             PR_fprintf(PR_STDOUT,
                                        "              distinguished name [%d]", dnLen);
                             if (dnLen >
                                     0 &&
                                 sslhexparse) {
                                 PR_fprintf(PR_STDOUT, " = {\n");
-                                print_hex(dnLen, hsdata +
-                                                     pos);
+                                print_hex(dnLen, hsdata + pos);
                                 PR_fprintf(PR_STDOUT, "              }\n");
                             } else {
                                 PR_fprintf(PR_STDOUT, "\n");
                             }
                         }
                         pos +=
                             dnLen;
                         exListLen -=
@@ -1791,18 +1789,17 @@ print_ssl3_handshake(unsigned char *reco
                 /* skip 4 bytes with handshake numbers, as in ssl3_HandleCertificateStatus */
                 data.type = siBuffer;
                 data.data = hsdata + 4;
                 data.len = sslh.length - 4;
                 print_status_response(&data);
 
                 PR_snprintf(ocspFileName, sizeof ocspFileName, "ocsp.%03d",
                             ++ocspFileNumber);
-                ofd = PR_Open(ocspFileName, PR_WRONLY |
-                                                PR_CREATE_FILE | PR_TRUNCATE,
+                ofd = PR_Open(ocspFileName, PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE,
                               0664);
                 if (!ofd) {
                     PR_fprintf(PR_STDOUT,
                                "               data = { couldn't save file '%s' }\n",
                                ocspFileName);
                 } else {
                     PR_Write(ofd, data.data, data.len);
                     PR_fprintf(PR_STDOUT,
@@ -2162,18 +2159,17 @@ print_ssl(DataBufferList *s, int length,
                                 break;
                         }
 
                         if (sslhexparse)
                             print_hex(recordLen - s->hMACsize, recordBuf);
                         break;
 
                     case 22: /* handshake */
-                        print_ssl3_handshake(recordBuf, recordLen -
-                                                            s->hMACsize,
+                        print_ssl3_handshake(recordBuf, recordLen - s->hMACsize,
                                              &sr, s);
                         break;
 
                     case 23: /* application data */
                         print_hex(recordLen -
                                       s->hMACsize,
                                   recordBuf);
                         break;
--- a/security/nss/coreconf/config.gypi
+++ b/security/nss/coreconf/config.gypi
@@ -91,17 +91,16 @@
     'disable_tests%': 0,
     'disable_chachapoly%': 0,
     'disable_dbm%': 0,
     'disable_libpkix%': 1,
     'disable_werror%': 0,
     'mozilla_client%': 0,
     'moz_fold_libs%': 0,
     'moz_folded_library_name%': '',
-    'ssl_enable_zlib%': 1,
     'sanitizer_flags%': 0,
     'test_build%': 0,
     'no_zdefs%': 0,
     'fuzz%': 0,
     'fuzz_tls%': 0,
     'fuzz_oss%': 0,
     'sign_libs%': 1,
     'use_pprof%': 0,
--- a/security/nss/coreconf/config.mk
+++ b/security/nss/coreconf/config.mk
@@ -167,17 +167,17 @@ DEFINES += -DNSS_DISABLE_CHACHAPOLY
 endif
 
 ifdef NSS_PKIX_NO_LDAP
 DEFINES += -DNSS_PKIX_NO_LDAP
 endif
 
 # FIPS support requires startup tests to be executed at load time of shared modules.
 # For performance reasons, these tests are disabled by default.
-# When compiling binaries that must support FIPS mode, 
+# When compiling binaries that must support FIPS mode,
 # you should define NSS_FORCE_FIPS
 #
 # NSS_NO_INIT_SUPPORT is always defined on platforms that don't support
 # executing the startup tests at library load time.
 ifndef NSS_FORCE_FIPS
 DEFINES += -DNSS_NO_INIT_SUPPORT
 endif
 
@@ -194,13 +194,8 @@ endif
 DEFINES += -DUSE_UTIL_DIRECTLY
 USE_UTIL_DIRECTLY = 1
 
 # Build with NO_NSPR_10_SUPPORT to avoid using obsolete NSPR features
 DEFINES += -DNO_NSPR_10_SUPPORT
 
 # Hide old, deprecated, TLS cipher suite names when building NSS
 DEFINES += -DSSL_DISABLE_DEPRECATED_CIPHER_SUITE_NAMES
-
-# Mozilla's mozilla/modules/zlib/src/zconf.h adds the MOZ_Z_ prefix to zlib
-# exported symbols, which causes problem when NSS is built as part of Mozilla.
-# So we add a NSS_SSL_ENABLE_ZLIB variable to allow Mozilla to turn this off.
-NSS_SSL_ENABLE_ZLIB = 1
--- a/security/nss/coreconf/coreconf.dep
+++ b/security/nss/coreconf/coreconf.dep
@@ -5,9 +5,8 @@
 
 /*
  * A dummy header file that is a dependency for all the object files.
  * Used to force a full recompilation of NSS in Mozilla's Tinderbox
  * depend builds.  See comments in rules.mk.
  */
 
 #error "Do not include this header file."
-
--- a/security/nss/cpputil/databuffer.cc
+++ b/security/nss/cpputil/databuffer.cc
@@ -57,31 +57,16 @@ size_t DataBuffer::Write(size_t index, c
 // Returns the offset of the end of the write.
 size_t DataBuffer::Write(size_t index, uint32_t val, size_t count) {
   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 DataBuffer::Read(size_t index, size_t count, uint32_t* val) const {
-  assert(count < sizeof(uint32_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;
-}
-
 void DataBuffer::Splice(const uint8_t* ins, size_t ins_len, size_t index,
                         size_t remove) {
   assert(ins);
   uint8_t* old_value = data_;
   size_t old_len = len_;
 
   // The amount of stuff remaining from the tail of the old.
   size_t tail_len = old_len - (std::min)(old_len, index + remove);
@@ -102,15 +87,41 @@ void DataBuffer::Splice(const uint8_t* i
   // The tail of the old.
   if (tail_len > 0) {
     Write(index + ins_len, old_value + index + remove, tail_len);
   }
 
   delete[] old_value;
 }
 
+// This can't use the same trick as Write(), since we might be reading from a
+// smaller data source.
+bool DataBuffer::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;
+}
+
+bool DataBuffer::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;
+}
+
 size_t DataBuffer::logging_limit = 32;
 
 /* static */ void DataBuffer::SetLogLimit(size_t limit) {
   DataBuffer::logging_limit = limit;
 }
 
 }  // namespace nss_test
--- a/security/nss/cpputil/databuffer.h
+++ b/security/nss/cpputil/databuffer.h
@@ -50,29 +50,29 @@ class DataBuffer {
   size_t Write(size_t index, const DataBuffer& buf) {
     return Write(index, buf.data(), buf.len());
   }
 
   // Write an integer, also performing host-to-network order conversion.
   // Returns the offset of the end of the write.
   size_t Write(size_t index, uint32_t val, size_t 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;
   // 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);
   void Append(const DataBuffer& buf) { Splice(buf, len_); }
 
+  bool Read(size_t index, size_t count, uint64_t* val) const;
+  bool Read(size_t index, size_t count, uint32_t* val) const;
+
   const uint8_t* data() const { return data_; }
   uint8_t* data() { return data_; }
   size_t len() const { return len_; }
   bool empty() const { return len_ == 0; }
 
   static void SetLogLimit(size_t limit);
   friend std::ostream& operator<<(std::ostream& stream, const DataBuffer& buf);
 
--- a/security/nss/cpputil/tls_parser.h
+++ b/security/nss/cpputil/tls_parser.h
@@ -20,16 +20,17 @@
 
 namespace nss_test {
 
 const uint8_t kTlsChangeCipherSpecType = 20;
 const uint8_t kTlsAlertType = 21;
 const uint8_t kTlsHandshakeType = 22;
 const uint8_t kTlsApplicationDataType = 23;
 const uint8_t kTlsAltHandshakeType = 24;
+const uint8_t kTlsAckType = 25;
 
 const uint8_t kTlsHandshakeClientHello = 1;
 const uint8_t kTlsHandshakeServerHello = 2;
 const uint8_t kTlsHandshakeNewSessionTicket = 4;
 const uint8_t kTlsHandshakeHelloRetryRequest = 6;
 const uint8_t kTlsHandshakeEncryptedExtensions = 8;
 const uint8_t kTlsHandshakeCertificate = 11;
 const uint8_t kTlsHandshakeServerKeyExchange = 12;
@@ -37,25 +38,25 @@ const uint8_t kTlsHandshakeCertificateRe
 const uint8_t kTlsHandshakeCertificateVerify = 15;
 const uint8_t kTlsHandshakeClientKeyExchange = 16;
 const uint8_t kTlsHandshakeFinished = 20;
 
 const uint8_t kTlsAlertWarning = 1;
 const uint8_t kTlsAlertFatal = 2;
 
 const uint8_t kTlsAlertCloseNotify = 0;
-const uint8_t kTlsAlertEndOfEarlyData = 1;
 const uint8_t kTlsAlertUnexpectedMessage = 10;
 const uint8_t kTlsAlertBadRecordMac = 20;
 const uint8_t kTlsAlertRecordOverflow = 22;
 const uint8_t kTlsAlertHandshakeFailure = 40;
 const uint8_t kTlsAlertIllegalParameter = 47;
 const uint8_t kTlsAlertDecodeError = 50;
 const uint8_t kTlsAlertDecryptError = 51;
 const uint8_t kTlsAlertProtocolVersion = 70;
+const uint8_t kTlsAlertInternalError = 80;
 const uint8_t kTlsAlertInappropriateFallback = 86;
 const uint8_t kTlsAlertMissingExtension = 109;
 const uint8_t kTlsAlertUnsupportedExtension = 110;
 const uint8_t kTlsAlertUnrecognizedName = 112;
 const uint8_t kTlsAlertNoApplicationProtocol = 120;
 
 const uint8_t kTlsFakeChangeCipherSpec[] = {
     kTlsChangeCipherSpecType,  // Type
--- a/security/nss/fuzz/tls_mutators.cc
+++ b/security/nss/fuzz/tls_mutators.cc
@@ -2,17 +2,19 @@
  * 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 <algorithm>
 #include "shared.h"
 #include "tls_parser.h"
 
 #include "ssl.h"
+extern "C" {
 #include "sslimpl.h"
+}
 
 using namespace nss_test;
 
 // Number of additional bytes in the TLS header.
 // Used to properly skip DTLS seqnums.
 static size_t gExtraHeaderBytes = 0;
 
 // Helper class to simplify TLS record manipulation.
@@ -35,17 +37,19 @@ class Record {
     memmove(dest + size_, other->data(), other->size() + other->remaining());
     // Insert the record.
     memcpy(dest, buf, size_);
   }
 
   void truncate(size_t length) {
     assert(length >= 5 + gExtraHeaderBytes);
     uint8_t *dest = const_cast<uint8_t *>(data_);
-    (void)ssl_EncodeUintX(length - 5 - gExtraHeaderBytes, 2, &dest[3]);
+    size_t l = length - (5 + gExtraHeaderBytes);
+    dest[3] = (l >> 8) & 0xff;
+    dest[4] = l & 0xff;
     memmove(dest + length, data_ + size_, remaining_);
   }
 
   void drop() {
     uint8_t *dest = const_cast<uint8_t *>(data_);
     memmove(dest, data_ + size_, remaining_);
   }
 
@@ -218,38 +222,42 @@ size_t FragmentRecord(uint8_t *data, siz
 
   // Find TLS records in the corpus.
   const auto records = ParseRecords(data, size);
   if (records.empty()) {
     return 0;
   }
 
   // Pick a record to fragment at random.
-  std::uniform_int_distribution<size_t> dist(0, records.size() - 1);
-  auto &rec = records.at(dist(rng));
+  std::uniform_int_distribution<size_t> rand_record(0, records.size() - 1);
+  auto &rec = records.at(rand_record(rng));
   uint8_t *rdata = const_cast<uint8_t *>(rec->data());
   size_t length = rec->size();
   size_t content_length = length - 5;
 
   if (content_length < 2) {
     return 0;
   }
 
   // Assign a new length to the first fragment.
-  size_t new_length = content_length / 2;
-  uint8_t *content = ssl_EncodeUintX(new_length, 2, &rdata[3]);
+  std::uniform_int_distribution<size_t> rand_size(1, content_length - 1);
+  size_t first_length = rand_size(rng);
+  size_t second_length = content_length - first_length;
+  rdata[3] = (first_length >> 8) & 0xff;
+  rdata[4] = first_length & 0xff;
+  uint8_t *second_record = rdata + 5 + first_length;
 
-  // Make room for one more header.
-  memmove(content + new_length + 5, content + new_length,
-          rec->remaining() + content_length - new_length);
+  // Make room for the header of the second record.
+  memmove(second_record + 5, second_record,
+          rec->remaining() + content_length - first_length);
 
   // Write second header.
-  memcpy(content + new_length, rdata, 3);
-  (void)ssl_EncodeUintX(content_length - new_length, 2,
-                        &content[new_length + 3]);
+  memcpy(second_record, rdata, 3);
+  second_record[3] = (second_length >> 8) & 0xff;
+  second_record[4] = second_length & 0xff;
 
   return size + 5;
 }
 
 // Cross-over function that merges and shuffles two transcripts.
 size_t CrossOver(const uint8_t *data1, size_t size1, const uint8_t *data2,
                  size_t size2, uint8_t *out, size_t max_out_size,
                  unsigned int seed) {
--- a/security/nss/gtests/common/util.h
+++ b/security/nss/gtests/common/util.h
@@ -5,17 +5,17 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef util_h__
 #define util_h__
 
 #include <cassert>
 #include <vector>
 
-std::vector<uint8_t> hex_string_to_bytes(std::string s) {
+static inline std::vector<uint8_t> hex_string_to_bytes(std::string s) {
   std::vector<uint8_t> bytes;
   for (size_t i = 0; i < s.length(); i += 2) {
     bytes.push_back(std::stoul(s.substr(i, 2), nullptr, 16));
   }
   return bytes;
 }
 
 #endif  // util_h__
--- a/security/nss/gtests/freebl_gtest/freebl_gtest.gyp
+++ b/security/nss/gtests/freebl_gtest/freebl_gtest.gyp
@@ -28,16 +28,17 @@
     {
       'target_name': 'freebl_gtest',
       'type': 'executable',
       'sources': [
         'mpi_unittest.cc',
         'dh_unittest.cc',
         'ecl_unittest.cc',
         'ghash_unittest.cc',
+        'rsa_unittest.cc',
         '<(DEPTH)/gtests/common/gtests.cc'
       ],
       'dependencies': [
         'freebl_gtest_deps',
         '<(DEPTH)/exports.gyp:nss_exports',
       ],
     },
     {
new file mode 100644
--- /dev/null
+++ b/security/nss/gtests/freebl_gtest/rsa_unittest.cc
@@ -0,0 +1,57 @@
+// 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 "gtest/gtest.h"
+
+#include <stdint.h>
+
+#include "blapi.h"
+#include "secitem.h"
+
+template <class T>
+struct ScopedDelete {
+  void operator()(T* ptr) {
+    if (ptr) {
+      PORT_FreeArena(ptr->arena, PR_TRUE);
+    }
+  }
+};
+
+typedef std::unique_ptr<RSAPrivateKey, ScopedDelete<RSAPrivateKey>>
+    ScopedRSAPrivateKey;
+
+class RSANewKeyTest : public ::testing::Test {
+ protected:
+  RSAPrivateKey* CreateKeyWithExponent(int keySizeInBits,
+                                       unsigned char publicExponent) {
+    SECItem exp = {siBuffer, 0, 0};
+    unsigned char pubExp[1] = {publicExponent};
+    exp.data = pubExp;
+    exp.len = 1;
+
+    return RSA_NewKey(keySizeInBits, &exp);
+  }
+};
+
+TEST_F(RSANewKeyTest, expOneTest) {
+  ScopedRSAPrivateKey key(CreateKeyWithExponent(2048, 0x01));
+  ASSERT_TRUE(key == nullptr);
+}
+TEST_F(RSANewKeyTest, expTwoTest) {
+  ScopedRSAPrivateKey key(CreateKeyWithExponent(2048, 0x02));
+  ASSERT_TRUE(key == nullptr);
+}
+TEST_F(RSANewKeyTest, expFourTest) {
+  ScopedRSAPrivateKey key(CreateKeyWithExponent(2048, 0x04));
+  ASSERT_TRUE(key == nullptr);
+}
+TEST_F(RSANewKeyTest, WrongKeysizeTest) {
+  ScopedRSAPrivateKey key(CreateKeyWithExponent(2047, 0x03));
+  ASSERT_TRUE(key == nullptr);
+}
+
+TEST_F(RSANewKeyTest, expThreeTest) {
+  ScopedRSAPrivateKey key(CreateKeyWithExponent(2048, 0x03));
+  ASSERT_TRUE(key != nullptr);
+}
--- a/security/nss/gtests/nss_bogo_shim/Makefile
+++ b/security/nss/gtests/nss_bogo_shim/Makefile
@@ -25,28 +25,22 @@ CXXFLAGS += -std=c++0x
 #######################################################################
 # (4) Include "local" platform-dependent assignments (OPTIONAL).      #
 #######################################################################
 
 include ../common/gtest.mk
 
 CFLAGS += -I$(CORE_DEPTH)/lib/ssl
 
-ifdef NSS_SSL_ENABLE_ZLIB
-include $(CORE_DEPTH)/coreconf/zlib.mk
-endif
-
 #######################################################################
 # (5) Execute "global" rules. (OPTIONAL)                              #
 #######################################################################
 
 include $(CORE_DEPTH)/coreconf/rules.mk
 
 #######################################################################
 # (6) Execute "component" rules. (OPTIONAL)                           #
 #######################################################################
 
 
 #######################################################################
 # (7) Execute "local" rules. (OPTIONAL).                              #
 #######################################################################
-
-
--- a/security/nss/gtests/nss_bogo_shim/config.json
+++ b/security/nss/gtests/nss_bogo_shim/config.json
@@ -1,17 +1,19 @@
 {
     "DisabledTests": {
         "### These tests break whenever we rev versions, so just leave them here for easy uncommenting":"",
-        "#*TLS13*":"(NSS=18, BoGo=16)",
-        "#*HelloRetryRequest*":"(NSS=18, BoGo=16)",
-        "#*KeyShare*":"(NSS=18, BoGo=16)",
-        "#*EncryptedExtensions*":"(NSS=18, BoGo=16)",
-        "#*SecondClientHello*":"(NSS=18, BoGo=16)",
-        "#*IgnoreClientVersionOrder*":"(NSS=18, BoGo=16)",
+        "*TLS13*":"(NSS=19, BoGo=18)",
+        "*HelloRetryRequest*":"(NSS=19, BoGo=18)",
+        "*KeyShare*":"(NSS=19, BoGo=18)",
+        "*EncryptedExtensions*":"(NSS=19, BoGo=18)",
+        "*SecondClientHello*":"(NSS=19, BoGo=18)",
+        "*IgnoreClientVersionOrder*":"(NSS=19, BoGo=18)",
+        "SkipEarlyData*":"(NSS=19, BoGo=18)",
+        "*Binder*":"(NSS=19, BoGo=18)",
         "Resume-Server-BinderWrongLength":"Alert disagreement (Bug 1317633)",
         "Resume-Server-NoPSKBinder":"Alert disagreement (Bug 1317633)",
         "CheckRecordVersion-TLS*":"Bug 1317634",
         "GREASE-Server-TLS13":"BoringSSL GREASEs without a flag, but we ignore it",
         "TLS13-ExpectNoSessionTicketOnBadKEMode-Server":"Bug in NSS. Don't send ticket when not permitted by KE modes (Bug 1317635)",
         "*KeyUpdate*":"KeyUpdate Unimplemented",
         "ClientAuth-NoFallback-TLS13":"Disagreement about alerts. Bug 1294975",
         "SendWarningAlerts-TLS13":"NSS needs to trigger on warning alerts",
@@ -61,9 +63,8 @@
     "ErrorMap" : {
         ":HANDSHAKE_FAILURE_ON_CLIENT_HELLO:":"SSL_ERROR_NO_CYPHER_OVERLAP",
         ":UNKNOWN_CIPHER_RETURNED:":"SSL_ERROR_NO_CYPHER_OVERLAP",
         ":OLD_SESSION_CIPHER_NOT_RETURNED:":"SSL_ERROR_RX_MALFORMED_SERVER_HELLO",
         ":NO_SHARED_CIPHER:":"SSL_ERROR_NO_CYPHER_OVERLAP",
         ":DIGEST_CHECK_FAILED:":"SSL_ERROR_BAD_HANDSHAKE_HASH_VALUE"
     }
 }
-
--- a/security/nss/gtests/pk11_gtest/manifest.mn
+++ b/security/nss/gtests/pk11_gtest/manifest.mn
@@ -6,16 +6,17 @@ CORE_DEPTH = ../..
 DEPTH      = ../..
 MODULE = nss
 
 CPPSRCS = \
       pk11_aeskeywrap_unittest.cc \
       pk11_chacha20poly1305_unittest.cc \
       pk11_curve25519_unittest.cc \
       pk11_ecdsa_unittest.cc \
+      pk11_encrypt_derive_unittest.cc \
       pk11_export_unittest.cc \
       pk11_pbkdf2_unittest.cc \
       pk11_prf_unittest.cc \
       pk11_prng_unittest.cc \
       pk11_rsapss_unittest.cc \
       pk11_der_private_key_import_unittest.cc \
       $(NULL)
 
new file mode 100644
--- /dev/null
+++ b/security/nss/gtests/pk11_gtest/pk11_encrypt_derive_unittest.cc
@@ -0,0 +1,210 @@
+/* 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 "pk11pub.h"
+#include "nssutil.h"
+#include <stdio.h>
+#include "prerror.h"
+#include "nss.h"
+#include "gtest/gtest.h"
+#include "scoped_ptrs.h"
+#include "cpputil.h"
+#include "databuffer.h"
+#include "util.h"
+
+#define MAX_KEY_SIZE 24
+
+namespace nss_test {
+
+static const uint8_t kIv[] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
+                              0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
+                              0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77};
+static const uint8_t kInput[] = {
+    0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x00, 0xff, 0xee, 0xdd, 0xcc,
+    0xbb, 0xaa, 0x99, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x00};
+
+class EncryptDeriveTest
+    : public ::testing::Test,
+      public ::testing::WithParamInterface<CK_MECHANISM_TYPE> {
+ public:
+  void TestEncryptDerive() {
+    ScopedPK11SymKey derived_key(PK11_Derive(key_.get(), derive_mech(),
+                                             derive_param(), encrypt_mech(),
+                                             CKA_DECRYPT, keysize()));
+    ASSERT_TRUE(derived_key);
+
+    uint8_t derived_key_data[MAX_KEY_SIZE];
+    ASSERT_GE(sizeof(derived_key_data), keysize());
+    GetKeyData(derived_key, derived_key_data, keysize());
+    RemoveChecksum(derived_key_data);
+
+    uint8_t reference_key_data[MAX_KEY_SIZE];
+    unsigned int reference_len = 0;
+    SECStatus rv = PK11_Encrypt(key_.get(), encrypt_mech(), encrypt_param(),
+                                reference_key_data, &reference_len, keysize(),
+                                kInput, keysize());
+    ASSERT_EQ(SECSuccess, rv);
+    ASSERT_EQ(keysize(), static_cast<size_t>(reference_len));
+    RemoveChecksum(reference_key_data);
+
+    EXPECT_EQ(DataBuffer(reference_key_data, keysize()),
+              DataBuffer(derived_key_data, keysize()));
+  }
+
+ protected:
+  unsigned int keysize() const { return 16; }
+
+ private:
+  CK_MECHANISM_TYPE encrypt_mech() const { return GetParam(); }
+
+  CK_MECHANISM_TYPE derive_mech() const {
+    switch (encrypt_mech()) {
+      case CKM_DES3_ECB:
+        return CKM_DES3_ECB_ENCRYPT_DATA;
+      case CKM_DES3_CBC:
+        return CKM_DES3_CBC_ENCRYPT_DATA;
+      case CKM_AES_ECB:
+        return CKM_AES_ECB_ENCRYPT_DATA;
+      case CKM_AES_CBC:
+        return CKM_AES_CBC_ENCRYPT_DATA;
+      case CKM_CAMELLIA_ECB:
+        return CKM_CAMELLIA_ECB_ENCRYPT_DATA;
+      case CKM_CAMELLIA_CBC:
+        return CKM_CAMELLIA_CBC_ENCRYPT_DATA;
+      case CKM_SEED_ECB:
+        return CKM_SEED_ECB_ENCRYPT_DATA;
+      case CKM_SEED_CBC:
+        return CKM_SEED_CBC_ENCRYPT_DATA;
+      default:
+        ADD_FAILURE() << "Unknown mechanism";
+        break;
+    }
+    return CKM_INVALID_MECHANISM;
+  }
+
+  SECItem* derive_param() const {
+    static CK_AES_CBC_ENCRYPT_DATA_PARAMS aes_data;
+    static CK_DES_CBC_ENCRYPT_DATA_PARAMS des_data;
+    static CK_KEY_DERIVATION_STRING_DATA string_data;
+    static SECItem param = {siBuffer, NULL, 0};
+
+    switch (encrypt_mech()) {
+      case CKM_DES3_ECB:
+      case CKM_AES_ECB:
+      case CKM_CAMELLIA_ECB:
+      case CKM_SEED_ECB:
+        string_data.pData = toUcharPtr(kInput);
+        string_data.ulLen = keysize();
+        param.data = reinterpret_cast<uint8_t*>(&string_data);
+        param.len = sizeof(string_data);
+        break;
+
+      case CKM_DES3_CBC:
+        des_data.pData = toUcharPtr(kInput);
+        des_data.length = keysize();
+        PORT_Memcpy(des_data.iv, kIv, 8);
+        param.data = reinterpret_cast<uint8_t*>(&des_data);
+        param.len = sizeof(des_data);
+        break;
+
+      case CKM_AES_CBC:
+      case CKM_CAMELLIA_CBC:
+      case CKM_SEED_CBC:
+        aes_data.pData = toUcharPtr(kInput);
+        aes_data.length = keysize();
+        PORT_Memcpy(aes_data.iv, kIv, keysize());
+        param.data = reinterpret_cast<uint8_t*>(&aes_data);
+        param.len = sizeof(aes_data);
+        break;
+
+      default:
+        ADD_FAILURE() << "Unknown mechanism";
+        break;
+    }
+    return &param;
+  }
+
+  SECItem* encrypt_param() const {
+    static SECItem param = {siBuffer, NULL, 0};
+
+    switch (encrypt_mech()) {
+      case CKM_DES3_ECB:
+      case CKM_AES_ECB:
+      case CKM_CAMELLIA_ECB:
+      case CKM_SEED_ECB:
+        // No parameter needed here.
+        break;
+
+      case CKM_DES3_CBC:
+      case CKM_AES_CBC:
+      case CKM_CAMELLIA_CBC:
+      case CKM_SEED_CBC:
+        param.data = toUcharPtr(kIv);
+        param.len = keysize();
+        break;
+
+      default:
+        ADD_FAILURE() << "Unknown mechanism";
+        break;
+    }
+    return &param;
+  }
+
+  virtual void SetUp() {
+    slot_.reset(PK11_GetBestSlot(derive_mech(), NULL));
+    ASSERT_TRUE(slot_);
+
+    key_.reset(PK11_TokenKeyGenWithFlags(slot_.get(), encrypt_mech(), NULL,
+                                         keysize(), NULL,
+                                         CKF_ENCRYPT | CKF_DERIVE, 0, NULL));
+    ASSERT_TRUE(key_);
+  }
+
+  void GetKeyData(ScopedPK11SymKey& key, uint8_t* buf, size_t max_len) const {
+    ASSERT_EQ(SECSuccess, PK11_ExtractKeyValue(key.get()));
+    SECItem* data = PK11_GetKeyData(key.get());
+    ASSERT_TRUE(data);
+    ASSERT_EQ(max_len, static_cast<size_t>(data->len));
+    PORT_Memcpy(buf, data->data, data->len);
+  }
+
+  // Remove checksum if the key is a 3DES key.
+  void RemoveChecksum(uint8_t* key_data) const {
+    if (encrypt_mech() != CKM_DES3_CBC && encrypt_mech() != CKM_DES3_ECB) {
+      return;
+    }
+    for (size_t i = 0; i < keysize(); ++i) {
+      key_data[i] &= 0xfe;
+    }
+  }
+
+  ScopedPK11SlotInfo slot_;
+  ScopedPK11SymKey key_;
+};
+
+TEST_P(EncryptDeriveTest, Test) { TestEncryptDerive(); }
+
+static const CK_MECHANISM_TYPE kEncryptDeriveMechanisms[] = {
+    CKM_DES3_ECB,     CKM_DES3_CBC,     CKM_AES_ECB,  CKM_AES_ECB, CKM_AES_CBC,
+    CKM_CAMELLIA_ECB, CKM_CAMELLIA_CBC, CKM_SEED_ECB, CKM_SEED_CBC};
+
+INSTANTIATE_TEST_CASE_P(EncryptDeriveTests, EncryptDeriveTest,
+                        ::testing::ValuesIn(kEncryptDeriveMechanisms));
+
+// This class handles the case where 3DES takes a 192-bit key
+// where all 24 octets will be used.
+class EncryptDerive3Test : public EncryptDeriveTest {
+ protected:
+  unsigned int keysize() const { return 24; }
+};
+
+TEST_P(EncryptDerive3Test, Test) { TestEncryptDerive(); }
+
+static const CK_MECHANISM_TYPE kDES3EncryptDeriveMechanisms[] = {CKM_DES3_ECB,
+                                                                 CKM_DES3_CBC};
+
+INSTANTIATE_TEST_CASE_P(Encrypt3DeriveTests, EncryptDerive3Test,
+                        ::testing::ValuesIn(kDES3EncryptDeriveMechanisms));
+
+}  // namespace nss_test
--- a/security/nss/gtests/pk11_gtest/pk11_gtest.gyp
+++ b/security/nss/gtests/pk11_gtest/pk11_gtest.gyp
@@ -11,16 +11,17 @@
       'target_name': 'pk11_gtest',
       'type': 'executable',
       'sources': [
         'pk11_aeskeywrap_unittest.cc',
         'pk11_aes_gcm_unittest.cc',
         'pk11_chacha20poly1305_unittest.cc',
         'pk11_curve25519_unittest.cc',
         'pk11_ecdsa_unittest.cc',
+        'pk11_encrypt_derive_unittest.cc',
         'pk11_pbkdf2_unittest.cc',
         'pk11_prf_unittest.cc',
         'pk11_prng_unittest.cc',
         'pk11_rsapss_unittest.cc',
         'pk11_der_private_key_import_unittest.cc',
         '<(DEPTH)/gtests/common/gtests.cc'
       ],
       'dependencies': [
--- a/security/nss/gtests/ssl_gtest/Makefile
+++ b/security/nss/gtests/ssl_gtest/Makefile
@@ -24,20 +24,16 @@ include $(CORE_DEPTH)/coreconf/config.mk
 #######################################################################
 # (4) Include "local" platform-dependent assignments (OPTIONAL).      #
 #######################################################################
 
 include ../common/gtest.mk
 
 CFLAGS += -I$(CORE_DEPTH)/lib/ssl
 
-ifdef NSS_SSL_ENABLE_ZLIB
-include $(CORE_DEPTH)/coreconf/zlib.mk
-endif
-
 ifdef NSS_DISABLE_TLS_1_3
 NSS_DISABLE_TLS_1_3=1
 # Run parameterized tests only, for which we can easily exclude TLS 1.3
 CPPSRCS := $(filter-out $(shell grep -l '^TEST_F' $(CPPSRCS)), $(CPPSRCS))
 CFLAGS += -DNSS_DISABLE_TLS_1_3
 endif
 
 #######################################################################
new file mode 100644
--- /dev/null
+++ b/security/nss/gtests/ssl_gtest/bloomfilter_unittest.cc
@@ -0,0 +1,108 @@
+/* -*- 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/. */
+
+extern "C" {
+#include "sslbloom.h"
+}
+
+#include "gtest_utils.h"
+
+namespace nss_test {
+
+// Some random-ish inputs to test with.  These don't result in collisions in any
+// of the configurations that are tested below.
+static const uint8_t kHashes1[] = {
+    0x79, 0x53, 0xb8, 0xdd, 0x6b, 0x98, 0xce, 0x00, 0xb7, 0xdc, 0xe8,
+    0x03, 0x70, 0x8c, 0xe3, 0xac, 0x06, 0x8b, 0x22, 0xfd, 0x0e, 0x34,
+    0x48, 0xe6, 0xe5, 0xe0, 0x8a, 0xd6, 0x16, 0x18, 0xe5, 0x48};
+static const uint8_t kHashes2[] = {
+    0xc6, 0xdd, 0x6e, 0xc4, 0x76, 0xb8, 0x55, 0xf2, 0xa4, 0xfc, 0x59,
+    0x04, 0xa4, 0x90, 0xdc, 0xa7, 0xa7, 0x0d, 0x94, 0x8f, 0xc2, 0xdc,
+    0x15, 0x6d, 0x48, 0x93, 0x9d, 0x05, 0xbb, 0x9a, 0xbc, 0xc1};
+
+typedef struct {
+  unsigned int k;
+  unsigned int bits;
+} BloomFilterConfig;
+
+class BloomFilterTest
+    : public ::testing::Test,
+      public ::testing::WithParamInterface<BloomFilterConfig> {
+ public:
+  BloomFilterTest() : filter_() {}
+
+  void SetUp() { Init(); }
+
+  void TearDown() { sslBloom_Destroy(&filter_); }
+
+ protected:
+  void Init() {
+    if (filter_.filter) {
+      sslBloom_Destroy(&filter_);
+    }
+    ASSERT_EQ(SECSuccess,
+              sslBloom_Init(&filter_, GetParam().k, GetParam().bits));
+  }
+
+  bool Check(const uint8_t* hashes) {
+    return sslBloom_Check(&filter_, hashes) ? true : false;
+  }
+
+  void Add(const uint8_t* hashes, bool expect_collision = false) {
+    EXPECT_EQ(expect_collision, sslBloom_Add(&filter_, hashes) ? true : false);
+    EXPECT_TRUE(Check(hashes));
+  }
+
+  sslBloomFilter filter_;
+};
+
+TEST_P(BloomFilterTest, InitOnly) {}
+
+TEST_P(BloomFilterTest, AddToEmpty) {
+  EXPECT_FALSE(Check(kHashes1));
+  Add(kHashes1);
+}
+
+TEST_P(BloomFilterTest, AddTwo) {
+  Add(kHashes1);
+  Add(kHashes2);
+}
+
+TEST_P(BloomFilterTest, AddOneTwice) {
+  Add(kHashes1);
+  Add(kHashes1, true);
+}
+
+TEST_P(BloomFilterTest, Zero) {
+  Add(kHashes1);
+  sslBloom_Zero(&filter_);
+  EXPECT_FALSE(Check(kHashes1));
+  EXPECT_FALSE(Check(kHashes2));
+}
+
+TEST_P(BloomFilterTest, Fill) {
+  sslBloom_Fill(&filter_);
+  EXPECT_TRUE(Check(kHashes1));
+  EXPECT_TRUE(Check(kHashes2));
+}
+
+static const BloomFilterConfig kBloomFilterConfigurations[] = {
+    {1, 1},    // 1 hash, 1 bit input - high chance of collision.
+    {1, 2},    // 1 hash, 2 bits - smaller than the basic unit size.
+    {1, 3},    // 1 hash, 3 bits - same as basic unit size.
+    {1, 4},    // 1 hash, 4 bits - 2 octets each.
+    {3, 10},   // 3 hashes over a reasonable number of bits.
+    {3, 3},    // Test that we can read multiple bits.
+    {4, 15},   // A credible filter.
+    {2, 18},   // A moderately large allocation.
+    {16, 16},  // Insane, use all of the bits from the hashes.
+    {16, 9},   // This also uses all of the bits from the hashes.
+};
+
+INSTANTIATE_TEST_CASE_P(BloomFilterConfigurations, BloomFilterTest,
+                        ::testing::ValuesIn(kBloomFilterConfigurations));
+
+}  // namespace nspr_test
--- a/security/nss/gtests/ssl_gtest/libssl_internals.c
+++ b/security/nss/gtests/ssl_gtest/libssl_internals.c
@@ -29,17 +29,16 @@ SECStatus SSLInt_IncrementClientHandshak
 SECStatus SSLInt_UpdateSSLv2ClientRandom(PRFileDesc *fd, uint8_t *rnd,
                                          size_t rnd_len, uint8_t *msg,
                                          size_t msg_len) {
   sslSocket *ss = ssl_FindSocket(fd);
   if (!ss) {
     return SECFailure;
   }
 
-  ssl3_InitState(ss);
   ssl3_RestartHandshakeHashes(ss);
 
   // Ensure we don't overrun hs.client_random.
   rnd_len = PR_MIN(SSL3_RANDOM_LENGTH, rnd_len);
 
   // Zero the client_random.
   PORT_Memset(ss->ssl3.hs.client_random, 0, SSL3_RANDOM_LENGTH);
 
@@ -68,80 +67,84 @@ void SSLInt_SetSelfEncryptMacKey(PK11Sym
 }
 
 SECStatus SSLInt_SetMTU(PRFileDesc *fd, PRUint16 mtu) {
   sslSocket *ss = ssl_FindSocket(fd);
   if (!ss) {
     return SECFailure;
   }
   ss->ssl3.mtu = mtu;
+  ss->ssl3.hs.rtRetries = 0; /* Avoid DTLS shrinking the MTU any more. */
   return SECSuccess;
 }
 
-PRInt32 SSLInt_CountTls13CipherSpecs(PRFileDesc *fd) {
+PRInt32 SSLInt_CountCipherSpecs(PRFileDesc *fd) {
   PRCList *cur_p;
   PRInt32 ct = 0;
 
   sslSocket *ss = ssl_FindSocket(fd);
   if (!ss) {
     return -1;
   }
 
   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_PrintCipherSpecs(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 spec epoch=%d (%s) refct=%d\n", SPEC_DIR(spec),
+            spec->epoch, spec->phase, 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 timer expiry by backdating when all active timers were started. We
+ * could set the remaining time to 0 but then backoff would not work properly if
+ * we decide to test it. */
+SECStatus SSLInt_ShiftDtlsTimers(PRFileDesc *fd, PRIntervalTime shift) {
+  size_t i;
   sslSocket *ss = ssl_FindSocket(fd);
   if (!ss) {
-    return;
+    return SECFailure;
   }
 
-  if (!ss->ssl3.hs.rtTimerCb) return;
-
-  ss->ssl3.hs.rtTimerStarted =
-      PR_IntervalNow() - PR_MillisecondsToInterval(ss->ssl3.hs.rtTimeoutMs + 1);
+  for (i = 0; i < PR_ARRAY_SIZE(ss->ssl3.hs.timers); ++i) {
+    if (ss->ssl3.hs.timers[i].cb) {
+      ss->ssl3.hs.timers[i].started -= shift;
+    }
+  }
+  return SECSuccess;
 }
 
 #define CHECK_SECRET(secret)                  \
   if (ss->ssl3.hs.secret) {                   \
     fprintf(stderr, "%s != NULL\n", #secret); \
     return PR_FALSE;                          \
   }
 
 PRBool SSLInt_CheckSecretsDestroyed(PRFileDesc *fd) {
   sslSocket *ss = ssl_FindSocket(fd);
   if (!ss) {
     return PR_FALSE;
   }
 
   CHECK_SECRET(currentSecret);
-  CHECK_SECRET(resumptionMasterSecret);
   CHECK_SECRET(dheSecret);
   CHECK_SECRET(clientEarlyTrafficSecret);
   CHECK_SECRET(clientHsTrafficSecret);
   CHECK_SECRET(serverHsTrafficSecret);
 
   return PR_TRUE;
 }
 
@@ -221,98 +224,74 @@ PRBool SSLInt_SendAlert(PRFileDesc *fd, 
   }
 
   SECStatus rv = SSL3_SendAlert(ss, level, type);
   if (rv != SECSuccess) return PR_FALSE;
 
   return PR_TRUE;
 }
 
-PRBool SSLInt_SendNewSessionTicket(PRFileDesc *fd) {
-  sslSocket *ss = ssl_FindSocket(fd);
-  if (!ss) {
-    return PR_FALSE;
-  }
-
-  ssl_GetSSL3HandshakeLock(ss);
-  ssl_GetXmitBufLock(ss);
-
-  SECStatus rv = tls13_SendNewSessionTicket(ss);
-  if (rv == SECSuccess) {
-    rv = ssl3_FlushHandshake(ss, 0);
-  }
-
-  ssl_ReleaseXmitBufLock(ss);
-  ssl_ReleaseSSL3HandshakeLock(ss);
-
-  return rv == SECSuccess;
-}
-
 SECStatus SSLInt_AdvanceReadSeqNum(PRFileDesc *fd, PRUint64 to) {
-  PRUint64 epoch;
   sslSocket *ss;
   ssl3CipherSpec *spec;
 
   ss = ssl_FindSocket(fd);
   if (!ss) {
     return SECFailure;
   }
-  if (to >= (1ULL << 48)) {
+  if (to >= RECORD_SEQ_MAX) {
     PORT_SetError(SEC_ERROR_INVALID_ARGS);
     return SECFailure;
   }
   ssl_GetSpecWriteLock(ss);
   spec = ss->ssl3.crSpec;
-  epoch = spec->read_seq_num >> 48;
-  spec->read_seq_num = (epoch << 48) | to;
+  spec->seqNum = to;
 
   /* For DTLS, we need to fix the record sequence number.  For this, we can just
    * scrub the entire structure on the assumption that the new sequence number
    * is far enough past the last received sequence number. */
-  if (to <= spec->recvdRecords.right + DTLS_RECVD_RECORDS_WINDOW) {
+  if (spec->seqNum <= spec->recvdRecords.right + DTLS_RECVD_RECORDS_WINDOW) {
     PORT_SetError(SEC_ERROR_INVALID_ARGS);
     return SECFailure;
   }
-  dtls_RecordSetRecvd(&spec->recvdRecords, to);
+  dtls_RecordSetRecvd(&spec->recvdRecords, spec->seqNum);
 
   ssl_ReleaseSpecWriteLock(ss);
   return SECSuccess;
 }
 
 SECStatus SSLInt_AdvanceWriteSeqNum(PRFileDesc *fd, PRUint64 to) {
-  PRUint64 epoch;
   sslSocket *ss;
 
   ss = ssl_FindSocket(fd);
   if (!ss) {
     return SECFailure;
   }
-  if (to >= (1ULL << 48)) {
+  if (to >= RECORD_SEQ_MAX) {
     PORT_SetError(SEC_ERROR_INVALID_ARGS);
     return SECFailure;
   }
   ssl_GetSpecWriteLock(ss);
-  epoch = ss->ssl3.cwSpec->write_seq_num >> 48;
-  ss->ssl3.cwSpec->write_seq_num = (epoch << 48) | to;
+  ss->ssl3.cwSpec->seqNum = to;
   ssl_ReleaseSpecWriteLock(ss);
   return SECSuccess;
 }
 
 SECStatus SSLInt_AdvanceWriteSeqByAWindow(PRFileDesc *fd, PRInt32 extra) {
   sslSocket *ss;
   sslSequenceNumber to;
 
   ss = ssl_FindSocket(fd);
   if (!ss) {
     return SECFailure;
   }
   ssl_GetSpecReadLock(ss);
-  to = ss->ssl3.cwSpec->write_seq_num + DTLS_RECVD_RECORDS_WINDOW + extra;
+  to = ss->ssl3.cwSpec->seqNum + DTLS_RECVD_RECORDS_WINDOW + extra;
   ssl_ReleaseSpecReadLock(ss);
-  return SSLInt_AdvanceWriteSeqNum(fd, to & RECORD_SEQ_MAX);
+  return SSLInt_AdvanceWriteSeqNum(fd, to);
 }
 
 SSLKEAType SSLInt_GetKEAType(SSLNamedGroup group) {
   const sslNamedGroupDef *groupDef = ssl_LookupNamedGroup(group);
   if (!groupDef) return ssl_kea_null;
 
   return groupDef->keaType;
 }
@@ -328,56 +307,30 @@ SECStatus SSLInt_SetCipherSpecChangeFunc
   }
 
   ss->ssl3.changedCipherSpecFunc = func;
   ss->ssl3.changedCipherSpecArg = arg;
 
   return SECSuccess;
 }
 
-static ssl3KeyMaterial *GetKeyingMaterial(PRBool isServer,
-                                          ssl3CipherSpec *spec) {
-  return isServer ? &spec->server : &spec->client;
-}
-
-PK11SymKey *SSLInt_CipherSpecToKey(PRBool isServer, ssl3CipherSpec *spec) {
-  return GetKeyingMaterial(isServer, spec)->write_key;
-}
-
-SSLCipherAlgorithm SSLInt_CipherSpecToAlgorithm(PRBool isServer,
-                                                ssl3CipherSpec *spec) {
-  return spec->cipher_def->calg;
-}
-
-unsigned char *SSLInt_CipherSpecToIv(PRBool isServer, ssl3CipherSpec *spec) {
-  return GetKeyingMaterial(isServer, spec)->write_iv;
+PK11SymKey *SSLInt_CipherSpecToKey(const ssl3CipherSpec *spec) {
+  return spec->keyMaterial.key;
 }
 
-SECStatus SSLInt_EnableShortHeaders(PRFileDesc *fd) {
-  sslSocket *ss;
-
-  ss = ssl_FindSocket(fd);
-  if (!ss) {
-    return SECFailure;
-  }
-
-  ss->opt.enableShortHeaders = PR_TRUE;
-  return SECSuccess;
+SSLCipherAlgorithm SSLInt_CipherSpecToAlgorithm(const ssl3CipherSpec *spec) {
+  return spec->cipherDef->calg;
 }
 
-SECStatus SSLInt_UsingShortHeaders(PRFileDesc *fd, PRBool *result) {
-  sslSocket *ss;
+const PRUint8 *SSLInt_CipherSpecToIv(const ssl3CipherSpec *spec) {
+  return spec->keyMaterial.iv;
+}
 
-  ss = ssl_FindSocket(fd);
-  if (!ss) {
-    return SECFailure;
-  }
-
-  *result = ss->ssl3.hs.shortHeaders;
-  return SECSuccess;
+PRUint16 SSLInt_CipherSpecToEpoch(const 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;
@@ -400,8 +353,12 @@ SECStatus SSLInt_SetSocketMaxEarlyDataSi
   /* Modifying both specs allows this to be used on either peer. */
   ssl_GetSpecWriteLock(ss);
   ss->ssl3.crSpec->earlyDataRemaining = size;
   ss->ssl3.cwSpec->earlyDataRemaining = size;
   ssl_ReleaseSpecWriteLock(ss);
 
   return SECSuccess;
 }
+
+void SSLInt_RolloverAntiReplay(void) {
+  tls13_AntiReplayRollover(ssl_TimeUsec());
+}
--- a/security/nss/gtests/ssl_gtest/libssl_internals.h
+++ b/security/nss/gtests/ssl_gtest/libssl_internals.h
@@ -19,39 +19,37 @@ 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);
+PRInt32 SSLInt_CountCipherSpecs(PRFileDesc *fd);
+void SSLInt_PrintCipherSpecs(const char *label, PRFileDesc *fd);
+SECStatus SSLInt_ShiftDtlsTimers(PRFileDesc *fd, PRIntervalTime shift);
 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);
-PRBool SSLInt_SendNewSessionTicket(PRFileDesc *fd);
 SECStatus SSLInt_AdvanceWriteSeqNum(PRFileDesc *fd, PRUint64 to);
 SECStatus SSLInt_AdvanceReadSeqNum(PRFileDesc *fd, PRUint64 to);
 SECStatus SSLInt_AdvanceWriteSeqByAWindow(PRFileDesc *fd, PRInt32 extra);
 SSLKEAType SSLInt_GetKEAType(SSLNamedGroup group);
 
 SECStatus SSLInt_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);
-SECStatus SSLInt_EnableShortHeaders(PRFileDesc *fd);
-SECStatus SSLInt_UsingShortHeaders(PRFileDesc *fd, PRBool *result);
+PRUint16 SSLInt_CipherSpecToEpoch(const ssl3CipherSpec *spec);
+PK11SymKey *SSLInt_CipherSpecToKey(const ssl3CipherSpec *spec);
+SSLCipherAlgorithm SSLInt_CipherSpecToAlgorithm(const ssl3CipherSpec *spec);
+const PRUint8 *SSLInt_CipherSpecToIv(const 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/security/nss/gtests/ssl_gtest/manifest.mn
+++ b/security/nss/gtests/ssl_gtest/manifest.mn
@@ -7,22 +7,23 @@ DEPTH      = ../..
 MODULE = nss
 
 # These sources have access to libssl internals
 CSRCS = \
       libssl_internals.c \
       $(NULL)
 
 CPPSRCS = \
+      bloomfilter_unittest.cc \
       ssl_0rtt_unittest.cc \
       ssl_agent_unittest.cc \
-      ssl_alths_unittest.cc \
       ssl_auth_unittest.cc \
       ssl_cert_ext_unittest.cc \
       ssl_ciphersuite_unittest.cc \
+      ssl_custext_unittest.cc \
       ssl_damage_unittest.cc \
       ssl_dhe_unittest.cc \
       ssl_drop_unittest.cc \
       ssl_ecdh_unittest.cc \
       ssl_ems_unittest.cc \
       ssl_exporter_unittest.cc \
       ssl_extension_unittest.cc \
       ssl_fragment_unittest.cc \
@@ -33,16 +34,17 @@ CPPSRCS = \
       ssl_keylog_unittest.cc \
       ssl_loopback_unittest.cc \
       ssl_misc_unittest.cc \
       ssl_record_unittest.cc \
       ssl_resumption_unittest.cc \
       ssl_renegotiation_unittest.cc \
       ssl_skip_unittest.cc \
       ssl_staticrsa_unittest.cc \
+      ssl_tls13compat_unittest.cc \
       ssl_v2_client_hello_unittest.cc \
       ssl_version_unittest.cc \
       ssl_versionpolicy_unittest.cc \
       selfencrypt_unittest.cc \
       test_io.cc \
       tls_agent.cc \
       tls_connect.cc \
       tls_hkdf_unittest.cc \
--- a/security/nss/gtests/ssl_gtest/ssl_0rtt_unittest.cc
+++ b/security/nss/gtests/ssl_gtest/ssl_0rtt_unittest.cc
@@ -40,26 +40,112 @@ TEST_P(TlsConnectTls13, ZeroRttServerRej
   client_->Set0RttEnabled(true);
   ExpectResumption(RESUME_TICKET);
   ZeroRttSendReceive(true, false);
   Handshake();
   CheckConnected();
   SendReceive();
 }
 
+TEST_P(TlsConnectTls13, ZeroRttApparentReplayAfterRestart) {
+  // The test fixtures call SSL_SetupAntiReplay() in SetUp().  This results in
+  // 0-RTT being rejected until at least one window passes.  SetupFor0Rtt()
+  // forces a rollover of the anti-replay filters, which clears this state.
+  // Here, we do the setup manually here without that forced rollover.
+
+  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();
+  StartConnect();
+  client_->Set0RttEnabled(true);
+  server_->Set0RttEnabled(true);
+  ExpectResumption(RESUME_TICKET);
+  ZeroRttSendReceive(true, false);
+  Handshake();
+  CheckConnected();
+  SendReceive();
+}
+
+class TlsZeroRttReplayTest : public TlsConnectTls13 {
+ private:
+  class SaveFirstPacket : public PacketFilter {
+   public:
+    PacketFilter::Action Filter(const DataBuffer& input,
+                                DataBuffer* output) override {
+      if (!packet_.len() && input.len()) {
+        packet_ = input;
+      }
+      return KEEP;
+    }
+
+    const DataBuffer& packet() const { return packet_; }
+
+   private:
+    DataBuffer packet_;
+  };
+
+ protected:
+  void RunTest(bool rollover) {
+    // Run the initial handshake
+    SetupForZeroRtt();
+
+    // Now run a true 0-RTT handshake, but capture the first packet.
+    auto first_packet = std::make_shared<SaveFirstPacket>();
+    client_->SetPacketFilter(first_packet);
+    client_->Set0RttEnabled(true);
+    server_->Set0RttEnabled(true);
+    ExpectResumption(RESUME_TICKET);
+    ZeroRttSendReceive(true, true);
+    Handshake();
+    EXPECT_LT(0U, first_packet->packet().len());
+    ExpectEarlyDataAccepted(true);
+    CheckConnected();
+    SendReceive();
+
+    if (rollover) {
+      SSLInt_RolloverAntiReplay();
+    }
+
+    // Now replay that packet against the server.
+    Reset();
+    server_->StartConnect();
+    server_->Set0RttEnabled(true);
+
+    // Capture the early_data extension, which should not appear.
+    auto early_data_ext =
+        std::make_shared<TlsExtensionCapture>(ssl_tls13_early_data_xtn);
+    server_->SetPacketFilter(early_data_ext);
+
+    // Finally, replay the ClientHello and force the server to consume it.  Stop
+    // after the server sends its first flight; the client will not be able to
+    // complete this handshake.
+    server_->adapter()->PacketReceived(first_packet->packet());
+    server_->Handshake();
+    EXPECT_FALSE(early_data_ext->captured());
+  }
+};
+
+TEST_P(TlsZeroRttReplayTest, ZeroRttReplay) { RunTest(false); }
+
+TEST_P(TlsZeroRttReplayTest, ZeroRttReplayAfterRollover) { RunTest(true); }
+
 // 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();
@@ -76,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);
@@ -95,16 +180,71 @@ TEST_P(TlsConnectTls13, ZeroRttServerOnl
 
   // Now make sure that things complete.
   Handshake();
   CheckConnected();
   SendReceive();
   CheckKeys();
 }
 
+// A small sleep after sending the ClientHello means that the ticket age that
+// arrives at the server is too low.  With a small tolerance for variation in
+// ticket age (which is determined by the |window| parameter that is passed to
+// SSL_SetupAntiReplay()), the server then rejects early data.
+TEST_P(TlsConnectTls13, ZeroRttRejectOldTicket) {
+  SetupForZeroRtt();
+  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);
+  ZeroRttSendReceive(true, false, []() {
+    PR_Sleep(PR_MillisecondsToInterval(10));
+    return true;
+  });
+  Handshake();
+  ExpectEarlyDataAccepted(false);
+  CheckConnected();
+  SendReceive();
+}
+
+// In this test, we falsely inflate the estimate of the RTT by delaying the
+// 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);
+  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);
+  StartConnect();
+  ZeroRttSendReceive(true, false);
+  Handshake();
+  CheckConnected();
+  SendReceive();
+}
+
 TEST_P(TlsConnectTls13, TestTls13ZeroRttAlpn) {
   EnableAlpn();
   SetupForZeroRtt();
   EnableAlpn();
   client_->Set0RttEnabled(true);
   server_->Set0RttEnabled(true);
   ExpectResumption(RESUME_TICKET);
   ExpectEarlyDataAccepted(true);
@@ -113,16 +253,24 @@ TEST_P(TlsConnectTls13, TestTls13ZeroRtt
     return true;
   });
   Handshake();
   CheckConnected();
   SendReceive();
   CheckAlpn("a");
 }
 
+// NOTE: In this test and those below, the client always sends
+// post-ServerHello alerts with the handshake keys, even if the server
+// has accepted 0-RTT.  In some cases, as with errors in
+// EncryptedExtensions, the client can't know the server's behavior,
+// and in others it's just simpler.  What the server is expecting
+// depends on whether it accepted 0-RTT or not. Eventually, we may
+// make the server trial decrypt.
+//
 // Have the server negotiate a different ALPN value, and therefore
 // reject 0-RTT.
 TEST_P(TlsConnectTls13, TestTls13ZeroRttAlpnChangeServer) {
   EnableAlpn();
   SetupForZeroRtt();
   static const uint8_t client_alpn[] = {0x01, 0x61, 0x01, 0x62};  // "a", "b"
   static const uint8_t server_alpn[] = {0x01, 0x62};              // "b"
   client_->EnableAlpn(client_alpn, sizeof(client_alpn));
@@ -151,42 +299,52 @@ TEST_P(TlsConnectTls13, TestTls13ZeroRtt
   server_->Set0RttEnabled(true);
   EnableAlpn();
   ExpectResumption(RESUME_TICKET);
   ZeroRttSendReceive(true, true, [this]() {
     PRUint8 b[] = {'b'};
     client_->CheckAlpn(SSL_NEXT_PROTO_EARLY_VALUE, "a");
     EXPECT_EQ(SECSuccess, SSLInt_Set0RttAlpn(client_->ssl_fd(), b, sizeof(b)));
     client_->CheckAlpn(SSL_NEXT_PROTO_EARLY_VALUE, "b");
-    ExpectAlert(client_, kTlsAlertIllegalParameter);
+    client_->ExpectSendAlert(kTlsAlertIllegalParameter);
     return true;
   });
-  Handshake();
+  if (variant_ == ssl_variant_stream) {
+    server_->ExpectSendAlert(kTlsAlertBadRecordMac);
+    Handshake();
+    server_->CheckErrorCode(SSL_ERROR_BAD_MAC_READ);
+  } else {
+    client_->Handshake();
+  }
   client_->CheckErrorCode(SSL_ERROR_NEXT_PROTOCOL_DATA_INVALID);
-  server_->CheckErrorCode(SSL_ERROR_ILLEGAL_PARAMETER_ALERT);
 }
 
 // Set up with no ALPN and then set the client so it thinks it has ALPN.
 // The server responds without the extension and the client returns an
 // error.
 TEST_P(TlsConnectTls13, TestTls13ZeroRttNoAlpnClient) {
   SetupForZeroRtt();
   client_->Set0RttEnabled(true);
   server_->Set0RttEnabled(true);
   ExpectResumption(RESUME_TICKET);
   ZeroRttSendReceive(true, true, [this]() {
     PRUint8 b[] = {'b'};
     EXPECT_EQ(SECSuccess, SSLInt_Set0RttAlpn(client_->ssl_fd(), b, 1));
     client_->CheckAlpn(SSL_NEXT_PROTO_EARLY_VALUE, "b");
-    ExpectAlert(client_, kTlsAlertIllegalParameter);
+    client_->ExpectSendAlert(kTlsAlertIllegalParameter);
     return true;
   });
-  Handshake();
+  if (variant_ == ssl_variant_stream) {
+    server_->ExpectSendAlert(kTlsAlertBadRecordMac);
+    Handshake();
+    server_->CheckErrorCode(SSL_ERROR_BAD_MAC_READ);
+  } else {
+    client_->Handshake();
+  }
   client_->CheckErrorCode(SSL_ERROR_NEXT_PROTOCOL_DATA_INVALID);
-  server_->CheckErrorCode(SSL_ERROR_ILLEGAL_PARAMETER_ALERT);
 }
 
 // Remove the old ALPN value and so the client will not offer early data.
 TEST_P(TlsConnectTls13, TestTls13ZeroRttAlpnChangeBoth) {
   EnableAlpn();
   SetupForZeroRtt();
   static const uint8_t alpn[] = {0x01, 0x62};  // "b"
   EnableAlpn(alpn, sizeof(alpn));
@@ -214,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);
@@ -257,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);
     }
@@ -306,17 +460,16 @@ TEST_P(TlsConnectTls13, SendTooMuchEarly
   const PRInt32 short_length = static_cast<PRInt32>(short_size);
   SSLInt_SetMaxEarlyDataSize(static_cast<PRUint32>(short_size));
   SetupForZeroRtt();
 
   client_->Set0RttEnabled(true);
   server_->Set0RttEnabled(true);
   ExpectResumption(RESUME_TICKET);
 
-  ExpectAlert(client_, kTlsAlertEndOfEarlyData);
   client_->Handshake();
   CheckEarlyDataLimit(client_, short_size);
 
   PRInt32 sent;
   // Writing more than the limit will succeed in TLS, but fail in DTLS.
   if (variant_ == ssl_variant_stream) {
     sent = PR_Write(client_->ssl_fd(), big_message,
                     static_cast<PRInt32>(strlen(big_message)));
@@ -360,17 +513,16 @@ TEST_P(TlsConnectTls13, ReceiveTooMuchEa
   const size_t limit = 5;
   SSLInt_SetMaxEarlyDataSize(limit);
   SetupForZeroRtt();
 
   client_->Set0RttEnabled(true);
   server_->Set0RttEnabled(true);
   ExpectResumption(RESUME_TICKET);
 
-  client_->ExpectSendAlert(kTlsAlertEndOfEarlyData);
   client_->Handshake();  // Send ClientHello
   CheckEarlyDataLimit(client_, limit);
 
   // Lift the limit on the client.
   EXPECT_EQ(SECSuccess,
             SSLInt_SetSocketMaxEarlyDataSize(client_->ssl_fd(), 1000));
 
   // Send message
@@ -435,17 +587,16 @@ TEST_P(TlsConnectTls13, ZeroRttOrdering)
 
   // Send (and hold) early data.
   static const std::vector<uint8_t> early_data = {3, 2, 1};
   EXPECT_EQ(static_cast<PRInt32>(early_data.size()),
             PR_Write(client_->ssl_fd(), early_data.data(), early_data.size()));
 
   // Send (and hold) the second client handshake flight.
   // The client sends EndOfEarlyData after seeing the server Finished.
-  ExpectAlert(client_, kTlsAlertEndOfEarlyData);
   server_->Handshake();
   client_->Handshake();
 
   // Send (and hold) 1-RTT data.
   static const std::vector<uint8_t> late_data = {7, 8, 9, 10};
   EXPECT_EQ(static_cast<PRInt32>(late_data.size()),
             PR_Write(client_->ssl_fd(), late_data.data(), late_data.size()));
 
@@ -479,9 +630,14 @@ TEST_P(TlsConnectTls13, ZeroRttOrdering)
   buf.resize(10);
   read = PR_Read(server_->ssl_fd(), buf.data(), buf.size());
   ASSERT_EQ(static_cast<PRInt32>(late_data.size()), read);
   buf.resize(read);
   EXPECT_EQ(late_data, buf);
   EXPECT_EQ(2U, step);
 }
 
+#ifndef NSS_DISABLE_TLS_1_3
+INSTANTIATE_TEST_CASE_P(Tls13ZeroRttReplayTest, TlsZeroRttReplayTest,
+                        TlsConnectTestBase::kTlsVariantsAll);
+#endif
+
 }  // namespace nss_test
--- a/security/nss/gtests/ssl_gtest/ssl_agent_unittest.cc
+++ b/security/nss/gtests/ssl_gtest/ssl_agent_unittest.cc
@@ -39,23 +39,24 @@ const static uint8_t kCannedTls13ClientH
     0xbf, 0x2a, 0xb5, 0x59, 0x64, 0xcc, 0x0c, 0x49, 0x95, 0x36, 0xe4, 0xd9,
     0x2f, 0xd4, 0x24, 0x66, 0x71, 0x6f, 0x5d, 0x70, 0xe2, 0xa0, 0xea, 0x26,
     0x00, 0x2b, 0x00, 0x03, 0x02, 0x7f, kD13, 0x00, 0x0d, 0x00, 0x20, 0x00,
     0x1e, 0x04, 0x03, 0x05, 0x03, 0x06, 0x03, 0x02, 0x03, 0x08, 0x04, 0x08,
     0x05, 0x08, 0x06, 0x04, 0x01, 0x05, 0x01, 0x06, 0x01, 0x02, 0x01, 0x04,
     0x02, 0x05, 0x02, 0x06, 0x02, 0x02, 0x02};
 
 const static uint8_t kCannedTls13ServerHello[] = {
-    0x7f, kD13, 0x9c, 0xbc, 0x14, 0x9b, 0x0e, 0x2e, 0xfa, 0x0d, 0xf3, 0xf0,
-    0x5c, 0x70, 0x7a, 0xe0, 0xd1, 0x9b, 0x3e, 0x5a, 0x44, 0x6b, 0xdf, 0xe5,
-    0xc2, 0x28, 0x64, 0xf7, 0x00, 0xc1, 0x9c, 0x08, 0x76, 0x08, 0x13, 0x01,
-    0x00, 0x28, 0x00, 0x28, 0x00, 0x24, 0x00, 0x1d, 0x00, 0x20, 0xc2, 0xcf,
-    0x23, 0x17, 0x64, 0x23, 0x03, 0xf0, 0xfb, 0x45, 0x98, 0x26, 0xd1, 0x65,
-    0x24, 0xa1, 0x6c, 0xa9, 0x80, 0x8f, 0x2c, 0xac, 0x0a, 0xea, 0x53, 0x3a,
-    0xcb, 0xe3, 0x08, 0x84, 0xae, 0x19};
+    0x03, 0x03, 0x9c, 0xbc, 0x14, 0x9b, 0x0e, 0x2e, 0xfa, 0x0d, 0xf3,
+    0xf0, 0x5c, 0x70, 0x7a, 0xe0, 0xd1, 0x9b, 0x3e, 0x5a, 0x44, 0x6b,
+    0xdf, 0xe5, 0xc2, 0x28, 0x64, 0xf7, 0x00, 0xc1, 0x9c, 0x08, 0x76,
+    0x08, 0x00, 0x13, 0x01, 0x00, 0x00, 0x2e, 0x00, 0x28, 0x00, 0x24,
+    0x00, 0x1d, 0x00, 0x20, 0xc2, 0xcf, 0x23, 0x17, 0x64, 0x23, 0x03,
+    0xf0, 0xfb, 0x45, 0x98, 0x26, 0xd1, 0x65, 0x24, 0xa1, 0x6c, 0xa9,
+    0x80, 0x8f, 0x2c, 0xac, 0x0a, 0xea, 0x53, 0x3a, 0xcb, 0xe3, 0x08,
+    0x84, 0xae, 0x19, 0x00, 0x2b, 0x00, 0x02, 0x7f, kD13};
 static const char *k0RttData = "ABCDEF";
 
 TEST_P(TlsAgentTest, EarlyFinished) {
   DataBuffer buffer;
   MakeTrivialHandshakeRecord(kTlsHandshakeFinished, 0, &buffer);
   ExpectAlert(kTlsAlertUnexpectedMessage);
   ProcessMessage(buffer, TlsAgent::STATE_ERROR,
                  SSL_ERROR_RX_UNEXPECTED_FINISHED);
deleted file mode 100644
--- a/security/nss/gtests/ssl_gtest/ssl_alths_unittest.cc
+++ /dev/null
@@ -1,189 +0,0 @@
-/* -*- 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 <memory>
-#include <vector>
-#include "ssl.h"
-#include "sslerr.h"
-#include "sslproto.h"
-
-#include "gtest_utils.h"
-#include "tls_connect.h"
-#include "tls_filter.h"
-#include "tls_parser.h"
-
-namespace nss_test {
-
-static const uint32_t kServerHelloVersionAlt = SSL_LIBRARY_VERSION_TLS_1_2;
-static const uint16_t kServerHelloVersionRegular =
-    0x7f00 | TLS_1_3_DRAFT_VERSION;
-
-class AltHandshakeTest : public TlsConnectStreamTls13 {
- protected:
-  void SetUp() {
-    TlsConnectStreamTls13::SetUp();
-    client_ccs_recorder_ =
-        std::make_shared<TlsRecordRecorder>(kTlsChangeCipherSpecType);
-    server_handshake_recorder_ =
-        std::make_shared<TlsRecordRecorder>(kTlsHandshakeType);
-    server_ccs_recorder_ =
-        std::make_shared<TlsRecordRecorder>(kTlsChangeCipherSpecType);
-    server_hello_recorder_ =
-        std::make_shared<TlsInspectorRecordHandshakeMessage>(
-            kTlsHandshakeServerHello);
-  }
-
-  void SetAltHandshakeTypeEnabled() {
-    client_->SetAltHandshakeTypeEnabled();
-    server_->SetAltHandshakeTypeEnabled();
-  }
-
-  void InstallFilters() {
-    client_->SetPacketFilter(client_ccs_recorder_);
-    auto chain = std::make_shared<ChainedPacketFilter>(ChainedPacketFilterInit(
-        {server_handshake_recorder_, server_ccs_recorder_,
-         server_hello_recorder_}));
-    server_->SetPacketFilter(chain);
-  }
-
-  void CheckServerHelloRecordVersion(uint16_t record_version) {
-    ASSERT_EQ(record_version,
-              server_handshake_recorder_->record(0).header.version());
-  }
-
-  void CheckServerHelloVersion(uint16_t server_hello_version) {
-    uint32_t ver;
-    ASSERT_TRUE(server_hello_recorder_->buffer().Read(0, 2, &ver));
-    ASSERT_EQ(server_hello_version, ver);
-  }
-
-  void CheckForRegularHandshake() {
-    EXPECT_EQ(0U, client_ccs_recorder_->count());
-    EXPECT_EQ(0U, server_ccs_recorder_->count());
-    CheckServerHelloVersion(kServerHelloVersionRegular);
-    CheckServerHelloRecordVersion(SSL_LIBRARY_VERSION_TLS_1_0);
-  }
-
-  void CheckForAltHandshake() {
-    EXPECT_EQ(1U, client_ccs_recorder_->count());
-    EXPECT_EQ(1U, server_ccs_recorder_->count());
-    CheckServerHelloVersion(kServerHelloVersionAlt);
-    CheckServerHelloRecordVersion(SSL_LIBRARY_VERSION_TLS_1_2);
-  }
-
-  std::shared_ptr<TlsRecordRecorder> client_ccs_recorder_;
-  std::shared_ptr<TlsRecordRecorder> server_handshake_recorder_;
-  std::shared_ptr<TlsRecordRecorder> server_ccs_recorder_;
-  std::shared_ptr<TlsInspectorRecordHandshakeMessage> server_hello_recorder_;
-};
-
-TEST_F(AltHandshakeTest, ClientOnly) {
-  client_->SetAltHandshakeTypeEnabled();
-  InstallFilters();
-  Connect();
-  CheckForRegularHandshake();
-}
-
-TEST_F(AltHandshakeTest, ServerOnly) {
-  server_->SetAltHandshakeTypeEnabled();
-  InstallFilters();
-  Connect();
-  CheckForRegularHandshake();
-}
-
-TEST_F(AltHandshakeTest, Enabled) {
-  SetAltHandshakeTypeEnabled();
-  InstallFilters();
-  Connect();
-  CheckForAltHandshake();
-}
-
-TEST_F(AltHandshakeTest, ZeroRtt) {
-  SetAltHandshakeTypeEnabled();
-  SetupForZeroRtt();
-  SetAltHandshakeTypeEnabled();
-  client_->Set0RttEnabled(true);
-  server_->Set0RttEnabled(true);
-
-  InstallFilters();
-
-  ExpectResumption(RESUME_TICKET);
-  ZeroRttSendReceive(true, true);
-  Handshake();
-  ExpectEarlyDataAccepted(true);
-  CheckConnected();
-
-  CheckForAltHandshake();
-}
-
-// Neither client nor server has the extension prior to resumption, so the
-// client doesn't send a CCS before its 0-RTT data.
-TEST_F(AltHandshakeTest, DisabledBeforeZeroRtt) {
-  SetupForZeroRtt();
-  SetAltHandshakeTypeEnabled();
-  client_->Set0RttEnabled(true);
-  server_->Set0RttEnabled(true);
-
-  InstallFilters();
-
-  ExpectResumption(RESUME_TICKET);
-  ZeroRttSendReceive(true, true);
-  Handshake();
-  ExpectEarlyDataAccepted(true);
-  CheckConnected();
-
-  EXPECT_EQ(0U, client_ccs_recorder_->count());
-  EXPECT_EQ(1U, server_ccs_recorder_->count());
-  CheckServerHelloVersion(kServerHelloVersionAlt);
-}
-
-// Both use the alternative in the initial handshake but only the server enables
-// it on resumption.
-TEST_F(AltHandshakeTest, ClientDisabledAfterZeroRtt) {
-  SetAltHandshakeTypeEnabled();
-  SetupForZeroRtt();
-  server_->SetAltHandshakeTypeEnabled();
-  client_->Set0RttEnabled(true);
-  server_->Set0RttEnabled(true);
-
-  InstallFilters();
-
-  ExpectResumption(RESUME_TICKET);
-  ZeroRttSendReceive(true, true);
-  Handshake();
-  ExpectEarlyDataAccepted(true);
-  CheckConnected();
-
-  CheckForRegularHandshake();
-}
-
-// If the alternative handshake isn't negotiated after 0-RTT, and the client has
-// it enabled, it will send a ChangeCipherSpec.  The server chokes on it if it
-// hasn't negotiated the alternative handshake.
-TEST_F(AltHandshakeTest, ServerDisabledAfterZeroRtt) {
-  SetAltHandshakeTypeEnabled();
-  SetupForZeroRtt();
-  client_->SetAltHandshakeTypeEnabled();
-  client_->Set0RttEnabled(true);
-  server_->Set0RttEnabled(true);
-
-  client_->ExpectSendAlert(kTlsAlertEndOfEarlyData);
-  client_->Handshake();  // Send ClientHello (and CCS)
-
-  server_->Handshake();  // Consume the ClientHello, which is OK.
-  client_->ExpectResumption();
-  client_->Handshake();  // Read the server handshake.
-  EXPECT_EQ(TlsAgent::STATE_CONNECTED, client_->state());
-
-  // Now the server reads the CCS instead of more handshake messages.
-  ExpectAlert(server_, kTlsAlertBadRecordMac);
-  server_->Handshake();
-  EXPECT_EQ(TlsAgent::STATE_ERROR, server_->state());
-  client_->Handshake();  // Consume the alert.
-  EXPECT_EQ(TlsAgent::STATE_ERROR, client_->state());
-}
-
-}  // nss_test
--- a/security/nss/gtests/ssl_gtest/ssl_auth_unittest.cc
+++ b/security/nss/gtests/ssl_gtest/ssl_auth_unittest.cc
@@ -154,23 +154,21 @@ TEST_P(TlsConnectTls12, ClientAuthBigRsa
   server_->RequestClientAuth(true);
   Connect();
   CheckKeys();
   CheckSigScheme(capture_cert_verify, 0, server_, ssl_sig_rsa_pss_sha256, 2048);
 }
 
 class TlsZeroCertificateRequestSigAlgsFilter : public TlsHandshakeFilter {
  public:
+  TlsZeroCertificateRequestSigAlgsFilter()
+      : TlsHandshakeFilter({kTlsHandshakeCertificateRequest}) {}
   virtual PacketFilter::Action FilterHandshake(
       const TlsHandshakeFilter::HandshakeHeader& header,
       const DataBuffer& input, DataBuffer* output) {
-    if (header.handshake_type() != kTlsHandshakeCertificateRequest) {
-      return KEEP;
-    }
-
     TlsParser parser(input);
     std::cerr << "Zeroing CertReq.supported_signature_algorithms" << std::endl;
 
     DataBuffer cert_types;
     if (!parser.ReadVariable(&cert_types, 1)) {
       ADD_FAILURE();
       return KEEP;
     }
@@ -594,18 +592,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.
@@ -623,18 +620,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/security/nss/gtests/ssl_gtest/ssl_ciphersuite_unittest.cc
+++ b/security/nss/gtests/ssl_gtest/ssl_ciphersuite_unittest.cc
@@ -26,21 +26,21 @@ namespace nss_test {
 typedef std::tuple<SSLProtocolVariant, uint16_t, uint16_t, SSLNamedGroup,
                    SSLSignatureScheme>
     CipherSuiteProfile;
 
 class TlsCipherSuiteTestBase : public TlsConnectTestBase {
  public:
   TlsCipherSuiteTestBase(SSLProtocolVariant variant, uint16_t version,
                          uint16_t cipher_suite, SSLNamedGroup group,
-                         SSLSignatureScheme signature_scheme)
+                         SSLSignatureScheme sig_scheme)
       : TlsConnectTestBase(variant, version),
         cipher_suite_(cipher_suite),
         group_(group),
-        signature_scheme_(signature_scheme),
+        sig_scheme_(sig_scheme),
         csinfo_({0}) {
     SECStatus rv =
         SSL_GetCipherSuiteInfo(cipher_suite_, &csinfo_, sizeof(csinfo_));
     EXPECT_EQ(SECSuccess, rv);
     if (rv == SECSuccess) {
       std::cerr << "Cipher suite: " << csinfo_.cipherSuiteName << std::endl;
     }
     auth_type_ = csinfo_.authType;
@@ -55,24 +55,24 @@ class TlsCipherSuiteTestBase : public Tl
     server_->EnableSingleCipher(cipher_suite_);
 
     if (version_ >= SSL_LIBRARY_VERSION_TLS_1_3) {
       std::vector<SSLNamedGroup> groups = {group_};
       client_->ConfigNamedGroups(groups);
       server_->ConfigNamedGroups(groups);
       kea_type_ = SSLInt_GetKEAType(group_);
 
-      client_->SetSignatureSchemes(&signature_scheme_, 1);
-      server_->SetSignatureSchemes(&signature_scheme_, 1);
+      client_->SetSignatureSchemes(&sig_scheme_, 1);
+      server_->SetSignatureSchemes(&sig_scheme_, 1);
     }
   }
 
   virtual void SetupCertificate() {
     if (version_ >= SSL_LIBRARY_VERSION_TLS_1_3) {
-      switch (signature_scheme_) {
+      switch (sig_scheme_) {
         case ssl_sig_rsa_pkcs1_sha256:
         case ssl_sig_rsa_pkcs1_sha384:
         case ssl_sig_rsa_pkcs1_sha512:
           Reset(TlsAgent::kServerRsaSign);
           auth_type_ = ssl_auth_rsa_sign;
           break;
         case ssl_sig_rsa_pss_sha256:
         case ssl_sig_rsa_pss_sha384:
@@ -88,18 +88,17 @@ class TlsCipherSuiteTestBase : public Tl
           Reset(TlsAgent::kServerEcdsa256);
           auth_type_ = ssl_auth_ecdsa;
           break;
         case ssl_sig_ecdsa_secp384r1_sha384:
           Reset(TlsAgent::kServerEcdsa384);
           auth_type_ = ssl_auth_ecdsa;
           break;
         default:
-          ASSERT_TRUE(false) << "Unsupported signature scheme: "
-                             << signature_scheme_;
+          ADD_FAILURE() << "Unsupported signature scheme: " << sig_scheme_;
           break;
       }
     } else {
       switch (csinfo_.authType) {
         case ssl_auth_rsa_sign:
           Reset(TlsAgent::kServerRsaSign);
           break;
         case ssl_auth_rsa_decrypt:
@@ -182,17 +181,17 @@ class TlsCipherSuiteTestBase : public Tl
     return limit;
   }
 
  protected:
   uint16_t cipher_suite_;
   SSLAuthType auth_type_;
   SSLKEAType kea_type_;
   SSLNamedGroup group_;
-  SSLSignatureScheme signature_scheme_;
+  SSLSignatureScheme sig_scheme_;
   SSLCipherSuiteInfo csinfo_;
 };
 
 class TlsCipherSuiteTest
     : public TlsCipherSuiteTestBase,
       public ::testing::WithParamInterface<CipherSuiteProfile> {
  public:
   TlsCipherSuiteTest()
new file mode 100644
--- /dev/null
+++ b/security/nss/gtests/ssl_gtest/ssl_custext_unittest.cc
@@ -0,0 +1,502 @@
+/* -*- 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 "ssl.h"
+#include "ssl3prot.h"
+#include "sslerr.h"
+#include "sslproto.h"
+#include "sslexp.h"
+
+#include <memory>
+
+#include "tls_connect.h"
+
+namespace nss_test {
+
+static void IncrementCounterArg(void *arg) {
+  if (arg) {
+    auto *called = reinterpret_cast<size_t *>(arg);
+    ++*called;
+  }
+}
+
+PRBool NoopExtensionWriter(PRFileDesc *fd, SSLHandshakeType message,
+                           PRUint8 *data, unsigned int *len,
+                           unsigned int maxLen, void *arg) {
+  IncrementCounterArg(arg);
+  return PR_FALSE;
+}
+
+PRBool EmptyExtensionWriter(PRFileDesc *fd, SSLHandshakeType message,
+                            PRUint8 *data, unsigned int *len,
+                            unsigned int maxLen, void *arg) {
+  IncrementCounterArg(arg);
+  return PR_TRUE;
+}
+
+SECStatus NoopExtensionHandler(PRFileDesc *fd, SSLHandshakeType message,
+                               const PRUint8 *data, unsigned int len,
+                               SSLAlertDescription *alert, void *arg) {
+  return SECSuccess;
+}
+
+// All of the (current) set of supported extensions, plus a few extra.
+static const uint16_t kManyExtensions[] = {
+    ssl_server_name_xtn,
+    ssl_cert_status_xtn,
+    ssl_supported_groups_xtn,
+    ssl_ec_point_formats_xtn,
+    ssl_signature_algorithms_xtn,
+    ssl_use_srtp_xtn,
+    ssl_app_layer_protocol_xtn,
+    ssl_signed_cert_timestamp_xtn,
+    ssl_padding_xtn,
+    ssl_extended_master_secret_xtn,
+    ssl_session_ticket_xtn,
+    ssl_tls13_key_share_xtn,
+    ssl_tls13_pre_shared_key_xtn,
+    ssl_tls13_early_data_xtn,
+    ssl_tls13_supported_versions_xtn,
+    ssl_tls13_cookie_xtn,
+    ssl_tls13_psk_key_exchange_modes_xtn,
+    ssl_tls13_ticket_early_data_info_xtn,
+    ssl_tls13_certificate_authorities_xtn,
+    ssl_next_proto_nego_xtn,
+    ssl_renegotiation_info_xtn,
+    ssl_tls13_short_header_xtn,
+    1,
+    0xffff};
+// The list here includes all extensions we expect to use (SSL_MAX_EXTENSIONS),
+// plus the deprecated values (see sslt.h), and two extra dummy values.
+PR_STATIC_ASSERT((SSL_MAX_EXTENSIONS + 5) == PR_ARRAY_SIZE(kManyExtensions));
+
+void InstallManyWriters(std::shared_ptr<TlsAgent> agent,
+                        SSLExtensionWriter writer, size_t *installed = nullptr,
+                        size_t *called = nullptr) {
+  for (size_t i = 0; i < PR_ARRAY_SIZE(kManyExtensions); ++i) {
+    SSLExtensionSupport support;
+    SECStatus rv = SSL_GetExtensionSupport(kManyExtensions[i], &support);
+    ASSERT_EQ(SECSuccess, rv) << "SSL_GetExtensionSupport cannot fail";
+
+    rv = SSL_InstallExtensionHooks(agent->ssl_fd(), kManyExtensions[i], writer,
+                                   called, NoopExtensionHandler, nullptr);
+    if (support == ssl_ext_native_only) {
+      EXPECT_EQ(SECFailure, rv);
+      EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError());
+    } else {
+      if (installed) {
+        ++*installed;
+      }
+      EXPECT_EQ(SECSuccess, rv);
+    }
+  }
+}
+
+TEST_F(TlsConnectStreamTls13, CustomExtensionAllNoopClient) {
+  EnsureTlsSetup();
+  size_t installed = 0;
+  size_t called = 0;
+  InstallManyWriters(client_, NoopExtensionWriter, &installed, &called);
+  EXPECT_LT(0U, installed);
+  Connect();
+  EXPECT_EQ(installed, called);
+}
+
+TEST_F(TlsConnectStreamTls13, CustomExtensionAllNoopServer) {
+  EnsureTlsSetup();
+  size_t installed = 0;
+  size_t called = 0;
+  InstallManyWriters(server_, NoopExtensionWriter, &installed, &called);
+  EXPECT_LT(0U, installed);
+  Connect();
+  // Extension writers are all called for each of ServerHello,
+  // EncryptedExtensions, and Certificate.
+  EXPECT_EQ(installed * 3, called);
+}
+
+TEST_F(TlsConnectStreamTls13, CustomExtensionEmptyWriterClient) {
+  EnsureTlsSetup();
+  InstallManyWriters(client_, EmptyExtensionWriter);
+  InstallManyWriters(server_, EmptyExtensionWriter);
+  Connect();
+}
+
+TEST_F(TlsConnectStreamTls13, CustomExtensionEmptyWriterServer) {
+  EnsureTlsSetup();
+  InstallManyWriters(server_, EmptyExtensionWriter);
+  // Sending extensions that the client doesn't expect leads to extensions
+  // appearing even if the client didn't send one, or in the wrong messages.
+  client_->ExpectSendAlert(kTlsAlertUnsupportedExtension);
+  server_->ExpectSendAlert(kTlsAlertBadRecordMac);
+  ConnectExpectFail();
+}
+
+// Install an writer to disable sending of a natively-supported extension.
+TEST_F(TlsConnectStreamTls13, CustomExtensionWriterDisable) {
+  EnsureTlsSetup();
+
+  // This option enables sending the extension via the native support.
+  SECStatus rv = SSL_OptionSet(client_->ssl_fd(),
+                               SSL_ENABLE_SIGNED_CERT_TIMESTAMPS, PR_TRUE);
+  EXPECT_EQ(SECSuccess, rv);
+
+  // This installs an override that doesn't do anything.  You have to specify
+  // something; passing all nullptr values removes an existing handler.
+  rv = SSL_InstallExtensionHooks(
+      client_->ssl_fd(), ssl_signed_cert_timestamp_xtn, NoopExtensionWriter,
+      nullptr, NoopExtensionHandler, nullptr);
+  EXPECT_EQ(SECSuccess, rv);
+  auto capture =
+      std::make_shared<TlsExtensionCapture>(ssl_signed_cert_timestamp_xtn);
+  client_->SetPacketFilter(capture);
+
+  Connect();
+  // So nothing will be sent.
+  EXPECT_FALSE(capture->captured());
+}
+
+// An extension that is unlikely to be parsed as valid.
+static uint8_t kNonsenseExtension[] = {91, 82, 73, 64, 55, 46, 37, 28, 19};
+
+static PRBool NonsenseExtensionWriter(PRFileDesc *fd, SSLHandshakeType message,
+                                      PRUint8 *data, unsigned int *len,
+                                      unsigned int maxLen, void *arg) {
+  TlsAgent *agent = reinterpret_cast<TlsAgent *>(arg);
+  EXPECT_NE(nullptr, agent);
+  EXPECT_NE(nullptr, data);
+  EXPECT_NE(nullptr, len);
+  EXPECT_EQ(0U, *len);
+  EXPECT_LT(0U, maxLen);
+  EXPECT_EQ(agent->ssl_fd(), fd);
+
+  if (message != ssl_hs_client_hello && message != ssl_hs_server_hello &&
+      message != ssl_hs_encrypted_extensions) {
+    return PR_FALSE;
+  }
+
+  *len = static_cast<unsigned int>(sizeof(kNonsenseExtension));
+  EXPECT_GE(maxLen, *len);
+  if (maxLen < *len) {
+    return PR_FALSE;
+  }
+  PORT_Memcpy(data, kNonsenseExtension, *len);
+  return PR_TRUE;
+}
+
+// Override the extension handler for an natively-supported and produce
+// nonsense, which results in a handshake failure.
+TEST_F(TlsConnectStreamTls13, CustomExtensionOverride) {
+  EnsureTlsSetup();
+
+  // This option enables sending the extension via the native support.
+  SECStatus rv = SSL_OptionSet(client_->ssl_fd(),
+                               SSL_ENABLE_SIGNED_CERT_TIMESTAMPS, PR_TRUE);
+  EXPECT_EQ(SECSuccess, rv);
+
+  // This installs an override that sends nonsense.
+  rv = SSL_InstallExtensionHooks(
+      client_->ssl_fd(), ssl_signed_cert_timestamp_xtn, NonsenseExtensionWriter,
+      client_.get(), NoopExtensionHandler, nullptr);
+  EXPECT_EQ(SECSuccess, rv);
+
+  // Capture it to see what we got.
+  auto capture =
+      std::make_shared<TlsExtensionCapture>(ssl_signed_cert_timestamp_xtn);
+  client_->SetPacketFilter(capture);
+
+  ConnectExpectAlert(server_, kTlsAlertDecodeError);
+
+  EXPECT_TRUE(capture->captured());
+  EXPECT_EQ(DataBuffer(kNonsenseExtension, sizeof(kNonsenseExtension)),
+            capture->extension());
+}
+
+static SECStatus NonsenseExtensionHandler(PRFileDesc *fd,
+                                          SSLHandshakeType message,
+                                          const PRUint8 *data, unsigned int len,
+                                          SSLAlertDescription *alert,
+                                          void *arg) {
+  TlsAgent *agent = reinterpret_cast<TlsAgent *>(arg);
+  EXPECT_EQ(agent->ssl_fd(), fd);
+  if (agent->role() == TlsAgent::SERVER) {
+    EXPECT_EQ(ssl_hs_client_hello, message);
+  } else {
+    EXPECT_TRUE(message == ssl_hs_server_hello ||
+                message == ssl_hs_encrypted_extensions);
+  }
+  EXPECT_EQ(DataBuffer(kNonsenseExtension, sizeof(kNonsenseExtension)),
+            DataBuffer(data, len));
+  EXPECT_NE(nullptr, alert);
+  return SECSuccess;
+}
+
+// Send nonsense in an extension from client to server.
+TEST_F(TlsConnectStreamTls13, CustomExtensionClientToServer) {
+  EnsureTlsSetup();
+
+  // This installs an override that sends nonsense.
+  const uint16_t extension_code = 0xffe5;
+  SECStatus rv = SSL_InstallExtensionHooks(
+      client_->ssl_fd(), extension_code, NonsenseExtensionWriter, client_.get(),
+      NoopExtensionHandler, nullptr);
+  EXPECT_EQ(SECSuccess, rv);
+
+  // Capture it to see what we got.
+  auto capture = std::make_shared<TlsExtensionCapture>(extension_code);
+  client_->SetPacketFilter(capture);
+
+  // Handle it so that the handshake completes.
+  rv = SSL_InstallExtensionHooks(server_->ssl_fd(), extension_code,
+                                 NoopExtensionWriter, nullptr,
+                                 NonsenseExtensionHandler, server_.get());
+  EXPECT_EQ(SECSuccess, rv);
+
+  Connect();
+
+  EXPECT_TRUE(capture->captured());
+  EXPECT_EQ(DataBuffer(kNonsenseExtension, sizeof(kNonsenseExtension)),
+            capture->extension());
+}
+
+static PRBool NonsenseExtensionWriterSH(PRFileDesc *fd,
+                                        SSLHandshakeType message, PRUint8 *data,
+                                        unsigned int *len, unsigned int maxLen,
+                                        void *arg) {
+  if (message == ssl_hs_server_hello) {
+    return NonsenseExtensionWriter(fd, message, data, len, maxLen, arg);
+  }
+  return PR_FALSE;
+}
+
+// Send nonsense in an extension from server to client, in ServerHello.
+TEST_F(TlsConnectStreamTls13, CustomExtensionServerToClientSH) {
+  EnsureTlsSetup();
+
+  // This installs an override that sends nothing but expects nonsense.
+  const uint16_t extension_code = 0xff5e;
+  SECStatus rv = SSL_InstallExtensionHooks(
+      client_->ssl_fd(), extension_code, EmptyExtensionWriter, nullptr,
+      NonsenseExtensionHandler, client_.get());
+  EXPECT_EQ(SECSuccess, rv);
+
+  // Have the server send nonsense.
+  rv = SSL_InstallExtensionHooks(server_->ssl_fd(), extension_code,
+                                 NonsenseExtensionWriterSH, server_.get(),
+                                 NoopExtensionHandler, nullptr);
+  EXPECT_EQ(SECSuccess, rv);
+
+  // Capture the extension from the ServerHello only and check it.
+  auto capture = std::make_shared<TlsExtensionCapture>(extension_code);
+  capture->SetHandshakeTypes({kTlsHandshakeServerHello});
+  server_->SetPacketFilter(capture);
+
+  Connect();
+
+  EXPECT_TRUE(capture->captured());
+  EXPECT_EQ(DataBuffer(kNonsenseExtension, sizeof(kNonsenseExtension)),
+            capture->extension());
+}
+
+static PRBool NonsenseExtensionWriterEE(PRFileDesc *fd,
+                                        SSLHandshakeType message, PRUint8 *data,
+                                        unsigned int *len, unsigned int maxLen,
+                                        void *arg) {
+  if (message == ssl_hs_encrypted_extensions) {
+    return NonsenseExtensionWriter(fd, message, data, len, maxLen, arg);
+  }
+  return PR_FALSE;
+}
+
+// Send nonsense in an extension from server to client, in EncryptedExtensions.
+TEST_F(TlsConnectStreamTls13, CustomExtensionServerToClientEE) {
+  EnsureTlsSetup();
+
+  // This installs an override that sends nothing but expects nonsense.
+  const uint16_t extension_code = 0xff5e;
+  SECStatus rv = SSL_InstallExtensionHooks(
+      client_->ssl_fd(), extension_code, EmptyExtensionWriter, nullptr,
+      NonsenseExtensionHandler, client_.get());
+  EXPECT_EQ(SECSuccess, rv);
+
+  // Have the server send nonsense.
+  rv = SSL_InstallExtensionHooks(server_->ssl_fd(), extension_code,
+                                 NonsenseExtensionWriterEE, server_.get(),
+                                 NoopExtensionHandler, nullptr);
+  EXPECT_EQ(SECSuccess, rv);
+
+  // Capture the extension from the EncryptedExtensions only and check it.
+  auto capture = std::make_shared<TlsExtensionCapture>(extension_code);
+  capture->SetHandshakeTypes({kTlsHandshakeEncryptedExtensions});
+  server_->SetTlsRecordFilter(capture);
+
+  Connect();
+
+  EXPECT_TRUE(capture->captured());
+  EXPECT_EQ(DataBuffer(kNonsenseExtension, sizeof(kNonsenseExtension)),
+            capture->extension());
+}
+
+TEST_F(TlsConnectStreamTls13, CustomExtensionUnsolicitedServer) {
+  EnsureTlsSetup();
+
+  const uint16_t extension_code = 0xff5e;
+  SECStatus rv = SSL_InstallExtensionHooks(
+      server_->ssl_fd(), extension_code, NonsenseExtensionWriter, server_.get(),
+      NoopExtensionHandler, nullptr);
+  EXPECT_EQ(SECSuccess, rv);
+
+  // Capture it to see what we got.
+  auto capture = std::make_shared<TlsExtensionCapture>(extension_code);
+  server_->SetPacketFilter(capture);
+
+  client_->ExpectSendAlert(kTlsAlertUnsupportedExtension);
+  server_->ExpectSendAlert(kTlsAlertBadRecordMac);
+  ConnectExpectFail();
+
+  EXPECT_TRUE(capture->captured());
+  EXPECT_EQ(DataBuffer(kNonsenseExtension, sizeof(kNonsenseExtension)),
+            capture->extension());
+}
+
+SECStatus RejectExtensionHandler(PRFileDesc *fd, SSLHandshakeType message,
+                                 const PRUint8 *data, unsigned int len,
+                                 SSLAlertDescription *alert, void *arg) {
+  return SECFailure;
+}
+
+TEST_F(TlsConnectStreamTls13, CustomExtensionServerReject) {
+  EnsureTlsSetup();
+
+  // This installs an override that sends nonsense.
+  const uint16_t extension_code = 0xffe7;
+  SECStatus rv = SSL_InstallExtensionHooks(client_->ssl_fd(), extension_code,
+                                           EmptyExtensionWriter, nullptr,
+                                           NoopExtensionHandler, nullptr);
+  EXPECT_EQ(SECSuccess, rv);
+
+  // Reject the extension for no good reason.
+  rv = SSL_InstallExtensionHooks(server_->ssl_fd(), extension_code,
+                                 NoopExtensionWriter, nullptr,
+                                 RejectExtensionHandler, nullptr);
+  EXPECT_EQ(SECSuccess, rv);
+
+  ConnectExpectAlert(server_, kTlsAlertHandshakeFailure);
+}
+
+// Send nonsense in an extension from client to server.
+TEST_F(TlsConnectStreamTls13, CustomExtensionClientReject) {
+  EnsureTlsSetup();
+
+  // This installs an override that sends nothing but expects nonsense.
+  const uint16_t extension_code = 0xff58;
+  SECStatus rv = SSL_InstallExtensionHooks(client_->ssl_fd(), extension_code,
+                                           EmptyExtensionWriter, nullptr,
+                                           RejectExtensionHandler, nullptr);
+  EXPECT_EQ(SECSuccess, rv);
+
+  // Have the server send nonsense.
+  rv = SSL_InstallExtensionHooks(server_->ssl_fd(), extension_code,
+                                 EmptyExtensionWriter, nullptr,
+                                 NoopExtensionHandler, nullptr);
+  EXPECT_EQ(SECSuccess, rv);
+
+  client_->ExpectSendAlert(kTlsAlertHandshakeFailure);
+  server_->ExpectSendAlert(kTlsAlertBadRecordMac);
+  ConnectExpectFail();
+}
+
+static const uint8_t kCustomAlert = 0xf6;
+
+SECStatus AlertExtensionHandler(PRFileDesc *fd, SSLHandshakeType message,
+                                const PRUint8 *data, unsigned int len,
+                                SSLAlertDescription *alert, void *arg) {
+  *alert = kCustomAlert;
+  return SECFailure;
+}
+
+TEST_F(TlsConnectStreamTls13, CustomExtensionServerRejectAlert) {
+  EnsureTlsSetup();
+
+  // This installs an override that sends nonsense.
+  const uint16_t extension_code = 0xffea;
+  SECStatus rv = SSL_InstallExtensionHooks(client_->ssl_fd(), extension_code,
+                                           EmptyExtensionWriter, nullptr,
+                                           NoopExtensionHandler, nullptr);
+  EXPECT_EQ(SECSuccess, rv);
+
+  // Reject the extension for no good reason.
+  rv = SSL_InstallExtensionHooks(server_->ssl_fd(), extension_code,
+                                 NoopExtensionWriter, nullptr,
+                                 AlertExtensionHandler, nullptr);
+  EXPECT_EQ(SECSuccess, rv);
+
+  ConnectExpectAlert(server_, kCustomAlert);
+}
+
+// Send nonsense in an extension from client to server.
+TEST_F(TlsConnectStreamTls13, CustomExtensionClientRejectAlert) {
+  EnsureTlsSetup();
+
+  // This installs an override that sends nothing but expects nonsense.
+  const uint16_t extension_code = 0xff5a;
+  SECStatus rv = SSL_InstallExtensionHooks(client_->ssl_fd(), extension_code,
+                                           EmptyExtensionWriter, nullptr,
+                                           AlertExtensionHandler, nullptr);
+  EXPECT_EQ(SECSuccess, rv);
+
+  // Have the server send nonsense.
+  rv = SSL_InstallExtensionHooks(server_->ssl_fd(), extension_code,
+                                 EmptyExtensionWriter, nullptr,
+                                 NoopExtensionHandler, nullptr);
+  EXPECT_EQ(SECSuccess, rv);
+
+  client_->ExpectSendAlert(kCustomAlert);
+  server_->ExpectSendAlert(kTlsAlertBadRecordMac);
+  ConnectExpectFail();
+}
+
+// Configure a custom extension hook badly.
+TEST_F(TlsConnectStreamTls13, CustomExtensionOnlyWriter) {
+  EnsureTlsSetup();
+
+  // This installs an override that sends nothing but expects nonsense.
+  SECStatus rv =
+      SSL_InstallExtensionHooks(client_->ssl_fd(), 0xff6c, EmptyExtensionWriter,
+                                nullptr, nullptr, nullptr);
+  EXPECT_EQ(SECFailure, rv);
+  EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError());
+}
+
+TEST_F(TlsConnectStreamTls13, CustomExtensionOnlyHandler) {
+  EnsureTlsSetup();
+
+  // This installs an override that sends nothing but expects nonsense.
+  SECStatus rv =
+      SSL_InstallExtensionHooks(client_->ssl_fd(), 0xff6d, nullptr, nullptr,
+                                NoopExtensionHandler, nullptr);
+  EXPECT_EQ(SECFailure, rv);
+  EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError());
+}
+
+TEST_F(TlsConnectStreamTls13, CustomExtensionOverrunBuffer) {
+  EnsureTlsSetup();
+  // This doesn't actually overrun the buffer, but it says that it does.
+  auto overrun_writer = [](PRFileDesc *fd, SSLHandshakeType message,
+                           PRUint8 *data, unsigned int *len,
+                           unsigned int maxLen, void *arg) -> PRBool {
+    *len = maxLen + 1;
+    return PR_TRUE;
+  };
+  SECStatus rv =
+      SSL_InstallExtensionHooks(client_->ssl_fd(), 0xff71, overrun_writer,
+                                nullptr, NoopExtensionHandler, nullptr);
+  EXPECT_EQ(SECSuccess, rv);
+  client_->StartConnect();
+  client_->Handshake();
+  client_->CheckErrorCode(SEC_ERROR_APPLICATION_CALLBACK_ERROR);
+}
+
+}  // namespace "nss_test"
--- a/security/nss/gtests/ssl_gtest/ssl_damage_unittest.cc
+++ b/security/nss/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());
 
@@ -46,26 +45,22 @@ TEST_F(TlsConnectTest, DamageSecretHandl
   client_->CheckErrorCode(SSL_ERROR_DECRYPT_ERROR_ALERT);
 }
 
 TEST_F(TlsConnectTest, DamageSecretHandleServerFinished) {
   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);
-  client_->ExpectSendAlert(kTlsAlertDecryptError);
-  // The server can't read the client's alert, so it also sends an alert.
-  server_->ExpectSendAlert(kTlsAlertBadRecordMac);
   server_->SetPacketFilter(std::make_shared<AfterRecordN>(
       server_, client_,
       0,  // ServerHello.
       [this]() { SSLInt_DamageServerHsTrafficSecret(client_->ssl_fd()); }));
-  ConnectExpectFail();
+  ConnectExpectAlert(client_, kTlsAlertDecryptError);
   client_->CheckErrorCode(SSL_ERROR_BAD_HANDSHAKE_HASH_VALUE);
-  server_->CheckErrorCode(SSL_ERROR_BAD_MAC_READ);
 }
 
 TEST_P(TlsConnectGenericPre13, DamageServerSignature) {
   EnsureTlsSetup();
   auto filter =
       std::make_shared<TlsLastByteDamager>(kTlsHandshakeServerKeyExchange);
   server_->SetTlsRecordFilter(filter);
   ExpectAlert(client_, kTlsAlertDecryptError);
@@ -74,42 +69,31 @@ TEST_P(TlsConnectGenericPre13, DamageSer
   server_->CheckErrorCode(SSL_ERROR_DECRYPT_ERROR_ALERT);
 }
 
 TEST_P(TlsConnectTls13, DamageServerSignature) {
   EnsureTlsSetup();
   auto filter =
       std::make_shared<TlsLastByteDamager>(kTlsHandshakeCertificateVerify);
   server_->SetTlsRecordFilter(filter);
-  filter->EnableDecryption();
-  client_->ExpectSendAlert(kTlsAlertDecryptError);
-  // The server can't read the client's alert, so it also sends an alert.
-  if (variant_ == ssl_variant_stream) {
-    server_->ExpectSendAlert(kTlsAlertBadRecordMac);
-    ConnectExpectFail();
-    server_->CheckErrorCode(SSL_ERROR_BAD_MAC_READ);
-  } else {
-    ConnectExpectFailOneSide(TlsAgent::CLIENT);
-  }
+  ConnectExpectAlert(client_, kTlsAlertDecryptError);
   client_->CheckErrorCode(SEC_ERROR_BAD_SIGNATURE);
 }
 
 TEST_P(TlsConnectGeneric, DamageClientSignature) {
   EnsureTlsSetup();
   client_->SetupClientAuth();
   server_->RequestClientAuth(true);
   auto filter =
       std::make_shared<TlsLastByteDamager>(kTlsHandshakeCertificateVerify);
   client_->SetTlsRecordFilter(filter);
   server_->ExpectSendAlert(kTlsAlertDecryptError);
-  filter->EnableDecryption();
   // 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/security/nss/gtests/ssl_gtest/ssl_dhe_unittest.cc
+++ b/security/nss/gtests/ssl_gtest/ssl_dhe_unittest.cc
@@ -98,24 +98,21 @@ TEST_P(TlsConnectGenericPre13, ConnectFf
     ConnectExpectAlert(server_, kTlsAlertHandshakeFailure);
     client_->CheckErrorCode(SSL_ERROR_NO_CYPHER_OVERLAP);
     server_->CheckErrorCode(SSL_ERROR_NO_CYPHER_OVERLAP);
   }
 }
 
 class TlsDheServerKeyExchangeDamager : public TlsHandshakeFilter {
  public:
-  TlsDheServerKeyExchangeDamager() {}
+  TlsDheServerKeyExchangeDamager()
+      : TlsHandshakeFilter({kTlsHandshakeServerKeyExchange}) {}
   virtual PacketFilter::Action FilterHandshake(
       const TlsHandshakeFilter::HandshakeHeader& header,
       const DataBuffer& input, DataBuffer* output) {
-    if (header.handshake_type() != kTlsHandshakeServerKeyExchange) {
-      return KEEP;
-    }
-
     // Damage the first octet of dh_p.  Anything other than the known prime will
     // be rejected as "weak" when we have SSL_REQUIRE_DH_NAMED_GROUPS enabled.
     *output = input;
     output->data()[3] ^= 73;
     return CHANGE;
   }
 };
 
@@ -139,17 +136,18 @@ class TlsDheSkeChangeY : public TlsHands
     kYZero,
     kYOne,
     kYPMinusOne,
     kYGreaterThanP,
     kYTooLarge,
     kYZeroPad
   };
 
-  TlsDheSkeChangeY(ChangeYTo change) : change_Y_(change) {}
+  TlsDheSkeChangeY(uint8_t handshake_type, ChangeYTo change)
+      : TlsHandshakeFilter({handshake_type}), change_Y_(change) {}
 
  protected:
   void ChangeY(const DataBuffer& input, DataBuffer* output, size_t offset,
                const DataBuffer& prime) {
     static const uint8_t kExtraZero = 0;
     static const uint8_t kTooLargeExtra = 1;
 
     uint32_t dh_Ys_len;
@@ -205,28 +203,26 @@ class TlsDheSkeChangeY : public TlsHands
 
  private:
   ChangeYTo change_Y_;
 };
 
 class TlsDheSkeChangeYServer : public TlsDheSkeChangeY {
  public:
   TlsDheSkeChangeYServer(ChangeYTo change, bool modify)
-      : TlsDheSkeChangeY(change), modify_(modify), p_() {}
+      : TlsDheSkeChangeY(kTlsHandshakeServerKeyExchange, change),
+        modify_(modify),
+        p_() {}
 
   const DataBuffer& prime() const { return p_; }
 
  protected:
   virtual PacketFilter::Action FilterHandshake(
       const TlsHandshakeFilter::HandshakeHeader& header,
       const DataBuffer& input, DataBuffer* output) override {
-    if (header.handshake_type() != kTlsHandshakeServerKeyExchange) {
-      return KEEP;
-    }
-
     size_t offset = 2;
     // Read dh_p
     uint32_t dh_len = 0;
     EXPECT_TRUE(input.Read(0, 2, &dh_len));
     EXPECT_GT(input.len(), offset + dh_len);
     p_.Assign(input.data() + offset, dh_len);
     offset += dh_len;
 
@@ -246,26 +242,23 @@ class TlsDheSkeChangeYServer : public Tl
   DataBuffer p_;
 };
 
 class TlsDheSkeChangeYClient : public TlsDheSkeChangeY {
  public:
   TlsDheSkeChangeYClient(
       ChangeYTo change,
       std::shared_ptr<const TlsDheSkeChangeYServer> server_filter)
-      : TlsDheSkeChangeY(change), server_filter_(server_filter) {}
+      : TlsDheSkeChangeY(kTlsHandshakeClientKeyExchange, change),
+        server_filter_(server_filter) {}
 
  protected:
   virtual PacketFilter::Action FilterHandshake(
       const TlsHandshakeFilter::HandshakeHeader& header,
       const DataBuffer& input, DataBuffer* output) override {
-    if (header.handshake_type() != kTlsHandshakeClientKeyExchange) {
-      return KEEP;
-    }
-
     ChangeY(input, output, 0, server_filter_->prime());
     return CHANGE;
   }
 
  private:
   std::shared_ptr<const TlsDheSkeChangeYServer> server_filter_;
 };
 
@@ -360,23 +353,20 @@ INSTANTIATE_TEST_CASE_P(
                        TlsConnectTestBase::kTlsV10ToV12, kAllY, kTrueFalse));
 INSTANTIATE_TEST_CASE_P(
     DamageYDatagram, TlsDamageDHYTest,
     ::testing::Combine(TlsConnectTestBase::kTlsVariantsDatagram,
                        TlsConnectTestBase::kTlsV11V12, kAllY, kTrueFalse));
 
 class TlsDheSkeMakePEven : public TlsHandshakeFilter {
  public:
+  TlsDheSkeMakePEven() : TlsHandshakeFilter({kTlsHandshakeServerKeyExchange}) {}
   virtual PacketFilter::Action FilterHandshake(
       const TlsHandshakeFilter::HandshakeHeader& header,
       const DataBuffer& input, DataBuffer* output) {
-    if (header.handshake_type() != kTlsHandshakeServerKeyExchange) {
-      return KEEP;
-    }
-
     // Find the end of dh_p
     uint32_t dh_len = 0;
     EXPECT_TRUE(input.Read(0, 2, &dh_len));
     EXPECT_GT(input.len(), 2 + dh_len) << "enough space for dh_p";
     size_t offset = 2 + dh_len - 1;
     EXPECT_TRUE((input.data()[offset] & 0x01) == 0x01) << "p should be odd";
 
     *output = input;
@@ -394,23 +384,20 @@ TEST_P(TlsConnectGenericPre13, MakeDhePE
   ConnectExpectAlert(client_, kTlsAlertIllegalParameter);
 
   client_->CheckErrorCode(SSL_ERROR_RX_MALFORMED_DHE_KEY_SHARE);
   server_->CheckErrorCode(SSL_ERROR_ILLEGAL_PARAMETER_ALERT);
 }
 
 class TlsDheSkeZeroPadP : public TlsHandshakeFilter {
  public:
+  TlsDheSkeZeroPadP() : TlsHandshakeFilter({kTlsHandshakeServerKeyExchange}) {}
   virtual PacketFilter::Action FilterHandshake(
       const TlsHandshakeFilter::HandshakeHeader& header,
       const DataBuffer& input, DataBuffer* output) {
-    if (header.handshake_type() != kTlsHandshakeServerKeyExchange) {
-      return KEEP;
-    }
-
     *output = input;
     uint32_t dh_len = 0;
     EXPECT_TRUE(input.Read(0, 2, &dh_len));
     static const uint8_t kZeroPad = 0;
     output->Write(0, dh_len + sizeof(kZeroPad), 2);  // increment the length
     output->Splice(&kZeroPad, sizeof(kZeroPad), 2);  // insert a zero
 
     return CHANGE;
@@ -554,26 +541,25 @@ TEST_P(TlsConnectTls13, ResumeFfdhe) {
             ssl_sig_rsa_pss_sha256);
   ASSERT_LT(0UL, clientCapture->extension().len());
   ASSERT_LT(0UL, serverCapture->extension().len());
 }
 
 class TlsDheSkeChangeSignature : public TlsHandshakeFilter {
  public:
   TlsDheSkeChangeSignature(uint16_t version, const uint8_t* data, size_t len)
-      : version_(version), data_(data), len_(len) {}
+      : TlsHandshakeFilter({kTlsHandshakeServerKeyExchange}),
+        version_(version),
+        data_(data),
+        len_(len) {}
 
  protected:
   virtual PacketFilter::Action FilterHandshake(const HandshakeHeader& header,
                                                const DataBuffer& input,
                                                DataBuffer* output) {
-    if (header.handshake_type() != kTlsHandshakeServerKeyExchange) {
-      return KEEP;
-    }
-
     TlsParser parser(input);
     EXPECT_TRUE(parser.SkipVariable(2));  // dh_p
     EXPECT_TRUE(parser.SkipVariable(2));  // dh_g
     EXPECT_TRUE(parser.SkipVariable(2));  // dh_Ys
 
     // Copy DH params to output.
     size_t offset = output->Write(0, input.data(), parser.consumed());
 
--- a/security/nss/gtests/ssl_gtest/ssl_drop_unittest.cc
+++ b/security/nss/gtests/ssl_gtest/ssl_drop_unittest.cc
@@ -1,75 +1,797 @@
 /* -*- 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 HandshakeAndAck(const std::shared_ptr<TlsAgent>& agent) {
+    agent->Handshake();  // Read flight.
+    ShiftDtlsTimers();
+    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, 500);  // 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_->Reset({0});
+  StartConnect();
+  client_->Handshake();
+  server_->Handshake();
+  CheckedHandshakeSendReceive();
+  CheckAcks(server_filters_, 0, {0x0002000000000000ULL});
+}
+
+TEST_F(TlsDropDatagram13, DropServerFirstFlightOnce) {
+  server_filters_.drop_->Reset(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_->Reset({0});
+  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_->Reset({1});
+  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_->Reset({0});
+  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.
+  ShiftDtlsTimers();
+  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_->Reset({1});
+  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_->Reset({2});
+  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_->Reset({3});
+  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});
+}
+
+// In this test, the Certificate message is sent four times, we drop all or part
+// of the first three attempts:
+// 1. Without fragmentation so that we can see how big it is - we drop that.
+// 2. In two pieces - we drop half AND the resulting ACK.
+// 3. In three pieces - we drop the middle piece.
+//
+// After that we let all the ACKs through and allow the handshake to complete
+// without further interference.
+//
+// This allows us to test that ranges of handshake messages are sent correctly
+// even when there are overlapping acknowledgments; that ACKs with duplicate or
+// overlapping message ranges are handled properly; and that extra
+// retransmissions are handled properly.
+class TlsFragmentationAndRecoveryTest : public TlsDropDatagram13 {
+ protected:
+  void RunTest(size_t dropped_half) {
+    FirstFlightDropCertificate();
+
+    SecondAttemptDropHalf(dropped_half);
+    size_t dropped_half_size = server_record_len(dropped_half);
+    size_t second_flight_count = server_filters_.records_->count();
+
+    ThirdAttemptDropMiddle();
+    size_t repaired_third_size = server_record_len((dropped_half == 0) ? 0 : 2);
+    size_t third_flight_count = server_filters_.records_->count();
+
+    AckAndCompleteRetransmission();
+    size_t final_server_flight_count = server_filters_.records_->count();
+    EXPECT_LE(3U, final_server_flight_count);  // CT(sixth), CV, Fin
+    CheckSizeOfSixth(dropped_half_size, repaired_third_size);
+
+    SendDelayedAck();
+    // Same number of messages as the last flight.
+    EXPECT_EQ(final_server_flight_count, server_filters_.records_->count());
+    // Double check that the Certificate size is still correct.
+    CheckSizeOfSixth(dropped_half_size, repaired_third_size);
+
+    CompleteHandshake(final_server_flight_count);
+
+    // This is the ACK for the first attempt to send a whole certificate.
+    std::vector<uint64_t> client_acks = {
+        0,                     // SH
+        0x0002000000000000ULL  // EE
+    };
+    CheckAcks(client_filters_, 0, client_acks);
+    // And from the second attempt for the half was kept (we delayed this ACK).
+    client_acks.push_back(0x0002000000000000ULL + second_flight_count +
+                          ~dropped_half % 2);
+    CheckAcks(client_filters_, 1, client_acks);
+    // And the third attempt where the first and last thirds got through.
+    client_acks.push_back(0x0002000000000000ULL + second_flight_count +
+                          third_flight_count - 1);
+    client_acks.push_back(0x0002000000000000ULL + second_flight_count +
+                          third_flight_count + 1);
+    CheckAcks(client_filters_, 2, client_acks);
+    CheckAcks(server_filters_, 0, {0x0002000000000000ULL});
+  }
+
+ private:
+  void FirstFlightDropCertificate() {
+    StartConnect();
+    client_->Handshake();
+
+    // Note: 1 << N is the Nth packet, starting from zero.
+    server_filters_.drop_->Reset(1 << 2);  // Drop Cert0.
+    server_->Handshake();
+    EXPECT_EQ(5U, server_filters_.records_->count());  // SH, EE, CT, CV, Fin
+    cert_len_ = server_filters_.records_->record(2).buffer.len();
+
+    HandshakeAndAck(client_);
+    EXPECT_EQ(2U, client_filters_.records_->count());
+  }
+
+  // Lower the MTU so that the server has to split the certificate in two
+  // pieces.  The server resends Certificate (in two), plus CV and Fin.
+  void SecondAttemptDropHalf(size_t dropped_half) {
+    ASSERT_LE(0U, dropped_half);
+    ASSERT_GT(2U, dropped_half);
+    server_filters_.records_->Clear();
+    server_filters_.drop_->Reset({dropped_half});  // Drop Cert1[half]
+    SplitServerMtu(2);
+    server_->Handshake();
+    EXPECT_LE(4U, server_filters_.records_->count());  // CT x2, CV, Fin
+
+    // Generate and capture the ACK from the client.
+    client_filters_.drop_->Reset({0});
+    HandshakeAndAck(client_);
+    EXPECT_EQ(3U, client_filters_.records_->count());
+  }
+
+  // Lower the MTU again so that the server sends Certificate cut into three
+  // pieces.  Drop the middle piece.
+  void ThirdAttemptDropMiddle() {
+    server_filters_.records_->Clear();
+    server_filters_.drop_->Reset({1});  // Drop Cert2[1] (of 3)
+    SplitServerMtu(3);
+    // Because we dropped the client ACK, the server retransmits on a timer.
+    ShiftDtlsTimers();
+    server_->Handshake();
+    EXPECT_LE(5U, server_filters_.records_->count());  // CT x3, CV, Fin
+  }
+
+  void AckAndCompleteRetransmission() {
+    // Generate ACKs.
+    HandshakeAndAck(client_);
+    // The server should send the final sixth of the certificate: the client has
+    // acknowledged the first half and the last third.  Also send CV and Fin.
+    server_filters_.records_->Clear();
+    server_->Handshake();
+  }
+
+  void CheckSizeOfSixth(size_t size_of_half, size_t size_of_third) {
+    // Work out if the final sixth is the right size.  We get the records with
+    // overheads added, which obscures the length of the payload.  We want to
+    // ensure that the server only sent the missing sixth of the Certificate.
+    //
+    // We captured |size_of_half + overhead| and |size_of_third + overhead| and
+    // want to calculate |size_of_third - size_of_third + overhead|.  We can't
+    // calculate |overhead|, but it is is (currently) always a handshake message
+    // header, a content type, and an authentication tag:
+    static const size_t record_overhead = 12 + 1 + 16;
+    EXPECT_EQ(size_of_half - size_of_third + record_overhead,
+              server_filters_.records_->record(0).buffer.len());
+  }
+
+  void SendDelayedAck() {
+    // Send the ACK we held back.  The reordered ACK doesn't add new
+    // information,
+    // but triggers an extra retransmission of the missing records again (even
+    // though the client has all that it needs).
+    client_->SendRecordDirect(client_filters_.records_->record(2));
+    server_filters_.records_->Clear();
+    server_->Handshake();
+  }
+
+  void CompleteHandshake(size_t extra_retransmissions) {
+    // All this messing around shouldn't cause a failure...
+    Handshake();
+    // ...but it leaves a mess.  Add an extra few calls to Handshake() for the
+    // client so that it absorbs the extra retransmissions.
+    for (size_t i = 0; i < extra_retransmissions; ++i) {
+      client_->Handshake();
+    }
+    CheckConnected();
+  }
+
+  // Split the server MTU so that the Certificate is split into |count| pieces.
+  // The calculation doesn't need to be perfect as long as the Certificate
+  // message is split into the right number of pieces.
+  void SplitServerMtu(size_t count) {
+    // Set the MTU based on the formula:
+    // bare_size = cert_len_ - actual_overhead
+    // MTU = ceil(bare_size / count) + pessimistic_overhead
+    //
+    // actual_overhead is the amount of actual overhead on the record we
+    // captured, which is (note that our length doesn't include the header):
+    static const size_t actual_overhead = 12 +  // handshake message header
+                                          1 +   // content type
+                                          16;   // authentication tag
+    size_t bare_size = cert_len_ - actual_overhead;
+
+    // pessimistic_overhead is the amount of expansion that NSS assumes will be
+    // added to each handshake record.  Right now, that is DTLS_MIN_FRAGMENT:
+    static const size_t pessimistic_overhead =
+        12 +  // handshake message header
+        1 +   // content type
+        13 +  // record header length
+        64;   // maximum record expansion: IV, MAC and block cipher expansion
+
+    size_t mtu = (bare_size + count - 1) / count + pessimistic_overhead;
+    if (g_ssl_gtest_verbose) {
+      std::cerr << "server: set MTU to " << mtu << std::endl;
+    }
+    EXPECT_EQ(SECSuccess, SSLInt_SetMTU(server_->ssl_fd(), mtu));
+  }
+
+  size_t server_record_len(size_t index) const {
+    return server_filters_.records_->record(index).buffer.len();
+  }
+
+  size_t cert_len_;
+};
+
+TEST_F(TlsFragmentationAndRecoveryTest, DropFirstHalf) { RunTest(0); }
+
+TEST_F(TlsFragmentationAndRecoveryTest, DropSecondHalf) { RunTest(1); }
+
+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_->Reset({1});
+  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_->Reset({1});
+  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)
+      : 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(newSpec),
+                          SSLInt_CipherSpecToAlgorithm(newSpec),
+                          SSLInt_CipherSpecToKey(newSpec),
+                          SSLInt_CipherSpecToIv(newSpec));
+    EXPECT_EQ(true, ret);
+    self->send_cipher_specs_.push_back(spec);
+  }
+
+  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, SSL_LIBRARY_VERSION_DTLS_1_2_WIRE, 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, SSL_LIBRARY_VERSION_DTLS_1_2_WIRE, 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_->Reset(0xff);
+  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.
+  ShiftDtlsTimers();
+  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_->Reset(0xff);
+  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_->Reset(0xff);
+  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 +828,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 +845,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/security/nss/gtests/ssl_gtest/ssl_ecdh_unittest.cc
+++ b/security/nss/gtests/ssl_gtest/ssl_ecdh_unittest.cc
@@ -188,28 +188,26 @@ TEST_P(TlsConnectGenericPre13, P384Prior
   Connect();
 
   CheckKeys(ssl_kea_ecdh, ssl_grp_ec_secp384r1, ssl_auth_rsa_sign,
             ssl_sig_rsa_pss_sha256);
 }
 
 class TlsKeyExchangeGroupCapture : public TlsHandshakeFilter {
  public:
-  TlsKeyExchangeGroupCapture() : group_(ssl_grp_none) {}
+  TlsKeyExchangeGroupCapture()
+      : TlsHandshakeFilter({kTlsHandshakeServerKeyExchange}),
+        group_(ssl_grp_none) {}
 
   SSLNamedGroup group() const { return group_; }
 
  protected:
   virtual PacketFilter::Action FilterHandshake(const HandshakeHeader &header,
                                                const DataBuffer &input,
                                                DataBuffer *output) {
-    if (header.handshake_type() != kTlsHandshakeServerKeyExchange) {
-      return KEEP;
-    }
-
     uint32_t value = 0;
     EXPECT_TRUE(input.Read(0, 1, &value));
     EXPECT_EQ(3U, value) << "curve type has to be 3";
 
     EXPECT_TRUE(input.Read(1, 2, &value));
     group_ = static_cast<SSLNamedGroup>(value);
 
     return KEEP;
@@ -513,46 +511,38 @@ TEST_P(TlsKeyExchangeTest13, MultipleCli
   const std::vector<SSLNamedGroup> shares = {ssl_grp_ec_curve25519,
                                              ssl_grp_ec_secp256r1};
   CheckKEXDetails(client_groups, shares);
 }
 
 // Replace the point in the client key exchange message with an empty one
 class ECCClientKEXFilter : public TlsHandshakeFilter {
  public:
-  ECCClientKEXFilter() {}
+  ECCClientKEXFilter() : TlsHandshakeFilter({kTlsHandshakeClientKeyExchange}) {}
 
  protected:
   virtual PacketFilter::Action FilterHandshake(const HandshakeHeader &header,
                                                const DataBuffer &input,
                                                DataBuffer *output) {
-    if (header.handshake_type() != kTlsHandshakeClientKeyExchange) {
-      return KEEP;
-    }
-
     // Replace the client key exchange message with an empty point
     output->Allocate(1);
     output->Write(0, 0U, 1);  // set point length 0
     return CHANGE;
   }
 };
 
 // Replace the point in the server key exchange message with an empty one
 class ECCServerKEXFilter : public TlsHandshakeFilter {
  public:
-  ECCServerKEXFilter() {}
+  ECCServerKEXFilter() : TlsHandshakeFilter({kTlsHandshakeServerKeyExchange}) {}
 
  protected:
   virtual PacketFilter::Action FilterHandshake(const HandshakeHeader &header,
                                                const DataBuffer &input,
                                                DataBuffer *output) {
-    if (header.handshake_type() != kTlsHandshakeServerKeyExchange) {
-      return KEEP;
-    }
-
     // Replace the server key exchange message with an empty point
     output->Allocate(4);
     output->Write(0, 3U, 1);  // named curve
     uint32_t curve = 0;
     EXPECT_TRUE(input.Read(1, 2, &curve));  // get curve id
     output->Write(1, curve, 2);             // write curve id
     output->Write(3, 0U, 1);                // point length 0
     return CHANGE;
--- a/security/nss/gtests/ssl_gtest/ssl_exporter_unittest.cc
+++ b/security/nss/gtests/ssl_gtest/ssl_exporter_unittest.cc
@@ -113,17 +113,16 @@ int32_t RegularExporterShouldFail(TlsAge
                             strlen(kExporterLabel), PR_TRUE, kExporterContext,
                             sizeof(kExporterContext), val, sizeof(val)))
       << "regular exporter should fail";
   return 0;
 }
 
 TEST_P(TlsConnectTls13, EarlyExporter) {
   SetupForZeroRtt();
-  ExpectAlert(client_, kTlsAlertEndOfEarlyData);
   client_->Set0RttEnabled(true);
   server_->Set0RttEnabled(true);
   ExpectResumption(RESUME_TICKET);
 
   client_->Handshake();  // Send ClientHello.
   uint8_t client_value[10] = {0};
   RegularExporterShouldFail(client_.get(), nullptr, 0);
 
--- a/security/nss/gtests/ssl_gtest/ssl_extension_unittest.cc
+++ b/security/nss/gtests/ssl_gtest/ssl_extension_unittest.cc
@@ -56,74 +56,28 @@ class TlsExtensionDamager : public TlsEx
     return CHANGE;
   }
 
  private:
   uint16_t extension_;
   size_t index_;
 };
 
-class TlsExtensionInjector : public TlsHandshakeFilter {
+class TlsExtensionAppender : public TlsHandshakeFilter {
  public:
-  TlsExtensionInjector(uint16_t ext, DataBuffer& data)
-      : extension_(ext), data_(data) {}
+  TlsExtensionAppender(uint8_t handshake_type, uint16_t ext, DataBuffer& data)
+      : TlsHandshakeFilter({handshake_type}), extension_(ext), data_(data) {}
 
   virtual PacketFilter::Action FilterHandshake(const HandshakeHeader& header,
                                                const DataBuffer& input,
                                                DataBuffer* output) {
     TlsParser parser(input);
     if (!TlsExtensionFilter::FindExtensions(&parser, header)) {
       return KEEP;
     }
-    size_t offset = parser.consumed();
-
-    *output = input;
-
-    // Increase the size of the extensions.
-    uint16_t ext_len;
-    memcpy(&ext_len, output->data() + offset, sizeof(ext_len));
-    ext_len = htons(ntohs(ext_len) + data_.len() + 4);
-    memcpy(output->data() + offset, &ext_len, sizeof(ext_len));
-
-    // Insert the extension type and length.
-    DataBuffer type_length;
-    type_length.Allocate(4);
-    type_length.Write(0, extension_, 2);
-    type_length.Write(2, data_.len(), 2);
-    output->Splice(type_length, offset + 2);
-
-    // Insert the payload.
-    if (data_.len() > 0) {
-      output->Splice(data_, offset + 6);
-    }
-
-    return CHANGE;
-  }
-
- private:
-  const uint16_t extension_;
-  const DataBuffer data_;
-};
-
-class TlsExtensionAppender : public TlsHandshakeFilter {
- public:
-  TlsExtensionAppender(uint8_t handshake_type, uint16_t ext, DataBuffer& data)
-      : handshake_type_(handshake_type), extension_(ext), data_(data) {}
-
-  virtual PacketFilter::Action FilterHandshake(const HandshakeHeader& header,
-                                               const DataBuffer& input,
-                                               DataBuffer* output) {
-    if (header.handshake_type() != handshake_type_) {
-      return KEEP;
-    }
-
-    TlsParser parser(input);
-    if (!TlsExtensionFilter::FindExtensions(&parser, header)) {
-      return KEEP;
-    }
     *output = input;
 
     // Increase the length of the extensions block.
     if (!UpdateLength(output, parser.consumed(), 2)) {
       return KEEP;
     }
 
     // Extensions in Certificate are nested twice.  Increase the size of the
@@ -154,17 +108,16 @@ class TlsExtensionAppender : public TlsH
       return false;
     }
 
     len += 4 + data_.len();
     output->Write(offset, len, size);
     return true;
   }
 
-  const uint8_t handshake_type_;
   const uint16_t extension_;
   const DataBuffer data_;
 };
 
 class TlsExtensionTestBase : public TlsConnectTestBase {
  protected:
   TlsExtensionTestBase(SSLProtocolVariant variant, uint16_t version)
       : TlsConnectTestBase(variant, version) {}
@@ -195,18 +148,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);
   }
 };
@@ -1004,17 +956,16 @@ class TlsBogusExtensionTest : public Tls
 
   void AddFilter(uint8_t message, uint16_t extension) {
     static uint8_t empty_buf[1] = {0};
     DataBuffer empty(empty_buf, 0);
     auto filter =
         std::make_shared<TlsExtensionAppender>(message, extension, empty);
     if (version_ >= SSL_LIBRARY_VERSION_TLS_1_3) {
       server_->SetTlsRecordFilter(filter);
-      filter->EnableDecryption();
     } else {
       server_->SetPacketFilter(filter);
     }
   }
 
   void Run(uint8_t message, uint16_t extension = 0xff) {
     EnsureTlsSetup();
     AddFilter(message, extension);
@@ -1027,27 +978,30 @@ class TlsBogusExtensionTestPre13 : publi
   void ConnectAndFail(uint8_t) override {
     ConnectExpectAlert(client_, kTlsAlertUnsupportedExtension);
   }
 };
 
 class TlsBogusExtensionTest13 : public TlsBogusExtensionTest {
  protected:
   void ConnectAndFail(uint8_t message) override {
-    if (message == kTlsHandshakeHelloRetryRequest) {
+    if (message != kTlsHandshakeServerHello) {
       ConnectExpectAlert(client_, kTlsAlertUnsupportedExtension);
       return;
     }
 
-    client_->StartConnect();
-    server_->StartConnect();
+    FailWithAlert(kTlsAlertUnsupportedExtension);
+  }
+
+  void FailWithAlert(uint8_t alert) {
+    StartConnect();
     client_->Handshake();  // ClientHello
     server_->Handshake();  // ServerHello
 
-    client_->ExpectSendAlert(kTlsAlertUnsupportedExtension);
+    client_->ExpectSendAlert(alert);
     client_->Handshake();
     if (variant_ == ssl_variant_stream) {
       server_->ExpectSendAlert(kTlsAlertBadRecordMac);
     }
     server_->Handshake();
   }
 };
 
@@ -1062,19 +1016,22 @@ TEST_P(TlsBogusExtensionTest13, AddBogus
 TEST_P(TlsBogusExtensionTest13, AddBogusExtensionEncryptedExtensions) {
   Run(kTlsHandshakeEncryptedExtensions);
 }
 
 TEST_P(TlsBogusExtensionTest13, AddBogusExtensionCertificate) {
   Run(kTlsHandshakeCertificate);
 }
 
+// It's perfectly valid to set unknown extensions in CertificateRequest.
 TEST_P(TlsBogusExtensionTest13, AddBogusExtensionCertificateRequest) {
   server_->RequestClientAuth(false);
-  Run(kTlsHandshakeCertificateRequest);
+  AddFilter(kTlsHandshakeCertificateRequest, 0xff);
+  ConnectExpectAlert(client_, kTlsAlertDecryptError);
+  client_->CheckErrorCode(SEC_ERROR_BAD_SIGNATURE);
 }
 
 TEST_P(TlsBogusExtensionTest13, AddBogusExtensionHelloRetryRequest) {
   static const std::vector<SSLNamedGroup> groups = {ssl_grp_ec_secp384r1};
   server_->ConfigNamedGroups(groups);
 
   Run(kTlsHandshakeHelloRetryRequest);
 }
@@ -1087,23 +1044,16 @@ TEST_P(TlsBogusExtensionTest13, AddVersi
   Run(kTlsHandshakeCertificate, ssl_tls13_supported_versions_xtn);
 }
 
 TEST_P(TlsBogusExtensionTest13, AddVersionExtensionCertificateRequest) {
   server_->RequestClientAuth(false);
   Run(kTlsHandshakeCertificateRequest, ssl_tls13_supported_versions_xtn);
 }
 
-TEST_P(TlsBogusExtensionTest13, AddVersionExtensionHelloRetryRequest) {
-  static const std::vector<SSLNamedGroup> groups = {ssl_grp_ec_secp384r1};
-  server_->ConfigNamedGroups(groups);
-
-  Run(kTlsHandshakeHelloRetryRequest, ssl_tls13_supported_versions_xtn);
-}
-
 // NewSessionTicket allows unknown extensions AND it isn't protected by the
 // Finished.  So adding an unknown extension doesn't cause an error.
 TEST_P(TlsBogusExtensionTest13, AddBogusExtensionNewSessionTicket) {
   ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
 
   AddFilter(kTlsHandshakeNewSessionTicket, 0xff);
   Connect();
   SendReceive();
--- a/security/nss/gtests/ssl_gtest/ssl_fragment_unittest.cc
+++ b/security/nss/gtests/ssl_gtest/ssl_fragment_unittest.cc
@@ -46,20 +46,26 @@ class RecordFragmenter : public PacketFi
       cursor_ = fragment_header.Write(output_, cursor_, record_fragment);
     }
 
     bool SplitRecord(TlsRecordHeader& record_header, DataBuffer& record) {
       TlsParser parser(record);
       while (parser.remaining()) {
         TlsHandshakeFilter::HandshakeHeader handshake_header;
         DataBuffer handshake_body;
-        if (!handshake_header.Parse(&parser, record_header, &handshake_body)) {
+        bool complete = false;
+        if (!handshake_header.Parse(&parser, record_header, DataBuffer(),
+                                    &handshake_body, &complete)) {
           ADD_FAILURE() << "couldn't parse handshake header";
           return false;
         }
+        if (!complete) {
+          ADD_FAILURE() << "don't want to deal with fragmented messages";
+          return false;
+        }
 
         DataBuffer record_fragment;
         // We can't fragment handshake records that are too small.
         if (handshake_body.len() < 2) {
           handshake_header.Write(&record_fragment, 0U, handshake_body);
           WriteRecord(record_header, record_fragment);
           continue;
         }
@@ -77,17 +83,17 @@ class RecordFragmenter : public PacketFi
     }
 
    public:
     bool Split() {
       TlsParser parser(input_);
       while (parser.remaining()) {
         TlsRecordHeader header;
         DataBuffer record;
-        if (!header.Parse(&parser, &record)) {
+        if (!header.Parse(0, &parser, &record)) {
           ADD_FAILURE() << "bad record header";
           return false;
         }
 
         if (::g_ssl_gtest_verbose) {
           std::cerr << "Record: " << header << ' ' << record << std::endl;
         }
 
--- a/security/nss/gtests/ssl_gtest/ssl_fuzz_unittest.cc
+++ b/security/nss/gtests/ssl_gtest/ssl_fuzz_unittest.cc
@@ -42,19 +42,19 @@ class TlsApplicationDataRecorder : publi
   const DataBuffer& buffer() const { return buffer_; }
 
  private:
   DataBuffer buffer_;
 };
 
 // Ensure that ssl_Time() returns a constant value.
 FUZZ_F(TlsFuzzTest, SSL_Time_Constant) {
-  PRUint32 now = ssl_Time();
+  PRUint32 now = ssl_TimeSec();
   PR_Sleep(PR_SecondsToInterval(2));
-  EXPECT_EQ(ssl_Time(), now);
+  EXPECT_EQ(ssl_TimeSec(), now);
 }
 
 // Check that due to the deterministic PRNG we derive
 // the same master secret in two consecutive TLS sessions.
 FUZZ_P(TlsConnectGeneric, DeterministicExporter) {
   const char kLabel[] = "label";
   std::vector<unsigned char> out1(32), out2(32);
 
@@ -210,81 +210,32 @@ FUZZ_P(TlsConnectGeneric, SessionTicketR
 
   Reset();
   ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
   ExpectResumption(RESUME_TICKET);
   Connect();
   SendReceive();
 }
 
-class TlsSessionTicketMacDamager : public TlsExtensionFilter {
- public:
-  TlsSessionTicketMacDamager() {}
-  virtual PacketFilter::Action FilterExtension(uint16_t extension_type,
-                                               const DataBuffer& input,
-                                               DataBuffer* output) {
-    if (extension_type != ssl_session_ticket_xtn &&
-        extension_type != ssl_tls13_pre_shared_key_xtn) {
-      return KEEP;
-    }
-
-    *output = input;
-
-    // Handle everything before TLS 1.3.
-    if (extension_type == ssl_session_ticket_xtn) {
-      // Modify the last byte of the MAC.
-      output->data()[output->len() - 1] ^= 0xff;
-    }
-
-    // Handle TLS 1.3.
-    if (extension_type == ssl_tls13_pre_shared_key_xtn) {
-      TlsParser parser(input);
-
-      uint32_t ids_len;
-      EXPECT_TRUE(parser.Read(&ids_len, 2) && ids_len > 0);
-
-      uint32_t ticket_len;
-      EXPECT_TRUE(parser.Read(&ticket_len, 2) && ticket_len > 0);
-
-      // Modify the last byte of the MAC.
-      output->data()[2 + 2 + ticket_len - 1] ^= 0xff;
-    }
-
-    return CHANGE;
-  }
-};
-
-// Check that session ticket resumption works with a bad MAC.
-FUZZ_P(TlsConnectGeneric, SessionTicketResumptionBadMac) {
-  ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
-  Connect();
-  SendReceive();
-
-  Reset();
-  ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
-  ExpectResumption(RESUME_TICKET);
-
-  client_->SetPacketFilter(std::make_shared<TlsSessionTicketMacDamager>());
-  Connect();
-  SendReceive();
-}
-
 // Check that session tickets are not encrypted.
 FUZZ_P(TlsConnectGeneric, UnencryptedSessionTickets) {
   ConfigureSessionCache(RESUME_TICKET, RESUME_TICKET);
 
   auto i1 = std::make_shared<TlsInspectorRecordHandshakeMessage>(
       kTlsHandshakeNewSessionTicket);
   server_->SetPacketFilter(i1);
   Connect();
 
+  std::cerr << "ticket" << i1->buffer() << std::endl;
   size_t offset = 4; /* lifetime */
   if (version_ == SSL_LIBRARY_VERSION_TLS_1_3) {
-    offset += 1 + 1 + /* ke_modes */
-              1 + 1;  /* auth_modes */
+    offset += 4; /* ticket_age_add */
+    uint32_t nonce_len = 0;
+    EXPECT_TRUE(i1->buffer().Read(offset, 1, &nonce_len));
+    offset += 1 + nonce_len;
   }
   offset += 2 + /* ticket length */
             2;  /* TLS_EX_SESS_TICKET_VERSION */
   // Check the protocol version number.
   uint32_t tls_version = 0;
   EXPECT_TRUE(i1->buffer().Read(offset, sizeof(version_), &tls_version));
   EXPECT_EQ(version_, static_cast<decltype(version_)>(tls_version));
 
--- a/security/nss/gtests/ssl_gtest/ssl_gtest.gyp
+++ b/security/nss/gtests/ssl_gtest/ssl_gtest.gyp
@@ -6,24 +6,25 @@
     '../../coreconf/config.gypi',
     '../common/gtest.gypi',
   ],
   'targets': [
     {
       'target_name': 'ssl_gtest',
       'type': 'executable',
       'sources': [
+        'bloomfilter_unittest.cc',
         'libssl_internals.c',
         'selfencrypt_unittest.cc',
         'ssl_0rtt_unittest.cc',
         'ssl_agent_unittest.cc',
-        'ssl_alths_unittest.cc',
         'ssl_auth_unittest.cc',
         'ssl_cert_ext_unittest.cc',
         'ssl_ciphersuite_unittest.cc',
+        'ssl_custext_unittest.cc',
         'ssl_damage_unittest.cc',
         'ssl_dhe_unittest.cc',
         'ssl_drop_unittest.cc',
         'ssl_ecdh_unittest.cc',
         'ssl_ems_unittest.cc',
         'ssl_exporter_unittest.cc',
         'ssl_extension_unittest.cc',
         'ssl_fuzz_unittest.cc',
@@ -34,16 +35,17 @@
         'ssl_keylog_unittest.cc',
         'ssl_loopback_unittest.cc',
         'ssl_misc_unittest.cc',
         'ssl_record_unittest.cc',
         'ssl_resumption_unittest.cc',
         'ssl_renegotiation_unittest.cc',
         'ssl_skip_unittest.cc',
         'ssl_staticrsa_unittest.cc',
+        'ssl_tls13compat_unittest.cc',
         'ssl_v2_client_hello_unittest.cc',
         'ssl_version_unittest.cc',
         'ssl_versionpolicy_unittest.cc',
         'test_io.cc',
         'tls_agent.cc',
         'tls_connect.cc',
         'tls_filter.cc',
         'tls_hkdf_unittest.cc',
--- a/security/nss/gtests/ssl_gtest/ssl_hrr_unittest.cc
+++ b/security/nss/gtests/ssl_gtest/ssl_hrr_unittest.cc
@@ -182,16 +182,600 @@ TEST_P(TlsConnectTls13, RetryWithSameKey
   static const std::vector<SSLNamedGroup> groups = {ssl_grp_ec_secp384r1,
                                                     ssl_grp_ec_secp521r1};
   server_->ConfigNamedGroups(groups);
   ConnectExpectAlert(server_, kTlsAlertIllegalParameter);
   EXPECT_EQ(SSL_ERROR_BAD_2ND_CLIENT_HELLO, server_->error_code());
   EXPECT_EQ(SSL_ERROR_ILLEGAL_PARAMETER_ALERT, client_->error_code());
 }
 
+// Here we modify the second ClientHello so that the client retries with the
+// same shares, even though the server wanted something else.
+TEST_P(TlsConnectTls13, RetryWithTwoShares) {
+  EnsureTlsSetup();
+  EXPECT_EQ(SECSuccess, SSL_SendAdditionalKeyShares(client_->ssl_fd(), 1));
+  client_->SetPacketFilter(std::make_shared<KeyShareReplayer>());
+
+  static const std::vector<SSLNamedGroup> groups = {ssl_grp_ec_secp384r1,
+                                                    ssl_grp_ec_secp521r1};
+  server_->ConfigNamedGroups(groups);
+  ConnectExpectAlert(server_, kTlsAlertIllegalParameter);
+  EXPECT_EQ(SSL_ERROR_BAD_2ND_CLIENT_HELLO, server_->error_code());
+  EXPECT_EQ(SSL_ERROR_ILLEGAL_PARAMETER_ALERT, client_->error_code());
+}
+
+TEST_P(TlsConnectTls13, RetryCallbackAccept) {
+  EnsureTlsSetup();
+
+  auto accept_hello = [](PRBool firstHello, const PRUint8* clientToken,
+                         unsigned int clientTokenLen, PRUint8* appToken,
+                         unsigned int* appTokenLen, unsigned int appTokenMax,
+                         void* arg) {
+    auto* called = reinterpret_cast<bool*>(arg);
+    *called = true;
+
+    EXPECT_TRUE(firstHello);
+    EXPECT_EQ(0U, clientTokenLen);
+    return ssl_hello_retry_accept;
+  };
+
+  bool cb_run = false;
+  EXPECT_EQ(SECSuccess, SSL_HelloRetryRequestCallback(server_->ssl_fd(),
+                                                      accept_hello, &cb_run));
+  Connect();
+  EXPECT_TRUE(cb_run);
+}
+
+TEST_P(TlsConnectTls13, RetryCallbackAcceptGroupMismatch) {
+  EnsureTlsSetup();
+
+  auto accept_hello_twice = [](PRBool firstHello, const PRUint8* clientToken,
+                               unsigned int clientTokenLen, PRUint8* appToken,
+                               unsigned int* appTokenLen,
+                               unsigned int appTokenMax, void* arg) {
+    auto* called = reinterpret_cast<size_t*>(arg);
+    ++*called;
+
+    EXPECT_EQ(0U, clientTokenLen);
+    return ssl_hello_retry_accept;
+  };
+
+  auto capture = std::make_shared<TlsExtensionCapture>(ssl_tls13_cookie_xtn);
+  capture->SetHandshakeTypes({kTlsHandshakeHelloRetryRequest});
+  server_->SetPacketFilter(capture);
+
+  static const std::vector<SSLNamedGroup> groups = {ssl_grp_ec_secp384r1};
+  server_->ConfigNamedGroups(groups);
+
+  size_t cb_run = 0;
+  EXPECT_EQ(SECSuccess, SSL_HelloRetryRequestCallback(
+                            server_->ssl_fd(), accept_hello_twice, &cb_run));
+  Connect();
+  EXPECT_EQ(2U, cb_run);
+  EXPECT_TRUE(capture->captured()) << "expected a cookie in HelloRetryRequest";
+}
+
+TEST_P(TlsConnectTls13, RetryCallbackFail) {
+  EnsureTlsSetup();
+
+  auto fail_hello = [](PRBool firstHello, const PRUint8* clientToken,
+                       unsigned int clientTokenLen, PRUint8* appToken,
+                       unsigned int* appTokenLen, unsigned int appTokenMax,
+                       void* arg) {
+    auto* called = reinterpret_cast<bool*>(arg);
+    *called = true;
+
+    EXPECT_TRUE(firstHello);
+    EXPECT_EQ(0U, clientTokenLen);
+    return ssl_hello_retry_fail;
+  };
+
+  bool cb_run = false;
+  EXPECT_EQ(SECSuccess, SSL_HelloRetryRequestCallback(server_->ssl_fd(),
+                                                      fail_hello, &cb_run));
+  ConnectExpectAlert(server_, kTlsAlertHandshakeFailure);
+  server_->CheckErrorCode(SSL_ERROR_APPLICATION_ABORT);
+  EXPECT_TRUE(cb_run);
+}
+
+// Asking for retry twice isn't allowed.
+TEST_P(TlsConnectTls13, RetryCallbackRequestHrrTwice) {
+  EnsureTlsSetup();
+
+  auto bad_callback = [](PRBool firstHello, const PRUint8* clientToken,
+                         unsigned int clientTokenLen, PRUint8* appToken,
+                         unsigned int* appTokenLen, unsigned int appTokenMax,
+                         void* arg) -> SSLHelloRetryRequestAction {
+    return ssl_hello_retry_request;
+  };
+  EXPECT_EQ(SECSuccess, SSL_HelloRetryRequestCallback(server_->ssl_fd(),
+                                                      bad_callback, NULL));
+  ConnectExpectAlert(server_, kTlsAlertInternalError);
+  server_->CheckErrorCode(SSL_ERROR_APP_CALLBACK_ERROR);
+}
+
+// Accepting the CH and modifying the token isn't allowed.
+TEST_P(TlsConnectTls13, RetryCallbackAcceptAndSetToken) {
+  EnsureTlsSetup();
+
+  auto bad_callback = [](PRBool firstHello, const PRUint8* clientToken,
+                         unsigned int clientTokenLen, PRUint8* appToken,
+                         unsigned int* appTokenLen, unsigned int appTokenMax,
+                         void* arg) -> SSLHelloRetryRequestAction {
+    *appTokenLen = 1;
+    return ssl_hello_retry_accept;
+  };
+  EXPECT_EQ(SECSuccess, SSL_HelloRetryRequestCallback(server_->ssl_fd(),
+                                                      bad_callback, NULL));
+  ConnectExpectAlert(server_, kTlsAlertInternalError);
+  server_->CheckErrorCode(SSL_ERROR_APP_CALLBACK_ERROR);
+}
+
+// As above, but with reject.
+TEST_P(TlsConnectTls13, RetryCallbackRejectAndSetToken) {
+  EnsureTlsSetup();
+
+  auto bad_callback = [](PRBool firstHello, const PRUint8* clientToken,
+                         unsigned int clientTokenLen, PRUint8* appToken,
+                         unsigned int* appTokenLen, unsigned int appTokenMax,
+                         void* arg) -> SSLHelloRetryRequestAction {
+    *appTokenLen = 1;
+    return ssl_hello_retry_fail;
+  };
+  EXPECT_EQ(SECSuccess, SSL_HelloRetryRequestCallback(server_->ssl_fd(),
+                                                      bad_callback, NULL));
+  ConnectExpectAlert(server_, kTlsAlertInternalError);
+  server_->CheckErrorCode(SSL_ERROR_APP_CALLBACK_ERROR);
+}
+
+// This is a (pretend) buffer overflow.
+TEST_P(TlsConnectTls13, RetryCallbackSetTooLargeToken) {
+  EnsureTlsSetup();
+
+  auto bad_callback = [](PRBool firstHello, const PRUint8* clientToken,
+                         unsigned int clientTokenLen, PRUint8* appToken,
+                         unsigned int* appTokenLen, unsigned int appTokenMax,
+                         void* arg) -> SSLHelloRetryRequestAction {
+    *appTokenLen = appTokenMax + 1;
+    return ssl_hello_retry_accept;
+  };
+  EXPECT_EQ(SECSuccess, SSL_HelloRetryRequestCallback(server_->ssl_fd(),
+                                                      bad_callback, NULL));
+  ConnectExpectAlert(server_, kTlsAlertInternalError);
+  server_->CheckErrorCode(SSL_ERROR_APP_CALLBACK_ERROR);
+}
+
+SSLHelloRetryRequestAction RetryHello(PRBool firstHello,
+                                      const PRUint8* clientToken,
+                                      unsigned int clientTokenLen,
+                                      PRUint8* appToken,
+                                      unsigned int* appTokenLen,
+                                      unsigned int appTokenMax, void* arg) {
+  auto* called = reinterpret_cast<size_t*>(arg);
+  ++*called;
+
+  EXPECT_EQ(0U, clientTokenLen);
+  return firstHello ? ssl_hello_retry_request : ssl_hello_retry_accept;
+}
+
+TEST_P(TlsConnectTls13, RetryCallbackRetry) {
+  EnsureTlsSetup();
+
+  auto capture_hrr = std::make_shared<TlsInspectorRecordHandshakeMessage>(
+      ssl_hs_hello_retry_request);
+  auto capture_key_share =
+      std::make_shared<TlsExtensionCapture>(ssl_tls13_key_share_xtn);
+  capture_key_share->SetHandshakeTypes({kTlsHandshakeHelloRetryRequest});
+  std::vector<std::shared_ptr<PacketFilter>> chain = {capture_hrr,
+                                                      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.
+  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";
+
+  auto capture_cookie =
+      std::make_shared<TlsExtensionCapture>(ssl_tls13_cookie_xtn);
+  client_->SetPacketFilter(capture_cookie);
+
+  Handshake();
+  CheckConnected();
+  EXPECT_EQ(2U, cb_called);
+  EXPECT_TRUE(capture_cookie->captured()) << "should have a cookie";
+}
+
+static size_t CountShares(const DataBuffer& key_share) {
+  size_t count = 0;
+  uint32_t len = 0;
+  size_t offset = 2;
+
+  EXPECT_TRUE(key_share.Read(0, 2, &len));
+  EXPECT_EQ(key_share.len() - 2, len);
+  while (offset < key_share.len()) {
+    offset += 2;  // Skip KeyShareEntry.group
+    EXPECT_TRUE(key_share.Read(offset, 2, &len));
+    offset += 2 + len;  // Skip KeyShareEntry.key_exchange
+    ++count;
+  }
+  return count;
+}
+
+TEST_P(TlsConnectTls13, RetryCallbackRetryWithAdditionalShares) {
+  EnsureTlsSetup();
+  EXPECT_EQ(SECSuccess, SSL_SendAdditionalKeyShares(client_->ssl_fd(), 1));
+
+  auto capture_server =
+      std::make_shared<TlsExtensionCapture>(ssl_tls13_key_share_xtn);
+  capture_server->SetHandshakeTypes({kTlsHandshakeHelloRetryRequest});
+  server_->SetPacketFilter(capture_server);
+
+  size_t cb_called = 0;
+  EXPECT_EQ(SECSuccess, SSL_HelloRetryRequestCallback(server_->ssl_fd(),
+                                                      RetryHello, &cb_called));
+
+  // Do the first message exchange.
+  StartConnect();
+  client_->Handshake();
+  server_->Handshake();
+
+  EXPECT_EQ(1U, cb_called) << "callback should be called once here";
+  EXPECT_FALSE(capture_server->captured())
+      << "no key_share extension expected from server";
+
+  auto capture_client_2nd =
+      std::make_shared<TlsExtensionCapture>(ssl_tls13_key_share_xtn);
+  client_->SetPacketFilter(capture_client_2nd);
+
+  Handshake();
+  CheckConnected();
+  EXPECT_EQ(2U, cb_called);
+  EXPECT_TRUE(capture_client_2nd->captured()) << "client should send key_share";
+  EXPECT_EQ(2U, CountShares(capture_client_2nd->extension()))
+      << "client should still send two shares";
+}
+
+// The callback should be run even if we have another reason to send
+// HelloRetryRequest.  In this case, the server sends HRR because the server
+// wants a P-384 key share and the client didn't offer one.
+TEST_P(TlsConnectTls13, RetryCallbackRetryWithGroupMismatch) {
+  EnsureTlsSetup();
+
+  auto capture_cookie =
+      std::make_shared<TlsExtensionCapture>(ssl_tls13_cookie_xtn);
+  capture_cookie->SetHandshakeTypes({kTlsHandshakeHelloRetryRequest});
+  auto capture_key_share =
+      std::make_shared<TlsExtensionCapture>(ssl_tls13_key_share_xtn);
+  capture_key_share->SetHandshakeTypes({kTlsHandshakeHelloRetryRequest});
+  server_->SetPacketFilter(std::make_shared<ChainedPacketFilter>(
+      ChainedPacketFilterInit{capture_cookie, capture_key_share}));
+
+  static const std::vector<SSLNamedGroup> groups = {ssl_grp_ec_secp384r1};
+  server_->ConfigNamedGroups(groups);
+
+  size_t cb_called = 0;
+  EXPECT_EQ(SECSuccess, SSL_HelloRetryRequestCallback(server_->ssl_fd(),
+                                                      RetryHello, &cb_called));
+  Connect();
+  EXPECT_EQ(2U, cb_called);
+  EXPECT_TRUE(capture_cookie->captured()) << "cookie expected";
+  EXPECT_TRUE(capture_key_share->captured()) << "key_share expected";
+}
+
+static const uint8_t kApplicationToken[] = {0x92, 0x44, 0x00};
+
+SSLHelloRetryRequestAction RetryHelloWithToken(
+    PRBool firstHello, const PRUint8* clientToken, unsigned int clientTokenLen,
+    PRUint8* appToken, unsigned int* appTokenLen, unsigned int appTokenMax,
+    void* arg) {
+  auto* called = reinterpret_cast<size_t*>(arg);
+  ++*called;
+
+  if (firstHello) {
+    memcpy(appToken, kApplicationToken, sizeof(kApplicationToken));
+    *appTokenLen = sizeof(kApplicationToken);
+    return ssl_hello_retry_request;
+  }
+
+  EXPECT_EQ(DataBuffer(kApplicationToken, sizeof(kApplicationToken)),
+            DataBuffer(clientToken, static_cast<size_t>(clientTokenLen)));
+  return ssl_hello_retry_accept;
+}
+
+TEST_P(TlsConnectTls13, RetryCallbackRetryWithToken) {
+  EnsureTlsSetup();
+
+  auto capture_key_share =
+      std::make_shared<TlsExtensionCapture>(ssl_tls13_key_share_xtn);
+  capture_key_share->SetHandshakeTypes({kTlsHandshakeHelloRetryRequest});
+  server_->SetPacketFilter(capture_key_share);
+
+  size_t cb_called = 0;
+  EXPECT_EQ(SECSuccess,
+            SSL_HelloRetryRequestCallback(server_->ssl_fd(),
+                                          RetryHelloWithToken, &cb_called));
+  Connect();
+  EXPECT_EQ(2U, cb_called);
+  EXPECT_FALSE(capture_key_share->captured()) << "no key share expected";
+}
+
+TEST_P(TlsConnectTls13, RetryCallbackRetryWithTokenAndGroupMismatch) {
+  EnsureTlsSetup();
+
+  static const std::vector<SSLNamedGroup> groups = {ssl_grp_ec_secp384r1};
+  server_->ConfigNamedGroups(groups);
+
+  auto capture_key_share =
+      std::make_shared<TlsExtensionCapture>(ssl_tls13_key_share_xtn);
+  capture_key_share->SetHandshakeTypes({kTlsHandshakeHelloRetryRequest});
+  server_->SetPacketFilter(capture_key_share);
+
+  size_t cb_called = 0;
+  EXPECT_EQ(SECSuccess,
+            SSL_HelloRetryRequestCallback(server_->ssl_fd(),
+                                          RetryHelloWithToken, &cb_called));
+  Connect();
+  EXPECT_EQ(2U, cb_called);
+  EXPECT_TRUE(capture_key_share->captured()) << "key share expected";
+}
+
+SSLHelloRetryRequestAction CheckTicketToken(
+    PRBool firstHello, const PRUint8* clientToken, unsigned int clientTokenLen,
+    PRUint8* appToken, unsigned int* appTokenLen, unsigned int appTokenMax,
+    void* arg) {
+  auto* called = reinterpret_cast<bool*>(arg);
+  *called = true;
+
+  EXPECT_TRUE(firstHello);
+  EXPECT_EQ(DataBuffer(kApplicationToken, sizeof(kApplicationToken)),
+            DataBuffer(clientToken, static_cast<size_t>(clientTokenLen)));
+  return ssl_hello_retry_accept;
+}
+
+// Stream because SSL_SendSessionTicket only supports that.
+TEST_F(TlsConnectStreamTls13, RetryCallbackWithSessionTicketToken) {
+  ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
+  Connect();
+  EXPECT_EQ(SECSuccess,
+            SSL_SendSessionTicket(server_->ssl_fd(), kApplicationToken,
+                                  sizeof(kApplicationToken)));
+  SendReceive();
+
+  Reset();
+  ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
+  ExpectResumption(RESUME_TICKET);
+
+  bool cb_run = false;
+  EXPECT_EQ(SECSuccess, SSL_HelloRetryRequestCallback(
+                            server_->ssl_fd(), CheckTicketToken, &cb_run));
+  Connect();
+  EXPECT_TRUE(cb_run);
+}
+
+void TriggerHelloRetryRequest(std::shared_ptr<TlsAgent>& client,
+                              std::shared_ptr<TlsAgent>& server) {
+  size_t cb_called = 0;
+  EXPECT_EQ(SECSuccess, SSL_HelloRetryRequestCallback(server->ssl_fd(),
+                                                      RetryHello, &cb_called));
+
+  // Start the handshake.
+  client->StartConnect();
+  server->StartConnect();
+  client->Handshake();
+  server->Handshake();
+  EXPECT_EQ(1U, cb_called);
+}
+
+TEST_P(TlsConnectTls13, RetryStateless) {
+  ConfigureSelfEncrypt();
+  EnsureTlsSetup();
+
+  TriggerHelloRetryRequest(client_, server_);
+  MakeNewServer();
+
+  Handshake();
+  SendReceive();
+}
+
+TEST_P(TlsConnectTls13, RetryStatefulDropCookie) {
+  ConfigureSelfEncrypt();
+  EnsureTlsSetup();
+
+  TriggerHelloRetryRequest(client_, server_);
+  client_->SetPacketFilter(
+      std::make_shared<TlsExtensionDropper>(ssl_tls13_cookie_xtn));
+
+  ExpectAlert(server_, kTlsAlertMissingExtension);
+  Handshake();
+  client_->CheckErrorCode(SSL_ERROR_MISSING_EXTENSION_ALERT);
+  server_->CheckErrorCode(SSL_ERROR_MISSING_COOKIE_EXTENSION);
+}
+
+// Stream only because DTLS drops bad packets.
+TEST_F(TlsConnectStreamTls13, RetryStatelessDamageFirstClientHello) {
+  ConfigureSelfEncrypt();
+  EnsureTlsSetup();
+
+  auto damage_ch = std::make_shared<TlsExtensionInjector>(0xfff3, DataBuffer());
+  client_->SetPacketFilter(damage_ch);
+
+  TriggerHelloRetryRequest(client_, server_);
+  MakeNewServer();
+
+  // Key exchange fails when the handshake continues because client and server
+  // disagree about the transcript.
+  client_->ExpectSendAlert(kTlsAlertBadRecordMac);
+  server_->ExpectSendAlert(kTlsAlertBadRecordMac);
+  Handshake();
+  server_->CheckErrorCode(SSL_ERROR_BAD_MAC_READ);
+  client_->CheckErrorCode(SSL_ERROR_BAD_MAC_READ);
+}
+
+TEST_F(TlsConnectStreamTls13, RetryStatelessDamageSecondClientHello) {
+  ConfigureSelfEncrypt();
+  EnsureTlsSetup();
+
+  TriggerHelloRetryRequest(client_, server_);
+  MakeNewServer();
+
+  auto damage_ch = std::make_shared<TlsExtensionInjector>(0xfff3, DataBuffer());
+  client_->SetPacketFilter(damage_ch);
+
+  // Key exchange fails when the handshake continues because client and server
+  // disagree about the transcript.
+  client_->ExpectSendAlert(kTlsAlertBadRecordMac);
+  server_->ExpectSendAlert(kTlsAlertBadRecordMac);
+  Handshake();
+  server_->CheckErrorCode(SSL_ERROR_BAD_MAC_READ);
+  client_->CheckErrorCode(SSL_ERROR_BAD_MAC_READ);
+}
+
+// Read the cipher suite from the HRR and disable it on the identified agent.
+static void DisableSuiteFromHrr(
+    std::shared_ptr<TlsAgent>& agent,
+    std::shared_ptr<TlsInspectorRecordHandshakeMessage>& capture_hrr) {
+  uint32_t tmp;
+  size_t offset = 2 + 32;  // skip version + server_random
+  ASSERT_TRUE(
+      capture_hrr->buffer().Read(offset, 1, &tmp));  // session_id length
+  EXPECT_EQ(0U, tmp);
+  offset += 1 + tmp;
+  ASSERT_TRUE(capture_hrr->buffer().Read(offset, 2, &tmp));  // suite
+  EXPECT_EQ(
+      SECSuccess,
+      SSL_CipherPrefSet(agent->ssl_fd(), static_cast<uint16_t>(tmp), PR_FALSE));
+}
+
+TEST_P(TlsConnectTls13, RetryStatelessDisableSuiteClient) {
+  ConfigureSelfEncrypt();
+  EnsureTlsSetup();
+
+  auto capture_hrr = std::make_shared<TlsInspectorRecordHandshakeMessage>(
+      ssl_hs_hello_retry_request);
+  server_->SetPacketFilter(capture_hrr);
+
+  TriggerHelloRetryRequest(client_, server_);
+  MakeNewServer();
+
+  DisableSuiteFromHrr(client_, capture_hrr);
+
+  // The client thinks that the HelloRetryRequest is bad, even though its
+  // because it changed its mind about the cipher suite.
+  ExpectAlert(client_, kTlsAlertIllegalParameter);
+  Handshake();
+  client_->CheckErrorCode(SSL_ERROR_NO_CYPHER_OVERLAP);
+  server_->CheckErrorCode(SSL_ERROR_ILLEGAL_PARAMETER_ALERT);
+}
+
+TEST_P(TlsConnectTls13, RetryStatelessDisableSuiteServer) {
+  ConfigureSelfEncrypt();
+  EnsureTlsSetup();
+
+  auto capture_hrr = std::make_shared<TlsInspectorRecordHandshakeMessage>(
+      ssl_hs_hello_retry_request);
+  server_->SetPacketFilter(capture_hrr);
+
+  TriggerHelloRetryRequest(client_, server_);
+  MakeNewServer();
+
+  DisableSuiteFromHrr(server_, capture_hrr);
+
+  ExpectAlert(server_, kTlsAlertIllegalParameter);
+  Handshake();
+  server_->CheckErrorCode(SSL_ERROR_BAD_2ND_CLIENT_HELLO);
+  client_->CheckErrorCode(SSL_ERROR_ILLEGAL_PARAMETER_ALERT);
+}
+
+TEST_P(TlsConnectTls13, RetryStatelessDisableGroupClient) {
+  ConfigureSelfEncrypt();
+  EnsureTlsSetup();
+
+  TriggerHelloRetryRequest(client_, server_);
+  MakeNewServer();
+
+  static const std::vector<SSLNamedGroup> groups = {ssl_grp_ec_secp384r1};
+  client_->ConfigNamedGroups(groups);
+
+  // We're into undefined behavior on the client side, but - at the point this
+  // test was written - the client here doesn't amend its key shares because the
+  // server doesn't ask it to.  The server notices that the key share (x25519)
+  // doesn't match the negotiated group (P-384) and objects.
+  ExpectAlert(server_, kTlsAlertIllegalParameter);
+  Handshake();
+  server_->CheckErrorCode(SSL_ERROR_BAD_2ND_CLIENT_HELLO);
+  client_->CheckErrorCode(SSL_ERROR_ILLEGAL_PARAMETER_ALERT);
+}
+
+TEST_P(TlsConnectTls13, RetryStatelessDisableGroupServer) {
+  ConfigureSelfEncrypt();
+  EnsureTlsSetup();
+
+  TriggerHelloRetryRequest(client_, server_);
+  MakeNewServer();
+
+  static const std::vector<SSLNamedGroup> groups = {ssl_grp_ec_secp384r1};
+  server_->ConfigNamedGroups(groups);
+
+  ExpectAlert(server_, kTlsAlertIllegalParameter);
+  Handshake();
+  server_->CheckErrorCode(SSL_ERROR_BAD_2ND_CLIENT_HELLO);
+  client_->CheckErrorCode(SSL_ERROR_ILLEGAL_PARAMETER_ALERT);
+}
+
+TEST_P(TlsConnectTls13, RetryStatelessBadCookie) {
+  ConfigureSelfEncrypt();
+  EnsureTlsSetup();
+
+  TriggerHelloRetryRequest(client_, server_);
+
+  // Now replace the self-encrypt MAC key with a garbage key.
+  static const uint8_t bad_hmac_key[32] = {0};
+  SECItem key_item = {siBuffer, const_cast<uint8_t*>(bad_hmac_key),
+                      sizeof(bad_hmac_key)};
+  ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
+  PK11SymKey* hmac_key =
+      PK11_ImportSymKey(slot.get(), CKM_SHA256_HMAC, PK11_OriginUnwrap,
+                        CKA_SIGN, &key_item, nullptr);
+  ASSERT_NE(nullptr, hmac_key);
+  SSLInt_SetSelfEncryptMacKey(hmac_key);  // Passes ownership.
+
+  MakeNewServer();
+
+  ExpectAlert(server_, kTlsAlertIllegalParameter);
+  Handshake();
+  server_->CheckErrorCode(SSL_ERROR_BAD_2ND_CLIENT_HELLO);
+  client_->CheckErrorCode(SSL_ERROR_ILLEGAL_PARAMETER_ALERT);
+}
+
+// Stream because the server doesn't consume the alert and terminate.
+TEST_F(TlsConnectStreamTls13, RetryWithDifferentCipherSuite) {
+  EnsureTlsSetup();
+  // Force a HelloRetryRequest.
+  static const std::vector<SSLNamedGroup> groups = {ssl_grp_ec_secp384r1};
+  server_->ConfigNamedGroups(groups);
+  // Then switch out the default suite (TLS_AES_128_GCM_SHA256).
+  server_->SetPacketFilter(std::make_shared<SelectedCipherSuiteReplacer>(
+      TLS_CHACHA20_POLY1305_SHA256));
+
+  client_->ExpectSendAlert(kTlsAlertIllegalParameter);
+  server_->ExpectSendAlert(kTlsAlertBadRecordMac);
+  ConnectExpectFail();
+  EXPECT_EQ(SSL_ERROR_RX_MALFORMED_SERVER_HELLO, client_->error_code());
+  EXPECT_EQ(SSL_ERROR_BAD_MAC_READ, server_->error_code());
+}
+
 // This tests that the second attempt at sending a ClientHello (after receiving
 // a HelloRetryRequest) is correctly retransmitted.
 TEST_F(TlsConnectDatagram13, DropClientSecondFlightWithHelloRetry) {
   static const std::vector<SSLNamedGroup> groups = {ssl_grp_ec_secp384r1,
                                                     ssl_grp_ec_secp521r1};
   server_->ConfigNamedGroups(groups);
   server_->SetPacketFilter(std::make_shared<SelectiveDropFilter>(0x2));
   Connect();
@@ -228,30 +812,77 @@ TEST_P(TlsKeyExchange13, ConnectEcdhePre
   server_->ConfigNamedGroups(server_groups);
   EXPECT_EQ(SECSuccess, SSL_SendAdditionalKeyShares(client_->ssl_fd(), 1));
 
   Connect();
   CheckKeys();
   CheckKEXDetails(client_groups, client_groups);
 }
 
+// The callback should be run even if we have another reason to send
+// HelloRetryRequest.  In this case, the server sends HRR because the server
+// wants an X25519 key share and the client didn't offer one.
+TEST_P(TlsKeyExchange13,
+       RetryCallbackRetryWithGroupMismatchAndAdditionalShares) {
+  EnsureKeyShareSetup();
+
+  static const std::vector<SSLNamedGroup> client_groups = {
+      ssl_grp_ec_secp256r1, ssl_grp_ec_secp384r1, ssl_grp_ec_curve25519};
+  client_->ConfigNamedGroups(client_groups);
+  static const std::vector<SSLNamedGroup> server_groups = {
+      ssl_grp_ec_curve25519};
+  server_->ConfigNamedGroups(server_groups);
+  EXPECT_EQ(SECSuccess, SSL_SendAdditionalKeyShares(client_->ssl_fd(), 1));
+
+  auto capture_server =
+      std::make_shared<TlsExtensionCapture>(ssl_tls13_key_share_xtn);
+  capture_server->SetHandshakeTypes({kTlsHandshakeHelloRetryRequest});
+  server_->SetPacketFilter(std::make_shared<ChainedPacketFilter>(
+      ChainedPacketFilterInit{capture_hrr_, capture_server}));
+
+  size_t cb_called = 0;
+  EXPECT_EQ(SECSuccess, SSL_HelloRetryRequestCallback(server_->ssl_fd(),
+                                                      RetryHello, &cb_called));
+
+  // Do the first message exchange.
+  StartConnect();
+  client_->Handshake();
+  server_->Handshake();
+
+  EXPECT_EQ(1U, cb_called) << "callback should be called once here";
+  EXPECT_TRUE(capture_server->captured()) << "key_share extension expected";
+
+  uint32_t server_group = 0;
+  EXPECT_TRUE(capture_server->extension().Read(0, 2, &server_group));
+  EXPECT_EQ(ssl_grp_ec_curve25519, static_cast<SSLNamedGroup>(server_group));
+
+  Handshake();
+  CheckConnected();
+  EXPECT_EQ(2U, cb_called);
+  EXPECT_TRUE(shares_capture2_->captured()) << "client should send shares";
+
+  CheckKeys();
+  static const std::vector<SSLNamedGroup> client_shares(
+      client_groups.begin(), client_groups.begin() + 2);
+  CheckKEXDetails(client_groups, client_shares, server_groups[0]);
+}
+
 TEST_F(TlsConnectTest, Select12AfterHelloRetryRequest) {
   EnsureTlsSetup();
   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_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_);
@@ -271,25 +902,40 @@ class HelloRetryRequestAgentTest : publi
     TlsAgentTestClient::SetUp();
     EnsureInit();
     agent_->StartConnect();
   }
 
   void MakeCannedHrr(const uint8_t* body, size_t len, DataBuffer* hrr_record,
                      uint32_t seq_num = 0) const {
     DataBuffer hrr_data;
-    hrr_data.Allocate(len + 4);
+    const uint8_t ssl_hello_retry_random[] = {
+        0xCF, 0x21, 0xAD, 0x74, 0xE5, 0x9A, 0x61, 0x11, 0xBE, 0x1D, 0x8C,
+        0x02, 0x1E, 0x65, 0xB8, 0x91, 0xC2, 0xA2, 0x11, 0x16, 0x7A, 0xBB,
+        0x8C, 0x5E, 0x07, 0x9E, 0x09, 0xE2, 0xC8, 0xA8, 0x33, 0x9C};
+
+    hrr_data.Allocate(len + 6);
     size_t i = 0;
+    i = hrr_data.Write(i, 0x0303, 2);
+    i = hrr_data.Write(i, ssl_hello_retry_random,
+                       sizeof(ssl_hello_retry_random));
+    i = hrr_data.Write(i, static_cast<uint32_t>(0), 1);  // session_id
+    i = hrr_data.Write(i, TLS_AES_128_GCM_SHA256, 2);
+    i = hrr_data.Write(i, ssl_compression_null, 1);
+    // Add extensions.  First a length, which includes the supported version.
+    i = hrr_data.Write(i, static_cast<uint32_t>(len) + 6, 2);
+    // Now the supported version.
+    i = hrr_data.Write(i, ssl_tls13_supported_versions_xtn, 2);
+    i = hrr_data.Write(i, 2, 2);
     i = hrr_data.Write(i, 0x7f00 | TLS_1_3_DRAFT_VERSION, 2);
-    i = hrr_data.Write(i, static_cast<uint32_t>(len), 2);
     if (len) {
       hrr_data.Write(i, body, len);
     }
     DataBuffer hrr;
-    MakeHandshakeMessage(kTlsHandshakeHelloRetryRequest, hrr_data.data(),
+    MakeHandshakeMessage(kTlsHandshakeServerHello, hrr_data.data(),
                          hrr_data.len(), &hrr, seq_num);
     MakeRecord(kTlsHandshakeType, SSL_LIBRARY_VERSION_TLS_1_3, hrr.data(),
                hrr.len(), hrr_record, seq_num);
   }
 
   void MakeGroupHrr(SSLNamedGroup group, DataBuffer* hrr_record,
                     uint32_t seq_num = 0) const {
     const uint8_t group_hrr[] = {
@@ -329,38 +975,16 @@ TEST_P(HelloRetryRequestAgentTest, Handl
 TEST_P(HelloRetryRequestAgentTest, HandleNoopHelloRetryRequest) {
   DataBuffer hrr;
   MakeCannedHrr(nullptr, 0U, &hrr);
   ExpectAlert(kTlsAlertDecodeError);
   ProcessMessage(hrr, TlsAgent::STATE_ERROR,
                  SSL_ERROR_RX_MALFORMED_HELLO_RETRY_REQUEST);
 }
 
-TEST_P(HelloRetryRequestAgentTest, HandleHelloRetryRequestCookie) {
-  const uint8_t canned_cookie_hrr[] = {
-      static_cast<uint8_t>(ssl_tls13_cookie_xtn >> 8),
-      static_cast<uint8_t>(ssl_tls13_cookie_xtn),
-      0,
-      5,  // length of cookie extension
-      0,
-      3,  // cookie value length
-      0xc0,
-      0x0c,
-      0x13};
-  DataBuffer hrr;
-  MakeCannedHrr(canned_cookie_hrr, sizeof(canned_cookie_hrr), &hrr);
-  auto capture = std::make_shared<TlsExtensionCapture>(ssl_tls13_cookie_xtn);
-  agent_->SetPacketFilter(capture);
-  ProcessMessage(hrr, TlsAgent::STATE_CONNECTING);
-  const size_t cookie_pos = 2 + 2;  // cookie_xtn, extension len
-  DataBuffer cookie(canned_cookie_hrr + cookie_pos,
-                    sizeof(canned_cookie_hrr) - cookie_pos);
-  EXPECT_EQ(cookie, capture->extension());
-}
-
 INSTANTIATE_TEST_CASE_P(HelloRetryRequestAgentTests, HelloRetryRequestAgentTest,
                         ::testing::Combine(TlsConnectTestBase::kTlsVariantsAll,
                                            TlsConnectTestBase::kTlsV13));
 #ifndef NSS_DISABLE_TLS_1_3
 INSTANTIATE_TEST_CASE_P(HelloRetryRequestKeyExchangeTests, TlsKeyExchange13,
                         ::testing::Combine(TlsConnectTestBase::kTlsVariantsAll,
                                            TlsConnectTestBase::kTlsV13));
 #endif
--- a/security/nss/gtests/ssl_gtest/ssl_loopback_unittest.cc
+++ b/security/nss/gtests/ssl_gtest/ssl_loopback_unittest.cc
@@ -80,23 +80,23 @@ class TlsAlertRecorder : public TlsRecor
   uint8_t description() const { return description_; }
 
  private:
   uint8_t level_;
   uint8_t description_;
 };
 
 class HelloTruncator : public TlsHandshakeFilter {
+ public:
+  HelloTruncator()
+      : TlsHandshakeFilter(
+            {kTlsHandshakeClientHello, kTlsHandshakeServerHello}) {}
   PacketFilter::Action FilterHandshake(const HandshakeHeader& header,
                                        const DataBuffer& input,
                                        DataBuffer* output) override {
-    if (header.handshake_type() != kTlsHandshakeClientHello &&
-        header.handshake_type() != kTlsHandshakeServerHello) {
-      return KEEP;
-    }
     output->Assign(input.data(), input.len() - 1);
     return CHANGE;
   }
 };
 
 // Verify that when NSS reports that an alert is sent, it is actually sent.
 TEST_P(TlsConnectGeneric, CaptureAlertServer) {
   client_->SetPacketFilter(std::make_shared<HelloTruncator>());
@@ -119,18 +119,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);
@@ -167,16 +166,115 @@ TEST_P(TlsConnectDatagram, ConnectSrtp) 
   SendReceive();
 }
 
 TEST_P(TlsConnectGeneric, ConnectSendReceive) {
   Connect();
   SendReceive();
 }
 
+class SaveTlsRecord : public TlsRecordFilter {
+ public:
+  SaveTlsRecord(size_t index) : index_(index), count_(0), contents_() {}
+
+  const DataBuffer& contents() const { return contents_; }
+
+ protected:
+  PacketFilter::Action FilterRecord(const TlsRecordHeader& header,
+                                    const DataBuffer& data,
+                                    DataBuffer* changed) override {
+    if (count_++ == index_) {
+      contents_ = data;
+    }
+    return KEEP;
+  }
+
+ private:
+  const size_t index_;
+  size_t count_;
+  DataBuffer contents_;
+};
+
+// Check that decrypting filters work and can read any record.
+// This test (currently) only works in TLS 1.3 where we can decrypt.
+TEST_F(TlsConnectStreamTls13, DecryptRecordClient) {
+  EnsureTlsSetup();
+  // 0 = ClientHello, 1 = Finished, 2 = SendReceive, 3 = SendBuffer
+  auto saved = std::make_shared<SaveTlsRecord>(3);
+  client_->SetTlsRecordFilter(saved);
+  Connect();
+  SendReceive();
+
+  static const uint8_t data[] = {0xde, 0xad, 0xdc};
+  DataBuffer buf(data, sizeof(data));
+  client_->SendBuffer(buf);
+  EXPECT_EQ(buf, saved->contents());
+}
+
+TEST_F(TlsConnectStreamTls13, DecryptRecordServer) {
+  EnsureTlsSetup();
+  // Disable tickets so that we are sure to not get NewSessionTicket.
+  EXPECT_EQ(SECSuccess, SSL_OptionSet(server_->ssl_fd(),
+                                      SSL_ENABLE_SESSION_TICKETS, PR_FALSE));
+  // 0 = ServerHello, 1 = other handshake, 2 = SendReceive, 3 = SendBuffer
+  auto saved = std::make_shared<SaveTlsRecord>(3);
+  server_->SetTlsRecordFilter(saved);
+  Connect();
+  SendReceive();
+
+  static const uint8_t data[] = {0xde, 0xad, 0xd5};
+  DataBuffer buf(data, sizeof(data));
+  server_->SendBuffer(buf);
+  EXPECT_EQ(buf, saved->contents());
+}
+
+class DropTlsRecord : public TlsRecordFilter {
+ public:
+  DropTlsRecord(size_t index) : index_(index), count_(0) {}
+
+ protected:
+  PacketFilter::Action FilterRecord(const TlsRecordHeader& header,
+                                    const DataBuffer& data,
+                                    DataBuffer* changed) override {
+    if (count_++ == index_) {
+      return DROP;
+    }
+    return KEEP;
+  }
+
+ private:
+  const size_t index_;
+  size_t count_;
+};
+
+// Test that decrypting filters work correctly and are able to drop records.
+TEST_F(TlsConnectStreamTls13, DropRecordServer) {
+  EnsureTlsSetup();
+  // Disable session tickets so that the server doesn't send an extra record.
+  EXPECT_EQ(SECSuccess, SSL_OptionSet(server_->ssl_fd(),
+                                      SSL_ENABLE_SESSION_TICKETS, PR_FALSE));
+
+  // 0 = ServerHello, 1 = other handshake, 2 = first write
+  server_->SetTlsRecordFilter(std::make_shared<DropTlsRecord>(2));
+  Connect();
+  server_->SendData(23, 23);  // This should be dropped, so it won't be counted.
+  server_->ResetSentBytes();
+  SendReceive();
+}
+
+TEST_F(TlsConnectStreamTls13, DropRecordClient) {
+  EnsureTlsSetup();
+  // 0 = ClientHello, 1 = Finished, 2 = first write
+  client_->SetTlsRecordFilter(std::make_shared<DropTlsRecord>(2));
+  Connect();
+  client_->SendData(26, 26);  // This should be dropped, so it won't be counted.
+  client_->ResetSentBytes();
+  SendReceive();
+}
+
 // The next two tests takes advantage of the fact that we
 // automatically read the first 1024 bytes, so if
 // we provide 1200 bytes, they overrun the read buffer
 // provided by the calling test.
 
 // DTLS should return an error.
 TEST_P(TlsConnectDatagram, ShortRead) {
   Connect();
@@ -213,28 +311,69 @@ TEST_P(TlsConnectGeneric, ConnectWithCom
   EnsureTlsSetup();
   client_->SetOption(SSL_ENABLE_DEFLATE, PR_TRUE);
   server_->SetOption(SSL_ENABLE_DEFLATE, PR_TRUE);
   Connect();
   EXPECT_FALSE(client_->is_compressed());
   SendReceive();
 }
 
-TEST_P(TlsConnectDatagram, TestDtlsHolddownExpiry) {
+class TlsHolddownTest : public TlsConnectDatagram {
+ protected:
+  // This causes all timers to run to completion.  It advances the clock and
+  // handshakes on both peers until both peers have no more timers pending,
+  // which should happen at the end of a handshake.  This is necessary to ensure
+  // that the relatively long holddown timer expires, but that any other timers
+  // also expire and run correctly.
+  void RunAllTimersDown() {
+    while (true) {
+      PRIntervalTime time;
+      SECStatus rv = DTLS_GetHandshakeTimeout(client_->ssl_fd(), &time);
+      if (rv != SECSuccess) {
+        rv = DTLS_GetHandshakeTimeout(server_->ssl_fd(), &time);
+        if (rv != SECSuccess) {
+          break;  // Neither peer has an outstanding timer.
+        }
+      }
+
+      if (g_ssl_gtest_verbose) {
+        std::cerr << "Shifting timers" << std::endl;
+      }
+      ShiftDtlsTimers();
+      Handshake();
+    }
+  }
+};
+
+TEST_P(TlsHolddownTest, TestDtlsHolddownExpiry) {
   Connect();
-  std::cerr << "Expiring holddown timer\n";
-  SSLInt_ForceTimerExpiry(client_->ssl_fd());
-  SSLInt_ForceTimerExpiry(server_->ssl_fd());
+  std::cerr << "Expiring holddown timer" << std::endl;
+  RunAllTimersDown();
   SendReceive();
   if (version_ >= SSL_LIBRARY_VERSION_TLS_1_3) {
     // One for send, one for receive.
-    EXPECT_EQ(2, SSLInt_CountTls13CipherSpecs(client_->ssl_fd()));
+    EXPECT_EQ(2, SSLInt_CountCipherSpecs(client_->ssl_fd()));
   }
 }
 
+TEST_P(TlsHolddownTest, TestDtlsHolddownExpiryResumption) {
+  ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
+  Connect();
+  SendReceive();
+
+  Reset();
+  ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
+  ExpectResumption(RESUME_TICKET);
+  Connect();
+  RunAllTimersDown();
+  SendReceive();
+  // One for send, one for receive.
+  EXPECT_EQ(2, SSLInt_CountCipherSpecs(client_->ssl_fd()));
+}
+
 class TlsPreCCSHeaderInjector : public TlsRecordFilter {
  public:
   TlsPreCCSHeaderInjector() {}
   virtual PacketFilter::Action FilterRecord(
       const TlsRecordHeader& record_header, const DataBuffer& input,
       size_t* offset, DataBuffer* output) override {
     if (record_header.content_type() != kTlsChangeCipherSpecType) return KEEP;
 
@@ -252,18 +391,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.
 }
 
@@ -284,46 +422,92 @@ 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_->adapter()->SetWriteError(PR_IO_ERROR);
   client_->Handshake();  // This will get an error, but shouldn't crash.
   client_->CheckErrorCode(SSL_ERROR_SOCKET_WRITE_FAILURE);
 }
 
-TEST_F(TlsConnectStreamTls13, NegotiateShortHeaders) {
-  client_->SetShortHeadersEnabled();
-  server_->SetShortHeadersEnabled();
-  client_->ExpectShortHeaders();
-  server_->ExpectShortHeaders();
+TEST_P(TlsConnectDatagram, BlockedWrite) {
+  Connect();
+
+  // Mark the socket as blocked.
+  client_->adapter()->SetWriteError(PR_WOULD_BLOCK_ERROR);
+  static const uint8_t data[] = {1, 2, 3};
+  int32_t rv = PR_Write(client_->ssl_fd(), data, sizeof(data));
+  EXPECT_GT(0, rv);
+  EXPECT_EQ(PR_WOULD_BLOCK_ERROR, PORT_GetError());
+
+  // Remove the write error and though the previous write failed, future reads
+  // and writes should just work as if it never happened.
+  client_->adapter()->SetWriteError(0);
+  SendReceive();
+}
+
+TEST_F(TlsConnectTest, ConnectSSLv3) {
+  ConfigureVersion(SSL_LIBRARY_VERSION_3_0);
+  EnableOnlyStaticRsaCiphers();
   Connect();
+  CheckKeys(ssl_kea_rsa, ssl_grp_none, ssl_auth_rsa_decrypt, ssl_sig_none);
+}
+
+TEST_F(TlsConnectTest, ConnectSSLv3ClientAuth) {
+  ConfigureVersion(SSL_LIBRARY_VERSION_3_0);
+  EnableOnlyStaticRsaCiphers();
+  client_->SetupClientAuth();
+  server_->RequestClientAuth(true);
+  Connect();
+  CheckKeys(ssl_kea_rsa, ssl_grp_none, ssl_auth_rsa_decrypt, ssl_sig_none);
+}
+
+static size_t ExpectedCbcLen(size_t in, size_t hmac = 20, size_t block = 16) {
+  // MAC-then-Encrypt expansion formula:
+  return ((in + hmac + (block - 1)) / block) * block;
+}
+
+TEST_F(TlsConnectTest, OneNRecordSplitting) {
+  ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_0);
+  EnsureTlsSetup();
+  ConnectWithCipherSuite(TLS_RSA_WITH_AES_128_CBC_SHA);
+  auto records = std::make_shared<TlsRecordRecorder>();
+  server_->SetPacketFilter(records);
+  // This should be split into 1, 16384 and 20.
+  DataBuffer big_buffer;
+  big_buffer.Allocate(1 + 16384 + 20);
+  server_->SendBuffer(big_buffer);
+  ASSERT_EQ(3U, records->count());
+  EXPECT_EQ(ExpectedCbcLen(1), records->record(0).buffer.len());
+  EXPECT_EQ(ExpectedCbcLen(16384), records->record(1).buffer.len());
+  EXPECT_EQ(ExpectedCbcLen(20), records->record(2).buffer.len());
 }
 
 INSTANTIATE_TEST_CASE_P(
     GenericStream, TlsConnectGeneric,
     ::testing::Combine(TlsConnectTestBase::kTlsVariantsStream,
                        TlsConnectTestBase::kTlsVAll));
 INSTANTIATE_TEST_CASE_P(
     GenericDatagram, TlsConnectGeneric,
     ::testing::Combine(TlsConnectTestBase::kTlsVariantsDatagram,
                        TlsConnectTestBase::kTlsV11Plus));
 
 INSTANTIATE_TEST_CASE_P(StreamOnly, TlsConnectStream,
                         TlsConnectTestBase::kTlsVAll);
 INSTANTIATE_TEST_CASE_P(DatagramOnly, TlsConnectDatagram,
                         TlsConnectTestBase::kTlsV11Plus);
+INSTANTIATE_TEST_CASE_P(DatagramHolddown, TlsHolddownTest,
+                        TlsConnectTestBase::kTlsV11Plus);
 
 INSTANTIATE_TEST_CASE_P(
     Pre12Stream, TlsConnectPre12,
     ::testing::Combine(TlsConnectTestBase::kTlsVariantsStream,
                        TlsConnectTestBase::kTlsV10V11));
 INSTANTIATE_TEST_CASE_P(
     Pre12Datagram, TlsConnectPre12,
     ::testing::Combine(TlsConnectTestBase::kTlsVariantsDatagram,
--- a/security/nss/gtests/ssl_gtest/ssl_record_unittest.cc
+++ b/security/nss/gtests/ssl_gtest/ssl_record_unittest.cc
@@ -132,32 +132,30 @@ class RecordReplacer : public TlsRecordF
 };
 
 TEST_F(TlsConnectStreamTls13, LargeRecord) {
   EnsureTlsSetup();
 
   const size_t record_limit = 16384;
   auto replacer = std::make_shared<RecordReplacer>(record_limit);
   client_->SetTlsRecordFilter(replacer);
-  replacer->EnableDecryption();
   Connect();
 
   replacer->Enable();
   client_->SendData(10);
   WAIT_(server_->received_bytes() == record_limit, 2000);
   ASSERT_EQ(record_limit, server_->received_bytes());
 }
 
 TEST_F(TlsConnectStreamTls13, TooLargeRecord) {
   EnsureTlsSetup();
 
   const size_t record_limit = 16384;
   auto replacer = std::make_shared<RecordReplacer>(record_limit + 1);
   client_->SetTlsRecordFilter(replacer);
-  replacer->EnableDecryption();
   Connect();
 
   replacer->Enable();
   ExpectAlert(server_, kTlsAlertRecordOverflow);
   client_->SendData(10);  // This is expanded.
 
   uint8_t buf[record_limit + 2];
   PRInt32 rv = PR_Read(server_->ssl_fd(), buf, sizeof(buf));
--- a/security/nss/gtests/ssl_gtest/ssl_renegotiation_unittest.cc
+++ b/security/nss/gtests/ssl_gtest/ssl_renegotiation_unittest.cc
@@ -66,27 +66,27 @@ TEST_P(TlsConnectStream, ConnectTls10And
   // Reset version and cipher suite so that the preinfo callback
   // doesn't fail.
   server_->ResetPreliminaryInfo();
   server_->StartRenegotiate();
 
   if (test_version >= SSL_LIBRARY_VERSION_TLS_1_3) {
     ExpectAlert(server_, kTlsAlertUnexpectedMessage);
   } else {
-    ExpectAlert(client_, kTlsAlertIllegalParameter);
+    ExpectAlert(server_, kTlsAlertProtocolVersion);
   }
 
   Handshake();
   if (test_version >= SSL_LIBRARY_VERSION_TLS_1_3) {
     // In TLS 1.3, the server detects this problem.
     client_->CheckErrorCode(SSL_ERROR_HANDSHAKE_UNEXPECTED_ALERT);
     server_->CheckErrorCode(SSL_ERROR_RENEGOTIATION_NOT_ALLOWED);
   } else {
-    client_->CheckErrorCode(SSL_ERROR_UNSUPPORTED_VERSION);
-    server_->CheckErrorCode(SSL_ERROR_ILLEGAL_PARAMETER_ALERT);
+    client_->CheckErrorCode(SSL_ERROR_PROTOCOL_VERSION_ALERT);
+    server_->CheckErrorCode(SSL_ERROR_UNSUPPORTED_VERSION);
   }
 }
 
 TEST_P(TlsConnectStream, ConnectTls10AndClientRenegotiateHigher) {
   if (version_ == SSL_LIBRARY_VERSION_TLS_1_0) {
     return;
   }
   // Set the client so it will accept any version from 1.0
@@ -105,27 +105,95 @@ TEST_P(TlsConnectStream, ConnectTls10And
   server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_0, test_version);
   // Reset version and cipher suite so that the preinfo callback
   // doesn't fail.
   server_->ResetPreliminaryInfo();
   client_->StartRenegotiate();
   if (test_version >= SSL_LIBRARY_VERSION_TLS_1_3) {
     ExpectAlert(server_, kTlsAlertUnexpectedMessage);
   } else {
-    ExpectAlert(client_, kTlsAlertIllegalParameter);
+    ExpectAlert(server_, kTlsAlertProtocolVersion);
   }
   Handshake();
   if (test_version >= SSL_LIBRARY_VERSION_TLS_1_3) {
     // In TLS 1.3, the server detects this problem.
     client_->CheckErrorCode(SSL_ERROR_HANDSHAKE_UNEXPECTED_ALERT);
     server_->CheckErrorCode(SSL_ERROR_RENEGOTIATION_NOT_ALLOWED);
   } else {
-    client_->CheckErrorCode(SSL_ERROR_UNSUPPORTED_VERSION);
-    server_->CheckErrorCode(SSL_ERROR_ILLEGAL_PARAMETER_ALERT);
+    client_->CheckErrorCode(SSL_ERROR_PROTOCOL_VERSION_ALERT);
+    server_->CheckErrorCode(SSL_ERROR_UNSUPPORTED_VERSION);
+  }
+}
+
+TEST_P(TlsConnectStream, ConnectAndServerRenegotiateLower) {
+  if (version_ == SSL_LIBRARY_VERSION_TLS_1_0) {
+    return;
+  }
+  Connect();
+
+  // Now renegotiate with the server set to TLS 1.0.
+  client_->PrepareForRenegotiate();
+  server_->PrepareForRenegotiate();
+  client_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_0, version_);
+  // Reset version and cipher suite so that the preinfo callback
+  // doesn't fail.
+  server_->ResetPreliminaryInfo();
+
+  SECStatus rv = SSL_ReHandshake(server_->ssl_fd(), PR_TRUE);
+  if (version_ >= SSL_LIBRARY_VERSION_TLS_1_3) {
+    EXPECT_EQ(SECFailure, rv);
+    return;
   }
+  ASSERT_EQ(SECSuccess, rv);
+
+  // Now, before handshaking, tweak the server configuration.
+  server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_0,
+                           SSL_LIBRARY_VERSION_TLS_1_0);
+
+  // The server should catch the own error.
+  ExpectAlert(server_, kTlsAlertProtocolVersion);
+
+  Handshake();
+  client_->CheckErrorCode(SSL_ERROR_PROTOCOL_VERSION_ALERT);
+  server_->CheckErrorCode(SSL_ERROR_UNSUPPORTED_VERSION);
+}
+
+TEST_P(TlsConnectStream, ConnectAndServerWontRenegotiateLower) {
+  if (version_ == SSL_LIBRARY_VERSION_TLS_1_0) {
+    return;
+  }
+  Connect();
+
+  // Now renegotiate with the server set to TLS 1.0.
+  client_->PrepareForRenegotiate();
+  server_->PrepareForRenegotiate();
+  client_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_0, version_);
+  server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_0,
+                           SSL_LIBRARY_VERSION_TLS_1_0);
+  // Reset version and cipher suite so that the preinfo callback
+  // doesn't fail.
+  server_->ResetPreliminaryInfo();
+
+  EXPECT_EQ(SECFailure, SSL_ReHandshake(server_->ssl_fd(), PR_TRUE));
+}
+
+TEST_P(TlsConnectStream, ConnectAndClientWontRenegotiateLower) {
+  if (version_ == SSL_LIBRARY_VERSION_TLS_1_0) {
+    return;
+  }
+  Connect();
+
+  // Now renegotiate with the client set to TLS 1.0.
+  client_->PrepareForRenegotiate();
+  server_->PrepareForRenegotiate();
+  server_->ResetPreliminaryInfo();
+  client_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_0,
+                           SSL_LIBRARY_VERSION_TLS_1_0);
+  // The client will refuse to renegotiate down.
+  EXPECT_EQ(SECFailure, SSL_ReHandshake(client_->ssl_fd(), PR_TRUE));
 }
 
 TEST_F(TlsConnectTest, Tls13RejectsRehandshakeClient) {
   EnsureTlsSetup();
   ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3);
   Connect();
   SECStatus rv = SSL_ReHandshake(client_->ssl_fd(), PR_TRUE);
   EXPECT_EQ(SECFailure, rv);
--- a/security/nss/gtests/ssl_gtest/ssl_resumption_unittest.cc
+++ b/security/nss/gtests/ssl_gtest/ssl_resumption_unittest.cc
@@ -4,16 +4,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include <functional>
 #include <memory>
 #include "secerr.h"
 #include "ssl.h"
 #include "sslerr.h"
+#include "sslexp.h"
 #include "sslproto.h"
 
 extern "C" {
 // This is not something that should make you happy.
 #include "libssl_internals.h"
 }
 
 #include "gtest_utils.h"
@@ -241,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();
@@ -452,46 +452,16 @@ TEST_P(TlsConnectGeneric, TestResumeServ
   Reset();
   ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
   ExpectResumption(RESUME_NONE);
   server_->EnableSingleCipher(ChooseAnotherCipher(version_));
   Connect();
   CheckKeys();
 }
 
-class SelectedCipherSuiteReplacer : public TlsHandshakeFilter {
- public:
-  SelectedCipherSuiteReplacer(uint16_t suite) : cipher_suite_(suite) {}
-
- protected:
-  PacketFilter::Action FilterHandshake(const HandshakeHeader& header,
-                                       const DataBuffer& input,
-                                       DataBuffer* output) override {
-    if (header.handshake_type() != kTlsHandshakeServerHello) {
-      return KEEP;
-    }
-
-    *output = input;
-    uint32_t temp = 0;
-    EXPECT_TRUE(input.Read(0, 2, &temp));
-    // Cipher suite is after version(2) and random(32).
-    size_t pos = 34;
-    if (temp < SSL_LIBRARY_VERSION_TLS_1_3) {
-      // In old versions, we have to skip a session_id too.
-      EXPECT_TRUE(input.Read(pos, 1, &temp));
-      pos += 1 + temp;
-    }
-    output->Write(pos, static_cast<uint32_t>(cipher_suite_), 2);
-    return CHANGE;
-  }
-
- private:
-  uint16_t cipher_suite_;
-};
-
 // Test that the client doesn't tolerate the server picking a different cipher
 // suite for resumption.
 TEST_P(TlsConnectStream, TestResumptionOverrideCipher) {
   ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
   server_->EnableSingleCipher(ChooseOneCipher(version_));
   Connect();
   SendReceive();
   CheckKeys(ssl_kea_ecdh, ssl_auth_rsa_sign);
@@ -515,26 +485,23 @@ TEST_P(TlsConnectStream, TestResumptionO
     server_->CheckErrorCode(SSL_ERROR_BAD_MAC_READ);
   } else {
     server_->CheckErrorCode(SSL_ERROR_HANDSHAKE_FAILURE_ALERT);
   }
 }
 
 class SelectedVersionReplacer : public TlsHandshakeFilter {
  public:
-  SelectedVersionReplacer(uint16_t version) : version_(version) {}
+  SelectedVersionReplacer(uint16_t version)
+      : TlsHandshakeFilter({kTlsHandshakeServerHello}), version_(version) {}
 
  protected:
   PacketFilter::Action FilterHandshake(const HandshakeHeader& header,
                                        const DataBuffer& input,
                                        DataBuffer* output) override {
-    if (header.handshake_type() != kTlsHandshakeServerHello) {
-      return KEEP;
-    }
-
     *output = input;
     output->Write(0, static_cast<uint32_t>(version_), 2);
     return CHANGE;
   }
 
  private:
   uint16_t version_;
 };
@@ -643,30 +610,170 @@ TEST_F(TlsConnectTest, TestTls13Resumpti
 // Check that resumption works after receiving two NST messages.
 TEST_F(TlsConnectTest, TestTls13ResumptionDuplicateNST) {
   ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
   ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3);
   Connect();
 
   // Clear the session ticket keys to invalidate the old ticket.
   SSLInt_ClearSelfEncryptKey();
-  SSLInt_SendNewSessionTicket(server_->ssl_fd());
+  SSL_SendSessionTicket(server_->ssl_fd(), NULL, 0);
 
   SendReceive();  // Need to read so that we absorb the session tickets.
   CheckKeys();
 
   // Resume the connection.
   Reset();
   ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
   ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3);
   ExpectResumption(RESUME_TICKET);
   Connect();
   SendReceive();
 }
 
+// Check that the value captured in a NewSessionTicket message matches the value
+// captured from a pre_shared_key extension.
+void NstTicketMatchesPskIdentity(const DataBuffer& nst, const DataBuffer& psk) {
+  uint32_t len;
+
+  size_t offset = 4 + 4;  // Skip ticket_lifetime and ticket_age_add.
+  ASSERT_TRUE(nst.Read(offset, 1, &len));
+  offset += 1 + len;  // Skip ticket_nonce.
+
+  ASSERT_TRUE(nst.Read(offset, 2, &len));
+  offset += 2;  // Skip the ticket length.
+  ASSERT_LE(offset + len, nst.len());
+  DataBuffer nst_ticket(nst.data() + offset, static_cast<size_t>(len));
+
+  offset = 2;  // Skip the identities length.
+  ASSERT_TRUE(psk.Read(offset, 2, &len));
+  offset += 2;  // Skip the identity length.
+  ASSERT_LE(offset + len, psk.len());
+  DataBuffer psk_ticket(psk.data() + offset, static_cast<size_t>(len));
+
+  EXPECT_EQ(nst_ticket, psk_ticket);
+}
+
+TEST_F(TlsConnectTest, TestTls13ResumptionDuplicateNSTWithToken) {
+  ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
+  ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3);
+
+  auto nst_capture = std::make_shared<TlsInspectorRecordHandshakeMessage>(
+      ssl_hs_new_session_ticket);
+  server_->SetTlsRecordFilter(nst_capture);
+  Connect();
+
+  // Clear the session ticket keys to invalidate the old ticket.
+  SSLInt_ClearSelfEncryptKey();
+  nst_capture->Reset();
+  uint8_t token[] = {0x20, 0x20, 0xff, 0x00};
+  EXPECT_EQ(SECSuccess,
+            SSL_SendSessionTicket(server_->ssl_fd(), token, sizeof(token)));
+
+  SendReceive();  // Need to read so that we absorb the session tickets.
+  CheckKeys();
+  EXPECT_LT(0U, nst_capture->buffer().len());
+
+  // Resume the connection.
+  Reset();
+  ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
+  ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3);
+  ExpectResumption(RESUME_TICKET);
+
+  auto psk_capture =
+      std::make_shared<TlsExtensionCapture>(ssl_tls13_pre_shared_key_xtn);
+  client_->SetPacketFilter(psk_capture);
+  Connect();
+  SendReceive();
+
+  NstTicketMatchesPskIdentity(nst_capture->buffer(), psk_capture->extension());
+}
+
+// Disable SSL_ENABLE_SESSION_TICKETS but ensure that tickets can still be sent
+// by invoking SSL_SendSessionTicket directly (and that the ticket is usable).
+TEST_F(TlsConnectTest, SendSessionTicketWithTicketsDisabled) {
+  ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
+  ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3);
+
+  EXPECT_EQ(SECSuccess, SSL_OptionSet(server_->ssl_fd(),
+                                      SSL_ENABLE_SESSION_TICKETS, PR_FALSE));
+
+  auto nst_capture = std::make_shared<TlsInspectorRecordHandshakeMessage>(
+      ssl_hs_new_session_ticket);
+  server_->SetTlsRecordFilter(nst_capture);
+  Connect();
+
+  EXPECT_EQ(0U, nst_capture->buffer().len()) << "expect nothing captured yet";
+
+  EXPECT_EQ(SECSuccess, SSL_SendSessionTicket(server_->ssl_fd(), NULL, 0));
+  EXPECT_LT(0U, nst_capture->buffer().len()) << "should capture now";
+
+  SendReceive();  // Ensure that the client reads the ticket.
+
+  // Resume the connection.
+  Reset();
+  ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
+  ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3);
+  ExpectResumption(RESUME_TICKET);
+
+  auto psk_capture =
+      std::make_shared<TlsExtensionCapture>(ssl_tls13_pre_shared_key_xtn);
+  client_->SetPacketFilter(psk_capture);
+  Connect();
+  SendReceive();
+
+  NstTicketMatchesPskIdentity(nst_capture->buffer(), psk_capture->extension());
+}
+
+// Test calling SSL_SendSessionTicket in inappropriate conditions.
+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());
+
+  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());
+}
+
+TEST_F(TlsConnectTest, SendSessionTicketMassiveToken) {
+  ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
+  ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3);
+  Connect();
+  // It should be safe to set length with a NULL token because the length should
+  // be checked before reading token.
+  EXPECT_EQ(SECFailure, SSL_SendSessionTicket(server_->ssl_fd(), NULL, 0x1ffff))
+      << "this is clearly too big";
+  EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError());
+
+  static const uint8_t big_token[0xffff] = {1};
+  EXPECT_EQ(SECFailure, SSL_SendSessionTicket(server_->ssl_fd(), big_token,
+                                              sizeof(big_token)))
+      << "this is too big, but that's not immediately obvious";
+  EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError());
+}
+
+TEST_F(TlsConnectDatagram13, SendSessionTicketDtls) {
+  ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
+  ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3);
+  Connect();
+  EXPECT_EQ(SECFailure, SSL_SendSessionTicket(server_->ssl_fd(), NULL, 0))
+      << "no extra tickets in DTLS until we have Ack support";
+  EXPECT_EQ(SSL_ERROR_FEATURE_NOT_SUPPORTED_FOR_VERSION, PORT_GetError());
+}
+
 TEST_F(TlsConnectTest, TestTls13ResumptionDowngrade) {
   ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
   ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3);
   Connect();
 
   SendReceive();  // Need to read so that we absorb the session tickets.
   CheckKeys();
 
@@ -710,22 +817,36 @@ TEST_F(TlsConnectTest, TestTls13Resumpti
   // Add filters that set downgrade SH.version to 1.2 and the cipher suite
   // to one that works with 1.2, so that we don't run into early sanity checks.
   // We will eventually fail the (sid.version == SH.version) check.
   std::vector<std::shared_ptr<PacketFilter>> filters;
   filters.push_back(std::make_shared<SelectedCipherSuiteReplacer>(
       TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256));
   filters.push_back(
       std::make_shared<SelectedVersionReplacer>(SSL_LIBRARY_VERSION_TLS_1_2));
+
+  // Drop a bunch of extensions so that we get past the SH processing.  The
+  // version extension says TLS 1.3, which is counter to our goal, the others
+  // are not permitted in TLS 1.2 handshakes.
+  filters.push_back(
+      std::make_shared<TlsExtensionDropper>(ssl_tls13_supported_versions_xtn));
+  filters.push_back(
+      std::make_shared<TlsExtensionDropper>(ssl_tls13_key_share_xtn));
+  filters.push_back(
+      std::make_shared<TlsExtensionDropper>(ssl_tls13_pre_shared_key_xtn));
   server_->SetPacketFilter(std::make_shared<ChainedPacketFilter>(filters));
 
-  client_->ExpectSendAlert(kTlsAlertDecodeError);
+  // The client here generates an unexpected_message alert when it receives an
+  // encrypted handshake message from the server (EncryptedExtension).  The
+  // client expects to receive an unencrypted TLS 1.2 Certificate message.
+  // The server can't decrypt the alert.
+  client_->ExpectSendAlert(kTlsAlertUnexpectedMessage);
   server_->ExpectSendAlert(kTlsAlertBadRecordMac);  // Server can't read
   ConnectExpectFail();
-  client_->CheckErrorCode(SSL_ERROR_RX_MALFORMED_SERVER_HELLO);
+  client_->CheckErrorCode(SSL_ERROR_RX_UNEXPECTED_APPLICATION_DATA);
   server_->CheckErrorCode(SSL_ERROR_BAD_MAC_READ);
 }
 
 TEST_P(TlsConnectGeneric, ReConnectTicket) {
   ConfigureSessionCache(RESUME_BOTH, RESUME_BOTH);
   server_->EnableSingleCipher(ChooseOneCipher(version_));
   Connect();
   SendReceive();
--- a/security/nss/gtests/ssl_gtest/ssl_skip_unittest.cc
+++ b/security/nss/gtests/ssl_gtest/ssl_skip_unittest.cc
@@ -38,17 +38,24 @@ class TlsHandshakeSkipFilter : public Tl
     size_t output_offset = 0U;
     output->Allocate(input.len());
 
     TlsParser parser(input);
     while (parser.remaining()) {
       size_t start = parser.consumed();
       TlsHandshakeFilter::HandshakeHeader header;
       DataBuffer ignored;
-      if (!header.Parse(&parser, record_header, &ignored)) {
+      bool complete = false;
+      if (!header.Parse(&parser, record_header, DataBuffer(), &ignored,
+                        &complete)) {
+        ADD_FAILURE() << "Error parsing handshake header";
+        return KEEP;
+      }
+      if (!complete) {
+        ADD_FAILURE() << "Don't want to deal with fragmented input";
         return KEEP;
       }
 
       if (skipped_ || header.handshake_type() != handshake_type_) {
         size_t entire_length = parser.consumed() - start;
         output->Write(output_offset, input.data() + start, entire_length);
         // DTLS sequence numbers need to be rewritten
         if (skipped_ && header.is_dtls()) {
@@ -96,36 +103,25 @@ class Tls13SkipTest : public TlsConnectT
                       public ::testing::WithParamInterface<SSLProtocolVariant> {
  protected:
   Tls13SkipTest()
       : TlsConnectTestBase(GetParam(), SSL_LIBRARY_VERSION_TLS_1_3) {}
 
   void ServerSkipTest(std::shared_ptr<TlsRecordFilter> filter, int32_t error) {
     EnsureTlsSetup();
     server_->SetTlsRecordFilter(filter);
-    filter->EnableDecryption();
-    client_->ExpectSendAlert(kTlsAlertUnexpectedMessage);
-    if (variant_ == ssl_variant_stream) {
-      server_->ExpectSendAlert(kTlsAlertBadRecordMac);
-      ConnectExpectFail();
-    } else {
-      ConnectExpectFailOneSide(TlsAgent::CLIENT);
-    }
+    ExpectAlert(client_, kTlsAlertUnexpectedMessage);
+    ConnectExpectFail();
     client_->CheckErrorCode(error);
-    if (variant_ == ssl_variant_stream) {
-      server_->CheckErrorCode(SSL_ERROR_BAD_MAC_READ);
-    } else {
-      ASSERT_EQ(TlsAgent::STATE_CONNECTING, server_->state());
-    }
+    server_->CheckErrorCode(SSL_ERROR_HANDSHAKE_UNEXPECTED_ALERT);
   }
 
   void ClientSkipTest(std::shared_ptr<TlsRecordFilter> filter, int32_t error) {
     EnsureTlsSetup();
     client_->SetTlsRecordFilter(filter);
-    filter->EnableDecryption();
     server_->ExpectSendAlert(kTlsAlertUnexpectedMessage);
     ConnectExpectFailOneSide(TlsAgent::SERVER);
 
     server_->CheckErrorCode(error);
     ASSERT_EQ(TlsAgent::STATE_CONNECTED, client_->state());
 
     client_->Handshake();  // Make sure to consume the alert the server sends.
   }
@@ -166,21 +162,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(
new file mode 100644
--- /dev/null
+++ b/security/nss/gtests/ssl_gtest/ssl_tls13compat_unittest.cc
@@ -0,0 +1,337 @@
+/* -*- 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 <memory>
+#include <vector>
+#include "ssl.h"
+#include "sslerr.h"
+#include "sslproto.h"
+
+#include "gtest_utils.h"
+#include "tls_connect.h"
+#include "tls_filter.h"
+#include "tls_parser.h"
+
+namespace nss_test {
+
+class Tls13CompatTest : public TlsConnectStreamTls13 {
+ protected:
+  void EnableCompatMode() {
+    client_->SetOption(SSL_ENABLE_TLS13_COMPAT_MODE, PR_TRUE);
+  }
+
+  void InstallFilters() {
+    EnsureTlsSetup();
+    client_recorders_.Install(client_);
+    server_recorders_.Install(server_);
+  }
+
+  void CheckRecordVersions() {
+    ASSERT_EQ(SSL_LIBRARY_VERSION_TLS_1_0,
+              client_recorders_.records_->record(0).header.version());
+    CheckRecordsAreTls12("client", client_recorders_.records_, 1);
+    CheckRecordsAreTls12("server", server_recorders_.records_, 0);
+  }
+
+  void CheckHelloVersions() {
+    uint32_t ver;
+    ASSERT_TRUE(server_recorders_.hello_->buffer().Read(0, 2, &ver));
+    ASSERT_EQ(SSL_LIBRARY_VERSION_TLS_1_2, static_cast<uint16_t>(ver));
+    ASSERT_TRUE(client_recorders_.hello_->buffer().Read(0, 2, &ver));
+    ASSERT_EQ(SSL_LIBRARY_VERSION_TLS_1_2, static_cast<uint16_t>(ver));
+  }
+
+  void CheckForCCS(bool expected_client, bool expected_server) {
+    client_recorders_.CheckForCCS(expected_client);
+    server_recorders_.CheckForCCS(expected_server);
+  }
+
+  void CheckForRegularHandshake() {
+    CheckRecordVersions();
+    CheckHelloVersions();
+    EXPECT_EQ(0U, client_recorders_.session_id_length());
+    EXPECT_EQ(0U, server_recorders_.session_id_length());
+    CheckForCCS(false, false);
+  }
+
+  void CheckForCompatHandshake() {
+    CheckRecordVersions();
+    CheckHelloVersions();
+    EXPECT_EQ(32U, client_recorders_.session_id_length());
+    EXPECT_EQ(32U, server_recorders_.session_id_length());
+    CheckForCCS(true, true);
+  }
+
+ private:
+  struct Recorders {
+    Recorders()
+        : records_(new TlsRecordRecorder()),
+          hello_(new TlsInspectorRecordHandshakeMessage(std::set<uint8_t>(
+              {kTlsHandshakeClientHello, kTlsHandshakeServerHello}))) {}
+
+    uint8_t session_id_length() const {
+      // session_id is always after version (2) and random (32).
+      uint32_t len = 0;
+      EXPECT_TRUE(hello_->buffer().Read(2 + 32, 1, &len));
+      return static_cast<uint8_t>(len);
+    }
+
+    void CheckForCCS(bool expected) const {
+      EXPECT_LT(0U, records_->count());
+      for (size_t i = 0; i < records_->count(); ++i) {
+        // Only the second record can be a CCS.
+        bool expected_match = expected && (i == 1);
+        EXPECT_EQ(expected_match,
+                  kTlsChangeCipherSpecType ==
+                      records_->record(i).header.content_type());
+      }
+    }
+
+    void Install(std::shared_ptr<TlsAgent>& agent) {
+      agent->SetPacketFilter(std::make_shared<ChainedPacketFilter>(
+          ChainedPacketFilterInit({records_, hello_})));
+    }
+
+    std::shared_ptr<TlsRecordRecorder> records_;
+    std::shared_ptr<TlsInspectorRecordHandshakeMessage> hello_;
+  };
+
+  void CheckRecordsAreTls12(const std::string& agent,
+                            const std::shared_ptr<TlsRecordRecorder>& records,
+                            size_t start) {
+    EXPECT_LE(start, records->count());
+    for (size_t i = start; i < records->count(); ++i) {
+      EXPECT_EQ(SSL_LIBRARY_VERSION_TLS_1_2,
+                records->record(i).header.version())
+          << agent << ": record " << i << " has wrong version";
+    }
+  }
+
+  Recorders client_recorders_;
+  Recorders server_recorders_;
+};
+
+TEST_F(Tls13CompatTest, Disabled) {
+  InstallFilters();
+  Connect();
+  CheckForRegularHandshake();
+}
+
+TEST_F(Tls13CompatTest, Enabled) {
+  EnableCompatMode();
+  InstallFilters();
+  Connect();
+  CheckForCompatHandshake();
+}
+
+TEST_F(Tls13CompatTest, EnabledZeroRtt) {
+  SetupForZeroRtt();
+  EnableCompatMode();
+  InstallFilters();
+
+  client_->Set0RttEnabled(true);
+  server_->Set0RttEnabled(true);
+  ExpectResumption(RESUME_TICKET);
+  ZeroRttSendReceive(true, true);
+  CheckForCCS(true, true);
+  Handshake();
+  ExpectEarlyDataAccepted(true);
+  CheckConnected();
+
+  CheckForCompatHandshake();
+}
+
+TEST_F(Tls13CompatTest, EnabledHrr) {
+  EnableCompatMode();
+  InstallFilters();
+
+  // Force a HelloRetryRequest.  The server sends CCS immediately.
+  server_->ConfigNamedGroups({ssl_grp_ec_secp384r1});
+  client_->StartConnect();
+  server_->StartConnect();
+  client_->Handshake();
+  server_->Handshake();
+  CheckForCCS(false, true);
+
+  Handshake();
+  CheckConnected();
+  CheckForCompatHandshake();
+}
+
+TEST_F(Tls13CompatTest, EnabledStatelessHrr) {
+  EnableCompatMode();
+  InstallFilters();
+
+  // Force a HelloRetryRequest
+  server_->ConfigNamedGroups({ssl_grp_ec_secp384r1});
+  client_->StartConnect();
+  server_->StartConnect();
+  client_->Handshake();
+  server_->Handshake();
+  CheckForCCS(false, true);
+
+  // A new server should just work, but not send another CCS.
+  MakeNewServer();
+  InstallFilters();
+  server_->ConfigNamedGroups({ssl_grp_ec_secp384r1});
+
+  Handshake();
+  CheckConnected();
+  CheckForCompatHandshake();
+}
+
+TEST_F(Tls13CompatTest, EnabledHrrZeroRtt) {
+  SetupForZeroRtt();
+  EnableCompatMode();
+  InstallFilters();
+  server_->ConfigNamedGroups({ssl_grp_ec_secp384r1});
+
+  // With 0-RTT, the client sends CCS immediately.  With HRR, the server sends
+  // CCS immediately too.
+  client_->Set0RttEnabled(true);
+  server_->Set0RttEnabled(true);
+  ExpectResumption(RESUME_TICKET);
+  ZeroRttSendReceive(true, false);
+  CheckForCCS(true, true);
+
+  Handshake();
+  ExpectEarlyDataAccepted(false);
+  CheckConnected();
+  CheckForCompatHandshake();
+}
+
+static const uint8_t kCannedCcs[] = {
+    kTlsChangeCipherSpecType,
+    SSL_LIBRARY_VERSION_TLS_1_2 >> 8,
+    SSL_LIBRARY_VERSION_TLS_1_2 & 0xff,
+    0,
+    1,  // length
+    1   // change_cipher_spec_choice
+};
+
+// A ChangeCipherSpec is ignored by a server because we have to tolerate it for
+// compatibility mode.  That doesn't mean that we have to tolerate it
+// unconditionally.  If we negotiate 1.3, we expect to see a cookie extension.
+TEST_F(TlsConnectStreamTls13, ChangeCipherSpecBeforeClientHello13) {
+  EnsureTlsSetup();
+  server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_2,
+                           SSL_LIBRARY_VERSION_TLS_1_3);
+  client_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_2,
+                           SSL_LIBRARY_VERSION_TLS_1_3);
+  // Client sends CCS before starting the handshake.
+  client_->SendDirect(DataBuffer(kCannedCcs, sizeof(kCannedCcs)));
+  ConnectExpectAlert(server_, kTlsAlertUnexpectedMessage);
+  server_->CheckErrorCode(SSL_ERROR_RX_UNEXPECTED_CHANGE_CIPHER);
+  client_->CheckErrorCode(SSL_ERROR_HANDSHAKE_UNEXPECTED_ALERT);
+}
+
+// A ChangeCipherSpec is ignored by a server because we have to tolerate it for
+// compatibility mode.  That doesn't mean that we have to tolerate it
+// unconditionally.  If we negotiate 1.3, we expect to see a cookie extension.
+TEST_F(TlsConnectStreamTls13, ChangeCipherSpecBeforeClientHelloTwice) {
+  EnsureTlsSetup();
+  server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_2,
+                           SSL_LIBRARY_VERSION_TLS_1_3);
+  client_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_2,
+                           SSL_LIBRARY_VERSION_TLS_1_3);
+  // Client sends CCS before starting the handshake.
+  client_->SendDirect(DataBuffer(kCannedCcs, sizeof(kCannedCcs)));
+  client_->SendDirect(DataBuffer(kCannedCcs, sizeof(kCannedCcs)));
+  ConnectExpectAlert(server_, kTlsAlertUnexpectedMessage);
+  server_->CheckErrorCode(SSL_ERROR_RX_UNEXPECTED_CHANGE_CIPHER);
+  client_->CheckErrorCode(SSL_ERROR_HANDSHAKE_UNEXPECTED_ALERT);
+}
+
+// If we negotiate 1.2, we abort.
+TEST_F(TlsConnectStreamTls13, ChangeCipherSpecBeforeClientHello12) {
+  EnsureTlsSetup();
+  server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_2,
+                           SSL_LIBRARY_VERSION_TLS_1_3);
+  client_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_2,
+                           SSL_LIBRARY_VERSION_TLS_1_2);
+  // Client sends CCS before starting the handshake.
+  client_->SendDirect(DataBuffer(kCannedCcs, sizeof(kCannedCcs)));
+  ConnectExpectAlert(server_, kTlsAlertUnexpectedMessage);
+  server_->CheckErrorCode(SSL_ERROR_RX_UNEXPECTED_CHANGE_CIPHER);
+  client_->CheckErrorCode(SSL_ERROR_HANDSHAKE_UNEXPECTED_ALERT);
+}
+
+TEST_F(TlsConnectDatagram13, CompatModeDtlsClient) {
+  EnsureTlsSetup();
+  client_->SetOption(SSL_ENABLE_TLS13_COMPAT_MODE, PR_TRUE);
+  auto client_records = std::make_shared<TlsRecordRecorder>();
+  client_->SetPacketFilter(client_records);
+  auto server_records = std::make_shared<TlsRecordRecorder>();
+  server_->SetPacketFilter(server_records);
+  Connect();
+
+  ASSERT_EQ(2U, client_records->count());  // CH, Fin
+  EXPECT_EQ(kTlsHandshakeType, client_records->record(0).header.content_type());
+  EXPECT_EQ(kTlsApplicationDataType,
+            client_records->record(1).header.content_type());
+
+  ASSERT_EQ(6U, server_records->count());  // SH, EE, CT, CV, Fin, Ack
+  EXPECT_EQ(kTlsHandshakeType, server_records->record(0).header.content_type());
+  for (size_t i = 1; i < server_records->count(); ++i) {
+    EXPECT_EQ(kTlsApplicationDataType,
+              server_records->record(i).header.content_type());
+  }
+}
+
+class AddSessionIdFilter : public TlsHandshakeFilter {
+ public:
+  AddSessionIdFilter() : TlsHandshakeFilter({ssl_hs_client_hello}) {}
+
+ protected:
+  PacketFilter::Action FilterHandshake(const HandshakeHeader& header,
+                                       const DataBuffer& input,
+                                       DataBuffer* output) override {
+    uint32_t session_id_len = 0;
+    EXPECT_TRUE(input.Read(2 + 32, 1, &session_id_len));
+    EXPECT_EQ(0U, session_id_len);
+    uint8_t session_id[33] = {32};  // 32 for length, the rest zero.
+    *output = input;
+    output->Splice(session_id, sizeof(session_id), 34, 1);
+    return CHANGE;
+  }
+};
+
+// Adding a session ID to a DTLS ClientHello should not trigger compatibility
+// mode.  It should be ignored instead.
+TEST_F(TlsConnectDatagram13, CompatModeDtlsServer) {
+  EnsureTlsSetup();
+  auto client_records = std::make_shared<TlsRecordRecorder>();
+  client_->SetPacketFilter(
+      std::make_shared<ChainedPacketFilter>(ChainedPacketFilterInit(
+          {client_records, std::make_shared<AddSessionIdFilter>()})));
+  auto server_hello = std::make_shared<TlsInspectorRecordHandshakeMessage>(
+      kTlsHandshakeServerHello);
+  auto server_records = std::make_shared<TlsRecordRecorder>();
+  server_->SetPacketFilter(std::make_shared<ChainedPacketFilter>(
+      ChainedPacketFilterInit({server_records, server_hello})));
+  StartConnect();
+  client_->Handshake();
+  server_->Handshake();
+  // The client will consume the ServerHello, but discard everything else
+  // because it doesn't decrypt.  And don't wait around for the client to ACK.
+  client_->Handshake();
+
+  ASSERT_EQ(1U, client_records->count());
+  EXPECT_EQ(kTlsHandshakeType, client_records->record(0).header.content_type());
+
+  ASSERT_EQ(5U, server_records->count());  // SH, EE, CT, CV, Fin
+  EXPECT_EQ(kTlsHandshakeType, server_records->record(0).header.content_type());
+  for (size_t i = 1; i < server_records->count(); ++i) {
+    EXPECT_EQ(kTlsApplicationDataType,
+              server_records->record(i).header.content_type());
+  }
+
+  uint32_t session_id_len = 0;
+  EXPECT_TRUE(server_hello->buffer().Read(2 + 32, 1, &session_id_len));
+  EXPECT_EQ(0U, session_id_len);
+}
+
+}  // nss_test
--- a/security/nss/gtests/ssl_gtest/ssl_version_unittest.cc
+++ b/security/nss/gtests/ssl_gtest/ssl_version_unittest.cc
@@ -153,18 +153,17 @@ TEST_F(TlsConnectTest, DisallowSSLv3Hell
 
   rv = SSL_VersionRangeSet(server_->ssl_fd(), &vrange);
   EXPECT_EQ(SECFailure, rv);
 }
 
 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);
@@ -213,25 +212,25 @@ TEST_F(Tls13NoSupportedVersions,
 }
 
 // Offer 1.3 but with ClientHello.legacy_version == TLS 1.4. This
 // causes a bad MAC error when we read EncryptedExtensions.
 TEST_F(TlsConnectStreamTls13, Tls14ClientHelloWithSupportedVersions) {
   client_->SetPacketFilter(
       std::make_shared<TlsInspectorClientHelloVersionSetter>(
           SSL_LIBRARY_VERSION_TLS_1_3 + 1));
-  auto capture = std::make_shared<TlsInspectorRecordHandshakeMessage>(
-      kTlsHandshakeServerHello);
+  auto capture =
+      std::make_shared<TlsExtensionCapture>(ssl_tls13_supported_versions_xtn);
   server_->SetPacketFilter(capture);
   client_->ExpectSendAlert(kTlsAlertBadRecordMac);
   server_->ExpectSendAlert(kTlsAlertBadRecordMac);
   ConnectExpectFail();
   client_->CheckErrorCode(SSL_ERROR_BAD_MAC_READ);
   server_->CheckErrorCode(SSL_ERROR_BAD_MAC_READ);
-  const DataBuffer& server_hello = capture->buffer();
-  ASSERT_GT(server_hello.len(), 2U);
-  uint32_t ver;
-  ASSERT_TRUE(server_hello.Read(0, 2, &ver));
+
+  ASSERT_EQ(2U, capture->extension().len());
+  uint32_t version = 0;
+  ASSERT_TRUE(capture->extension().Read(0, 2, &version));
   // This way we don't need to change with new draft version.
-  ASSERT_LT(static_cast<uint32_t>(SSL_LIBRARY_VERSION_TLS_1_2), ver);
+  ASSERT_LT(static_cast<uint32_t>(SSL_LIBRARY_VERSION_TLS_1_2), version);
 }
 
 }  // namespace nss_test
--- a/security/nss/gtests/ssl_gtest/test_io.cc
+++ b/security/nss/gtests/ssl_gtest/test_io.cc
@@ -93,28 +93,33 @@ int32_t DummyPrSocket::Recv(PRFileDesc *
   size_t count = front.len();
   memcpy(buf, front.data(), count);
 
   input_.pop();
   return static_cast<int32_t>(count);
 }
 
 int32_t DummyPrSocket::Write(PRFileDesc *f, const void *buf, int32_t length) {
+  if (write_error_) {
+    PR_SetError(write_error_, 0);
+    return -1;
+  }
+
   auto peer = peer_.lock();
-  if (!peer || !writeable_) {
+  if (!peer) {
     PR_SetError(PR_IO_ERROR, 0);
     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/security/nss/gtests/ssl_gtest/test_io.h
+++ b/security/nss/gtests/ssl_gtest/test_io.h
@@ -28,37 +28,49 @@ 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_(),
         input_(),
         filter_(nullptr),
-        writeable_(true) {}
+        write_error_(0) {}
   virtual ~DummyPrSocket() {}
 
   // Create a file descriptor that will reference this object.  The fd must not
   // live longer than this adapter; call PR_Close() before.
   ScopedPRFileDesc CreateFD();
 
   std::weak_ptr<DummyPrSocket>& peer() { return peer_; }
   void SetPeer(const std::shared_ptr<DummyPrSocket>& peer) { peer_ = peer; }
@@ -66,17 +78,17 @@ class DummyPrSocket : public DummyIOLaye
   // Drops peer, packet filter and any outstanding packets.
   void Reset();
 
   void PacketReceived(const DataBuffer& data);
   int32_t Read(PRFileDesc* f, void* data, int32_t len) override;
   int32_t Recv(PRFileDesc* f, void* buf, int32_t buflen, int32_t flags,
                PRIntervalTime to) override;
   int32_t Write(PRFileDesc* f, const void* buf, int32_t length) override;
-  void CloseWrites() { writeable_ = false; }
+  void SetWriteError(PRErrorCode code) { write_error_ = code; }
 
   SSLProtocolVariant variant() const { return variant_; }
   bool readable() const { return !input_.empty(); }
 
  private:
   class Packet : public DataBuffer {
    public:
     Packet(const DataBuffer& buf) : DataBuffer(buf), offset_(0) {}
@@ -93,17 +105,17 @@ class DummyPrSocket : public DummyIOLaye
     size_t offset_;
   };
 
   const std::string name_;
   SSLProtocolVariant variant_;
   std::weak_ptr<DummyPrSocket> peer_;
   std::queue<Packet> input_;
   std::shared_ptr<PacketFilter> filter_;
-  bool writeable_;
+  PRErrorCode write_error_;
 };
 
 // Marker interface.
 class PollTarget {};
 
 enum Event { READABLE_EVENT, TIMER_EVENT /* Must be last */ };
 
 typedef void (*PollCallback)(PollTarget*, Event);
--- a/security/nss/gtests/ssl_gtest/tls_agent.cc
+++ b/security/nss/gtests/ssl_gtest/tls_agent.cc
@@ -68,17 +68,16 @@ TlsAgent::TlsAgent(const std::string& na
       handshake_callback_called_(false),
       error_code_(0),
       send_ctr_(0),
       recv_ctr_(0),
       expect_readwrite_error_(false),
       handshake_callback_(),
       auth_certificate_callback_(),
       sni_callback_(),
-      expect_short_headers_(false),
       skip_version_checks_(false) {
   memset(&info_, 0, sizeof(info_));
   memset(&csinfo_, 0, sizeof(csinfo_));
   SECStatus rv = SSL_VersionRangeGetDefault(variant_, &vrange_);
   EXPECT_EQ(SECSuccess, rv);
 }
 
 TlsAgent::~TlsAgent() {
@@ -88,21 +87,21 @@ TlsAgent::~TlsAgent() {
 
   if (adapter_) {
     Poller::Instance()->Cancel(READABLE_EVENT, adapter_);
   }
 
   // Add failures manually, if any, so we don't throw in a destructor.
   if (expected_received_alert_ != kTlsAlertCloseNotify ||
       expected_received_alert_level_ != kTlsAlertWarning) {
-    ADD_FAILURE() << "Wrong expected_received_alert status";
+    ADD_FAILURE() << "Wrong expected_received_alert status: " << role_str();
   }
   if (expected_sent_alert_ != kTlsAlertCloseNotify ||
       expected_sent_alert_level_ != kTlsAlertWarning) {
-    ADD_FAILURE() << "Wrong expected_sent_alert status";
+    ADD_FAILURE() << "Wrong expected_sent_alert status: " << role_str();
   }
 }
 
 void TlsAgent::SetState(State state) {
   if (state_ == state) return;
 
   LOG("Changing state from " << state_ << " to " << state);
   state_ = state;
@@ -372,30 +371,16 @@ void TlsAgent::ConfigNamedGroups(const s
   SECStatus rv = SSL_NamedGroupConfig(ssl_fd(), &groups[0], groups.size());
   EXPECT_EQ(SECSuccess, rv);
 }
 
 void TlsAgent::Set0RttEnabled(bool en) {
   SetOption(SSL_ENABLE_0RTT_DATA, en ? PR_TRUE : PR_FALSE);
 }
 
-void TlsAgent::SetShortHeadersEnabled() {
-  EXPECT_TRUE(EnsureTlsSetup());
-
-  SECStatus rv = SSLInt_EnableShortHeaders(ssl_fd());
-  EXPECT_EQ(SECSuccess, rv);
-}
-
-void TlsAgent::SetAltHandshakeTypeEnabled() {
-  EXPECT_TRUE(EnsureTlsSetup());
-
-  SECStatus rv = SSL_UseAltServerHelloType(ssl_fd(), PR_TRUE);
-  EXPECT_EQ(SECSuccess, rv);
-}
-
 void TlsAgent::SetVersionRange(uint16_t minver, uint16_t maxver) {
   vrange_.min = minver;
   vrange_.max = maxver;
 
   if (ssl_fd()) {
     SECStatus rv = SSL_VersionRangeSet(ssl_fd(), &vrange_);
     EXPECT_EQ(SECSuccess, rv);
   }
@@ -409,18 +394,16 @@ void TlsAgent::GetVersionRange(uint16_t*
 void TlsAgent::SetExpectedVersion(uint16_t version) {
   expected_version_ = version;
 }
 
 void TlsAgent::SetServerKeyBits(uint16_t bits) { server_key_bits_ = bits; }
 
 void TlsAgent::ExpectReadWriteError() { expect_readwrite_error_ = true; }
 
-void TlsAgent::ExpectShortHeaders() { expect_short_headers_ = true; }
-
 void TlsAgent::SkipVersionChecks() { skip_version_checks_ = true; }
 
 void TlsAgent::SetSignatureSchemes(const SSLSignatureScheme* schemes,
                                    size_t count) {
   EXPECT_TRUE(EnsureTlsSetup());
   EXPECT_LE(count, SSL_SignatureMaxCount());
   EXPECT_EQ(SECSuccess,
             SSL_SignatureSchemePrefSet(ssl_fd(), schemes,
@@ -599,22 +582,18 @@ void TlsAgent::CheckSrtp() const {
 void TlsAgent::CheckErrorCode(int32_t expected) const {
   EXPECT_EQ(STATE_ERROR, state_);
   EXPECT_EQ(expected, error_code_)
       << "Got error code " << PORT_ErrorToName(error_code_) << " expecting "
       << PORT_ErrorToName(expected) << std::endl;
 }
 
 static uint8_t GetExpectedAlertLevel(uint8_t alert) {
-  switch (alert) {
-    case kTlsAlertCloseNotify:
-    case kTlsAlertEndOfEarlyData:
-      return kTlsAlertWarning;
-    default:
-      break;
+  if (alert == kTlsAlertCloseNotify) {
+    return kTlsAlertWarning;
   }
   return kTlsAlertFatal;
 }
 
 void TlsAgent::ExpectReceiveAlert(uint8_t alert, uint8_t level) {
   expected_received_alert_ = alert;
   if (level == 0) {
     expected_received_alert_level_ = GetExpectedAlertLevel(alert);
@@ -707,16 +686,60 @@ void TlsAgent::CheckCallbacks() const {
   }
 }
 
 void TlsAgent::ResetPreliminaryInfo() {
   expected_version_ = 0;
   expected_cipher_suite_ = 0;
 }
 
+void TlsAgent::ValidateCipherSpecs() {
+  PRInt32 cipherSpecs = SSLInt_CountCipherSpecs(ssl_fd());
+  // We use one ciphersuite in each direction.
+  PRInt32 expected = 2;
+  if (variant_ == ssl_variant_datagram) {
+    // For DTLS 1.3, the client retains the cipher spec for early data and the
+    // handshake so that it can retransmit EndOfEarlyData and its final flight.
+    // 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 (expected_version_ >= SSL_LIBRARY_VERSION_TLS_1_3) {
+      if (role_ == CLIENT) {
+        expected = info_.earlyDataAccepted ? 5 : 4;
+      } else {
+        expected = 3;
+      }
+    } else {
+      // For DTLS 1.1 and 1.2, the last endpoint to send maintains a cipher spec
+      // until the holddown timer runs down.
+      if (expect_resumption_) {
+        if (role_ == CLIENT) {
+          expected = 3;
+        }
+      } else {
+        if (role_ == SERVER) {
+          expected = 3;
+        }
+      }
+    }
+  }
+  // This function will be run before the handshake completes if false start is
+  // enabled.  In that case, the client will still be reading cleartext, but
+  // will have a spec prepared for reading ciphertext.  With DTLS, the client
+  // will also have a spec retained for retransmission of handshake messages.
+  if (role_ == CLIENT && falsestart_enabled_ && !handshake_callback_called_) {
+    EXPECT_GT(SSL_LIBRARY_VERSION_TLS_1_3, expected_version_);
+    expected = (variant_ == ssl_variant_datagram) ? 4 : 3;
+  }
+  EXPECT_EQ(expected, cipherSpecs);
+  if (expected != cipherSpecs) {
+    SSLInt_PrintCipherSpecs(role_str().c_str(), ssl_fd());
+  }
+}
+
 void TlsAgent::Connected() {
   if (state_ == STATE_CONNECTED) {
     return;
   }
 
   LOG("Handshake success");
   CheckPreliminaryInfo();
   CheckCallbacks();
@@ -732,32 +755,18 @@ void TlsAgent::Connected() {
   // that the final values are correct.
   EXPECT_EQ(expected_version_, info_.protocolVersion);
   EXPECT_EQ(expected_cipher_suite_, info_.cipherSuite);
 
   rv = SSL_GetCipherSuiteInfo(info_.cipherSuite, &csinfo_, sizeof(csinfo_));
   EXPECT_EQ(SECSuccess, rv);
   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, plus one that's kept around
-    // by DTLS for retransmission.
-    PRInt32 expected =
-        ((variant_ == ssl_variant_datagram) && (role_ == CLIENT)) ? 3 : 2;
-    EXPECT_EQ(expected, cipherSuites);
-    if (expected != cipherSuites) {
-      SSLInt_PrintTls13CipherSpecs(ssl_fd());
-    }
-  }
+  ValidateCipherSpecs();
 
-  PRBool short_headers;
-  rv = SSLInt_UsingShortHeaders(ssl_fd(), &short_headers);
-  EXPECT_EQ(SECSuccess, rv);
-  EXPECT_EQ((PRBool)expect_short_headers_, short_headers);
   SetState(STATE_CONNECTED);
 }
 
 void TlsAgent::EnableExtendedMasterSecret() {
   SetOption(SSL_ENABLE_EXTENDED_MASTER_SECRET, PR_TRUE);
 }
 
 void TlsAgent::CheckExtendedMasterSecret(bool expected) {
@@ -842,16 +851,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));
@@ -877,16 +894,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/security/nss/gtests/ssl_gtest/tls_agent.h
+++ b/security/nss/gtests/ssl_gtest/tls_agent.h
@@ -75,19 +75,21 @@ class TlsAgent : public PollTarget {
 
   TlsAgent(const std::string& name, Role role, SSLProtocolVariant variant);
   virtual ~TlsAgent();
 
   void SetPeer(std::shared_ptr<TlsAgent>& peer) {
     adapter_->SetPeer(peer->adapter_);
   }
 
+  // Set a filter that can access plaintext (TLS 1.3 only).
   void SetTlsRecordFilter(std::shared_ptr<TlsRecordFilter> filter) {
     filter->SetAgent(this);
     adapter_->SetPacketFilter(filter);
+    filter->EnableDecryption();
   }
 
   void SetPacketFilter(std::shared_ptr<PacketFilter> filter) {
     adapter_->SetPacketFilter(filter);
   }
 
   void DeletePacketFilter() { adapter_->SetPacketFilter(nullptr); }
 
@@ -120,42 +122,43 @@ class TlsAgent : public PollTarget {
 
   void SetupClientAuth();
   void RequestClientAuth(bool requireAuth);
 
   void SetOption(int32_t option, int value);
   void ConfigureSessionCache(SessionResumptionMode mode);
   void Set0RttEnabled(bool en);
   void SetFallbackSCSVEnabled(bool en);
-  void SetShortHeadersEnabled();
-  void SetAltHandshakeTypeEnabled();
   void SetVersionRange(uint16_t minver, uint16_t maxver);
   void GetVersionRange(uint16_t* minver, uint16_t* maxver);
   void CheckPreliminaryInfo();
   void ResetPreliminaryInfo();
   void SetExpectedVersion(uint16_t version);
   void SetServerKeyBits(uint16_t bits);
   void ExpectReadWriteError();
   void EnableFalseStart();
   void ExpectResumption();
-  void ExpectShortHeaders();
   void SkipVersionChecks();
   void SetSignatureSchemes(const SSLSignatureScheme* schemes, size_t count);
   void EnableAlpn(const uint8_t* val, size_t len);
   void CheckAlpn(SSLNextProtoState expected_state,
                  const std::string& expected = "") const;
   void EnableSrtp();
   void CheckSrtp() const;
   void CheckErrorCode(int32_t expected) const;
   void WaitForErrorCode(int32_t expected, uint32_t delay) const;
   // 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 SetDowngradeCheckVersion(uint16_t version);
   void CheckSecretsDestroyed();
   void ConfigNamedGroups(const std::vector<SSLNamedGroup>& groups);
@@ -163,16 +166,18 @@ class TlsAgent : public PollTarget {
   bool GetPeerChainLength(size_t* count);
   void CheckCipherSuite(uint16_t cipher_suite);
 
   const std::string& name() const { return name_; }
 
   Role role() const { return role_; }
   std::string role_str() const { return role_ == SERVER ? "server" : "client"; }
 
+  SSLProtocolVariant variant() const { return variant_; }
+
   State state() const { return state_; }
 
   const CERTCertificate* peer_cert() const {
     return SSL_PeerCertificate(ssl_fd_.get());
   }
 
   const char* state_str() const { return state_str(state()); }
 
@@ -246,16 +251,17 @@ class TlsAgent : public PollTarget {
 
   void ExpectReceiveAlert(uint8_t alert, uint8_t level = 0);
   void ExpectSendAlert(uint8_t alert, uint8_t level = 0);
 
  private:
   const static char* states[];
 
   void SetState(State state);
+  void ValidateCipherSpecs();
 
   // Dummy auth certificate hook.
   static SECStatus AuthCertificateHook(void* arg, PRFileDesc* fd,
                                        PRBool checksig, PRBool isServer) {
     TlsAgent* agent = reinterpret_cast<TlsAgent*>(arg);
     agent->CheckPreliminaryInfo();
     agent->auth_certificate_hook_called_ = true;
     if (agent->auth_certificate_callback_) {
@@ -381,17 +387,16 @@ class TlsAgent : public PollTarget {
   SSLVersionRange vrange_;
   PRErrorCode error_code_;
   size_t send_ctr_;
   size_t recv_ctr_;
   bool expect_readwrite_error_;
   HandshakeCallbackFunction handshake_callback_;
   AuthCertificateCallbackFunction auth_certificate_callback_;
   SniCallbackFunction sni_callback_;
-  bool expect_short_headers_;
   bool skip_version_checks_;
 };
 
 inline std::ostream& operator<<(std::ostream& stream,
                                 const TlsAgent::State& state) {
   return stream << TlsAgent::state_str(state);
 }
 
--- a/security/nss/gtests/ssl_gtest/tls_connect.cc
+++ b/security/nss/gtests/ssl_gtest/tls_connect.cc
@@ -84,16 +84,18 @@ static const uint16_t kTlsVAllArr[] = {
     SSL_LIBRARY_VERSION_TLS_1_0};
 ::testing::internal::ParamGenerator<uint16_t> TlsConnectTestBase::kTlsVAll =
     ::testing::ValuesIn(kTlsVAllArr);
 
 std::string VersionString(uint16_t version) {
   switch (version) {
     case 0:
       return "(no version)";
+    case SSL_LIBRARY_VERSION_3_0:
+      return "1.0";
     case SSL_LIBRARY_VERSION_TLS_1_0:
       return "1.0";
     case SSL_LIBRARY_VERSION_TLS_1_1:
       return "1.1";
     case SSL_LIBRARY_VERSION_TLS_1_2:
       return "1.2";
     case SSL_LIBRARY_VERSION_TLS_1_3:
       return "1.3";
@@ -175,16 +177,17 @@ void TlsConnectTestBase::ClearServerCach
   SSL_ConfigServerSessionIDCache(1024, 0, 0, g_working_dir_path.c_str());
 }
 
 void TlsConnectTestBase::SetUp() {
   SSL_ConfigServerSessionIDCache(1024, 0, 0, g_working_dir_path.c_str());
   SSLInt_ClearSelfEncryptKey();
   SSLInt_SetTicketLifetime(30);
   SSLInt_SetMaxEarlyDataSize(1024);
+  SSL_SetupAntiReplay(1 * PR_USEC_PER_SEC, 1, 3);
   ClearStats();
   Init();
 }
 
 void TlsConnectTestBase::TearDown() {
   client_ = nullptr;
   server_ = nullptr;
 
@@ -216,16 +219,28 @@ void TlsConnectTestBase::Reset(const std
   if (skip_version_checks_) {
     client_->SkipVersionChecks();
     server_->SkipVersionChecks();
   }
 
   Init();
 }
 
+void TlsConnectTestBase::MakeNewServer() {
+  auto replacement = std::make_shared<TlsAgent>(
+      server_->name(), TlsAgent::SERVER, server_->variant());
+  server_ = replacement;
+  if (version_) {
+    server_->SetVersionRange(version_, version_);
+  }
+  client_->SetPeer(server_);
+  server_->SetPeer(client_);
+  server_->StartConnect();
+}
+
 void TlsConnectTestBase::ExpectResumption(SessionResumptionMode expected,
                                           uint8_t num_resumptions) {
   expected_resumption_mode_ = expected;
   if (expected != RESUME_NONE) {
     client_->ExpectResumption();
     server_->ExpectResumption();
     expected_resumptions_ = num_resumptions;
   }
@@ -258,32 +273,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_CountCipherSpecs(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_PrintCipherSpecs("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());
@@ -386,18 +419,17 @@ void TlsConnectTestBase::CheckKeysResump
                                              SSLSignatureScheme sig_scheme) {
   CheckKeys(kea_type, kea_group, auth_type, sig_scheme);
   EXPECT_TRUE(expected_resumption_mode_ != RESUME_NONE);
   client_->CheckOriginalKEA(original_kea_group);
   server_->CheckOriginalKEA(original_kea_group);
 }
 
 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();
@@ -408,18 +440,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_;
   }
@@ -468,31 +499,35 @@ void TlsConnectTestBase::EnableSomeEcdhC
     server_->EnableCiphersByAuthType(ssl_auth_ecdh_rsa);
     server_->EnableCiphersByAuthType(ssl_auth_ecdh_ecdsa);
   } else {
     client_->ConfigNamedGroups(kECDHEGroups);
     server_->ConfigNamedGroups(kECDHEGroups);
   }
 }
 
+void TlsConnectTestBase::ConfigureSelfEncrypt() {
+  ScopedCERTCertificate cert;
+  ScopedSECKEYPrivateKey privKey;
+  ASSERT_TRUE(
+      TlsAgent::LoadCertificate(TlsAgent::kServerRsaDecrypt, &cert, &privKey));
+
+  ScopedSECKEYPublicKey pubKey(CERT_ExtractPublicKey(cert.get()));
+  ASSERT_TRUE(pubKey);
+
+  EXPECT_EQ(SECSuccess,
+            SSL_SetSessionTicketKeyPair(pubKey.get(), privKey.get()));
+}
+
 void TlsConnectTestBase::ConfigureSessionCache(SessionResumptionMode client,
                                                SessionResumptionMode server) {
   client_->ConfigureSessionCache(client);
   server_->ConfigureSessionCache(server);
   if ((server & RESUME_TICKET) != 0) {
-    ScopedCERTCertificate cert;
-    ScopedSECKEYPrivateKey privKey;
-    ASSERT_TRUE(TlsAgent::LoadCertificate(TlsAgent::kServerRsaDecrypt, &cert,
-                                          &privKey));
-
-    ScopedSECKEYPublicKey pubKey(CERT_ExtractPublicKey(cert.get()));
-    ASSERT_TRUE(pubKey);
-
-    EXPECT_EQ(SECSuccess,
-              SSL_SetSessionTicketKeyPair(pubKey.get(), privKey.get()));
+    ConfigureSelfEncrypt();
   }
 }
 
 void TlsConnectTestBase::CheckResumption(SessionResumptionMode expected) {
   EXPECT_NE(RESUME_BOTH, expected);
 
   int resume_count = expected ? expected_resumptions_ : 0;
   int stateless_count = (expected & RESUME_TICKET) ? expected_resumptions_ : 0;
@@ -561,33 +596,28 @@ void TlsConnectTestBase::CheckSrtp() con
 void TlsConnectTestBase::SendReceive() {
   client_->SendData(50);
   server_->SendData(50);
   Receive(50);
 }
 
 // Do a first connection so we can do 0-RTT on the second one.
 void TlsConnectTestBase::SetupForZeroRtt() {
+  // If we don't do this, then all 0-RTT attempts will be rejected.
+  SSLInt_RolloverAntiReplay();
+
   ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
-  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);
+  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();
-  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();
 }
 
 // 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.
@@ -597,32 +627,28 @@ void TlsConnectTestBase::SetupForResume(
 }
 
 void TlsConnectTestBase::ZeroRttSendReceive(
     bool expect_writable, bool expect_readable,
     std::function<bool()> post_clienthello_check) {
   const char* k0RttData = "ABCDEF";
   const PRInt32 k0RttDataLen = static_cast<PRInt32>(strlen(k0RttData));
 
-  if (expect_writable && expect_readable) {
-    ExpectAlert(client_, kTlsAlertEndOfEarlyData);
-  }
-
   client_->Handshake();  // Send ClientHello.
   if (post_clienthello_check) {
     if (!post_clienthello_check()) return;
   }
   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);
@@ -666,16 +692,40 @@ void TlsConnectTestBase::DisableECDHESer
 }
 
 void TlsConnectTestBase::SkipVersionChecks() {
   skip_version_checks_ = true;
   client_->SkipVersionChecks();
   server_->SkipVersionChecks();
 }
 
+// Shift the DTLS timers, to the minimum time necessary to let the next timer
+// run on either client or server.  This allows tests to skip waiting without
+// having timers run out of order.
+void TlsConnectTestBase::ShiftDtlsTimers() {
+  PRIntervalTime time_shift = PR_INTERVAL_NO_TIMEOUT;
+  PRIntervalTime time;
+  SECStatus rv = DTLS_GetHandshakeTimeout(client_->ssl_fd(), &time);
+  if (rv == SECSuccess) {
+    time_shift = time;
+  }
+  rv = DTLS_GetHandshakeTimeout(server_->ssl_fd(), &time);
+  if (rv == SECSuccess &&
+      (time < time_shift || time_shift == PR_INTERVAL_NO_TIMEOUT)) {
+    time_shift = time;
+  }
+
+  if (time_shift == PR_INTERVAL_NO_TIMEOUT) {
+    return;
+  }
+
+  EXPECT_EQ(SECSuccess, SSLInt_ShiftDtlsTimers(client_->ssl_fd(), time_shift));
+  EXPECT_EQ(SECSuccess, SSLInt_ShiftDtlsTimers(server_->ssl_fd(), time_shift));
+}
+
 TlsConnectGeneric::TlsConnectGeneric()
     : TlsConnectTestBase(std::get<0>(GetParam()), std::get<1>(GetParam())) {}
 
 TlsConnectPre12::TlsConnectPre12()
     : TlsConnectTestBase(std::get<0>(GetParam()), std::get<1>(GetParam())) {}
 
 TlsConnectTls12::TlsConnectTls12()
     : TlsConnectTestBase(GetParam(), SSL_LIBRARY_VERSION_TLS_1_2) {}
@@ -704,60 +754,66 @@ void TlsKeyExchangeTest::EnsureKeyShareS
 
 void TlsKeyExchangeTest::ConfigNamedGroups(
     const std::vector<SSLNamedGroup>& groups) {
   client_->ConfigNamedGroups(groups);
   server_->ConfigNamedGroups(groups);
 }
 
 std::vector<SSLNamedGroup> TlsKeyExchangeTest::GetGroupDetails(
-    const DataBuffer& ext) {
+    const std::shared_ptr<TlsExtensionCapture>& capture) {
+  EXPECT_TRUE(capture->captured());
+  const DataBuffer& ext = capture->extension();
+
   uint32_t tmp = 0;
   EXPECT_TRUE(ext.Read(0, 2, &tmp));
   EXPECT_EQ(ext.len() - 2, static_cast<size_t>(tmp));
   EXPECT_TRUE(ext.len() % 2 == 0);
+
   std::vector<SSLNamedGroup> groups;
   for (size_t i = 1; i < ext.len() / 2; i += 1) {
     EXPECT_TRUE(ext.Read(2 * i, 2, &tmp));
     groups.push_back(static_cast<SSLNamedGroup>(tmp));
   }
   return groups;
 }
 
 std::vector<SSLNamedGroup> TlsKeyExchangeTest::GetShareDetails(
-    const DataBuffer& ext) {
+    const std::shared_ptr<TlsExtensionCapture>& capture) {
+  EXPECT_TRUE(capture->captured());
+  const DataBuffer& ext = capture->extension();
+
   uint32_t tmp = 0;
   EXPECT_TRUE(ext.Read(0, 2, &tmp));
   EXPECT_EQ(ext.len() - 2, static_cast<size_t>(tmp));
+
   std::vector<SSLNamedGroup> shares;
   size_t i = 2;
   while (i < ext.len()) {
     EXPECT_TRUE(ext.Read(i, 2, &tmp));
     shares.push_back(static_cast<SSLNamedGroup>(tmp));
     EXPECT_TRUE(ext.Read(i + 2, 2, &tmp));
     i += 4 + tmp;
   }
   EXPECT_EQ(ext.len(), i);
   return shares;
 }
 
 void TlsKeyExchangeTest::CheckKEXDetails(
     const std::vector<SSLNamedGroup>& expected_groups,
     const std::vector<SSLNamedGroup>& expected_shares, bool expect_hrr) {
-  std::vector<SSLNamedGroup> groups =
-      GetGroupDetails(groups_capture_->extension());
+  std::vector<SSLNamedGroup> groups = GetGroupDetails(groups_capture_);
   EXPECT_EQ(expected_groups, groups);
 
   if (version_ >= SSL_LIBRARY_VERSION_TLS_1_3) {
     ASSERT_LT(0U, expected_shares.size());
-    std::vector<SSLNamedGroup> shares =
-        GetShareDetails(shares_capture_->extension());
+    std::vector<SSLNamedGroup> shares = GetShareDetails(shares_capture_);
     EXPECT_EQ(expected_shares, shares);
   } else {
-    EXPECT_EQ(0U, shares_capture_->extension().len());
+    EXPECT_FALSE(shares_capture_->captured());
   }
 
   EXPECT_EQ(expect_hrr, capture_hrr_->buffer().len() != 0);
 }
 
 void TlsKeyExchangeTest::CheckKEXDetails(
     const std::vector<SSLNamedGroup>& expected_groups,
     const std::vector<SSLNamedGroup>& expected_shares) {
@@ -769,13 +825,11 @@ void TlsKeyExchangeTest::CheckKEXDetails
     const std::vector<SSLNamedGroup>& expected_shares,
     SSLNamedGroup expected_share2) {
   CheckKEXDetails(expected_groups, expected_shares, true);
 
   for (auto it : expected_shares) {
     EXPECT_NE(expected_share2, it);
   }
   std::vector<SSLNamedGroup> expected_shares2 = {expected_share2};
-  std::vector<SSLNamedGroup> shares =
-      GetShareDetails(shares_capture2_->extension());
-  EXPECT_EQ(expected_shares2, shares);
+  EXPECT_EQ(expected_shares2, GetShareDetails(shares_capture2_));
 }
 }  // namespace nss_test
--- a/security/nss/gtests/ssl_gtest/tls_connect.h
+++ b/security/nss/gtests/ssl_gtest/tls_connect.h
@@ -56,17 +56,21 @@ class TlsConnectTestBase : public ::test
   void ClearServerCache();
   // 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");
+  // Replace the server.
+  void MakeNewServer();
 
+  // 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();
@@ -96,16 +100,17 @@ class TlsConnectTestBase : public ::test
   // Expect resumption of a particular type.
   void ExpectResumption(SessionResumptionMode expected,
                         uint8_t num_resumed = 1);
   void DisableAllCiphers();
   void EnableOnlyStaticRsaCiphers();
   void EnableOnlyDheCiphers();
   void EnableSomeEcdhCiphers();
   void EnableExtendedMasterSecret();
+  void ConfigureSelfEncrypt();
   void ConfigureSessionCache(SessionResumptionMode client,
                              SessionResumptionMode server);
   void EnableAlpn();
   void EnableAlpn(const uint8_t* val, size_t len);
   void EnsureModelSockets();
   void CheckAlpn(const std::string& val);
   void EnableSrtp();
   void CheckSrtp() const;
@@ -116,16 +121,19 @@ class TlsConnectTestBase : public ::test
       bool expect_writable, bool expect_readable,
       std::function<bool()> post_clienthello_check = nullptr);
   void Receive(size_t amount);
   void ExpectExtendedMasterSecret(bool expected);
   void ExpectEarlyDataAccepted(bool expected);
   void DisableECDHEServerKeyReuse();
   void SkipVersionChecks();
 
+  // Move the DTLS timers for both endpoints to pop the next timer.
+  void ShiftDtlsTimers();
+
  protected:
   SSLProtocolVariant variant_;
   std::shared_ptr<TlsAgent> client_;
   std::shared_ptr<TlsAgent> server_;
   std::unique_ptr<TlsAgent> client_model_;
   std::unique_ptr<TlsAgent> server_model_;
   uint16_t version_;
   SessionResumptionMode expected_resumption_mode_;
@@ -246,30 +254,37 @@ 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_;
   std::shared_ptr<TlsInspectorRecordHandshakeMessage> capture_hrr_;
 
   void EnsureKeyShareSetup();
   void ConfigNamedGroups(const std::vector<SSLNamedGroup>& groups);
-  std::vector<SSLNamedGroup> GetGroupDetails(const DataBuffer& ext);
-  std::vector<SSLNamedGroup> GetShareDetails(const DataBuffer& ext);
+  std::vector<SSLNamedGroup> GetGroupDetails(
+      const std::shared_ptr<TlsExtensionCapture>& capture);
+  std::vector<SSLNamedGroup> GetShareDetails(
+      const std::shared_ptr<TlsExtensionCapture>& capture);
   void CheckKEXDetails(const std::vector<SSLNamedGroup>& expectedGroups,
                        const std::vector<SSLNamedGroup>& expectedShares);
   void CheckKEXDetails(const std::vector<SSLNamedGroup>& expectedGroups,
                        const std::vector<SSLNamedGroup>& expectedShares,
                        SSLNamedGroup expectedShare2);
 
  private:
   void CheckKEXDetails(const std::vector<SSLNamedGroup>& expectedGroups,
--- a/security/nss/gtests/ssl_gtest/tls_filter.cc
+++ b/security/nss/gtests/ssl_gtest/tls_filter.cc
@@ -7,16 +7,17 @@
 #include "tls_filter.h"
 #include "sslproto.h"
 
 extern "C" {
 // This is not something that should make you happy.
 #include "libssl_internals.h"
 }
 
+#include <cassert>
 #include <iostream>
 #include "gtest_utils.h"
 #include "tls_agent.h"
 #include "tls_filter.h"
 #include "tls_protect.h"
 
 namespace nss_test {
 
@@ -52,47 +53,64 @@ void TlsRecordFilter::EnableDecryption()
 }
 
 void TlsRecordFilter::CipherSpecChanged(void* arg, PRBool sending,
                                         ssl3CipherSpec* newSpec) {
   TlsRecordFilter* self = static_cast<TlsRecordFilter*>(arg);
   PRBool isServer = self->agent()->role() == TlsAgent::SERVER;
 
   if (g_ssl_gtest_verbose) {
-    std::cerr << "Cipher spec changed. Role="
-              << (isServer ? "server" : "client")
-              << " direction=" << (sending ? "send" : "receive") << std::endl;
+    std::cerr << (isServer ? "server" : "client") << ": "
+              << (sending ? "send" : "receive")
+              << " cipher spec changed:  " << newSpec->epoch << " ("
+              << newSpec->phase << ")" << std::endl;
   }
-  if (!sending) return;
+  if (!sending) {
+    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),
-                               SSLInt_CipherSpecToKey(isServer, newSpec),
-                               SSLInt_CipherSpecToIv(isServer, newSpec));
+  bool ret = self->cipher_spec_->Init(
+      SSLInt_CipherSpecToEpoch(newSpec), SSLInt_CipherSpecToAlgorithm(newSpec),
+      SSLInt_CipherSpecToKey(newSpec), SSLInt_CipherSpecToIv(newSpec));
   EXPECT_EQ(true, ret);
 }
 
 PacketFilter::Action TlsRecordFilter::Filter(const DataBuffer& input,
                                              DataBuffer* output) {
   bool changed = false;
   size_t offset = 0U;
   output->Allocate(input.len());
 
   TlsParser parser(input);
 
   while (parser.remaining()) {
     TlsRecordHeader header;
     DataBuffer record;
 
-    if (!header.Parse(&parser, &record)) {
+    if (!header.Parse(in_sequence_number_, &parser, &record)) {
       ADD_FAILURE() << "not a valid record";
       return KEEP;
     }
 
+    // Track the sequence number, which is necessary for stream mode (the
+    // sequence number is in the header for datagram).
+    //
+    // This isn't perfectly robust.  If there is a change from an active cipher
+    // spec to another active cipher spec (KeyUpdate for instance) AND writes
+    // are consolidated across that change AND packets were dropped from the
+    // older epoch, we will not correctly re-encrypt records in the old epoch to
+    // update their sequence numbers.
+    if (cipher_spec_ && header.content_type() == kTlsApplicationDataType) {
+      ++in_sequence_number_;
+    }
+
     if (FilterRecord(header, record, &offset, output) != KEEP) {
       changed = true;
     } else {
       offset = header.Write(output, offset, record);
     }
   }
   output->Truncate(offset);
 
@@ -115,61 +133,82 @@ PacketFilter::Action TlsRecordFilter::Fi
   if (!Unprotect(header, record, &inner_content_type, &plaintext)) {
     return KEEP;
   }
 
   TlsRecordHeader real_header = {header.version(), inner_content_type,
                                  header.sequence_number()};
 
   PacketFilter::Action action = FilterRecord(real_header, plaintext, &filtered);
+  // In stream mode, even if something doesn't change we need to re-encrypt if
+  // previous packets were dropped.
   if (action == KEEP) {
-    return 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());
-  std::cerr << "record old: " << plaintext << std::endl;
-  std::cerr << "record new: " << filtered << std::endl;
+  if (action != KEEP) {
+    std::cerr << "record old: " << plaintext << std::endl;
+    std::cerr << "record new: " << filtered << std::endl;
+  }
+
+  uint64_t seq_num;
+  if (header.is_dtls() || !cipher_spec_ ||
+      header.content_type() != kTlsApplicationDataType) {
+    seq_num = header.sequence_number();
+  } else {
+    seq_num = out_sequence_number_++;
+  }
+  TlsRecordHeader out_header = {header.version(), header.content_type(),
+                                seq_num};
 
   DataBuffer ciphertext;
-  bool rv = Protect(header, inner_content_type, filtered, &ciphertext);
+  bool rv = Protect(out_header, inner_content_type, filtered, &ciphertext);
   EXPECT_TRUE(rv);
   if (!rv) {
     return KEEP;
   }
-  *offset = header.Write(output, *offset, ciphertext);
+  *offset = out_header.Write(output, *offset, ciphertext);
   return CHANGE;
 }
 
-bool TlsRecordHeader::Parse(TlsParser* parser, DataBuffer* body) {
+bool TlsRecordHeader::Parse(uint64_t sequence_number, TlsParser* parser,
+                            DataBuffer* body) {
   if (!parser->Read(&content_type_)) {
     return false;
   }
 
   uint32_t version;
   if (!parser->Read(&version, 2)) {
     return false;
   }
   version_ = version;
 
-  sequence_number_ = 0;
+  // If this is DTLS, overwrite the sequence number.
   if (IsDtls(version)) {
     uint32_t tmp;
     if (!parser->Read(&tmp, 4)) {
       return false;
     }
     sequence_number_ = static_cast<uint64_t>(tmp) << 32;
     if (!parser->Read(&tmp, 4)) {
       return false;
     }
     sequence_number_ |= static_cast<uint64_t>(tmp);
+  } else {
+    sequence_number_ = sequence_number;
   }
   return parser->ReadVariable(body, 2);
 }
 
 size_t TlsRecordHeader::Write(DataBuffer* buffer, size_t offset,
                               const DataBuffer& body) const {
   offset = buffer->Write(offset, content_type_, 1);
   offset = buffer->Write(offset, version_, 2);
@@ -188,46 +227,80 @@ bool TlsRecordFilter::Unprotect(const Tl
                                 uint8_t* inner_content_type,
                                 DataBuffer* plaintext) {
   if (!cipher_spec_ || header.content_type() != kTlsApplicationDataType) {
     *inner_content_type = header.content_type();
     *plaintext = ciphertext;
     return true;
   }
 
-  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;
   }
 
   *inner_content_type = plaintext->data()[len - 1];
   plaintext->Truncate(len - 1);
+  if (g_ssl_gtest_verbose) {
+    std::cerr << "unprotect: " << std::hex << header.sequence_number()
+              << std::dec << " type=" << static_cast<int>(*inner_content_type)
+              << " " << *plaintext << std::endl;
+  }
 
   return true;
 }
 
 bool TlsRecordFilter::Protect(const TlsRecordHeader& header,
                               uint8_t inner_content_type,
                               const DataBuffer& plaintext,
                               DataBuffer* ciphertext) {
   if (!cipher_spec_ || header.content_type() != kTlsApplicationDataType) {
     *ciphertext = plaintext;
     return true;
   }
+  if (g_ssl_gtest_verbose) {
+    std::cerr << "protect: " << header.sequence_number() << std::endl;
+  }
   DataBuffer padded = plaintext;
   padded.Write(padded.len(), inner_content_type, 1);
   return cipher_spec_->Protect(header, padded, ciphertext);
 }
 
+bool IsHelloRetry(const DataBuffer& body) {
+  static const uint8_t ssl_hello_retry_random[] = {
+      0xCF, 0x21, 0xAD, 0x74, 0xE5, 0x9A, 0x61, 0x11, 0xBE, 0x1D, 0x8C,
+      0x02, 0x1E, 0x65, 0xB8, 0x91, 0xC2, 0xA2, 0x11, 0x16, 0x7A, 0xBB,
+      0x8C, 0x5E, 0x07, 0x9E, 0x09, 0xE2, 0xC8, 0xA8, 0x33, 0x9C};
+  return memcmp(body.data() + 2, ssl_hello_retry_random,
+                sizeof(ssl_hello_retry_random)) == 0;
+}
+
+bool TlsHandshakeFilter::IsFilteredType(const HandshakeHeader& header,
+                                        const DataBuffer& body) {
+  if (handshake_types_.empty()) {
+    return true;
+  }
+
+  uint8_t type = header.handshake_type();
+  if (type == kTlsHandshakeServerHello) {
+    if (IsHelloRetry(body)) {
+      type = kTlsHandshakeHelloRetryRequest;
+    }
+  }
+  return handshake_types_.count(type) > 0U;
+}
+
 PacketFilter::Action TlsHandshakeFilter::FilterRecord(
     const TlsRecordHeader& record_header, const DataBuffer& input,
     DataBuffer* output) {
   // Check that the first byte is as requested.
   if ((record_header.content_type() != kTlsHandshakeType) &&
       (record_header.content_type() != kTlsAltHandshakeType)) {
     return KEEP;
   }
@@ -235,86 +308,127 @@ PacketFilter::Action TlsHandshakeFilter:
   bool changed = false;
   size_t offset = 0U;
   output->Allocate(input.len());  // Preallocate a little.
 
   TlsParser parser(input);
   while (parser.remaining()) {
     HandshakeHeader header;
     DataBuffer handshake;
-    if (!header.Parse(&parser, record_header, &handshake)) {
+    bool complete = false;
+    if (!header.Parse(&parser, record_header, preceding_fragment_, &handshake,
+                      &complete)) {
       return KEEP;
     }
 
+    if (!complete) {
+      EXPECT_TRUE(record_header.is_dtls());
+      // Save the fragment and drop it from this record.  Fragments are
+      // coalesced with the last fragment of the handshake message.
+      changed = true;
+      preceding_fragment_.Assign(handshake);
+      continue;
+    }
+    preceding_fragment_.Truncate(0);
+
     DataBuffer filtered;
-    PacketFilter::Action action = FilterHandshake(header, handshake, &filtered);
+    PacketFilter::Action action;
+    if (!IsFilteredType(header, handshake)) {
+      action = KEEP;
+    } else {
+      action = FilterHandshake(header, handshake, &filtered);
+    }
     if (action == DROP) {
       changed = true;
       std::cerr << "handshake drop: " << handshake << std::endl;
       continue;
     }
 
     const DataBuffer* source = &handshake;
     if (action == CHANGE) {
       EXPECT_GT(0x1000000U, filtered.len());
       changed = true;
       std::cerr << "handshake old: " << handshake << std::endl;
       std::cerr << "handshake new: " << filtered << std::endl;
       source = &filtered;
+    } else if (preceding_fragment_.len()) {
+      changed = true;
     }
 
     offset = header.Write(output, offset, *source);
   }
   output->Truncate(offset);
   return changed ? (offset ? CHANGE : DROP) : KEEP;
 }
 
 bool TlsHandshakeFilter::HandshakeHeader::ReadLength(
-    TlsParser* parser, const TlsRecordHeader& header, uint32_t* length) {
-  if (!parser->Read(length, 3)) {
+    TlsParser* parser, const TlsRecordHeader& header, uint32_t expected_offset,
+    uint32_t* length, bool* last_fragment) {
+  uint32_t message_length;
+  if (!parser->Read(&message_length, 3)) {
     return false;  // malformed
   }
 
   if (!header.is_dtls()) {
+    *last_fragment = true;
+    *length = message_length;
     return true;  // nothing left to do
   }
 
   // Read and check DTLS parameters
   uint32_t message_seq_tmp;
   if (!parser->Read(&message_seq_tmp, 2)) {  // sequence number
     return false;
   }
   message_seq_ = message_seq_tmp;
 
-  uint32_t fragment_offset;
-  if (!parser->Read(&fragment_offset, 3)) {
+  uint32_t offset = 0;
+  if (!parser->Read(&offset, 3)) {
+    return false;
+  }
+  // We only parse if the fragments are all complete and in order.
+  if (offset != expected_offset) {
+    EXPECT_NE(0U, header.epoch())
+        << "Received out of order handshake fragment for epoch 0";
     return false;
   }
 
-  uint32_t fragment_length;
-  if (!parser->Read(&fragment_length, 3)) {
+  // For DTLS, we return the length of just this fragment.
+  if (!parser->Read(length, 3)) {
     return false;
   }
 
-  // All current tests where we are using this code don't fragment.
-  return (fragment_offset == 0 && fragment_length == *length);
+  // It's a fragment if the entire message is longer than what we have.
+  *last_fragment = message_length == (*length + offset);
+  return true;
 }
 
 bool TlsHandshakeFilter::HandshakeHeader::Parse(
-    TlsParser* parser, const TlsRecordHeader& record_header, DataBuffer* body) {
+    TlsParser* parser, const TlsRecordHeader& record_header,
+    const DataBuffer& preceding_fragment, DataBuffer* body, bool* complete) {
+  *complete = false;
+
   version_ = record_header.version();
   if (!parser->Read(&handshake_type_)) {
     return false;  // malformed
   }
+
   uint32_t length;
-  if (!ReadLength(parser, record_header, &length)) {
+  if (!ReadLength(parser, record_header, preceding_fragment.len(), &length,
+                  complete)) {
     return false;
   }
 
-  return parser->Read(body, length);
+  if (!parser->Read(body, length)) {
+    return false;
+  }
+  if (preceding_fragment.len()) {
+    body->Splice(preceding_fragment, 0);
+  }
+  return true;
 }
 
 size_t TlsHandshakeFilter::HandshakeHeader::WriteFragment(
     DataBuffer* buffer, size_t offset, const DataBuffer& body,
     size_t fragment_offset, size_t fragment_length) const {
   EXPECT_TRUE(is_dtls());
   EXPECT_GE(body.len(), fragment_offset + fragment_length);
   offset = buffer->Write(offset, handshake_type(), 1);
@@ -341,31 +455,25 @@ size_t TlsHandshakeFilter::HandshakeHead
 PacketFilter::Action TlsInspectorRecordHandshakeMessage::FilterHandshake(
     const HandshakeHeader& header, const DataBuffer& input,
     DataBuffer* output) {
   // Only do this once.
   if (buffer_.len()) {
     return KEEP;
   }
 
-  if (header.handshake_type() == handshake_type_) {
-    buffer_ = input;
-  }
+  buffer_ = input;
   return KEEP;
 }
 
 PacketFilter::Action TlsInspectorReplaceHandshakeMessage::FilterHandshake(
     const HandshakeHeader& header, const DataBuffer& input,
     DataBuffer* output) {
-  if (header.handshake_type() == handshake_type_) {
-    *output = buffer_;
-    return CHANGE;
-  }
-
-  return KEEP;
+  *output = buffer_;
+  return CHANGE;
 }
 
 PacketFilter::Action TlsRecordRecorder::FilterRecord(
     const TlsRecordHeader& header, const DataBuffer& input,
     DataBuffer* output) {
   if (!filter_ || (header.content_type() == ct_)) {
     records_.push_back({header, input});
   }
@@ -393,17 +501,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;
     }
@@ -450,41 +558,25 @@ bool FindServerHelloExtensions(TlsParser
   if (NormalizeTlsVersion(version) <= SSL_LIBRARY_VERSION_TLS_1_2) {
     if (!parser->Skip(1)) {  // compression method
       return false;
     }
   }
   return true;
 }
 
-static bool FindHelloRetryExtensions(TlsParser* parser,
-