Bug 1317947 - land NSS 0x5f2db99c258f, r=me
authorFranziskus Kiefer <franziskuskiefer@gmail.com>
Fri, 02 Dec 2016 12:20:41 +0100
changeset 325134 e777cf9e040ff9694a8ba747a6aadf75b8e9fcf8
parent 325133 1f73de3c604de4b6d087df99171cb0d3ef9ce498
child 325135 d277102b35f954c38255d2fc683b7bbb2a2a3024
push id24
push usermaklebus@msu.edu
push dateTue, 20 Dec 2016 03:11:33 +0000
reviewersme
bugs1317947
milestone53.0a1
Bug 1317947 - land NSS 0x5f2db99c258f, r=me
security/nss/TAG-INFO
security/nss/automation/taskcluster/graph/src/extend.js
security/nss/build.sh
security/nss/cmd/tstclnt/tstclnt.c
security/nss/coreconf/config.gypi
security/nss/coreconf/coreconf.dep
security/nss/coreconf/detect_host_arch.py
security/nss/coreconf/werror.py
security/nss/fuzz/asn1_mutators.cc
security/nss/fuzz/asn1_mutators.h
security/nss/fuzz/clone_corpus.sh
security/nss/fuzz/clone_libfuzzer.sh
security/nss/fuzz/fuzz.gyp
security/nss/fuzz/git-copy.sh
security/nss/fuzz/libFuzzer/libFuzzer.gyp
security/nss/fuzz/nssfuzz.cc
security/nss/fuzz/pkcs8_target.cc
security/nss/fuzz/quickder_targets.cc
security/nss/fuzz/registry.h
security/nss/gtests/nss_bogo_shim/nss_bogo_shim.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_auth_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_loopback_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/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_protect.cc
security/nss/gtests/ssl_gtest/tls_protect.h
security/nss/lib/certhigh/ocsp.c
security/nss/lib/freebl/rsa.c
security/nss/lib/ssl/SSLerrs.h
security/nss/lib/ssl/dtlscon.c
security/nss/lib/ssl/ssl.def
security/nss/lib/ssl/ssl3con.c
security/nss/lib/ssl/ssl3ext.c
security/nss/lib/ssl/ssl3ext.h
security/nss/lib/ssl/ssl3exthandle.c
security/nss/lib/ssl/sslimpl.h
security/nss/lib/ssl/sslproto.h
security/nss/lib/ssl/tls13con.c
security/nss/lib/ssl/tls13exthandle.c
security/nss/readme.md
--- a/security/nss/TAG-INFO
+++ b/security/nss/TAG-INFO
@@ -1,1 +1,1 @@
-0ce009e2303a
+5f2db99c258f
--- a/security/nss/automation/taskcluster/graph/src/extend.js
+++ b/security/nss/automation/taskcluster/graph/src/extend.js
@@ -274,17 +274,17 @@ async function scheduleFuzzing() {
   };
 
   // Build base definition.
   let build_base = merge({
     command: [
       "/bin/bash",
       "-c",
       "bin/checkout.sh && " +
-      "nss/automation/taskcluster/scripts/build_gyp.sh -g -v --fuzz"
+      "nss/automation/taskcluster/scripts/build_gyp.sh -g -v --fuzz --ubsan"
     ],
     artifacts: {
       public: {
         expires: 24 * 7,
         type: "directory",
         path: "/home/worker/artifacts"
       }
     },
--- a/security/nss/build.sh
+++ b/security/nss/build.sh
@@ -8,17 +8,17 @@ set -e
 
 source $(dirname $0)/coreconf/nspr.sh
 
 # Usage info
 show_help() {
 cat << EOF
 
 Usage: ${0##*/} [-hcgv] [-j <n>] [--test] [--fuzz] [--scan-build[=output]]
-                [-m32] [--opt|-o] [--asan] [--ubsan] [--sancov[=edge|bb|func]]
+                [-m32] [--opt|-o] [--asan] [--ubsan] [--sancov[=edge|bb|func|...]]
                 [--pprof] [--msan]
 
 This script builds NSS with gyp and ninja.
 
 This build system is still under development.  It does not yet support all
 the features or platforms that NSS supports.
 
 NSS build tool options:
@@ -49,16 +49,17 @@ fi
 
 opt_build=0
 build_64=0
 clean=0
 rebuild_gyp=0
 target=Debug
 verbose=0
 fuzz=0
+sancov_default=edge,indirect-calls,8bit-counters
 
 # parse parameters to store in config
 params=$(echo "$*" | perl -pe 's/-c|-v|-g|-j [0-9]*|-h//g' | perl -pe 's/^\s*(.*?)\s*$/\1/')
 params=$(echo "$params $CC $CCC" | tr " " "\n" | perl -pe '/^\s*$/d')
 params=$(echo "${params[*]}" | sort)
 
 cwd=$(cd $(dirname $0); pwd -P)
 dist_dir="$cwd/../dist"
@@ -72,21 +73,19 @@ fi
 gyp_params=()
 ninja_params=()
 scanbuild=()
 
 enable_fuzz()
 {
     fuzz=1
     nspr_sanitizer asan
-    nspr_sanitizer ubsan
-    nspr_sanitizer sancov edge
+    nspr_sanitizer sancov $sancov_default
     gyp_params+=(-Duse_asan=1)
-    gyp_params+=(-Duse_ubsan=1)
-    gyp_params+=(-Duse_sancov=edge)
+    gyp_params+=(-Duse_sancov=$sancov_default)
 
     # Adding debug symbols even for opt builds.
     nspr_opt+=(--enable-debug-symbols)
 }
 
 # parse command line arguments
 while [ $# -gt 0 ]; do
     case $1 in
@@ -97,17 +96,17 @@ while [ $# -gt 0 ]; do
         --test) gyp_params+=(-Dtest_build=1) ;;
         --fuzz) gyp_params+=(-Dtest_build=1 -Dfuzz=1); enable_fuzz ;;
         --scan-build) scanbuild=(scan-build) ;;
         --scan-build=?*) scanbuild=(scan-build -o "${1#*=}") ;;
         --opt|-o) opt_build=1 ;;
         -m32|--m32) build_64=0 ;;
         --asan) gyp_params+=(-Duse_asan=1); nspr_sanitizer asan ;;
         --ubsan) gyp_params+=(-Duse_ubsan=1); nspr_sanitizer ubsan ;;
-        --sancov) gyp_params+=(-Duse_sancov=edge); nspr_sanitizer sancov edge ;;
+        --sancov) gyp_params+=(-Duse_sancov=$sancov_default); nspr_sanitizer sancov $sancov_default ;;
         --sancov=?*) gyp_params+=(-Duse_sancov="${1#*=}"); nspr_sanitizer sancov "${1#*=}" ;;
         --pprof) gyp_params+=(-Duse_pprof=1) ;;
         --msan) gyp_params+=(-Duse_msan=1); nspr_sanitizer msan ;;
         *) show_help; exit ;;
     esac
     shift
 done
 
@@ -116,17 +115,16 @@ if [ "$opt_build" = "1" ]; then
     nspr_opt+=(--disable-debug --enable-optimize)
 else
     target=Debug
 fi
 if [ "$build_64" == "1" ]; then
     nspr_opt+=(--enable-64bit)
 else
     gyp_params+=(-Dtarget_arch=ia32)
-    nspr_opt+=(--enable-x32)
 fi
 
 # clone fuzzing stuff
 if [ "$fuzz" = "1" ]; then
     [ $verbose = 0 ] && exec 3>/dev/null || exec 3>&1
 
     echo "[1/2] Cloning libFuzzer files ..."
     $cwd/fuzz/clone_libfuzzer.sh 1>&3 2>&3
--- a/security/nss/cmd/tstclnt/tstclnt.c
+++ b/security/nss/cmd/tstclnt/tstclnt.c
@@ -164,30 +164,16 @@ printSecurityInfo(PRFileDesc *fd)
     scts = SSL_PeerSignedCertTimestamps(fd);
     if (scts && scts->len) {
         fprintf(stderr, "Received a Signed Certificate Timestamp of length"
                         " %u\n",
                 scts->len);
     }
 }
 
-void
-handshakeCallback(PRFileDesc *fd, void *client_data)
-{
-    const char *secondHandshakeName = (char *)client_data;
-    if (secondHandshakeName) {
-        SSL_SetURL(fd, secondHandshakeName);
-    }
-    printSecurityInfo(fd);
-    if (renegotiationsDone < renegotiationsToDo) {
-        SSL_ReHandshake(fd, (renegotiationsToDo < 2));
-        ++renegotiationsDone;
-    }
-}
-
 static void
 PrintUsageHeader(const char *progName)
 {
     fprintf(stderr,
             "Usage:  %s -h host [-a 1st_hs_name ] [-a 2nd_hs_name ] [-p port]\n"
             "[-D | -d certdir] [-C] [-b | -R root-module] \n"
             "[-n nickname] [-Bafosvx] [-c ciphers] [-Y] [-Z]\n"
             "[-V [min-version]:[max-version]] [-K] [-T] [-U]\n"
@@ -918,23 +904,29 @@ PRBool clientSpeaksFirst = PR_FALSE;
 PRBool skipProtoHeader = PR_FALSE;
 ServerCertAuth serverCertAuth;
 char *hs1SniHostName = NULL;
 char *hs2SniHostName = NULL;
 PRUint16 portno = 443;
 int override = 0;
 char *requestString = NULL;
 PRInt32 requestStringLen = 0;
+PRBool requestSent = PR_FALSE;
 PRBool enableZeroRtt = PR_FALSE;
 
 static int
-writeBytesToServer(PRFileDesc *s, PRPollDesc *pollset, const char *buf, int nb)
+writeBytesToServer(PRFileDesc *s, const char *buf, int nb)
 {
     SECStatus rv;
     const char *bufp = buf;
+    PRPollDesc pollDesc;
+
+    pollDesc.in_flags = PR_POLL_WRITE | PR_POLL_EXCEPT;
+    pollDesc.out_flags = 0;
+    pollDesc.fd = s;
 
     FPRINTF(stderr, "%s: Writing %d bytes to server\n",
             progName, nb);
     do {
         PRInt32 cc = PR_Send(s, bufp, nb, 0, maxInterval);
         if (cc < 0) {
             PRErrorCode err = PR_GetError();
             if (err != PR_WOULD_BLOCK_ERROR) {
@@ -951,49 +943,80 @@ writeBytesToServer(PRFileDesc *s, PRPoll
 
         rv = restartHandshakeAfterServerCertIfNeeded(s,
                                                      &serverCertAuth, override);
         if (rv != SECSuccess) {
             SECU_PrintError(progName, "authentication of server cert failed");
             return EXIT_CODE_HANDSHAKE_FAILED;
         }
 
-        pollset[SSOCK_FD].in_flags = PR_POLL_WRITE | PR_POLL_EXCEPT;
-        pollset[SSOCK_FD].out_flags = 0;
+        pollDesc.in_flags = PR_POLL_WRITE | PR_POLL_EXCEPT;
+        pollDesc.out_flags = 0;
         FPRINTF(stderr,
                 "%s: about to call PR_Poll on writable socket !\n",
                 progName);
-        cc = PR_Poll(pollset, 1, PR_INTERVAL_NO_TIMEOUT);
+        cc = PR_Poll(&pollDesc, 1, PR_INTERVAL_NO_TIMEOUT);
         if (cc < 0) {
             SECU_PrintError(progName,
                             "PR_Poll failed");
             return -1;
         }
         FPRINTF(stderr,
                 "%s: PR_Poll returned with writable socket !\n",
                 progName);
     } while (1);
 
     return 0;
 }
 
+void
+handshakeCallback(PRFileDesc *fd, void *client_data)
+{
+    const char *secondHandshakeName = (char *)client_data;
+    if (secondHandshakeName) {
+        SSL_SetURL(fd, secondHandshakeName);
+    }
+    printSecurityInfo(fd);
+    if (renegotiationsDone < renegotiationsToDo) {
+        SSL_ReHandshake(fd, (renegotiationsToDo < 2));
+        ++renegotiationsDone;
+    }
+    if (requestString && requestSent) {
+        /* This data was sent in 0-RTT. */
+        SSLChannelInfo info;
+        SECStatus rv;
+
+        rv = SSL_GetChannelInfo(fd, &info, sizeof(info));
+        if (rv != SECSuccess)
+            return;
+
+        if (!info.earlyDataAccepted) {
+            FPRINTF(stderr, "Early data rejected. Re-sending\n");
+            writeBytesToServer(fd, requestString, requestStringLen);
+        }
+    }
+}
+
+#define REQUEST_WAITING (requestString && !requestSent)
+
 static int
 run_client(void)
 {
     int headerSeparatorPtrnId = 0;
     int error = 0;
     SECStatus rv;
     PRStatus status;
     PRInt32 filesReady;
     int npds;
     PRFileDesc *s = NULL;
     PRFileDesc *std_out;
     PRPollDesc pollset[2];
     PRBool wrStarted = PR_FALSE;
-    char *requestStringInt = requestString;
+
+    requestSent = PR_FALSE;
 
     /* Create socket */
     s = PR_OpenTCPSocket(addr.raw.family);
     if (s == NULL) {
         SECU_PrintError(progName, "error creating socket");
         error = 1;
         goto done;
     }
@@ -1240,17 +1263,17 @@ run_client(void)
             goto done;
         }
     }
 
     pollset[SSOCK_FD].fd = s;
     pollset[SSOCK_FD].in_flags = PR_POLL_EXCEPT |
                                  (clientSpeaksFirst ? 0 : PR_POLL_READ);
     pollset[STDIN_FD].fd = PR_GetSpecialFD(PR_StandardInput);
-    if (!requestStringInt) {
+    if (!REQUEST_WAITING) {
         pollset[STDIN_FD].in_flags = PR_POLL_READ;
         npds = 2;
     } else {
         npds = 1;
     }
     std_out = PR_GetSpecialFD(PR_StandardOutput);
 
 #if defined(WIN32) || defined(OS2)
@@ -1290,17 +1313,17 @@ run_client(void)
     }
 
     /*
     ** Select on stdin and on the socket. Write data from stdin to
     ** socket, read data from socket and write to stdout.
     */
     FPRINTF(stderr, "%s: ready...\n", progName);
     while ((pollset[SSOCK_FD].in_flags | pollset[STDIN_FD].in_flags) ||
-           requestStringInt) {
+           REQUEST_WAITING) {
         char buf[4000]; /* buffer for stdin */
         int nb;         /* num bytes read from stdin. */
 
         rv = restartHandshakeAfterServerCertIfNeeded(s, &serverCertAuth,
                                                      override);
         if (rv != SECSuccess) {
             error = EXIT_CODE_HANDSHAKE_FAILED;
             SECU_PrintError(progName, "authentication of server cert failed");
@@ -1328,40 +1351,39 @@ run_client(void)
                     "%s: PR_Poll returned 0x%02x for stdin out_flags.\n",
                     progName, pollset[STDIN_FD].out_flags);
         }
         if (pollset[SSOCK_FD].in_flags) {
             FPRINTF(stderr,
                     "%s: PR_Poll returned 0x%02x for socket out_flags.\n",
                     progName, pollset[SSOCK_FD].out_flags);
         }
-        if (requestStringInt) {
-            error = writeBytesToServer(s, pollset,
-                                       requestStringInt, requestStringLen);
+        if (REQUEST_WAITING) {
+            error = writeBytesToServer(s, requestString, requestStringLen);
             if (error) {
                 goto done;
             }
-            requestStringInt = NULL;
+            requestSent = PR_TRUE;
             pollset[SSOCK_FD].in_flags = PR_POLL_READ;
         }
         if (pollset[STDIN_FD].out_flags & PR_POLL_READ) {
             /* Read from stdin and write to socket */
             nb = PR_Read(pollset[STDIN_FD].fd, buf, sizeof(buf));
             FPRINTF(stderr, "%s: stdin read %d bytes\n", progName, nb);
             if (nb < 0) {
                 if (PR_GetError() != PR_WOULD_BLOCK_ERROR) {
                     SECU_PrintError(progName, "read from stdin failed");
                     error = 1;
                     break;
                 }
             } else if (nb == 0) {
                 /* EOF on stdin, stop polling stdin for read. */
                 pollset[STDIN_FD].in_flags = 0;
             } else {
-                error = writeBytesToServer(s, pollset, buf, nb);
+                error = writeBytesToServer(s, buf, nb);
                 if (error) {
                     goto done;
                 }
                 pollset[SSOCK_FD].in_flags = PR_POLL_READ;
             }
         }
 
         if (pollset[SSOCK_FD].in_flags) {
--- a/security/nss/coreconf/config.gypi
+++ b/security/nss/coreconf/config.gypi
@@ -268,32 +268,40 @@
               [ 'target_arch=="ia32"', {
                 'cflags': ['-m32'],
                 'ldflags': ['-m32'],
               }],
               [ 'target_arch=="x64"', {
                 'cflags': ['-m64'],
                 'ldflags': ['-m64'],
               }],
-              [ 'use_pprof==1' , {
-                'ldflags': [ '-lprofiler' ],
-              }],
+            ],
+          }],
+          [ 'use_pprof==1 and OS=="linux"', {
+            'ldflags': [ '-lprofiler' ],
+          }],
+          [ 'use_pprof==1 and OS=="mac"', {
+            'xcode_settings': {
+              'OTHER_LDFLAGS': [ '-lprofiler' ],
+            },
+            'library_dirs': [
+              '/usr/local/lib/',
             ],
           }],
           [ 'disable_werror==0 and (OS=="linux" or OS=="mac")', {
             'cflags': [
               '<!@(<(python) <(DEPTH)/coreconf/werror.py)',
             ],
           }],
           [ 'fuzz==1', {
             'cflags': [
               '-Wno-unused-function',
             ]
           }],
-          [ 'use_asan==1 or use_ubsan==1', {
+          [ 'fuzz==1 or use_asan==1 or use_ubsan==1', {
             'cflags': ['-O1'],
             'xcode_settings': {
               'GCC_OPTIMIZATION_LEVEL': '1', # -O1
             }
           }],
           [ 'use_asan==1', {
             'variables': {
               'asan_flags': '<!(<(python) <(DEPTH)/coreconf/sanitizers.py asan)',
--- 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/coreconf/detect_host_arch.py
+++ b/security/nss/coreconf/detect_host_arch.py
@@ -9,17 +9,17 @@ from __future__ import print_function
 import fnmatch
 import platform
 
 def main():
     host_arch = platform.machine().lower()
     if host_arch in ('amd64', 'x86_64'):
         host_arch = 'x64'
     elif fnmatch.fnmatch(host_arch, 'i?86') or host_arch == 'i86pc':
-        host_arch = 'x64'
+        host_arch = 'ia32'
     elif host_arch.startswith('arm'):
         host_arch = 'arm'
     elif host_arch.startswith('mips'):
         host_arch = 'mips'
     print(host_arch)
 
 if __name__ == '__main__':
     main()
--- a/security/nss/coreconf/werror.py
+++ b/security/nss/coreconf/werror.py
@@ -20,16 +20,17 @@ def main():
         if not warning_supported('all'):
             return False
 
         # If we aren't clang, make sure we have gcc 4.8 at least
         if not cc_is_clang:
             try:
                 v = subprocess.check_output([cc, '-dumpversion'], stderr=sink)
                 v = v.strip(' \r\n').split('.')
+                v = list(map(int, v))
                 if v[0] < 4 or (v[0] == 4 and v[1] < 8):
                     # gcc 4.8 minimum
                     return False
             except OSError:
                 return False
         return True
 
     if not can_enable():
new file mode 100644
--- /dev/null
+++ b/security/nss/fuzz/asn1_mutators.cc
@@ -0,0 +1,117 @@
+/* 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 <assert.h>
+#include <string.h>
+#include <tuple>
+
+#include "FuzzerRandom.h"
+#include "asn1_mutators.h"
+
+using namespace std;
+
+static tuple<uint8_t *, size_t> ParseItem(uint8_t *Data, size_t MaxLength) {
+  // Short form. Bit 8 has value "0" and bits 7-1 give the length.
+  if ((Data[1] & 0x80) == 0) {
+    size_t length = min(static_cast<size_t>(Data[1]), MaxLength - 2);
+    return make_tuple(&Data[2], length);
+  }
+
+  // Constructed, indefinite length. Read until {0x00, 0x00}.
+  if (Data[1] == 0x80) {
+    void *offset = memmem(&Data[2], MaxLength - 2, "\0", 2);
+    size_t length = offset ? (static_cast<uint8_t *>(offset) - &Data[2]) + 2
+                           : MaxLength - 2;
+    return make_tuple(&Data[2], length);
+  }
+
+  // Long form. Two to 127 octets. Bit 8 of first octet has value "1"
+  // and bits 7-1 give the number of additional length octets.
+  size_t octets = min(static_cast<size_t>(Data[1] & 0x7f), MaxLength - 2);
+
+  // Handle lengths bigger than 32 bits.
+  if (octets > 4) {
+    // Ignore any further children, assign remaining length.
+    return make_tuple(&Data[2] + octets, MaxLength - 2 - octets);
+  }
+
+  // Parse the length.
+  size_t length = 0;
+  for (size_t j = 0; j < octets; j++) {
+    length = (length << 8) | Data[2 + j];
+  }
+
+  length = min(length, MaxLength - 2 - octets);
+  return make_tuple(&Data[2] + octets, length);
+}
+
+static vector<uint8_t *> ParseItems(uint8_t *Data, size_t Size) {
+  vector<uint8_t *> items;
+  vector<size_t> lengths;
+
+  // The first item is always the whole corpus.
+  items.push_back(Data);
+  lengths.push_back(Size);
+
+  // Can't use iterators here because the `items` vector is modified inside the
+  // loop. That's safe as long as we always check `items.size()` before every
+  // iteration, and only call `.push_back()` to append new items we found.
+  // Items are accessed through `items.at()`, we hold no references.
+  for (size_t i = 0; i < items.size(); i++) {
+    uint8_t *item = items.at(i);
+    size_t remaining = lengths.at(i);
+
+    // Empty or primitive items have no children.
+    if (remaining == 0 || (0x20 & item[0]) == 0) {
+      continue;
+    }
+
+    while (remaining > 2) {
+      uint8_t *content;
+      size_t length;
+
+      tie(content, length) = ParseItem(item, remaining);
+
+      if (length > 0) {
+        // Record the item.
+        items.push_back(content);
+
+        // Record the length for further parsing.
+        lengths.push_back(length);
+      }
+
+      // Reduce number of bytes left in current item.
+      remaining -= length + (content - item);
+
+      // Skip the item we just parsed.
+      item = content + length;
+    }
+  }
+
+  return items;
+}
+
+size_t ASN1MutatorFlipConstructed(uint8_t *Data, size_t Size, size_t MaxSize,
+                                  unsigned int Seed) {
+  fuzzer::Random R(Seed);
+  auto items = ParseItems(Data, Size);
+  uint8_t *item = items.at(R(items.size()));
+
+  // Flip "constructed" type bit.
+  item[0] ^= 0x20;
+
+  return Size;
+}
+
+size_t ASN1MutatorChangeType(uint8_t *Data, size_t Size, size_t MaxSize,
+                             unsigned int Seed) {
+  fuzzer::Random R(Seed);
+  auto items = ParseItems(Data, Size);
+  uint8_t *item = items.at(R(items.size()));
+
+  // Change type to a random int [0, 31).
+  item[0] = R(31);
+
+  return Size;
+}
new file mode 100644
--- /dev/null
+++ b/security/nss/fuzz/asn1_mutators.h
@@ -0,0 +1,16 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef asn1_mutators_h__
+#define asn1_mutators_h__
+
+#include <stdint.h>
+#include <cstddef>
+
+size_t ASN1MutatorFlipConstructed(uint8_t *Data, size_t Size, size_t MaxSize,
+                                  unsigned int Seed);
+size_t ASN1MutatorChangeType(uint8_t *Data, size_t Size, size_t MaxSize,
+                             unsigned int Seed);
+
+#endif  // asn1_mutators_h__
--- a/security/nss/fuzz/clone_corpus.sh
+++ b/security/nss/fuzz/clone_corpus.sh
@@ -1,11 +1,4 @@
 #!/bin/sh
 
-cd $(dirname $0)
-
-mkdir tmp/
-git clone --no-checkout --depth 1 https://github.com/mozilla/nss-fuzzing-corpus tmp/
-(cd tmp && git reset --hard master)
-
-mkdir -p corpus
-cp -r tmp/* corpus
-rm -Rf tmp/
+d=$(dirname $0)
+$d/git-copy.sh https://github.com/mozilla/nss-fuzzing-corpus master $d/corpus
--- a/security/nss/fuzz/clone_libfuzzer.sh
+++ b/security/nss/fuzz/clone_libfuzzer.sh
@@ -1,11 +1,22 @@
 #!/bin/sh
 
-cd $(dirname $0)
+d=$(dirname $0)
+$d/git-copy.sh https://chromium.googlesource.com/chromium/llvm-project/llvm/lib/Fuzzer 1b543d6e5073b56be214394890c9193979a3d7e1 $d/libFuzzer
 
-mkdir tmp/
-git clone --no-checkout --depth 1 https://chromium.googlesource.com/chromium/llvm-project/llvm/lib/Fuzzer tmp/
-(cd tmp && git reset --hard 1b543d6e5073b56be214394890c9193979a3d7e1)
+cat <<EOF | patch -p0 -d $d
+diff --git libFuzzer/FuzzerMutate.cpp libFuzzer/FuzzerMutate.cpp
+--- libFuzzer/FuzzerMutate.cpp
++++ libFuzzer/FuzzerMutate.cpp
+@@ -53,10 +53,9 @@
+     DefaultMutators.push_back(
+         {&MutationDispatcher::Mutate_AddWordFromTORC, "CMP"});
 
-mkdir -p libFuzzer
-cp tmp/*.cpp tmp/*.h tmp/*.def libFuzzer
-rm -Rf tmp/
++  Mutators = DefaultMutators;
+   if (EF->LLVMFuzzerCustomMutator)
+     Mutators.push_back({&MutationDispatcher::Mutate_Custom, "Custom"});
+-  else
+-    Mutators = DefaultMutators;
+
+   if (EF->LLVMFuzzerCustomCrossOver)
+     Mutators.push_back(
+EOF
--- a/security/nss/fuzz/fuzz.gyp
+++ b/security/nss/fuzz/fuzz.gyp
@@ -3,27 +3,77 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 {
   'includes': [
     '../coreconf/config.gypi',
     '../cmd/platlibs.gypi'
   ],
   'targets': [
     {
+      'target_name': 'libFuzzer',
+      'type': 'static_library',
+      'sources': [
+        'libFuzzer/FuzzerCrossOver.cpp',
+        'libFuzzer/FuzzerDriver.cpp',
+        'libFuzzer/FuzzerExtFunctionsDlsym.cpp',
+        'libFuzzer/FuzzerExtFunctionsWeak.cpp',
+        'libFuzzer/FuzzerIO.cpp',
+        'libFuzzer/FuzzerLoop.cpp',
+        'libFuzzer/FuzzerMutate.cpp',
+        'libFuzzer/FuzzerSHA1.cpp',
+        'libFuzzer/FuzzerTracePC.cpp',
+        'libFuzzer/FuzzerTraceState.cpp',
+        'libFuzzer/FuzzerUtil.cpp',
+        'libFuzzer/FuzzerUtilDarwin.cpp',
+        'libFuzzer/FuzzerUtilLinux.cpp',
+      ],
+      'cflags': [
+        '-O2',
+      ],
+      'cflags!': [
+        '-O1',
+      ],
+      'cflags/': [
+        ['exclude', '-fsanitize'],
+      ],
+      'xcode_settings': {
+        'GCC_OPTIMIZATION_LEVEL': '2', # -O2
+        'OTHER_CFLAGS/': [
+          ['exclude', '-fsanitize'],
+        ],
+      },
+    },
+    {
       'target_name': 'nssfuzz',
       'type': 'executable',
       'sources': [
+        'asn1_mutators.cc',
         'nssfuzz.cc',
         'pkcs8_target.cc',
         'quickder_targets.cc',
       ],
       'dependencies': [
         '<(DEPTH)/exports.gyp:nss_exports',
-        '<(DEPTH)/fuzz/libFuzzer/libFuzzer.gyp:libFuzzer'
-      ]
+        'libFuzzer',
+      ],
+      'cflags': [
+        '-O2',
+      ],
+      'cflags!': [
+        '-O1',
+      ],
+      'cflags/': [
+        ['exclude', '-fsanitize-coverage'],
+      ],
+      'xcode_settings': {
+        'GCC_OPTIMIZATION_LEVEL': '2', # -O2
+        'OTHER_CFLAGS/': [
+          ['exclude', '-fsanitize-coverage'],
+        ],
+      },
     }
   ],
   'target_defaults': {
     'include_dirs': [
       'libFuzzer',
     ],
   },
   'variables': {
new file mode 100755
--- /dev/null
+++ b/security/nss/fuzz/git-copy.sh
@@ -0,0 +1,32 @@
+#!/bin/sh
+
+set -e
+
+if [ $# -lt 3 ]; then
+  echo "Usage: $0 <repo> <branch> <directory>" 1>&2
+  exit 2
+fi
+
+REPO=$1
+COMMIT=$2
+DIR=$3
+
+echo "Copy '$COMMIT' from '$REPO' to '$DIR'"
+if [ -f $DIR/.git-copy ]; then
+  CURRENT=$(cat $DIR/.git-copy)
+  if [ $(echo -n $COMMIT | wc -c) != "40" ]; then
+    ACTUAL=$(git ls-remote $REPO $COMMIT | cut -c 1-40 -)
+  else
+    ACTUAL=$COMMIT
+  fi
+  if [ CURRENT = ACTUAL ]; then
+    echo "Up to date."
+  fi
+fi
+
+mkdir -p $DIR
+git -C $DIR init -q
+git -C $DIR fetch -q --depth=1 $REPO $COMMIT:git-copy-tmp
+git -C $DIR reset --hard git-copy-tmp
+git -C $DIR show-ref HEAD | cut -c 1-40 - > $DIR/.git-copy
+rm -rf $DIR/.git
deleted file mode 100644
--- a/security/nss/fuzz/libFuzzer/libFuzzer.gyp
+++ /dev/null
@@ -1,43 +0,0 @@
-# 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/.
-{
-  'includes': [
-    '../../coreconf/config.gypi'
-  ],
-  'targets': [
-    {
-      'target_name': 'libFuzzer',
-      'type': 'static_library',
-      'sources': [
-        'FuzzerCrossOver.cpp',
-        'FuzzerDriver.cpp',
-        'FuzzerExtFunctionsDlsym.cpp',
-        'FuzzerExtFunctionsWeak.cpp',
-        'FuzzerIO.cpp',
-        'FuzzerLoop.cpp',
-        'FuzzerMutate.cpp',
-        'FuzzerSHA1.cpp',
-        'FuzzerTracePC.cpp',
-        'FuzzerTraceState.cpp',
-        'FuzzerUtil.cpp',
-        'FuzzerUtilDarwin.cpp',
-        'FuzzerUtilLinux.cpp',
-      ],
-      'cflags': [
-        '-O2',
-      ],
-      'cflags/': [
-        ['exclude', '-fsanitize='],
-        ['exclude', '-fsanitize-'],
-      ],
-      'xcode_settings': {
-        'GCC_OPTIMIZATION_LEVEL': '2', # -O2
-        'OTHER_CFLAGS/': [
-          ['exclude', '-fsanitize='],
-          ['exclude', '-fsanitize-'],
-        ],
-      },
-    }
-  ],
-}
--- a/security/nss/fuzz/nssfuzz.cc
+++ b/security/nss/fuzz/nssfuzz.cc
@@ -1,27 +1,27 @@
 /* -*- 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 <iomanip>
 #include <iostream>
-#include <memory>
-
-#include "keyhi.h"
-#include "pk11pub.h"
 
 #include "FuzzerInternal.h"
+#include "FuzzerMutate.h"
+#include "FuzzerRandom.h"
 #include "registry.h"
 #include "shared.h"
 
 using namespace std;
 
+static vector<Mutator> gMutators;
+
 class Args {
  public:
   Args(int argc, char **argv) : args_(argv, argv + argc) {}
 
   string &operator[](const int idx) { return args_[idx]; }
 
   bool Has(const string &arg) {
     return any_of(args_.begin(), args_.end(),
@@ -122,16 +122,20 @@ int main(int argc, char **argv) {
 
   if (args.count() < 2 || !Registry::Has(args[1])) {
     printUsage(args);
     return 1;
   }
 
   string targetName(args[1]);
 
+  // Add target mutators.
+  auto mutators = Registry::Mutators(targetName);
+  gMutators.insert(gMutators.end(), mutators.begin(), mutators.end());
+
   // Remove the target argument when -workers=x or -jobs=y is NOT given.
   // If both are given, libFuzzer will spawn multiple processes for the target.
   if (!args.Has("-workers=") || !args.Has("-jobs=")) {
     args.Remove(1);
   }
 
   // Set default max_len arg, if none given and we're not merging.
   if (!args.Has("-max_len=") && !args.Has("-merge=1")) {
@@ -141,8 +145,19 @@ int main(int argc, char **argv) {
 
   // Hand control to the libFuzzer driver.
   vector<char *> args_new(args.argv());
   argc = args_new.size();
   argv = args_new.data();
 
   return fuzzer::FuzzerDriver(&argc, &argv, Registry::Func(targetName));
 }
+
+extern "C" size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size,
+                                          size_t MaxSize, unsigned int Seed) {
+  if (gMutators.empty()) {
+    return 0;
+  }
+
+  // Forward to a pseudorandom mutator.
+  fuzzer::Random R(Seed);
+  return gMutators.at(R(gMutators.size()))(Data, Size, MaxSize, Seed);
+}
--- a/security/nss/fuzz/pkcs8_target.cc
+++ b/security/nss/fuzz/pkcs8_target.cc
@@ -29,9 +29,10 @@ extern "C" int pkcs8_fuzzing_target(cons
                                                nullptr) == SECSuccess) {
     SECKEY_DestroyPrivateKey(key);
   }
 
   PK11_FreeSlot(slot);
   return 0;
 }
 
-REGISTER_FUZZING_TARGET("pkcs8", pkcs8_fuzzing_target, 2048, "PKCS#8 Import")
+REGISTER_FUZZING_TARGET("pkcs8", pkcs8_fuzzing_target, 2048, "PKCS#8 Import",
+                        {})
--- a/security/nss/fuzz/quickder_targets.cc
+++ b/security/nss/fuzz/quickder_targets.cc
@@ -1,16 +1,16 @@
 /* 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 <stdint.h>
 
+#include "asn1_mutators.h"
 #include "cert.h"
-
 #include "registry.h"
 
 void QuickDERDecode(void *dst, const SEC_ASN1Template *tpl, const uint8_t *buf,
                     size_t len) {
   PORTCheapArenaPool pool;
   SECItem data = {siBuffer, const_cast<unsigned char *>(buf),
                   static_cast<unsigned int>(len)};
 
@@ -20,17 +20,19 @@ void QuickDERDecode(void *dst, const SEC
 }
 
 extern "C" int cert_fuzzing_target(const uint8_t *Data, size_t Size) {
   CERTCertificate cert;
   QuickDERDecode(&cert, SEC_SignedCertificateTemplate, Data, Size);
   return 0;
 }
 
-REGISTER_FUZZING_TARGET("cert", cert_fuzzing_target, 3072, "Certificate Import")
+REGISTER_FUZZING_TARGET("cert", cert_fuzzing_target, 3072, "Certificate Import",
+                        {&ASN1MutatorFlipConstructed, &ASN1MutatorChangeType})
 
 extern "C" int spki_fuzzing_target(const uint8_t *Data, size_t Size) {
   CERTSubjectPublicKeyInfo spki;
   QuickDERDecode(&spki, CERT_SubjectPublicKeyInfoTemplate, Data, Size);
   return 0;
 }
 
-REGISTER_FUZZING_TARGET("spki", spki_fuzzing_target, 1024, "SPKI Import")
+REGISTER_FUZZING_TARGET("spki", spki_fuzzing_target, 1024, "SPKI Import",
+                        {&ASN1MutatorFlipConstructed, &ASN1MutatorChangeType})
--- a/security/nss/fuzz/registry.h
+++ b/security/nss/fuzz/registry.h
@@ -6,66 +6,74 @@
 
 #ifndef registry_h__
 #define registry_h__
 
 #include <map>
 #include "FuzzerInternal.h"
 #include "nss.h"
 
+using namespace fuzzer;
+using namespace std;
+
+typedef decltype(LLVMFuzzerCustomMutator)* Mutator;
+
 class Registry {
  public:
-  static void Add(std::string name, fuzzer::UserCallback func, uint16_t max_len,
-                  std::string desc) {
+  static void Add(string name, UserCallback func, uint16_t max_len, string desc,
+                  vector<Mutator> mutators = {}) {
     assert(!Has(name));
-    GetInstance().targets_[name] = TargetData(func, max_len, desc);
+    GetInstance().targets_[name] = TargetData(func, max_len, desc, mutators);
   }
 
-  static bool Has(std::string name) {
+  static bool Has(string name) {
     return GetInstance().targets_.count(name) > 0;
   }
 
-  static fuzzer::UserCallback Func(std::string name) {
+  static UserCallback Func(string name) {
     assert(Has(name));
-    return std::get<0>(Get(name));
+    return get<0>(Get(name));
+  }
+
+  static uint16_t MaxLen(string name) {
+    assert(Has(name));
+    return get<1>(Get(name));
   }
 
-  static uint16_t MaxLen(std::string name) {
+  static string& Desc(string name) {
     assert(Has(name));
-    return std::get<1>(Get(name));
+    return get<2>(Get(name));
   }
 
-  static std::string& Desc(std::string name) {
+  static vector<Mutator>& Mutators(string name) {
     assert(Has(name));
-    return std::get<2>(Get(name));
+    return get<3>(Get(name));
   }
 
-  static std::vector<std::string> Names() {
-    std::vector<std::string> names;
+  static vector<string> Names() {
+    vector<string> names;
     for (auto& it : GetInstance().targets_) {
       names.push_back(it.first);
     }
     return names;
   }
 
  private:
-  typedef std::tuple<fuzzer::UserCallback, uint16_t, std::string> TargetData;
+  typedef tuple<UserCallback, uint16_t, string, vector<Mutator>> TargetData;
 
   static Registry& GetInstance() {
     static Registry registry;
     return registry;
   }
 
-  static TargetData& Get(std::string name) {
-    return GetInstance().targets_[name];
-  }
+  static TargetData& Get(string name) { return GetInstance().targets_[name]; }
 
   Registry() {}
 
-  std::map<std::string, TargetData> targets_;
+  map<string, TargetData> targets_;
 };
 
-#define REGISTER_FUZZING_TARGET(name, func, max_len, desc)     \
-  static void __attribute__((constructor)) Register_##func() { \
-    Registry::Add(name, func, max_len, desc);                  \
+#define REGISTER_FUZZING_TARGET(name, func, max_len, desc, ...) \
+  static void __attribute__((constructor)) Register_##func() {  \
+    Registry::Add(name, func, max_len, desc, __VA_ARGS__);      \
   }
 
 #endif  // registry_h__
--- a/security/nss/gtests/nss_bogo_shim/nss_bogo_shim.cc
+++ b/security/nss/gtests/nss_bogo_shim/nss_bogo_shim.cc
@@ -10,16 +10,17 @@
 #include <memory>
 #include "nspr.h"
 #include "nss.h"
 #include "prio.h"
 #include "prnetdb.h"
 #include "ssl.h"
 #include "sslerr.h"
 #include "sslproto.h"
+#include "ssl3prot.h"
 
 #include "nsskeys.h"
 
 static const char* kVersionDisableFlags[] = {
   "no-ssl3",
   "no-tls1",
   "no-tls11",
   "no-tls12",
@@ -147,43 +148,76 @@ class TestAgent {
         rv = SSL_GetClientAuthDataHook(ssl_fd_, GetClientAuthDataHook, this);
         if (rv != SECSuccess) return false;
       }
     }
 
     return true;
   }
 
+  static bool ConvertFromWireVersion(SSLProtocolVariant variant,
+                                     int wire_version,
+                                     uint16_t* lib_version) {
+    // These default values are used when {min,max}-version isn't given.
+    if (wire_version == 0 || wire_version == 0xffff) {
+      *lib_version = static_cast<uint16_t>(wire_version);
+      return true;
+    }
+
+#ifdef TLS_1_3_DRAFT_VERSION
+    if (wire_version == (0x7f00 | TLS_1_3_DRAFT_VERSION)) {
+      // N.B. SSL_LIBRARY_VERSION_DTLS_1_3_WIRE == SSL_LIBRARY_VERSION_TLS_1_3
+      wire_version = SSL_LIBRARY_VERSION_TLS_1_3;
+    }
+#endif
+
+    if (variant == ssl_variant_datagram) {
+      switch (wire_version) {
+      case SSL_LIBRARY_VERSION_DTLS_1_0_WIRE:
+        *lib_version = SSL_LIBRARY_VERSION_DTLS_1_0;
+        break;
+      case SSL_LIBRARY_VERSION_DTLS_1_2_WIRE:
+        *lib_version = SSL_LIBRARY_VERSION_DTLS_1_2;
+        break;
+      case SSL_LIBRARY_VERSION_DTLS_1_3_WIRE:
+        *lib_version = SSL_LIBRARY_VERSION_DTLS_1_3;
+        break;
+      default:
+        std::cerr << "Unrecognized DTLS version " << wire_version << ".\n";
+        return false;
+      }
+    } else {
+      if (wire_version < SSL_LIBRARY_VERSION_3_0 ||
+          wire_version > SSL_LIBRARY_VERSION_TLS_1_3) {
+        std::cerr << "Unrecognized TLS version " << wire_version << ".\n";
+        return false;
+      }
+      *lib_version = static_cast<uint16_t>(wire_version);
+    }
+    return true;
+  }
+
   bool GetVersionRange(SSLVersionRange* range_out, SSLProtocolVariant variant) {
     SSLVersionRange supported;
     if (SSL_VersionRangeGetSupported(variant, &supported) != SECSuccess) {
       return false;
     }
 
-    auto max_allowed = static_cast<uint16_t>(cfg_.get<int>("max-version"));
-    if (variant == ssl_variant_datagram) {
-      // For DTLS this is the wire version; adjust if needed.
-      switch (max_allowed) {
-      case SSL_LIBRARY_VERSION_DTLS_1_0_WIRE:
-        max_allowed = SSL_LIBRARY_VERSION_DTLS_1_0;
-        break;
-      case SSL_LIBRARY_VERSION_DTLS_1_2_WIRE:
-        max_allowed = SSL_LIBRARY_VERSION_DTLS_1_2;
-        break;
-      case SSL_LIBRARY_VERSION_DTLS_1_3_WIRE:
-        max_allowed = SSL_LIBRARY_VERSION_DTLS_1_3;
-        break;
-      case 0xffff: // No maximum specified.
-        break;
-      default:
-        // Unrecognized DTLS version.
-        return false;
-      }
+    uint16_t min_allowed;
+    uint16_t max_allowed;
+    if (!ConvertFromWireVersion(variant, cfg_.get<int>("min-version"),
+                                &min_allowed)) {
+      return false;
+    }
+    if (!ConvertFromWireVersion(variant, cfg_.get<int>("max-version"),
+                                &max_allowed)) {
+      return false;
     }
 
+    min_allowed = std::max(min_allowed, supported.min);
     max_allowed = std::min(max_allowed, supported.max);
 
     bool found_min = false;
     bool found_max = false;
     // Ignore -no-ssl3, because SSLv3 is never supported.
     for (size_t i = 1; i < PR_ARRAY_SIZE(kVersionDisableFlags); ++i) {
       auto version =
         static_cast<uint16_t>(SSL_LIBRARY_VERSION_TLS_1_0 + (i - 1));
@@ -194,17 +228,17 @@ class TestAgent {
           // DTLS 1.1 doesn't exist.
           continue;
         }
         if (version == SSL_LIBRARY_VERSION_TLS_1_0) {
           version = SSL_LIBRARY_VERSION_DTLS_1_0;
         }
       }
 
-      if (version < supported.min) {
+      if (version < min_allowed) {
         continue;
       }
       if (version > max_allowed) {
         break;
       }
 
       const bool allowed = !cfg_.get<bool>(kVersionDisableFlags[i]);
 
@@ -215,22 +249,24 @@ class TestAgent {
       if (found_min && !found_max) {
         if (allowed) {
           range_out->max = version;
         } else {
           found_max = true;
         }
       }
       if (found_max && allowed) {
-        // Discontiguous range.
+        std::cerr << "Discontiguous version range.\n";
         return false;
       }
     }
 
-    // Iff found_min is still false, no usable version was found.
+    if (!found_min) {
+      std::cerr << "All versions disabled.\n";
+    }
     return found_min;
   }
 
   bool SetupOptions() {
     SECStatus rv = SSL_OptionSet(ssl_fd_, SSL_ENABLE_SESSION_TICKETS, PR_TRUE);
     if (rv != SECSuccess) return false;
 
     SSLVersionRange vrange;
@@ -349,16 +385,17 @@ class TestAgent {
 std::unique_ptr<const Config> ReadConfig(int argc, char** argv) {
   std::unique_ptr<Config> cfg(new Config());
 
   cfg->AddEntry<int>("port", 0);
   cfg->AddEntry<bool>("server", false);
   cfg->AddEntry<int>("resume-count", 0);
   cfg->AddEntry<std::string>("key-file", "");
   cfg->AddEntry<std::string>("cert-file", "");
+  cfg->AddEntry<int>("min-version", 0);
   cfg->AddEntry<int>("max-version", 0xffff);
   for (auto flag : kVersionDisableFlags) {
     cfg->AddEntry<bool>(flag, false);
   }
 
   auto rv = cfg->ParseArgs(argc, argv);
   switch (rv) {
     case Config::kOK:
--- a/security/nss/gtests/ssl_gtest/libssl_internals.c
+++ b/security/nss/gtests/ssl_gtest/libssl_internals.c
@@ -5,18 +5,16 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* This file contains functions for frobbing the internals of libssl */
 #include "libssl_internals.h"
 
 #include "nss.h"
 #include "pk11pub.h"
 #include "seccomon.h"
-#include "ssl.h"
-#include "sslimpl.h"
 
 SECStatus SSLInt_IncrementClientHandshakeVersion(PRFileDesc *fd) {
   sslSocket *ss = ssl_FindSocket(fd);
   if (!ss) {
     return SECFailure;
   }
 
   ++ss->clientHelloVersion;
@@ -308,8 +306,42 @@ SECStatus SSLInt_AdvanceWriteSeqByAWindo
 }
 
 SSLKEAType SSLInt_GetKEAType(SSLNamedGroup group) {
   const sslNamedGroupDef *groupDef = ssl_LookupNamedGroup(group);
   if (!groupDef) return ssl_kea_null;
 
   return groupDef->keaType;
 }
+
+SECStatus SSLInt_SetCipherSpecChangeFunc(PRFileDesc *fd,
+                                         sslCipherSpecChangedFunc func,
+                                         void *arg) {
+  sslSocket *ss;
+
+  ss = ssl_FindSocket(fd);
+  if (!ss) {
+    return SECFailure;
+  }
+
+  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;
+}
--- a/security/nss/gtests/ssl_gtest/libssl_internals.h
+++ b/security/nss/gtests/ssl_gtest/libssl_internals.h
@@ -6,16 +6,18 @@
 
 #ifndef libssl_internals_h_
 #define libssl_internals_h_
 
 #include <stdint.h>
 
 #include "prio.h"
 #include "seccomon.h"
+#include "ssl.h"
+#include "sslimpl.h"
 #include "sslt.h"
 
 SECStatus SSLInt_IncrementClientHandshakeVersion(PRFileDesc *fd);
 
 SECStatus SSLInt_UpdateSSLv2ClientRandom(PRFileDesc *fd, uint8_t *rnd,
                                          size_t rnd_len, uint8_t *msg,
                                          size_t msg_len);
 
@@ -33,9 +35,17 @@ SECStatus SSLInt_Set0RttAlpn(PRFileDesc 
 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);
+
 #endif  // ndef libssl_internals_h_
--- a/security/nss/gtests/ssl_gtest/manifest.mn
+++ b/security/nss/gtests/ssl_gtest/manifest.mn
@@ -19,32 +19,34 @@ CPPSRCS = \
       ssl_ciphersuite_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 \
       ssl_fuzz_unittest.cc \
       ssl_gtest.cc \
       ssl_hrr_unittest.cc \
       ssl_loopback_unittest.cc \
       ssl_record_unittest.cc \
       ssl_resumption_unittest.cc \
       ssl_skip_unittest.cc \
       ssl_staticrsa_unittest.cc \
       ssl_v2_client_hello_unittest.cc \
       ssl_version_unittest.cc \
       test_io.cc \
       tls_agent.cc \
       tls_connect.cc \
       tls_hkdf_unittest.cc \
       tls_filter.cc \
       tls_parser.cc \
+      tls_protect.cc \
       $(NULL)
 
 INCLUDES += -I$(CORE_DEPTH)/gtests/google_test/gtest/include \
             -I$(CORE_DEPTH)/gtests/common
 
 REQUIRES = nspr nss libdbm gtest
 
 PROGRAM = ssl_gtest
--- a/security/nss/gtests/ssl_gtest/ssl_0rtt_unittest.cc
+++ b/security/nss/gtests/ssl_gtest/ssl_0rtt_unittest.cc
@@ -195,9 +195,87 @@ TEST_P(TlsConnectTls13, TestTls13ZeroRtt
     return false;
   });
   Handshake();
   CheckConnected();
   SendReceive();
   CheckAlpn("b");
 }
 
+// The client should abort the connection when sending a 0-rtt handshake but
+// the servers responds with a TLS 1.2 ServerHello. (no app data sent)
+TEST_P(TlsConnectTls13, TestTls13ZeroRttDowngrade) {
+  ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
+  server_->Set0RttEnabled(true);  // set ticket_allow_early_data
+  Connect();
+
+  SendReceive();  // Need to read so that we absorb the session tickets.
+  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();
+
+  // 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_->Handshake();
+  server_->Handshake();
+  ASSERT_TRUE_WAIT(
+      (client_->error_code() == SSL_ERROR_RX_MALFORMED_SERVER_HELLO), 2000);
+
+  // DTLS will timeout as we bump the epoch when installing the early app data
+  // cipher suite. Thus the encrypted alert will be ignored.
+  if (mode_ == STREAM) {
+    // The client sends an encrypted alert message.
+    ASSERT_TRUE_WAIT(
+        (server_->error_code() == SSL_ERROR_RX_UNEXPECTED_APPLICATION_DATA),
+        2000);
+  }
+}
+
+// The client should abort the connection when sending a 0-rtt handshake but
+// the servers responds with a TLS 1.2 ServerHello. (with app data)
+TEST_P(TlsConnectTls13, TestTls13ZeroRttDowngradeEarlyData) {
+  ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
+  server_->Set0RttEnabled(true);  // set ticket_allow_early_data
+  Connect();
+
+  SendReceive();  // Need to read so that we absorb the session tickets.
+  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();
+
+  // 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);
+
+  client_->Handshake();
+  server_->Handshake();
+  ASSERT_TRUE_WAIT(
+      (client_->error_code() == SSL_ERROR_RX_MALFORMED_SERVER_HELLO), 2000);
+
+  // DTLS will timeout as we bump the epoch when installing the early app data
+  // cipher suite. Thus the encrypted alert will be ignored.
+  if (mode_ == STREAM) {
+    // The server sends an alert when receiving the early app data record.
+    ASSERT_TRUE_WAIT(
+        (server_->error_code() == SSL_ERROR_RX_UNEXPECTED_APPLICATION_DATA),
+        2000);
+  }
+}
+
 }  // namespace nss_test
--- a/security/nss/gtests/ssl_gtest/ssl_auth_unittest.cc
+++ b/security/nss/gtests/ssl_gtest/ssl_auth_unittest.cc
@@ -284,17 +284,17 @@ class BeforeFinished : public TlsRecordF
                  VoidFunction before_finished)
       : client_(client),
         server_(server),
         before_ccs_(before_ccs),
         before_finished_(before_finished),
         state_(BEFORE_CCS) {}
 
  protected:
-  virtual PacketFilter::Action FilterRecord(const RecordHeader& header,
+  virtual PacketFilter::Action FilterRecord(const TlsRecordHeader& header,
                                             const DataBuffer& body,
                                             DataBuffer* out) {
     switch (state_) {
       case BEFORE_CCS:
         // Awaken when we see the CCS.
         if (header.content_type() == kTlsChangeCipherSpecType) {
           before_ccs_();
 
@@ -502,17 +502,17 @@ TEST_P(TlsConnectGenericPre13, AuthCompl
 
   // This should allow the handshake to complete now.
   EXPECT_EQ(SECSuccess, SSL_AuthCertificateComplete(client_->ssl_fd(), 0));
   client_->Handshake();  // Transition to connected
   EXPECT_EQ(TlsAgent::STATE_CONNECTED, client_->state());
   EXPECT_EQ(TlsAgent::STATE_CONNECTED, server_->state());
 
   // Remove this before closing or the close_notify alert will trigger it.
-  client_->SetPacketFilter(nullptr);
+  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();
@@ -523,17 +523,17 @@ TEST_P(TlsConnectTls13, AuthCompleteDela
   EXPECT_EQ(TlsAgent::STATE_CONNECTING, server_->state());
 
   // The client will send nothing until AuthCertificateComplete is called.
   client_->SetPacketFilter(new EnforceNoActivity());
   client_->Handshake();
   EXPECT_EQ(TlsAgent::STATE_CONNECTING, client_->state());
 
   // This should allow the handshake to complete now.
-  client_->SetPacketFilter(nullptr);
+  client_->DeletePacketFilter();
   EXPECT_EQ(SECSuccess, SSL_AuthCertificateComplete(client_->ssl_fd(), 0));
   client_->Handshake();  // Send Finished
   server_->Handshake();  // Transition to connected and send NewSessionTicket
   EXPECT_EQ(TlsAgent::STATE_CONNECTED, client_->state());
   EXPECT_EQ(TlsAgent::STATE_CONNECTED, server_->state());
 }
 
 static const SSLExtraServerCertData ServerCertDataRsaPkcs1Decrypt = {
new file mode 100644
--- /dev/null
+++ b/security/nss/gtests/ssl_gtest/ssl_fragment_unittest.cc
@@ -0,0 +1,157 @@
+/* -*- 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 "sslerr.h"
+#include "sslproto.h"
+
+#include "gtest_utils.h"
+#include "scoped_ptrs.h"
+#include "tls_connect.h"
+#include "tls_filter.h"
+#include "tls_parser.h"
+
+namespace nss_test {
+
+// This class cuts every unencrypted handshake record into two parts.
+class RecordFragmenter : public PacketFilter {
+ public:
+  RecordFragmenter() : sequence_number_(0), splitting_(true) {}
+
+ private:
+  class HandshakeSplitter {
+   public:
+    HandshakeSplitter(const DataBuffer& input, DataBuffer* output,
+                      uint64_t* sequence_number)
+        : input_(input),
+          output_(output),
+          cursor_(0),
+          sequence_number_(sequence_number) {}
+
+   private:
+    void WriteRecord(TlsRecordHeader& record_header,
+                     DataBuffer& record_fragment) {
+      TlsRecordHeader fragment_header(record_header.version(),
+                                      record_header.content_type(),
+                                      *sequence_number_);
+      ++*sequence_number_;
+      if (::g_ssl_gtest_verbose) {
+        std::cerr << "Fragment: " << fragment_header << ' ' << record_fragment
+                  << std::endl;
+      }
+      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)) {
+          ADD_FAILURE() << "couldn't parse handshake header";
+          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;
+        }
+
+        size_t cut = handshake_body.len() / 2;
+        handshake_header.WriteFragment(&record_fragment, 0U, handshake_body, 0U,
+                                       cut);
+        WriteRecord(record_header, record_fragment);
+
+        handshake_header.WriteFragment(&record_fragment, 0U, handshake_body,
+                                       cut, handshake_body.len() - cut);
+        WriteRecord(record_header, record_fragment);
+      }
+      return true;
+    }
+
+   public:
+    bool Split() {
+      TlsParser parser(input_);
+      while (parser.remaining()) {
+        TlsRecordHeader header;
+        DataBuffer record;
+        if (!header.Parse(&parser, &record)) {
+          ADD_FAILURE() << "bad record header";
+          return false;
+        }
+
+        if (::g_ssl_gtest_verbose) {
+          std::cerr << "Record: " << header << ' ' << record << std::endl;
+        }
+
+        // Don't touch packets from a non-zero epoch.  Leave these unmodified.
+        if ((header.sequence_number() >> 48) != 0ULL) {
+          cursor_ = header.Write(output_, cursor_, record);
+          continue;
+        }
+
+        // Just rewrite the sequence number (CCS only).
+        if (header.content_type() != kTlsHandshakeType) {
+          EXPECT_EQ(kTlsChangeCipherSpecType, header.content_type());
+          WriteRecord(header, record);
+          continue;
+        }
+
+        if (!SplitRecord(header, record)) {
+          return false;
+        }
+      }
+      return true;
+    }
+
+   private:
+    const DataBuffer& input_;
+    DataBuffer* output_;
+    size_t cursor_;
+    uint64_t* sequence_number_;
+  };
+
+ protected:
+  virtual PacketFilter::Action Filter(const DataBuffer& input,
+                                      DataBuffer* output) override {
+    if (!splitting_) {
+      return KEEP;
+    }
+
+    output->Allocate(input.len());
+    HandshakeSplitter splitter(input, output, &sequence_number_);
+    if (!splitter.Split()) {
+      // If splitting fails, we obviously reached encrypted packets.
+      // Stop splitting from that point onward.
+      splitting_ = false;
+      return KEEP;
+    }
+
+    return CHANGE;
+  }
+
+ private:
+  uint64_t sequence_number_;
+  bool splitting_;
+};
+
+TEST_P(TlsConnectDatagram, FragmentClientPackets) {
+  client_->SetPacketFilter(new RecordFragmenter());
+  Connect();
+  SendReceive();
+}
+
+TEST_P(TlsConnectDatagram, FragmentServerPackets) {
+  server_->SetPacketFilter(new RecordFragmenter());
+  Connect();
+  SendReceive();
+}
+
+}  // namespace nss_test
--- a/security/nss/gtests/ssl_gtest/ssl_fuzz_unittest.cc
+++ b/security/nss/gtests/ssl_gtest/ssl_fuzz_unittest.cc
@@ -7,28 +7,37 @@
 #include "sslimpl.h"
 #include "tls_connect.h"
 
 #include "gtest/gtest.h"
 
 namespace nss_test {
 
 #ifdef UNSAFE_FUZZER_MODE
+#define FUZZ_F(c, f) TEST_F(c, Fuzz_##f)
+#define FUZZ_P(c, f) TEST_P(c, Fuzz_##f)
+#else
+#define FUZZ_F(c, f) TEST_F(c, DISABLED_Fuzz_##f)
+#define FUZZ_P(c, f) TEST_P(c, DISABLED_Fuzz_##f)
+// RNG_ResetForFuzzing() isn't exported from the shared libraries, rather than
+// fail to link to it, make it fail (we're not running it anyway).
+#define RNG_ResetForFuzzing() SECFailure
+#endif
 
 const uint8_t kShortEmptyFinished[8] = {0};
 const uint8_t kLongEmptyFinished[128] = {0};
 
 class TlsFuzzTest : public ::testing::Test {};
 
 // Record the application data stream.
 class TlsApplicationDataRecorder : public TlsRecordFilter {
  public:
   TlsApplicationDataRecorder() : buffer_() {}
 
-  virtual PacketFilter::Action FilterRecord(const RecordHeader& header,
+  virtual PacketFilter::Action FilterRecord(const TlsRecordHeader& header,
                                             const DataBuffer& input,
                                             DataBuffer* output) {
     if (header.content_type() == kTlsApplicationDataType) {
       buffer_.Append(input);
     }
 
     return KEEP;
   }
@@ -56,102 +65,101 @@ class TlsSignatureDamager : public TlsHa
     output->data()[output->len() - 1]++;
     return CHANGE;
   }
 
  private:
   uint8_t type_;
 };
 
-void ResetState() {
-  // Clear the list of RSA blinding params.
-  BL_Cleanup();
-
-  // Reinit the list of RSA blinding params.
-  EXPECT_EQ(SECSuccess, BL_Init());
-
-  // Reset the RNG state.
-  EXPECT_EQ(SECSuccess, RNG_ResetForFuzzing());
-}
-
 // Ensure that ssl_Time() returns a constant value.
-TEST_F(TlsFuzzTest, Fuzz_SSL_Time_Constant) {
-  PRInt32 now = ssl_Time();
+FUZZ_F(TlsFuzzTest, SSL_Time_Constant) {
+  PRUint32 now = ssl_Time();
   PR_Sleep(PR_SecondsToInterval(2));
   EXPECT_EQ(ssl_Time(), now);
 }
 
 // Check that due to the deterministic PRNG we derive
 // the same master secret in two consecutive TLS sessions.
-TEST_P(TlsConnectGeneric, Fuzz_DeterministicExporter) {
+FUZZ_P(TlsConnectGeneric, DeterministicExporter) {
   const char kLabel[] = "label";
   std::vector<unsigned char> out1(32), out2(32);
 
+  // Make sure we have RSA blinding params.
+  Connect();
+
+  Reset();
   ConfigureSessionCache(RESUME_NONE, RESUME_NONE);
   DisableECDHEServerKeyReuse();
 
-  ResetState();
+  // Reset the RNG state.
+  EXPECT_EQ(SECSuccess, RNG_ResetForFuzzing());
   Connect();
 
   // Export a key derived from the MS and nonces.
   SECStatus rv =
       SSL_ExportKeyingMaterial(client_->ssl_fd(), kLabel, strlen(kLabel), false,
                                NULL, 0, out1.data(), out1.size());
   EXPECT_EQ(SECSuccess, rv);
 
   Reset();
   ConfigureSessionCache(RESUME_NONE, RESUME_NONE);
   DisableECDHEServerKeyReuse();
 
-  ResetState();
+  // Reset the RNG state.
+  EXPECT_EQ(SECSuccess, RNG_ResetForFuzzing());
   Connect();
 
   // Export another key derived from the MS and nonces.
   rv = SSL_ExportKeyingMaterial(client_->ssl_fd(), kLabel, strlen(kLabel),
                                 false, NULL, 0, out2.data(), out2.size());
   EXPECT_EQ(SECSuccess, rv);
 
   // The two exported keys should be the same.
   EXPECT_EQ(out1, out2);
 }
 
 // Check that due to the deterministic RNG two consecutive
 // TLS sessions will have the exact same transcript.
-TEST_P(TlsConnectGeneric, Fuzz_DeterministicTranscript) {
+FUZZ_P(TlsConnectGeneric, DeterministicTranscript) {
+  // Make sure we have RSA blinding params.
+  Connect();
+
   // Connect a few times and compare the transcripts byte-by-byte.
   DataBuffer last;
   for (size_t i = 0; i < 5; i++) {
     Reset();
     ConfigureSessionCache(RESUME_NONE, RESUME_NONE);
     DisableECDHEServerKeyReuse();
 
     DataBuffer buffer;
     client_->SetPacketFilter(new TlsConversationRecorder(buffer));
     server_->SetPacketFilter(new TlsConversationRecorder(buffer));
 
-    ResetState();
+    // Reset the RNG state.
+    EXPECT_EQ(SECSuccess, RNG_ResetForFuzzing());
     Connect();
 
     // Ensure the filters go away before |buffer| does.
-    client_->SetPacketFilter(nullptr);
-    server_->SetPacketFilter(nullptr);
+    client_->DeletePacketFilter();
+    server_->DeletePacketFilter();
 
     if (last.len() > 0) {
       EXPECT_EQ(last, buffer);
     }
 
     last = buffer;
   }
 }
 
 // Check that we can establish and use a connection
 // with all supported TLS versions, STREAM and DGRAM.
 // Check that records are NOT encrypted.
 // Check that records don't have a MAC.
-TEST_P(TlsConnectGeneric, Fuzz_ConnectSendReceive_NullCipher) {
+FUZZ_P(TlsConnectGeneric, ConnectSendReceive_NullCipher) {
   EnsureTlsSetup();
 
   // Set up app data filters.
   auto client_recorder = new TlsApplicationDataRecorder();
   client_->SetPacketFilter(client_recorder);
   auto server_recorder = new TlsApplicationDataRecorder();
   server_->SetPacketFilter(server_recorder);
 
@@ -170,54 +178,52 @@ TEST_P(TlsConnectGeneric, Fuzz_ConnectSe
   Receive(buf.len());
 
   // Check for plaintext on the wire.
   EXPECT_EQ(buf, client_recorder->buffer());
   EXPECT_EQ(buf, server_recorder->buffer());
 }
 
 // Check that an invalid Finished message doesn't abort the connection.
-TEST_P(TlsConnectGeneric, Fuzz_BogusClientFinished) {
+FUZZ_P(TlsConnectGeneric, BogusClientFinished) {
   EnsureTlsSetup();
 
   auto i1 = new TlsInspectorReplaceHandshakeMessage(
       kTlsHandshakeFinished,
       DataBuffer(kShortEmptyFinished, sizeof(kShortEmptyFinished)));
   client_->SetPacketFilter(i1);
   Connect();
   SendReceive();
 }
 
 // Check that an invalid Finished message doesn't abort the connection.
-TEST_P(TlsConnectGeneric, Fuzz_BogusServerFinished) {
+FUZZ_P(TlsConnectGeneric, BogusServerFinished) {
   EnsureTlsSetup();
 
   auto i1 = new TlsInspectorReplaceHandshakeMessage(
       kTlsHandshakeFinished,
       DataBuffer(kLongEmptyFinished, sizeof(kLongEmptyFinished)));
   server_->SetPacketFilter(i1);
   Connect();
   SendReceive();
 }
 
 // Check that an invalid server auth signature doesn't abort the connection.
-TEST_P(TlsConnectGeneric, Fuzz_BogusServerAuthSignature) {
+FUZZ_P(TlsConnectGeneric, BogusServerAuthSignature) {
   EnsureTlsSetup();
   uint8_t msg_type = version_ == SSL_LIBRARY_VERSION_TLS_1_3
                          ? kTlsHandshakeCertificateVerify
                          : kTlsHandshakeServerKeyExchange;
   server_->SetPacketFilter(new TlsSignatureDamager(msg_type));
   Connect();
   SendReceive();
 }
 
 // Check that an invalid client auth signature doesn't abort the connection.
-TEST_P(TlsConnectGeneric, Fuzz_BogusClientAuthSignature) {
+FUZZ_P(TlsConnectGeneric, BogusClientAuthSignature) {
   EnsureTlsSetup();
   client_->SetupClientAuth();
   server_->RequestClientAuth(true);
   client_->SetPacketFilter(
       new TlsSignatureDamager(kTlsHandshakeCertificateVerify));
   Connect();
 }
-
-#endif
 }
--- a/security/nss/gtests/ssl_gtest/ssl_gtest.gyp
+++ b/security/nss/gtests/ssl_gtest/ssl_gtest.gyp
@@ -20,31 +20,33 @@
         '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',
+        'ssl_fragment_unittest.cc',
         'ssl_gtest.cc',
         'ssl_hrr_unittest.cc',
         'ssl_loopback_unittest.cc',
         'ssl_record_unittest.cc',
         'ssl_resumption_unittest.cc',
         'ssl_skip_unittest.cc',
         'ssl_staticrsa_unittest.cc',
         'ssl_v2_client_hello_unittest.cc',
         'ssl_version_unittest.cc',
         'test_io.cc',
         'tls_agent.cc',
         'tls_connect.cc',
         'tls_filter.cc',
         'tls_hkdf_unittest.cc',
-        'tls_parser.cc'
+        'tls_parser.cc',
+        'tls_protect.cc'
       ],
       'dependencies': [
         '<(DEPTH)/exports.gyp:nss_exports',
         '<(DEPTH)/lib/util/util.gyp:nssutil3',
         '<(DEPTH)/lib/sqlite/sqlite.gyp:sqlite3',
         '<(DEPTH)/gtests/google_test/google_test.gyp:gtest',
         '<(DEPTH)/lib/softoken/softoken.gyp:softokn',
         '<(DEPTH)/lib/smime/smime.gyp:smime',
--- a/security/nss/gtests/ssl_gtest/ssl_loopback_unittest.cc
+++ b/security/nss/gtests/ssl_gtest/ssl_loopback_unittest.cc
@@ -156,26 +156,25 @@ TEST_P(TlsConnectDatagram, TestDtlsHoldd
     // One for send, one for receive.
     EXPECT_EQ(2, SSLInt_CountTls13CipherSpecs(client_->ssl_fd()));
   }
 }
 
 class TlsPreCCSHeaderInjector : public TlsRecordFilter {
  public:
   TlsPreCCSHeaderInjector() {}
-  virtual PacketFilter::Action FilterRecord(const RecordHeader& record_header,
-                                            const DataBuffer& input,
-                                            size_t* offset,
-                                            DataBuffer* output) override {
+  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;
 
     std::cerr << "Injecting Finished header before CCS\n";
     const uint8_t hhdr[] = {kTlsHandshakeFinished, 0x00, 0x00, 0x0c};
     DataBuffer hhdr_buf(hhdr, sizeof(hhdr));
-    RecordHeader nhdr(record_header.version(), kTlsHandshakeType, 0);
+    TlsRecordHeader nhdr(record_header.version(), kTlsHandshakeType, 0);
     *offset = nhdr.Write(output, *offset, hhdr_buf);
     *offset = record_header.Write(output, *offset, input);
     return CHANGE;
   }
 };
 
 TEST_P(TlsConnectStreamPre13, ClientFinishedHeaderBeforeCCS) {
   client_->SetPacketFilter(new TlsPreCCSHeaderInjector());
--- a/security/nss/gtests/ssl_gtest/ssl_resumption_unittest.cc
+++ b/security/nss/gtests/ssl_gtest/ssl_resumption_unittest.cc
@@ -16,16 +16,17 @@ extern "C" {
 #include "libssl_internals.h"
 }
 
 #include "gtest_utils.h"
 #include "scoped_ptrs.h"
 #include "tls_connect.h"
 #include "tls_filter.h"
 #include "tls_parser.h"
+#include "tls_protect.h"
 
 namespace nss_test {
 
 class TlsServerKeyExchangeEcdhe {
  public:
   bool Parse(const DataBuffer& buffer) {
     TlsParser parser(buffer);
 
@@ -574,9 +575,68 @@ TEST_F(TlsConnectTest, TestTls13Resumpti
   Reset();
   ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
   ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3);
   ExpectResumption(RESUME_TICKET);
   Connect();
   SendReceive();
 }
 
+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();
+
+  // Try resuming the connection. This will fail resuming the 1.3 session
+  // from before, but will successfully establish a 1.2 connection.
+  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);
+  Connect();
+
+  // Renegotiate to ensure we don't carryover any state
+  // from the 1.3 resumption attempt.
+  client_->SetExpectedVersion(SSL_LIBRARY_VERSION_TLS_1_2);
+  client_->PrepareForRenegotiate();
+  server_->StartRenegotiate();
+  Handshake();
+
+  SendReceive();
+  CheckKeys();
+}
+
+TEST_F(TlsConnectTest, TestTls13ResumptionForcedDowngrade) {
+  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();
+
+  // Try resuming the connection.
+  Reset();
+  ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3);
+  ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
+  // Enable the lower version on the client.
+  client_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_2,
+                           SSL_LIBRARY_VERSION_TLS_1_3);
+
+  // 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<PacketFilter*> filters;
+  filters.push_back(new SelectedCipherSuiteReplacer(
+      TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256));
+  filters.push_back(new SelectedVersionReplacer(SSL_LIBRARY_VERSION_TLS_1_2));
+  server_->SetPacketFilter(new ChainedPacketFilter(filters));
+
+  ConnectExpectFail();
+  client_->CheckErrorCode(SSL_ERROR_RX_MALFORMED_SERVER_HELLO);
+  server_->CheckErrorCode(SSL_ERROR_BAD_MAC_READ);
+}
+
 }  // namespace nss_test
--- a/security/nss/gtests/ssl_gtest/ssl_skip_unittest.cc
+++ b/security/nss/gtests/ssl_gtest/ssl_skip_unittest.cc
@@ -23,19 +23,19 @@ class TlsHandshakeSkipFilter : public Tl
  public:
   // A TLS record filter that skips handshake messages of the identified type.
   TlsHandshakeSkipFilter(uint8_t handshake_type)
       : handshake_type_(handshake_type), skipped_(false) {}
 
  protected:
   // Takes a record; if it is a handshake record, it removes the first handshake
   // message that is of handshake_type_ type.
-  virtual PacketFilter::Action FilterRecord(const RecordHeader& record_header,
-                                            const DataBuffer& input,
-                                            DataBuffer* output) {
+  virtual PacketFilter::Action FilterRecord(
+      const TlsRecordHeader& record_header, const DataBuffer& input,
+      DataBuffer* output) {
     if (record_header.content_type() != kTlsHandshakeType) {
       return KEEP;
     }
 
     size_t output_offset = 0U;
     output->Allocate(input.len());
 
     TlsParser parser(input);
@@ -93,16 +93,50 @@ class TlsSkipTest
       server_->SetPacketFilter(filter);
     }
     ConnectExpectFail();
     EXPECT_EQ(kTlsAlertFatal, alert_recorder->level());
     EXPECT_EQ(alert, alert_recorder->description());
   }
 };
 
+class Tls13SkipTest : public TlsConnectTestBase,
+                      public ::testing::WithParamInterface<std::string> {
+ protected:
+  Tls13SkipTest()
+      : TlsConnectTestBase(GetParam(), SSL_LIBRARY_VERSION_TLS_1_3) {}
+
+  void ServerSkipTest(TlsRecordFilter* filter, int32_t error) {
+    EnsureTlsSetup();
+    server_->SetPacketFilter(filter);
+    filter->EnableDecryption();
+    if (mode_ == STREAM) {
+      ConnectExpectFail();
+    } else {
+      ConnectExpectFailOneSide(TlsAgent::CLIENT);
+    }
+    client_->CheckErrorCode(error);
+    if (mode_ == STREAM) {
+      server_->CheckErrorCode(SSL_ERROR_BAD_MAC_READ);
+    } else {
+      ASSERT_EQ(TlsAgent::STATE_CONNECTING, server_->state());
+    }
+  }
+
+  void ClientSkipTest(TlsRecordFilter* filter, int32_t error) {
+    EnsureTlsSetup();
+    client_->SetPacketFilter(filter);
+    filter->EnableDecryption();
+    ConnectExpectFailOneSide(TlsAgent::SERVER);
+
+    server_->CheckErrorCode(error);
+    ASSERT_EQ(TlsAgent::STATE_CONNECTED, client_->state());
+  }
+};
+
 TEST_P(TlsSkipTest, SkipCertificateRsa) {
   EnableOnlyStaticRsaCiphers();
   ServerSkipTest(new TlsHandshakeSkipFilter(kTlsHandshakeCertificate));
   client_->CheckErrorCode(SSL_ERROR_RX_UNEXPECTED_HELLO_DONE);
 }
 
 TEST_P(TlsSkipTest, SkipCertificateDhe) {
   ServerSkipTest(new TlsHandshakeSkipFilter(kTlsHandshakeCertificate));
@@ -143,16 +177,46 @@ TEST_P(TlsSkipTest, SkipCertAndKeyExchEc
   Reset(TlsAgent::kServerEcdsa256);
   auto chain = new ChainedPacketFilter();
   chain->Add(new TlsHandshakeSkipFilter(kTlsHandshakeCertificate));
   chain->Add(new TlsHandshakeSkipFilter(kTlsHandshakeServerKeyExchange));
   ServerSkipTest(chain);
   client_->CheckErrorCode(SSL_ERROR_RX_UNEXPECTED_HELLO_DONE);
 }
 
+TEST_P(Tls13SkipTest, SkipEncryptedExtensions) {
+  ServerSkipTest(new TlsHandshakeSkipFilter(kTlsHandshakeEncryptedExtensions),
+                 SSL_ERROR_RX_UNEXPECTED_CERTIFICATE);
+}
+
+TEST_P(Tls13SkipTest, SkipServerCertificate) {
+  ServerSkipTest(new TlsHandshakeSkipFilter(kTlsHandshakeCertificate),
+                 SSL_ERROR_RX_UNEXPECTED_CERT_VERIFY);
+}
+
+TEST_P(Tls13SkipTest, SkipServerCertificateVerify) {
+  ServerSkipTest(new TlsHandshakeSkipFilter(kTlsHandshakeCertificateVerify),
+                 SSL_ERROR_RX_UNEXPECTED_FINISHED);
+}
+
+TEST_P(Tls13SkipTest, SkipClientCertificate) {
+  client_->SetupClientAuth();
+  server_->RequestClientAuth(true);
+  ClientSkipTest(new TlsHandshakeSkipFilter(kTlsHandshakeCertificate),
+                 SSL_ERROR_RX_UNEXPECTED_CERT_VERIFY);
+}
+
+TEST_P(Tls13SkipTest, SkipClientCertificateVerify) {
+  client_->SetupClientAuth();
+  server_->RequestClientAuth(true);
+  ClientSkipTest(new TlsHandshakeSkipFilter(kTlsHandshakeCertificateVerify),
+                 SSL_ERROR_RX_UNEXPECTED_FINISHED);
+}
+
 INSTANTIATE_TEST_CASE_P(SkipTls10, TlsSkipTest,
                         ::testing::Combine(TlsConnectTestBase::kTlsModesStream,
                                            TlsConnectTestBase::kTlsV10));
 INSTANTIATE_TEST_CASE_P(SkipVariants, TlsSkipTest,
                         ::testing::Combine(TlsConnectTestBase::kTlsModesAll,
                                            TlsConnectTestBase::kTlsV11V12));
-
+INSTANTIATE_TEST_CASE_P(Skip13Variants, Tls13SkipTest,
+                        TlsConnectTestBase::kTlsModesAll);
 }  // namespace nss_test
--- a/security/nss/gtests/ssl_gtest/tls_agent.cc
+++ b/security/nss/gtests/ssl_gtest/tls_agent.cc
@@ -844,16 +844,17 @@ void TlsAgent::ConfigureSessionCache(Ses
   EXPECT_EQ(SECSuccess, rv);
 
   rv = SSL_OptionSet(ssl_fd_, SSL_ENABLE_SESSION_TICKETS,
                      mode & RESUME_TICKET ? PR_TRUE : PR_FALSE);
   EXPECT_EQ(SECSuccess, rv);
 }
 
 void TlsAgent::DisableECDHEServerKeyReuse() {
+  ASSERT_TRUE(EnsureTlsSetup());
   ASSERT_EQ(TlsAgent::SERVER, role_);
   SECStatus rv = SSL_OptionSet(ssl_fd_, SSL_REUSE_SERVER_ECDHE_KEY, PR_FALSE);
   EXPECT_EQ(SECSuccess, rv);
 }
 
 static const std::string kTlsRolesAllArr[] = {"CLIENT", "SERVER"};
 ::testing::internal::ParamGenerator<std::string>
     TlsAgentTestBase::kTlsRolesAll = ::testing::ValuesIn(kTlsRolesAllArr);
--- a/security/nss/gtests/ssl_gtest/tls_agent.h
+++ b/security/nss/gtests/ssl_gtest/tls_agent.h
@@ -9,16 +9,17 @@
 
 #include "prio.h"
 #include "ssl.h"
 
 #include <functional>
 #include <iostream>
 
 #include "test_io.h"
+#include "tls_filter.h"
 
 #define GTEST_HAS_RTTI 0
 #include "gtest/gtest.h"
 
 extern bool g_ssl_gtest_verbose;
 
 namespace nss_test {
 
@@ -80,20 +81,27 @@ class TlsAgent : public PollTarget {
     adapter_ = DummyPrSocket::GetAdapter(pr_fd_);
     if (!adapter_) return false;
 
     return true;
   }
 
   void SetPeer(TlsAgent* peer) { adapter_->SetPeer(peer->adapter_); }
 
+  void SetPacketFilter(TlsRecordFilter* filter) {
+    filter->SetAgent(this);
+    adapter_->SetPacketFilter(filter);
+  }
+
   void SetPacketFilter(PacketFilter* filter) {
     adapter_->SetPacketFilter(filter);
   }
 
+  void DeletePacketFilter() { adapter_->SetPacketFilter(nullptr); }
+
   void StartConnect(PRFileDesc* model = nullptr);
   void CheckKEA(SSLKEAType kea_type, SSLNamedGroup group,
                 size_t kea_size = 0) const;
   void CheckAuthType(SSLAuthType auth_type,
                      SSLSignatureScheme sig_scheme) const;
 
   void DisableAllCiphers();
   void EnableCiphersByAuthType(SSLAuthType authType);
@@ -166,17 +174,17 @@ class TlsAgent : public PollTarget {
   const CERTCertificate* peer_cert() const {
     return SSL_PeerCertificate(ssl_fd_);
   }
 
   const char* state_str() const { return state_str(state()); }
 
   static const char* state_str(State state) { return states[state]; }
 
-  PRFileDesc* ssl_fd() { return ssl_fd_; }
+  PRFileDesc* ssl_fd() const { return ssl_fd_; }
   DummyPrSocket* adapter() { return adapter_; }
 
   bool is_compressed() const {
     return info_.compressionMethod != ssl_compression_null;
   }
   uint16_t server_key_bits() const { return server_key_bits_; }
   uint16_t min_version() const { return vrange_.min; }
   uint16_t max_version() const { return vrange_.max; }
--- a/security/nss/gtests/ssl_gtest/tls_connect.cc
+++ b/security/nss/gtests/ssl_gtest/tls_connect.cc
@@ -368,16 +368,32 @@ void TlsConnectTestBase::CheckKeys() con
 void TlsConnectTestBase::ConnectExpectFail() {
   server_->StartConnect();
   client_->StartConnect();
   Handshake();
   ASSERT_EQ(TlsAgent::STATE_ERROR, client_->state());
   ASSERT_EQ(TlsAgent::STATE_ERROR, server_->state());
 }
 
+void TlsConnectTestBase::ConnectExpectFailOneSide(TlsAgent::Role failing_side) {
+  server_->StartConnect();
+  client_->StartConnect();
+  client_->SetServerKeyBits(server_->server_key_bits());
+  client_->Handshake();
+  server_->Handshake();
+  TlsAgent* fail_agent;
+
+  if (failing_side == TlsAgent::CLIENT) {
+    fail_agent = client_;
+  } else {
+    fail_agent = server_;
+  }
+  ASSERT_TRUE_WAIT(fail_agent->state() == TlsAgent::STATE_ERROR, 5000);
+}
+
 void TlsConnectTestBase::ConfigureVersion(uint16_t version) {
   client_->SetVersionRange(version, version);
   server_->SetVersionRange(version, version);
 }
 
 void TlsConnectTestBase::SetExpectedVersion(uint16_t version) {
   client_->SetExpectedVersion(version);
   server_->SetExpectedVersion(version);
--- a/security/nss/gtests/ssl_gtest/tls_connect.h
+++ b/security/nss/gtests/ssl_gtest/tls_connect.h
@@ -63,16 +63,17 @@ class TlsConnectTestBase : public ::test
   // 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();
+  void ConnectExpectFailOneSide(TlsAgent::Role failingSide);
   void ConnectWithCipherSuite(uint16_t cipher_suite);
   // Check that the keys used in the handshake match expectations.
   void CheckKeys(SSLKEAType kea_type, SSLNamedGroup kea_group,
                  SSLAuthType auth_type, SSLSignatureScheme sig_scheme) const;
   // This version guesses some of the values.
   void CheckKeys(SSLKEAType kea_type, SSLAuthType auth_type) const;
   // This version assumes defaults.
   void CheckKeys() const;
--- a/security/nss/gtests/ssl_gtest/tls_filter.cc
+++ b/security/nss/gtests/ssl_gtest/tls_filter.cc
@@ -10,30 +10,86 @@
 extern "C" {
 // This is not something that should make you happy.
 #include "libssl_internals.h"
 }
 
 #include <iostream>
 #include "gtest_utils.h"
 #include "tls_agent.h"
+#include "tls_filter.h"
+#include "tls_protect.h"
 
 namespace nss_test {
 
+void TlsVersioned::WriteStream(std::ostream& stream) const {
+  stream << (is_dtls() ? "DTLS " : "TLS ");
+  switch (version()) {
+    case 0:
+      stream << "(no version)";
+      break;
+    case SSL_LIBRARY_VERSION_TLS_1_0:
+      stream << "1.0";
+      break;
+    case SSL_LIBRARY_VERSION_DTLS_1_0_WIRE:
+    case SSL_LIBRARY_VERSION_TLS_1_1:
+      stream << (is_dtls() ? "1.0" : "1.1");
+      break;
+    case SSL_LIBRARY_VERSION_DTLS_1_2_WIRE:
+    case SSL_LIBRARY_VERSION_TLS_1_2:
+      stream << "1.2";
+      break;
+    case SSL_LIBRARY_VERSION_TLS_1_3:
+      stream << "1.3";
+      break;
+    default:
+      stream << "Invalid version: " << version();
+      break;
+  }
+}
+
+void TlsRecordFilter::EnableDecryption() {
+  SSLInt_SetCipherSpecChangeFunc(agent()->ssl_fd(), CipherSpecChanged,
+                                 (void*)this);
+}
+
+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;
+  }
+  if (!sending) return;
+
+  self->cipher_spec_.reset(new TlsCipherSpec());
+  bool ret =
+      self->cipher_spec_->Init(SSLInt_CipherSpecToAlgorithm(isServer, newSpec),
+                               SSLInt_CipherSpecToKey(isServer, newSpec),
+                               SSLInt_CipherSpecToIv(isServer, newSpec));
+  EXPECT_EQ(true, ret);
+}
+
 PacketFilter::Action TlsRecordFilter::Filter(const DataBuffer& input,
                                              DataBuffer* output) {
   bool changed = false;
   size_t offset = 0U;
   output->Allocate(input.len());
 
   TlsParser parser(input);
+
   while (parser.remaining()) {
-    RecordHeader header;
+    TlsRecordHeader header;
     DataBuffer record;
+
     if (!header.Parse(&parser, &record)) {
+      ADD_FAILURE() << "not a valid record";
       return KEEP;
     }
 
     if (FilterRecord(header, record, &offset, output) != KEEP) {
       changed = true;
     } else {
       offset = header.Write(output, offset, record);
     }
@@ -44,44 +100,55 @@ PacketFilter::Action TlsRecordFilter::Fi
   if (changed) {
     ++count_;
     return (offset == 0) ? DROP : CHANGE;
   }
 
   return KEEP;
 }
 
-PacketFilter::Action TlsRecordFilter::FilterRecord(const RecordHeader& header,
-                                                   const DataBuffer& record,
-                                                   size_t* offset,
-                                                   DataBuffer* output) {
+PacketFilter::Action TlsRecordFilter::FilterRecord(
+    const TlsRecordHeader& header, const DataBuffer& record, size_t* offset,
+    DataBuffer* output) {
   DataBuffer filtered;
-  PacketFilter::Action action = FilterRecord(header, record, &filtered);
+  uint8_t inner_content_type;
+  DataBuffer plaintext;
+
+  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);
   if (action == KEEP) {
     return KEEP;
   }
 
   if (action == DROP) {
     std::cerr << "record drop: " << record << std::endl;
     return DROP;
   }
 
-  const DataBuffer* source = &record;
-  if (action == CHANGE) {
-    EXPECT_GT(0x10000U, filtered.len());
-    std::cerr << "record old: " << record << std::endl;
-    std::cerr << "record new: " << filtered << std::endl;
-    source = &filtered;
+  EXPECT_GT(0x10000U, filtered.len());
+  std::cerr << "record old: " << plaintext << std::endl;
+  std::cerr << "record new: " << filtered << std::endl;
+
+  DataBuffer ciphertext;
+  bool rv = Protect(header, inner_content_type, filtered, &ciphertext);
+  EXPECT_TRUE(rv);
+  if (!rv) {
+    return KEEP;
   }
-
-  *offset = header.Write(output, *offset, *source);
+  *offset = header.Write(output, *offset, ciphertext);
   return CHANGE;
 }
 
-bool TlsRecordFilter::RecordHeader::Parse(TlsParser* parser, DataBuffer* body) {
+bool TlsRecordHeader::Parse(TlsParser* parser, DataBuffer* body) {
   if (!parser->Read(&content_type_)) {
     return false;
   }
 
   uint32_t version;
   if (!parser->Read(&version, 2)) {
     return false;
   }
@@ -97,32 +164,72 @@ bool TlsRecordFilter::RecordHeader::Pars
     if (!parser->Read(&tmp, 4)) {
       return false;
     }
     sequence_number_ |= static_cast<uint64_t>(tmp);
   }
   return parser->ReadVariable(body, 2);
 }
 
-size_t TlsRecordFilter::RecordHeader::Write(DataBuffer* buffer, size_t offset,
-                                            const DataBuffer& body) const {
+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);
   if (is_dtls()) {
     // write epoch (2 octet), and seqnum (6 octet)
     offset = buffer->Write(offset, sequence_number_ >> 32, 4);
     offset = buffer->Write(offset, sequence_number_ & 0xffffffff, 4);
   }
   offset = buffer->Write(offset, body.len(), 2);
   offset = buffer->Write(offset, body);
   return offset;
 }
 
+bool TlsRecordFilter::Unprotect(const TlsRecordHeader& header,
+                                const DataBuffer& ciphertext,
+                                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;
+
+  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);
+
+  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;
+  }
+  DataBuffer padded = plaintext;
+  padded.Write(padded.len(), inner_content_type, 1);
+  return cipher_spec_->Protect(header, padded, ciphertext);
+}
+
 PacketFilter::Action TlsHandshakeFilter::FilterRecord(
-    const RecordHeader& record_header, const DataBuffer& input,
+    const TlsRecordHeader& record_header, const DataBuffer& input,
     DataBuffer* output) {
   // Check that the first byte is as requested.
   if (record_header.content_type() != kTlsHandshakeType) {
     return KEEP;
   }
 
   bool changed = false;
   size_t offset = 0U;
@@ -154,19 +261,18 @@ PacketFilter::Action TlsHandshakeFilter:
     }
 
     offset = header.Write(output, offset, *source);
   }
   output->Truncate(offset);
   return changed ? (offset ? CHANGE : DROP) : KEEP;
 }
 
-bool TlsHandshakeFilter::HandshakeHeader::ReadLength(TlsParser* parser,
-                                                     const RecordHeader& header,
-                                                     uint32_t* length) {
+bool TlsHandshakeFilter::HandshakeHeader::ReadLength(
+    TlsParser* parser, const TlsRecordHeader& header, uint32_t* length) {
   if (!parser->Read(length, 3)) {
     return false;  // malformed
   }
 
   if (!header.is_dtls()) {
     return true;  // nothing left to do
   }
 
@@ -187,38 +293,51 @@ bool TlsHandshakeFilter::HandshakeHeader
     return false;
   }
 
   // All current tests where we are using this code don't fragment.
   return (fragment_offset == 0 && fragment_length == *length);
 }
 
 bool TlsHandshakeFilter::HandshakeHeader::Parse(
-    TlsParser* parser, const RecordHeader& record_header, DataBuffer* body) {
+    TlsParser* parser, const TlsRecordHeader& record_header, DataBuffer* body) {
   version_ = record_header.version();
   if (!parser->Read(&handshake_type_)) {
     return false;  // malformed
   }
   uint32_t length;
   if (!ReadLength(parser, record_header, &length)) {
     return false;
   }
 
   return parser->Read(body, length);
 }
 
-size_t TlsHandshakeFilter::HandshakeHeader::Write(
-    DataBuffer* buffer, size_t offset, const DataBuffer& body) const {
+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);
   offset = buffer->Write(offset, body.len(), 3);
+  offset = buffer->Write(offset, message_seq_, 2);
+  offset = buffer->Write(offset, fragment_offset, 3);
+  offset = buffer->Write(offset, fragment_length, 3);
+  offset =
+      buffer->Write(offset, body.data() + fragment_offset, fragment_length);
+  return offset;
+}
+
+size_t TlsHandshakeFilter::HandshakeHeader::Write(
+    DataBuffer* buffer, size_t offset, const DataBuffer& body) const {
   if (is_dtls()) {
-    offset = buffer->Write(offset, message_seq_, 2);
-    offset = buffer->Write(offset, 0U, 3);  // fragment_offset
-    offset = buffer->Write(offset, body.len(), 3);
+    return WriteFragment(buffer, offset, body, 0U, body.len());
   }
+  offset = buffer->Write(offset, handshake_type(), 1);
+  offset = buffer->Write(offset, body.len(), 3);
   offset = buffer->Write(offset, body);
   return offset;
 }
 
 PacketFilter::Action TlsInspectorRecordHandshakeMessage::FilterHandshake(
     const HandshakeHeader& header, const DataBuffer& input,
     DataBuffer* output) {
   // Only do this once.
@@ -239,24 +358,25 @@ PacketFilter::Action TlsInspectorReplace
     *output = buffer_;
     return CHANGE;
   }
 
   return KEEP;
 }
 
 PacketFilter::Action TlsConversationRecorder::FilterRecord(
-    const RecordHeader& header, const DataBuffer& input, DataBuffer* output) {
+    const TlsRecordHeader& header, const DataBuffer& input,
+    DataBuffer* output) {
   buffer_.Append(input);
   return KEEP;
 }
 
-PacketFilter::Action TlsAlertRecorder::FilterRecord(const RecordHeader& header,
-                                                    const DataBuffer& input,
-                                                    DataBuffer* output) {
+PacketFilter::Action TlsAlertRecorder::FilterRecord(
+    const TlsRecordHeader& header, const DataBuffer& input,
+    DataBuffer* output) {
   if (level_ == kTlsAlertFatal) {  // already fatal
     return KEEP;
   }
   if (header.content_type() != kTlsAlertType) {
     return KEEP;
   }
 
   std::cerr << "Alert: " << input << std::endl;
@@ -313,17 +433,17 @@ PacketFilter::Action TlsExtensionFilter:
       return KEEP;
     }
     return FilterExtensions(&parser, input, output);
   }
   return KEEP;
 }
 
 bool TlsExtensionFilter::FindClientHelloExtensions(TlsParser* parser,
-                                                   const Versioned& header) {
+                                                   const TlsVersioned& header) {
   if (!parser->Skip(2 + 32)) {  // version + random
     return false;
   }
   if (!parser->SkipVariable(1)) {  // session ID
     return false;
   }
   if (header.is_dtls() && !parser->SkipVariable(1)) {  // DTLS cookie
     return false;
@@ -451,17 +571,17 @@ PacketFilter::Action TlsExtensionReplace
 PacketFilter::Action TlsExtensionDropper::FilterExtension(
     uint16_t extension_type, const DataBuffer& input, DataBuffer* output) {
   if (extension_type == extension_) {
     return DROP;
   }
   return KEEP;
 }
 
-PacketFilter::Action AfterRecordN::FilterRecord(const RecordHeader& header,
+PacketFilter::Action AfterRecordN::FilterRecord(const TlsRecordHeader& header,
                                                 const DataBuffer& body,
                                                 DataBuffer* out) {
   if (counter_++ == record_) {
     DataBuffer buf;
     header.Write(&buf, 0, body);
     src_->SendDirect(buf);
     dest_->Handshake();
     func_();
--- a/security/nss/gtests/ssl_gtest/tls_filter.h
+++ b/security/nss/gtests/ssl_gtest/tls_filter.h
@@ -8,123 +8,180 @@
 #define tls_filter_h_
 
 #include <functional>
 #include <memory>
 #include <vector>
 
 #include "test_io.h"
 #include "tls_parser.h"
+#include "tls_protect.h"
+
+extern "C" {
+#include "libssl_internals.h"
+}
 
 namespace nss_test {
 
+class TlsCipherSpec;
+class TlsAgent;
+
+class TlsVersioned {
+ public:
+  TlsVersioned() : version_(0) {}
+  explicit TlsVersioned(uint16_t version) : version_(version) {}
+
+  bool is_dtls() const { return IsDtls(version_); }
+  uint16_t version() const { return version_; }
+
+  void WriteStream(std::ostream& stream) const;
+
+ protected:
+  uint16_t version_;
+};
+
+class TlsRecordHeader : public TlsVersioned {
+ public:
+  TlsRecordHeader() : TlsVersioned(), content_type_(0), sequence_number_(0) {}
+  TlsRecordHeader(uint16_t version, uint8_t content_type,
+                  uint64_t sequence_number)
+      : TlsVersioned(version),
+        content_type_(content_type),
+        sequence_number_(sequence_number) {}
+
+  uint8_t content_type() const { return content_type_; }
+  uint64_t sequence_number() const { return sequence_number_; }
+  size_t header_length() const { return is_dtls() ? 11 : 3; }
+
+  // Parse the header; return true if successful; body in an outparam if OK.
+  bool Parse(TlsParser* parser, DataBuffer* body);
+  // Write the header and body to a buffer at the given offset.
+  // Return the offset of the end of the write.
+  size_t Write(DataBuffer* buffer, size_t offset, const DataBuffer& body) const;
+
+ private:
+  uint8_t content_type_;
+  uint64_t sequence_number_;
+};
+
 // Abstract filter that operates on entire (D)TLS records.
 class TlsRecordFilter : public PacketFilter {
  public:
-  TlsRecordFilter() : count_(0) {}
+  TlsRecordFilter() : agent_(nullptr), count_(0), cipher_spec_() {}
+
+  void SetAgent(const TlsAgent* agent) { agent_ = agent; }
+  const TlsAgent* agent() const { return agent_; }
 
   // External interface. Overrides PacketFilter.
   PacketFilter::Action Filter(const DataBuffer& input, DataBuffer* output);
 
   // Report how many packets were altered by the filter.
   size_t filtered_packets() const { return count_; }
 
-  class Versioned {
-   public:
-    Versioned() : version_(0) {}
-    explicit Versioned(uint16_t version) : version_(version) {}
-
-    bool is_dtls() const { return IsDtls(version_); }
-    uint16_t version() const { return version_; }
-
-   protected:
-    uint16_t version_;
-  };
-
-  class RecordHeader : public Versioned {
-   public:
-    RecordHeader() : Versioned(), content_type_(0), sequence_number_(0) {}
-    RecordHeader(uint16_t version, uint8_t content_type,
-                 uint64_t sequence_number)
-        : Versioned(version),
-          content_type_(content_type),
-          sequence_number_(sequence_number) {}
-
-    uint8_t content_type() const { return content_type_; }
-    uint64_t sequence_number() const { return sequence_number_; }
-    size_t header_length() const { return is_dtls() ? 11 : 3; }
-
-    // Parse the header; return true if successful; body in an outparam if OK.
-    bool Parse(TlsParser* parser, DataBuffer* body);
-    // Write the header and body to a buffer at the given offset.
-    // Return the offset of the end of the write.
-    size_t Write(DataBuffer* buffer, size_t offset,
-                 const DataBuffer& body) const;
-
-   private:
-    uint8_t content_type_;
-    uint64_t sequence_number_;
-  };
+  // Enable decryption. This only works properly for TLS 1.3 and above.
+  // Enabling it for lower version tests will cause undefined
+  // behavior.
+  void EnableDecryption();
+  bool Unprotect(const TlsRecordHeader& header, const DataBuffer& cipherText,
+                 uint8_t* inner_content_type, DataBuffer* plaintext);
+  bool Protect(const TlsRecordHeader& header, uint8_t inner_content_type,
+               const DataBuffer& plaintext, DataBuffer* ciphertext);
 
  protected:
   // There are two filter functions which can be overriden. Both are
   // called with the header and the record but the outer one is called
   // with a raw pointer to let you write into the buffer and lets you
   // do anything with this section of the stream. The inner one
   // just lets you change the record contents. By default, the
   // outer one calls the inner one, so if you override the outer
   // one, the inner one is never called unless you call it yourself.
-  virtual PacketFilter::Action FilterRecord(const RecordHeader& header,
+  virtual PacketFilter::Action FilterRecord(const TlsRecordHeader& header,
                                             const DataBuffer& record,
                                             size_t* offset, DataBuffer* output);
 
   // The record filter receives the record contentType, version and DTLS
   // sequence number (which is zero for TLS), plus the existing record payload.
   // It returns an action (KEEP, CHANGE, DROP).  It writes to the `changed`
   // outparam with the new record contents if it chooses to CHANGE the record.
-  virtual PacketFilter::Action FilterRecord(const RecordHeader& header,
+  virtual PacketFilter::Action FilterRecord(const TlsRecordHeader& header,
                                             const DataBuffer& data,
                                             DataBuffer* changed) {
     return KEEP;
   }
 
  private:
+  static void CipherSpecChanged(void* arg, PRBool sending,
+                                ssl3CipherSpec* newSpec);
+
+  const TlsAgent* agent_;
   size_t count_;
+  std::unique_ptr<TlsCipherSpec> cipher_spec_;
 };
 
+inline std::ostream& operator<<(std::ostream& stream, TlsVersioned v) {
+  v.WriteStream(stream);
+  return stream;
+}
+
+inline std::ostream& operator<<(std::ostream& stream, TlsRecordHeader& hdr) {
+  hdr.WriteStream(stream);
+  stream << ' ';
+  switch (hdr.content_type()) {
+    case kTlsChangeCipherSpecType:
+      stream << "CCS";
+      break;
+    case kTlsAlertType:
+      stream << "Alert";
+      break;
+    case kTlsHandshakeType:
+      stream << "Handshake";
+      break;
+    case kTlsApplicationDataType:
+      stream << "Data";
+      break;
+    default:
+      stream << '<' << hdr.content_type() << '>';
+      break;
+  }
+  return stream << ' ' << std::hex << hdr.sequence_number() << std::dec;
+}
+
 // Abstract filter that operates on handshake messages rather than records.
 // This assumes that the handshake messages are written in a block as entire
 // records and that they don't span records or anything crazy like that.
 class TlsHandshakeFilter : public TlsRecordFilter {
  public:
   TlsHandshakeFilter() {}
 
-  class HandshakeHeader : public Versioned {
+  class HandshakeHeader : public TlsVersioned {
    public:
-    HandshakeHeader() : Versioned(), handshake_type_(0), message_seq_(0) {}
+    HandshakeHeader() : TlsVersioned(), handshake_type_(0), message_seq_(0) {}
 
     uint8_t handshake_type() const { return handshake_type_; }
-    bool Parse(TlsParser* parser, const RecordHeader& record_header,
+    bool Parse(TlsParser* parser, const TlsRecordHeader& record_header,
                DataBuffer* body);
     size_t Write(DataBuffer* buffer, size_t offset,
                  const DataBuffer& body) const;
+    size_t WriteFragment(DataBuffer* buffer, size_t offset,
+                         const DataBuffer& body, size_t fragment_offset,
+                         size_t fragment_length) const;
 
    private:
     // Reads the length from the record header.
     // This also reads the DTLS fragment information and checks it.
-    bool ReadLength(TlsParser* parser, const RecordHeader& header,
+    bool ReadLength(TlsParser* parser, const TlsRecordHeader& header,
                     uint32_t* length);
 
     uint8_t handshake_type_;
     uint16_t message_seq_;
     // fragment_offset is always zero in these tests.
   };
 
  protected:
-  virtual PacketFilter::Action FilterRecord(const RecordHeader& header,
+  virtual PacketFilter::Action FilterRecord(const TlsRecordHeader& header,
                                             const DataBuffer& input,
                                             DataBuffer* output);
   virtual PacketFilter::Action FilterHandshake(const HandshakeHeader& header,
                                                const DataBuffer& input,
                                                DataBuffer* output) = 0;
 
  private:
 };
@@ -162,31 +219,31 @@ class TlsInspectorReplaceHandshakeMessag
   DataBuffer buffer_;
 };
 
 // Make a copy of the complete conversation.
 class TlsConversationRecorder : public TlsRecordFilter {
  public:
   TlsConversationRecorder(DataBuffer& buffer) : buffer_(buffer) {}
 
-  virtual PacketFilter::Action FilterRecord(const RecordHeader& header,
+  virtual PacketFilter::Action FilterRecord(const TlsRecordHeader& header,
                                             const DataBuffer& input,
                                             DataBuffer* output);
 
  private:
   DataBuffer& buffer_;
 };
 
 // Records an alert.  If an alert has already been recorded, it won't save the
 // new alert unless the old alert is a warning and the new one is fatal.
 class TlsAlertRecorder : public TlsRecordFilter {
  public:
   TlsAlertRecorder() : level_(255), description_(255) {}
 
-  virtual PacketFilter::Action FilterRecord(const RecordHeader& header,
+  virtual PacketFilter::Action FilterRecord(const TlsRecordHeader& header,
                                             const DataBuffer& input,
                                             DataBuffer* output);
 
   uint8_t level() const { return level_; }
   uint8_t description() const { return description_; }
 
  private:
   uint8_t level_;
@@ -218,17 +275,17 @@ class TlsExtensionFilter : public TlsHan
                                        DataBuffer* output) override;
 
   virtual PacketFilter::Action FilterExtension(uint16_t extension_type,
                                                const DataBuffer& input,
                                                DataBuffer* output) = 0;
 
  public:
   static bool FindClientHelloExtensions(TlsParser* parser,
-                                        const Versioned& header);
+                                        const TlsVersioned& header);
   static bool FindServerHelloExtensions(TlsParser* parser);
 
  private:
   PacketFilter::Action FilterExtensions(TlsParser* parser,
                                         const DataBuffer& input,
                                         DataBuffer* output);
 };
 
@@ -279,17 +336,17 @@ class TlsAgent;
 typedef std::function<void(void)> VoidFunction;
 
 class AfterRecordN : public TlsRecordFilter {
  public:
   AfterRecordN(TlsAgent* src, TlsAgent* dest, unsigned int record,
                VoidFunction func)
       : src_(src), dest_(dest), record_(record), func_(func), counter_(0) {}
 
-  virtual PacketFilter::Action FilterRecord(const RecordHeader& header,
+  virtual PacketFilter::Action FilterRecord(const TlsRecordHeader& header,
                                             const DataBuffer& body,
                                             DataBuffer* out) override;
 
  private:
   TlsAgent* src_;
   TlsAgent* dest_;
   unsigned int record_;
   VoidFunction func_;
new file mode 100644
--- /dev/null
+++ b/security/nss/gtests/ssl_gtest/tls_protect.cc
@@ -0,0 +1,145 @@
+/* -*- 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 "tls_protect.h"
+#include "tls_filter.h"
+
+namespace nss_test {
+
+AeadCipher::~AeadCipher() {
+  if (key_) {
+    PK11_FreeSymKey(key_);
+  }
+}
+
+bool AeadCipher::Init(PK11SymKey *key, const uint8_t *iv) {
+  key_ = PK11_ReferenceSymKey(key);
+  if (!key_) return false;
+
+  memcpy(iv_, iv, sizeof(iv_));
+  return true;
+}
+
+void AeadCipher::FormatNonce(uint64_t seq, uint8_t *nonce) {
+  memcpy(nonce, iv_, 12);
+
+  for (size_t i = 0; i < 8; ++i) {
+    nonce[12 - (i + 1)] ^= seq & 0xff;
+    seq >>= 8;
+  }
+
+  DataBuffer d(nonce, 12);
+  std::cerr << "Nonce " << d << std::endl;
+}
+
+bool AeadCipher::AeadInner(bool decrypt, void *params, size_t param_length,
+                           const uint8_t *in, size_t inlen, uint8_t *out,
+                           size_t *outlen, size_t maxlen) {
+  SECStatus rv;
+  unsigned int uoutlen = 0;
+  SECItem param = {
+      siBuffer, static_cast<unsigned char *>(params),
+      static_cast<unsigned int>(param_length),
+  };
+
+  if (decrypt) {
+    rv = PK11_Decrypt(key_, mech_, &param, out, &uoutlen, maxlen, in, inlen);
+  } else {
+    rv = PK11_Encrypt(key_, mech_, &param, out, &uoutlen, maxlen, in, inlen);
+  }
+  *outlen = (int)uoutlen;
+
+  return rv == SECSuccess;
+}
+
+bool AeadCipherAesGcm::Aead(bool decrypt, uint64_t seq, const uint8_t *in,
+                            size_t inlen, uint8_t *out, size_t *outlen,
+                            size_t maxlen) {
+  CK_GCM_PARAMS aeadParams;
+  unsigned char nonce[12];
+
+  memset(&aeadParams, 0, sizeof(aeadParams));
+  aeadParams.pIv = nonce;
+  aeadParams.ulIvLen = sizeof(nonce);
+  aeadParams.pAAD = NULL;
+  aeadParams.ulAADLen = 0;
+  aeadParams.ulTagBits = 128;
+
+  FormatNonce(seq, nonce);
+  return AeadInner(decrypt, (unsigned char *)&aeadParams, sizeof(aeadParams),
+                   in, inlen, out, outlen, maxlen);
+}
+
+bool AeadCipherChacha20Poly1305::Aead(bool decrypt, uint64_t seq,
+                                      const uint8_t *in, size_t inlen,
+                                      uint8_t *out, size_t *outlen,
+                                      size_t maxlen) {
+  CK_NSS_AEAD_PARAMS aeadParams;
+  unsigned char nonce[12];
+
+  memset(&aeadParams, 0, sizeof(aeadParams));
+  aeadParams.pNonce = nonce;
+  aeadParams.ulNonceLen = sizeof(nonce);
+  aeadParams.pAAD = NULL;
+  aeadParams.ulAADLen = 0;
+  aeadParams.ulTagLen = 16;
+
+  FormatNonce(seq, nonce);
+  return AeadInner(decrypt, (unsigned char *)&aeadParams, sizeof(aeadParams),
+                   in, inlen, out, outlen, maxlen);
+}
+
+bool TlsCipherSpec::Init(SSLCipherAlgorithm cipher, PK11SymKey *key,
+                         const uint8_t *iv) {
+  switch (cipher) {
+    case ssl_calg_aes_gcm:
+      aead_.reset(new AeadCipherAesGcm());
+      break;
+    case ssl_calg_chacha20:
+      aead_.reset(new AeadCipherChacha20Poly1305());
+      break;
+    default:
+      return false;
+  }
+
+  return aead_->Init(key, iv);
+}
+
+bool TlsCipherSpec::Unprotect(const TlsRecordHeader &header,
+                              const DataBuffer &ciphertext,
+                              DataBuffer *plaintext) {
+  // Make space.
+  plaintext->Allocate(ciphertext.len());
+
+  size_t len;
+  bool ret =
+      aead_->Aead(true, header.sequence_number(), ciphertext.data(),
+                  ciphertext.len(), plaintext->data(), &len, plaintext->len());
+  if (!ret) return false;
+
+  plaintext->Truncate(len);
+
+  return true;
+}
+
+bool TlsCipherSpec::Protect(const TlsRecordHeader &header,
+                            const DataBuffer &plaintext,
+                            DataBuffer *ciphertext) {
+  // Make a padded buffer.
+
+  ciphertext->Allocate(plaintext.len() +
+                       32);  // Room for any plausible auth tag
+  size_t len;
+  bool ret =
+      aead_->Aead(false, header.sequence_number(), plaintext.data(),
+                  plaintext.len(), ciphertext->data(), &len, ciphertext->len());
+  if (!ret) return false;
+  ciphertext->Truncate(len);
+
+  return true;
+}
+
+}  // namespace nss_test
new file mode 100644
--- /dev/null
+++ b/security/nss/gtests/ssl_gtest/tls_protect.h
@@ -0,0 +1,76 @@
+/* -*- 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/. */
+
+#ifndef tls_protection_h_
+#define tls_protection_h_
+
+#include <cstdint>
+#include <memory>
+
+#include "databuffer.h"
+#include "pk11pub.h"
+#include "sslt.h"
+
+namespace nss_test {
+class TlsRecordHeader;
+
+class AeadCipher {
+ public:
+  AeadCipher(CK_MECHANISM_TYPE mech) : mech_(mech), key_(nullptr) {}
+  ~AeadCipher();
+
+  bool Init(PK11SymKey *key, const uint8_t *iv);
+  virtual bool Aead(bool decrypt, uint64_t seq, const uint8_t *in, size_t inlen,
+                    uint8_t *out, size_t *outlen, size_t maxlen) = 0;
+
+ protected:
+  void FormatNonce(uint64_t seq, uint8_t *nonce);
+  bool AeadInner(bool decrypt, void *params, size_t param_length,
+                 const uint8_t *in, size_t inlen, uint8_t *out, size_t *outlen,
+                 size_t maxlen);
+
+  CK_MECHANISM_TYPE mech_;
+  PK11SymKey *key_;
+  uint8_t iv_[12];
+};
+
+class AeadCipherChacha20Poly1305 : public AeadCipher {
+ public:
+  AeadCipherChacha20Poly1305() : AeadCipher(CKM_NSS_CHACHA20_POLY1305) {}
+
+ protected:
+  bool Aead(bool decrypt, uint64_t seq, const uint8_t *in, size_t inlen,
+            uint8_t *out, size_t *outlen, size_t maxlen);
+};
+
+class AeadCipherAesGcm : public AeadCipher {
+ public:
+  AeadCipherAesGcm() : AeadCipher(CKM_AES_GCM) {}
+
+ protected:
+  bool Aead(bool decrypt, uint64_t seq, const uint8_t *in, size_t inlen,
+            uint8_t *out, size_t *outlen, size_t maxlen);
+};
+
+// Our analog of ssl3CipherSpec
+class TlsCipherSpec {
+ public:
+  TlsCipherSpec() : aead_() {}
+
+  bool Init(SSLCipherAlgorithm cipher, PK11SymKey *key, const uint8_t *iv);
+
+  bool Protect(const TlsRecordHeader &header, const DataBuffer &plaintext,
+               DataBuffer *ciphertext);
+  bool Unprotect(const TlsRecordHeader &header, const DataBuffer &ciphertext,
+                 DataBuffer *plaintext);
+
+ private:
+  std::unique_ptr<AeadCipher> aead_;
+};
+
+}  // namespace nss_test
+
+#endif
--- a/security/nss/lib/certhigh/ocsp.c
+++ b/security/nss/lib/certhigh/ocsp.c
@@ -2190,17 +2190,17 @@ CERT_CreateOCSPRequest(CERTCertList *cer
 void
 SetRequestExts(void *object, CERTCertExtension **exts)
 {
     CERTOCSPRequest *request = (CERTOCSPRequest *)object;
 
     request->tbsRequest->requestExtensions = exts;
 }
 
-#if defined(__GNUC__)
+#if defined(__GNUC__) && !defined(NSS_NO_GCC48)
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wvarargs"
 #endif
 SECStatus
 CERT_AddOCSPAcceptableResponses(CERTOCSPRequest *request,
                                 SECOidTag responseType0, ...)
 {
     void *extHandle;
@@ -2260,17 +2260,17 @@ CERT_AddOCSPAcceptableResponses(CERTOCSP
 
 loser:
     if (acceptableResponses != NULL)
         PORT_Free(acceptableResponses);
     if (extHandle != NULL)
         (void)CERT_FinishExtensions(extHandle);
     return rv;
 }
-#if defined(__GNUC__)
+#if defined(__GNUC__) && !defined(NSS_NO_GCC48)
 #pragma GCC diagnostic pop
 #endif
 
 /*
  * FUNCTION: CERT_DestroyOCSPRequest
  *   Frees an OCSP Request structure.
  * INPUTS:
  *   CERTOCSPRequest *request
--- a/security/nss/lib/freebl/rsa.c
+++ b/security/nss/lib/freebl/rsa.c
@@ -1231,17 +1231,20 @@ get_blinding_params(RSAPrivateKey *key, 
             */
             PR_INSERT_BEFORE(&rsabp->link, el);
         }
 
         /* We've found (or created) the RSAblindingParams struct for this key.
          * Now, search its list of ready blinding params for a usable one.
          */
         while (0 != (bp = rsabp->bp)) {
-            if (--(bp->counter) > 0) {
+#ifndef UNSAFE_FUZZER_MODE
+            if (--(bp->counter) > 0)
+#endif
+            {
                 /* Found a match and there are still remaining uses left */
                 /* Return the parameters */
                 CHECK_MPI_OK(mp_copy(&bp->f, f));
                 CHECK_MPI_OK(mp_copy(&bp->g, g));
 
                 PZ_Unlock(blindingParamsList.lock);
                 return SECSuccess;
             }
--- a/security/nss/lib/ssl/SSLerrs.h
+++ b/security/nss/lib/ssl/SSLerrs.h
@@ -499,9 +499,9 @@ ER3(SSL_ERROR_BAD_2ND_CLIENT_HELLO, (SSL
 
 ER3(SSL_ERROR_MISSING_SIGNATURE_ALGORITHMS_EXTENSION, (SSL_ERROR_BASE + 157),
     "SSL expected a signature algorithms extension.")
 
 ER3(SSL_ERROR_MALFORMED_PSK_KEY_EXCHANGE_MODES, (SSL_ERROR_BASE + 158),
     "SSL received a malformed PSK key exchange modes extension.")
 
 ER3(SSL_ERROR_MISSING_PSK_KEY_EXCHANGE_MODES, (SSL_ERROR_BASE + 159),
-    "SSL expected a missing PSK key exchange modes extension.")
+    "SSL expected a PSK key exchange modes extension.")
--- a/security/nss/lib/ssl/dtlscon.c
+++ b/security/nss/lib/ssl/dtlscon.c
@@ -230,16 +230,36 @@ dtls_RetransmitDetected(sslSocket *ss)
 
     } else {
         PORT_Assert(ss->ssl3.hs.rtTimerCb == NULL);
         /* ... and ignore it. */
     }
     return rv;
 }
 
+static SECStatus
+dtls_HandleHandshakeMessage(sslSocket *ss, SSL3Opaque *data, PRBool last)
+{
+
+    /* At this point we are advancing our state machine, so we can free our last
+     * flight of messages. */
+    dtls_FreeHandshakeMessages(&ss->ssl3.hs.lastMessageFlight);
+    ss->ssl3.hs.recvdHighWater = -1;
+
+    /* Reset the timer to the initial value if the retry counter
+     * is 0, per Sec. 4.2.4.1 */
+    dtls_CancelTimer(ss);
+    if (ss->ssl3.hs.rtRetries == 0) {
+        ss->ssl3.hs.rtTimeoutMs = DTLS_RETRANSMIT_INITIAL_MS;
+    }
+
+    return ssl3_HandleHandshakeMessage(ss, data, ss->ssl3.hs.msg_len,
+                                       last);
+}
+
 /* Called only from ssl3_HandleRecord, for each (deciphered) DTLS record.
  * origBuf is the decrypted ssl record content and is expected to contain
  * complete handshake records
  * Caller must hold the handshake and RecvBuf locks.
  *
  * Note that this code uses msg_len for two purposes:
  *
  * (1) To pass the length to ssl3_HandleHandshakeMessage()
@@ -324,33 +344,20 @@ dtls_HandleHandshake(sslSocket *ss, sslB
          */
         if ((message_seq == ss->ssl3.hs.recvMessageSeq) &&
             (fragment_offset == 0) &&
             (fragment_length == message_length)) {
             /* Complete next message. Process immediately */
             ss->ssl3.hs.msg_type = (SSL3HandshakeType)type;
             ss->ssl3.hs.msg_len = message_length;
 
-            /* At this point we are advancing our state machine, so
-             * we can free our last flight of messages */
-            dtls_FreeHandshakeMessages(&ss->ssl3.hs.lastMessageFlight);
-            ss->ssl3.hs.recvdHighWater = -1;
-            dtls_CancelTimer(ss);
-
-            /* Reset the timer to the initial value if the retry counter
-             * is 0, per Sec. 4.2.4.1 */
-            if (ss->ssl3.hs.rtRetries == 0) {
-                ss->ssl3.hs.rtTimeoutMs = DTLS_RETRANSMIT_INITIAL_MS;
-            }
-
-            rv = ssl3_HandleHandshakeMessage(ss, buf.buf, ss->ssl3.hs.msg_len,
+            rv = dtls_HandleHandshakeMessage(ss, buf.buf,
                                              buf.len == fragment_length);
             if (rv == SECFailure) {
-                /* Do not attempt to process rest of messages in this record */
-                break;
+                break; /* Discard the remainder of the record. */
             }
         } else {
             if (message_seq < ss->ssl3.hs.recvMessageSeq) {
                 /* Case 3: we do an immediate retransmit if we're
                  * in a waiting state. */
                 rv = dtls_RetransmitDetected(ss);
                 break;
             } else if (message_seq > ss->ssl3.hs.recvMessageSeq) {
@@ -441,34 +448,21 @@ dtls_HandleHandshake(sslSocket *ss, sslB
                         ss->ssl3.hs.recvdHighWater++;
                     } else {
                         break;
                     }
                 }
 
                 /* If we have all the bytes, then we are good to go */
                 if (ss->ssl3.hs.recvdHighWater == ss->ssl3.hs.msg_len) {
-                    ss->ssl3.hs.recvdHighWater = -1;
-
-                    rv = ssl3_HandleHandshakeMessage(
-                        ss,
-                        ss->ssl3.hs.msg_body.buf, ss->ssl3.hs.msg_len,
-                        buf.len == fragment_length);
-                    if (rv == SECFailure)
-                        break; /* Skip rest of record */
+                    rv = dtls_HandleHandshakeMessage(ss, ss->ssl3.hs.msg_body.buf,
+                                                     buf.len == fragment_length);
 
-                    /* At this point we are advancing our state machine, so
-                     * we can free our last flight of messages */
-                    dtls_FreeHandshakeMessages(&ss->ssl3.hs.lastMessageFlight);
-                    dtls_CancelTimer(ss);
-
-                    /* If there have been no retries this time, reset the
-                     * timer value to the default per Section 4.2.4.1 */
-                    if (ss->ssl3.hs.rtRetries == 0) {
-                        ss->ssl3.hs.rtTimeoutMs = DTLS_RETRANSMIT_INITIAL_MS;
+                    if (rv == SECFailure) {
+                        break; /* Discard the rest of the record. */
                     }
                 }
             }
         }
 
         buf.buf += fragment_length;
         buf.len -= fragment_length;
     }
--- a/security/nss/lib/ssl/ssl.def
+++ b/security/nss/lib/ssl/ssl.def
@@ -209,20 +209,15 @@ SSL_ConfigServerCert;
 ;+NSS_3.27 {    # NSS 3.27 release
 ;+    global:
 SSL_NamedGroupConfig;
 ;+    local:
 ;+*;
 ;+};
 ;+NSS_3.28 {    # NSS 3.28 release
 ;+    global:
+SSL_ExportEarlyKeyingMaterial;
 SSL_SendAdditionalKeyShares;
 SSL_SignatureSchemePrefSet;
 SSL_SignatureSchemePrefGet;
 ;+    local:
 ;+*;
 ;+};
-;+NSS_3.29 {    # NSS 3.29 release
-;+    global:
-SSL_ExportEarlyKeyingMaterial;
-;+    local:
-;+*;
-;+};
--- a/security/nss/lib/ssl/ssl3con.c
+++ b/security/nss/lib/ssl/ssl3con.c
@@ -1083,20 +1083,21 @@ ssl3_NegotiateVersion(sslSocket *ss, SSL
 
 /* Used by the client when the server produces a version number.
  * This reads, validates, and normalizes the value. */
 SECStatus
 ssl_ClientReadVersion(sslSocket *ss, SSL3Opaque **b, unsigned int *len,
                       SSL3ProtocolVersion *version)
 {
     SSL3ProtocolVersion v;
-    PRInt32 temp;
-
-    temp = ssl3_ConsumeHandshakeNumber(ss, 2, b, len);
-    if (temp < 0) {
+    PRUint32 temp;
+    SECStatus rv;
+
+    rv = ssl3_ConsumeHandshakeNumber(ss, &temp, 2, b, len);
+    if (rv != SECSuccess) {
         return SECFailure; /* alert has been sent */
     }
 
 #ifdef TLS_1_3_DRAFT_VERSION
     if (temp == SSL_LIBRARY_VERSION_TLS_1_3) {
         (void)SSL3_SendAlert(ss, alert_fatal, protocol_version);
         PORT_SetError(SSL_ERROR_UNSUPPORTED_VERSION);
         return SECFailure;
@@ -4306,17 +4307,17 @@ ssl3_AppendHandshakeHeader(sslSocket *ss
  * stream "b" (which is *length bytes long). Copy them into buffer "v".
  * Reduces *length by bytes.  Advances *b by bytes.
  *
  * If this function returns SECFailure, it has already sent an alert,
  * and has set a generic error code.  The caller should probably
  * override the generic error code by setting another.
  */
 SECStatus
-ssl3_ConsumeHandshake(sslSocket *ss, void *v, PRInt32 bytes, SSL3Opaque **b,
+ssl3_ConsumeHandshake(sslSocket *ss, void *v, PRUint32 bytes, SSL3Opaque **b,
                       PRUint32 *length)
 {
     PORT_Assert(ss->opt.noLocks || ssl_HaveRecvBufLock(ss));
     PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
 
     if ((PRUint32)bytes > *length) {
         return ssl3_DecodeError(ss);
     }
@@ -4324,47 +4325,43 @@ ssl3_ConsumeHandshake(sslSocket *ss, voi
     PRINT_BUF(60, (ss, "consume bytes:", *b, bytes));
     *b += bytes;
     *length -= bytes;
     return SECSuccess;
 }
 
 /* Read up the next "bytes" number of bytes from the (decrypted) input
  * stream "b" (which is *length bytes long), and interpret them as an
- * integer in network byte order.  Returns the received value.
+ * integer in network byte order.  Sets *num to the received value.
  * Reduces *length by bytes.  Advances *b by bytes.
  *
- * Returns SECFailure (-1) on failure.
- * This value is indistinguishable from the equivalent received value.
- * Only positive numbers are to be received this way.
- * Thus, the largest value that may be sent this way is 0x7fffffff.
  * On error, an alert has been sent, and a generic error code has been set.
  */
-PRInt32
-ssl3_ConsumeHandshakeNumber(sslSocket *ss, PRInt32 bytes, SSL3Opaque **b,
-                            PRUint32 *length)
+SECStatus
+ssl3_ConsumeHandshakeNumber(sslSocket *ss, PRUint32 *num, PRUint32 bytes,
+                            SSL3Opaque **b, PRUint32 *length)
 {
     PRUint8 *buf = *b;
     int i;
-    PRInt32 num = 0;
 
     PORT_Assert(ss->opt.noLocks || ssl_HaveRecvBufLock(ss));
     PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
-    PORT_Assert(bytes <= sizeof num);
-
-    if ((PRUint32)bytes > *length) {
+
+    *num = 0;
+    if (bytes > *length || bytes > sizeof(*num)) {
         return ssl3_DecodeError(ss);
     }
     PRINT_BUF(60, (ss, "consume bytes:", *b, bytes));
 
-    for (i = 0; i < bytes; i++)
-        num = (num << 8) + buf[i];
+    for (i = 0; i < bytes; i++) {
+        *num = (*num << 8) + buf[i];
+    }
     *b += bytes;
     *length -= bytes;
-    return num;
+    return SECSuccess;
 }
 
 /* Read in two values from the incoming decrypted byte stream "b", which is
  * *length bytes long.  The first value is a number whose size is "bytes"
  * bytes long.  The second value is a byte-string whose size is the value
  * of the first number received.  The latter byte-string, and its length,
  * is returned in the SECItem i.
  *
@@ -4372,31 +4369,32 @@ ssl3_ConsumeHandshakeNumber(sslSocket *s
  * On error, an alert has been sent, and a generic error code has been set.
  *
  * RADICAL CHANGE for NSS 3.11.  All callers of this function make copies
  * of the data returned in the SECItem *i, so making a copy of it here
  * is simply wasteful.  So, This function now just sets SECItem *i to
  * point to the values in the buffer **b.
  */
 SECStatus
-ssl3_ConsumeHandshakeVariable(sslSocket *ss, SECItem *i, PRInt32 bytes,
+ssl3_ConsumeHandshakeVariable(sslSocket *ss, SECItem *i, PRUint32 bytes,
                               SSL3Opaque **b, PRUint32 *length)
 {
-    PRInt32 count;
+    PRUint32 count;
+    SECStatus rv;
 
     PORT_Assert(bytes <= 3);
     i->len = 0;
     i->data = NULL;
     i->type = siBuffer;
-    count = ssl3_ConsumeHandshakeNumber(ss, bytes, b, length);
-    if (count < 0) { /* Can't test for SECSuccess here. */
+    rv = ssl3_ConsumeHandshakeNumber(ss, &count, bytes, b, length);
+    if (rv != SECSuccess) {
         return SECFailure;
     }
     if (count > 0) {
-        if ((PRUint32)count > *length) {
+        if (count > *length) {
             return ssl3_DecodeError(ss);
         }
         i->data = *b;
         i->len = count;
         *b += count;
         *length -= count;
     }
     return SECSuccess;
@@ -4657,20 +4655,21 @@ ssl_IsRsaPssSignatureScheme(SSLSignature
  * SignatureAndHashAlgorithm) structure from |b| and puts the resulting value
  * into |out|. |b| and |length| are updated accordingly.
  *
  * See https://tools.ietf.org/html/rfc5246#section-7.4.1.4.1 */
 SECStatus
 ssl_ConsumeSignatureScheme(sslSocket *ss, SSL3Opaque **b,
                            PRUint32 *length, SSLSignatureScheme *out)
 {
-    PRInt32 tmp;
-
-    tmp = ssl3_ConsumeHandshakeNumber(ss, 2, b, length);
-    if (tmp < 0) {
+    PRUint32 tmp;
+    SECStatus rv;
+
+    rv = ssl3_ConsumeHandshakeNumber(ss, &tmp, 2, b, length);
+    if (rv != SECSuccess) {
         return SECFailure; /* Error code set already. */
     }
     if (!ssl_IsSupportedSignatureScheme((SSLSignatureScheme)tmp)) {
         PORT_SetError(SSL_ERROR_UNSUPPORTED_SIGNATURE_ALGORITHM);
         return SECFailure;
     }
     *out = (SSLSignatureScheme)tmp;
     return SECSuccess;
@@ -6582,17 +6581,17 @@ ssl3_SetCipherSuite(sslSocket *ss, ssl3C
 
 /* Called from ssl3_HandleHandshakeMessage() when it has deciphered a complete
  * ssl3 ServerHello message.
  * Caller must hold Handshake and RecvBuf locks.
  */
 static SECStatus
 ssl3_HandleServerHello(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
 {
-    PRInt32 temp; /* allow for consume number failure */
+    PRUint32 temp;
     PRBool suite_found = PR_FALSE;
     int i;
     int errCode = SSL_ERROR_RX_MALFORMED_SERVER_HELLO;
     SECStatus rv;
     SECItem sidBytes = { siBuffer, NULL, 0 };
     PRBool isTLS = PR_FALSE;
     SSL3AlertDescription desc = illegal_parameter;
 #ifndef TLS_1_3_DRAFT_VERSION
@@ -6625,18 +6624,21 @@ ssl3_HandleServerHello(sslSocket *ss, SS
         ss->ssl3.clientPrivateKey = NULL;
     }
 
     rv = ssl_ClientReadVersion(ss, &b, &length, &ss->version);
     if (rv != SECSuccess) {
         goto loser; /* alert has been sent */
     }
 
-    /* We got a HelloRetryRequest, but the server didn't pick 1.3.  Scream. */
-    if (ss->ssl3.hs.helloRetry && ss->version < SSL_LIBRARY_VERSION_TLS_1_3) {
+    /* The server didn't pick 1.3 although we either received a
+     * HelloRetryRequest, or we prepared to send early app data. */
+    if (ss->version < SSL_LIBRARY_VERSION_TLS_1_3 &&
+        (ss->ssl3.hs.helloRetry || ss->ssl3.hs.zeroRttState == ssl_0rtt_sent)) {
+        /* SSL3_SendAlert() will uncache the SID. */
         desc = illegal_parameter;
         errCode = SSL_ERROR_RX_MALFORMED_SERVER_HELLO;
         goto alert_loser;
     }
 
     /* Check that the server negotiated the same version as it did
      * in the first handshake. This isn't really the best place for
      * us to be getting this version number, but it's what we have.
@@ -6697,18 +6699,18 @@ ssl3_HandleServerHello(sslSocket *ss, SS
         if (sidBytes.len > SSL3_SESSIONID_BYTES) {
             if (isTLS)
                 desc = decode_error;
             goto alert_loser; /* malformed. */
         }
     }
 
     /* find selected cipher suite in our list. */
-    temp = ssl3_ConsumeHandshakeNumber(ss, 2, &b, &length);
-    if (temp < 0) {
+    rv = ssl3_ConsumeHandshakeNumber(ss, &temp, 2, &b, &length);
+    if (rv != SECSuccess) {
         goto loser; /* alert has been sent */
     }
     i = ssl3_config_match_init(ss);
     PORT_Assert(i > 0);
     if (i <= 0) {
         errCode = PORT_GetError();
         goto loser;
     }
@@ -6743,18 +6745,18 @@ ssl3_HandleServerHello(sslSocket *ss, SS
     if (rv != SECSuccess) {
         desc = internal_error;
         errCode = PORT_GetError();
         goto alert_loser;
     }
 
     if (ss->version < SSL_LIBRARY_VERSION_TLS_1_3) {
         /* find selected compression method in our list. */
-        temp = ssl3_ConsumeHandshakeNumber(ss, 1, &b, &length);
-        if (temp < 0) {
+        rv = ssl3_ConsumeHandshakeNumber(ss, &temp, 1, &b, &length);
+        if (rv != SECSuccess) {
             goto loser; /* alert has been sent */
         }
         suite_found = PR_FALSE;
         for (i = 0; i < ssl_compression_method_count; i++) {
             if (temp == ssl_compression_methods[i]) {
                 if (!ssl_CompressionEnabled(ss, ssl_compression_methods[i])) {
                     break; /* failure */
                 }
@@ -6986,16 +6988,29 @@ ssl3_HandleServerHelloPart2(sslSocket *s
         } while (0);
     }
 
     if (sid_match)
         SSL_AtomicIncrementLong(&ssl3stats.hsh_sid_cache_not_ok);
     else
         SSL_AtomicIncrementLong(&ssl3stats.hsh_sid_cache_misses);
 
+    /* We tried to resume a 1.3 session but the server negotiated 1.2. */
+    if (ss->statelessResume) {
+        PORT_Assert(sid->version == SSL_LIBRARY_VERSION_TLS_1_3);
+        PORT_Assert(ss->ssl3.hs.currentSecret);
+
+        /* Reset resumption state, only used by 1.3 code. */
+        ss->statelessResume = PR_FALSE;
+
+        /* Clear TLS 1.3 early data traffic key. */
+        PK11_FreeSymKey(ss->ssl3.hs.currentSecret);
+        ss->ssl3.hs.currentSecret = NULL;
+    }
+
     /* throw the old one away */
     sid->u.ssl3.keys.resumable = PR_FALSE;
     ss->sec.uncache(sid);
     ssl_FreeSID(sid);
 
     /* get a new sid */
     ss->sec.ci.sid = sid = ssl3_NewSessionID(ss, PR_FALSE);
     if (sid == NULL) {
@@ -7252,46 +7267,47 @@ typedef struct dnameNode {
  * Called from:
  * ssl3_HandleCertificateRequest
  * tls13_HandleCertificateRequest
  */
 SECStatus
 ssl3_ParseCertificateRequestCAs(sslSocket *ss, SSL3Opaque **b, PRUint32 *length,
                                 PLArenaPool *arena, CERTDistNames *ca_list)
 {
-    PRInt32 remaining;
+    PRUint32 remaining;
     int nnames = 0;
     dnameNode *node;
+    SECStatus rv;
     int i;
 
-    remaining = ssl3_ConsumeHandshakeNumber(ss, 2, b, length);
-    if (remaining < 0)
+    rv = ssl3_ConsumeHandshakeNumber(ss, &remaining, 2, b, length);
+    if (rv != SECSuccess)
         return SECFailure; /* malformed, alert has been sent */
 
-    if ((PRUint32)remaining > *length)
+    if (remaining > *length)
         goto alert_loser;
 
     ca_list->head = node = PORT_ArenaZNew(arena, dnameNode);
     if (node == NULL)
         goto no_mem;
 
     while (remaining > 0) {
-        PRInt32 len;
+        PRUint32 len;
 
         if (remaining < 2)
             goto alert_loser; /* malformed */
 
-        node->name.len = len = ssl3_ConsumeHandshakeNumber(ss, 2, b, length);
-        if (len <= 0)
+        rv = ssl3_ConsumeHandshakeNumber(ss, &len, 2, b, length);
+        if (rv != SECSuccess)
             return SECFailure; /* malformed, alert has been sent */
+        if (len == 0 || remaining < len + 2)
+            goto alert_loser; /* malformed */
 
         remaining -= 2;
-        if (remaining < len)
-            goto alert_loser; /* malformed */
-
+        node->name.len = len;
         node->name.data = *b;
         *b += len;
         *length -= len;
         remaining -= len;
         nnames++;
         if (remaining <= 0)
             break; /* success */
 
@@ -7357,19 +7373,19 @@ ssl_ParseSignatureSchemes(const sslSocke
         schemes = PORT_ZNewArray(SSLSignatureScheme, max);
     }
     if (!schemes) {
         ssl3_ExtSendAlert(ss, alert_fatal, internal_error);
         return SECFailure;
     }
 
     for (; max; --max) {
-        PRInt32 tmp;
-        tmp = ssl3_ExtConsumeHandshakeNumber(ss, 2, &buf.data, &buf.len);
-        if (tmp < 0) {
+        PRUint32 tmp;
+        rv = ssl3_ExtConsumeHandshakeNumber(ss, &tmp, 2, &buf.data, &buf.len);
+        if (rv != SECSuccess) {
             PORT_Assert(0);
             PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
             return SECFailure;
         }
         if (ssl_IsSupportedSignatureScheme((SSLSignatureScheme)tmp)) {
             schemes[numSchemes++] = (SSLSignatureScheme)tmp;
         }
     }
@@ -8223,17 +8239,17 @@ ssl3_SelectServerCert(sslSocket *ss)
 /* Called from ssl3_HandleHandshakeMessage() when it has deciphered a complete
  * ssl3 Client Hello message.
  * Caller must hold Handshake and RecvBuf locks.
  */
 static SECStatus
 ssl3_HandleClientHello(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
 {
     sslSessionID *sid = NULL;
-    PRInt32 tmp;
+    PRUint32 tmp;
     unsigned int i;
     SECStatus rv;
     int errCode = SSL_ERROR_RX_MALFORMED_CLIENT_HELLO;
     SSL3AlertDescription desc = illegal_parameter;
     SSL3AlertLevel level = alert_fatal;
     SSL3ProtocolVersion version;
     TLSExtension *versionExtension;
     SECItem sidBytes = { siBuffer, NULL, 0 };
@@ -8283,18 +8299,18 @@ ssl3_HandleClientHello(sslSocket *ss, SS
      */
     ssl3_ResetExtensionData(&ss->xtnData);
     ss->statelessResume = PR_FALSE;
 
     if (IS_DTLS(ss)) {
         dtls_RehandshakeCleanup(ss);
     }
 
-    tmp = ssl3_ConsumeHandshakeNumber(ss, 2, &b, &length);
-    if (tmp < 0)
+    rv = ssl3_ConsumeHandshakeNumber(ss, &tmp, 2, &b, &length);
+    if (rv != SECSuccess)
         goto loser; /* malformed, alert already sent */
 
     /* Translate the version. */
     if (IS_DTLS(ss)) {
         ss->clientHelloVersion = version =
             dtls_DTLSVersionToTLSVersion((SSL3ProtocolVersion)tmp);
     } else {
         ss->clientHelloVersion = version = (SSL3ProtocolVersion)tmp;
@@ -8337,19 +8353,19 @@ ssl3_HandleClientHello(sslSocket *ss, SS
      * we are restarting a previous session until extensions have been
      * parsed, since we might have received a SessionTicket extension.
      * Note: we allow extensions even when negotiating SSL3 for the sake
      * of interoperability (and backwards compatibility).
      */
 
     if (length) {
         /* Get length of hello extensions */
-        PRInt32 extension_length;
-        extension_length = ssl3_ConsumeHandshakeNumber(ss, 2, &b, &length);
-        if (extension_length < 0) {
+        PRUint32 extension_length;
+        rv = ssl3_ConsumeHandshakeNumber(ss, &extension_length, 2, &b, &length);
+        if (rv != SECSuccess) {
             goto loser; /* alert already sent */
         }
         if (extension_length != length) {
             ssl3_DecodeError(ss); /* send alert */
             goto loser;
         }
 
         rv = ssl3_ParseExtensions(ss, &b, &length);
@@ -9891,19 +9907,19 @@ ssl3_HandleRSAClientKeyExchange(sslSocke
     PORT_Assert(ss->opt.noLocks || ssl_HaveRecvBufLock(ss));
     PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
     PORT_Assert(ss->ssl3.prSpec == ss->ssl3.pwSpec);
 
     enc_pms.data = b;
     enc_pms.len = length;
 
     if (ss->ssl3.prSpec->version > SSL_LIBRARY_VERSION_3_0) { /* isTLS */
-        PRInt32 kLen;
-        kLen = ssl3_ConsumeHandshakeNumber(ss, 2, &enc_pms.data, &enc_pms.len);
-        if (kLen < 0) {
+        PRUint32 kLen;
+        rv = ssl3_ConsumeHandshakeNumber(ss, &kLen, 2, &enc_pms.data, &enc_pms.len);
+        if (rv != SECSuccess) {
             PORT_SetError(SSL_ERROR_CLIENT_KEY_EXCHANGE_FAILURE);
             return SECFailure;
         }
         if ((unsigned)kLen < enc_pms.len) {
             enc_pms.len = kLen;
         }
     }
 
@@ -10213,16 +10229,17 @@ loser:
     return rv;
 }
 
 static SECStatus
 ssl3_HandleNewSessionTicket(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
 {
     SECStatus rv;
     SECItem ticketData;
+    PRUint32 temp;
 
     SSL_TRC(3, ("%d: SSL3[%d]: handle session_ticket handshake",
                 SSL_GETPID(), ss->fd));
 
     PORT_Assert(ss->opt.noLocks || ssl_HaveRecvBufLock(ss));
     PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
 
     PORT_Assert(!ss->ssl3.hs.newSessionTicket.ticket.data);
@@ -10239,18 +10256,23 @@ ssl3_HandleNewSessionTicket(sslSocket *s
      * ssl3_FinishHandshake for more details.
      */
     ss->ssl3.hs.newSessionTicket.received_timestamp = ssl_Time();
     if (length < 4) {
         (void)SSL3_SendAlert(ss, alert_fatal, decode_error);
         PORT_SetError(SSL_ERROR_RX_MALFORMED_NEW_SESSION_TICKET);
         return SECFailure;
     }
-    ss->ssl3.hs.newSessionTicket.ticket_lifetime_hint =
-        (PRUint32)ssl3_ConsumeHandshakeNumber(ss, 4, &b, &length);
+
+    rv = ssl3_ConsumeHandshakeNumber(ss, &temp, 4, &b, &length);
+    if (rv != SECSuccess) {
+        PORT_SetError(SSL_ERROR_RX_MALFORMED_NEW_SESSION_TICKET);
+        return SECFailure;
+    }
+    ss->ssl3.hs.newSessionTicket.ticket_lifetime_hint = temp;
 
     rv = ssl3_ConsumeHandshakeVariable(ss, &ticketData, 2, &b, &length);
     if (rv != SECSuccess || length != 0) {
         (void)SSL3_SendAlert(ss, alert_fatal, decode_error);
         PORT_SetError(SSL_ERROR_RX_MALFORMED_NEW_SESSION_TICKET);
         return SECFailure; /* malformed */
     }
     /* If the server sent a zero-length ticket, ignore it and keep the
@@ -10535,31 +10557,30 @@ ssl3_HandleCertificateStatus(sslSocket *
     }
 
     return ssl3_AuthCertificate(ss);
 }
 
 SECStatus
 ssl_ReadCertificateStatus(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
 {
-    PRInt32 status, len;
+    PRUint32 status, len;
+    SECStatus rv;
 
     PORT_Assert(!ss->sec.isServer);
 
     /* Consume the CertificateStatusType enum */
-    status = ssl3_ConsumeHandshakeNumber(ss, 1, &b, &length);
-    if (status != 1 /* ocsp */) {
-        ssl3_DecodeError(ss); /* sets error code */
-        return SECFailure;
-    }
-
-    len = ssl3_ConsumeHandshakeNumber(ss, 3, &b, &length);
-    if (len != length) {
-        ssl3_DecodeError(ss); /* sets error code */
-        return SECFailure;
+    rv = ssl3_ConsumeHandshakeNumber(ss, &status, 1, &b, &length);
+    if (rv != SECSuccess || status != 1 /* ocsp */) {
+        return ssl3_DecodeError(ss);
+    }
+
+    rv = ssl3_ConsumeHandshakeNumber(ss, &len, 3, &b, &length);
+    if (rv != SECSuccess || len != length) {
+        return ssl3_DecodeError(ss);
     }
 
 #define MAX_CERTSTATUS_LEN 0x1ffff /* 128k - 1 */
     if (length > MAX_CERTSTATUS_LEN) {
         ssl3_DecodeError(ss); /* sets error code */
         return SECFailure;
     }
 #undef MAX_CERTSTATUS_LEN
@@ -10606,37 +10627,37 @@ ssl3_HandleCertificate(sslSocket *ss, SS
 
 /* Called from ssl3_HandleCertificate
  */
 SECStatus
 ssl3_CompleteHandleCertificate(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
 {
     ssl3CertNode *c;
     ssl3CertNode *lastCert = NULL;
-    PRInt32 remaining = 0;
-    PRInt32 size;
+    PRUint32 remaining = 0;
+    PRUint32 size;
     SECStatus rv;
     PRBool isServer = ss->sec.isServer;
     PRBool isTLS;
     SSL3AlertDescription desc;
     int errCode = SSL_ERROR_RX_MALFORMED_CERTIFICATE;
     SECItem certItem;
 
     ssl3_CleanupPeerCerts(ss);
     isTLS = (PRBool)(ss->ssl3.prSpec->version > SSL_LIBRARY_VERSION_3_0);
 
     /* It is reported that some TLS client sends a Certificate message
     ** with a zero-length message body.  We'll treat that case like a
     ** normal no_certificates message to maximize interoperability.
     */
     if (length) {
-        remaining = ssl3_ConsumeHandshakeNumber(ss, 3, &b, &length);
-        if (remaining < 0)
+        rv = ssl3_ConsumeHandshakeNumber(ss, &remaining, 3, &b, &length);
+        if (rv != SECSuccess)
             goto loser; /* fatal alert already sent by ConsumeHandshake. */
-        if ((PRUint32)remaining > length)
+        if (remaining > length)
             goto decode_loser;
     }
 
     if (!remaining) {
         if (!(isTLS && isServer)) {
             desc = bad_certificate;
             goto alert_loser;
         }
@@ -10657,25 +10678,24 @@ ssl3_CompleteHandleCertificate(sslSocket
     }
 
     ss->ssl3.peerCertArena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
     if (ss->ssl3.peerCertArena == NULL) {
         goto loser; /* don't send alerts on memory errors */
     }
 
     /* First get the peer cert. */
-    remaining -= 3;
-    if (remaining < 0)
+    if (remaining < 3)
         goto decode_loser;
 
-    size = ssl3_ConsumeHandshakeNumber(ss, 3, &b, &length);
-    if (size <= 0)
+    remaining -= 3;
+    rv = ssl3_ConsumeHandshakeNumber(ss, &size, 3, &b, &length);
+    if (rv != SECSuccess)
         goto loser; /* fatal alert already sent by ConsumeHandshake. */
-
-    if (remaining < size)
+    if (size == 0 || remaining < size)
         goto decode_loser;
 
     certItem.data = b;
     certItem.len = size;
     b += size;
     length -= size;
     remaining -= size;
 
@@ -10685,25 +10705,24 @@ ssl3_CompleteHandleCertificate(sslSocket
         /* We should report an alert if the cert was bad, but not if the
          * problem was just some local problem, like memory error.
          */
         goto ambiguous_err;
     }
 
     /* Now get all of the CA certs. */
     while (remaining > 0) {
-        remaining -= 3;
-        if (remaining < 0)
+        if (remaining < 3)
             goto decode_loser;
 
-        size = ssl3_ConsumeHandshakeNumber(ss, 3, &b, &length);
-        if (size <= 0)
+        remaining -= 3;
+        rv = ssl3_ConsumeHandshakeNumber(ss, &size, 3, &b, &length);
+        if (rv != SECSuccess)
             goto loser; /* fatal alert already sent by ConsumeHandshake. */
-
-        if (remaining < size)
+        if (size == 0 || remaining < size)
             goto decode_loser;
 
         certItem.data = b;
         certItem.len = size;
         b += size;
         length -= size;
         remaining -= size;
 
@@ -10722,19 +10741,16 @@ ssl3_CompleteHandleCertificate(sslSocket
         if (lastCert) {
             lastCert->next = c;
         } else {
             ss->ssl3.peerCertChain = c;
         }
         lastCert = c;
     }
 
-    if (remaining != 0)
-        goto decode_loser;
-
     SECKEY_UpdateCertPQG(ss->sec.peerCert);
 
     if (!isServer &&
         ss->version < SSL_LIBRARY_VERSION_TLS_1_3 &&
         ssl3_ExtensionNegotiated(ss, ssl_cert_status_xtn)) {
         ss->ssl3.hs.ws = wait_certificate_status;
         rv = SECSuccess;
     } else {
--- a/security/nss/lib/ssl/ssl3ext.c
+++ b/security/nss/lib/ssl/ssl3ext.c
@@ -166,25 +166,25 @@ ssl3_ClientExtensionAdvertised(const ssl
 SECStatus
 ssl3_ParseExtensions(sslSocket *ss, SSL3Opaque **b, PRUint32 *length)
 {
     /* Clean out the extensions list. */
     ssl3_DestroyRemoteExtensions(&ss->ssl3.hs.remoteExtensions);
 
     while (*length) {
         SECStatus rv;
-        PRInt32 extension_type;
+        PRUint32 extension_type;
         SECItem extension_data = { siBuffer, NULL, 0 };
         TLSExtension *extension;
         PRCList *cursor;
 
         /* Get the extension's type field */
-        extension_type = ssl3_ConsumeHandshakeNumber(ss, 2, b, length);
-        if (extension_type < 0) { /* failure to decode extension_type */
-            return SECFailure;    /* alert already sent */
+        rv = ssl3_ConsumeHandshakeNumber(ss, &extension_type, 2, b, length);
+        if (rv != SECSuccess) {
+            return SECFailure; /* alert already sent */
         }
 
         SSL_TRC(10, ("%d: SSL3[%d]: parsing extension %d",
                      SSL_GETPID(), ss->fd, extension_type));
         /* Check whether an extension has been sent multiple times. */
         for (cursor = PR_NEXT_LINK(&ss->ssl3.hs.remoteExtensions);
              cursor != &ss->ssl3.hs.remoteExtensions;
              cursor = PR_NEXT_LINK(cursor)) {
@@ -500,28 +500,28 @@ ssl3_ExtSendAlert(const sslSocket *ss, S
 
 void
 ssl3_ExtDecodeError(const sslSocket *ss)
 {
     (void)ssl3_DecodeError((sslSocket *)ss);
 }
 
 SECStatus
-ssl3_ExtConsumeHandshake(const sslSocket *ss, void *v, PRInt32 bytes,
+ssl3_ExtConsumeHandshake(const sslSocket *ss, void *v, PRUint32 bytes,
                          SSL3Opaque **b, PRUint32 *length)
 {
     return ssl3_ConsumeHandshake((sslSocket *)ss, v, bytes, b, length);
 }
 
-PRInt32
-ssl3_ExtConsumeHandshakeNumber(const sslSocket *ss, PRInt32 bytes,
-                               SSL3Opaque **b, PRUint32 *length)
+SECStatus
+ssl3_ExtConsumeHandshakeNumber(const sslSocket *ss, PRUint32 *num,
+                               PRUint32 bytes, SSL3Opaque **b, PRUint32 *length)
 {
-    return ssl3_ConsumeHandshakeNumber((sslSocket *)ss, bytes, b, length);
+    return ssl3_ConsumeHandshakeNumber((sslSocket *)ss, num, bytes, b, length);
 }
 
 SECStatus
 ssl3_ExtConsumeHandshakeVariable(const sslSocket *ss, SECItem *i,
-                                 PRInt32 bytes, SSL3Opaque **b,
+                                 PRUint32 bytes, SSL3Opaque **b,
                                  PRUint32 *length)
 {
     return ssl3_ConsumeHandshakeVariable((sslSocket *)ss, i, bytes, b, length);
 }
--- a/security/nss/lib/ssl/ssl3ext.h
+++ b/security/nss/lib/ssl/ssl3ext.h
@@ -140,17 +140,18 @@ SECStatus ssl3_ExtAppendHandshake(const 
 SECStatus ssl3_ExtAppendHandshakeNumber(const sslSocket *ss, PRInt32 num,
                                         PRInt32 lenSize);
 SECStatus ssl3_ExtAppendHandshakeVariable(const sslSocket *ss,
                                           const SSL3Opaque *src, PRInt32 bytes,
                                           PRInt32 lenSize);
 void ssl3_ExtSendAlert(const sslSocket *ss, SSL3AlertLevel level,
                        SSL3AlertDescription desc);
 void ssl3_ExtDecodeError(const sslSocket *ss);
-SECStatus ssl3_ExtConsumeHandshake(const sslSocket *ss, void *v, PRInt32 bytes,
+SECStatus ssl3_ExtConsumeHandshake(const sslSocket *ss, void *v, PRUint32 bytes,
                                    SSL3Opaque **b, PRUint32 *length);
-PRInt32 ssl3_ExtConsumeHandshakeNumber(const sslSocket *ss, PRInt32 bytes,
-                                       SSL3Opaque **b, PRUint32 *length);
+SECStatus ssl3_ExtConsumeHandshakeNumber(const sslSocket *ss, PRUint32 *num,
+                                         PRUint32 bytes, SSL3Opaque **b,
+                                         PRUint32 *length);
 SECStatus ssl3_ExtConsumeHandshakeVariable(const sslSocket *ss, SECItem *i,
-                                           PRInt32 bytes, SSL3Opaque **b,
+                                           PRUint32 bytes, SSL3Opaque **b,
                                            PRUint32 *length);
 
 #endif
--- a/security/nss/lib/ssl/ssl3exthandle.c
+++ b/security/nss/lib/ssl/ssl3exthandle.c
@@ -218,46 +218,46 @@ ssl3_SendServerNameXtn(const sslSocket *
     return 4;
 }
 
 /* Handle an incoming SNI extension. */
 SECStatus
 ssl3_HandleServerNameXtn(const sslSocket *ss, TLSExtensionData *xtnData, PRUint16 ex_type, SECItem *data)
 {
     SECItem *names = NULL;
-    PRInt32 listLenBytes = 0;
+    PRUint32 listLenBytes = 0;
+    SECStatus rv;
 
     if (!ss->sec.isServer) {
         return SECSuccess; /* ignore extension */
     }
 
     /* Server side - consume client data and register server sender. */
     /* do not parse the data if don't have user extension handling function. */
     if (!ss->sniSocketConfig) {
         return SECSuccess;
     }
 
     /* length of server_name_list */
-    listLenBytes = ssl3_ExtConsumeHandshakeNumber(ss, 2, &data->data, &data->len);
-    if (listLenBytes < 0) {
+    rv = ssl3_ExtConsumeHandshakeNumber(ss, &listLenBytes, 2, &data->data, &data->len);
+    if (rv != SECSuccess) {
         goto loser; /* alert already sent */
     }
     if (listLenBytes == 0 || listLenBytes != data->len) {
         goto alert_loser;
     }
 
     /* Read ServerNameList. */
     while (data->len > 0) {
         SECItem tmp;
-        SECStatus rv;
-        PRInt32 type;
+        PRUint32 type;
 
         /* Read Name Type. */
-        type = ssl3_ExtConsumeHandshakeNumber(ss, 1, &data->data, &data->len);
-        if (type < 0) { /* i.e., SECFailure cast to PRint32 */
+        rv = ssl3_ExtConsumeHandshakeNumber(ss, &type, 1, &data->data, &data->len);
+        if (rv != SECSuccess) {
             /* alert sent in ConsumeHandshakeNumber */
             goto loser;
         }
 
         /* Read ServerName (length and value). */
         rv = ssl3_ExtConsumeHandshakeVariable(ss, &tmp, 2, &data->data, &data->len);
         if (rv != SECSuccess) {
             goto loser;
@@ -537,32 +537,32 @@ ssl3_SelectAppProtocol(const sslSocket *
     xtnData->negotiated[xtnData->numNegotiated++] = ex_type;
     return SECITEM_CopyItem(NULL, &xtnData->nextProto, &result);
 }
 
 /* handle an incoming ALPN extension at the server */
 SECStatus
 ssl3_ServerHandleAppProtoXtn(const sslSocket *ss, TLSExtensionData *xtnData, PRUint16 ex_type, SECItem *data)
 {
-    int count;
+    PRUint32 count;
     SECStatus rv;
 
     /* We expressly don't want to allow ALPN on renegotiation,
      * despite it being permitted by the spec. */
     if (ss->firstHsDone || data->len == 0) {
         /* Clients MUST send a non-empty ALPN extension. */
         ssl3_ExtSendAlert(ss, alert_fatal, illegal_parameter);
         PORT_SetError(SSL_ERROR_NEXT_PROTOCOL_DATA_INVALID);
         return SECFailure;
     }
 
     /* Unlike NPN, ALPN has extra redundant length information so that
      * the extension is the same in both ClientHello and ServerHello. */
-    count = ssl3_ExtConsumeHandshakeNumber(ss, 2, &data->data, &data->len);
-    if (count != data->len) {
+    rv = ssl3_ExtConsumeHandshakeNumber(ss, &count, 2, &data->data, &data->len);
+    if (rv != SECSuccess || count != data->len) {
         ssl3_ExtDecodeError(ss);
         return SECFailure;
     }
 
     if (!ss->nextProtoCallback) {
         /* we're not configured for it */
         return SECSuccess;
     }
@@ -616,17 +616,17 @@ ssl3_ClientHandleNextProtoNegoXtn(const 
 
     return ssl3_SelectAppProtocol(ss, xtnData, ex_type, data);
 }
 
 SECStatus
 ssl3_ClientHandleAppProtoXtn(const sslSocket *ss, TLSExtensionData *xtnData, PRUint16 ex_type, SECItem *data)
 {
     SECStatus rv;
-    PRInt32 list_len;
+    PRUint32 list_len;
     SECItem protocol_name;
 
     if (ssl3_ExtensionNegotiated(ss, ssl_next_proto_nego_xtn)) {
         PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
         return SECFailure;
     }
 
     /* The extension data from the server has the following format:
@@ -634,19 +634,20 @@ ssl3_ClientHandleAppProtoXtn(const sslSo
      *   uint8 len;  // where len >= 1
      *   uint8 protocol_name[len]; */
     if (data->len < 4 || data->len > 2 + 1 + 255) {
         ssl3_ExtSendAlert(ss, alert_fatal, decode_error);
         PORT_SetError(SSL_ERROR_NEXT_PROTOCOL_DATA_INVALID);
         return SECFailure;
     }
 
-    list_len = ssl3_ExtConsumeHandshakeNumber(ss, 2, &data->data, &data->len);
+    rv = ssl3_ExtConsumeHandshakeNumber(ss, &list_len, 2, &data->data,
+                                        &data->len);
     /* The list has to be the entire extension. */
-    if (list_len != data->len) {
+    if (rv != SECSuccess || list_len != data->len) {
         ssl3_ExtSendAlert(ss, alert_fatal, decode_error);
         PORT_SetError(SSL_ERROR_NEXT_PROTOCOL_DATA_INVALID);
         return SECFailure;
     }
 
     rv = ssl3_ExtConsumeHandshakeVariable(ss, &protocol_name, 1,
                                           &data->data, &data->len);
     /* The list must have exactly one value. */
@@ -1358,19 +1359,19 @@ ssl3_ProcessSessionTicketCommon(sslSocke
     PK11Context *hmac_ctx;
     CK_MECHANISM_TYPE macMech = CKM_SHA256_HMAC;
     PK11Context *aes_ctx;
     CK_MECHANISM_TYPE cipherMech = CKM_AES_CBC;
     unsigned char *padding;
     PRUint32 padding_length;
     unsigned char *buffer;
     unsigned int buffer_len;
-    PRInt32 temp;
+    PRUint32 temp;
     SECItem cert_item;
-    PRInt8 nameType = TLS_STE_NO_SERVER_NAME;
+    PRUint32 nameType;
     SECItem macParam = { siBuffer, NULL, 0 };
     SECItem alpn_item;
     SECItem ivItem;
 
     /* Turn off stateless session resumption if the client sends a
      * SessionTicket extension, even if the extension turns out to be
      * malformed (ss->sec.ci.sid is non-NULL when doing session
      * renegotiation.)
@@ -1494,62 +1495,62 @@ ssl3_ProcessSessionTicketCommon(sslSocke
 
     parsed_session_ticket = PORT_ZAlloc(sizeof(SessionTicket));
     if (parsed_session_ticket == NULL) {
         rv = SECFailure;
         goto loser;
     }
 
     /* Read ticket_version and reject if the version is wrong */
-    temp = ssl3_ExtConsumeHandshakeNumber(ss, 2, &buffer, &buffer_len);
-    if (temp != TLS_EX_SESS_TICKET_VERSION)
+    rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 2, &buffer, &buffer_len);
+    if (rv != SECSuccess || temp != TLS_EX_SESS_TICKET_VERSION)
         goto no_ticket;
 
     parsed_session_ticket->ticket_version = (SSL3ProtocolVersion)temp;
 
     /* Read SSLVersion. */
-    temp = ssl3_ExtConsumeHandshakeNumber(ss, 2, &buffer, &buffer_len);
-    if (temp < 0)
+    rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 2, &buffer, &buffer_len);
+    if (rv != SECSuccess)
         goto no_ticket;
     parsed_session_ticket->ssl_version = (SSL3ProtocolVersion)temp;
 
     /* Read cipher_suite. */
-    temp = ssl3_ExtConsumeHandshakeNumber(ss, 2, &buffer, &buffer_len);
-    if (temp < 0)
+    rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 2, &buffer, &buffer_len);
+    if (rv != SECSuccess)
         goto no_ticket;
     parsed_session_ticket->cipher_suite = (ssl3CipherSuite)temp;
 
     /* Read compression_method. */
-    temp = ssl3_ExtConsumeHandshakeNumber(ss, 1, &buffer, &buffer_len);
-    if (temp < 0)
+    rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 1, &buffer, &buffer_len);
+    if (rv != SECSuccess)
         goto no_ticket;
     parsed_session_ticket->compression_method = (SSLCompressionMethod)temp;
 
     /* Read cipher spec parameters. */
-    temp = ssl3_ExtConsumeHandshakeNumber(ss, 1, &buffer, &buffer_len);
-    if (temp < 0)
+    rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 1, &buffer, &buffer_len);
+    if (rv != SECSuccess)
         goto no_ticket;
     parsed_session_ticket->authType = (SSLAuthType)temp;
-    temp = ssl3_ExtConsumeHandshakeNumber(ss, 4, &buffer, &buffer_len);
-    if (temp < 0)
+    rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 4, &buffer, &buffer_len);
+    if (rv != SECSuccess)
         goto no_ticket;
-    parsed_session_ticket->authKeyBits = (PRUint32)temp;
-    temp = ssl3_ExtConsumeHandshakeNumber(ss, 1, &buffer, &buffer_len);
-    if (temp < 0)
+    parsed_session_ticket->authKeyBits = temp;
+    rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 1, &buffer, &buffer_len);
+    if (rv != SECSuccess)
         goto no_ticket;
     parsed_session_ticket->keaType = (SSLKEAType)temp;
-    temp = ssl3_ExtConsumeHandshakeNumber(ss, 4, &buffer, &buffer_len);
-    if (temp < 0)
+    rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 4, &buffer, &buffer_len);
+    if (rv != SECSuccess)
         goto no_ticket;
-    parsed_session_ticket->keaKeyBits = (PRUint32)temp;
+    parsed_session_ticket->keaKeyBits = temp;
 
     /* Read certificate slot */
     parsed_session_ticket->certType.authType = parsed_session_ticket->authType;
-    temp = ssl3_ExtConsumeHandshakeNumber(ss, 1, &buffer, &buffer_len);
-    if (temp < 0)
+    rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 1, &buffer, &buffer_len);
+    if (rv != SECSuccess)
         goto no_ticket;
     switch (parsed_session_ticket->authType) {
         case ssl_auth_ecdsa:
         case ssl_auth_ecdh_rsa:
         case ssl_auth_ecdh_ecdsa: {
             const sslNamedGroupDef *group =
                 ssl_LookupNamedGroup((SSLNamedGroup)temp);
             if (!group || group->keaType != ssl_kea_ecdh) {
@@ -1557,46 +1558,46 @@ ssl3_ProcessSessionTicketCommon(sslSocke
             }
             parsed_session_ticket->certType.namedCurve = group;
         } break;
         default:
             break;
     }
 
     /* Read wrapped master_secret. */
-    temp = ssl3_ExtConsumeHandshakeNumber(ss, 1, &buffer, &buffer_len);
-    if (temp < 0)
+    rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 1, &buffer, &buffer_len);
+    if (rv != SECSuccess)
         goto no_ticket;
     parsed_session_ticket->ms_is_wrapped = (PRBool)temp;
 
-    temp = ssl3_ExtConsumeHandshakeNumber(ss, 4, &buffer, &buffer_len);
-    if (temp < 0)
+    rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 4, &buffer, &buffer_len);
+    if (rv != SECSuccess)
         goto no_ticket;
     parsed_session_ticket->msWrapMech = (CK_MECHANISM_TYPE)temp;
 
-    temp = ssl3_ExtConsumeHandshakeNumber(ss, 2, &buffer, &buffer_len);
-    if (temp < 0)
+    rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 2, &buffer, &buffer_len);
+    if (rv != SECSuccess)
         goto no_ticket;
     parsed_session_ticket->ms_length = (PRUint16)temp;
     if (parsed_session_ticket->ms_length == 0 || /* sanity check MS. */
         parsed_session_ticket->ms_length >
             sizeof(parsed_session_ticket->master_secret))
         goto no_ticket;
 
     /* Allow for the wrapped master secret to be longer. */
     if (buffer_len < parsed_session_ticket->ms_length)
         goto no_ticket;
     PORT_Memcpy(parsed_session_ticket->master_secret, buffer,
                 parsed_session_ticket->ms_length);
     buffer += parsed_session_ticket->ms_length;
     buffer_len -= parsed_session_ticket->ms_length;
 
     /* Read client_identity */
-    temp = ssl3_ExtConsumeHandshakeNumber(ss, 1, &buffer, &buffer_len);
-    if (temp < 0)
+    rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 1, &buffer, &buffer_len);
+    if (rv != SECSuccess)
         goto no_ticket;
     parsed_session_ticket->client_identity.client_auth_type =
         (ClientAuthenticationType)temp;
     switch (parsed_session_ticket->client_identity.client_auth_type) {
         case CLIENT_AUTH_ANONYMOUS:
             break;
         case CLIENT_AUTH_CERTIFICATE:
             rv = ssl3_ExtConsumeHandshakeVariable(ss, &cert_item, 3,
@@ -1607,40 +1608,41 @@ ssl3_ProcessSessionTicketCommon(sslSocke
                                   &cert_item);
             if (rv != SECSuccess)
                 goto no_ticket;
             break;
         default:
             goto no_ticket;
     }
     /* Read timestamp. */
-    temp = ssl3_ExtConsumeHandshakeNumber(ss, 4, &buffer, &buffer_len);
-    if (temp < 0)
+    rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 4, &buffer, &buffer_len);
+    if (rv != SECSuccess)
         goto no_ticket;
-    parsed_session_ticket->timestamp = (PRUint32)temp;
+    parsed_session_ticket->timestamp = temp;
 
     /* Read server name */
-    nameType =
-        ssl3_ExtConsumeHandshakeNumber(ss, 1, &buffer, &buffer_len);
-    if (nameType != TLS_STE_NO_SERVER_NAME) {
+    rv = ssl3_ExtConsumeHandshakeNumber(ss, &nameType, 1, &buffer, &buffer_len);
+    if (rv != SECSuccess)
+        goto no_ticket;
+    if ((PRInt8)nameType != TLS_STE_NO_SERVER_NAME) {
         SECItem name_item;
         rv = ssl3_ExtConsumeHandshakeVariable(ss, &name_item, 2, &buffer,
                                               &buffer_len);
         if (rv != SECSuccess)
             goto no_ticket;
         rv = SECITEM_CopyItem(NULL, &parsed_session_ticket->srvName,
                               &name_item);
         if (rv != SECSuccess)
             goto no_ticket;
-        parsed_session_ticket->srvName.type = nameType;
+        parsed_session_ticket->srvName.type = (PRUint8)nameType;
     }
 
     /* Read extendedMasterSecretUsed */
-    temp = ssl3_ExtConsumeHandshakeNumber(ss, 1, &buffer, &buffer_len);
-    if (temp < 0)
+    rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 1, &buffer, &buffer_len);
+    if (rv != SECSuccess)
         goto no_ticket;
     PORT_Assert(temp == PR_TRUE || temp == PR_FALSE);
     parsed_session_ticket->extendedMasterSecretUsed = (PRBool)temp;
 
     rv = ssl3_ExtConsumeHandshake(ss, &parsed_session_ticket->flags, 4,
                                   &buffer, &buffer_len);
     if (rv != SECSuccess)
         goto no_ticket;
@@ -2479,45 +2481,47 @@ ssl3_HandleSupportedPointFormatsXtn(cons
     /* Poor client doesn't support uncompressed points. */
     PORT_SetError(SSL_ERROR_RX_MALFORMED_HANDSHAKE);
     return SECFailure;
 }
 
 static SECStatus
 ssl_UpdateSupportedGroups(sslSocket *ss, SECItem *data)
 {
-    PRInt32 list_len;
+    SECStatus rv;
+    PRUint32 list_len;
     unsigned int i;
     const sslNamedGroupDef *enabled[SSL_NAMED_GROUP_COUNT] = { 0 };
     PORT_Assert(SSL_NAMED_GROUP_COUNT == PR_ARRAY_SIZE(enabled));
 
     if (!data->data || data->len < 4) {
         (void)ssl3_DecodeError(ss);
         return SECFailure;
     }
 
     /* get the length of elliptic_curve_list */
-    list_len = ssl3_ConsumeHandshakeNumber(ss, 2, &data->data, &data->len);
-    if (list_len < 0 || data->len != list_len || (data->len % 2) != 0) {
+    rv = ssl3_ConsumeHandshakeNumber(ss, &list_len, 2, &data->data, &data->len);
+    if (rv != SECSuccess || data->len != list_len || (data->len % 2) != 0) {
         (void)ssl3_DecodeError(ss);
         return SECFailure;
     }
 
     /* disable all groups and remember the enabled groups */
     for (i = 0; i < SSL_NAMED_GROUP_COUNT; ++i) {
         enabled[i] = ss->namedGroupPreferences[i];
         ss->namedGroupPreferences[i] = NULL;
     }
 
     /* Read groups from data and enable if in |enabled| */
     while (data->len) {
         const sslNamedGroupDef *group;
-        PRInt32 curve_name =
-            ssl3_ConsumeHandshakeNumber(ss, 2, &data->data, &data->len);
-        if (curve_name < 0) {
+        PRUint32 curve_name;
+        rv = ssl3_ConsumeHandshakeNumber(ss, &curve_name, 2, &data->data,
+                                         &data->len);
+        if (rv != SECSuccess) {
             return SECFailure; /* fatal alert already sent */
         }
         group = ssl_LookupNamedGroup(curve_name);
         if (group) {
             for (i = 0; i < SSL_NAMED_GROUP_COUNT; ++i) {
                 if (enabled[i] && group == enabled[i]) {
                     ss->namedGroupPreferences[i] = enabled[i];
                     break;
--- a/security/nss/lib/ssl/sslimpl.h
+++ b/security/nss/lib/ssl/sslimpl.h
@@ -29,17 +29,17 @@
 #include "nssrwlk.h"
 #include "prthread.h"
 #include "prclist.h"
 #include "private/pprthred.h"
 
 #include "sslt.h" /* for some formerly private types, now public */
 
 typedef struct sslSocketStr sslSocket;
-
+typedef struct ssl3CipherSpecStr ssl3CipherSpec;
 #include "ssl3ext.h"
 
 /* to make some of these old enums public without namespace pollution,
 ** it was necessary to prepend ssl_ to the names.
 ** These #defines preserve compatibility with the old code here in libssl.
 */
 typedef SSLMACAlgorithm SSL3MACAlgorithm;
 
@@ -195,16 +195,19 @@ struct ssl3CertNodeStr {
 typedef SECStatus (*sslHandshakeFunc)(sslSocket *ss);
 
 typedef void (*sslSessionIDCacheFunc)(sslSessionID *sid);
 typedef void (*sslSessionIDUncacheFunc)(sslSessionID *sid);
 typedef sslSessionID *(*sslSessionIDLookupFunc)(const PRIPv6Addr *addr,
                                                 unsigned char *sid,
                                                 unsigned int sidLen,
                                                 CERTCertDBHandle *dbHandle);
+typedef void (*sslCipherSpecChangedFunc)(void *arg,
+                                         PRBool sending,
+                                         ssl3CipherSpec *newSpec);
 
 /* Socket ops */
 struct sslSocketOpsStr {
     int (*connect)(sslSocket *, const PRNetAddr *);
     PRFileDesc *(*accept)(sslSocket *, PRNetAddr *);
     int (*bind)(sslSocket *, const PRNetAddr *);
     int (*listen)(sslSocket *, int);
     int (*shutdown)(sslSocket *, int);
@@ -463,17 +466,17 @@ typedef struct DTLSRecvdRecordsStr {
     sslSequenceNumber right;
 } DTLSRecvdRecords;
 
 /*
 ** These are the "specs" in the "ssl3" struct.
 ** Access to the pointers to these specs, and all the specs' contents
 ** (direct and indirect) is protected by the reader/writer lock ss->specLock.
 */
-typedef struct {
+struct ssl3CipherSpecStr {
     PRCList link;
     const ssl3BulkCipherDef *cipher_def;
     const ssl3MACDef *mac_def;
     SSLCompressionMethod compression_method;
     int mac_size;
     SSLCipher encode;
     SSLCipher decode;
     SSLAEADCipher aead;
@@ -493,17 +496,17 @@ typedef struct {
     ssl3KeyMaterial client;
     ssl3KeyMaterial server;
     SECItem msItem;
     DTLSEpoch epoch;
     DTLSRecvdRecords recvdRecords;
 
     PRUint8 refCt;
     const char *phase;
-} ssl3CipherSpec;
+};
 
 typedef enum { never_cached,
                in_client_cache,
                in_server_cache,
                invalid_cache /* no longer in any cache. */
 } Cached;
 
 #include "sslcert.h"
@@ -887,16 +890,21 @@ struct ssl3StateStr {
     ** The following Specs and Spec pointers must be protected using the
     ** Spec Lock.
     */
     ssl3CipherSpec *crSpec; /* current read spec. */
     ssl3CipherSpec *prSpec; /* pending read spec. */
     ssl3CipherSpec *cwSpec; /* current write spec. */
     ssl3CipherSpec *pwSpec; /* pending write spec. */
 
+    /* Internal callback for when we do a cipher suite change. Used for
+     * debugging in TLS 1.3. This can only be set by non-public functions. */
+    sslCipherSpecChangedFunc changedCipherSpecFunc;
+    void *changedCipherSpecArg;
+
     CERTCertificate *clientCertificate;   /* used by client */
     SECKEYPrivateKey *clientPrivateKey;   /* used by client */
     CERTCertificateList *clientCertChain; /* used by client */
     PRBool sendEmptyCert;                 /* used by client */
 
     int policy;
     /* This says what cipher suites we can do, and should
      * be either SSL_ALLOWED or SSL_RESTRICTED
@@ -1640,22 +1648,23 @@ extern SECStatus ssl3_AppendHandshake(ss
 extern SECStatus ssl3_AppendHandshakeHeader(sslSocket *ss,
                                             SSL3HandshakeType t, PRUint32 length);
 extern SECStatus ssl3_AppendHandshakeNumber(sslSocket *ss, PRInt32 num,
                                             PRInt32 lenSize);
 extern SECStatus ssl3_AppendHandshakeVariable(sslSocket *ss,
                                               const SSL3Opaque *src, PRInt32 bytes, PRInt32 lenSize);
 extern SECStatus ssl3_AppendSignatureAndHashAlgorithm(
     sslSocket *ss, const SSLSignatureAndHashAlg *sigAndHash);
-extern SECStatus ssl3_ConsumeHandshake(sslSocket *ss, void *v, PRInt32 bytes,
+extern SECStatus ssl3_ConsumeHandshake(sslSocket *ss, void *v, PRUint32 bytes,
                                        SSL3Opaque **b, PRUint32 *length);
-extern PRInt32 ssl3_ConsumeHandshakeNumber(sslSocket *ss, PRInt32 bytes,
-                                           SSL3Opaque **b, PRUint32 *length);
+extern SECStatus ssl3_ConsumeHandshakeNumber(sslSocket *ss, PRUint32 *num,
+                                             PRUint32 bytes, SSL3Opaque **b,
+                                             PRUint32 *length);
 extern SECStatus ssl3_ConsumeHandshakeVariable(sslSocket *ss, SECItem *i,
-                                               PRInt32 bytes, SSL3Opaque **b,
+                                               PRUint32 bytes, SSL3Opaque **b,
                                                PRUint32 *length);
 extern PRUint8 *ssl_EncodeUintX(PRUint64 value, unsigned int bytes,
                                 PRUint8 *to);
 extern PRBool ssl_IsSupportedSignatureScheme(SSLSignatureScheme scheme);
 extern SECStatus ssl_CheckSignatureSchemeConsistency(
     sslSocket *ss, SSLSignatureScheme scheme, CERTCertificate *cert);
 extern SECStatus ssl_ParseSignatureSchemes(const sslSocket *ss, PLArenaPool *arena,
                                            SSLSignatureScheme **schemesOut,
--- a/security/nss/lib/ssl/sslproto.h
+++ b/security/nss/lib/ssl/sslproto.h
@@ -204,16 +204,26 @@
 #define TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 0xCCA9
 #define TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256     0xCCAA
 
 /* Special TLS 1.3 cipher suites that really just specify AEAD */
 #define TLS_AES_128_GCM_SHA256                0x1301
 #define TLS_AES_256_GCM_SHA384                0x1302
 #define TLS_CHACHA20_POLY1305_SHA256          0x1303
 
+/* PSK cipher suites. NSS doesn't actually support these, but we
+ * exposed them when TLS 1.3 used them so we need to keep them
+ * in the API. */
+#define TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256   0xCCAC
+#define TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256     0xCCAD
+#define TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256   0xD001
+#define TLS_ECDHE_PSK_WITH_AES_256_GCM_SHA384   0xD002
+#define TLS_DHE_PSK_WITH_AES_128_GCM_SHA256     0x00AA /* RFC 5487 */
+#define TLS_DHE_PSK_WITH_AES_256_GCM_SHA384     0x00AB /* RFC 5487 */
+
 /* DTLS-SRTP cipher suites from RFC 5764 */
 /* If you modify this, also modify MAX_DTLS_SRTP_CIPHER_SUITES in sslimpl.h */
 #define SRTP_AES128_CM_HMAC_SHA1_80             0x0001
 #define SRTP_AES128_CM_HMAC_SHA1_32             0x0002
 #define SRTP_NULL_HMAC_SHA1_80                  0x0005
 #define SRTP_NULL_HMAC_SHA1_32                  0x0006
 
 /* DO NOT USE. (deprecated, will be removed) */
--- a/security/nss/lib/ssl/tls13con.c
+++ b/security/nss/lib/ssl/tls13con.c
@@ -1664,17 +1664,17 @@ tls13_SendCertificateRequest(sslSocket *
 
     return SECSuccess;
 }
 
 SECStatus
 tls13_HandleHelloRetryRequest(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
 {
     SECStatus rv;
-    PRInt32 tmp;
+    PRUint32 tmp;
     SSL3ProtocolVersion version;
 
     SSL_TRC(3, ("%d: TLS13[%d]: handle hello retry request",
                 SSL_GETPID(), ss->fd));
 
     PORT_Assert(ss->opt.noLocks || ssl_HaveRecvBufLock(ss));
     PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
 
@@ -1713,18 +1713,18 @@ tls13_HandleHelloRetryRequest(sslSocket 
     }
     if (version > ss->vrange.max || version < SSL_LIBRARY_VERSION_TLS_1_3) {
         FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_HELLO_RETRY_REQUEST,
                     protocol_version);
         return SECFailure;
     }
 
     /* Extensions. */
-    tmp = ssl3_ConsumeHandshakeNumber(ss, 2, &b, &length);
-    if (tmp < 0) {
+    rv = ssl3_ConsumeHandshakeNumber(ss, &tmp, 2, &b, &length);
+    if (rv != SECSuccess) {
         return SECFailure; /* error code already set */
     }
     /* Extensions must be non-empty and use the remainder of the message.
      * This means that a HelloRetryRequest cannot be a no-op: we must have an
      * extension, it must be one that we understand and recognize as being valid
      * for HelloRetryRequest, and all the extensions we permit cause us to
      * modify our ClientHello in some way. */
     if (!tmp || tmp != length) {
@@ -1752,17 +1752,17 @@ tls13_HandleHelloRetryRequest(sslSocket 
 
 static SECStatus
 tls13_HandleCertificateRequest(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
 {
     SECStatus rv;
     TLS13CertificateRequest *certRequest = NULL;
     SECItem context = { siBuffer, NULL, 0 };
     PLArenaPool *arena;
-    PRInt32 extensionsLength;
+    PRUint32 extensionsLength;
 
     SSL_TRC(3, ("%d: TLS13[%d]: handle certificate_request sequence",
                 SSL_GETPID(), ss->fd));
 
     PORT_Assert(ss->opt.noLocks || ssl_HaveRecvBufLock(ss));
     PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
 
     /* Client */
@@ -1811,18 +1811,18 @@ tls13_HandleCertificateRequest(sslSocket
     }
 
     rv = ssl3_ParseCertificateRequestCAs(ss, &b, &length, arena,
                                          &certRequest->ca_list);
     if (rv != SECSuccess)
         goto loser; /* alert already sent */
 
     /* Verify that the extensions length is correct. */
-    extensionsLength = ssl3_ConsumeHandshakeNumber(ss, 2, &b, &length);
-    if (extensionsLength < 0) {
+    rv = ssl3_ConsumeHandshakeNumber(ss, &extensionsLength, 2, &b, &length);
+    if (rv != SECSuccess) {
         goto loser; /* alert already sent */
     }
     if (extensionsLength != length) {
         FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_CERT_REQUEST,
                     illegal_parameter);
         goto loser;
     }
 
@@ -2769,16 +2769,20 @@ tls13_SetCipherSpec(sslSocket *ss, Traff
     *specp = spec;                   /* Overwrite. */
     ssl_ReleaseSpecWriteLock(ss);
 
     SSL_TRC(3, ("%d: TLS13[%d]: %s installed key for phase='%s'.%d dir=%s",
                 SSL_GETPID(), ss->fd, SSL_ROLE(ss),
                 spec->phase, spec->epoch,
                 direction == CipherSpecRead ? "read" : "write"));
 
+    if (ss->ssl3.changedCipherSpecFunc) {
+        ss->ssl3.changedCipherSpecFunc(ss->ssl3.changedCipherSpecArg,
+                                       direction == CipherSpecWrite, spec);
+    }
     return SECSuccess;
 }
 
 static SECStatus
 tls13_ComputeHandshakeHashes(sslSocket *ss,
                              SSL3Hashes *hashes)
 {
     SECStatus rv;
@@ -2914,16 +2918,17 @@ tls13_WriteNonce(ssl3KeyMaterial *keys,
     PORT_Assert(nonceLen == 12);
     memcpy(nonce, keys->write_iv, 12);
 
     /* XOR the last 8 bytes of the IV with the sequence number. */
     PORT_Assert(seqNumLen == 8);
     for (i = 0; i < 8; ++i) {
         nonce[4 + i] ^= seqNumBuf[i];
     }
+    PRINT_BUF(50, (NULL, "Nonce", nonce, nonceLen));
 }
 
 /* Implement the SSLAEADCipher interface defined in sslimpl.h.
  *
  * That interface takes the additional data (see below) and reinterprets that as
  * a sequence number. In TLS 1.3 there is no additional data so this value is
  * just the encoded sequence number.
  */
@@ -3003,33 +3008,33 @@ tls13_ChaCha20Poly1305(ssl3KeyMaterial *
                       CKM_NSS_CHACHA20_POLY1305,
                       (unsigned char *)&aeadParams, sizeof(aeadParams));
 }
 
 static SECStatus
 tls13_HandleEncryptedExtensions(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
 {
     SECStatus rv;
-    PRInt32 innerLength;
+    PRUint32 innerLength;
     SECItem oldNpn = { siBuffer, NULL, 0 };
 
     PORT_Assert(ss->opt.noLocks || ssl_HaveRecvBufLock(ss));
     PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
 
     SSL_TRC(3, ("%d: TLS13[%d]: handle encrypted extensions",
                 SSL_GETPID(), ss->fd));
 
     rv = TLS13_CHECK_HS_STATE(ss, SSL_ERROR_RX_UNEXPECTED_ENCRYPTED_EXTENSIONS,
                               wait_encrypted_extensions);
     if (rv != SECSuccess) {
         return SECFailure;
     }
 
-    innerLength = ssl3_ConsumeHandshakeNumber(ss, 2, &b, &length);
-    if (innerLength < 0) {
+    rv = ssl3_ConsumeHandshakeNumber(ss, &innerLength, 2, &b, &length);
+    if (rv != SECSuccess) {
         return SECFailure; /* Alert already sent. */
     }
     if (innerLength != length) {
         FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_ENCRYPTED_EXTENSIONS,
                     illegal_parameter);
         return SECFailure;
     }
 
@@ -3838,17 +3843,16 @@ loser:
     }
     return SECFailure;
 }
 
 static SECStatus
 tls13_HandleNewSessionTicket(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
 {
     SECStatus rv;
-    PRInt32 tmp;
     PRUint32 utmp;
     NewSessionTicket ticket = { 0 };
     SECItem data;
     SECItem ticket_data;
 
     SSL_TRC(3, ("%d: TLS13[%d]: handle new session ticket message",
                 SSL_GETPID(), ss->fd));
 
@@ -3859,23 +3863,23 @@ tls13_HandleNewSessionTicket(sslSocket *
     }
     if (!ss->firstHsDone || ss->sec.isServer) {
         FATAL_ERROR(ss, SSL_ERROR_RX_UNEXPECTED_NEW_SESSION_TICKET,
                     unexpected_message);
         return SECFailure;
     }
 
     ticket.received_timestamp = ssl_Time();
-    tmp = ssl3_ConsumeHandshakeNumber(ss, 4, &b, &length);
-    if (tmp < 0) {
+    rv = ssl3_ConsumeHandshakeNumber(ss, &ticket.ticket_lifetime_hint, 4, &b,
+                                     &length);
+    if (rv != SECSuccess) {
         FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_NEW_SESSION_TICKET,
                     decode_error);
         return SECFailure;
     }
-    ticket.ticket_lifetime_hint = (PRUint32)tmp;
     ticket.ticket.type = siBuffer;
 
     rv = ssl3_ConsumeHandshake(ss, &utmp, sizeof(utmp),
                                &b, &length);
     if (rv != SECSuccess) {
         PORT_SetError(SSL_ERROR_RX_MALFORMED_NEW_SESSION_TICKET);
         return SECFailure;
     }
--- a/security/nss/lib/ssl/tls13exthandle.c
+++ b/security/nss/lib/ssl/tls13exthandle.c
@@ -203,23 +203,23 @@ tls13_ClientSendKeyShareXtn(const sslSoc
 loser:
     return -1;
 }
 
 static SECStatus
 tls13_HandleKeyShareEntry(const sslSocket *ss, TLSExtensionData *xtnData, SECItem *data)
 {
     SECStatus rv;
-    PRInt32 group;
+    PRUint32 group;
     const sslNamedGroupDef *groupDef;
     TLS13KeyShareEntry *ks = NULL;
     SECItem share = { siBuffer, NULL, 0 };
 
-    group = ssl3_ExtConsumeHandshakeNumber(ss, 2, &data->data, &data->len);
-    if (group < 0) {
+    rv = ssl3_ExtConsumeHandshakeNumber(ss, &group, 2, &data->data, &data->len);
+    if (rv != SECSuccess) {
         PORT_SetError(SSL_ERROR_RX_MALFORMED_KEY_SHARE);
         goto loser;
     }
     groupDef = ssl_LookupNamedGroup(group);
     rv = ssl3_ExtConsumeHandshakeVariable(ss, &share, 2, &data->data,
                                           &data->len);
     if (rv != SECSuccess) {
         goto loser;
@@ -280,27 +280,27 @@ tls13_ClientHandleKeyShareXtn(const sslS
 
     return SECSuccess;
 }
 
 SECStatus
 tls13_ClientHandleKeyShareXtnHrr(const sslSocket *ss, TLSExtensionData *xtnData, PRUint16 ex_type, SECItem *data)
 {
     SECStatus rv;
-    PRInt32 tmp;
+    PRUint32 tmp;
     const sslNamedGroupDef *group;
 
     PORT_Assert(!ss->sec.isServer);
     PORT_Assert(ss->vrange.max >= SSL_LIBRARY_VERSION_TLS_1_3);
 
     SSL_TRC(3, ("%d: SSL3[%d]: handle key_share extension in HRR",
                 SSL_GETPID(), ss->fd));
 
-    tmp = ssl3_ExtConsumeHandshakeNumber(ss, 2, &data->data, &data->len);
-    if (tmp < 0) {
+    rv = ssl3_ExtConsumeHandshakeNumber(ss, &tmp, 2, &data->data, &data->len);
+    if (rv != SECSuccess) {
         return SECFailure; /* error code already set */
     }
     if (data->len) {
         ssl3_ExtSendAlert(ss, alert_fatal, decode_error);
         PORT_SetError(SSL_ERROR_RX_MALFORMED_HELLO_RETRY_REQUEST);
         return SECFailure;
     }
 
@@ -330,33 +330,33 @@ tls13_ClientHandleKeyShareXtnHrr(const s
 
 /* Handle an incoming KeyShare extension at the server and copy to
  * |xtnData->remoteKeyShares| for future use. The key
  * share is processed in tls13_HandleClientKeyShare(). */
 SECStatus
 tls13_ServerHandleKeyShareXtn(const sslSocket *ss, TLSExtensionData *xtnData, PRUint16 ex_type, SECItem *data)
 {
     SECStatus rv;
-    PRInt32 length;
+    PRUint32 length;
 
     PORT_Assert(ss->sec.isServer);
     PORT_Assert(PR_CLIST_IS_EMPTY(&xtnData->remoteKeyShares));
 
     if (ss->version < SSL_LIBRARY_VERSION_TLS_1_3) {
         return SECSuccess;
     }
 
     SSL_TRC(3, ("%d: SSL3[%d]: handle key_share extension",
                 SSL_GETPID(), ss->fd));
 
     /* Redundant length because of TLS encoding (this vector consumes
      * the entire extension.) */
-    length = ssl3_ExtConsumeHandshakeNumber(ss, 2, &data->data,
-                                            &data->len);
-    if (length < 0)
+    rv = ssl3_ExtConsumeHandshakeNumber(ss, &length, 2, &data->data,
+                                        &data->len);
+    if (rv != SECSuccess)
         goto loser;
     if (length != data->len) {
         /* Check for consistency */
         PORT_SetError(SSL_ERROR_RX_MALFORMED_KEY_SHARE);
         goto loser;
     }
 
     while (data->len) {
@@ -679,28 +679,29 @@ tls13_ServerSendPreSharedKeyXtn(const ss
 }
 
 /* Handle a TLS 1.3 PreSharedKey Extension. We only accept PSKs
  * that contain session tickets. */
 SECStatus
 tls13_ClientHandlePreSharedKeyXtn(const sslSocket *ss, TLSExtensionData *xtnData, PRUint16 ex_type,
                                   SECItem *data)
 {
-    PRInt32 index;
+    PRUint32 index;
+    SECStatus rv;
 
     SSL_TRC(3, ("%d: SSL3[%d]: handle pre_shared_key extension",
                 SSL_GETPID(), ss->fd));
 
     /* If we are doing < TLS 1.3, then ignore this. */
     if (ss->version < SSL_LIBRARY_VERSION_TLS_1_3) {
         return SECSuccess;
     }
 
-    index = ssl3_ExtConsumeHandshakeNumber(ss, 2, &data->data, &data->len);
-    if (index < 0)
+    rv = ssl3_ExtConsumeHandshakeNumber(ss, &index, 2, &data->data, &data->len);
+    if (rv != SECSuccess)
         return SECFailure;
 
     /* This should be the end of the extension. */
     if (data->len) {
         PORT_SetError(SSL_ERROR_MALFORMED_PRE_SHARED_KEY);
         return SECFailure;
     }
 
--- a/security/nss/readme.md
+++ b/security/nss/readme.md
@@ -1,98 +1,169 @@
 # Network Security Services
 
-Network Security Services (NSS) is a set of libraries designed to support cross-platform development of security-enabled client and server applications. NSS supports SSL v3-TLS 1.2 (experimental TLS 1.3), PKCS #5, PKCS #7, PKCS #11, PKCS #12, S/MIME, X.509 v3 certificates, and other security standards.
+Network Security Services (NSS) is a set of libraries designed to support
+cross-platform development of security-enabled client and server
+applications. NSS supports SSL v3-TLS 1.2 (experimental TLS 1.3), PKCS #5, PKCS
+#7, PKCS #11, PKCS #12, S/MIME, X.509 v3 certificates, and other security
+standards.
 
 ## Getting started
-In order to get started create a new directory on that you will be uses as your local work area, and check out NSS and NSPR. (Note that there's no git mirror of NSPR and you require mercurial to get the latest NSPR source.)
+
+In order to get started create a new directory on that you will be uses as your
+local work area, and check out NSS and NSPR. (Note that there's no git mirror of
+NSPR and you require mercurial to get the latest NSPR source.)
 
     git clone https://github.com/nss-dev/nss.git
     hg clone https://hg.mozilla.org/projects/nspr
 
 NSS can also be cloned with mercurial `
     hg clone https://hg.mozilla.org/projects/nspr`
 
 ## Building NSS
-*This build system is under development. It does not yet support all the features or platforms that NSS supports. To build on anything other than Mac or Linux please use the legacy build system as described below.*
+
+**This build system is under development. It does not yet support all the
+features or platforms that NSS supports. To build on anything other than Mac or
+Linux please use the legacy build system as described below.**
 
 Build requirements:
 
 * [gyp](https://gyp.gsrc.io/)
 * [ninja](https://ninja-build.org/)
 
 After changing into the NSS directory a typical build is done as follows
 
     ./build.sh
 
-Once the build is done the build output is found in the directory `../dist/*.OBJ`, where `*` will be a name dynamically derived from your system's architecture. Exported header files can be found in the `include` directory, library files in directory `lib`, and tools in directory `bin`. In order to run the tools, set your system environment to use the libraries of your build from the "lib" directory, e.g., using the `LD_LIBRARY_PATH` or `DYLD_LIBRARY_PATH`.
+Once the build is done the build output is found in the directory
+`../dist/*.OBJ`, where `*` will be a name dynamically derived from your system's
+architecture. Exported header files can be found in the `include` directory,
+library files in directory `lib`, and tools in directory `bin`. In order to run
+the tools, set your system environment to use the libraries of your build from
+the "lib" directory, e.g., using the `LD_LIBRARY_PATH` or `DYLD_LIBRARY_PATH`.
 
----
     Usage: build.sh [-hcgv] [-j <n>] [--test] [--fuzz] [--scan-build[=output]]
-                    [-m32] [--opt|-o]
+                    [-m32] [--opt|-o] [--asan] [--ubsan] [--sancov[=edge|bb|func]]
+                    [--pprof] [--msan]
+
+    This script builds NSS with gyp and ninja.
+
+    This build system is still under development.  It does not yet support all
+    the features or platforms that NSS supports.
+
+    NSS build tool options:
 
-    -h            display this help and exit
-    -c            clean before build
-    -g            force a rebuild of gyp (and NSPR, because why not)
-    -j <n>        run at most <n> concurrent jobs
-    -v            verbose build
-    -m32          do a 32-bit build on a 64-bit system
-    --test        ignore map files and export everything we have
-    --fuzz        enable fuzzing mode. this always enables test builds
-    --scan-build  run the build with scan-build (scan-build has to be in the path)
-                  --scan-build=/out/path sets the output path for scan-build
-    --opt|-o      do an opt build
+        -h            display this help and exit
+        -c            clean before build
+        -g            force a rebuild of gyp (and NSPR, because why not)
+        -j <n>        run at most <n> concurrent jobs
+        -v            verbose build
+        -m32          do a 32-bit build on a 64-bit system
+        --test        ignore map files and export everything we have
+        --fuzz        enable fuzzing mode. this always enables test builds
+        --scan-build  run the build with scan-build (scan-build has to be in the path)
+                      --scan-build=/out/path sets the output path for scan-build
+        --opt|-o      do an opt build
+        --asan        do an asan build
+        --ubsan       do an ubsan build
+        --msan        do an msan build
+        --sancov      do sanitize coverage builds
+                      --sancov=func sets coverage to function level for example
+        --pprof       build with gperftool support
+
 
 ## Building NSS (legacy build system)
-After changing into the NSS directory a typical build of 32-bit NSS is done as follows
+
+After changing into the NSS directory a typical build of 32-bit NSS is done as
+follows:
 
     make nss_build_all
 
 The following environment variables might be useful:
+
 * `BUILD_OPT=1` to get an optimised build
+
 * `USE_64=1` to get a 64-bit build (recommended)
 
-The complete list of environment variables can be found [here](https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS/Reference/NSS_environment_variables).
+The complete list of environment variables can be found
+[here](https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS/Reference/NSS_environment_variables).
 
-To clean the build directory run
+To clean the build directory run:
 
     make nss_clean_all
 
 ## Tests
+
 ### Setup
-Make sure that the address `$HOST.$DOMSUF` on your computer is available. This is necessary because NSS tests generate certificates and establish TLS connections, which requires a fully qualified domain name.
-You can test this by calling `ping $HOST.$DOMSUF`. If this is working, you're all set.
-If it's not, set or export
+
+Make sure that the address `$HOST.$DOMSUF` on your computer is available. This
+is necessary because NSS tests generate certificates and establish TLS
+connections, which requires a fully qualified domain name.
+You can test this by
+calling `ping $HOST.$DOMSUF`. If this is working, you're all set.  If it's not,
+set or export:
 
     HOST=nss
     DOMSUF=local
-Note that you might have to add `nss.local` to `/etc/hosts` if it's not there. The entry should look something like `127.0.0.1       nss.local   nss`.
-If you get name resolution errors, try to disable IPv6 on the loopback device, i.e. comment the lines starting with `::1` in your `/etc/hosts` .
+
+Note that you might have to add `nss.local` to `/etc/hosts` if it's not
+there. The entry should look something like `127.0.0.1 nss.local nss`.
+
+If you get name resolution errors, try to ensure that you are using an IPv4
+address; IPv6 is the default on many systems for the loopback device which
+doesn't work.
 
 ### Running tests
-*Runnning all tests will take a while!*
+
+**Runnning all tests will take a while!**
 
     cd tests
     ./all.sh
-Make sure that all environment variables set for the build are set while running the tests as well.
-Test results are published in the folder `../../test_results/`.
-Individual tests can be run with the `NSS_TESTS` environment variable, e.g. `NSS_TESTS=ssl_gtests ./all.sh` or by changing into the according directory and running the bash script there `cd ssl_gtests && ./ssl_gtests.sh`.  The following tests are available:
+
+Make sure that all environment variables set for the build are set while running
+the tests as well.  Test results are published in the folder
+`../../test_results/`.
+
+Individual tests can be run with the `NSS_TESTS` environment variable,
+e.g. `NSS_TESTS=ssl_gtests ./all.sh` or by changing into the according directory
+and running the bash script there `cd ssl_gtests && ./ssl_gtests.sh`.  The
+following tests are available:
 
     cipher lowhash libpkix cert dbtests tools fips sdr crmf smime ssl ocsp merge pkits chains ec gtests ssl_gtests bogo
 
-To make tests run faster it's recommended to set `NSS_CYCLES=standard` to run only the standard cycle.
+To make tests run faster it's recommended to set `NSS_CYCLES=standard` to run
+only the standard cycle.
 
 ## Releases
-NSS releases can be found at [Mozilla's download server](https://ftp.mozilla.org/pub/security/nss/releases/). Because NSS depends on the base library NSPR you should download the archive that combines both NSS and NSPR.
+
+NSS releases can be found at [Mozilla's download
+server](https://ftp.mozilla.org/pub/security/nss/releases/). Because NSS depends
+on the base library NSPR you should download the archive that combines both NSS
+and NSPR.
 
 ## Contributing
-[Bugzilla](https://bugzilla.mozilla.org/) is used to track NSS development and bugs. File new bugs in the NSS product.
-A list with good first bugs to start with are [listed here](https://bugzilla.mozilla.org/buglist.cgi?keywords=good-first-bug%2C%20&keywords_type=allwords&list_id=13238861&resolution=---&query_format=advanced&product=NSS).
+
+[Bugzilla](https://bugzilla.mozilla.org/) is used to track NSS development and
+bugs. File new bugs in the NSS product.
+
+A list with good first bugs to start with are [listed
+here](https://bugzilla.mozilla.org/buglist.cgi?keywords=good-first-bug%2C%20&keywords_type=allwords&list_id=13238861&resolution=---&query_format=advanced&product=NSS).
 
 ### NSS Folder Structure
+
 The nss directory contains the following important subdirectories:
+
 - `coreconf` contains the build logic.
+
 - `lib` contains all library code that is used to create the runtime libraries.
-- `cmd` contains a set of various tool programs that are built with NSS. Several tools are general purpose and can be used to inspect and manipulate the storage files that software using the NSS library creates and modifies. Other tools are only used for testing purposes.
-- `test` and `gtests` contain the NSS test suite. While `test` contains shell scripts to drive test programs in `cmd`, `gtests` holds a set of [gtests](https://github.com/google/googletest).
+
+- `cmd` contains a set of various tool programs that are built with NSS. Several
+  tools are general purpose and can be used to inspect and manipulate the
+  storage files that software using the NSS library creates and modifies. Other
+  tools are only used for testing purposes.
 
-A more comprehensible overview of the NSS folder structure and API guidelines can be found [here](https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS/NSS_API_Guidelines).
+- `test` and `gtests` contain the NSS test suite. While `test` contains shell
+  scripts to drive test programs in `cmd`, `gtests` holds a set of
+  [gtests](https://github.com/google/googletest).
 
+A more comprehensible overview of the NSS folder structure and API guidelines
+can be found
+[here](https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS/NSS_API_Guidelines).