Bug 1296266 - NSS 3.27 tip d9f7136f2ae3, r=ttaubert
authorFranziskus Kiefer <franziskuskiefer@gmail.com>
Fri, 16 Sep 2016 10:00:57 -0700
changeset 314358 a5183ca6cbadc527492427a56c5660c9a80c16fd
parent 314357 672663f1a298ef69d17742a8da54c44f3b788aca
child 314359 cc2cbca41195523582e22a3677bce09820c05d3c
push id81865
push userfranziskuskiefer@gmail.com
push dateMon, 19 Sep 2016 06:25:22 +0000
treeherdermozilla-inbound@a5183ca6cbad [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersttaubert
bugs1296266
milestone51.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1296266 - NSS 3.27 tip d9f7136f2ae3, r=ttaubert
.eslintignore
security/nss/.taskcluster.yml
security/nss/TAG-INFO
security/nss/automation/taskcluster/decision_task.yml
security/nss/automation/taskcluster/docker/setup.sh
security/nss/automation/taskcluster/graph/arm/_build_base.yml
security/nss/automation/taskcluster/graph/arm/_test_base.yml
security/nss/automation/taskcluster/graph/arm/build32-debug.yml
security/nss/automation/taskcluster/graph/build.js
security/nss/automation/taskcluster/graph/image_builder.js
security/nss/automation/taskcluster/graph/image_builder.yml
security/nss/automation/taskcluster/graph/linux/_build_base.yml
security/nss/automation/taskcluster/graph/linux/_test_base.yml
security/nss/automation/taskcluster/graph/linux/build32-debug.yml
security/nss/automation/taskcluster/graph/linux/build32-opt.yml
security/nss/automation/taskcluster/graph/linux/build64-asan.yml
security/nss/automation/taskcluster/graph/linux/build64-debug.yml
security/nss/automation/taskcluster/graph/linux/build64-opt.yml
security/nss/automation/taskcluster/graph/package.json
security/nss/automation/taskcluster/graph/src/context_hash.js
security/nss/automation/taskcluster/graph/src/extend.js
security/nss/automation/taskcluster/graph/src/image_builder.js
security/nss/automation/taskcluster/graph/src/index.js
security/nss/automation/taskcluster/graph/src/merge.js
security/nss/automation/taskcluster/graph/src/queue.js
security/nss/automation/taskcluster/graph/src/try_syntax.js
security/nss/automation/taskcluster/graph/tests/chains.yml
security/nss/automation/taskcluster/graph/tests/cipher.yml
security/nss/automation/taskcluster/graph/tests/crmf.yml
security/nss/automation/taskcluster/graph/tests/db.yml
security/nss/automation/taskcluster/graph/tests/ec.yml
security/nss/automation/taskcluster/graph/tests/fips.yml
security/nss/automation/taskcluster/graph/tests/gtests.yml
security/nss/automation/taskcluster/graph/tests/lowhash.yml
security/nss/automation/taskcluster/graph/tests/merge.yml
security/nss/automation/taskcluster/graph/tests/sdr.yml
security/nss/automation/taskcluster/graph/tests/smime.yml
security/nss/automation/taskcluster/graph/tests/ssl.yml
security/nss/automation/taskcluster/graph/tests/tools.yml
security/nss/automation/taskcluster/graph/tools/_build_base.yml
security/nss/automation/taskcluster/graph/tools/clang-format.yml
security/nss/automation/taskcluster/graph/tools/scan-build.yml
security/nss/automation/taskcluster/graph/try_syntax.js
security/nss/automation/taskcluster/graph/windows/_build_base.yml
security/nss/automation/taskcluster/graph/windows/_test_base.yml
security/nss/automation/taskcluster/graph/windows/build64-debug.yml
security/nss/automation/taskcluster/graph/windows/build64-opt.yml
security/nss/automation/taskcluster/graph/yaml.js
security/nss/automation/taskcluster/scripts/build.sh
security/nss/automation/taskcluster/scripts/extend_task_graph.sh
security/nss/automation/taskcluster/scripts/gen_certs.sh
security/nss/automation/taskcluster/scripts/run_clang_format.sh
security/nss/automation/taskcluster/scripts/run_tests.sh
security/nss/automation/taskcluster/scripts/tools.sh
security/nss/automation/taskcluster/windows/build.sh
security/nss/automation/taskcluster/windows/gen_certs.sh
security/nss/automation/taskcluster/windows/releng.manifest
security/nss/automation/taskcluster/windows/setup.sh
security/nss/cmd/lib/basicutil.c
security/nss/cmd/modutil/install.c
security/nss/coreconf/coreconf.dep
security/nss/doc/certutil.xml
security/nss/doc/html/certutil.html
security/nss/doc/nroff/certutil.1
security/nss/external_tests/ssl_gtest/manifest.mn
security/nss/external_tests/ssl_gtest/ssl_0rtt_unittest.cc
security/nss/external_tests/ssl_gtest/ssl_auth_unittest.cc
security/nss/external_tests/ssl_gtest/ssl_ciphersuite_unittest.cc
security/nss/external_tests/ssl_gtest/ssl_damage_unittest.cc
security/nss/external_tests/ssl_gtest/ssl_dhe_unittest.cc
security/nss/external_tests/ssl_gtest/ssl_drop_unittest.cc
security/nss/external_tests/ssl_gtest/ssl_ecdh_unittest.cc
security/nss/external_tests/ssl_gtest/ssl_ems_unittest.cc
security/nss/external_tests/ssl_gtest/ssl_extension_unittest.cc
security/nss/external_tests/ssl_gtest/ssl_gtest.cc
security/nss/external_tests/ssl_gtest/ssl_hrr_unittest.cc
security/nss/external_tests/ssl_gtest/ssl_loopback_unittest.cc
security/nss/external_tests/ssl_gtest/ssl_record_unittest.cc
security/nss/external_tests/ssl_gtest/ssl_resumption_unittest.cc
security/nss/external_tests/ssl_gtest/ssl_staticrsa_unittest.cc
security/nss/external_tests/ssl_gtest/ssl_v2_client_hello_unittest.cc
security/nss/external_tests/ssl_gtest/ssl_version_unittest.cc
security/nss/external_tests/ssl_gtest/test_io.cc
security/nss/external_tests/ssl_gtest/test_io.h
security/nss/external_tests/ssl_gtest/tls_agent.cc
security/nss/external_tests/ssl_gtest/tls_agent.h
security/nss/external_tests/ssl_gtest/tls_connect.cc
security/nss/external_tests/ssl_gtest/tls_connect.h
security/nss/external_tests/ssl_gtest/tls_filter.cc
security/nss/external_tests/ssl_gtest/tls_filter.h
security/nss/external_tests/ssl_gtest/tls_parser.h
security/nss/lib/certhigh/ocsp.c
security/nss/lib/ssl/SSLerrs.h
security/nss/lib/ssl/derive.c
security/nss/lib/ssl/dtlscon.c
security/nss/lib/ssl/ssl.def
security/nss/lib/ssl/ssl.h
security/nss/lib/ssl/ssl3con.c
security/nss/lib/ssl/ssl3ecc.c
security/nss/lib/ssl/ssl3ext.c
security/nss/lib/ssl/sslcon.c
security/nss/lib/ssl/sslerr.h
security/nss/lib/ssl/sslimpl.h
security/nss/lib/ssl/sslinfo.c
security/nss/lib/ssl/sslsecur.c
security/nss/lib/ssl/sslsock.c
security/nss/lib/ssl/tls13con.c
security/nss/lib/ssl/tls13con.h
--- a/.eslintignore
+++ b/.eslintignore
@@ -246,8 +246,11 @@ toolkit/identity/FirefoxAccounts.jsm
 toolkit/modules/AppConstants.jsm
 toolkit/mozapps/downloads/nsHelperAppDlg.js
 toolkit/mozapps/extensions/internal/AddonConstants.jsm
 toolkit/mozapps/update/tests/data/xpcshellConstantsPP.js
 toolkit/webapps/**
 
 # Third party
 toolkit/modules/third_party/**
+
+#NSS
+security/nss/**
new file mode 100644
--- /dev/null
+++ b/security/nss/.taskcluster.yml
@@ -0,0 +1,89 @@
+---
+version: 0
+metadata:
+  name: "NSS Continuous Integration"
+  description: "The Taskcluster task graph for the NSS tree"
+  owner: "mozilla-taskcluster-maintenance@mozilla.com"
+  source: {{{source}}}
+
+scopes:
+  # Note the below scopes are insecure however these get overriden on the server
+  # side to whatever scopes are set by mozilla-taskcluster.
+  - queue:*
+  - docker-worker:*
+  - scheduler:*
+
+# Available mustache parameters (see the mozilla-taskcluster source):
+#
+# - owner:          push user (email address)
+# - source:         URL of this YAML file
+# - url:            repository URL
+# - project:        alias for the destination repository (basename of
+#                   the repo url)
+# - level:          SCM level of the destination repository
+#                   (1 = try, 3 = core)
+# - revision:       (short) hg revision of the head of the push
+# - revision_hash:  (long) hg revision of the head of the push
+# - comment:        comment of the push
+# - pushlog_id:     id in the pushlog table of the repository
+#
+# and functions:
+# - as_slugid:      convert a label into a slugId
+# - from_now:       generate a timestamp at a fixed offset from now
+
+tasks:
+  - taskId: '{{#as_slugid}}decision task{{/as_slugid}}'
+    reruns: 3
+    task:
+      created: '{{now}}'
+      deadline: '{{#from_now}}1 day{{/from_now}}'
+      expires: '{{#from_now}}14 days{{/from_now}}'
+
+      metadata:
+        owner: mozilla-taskcluster-maintenance@mozilla.com
+        source: {{{source}}}
+        name: "NSS Decision Task"
+        description: |
+            The task that creates all of the other tasks in the task graph
+
+      workerType: "hg-worker"
+      provisionerId: "aws-provisioner-v1"
+
+      tags:
+        createdForUser: {{owner}}
+
+      routes:
+        - "tc-treeherder-stage.v2.{{project}}.{{revision}}.{{pushlog_id}}"
+        - "tc-treeherder.v2.{{project}}.{{revision}}.{{pushlog_id}}"
+
+      payload:
+        image: ttaubert/nss-decision:0.0.2
+
+        env:
+          TC_OWNER: {{owner}}
+          TC_SOURCE: {{{source}}}
+          TC_PROJECT: {{project}}
+          TC_COMMENT: '{{comment}}'
+          NSS_PUSHLOG_ID: '{{pushlog_id}}'
+          NSS_HEAD_REPOSITORY: '{{{url}}}'
+          NSS_HEAD_REVISION: '{{revision}}'
+
+        maxRunTime: 1800
+
+        command:
+          - bash
+          - -cx
+          - >
+            bin/checkout.sh &&
+            nss/automation/taskcluster/scripts/extend_task_graph.sh
+
+        features:
+          taskclusterProxy: true
+
+      extra:
+        treeherder:
+          symbol: D
+          build:
+            platform: nss-decision
+          machine:
+            platform: nss-decision
--- a/security/nss/TAG-INFO
+++ b/security/nss/TAG-INFO
@@ -1,1 +1,1 @@
-NSS_3_27_BETA3
+NSS_3_27_BRANCH
deleted file mode 100644
--- a/security/nss/automation/taskcluster/decision_task.yml
+++ /dev/null
@@ -1,95 +0,0 @@
----
-version: 0
-metadata:
-  name: "NSS Continuous Integration"
-  description: "The Taskcluster task graph for the NSS tree"
-  owner: "mozilla-taskcluster-maintenance@mozilla.com"
-  source: {{{source}}}
-
-scopes:
-  # Note the below scopes are insecure however these get overriden on the server
-  # side to whatever scopes are set by mozilla-taskcluster.
-  - queue:*
-  - docker-worker:*
-  - scheduler:*
-
-# Available mustache parameters (see the mozilla-taskcluster source):
-#
-# - owner:          push user (email address)
-# - source:         URL of this YAML file
-# - url:            repository URL
-# - project:        alias for the destination repository (basename of
-#                   the repo url)
-# - level:          SCM level of the destination repository
-#                   (1 = try, 3 = core)
-# - revision:       (short) hg revision of the head of the push
-# - revision_hash:  (long) hg revision of the head of the push
-# - comment:        comment of the push
-# - pushlog_id:     id in the pushlog table of the repository
-#
-# and functions:
-# - as_slugid:      convert a label into a slugId
-# - from_now:       generate a timestamp at a fixed offset from now
-
-tasks:
-  - taskId: '{{#as_slugid}}decision task{{/as_slugid}}'
-    reruns: 3
-    task:
-      created: '{{now}}'
-      deadline: '{{#from_now}}1 day{{/from_now}}'
-      expires: '{{#from_now}}14 days{{/from_now}}'
-
-      metadata:
-        owner: mozilla-taskcluster-maintenance@mozilla.com
-        source: {{{source}}}
-        name: "NSS Decision Task"
-        description: |
-            The task that creates all of the other tasks in the task graph
-
-      workerType: "hg-worker"
-      provisionerId: "aws-provisioner-v1"
-
-      tags:
-        createdForUser: {{owner}}
-
-      routes:
-        - "tc-treeherder-stage.v2.{{project}}.{{revision}}.{{pushlog_id}}"
-        - "tc-treeherder.v2.{{project}}.{{revision}}.{{pushlog_id}}"
-
-      payload:
-        image: ttaubert/nss-decision:0.0.2
-
-        env:
-          TC_OWNER: {{owner}}
-          TC_SOURCE: {{{source}}}
-          TC_PROJECT: {{project}}
-          TC_COMMENT: '{{comment}}'
-          NSS_PUSHLOG_ID: '{{pushlog_id}}'
-          NSS_HEAD_REPOSITORY: '{{{url}}}'
-          NSS_HEAD_REVISION: '{{revision}}'
-
-        maxRunTime: 1800
-
-        command:
-          - bash
-          - -cx
-          - >
-            bin/checkout.sh &&
-            nss/automation/taskcluster/scripts/extend_task_graph.sh
-
-        artifacts:
-          public:
-            type: "directory"
-            path: "/home/worker/artifacts"
-            expires: "{{#from_now}}7 days{{/from_now}}"
-
-        graphs:
-          - /home/worker/artifacts/graph.json
-
-      extra:
-        treeherder:
-          symbol: D
-          build:
-            platform: nss-decision
-          machine:
-            platform: nss-decision
--- a/security/nss/automation/taskcluster/docker/setup.sh
+++ b/security/nss/automation/taskcluster/docker/setup.sh
@@ -39,18 +39,18 @@ echo "deb http://ppa.launchpad.net/ubunt
 
 # Install packages.
 apt-get -y update
 apt-get install -y --no-install-recommends ${apt_packages[@]}
 
 # 32-bit builds
 ln -s /usr/include/x86_64-linux-gnu/zconf.h /usr/include
 
-# Install clang-3.8 into /usr/local/.
-curl http://llvm.org/releases/3.8.0/clang+llvm-3.8.0-x86_64-linux-gnu-ubuntu-16.04.tar.xz | tar xJv -C /usr/local --strip-components=1
+# Install clang-3.9 into /usr/local/.
+curl http://llvm.org/releases/3.9.0/clang+llvm-3.9.0-x86_64-linux-gnu-ubuntu-16.04.tar.xz | tar xJv -C /usr/local --strip-components=1
 
 # Compiler options.
 update-alternatives --install /usr/bin/gcc gcc /usr/local/bin/clang 5
 update-alternatives --install /usr/bin/g++ g++ /usr/local/bin/clang++ 5
 update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-4.8 10
 update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-4.8 10
 update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-6 20
 update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-6 20
deleted file mode 100644
--- a/security/nss/automation/taskcluster/graph/arm/_build_base.yml
+++ /dev/null
@@ -1,40 +0,0 @@
----
-reruns: 2
-
-task:
-  created: !from_now 0
-  deadline: !from_now 24
-  provisionerId: localprovisioner
-  workerType: nss-rpi
-  schedulerId: task-graph-scheduler
-
-  metadata:
-    owner: !env TC_OWNER
-    source: !env TC_SOURCE
-
-  payload:
-    maxRunTime: 7200
-    image: ttaubert/nss-rpi-ci:0.0.3
-
-    artifacts:
-      public:
-        type: directory
-        path: /home/worker/artifacts
-        expires: !from_now 24
-
-    command:
-      - "/bin/bash"
-      - "-c"
-      - "bin/checkout.sh && nss/automation/taskcluster/scripts/build.sh"
-
-    env:
-      NSS_HEAD_REPOSITORY: !env NSS_HEAD_REPOSITORY
-      NSS_HEAD_REVISION: !env NSS_HEAD_REVISION
-      GCC_VERSION: gcc-5
-      GXX_VERSION: g++-5
-
-  extra:
-    treeherder:
-      tier: 3 # hide jobs by default
-      jobKind: build
-      symbol: B
deleted file mode 100644
--- a/security/nss/automation/taskcluster/graph/arm/_test_base.yml
+++ /dev/null
@@ -1,27 +0,0 @@
----
-reruns: 2
-
-task:
-  created: !from_now 0
-  deadline: !from_now 24
-  provisionerId: localprovisioner
-  workerType: nss-rpi
-  schedulerId: task-graph-scheduler
-
-  metadata:
-    owner: !env TC_OWNER
-    source: !env TC_SOURCE
-
-  payload:
-    maxRunTime: 7200
-    image: ttaubert/nss-rpi-ci:0.0.3
-
-    command:
-      - "/bin/bash"
-      - "-c"
-      - "bin/checkout.sh && nss/automation/taskcluster/scripts/run_tests.sh"
-
-  extra:
-    treeherder:
-      tier: 3 # hide jobs by default
-      jobKind: test
deleted file mode 100644
--- a/security/nss/automation/taskcluster/graph/arm/build32-debug.yml
+++ /dev/null
@@ -1,29 +0,0 @@
----
-- task:
-    metadata:
-      name: "Linux 32 (ARM, debug)"
-      description: "Linux 32 (ARM, debug)"
-
-    extra:
-      treeherder:
-        build:
-          platform: linux32
-        machine:
-          platform: linux32
-        collection:
-          arm-debug: true
-
-  tests:
-    - chains
-    - cipher
-    - crmf
-    - db
-    - ec
-    - fips
-    - gtests
-    - lowhash
-    - merge
-    - sdr
-    - smime
-    - ssl
-    - tools
deleted file mode 100644
--- a/security/nss/automation/taskcluster/graph/build.js
+++ /dev/null
@@ -1,147 +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/. */
-
-var fs = require("fs");
-var path = require("path");
-var merge = require("merge");
-var slugid = require("slugid");
-var flatmap = require("flatmap");
-
-var yaml = require("./yaml");
-var try_syntax = require("./try_syntax");
-var image_builder = require("./image_builder");
-
-// Default values for debugging.
-var TC_OWNER = process.env.TC_OWNER || "{{tc_owner}}";
-var TC_SOURCE = process.env.TC_SOURCE || "{{tc_source}}";
-var TC_PROJECT = process.env.TC_PROJECT || "{{tc_project}}";
-var TC_COMMENT = process.env.TC_COMMENT || "{{tc_comment}}";
-var NSS_PUSHLOG_ID = process.env.NSS_PUSHLOG_ID || "{{nss_pushlog_id}}";
-var NSS_HEAD_REVISION = process.env.NSS_HEAD_REVISION || "{{nss_head_rev}}";
-
-// Add base information to the given task.
-function decorateTask(task) {
-  // Assign random task id.
-  task.taskId = slugid.v4();
-
-  // TreeHerder routes.
-  task.task.routes = [
-    "tc-treeherder-stage.v2." + TC_PROJECT + "." + NSS_HEAD_REVISION + "." + NSS_PUSHLOG_ID,
-    "tc-treeherder.v2." + TC_PROJECT + "." + NSS_HEAD_REVISION + "." + NSS_PUSHLOG_ID
-  ];
-}
-
-// Generate all tasks for a given build.
-function generateBuildTasks(platform, file) {
-  var dir = path.join(__dirname, "./" + platform);
-
-  // Parse base definitions.
-  var buildBase = yaml.parse(path.join(dir, "_build_base.yml"), {});
-  var testBase = yaml.parse(path.join(dir, "_test_base.yml"), {});
-
-  return flatmap(yaml.parse(path.join(dir, file)), function (task) {
-    // Merge base build task definition with the current one.
-    var tasks = [task = merge.recursive(true, buildBase, task)];
-
-    // Add base info.
-    decorateTask(task);
-
-    // Generate test tasks.
-    if (task.tests) {
-      // The base definition for all tests of this platform.
-      var base = merge.recursive(true, {
-        requires: [task.taskId],
-
-        task: {
-          payload: {
-            env: {
-              TC_PARENT_TASK_ID: task.taskId
-            }
-          }
-        }
-      }, testBase);
-
-      // Generate and append test task definitions.
-      tasks = tasks.concat(flatmap(task.tests, function (name) {
-        return generateTestTasks(name, base, task);
-      }));
-
-      // |tests| is not part of the schema.
-      delete task.tests;
-    }
-
-    return tasks;
-  });
-}
-
-// Generate all tasks for a given test.
-function generateTestTasks(name, base, task) {
-  // Load test definitions.
-  var dir = path.join(__dirname, "./tests");
-  var tests = yaml.parse(path.join(dir, name + ".yml"));
-
-  return tests.map(function (test) {
-    // Merge test with base definition.
-    test = merge.recursive(true, base, test);
-
-    // Add base info.
-    decorateTask(test);
-
-    // We only want to carry over environment variables...
-    test.task.payload.env =
-      merge.recursive(true, task.task.payload.env,
-                            test.task.payload.env);
-
-    // ...and TreeHerder configuration data.
-    test.task.extra.treeherder =
-      merge.recursive(true, task.task.extra.treeherder,
-                            test.task.extra.treeherder);
-
-    return test;
-  });
-}
-
-// Generate all tasks for a given platform.
-function generatePlatformTasks(platform) {
-  var dir = path.join(__dirname, "./" + platform);
-  var buildBase = yaml.parse(path.join(dir, "_build_base.yml"), {});
-  var testBase = yaml.parse(path.join(dir, "_test_base.yml"), {});
-
-  // Parse all build tasks.
-  return flatmap(fs.readdirSync(dir), function (file) {
-    if (!file.startsWith("_") && file.endsWith(".yml")) {
-      var tasks = generateBuildTasks(platform, file);
-
-      // Convert env variables to strings.
-      tasks.forEach(function (task) {
-        var env = task.task.payload.env || {};
-        Object.keys(env).forEach(function (name) {
-          if (typeof(env[name]) != "undefined") {
-            env[name] = env[name] + "";
-          }
-        });
-      });
-
-      return tasks;
-    }
-  });
-}
-
-// Construct the task graph.
-var graph = {
-  tasks: flatmap(["linux", "windows", "arm", "tools"], generatePlatformTasks)
-};
-
-// Filter tasks when try syntax is given.
-if (TC_PROJECT == "nss-try") {
-  graph.tasks = try_syntax.filterTasks(graph.tasks, TC_COMMENT);
-}
-
-// Inject the image builder tasks and dependencies.
-image_builder.asyncTweakTasks(graph.tasks).then(function (tasks) {
-  graph.tasks = tasks;
-
-  // Output the final graph.
-  process.stdout.write(JSON.stringify(graph, null, 2));
-});
deleted file mode 100644
--- a/security/nss/automation/taskcluster/graph/image_builder.js
+++ /dev/null
@@ -1,148 +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/. */
-
-var fs = require("fs");
-var path = require("path");
-var crypto = require("crypto");
-var slugid = require("slugid");
-var flatmap = require("flatmap");
-var taskcluster = require("taskcluster-client");
-
-var yaml = require("./yaml");
-
-// Default values for debugging.
-var TC_PROJECT = process.env.TC_PROJECT || "{{tc_project}}";
-var NSS_PUSHLOG_ID = process.env.NSS_PUSHLOG_ID || "{{nss_pushlog_id}}";
-var NSS_HEAD_REVISION = process.env.NSS_HEAD_REVISION || "{{nss_head_rev}}";
-
-// Add base information to the given task.
-function decorateTask(task) {
-  // Assign random task id.
-  task.taskId = slugid.v4();
-
-  // TreeHerder routes.
-  task.task.routes = [
-    "tc-treeherder-stage.v2." + TC_PROJECT + "." + NSS_HEAD_REVISION + "." + NSS_PUSHLOG_ID,
-    "tc-treeherder.v2." + TC_PROJECT + "." + NSS_HEAD_REVISION + "." + NSS_PUSHLOG_ID
-  ];
-}
-
-// Compute the SHA-256 digest.
-function sha256(data) {
-  var hash = crypto.createHash("sha256");
-  hash.update(data);
-  return hash.digest("hex");
-}
-
-// Recursively collect a list of all files of a given directory.
-function collectFilesInDirectory(dir) {
-  return flatmap(fs.readdirSync(dir), function (entry) {
-    var entry_path = path.join(dir, entry);
-
-    if (fs.lstatSync(entry_path).isDirectory()) {
-      return collectFilesInDirectory(entry_path);
-    }
-
-    return [entry_path];
-  });
-}
-
-// Compute a context hash for the given context path.
-function computeContextHash(context_path) {
-  var root = path.join(__dirname, "../../..");
-  var dir = path.join(root, context_path);
-  var files = collectFilesInDirectory(dir).sort();
-  var hashes = files.map(function (file) {
-    return sha256(file + "|" + fs.readFileSync(file, "utf-8"));
-  });
-
-  return sha256(hashes.join(","));
-}
-
-// Generates the image-builder task description.
-function generateImageBuilderTask(context_path) {
-  var task = yaml.parse(path.join(__dirname, "image_builder.yml"), {});
-
-  // Add base info.
-  decorateTask(task);
-
-  // Add info for docker image building.
-  task.task.payload.env.CONTEXT_PATH = context_path;
-  task.task.payload.env.HASH = computeContextHash(context_path);
-
-  return task;
-}
-
-// Returns a Promise<bool> that tells whether the task with the given id
-// has a public/image.tar artifact with a ready-to-use docker image.
-function asyncTaskHasImageArtifact(taskId) {
-  var queue = new taskcluster.Queue();
-
-  return queue.listLatestArtifacts(taskId).then(function (result) {
-    return result.artifacts.some(function (artifact) {
-      return artifact.name == "public/image.tar";
-    });
-  }, function () {
-    return false;
-  });
-}
-
-// Returns a Promise<task-id|null> with either a task id or null, depending
-// on whether we could find a task in the given namespace with a docker image.
-function asyncFindTaskWithImageArtifact(ns) {
-  var index = new taskcluster.Index();
-
-  return index.findTask(ns).then(function (result) {
-    return asyncTaskHasImageArtifact(result.taskId).then(function (has_image) {
-      return has_image ? result.taskId : null;
-    });
-  }, function () {
-    return null;
-  });
-}
-
-// Tweak the given list of tasks by injecting the image-builder task
-// and setting the right dependencies where needed.
-function asyncTweakTasks(tasks) {
-  var id = "linux";
-  var cx_path = "automation/taskcluster/docker";
-  var hash = computeContextHash(cx_path);
-  var ns = "docker.images.v1." + TC_PROJECT + "." + id + ".hash." + hash;
-  var additional_tasks = [];
-
-  // Check whether the docker image was already built.
-  return asyncFindTaskWithImageArtifact(ns).then(function (taskId) {
-    var builder_task;
-
-    if (!taskId) {
-      // No docker image found, add a task to build one.
-      builder_task = generateImageBuilderTask(cx_path);
-      taskId = builder_task.taskId;
-
-      // Add a route so we can find the task later again.
-      builder_task.task.routes.push("index." + ns);
-      additional_tasks.push(builder_task);
-    }
-
-    tasks.forEach(function (task) {
-      if (task.task.payload.image == cx_path) {
-        task.task.payload.image = {
-          path: "public/image.tar",
-          type: "task-image",
-          taskId: taskId
-        };
-
-        // Add a dependency only for top-level tasks (builds & tools) and only
-        // if we added an image building task. Otherwise we don't need to wait.
-        if (builder_task && !task.requires) {
-          task.requires = [taskId];
-        }
-      }
-    });
-
-    return additional_tasks.concat(tasks);
-  });
-}
-
-module.exports.asyncTweakTasks = asyncTweakTasks;
deleted file mode 100644
--- a/security/nss/automation/taskcluster/graph/image_builder.yml
+++ /dev/null
@@ -1,49 +0,0 @@
----
-reruns: 2
-
-task:
-  created: !from_now 0
-  deadline: !from_now 24
-  provisionerId: aws-provisioner-v1
-  workerType: hg-worker
-  schedulerId: task-graph-scheduler
-
-  metadata:
-    name: Image Builder
-    description: Image Builder
-    owner: !env TC_OWNER
-    source: !env TC_SOURCE
-
-  payload:
-    maxRunTime: 3600
-    image: taskcluster/image_builder:0.1.5
-
-    artifacts:
-      public/image.tar:
-        type: file
-        path: /artifacts/image.tar
-        expires: !from_now 8760
-
-    command:
-      - "/bin/bash"
-      - "-c"
-      - "/home/worker/bin/build_image.sh"
-
-    env:
-      HEAD_REPOSITORY: !env NSS_HEAD_REPOSITORY
-      BASE_REPOSITORY: !env NSS_HEAD_REPOSITORY
-      HEAD_REV: !env NSS_HEAD_REVISION
-      HEAD_REF: !env NSS_HEAD_REVISION
-      PROJECT: !env TC_PROJECT
-
-    features:
-      dind: true
-
-  extra:
-    treeherder:
-      build:
-        platform: nss-decision
-      machine:
-        platform: nss-decision
-      jobKind: build
-      symbol: I
deleted file mode 100644
--- a/security/nss/automation/taskcluster/graph/linux/_build_base.yml
+++ /dev/null
@@ -1,39 +0,0 @@
----
-reruns: 2
-
-task:
-  created: !from_now 0
-  deadline: !from_now 24
-  provisionerId: aws-provisioner-v1
-  workerType: hg-worker
-  schedulerId: task-graph-scheduler
-
-  metadata:
-    owner: !env TC_OWNER
-    source: !env TC_SOURCE
-
-  payload:
-    maxRunTime: 3600
-    image: automation/taskcluster/docker
-
-    artifacts:
-      public:
-        type: directory
-        path: /home/worker/artifacts
-        expires: !from_now 24
-
-    command:
-      - "/bin/bash"
-      - "-c"
-      - "bin/checkout.sh && nss/automation/taskcluster/scripts/build.sh"
-
-    env:
-      NSS_HEAD_REPOSITORY: !env NSS_HEAD_REPOSITORY
-      NSS_HEAD_REVISION: !env NSS_HEAD_REVISION
-      GCC_VERSION: gcc-5
-      GXX_VERSION: g++-5
-
-  extra:
-    treeherder:
-      jobKind: build
-      symbol: B
deleted file mode 100644
--- a/security/nss/automation/taskcluster/graph/linux/_test_base.yml
+++ /dev/null
@@ -1,26 +0,0 @@
----
-reruns: 2
-
-task:
-  created: !from_now 0
-  deadline: !from_now 24
-  provisionerId: aws-provisioner-v1
-  workerType: hg-worker
-  schedulerId: task-graph-scheduler
-
-  metadata:
-    owner: !env TC_OWNER
-    source: !env TC_SOURCE
-
-  payload:
-    maxRunTime: 3600
-    image: automation/taskcluster/docker
-
-    command:
-      - "/bin/bash"
-      - "-c"
-      - "bin/checkout.sh && nss/automation/taskcluster/scripts/run_tests.sh"
-
-  extra:
-    treeherder:
-      jobKind: test
deleted file mode 100644
--- a/security/nss/automation/taskcluster/graph/linux/build32-debug.yml
+++ /dev/null
@@ -1,116 +0,0 @@
----
-- task:
-    metadata:
-      name: "Linux 32 (debug)"
-      description: "Linux 32 (debug)"
-
-    extra:
-      treeherder:
-        build:
-          platform: linux32
-        machine:
-          platform: linux32
-        collection:
-          debug: true
-
-  tests:
-    - chains
-    - cipher
-    - crmf
-    - db
-    - ec
-    - fips
-    - gtests
-    - lowhash
-    - merge
-    - sdr
-    - smime
-    - ssl
-    - tools
-
-- task:
-    metadata:
-      name: "Linux 32 (debug, clang-3.8)"
-      description: "Linux 32 (debug, clang-3.8)"
-
-    payload:
-      env:
-        GCC_VERSION: clang
-        GXX_VERSION: clang++
-
-    extra:
-      treeherder:
-        build:
-          platform: linux32
-        machine:
-          platform: linux32
-        collection:
-          debug: true
-        groupSymbol: Builds
-        groupName: Various builds
-        symbol: clang-3.8
-
-- task:
-    metadata:
-      name: "Linux 32 (debug, gcc-4.8)"
-      description: "Linux 32 (debug, gcc-4.8)"
-
-    payload:
-      env:
-        GCC_VERSION: gcc-4.8
-        GXX_VERSION: g++-4.8
-
-    extra:
-      treeherder:
-        build:
-          platform: linux32
-        machine:
-          platform: linux32
-        collection:
-          debug: true
-        groupSymbol: Builds
-        groupName: Various builds
-        symbol: gcc-4.8
-
-- task:
-    metadata:
-      name: "Linux 32 (debug, gcc-6.1)"
-      description: "Linux 32 (debug, gcc-6.1)"
-
-    payload:
-      env:
-        GCC_VERSION: gcc-6
-        GXX_VERSION: g++-6
-
-    extra:
-      treeherder:
-        build:
-          platform: linux32
-        machine:
-          platform: linux32
-        collection:
-          debug: true
-        groupSymbol: Builds
-        groupName: Various builds
-        symbol: gcc-6.1
-
-- task:
-    metadata:
-      name: "Linux 32 (debug, NSS_NO_PKCS11_BYPASS=1)"
-      description: "Linux 32 (debug, NSS_NO_PKCS11_BYPASS=1)"
-
-    payload:
-      env:
-        NSS_NO_PKCS11_BYPASS: 1
-
-    extra:
-      treeherder:
-        build:
-          platform: linux32
-        machine:
-          platform: linux32
-        collection:
-          debug: true
-        groupSymbol: Builds
-        groupName: Various builds
-        symbol: noPkcs11Bypass
deleted file mode 100644
--- a/security/nss/automation/taskcluster/graph/linux/build32-opt.yml
+++ /dev/null
@@ -1,124 +0,0 @@
----
-- task:
-    metadata:
-      name: "Linux 32 (opt)"
-      description: "Linux 32 (opt)"
-
-    payload:
-      env:
-        BUILD_OPT: 1
-
-    extra:
-      treeherder:
-        build:
-          platform: linux32
-        machine:
-          platform: linux32
-        collection:
-          opt: true
-
-  tests:
-    - chains
-    - cipher
-    - crmf
-    - db
-    - ec
-    - fips
-    - gtests
-    - lowhash
-    - merge
-    - sdr
-    - smime
-    - ssl
-    - tools
-
-- task:
-    metadata:
-      name: "Linux 32 (opt, clang-3.8)"
-      description: "Linux 32 (opt, clang-3.8)"
-
-    payload:
-      env:
-        GCC_VERSION: clang
-        GXX_VERSION: clang++
-        BUILD_OPT: 1
-
-    extra:
-      treeherder:
-        build:
-          platform: linux32
-        machine:
-          platform: linux32
-        collection:
-          opt: true
-        groupSymbol: Builds
-        groupName: Various builds
-        symbol: clang-3.8
-
-- task:
-    metadata:
-      name: "Linux 32 (opt, gcc-4.8)"
-      description: "Linux 32 (opt, gcc-4.8)"
-
-    payload:
-      env:
-        GCC_VERSION: gcc-4.8
-        GXX_VERSION: g++-4.8
-        BUILD_OPT: 1
-
-    extra:
-      treeherder:
-        build:
-          platform: linux32
-        machine:
-          platform: linux32
-        collection:
-          opt: true
-        groupSymbol: Builds
-        groupName: Various builds
-        symbol: gcc-4.8
-
-- task:
-    metadata:
-      name: "Linux 32 (opt, gcc-6.1)"
-      description: "Linux 32 (opt, gcc-6.1)"
-
-    payload:
-      env:
-        GCC_VERSION: gcc-6
-        GXX_VERSION: g++-6
-        BUILD_OPT: 1
-
-    extra:
-      treeherder:
-        build:
-          platform: linux32
-        machine:
-          platform: linux32
-        collection:
-          opt: true
-        groupSymbol: Builds
-        groupName: Various builds
-        symbol: gcc-6.1
-
-- task:
-    metadata:
-      name: "Linux 32 (opt, NSS_NO_PKCS11_BYPASS=1)"
-      description: "Linux 32 (opt, NSS_NO_PKCS11_BYPASS=1)"
-
-    payload:
-      env:
-        NSS_NO_PKCS11_BYPASS: 1
-        BUILD_OPT: 1
-
-    extra:
-      treeherder:
-        build:
-          platform: linux32
-        machine:
-          platform: linux32
-        collection:
-          opt: true
-        groupSymbol: Builds
-        groupName: Various builds
-        symbol: noPkcs11Bypass
deleted file mode 100644
--- a/security/nss/automation/taskcluster/graph/linux/build64-asan.yml
+++ /dev/null
@@ -1,38 +0,0 @@
----
-- task:
-    metadata:
-      name: "Linux 64 (ASan, debug)"
-      description: "Linux 64 (ASan, debug)"
-
-    payload:
-      env:
-        GCC_VERSION: clang
-        GXX_VERSION: clang++
-        NSS_DISABLE_ARENA_FREE_LIST: 1
-        NSS_DISABLE_UNLOAD: 1
-        USE_ASAN: 1
-        USE_64: 1
-
-    extra:
-      treeherder:
-        build:
-          platform: linux64
-        machine:
-          platform: linux64
-        collection:
-          asan: true
-
-  tests:
-    - chains
-    - cipher
-    - crmf
-    - db
-    - ec
-    - fips
-    - gtests
-    - lowhash
-    - merge
-    - sdr
-    - smime
-    - ssl
-    - tools
deleted file mode 100644
--- a/security/nss/automation/taskcluster/graph/linux/build64-debug.yml
+++ /dev/null
@@ -1,146 +0,0 @@
----
-- task:
-    metadata:
-      name: "Linux 64 (debug)"
-      description: "Linux 64 (debug)"
-
-    payload:
-      env:
-        USE_64: 1
-
-    extra:
-      treeherder:
-        build:
-          platform: linux64
-        machine:
-          platform: linux64
-        collection:
-          debug: true
-
-  tests:
-    - chains
-    - cipher
-    - crmf
-    - db
-    - ec
-    - fips
-    - gtests
-    - lowhash
-    - merge
-    - sdr
-    - smime
-    - ssl
-    - tools
-
-- task:
-    metadata:
-      name: "Linux 64 (debug, clang-3.8)"
-      description: "Linux 64 (debug, clang-3.8)"
-
-    payload:
-      env:
-        GCC_VERSION: clang
-        GXX_VERSION: clang++
-        USE_64: 1
-
-    extra:
-      treeherder:
-        build:
-          platform: linux64
-        machine:
-          platform: linux64
-        collection:
-          debug: true
-        groupSymbol: Builds
-        groupName: Various builds
-        symbol: clang-3.8
-
-- task:
-    metadata:
-      name: "Linux 64 (debug, gcc-4.8)"
-      description: "Linux 64 (debug, gcc-4.8)"
-
-    payload:
-      env:
-        GCC_VERSION: gcc-4.8
-        GXX_VERSION: g++-4.8
-        USE_64: 1
-
-    extra:
-      treeherder:
-        build:
-          platform: linux64
-        machine:
-          platform: linux64
-        collection:
-          debug: true
-        groupSymbol: Builds
-        groupName: Various builds
-        symbol: gcc-4.8
-
-- task:
-    metadata:
-      name: "Linux 64 (debug, gcc-6.1)"
-      description: "Linux 64 (debug, gcc-6.1)"
-
-    payload:
-      env:
-        GCC_VERSION: gcc-6
-        GXX_VERSION: g++-6
-        USE_64: 1
-
-    extra:
-      treeherder:
-        build:
-          platform: linux64
-        machine:
-          platform: linux64
-        collection:
-          debug: true
-        groupSymbol: Builds
-        groupName: Various builds
-        symbol: gcc-6.1
-
-- task:
-    metadata:
-      name: "Linux 64 (debug, NSS_NO_PKCS11_BYPASS=1)"
-      description: "Linux 64 (debug, NSS_NO_PKCS11_BYPASS=1)"
-
-    payload:
-      env:
-        NSS_NO_PKCS11_BYPASS: 1
-        USE_64: 1
-
-    extra:
-      treeherder:
-        build:
-          platform: linux64
-        machine:
-          platform: linux64
-        collection:
-          debug: true
-        groupSymbol: Builds
-        groupName: Various builds
-        symbol: noPkcs11Bypass
-
-- task:
-    metadata:
-      name: "Linux 64 (debug, NSS_DISABLE_LIBPKIX=1)"
-      description: "Linux 64 (debug, NSS_DISABLE_LIBPKIX=1)"
-
-    payload:
-      env:
-        NSS_DISABLE_LIBPKIX: 1
-        USE_64: 1
-
-    extra:
-      treeherder:
-        build:
-          platform: linux64
-        machine:
-          platform: linux64
-        collection:
-          debug: true
-        groupSymbol: Builds
-        groupName: Various builds
-        symbol: noLibpkix
deleted file mode 100644
--- a/security/nss/automation/taskcluster/graph/linux/build64-opt.yml
+++ /dev/null
@@ -1,129 +0,0 @@
----
-- task:
-    metadata:
-      name: "Linux 64 (opt)"
-      description: "Linux 64 (opt)"
-
-    payload:
-      env:
-        BUILD_OPT: 1
-        USE_64: 1
-
-    extra:
-      treeherder:
-        build:
-          platform: linux64
-        machine:
-          platform: linux64
-        collection:
-          opt: true
-
-  tests:
-    - chains
-    - cipher
-    - crmf
-    - db
-    - ec
-    - fips
-    - gtests
-    - lowhash
-    - merge
-    - sdr
-    - smime
-    - ssl
-    - tools
-
-- task:
-    metadata:
-      name: "Linux 64 (opt, clang-3.8)"
-      description: "Linux 64 (opt, clang-3.8)"
-
-    payload:
-      env:
-        GCC_VERSION: clang
-        GXX_VERSION: clang++
-        BUILD_OPT: 1
-        USE_64: 1
-
-    extra:
-      treeherder:
-        build:
-          platform: linux64
-        machine:
-          platform: linux64
-        collection:
-          opt: true
-        groupSymbol: Builds
-        groupName: Various builds
-        symbol: clang-3.8
-
-- task:
-    metadata:
-      name: "Linux 64 (opt, gcc-4.8)"
-      description: "Linux 64 (opt, gcc-4.8)"
-
-    payload:
-      env:
-        GCC_VERSION: gcc-4.8
-        GXX_VERSION: g++-4.8
-        BUILD_OPT: 1
-        USE_64: 1
-
-    extra:
-      treeherder:
-        build:
-          platform: linux64
-        machine:
-          platform: linux64
-        collection:
-          opt: true
-        groupSymbol: Builds
-        groupName: Various builds
-        symbol: gcc-4.8
-
-- task:
-    metadata:
-      name: "Linux 64 (opt, gcc-6.1)"
-      description: "Linux 64 (opt, gcc-6.1)"
-
-    payload:
-      env:
-        GCC_VERSION: gcc-6
-        GXX_VERSION: g++-6
-        BUILD_OPT: 1
-        USE_64: 1
-
-    extra:
-      treeherder:
-        build:
-          platform: linux64
-        machine:
-          platform: linux64
-        collection:
-          opt: true
-        groupSymbol: Builds
-        groupName: Various builds
-        symbol: gcc-6.1
-
-- task:
-    metadata:
-      name: "Linux 64 (opt, NSS_NO_PKCS11_BYPASS=1)"
-      description: "Linux 64 (opt, NSS_NO_PKCS11_BYPASS=1)"
-
-    payload:
-      env:
-        NSS_NO_PKCS11_BYPASS: 1
-        BUILD_OPT: 1
-        USE_64: 1
-
-    extra:
-      treeherder:
-        build:
-          platform: linux64
-        machine:
-          platform: linux64
-        collection:
-          opt: true
-        groupSymbol: Builds
-        groupName: Various builds
-        symbol: noPkcs11Bypass
--- a/security/nss/automation/taskcluster/graph/package.json
+++ b/security/nss/automation/taskcluster/graph/package.json
@@ -1,15 +1,23 @@
 {
   "name": "decision-task",
   "version": "0.0.1",
   "private": true,
   "author": "Tim Taubert <ttaubert@mozilla.com>",
   "description": "Decision Task for NSS",
+  "scripts": {
+    "compile": "babel-compile -p taskcluster src:lib",
+    "install": "npm run compile"
+  },
   "dependencies": {
+    "babel-cli": "^6.14.0",
+    "babel-compile": "^2.0.0",
+    "babel-preset-taskcluster": "^3.0.0",
+    "babel-runtime": "^6.11.6",
     "flatmap": "0.0.3",
     "intersect": "^1.0.1",
     "js-yaml": "^3.6.1",
     "merge": "^1.2.0",
     "minimist": "^1.2.0",
     "slugid": "^1.1.0",
     "taskcluster-client": "^1.2.1"
   }
new file mode 100644
--- /dev/null
+++ b/security/nss/automation/taskcluster/graph/src/context_hash.js
@@ -0,0 +1,40 @@
+/* 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/. */
+
+import fs from "fs";
+import path from "path";
+import crypto from "crypto";
+import flatmap from "flatmap";
+
+// Compute the SHA-256 digest.
+function sha256(data) {
+  let hash = crypto.createHash("sha256");
+  hash.update(data);
+  return hash.digest("hex");
+}
+
+// Recursively collect a list of all files of a given directory.
+function collectFilesInDirectory(dir) {
+  return flatmap(fs.readdirSync(dir), entry => {
+    let entry_path = path.join(dir, entry);
+
+    if (fs.lstatSync(entry_path).isDirectory()) {
+      return collectFilesInDirectory(entry_path);
+    }
+
+    return [entry_path];
+  });
+}
+
+// Compute a context hash for the given context path.
+export default function (context_path) {
+  let root = path.join(__dirname, "../../../..");
+  let dir = path.join(root, context_path);
+  let files = collectFilesInDirectory(dir).sort();
+  let hashes = files.map(file => {
+    return sha256(file + "|" + fs.readFileSync(file, "utf-8"));
+  });
+
+  return sha256(hashes.join(","));
+}
new file mode 100644
--- /dev/null
+++ b/security/nss/automation/taskcluster/graph/src/extend.js
@@ -0,0 +1,381 @@
+/* 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/. */
+
+import merge from "./merge";
+import * as queue from "./queue";
+
+const LINUX_IMAGE = {name: "linux", path: "automation/taskcluster/docker"};
+
+const WINDOWS_CHECKOUT_CMD =
+  "bash -c \"hg clone -r $NSS_HEAD_REVISION $NSS_HEAD_REPOSITORY nss || " +
+    "(sleep 2; hg clone -r $NSS_HEAD_REVISION $NSS_HEAD_REPOSITORY nss) || " +
+    "(sleep 5; hg clone -r $NSS_HEAD_REVISION $NSS_HEAD_REPOSITORY nss)\"";
+
+/*****************************************************************************/
+
+queue.filter(task => {
+  if (task.group == "Builds") {
+    // Remove extra builds on ASan and ARM.
+    if (task.collection == "asan" || task.collection == "arm-debug") {
+      return false;
+    }
+
+    // Remove extra builds w/o libpkix for non-linux64-debug.
+    if (task.symbol == "noLibpkix" &&
+        (task.platform != "linux64" || task.collection != "debug")) {
+      return false;
+    }
+  }
+
+  return true;
+});
+
+queue.map(task => {
+  if (task.collection == "asan") {
+    // CRMF and FIPS tests still leak, unfortunately.
+    if (task.tests == "crmf" || task.tests == "fips") {
+      task.env.ASAN_OPTIONS = "detect_leaks=0";
+    }
+
+    // SSL(standard) runs on ASan take some time.
+    if (task.tests == "ssl" && task.cycle == "standard") {
+      task.maxRunTime = 7200;
+    }
+  }
+
+  if (task.collection == "arm-debug") {
+    // These tests take quite some time on our poor ARM devices.
+    if (task.tests == "chains" || (task.tests == "ssl" && task.cycle == "standard")) {
+      task.maxRunTime = 14400;
+    }
+  }
+
+  return task;
+});
+
+/*****************************************************************************/
+
+export default async function main() {
+  await scheduleLinux("Linux 32 (opt)", {
+    env: {BUILD_OPT: "1"},
+    platform: "linux32",
+    image: LINUX_IMAGE
+  });
+
+  await scheduleLinux("Linux 32 (debug)", {
+    platform: "linux32",
+    collection: "debug",
+    image: LINUX_IMAGE
+  });
+
+  await scheduleLinux("Linux 64 (opt)", {
+    env: {USE_64: "1", BUILD_OPT: "1"},
+    platform: "linux64",
+    image: LINUX_IMAGE
+  });
+
+  await scheduleLinux("Linux 64 (debug)", {
+    env: {USE_64: "1"},
+    platform: "linux64",
+    collection: "debug",
+    image: LINUX_IMAGE
+  });
+
+  await scheduleLinux("Linux 64 (ASan, debug)", {
+    env: {
+      NSS_DISABLE_ARENA_FREE_LIST: "1",
+      NSS_DISABLE_UNLOAD: "1",
+      GCC_VERSION: "clang",
+      GXX_VERSION: "clang++",
+      USE_ASAN: "1",
+      USE_64: "1"
+    },
+    platform: "linux64",
+    collection: "asan",
+    image: LINUX_IMAGE
+  });
+
+  await scheduleWindows("Windows 2012 64 (opt)", {
+    env: {BUILD_OPT: "1"}
+  });
+
+  await scheduleWindows("Windows 2012 64 (debug)", {
+    collection: "debug"
+  });
+
+  await scheduleTools();
+
+  await scheduleLinux("Linux 32 (ARM, debug)", {
+    image: "ttaubert/nss-rpi-ci:0.0.3",
+    provisioner: "localprovisioner",
+    collection: "arm-debug",
+    workerType: "nss-rpi",
+    platform: "linux32",
+    maxRunTime: 7200,
+    tier: 3
+  });
+}
+
+/*****************************************************************************/
+
+async function scheduleLinux(name, base) {
+  // Build base definition.
+  let build_base = merge(base, {
+    command: [
+      "/bin/bash",
+      "-c",
+      "bin/checkout.sh && nss/automation/taskcluster/scripts/build.sh"
+    ],
+    artifacts: {
+      public: {
+        expires: 24 * 7,
+        type: "directory",
+        path: "/home/worker/artifacts"
+      }
+    },
+    kind: "build",
+    symbol: "B"
+  });
+
+  // The task that builds NSPR+NSS.
+  let task_build = queue.scheduleTask(merge(build_base, {name}));
+
+  // The task that generates certificates.
+  let task_cert = queue.scheduleTask(merge(build_base, {
+    name: "Certificates",
+    command: [
+      "/bin/bash",
+      "-c",
+      "bin/checkout.sh && nss/automation/taskcluster/scripts/gen_certs.sh"
+    ],
+    parent: task_build,
+    symbol: "Certs"
+  }));
+
+  // Schedule tests.
+  scheduleTests(task_build, task_cert, merge(base, {
+    command: [
+      "/bin/bash",
+      "-c",
+      "bin/checkout.sh && nss/automation/taskcluster/scripts/run_tests.sh"
+    ]
+  }));
+
+  // Extra builds.
+  let extra_base = merge({group: "Builds"}, build_base);
+  queue.scheduleTask(merge(extra_base, {
+    name: `${name} w/ clang-3.9`,
+    env: {
+      GCC_VERSION: "clang",
+      GXX_VERSION: "clang++"
+    },
+    symbol: "clang-3.9"
+  }));
+
+  queue.scheduleTask(merge(extra_base, {
+    name: `${name} w/ gcc-4.8`,
+    env: {
+      GCC_VERSION: "gcc-4.8",
+      GXX_VERSION: "g++-4.8"
+    },
+    symbol: "gcc-4.8"
+  }));
+
+  queue.scheduleTask(merge(extra_base, {
+    name: `${name} w/ gcc-6.1`,
+    env: {
+      GCC_VERSION: "gcc-6",
+      GXX_VERSION: "g++-6"
+    },
+    symbol: "gcc-6.1"
+  }));
+
+  queue.scheduleTask(merge(extra_base, {
+    name: `${name} w/ NSS_NO_PKCS11_BYPASS=1`,
+    env: {NSS_NO_PKCS11_BYPASS: "1"},
+    symbol: "noPkcs11Bypass"
+  }));
+
+  queue.scheduleTask(merge(extra_base, {
+    name: `${name} w/ NSS_DISABLE_LIBPKIX=1`,
+    env: {NSS_DISABLE_LIBPKIX: "1"},
+    symbol: "noLibpkix"
+  }));
+
+  return queue.submit();
+}
+
+/*****************************************************************************/
+
+async function scheduleWindows(name, base) {
+  base = merge(base, {
+    workerType: "nss-win2012r2",
+    platform: "windows2012-64",
+    env: {
+      PATH: "c:\\mozilla-build\\python;c:\\mozilla-build\\msys\\local\\bin;" +
+            "c:\\mozilla-build\\7zip;c:\\mozilla-build\\info-zip;" +
+            "c:\\mozilla-build\\python\\Scripts;c:\\mozilla-build\\yasm;" +
+            "c:\\mozilla-build\\msys\\bin;c:\\Windows\\system32;" +
+            "c:\\mozilla-build\\upx391w;c:\\mozilla-build\\moztools-x64\\bin;" +
+            "c:\\mozilla-build\\wget",
+      DOMSUF: "localdomain",
+      HOST: "localhost",
+      USE_64: "1"
+    }
+  });
+
+  // Build base definition.
+  let build_base = merge(base, {
+    command: [
+      WINDOWS_CHECKOUT_CMD,
+      "bash -c nss/automation/taskcluster/windows/build.sh"
+    ],
+    artifacts: [{
+      expires: 24 * 7,
+      type: "directory",
+      path: "public\\build"
+    }],
+    kind: "build",
+    symbol: "B"
+  });
+
+  // The task that builds NSPR+NSS.
+  let task_build = queue.scheduleTask(merge(build_base, {name}));
+
+  // The task that generates certificates.
+  let task_cert = queue.scheduleTask(merge(build_base, {
+    name: "Certificates",
+    command: [
+      WINDOWS_CHECKOUT_CMD,
+      "bash -c nss/automation/taskcluster/windows/gen_certs.sh"
+    ],
+    parent: task_build,
+    symbol: "Certs"
+  }));
+
+  // Schedule tests.
+  scheduleTests(task_build, task_cert, merge(base, {
+    command: [
+      WINDOWS_CHECKOUT_CMD,
+      "bash -c nss/automation/taskcluster/windows/run_tests.sh"
+    ]
+  }));
+
+  // Extra builds.
+  let extra_base = merge({group: "Builds"}, build_base);
+  queue.scheduleTask(merge(extra_base, {
+    name: `${name} w/ NSS_NO_PKCS11_BYPASS=1`,
+    env: {NSS_NO_PKCS11_BYPASS: "1"},
+    symbol: "noPkcs11Bypass"
+  }));
+
+  return queue.submit();
+}
+
+/*****************************************************************************/
+
+function scheduleTests(task_build, task_cert, test_base) {
+  test_base = merge({kind: "test"}, test_base);
+
+  // Schedule tests that do NOT need certificates.
+  let no_cert_base = merge(test_base, {parent: task_build});
+  queue.scheduleTask(merge(no_cert_base, {
+    name: "Gtests", symbol: "Gtest", tests: "ssl_gtests gtests"
+  }));
+  queue.scheduleTask(merge(no_cert_base, {
+    name: "Chains tests", symbol: "Chains", tests: "chains"
+  }));
+  queue.scheduleTask(merge(no_cert_base, {
+    name: "Cipher tests", symbol: "Cipher", tests: "cipher"
+  }));
+  queue.scheduleTask(merge(no_cert_base, {
+    name: "EC tests", symbol: "EC", tests: "ec"
+  }));
+  queue.scheduleTask(merge(no_cert_base, {
+    name: "Lowhash tests", symbol: "Lowhash", tests: "lowhash"
+  }));
+  queue.scheduleTask(merge(no_cert_base, {
+    name: "SDR tests", symbol: "SDR", tests: "sdr"
+  }));
+
+  // Schedule tests that need certificates.
+  let cert_base = merge(test_base, {parent: task_cert});
+  queue.scheduleTask(merge(cert_base, {
+    name: "CRMF tests", symbol: "CRMF", tests: "crmf"
+  }));
+  queue.scheduleTask(merge(cert_base, {
+    name: "DB tests", symbol: "DB", tests: "dbtests"
+  }));
+  queue.scheduleTask(merge(cert_base, {
+    name: "FIPS tests", symbol: "FIPS", tests: "fips"
+  }));
+  queue.scheduleTask(merge(cert_base, {
+    name: "Merge tests", symbol: "Merge", tests: "merge"
+  }));
+  queue.scheduleTask(merge(cert_base, {
+    name: "S/MIME tests", symbol: "SMIME", tests: "smime"
+  }));
+  queue.scheduleTask(merge(cert_base, {
+    name: "Tools tests", symbol: "Tools", tests: "tools"
+  }));
+
+  // SSL tests, need certificates too.
+  let ssl_base = merge(cert_base, {tests: "ssl", group: "SSL"});
+  queue.scheduleTask(merge(ssl_base, {
+    name: "SSL tests (standard)", symbol: "standard", cycle: "standard"
+  }));
+  queue.scheduleTask(merge(ssl_base, {
+    name: "SSL tests (pkix)", symbol: "pkix", cycle: "pkix"
+  }));
+  queue.scheduleTask(merge(ssl_base, {
+    name: "SSL tests (sharedb)", symbol: "sharedb", cycle: "sharedb"
+  }));
+  queue.scheduleTask(merge(ssl_base, {
+    name: "SSL tests (upgradedb)", symbol: "upgradedb", cycle: "upgradedb"
+  }));
+}
+
+/*****************************************************************************/
+
+async function scheduleTools() {
+  let base = {
+    image: LINUX_IMAGE,
+    platform: "nss-tools",
+    kind: "test"
+  };
+
+  queue.scheduleTask(merge(base, {
+    symbol: "clang-format-3.9",
+    name: "clang-format-3.9",
+    command: [
+      "/bin/bash",
+      "-c",
+      "bin/checkout.sh && nss/automation/taskcluster/scripts/run_clang_format.sh"
+    ]
+  }));
+
+  queue.scheduleTask(merge(base, {
+    symbol: "scan-build-3.9",
+    name: "scan-build-3.9",
+    env: {
+      USE_64: "1",
+      GCC_VERSION: "clang",
+      GXX_VERSION: "clang++"
+    },
+    artifacts: {
+      public: {
+        expires: 24,
+        type: "directory",
+        path: "/home/worker/artifacts"
+      }
+    },
+    command: [
+      "/bin/bash",
+      "-c",
+      "bin/checkout.sh && nss/automation/taskcluster/scripts/run_scan_build.sh"
+    ]
+  }));
+
+  return queue.submit();
+}
new file mode 100644
--- /dev/null
+++ b/security/nss/automation/taskcluster/graph/src/image_builder.js
@@ -0,0 +1,62 @@
+/* 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/. */
+
+import * as queue from "./queue";
+import context_hash from "./context_hash";
+import taskcluster from "taskcluster-client";
+
+async function taskHasImageArtifact(taskId) {
+  let queue = new taskcluster.Queue();
+  let {artifacts} = await queue.listLatestArtifacts(taskId);
+  return artifacts.some(artifact => artifact.name == "public/image.tar");
+}
+
+async function findTaskWithImageArtifact(ns) {
+  let index = new taskcluster.Index();
+  let {taskId} = await index.findTask(ns);
+  let has_image = await taskHasImageArtifact(taskId);
+  return has_image ? taskId : null;
+}
+
+export async function findTask({name, path}) {
+  let hash = await context_hash(path);
+  let ns = `docker.images.v1.${process.env.TC_PROJECT}.${name}.hash.${hash}`;
+  return findTaskWithImageArtifact(ns).catch(() => null);
+}
+
+export async function buildTask({name, path}) {
+  let hash = await context_hash(path);
+  let ns = `docker.images.v1.${process.env.TC_PROJECT}.${name}.hash.${hash}`;
+
+  return {
+    name: "Image Builder",
+    image: "taskcluster/image_builder:0.1.5",
+    routes: ["index." + ns],
+    env: {
+      HEAD_REPOSITORY: process.env.NSS_HEAD_REPOSITORY,
+      BASE_REPOSITORY: process.env.NSS_HEAD_REPOSITORY,
+      HEAD_REV: process.env.NSS_HEAD_REVISION,
+      HEAD_REF: process.env.NSS_HEAD_REVISION,
+      PROJECT: process.env.TC_PROJECT,
+      CONTEXT_PATH: path,
+      HASH: hash
+    },
+    artifacts: {
+      "public/image.tar": {
+        type: "file",
+        expires: 24 * 365,
+        path: "/artifacts/image.tar"
+      }
+    },
+    command: [
+      "/bin/bash",
+      "-c",
+      "/home/worker/bin/build_image.sh"
+    ],
+    platform: "nss-decision",
+    features: ["dind"],
+    kind: "build",
+    symbol: "I"
+  };
+}
new file mode 100644
--- /dev/null
+++ b/security/nss/automation/taskcluster/graph/src/index.js
@@ -0,0 +1,14 @@
+/* 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/. */
+
+import * as try_syntax from "./try_syntax";
+import extend from "./extend";
+
+// Init try syntax filter.
+if (process.env.TC_PROJECT == "nss-try") {
+  try_syntax.initFilter();
+}
+
+// Extend the task graph.
+extend().catch(console.error);
new file mode 100644
--- /dev/null
+++ b/security/nss/automation/taskcluster/graph/src/merge.js
@@ -0,0 +1,10 @@
+/* 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/. */
+
+import {recursive as merge} from "merge";
+
+// We always want to clone.
+export default function (...args) {
+  return merge(true, ...args);
+}
new file mode 100644
--- /dev/null
+++ b/security/nss/automation/taskcluster/graph/src/queue.js
@@ -0,0 +1,242 @@
+/* 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/. */
+
+import {clone} from "merge";
+import merge from "./merge";
+import slugid from "slugid";
+import taskcluster from "taskcluster-client";
+import * as image_builder from "./image_builder";
+
+let maps = [];
+let filters = [];
+
+let tasks = new Map();
+let image_tasks = new Map();
+
+let queue = new taskcluster.Queue({
+  baseUrl: "http://taskcluster/queue/v1/"
+});
+
+function fromNow(hours) {
+  let d = new Date();
+  d.setHours(d.getHours() + (hours|0));
+  return d.toJSON();
+}
+
+function parseRoutes(routes) {
+  return [
+    `tc-treeherder.v2.${process.env.TC_PROJECT}.${process.env.NSS_HEAD_REVISION}.${process.env.NSS_PUSHLOG_ID}`,
+    ...routes
+  ];
+}
+
+function parseFeatures(list) {
+  return list.reduce((map, feature) => {
+    map[feature] = true;
+    return map;
+  }, {});
+}
+
+function parseArtifacts(artifacts) {
+  let copy = clone(artifacts);
+  Object.keys(copy).forEach(key => {
+    copy[key].expires = fromNow(copy[key].expires);
+  });
+  return copy;
+}
+
+function parseCollection(name) {
+  let collection = {};
+  collection[name] = true;
+  return collection;
+}
+
+function parseTreeherder(def) {
+  let treeherder = {
+    build: {
+      platform: def.platform
+    },
+    machine: {
+      platform: def.platform
+    },
+    symbol: def.symbol,
+    jobKind: def.kind
+  };
+
+  if (def.group) {
+    treeherder.groupSymbol = def.group;
+  }
+
+  if (def.collection) {
+    treeherder.collection = parseCollection(def.collection);
+  }
+
+  if (def.tier) {
+    treeherder.tier = def.tier;
+  }
+
+  return treeherder;
+}
+
+function convertTask(def) {
+  let dependencies = [];
+
+  let env = merge({
+    NSS_HEAD_REPOSITORY: process.env.NSS_HEAD_REPOSITORY,
+    NSS_HEAD_REVISION: process.env.NSS_HEAD_REVISION
+  }, def.env || {});
+
+  if (def.parent) {
+    dependencies.push(def.parent);
+    env.TC_PARENT_TASK_ID = def.parent;
+  }
+
+  if (def.tests) {
+    env.NSS_TESTS = def.tests;
+  }
+
+  if (def.cycle) {
+    env.NSS_CYCLES = def.cycle;
+  }
+
+  let payload = {
+    env,
+    command: def.command,
+    maxRunTime: def.maxRunTime || 3600
+  };
+
+  if (def.image) {
+    payload.image = def.image;
+  }
+
+  if (def.features) {
+    payload.features = parseFeatures(def.features);
+  }
+
+  if (def.artifacts) {
+    payload.artifacts = parseArtifacts(def.artifacts);
+  }
+
+  return {
+    provisionerId: def.provisioner || "aws-provisioner-v1",
+    workerType: def.workerType || "hg-worker",
+    schedulerId: "task-graph-scheduler",
+
+    created: fromNow(0),
+    deadline: fromNow(24),
+
+    dependencies,
+    routes: parseRoutes(def.routes || []),
+
+    metadata: {
+      name: def.name,
+      description: def.name,
+      owner: process.env.TC_OWNER,
+      source: process.env.TC_SOURCE
+    },
+
+    payload,
+
+    extra: {
+      treeherder: parseTreeherder(def)
+    }
+  };
+}
+
+export function map(fun) {
+  maps.push(fun);
+}
+
+export function filter(fun) {
+  filters.push(fun);
+}
+
+export function scheduleTask(def) {
+  let taskId = slugid.v4();
+  tasks.set(taskId, merge({}, def));
+  return taskId;
+}
+
+export async function submit() {
+  let promises = new Map();
+
+  for (let [taskId, task] of tasks) {
+    // Allow filtering tasks before we schedule them.
+    if (!filters.every(filter => filter(task))) {
+      continue;
+    }
+
+    // Allow changing tasks before we schedule them.
+    maps.forEach(map => { task = map(merge({}, task)) });
+
+    let log_id = `${task.name} @ ${task.platform}[${task.collection || "opt"}]`;
+    console.log(`+ Submitting ${log_id}.`);
+
+    let parent = task.parent;
+
+    // Convert the task definition.
+    task = await convertTask(task);
+
+    // Convert the docker image definition.
+    let image_def = task.payload.image;
+    if (image_def && image_def.hasOwnProperty("path")) {
+      let key = `${image_def.name}:${image_def.path}`;
+      let data = {};
+
+      // Check the cache first.
+      if (image_tasks.has(key)) {
+        data = image_tasks.get(key);
+      } else {
+        data.taskId = await image_builder.findTask(image_def);
+        data.isPending = !data.taskId;
+
+        // No task found.
+        if (data.isPending) {
+          let image_task = await image_builder.buildTask(image_def);
+
+          // Schedule a new image builder task immediately.
+          data.taskId = slugid.v4();
+
+          try {
+            await queue.createTask(data.taskId, convertTask(image_task));
+          } catch (e) {
+            console.error("! FAIL: Scheduling image builder task failed.");
+            continue; /* Skip this task on failure. */
+          }
+        }
+
+        // Store in cache.
+        image_tasks.set(key, data);
+      }
+
+      if (data.isPending) {
+        task.dependencies.push(data.taskId);
+      }
+
+      task.payload.image = {
+        path: "public/image.tar",
+        taskId: data.taskId,
+        type: "task-image"
+      };
+    }
+
+    // Wait for the parent task to be created before scheduling dependants.
+    let predecessor = parent ? promises.get(parent) : Promise.resolve();
+
+    promises.set(taskId, predecessor.then(() => {
+      // Schedule the task.
+      return queue.createTask(taskId, task).catch(err => {
+        console.error(`! FAIL: Scheduling ${log_id} failed.`, err);
+      });
+    }));
+  }
+
+  // Wait for all requests to finish.
+  if (promises.length) {
+    await Promise.all([...promises.values()]);
+    console.log("=== Total:", promises.length, "tasks. ===");
+  }
+
+  tasks.clear();
+}
new file mode 100644
--- /dev/null
+++ b/security/nss/automation/taskcluster/graph/src/try_syntax.js
@@ -0,0 +1,140 @@
+/* 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/. */
+
+import * as queue from "./queue";
+import intersect from "intersect";
+import parse_args from "minimist";
+
+function parseOptions(opts) {
+  opts = parse_args(opts.split(/\s+/), {
+    default: {build: "do", platform: "all", unittests: "none", tools: "none"},
+    alias: {b: "build", p: "platform", u: "unittests", t: "tools", e: "extra-builds"},
+    string: ["build", "platform", "unittests", "tools", "extra-builds"]
+  });
+
+  // Parse build types (d=debug, o=opt).
+  let builds = intersect(opts.build.split(""), ["d", "o"]);
+
+  // If the given value is nonsense default to debug and opt builds.
+  if (builds.length == 0) {
+    builds = ["d", "o"];
+  }
+
+  // Parse platforms.
+  let allPlatforms = ["linux", "linux64", "linux64-asan", "win64", "arm"];
+  let platforms = intersect(opts.platform.split(/\s*,\s*/), allPlatforms);
+
+  // If the given value is nonsense or "none" default to all platforms.
+  if (platforms.length == 0 && opts.platform != "none") {
+    platforms = allPlatforms;
+  }
+
+  // Parse unit tests.
+  let allUnitTests = ["crmf", "chains", "cipher", "db", "ec", "fips", "gtest",
+                      "lowhash", "merge", "sdr", "smime", "tools", "ssl"];
+  let unittests = intersect(opts.unittests.split(/\s*,\s*/), allUnitTests);
+
+  // If the given value is "all" run all tests.
+  // If it's nonsense then don't run any tests.
+  if (opts.unittests == "all") {
+    unittests = allUnitTests;
+  } else if (unittests.length == 0) {
+    unittests = [];
+  }
+
+  // Parse tools.
+  let allTools = ["clang-format", "scan-build"];
+  let tools = intersect(opts.tools.split(/\s*,\s*/), allTools);
+
+  // If the given value is "all" run all tools.
+  // If it's nonsense then don't run any tools.
+  if (opts.tools == "all") {
+    tools = allTools;
+  } else if (tools.length == 0) {
+    tools = [];
+  }
+
+  return {
+    builds: builds,
+    platforms: platforms,
+    unittests: unittests,
+    extra: (opts.e == "all"),
+    tools: tools
+  };
+}
+
+function filter(opts) {
+  return function (task) {
+    // Filter tools. We can immediately return here as those
+    // are not affected by platform or build type selectors.
+    if (task.platform == "nss-tools") {
+      return opts.tools.some(tool => {
+        return task.symbol.toLowerCase().startsWith(tool);
+      });
+    }
+
+    // Filter unit tests.
+    if (task.tests) {
+      let found = opts.unittests.some(test => {
+        return (task.group || task.symbol).toLowerCase().startsWith(test);
+      });
+
+      if (!found) {
+        return false;
+      }
+    }
+
+    // Filter extra builds.
+    if (task.group == "Builds" && !opts.extra) {
+      return false;
+    }
+
+    let coll = name => name == (task.collection || "opt");
+
+    // Filter by platform.
+    let found = opts.platforms.some(platform => {
+      let aliases = {
+        "linux": "linux32",
+        "linux64-asan": "linux64",
+        "win64": "windows2012-64",
+        "arm": "linux32"
+      };
+
+      // Check the platform name.
+      let keep = (task.platform == (aliases[platform] || platform));
+
+      // Additional checks.
+      if (platform == "linux64-asan") {
+        keep &= coll("asan");
+      } else if (platform == "arm") {
+        keep &= coll("arm-opt") || coll("arm-debug");
+      } else {
+        keep &= coll("opt") || coll("debug");
+      }
+
+      return keep;
+    });
+
+    if (!found) {
+      return false;
+    }
+
+    // Finally, filter by build type.
+    let isDebug = coll("debug") || coll("asan") || coll("arm-debug");
+    return (isDebug && opts.builds.includes("d")) ||
+           (!isDebug && opts.builds.includes("o"));
+  }
+}
+
+export function initFilter() {
+  let comment = process.env.TC_COMMENT || "";
+
+  // Check for try syntax in changeset comment.
+  let match = comment.match(/^\s*try:\s*(.*)\s*$/);
+
+  // Add try syntax filter.
+  if (match) {
+    queue.filter(filter(parseOptions(match[1])));
+  }
+}
deleted file mode 100644
--- a/security/nss/automation/taskcluster/graph/tests/chains.yml
+++ /dev/null
@@ -1,14 +0,0 @@
----
-- task:
-    metadata:
-      name: Chains tests
-      description: Chains tests
-
-    payload:
-      maxRunTime: 14400
-      env:
-        NSS_TESTS: chains
-
-    extra:
-      treeherder:
-        symbol: Chains
deleted file mode 100644
--- a/security/nss/automation/taskcluster/graph/tests/cipher.yml
+++ /dev/null
@@ -1,13 +0,0 @@
----
-- task:
-    metadata:
-      name: Cipher tests
-      description: Cipher tests
-
-    payload:
-      env:
-        NSS_TESTS: cipher
-
-    extra:
-      treeherder:
-        symbol: Cipher
deleted file mode 100644
--- a/security/nss/automation/taskcluster/graph/tests/crmf.yml
+++ /dev/null
@@ -1,14 +0,0 @@
----
-- task:
-    metadata:
-      name: CRMF tests
-      description: CRMF tests
-
-    payload:
-      env:
-        ASAN_OPTIONS: detect_leaks=0
-        NSS_TESTS: crmf
-
-    extra:
-      treeherder:
-        symbol: CRMF
deleted file mode 100644
--- a/security/nss/automation/taskcluster/graph/tests/db.yml
+++ /dev/null
@@ -1,13 +0,0 @@
----
-- task:
-    metadata:
-      name: DB tests
-      description: DB tests
-
-    payload:
-      env:
-        NSS_TESTS: dbtests
-
-    extra:
-      treeherder:
-        symbol: DB
deleted file mode 100644
--- a/security/nss/automation/taskcluster/graph/tests/ec.yml
+++ /dev/null
@@ -1,13 +0,0 @@
----
-- task:
-    metadata:
-      name: EC tests
-      description: EC tests
-
-    payload:
-      env:
-        NSS_TESTS: ec
-
-    extra:
-      treeherder:
-        symbol: EC
deleted file mode 100644
--- a/security/nss/automation/taskcluster/graph/tests/fips.yml
+++ /dev/null
@@ -1,14 +0,0 @@
----
-- task:
-    metadata:
-      name: FIPS tests
-      description: FIPS tests
-
-    payload:
-      env:
-        ASAN_OPTIONS: detect_leaks=0
-        NSS_TESTS: fips
-
-    extra:
-      treeherder:
-        symbol: FIPS
deleted file mode 100644
--- a/security/nss/automation/taskcluster/graph/tests/gtests.yml
+++ /dev/null
@@ -1,13 +0,0 @@
----
-- task:
-    metadata:
-      name: GTests
-      description: GTests
-
-    payload:
-      env:
-        NSS_TESTS: ssl_gtests gtests
-
-    extra:
-      treeherder:
-        symbol: GTest
deleted file mode 100644
--- a/security/nss/automation/taskcluster/graph/tests/lowhash.yml
+++ /dev/null
@@ -1,13 +0,0 @@
----
-- task:
-    metadata:
-      name: Lowhash tests
-      description: Lowhash tests
-
-    payload:
-      env:
-        NSS_TESTS: lowhash
-
-    extra:
-      treeherder:
-        symbol: Lowhash
deleted file mode 100644
--- a/security/nss/automation/taskcluster/graph/tests/merge.yml
+++ /dev/null
@@ -1,13 +0,0 @@
----
-- task:
-    metadata:
-      name: Merge tests
-      description: Merge tests
-
-    payload:
-      env:
-        NSS_TESTS: merge
-
-    extra:
-      treeherder:
-        symbol: Merge
deleted file mode 100644
--- a/security/nss/automation/taskcluster/graph/tests/sdr.yml
+++ /dev/null
@@ -1,13 +0,0 @@
----
-- task:
-    metadata:
-      name: SDR tests
-      description: SDR tests
-
-    payload:
-      env:
-        NSS_TESTS: sdr
-
-    extra:
-      treeherder:
-        symbol: SDR
deleted file mode 100644
--- a/security/nss/automation/taskcluster/graph/tests/smime.yml
+++ /dev/null
@@ -1,13 +0,0 @@
----
-- task:
-    metadata:
-      name: S/MIME tests
-      description: S/MIME tests
-
-    payload:
-      env:
-        NSS_TESTS: smime
-
-    extra:
-      treeherder:
-        symbol: SMIME
deleted file mode 100644
--- a/security/nss/automation/taskcluster/graph/tests/ssl.yml
+++ /dev/null
@@ -1,65 +0,0 @@
----
-- task:
-    metadata:
-      name: "SSL tests (standard)"
-      description: "SSL tests (standard)"
-
-    payload:
-      maxRunTime: 14400
-      env:
-        NSS_CYCLES: standard
-        NSS_TESTS: ssl
-
-    extra:
-      treeherder:
-        symbol: standard
-        groupSymbol: SSL
-        groupName: SSL tests
-
-- task:
-    metadata:
-      name: "SSL tests (pkix)"
-      description: "SSL tests (pkix)"
-
-    payload:
-      env:
-        NSS_CYCLES: pkix
-        NSS_TESTS: ssl
-
-    extra:
-      treeherder:
-        symbol: pkix
-        groupSymbol: SSL
-        groupName: SSL tests
-
-- task:
-    metadata:
-      name: "SSL tests (sharedb)"
-      description: "SSL tests (sharedb)"
-
-    payload:
-      env:
-        NSS_CYCLES: sharedb
-        NSS_TESTS: ssl
-
-    extra:
-      treeherder:
-        symbol: sharedb
-        groupSymbol: SSL
-        groupName: SSL tests
-
-- task:
-    metadata:
-      name: "SSL tests (upgradedb)"
-      description: "SSL tests (upgradedb)"
-
-    payload:
-      env:
-        NSS_CYCLES: upgradedb
-        NSS_TESTS: ssl
-
-    extra:
-      treeherder:
-        symbol: upgradedb
-        groupSymbol: SSL
-        groupName: SSL tests
deleted file mode 100644
--- a/security/nss/automation/taskcluster/graph/tests/tools.yml
+++ /dev/null
@@ -1,13 +0,0 @@
----
-- task:
-    metadata:
-      name: Tools tests
-      description: Tools tests
-
-    payload:
-      env:
-        NSS_TESTS: tools
-
-    extra:
-      treeherder:
-        symbol: Tools
deleted file mode 100644
--- a/security/nss/automation/taskcluster/graph/tools/_build_base.yml
+++ /dev/null
@@ -1,29 +0,0 @@
----
-reruns: 0
-
-task:
-  created: !from_now 0
-  deadline: !from_now 24
-  provisionerId: aws-provisioner-v1
-  workerType: hg-worker
-  schedulerId: task-graph-scheduler
-
-  metadata:
-    owner: !env TC_OWNER
-    source: !env TC_SOURCE
-
-  payload:
-    maxRunTime: 3600
-    image: automation/taskcluster/docker
-
-    env:
-      NSS_HEAD_REPOSITORY: !env NSS_HEAD_REPOSITORY
-      NSS_HEAD_REVISION: !env NSS_HEAD_REVISION
-
-  extra:
-    treeherder:
-      build:
-        platform: nss-tools
-      machine:
-        platform: nss-tools
-      jobKind: test
deleted file mode 100644
--- a/security/nss/automation/taskcluster/graph/tools/clang-format.yml
+++ /dev/null
@@ -1,15 +0,0 @@
----
-- task:
-    metadata:
-      name: clang-format-3.8
-      description: clang-format-3.8
-
-    payload:
-      command:
-        - "/bin/bash"
-        - "-c"
-        - "bin/checkout.sh && nss/automation/taskcluster/scripts/run_clang_format.sh"
-
-    extra:
-      treeherder:
-        symbol: clang-format-3.8
deleted file mode 100644
--- a/security/nss/automation/taskcluster/graph/tools/scan-build.yml
+++ /dev/null
@@ -1,26 +0,0 @@
----
-- task:
-    metadata:
-      name: scan-build-3.8
-      description: scan-build-3.8
-
-    payload:
-      artifacts:
-        public:
-          type: directory
-          path: /home/worker/artifacts
-          expires: !from_now 24
-
-      command:
-        - "/bin/bash"
-        - "-c"
-        - "bin/checkout.sh && nss/automation/taskcluster/scripts/run_scan_build.sh"
-
-      env:
-        GCC_VERSION: clang
-        GXX_VERSION: clang++
-        USE_64: 1
-
-    extra:
-      treeherder:
-        symbol: scan-build-3.8
deleted file mode 100644
--- a/security/nss/automation/taskcluster/graph/try_syntax.js
+++ /dev/null
@@ -1,143 +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/. */
-
-var intersect = require("intersect");
-var parse_args = require("minimist");
-
-function parseOptions(opts) {
-  opts = parse_args(opts.split(/\s+/), {
-    default: {build: "do", platform: "all", unittests: "none", tools: "none"},
-    alias: {b: "build", p: "platform", u: "unittests", t: "tools", e: "extra-builds"},
-    string: ["build", "platform", "unittests", "tools", "extra-builds"]
-  });
-
-  // Parse build types (d=debug, o=opt).
-  var builds = intersect(opts.build.split(""), ["d", "o"]);
-
-  // If the given value is nonsense default to debug and opt builds.
-  if (builds.length == 0) {
-    builds = ["d", "o"];
-  }
-
-  // Parse platforms.
-  var allPlatforms = ["linux", "linux64", "linux64-asan", "win64", "arm"];
-  var platforms = intersect(opts.platform.split(/\s*,\s*/), allPlatforms);
-
-  // If the given value is nonsense or "none" default to all platforms.
-  if (platforms.length == 0 && opts.platform != "none") {
-    platforms = allPlatforms;
-  }
-
-  // Parse unit tests.
-  var allUnitTests = ["crmf", "chains", "cipher", "db", "ec", "fips", "gtest",
-                      "lowhash", "merge", "sdr", "smime", "tools", "ssl"];
-  var unittests = intersect(opts.unittests.split(/\s*,\s*/), allUnitTests);
-
-  // If the given value is "all" run all tests.
-  // If it's nonsense then don't run any tests.
-  if (opts.unittests == "all") {
-    unittests = allUnitTests;
-  } else if (unittests.length == 0) {
-    unittests = [];
-  }
-
-  // Parse tools.
-  var allTools = ["clang-format", "scan-build"];
-  var tools = intersect(opts.tools.split(/\s*,\s*/), allTools);
-
-  // If the given value is "all" run all tools.
-  // If it's nonsense then don't run any tools.
-  if (opts.tools == "all") {
-    tools = allTools;
-  } else if (tools.length == 0) {
-    tools = [];
-  }
-
-  return {
-    builds: builds,
-    platforms: platforms,
-    unittests: unittests,
-    extra: (opts.e == "all"),
-    tools: tools
-  };
-}
-
-function filterTasks(tasks, comment) {
-  // Check for try syntax in changeset comment.
-  var match = comment.match(/^\s*try:\s*(.*)\s*$/);
-  if (!match) {
-    return tasks;
-  }
-
-  var opts = parseOptions(match[1]);
-
-  return tasks.filter(function (task) {
-    var env = task.task.payload.env || {};
-    var th = task.task.extra.treeherder;
-    var machine = th.machine.platform;
-    var coll = th.collection || {};
-    var found;
-
-    // Filter tools. We can immediately return here as those
-    // are not affected by platform or build type selectors.
-    if (machine == "nss-tools") {
-      return opts.tools.some(function (tool) {
-        var symbol = th.symbol.toLowerCase();
-        return symbol.startsWith(tool);
-      });
-    }
-
-    // Filter unit tests.
-    if (env.NSS_TESTS && env.TC_PARENT_TASK_ID) {
-      found = opts.unittests.some(function (test) {
-        var symbol = (th.groupSymbol || th.symbol).toLowerCase();
-        return symbol.startsWith(test);
-      });
-
-      if (!found) {
-        return false;
-      }
-    }
-
-    // Filter extra builds.
-    if (th.groupSymbol == "Builds" && !opts.extra) {
-      return false;
-    }
-
-    // Filter by platform.
-    found = opts.platforms.some(function (platform) {
-      var aliases = {
-        "linux": "linux32",
-        "linux64-asan": "linux64",
-        "win64": "windows2012-64",
-        "arm": "linux32"
-      };
-
-      // Check the platform name.
-      var keep = machine == (aliases[platform] || platform);
-
-      // Additional checks.
-      if (platform == "linux64-asan") {
-        keep &= coll.asan;
-      } else if (platform == "arm") {
-        keep &= (coll["arm-opt"] || coll["arm-debug"]);
-      } else {
-        keep &= (coll.opt || coll.debug);
-      }
-
-      return keep;
-    });
-
-    if (!found) {
-      return false;
-    }
-
-    // Finally, filter by build type.
-    var isDebug = coll.debug || coll.asan || coll["arm-debug"];
-    return (isDebug && opts.builds.indexOf("d") > -1) ||
-           (!isDebug && opts.builds.indexOf("o") > -1);
-  });
-}
-
-module.exports.filterTasks = filterTasks;
deleted file mode 100644
--- a/security/nss/automation/taskcluster/graph/windows/_build_base.yml
+++ /dev/null
@@ -1,37 +0,0 @@
----
-reruns: 2
-
-task:
-  created: !from_now 0
-  deadline: !from_now 24
-  provisionerId: aws-provisioner-v1
-  workerType: nss-win2012r2
-  schedulerId: task-graph-scheduler
-
-  metadata:
-    owner: !env TC_OWNER
-    source: !env TC_SOURCE
-
-  payload:
-    maxRunTime: 3600
-
-    artifacts:
-      - type: directory
-        path: "public\\build"
-        expires: !from_now 24
-
-    command:
-      - "bash -c \"hg clone -r $NSS_HEAD_REVISION $NSS_HEAD_REPOSITORY nss || (sleep 2; hg clone -r $NSS_HEAD_REVISION $NSS_HEAD_REPOSITORY nss) || (sleep 5; hg clone -r $NSS_HEAD_REVISION $NSS_HEAD_REPOSITORY nss)\""
-      - "bash -c nss/automation/taskcluster/windows/build.sh"
-
-    env:
-      PATH: "c:\\mozilla-build\\python;c:\\mozilla-build\\msys\\local\\bin;c:\\mozilla-build\\7zip;c:\\mozilla-build\\info-zip;c:\\mozilla-build\\python\\Scripts;c:\\mozilla-build\\yasm;c:\\mozilla-build\\msys\\bin;c:\\Windows\\system32;c:\\mozilla-build\\upx391w;c:\\mozilla-build\\moztools-x64\\bin;c:\\mozilla-build\\wget"
-      NSS_HEAD_REPOSITORY: !env NSS_HEAD_REPOSITORY
-      NSS_HEAD_REVISION: !env NSS_HEAD_REVISION
-      DOMSUF: localdomain
-      HOST: localhost
-
-  extra:
-    treeherder:
-      jobKind: build
-      symbol: B
deleted file mode 100644
--- a/security/nss/automation/taskcluster/graph/windows/_test_base.yml
+++ /dev/null
@@ -1,24 +0,0 @@
----
-reruns: 2
-
-task:
-  created: !from_now 0
-  deadline: !from_now 24
-  provisionerId: aws-provisioner-v1
-  workerType: nss-win2012r2
-  schedulerId: task-graph-scheduler
-
-  metadata:
-    owner: !env TC_OWNER
-    source: !env TC_SOURCE
-
-  payload:
-    maxRunTime: 3600
-
-    command:
-      - "hg clone -r %NSS_HEAD_REVISION% %NSS_HEAD_REPOSITORY% nss"
-      - "bash -c nss/automation/taskcluster/windows/run_tests.sh"
-
-  extra:
-    treeherder:
-      jobKind: test
deleted file mode 100644
--- a/security/nss/automation/taskcluster/graph/windows/build64-debug.yml
+++ /dev/null
@@ -1,55 +0,0 @@
----
-- task:
-    metadata:
-      name: "Windows 2012 64 (debug)"
-      description: "Windows 2012 64 (debug)"
-
-    payload:
-      env:
-        USE_64: 1
-
-    extra:
-      treeherder:
-        build:
-          platform: windows2012-64
-        machine:
-          platform: windows2012-64
-        collection:
-          debug: true
-
-  tests:
-    - chains
-    - cipher
-    - crmf
-    - db
-    - ec
-    - fips
-    - gtests
-    - lowhash
-    - merge
-    - sdr
-    - smime
-    - ssl
-    - tools
-
-- task:
-    metadata:
-      name: "Windows 2012 64 (debug, NSS_NO_PKCS11_BYPASS=1)"
-      description: "Windows 2012 64 (debug, NSS_NO_PKCS11_BYPASS=1)"
-
-    payload:
-      env:
-        NSS_NO_PKCS11_BYPASS: 1
-        USE_64: 1
-
-    extra:
-      treeherder:
-        build:
-          platform: windows2012-64
-        machine:
-          platform: windows2012-64
-        collection:
-          debug: true
-        groupSymbol: Builds
-        groupName: Various builds
-        symbol: noPkcs11Bypass
deleted file mode 100644
--- a/security/nss/automation/taskcluster/graph/windows/build64-opt.yml
+++ /dev/null
@@ -1,57 +0,0 @@
----
-- task:
-    metadata:
-      name: "Windows 2012 64 (opt)"
-      description: "Windows 2012 64 (opt)"
-
-    payload:
-      env:
-        BUILD_OPT: 1
-        USE_64: 1
-
-    extra:
-      treeherder:
-        build:
-          platform: windows2012-64
-        machine:
-          platform: windows2012-64
-        collection:
-          opt: true
-
-  tests:
-    - chains
-    - cipher
-    - crmf
-    - db
-    - ec
-    - fips
-    - gtests
-    - lowhash
-    - merge
-    - sdr
-    - smime
-    - ssl
-    - tools
-
-- task:
-    metadata:
-      name: "Windows 2012 64 (opt, NSS_NO_PKCS11_BYPASS=1)"
-      description: "Windows 2012 64 (opt, NSS_NO_PKCS11_BYPASS=1)"
-
-    payload:
-      env:
-        NSS_NO_PKCS11_BYPASS: 1
-        BUILD_OPT: 1
-        USE_64: 1
-
-    extra:
-      treeherder:
-        build:
-          platform: windows2012-64
-        machine:
-          platform: windows2012-64
-        collection:
-          opt: true
-        groupSymbol: Builds
-        groupName: Various builds
-        symbol: noPkcs11Bypass
deleted file mode 100644
--- a/security/nss/automation/taskcluster/graph/yaml.js
+++ /dev/null
@@ -1,51 +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/. */
-
-var fs = require("fs");
-var yaml = require("js-yaml");
-
-// Register custom YAML types.
-var YAML_SCHEMA = yaml.Schema.create([
-  // Point in time at $now + x hours.
-  new yaml.Type('!from_now', {
-    kind: "scalar",
-
-    resolve: function (data) {
-      return true;
-    },
-
-    construct: function (data) {
-      var d = new Date();
-      d.setHours(d.getHours() + (data|0));
-      return d.toJSON();
-    }
-  }),
-
-  // Environment variables.
-  new yaml.Type('!env', {
-    kind: "scalar",
-
-    resolve: function (data) {
-      return true;
-    },
-
-    construct: function (data) {
-      return process.env[data] || "{{" + data.toLowerCase() + "}}";
-    }
-  })
-]);
-
-// Parse a given YAML file.
-function parse(file, fallback) {
-  // Return fallback if the file doesn't exist.
-  if (!fs.existsSync(file) && fallback) {
-    return fallback;
-  }
-
-  // Otherwise, read the file or fail.
-  var source = fs.readFileSync(file, "utf-8");
-  return yaml.load(source, {schema: YAML_SCHEMA});
-}
-
-module.exports.parse = parse;
--- a/security/nss/automation/taskcluster/scripts/build.sh
+++ b/security/nss/automation/taskcluster/scripts/build.sh
@@ -13,17 +13,11 @@ if [[ $(id -u) -eq 0 ]]; then
 fi
 
 # Clone NSPR if needed.
 hg_clone https://hg.mozilla.org/projects/nspr nspr default
 
 # Build.
 make -C nss nss_build_all
 
-# Generate certificates.
-NSS_TESTS=cert NSS_CYCLES="standard pkix sharedb" $(dirname $0)/run_tests.sh
-
-# Reset test counter so that test runs pick up our certificates.
-echo 1 > tests_results/security/localhost
-
 # Package.
 mkdir artifacts
-tar cvfjh artifacts/dist.tar.bz2 dist tests_results
+tar cvfjh artifacts/dist.tar.bz2 dist
--- a/security/nss/automation/taskcluster/scripts/extend_task_graph.sh
+++ b/security/nss/automation/taskcluster/scripts/extend_task_graph.sh
@@ -7,10 +7,10 @@ if [ $(id -u) = 0 ]; then
     exec su worker $0
 fi
 
 mkdir -p /home/worker/artifacts
 
 # Install Node.JS dependencies.
 cd nss/automation/taskcluster/graph/ && npm install
 
-# Build the task graph definition.
-nodejs build.js > /home/worker/artifacts/graph.json
+# Extend the task graph.
+node lib/index.js
new file mode 100755
--- /dev/null
+++ b/security/nss/automation/taskcluster/scripts/gen_certs.sh
@@ -0,0 +1,29 @@
+#!/usr/bin/env bash
+
+set -v -e -x
+
+source $(dirname $0)/tools.sh
+
+if [ $(id -u) = 0 ]; then
+    # Set compiler.
+    switch_compilers
+
+    # Stupid Docker.
+    echo "127.0.0.1 localhost.localdomain" >> /etc/hosts
+
+    # Drop privileges by re-running this script.
+    exec su worker $0
+fi
+
+# Fetch artifact if needed.
+fetch_dist
+
+# Generate certificates.
+NSS_TESTS=cert NSS_CYCLES="standard pkix sharedb" $(dirname $0)/run_tests.sh
+
+# Reset test counter so that test runs pick up our certificates.
+echo 1 > tests_results/security/localhost
+
+# Package.
+mkdir artifacts
+tar cvfjh artifacts/dist.tar.bz2 dist tests_results
--- a/security/nss/automation/taskcluster/scripts/run_clang_format.sh
+++ b/security/nss/automation/taskcluster/scripts/run_clang_format.sh
@@ -2,28 +2,22 @@
 
 set -v -e -x
 
 if [ $(id -u) -eq 0 ]; then
     # Drop privileges by re-running this script.
     exec su worker $0 "$@"
 fi
 
-# Apply clang-format 3.8 on the provided folder and verify that this doesn't change any file.
+# Apply clang-format on the provided folder and verify that this doesn't change any file.
 # If any file differs after formatting, the script eventually exits with 1.
 # Any differences between formatted and unformatted files is printed to stdout to give a hint what's wrong.
 
 # Includes a default set of directories.
 
-apply=false
-if [ $1 = "--apply" ]; then
-    apply=true
-    shift
-fi
-
 if [ $# -gt 0 ]; then
     dirs=("$@")
 else
     top=$(dirname $0)/../../..
     dirs=( \
          "$top/cmd" \
          "$top/lib/base" \
          "$top/lib/certdb" \
@@ -42,20 +36,20 @@ else
          "$top/external_tests/common" \
          "$top/external_tests/der_gtest" \
          "$top/external_tests/pk11_gtest" \
          "$top/external_tests/ssl_gtest" \
          "$top/external_tests/util_gtest" \
     )
 fi
 
-STATUS=0
 for dir in "${dirs[@]}"; do
-    for i in $(find "$dir" -type f \( -name '*.[ch]' -o -name '*.cc' \) -print); do
-        if $apply; then
-            clang-format -i "$i"
-        elif ! clang-format "$i" | diff -Naur "$i" -; then
-            echo "Sorry, $i is not formatted properly. Please use clang-format 3.8 on your patch before landing."
-            STATUS=1
-        fi
-    done
+    find "$dir" -type f \( -name '*.[ch]' -o -name '*.cc' \) -exec clang-format -i {} \+
 done
-exit $STATUS
+
+TMPFILE=$(mktemp /tmp/$(basename $0).XXXXXX)
+trap 'rm $TMPFILE' exit
+if (cd $(dirname $0); hg root >/dev/null 2>&1); then
+    hg diff --git "$top" | tee $TMPFILE
+else
+    git -C "$top" diff | tee $TMPFILE
+fi
+[[ ! -s $TMPFILE ]]
--- a/security/nss/automation/taskcluster/scripts/run_tests.sh
+++ b/security/nss/automation/taskcluster/scripts/run_tests.sh
@@ -1,25 +1,22 @@
 #!/usr/bin/env bash
 
 set -v -e -x
 
+source $(dirname $0)/tools.sh
+
 if [ $(id -u) = 0 ]; then
-    source $(dirname $0)/tools.sh
-
     # Set compiler.
     switch_compilers
 
     # Stupid Docker.
     echo "127.0.0.1 localhost.localdomain" >> /etc/hosts
 
     # Drop privileges by re-running this script.
     exec su worker $0
 fi
 
 # Fetch artifact if needed.
-if [ ! -d "dist" ]; then
-    curl --retry 3 -Lo dist.tar.bz2 https://queue.taskcluster.net/v1/task/$TC_PARENT_TASK_ID/artifacts/public/dist.tar.bz2
-    tar xvjf dist.tar.bz2
-fi
+fetch_dist
 
 # Run tests.
 cd nss/tests && ./all.sh
--- a/security/nss/automation/taskcluster/scripts/tools.sh
+++ b/security/nss/automation/taskcluster/scripts/tools.sh
@@ -22,8 +22,20 @@ hg_clone() {
     rev=${3:-@}
     for i in 0 2 5; do
         sleep $i
         hg clone -r "$rev" "$repo" "$dir" && return
         rm -rf "$dir"
     done
     exit 1
 }
+
+fetch_dist() {
+    url=https://queue.taskcluster.net/v1/task/$TC_PARENT_TASK_ID/artifacts/public/dist.tar.bz2
+    if [ ! -d "dist" ]; then
+        for i in 0 2 5; do
+            sleep $i
+            curl --retry 3 -Lo dist.tar.bz2 $url && tar xvjf dist.tar.bz2 && return
+            rm -fr dist.tar.bz2 dist
+        done
+        exit 1
+    fi
+}
--- a/security/nss/automation/taskcluster/windows/build.sh
+++ b/security/nss/automation/taskcluster/windows/build.sh
@@ -6,16 +6,10 @@ set -v -e -x
 source $(dirname $0)/setup.sh
 
 # Clone NSPR.
 hg_clone https://hg.mozilla.org/projects/nspr nspr default
 
 # Build.
 make -C nss nss_build_all
 
-# Generate certificates.
-NSS_TESTS=cert NSS_CYCLES="standard pkix sharedb" nss/tests/all.sh
-
-# Reset test counter so that test runs pick up our certificates.
-echo 1 > tests_results/security/localhost
-
 # Package.
-7z a public/build/dist.7z dist tests_results
+7z a public/build/dist.7z dist
new file mode 100644
--- /dev/null
+++ b/security/nss/automation/taskcluster/windows/gen_certs.sh
@@ -0,0 +1,19 @@
+#!/usr/bin/env bash
+
+set -v -e -x
+
+# Set up the toolchain.
+source $(dirname $0)/setup.sh
+
+# Fetch artifact.
+wget -t 3 --retry-connrefused -w 5 --random-wait https://queue.taskcluster.net/v1/task/$TC_PARENT_TASK_ID/artifacts/public/build/dist.7z -O dist.7z
+7z x dist.7z
+
+# Generate certificates.
+NSS_TESTS=cert NSS_CYCLES="standard pkix sharedb" nss/tests/all.sh
+
+# Reset test counter so that test runs pick up our certificates.
+echo 1 > tests_results/security/localhost
+
+# Package.
+7z a public/build/dist.7z dist tests_results
--- a/security/nss/automation/taskcluster/windows/releng.manifest
+++ b/security/nss/automation/taskcluster/windows/releng.manifest
@@ -1,10 +1,10 @@
 [
   {
-    "version": "Visual Studio 2015 Update 3 14.0.25425.01 / SDK 10.0.14393.0",
-    "size": 326656969,
-    "digest": "babc414ffc0457d27f5a1ed24a8e4873afbe2f1c1a4075469a27c005e1babc3b2a788f643f825efedff95b79686664c67ec4340ed535487168a3482e68559bc7",
+    "version": "Visual Studio 2015 Update 2 / SDK 10.0.10586.0/212",
+    "size": 332442800,
+    "digest": "995394a4a515c7cb0f8595f26f5395361a638870dd0bbfcc22193fe1d98a0c47126057d5999cc494f3f3eac5cb49160e79757c468f83ee5797298e286ef6252c",
     "algorithm": "sha512",
-    "filename": "vs2015u3.zip",
+    "filename": "vs2015u2.zip",
     "unpack": true
   }
 ]
--- a/security/nss/automation/taskcluster/windows/setup.sh
+++ b/security/nss/automation/taskcluster/windows/setup.sh
@@ -13,18 +13,18 @@ hg_clone() {
         rm -rf "$dir"
     done
     exit 1
 }
 
 hg_clone https://hg.mozilla.org/build/tools tools default
 
 tools/scripts/tooltool/tooltool_wrapper.sh $(dirname $0)/releng.manifest https://api.pub.build.mozilla.org/tooltool/ non-existant-file.sh /c/mozilla-build/python/python.exe /c/builds/tooltool.py --authentication-file /c/builds/relengapi.tok -c /c/builds/tooltool_cache
-VSPATH="$(pwd)/vs2015u3"
+VSPATH="$(pwd)/vs2015u2"
 
 export WINDOWSSDKDIR="${VSPATH}/SDK"
 export WIN32_REDIST_DIR="${VSPATH}/VC/redist/x64/Microsoft.VC140.CRT"
 export WIN_UCRT_REDIST_DIR="${VSPATH}/SDK/Redist/ucrt/DLLs/x64"
 
 export PATH="${VSPATH}/VC/bin/amd64:${VSPATH}/VC/bin:${VSPATH}/SDK/bin/x64:${VSPATH}/VC/redist/x64/Microsoft.VC140.CRT:${VSPATH}/SDK/Redist/ucrt/DLLs/x64:${PATH}"
 
-export INCLUDE="${VSPATH}/VC/include:${VSPATH}/SDK/Include/10.0.14393.0/ucrt:${VSPATH}/SDK/Include/10.0.14393.0/shared:${VSPATH}/SDK/Include/10.0.14393.0/um"
-export LIB="${VSPATH}/VC/lib/amd64:${VSPATH}/SDK/lib/10.0.14393.0/ucrt/x64:${VSPATH}/SDK/lib/10.0.14393.0/um/x64"
+export INCLUDE="${VSPATH}/VC/include:${VSPATH}/SDK/Include/10.0.10586.0/ucrt:${VSPATH}/SDK/Include/10.0.10586.0/shared:${VSPATH}/SDK/Include/10.0.10586.0/um"
+export LIB="${VSPATH}/VC/lib/amd64:${VSPATH}/SDK/lib/10.0.10586.0/ucrt/x64:${VSPATH}/SDK/lib/10.0.10586.0/um/x64"
--- a/security/nss/cmd/lib/basicutil.c
+++ b/security/nss/cmd/lib/basicutil.c
@@ -682,22 +682,17 @@ SECU_SECItemToHex(const SECItem *item, c
         *dst = '\0';
     }
 }
 
 static unsigned char
 nibble(char c)
 {
     c = PORT_Tolower(c);
-    return (c >= '0' && c <= '9') ? c - '0' : (c >=
-                                                   'a' &&
-                                               c <=
-                                                   'f')
-                                                  ? c - 'a' + 10
-                                                  : -1;
+    return (c >= '0' && c <= '9') ? c - '0' : (c >= 'a' && c <= 'f') ? c - 'a' + 10 : -1;
 }
 
 SECStatus
 SECU_SECItemHexStringToBinary(SECItem *srcdest)
 {
     unsigned int i;
 
     if (!srcdest) {
--- a/security/nss/cmd/modutil/install.c
+++ b/security/nss/cmd/modutil/install.c
@@ -262,17 +262,17 @@ Pk11Install_Release()
 #include <stdarg.h>
 #endif
 
 #ifdef OSF1
 static void
 error(long va_alist, ...)
 #else
 static void
-error(Pk11Install_Error errcode, ...)
+error(PRErrorCode errcode, ...)
 #endif
 {
 
     va_list ap;
     char *errstr;
     Pk11Install_ErrorHandler handler;
 
     if (!errorHandlerLock) {
--- 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/doc/certutil.xml
+++ b/security/nss/doc/certutil.xml
@@ -376,28 +376,25 @@ of the attribute codes:
 		<command>C</command> - Trusted CA (implies c)
 	</para>
 	</listitem>
 	<listitem>
 	<para>
 		<command>T</command> - trusted CA for client authentication (ssl server only)
 	</para>
 	</listitem>
-	<listitem>
-	<para>
-		<command>u</command> - user
-	</para>
-	</listitem>
 	</itemizedlist>
 	<para>
 		The attribute codes for the categories are separated by commas, and the entire set of attributes enclosed by quotation marks. For example:
 	</para>
-<para><command>-t "TCu,Cu,Tu"</command></para>
+<para><command>-t "TC,C,T"</command></para>
 	<para>
-	Use the -L option to see a list of the current certificates and trust attributes in a certificate database. </para></listitem>
+	Use the -L option to see a list of the current certificates and trust attributes in a certificate database. </para>
+	<para>
+	Note that the output of the -L option may include "u" flag, which means that there is a private key associated with the certificate. It is a dynamic flag and you cannot set it with certutil. </para></listitem>
       </varlistentry>
 
       <varlistentry>
         <term>-u certusage</term>
         <listitem><para>Specify a usage context to apply when validating a certificate with the -V option.</para><para>The contexts are the following:</para>
 	<itemizedlist>
 	<listitem>
 <para><command>C</command> (as an SSL client)</para>
@@ -855,17 +852,17 @@ Generating key.  This may take a few mom
 	</para>
 <programlisting>$ certutil -S -s "CN=Example CA" -n my-ca-cert -x -t "C,C,C" -1 -2 -5 -m 3650</programlisting>
 	<para>
 The interative prompts for key usage and whether any extensions are critical and responses have been ommitted for brevity.
 	</para>
 	<para>
 		From there, new certificates can reference the self-signed certificate:
 	</para>
-<programlisting>$ certutil -S -s "CN=My Server Cert" -n my-server-cert -c "my-ca-cert" -t "u,u,u" -1 -5 -6 -8 -m 730</programlisting>
+<programlisting>$ certutil -S -s "CN=My Server Cert" -n my-server-cert -c "my-ca-cert" -t ",," -1 -5 -6 -8 -m 730</programlisting>
 
 	<para><command>Generating a Certificate from a Certificate Request</command></para>
 	<para>
 		When a certificate request is created, a certificate can be generated by using the request and then referencing a certificate authority signing certificate (the <emphasis>issuer</emphasis> specified in the <option>-c</option> argument). The issuing certificate must be in the certificate database in the specified directory.
 	</para>
 <programlisting>certutil -C -c issuer -i cert-request-file -o output-file [-m serial-number] [-v valid-months] [-w offset-months] -d [sql:]directory [-1] [-2] [-3] [-4] [-5 keyword] [-6 keyword] [-7 emailAddress] [-8 dns-names]</programlisting>
 	<para>
 		For example:
@@ -1018,21 +1015,21 @@ certutil: Checking token "NSS Certificat
 	<para><command>Adding Certificates to the Database</command></para>
 	<para>
 		Existing certificates or certificate requests can be added manually to the certificate database, even if they were generated elsewhere. This uses the <option>-A</option> command option.
 	</para>
 <programlisting>certutil -A -n certname -t trustargs -d [sql:]directory [-a] [-i input-file]</programlisting>
 	<para>
 		For example:
 	</para>
-<programlisting>$ certutil -A -n "CN=My SSL Certificate" -t "u,u,u" -d sql:/home/my/sharednssdb -i /home/example-certs/cert.cer</programlisting>
+<programlisting>$ certutil -A -n "CN=My SSL Certificate" -t ",," -d sql:/home/my/sharednssdb -i /home/example-certs/cert.cer</programlisting>
 	<para>
 		A related command option, <option>-E</option>, is used specifically to add email certificates to the certificate database. The <option>-E</option> command has the same arguments as the <option>-A</option> command. The trust arguments for certificates have the format <emphasis>SSL,S/MIME,Code-signing</emphasis>, so the middle trust settings relate most to email certificates (though the others can be set). For example:
 	</para>
-<programlisting>$ certutil -E -n "CN=John Smith Email Cert" -t ",Pu," -d sql:/home/my/sharednssdb -i /home/example-certs/email.cer</programlisting>
+<programlisting>$ certutil -E -n "CN=John Smith Email Cert" -t ",P," -d sql:/home/my/sharednssdb -i /home/example-certs/email.cer</programlisting>
 
 	<para><command>Deleting Certificates to the Database</command></para>
 	<para>
 		Certificates can be deleted from a database using the <option>-D</option> option. The only required options are to give the security database directory and to identify the certificate nickname.
 	</para>
 <programlisting>certutil -D -d [sql:]directory -n "nickname"</programlisting>
 	<para>
 		For example:
@@ -1052,17 +1049,17 @@ certutil: Checking token "NSS Certificat
 	<para><command>Modifying Certificate Trust Settings</command></para>
 	<para>
 		The trust settings (which relate to the operations that a certificate is allowed to be used for) can be changed after a certificate is created or added to the database. This is especially useful for CA certificates, but it can be performed for any type of certificate.
 	</para>
 <programlisting>certutil -M -n certificate-name -t trust-args -d [sql:]directory</programlisting>
 	<para>
 		For example:
 	</para>
-<programlisting>$ certutil -M -n "My CA Certificate" -d sql:/home/my/sharednssdb -t "CTu,CTu,CTu"</programlisting>
+<programlisting>$ certutil -M -n "My CA Certificate" -d sql:/home/my/sharednssdb -t "CT,CT,CT"</programlisting>
 
 	<para><command>Printing the Certificate Chain</command></para>
 	<para>
 		Certificates can be issued in <emphasis>chains</emphasis> because every certificate authority itself has a certificate; when a CA issues a certificate, it essentially stamps that certificate with its own fingerprint. The <option>-O</option> prints the full chain of a certificate, going from the initial CA (the root CA) through ever intermediary CA to the actual certificate. For example, for an email certificate with two CAs in the chain:
 	</para>
 <programlisting>$ certutil -d sql:/home/my/sharednssdb -O -n "jsmith@example.com"
 "Builtin Object Token:Thawte Personal Freemail CA" [E=personal-freemail@thawte.com,CN=Thawte Personal Freemail CA,OU=Certification Services Division,O=Thawte Consulting,L=Cape Town,ST=Western Cape,C=ZA]
 
--- a/security/nss/doc/html/certutil.html
+++ b/security/nss/doc/html/certutil.html
@@ -1,9 +1,9 @@
-<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><title>CERTUTIL</title><meta name="generator" content="DocBook XSL Stylesheets V1.78.1"><link rel="home" href="index.html" title="CERTUTIL"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">CERTUTIL</th></tr></table><hr></div><div class="refentry"><a name="certutil"></a><div class="titlepage"></div><div class="refnamediv"><h2>Name</h2><p>certutil — Manage keys and certificate in both NSS databases and other NSS tokens</p></div><div class="refsynopsisdiv"><h2>Synopsis</h2><div class="cmdsynopsis"><p><code class="command">certutil</code>  [<em class="replaceable"><code>options</code></em>] [[<em class="replaceable"><code>arguments</code></em>]]</p></div></div><div class="refsection"><a name="idm139861295402064"></a><h2>STATUS</h2><p>This documentation is still work in progress. Please contribute to the initial review in <a class="ulink" href="https://bugzilla.mozilla.org/show_bug.cgi?id=836477" target="_top">Mozilla NSS bug 836477</a>
+<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><title>CERTUTIL</title><meta name="generator" content="DocBook XSL Stylesheets V1.78.1"><link rel="home" href="index.html" title="CERTUTIL"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">CERTUTIL</th></tr></table><hr></div><div class="refentry"><a name="certutil"></a><div class="titlepage"></div><div class="refnamediv"><h2>Name</h2><p>certutil — Manage keys and certificate in both NSS databases and other NSS tokens</p></div><div class="refsynopsisdiv"><h2>Synopsis</h2><div class="cmdsynopsis"><p><code class="command">certutil</code>  [<em class="replaceable"><code>options</code></em>] [[<em class="replaceable"><code>arguments</code></em>]]</p></div></div><div class="refsection"><a name="idm139774553663312"></a><h2>STATUS</h2><p>This documentation is still work in progress. Please contribute to the initial review in <a class="ulink" href="https://bugzilla.mozilla.org/show_bug.cgi?id=836477" target="_top">Mozilla NSS bug 836477</a>
     </p></div><div class="refsection"><a name="description"></a><h2>Description</h2><p>The Certificate Database Tool, <span class="command"><strong>certutil</strong></span>, is a command-line utility that can create and modify certificate and key databases. It can specifically list, generate, modify, or delete certificates, create or change the password, generate new public and private key pairs, display the contents of the key database, or delete key pairs within the key database.</p><p>Certificate issuance, part of the key and certificate management process, requires that keys and certificates be created in the key database. This document discusses certificate and key database management. For information on the security module database management, see the <span class="command"><strong>modutil</strong></span> manpage.</p></div><div class="refsection"><a name="options"></a><h2>Command Options and Arguments</h2><p>Running <span class="command"><strong>certutil</strong></span> always requires one and only one command option to specify the type of certificate operation. Each command option may take zero or more arguments. The command option <code class="option">-H</code> will list all the command options and their relevant arguments.</p><p><span class="command"><strong>Command Options</strong></span></p><div class="variablelist"><dl class="variablelist"><dt><span class="term">-A </span></dt><dd><p>Add an existing certificate to a certificate database. The certificate database should already exist; if one is not present, this command option will initialize one by default.</p></dd><dt><span class="term">-B</span></dt><dd><p>Run a series of commands from the specified batch file. This requires the <code class="option">-i</code> argument.</p></dd><dt><span class="term">-C </span></dt><dd><p>Create a new binary certificate file from a binary certificate request file. Use the <code class="option">-i</code> argument to specify the certificate request file. If this argument is not used, <span class="command"><strong>certutil</strong></span> prompts for a filename. </p></dd><dt><span class="term">-D </span></dt><dd><p>Delete a certificate from the certificate database.</p></dd><dt><span class="term">--rename </span></dt><dd><p>Change the database nickname of a certificate.</p></dd><dt><span class="term">-E </span></dt><dd><p>Add an email certificate to the certificate database.</p></dd><dt><span class="term">-F</span></dt><dd><p>Delete a private key from a key database. Specify the key to delete with the -n argument. Specify the database from which to delete the key with the 
 <code class="option">-d</code> argument. Use the <code class="option">-k</code> argument to specify explicitly whether to delete a DSA, RSA, or ECC key. If you don't use the <code class="option">-k</code> argument, the option looks for an RSA key matching the specified nickname. 
 </p><p>
 When you delete keys, be sure to also remove any certificates associated with those keys from the certificate database, by using -D. Some smart cards do not let you remove a public key you have generated. In such a case, only the private key is deleted from the key pair. You can display the public key with the command certutil -K -h tokenname. </p></dd><dt><span class="term">-G </span></dt><dd><p>Generate a new public and private key pair within a key database. The key database should already exist; if one is not present, this command option will initialize one by default. Some smart cards can store only one key pair. If you create a new key pair for such a card, the previous pair is overwritten.</p></dd><dt><span class="term">-H </span></dt><dd><p>Display a list of the command options and arguments.</p></dd><dt><span class="term">-K </span></dt><dd><p>List the key ID of keys in the key database. A key ID is the modulus of the RSA key or the publicValue of the DSA key. IDs are displayed in hexadecimal ("0x" is not shown).</p></dd><dt><span class="term">-L </span></dt><dd><p>List all the certificates, or display information about a named certificate, in a certificate database.
 Use the -h tokenname argument to specify the certificate database on a particular hardware or software token.</p></dd><dt><span class="term">-M </span></dt><dd><p>Modify a certificate's trust attributes using the values of the -t argument.</p></dd><dt><span class="term">-N</span></dt><dd><p>Create new certificate and key databases.</p></dd><dt><span class="term">-O </span></dt><dd><p>Print the certificate chain.</p></dd><dt><span class="term">-R</span></dt><dd><p>Create a certificate request file that can be submitted to a Certificate Authority (CA) for processing into a finished certificate. Output defaults to standard out unless you use -o output-file argument.
 
 Use the -a argument to specify ASCII output.</p></dd><dt><span class="term">-S </span></dt><dd><p>Create an individual certificate and add it to a certificate database.</p></dd><dt><span class="term">-T </span></dt><dd><p>Reset the key database or token.</p></dd><dt><span class="term">-U </span></dt><dd><p>List all available modules or print a single named module.</p></dd><dt><span class="term">-V </span></dt><dd><p>Check the validity of a certificate and its attributes.</p></dd><dt><span class="term">-W </span></dt><dd><p>Change the password to a key database.</p></dd><dt><span class="term">--merge</span></dt><dd><p>Merge two databases into one.</p></dd><dt><span class="term">--upgrade-merge</span></dt><dd><p>Upgrade an old database and merge it into a new database. This is used to migrate legacy NSS databases (<code class="filename">cert8.db</code> and <code class="filename">key3.db</code>) into the newer SQLite databases (<code class="filename">cert9.db</code> and <code class="filename">key4.db</code>).</p></dd></dl></div><p><span class="command"><strong>Arguments</strong></span></p><p>Arguments modify a command option and are usually lower case, numbers, or symbols.</p><div class="variablelist"><dl class="variablelist"><dt><span class="term">-a</span></dt><dd><p>Use ASCII format or allow the use of ASCII format for input or output. This formatting follows RFC 1113. 
 For certificate requests, ASCII output defaults to standard output unless redirected.</p></dd><dt><span class="term">-b validity-time</span></dt><dd><p>Specify a time at which a certificate is required to be valid. Use when checking certificate validity with the <code class="option">-V</code> option. The format of the <span class="emphasis"><em>validity-time</em></span> argument is <span class="emphasis"><em>YYMMDDHHMMSS[+HHMM|-HHMM|Z]</em></span>, which allows offsets to be set relative to the validity end time. Specifying seconds (<span class="emphasis"><em>SS</em></span>) is optional. When specifying an explicit time, use a Z at the end of the term, <span class="emphasis"><em>YYMMDDHHMMSSZ</em></span>, to close it. When specifying an offset time, use <span class="emphasis"><em>YYMMDDHHMMSS+HHMM</em></span> or <span class="emphasis"><em>YYMMDDHHMMSS-HHMM</em></span> for adding or subtracting time, respectively.
@@ -46,22 +46,21 @@ of the attribute codes:
 	</p></li><li class="listitem"><p>
 		<span class="command"><strong>P</strong></span> - Trusted peer (implies p)
 	</p></li><li class="listitem"><p>
 		<span class="command"><strong>c</strong></span> - Valid CA
 	</p></li><li class="listitem"><p>
 		<span class="command"><strong>C</strong></span> - Trusted CA (implies c)
 	</p></li><li class="listitem"><p>
 		<span class="command"><strong>T</strong></span> - trusted CA for client authentication (ssl server only)
-	</p></li><li class="listitem"><p>
-		<span class="command"><strong>u</strong></span> - user
 	</p></li></ul></div><p>
 		The attribute codes for the categories are separated by commas, and the entire set of attributes enclosed by quotation marks. For example:
-	</p><p><span class="command"><strong>-t "TCu,Cu,Tu"</strong></span></p><p>
-	Use the -L option to see a list of the current certificates and trust attributes in a certificate database. </p></dd><dt><span class="term">-u certusage</span></dt><dd><p>Specify a usage context to apply when validating a certificate with the -V option.</p><p>The contexts are the following:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><p><span class="command"><strong>C</strong></span> (as an SSL client)</p></li><li class="listitem"><p><span class="command"><strong>V</strong></span> (as an SSL server)</p></li><li class="listitem"><p><span class="command"><strong>L</strong></span> (as an SSL CA)</p></li><li class="listitem"><p><span class="command"><strong>A</strong></span> (as Any CA)</p></li><li class="listitem"><p><span class="command"><strong>Y</strong></span> (Verify CA)</p></li><li class="listitem"><p><span class="command"><strong>S</strong></span> (as an email signer)</p></li><li class="listitem"><p><span class="command"><strong>R</strong></span> (as an email recipient)</p></li><li class="listitem"><p><span class="command"><strong>O</strong></span> (as an OCSP status responder)</p></li><li class="listitem"><p><span class="command"><strong>J</strong></span> (as an object signer)</p></li></ul></div></dd><dt><span class="term">-v valid-months</span></dt><dd><p>Set the number of months a new certificate will be valid. The validity period begins at the current system time unless an offset is added or subtracted with the <code class="option">-w</code> option. If this argument is not used, the default validity period is three months. </p></dd><dt><span class="term">-w offset-months</span></dt><dd><p>Set an offset from the current system time, in months, 
+	</p><p><span class="command"><strong>-t "TC,C,T"</strong></span></p><p>
+	Use the -L option to see a list of the current certificates and trust attributes in a certificate database. </p><p>
+	Note that the output of the -L option may include "u" flag, which means that there is a private key associated with the certificate. It is a dynamic flag and you cannot set it with certutil. </p></dd><dt><span class="term">-u certusage</span></dt><dd><p>Specify a usage context to apply when validating a certificate with the -V option.</p><p>The contexts are the following:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><p><span class="command"><strong>C</strong></span> (as an SSL client)</p></li><li class="listitem"><p><span class="command"><strong>V</strong></span> (as an SSL server)</p></li><li class="listitem"><p><span class="command"><strong>L</strong></span> (as an SSL CA)</p></li><li class="listitem"><p><span class="command"><strong>A</strong></span> (as Any CA)</p></li><li class="listitem"><p><span class="command"><strong>Y</strong></span> (Verify CA)</p></li><li class="listitem"><p><span class="command"><strong>S</strong></span> (as an email signer)</p></li><li class="listitem"><p><span class="command"><strong>R</strong></span> (as an email recipient)</p></li><li class="listitem"><p><span class="command"><strong>O</strong></span> (as an OCSP status responder)</p></li><li class="listitem"><p><span class="command"><strong>J</strong></span> (as an object signer)</p></li></ul></div></dd><dt><span class="term">-v valid-months</span></dt><dd><p>Set the number of months a new certificate will be valid. The validity period begins at the current system time unless an offset is added or subtracted with the <code class="option">-w</code> option. If this argument is not used, the default validity period is three months. </p></dd><dt><span class="term">-w offset-months</span></dt><dd><p>Set an offset from the current system time, in months, 
  for the beginning of a certificate's validity period. Use when creating 
  the certificate or adding it to a database. Express the offset in integers, 
  using a minus sign (-) to indicate a negative offset. If this argument is 
  not used, the validity period begins at the current system time. The length 
  of the validity period is set with the -v argument. </p></dd><dt><span class="term">-X </span></dt><dd><p>Force the key and certificate database to open in read-write mode. This is used with the <code class="option">-U</code> and <code class="option">-L</code> command options.</p></dd><dt><span class="term">-x </span></dt><dd><p>Use <span class="command"><strong>certutil</strong></span> to generate the signature for a certificate being created or added to a database, rather than obtaining a signature from a separate CA.</p></dd><dt><span class="term">-y exp</span></dt><dd><p>Set an alternate exponent value to use in generating a new RSA public key for the database, instead of the default value of 65537. The available alternate values are 3 and 17.</p></dd><dt><span class="term">-z noise-file</span></dt><dd><p>Read a seed value from the specified file to generate a new private and public key pair. This argument makes it possible to use hardware-generated seed values or manually create a value from the keyboard. The minimum file size is 20 bytes.</p></dd><dt><span class="term">-Z hashAlg</span></dt><dd><p>Specify the hash algorithm to use with the -C, -S or -R command options. Possible keywords:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><p>MD2</p></li><li class="listitem"><p>MD4</p></li><li class="listitem"><p>MD5</p></li><li class="listitem"><p>SHA1</p></li><li class="listitem"><p>SHA224</p></li><li class="listitem"><p>SHA256</p></li><li class="listitem"><p>SHA384</p></li><li class="listitem"><p>SHA512</p></li></ul></div></dd><dt><span class="term">-0 SSO_password</span></dt><dd><p>Set a site security officer password on a token.</p></dd><dt><span class="term">-1 | --keyUsage keyword,keyword</span></dt><dd><p>Set an X.509 V3 Certificate Type Extension in the certificate. There are several available keywords:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><p>
 		digitalSignature
 	</p></li><li class="listitem"><p>
 		nonRepudiation
@@ -157,17 +156,17 @@ Generating key.  This may take a few mom
 	</p><pre class="programlisting">$ certutil -S -k rsa|dsa|ec -n certname -s subject [-c issuer |-x] -t trustargs -d [sql:]directory [-m serial-number] [-v valid-months] [-w offset-months] [-p phone] [-1] [-2] [-3] [-4] [-5 keyword] [-6 keyword] [-7 emailAddress] [-8 dns-names] [--extAIA] [--extSIA] [--extCP] [--extPM] [--extPC] [--extIA] [--extSKID]</pre><p>
 		The series of numbers and <code class="option">--ext*</code> options set certificate extensions that can be added to the certificate when it is generated by the CA. Interactive prompts will result.
 	</p><p>
 		For example, this creates a self-signed certificate:
 	</p><pre class="programlisting">$ certutil -S -s "CN=Example CA" -n my-ca-cert -x -t "C,C,C" -1 -2 -5 -m 3650</pre><p>
 The interative prompts for key usage and whether any extensions are critical and responses have been ommitted for brevity.
 	</p><p>
 		From there, new certificates can reference the self-signed certificate:
-	</p><pre class="programlisting">$ certutil -S -s "CN=My Server Cert" -n my-server-cert -c "my-ca-cert" -t "u,u,u" -1 -5 -6 -8 -m 730</pre><p><span class="command"><strong>Generating a Certificate from a Certificate Request</strong></span></p><p>
+	</p><pre class="programlisting">$ certutil -S -s "CN=My Server Cert" -n my-server-cert -c "my-ca-cert" -t ",," -1 -5 -6 -8 -m 730</pre><p><span class="command"><strong>Generating a Certificate from a Certificate Request</strong></span></p><p>
 		When a certificate request is created, a certificate can be generated by using the request and then referencing a certificate authority signing certificate (the <span class="emphasis"><em>issuer</em></span> specified in the <code class="option">-c</code> argument). The issuing certificate must be in the certificate database in the specified directory.
 	</p><pre class="programlisting">certutil -C -c issuer -i cert-request-file -o output-file [-m serial-number] [-v valid-months] [-w offset-months] -d [sql:]directory [-1] [-2] [-3] [-4] [-5 keyword] [-6 keyword] [-7 emailAddress] [-8 dns-names]</pre><p>
 		For example:
 	</p><pre class="programlisting">$ certutil -C -c "my-ca-cert" -i /home/certs/cert.req -o cert.cer -m 010 -v 12 -w 1 -d sql:$HOME/nssdb -1 nonRepudiation,dataEncipherment -5 sslClient -6 clientAuth -7 jsmith@example.com</pre><p><span class="command"><strong>Listing Certificates</strong></span></p><p>
 		The <code class="option">-L</code> command option lists all of the certificates listed in the certificate database. The path to the directory (<code class="option">-d</code>) is required.
 	</p><pre class="programlisting">$ certutil -L -d sql:/home/my/sharednssdb
 
 Certificate Nickname                                         Trust Attributes
@@ -279,31 +278,31 @@ certutil: Checking token "NSS Certificat
     slot: NSS User Private Key and Certificate Services                  
    token: NSS Certificate DB
 
     slot: NSS Internal Cryptographic Services                            
    token: NSS Generic Crypto Services</pre><p><span class="command"><strong>Adding Certificates to the Database</strong></span></p><p>
 		Existing certificates or certificate requests can be added manually to the certificate database, even if they were generated elsewhere. This uses the <code class="option">-A</code> command option.
 	</p><pre class="programlisting">certutil -A -n certname -t trustargs -d [sql:]directory [-a] [-i input-file]</pre><p>
 		For example:
-	</p><pre class="programlisting">$ certutil -A -n "CN=My SSL Certificate" -t "u,u,u" -d sql:/home/my/sharednssdb -i /home/example-certs/cert.cer</pre><p>
+	</p><pre class="programlisting">$ certutil -A -n "CN=My SSL Certificate" -t ",," -d sql:/home/my/sharednssdb -i /home/example-certs/cert.cer</pre><p>
 		A related command option, <code class="option">-E</code>, is used specifically to add email certificates to the certificate database. The <code class="option">-E</code> command has the same arguments as the <code class="option">-A</code> command. The trust arguments for certificates have the format <span class="emphasis"><em>SSL,S/MIME,Code-signing</em></span>, so the middle trust settings relate most to email certificates (though the others can be set). For example:
-	</p><pre class="programlisting">$ certutil -E -n "CN=John Smith Email Cert" -t ",Pu," -d sql:/home/my/sharednssdb -i /home/example-certs/email.cer</pre><p><span class="command"><strong>Deleting Certificates to the Database</strong></span></p><p>
+	</p><pre class="programlisting">$ certutil -E -n "CN=John Smith Email Cert" -t ",P," -d sql:/home/my/sharednssdb -i /home/example-certs/email.cer</pre><p><span class="command"><strong>Deleting Certificates to the Database</strong></span></p><p>
 		Certificates can be deleted from a database using the <code class="option">-D</code> option. The only required options are to give the security database directory and to identify the certificate nickname.
 	</p><pre class="programlisting">certutil -D -d [sql:]directory -n "nickname"</pre><p>
 		For example:
 	</p><pre class="programlisting">$ certutil -D -d sql:/home/my/sharednssdb -n "my-ssl-cert"</pre><p><span class="command"><strong>Validating Certificates</strong></span></p><p>
 		A certificate contains an expiration date in itself, and expired certificates are easily rejected. However, certificates can also be revoked before they hit their expiration date. Checking whether a certificate has been revoked requires validating the certificate. Validation can also be used to ensure that the certificate is only used for the purposes it was initially issued for. Validation is carried out by the <code class="option">-V</code> command option.
 	</p><pre class="programlisting">certutil -V -n certificate-name [-b time] [-e] [-u cert-usage] -d [sql:]directory</pre><p>
 		For example, to validate an email certificate:
 	</p><pre class="programlisting">$ certutil -V -n "John Smith's Email Cert" -e -u S,R -d sql:/home/my/sharednssdb</pre><p><span class="command"><strong>Modifying Certificate Trust Settings</strong></span></p><p>
 		The trust settings (which relate to the operations that a certificate is allowed to be used for) can be changed after a certificate is created or added to the database. This is especially useful for CA certificates, but it can be performed for any type of certificate.
 	</p><pre class="programlisting">certutil -M -n certificate-name -t trust-args -d [sql:]directory</pre><p>
 		For example:
-	</p><pre class="programlisting">$ certutil -M -n "My CA Certificate" -d sql:/home/my/sharednssdb -t "CTu,CTu,CTu"</pre><p><span class="command"><strong>Printing the Certificate Chain</strong></span></p><p>
+	</p><pre class="programlisting">$ certutil -M -n "My CA Certificate" -d sql:/home/my/sharednssdb -t "CT,CT,CT"</pre><p><span class="command"><strong>Printing the Certificate Chain</strong></span></p><p>
 		Certificates can be issued in <span class="emphasis"><em>chains</em></span> because every certificate authority itself has a certificate; when a CA issues a certificate, it essentially stamps that certificate with its own fingerprint. The <code class="option">-O</code> prints the full chain of a certificate, going from the initial CA (the root CA) through ever intermediary CA to the actual certificate. For example, for an email certificate with two CAs in the chain:
 	</p><pre class="programlisting">$ certutil -d sql:/home/my/sharednssdb -O -n "jsmith@example.com"
 "Builtin Object Token:Thawte Personal Freemail CA" [E=personal-freemail@thawte.com,CN=Thawte Personal Freemail CA,OU=Certification Services Division,O=Thawte Consulting,L=Cape Town,ST=Western Cape,C=ZA]
 
   "Thawte Personal Freemail Issuing CA - Thawte Consulting" [CN=Thawte Personal Freemail Issuing CA,O=Thawte Consulting (Pty) Ltd.,C=ZA]
 
     "(null)" [E=jsmith@example.com,CN=Thawte Freemail Member]</pre><p><span class="command"><strong>Resetting a Token</strong></span></p><p>
 		The device which stores certificates -- both external hardware devices and internal software databases -- can be blanked and reused. This operation is performed on the device which stores the data, not directly on the security databases, so the location must be referenced through the token name (<code class="option">-h</code>) as well as any directory path. If there is no external token used, the default value is internal.
--- a/security/nss/doc/nroff/certutil.1
+++ b/security/nss/doc/nroff/certutil.1
@@ -1,18 +1,18 @@
 '\" t
 .\"     Title: CERTUTIL
 .\"    Author: [see the "Authors" section]
 .\" Generator: DocBook XSL Stylesheets v1.78.1 <http://docbook.sf.net/>
-.\"      Date:  7 September 2016
+.\"      Date:  8 September 2016
 .\"    Manual: NSS Security Tools
 .\"    Source: nss-tools
 .\"  Language: English
 .\"
-.TH "CERTUTIL" "1" "7 September 2016" "nss-tools" "NSS Security Tools"
+.TH "CERTUTIL" "1" "8 September 2016" "nss-tools" "NSS Security Tools"
 .\" -----------------------------------------------------------------
 .\" * Define some portability stuff
 .\" -----------------------------------------------------------------
 .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 .\" http://bugs.debian.org/507673
 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
 .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 .ie \n(.g .ds Aq \(aq
@@ -447,33 +447,23 @@ for each trust setting\&. In each catego
 .el \{\
 .sp -1
 .IP \(bu 2.3
 .\}
 \fBT\fR
 \- trusted CA for client authentication (ssl server only)
 .RE
 .sp
-.RS 4
-.ie n \{\
-\h'-04'\(bu\h'+03'\c
-.\}
-.el \{\
-.sp -1
-.IP \(bu 2.3
-.\}
-\fBu\fR
-\- user
-.RE
-.sp
 The attribute codes for the categories are separated by commas, and the entire set of attributes enclosed by quotation marks\&. For example:
 .sp
-\fB\-t "TCu,Cu,Tu"\fR
+\fB\-t "TC,C,T"\fR
 .sp
 Use the \-L option to see a list of the current certificates and trust attributes in a certificate database\&.
+.sp
+Note that the output of the \-L option may include "u" flag, which means that there is a private key associated with the certificate\&. It is a dynamic flag and you cannot set it with certutil\&.
 .RE
 .PP
 \-u certusage
 .RS 4
 Specify a usage context to apply when validating a certificate with the \-V option\&.
 .sp
 The contexts are the following:
 .sp
@@ -1317,17 +1307,17 @@ For example, this creates a self\-signed
 The interative prompts for key usage and whether any extensions are critical and responses have been ommitted for brevity\&.
 .PP
 From there, new certificates can reference the self\-signed certificate:
 .sp
 .if n \{\
 .RS 4
 .\}
 .nf
-$ certutil \-S \-s "CN=My Server Cert" \-n my\-server\-cert \-c "my\-ca\-cert" \-t "u,u,u" \-1 \-5 \-6 \-8 \-m 730
+$ certutil \-S \-s "CN=My Server Cert" \-n my\-server\-cert \-c "my\-ca\-cert" \-t ",," \-1 \-5 \-6 \-8 \-m 730
 .fi
 .if n \{\
 .RE
 .\}
 .PP
 \fBGenerating a Certificate from a Certificate Request\fR
 .PP
 When a certificate request is created, a certificate can be generated by using the request and then referencing a certificate authority signing certificate (the
@@ -1593,17 +1583,17 @@ certutil \-A \-n certname \-t trustargs 
 .\}
 .PP
 For example:
 .sp
 .if n \{\
 .RS 4
 .\}
 .nf
-$ certutil \-A \-n "CN=My SSL Certificate" \-t "u,u,u" \-d sql:/home/my/sharednssdb \-i /home/example\-certs/cert\&.cer
+$ certutil \-A \-n "CN=My SSL Certificate" \-t ",," \-d sql:/home/my/sharednssdb \-i /home/example\-certs/cert\&.cer
 .fi
 .if n \{\
 .RE
 .\}
 .PP
 A related command option,
 \fB\-E\fR, is used specifically to add email certificates to the certificate database\&. The
 \fB\-E\fR
@@ -1611,17 +1601,17 @@ command has the same arguments as the
 \fB\-A\fR
 command\&. The trust arguments for certificates have the format
 \fISSL,S/MIME,Code\-signing\fR, so the middle trust settings relate most to email certificates (though the others can be set)\&. For example:
 .sp
 .if n \{\
 .RS 4
 .\}
 .nf
-$ certutil \-E \-n "CN=John Smith Email Cert" \-t ",Pu," \-d sql:/home/my/sharednssdb \-i /home/example\-certs/email\&.cer
+$ certutil \-E \-n "CN=John Smith Email Cert" \-t ",P," \-d sql:/home/my/sharednssdb \-i /home/example\-certs/email\&.cer
 .fi
 .if n \{\
 .RE
 .\}
 .PP
 \fBDeleting Certificates to the Database\fR
 .PP
 Certificates can be deleted from a database using the
@@ -1693,17 +1683,17 @@ certutil \-M \-n certificate\-name \-t t
 .\}
 .PP
 For example:
 .sp
 .if n \{\
 .RS 4
 .\}
 .nf
-$ certutil \-M \-n "My CA Certificate" \-d sql:/home/my/sharednssdb \-t "CTu,CTu,CTu"
+$ certutil \-M \-n "My CA Certificate" \-d sql:/home/my/sharednssdb \-t "CT,CT,CT"
 .fi
 .if n \{\
 .RE
 .\}
 .PP
 \fBPrinting the Certificate Chain\fR
 .PP
 Certificates can be issued in
--- a/security/nss/external_tests/ssl_gtest/manifest.mn
+++ b/security/nss/external_tests/ssl_gtest/manifest.mn
@@ -19,16 +19,17 @@ 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_extension_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 \
--- a/security/nss/external_tests/ssl_gtest/ssl_0rtt_unittest.cc
+++ b/security/nss/external_tests/ssl_gtest/ssl_0rtt_unittest.cc
@@ -1,73 +1,68 @@
 /* -*- 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 "secerr.h"
 #include "sslerr.h"
 #include "sslproto.h"
 
 extern "C" {
 // This is not something that should make you happy.
 #include "libssl_internals.h"
 }
 
 #include "gtest_utils.h"
 #include "scoped_ptrs.h"
 #include "tls_connect.h"
 #include "tls_filter.h"
 #include "tls_parser.h"
 
 namespace nss_test {
 
-TEST_F(TlsConnectTest, DamageSecretHandleZeroRttClientFinished) {
+TEST_P(TlsConnectTls13, ZeroRtt) {
   SetupForZeroRtt();
   client_->Set0RttEnabled(true);
   server_->Set0RttEnabled(true);
-  client_->SetPacketFilter(new AfterRecordN(
-      client_, server_,
-      0,  // ClientHello.
-      [this]() { SSLInt_DamageEarlyTrafficSecret(server_->ssl_fd()); }));
-  ConnectExpectFail();
-  client_->CheckErrorCode(SSL_ERROR_DECRYPT_ERROR_ALERT);
-  server_->CheckErrorCode(SSL_ERROR_BAD_HANDSHAKE_HASH_VALUE);
+  ExpectResumption(RESUME_TICKET);
+  ZeroRttSendReceive(true);
+  Handshake();
+  ExpectEarlyDataAccepted(true);
+  CheckConnected();
+  SendReceive();
 }
 
-TEST_F(TlsConnectTest, ZeroRttServerRejectByOption) {
+TEST_P(TlsConnectTls13, ZeroRttServerRejectByOption) {
   SetupForZeroRtt();
   client_->Set0RttEnabled(true);
   ExpectResumption(RESUME_TICKET);
   ZeroRttSendReceive(false);
   Handshake();
   CheckConnected();
   SendReceive();
 }
 
-TEST_F(TlsConnectTest, ZeroRttServerForgetTicket) {
+TEST_P(TlsConnectTls13, ZeroRttServerForgetTicket) {
   SetupForZeroRtt();
   client_->Set0RttEnabled(true);
   server_->Set0RttEnabled(true);
   ClearServerCache();
   ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
   ExpectResumption(RESUME_NONE);
   ZeroRttSendReceive(false);
   Handshake();
   CheckConnected();
   SendReceive();
 }
 
-TEST_F(TlsConnectTest, ZeroRttServerOnly) {
-  client_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_1,
-                           SSL_LIBRARY_VERSION_TLS_1_3);
-  server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_1,
-                           SSL_LIBRARY_VERSION_TLS_1_3);
+TEST_P(TlsConnectTls13, ZeroRttServerOnly) {
   ExpectResumption(RESUME_NONE);
   server_->Set0RttEnabled(true);
   client_->StartConnect();
   server_->StartConnect();
 
   // Client sends ordinary ClientHello.
   client_->Handshake();
 
@@ -79,29 +74,17 @@ TEST_F(TlsConnectTest, ZeroRttServerOnly
 
   // Now make sure that things complete.
   Handshake();
   CheckConnected();
   SendReceive();
   CheckKeys(ssl_kea_ecdh, ssl_auth_rsa_sign);
 }
 
-TEST_F(TlsConnectTest, ZeroRtt) {
-  SetupForZeroRtt();
-  client_->Set0RttEnabled(true);
-  server_->Set0RttEnabled(true);
-  ExpectResumption(RESUME_TICKET);
-  ZeroRttSendReceive(true);
-  Handshake();
-  ExpectEarlyDataAccepted(true);
-  CheckConnected();
-  SendReceive();
-}
-
-TEST_F(TlsConnectTest, TestTls13ZeroRttAlpn) {
+TEST_P(TlsConnectTls13, TestTls13ZeroRttAlpn) {
   EnableAlpn();
   SetupForZeroRtt();
   EnableAlpn();
   client_->Set0RttEnabled(true);
   server_->Set0RttEnabled(true);
   ExpectResumption(RESUME_TICKET);
   ExpectEarlyDataAccepted(true);
   ZeroRttSendReceive(true, [this]() {
@@ -109,38 +92,19 @@ TEST_F(TlsConnectTest, TestTls13ZeroRttA
     return true;
   });
   Handshake();
   CheckConnected();
   SendReceive();
   CheckAlpn("a");
 }
 
-// Remove the old ALPN value and so the client will not offer ALPN.
-TEST_F(TlsConnectTest, TestTls13ZeroRttAlpnChangeBoth) {
-  EnableAlpn();
-  SetupForZeroRtt();
-  static const uint8_t alpn[] = {0x01, 0x62};  // "b"
-  EnableAlpn(alpn, sizeof(alpn));
-  client_->Set0RttEnabled(true);
-  server_->Set0RttEnabled(true);
-  ExpectResumption(RESUME_TICKET);
-  ZeroRttSendReceive(false, [this]() {
-    client_->CheckAlpn(SSL_NEXT_PROTO_NO_SUPPORT);
-    return false;
-  });
-  Handshake();
-  CheckConnected();
-  SendReceive();
-  CheckAlpn("b");
-}
-
 // Have the server negotiate a different ALPN value, and therefore
 // reject 0-RTT.
-TEST_F(TlsConnectTest, TestTls13ZeroRttAlpnChangeServer) {
+TEST_P(TlsConnectTls13, TestTls13ZeroRttAlpnChangeServer) {
   EnableAlpn();
   SetupForZeroRtt();
   static const uint8_t client_alpn[] = {0x01, 0x61, 0x01, 0x62};  // "a", "b"
   static const uint8_t server_alpn[] = {0x01, 0x62};              // "b"
   client_->EnableAlpn(client_alpn, sizeof(client_alpn));
   server_->EnableAlpn(server_alpn, sizeof(server_alpn));
   client_->Set0RttEnabled(true);
   server_->Set0RttEnabled(true);
@@ -154,17 +118,17 @@ TEST_F(TlsConnectTest, TestTls13ZeroRttA
   SendReceive();
   CheckAlpn("b");
 }
 
 // Check that the client validates the ALPN selection of the server.
 // Stomp the ALPN on the client after sending the ClientHello so
 // that the server selection appears to be incorrect. The client
 // should then fail the connection.
-TEST_F(TlsConnectTest, TestTls13ZeroRttNoAlpnServer) {
+TEST_P(TlsConnectTls13, TestTls13ZeroRttNoAlpnServer) {
   EnableAlpn();
   SetupForZeroRtt();
   client_->Set0RttEnabled(true);
   server_->Set0RttEnabled(true);
   EnableAlpn();
   ExpectResumption(RESUME_TICKET);
   ZeroRttSendReceive(true, [this]() {
     PRUint8 b[] = {'b'};
@@ -176,25 +140,57 @@ TEST_F(TlsConnectTest, TestTls13ZeroRttN
   Handshake();
   client_->CheckErrorCode(SSL_ERROR_NEXT_PROTOCOL_DATA_INVALID);
   server_->CheckErrorCode(SSL_ERROR_ILLEGAL_PARAMETER_ALERT);
 }
 
 // Set up with no ALPN and then set the client so it thinks it has ALPN.
 // The server responds without the extension and the client returns an
 // error.
-TEST_F(TlsConnectTest, TestTls13ZeroRttNoAlpnClient) {
+TEST_P(TlsConnectTls13, TestTls13ZeroRttNoAlpnClient) {
   SetupForZeroRtt();
   client_->Set0RttEnabled(true);
   server_->Set0RttEnabled(true);
   ExpectResumption(RESUME_TICKET);
   ZeroRttSendReceive(true, [this]() {
     PRUint8 b[] = {'b'};
     EXPECT_EQ(SECSuccess, SSLInt_Set0RttAlpn(client_->ssl_fd(), b, 1));
     client_->CheckAlpn(SSL_NEXT_PROTO_EARLY_VALUE, "b");
     return true;
   });
   Handshake();
   client_->CheckErrorCode(SSL_ERROR_NEXT_PROTOCOL_DATA_INVALID);
   server_->CheckErrorCode(SSL_ERROR_ILLEGAL_PARAMETER_ALERT);
 }
 
+// Remove the old ALPN value and so the client will not offer early data.
+TEST_P(TlsConnectTls13, TestTls13ZeroRttAlpnChangeBoth) {
+  EnableAlpn();
+  SetupForZeroRtt();
+  static const uint8_t alpn[] = {0x01, 0x62};  // "b"
+  EnableAlpn(alpn, sizeof(alpn));
+  client_->Set0RttEnabled(true);
+  server_->Set0RttEnabled(true);
+  ExpectResumption(RESUME_TICKET);
+  ZeroRttSendReceive(false, [this]() {
+    client_->CheckAlpn(SSL_NEXT_PROTO_NO_SUPPORT);
+    return false;
+  });
+  Handshake();
+  CheckConnected();
+  SendReceive();
+  CheckAlpn("b");
+}
+
+TEST_F(TlsConnectTest, DamageSecretHandleZeroRttClientFinished) {
+  SetupForZeroRtt();
+  client_->Set0RttEnabled(true);
+  server_->Set0RttEnabled(true);
+  client_->SetPacketFilter(new AfterRecordN(
+      client_, server_,
+      0,  // ClientHello.
+      [this]() { SSLInt_DamageEarlyTrafficSecret(server_->ssl_fd()); }));
+  ConnectExpectFail();
+  client_->CheckErrorCode(SSL_ERROR_DECRYPT_ERROR_ALERT);
+  server_->CheckErrorCode(SSL_ERROR_BAD_HANDSHAKE_HASH_VALUE);
+}
+
 }  // namespace nss_test
--- a/security/nss/external_tests/ssl_gtest/ssl_auth_unittest.cc
+++ b/security/nss/external_tests/ssl_gtest/ssl_auth_unittest.cc
@@ -1,16 +1,16 @@
 /* -*- 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 "secerr.h"
 #include "sslerr.h"
 #include "sslproto.h"
 
 extern "C" {
 // This is not something that should make you happy.
 #include "libssl_internals.h"
 }
 
@@ -63,22 +63,24 @@ TEST_P(TlsConnectGeneric, ClientAuthBigR
   Reset(TlsAgent::kServerRsa, TlsAgent::kRsa2048);
   client_->SetupClientAuth();
   server_->RequestClientAuth(true);
   Connect();
   CheckKeys(ssl_kea_ecdh, ssl_auth_rsa_sign);
 }
 
 // Offset is the position in the captured buffer where the signature sits.
-static void CheckSigAlgs(TlsInspectorRecordHandshakeMessage* capture,
-                         size_t offset, TlsAgent* peer,
-                         SSLHashType expected_hash, size_t expected_size) {
+static void CheckSigScheme(TlsInspectorRecordHandshakeMessage* capture,
+                           size_t offset, TlsAgent* peer,
+                           uint16_t expected_scheme, size_t expected_size) {
   EXPECT_LT(offset + 2U, capture->buffer().len());
-  EXPECT_EQ(expected_hash, capture->buffer().data()[offset]);
-  EXPECT_EQ(ssl_sign_rsa, capture->buffer().data()[offset + 1]);
+
+  uint32_t scheme = 0;
+  capture->buffer().Read(offset, 2, &scheme);
+  EXPECT_EQ(expected_scheme, static_cast<uint16_t>(scheme));
 
   ScopedCERTCertificate remote_cert(SSL_PeerCertificate(peer->ssl_fd()));
   ScopedSECKEYPublicKey remote_key(CERT_ExtractPublicKey(remote_cert.get()));
   EXPECT_EQ(expected_size, SECKEY_PublicKeyStrengthInBits(remote_key.get()));
 }
 
 // The server should prefer SHA-256 by default, even for the small key size used
 // in the default certificate.
@@ -92,42 +94,45 @@ TEST_P(TlsConnectTls12, ServerAuthCheckS
 
   const DataBuffer& buffer = capture_ske->buffer();
   EXPECT_LT(3U, buffer.len());
   EXPECT_EQ(3U, buffer.data()[0]) << "curve_type == named_curve";
   uint32_t tmp;
   EXPECT_TRUE(buffer.Read(1, 2, &tmp)) << "read NamedCurve";
   EXPECT_EQ(ssl_grp_ec_secp256r1, tmp);
   EXPECT_TRUE(buffer.Read(3, 1, &tmp)) << " read ECPoint";
-  CheckSigAlgs(capture_ske, 4 + tmp, client_, ssl_hash_sha256, 1024);
+  CheckSigScheme(capture_ske, 4 + tmp, client_, kTlsSigSchemeRsaPssSha256,
+                 1024);
 }
 
 TEST_P(TlsConnectTls12, ClientAuthCheckSigAlg) {
   EnsureTlsSetup();
   auto capture_cert_verify =
       new TlsInspectorRecordHandshakeMessage(kTlsHandshakeCertificateVerify);
   client_->SetPacketFilter(capture_cert_verify);
   client_->SetupClientAuth();
   server_->RequestClientAuth(true);
   Connect();
   CheckKeys(ssl_kea_ecdh, ssl_auth_rsa_sign);
 
-  CheckSigAlgs(capture_cert_verify, 0, server_, ssl_hash_sha1, 1024);
+  CheckSigScheme(capture_cert_verify, 0, server_, kTlsSigSchemeRsaPkcs1Sha1,
+                 1024);
 }
 
 TEST_P(TlsConnectTls12, ClientAuthBigRsaCheckSigAlg) {
   Reset(TlsAgent::kServerRsa, TlsAgent::kRsa2048);
   auto capture_cert_verify =
       new TlsInspectorRecordHandshakeMessage(kTlsHandshakeCertificateVerify);
   client_->SetPacketFilter(capture_cert_verify);
   client_->SetupClientAuth();
   server_->RequestClientAuth(true);
   Connect();
   CheckKeys(ssl_kea_ecdh, ssl_auth_rsa_sign);
-  CheckSigAlgs(capture_cert_verify, 0, server_, ssl_hash_sha256, 2048);
+  CheckSigScheme(capture_cert_verify, 0, server_, kTlsSigSchemeRsaPssSha256,
+                 2048);
 }
 
 static const SSLSignatureAndHashAlg SignatureEcdsaSha384[] = {
     {ssl_hash_sha384, ssl_sign_ecdsa}};
 static const SSLSignatureAndHashAlg SignatureEcdsaSha256[] = {
     {ssl_hash_sha256, ssl_sign_ecdsa}};
 static const SSLSignatureAndHashAlg SignatureRsaSha384[] = {
     {ssl_hash_sha384, ssl_sign_rsa}};
--- a/security/nss/external_tests/ssl_gtest/ssl_ciphersuite_unittest.cc
+++ b/security/nss/external_tests/ssl_gtest/ssl_ciphersuite_unittest.cc
@@ -1,18 +1,18 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#include "ssl.h"
 #include <functional>
 #include <memory>
 #include "secerr.h"
+#include "ssl.h"
 #include "sslerr.h"
 #include "sslproto.h"
 
 extern "C" {
 // This is not something that should make you happy.
 #include "libssl_internals.h"
 }
 
--- a/security/nss/external_tests/ssl_gtest/ssl_damage_unittest.cc
+++ b/security/nss/external_tests/ssl_gtest/ssl_damage_unittest.cc
@@ -1,18 +1,18 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#include "ssl.h"
 #include <functional>
 #include <memory>
 #include "secerr.h"
+#include "ssl.h"
 #include "sslerr.h"
 #include "sslproto.h"
 
 extern "C" {
 // This is not something that should make you happy.
 #include "libssl_internals.h"
 }
 
@@ -50,12 +50,12 @@ TEST_F(TlsConnectTest, DamageSecretHandl
   server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_1,
                            SSL_LIBRARY_VERSION_TLS_1_3);
   server_->SetPacketFilter(new AfterRecordN(
       server_, client_,
       0,  // ServerHello.
       [this]() { SSLInt_DamageHsTrafficSecret(client_->ssl_fd()); }));
   ConnectExpectFail();
   client_->CheckErrorCode(SSL_ERROR_BAD_HANDSHAKE_HASH_VALUE);
-  server_->CheckErrorCode(SSL_ERROR_DECRYPT_ERROR_ALERT);
+  server_->CheckErrorCode(SSL_ERROR_BAD_MAC_READ);
 }
 
 }  // namespace nspr_test
--- a/security/nss/external_tests/ssl_gtest/ssl_dhe_unittest.cc
+++ b/security/nss/external_tests/ssl_gtest/ssl_dhe_unittest.cc
@@ -1,19 +1,19 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#include "ssl.h"
 #include <functional>
 #include <memory>
 #include <set>
 #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"
@@ -21,62 +21,16 @@
 namespace nss_test {
 
 TEST_P(TlsConnectGeneric, ConnectDhe) {
   EnableOnlyDheCiphers();
   Connect();
   CheckKeys(ssl_kea_dh, ssl_auth_rsa_sign);
 }
 
-// Track groups and make sure that there are no duplicates.
-class CheckDuplicateGroup {
- public:
-  void AddAndCheckGroup(uint16_t group) {
-    EXPECT_EQ(groups_.end(), groups_.find(group))
-        << "Group " << group << " should not be duplicated";
-    groups_.insert(group);
-  }
-
- private:
-  std::set<uint16_t> groups_;
-};
-
-// Check the group of each of the supported groups
-static void CheckGroups(const DataBuffer& groups,
-                        std::function<void(uint16_t)> check_group) {
-  CheckDuplicateGroup group_set;
-  uint32_t tmp;
-  EXPECT_TRUE(groups.Read(0, 2, &tmp));
-  EXPECT_EQ(groups.len() - 2, static_cast<size_t>(tmp));
-  for (size_t i = 2; i < groups.len(); i += 2) {
-    EXPECT_TRUE(groups.Read(i, 2, &tmp));
-    uint16_t group = static_cast<uint16_t>(tmp);
-    group_set.AddAndCheckGroup(group);
-    check_group(group);
-  }
-}
-
-// Check the group of each of the shares
-static void CheckShares(const DataBuffer& shares,
-                        std::function<void(uint16_t)> check_group) {
-  CheckDuplicateGroup group_set;
-  uint32_t tmp;
-  EXPECT_TRUE(shares.Read(0, 2, &tmp));
-  EXPECT_EQ(shares.len() - 2, static_cast<size_t>(tmp));
-  size_t i;
-  for (i = 2; i < shares.len(); i += 4 + tmp) {
-    ASSERT_TRUE(shares.Read(i, 2, &tmp));
-    uint16_t group = static_cast<uint16_t>(tmp);
-    group_set.AddAndCheckGroup(group);
-    check_group(group);
-    ASSERT_TRUE(shares.Read(i + 2, 2, &tmp));
-  }
-  EXPECT_EQ(shares.len(), i);
-}
-
 TEST_P(TlsConnectTls13, SharesForBothEcdheAndDhe) {
   EnsureTlsSetup();
   client_->DisableAllCiphers();
   client_->EnableCiphersByKeyExchange(ssl_kea_ecdh);
   client_->EnableCiphersByKeyExchange(ssl_kea_dh);
 
   auto groups_capture = new TlsExtensionCapture(ssl_supported_groups_xtn);
   auto shares_capture = new TlsExtensionCapture(ssl_tls13_key_share_xtn);
@@ -85,17 +39,17 @@ TEST_P(TlsConnectTls13, SharesForBothEcd
   captures.push_back(shares_capture);
   client_->SetPacketFilter(new ChainedPacketFilter(captures));
 
   Connect();
 
   CheckKeys(ssl_kea_ecdh, ssl_auth_rsa_sign);
 
   bool ec, dh;
-  auto track_group_type = [&ec, &dh](uint16_t group) {
+  auto track_group_type = [&ec, &dh](SSLNamedGroup group) {
     if ((group & 0xff00U) == 0x100U) {
       dh = true;
     } else {
       ec = true;
     }
   };
   CheckGroups(groups_capture->extension(), track_group_type);
   CheckShares(shares_capture->extension(), track_group_type);
@@ -113,17 +67,17 @@ TEST_P(TlsConnectTls13, NoDheOnEcdheConn
   std::vector<PacketFilter*> captures;
   captures.push_back(groups_capture);
   captures.push_back(shares_capture);
   client_->SetPacketFilter(new ChainedPacketFilter(captures));
 
   Connect();
 
   CheckKeys(ssl_kea_ecdh, ssl_auth_rsa_sign);
-  auto is_ecc = [](uint16_t group) { EXPECT_NE(0x100U, group & 0xff00U); };
+  auto is_ecc = [](SSLNamedGroup group) { EXPECT_NE(0x100U, group & 0xff00U); };
   CheckGroups(groups_capture->extension(), is_ecc);
   CheckShares(shares_capture->extension(), is_ecc);
 }
 
 TEST_P(TlsConnectGeneric, ConnectFfdheClient) {
   EnableOnlyDheCiphers();
   EXPECT_EQ(SECSuccess, SSL_OptionSet(client_->ssl_fd(),
                                       SSL_REQUIRE_DH_NAMED_GROUPS, PR_TRUE));
@@ -132,17 +86,17 @@ TEST_P(TlsConnectGeneric, ConnectFfdheCl
   std::vector<PacketFilter*> captures;
   captures.push_back(groups_capture);
   captures.push_back(shares_capture);
   client_->SetPacketFilter(new ChainedPacketFilter(captures));
 
   Connect();
 
   CheckKeys(ssl_kea_dh, ssl_auth_rsa_sign);
-  auto is_ffdhe = [](uint16_t group) {
+  auto is_ffdhe = [](SSLNamedGroup group) {
     // The group has to be in this range.
     EXPECT_LE(ssl_grp_ffdhe_2048, group);
     EXPECT_GE(ssl_grp_ffdhe_8192, group);
   };
   CheckGroups(groups_capture->extension(), is_ffdhe);
   if (version_ == SSL_LIBRARY_VERSION_TLS_1_3) {
     CheckShares(shares_capture->extension(), is_ffdhe);
   } else {
@@ -502,47 +456,64 @@ TEST_P(TlsConnectGenericPre13, WeakDHGro
   EXPECT_EQ(SECSuccess,
             SSL_EnableWeakDHEPrimeGroup(server_->ssl_fd(), PR_TRUE));
 
   Connect();
 }
 
 TEST_P(TlsConnectGeneric, Ffdhe3072) {
   EnableOnlyDheCiphers();
-  client_->ConfigNamedGroup(ssl_grp_ffdhe_2048, false);
+  SSLNamedGroup groups[] = {ssl_grp_ffdhe_3072};
+  client_->ConfigNamedGroups(groups, PR_ARRAY_SIZE(groups));
 
   Connect();
 }
 
 TEST_P(TlsConnectGenericPre13, PreferredFfdhe) {
   EnableOnlyDheCiphers();
   static const SSLDHEGroupType groups[] = {ssl_ff_dhe_3072_group,
                                            ssl_ff_dhe_2048_group};
   EXPECT_EQ(SECSuccess, SSL_DHEGroupPrefSet(server_->ssl_fd(), groups,
                                             PR_ARRAY_SIZE(groups)));
 
   Connect();
   CheckKeys(ssl_kea_dh, ssl_auth_rsa_sign, 3072);
 }
 
+TEST_P(TlsConnectGenericPre13, MismatchDHE) {
+  EnableOnlyDheCiphers();
+  EXPECT_EQ(SECSuccess, SSL_OptionSet(client_->ssl_fd(),
+                                      SSL_REQUIRE_DH_NAMED_GROUPS, PR_TRUE));
+  static const SSLDHEGroupType serverGroups[] = {ssl_ff_dhe_3072_group};
+  EXPECT_EQ(SECSuccess, SSL_DHEGroupPrefSet(server_->ssl_fd(), serverGroups,
+                                            PR_ARRAY_SIZE(serverGroups)));
+  static const SSLDHEGroupType clientGroups[] = {ssl_ff_dhe_2048_group};
+  EXPECT_EQ(SECSuccess, SSL_DHEGroupPrefSet(client_->ssl_fd(), clientGroups,
+                                            PR_ARRAY_SIZE(clientGroups)));
+
+  ConnectExpectFail();
+  server_->CheckErrorCode(SSL_ERROR_NO_CYPHER_OVERLAP);
+  client_->CheckErrorCode(SSL_ERROR_NO_CYPHER_OVERLAP);
+}
+
 TEST_P(TlsConnectTls13, ResumeFfdhe) {
   EnableOnlyDheCiphers();
   ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
   Connect();
   SendReceive();  // Need to read so that we absorb the session ticket.
   CheckKeys(ssl_kea_dh, ssl_auth_rsa_sign);
 
   Reset();
   ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
   EnableOnlyDheCiphers();
   TlsExtensionCapture* clientCapture =
-      new TlsExtensionCapture(kTlsExtensionPreSharedKey);
+      new TlsExtensionCapture(ssl_tls13_pre_shared_key_xtn);
   client_->SetPacketFilter(clientCapture);
   TlsExtensionCapture* serverCapture =
-      new TlsExtensionCapture(kTlsExtensionPreSharedKey);
+      new TlsExtensionCapture(ssl_tls13_pre_shared_key_xtn);
   server_->SetPacketFilter(serverCapture);
   ExpectResumption(RESUME_TICKET);
   Connect();
   CheckKeys(ssl_kea_dh, ssl_auth_rsa_sign);
   ASSERT_LT(0UL, clientCapture->extension().len());
   ASSERT_LT(0UL, serverCapture->extension().len());
 }
 
--- a/security/nss/external_tests/ssl_gtest/ssl_drop_unittest.cc
+++ b/security/nss/external_tests/ssl_gtest/ssl_drop_unittest.cc
@@ -1,49 +1,30 @@
 /* -*- 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 "secerr.h"
 
 extern "C" {
 // This is not something that should make you happy.
 #include "libssl_internals.h"
 }
 
 #include "gtest_utils.h"
 #include "scoped_ptrs.h"
 #include "tls_connect.h"
 #include "tls_filter.h"
 #include "tls_parser.h"
 
 namespace nss_test {
 
-// This class selectively drops complete writes.  This relies on the fact that
-// writes in libssl are on record boundaries.
-class SelectiveDropFilter : public PacketFilter, public PollTarget {
- public:
-  SelectiveDropFilter(uint32_t pattern) : pattern_(pattern), counter_(0) {}
-
- protected:
-  virtual Action Filter(const DataBuffer& input, DataBuffer* output) override {
-    if (counter_ >= 32) {
-      return KEEP;
-    }
-    return ((1 << counter_++) & pattern_) ? DROP : KEEP;
-  }
-
- private:
-  const uint32_t pattern_;
-  uint8_t counter_;
-};
-
 TEST_P(TlsConnectDatagram, DropClientFirstFlightOnce) {
   client_->SetPacketFilter(new SelectiveDropFilter(0x1));
   Connect();
   SendReceive();
 }
 
 TEST_P(TlsConnectDatagram, DropServerFirstFlightOnce) {
   server_->SetPacketFilter(new SelectiveDropFilter(0x1));
--- a/security/nss/external_tests/ssl_gtest/ssl_ecdh_unittest.cc
+++ b/security/nss/external_tests/ssl_gtest/ssl_ecdh_unittest.cc
@@ -1,18 +1,18 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#include "ssl.h"
 #include <functional>
 #include <memory>
 #include "secerr.h"
+#include "ssl.h"
 #include "sslerr.h"
 #include "sslproto.h"
 
 extern "C" {
 // This is not something that should make you happy.
 #include "libssl_internals.h"
 }
 
@@ -46,43 +46,243 @@ TEST_P(TlsConnectGenericPre13, ConnectEc
 TEST_P(TlsConnectGeneric, ConnectEcdhe) {
   Connect();
   CheckKeys(ssl_kea_ecdh, ssl_auth_rsa_sign);
 }
 
 // If we pick a 256-bit cipher suite and use a P-384 certificate, the server
 // should choose P-384 for key exchange too.  Only valid for TLS >=1.2 because
 // we don't have 256-bit ciphers before then.
-// TODO: Re-enable for 1.3 when Bug 1286140 lands.
-TEST_P(TlsConnectTls12, ConnectEcdheP384) {
+TEST_P(TlsConnectTls12Plus, ConnectEcdheP384) {
   Reset(TlsAgent::kServerEcdsa384);
   ConnectWithCipherSuite(TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256);
   CheckKeys(ssl_kea_ecdh, ssl_auth_ecdsa, 384);
 }
 
-TEST_P(TlsConnectGeneric, ConnectEcdheP384) {
+TEST_P(TlsConnectGeneric, ConnectEcdheP384Client) {
   EnsureTlsSetup();
-  client_->ConfigNamedGroup(ssl_grp_ec_secp256r1, false);
+  const SSLNamedGroup groups[] = {ssl_grp_ec_secp384r1, ssl_grp_ffdhe_2048};
+  client_->ConfigNamedGroups(groups, PR_ARRAY_SIZE(groups));
+  server_->ConfigNamedGroups(groups, PR_ARRAY_SIZE(groups));
   Connect();
   CheckKeys(ssl_kea_ecdh, ssl_auth_rsa_sign, 384);
 }
 
+// This causes a HelloRetryRequest in TLS 1.3.  Earlier versions don't care.
+TEST_P(TlsConnectGeneric, ConnectEcdheP384Server) {
+  EnsureTlsSetup();
+  auto hrr_capture =
+      new TlsInspectorRecordHandshakeMessage(kTlsHandshakeHelloRetryRequest);
+  server_->SetPacketFilter(hrr_capture);
+  const SSLNamedGroup groups[] = {ssl_grp_ec_secp384r1};
+  server_->ConfigNamedGroups(groups, PR_ARRAY_SIZE(groups));
+  Connect();
+  CheckKeys(ssl_kea_ecdh, ssl_auth_rsa_sign, 384);
+  EXPECT_EQ(version_ == SSL_LIBRARY_VERSION_TLS_1_3,
+            hrr_capture->buffer().len() != 0);
+}
+
 // This enables only P-256 on the client and disables it on the server.
 // This test will fail when we add other groups that identify as ECDHE.
 TEST_P(TlsConnectGeneric, ConnectEcdheGroupMismatch) {
   EnsureTlsSetup();
-  client_->ConfigNamedGroup(ssl_grp_ec_secp256r1, true);
-  client_->ConfigNamedGroup(ssl_grp_ec_secp384r1, false);
-  client_->ConfigNamedGroup(ssl_grp_ec_secp521r1, false);
-  server_->ConfigNamedGroup(ssl_grp_ec_secp256r1, false);
+  const SSLNamedGroup clientGroups[] = {ssl_grp_ec_secp256r1,
+                                        ssl_grp_ffdhe_2048};
+  const SSLNamedGroup serverGroups[] = {ssl_grp_ffdhe_2048};
+  client_->ConfigNamedGroups(clientGroups, PR_ARRAY_SIZE(clientGroups));
+  server_->ConfigNamedGroups(serverGroups, PR_ARRAY_SIZE(serverGroups));
 
   Connect();
   CheckKeys(ssl_kea_dh, ssl_auth_rsa_sign);
 }
 
+class TlsKeyExchangeTest : public TlsConnectGeneric {
+ protected:
+  TlsExtensionCapture *groups_capture_;
+  TlsExtensionCapture *shares_capture_;
+  TlsInspectorRecordHandshakeMessage *capture_hrr_;
+
+  void EnsureKeyShareSetup(const SSLNamedGroup *groups, size_t num) {
+    EnsureTlsSetup();
+    groups_capture_ = new TlsExtensionCapture(ssl_supported_groups_xtn);
+    shares_capture_ = new TlsExtensionCapture(ssl_tls13_key_share_xtn);
+    std::vector<PacketFilter *> captures;
+    captures.push_back(groups_capture_);
+    captures.push_back(shares_capture_);
+    client_->SetPacketFilter(new ChainedPacketFilter(captures));
+    capture_hrr_ =
+        new TlsInspectorRecordHandshakeMessage(kTlsHandshakeHelloRetryRequest);
+    server_->SetPacketFilter(capture_hrr_);
+
+    if (groups) {
+      client_->ConfigNamedGroups(groups, num);
+      server_->ConfigNamedGroups(groups, num);
+    }
+  }
+
+  std::vector<SSLNamedGroup> GetGroupDetails(const DataBuffer &ext) {
+    uint32_t tmp = 0;
+    EXPECT_TRUE(ext.Read(0, 2, &tmp));
+    EXPECT_EQ(ext.len() - 2, static_cast<size_t>(tmp));
+    EXPECT_TRUE(ext.len() % 2 == 0);
+    std::vector<SSLNamedGroup> groups;
+    for (size_t i = 1; i < ext.len() / 2; i += 1) {
+      EXPECT_TRUE(ext.Read(2 * i, 2, &tmp));
+      groups.push_back(static_cast<SSLNamedGroup>(tmp));
+    }
+    return groups;
+  }
+
+  std::vector<SSLNamedGroup> GetShareDetails(const DataBuffer &ext) {
+    uint32_t tmp = 0;
+    EXPECT_TRUE(ext.Read(0, 2, &tmp));
+    EXPECT_EQ(ext.len() - 2, static_cast<size_t>(tmp));
+    std::vector<SSLNamedGroup> shares;
+    size_t i = 2;
+    while (i < ext.len()) {
+      EXPECT_TRUE(ext.Read(i, 2, &tmp));
+      shares.push_back(static_cast<SSLNamedGroup>(tmp));
+      EXPECT_TRUE(ext.Read(i + 2, 2, &tmp));
+      i += 4 + tmp;
+    }
+    EXPECT_EQ(ext.len(), i);
+    return shares;
+  }
+
+  void CheckKEXDetails(std::vector<SSLNamedGroup> expectedGroups,
+                       std::vector<SSLNamedGroup> expectedShares) {
+    std::vector<SSLNamedGroup> groups =
+        GetGroupDetails(groups_capture_->extension());
+    EXPECT_EQ(expectedGroups, groups);
+
+    if (version_ >= SSL_LIBRARY_VERSION_TLS_1_3) {
+      ASSERT_TRUE(expectedShares.size());
+      std::vector<SSLNamedGroup> shares =
+          GetShareDetails(shares_capture_->extension());
+      EXPECT_EQ(expectedShares, shares);
+    } else {
+      EXPECT_EQ(0U, shares_capture_->extension().len());
+    }
+
+    EXPECT_EQ(0U, capture_hrr_->buffer().len())
+        << "we didn't expect a hello retry request.";
+  }
+};
+
+TEST_P(TlsKeyExchangeTest, P384Priority) {
+  /* P256, P384 and P521 are enabled. Both prefer P384. */
+  const SSLNamedGroup groups[] = {ssl_grp_ec_secp384r1, ssl_grp_ec_secp256r1,
+                                  ssl_grp_ec_secp521r1};
+  EnsureKeyShareSetup(groups, PR_ARRAY_SIZE(groups));
+  client_->DisableAllCiphers();
+  client_->EnableCiphersByKeyExchange(ssl_kea_ecdh);
+  Connect();
+
+  CheckKeys(ssl_kea_ecdh, ssl_auth_rsa_sign, 384);
+
+  std::vector<SSLNamedGroup> shares = {ssl_grp_ec_secp384r1};
+  std::vector<SSLNamedGroup> expected_groups(groups,
+                                             groups + PR_ARRAY_SIZE(groups));
+  CheckKEXDetails(expected_groups, shares);
+}
+
+TEST_P(TlsKeyExchangeTest, DuplicateGroupConfig) {
+  const SSLNamedGroup groups[] = {ssl_grp_ec_secp384r1, ssl_grp_ec_secp384r1,
+                                  ssl_grp_ec_secp384r1, ssl_grp_ec_secp256r1,
+                                  ssl_grp_ec_secp256r1};
+  EnsureKeyShareSetup(groups, PR_ARRAY_SIZE(groups));
+  client_->DisableAllCiphers();
+  client_->EnableCiphersByKeyExchange(ssl_kea_ecdh);
+  Connect();
+
+  CheckKeys(ssl_kea_ecdh, ssl_auth_rsa_sign, 384);
+
+  std::vector<SSLNamedGroup> shares = {ssl_grp_ec_secp384r1};
+  std::vector<SSLNamedGroup> expectedGroups = {ssl_grp_ec_secp384r1,
+                                               ssl_grp_ec_secp256r1};
+  CheckKEXDetails(expectedGroups, shares);
+}
+
+TEST_P(TlsKeyExchangeTest, P384PriorityDHEnabled) {
+  /* P256, P384,  P521, and FFDHE2048 are enabled. Both prefer P384. */
+  const SSLNamedGroup groups[] = {ssl_grp_ec_secp384r1, ssl_grp_ffdhe_2048,
+                                  ssl_grp_ec_secp256r1, ssl_grp_ec_secp521r1};
+  EnsureKeyShareSetup(groups, PR_ARRAY_SIZE(groups));
+  Connect();
+
+  CheckKeys(ssl_kea_ecdh, ssl_auth_rsa_sign, 384);
+
+  if (version_ >= SSL_LIBRARY_VERSION_TLS_1_3) {
+    std::vector<SSLNamedGroup> shares = {ssl_grp_ec_secp384r1};
+    std::vector<SSLNamedGroup> expected_groups(groups,
+                                               groups + PR_ARRAY_SIZE(groups));
+    CheckKEXDetails(expected_groups, shares);
+  } else {
+    std::vector<SSLNamedGroup> oldtlsgroups = {
+        ssl_grp_ec_secp384r1, ssl_grp_ec_secp256r1, ssl_grp_ec_secp521r1};
+    CheckKEXDetails(oldtlsgroups, std::vector<SSLNamedGroup>());
+  }
+}
+
+TEST_P(TlsConnectGenericPre13, P384PriorityOnServer) {
+  EnsureTlsSetup();
+  client_->DisableAllCiphers();
+  client_->EnableCiphersByKeyExchange(ssl_kea_ecdh);
+
+  /* The server prefers P384. It has to win. */
+  const SSLNamedGroup serverGroups[] = {
+      ssl_grp_ec_secp384r1, ssl_grp_ec_secp256r1, ssl_grp_ec_secp521r1};
+  server_->ConfigNamedGroups(serverGroups, PR_ARRAY_SIZE(serverGroups));
+
+  Connect();
+
+  CheckKeys(ssl_kea_ecdh, ssl_auth_rsa_sign, 384);
+}
+
+TEST_P(TlsConnectGenericPre13, P384PriorityFromModelSocket) {
+#ifdef NSS_ECC_MORE_THAN_SUITE_B
+  // We can't run this test with a model socket and more than suite B.
+  return;
+#endif
+  EnsureModelSockets();
+
+  /* Both prefer P384, set on the model socket. */
+  const SSLNamedGroup groups[] = {ssl_grp_ec_secp384r1, ssl_grp_ec_secp256r1,
+                                  ssl_grp_ec_secp521r1, ssl_grp_ffdhe_2048};
+  client_model_->ConfigNamedGroups(groups, PR_ARRAY_SIZE(groups));
+  server_model_->ConfigNamedGroups(groups, PR_ARRAY_SIZE(groups));
+
+  Connect();
+
+  CheckKeys(ssl_kea_ecdh, ssl_auth_rsa_sign, 384);
+}
+
+TEST_P(TlsConnectStreamPre13, ConfiguredGroupsRenegotiate) {
+  EnsureTlsSetup();
+  client_->DisableAllCiphers();
+  client_->EnableCiphersByKeyExchange(ssl_kea_ecdh);
+
+  const SSLNamedGroup serverGroups[] = {ssl_grp_ec_secp256r1,
+                                        ssl_grp_ec_secp384r1};
+  const SSLNamedGroup clientGroups[] = {ssl_grp_ec_secp384r1};
+  server_->ConfigNamedGroups(clientGroups, PR_ARRAY_SIZE(clientGroups));
+  client_->ConfigNamedGroups(serverGroups, PR_ARRAY_SIZE(serverGroups));
+
+  Connect();
+
+  CheckKeys(ssl_kea_ecdh, ssl_auth_rsa_sign, 384);
+  CheckConnected();
+
+  // The renegotiation has to use the same preferences as the original session.
+  server_->PrepareForRenegotiate();
+  client_->StartRenegotiate();
+  Handshake();
+  CheckKeys(ssl_kea_ecdh, ssl_auth_rsa_sign, 384);
+}
+
 // Replace the point in the client key exchange message with an empty one
 class ECCClientKEXFilter : public TlsHandshakeFilter {
  public:
   ECCClientKEXFilter() {}
 
  protected:
   virtual PacketFilter::Action FilterHandshake(const HandshakeHeader &header,
                                                const DataBuffer &input,
@@ -131,9 +331,13 @@ TEST_P(TlsConnectGenericPre13, ConnectEC
 
 TEST_P(TlsConnectGenericPre13, ConnectECDHEmptyClientPoint) {
   // add packet filter
   client_->SetPacketFilter(new ECCClientKEXFilter());
   ConnectExpectFail();
   server_->CheckErrorCode(SSL_ERROR_RX_MALFORMED_CLIENT_KEY_EXCH);
 }
 
-}  // namespace nspr_test
+INSTANTIATE_TEST_CASE_P(KeyExchangeTest, TlsKeyExchangeTest,
+                        ::testing::Combine(TlsConnectTestBase::kTlsModesAll,
+                                           TlsConnectTestBase::kTlsV11Plus));
+
+}  // namespace nss_test
--- a/security/nss/external_tests/ssl_gtest/ssl_ems_unittest.cc
+++ b/security/nss/external_tests/ssl_gtest/ssl_ems_unittest.cc
@@ -1,16 +1,16 @@
 /* -*- 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 "secerr.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"
--- a/security/nss/external_tests/ssl_gtest/ssl_extension_unittest.cc
+++ b/security/nss/external_tests/ssl_gtest/ssl_extension_unittest.cc
@@ -537,18 +537,18 @@ TEST_P(TlsExtensionTest13, ModifyDraftVe
   // As above, dropping back to 1.2 fails.
   client_->SetPacketFilter(
       new TlsExtensionDamager(ssl_tls13_draft_version_xtn, 1));
   ConnectExpectFail();
   EXPECT_EQ(SSL_ERROR_PROTOCOL_VERSION_ALERT, client_->error_code());
   EXPECT_EQ(SSL_ERROR_UNSUPPORTED_VERSION, server_->error_code());
 }
 
-// This test only works with TLS because the MAC error causes a
-// timeout on the server.
+// This test only works in stream mode because the MAC error causes a timeout on
+// the server with datagram.
 TEST_F(TlsExtensionTest13Stream, DropServerKeyShare) {
   EnsureTlsSetup();
   server_->SetPacketFilter(new TlsExtensionDropper(ssl_tls13_key_share_xtn));
   ConnectExpectFail();
   EXPECT_EQ(SSL_ERROR_MISSING_KEY_SHARE, client_->error_code());
   // We are trying to decrypt but we can't. Kind of a screwy error
   // from the TLS 1.3 stack.
   EXPECT_EQ(SSL_ERROR_BAD_MAC_READ, server_->error_code());
--- a/security/nss/external_tests/ssl_gtest/ssl_gtest.cc
+++ b/security/nss/external_tests/ssl_gtest/ssl_gtest.cc
@@ -1,12 +1,12 @@
-#include "ssl.h"
 #include "nspr.h"
 #include "nss.h"
 #include "prenv.h"
+#include "ssl.h"
 
 #include <cstdlib>
 
 #include "test_io.h"
 
 #define GTEST_HAS_RTTI 0
 #include "gtest/gtest.h"
 
new file mode 100644
--- /dev/null
+++ b/security/nss/external_tests/ssl_gtest/ssl_hrr_unittest.cc
@@ -0,0 +1,197 @@
+/* -*- 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 {
+
+TEST_P(TlsConnectTls13, HelloRetryRequestAbortsZeroRtt) {
+  const char* k0RttData = "Such is life";
+  const PRInt32 k0RttDataLen = static_cast<PRInt32>(strlen(k0RttData));
+
+  SetupForZeroRtt();  // initial handshake as normal
+
+  const SSLNamedGroup groups[2] = {ssl_grp_ec_secp384r1, ssl_grp_ec_secp521r1};
+  server_->ConfigNamedGroups(groups, PR_ARRAY_SIZE(groups));
+  client_->Set0RttEnabled(true);
+  server_->Set0RttEnabled(true);
+  ExpectResumption(RESUME_TICKET);
+
+  // Send first ClientHello and send 0-RTT data
+  auto capture_early_data = new TlsExtensionCapture(ssl_tls13_early_data_xtn);
+  client_->SetPacketFilter(capture_early_data);
+  client_->Handshake();
+  EXPECT_EQ(k0RttDataLen, PR_Write(client_->ssl_fd(), k0RttData,
+                                   k0RttDataLen));  // 0-RTT write.
+  EXPECT_LT(0U, capture_early_data->extension().len());
+
+  // Send the HelloRetryRequest
+  auto hrr_capture =
+      new TlsInspectorRecordHandshakeMessage(kTlsHandshakeHelloRetryRequest);
+  server_->SetPacketFilter(hrr_capture);
+  server_->Handshake();
+  EXPECT_LT(0U, hrr_capture->buffer().len());
+
+  // The server can't read
+  std::vector<uint8_t> buf(k0RttDataLen);
+  EXPECT_EQ(SECFailure, PR_Read(server_->ssl_fd(), buf.data(), k0RttDataLen));
+  EXPECT_EQ(PR_WOULD_BLOCK_ERROR, PORT_GetError());
+
+  // Make a new capture for the early data.
+  capture_early_data = new TlsExtensionCapture(ssl_tls13_early_data_xtn);
+  client_->SetPacketFilter(capture_early_data);
+
+  // Complete the handshake successfully
+  Handshake();
+  ExpectEarlyDataAccepted(false);  // The server should reject 0-RTT
+  CheckConnected();
+  SendReceive();
+  EXPECT_EQ(0U, capture_early_data->extension().len());
+}
+
+class KeyShareReplayer : public TlsExtensionFilter {
+ public:
+  KeyShareReplayer() {}
+
+  virtual PacketFilter::Action FilterExtension(uint16_t extension_type,
+                                               const DataBuffer& input,
+                                               DataBuffer* output) {
+    if (extension_type != ssl_tls13_key_share_xtn) {
+      return KEEP;
+    }
+
+    if (!data_.len()) {
+      data_ = input;
+      return KEEP;
+    }
+
+    *output = data_;
+    return CHANGE;
+  }
+
+ private:
+  DataBuffer data_;
+};
+
+// This forces a HelloRetryRequest by disabling P-256 on the server.  However,
+// the second ClientHello is modified so that it omits the requested share.  The
+// server should reject this.
+TEST_P(TlsConnectTls13, RetryWithSameKeyShare) {
+  EnsureTlsSetup();
+  client_->SetPacketFilter(new KeyShareReplayer());
+  const SSLNamedGroup groups[2] = {ssl_grp_ec_secp384r1, ssl_grp_ec_secp521r1};
+  server_->ConfigNamedGroups(groups, PR_ARRAY_SIZE(groups));
+  ConnectExpectFail();
+  EXPECT_EQ(SSL_ERROR_BAD_2ND_CLIENT_HELLO, server_->error_code());
+  EXPECT_EQ(SSL_ERROR_ILLEGAL_PARAMETER_ALERT, client_->error_code());
+}
+
+// This tests that the second attempt at sending a ClientHello (after receiving
+// a HelloRetryRequest) is correctly retransmitted.
+TEST_F(TlsConnectDatagram13, DropClientSecondFlightWithHelloRetry) {
+  const SSLNamedGroup groups[2] = {ssl_grp_ec_secp384r1, ssl_grp_ec_secp521r1};
+  server_->ConfigNamedGroups(groups, PR_ARRAY_SIZE(groups));
+  server_->SetPacketFilter(new SelectiveDropFilter(0x2));
+  Connect();
+}
+
+TEST_F(TlsConnectTest, Select12AfterHelloRetryRequest) {
+  EnsureTlsSetup();
+  client_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_2,
+                           SSL_LIBRARY_VERSION_TLS_1_3);
+  server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_2,
+                           SSL_LIBRARY_VERSION_TLS_1_3);
+  const SSLNamedGroup groups[2] = {ssl_grp_ec_secp384r1, ssl_grp_ec_secp521r1};
+  server_->ConfigNamedGroups(groups, PR_ARRAY_SIZE(groups));
+  client_->StartConnect();
+  server_->StartConnect();
+
+  client_->Handshake();
+  server_->Handshake();
+
+  // Here we replace the TLS server with one that does TLS 1.2 only.
+  // This will happily send the client a TLS 1.2 ServerHello.
+  TlsAgent* replacement_server =
+      new TlsAgent(server_->name(), TlsAgent::SERVER, mode_);
+  delete server_;
+  server_ = replacement_server;
+  server_->Init();
+  client_->SetPeer(server_);
+  server_->SetPeer(client_);
+  server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_2,
+                           SSL_LIBRARY_VERSION_TLS_1_2);
+
+  ConnectExpectFail();
+  EXPECT_EQ(SSL_ERROR_ILLEGAL_PARAMETER_ALERT, server_->error_code());
+  EXPECT_EQ(SSL_ERROR_RX_MALFORMED_SERVER_HELLO, client_->error_code());
+}
+
+class HelloRetryRequestAgentTest : public TlsAgentTestClient {
+ protected:
+  void MakeHelloRetryRequestRecord(SSLNamedGroup group, DataBuffer* hrr_record,
+                                   uint32_t seq_num = 0) const {
+    const uint8_t canned_hrr[] = {
+        SSL_LIBRARY_VERSION_TLS_1_3 >> 8,
+        SSL_LIBRARY_VERSION_TLS_1_3 & 0xff,
+        0,
+        0,  // The cipher suite is ignored.
+        static_cast<uint8_t>(group >> 8),
+        static_cast<uint8_t>(group),
+        0,
+        0  // no extensions
+    };
+    DataBuffer hrr;
+    MakeHandshakeMessage(kTlsHandshakeHelloRetryRequest, canned_hrr,
+                         sizeof(canned_hrr), &hrr, seq_num);
+    MakeRecord(kTlsHandshakeType, SSL_LIBRARY_VERSION_TLS_1_3, hrr.data(),
+               hrr.len(), hrr_record, seq_num);
+  }
+};
+
+// Send two HelloRetryRequest messages in response to the ClientHello.  The are
+// constructed to appear legitimate by asking for a new share in each, so that
+// the client has to count to work out that the server is being unreasonable.
+TEST_P(HelloRetryRequestAgentTest, SendSecondHelloRetryRequest) {
+  EnsureInit();
+  agent_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_3,
+                          SSL_LIBRARY_VERSION_TLS_1_3);
+  agent_->StartConnect();
+
+  DataBuffer hrr_record;
+  MakeHelloRetryRequestRecord(ssl_grp_ec_secp384r1, &hrr_record, 0);
+  ProcessMessage(hrr_record, TlsAgent::STATE_CONNECTING);
+  MakeHelloRetryRequestRecord(ssl_grp_ec_secp521r1, &hrr_record, 1);
+  ProcessMessage(hrr_record, TlsAgent::STATE_ERROR,
+                 SSL_ERROR_RX_UNEXPECTED_HELLO_RETRY_REQUEST);
+}
+
+// Here the client receives a HelloRetryRequest with a group that they already
+// provided a share for.
+TEST_P(HelloRetryRequestAgentTest, HandleBogusHelloRetryRequest) {
+  EnsureInit();
+  agent_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_3,
+                          SSL_LIBRARY_VERSION_TLS_1_3);
+  agent_->StartConnect();
+
+  DataBuffer hrr_record;
+  MakeHelloRetryRequestRecord(ssl_grp_ec_secp256r1, &hrr_record);
+  ProcessMessage(hrr_record, TlsAgent::STATE_ERROR,
+                 SSL_ERROR_RX_MALFORMED_HELLO_RETRY_REQUEST);
+}
+
+INSTANTIATE_TEST_CASE_P(HelloRetryRequestAgentTests, HelloRetryRequestAgentTest,
+                        TlsConnectTestBase::kTlsModesAll);
+
+}  // namespace nss_test
--- a/security/nss/external_tests/ssl_gtest/ssl_loopback_unittest.cc
+++ b/security/nss/external_tests/ssl_gtest/ssl_loopback_unittest.cc
@@ -1,18 +1,18 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#include "ssl.h"
 #include <functional>
 #include <memory>
 #include "secerr.h"
+#include "ssl.h"
 #include "sslerr.h"
 #include "sslproto.h"
 
 extern "C" {
 // This is not something that should make you happy.
 #include "libssl_internals.h"
 }
 
@@ -48,16 +48,18 @@ TEST_P(TlsConnectGenericPre13, ConnectFa
 TEST_P(TlsConnectGeneric, ConnectAlpn) {
   EnableAlpn();
   Connect();
   CheckAlpn("a");
 }
 
 TEST_P(TlsConnectGeneric, ConnectAlpnClone) {
   EnsureModelSockets();
+  client_model_->EnableAlpn(alpn_dummy_val_, sizeof(alpn_dummy_val_));
+  server_model_->EnableAlpn(alpn_dummy_val_, sizeof(alpn_dummy_val_));
   Connect();
   CheckAlpn("a");
 }
 
 TEST_P(TlsConnectDatagram, ConnectSrtp) {
   EnableSrtp();
   Connect();
   CheckSrtp();
--- a/security/nss/external_tests/ssl_gtest/ssl_record_unittest.cc
+++ b/security/nss/external_tests/ssl_gtest/ssl_record_unittest.cc
@@ -1,15 +1,16 @@
 /* -*- 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 "nss.h"
 #include "ssl.h"
-#include "nss.h"
 #include "sslimpl.h"
 
 #include "databuffer.h"
 #include "gtest_utils.h"
 
 namespace nss_test {
 
 const static size_t kMacSize = 20;
--- a/security/nss/external_tests/ssl_gtest/ssl_resumption_unittest.cc
+++ b/security/nss/external_tests/ssl_gtest/ssl_resumption_unittest.cc
@@ -1,18 +1,18 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#include "ssl.h"
 #include <functional>
 #include <memory>
 #include "secerr.h"
+#include "ssl.h"
 #include "sslerr.h"
 #include "sslproto.h"
 
 extern "C" {
 // This is not something that should make you happy.
 #include "libssl_internals.h"
 }
 
@@ -323,17 +323,18 @@ TEST_F(TlsConnectTest, TestTls13Resumpti
   Connect();
   SendReceive();  // Need to read so that we absorb the session ticket.
   CheckKeys(ssl_kea_ecdh, ssl_auth_rsa_sign);
   uint16_t original_suite;
   EXPECT_TRUE(client_->cipher_suite(&original_suite));
 
   Reset();
   ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
-  TlsExtensionCapture* c1 = new TlsExtensionCapture(kTlsExtensionPreSharedKey);
+  TlsExtensionCapture* c1 =
+      new TlsExtensionCapture(ssl_tls13_pre_shared_key_xtn);
   client_->SetPacketFilter(c1);
   client_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_1,
                            SSL_LIBRARY_VERSION_TLS_1_3);
   server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_1,
                            SSL_LIBRARY_VERSION_TLS_1_3);
   ExpectResumption(RESUME_TICKET);
   Connect();
   SendReceive();
@@ -343,17 +344,18 @@ TEST_F(TlsConnectTest, TestTls13Resumpti
   ASSERT_LT(0U, initialTicket.len());
 
   ScopedCERTCertificate cert1(SSL_PeerCertificate(client_->ssl_fd()));
   ASSERT_TRUE(!!cert1.get());
 
   Reset();
   ClearStats();
   ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
-  TlsExtensionCapture* c2 = new TlsExtensionCapture(kTlsExtensionPreSharedKey);
+  TlsExtensionCapture* c2 =
+      new TlsExtensionCapture(ssl_tls13_pre_shared_key_xtn);
   client_->SetPacketFilter(c2);
   client_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_1,
                            SSL_LIBRARY_VERSION_TLS_1_3);
   server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_1,
                            SSL_LIBRARY_VERSION_TLS_1_3);
   ExpectResumption(RESUME_TICKET);
   Connect();
   SendReceive();
@@ -383,17 +385,17 @@ TEST_F(TlsConnectTest, DisableClientPSKA
                            SSL_LIBRARY_VERSION_TLS_1_3);
   Connect();
   SendReceive();  // Need to read so that we absorb the session ticket.
   CheckKeys(ssl_kea_ecdh, ssl_auth_rsa_sign);
 
   Reset();
   ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
   TlsExtensionCapture* capture =
-      new TlsExtensionCapture(kTlsExtensionPreSharedKey);
+      new TlsExtensionCapture(ssl_tls13_pre_shared_key_xtn);
   client_->SetPacketFilter(capture);
   client_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_1,
                            SSL_LIBRARY_VERSION_TLS_1_3);
   server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_1,
                            SSL_LIBRARY_VERSION_TLS_1_3);
   // We need to disable ALL PSK cipher suites with the same symmetric cipher and
   // PRF hash.  Otherwise the server will just use a different key exchange.
   client_->DisableAllCiphers();
@@ -412,20 +414,20 @@ TEST_F(TlsConnectTest, DisableServerPSKA
                            SSL_LIBRARY_VERSION_TLS_1_3);
   Connect();
   SendReceive();  // Need to read so that we absorb the session ticket.
   CheckKeys(ssl_kea_ecdh, ssl_auth_rsa_sign);
 
   Reset();
   ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
   TlsExtensionCapture* clientCapture =
-      new TlsExtensionCapture(kTlsExtensionPreSharedKey);
+      new TlsExtensionCapture(ssl_tls13_pre_shared_key_xtn);
   client_->SetPacketFilter(clientCapture);
   TlsExtensionCapture* serverCapture =
-      new TlsExtensionCapture(kTlsExtensionPreSharedKey);
+      new TlsExtensionCapture(ssl_tls13_pre_shared_key_xtn);
   server_->SetPacketFilter(serverCapture);
   client_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_1,
                            SSL_LIBRARY_VERSION_TLS_1_3);
   server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_1,
                            SSL_LIBRARY_VERSION_TLS_1_3);
   // We need to disable ALL PSK cipher suites with the same symmetric cipher and
   // PRF hash.  Otherwise the server will just use a different key exchange.
   server_->DisableAllCiphers();
--- a/security/nss/external_tests/ssl_gtest/ssl_staticrsa_unittest.cc
+++ b/security/nss/external_tests/ssl_gtest/ssl_staticrsa_unittest.cc
@@ -1,18 +1,18 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#include "ssl.h"
 #include <functional>
 #include <memory>
 #include "secerr.h"
+#include "ssl.h"
 #include "sslerr.h"
 #include "sslproto.h"
 
 extern "C" {
 // This is not something that should make you happy.
 #include "libssl_internals.h"
 }
 
--- a/security/nss/external_tests/ssl_gtest/ssl_v2_client_hello_unittest.cc
+++ b/security/nss/external_tests/ssl_gtest/ssl_v2_client_hello_unittest.cc
@@ -1,16 +1,16 @@
 /* -*- 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 "pk11pub.h"
 #include "ssl.h"
-#include "pk11pub.h"
 #include "sslerr.h"
 #include "sslproto.h"
 
 extern "C" {
 // This is not something that should make you happy.
 #include "libssl_internals.h"
 }
 
--- a/security/nss/external_tests/ssl_gtest/ssl_version_unittest.cc
+++ b/security/nss/external_tests/ssl_gtest/ssl_version_unittest.cc
@@ -1,16 +1,16 @@
 /* -*- 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 "secerr.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"
--- a/security/nss/external_tests/ssl_gtest/test_io.cc
+++ b/security/nss/external_tests/ssl_gtest/test_io.cc
@@ -255,16 +255,23 @@ static PRStatus DummyConnectContinue(PRF
 
 static int32_t DummyReserved(PRFileDesc *f) {
   UNIMPLEMENTED();
   return -1;
 }
 
 DummyPrSocket::~DummyPrSocket() { Reset(); }
 
+void DummyPrSocket::SetPacketFilter(PacketFilter *filter) {
+  if (filter_) {
+    delete filter_;
+  }
+  filter_ = filter;
+}
+
 void DummyPrSocket::Reset() {
   delete filter_;
   if (peer_) {
     peer_->SetPeer(nullptr);
     peer_ = nullptr;
   }
   while (!input_.empty()) {
     Packet *front = input_.front();
--- a/security/nss/external_tests/ssl_gtest/test_io.h
+++ b/security/nss/external_tests/ssl_gtest/test_io.h
@@ -53,17 +53,17 @@ class DummyPrSocket {
   ~DummyPrSocket();
 
   static PRFileDesc* CreateFD(const std::string& name,
                               Mode mode);  // Returns an FD.
   static DummyPrSocket* GetAdapter(PRFileDesc* fd);
 
   DummyPrSocket* peer() const { return peer_; }
   void SetPeer(DummyPrSocket* peer) { peer_ = peer; }
-  void SetPacketFilter(PacketFilter* filter) { filter_ = filter; }
+  void SetPacketFilter(PacketFilter* filter);
   // Drops peer, packet filter and any outstanding packets.
   void Reset();
 
   void PacketReceived(const DataBuffer& data);
   int32_t Read(void* data, int32_t len);
   int32_t Recv(void* buf, int32_t buflen);
   int32_t Write(const void* buf, int32_t length);
 
--- a/security/nss/external_tests/ssl_gtest/tls_agent.cc
+++ b/security/nss/external_tests/ssl_gtest/tls_agent.cc
@@ -87,16 +87,23 @@ TlsAgent::~TlsAgent() {
     PR_Close(pr_fd_);
   }
 
   if (ssl_fd_) {
     PR_Close(ssl_fd_);
   }
 }
 
+void TlsAgent::SetState(State state) {
+  if (state_ == state) return;
+
+  LOG("Changing state from " << state_ << " to " << state);
+  state_ = state;
+}
+
 bool TlsAgent::ConfigServerCert(const std::string& name, bool updateKeyBits,
                                 const SSLExtraServerCertData* serverCertData) {
   ScopedCERTCertificate cert(PK11_FindCertFromNickname(name.c_str(), nullptr));
   EXPECT_NE(nullptr, cert.get());
   if (!cert.get()) return false;
 
   ScopedSECKEYPublicKey pub(CERT_ExtractPublicKey(cert.get()));
   EXPECT_NE(nullptr, pub.get());
@@ -116,28 +123,20 @@ bool TlsAgent::ConfigServerCert(const st
                             serverCertData ? sizeof(*serverCertData) : 0);
   return rv == SECSuccess;
 }
 
 // The tests expect that only curves secp256r1, secp384r1, and secp521r1
 // (NIST P-256, P-384, and P-521) are enabled. Disable all other curves.
 void TlsAgent::DisableLameGroups() {
 #ifdef NSS_ECC_MORE_THAN_SUITE_B
-  static const SSLNamedGroup lame_groups[] = {
-      ssl_grp_ec_sect163k1, ssl_grp_ec_sect163r1, ssl_grp_ec_sect163r2,
-      ssl_grp_ec_sect193r1, ssl_grp_ec_sect193r2, ssl_grp_ec_sect233k1,
-      ssl_grp_ec_sect233r1, ssl_grp_ec_sect239k1, ssl_grp_ec_sect283k1,
-      ssl_grp_ec_sect283r1, ssl_grp_ec_sect409k1, ssl_grp_ec_sect409r1,
-      ssl_grp_ec_sect571k1, ssl_grp_ec_sect571r1, ssl_grp_ec_secp160k1,
-      ssl_grp_ec_secp160r1, ssl_grp_ec_secp160r2, ssl_grp_ec_secp192k1,
-      ssl_grp_ec_secp192r1, ssl_grp_ec_secp224k1, ssl_grp_ec_secp224r1,
-      ssl_grp_ec_secp256k1};
-  for (size_t i = 0; i < PR_ARRAY_SIZE(lame_groups); ++i) {
-    ConfigNamedGroup(lame_groups[i], false);
-  }
+  static const SSLNamedGroup groups[] = {
+      ssl_grp_ec_secp256r1, ssl_grp_ec_secp384r1, ssl_grp_ec_secp521r1,
+      ssl_grp_ffdhe_2048,   ssl_grp_ffdhe_3072,   ssl_grp_ffdhe_4096};
+  ConfigNamedGroups(groups, PR_ARRAY_SIZE(groups));
 #endif
 }
 
 bool TlsAgent::EnsureTlsSetup(PRFileDesc* modelSocket) {
   // Don't set up twice
   if (ssl_fd_) return true;
 
   if (adapter_->mode() == STREAM) {
@@ -285,19 +284,19 @@ void TlsAgent::EnableCiphersByAuthType(S
 }
 
 void TlsAgent::EnableSingleCipher(uint16_t cipher) {
   DisableAllCiphers();
   SECStatus rv = SSL_CipherPrefSet(ssl_fd_, cipher, PR_TRUE);
   EXPECT_EQ(SECSuccess, rv);
 }
 
-void TlsAgent::ConfigNamedGroup(SSLNamedGroup group, bool en) {
+void TlsAgent::ConfigNamedGroups(const SSLNamedGroup* groups, size_t num) {
   EXPECT_TRUE(EnsureTlsSetup());
-  SECStatus rv = SSL_NamedGroupPrefSet(ssl_fd_, group, en ? PR_TRUE : PR_FALSE);
+  SECStatus rv = SSL_NamedGroupConfig(ssl_fd_, groups, num);
   EXPECT_EQ(SECSuccess, rv);
 }
 
 void TlsAgent::SetSessionTicketsEnabled(bool en) {
   EXPECT_TRUE(EnsureTlsSetup());
 
   SECStatus rv = SSL_OptionSet(ssl_fd_, SSL_ENABLE_SESSION_TICKETS,
                                en ? PR_TRUE : PR_FALSE);
@@ -797,16 +796,17 @@ void TlsAgentTestBase::EnsureInit() {
   if (!agent_) {
     Init();
   }
 }
 
 void TlsAgentTestBase::ProcessMessage(const DataBuffer& buffer,
                                       TlsAgent::State expected_state,
                                       int32_t error_code) {
+  std::cerr << "Process message: " << buffer << std::endl;
   EnsureInit();
   agent_->adapter()->PacketReceived(buffer);
   agent_->Handshake();
 
   ASSERT_EQ(expected_state, agent_->state());
 
   if (expected_state == TlsAgent::STATE_ERROR) {
     ASSERT_EQ(error_code, agent_->error_code());
@@ -825,29 +825,32 @@ void TlsAgentTestBase::MakeRecord(Mode m
     index = out->Write(index, seq_num & PR_UINT32_MAX, 4);
   }
   index = out->Write(index, len, 2);
   out->Write(index, buf, len);
 }
 
 void TlsAgentTestBase::MakeRecord(uint8_t type, uint16_t version,
                                   const uint8_t* buf, size_t len,
-                                  DataBuffer* out, uint64_t seq_num) {
+                                  DataBuffer* out, uint64_t seq_num) const {
   MakeRecord(mode_, type, version, buf, len, out, seq_num);
 }
 
 void TlsAgentTestBase::MakeHandshakeMessage(uint8_t hs_type,
                                             const uint8_t* data, size_t hs_len,
-                                            DataBuffer* out, uint64_t seq_num) {
+                                            DataBuffer* out,
+                                            uint64_t seq_num) const {
   return MakeHandshakeMessageFragment(hs_type, data, hs_len, out, seq_num, 0,
                                       0);
 }
+
 void TlsAgentTestBase::MakeHandshakeMessageFragment(
     uint8_t hs_type, const uint8_t* data, size_t hs_len, DataBuffer* out,
-    uint64_t seq_num, uint32_t fragment_offset, uint32_t fragment_length) {
+    uint64_t seq_num, uint32_t fragment_offset,
+    uint32_t fragment_length) const {
   size_t index = 0;
   if (!fragment_length) fragment_length = hs_len;
   index = out->Write(index, hs_type, 1);  // Handshake record type.
   index = out->Write(index, hs_len, 3);   // Handshake length
   if (mode_ == DGRAM) {
     index = out->Write(index, seq_num, 2);
     index = out->Write(index, fragment_offset, 3);
     index = out->Write(index, fragment_length, 3);
--- a/security/nss/external_tests/ssl_gtest/tls_agent.h
+++ b/security/nss/external_tests/ssl_gtest/tls_agent.h
@@ -136,32 +136,32 @@ class TlsAgent : public PollTarget {
   void ResetSentBytes();  // Hack to test drops.
   void EnableExtendedMasterSecret();
   void CheckExtendedMasterSecret(bool expected);
   void CheckEarlyDataAccepted(bool expected);
   void DisableRollbackDetection();
   void EnableCompression();
   void SetDowngradeCheckVersion(uint16_t version);
   void CheckSecretsDestroyed();
-  void ConfigNamedGroup(SSLNamedGroup group, bool en);
+  void ConfigNamedGroups(const SSLNamedGroup* groups, size_t num);
 
   const std::string& name() const { return name_; }
 
   Role role() const { return role_; }
   std::string role_str() const { return role_ == SERVER ? "server" : "client"; }
 
   State state() const { return state_; }
 
   const CERTCertificate* peer_cert() const {
     return SSL_PeerCertificate(ssl_fd_);
   }
 
   const char* state_str() const { return state_str(state()); }
 
-  const char* state_str(State state) const { return states[state]; }
+  static const char* state_str(State state) { return states[state]; }
 
   PRFileDesc* ssl_fd() { 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_; }
@@ -208,23 +208,17 @@ class TlsAgent : public PollTarget {
 
   void SetSniCallback(SniCallbackFunction sni_callback) {
     sni_callback_ = sni_callback;
   }
 
  private:
   const static char* states[];
 
-  void SetState(State state) {
-    if (state_ == state) return;
-
-    LOG("Changing state from " << state_str(state_) << " to "
-                               << state_str(state));
-    state_ = state;
-  }
+  void SetState(State state);
 
   // Dummy auth certificate hook.
   static SECStatus AuthCertificateHook(void* arg, PRFileDesc* fd,
                                        PRBool checksig, PRBool isServer) {
     TlsAgent* agent = reinterpret_cast<TlsAgent*>(arg);
     agent->CheckPreliminaryInfo();
     agent->auth_certificate_hook_called_ = true;
     if (agent->auth_certificate_callback_) {
@@ -335,42 +329,47 @@ class TlsAgent : public PollTarget {
   size_t send_ctr_;
   size_t recv_ctr_;
   bool expect_readwrite_error_;
   HandshakeCallbackFunction handshake_callback_;
   AuthCertificateCallbackFunction auth_certificate_callback_;
   SniCallbackFunction sni_callback_;
 };
 
+inline std::ostream& operator<<(std::ostream& stream,
+                                const TlsAgent::State& state) {
+  return stream << TlsAgent::state_str(state);
+}
+
 class TlsAgentTestBase : public ::testing::Test {
  public:
   static ::testing::internal::ParamGenerator<std::string> kTlsRolesAll;
 
   TlsAgentTestBase(TlsAgent::Role role, Mode mode)
       : agent_(nullptr), fd_(nullptr), role_(role), mode_(mode) {}
   ~TlsAgentTestBase() {
     if (fd_) {
       PR_Close(fd_);
     }
   }
 
   void SetUp();
   void TearDown();
 
-  void MakeRecord(uint8_t type, uint16_t version, const uint8_t* buf,
-                  size_t len, DataBuffer* out, uint64_t seq_num = 0);
   static void MakeRecord(Mode mode, uint8_t type, uint16_t version,
                          const uint8_t* buf, size_t len, DataBuffer* out,
                          uint64_t seq_num = 0);
+  void MakeRecord(uint8_t type, uint16_t version, const uint8_t* buf,
+                  size_t len, DataBuffer* out, uint64_t seq_num = 0) const;
   void MakeHandshakeMessage(uint8_t hs_type, const uint8_t* data, size_t hs_len,
-                            DataBuffer* out, uint64_t seq_num = 0);
+                            DataBuffer* out, uint64_t seq_num = 0) const;
   void MakeHandshakeMessageFragment(uint8_t hs_type, const uint8_t* data,
                                     size_t hs_len, DataBuffer* out,
                                     uint64_t seq_num, uint32_t fragment_offset,
-                                    uint32_t fragment_length);
+                                    uint32_t fragment_length) const;
   static void MakeTrivialHandshakeRecord(uint8_t hs_type, size_t hs_len,
                                          DataBuffer* out);
   static inline TlsAgent::Role ToRole(const std::string& str) {
     return str == "CLIENT" ? TlsAgent::CLIENT : TlsAgent::SERVER;
   }
 
   static inline Mode ToMode(const std::string& str) {
     return str == "TLS" ? STREAM : DGRAM;
--- a/security/nss/external_tests/ssl_gtest/tls_connect.cc
+++ b/security/nss/external_tests/ssl_gtest/tls_connect.cc
@@ -6,16 +6,17 @@
 
 #include "tls_connect.h"
 extern "C" {
 #include "libssl_internals.h"
 }
 
 #include <iostream>
 
+#include "databuffer.h"
 #include "gtest_utils.h"
 #include "sslproto.h"
 
 extern std::string g_working_dir_path;
 
 namespace nss_test {
 
 static const std::string kTlsModesStreamArr[] = {"TLS"};
@@ -115,16 +116,49 @@ TlsConnectTestBase::TlsConnectTestBase(M
   } else {
     v = VersionString(version_);
   }
   std::cerr << "Version: " << mode_ << " " << v << std::endl;
 }
 
 TlsConnectTestBase::~TlsConnectTestBase() {}
 
+// Check the group of each of the supported groups
+void TlsConnectTestBase::CheckGroups(
+    const DataBuffer& groups, std::function<void(SSLNamedGroup)> check_group) {
+  DuplicateGroupChecker group_set;
+  uint32_t tmp = 0;
+  EXPECT_TRUE(groups.Read(0, 2, &tmp));
+  EXPECT_EQ(groups.len() - 2, static_cast<size_t>(tmp));
+  for (size_t i = 2; i < groups.len(); i += 2) {
+    EXPECT_TRUE(groups.Read(i, 2, &tmp));
+    SSLNamedGroup group = static_cast<SSLNamedGroup>(tmp);
+    group_set.AddAndCheckGroup(group);
+    check_group(group);
+  }
+}
+
+// Check the group of each of the shares
+void TlsConnectTestBase::CheckShares(
+    const DataBuffer& shares, std::function<void(SSLNamedGroup)> check_group) {
+  DuplicateGroupChecker group_set;
+  uint32_t tmp = 0;
+  EXPECT_TRUE(shares.Read(0, 2, &tmp));
+  EXPECT_EQ(shares.len() - 2, static_cast<size_t>(tmp));
+  size_t i;
+  for (i = 2; i < shares.len(); i += 4 + tmp) {
+    ASSERT_TRUE(shares.Read(i, 2, &tmp));
+    SSLNamedGroup group = static_cast<SSLNamedGroup>(tmp);
+    group_set.AddAndCheckGroup(group);
+    check_group(group);
+    ASSERT_TRUE(shares.Read(i + 2, 2, &tmp));
+  }
+  EXPECT_EQ(shares.len(), i);
+}
+
 void TlsConnectTestBase::ClearStats() {
   // Clear statistics.
   SSL3Statistics* stats = SSL_GetStatistics();
   memset(stats, 0, sizeof(*stats));
 }
 
 void TlsConnectTestBase::ClearServerCache() {
   SSL_ShutdownServerSessionIDCache();
@@ -339,33 +373,37 @@ void TlsConnectTestBase::ConfigureSessio
     // even if it won't be used for the connection.
     server_->ConfigServerCert(TlsAgent::kServerRsaDecrypt);
   }
 }
 
 void TlsConnectTestBase::CheckResumption(SessionResumptionMode expected) {
   EXPECT_NE(RESUME_BOTH, expected);
 
-  int resume_ct = expected ? 1 : 0;
-  int stateless_ct = (expected & RESUME_TICKET) ? 1 : 0;
+  int resume_count = expected ? 1 : 0;
+  int stateless_count = (expected & RESUME_TICKET) ? 1 : 0;
 
+  // Note: hch == server counter; hsh == client counter.
   SSL3Statistics* stats = SSL_GetStatistics();
-  EXPECT_EQ(resume_ct, stats->hch_sid_cache_hits);
-  EXPECT_EQ(resume_ct, stats->hsh_sid_cache_hits);
+  EXPECT_EQ(resume_count, stats->hch_sid_cache_hits);
+  EXPECT_EQ(resume_count, stats->hsh_sid_cache_hits);
+
+  EXPECT_EQ(stateless_count, stats->hch_sid_stateless_resumes);
+  EXPECT_EQ(stateless_count, stats->hsh_sid_stateless_resumes);
 
-  EXPECT_EQ(stateless_ct, stats->hch_sid_stateless_resumes);
-  EXPECT_EQ(stateless_ct, stats->hsh_sid_stateless_resumes);
-
-  if (resume_ct && client_->version() < SSL_LIBRARY_VERSION_TLS_1_3) {
-    // Check that the last two session ids match.
-    // TLS 1.3 doesn't do session id-based resumption. It's all
-    // tickets.
-    EXPECT_EQ(2U, session_ids_.size());
-    EXPECT_EQ(session_ids_[session_ids_.size() - 1],
-              session_ids_[session_ids_.size() - 2]);
+  if (expected != RESUME_NONE) {
+    if (client_->version() < SSL_LIBRARY_VERSION_TLS_1_3) {
+      // Check that the last two session ids match.
+      ASSERT_EQ(2U, session_ids_.size());
+      EXPECT_EQ(session_ids_[session_ids_.size() - 1],
+                session_ids_[session_ids_.size() - 2]);
+    } else {
+      // TLS 1.3 only uses tickets.
+      EXPECT_TRUE(expected & RESUME_TICKET);
+    }
   }
 }
 
 void TlsConnectTestBase::EnableAlpn() {
   client_->EnableAlpn(alpn_dummy_val_, sizeof(alpn_dummy_val_));
   server_->EnableAlpn(alpn_dummy_val_, sizeof(alpn_dummy_val_));
 }
 
@@ -380,21 +418,16 @@ void TlsConnectTestBase::EnsureModelSock
     ASSERT_EQ(server_model_, nullptr);
     client_model_ = new TlsAgent(TlsAgent::kClient, TlsAgent::CLIENT, mode_);
     server_model_ = new TlsAgent(TlsAgent::kServerRsa, TlsAgent::SERVER, mode_);
   }
 
   // Initialise agents.
   ASSERT_TRUE(client_model_->Init());
   ASSERT_TRUE(server_model_->Init());
-
-  // Set desired properties on the models.
-  // For now only ALPN.
-  client_model_->EnableAlpn(alpn_dummy_val_, sizeof(alpn_dummy_val_));
-  server_model_->EnableAlpn(alpn_dummy_val_, sizeof(alpn_dummy_val_));
 }
 
 void TlsConnectTestBase::CheckAlpn(const std::string& val) {
   client_->CheckAlpn(SSL_NEXT_PROTO_SELECTED, val);
   server_->CheckAlpn(SSL_NEXT_PROTO_NEGOTIATED, val);
 }
 
 void TlsConnectTestBase::EnableSrtp() {
--- a/security/nss/external_tests/ssl_gtest/tls_connect.h
+++ b/security/nss/external_tests/ssl_gtest/tls_connect.h
@@ -68,16 +68,20 @@ class TlsConnectTestBase : public ::test
   void Connect();
   // Check that the connection was successfully established.
   void CheckConnected();
   // Connect and expect it to fail.
   void ConnectExpectFail();
   void ConnectWithCipherSuite(uint16_t cipher_suite);
   void CheckKeys(SSLKEAType kea_type, SSLAuthType auth_type,
                  size_t kea_size = 0) const;
+  void CheckGroups(const DataBuffer& groups,
+                   std::function<void(SSLNamedGroup)> check_group);
+  void CheckShares(const DataBuffer& shares,
+                   std::function<void(SSLNamedGroup)> check_group);
 
   void SetExpectedVersion(uint16_t version);
   // Expect resumption of a particular type.
   void ExpectResumption(SessionResumptionMode expected);
   void DisableAllCiphers();
   void EnableOnlyStaticRsaCiphers();
   void EnableOnlyDheCiphers();
   void EnableSomeEcdhCiphers();
@@ -117,16 +121,29 @@ class TlsConnectTestBase : public ::test
 
  private:
   void CheckResumption(SessionResumptionMode expected);
   void CheckExtendedMasterSecret();
   void CheckEarlyDataAccepted();
 
   bool expect_extended_master_secret_;
   bool expect_early_data_accepted_;
+
+  // Track groups and make sure that there are no duplicates.
+  class DuplicateGroupChecker {
+   public:
+    void AddAndCheckGroup(SSLNamedGroup group) {
+      EXPECT_EQ(groups_.end(), groups_.find(group))
+          << "Group " << group << " should not be duplicated";
+      groups_.insert(group);
+    }
+
+   private:
+    std::set<SSLNamedGroup> groups_;
+  };
 };
 
 // A non-parametrized TLS test base.
 class TlsConnectTest : public TlsConnectTestBase {
  public:
   TlsConnectTest() : TlsConnectTestBase(STREAM, 0) {}
 };
 
--- a/security/nss/external_tests/ssl_gtest/tls_filter.cc
+++ b/security/nss/external_tests/ssl_gtest/tls_filter.cc
@@ -459,9 +459,17 @@ PacketFilter::Action TlsInspectorClientH
     DataBuffer* output) {
   if (header.handshake_type() == kTlsHandshakeClientKeyExchange) {
     EXPECT_EQ(SECSuccess,
               SSLInt_IncrementClientHandshakeVersion(server_->ssl_fd()));
   }
   return KEEP;
 }
 
+PacketFilter::Action SelectiveDropFilter::Filter(const DataBuffer& input,
+                                                 DataBuffer* output) {
+  if (counter_ >= 32) {
+    return KEEP;
+  }
+  return ((1 << counter_++) & pattern_) ? DROP : KEEP;
+}
+
 }  // namespace nss_test
--- a/security/nss/external_tests/ssl_gtest/tls_filter.h
+++ b/security/nss/external_tests/ssl_gtest/tls_filter.h
@@ -218,20 +218,22 @@ class TlsExtensionFilter : public TlsHan
                                         const DataBuffer& input,
                                         DataBuffer* output);
 };
 
 class TlsExtensionCapture : public TlsExtensionFilter {
  public:
   TlsExtensionCapture(uint16_t ext) : extension_(ext), data_() {}
 
+  const DataBuffer& extension() const { return data_; }
+
+ protected:
   virtual PacketFilter::Action FilterExtension(uint16_t extension_type,
                                                const DataBuffer& input,
                                                DataBuffer* output);
-  const DataBuffer& extension() const { return data_; }
 
  private:
   const uint16_t extension_;
   DataBuffer data_;
 };
 
 class TlsExtensionReplacer : public TlsExtensionFilter {
  public:
@@ -276,11 +278,26 @@ class TlsInspectorClientHelloVersionChan
   virtual PacketFilter::Action FilterHandshake(const HandshakeHeader& header,
                                                const DataBuffer& input,
                                                DataBuffer* output);
 
  private:
   TlsAgent* server_;
 };
 
+// This class selectively drops complete writes.  This relies on the fact that
+// writes in libssl are on record boundaries.
+class SelectiveDropFilter : public PacketFilter {
+ public:
+  SelectiveDropFilter(uint32_t pattern) : pattern_(pattern), counter_(0) {}
+
+ protected:
+  virtual PacketFilter::Action Filter(const DataBuffer& input,
+                                      DataBuffer* output) override;
+
+ private:
+  const uint32_t pattern_;
+  uint8_t counter_;
+};
+
 }  // namespace nss_test
 
 #endif
--- a/security/nss/external_tests/ssl_gtest/tls_parser.h
+++ b/security/nss/external_tests/ssl_gtest/tls_parser.h
@@ -21,16 +21,17 @@ namespace nss_test {
 
 const uint8_t kTlsChangeCipherSpecType = 20;
 const uint8_t kTlsAlertType = 21;
 const uint8_t kTlsHandshakeType = 22;
 const uint8_t kTlsApplicationDataType = 23;
 
 const uint8_t kTlsHandshakeClientHello = 1;
 const uint8_t kTlsHandshakeServerHello = 2;
+const uint8_t kTlsHandshakeHelloRetryRequest = 6;
 const uint8_t kTlsHandshakeEncryptedExtensions = 8;
 const uint8_t kTlsHandshakeCertificate = 11;
 const uint8_t kTlsHandshakeServerKeyExchange = 12;
 const uint8_t kTlsHandshakeCertificateVerify = 15;
 const uint8_t kTlsHandshakeClientKeyExchange = 16;
 const uint8_t kTlsHandshakeFinished = 20;
 
 const uint8_t kTlsAlertWarning = 1;
@@ -41,17 +42,18 @@ const uint8_t kTlsAlertBadRecordMac = 20
 const uint8_t kTlsAlertHandshakeFailure = 40;
 const uint8_t kTlsAlertIllegalParameter = 47;
 const uint8_t kTlsAlertDecodeError = 50;
 const uint8_t kTlsAlertDecryptError = 51;
 const uint8_t kTlsAlertMissingExtension = 109;
 const uint8_t kTlsAlertUnsupportedExtension = 110;
 const uint8_t kTlsAlertNoApplicationProtocol = 120;
 
-const uint8_t kTlsExtensionPreSharedKey = 41;
+const uint16_t kTlsSigSchemeRsaPkcs1Sha1 = 0x0201;
+const uint16_t kTlsSigSchemeRsaPssSha256 = 0x0700;
 
 const uint8_t kTlsFakeChangeCipherSpec[] = {
     kTlsChangeCipherSpecType,  // Type
     0xfe,
     0xff,  // Version
     0x00,
     0x00,
     0x00,
--- a/security/nss/lib/certhigh/ocsp.c
+++ b/security/nss/lib/certhigh/ocsp.c
@@ -2190,16 +2190,20 @@ CERT_CreateOCSPRequest(CERTCertList *cer
 void
 SetRequestExts(void *object, CERTCertExtension **exts)
 {
     CERTOCSPRequest *request = (CERTOCSPRequest *)object;
 
     request->tbsRequest->requestExtensions = exts;
 }
 
+#if defined(__GNUC__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wvarargs"
+#endif
 SECStatus
 CERT_AddOCSPAcceptableResponses(CERTOCSPRequest *request,
                                 SECOidTag responseType0, ...)
 {
     void *extHandle;
     va_list ap;
     int i, count;
     SECOidTag responseType;
@@ -2256,16 +2260,19 @@ CERT_AddOCSPAcceptableResponses(CERTOCSP
 
 loser:
     if (acceptableResponses != NULL)
         PORT_Free(acceptableResponses);
     if (extHandle != NULL)
         (void)CERT_FinishExtensions(extHandle);
     return rv;
 }
+#if defined(__GNUC__)
+#pragma GCC diagnostic pop
+#endif
 
 /*
  * FUNCTION: CERT_DestroyOCSPRequest
  *   Frees an OCSP Request structure.
  * INPUTS:
  *   CERTOCSPRequest *request
  *     Pointer to CERTOCSPRequest to be freed.
  * RETURN:
--- a/security/nss/lib/ssl/SSLerrs.h
+++ b/security/nss/lib/ssl/SSLerrs.h
@@ -482,8 +482,17 @@ ER3(SSL_ERROR_MISSING_ALPN_EXTENSION, (S
 ER3(SSL_ERROR_RX_UNEXPECTED_EXTENSION, (SSL_ERROR_BASE + 151),
     "SSL received an unexpected extension.")
 
 ER3(SSL_ERROR_MISSING_SUPPORTED_GROUPS, (SSL_ERROR_BASE + 152),
     "SSL expected a supported groups extension.")
 
 ER3(SSL_ERROR_TOO_MANY_RECORDS, (SSL_ERROR_BASE + 153),
     "SSL sent or received too many records with the same symmetric key.")
+
+ER3(SSL_ERROR_RX_UNEXPECTED_HELLO_RETRY_REQUEST, (SSL_ERROR_BASE + 154),
+    "SSL received an unexpected Hello Retry Request handshake message.")
+
+ER3(SSL_ERROR_RX_MALFORMED_HELLO_RETRY_REQUEST, (SSL_ERROR_BASE + 155),
+    "SSL received a malformed Hello Retry Request handshake message.")
+
+ER3(SSL_ERROR_BAD_2ND_CLIENT_HELLO, (SSL_ERROR_BASE + 156),
+    "SSL received a second Client Hello message without a usable key share.")
--- a/security/nss/lib/ssl/derive.c
+++ b/security/nss/lib/ssl/derive.c
@@ -785,19 +785,17 @@ SSL_CanBypass(CERTCertificate *cert, SEC
                 serverKeyStrengthInBits *= BPB;
 
                 signatureKeyStrength =
                     SSL_RSASTRENGTH_TO_ECSTRENGTH(serverKeyStrengthInBits);
 
                 if (requiredECCbits > signatureKeyStrength)
                     requiredECCbits = signatureKeyStrength;
 
-                ecGroup =
-                    ssl_GetECGroupWithStrength(PR_UINT32_MAX,
-                                               requiredECCbits);
+                ecGroup = ssl_GetECGroupWithStrength(NULL, requiredECCbits);
                 rv = ssl_NamedGroup2ECParams(NULL, ecGroup, &ecParams);
                 if (rv == SECFailure) {
                     break;
                 }
                 pecParams = &ecParams;
             }
 
             if (testecdhe) {
--- a/security/nss/lib/ssl/dtlscon.c
+++ b/security/nss/lib/ssl/dtlscon.c
@@ -941,17 +941,21 @@ dtls_FinishedTimerCb(sslSocket *ss)
  * lost.
  *
  * XXX OK for now. Figure out how to handle the combination
  * of Finished lost and rehandshake
  */
 void
 dtls_RehandshakeCleanup(sslSocket *ss)
 {
-    PORT_Assert(ss->version < SSL_LIBRARY_VERSION_TLS_1_3);
+    /* Skip this if we are handling a second ClientHello. */
+    if (ss->ssl3.hs.helloRetry) {
+        return;
+    }
+    PORT_Assert((ss->version < SSL_LIBRARY_VERSION_TLS_1_3));
     dtls_CancelTimer(ss);
     ssl3_DestroyCipherSpec(ss->ssl3.pwSpec, PR_FALSE);
     ss->ssl3.hs.sendMessageSeq = 0;
     ss->ssl3.hs.recvMessageSeq = 0;
 }
 
 /* Set the MTU to the next step less than or equal to the
  * advertised value. Also used to downgrade the MTU by
@@ -1034,17 +1038,17 @@ dtls_HandleHelloVerifyRequest(sslSocket 
     }
 
     PORT_Memcpy(ss->ssl3.hs.cookie, cookie.data, cookie.len);
     ss->ssl3.hs.cookieLen = cookie.len;
 
     ssl_GetXmitBufLock(ss); /*******************************/
 
     /* Now re-send the client hello */
-    rv = ssl3_SendClientHello(ss, PR_TRUE);
+    rv = ssl3_SendClientHello(ss, client_hello_retransmit);
 
     ssl_ReleaseXmitBufLock(ss); /*******************************/
 
     if (rv == SECSuccess)
         return rv;
 
 alert_loser:
     (void)SSL3_SendAlert(ss, alert_fatal, desc);
@@ -1184,24 +1188,26 @@ DTLS_GetHandshakeTimeout(PRFileDesc *soc
  * seems like a good tradeoff for implementation effort and is
  * consistent with the guidance of RFC 6347 Sections 4.1 and 4.2.4.1.
  *
  * If the packet is not relevant, this function returns PR_FALSE.
  * If the packet is relevant, this function returns PR_TRUE
  * and sets |*seqNum| to the packet sequence number.
  */
 PRBool
-dtls_IsRelevant(sslSocket *ss, const ssl3CipherSpec *crSpec,
-                const SSL3Ciphertext *cText, sslSequenceNumber *seqNum)
+dtls_IsRelevant(sslSocket *ss, const SSL3Ciphertext *cText,
+                PRBool *sameEpoch, PRUint64 *seqNum)
 {
+    const ssl3CipherSpec *crSpec = ss->ssl3.crSpec;
     DTLSEpoch epoch;
     sslSequenceNumber dtls_seq_num;
 
     epoch = cText->seq_num >> 48;
-    if (crSpec->epoch != epoch) {
+    *sameEpoch = crSpec->epoch == epoch;
+    if (!*sameEpoch) {
         SSL_DBG(("%d: SSL3[%d]: dtls_IsRelevant, received packet "
                  "from irrelevant epoch %d",
                  SSL_GETPID(), ss->fd, epoch));
         return PR_FALSE;
     }
 
     dtls_seq_num = cText->seq_num & RECORD_SEQ_MAX;
     if (dtls_RecordGetRecvd(&crSpec->recvdRecords, dtls_seq_num) != 0) {
@@ -1224,21 +1230,27 @@ dtls_IsRelevant(sslSocket *ss, const ssl
  * handshake messages do not trigger retransmission, so we are a little more
  * exposed to loss than is ideal.
  *
  * Note: This isn't an issue in earlier versions because the second-to-last
  * flight (sent by the server) includes the Finished message, which is not
  * dropped because it has the same epoch that the client currently expects.
  */
 SECStatus
-dtls_MaybeRetransmitHandshake(sslSocket *ss, const SSL3Ciphertext *cText)
+dtls_MaybeRetransmitHandshake(sslSocket *ss, const SSL3Ciphertext *cText,
+                              PRBool sameEpoch)
 {
     SECStatus rv = SECSuccess;
     DTLSEpoch messageEpoch = cText->seq_num >> 48;
 
+    /* Drop messages from other epochs if we are ignoring things. */
+    if (!sameEpoch && ss->ssl3.hs.zeroRttIgnore != ssl_0rtt_ignore_none) {
+        return SECSuccess;
+    }
+
     if (!ss->sec.isServer && ss->version >= SSL_LIBRARY_VERSION_TLS_1_3 &&
         messageEpoch == 0 && cText->type == content_handshake) {
         ssl_GetSSL3HandshakeLock(ss);
         if (ss->ssl3.hs.rtTimerCb == dtls_FinishedTimerCb &&
             ss->ssl3.hs.ws == idle_handshake) {
             rv = dtls_RetransmitDetected(ss);
         }
         ssl_ReleaseSSL3HandshakeLock(ss);
--- a/security/nss/lib/ssl/ssl.def
+++ b/security/nss/lib/ssl/ssl.def
@@ -203,12 +203,12 @@ SSL_SetDowngradeCheckVersion;
 ;+NSS_3.24 {    # NSS 3.24 release
 ;+    global:
 SSL_ConfigServerCert;
 ;+    local:
 ;+*;
 ;+};
 ;+NSS_3.27 {    # NSS 3.27 release
 ;+    global:
-SSL_NamedGroupPrefSet;
+SSL_NamedGroupConfig;
 ;+    local:
 ;+*;
 ;+};
--- a/security/nss/lib/ssl/ssl.h
+++ b/security/nss/lib/ssl/ssl.h
@@ -211,17 +211,17 @@ SSL_IMPORT PRFileDesc *DTLS_ImportFD(PRF
  * (formerly supported_curves) to signal which pre-defined groups are OK.
  *
  * This option causes an NSS client to use this extension and demand that those
  * groups be used.  A client will signal any enabled DHE groups in the
  * supported_groups extension and reject groups that don't match what it has
  * enabled.  A server will only negotiate TLS_DHE_* cipher suites if the
  * client includes the extension.
  *
- * See SSL_NamedGroupPrefSet() for how to control which groups are enabled.
+ * See SSL_NamedGroupConfig() for how to control which groups are enabled.
  *
  * This option cannot be enabled if NSS is not compiled with ECC support.
  */
 #define SSL_REQUIRE_DH_NAMED_GROUPS 32
 
 /* Allow 0-RTT data (for TLS 1.3).
  *
  * When this option is set, the server's session tickets will contain
@@ -387,26 +387,26 @@ SSL_IMPORT SECStatus SSL_SignaturePrefGe
 
 /*
 ** Returns the maximum number of signature algorithms that are supported and
 ** can be set or retrieved using SSL_SignaturePrefSet or SSL_SignaturePrefGet.
 */
 SSL_IMPORT unsigned int SSL_SignatureMaxCount();
 
 /*
-** Enable or disable a named group (see SSLNamedGroup).  This includes both EC
-** and FF groups using in Diffie-Hellman key exchange, as well as the EC groups
-** used in ECDSA signatures.  By default libssl enables all supported groups.
-** NSS uses its own preferences to select a group though it will select the
-** first group from SSL_DHEGroupPrefSet if that was called.
+** Define custom priorities for EC and FF groups used in DH key exchange and EC
+** groups for ECDSA. This only changes the order of enabled lists (and thus
+** their priorities) and enables all groups in |groups| while disabling all other
+** groups.
 */
-SSL_IMPORT SECStatus SSL_NamedGroupPrefSet(PRFileDesc *fd, SSLNamedGroup group,
-                                           PRBool enable);
+SSL_IMPORT SECStatus SSL_NamedGroupConfig(PRFileDesc *fd,
+                                          const SSLNamedGroup *groups,
+                                          unsigned int num_groups);
 
-/* Deprecated: use SSL_NamedGroupPrefSet() instead.
+/* Deprecated: use SSL_NamedGroupConfig() instead.
 ** SSL_DHEGroupPrefSet is used to configure the set of allowed/enabled DHE group
 ** parameters that can be used by NSS for the given server socket.
 ** The first item in the array is used as the default group, if no other
 ** selection criteria can be used by NSS.
 ** The set is provided as an array of identifiers as defined by SSLDHEGroupType.
 ** If more than one group identifier is provided, NSS will select the one to use.
 ** For example, a TLS extension sent by the client might indicate a preference.
 */
@@ -427,21 +427,21 @@ SSL_IMPORT SECStatus SSL_DHEGroupPrefSet
 ** It is allowed to call this API will a NULL value for parameter fd,
 ** which will prepare the global parameters that NSS will reuse for the remainder
 ** of the process lifetime. This can be used early after startup of a process,
 ** to avoid a delay when handling incoming client connections.
 ** This preparation with a NULL for parameter fd will NOT enable the weak group
 ** on sockets. The function needs to be called again for every socket that
 ** should use the weak group.
 **
-** It is allowed to use this API in combination with the SSL_NamedGroupPrefSet API.
-** If both APIs have been called, the weakest group will be used,
-** unless it is certain that the client supports larger group parameters.
-** The weak group will be used as the default group, overriding the preference
-** for the first group potentially set with a call to SSL_DHEGroupPrefSet.
+** It is allowed to use this API in combination with the SSL_NamedGroupConfig API.
+** If both APIs have been called, the weakest group will be used, unless it is
+** certain that the client supports larger group parameters. The weak group will
+** be used as the default group for TLS <= 1.2, overriding the preference for
+** the first group potentially set with a call to SSL_NamedGroupConfig.
 */
 SSL_IMPORT SECStatus SSL_EnableWeakDHEPrimeGroup(PRFileDesc *fd, PRBool enabled);
 
 /* SSL Version Range API
 **
 ** This API should be used to control SSL 3.0 & TLS support instead of the
 ** older SSL_Option* API; however, the SSL_Option* API MUST still be used to
 ** control SSL 2.0 support. In this version of libssl, SSL 3.0 and TLS 1.0 are
--- a/security/nss/lib/ssl/ssl3con.c
+++ b/security/nss/lib/ssl/ssl3con.c
@@ -76,16 +76,20 @@ static SECStatus Null_Cipher(void *ctx, 
 #ifndef NO_PKCS11_BYPASS
 static SECStatus ssl3_AESGCMBypass(ssl3KeyMaterial *keys, PRBool doDecrypt,
                                    unsigned char *out, int *outlen, int maxout,
                                    const unsigned char *in, int inlen,
                                    const unsigned char *additionalData,
                                    int additionalDataLen);
 #endif
 
+static CK_MECHANISM_TYPE ssl3_GetHashMechanismByHashType(SSLHashType hashType);
+static CK_MECHANISM_TYPE ssl3_GetMgfMechanismByHashType(SSLHashType hash);
+PRBool ssl_IsRsaPssSignatureScheme(SignatureScheme scheme);
+
 #define MAX_SEND_BUF_LENGTH 32000 /* watch for 16-bit integer overflow */
 #define MIN_SEND_BUF_LENGTH 4000
 
 /* This list of SSL3 cipher suites is sorted in descending order of
  * precedence (desirability).  It only includes cipher suites we implement.
  * This table is modified by SSL3_SetPolicy(). The ordering of cipher suites
  * in this table must match the ordering in SSL_ImplementedCiphers (sslenum.c)
  *
@@ -205,20 +209,19 @@ static ssl3CipherSuiteCfg cipherSuites[s
  * order of signature types is based on the same rules for ordering we use for
  * cipher suites just for consistency.
  */
 static const SignatureScheme defaultSignatureSchemes[] = {
     ssl_sig_ecdsa_secp256r1_sha256,
     ssl_sig_ecdsa_secp384r1_sha384,
     ssl_sig_ecdsa_secp521r1_sha512,
     ssl_sig_ecdsa_sha1,
-    /* bug 1280439 will restore these */
-    /* ssl_sig_rsa_pss_sha256,
-       ssl_sig_rsa_pss_sha384,
-       ssl_sig_rsa_pss_sha512, */
+    ssl_sig_rsa_pss_sha256,
+    ssl_sig_rsa_pss_sha384,
+    ssl_sig_rsa_pss_sha512,
     ssl_sig_rsa_pkcs1_sha256,
     ssl_sig_rsa_pkcs1_sha384,
     ssl_sig_rsa_pkcs1_sha512,
     ssl_sig_rsa_pkcs1_sha1,
     ssl_sig_dsa_sha256,
     ssl_sig_dsa_sha384,
     ssl_sig_dsa_sha512,
     ssl_sig_dsa_sha1
@@ -339,19 +342,16 @@ static const ssl3BulkCipherDef bulk_ciph
      SEC_OID_AES_128_GCM, "AES-128-GCM", MR_128},
     {cipher_aes_256_gcm,  calg_aes_gcm,  32,32, type_aead,   4, 0,16, 8,
      SEC_OID_AES_256_GCM, "AES-256-GCM", MR_128},
     {cipher_chacha20,     calg_chacha20, 32,32, type_aead,  12, 0,16, 0,
      SEC_OID_CHACHA20_POLY1305, "ChaCha20-Poly1305", MR_MAX},
     {cipher_missing,      calg_null,      0, 0, type_stream, 0, 0, 0, 0,
      SEC_OID_UNKNOWN,     "missing", 0U},
 };
-#undef MR_MAX
-#undef MR_128
-#undef MR_LOW
 
 static const ssl3KEADef kea_defs[] =
 { /* indexed by SSL3KeyExchangeAlgorithm */
     /* kea            exchKeyType signKeyType authKeyType, is_limited limit tls_keygen ephemeral  oid */
     {kea_null,           ssl_kea_null, nullKey, ssl_auth_null, PR_FALSE,   0, PR_FALSE, PR_FALSE, 0},
     {kea_rsa,            ssl_kea_rsa,  nullKey, ssl_auth_rsa_decrypt,  PR_FALSE,   0, PR_FALSE, PR_FALSE, SEC_OID_TLS_RSA},
     /* note: export suites abuse RSA, but these will be removed soon */
     {kea_rsa_export,     ssl_kea_rsa,  rsaKey, ssl_auth_rsa_sign, PR_TRUE,  512, PR_FALSE, PR_FALSE, SEC_OID_TLS_RSA_EXPORT},
@@ -617,16 +617,19 @@ ssl3_DecodeHandshakeType(int msgType)
             rv = "server_hello  (2)";
             break;
         case hello_verify_request:
             rv = "hello_verify_request (3)";
             break;
         case new_session_ticket:
             rv = "session_ticket (4)";
             break;
+        case hello_retry_request:
+            rv = "hello_retry_request (6)";
+            break;
         case encrypted_extensions:
             rv = "encrypted_extensions (8)";
             break;
         case certificate:
             rv = "certificate  (11)";
             break;
         case server_key_exchange:
             rv = "server_key_exchange (12)";
@@ -830,19 +833,20 @@ ssl_LookupCipherSuiteCfg(ssl3CipherSuite
     PORT_SetError(SSL_ERROR_UNKNOWN_CIPHER_SUITE);
     return NULL;
 }
 
 static PRBool
 ssl_NamedGroupTypeEnabled(const sslSocket *ss, NamedGroupType groupType)
 {
     unsigned int i;
-    for (i = 0; i < ssl_named_group_count; ++i) {
-        if (ssl_named_groups[i].type == groupType &&
-            ssl_NamedGroupEnabled(ss, &ssl_named_groups[i])) {
+    for (i = 0; i < SSL_NAMED_GROUP_COUNT; ++i) {
+        if (ss->namedGroupPreferences[i] &&
+            ss->namedGroupPreferences[i]->type == groupType &&
+            ssl_NamedGroupEnabled(ss, ss->namedGroupPreferences[i])) {
             return PR_TRUE;
         }
     }
     return PR_FALSE;
 }
 
 static PRBool
 ssl_KEAEnabled(const sslSocket *ss, SSLKEAType keaType)
@@ -856,16 +860,21 @@ ssl_KEAEnabled(const sslSocket *ss, SSLK
             if (ss->sec.isServer && !ss->opt.enableServerDhe) {
                 return PR_FALSE;
             }
 
             /* No need to check for a common FFDHE group if we are in TLS 1.2 or
              * earlier and named groups aren't required. */
             if (!ss->opt.requireDHENamedGroups &&
                 ss->version < SSL_LIBRARY_VERSION_TLS_1_3) {
+                /* If the client indicates support for named FFDHE groups, check
+                 * that we have one in common. */
+                if (ss->sec.isServer && ss->ssl3.hs.peerSupportsFfdheGroups) {
+                    return ssl_NamedGroupTypeEnabled(ss, group_type_ff);
+                }
                 return PR_TRUE;
             }
             /* If the server requires the extension, then the client must have
              * already sent a ffdhe group. peerSupportsFfdheGroups is set to true in
              * ssl_HandleSupportedGroupsXtn(). */
             if (ss->sec.isServer && !ss->ssl3.hs.peerSupportsFfdheGroups) {
                 return PR_FALSE;
             }
@@ -1145,22 +1154,23 @@ ssl3_GetNewRandom(SSL3Random *random)
     if (rv != SECSuccess) {
         ssl_MapLowLevelError(SSL_ERROR_GENERATE_RANDOM_FAILURE);
     }
     return rv;
 }
 
 /* Called by ssl3_SendServerKeyExchange and ssl3_SendCertificateVerify */
 SECStatus
-ssl3_SignHashes(SSL3Hashes *hash, SECKEYPrivateKey *key, SECItem *buf,
-                PRBool isTLS)
+ssl3_SignHashes(sslSocket *ss, SSL3Hashes *hash, SECKEYPrivateKey *key,
+                SECItem *buf)
 {
     SECStatus rv = SECFailure;
     PRBool doDerEncode = PR_FALSE;
-    int signatureLen;
+    PRBool isTLS = (PRBool)(ss->ssl3.pwSpec->version > SSL_LIBRARY_VERSION_3_0);
+    PRBool useRsaPss = ssl_IsRsaPssSignatureScheme(ss->ssl3.hs.signatureScheme);
     SECItem hashItem;
 
     buf->data = NULL;
 
     switch (SECKEY_GetPrivateKeyType(key)) {
         case rsaKey:
             hashItem.data = hash->u.raw;
             hashItem.len = hash->len;
@@ -1190,29 +1200,45 @@ ssl3_SignHashes(SSL3Hashes *hash, SECKEY
             }
             break;
         default:
             PORT_SetError(SEC_ERROR_INVALID_KEY);
             goto done;
     }
     PRINT_BUF(60, (NULL, "hash(es) to be signed", hashItem.data, hashItem.len));
 
-    if (hash->hashAlg == ssl_hash_none) {
-        signatureLen = PK11_SignatureLen(key);
+    if (useRsaPss || hash->hashAlg == ssl_hash_none) {
+        CK_MECHANISM_TYPE mech = PK11_MapSignKeyType(key->keyType);
+        int signatureLen = PK11_SignatureLen(key);
+
+        SECItem *params = NULL;
+        CK_RSA_PKCS_PSS_PARAMS pssParams;
+        SECItem pssParamsItem = { siBuffer,
+                                  (unsigned char *)&pssParams,
+                                  sizeof(pssParams) };
+
         if (signatureLen <= 0) {
             PORT_SetError(SEC_ERROR_INVALID_KEY);
             goto done;
         }
 
         buf->len = (unsigned)signatureLen;
         buf->data = (unsigned char *)PORT_Alloc(signatureLen);
         if (!buf->data)
             goto done; /* error code was set. */
 
-        rv = PK11_Sign(key, buf, &hashItem);
+        if (useRsaPss) {
+            pssParams.hashAlg = ssl3_GetHashMechanismByHashType(hash->hashAlg);
+            pssParams.mgf = ssl3_GetMgfMechanismByHashType(hash->hashAlg);
+            pssParams.sLen = hashItem.len;
+            params = &pssParamsItem;
+            mech = CKM_RSA_PKCS_PSS;
+        }
+
+        rv = PK11_SignWithMechanism(key, mech, params, buf, &hashItem);
     } else {
         SECOidTag hashOID = ssl3_HashTypeToOID(hash->hashAlg);
         rv = SGN_Digest(key, hashOID, buf, &hashItem);
     }
     if (rv != SECSuccess) {
         ssl_MapLowLevelError(SSL_ERROR_SIGN_HASHES_FAILURE);
     } else if (doDerEncode) {
         SECItem derSig = { siBuffer, NULL, 0 };
@@ -1233,30 +1259,32 @@ done:
         PORT_Free(buf->data);
         buf->data = NULL;
     }
     return rv;
 }
 
 /* Called from ssl3_HandleServerKeyExchange, ssl3_HandleCertificateVerify */
 SECStatus
-ssl3_VerifySignedHashes(SSL3Hashes *hash, CERTCertificate *cert,
-                        SECItem *buf, PRBool isTLS, void *pwArg)
+ssl3_VerifySignedHashes(sslSocket *ss, SignatureScheme scheme, SSL3Hashes *hash,
+                        SECItem *buf)
 {
     SECKEYPublicKey *key;
     SECItem *signature = NULL;
     SECStatus rv;
     SECItem hashItem;
     SECOidTag encAlg;
     SECOidTag hashAlg;
+    void *pwArg = ss->pkcs11PinArg;
+    PRBool isRsaPssScheme = ssl_IsRsaPssSignatureScheme(scheme);
 
     PRINT_BUF(60, (NULL, "check signed hashes",
                    buf->data, buf->len));
 
-    key = CERT_ExtractPublicKey(cert);
+    key = CERT_ExtractPublicKey(ss->sec.peerCert);
     if (key == NULL) {
         ssl_MapLowLevelError(SSL_ERROR_EXTRACT_PUBLIC_KEY_FAILURE);
         return SECFailure;
     }
 
     hashAlg = ssl3_HashTypeToOID(hash->hashAlg);
     switch (SECKEY_GetPublicKeyType(key)) {
         case rsaKey:
@@ -1271,17 +1299,18 @@ ssl3_VerifySignedHashes(SSL3Hashes *hash
             if (hash->hashAlg == ssl_hash_none) {
                 hashItem.data = hash->u.s.sha;
                 hashItem.len = sizeof(hash->u.s.sha);
             } else {
                 hashItem.data = hash->u.raw;
                 hashItem.len = hash->len;
             }
             /* Allow DER encoded DSA signatures in SSL 3.0 */
-            if (isTLS || buf->len != SECKEY_SignatureLen(key)) {
+            if (ss->ssl3.prSpec->version > SSL_LIBRARY_VERSION_3_0 ||
+                buf->len != SECKEY_SignatureLen(key)) {
                 signature = DSAU_DecodeDerSigToLen(buf, SECKEY_SignatureLen(key));
                 if (!signature) {
                     PORT_SetError(SSL_ERROR_BAD_HANDSHAKE_HASH_VALUE);
                     return SECFailure;
                 }
                 buf = signature;
             }
             break;
@@ -1308,23 +1337,41 @@ ssl3_VerifySignedHashes(SSL3Hashes *hash
             SECKEY_DestroyPublicKey(key);
             PORT_SetError(SEC_ERROR_UNSUPPORTED_KEYALG);
             return SECFailure;
     }
 
     PRINT_BUF(60, (NULL, "hash(es) to be verified",
                    hashItem.data, hashItem.len));
 
-    if (hashAlg == SEC_OID_UNKNOWN || SECKEY_GetPublicKeyType(key) == dsaKey) {
+    if (isRsaPssScheme ||
+        hashAlg == SEC_OID_UNKNOWN ||
+        SECKEY_GetPublicKeyType(key) == dsaKey) {
         /* VFY_VerifyDigestDirect requires DSA signatures to be DER-encoded.
          * DSA signatures are DER-encoded in TLS but not in SSL3 and the code
          * above always removes the DER encoding of DSA signatures when
          * present. Thus DSA signatures are always verified with PK11_Verify.
          */
-        rv = PK11_Verify(key, buf, &hashItem, pwArg);
+        CK_MECHANISM_TYPE mech = PK11_MapSignKeyType(key->keyType);
+
+        SECItem *params = NULL;
+        CK_RSA_PKCS_PSS_PARAMS pssParams;
+        SECItem pssParamsItem = { siBuffer,
+                                  (unsigned char *)&pssParams,
+                                  sizeof(pssParams) };
+
+        if (isRsaPssScheme) {
+            pssParams.hashAlg = ssl3_GetHashMechanismByHashType(hash->hashAlg);
+            pssParams.mgf = ssl3_GetMgfMechanismByHashType(hash->hashAlg);
+            pssParams.sLen = hashItem.len;
+            params = &pssParamsItem;
+            mech = CKM_RSA_PKCS_PSS;
+        }
+
+        rv = PK11_VerifyWithMechanism(key, mech, params, buf, &hashItem, pwArg);
     } else {
         rv = VFY_VerifyDigestDirect(&hashItem, key, buf, encAlg, hashAlg,
                                     pwArg);
     }
     SECKEY_DestroyPublicKey(key);
     if (signature) {
         SECITEM_FreeItem(signature, PR_TRUE);
     }
@@ -1475,53 +1522,48 @@ ssl3_ComputeExportRSAKeyHash(SSLHashType
 /* Called from ssl3_HandleServerKeyExchange. */
 static SECStatus
 ssl3_ComputeDHKeyHash(sslSocket *ss, SSLHashType hashAlg, SSL3Hashes *hashes,
                       SECItem dh_p, SECItem dh_g, SECItem dh_Ys, PRBool padY)
 {
     PRUint8 *hashBuf;
     PRUint8 *pBuf;
     SECStatus rv = SECSuccess;
-    unsigned int bufLen;
+    unsigned int bufLen, yLen;
     PRUint8 buf[2 * SSL3_RANDOM_LENGTH + 2 + 4096 / 8 + 2 + 4096 / 8];
 
     PORT_Assert(dh_p.data);
     PORT_Assert(dh_g.data);
     PORT_Assert(dh_Ys.data);
 
+    yLen = padY ? dh_p.len : dh_Ys.len;
     bufLen = 2 * SSL3_RANDOM_LENGTH +
              2 + dh_p.len +
              2 + dh_g.len +
-             2 + (padY ? dh_p.len : dh_Ys.len);
+             2 + yLen;
     if (bufLen <= sizeof buf) {
         hashBuf = buf;
     } else {
         hashBuf = PORT_Alloc(bufLen);
         if (!hashBuf) {
             return SECFailure;
         }
     }
 
     memcpy(hashBuf, &ss->ssl3.hs.client_random, SSL3_RANDOM_LENGTH);
     pBuf = hashBuf + SSL3_RANDOM_LENGTH;
     memcpy(pBuf, &ss->ssl3.hs.server_random, SSL3_RANDOM_LENGTH);
     pBuf += SSL3_RANDOM_LENGTH;
-    pBuf[0] = (PRUint8)(dh_p.len >> 8);
-    pBuf[1] = (PRUint8)(dh_p.len);
-    pBuf += 2;
+    pBuf = ssl_EncodeUintX(dh_p.len, 2, pBuf);
     memcpy(pBuf, dh_p.data, dh_p.len);
     pBuf += dh_p.len;
-    pBuf[0] = (PRUint8)(dh_g.len >> 8);
-    pBuf[1] = (PRUint8)(dh_g.len);
-    pBuf += 2;
+    pBuf = ssl_EncodeUintX(dh_g.len, 2, pBuf);
     memcpy(pBuf, dh_g.data, dh_g.len);
     pBuf += dh_g.len;
-    pBuf[0] = (PRUint8)(dh_p.len >> 8);
-    pBuf[1] = (PRUint8)(dh_p.len);
-    pBuf += 2;
+    pBuf = ssl_EncodeUintX(yLen, 2, pBuf);
     if (padY && dh_p.len > dh_Ys.len) {
         memset(pBuf, 0, dh_p.len - dh_Ys.len);
         pBuf += dh_p.len - dh_Ys.len;
     }
     /* If we're padding Y, dh_Ys can't be longer than dh_p. */
     PORT_Assert(!padY || dh_p.len >= dh_Ys.len);
     memcpy(pBuf, dh_Ys.data, dh_Ys.len);
     pBuf += dh_Ys.len;
@@ -3470,21 +3512,20 @@ ssl3_FlushHandshakeMessages(sslSocket *s
     if (!ss->sec.ci.sendBuf.buf || !ss->sec.ci.sendBuf.len)
         return SECSuccess;
 
     /* only these flags are allowed */
     PORT_Assert(!(flags & ~allowedFlags));
     if ((flags & ~allowedFlags) != 0) {
         PORT_SetError(SEC_ERROR_INVALID_ARGS);
         return SECFailure;
-    } else {
-        count = ssl3_SendRecord(ss, NULL, content_handshake,
-                                ss->sec.ci.sendBuf.buf,
-                                ss->sec.ci.sendBuf.len, flags);
-    }
+    }
+    count = ssl3_SendRecord(ss, NULL, content_handshake,
+                            ss->sec.ci.sendBuf.buf,
+                            ss->sec.ci.sendBuf.len, flags);
     if (count < 0) {
         int err = PORT_GetError();
         PORT_Assert(err != PR_WOULD_BLOCK_ERROR);
         if (err == PR_WOULD_BLOCK_ERROR) {
             PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
         }
         rv = SECFailure;
     } else if ((unsigned int)count < ss->sec.ci.sendBuf.len) {
@@ -3847,20 +3888,16 @@ ssl3_HandleAlert(sslSocket *ss, sslBuffe
              * XXX on the client hello is a failure to match ciphers.
              */
             error = SSL_ERROR_NO_CYPHER_OVERLAP;
         }
         PORT_SetError(error);
         return SECFailure;
     }
     if (desc == end_of_early_data) {
-        if (ss->version < SSL_LIBRARY_VERSION_TLS_1_3) {
-            PORT_SetError(error);
-            return SECFailure;
-        }
         return tls13_HandleEndOfEarlyData(ss);
     }
     if ((desc == no_certificate) && (ss->ssl3.hs.ws == wait_client_cert)) {
         /* I'm a server. I've requested a client cert. He hasn't got one. */
         SECStatus rv;
 
         PORT_Assert(ss->sec.isServer);
         ss->ssl3.hs.ws = wait_client_key;
@@ -4007,16 +4044,32 @@ ssl3_HandleChangeCipherSpecs(sslSocket *
      */
     if (ss->ssl3.prSpec == ss->ssl3.pwSpec) {
         ssl3_DestroyCipherSpec(ss->ssl3.prSpec, PR_FALSE /*freeSrvName*/);
     }
     ssl_ReleaseSpecWriteLock(ss); /*************************************/
     return SECSuccess;
 }
 
+static CK_MECHANISM_TYPE
+ssl3_GetMgfMechanismByHashType(SSLHashType hash)
+{
+    switch (hash) {
+        case ssl_hash_sha256:
+            return CKG_MGF1_SHA256;
+        case ssl_hash_sha384:
+            return CKG_MGF1_SHA384;
+        case ssl_hash_sha512:
+            return CKG_MGF1_SHA512;
+        default:
+            PORT_Assert(0);
+    }
+    return CKG_MGF1_SHA256;
+}
+
 /* Function valid for >= TLS 1.2, only. */
 static CK_MECHANISM_TYPE
 ssl3_GetHashMechanismByHashType(SSLHashType hashType)
 {
     switch (hashType) {
         case ssl_hash_sha512:
             return CKM_SHA512;
         case ssl_hash_sha384:
@@ -5140,33 +5193,47 @@ ssl_CheckSignatureSchemeConsistency(
 PRBool
 ssl_IsSupportedSignatureScheme(SignatureScheme scheme)
 {
     switch (scheme) {
         case ssl_sig_rsa_pkcs1_sha1:
         case ssl_sig_rsa_pkcs1_sha256:
         case ssl_sig_rsa_pkcs1_sha384:
         case ssl_sig_rsa_pkcs1_sha512:
+        case ssl_sig_rsa_pss_sha256:
+        case ssl_sig_rsa_pss_sha384:
+        case ssl_sig_rsa_pss_sha512:
         case ssl_sig_ecdsa_secp256r1_sha256:
         case ssl_sig_ecdsa_secp384r1_sha384:
         case ssl_sig_ecdsa_secp521r1_sha512:
         case ssl_sig_dsa_sha1:
         case ssl_sig_dsa_sha256:
         case ssl_sig_dsa_sha384:
         case ssl_sig_dsa_sha512:
         case ssl_sig_ecdsa_sha1:
             return PR_TRUE;
 
+        case ssl_sig_none:
+        case ssl_sig_ed25519:
+        case ssl_sig_ed448:
+            return PR_FALSE;
+    }
+    return PR_FALSE;
+}
+
+PRBool
+ssl_IsRsaPssSignatureScheme(SignatureScheme scheme)
+{
+    switch (scheme) {
         case ssl_sig_rsa_pss_sha256:
         case ssl_sig_rsa_pss_sha384:
         case ssl_sig_rsa_pss_sha512:
-        /* Bug 1280439 will add PSS. */
-        case ssl_sig_none:
-        case ssl_sig_ed25519:
-        case ssl_sig_ed448:
+            return PR_TRUE;
+
+        default:
             return PR_FALSE;
     }
     return PR_FALSE;
 }
 
 /* ssl_ConsumeSignatureScheme reads a SignatureScheme (formerly
  * SignatureAndHashAlgorithm) structure from |b| and puts the resulting value
  * into |out|. |b| and |length| are updated accordingly.
@@ -5570,71 +5637,100 @@ ssl3_ComputeHandshakeHashes(sslSocket *s
     return rv;
 }
 
 /**************************************************************************
  * end of Handshake Hash functions.
  * Begin Send and Handle functions for handshakes.
  **************************************************************************/
 
+#ifdef TRACE
+#define CHTYPE(t)          \
+    case client_hello_##t: \
+        return #t;
+
+static const char *
+ssl_ClientHelloTypeName(sslClientHelloType type)
+{
+    switch (type) {
+        CHTYPE(initial);
+        CHTYPE(retry);
+        CHTYPE(retransmit);    /* DTLS only */
+        CHTYPE(renegotiation); /* TLS <= 1.2 only */
+    }
+    PORT_Assert(0);
+    return NULL;
+}
+#undef CHTYPE
+#endif
+
 /* Called from ssl3_HandleHelloRequest(),
  *             ssl3_RedoHandshake()
  *             ssl_BeginClientHandshake (when resuming ssl3 session)
  *             dtls_HandleHelloVerifyRequest(with resending=PR_TRUE)
+ *
+ * The |type| argument indicates what is going on here:
+ * - client_hello_initial is set for the very first ClientHello
+ * - client_hello_retry indicates that this is a second attempt after receiving
+ *   a HelloRetryRequest (in TLS 1.3)
+ * - client_hello_retransmit is used in DTLS when resending
+ * - client_hello_renegotiation is used to renegotiate (in TLS <1.3)
  */
 SECStatus
-ssl3_SendClientHello(sslSocket *ss, PRBool resending)
+ssl3_SendClientHello(sslSocket *ss, sslClientHelloType type)
 {
     sslSessionID *sid;
     ssl3CipherSpec *cwSpec;
     SECStatus rv;
     int i;
     int length;
     int num_suites;
     int actual_count = 0;
     PRBool isTLS = PR_FALSE;
     PRBool requestingResume = PR_FALSE, fallbackSCSV = PR_FALSE;
     PRInt32 total_exten_len = 0;
     unsigned paddingExtensionLen;
     unsigned numCompressionMethods;
-    PRInt32 flags;
-
-    SSL_TRC(3, ("%d: SSL3[%d]: send client_hello handshake", SSL_GETPID(),
-                ss->fd));
+
+    SSL_TRC(3, ("%d: SSL3[%d]: send %s ClientHello handshake", SSL_GETPID(),
+                ss->fd, ssl_ClientHelloTypeName(type)));
 
     PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
     PORT_Assert(ss->opt.noLocks || ssl_HaveXmitBufLock(ss));
 
-    rv = ssl3_InitState(ss);
-    if (rv != SECSuccess) {
-        return rv; /* ssl3_InitState has set the error code. */
-    }
+    /* If we are responding to a HelloRetryRequest, don't reinitialize. We need
+     * to maintain the handshake hashes. */
+    if (ss->ssl3.hs.helloRetry) {
+        PORT_Assert(type == client_hello_retry);
+    } else {
+        rv = ssl3_InitState(ss);
+        if (rv != SECSuccess) {
+            return rv; /* ssl3_InitState has set the error code. */
+        }
+
+        rv = ssl3_RestartHandshakeHashes(ss);
+        if (rv != SECSuccess) {
+            return rv;
+        }
+    }
+
     /* These must be reset every handshake. */
     ss->ssl3.hs.sendingSCSV = PR_FALSE;
     ss->ssl3.hs.preliminaryInfo = 0;
-    PORT_Assert(IS_DTLS(ss) || !resending);
-
+    PORT_Assert(IS_DTLS(ss) || type != client_hello_retransmit);
     SECITEM_FreeItem(&ss->ssl3.hs.newSessionTicket.ticket, PR_FALSE);
     ss->ssl3.hs.receivedNewSessionTicket = PR_FALSE;
-
-    /* We might be starting a session renegotiation in which case we should
-     * clear previous state.
-     */
     PORT_Memset(&ss->xtnData, 0, sizeof(TLSExtensionData));
 
-    rv = ssl3_RestartHandshakeHashes(ss);
-    if (rv != SECSuccess) {
-        return rv;
-    }
-
     /*
      * During a renegotiation, ss->clientHelloVersion will be used again to
      * work around a Windows SChannel bug. Ensure that it is still enabled.
      */
     if (ss->firstHsDone) {
+        PORT_Assert(type != client_hello_initial);
         if (SSL_ALL_VERSIONS_DISABLED(&ss->vrange)) {
             PORT_SetError(SSL_ERROR_SSL_DISABLED);
             return SECFailure;
         }
 
         if (ss->clientHelloVersion < ss->vrange.min ||
             ss->clientHelloVersion > ss->vrange.max) {
             PORT_SetError(SSL_ERROR_NO_CYPHER_OVERLAP);
@@ -5771,17 +5867,18 @@ ssl3_SendClientHello(sslSocket *ss, PRBo
         }
 
         sid = ssl3_NewSessionID(ss, PR_FALSE);
         if (!sid) {
             return SECFailure; /* memory error is set */
         }
     }
 
-    if (ss->version >= SSL_LIBRARY_VERSION_TLS_1_3) {
+    if (ss->vrange.max >= SSL_LIBRARY_VERSION_TLS_1_3 &&
+        type == client_hello_initial) {
         rv = tls13_SetupClientHello(ss);
         if (rv != SECSuccess) {
             if (sid) {
                 ssl_FreeSID(sid);
             }
             return rv;
         }
     }
@@ -5929,17 +6026,18 @@ ssl3_SendClientHello(sslSocket *ss, PRBo
     }
     if (rv != SECSuccess) {
         if (sid->u.ssl3.lock) {
             PR_RWLock_Unlock(sid->u.ssl3.lock);
         }
         return rv; /* err set by ssl3_AppendHandshake* */
     }
 
-    if (!resending) { /* Don't re-generate if we are in DTLS re-sending mode */
+    /* Generate a new random if this is the first attempt. */
+    if (type == client_hello_initial) {
         rv = ssl3_GetNewRandom(&ss->ssl3.hs.client_random);
         if (rv != SECSuccess) {
             if (sid->u.ssl3.lock) {
                 PR_RWLock_Unlock(sid->u.ssl3.lock);
             }
             return rv; /* err set by GetNewRandom. */
         }
     }
@@ -6103,31 +6201,48 @@ ssl3_SendClientHello(sslSocket *ss, PRBo
 
     if (ss->ssl3.hs.sendingSCSV) {
         /* Since we sent the SCSV, pretend we sent empty RI extension. */
         TLSExtensionData *xtnData = &ss->xtnData;
         xtnData->advertised[xtnData->numAdvertised++] =
             ssl_renegotiation_info_xtn;
     }
 
-    flags = 0;
-    if (!ss->firstHsDone && !IS_DTLS(ss)) {
-        flags |= ssl_SEND_FLAG_CAP_RECORD_VERSION;
-    }
-    rv = ssl3_FlushHandshake(ss, flags);
-    if (rv != SECSuccess) {
-        return rv; /* error code set by ssl3_FlushHandshake */
-    }
     if (ss->version >= SSL_LIBRARY_VERSION_TLS_1_3) {
         rv = tls13_MaybeDo0RTTHandshake(ss);
         if (rv != SECSuccess) {
             return SECFailure; /* error code set already. */
         }
     }
 
+    /* On TLS (but not DTLS), if we sent 0-RTT, then we will have data in the
+     * pending buffer. This just pushes a little of that out.  If we didn't do
+     * that, we wouldn't send a ClientHello the first time and applications
+     * would have to push SSL_ForceHandshake() twice. This should go away once
+     * we have Finished stuffed in the ClientHello. */
+    if (!IS_DTLS(ss) && ss->ssl3.hs.zeroRttState == ssl_0rtt_sent) {
+        int sent;
+
+        PORT_Assert(ss->version >= SSL_LIBRARY_VERSION_TLS_1_3);
+        PORT_Assert(ss->pendingBuf.len);
+        sent = ssl_SendSavedWriteData(ss);
+        if (sent < 0) {
+            return SECFailure;
+        }
+    } else {
+        PRInt32 flags = 0;
+        if (!ss->firstHsDone && !IS_DTLS(ss)) {
+            flags |= ssl_SEND_FLAG_CAP_RECORD_VERSION;
+        }
+        rv = ssl3_FlushHandshake(ss, flags);
+        if (rv != SECSuccess) {
+            return rv; /* error code set by ssl3_FlushHandshake */
+        }
+    }
+
     ss->ssl3.hs.ws = wait_server_hello;
     return SECSuccess;
 }
 
 /* Called from ssl3_HandlePostHelloHandshakeMessage() when it has deciphered a
  * complete ssl3 Hello Request.
  * Caller must hold Handshake and RecvBuf locks.
  */
@@ -6164,17 +6279,17 @@ ssl3_HandleHelloRequest(sslSocket *ss)
         ss->sec.ci.sid = NULL;
     }
 
     if (IS_DTLS(ss)) {
         dtls_RehandshakeCleanup(ss);
     }
 
     ssl_GetXmitBufLock(ss);
-    rv = ssl3_SendClientHello(ss, PR_FALSE);
+    rv = ssl3_SendClientHello(ss, client_hello_renegotiation);
     ssl_ReleaseXmitBufLock(ss);
 
     return rv;
 }
 
 #define UNKNOWN_WRAP_MECHANISM 0x7fffffff
 
 static const CK_MECHANISM_TYPE wrapMechanismList[SSL_NUM_WRAP_MECHS] = {
@@ -7076,17 +7191,16 @@ ssl_PickClientSignatureScheme(sslSocket 
     return rv;
 }
 
 /* Called from ssl3_HandleServerHelloDone(). */
 static SECStatus
 ssl3_SendCertificateVerify(sslSocket *ss, SECKEYPrivateKey *privKey)
 {
     SECStatus rv = SECFailure;
-    PRBool isTLS;
     PRBool isTLS12;
     SECItem buf = { siBuffer, NULL, 0 };
     SSL3Hashes hashes;
     unsigned int len;
     SSLHashType hashAlg;
 
     PORT_Assert(ss->opt.noLocks || ssl_HaveXmitBufLock(ss));
     PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
@@ -7123,21 +7237,20 @@ ssl3_SendCertificateVerify(sslSocket *ss
     } else {
         rv = ssl3_ComputeHandshakeHashes(ss, ss->ssl3.pwSpec, &hashes, 0);
     }
     ssl_ReleaseSpecReadLock(ss);
     if (rv != SECSuccess) {
         goto done; /* err code was set by ssl3_ComputeHandshakeHashes */
     }
 
-    isTLS = (PRBool)(ss->version > SSL_LIBRARY_VERSION_3_0);
     isTLS12 = (PRBool)(ss->version == SSL_LIBRARY_VERSION_TLS_1_2);
     PORT_Assert(ss->version <= SSL_LIBRARY_VERSION_TLS_1_2);
 
-    rv = ssl3_SignHashes(&hashes, privKey, &buf, isTLS);
+    rv = ssl3_SignHashes(ss, &hashes, privKey, &buf);
     if (rv == SECSuccess && !ss->sec.isServer) {
         /* Remember the info about the slot that did the signing.
         ** Later, when doing an SSL restart handshake, verify this.
         ** These calls are mere accessors, and can't fail.
         */
         PK11SlotInfo *slot;
         sslSessionID *sid = ss->sec.ci.sid;
 
@@ -7261,16 +7374,23 @@ ssl3_HandleServerHello(sslSocket *ss, SS
          * Therefore we do not check for matching here.
          */
         version = dtls_DTLSVersionToTLSVersion(version);
         if (version == 0) { /* Insane version number */
             goto alert_loser;
         }
     }
 
+    /* We got a HelloRetryRequest, but the server didn't pick 1.3.  Scream. */
+    if (ss->ssl3.hs.helloRetry && version < SSL_LIBRARY_VERSION_TLS_1_3) {
+        desc = illegal_parameter;
+        errCode = SSL_ERROR_RX_MALFORMED_SERVER_HELLO;
+        goto alert_loser;
+    }
+
     rv = ssl3_NegotiateVersion(ss, version, PR_FALSE);
     if (rv != SECSuccess) {
         desc = (version > SSL_LIBRARY_VERSION_3_0) ? protocol_version
                                                    : handshake_failure;
         errCode = SSL_ERROR_UNSUPPORTED_VERSION;
         goto alert_loser;
     }
     /* Check that the server negotiated the same version as it did
@@ -7693,16 +7813,17 @@ loser:
 static SECStatus
 ssl_HandleRSAServerKeyExchange(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
 {
     SECStatus rv;
     int errCode = SSL_ERROR_RX_MALFORMED_SERVER_KEY_EXCH;
     SSL3AlertDescription desc = illegal_parameter;
     SSLHashType hashAlg;
     PRBool isTLS = ss->ssl3.prSpec->version > SSL_LIBRARY_VERSION_3_0;
+    SignatureScheme sigScheme = ssl_sig_none;
 
     SECItem modulus = { siBuffer, NULL, 0 };
     SECItem exponent = { siBuffer, NULL, 0 };
 
     SSL3Hashes hashes;
     SECItem signature = { siBuffer, NULL, 0 };
     PLArenaPool *arena = NULL;
     SECKEYPublicKey *peerKey = NULL;
@@ -7719,18 +7840,16 @@ ssl_HandleRSAServerKeyExchange(sslSocket
         desc = isTLS ? insufficient_security : illegal_parameter;
         goto alert_loser;
     }
     rv = ssl3_ConsumeHandshakeVariable(ss, &exponent, 2, &b, &length);
     if (rv != SECSuccess) {
         goto loser; /* malformed. */
     }
     if (ss->version == SSL_LIBRARY_VERSION_TLS_1_2) {
-        SignatureScheme sigScheme;
-
         rv = ssl_ConsumeSignatureScheme(ss, &b, &length, &sigScheme);
         if (rv != SECSuccess) {
             goto loser; /* malformed or unsupported. */
         }
         rv = ssl_CheckSignatureSchemeConsistency(ss, sigScheme,
                                                  ss->sec.peerCert);
         if (rv != SECSuccess) {
             goto loser;
@@ -7761,18 +7880,17 @@ ssl_HandleRSAServerKeyExchange(sslSocket
                                       &ss->ssl3.hs.client_random,
                                       &ss->ssl3.hs.server_random,
                                       &hashes, ss->opt.bypassPKCS11);
     if (rv != SECSuccess) {
         errCode =
             ssl_MapLowLevelError(SSL_ERROR_SERVER_KEY_EXCHANGE_FAILURE);
         goto alert_loser;
     }
-    rv = ssl3_VerifySignedHashes(&hashes, ss->sec.peerCert, &signature,
-                                 isTLS, ss->pkcs11PinArg);
+    rv = ssl3_VerifySignedHashes(ss, sigScheme, &hashes, &signature);
     if (rv != SECSuccess) {
         errCode =
             ssl_MapLowLevelError(SSL_ERROR_SERVER_KEY_EXCHANGE_FAILURE);
         goto alert_loser;
     }
 
     /*
      * we really need to build a new key here because we can no longer
@@ -7816,16 +7934,17 @@ loser:
 static SECStatus
 ssl_HandleDHServerKeyExchange(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
 {
     SECStatus rv;
     int errCode = SSL_ERROR_RX_MALFORMED_SERVER_KEY_EXCH;
     SSL3AlertDescription desc = illegal_parameter;
     SSLHashType hashAlg;
     PRBool isTLS = ss->ssl3.prSpec->version > SSL_LIBRARY_VERSION_3_0;
+    SignatureScheme sigScheme;
 
     SECItem dh_p = { siBuffer, NULL, 0 };
     SECItem dh_g = { siBuffer, NULL, 0 };
     SECItem dh_Ys = { siBuffer, NULL, 0 };
     unsigned dh_p_bits;
     unsigned dh_g_bits;
     PRInt32 minDH;
 
@@ -7871,31 +7990,30 @@ ssl_HandleDHServerKeyExchange(sslSocket 
         goto loser; /* malformed. */
     }
     if (!ssl_IsValidDHEShare(&dh_p, &dh_Ys)) {
         errCode = SSL_ERROR_RX_MALFORMED_DHE_KEY_SHARE;
         goto alert_loser;
     }
 
     if (ss->version >= SSL_LIBRARY_VERSION_TLS_1_2) {
-        SignatureScheme sigScheme;
-
         rv = ssl_ConsumeSignatureScheme(ss, &b, &length, &sigScheme);
         if (rv != SECSuccess) {
             goto loser; /* malformed or unsupported. */
         }
         rv = ssl_CheckSignatureSchemeConsistency(ss, sigScheme,
                                                  ss->sec.peerCert);
         if (rv != SECSuccess) {
             goto loser;
         }
         hashAlg = ssl_SignatureSchemeToHashType(sigScheme);
     } else {
         /* Use ssl_hash_none to represent the MD5+SHA1 combo. */
         hashAlg = ssl_hash_none;
+        sigScheme = ssl_sig_none;
     }
     rv = ssl3_ConsumeHandshakeVariable(ss, &signature, 2, &b, &length);
     if (rv != SECSuccess) {
         goto loser; /* malformed. */
     }
     if (length != 0) {
         if (isTLS) {
             desc = decode_error;
@@ -7916,18 +8034,17 @@ ssl_HandleDHServerKeyExchange(sslSocket 
      */
     rv = ssl3_ComputeDHKeyHash(ss, hashAlg, &hashes,
                                dh_p, dh_g, dh_Ys, PR_FALSE /* padY */);
     if (rv != SECSuccess) {
         errCode =
             ssl_MapLowLevelError(SSL_ERROR_SERVER_KEY_EXCHANGE_FAILURE);
         goto alert_loser;
     }
-    rv = ssl3_VerifySignedHashes(&hashes, ss->sec.peerCert, &signature,
-                                 isTLS, ss->pkcs11PinArg);
+    rv = ssl3_VerifySignedHashes(ss, sigScheme, &hashes, &signature);
     if (rv != SECSuccess) {
         errCode =
             ssl_MapLowLevelError(SSL_ERROR_SERVER_KEY_EXCHANGE_FAILURE);
         goto alert_loser;
     }
 
     /*
      * we really need to build a new key here because we can no longer
@@ -8805,31 +8922,32 @@ ssl3_KEASupportsTickets(const ssl3KEADef
 ** 1.0 and selecting one of those export cipher suites. However, a secure
 ** TLS 1.1 client should not have export cipher suites enabled at all,
 ** and a TLS 1.1 client should definitely not be offering *only* export
 ** cipher suites. Therefore, we refuse to negotiate export cipher suites
 ** with any client that indicates support for TLS 1.1 or higher when we
 ** (the server) have TLS 1.1 support enabled.
 */
 SECStatus
-ssl3_NegotiateCipherSuite(sslSocket *ss, const SECItem *suites)
+ssl3_NegotiateCipherSuite(sslSocket *ss, const SECItem *suites,
+                          PRBool initHashes)
 {
     int j;
     int i;
 
     for (j = 0; j < ssl_V3_SUITES_IMPLEMENTED; j++) {
         ssl3CipherSuiteCfg *suite = &ss->cipherSuites[j];
         SSLVersionRange vrange = { ss->version, ss->version };
         if (!config_match(suite, ss->ssl3.policy, &vrange, ss)) {
             continue;
         }
         for (i = 0; i + 1 < suites->len; i += 2) {
             PRUint16 suite_i = (suites->data[i] << 8) | suites->data[i + 1];
             if (suite_i == suite->cipher_suite) {
-                return ssl3_SetCipherSuite(ss, suite_i, PR_TRUE);
+                return ssl3_SetCipherSuite(ss, suite_i, initHashes);
             }
         }
     }
     return SECFailure;
 }
 
 /*
  * Call the SNI config hook.
@@ -9507,17 +9625,17 @@ ssl3_HandleClientHelloPart2(sslSocket *s
     j = ssl3_config_match_init(ss);
     if (j <= 0) { /* no ciphers are working/supported by PK11 */
         desc = internal_error;
         errCode = PORT_GetError(); /* error code is already set. */
         goto alert_loser;
     }
 #endif
 
-    rv = ssl3_NegotiateCipherSuite(ss, suites);
+    rv = ssl3_NegotiateCipherSuite(ss, suites, PR_TRUE);
     if (rv != SECSuccess) {
         desc = handshake_failure;
         errCode = SSL_ERROR_NO_CYPHER_OVERLAP;
         goto alert_loser;
     }
 
     if (canOfferSessionTicket)
         canOfferSessionTicket =
@@ -10226,17 +10344,16 @@ ssl_CreateDHEKeyPair(const namedGroupDef
 }
 
 static SECStatus
 ssl3_SendDHServerKeyExchange(sslSocket *ss)
 {
     const ssl3KEADef *kea_def = ss->ssl3.hs.kea_def;
     SECStatus rv = SECFailure;
     int length;
-    PRBool isTLS;
     SECItem signed_hash = { siBuffer, NULL, 0 };
     SSL3Hashes hashes;
     SSLHashType hashAlg;
 
     const ssl3DHParams *params;
     sslEphemeralKeyPair *keyPair;
     SECKEYPublicKey *pubKey;
     SECKEYPrivateKey *certPrivateKey;
@@ -10244,22 +10361,23 @@ ssl3_SendDHServerKeyExchange(sslSocket *
 
     if (kea_def->kea != kea_dhe_dss && kea_def->kea != kea_dhe_rsa) {
         /* TODO: Support DH_anon. It might be sufficient to drop the signature.
                  See bug 1170510. */
         PORT_SetError(SSL_ERROR_SERVER_KEY_EXCHANGE_FAILURE);
         return SECFailure;
     }
 
-    rv = ssl_SelectDHEParams(ss, &groupDef, &params);
+    rv = ssl_SelectDHEGroup(ss, &groupDef);
     if (rv == SECFailure) {
         PORT_SetError(SSL_ERROR_NO_CYPHER_OVERLAP);
         return SECFailure;
     }
 
+    params = ssl_GetDHEParams(groupDef);
     rv = ssl_CreateDHEKeyPair(groupDef, params, &keyPair);
     if (rv == SECFailure) {
         ssl_MapLowLevelError(SEC_ERROR_KEYGEN_FAIL);
         return SECFailure;
     }
     PR_APPEND_LINK(&keyPair->link, &ss->ephemeralKeyPairs);
 
     if (ss->ssl3.pwSpec->version == SSL_LIBRARY_VERSION_TLS_1_2) {
@@ -10278,19 +10396,18 @@ ssl3_SendDHServerKeyExchange(sslSocket *
                                pubKey->u.dh.base,
                                pubKey->u.dh.publicValue,
                                PR_TRUE /* padY */);
     if (rv != SECSuccess) {
         ssl_MapLowLevelError(SSL_ERROR_SERVER_KEY_EXCHANGE_FAILURE);
         goto loser;
     }
 
-    isTLS = (PRBool)(ss->ssl3.pwSpec->version > SSL_LIBRARY_VERSION_3_0);
     certPrivateKey = ss->sec.serverCert->serverKeyPair->privKey;
-    rv = ssl3_SignHashes(&hashes, certPrivateKey, &signed_hash, isTLS);
+    rv = ssl3_SignHashes(ss, &hashes, certPrivateKey, &signed_hash);
     if (rv != SECSuccess) {
         goto loser; /* ssl3_SignHashes has set err. */
     }
     if (signed_hash.data == NULL) {
         PORT_SetError(SSL_ERROR_SERVER_KEY_EXCHANGE_FAILURE);
         goto loser;
     }
     length = 2 + pubKey->u.dh.prime.len +
@@ -10346,17 +10463,16 @@ loser:
 }
 
 static SECStatus
 ssl3_SendServerKeyExchange(sslSocket *ss)
 {
     const ssl3KEADef *kea_def = ss->ssl3.hs.kea_def;
     SECStatus rv = SECFailure;
     int length;
-    PRBool isTLS;
     SECItem signed_hash = { siBuffer, NULL, 0 };
     SSL3Hashes hashes;
     SECKEYPublicKey *sdPub; /* public key for step-down */
     SSLHashType hashAlg;
 
     SSL_TRC(3, ("%d: SSL3[%d]: send server_key_exchange handshake",
                 SSL_GETPID(), ss->fd));
 
@@ -10385,19 +10501,19 @@ ssl3_SendServerKeyExchange(sslSocket *ss
                                               &ss->ssl3.hs.client_random,
                                               &ss->ssl3.hs.server_random,
                                               &hashes, ss->opt.bypassPKCS11);
             if (rv != SECSuccess) {
                 ssl_MapLowLevelError(SSL_ERROR_SERVER_KEY_EXCHANGE_FAILURE);
                 return rv;
             }
 
-            isTLS = (PRBool)(ss->ssl3.pwSpec->version > SSL_LIBRARY_VERSION_3_0);
-            rv = ssl3_SignHashes(&hashes, ss->sec.serverCert->serverKeyPair->privKey,
-                                 &signed_hash, isTLS);
+            rv = ssl3_SignHashes(ss, &hashes,
+                                 ss->sec.serverCert->serverKeyPair->privKey,
+                                 &signed_hash);
             if (rv != SECSuccess) {
                 goto loser; /* ssl3_SignHashes has set err. */
             }
             if (signed_hash.data == NULL) {
                 /* how can this happen and rv == SECSuccess ?? */
                 PORT_SetError(SSL_ERROR_SERVER_KEY_EXCHANGE_FAILURE);
                 goto loser;
             }
@@ -10619,17 +10735,17 @@ static SECStatus
 ssl3_HandleCertificateVerify(sslSocket *ss, SSL3Opaque *b, PRUint32 length,
                              SSL3Hashes *hashes)
 {
     SECItem signed_hash = { siBuffer, NULL, 0 };
     SECStatus rv;
     int errCode = SSL_ERROR_RX_MALFORMED_CERT_VERIFY;
     SSL3AlertDescription desc = handshake_failure;
     PRBool isTLS;
-    SignatureScheme sigScheme;
+    SignatureScheme sigScheme = ssl_sig_none;
     SSLHashType hashAlg;
     SSL3Hashes localHashes;
     SSL3Hashes *hashesForVerify = NULL;
 
     SSL_TRC(3, ("%d: SSL3[%d]: handle certificate_verify handshake",
                 SSL_GETPID(), ss->fd));
     PORT_Assert(ss->opt.noLocks || ssl_HaveRecvBufLock(ss));
     PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
@@ -10695,18 +10811,17 @@ ssl3_HandleCertificateVerify(sslSocket *
     }
 
     rv = ssl3_ConsumeHandshakeVariable(ss, &signed_hash, 2, &b, &length);
     if (rv != SECSuccess) {
         goto loser; /* malformed. */
     }
 
     /* XXX verify that the key & kea match */
-    rv = ssl3_VerifySignedHashes(hashesForVerify, ss->sec.peerCert, &signed_hash,
-                                 isTLS, ss->pkcs11PinArg);
+    rv = ssl3_VerifySignedHashes(ss, sigScheme, hashesForVerify, &signed_hash);
     if (rv != SECSuccess) {
         errCode = PORT_GetError();
         desc = isTLS ? decrypt_error : handshake_failure;
         goto alert_loser;
     }
 
     signed_hash.data = NULL;
 
@@ -11097,30 +11212,30 @@ ssl3_HandleClientKeyExchange(sslSocket *
             ss->stepDownKeyPair == NULL) {
             /* shouldn't happen, don't use step down if it does */
             goto skip;
         }
         serverKeyPair = ss->stepDownKeyPair;
         ss->sec.keaKeyBits = EXPORT_RSA_KEY_LENGTH * BPB;
     } else
     skip:
-    if (kea_def->ephemeral) {
-        sslEphemeralKeyPair *keyPair;
-        /* There should be exactly one pair. */
-        PORT_Assert(!PR_CLIST_IS_EMPTY(&ss->ephemeralKeyPairs));
-        PORT_Assert(PR_PREV_LINK(&ss->ephemeralKeyPairs) ==
-                    PR_NEXT_LINK(&ss->ephemeralKeyPairs));
-        keyPair = (sslEphemeralKeyPair *)PR_NEXT_LINK(&ss->ephemeralKeyPairs);
-        serverKeyPair = keyPair->keys;
-        ss->sec.keaKeyBits =
-            SECKEY_PublicKeyStrengthInBits(serverKeyPair->pubKey);
-    } else {
-        serverKeyPair = ss->sec.serverCert->serverKeyPair;
-        ss->sec.keaKeyBits = ss->sec.serverCert->serverKeyBits;
-    }
+        if (kea_def->ephemeral) {
+            sslEphemeralKeyPair *keyPair;
+            /* There should be exactly one pair. */
+            PORT_Assert(!PR_CLIST_IS_EMPTY(&ss->ephemeralKeyPairs));
+            PORT_Assert(PR_PREV_LINK(&ss->ephemeralKeyPairs) ==
+                        PR_NEXT_LINK(&ss->ephemeralKeyPairs));
+            keyPair = (sslEphemeralKeyPair *)PR_NEXT_LINK(&ss->ephemeralKeyPairs);
+            serverKeyPair = keyPair->keys;
+            ss->sec.keaKeyBits =
+                SECKEY_PublicKeyStrengthInBits(serverKeyPair->pubKey);
+        } else {
+            serverKeyPair = ss->sec.serverCert->serverKeyPair;
+            ss->sec.keaKeyBits = ss->sec.serverCert->serverKeyBits;
+        }
 
     if (!serverKeyPair) {
         SSL3_SendAlert(ss, alert_fatal, handshake_failure);
         PORT_SetError(SSL_ERROR_NO_SERVER_KEY_FOR_ALG);
         return SECFailure;
     }
     PORT_Assert(serverKeyPair->pubKey);
     PORT_Assert(serverKeyPair->privKey);
@@ -12550,17 +12665,16 @@ ssl3_FillInCachedSID(sslSocket *ss, sslS
         ss->version >= SSL_LIBRARY_VERSION_TLS_1_3 ? ss->ssl3.hs.origCipherSuite : ss->ssl3.hs.cipher_suite;
     sid->u.ssl3.compression = ss->ssl3.hs.compression;
     sid->u.ssl3.policy = ss->ssl3.policy;
     sid->version = ss->version;
     sid->authType = ss->sec.authType;
     sid->authKeyBits = ss->sec.authKeyBits;
     sid->keaType = ss->sec.keaType;
     sid->keaKeyBits = ss->sec.keaKeyBits;
-    sid->namedGroups = ss->namedGroups;
     sid->lastAccessTime = sid->creationTime = ssl_Time();
     sid->expirationTime = sid->creationTime + ssl3_sid_timeout;
     sid->localCert = CERT_DupCertificate(ss->sec.localCert);
     if (ss->sec.isServer) {
         memcpy(&sid->certType, &ss->sec.serverCert->certType, sizeof(sid->certType));
     } else {
         sid->certType.authType = ssl_auth_null;
     }
@@ -12691,24 +12805,21 @@ ssl3_HandleHandshakeMessage(sslSocket *s
                 hashes.u.pointer_to_hash_input.len = ss->ssl3.hs.messages.len;
                 hashesPtr = &hashes;
             } else {
                 computeHashes = PR_TRUE;
             }
         }
     } else {
         if (type == certificate_verify) {
-            computeHashes =
-                TLS13_IN_HS_STATE(ss, wait_cert_verify,
-                                  wait_0rtt_trial_decrypt);
+            computeHashes = TLS13_IN_HS_STATE(ss, wait_cert_verify);
         } else if (type == finished) {
             computeHashes =
                 TLS13_IN_HS_STATE(ss, wait_cert_request, wait_finished,
-                                  wait_0rtt_finished,
-                                  wait_0rtt_trial_decrypt);
+                                  wait_0rtt_finished);
         }
     }
 
     ssl_GetSpecReadLock(ss); /************************************/
     if (computeHashes) {
         SSL3Sender sender = (SSL3Sender)0;
         ssl3CipherSpec *rSpec = ss->version >= SSL_LIBRARY_VERSION_TLS_1_3 ? ss->ssl3.crSpec
                                                                            : ss->ssl3.prSpec;
@@ -12729,18 +12840,19 @@ ssl3_HandleHandshakeMessage(sslSocket *s
     SSL_TRC(30, ("%d: SSL3[%d]: handle handshake message: %s", SSL_GETPID(),
                  ss->fd, ssl3_DecodeHandshakeType(ss->ssl3.hs.msg_type)));
 
     hdr[0] = (PRUint8)ss->ssl3.hs.msg_type;
     hdr[1] = (PRUint8)(length >> 16);
     hdr[2] = (PRUint8)(length >> 8);
     hdr[3] = (PRUint8)(length);
 
-    /* Start new handshake hashes when we start a new handshake */
-    if (ss->ssl3.hs.msg_type == client_hello) {
+    /* Start new handshake hashes when we start a new handshake.  Unless this is
+     * TLS 1.3 and we sent a HelloRetryRequest. */
+    if (ss->ssl3.hs.msg_type == client_hello && !ss->ssl3.hs.helloRetry) {
         rv = ssl3_RestartHandshakeHashes(ss);
         if (rv != SECSuccess) {
             return rv;
         }
     }
     /* We should not include hello_request and hello_verify_request messages
      * in the handshake hashes */
     if ((ss->ssl3.hs.msg_type != hello_request) &&
@@ -13548,21 +13660,23 @@ ssl3_HandleRecord(sslSocket *ss, SSL3Cip
         goto process_it;
     }
 
     ssl_GetSpecReadLock(ss); /******************************************/
     crSpec = ss->ssl3.crSpec;
     isTLS = (PRBool)(crSpec->version > SSL_LIBRARY_VERSION_3_0);
 
     if (IS_DTLS(ss)) {
-        if (!dtls_IsRelevant(ss, crSpec, cText, &seq_num)) {
+        PRBool sameEpoch;
+        if (!dtls_IsRelevant(ss, cText, &sameEpoch, &seq_num)) {
             ssl_ReleaseSpecReadLock(ss); /*****************************/
             databuf->len = 0;            /* Needed to ensure data not left around */
-            /* Drop the packet, but first see if retransmission is needed. */
-            return dtls_MaybeRetransmitHandshake(ss, cText);
+
+            /* Maybe retransmit if needed. */
+            return dtls_MaybeRetransmitHandshake(ss, cText, sameEpoch);
         }
     } else {
         seq_num = crSpec->read_seq_num + 1;
     }
     if (seq_num >= crSpec->cipher_def->max_records) {
         ssl_ReleaseSpecReadLock(ss); /*****************************/
         SSL_TRC(3, ("%d: SSL[%d]: read sequence number at limit 0x%0llx",
                     SSL_GETPID(), ss->fd, seq_num));
@@ -13588,33 +13702,48 @@ ssl3_HandleRecord(sslSocket *ss, SSL3Cip
             SSL_DBG(("%d: SSL3[%d]: HandleRecord, tried to get %d bytes",
                      SSL_GETPID(), ss->fd, MAX_FRAGMENT_LENGTH + 2048));
             /* sslBuffer_Grow has set a memory error code. */
             /* Perhaps we should send an alert. (but we have no memory!) */
             return SECFailure;
         }
     }
 
+    /* We're waiting for another ClientHello, which will appear unencrypted.
+     * Use the content type to tell whether this is should be discarded.
+     *
+     * XXX If we decide to remove the content type from encrypted records, this
+     *     will become much more difficult to manage. */
+    if (ss->ssl3.hs.zeroRttIgnore == ssl_0rtt_ignore_hrr &&
+        cText->type == content_application_data) {
+        ssl_ReleaseSpecReadLock(ss); /*****************************/
+        PORT_Assert(ss->ssl3.hs.ws == wait_client_hello);
+        databuf->len = 0;
+        return SECSuccess;
+    }
+
     /* IMPORTANT: Unprotect functions MUST NOT send alerts
      * because we still hold the spec read lock. Instead, if they
      * return SECFailure, they set *alert to the alert to be sent. */
     if (crSpec->version < SSL_LIBRARY_VERSION_TLS_1_3 ||
         crSpec->cipher_def->calg == ssl_calg_null) {
         /* Unencrypted TLS 1.3 records use the pre-TLS 1.3 format. */
         rv = ssl3_UnprotectRecord(ss, cText, plaintext, &alert);
     } else {
         rv = tls13_UnprotectRecord(ss, cText, plaintext, &alert);
     }
 
     if (rv != SECSuccess) {
         ssl_ReleaseSpecReadLock(ss); /***************************/
 
         SSL_DBG(("%d: SSL3[%d]: decryption failed", SSL_GETPID(), ss->fd));
 
-        if (IS_DTLS(ss) || TLS13_IN_HS_STATE(ss, wait_0rtt_trial_decrypt)) {
+        if (IS_DTLS(ss) ||
+            (ss->sec.isServer &&
+             ss->ssl3.hs.zeroRttIgnore == ssl_0rtt_ignore_trial)) {
             /* Silently drop the packet */
             databuf->len = 0; /* Needed to ensure data not left around */
             return SECSuccess;
         } else {
             int errCode = PORT_GetError();
             SSL3_SendAlert(ss, alert_fatal, alert);
             /* Reset the error code in case SSL3_SendAlert called
              * PORT_SetError(). */
@@ -13709,17 +13838,17 @@ ssl3_HandleRecord(sslSocket *ss, SSL3Cip
     /* Application data records are processed by the caller of this
     ** function, not by this function.
     */
     if (rType == content_application_data) {
         if (ss->firstHsDone)
             return SECSuccess;
         if (ss->version >= SSL_LIBRARY_VERSION_TLS_1_3 &&
             ss->sec.isServer &&
-            TLS13_IN_HS_STATE(ss, wait_0rtt_end_of_early_data)) {
+            ss->ssl3.hs.zeroRttState == ssl_0rtt_accepted) {
             return tls13_HandleEarlyApplicationData(ss, databuf);
         }
         (void)SSL3_SendAlert(ss, alert_fatal, unexpected_message);
         PORT_SetError(SSL_ERROR_RX_UNEXPECTED_APPLICATION_DATA);
         return SECFailure;
     }
 
 /* It's a record that must be handled by ssl itself, not the application.
@@ -13830,22 +13959,22 @@ ssl3_InitState(sslSocket *ss)
     ss->ssl3.policy = SSL_ALLOWED;
 
     ssl_GetSpecWriteLock(ss);
     ss->ssl3.crSpec = ss->ssl3.cwSpec = &ss->ssl3.specs[0];
     ss->ssl3.prSpec = ss->ssl3.pwSpec = &ss->ssl3.specs[1];
     ssl3_InitCipherSpec(ss->ssl3.crSpec);
     ssl3_InitCipherSpec(ss->ssl3.prSpec);
     ss->ssl3.crSpec->version = ss->ssl3.prSpec->version = ss->vrange.max;
+    ssl_ReleaseSpecWriteLock(ss);
+
     ss->ssl3.hs.sendingSCSV = PR_FALSE;
     ss->ssl3.hs.preliminaryInfo = 0;
     ss->ssl3.hs.peerSupportsFfdheGroups = PR_FALSE;
-
     ss->ssl3.hs.ws = (ss->sec.isServer) ? wait_client_hello : wait_server_hello;
-    ssl_ReleaseSpecWriteLock(ss);
 
     PORT_Memset(&ss->xtnData, 0, sizeof(TLSExtensionData));
 
     if (IS_DTLS(ss)) {
         ss->ssl3.hs.sendMessageSeq = 0;
         ss->ssl3.hs.recvMessageSeq = 0;
         ss->ssl3.hs.rtTimeoutMs = DTLS_RETRANSMIT_INITIAL_MS;
         ss->ssl3.hs.rtRetries = 0;
@@ -13868,16 +13997,18 @@ ssl3_InitState(sslSocket *ss)
     PORT_Assert(!ss->ssl3.hs.messages.buf && !ss->ssl3.hs.messages.space);
     ss->ssl3.hs.messages.buf = NULL;
     ss->ssl3.hs.messages.space = 0;
 
     ss->ssl3.hs.receivedNewSessionTicket = PR_FALSE;
     PORT_Memset(&ss->ssl3.hs.newSessionTicket, 0,
                 sizeof(ss->ssl3.hs.newSessionTicket));
 
+    ss->ssl3.hs.zeroRttState = ssl_0rtt_none;
+
     ssl_DisableNonSuiteBGroups(ss);
 
     ss->ssl3.initialized = PR_TRUE;
     return SECSuccess;
 }
 
 /*
  * Creates the public and private RSA keys for SSL Step down.
@@ -14138,18 +14269,21 @@ ssl3_RedoHandshake(sslSocket *ss, PRBool
             ss->sec.uncache(sid); /* remove it from whichever cache it's in. */
         ssl_FreeSID(sid);         /* dec ref count and free if zero. */
         ss->sec.ci.sid = NULL;
     }
 
     ssl_GetXmitBufLock(ss); /**************************************/
 
     /* start off a new handshake. */
-    rv = (ss->sec.isServer) ? ssl3_SendHelloRequest(ss)
-                            : ssl3_SendClientHello(ss, PR_FALSE);
+    if (ss->sec.isServer) {
+        rv = ssl3_SendHelloRequest(ss);
+    } else {
+        rv = ssl3_SendClientHello(ss, client_hello_renegotiation);
+    }
 
     ssl_ReleaseXmitBufLock(ss); /**************************************/
     return rv;
 }
 
 /* Called from ssl_DestroySocketContents() in sslsock.c */
 void
 ssl3_DestroySSL3Info(sslSocket *ss)
@@ -14238,17 +14372,17 @@ ssl3_DestroySSL3Info(sslSocket *ss)
         SECITEM_FreeItem(&ss->ssl3.hs.resumptionContext, PR_FALSE);
     if (ss->ssl3.hs.earlyTrafficSecret)
         PK11_FreeSymKey(ss->ssl3.hs.earlyTrafficSecret);
     if (ss->ssl3.hs.hsTrafficSecret)
         PK11_FreeSymKey(ss->ssl3.hs.hsTrafficSecret);
     if (ss->ssl3.hs.trafficSecret)
         PK11_FreeSymKey(ss->ssl3.hs.trafficSecret);
 
-    ss->ssl3.hs.doing0Rtt = PR_FALSE;
+    ss->ssl3.hs.zeroRttState = ssl_0rtt_none;
     /* Destroy TLS 1.3 buffered early data. */
     tls13_DestroyEarlyData(&ss->ssl3.hs.bufferedEarlyData);
 
     ss->ssl3.initialized = PR_FALSE;
 
     SECITEM_FreeItem(&ss->ssl3.nextProto, PR_FALSE);
 }
 
--- a/security/nss/lib/ssl/ssl3ecc.c
+++ b/security/nss/lib/ssl/ssl3ecc.c
@@ -124,17 +124,17 @@ ssl_ECPubKey2NamedGroup(const SECKEYPubl
     oid.data = params->data + 2;
     if ((oidData = SECOID_FindOID(&oid)) == NULL)
         return NULL;
     if ((NSS_GetAlgorithmPolicy(oidData->offset, &policyFlags) ==
          SECSuccess) &&
         !(policyFlags & NSS_USE_ALG_IN_SSL_KX)) {
         return NULL;
     }
-    for (i = 0; i < ssl_named_group_count; ++i) {
+    for (i = 0; i < SSL_NAMED_GROUP_COUNT; ++i) {
         if (ssl_named_groups[i].oidTag == oidData->offset) {
             return &ssl_named_groups[i];
         }
     }
 
     return NULL;
 }
 
@@ -419,27 +419,33 @@ tls13_ImportECDHKeyShare(sslSocket *ss, 
     if (rv != SECSuccess) {
         return SECFailure;
     }
 
     return SECSuccess;
 }
 
 const namedGroupDef *
-ssl_GetECGroupWithStrength(PRUint32 curvemsk, unsigned int requiredECCbits)
+ssl_GetECGroupWithStrength(sslSocket *ss, unsigned int requiredECCbits)
 {
     int i;
 
-    for (i = 0; i < ssl_named_group_count; i++) {
-        if (ssl_named_groups[i].type != group_type_ec ||
-            ssl_named_groups[i].bits < requiredECCbits) {
+    for (i = 0; i < SSL_NAMED_GROUP_COUNT; ++i) {
+        const namedGroupDef *group;
+        if (ss) {
+            group = ss->namedGroupPreferences[i];
+        } else {
+            group = &ssl_named_groups[i];
+        }
+        if (!group || group->type != group_type_ec ||
+            group->bits < requiredECCbits) {
             continue;
         }
-        if ((curvemsk & (1U << i))) {
-            return &ssl_named_groups[i];
+        if (!ss || ssl_NamedGroupEnabled(ss, group)) {
+            return group;
         }
     }
     PORT_SetError(SSL_ERROR_NO_CYPHER_OVERLAP);
     return NULL;
 }
 
 /* Find the "weakest link".  Get the strength of the signature and symmetric
  * keys and choose a curve based on the weakest of those two. */
@@ -481,17 +487,17 @@ ssl_GetECGroupForServerSocket(sslSocket 
     bulkCipher = ssl_GetBulkCipherDef(ss->ssl3.hs.suite_def);
     requiredECCbits = bulkCipher->key_size * BPB * 2;
     PORT_Assert(requiredECCbits ||
                 ss->ssl3.hs.suite_def->bulk_cipher_alg == cipher_null);
     if (requiredECCbits > certKeySize) {
         requiredECCbits = certKeySize;
     }
 
-    return ssl_GetECGroupWithStrength(ss->namedGroups, requiredECCbits);
+    return ssl_GetECGroupWithStrength(ss, requiredECCbits);
 }
 
 /* function to clear out the lists */
 static SECStatus
 ssl_ShutdownECDHECurves(void *appData, void *nssData)
 {
     int i;
 
@@ -503,17 +509,17 @@ ssl_ShutdownECDHECurves(void *appData, v
     memset(gECDHEKeyPairs, 0, sizeof(gECDHEKeyPairs));
     return SECSuccess;
 }
 
 static PRStatus
 ssl_ECRegister(void)
 {
     SECStatus rv;
-    PORT_Assert(PR_ARRAY_SIZE(gECDHEKeyPairs) == ssl_named_group_count);
+    PORT_Assert(PR_ARRAY_SIZE(gECDHEKeyPairs) == SSL_NAMED_GROUP_COUNT);
     rv = NSS_RegisterShutdown(ssl_ShutdownECDHECurves, gECDHEKeyPairs);
     if (rv != SECSuccess) {
         gECDHEInitError = PORT_GetError();
     }
     return (PRStatus)rv;
 }
 
 /* Create an ECDHE key pair for a given curve */
@@ -619,16 +625,17 @@ ssl3_HandleECDHServerKeyExchange(sslSock
     SECKEYPublicKey *peerKey = NULL;
     PRBool isTLS;
     SECStatus rv;
     int errCode = SSL_ERROR_RX_MALFORMED_SERVER_KEY_EXCH;
     SSL3AlertDescription desc = illegal_parameter;
     SSL3Hashes hashes;
     SECItem signature = { siBuffer, NULL, 0 };
     SSLHashType hashAlg;
+    SignatureScheme sigScheme;
 
     SECItem ec_params = { siBuffer, NULL, 0 };
     SECItem ec_point = { siBuffer, NULL, 0 };
     unsigned char paramBuf[3]; /* only for curve_type == named_curve */
     const namedGroupDef *ecGroup;
 
     isTLS = (PRBool)(ss->ssl3.prSpec->version > SSL_LIBRARY_VERSION_3_0);
 
@@ -667,30 +674,30 @@ ssl3_HandleECDHServerKeyExchange(sslSock
     if (ec_point.data[0] != EC_POINT_FORM_UNCOMPRESSED) {
         errCode = SEC_ERROR_UNSUPPORTED_EC_POINT_FORM;
         desc = handshake_failure;
         goto alert_loser;
     }
 
     PORT_Assert(ss->ssl3.prSpec->version <= SSL_LIBRARY_VERSION_TLS_1_2);
     if (ss->ssl3.prSpec->version == SSL_LIBRARY_VERSION_TLS_1_2) {
-        SignatureScheme sigScheme;
         rv = ssl_ConsumeSignatureScheme(ss, &b, &length, &sigScheme);
         if (rv != SECSuccess) {
             goto loser; /* malformed or unsupported. */
         }
         rv = ssl_CheckSignatureSchemeConsistency(ss, sigScheme,
                                                  ss->sec.peerCert);
         if (rv != SECSuccess) {
             goto loser;
         }
         hashAlg = ssl_SignatureSchemeToHashType(sigScheme);
     } else {
         /* Use ssl_hash_none to represent the MD5+SHA1 combo. */
         hashAlg = ssl_hash_none;
+        sigScheme = ssl_sig_none;
     }
 
     rv = ssl3_ConsumeHandshakeVariable(ss, &signature, 2, &b, &length);
     if (rv != SECSuccess) {
         goto loser; /* malformed. */
     }
 
     if (length != 0) {
@@ -715,18 +722,17 @@ ssl3_HandleECDHServerKeyExchange(sslSock
                                  &ss->ssl3.hs.server_random,
                                  &hashes, ss->opt.bypassPKCS11);
 
     if (rv != SECSuccess) {
         errCode =
             ssl_MapLowLevelError(SSL_ERROR_SERVER_KEY_EXCHANGE_FAILURE);
         goto alert_loser;
     }
-    rv = ssl3_VerifySignedHashes(&hashes, ss->sec.peerCert, &signature,
-                                 isTLS, ss->pkcs11PinArg);
+    rv = ssl3_VerifySignedHashes(ss, sigScheme, &hashes, &signature);
     if (rv != SECSuccess) {
         errCode =
             ssl_MapLowLevelError(SSL_ERROR_SERVER_KEY_EXCHANGE_FAILURE);
         goto alert_loser;
     }
 
     arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
     if (arena == NULL) {
@@ -775,17 +781,17 @@ loser:
     return SECFailure;
 }
 
 SECStatus
 ssl3_SendECDHServerKeyExchange(sslSocket *ss)
 {
     SECStatus rv = SECFailure;
     int length;
-    PRBool isTLS, isTLS12;
+    PRBool isTLS12;
     SECItem signed_hash = { siBuffer, NULL, 0 };
     SSLHashType hashAlg = ssl_hash_none;
     SSL3Hashes hashes;
 
     SECItem ec_params = { siBuffer, NULL, 0 };
     unsigned char paramBuf[3];
     const namedGroupDef *ecGroup;
     sslEphemeralKeyPair *keyPair;
@@ -838,21 +844,20 @@ ssl3_SendECDHServerKeyExchange(sslSocket
                                  &ss->ssl3.hs.client_random,
                                  &ss->ssl3.hs.server_random,
                                  &hashes, ss->opt.bypassPKCS11);
     if (rv != SECSuccess) {
         ssl_MapLowLevelError(SSL_ERROR_SERVER_KEY_EXCHANGE_FAILURE);
         goto loser;
     }
 
-    isTLS = (PRBool)(ss->ssl3.pwSpec->version > SSL_LIBRARY_VERSION_3_0);
     isTLS12 = (PRBool)(ss->ssl3.pwSpec->version >= SSL_LIBRARY_VERSION_TLS_1_2);
 
-    rv = ssl3_SignHashes(&hashes, ss->sec.serverCert->serverKeyPair->privKey,
-                         &signed_hash, isTLS);
+    rv = ssl3_SignHashes(ss, &hashes,
+                         ss->sec.serverCert->serverKeyPair->privKey, &signed_hash);
     if (rv != SECSuccess) {
         goto loser; /* ssl3_SignHashes has set err. */
     }
     if (signed_hash.data == NULL) {
         /* how can this happen and rv == SECSuccess ?? */
         PORT_SetError(SSL_ERROR_SERVER_KEY_EXCHANGE_FAILURE);
         goto loser;
     }
@@ -1010,21 +1015,21 @@ ssl_DisableNonSuiteBGroups(sslSocket *ss
     slot = PK11_GetBestSlotWithAttributes(CKM_ECDH1_DERIVE, 0, 163,
                                           ss->pkcs11PinArg);
     if (slot) {
         /* Looks like we're committed to having lots of curves. */
         PK11_FreeSlot(slot);
         return;
     }
 
-    for (i = 0; i < ssl_named_group_count; ++i) {
-        PORT_Assert(ssl_named_groups[i].index == i);
-        if (ssl_named_groups[i].type == group_type_ec &&
-            !ssl_named_groups[i].suiteb) {
-            ss->namedGroups &= ~(1U << ssl_named_groups[i].index);
+    for (i = 0; i < SSL_NAMED_GROUP_COUNT; ++i) {
+        if (ss->namedGroupPreferences[i] &&
+            ss->namedGroupPreferences[i]->type == group_type_ec &&
+            !ss->namedGroupPreferences[i]->suiteb) {
+            ss->namedGroupPreferences[i] = NULL;
         }
     }
 }
 
 /* Send our Supported Groups extension. */
 PRInt32
 ssl_SendSupportedGroupsXtn(sslSocket *ss, PRBool append, PRUint32 maxBytes)
 {
@@ -1044,34 +1049,36 @@ ssl_SendSupportedGroupsXtn(sslSocket *ss
     if (ss->opt.requireDHENamedGroups ||
         ss->vrange.max >= SSL_LIBRARY_VERSION_TLS_1_3) {
         ff = ssl_IsDHEEnabled(ss);
     }
     if (!ec && !ff) {
         return 0;
     }
 
-    PORT_Assert(sizeof(enabledGroups) > ssl_named_group_count * 2);
-    for (i = 0; i < ssl_named_group_count; ++i) {
-        if (ssl_named_groups[i].type == group_type_ec && !ec) {
+    PORT_Assert(sizeof(enabledGroups) > SSL_NAMED_GROUP_COUNT * 2);
+    for (i = 0; i < SSL_NAMED_GROUP_COUNT; ++i) {
+        const namedGroupDef *group = ss->namedGroupPreferences[i];
+        if (!group) {
             continue;
         }
-        if (ssl_named_groups[i].type == group_type_ff && !ff) {
+        if (group->type == group_type_ec && !ec) {
             continue;
         }
-        if (!ssl_NamedGroupEnabled(ss, &ssl_named_groups[i])) {
+        if (group->type == group_type_ff && !ff) {
+            continue;
+        }
+        if (!ssl_NamedGroupEnabled(ss, group)) {
             continue;
         }
 
         if (append) {
-            enabledGroups[enabledGroupsLen++] = ssl_named_groups[i].name >> 8;
-            enabledGroups[enabledGroupsLen++] = ssl_named_groups[i].name & 0xff;
-        } else {
-            enabledGroupsLen += 2;
+            (void)ssl_EncodeUintX(group->name, 2, &enabledGroups[enabledGroupsLen]);
         }
+        enabledGroupsLen += 2;
     }
 
     extension_length =
         2 /* extension type */ +
         2 /* extension length */ +
         2 /* enabled groups length */ +
         enabledGroupsLen;
 
@@ -1163,66 +1170,77 @@ ssl3_HandleSupportedPointFormatsXtn(sslS
     PORT_SetError(SSL_ERROR_RX_MALFORMED_HANDSHAKE);
     return SECFailure;
 }
 
 static SECStatus
 ssl_UpdateSupportedGroups(sslSocket *ss, SECItem *data)
 {
     PRInt32 list_len;
-    PRUint32 peerGroups = 0;
+    unsigned int i;
+    const namedGroupDef *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) {
         (void)ssl3_DecodeError(ss);
         return SECFailure;
     }
 
-    /* build bit vector of peer's supported groups */
+    /* 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 namedGroupDef *group;
         PRInt32 curve_name =
             ssl3_ConsumeHandshakeNumber(ss, 2, &data->data, &data->len);
         if (curve_name < 0) {
             return SECFailure; /* fatal alert already sent */
         }
         group = ssl_LookupNamedGroup(curve_name);
         if (group) {
-            peerGroups |= (1U << group->index);
+            for (i = 0; i < SSL_NAMED_GROUP_COUNT; ++i) {
+                if (enabled[i] && group == enabled[i]) {
+                    ss->namedGroupPreferences[i] = enabled[i];
+                    break;
+                }
+            }
         }
 
         /* "Codepoints in the NamedCurve registry with a high byte of 0x01 (that
          * is, between 256 and 511 inclusive) are set aside for FFDHE groups,"
          * -- https://tools.ietf.org/html/draft-ietf-tls-negotiated-ff-dhe-10
          */
         if ((curve_name & 0xff00) == 0x0100) {
             ss->ssl3.hs.peerSupportsFfdheGroups = PR_TRUE;
         }
     }
+
     /* Note: if ss->opt.requireDHENamedGroups is set, we disable DHE cipher
      * suites, but we do that in ssl3_config_match(). */
     if (!ss->opt.requireDHENamedGroups && !ss->ssl3.hs.peerSupportsFfdheGroups) {
         /* If we don't require that DHE use named groups, and no FFDHE was
          * included, we pretend that they support all the FFDHE groups we do. */
-        unsigned int i;
-        for (i = 0; i < ssl_named_group_count; ++i) {
-            if (ssl_named_groups[i].type == group_type_ff) {
-                peerGroups |= (1U << ssl_named_groups[i].index);
+        for (i = 0; i < SSL_NAMED_GROUP_COUNT; ++i) {
+            if (enabled[i] && enabled[i]->type == group_type_ff) {
+                ss->namedGroupPreferences[i] = enabled[i];
             }
         }
     }
 
-    /* What curves do we support in common? */
-    ss->namedGroups &= peerGroups;
     return SECSuccess;
 }
 
 /* Ensure that the curve in our server cert is one of the ones supported
  * by the remote client, and disable all ECC cipher suites if not.
  */
 SECStatus
 ssl_HandleSupportedGroupsXtn(sslSocket *ss, PRUint16 ex_type, SECItem *data)
--- a/security/nss/lib/ssl/ssl3ext.c
+++ b/security/nss/lib/ssl/ssl3ext.c
@@ -2126,16 +2126,19 @@ ssl3_ProcessSessionTicketCommon(sslSocke
             sid->peerCert = CERT_NewTempCertificate(ss->dbHandle,
                                                     &parsed_session_ticket->peer_cert, NULL, PR_FALSE, PR_TRUE);
             if (sid->peerCert == NULL) {
                 rv = SECFailure;
                 goto loser;
             }
         }
         if (parsed_session_ticket->srvName.data != NULL) {
+            if (sid->u.ssl3.srvName.data) {
+                SECITEM_FreeItem(&sid->u.ssl3.srvName, PR_FALSE);
+            }
             sid->u.ssl3.srvName = parsed_session_ticket->srvName;
         }
         if (parsed_session_ticket->alpnSelection.data != NULL) {
             sid->u.ssl3.alpnSelection = parsed_session_ticket->alpnSelection;
             /* So we don't free below. */
             parsed_session_ticket->alpnSelection.data = NULL;
         }
         ss->statelessResume = PR_TRUE;
@@ -3295,25 +3298,26 @@ tls13_HandleKeyShareEntry(sslSocket *ss,
     SECItem share = { siBuffer, NULL, 0 };
 
     group = ssl3_ConsumeHandshakeNumber(ss, 2, &data->data, &data->len);
     if (group < 0) {
         PORT_SetError(SSL_ERROR_RX_MALFORMED_KEY_SHARE);
         goto loser;
     }
     groupDef = ssl_LookupNamedGroup(group);
+    rv = ssl3_ConsumeHandshakeVariable(ss, &share, 2, &data->data,
+                                       &data->len);
+    if (rv != SECSuccess) {
+        goto loser;
+    }
+    /* If the group is disabled, continue. */
     if (!groupDef) {
         return SECSuccess;
     }
 
-    rv = ssl3_ConsumeHandshakeVariable(ss, &share, 2, &data->data,
-                                       &data->len);
-    if (rv != SECSuccess)
-        goto loser;
-
     ks = PORT_ZNew(TLS13KeyShareEntry);
     if (!ks)
         goto loser;
     ks->group = groupDef;
 
     rv = SECITEM_CopyItem(NULL, &ks->key_exchange, &share);
     if (rv != SECSuccess)
         goto loser;
@@ -3719,17 +3723,17 @@ tls13_ClientSendEarlyDataXtn(sslSocket *
         age = ssl_Time() - session_ticket->received_timestamp;
         age += session_ticket->ticket_age_add;
 
         rv = ssl3_AppendHandshakeNumber(ss, age, 4);
         if (rv != SECSuccess)
             return -1;
     }
 
-    ss->ssl3.hs.doing0Rtt = PR_TRUE;
+    ss->ssl3.hs.zeroRttState = ssl_0rtt_sent;
     ss->xtnData.advertised[ss->xtnData.numAdvertised++] =
         ssl_tls13_early_data_xtn;
 
     return extension_length;
 }
 
 static SECStatus
 tls13_ServerHandleEarlyDataXtn(sslSocket *ss, PRUint16 ex_type,
@@ -3753,35 +3757,43 @@ tls13_ServerHandleEarlyDataXtn(sslSocket
         return SECFailure;
     }
 
     if (data->len) {
         PORT_SetError(SSL_ERROR_MALFORMED_EARLY_DATA);
         return SECFailure;
     }
 
-    /* Keep track of negotiated extensions.
-     * IMPORTANT: Record the presence of the extension only. This is
-     * only one of several things that the 0-RTT processing code uses
-     * in determining whether to accept 0-RTT.
-     */
+    if (IS_DTLS(ss)) {
+        /* Save the null spec, which we should be currently reading.  We will
+         * use this when 0-RTT sending is over. */
+        ssl_GetSpecReadLock(ss);
+        ss->ssl3.hs.nullSpec = ss->ssl3.crSpec;
+        tls13_CipherSpecAddRef(ss->ssl3.hs.nullSpec);
+        PORT_Assert(ss->ssl3.hs.nullSpec->cipher_def->cipher == cipher_null);
+        ssl_ReleaseSpecReadLock(ss);
+    }
+
+    ss->ssl3.hs.zeroRttState = ssl_0rtt_sent;
+
     ss->xtnData.negotiated[ss->xtnData.numNegotiated++] = ex_type;
 
     return SECSuccess;
 }
 
 /* This is only registered if we are sending it. */
 SECStatus
 tls13_ServerSendEarlyDataXtn(sslSocket *ss,
                              PRBool append,
                              PRUint32 maxBytes)
 {
     SSL_TRC(3, ("%d: TLS13[%d]: send early_data extension",
                 SSL_GETPID(), ss->fd));
 
+    PORT_Assert(ss->ssl3.hs.zeroRttState == ssl_0rtt_accepted);
     if (maxBytes < 4) {
         PORT_Assert(0);
         return 0;
     }
 
     if (append) {
         SECStatus rv;
 
@@ -3813,11 +3825,12 @@ tls13_ClientHandleEarlyDataXtn(sslSocket
 
     if (data->len != 0) {
         PORT_SetError(SSL_ERROR_MALFORMED_EARLY_DATA);
         return SECFailure;
     }
 
     /* Keep track of negotiated extensions. */
     ss->xtnData.negotiated[ss->xtnData.numNegotiated++] = ex_type;
+    ss->ssl3.hs.zeroRttState = ssl_0rtt_accepted;
 
     return SECSuccess;
 }
--- a/security/nss/lib/ssl/sslcon.c
+++ b/security/nss/lib/ssl/sslcon.c
@@ -200,17 +200,17 @@ ssl_BeginClientHandshake(sslSocket *ss)
     ss->gs.state = GS_INIT;
     ss->handshake = ssl_GatherRecord1stHandshake;
 
     /* ssl3_SendClientHello will override this if it succeeds. */
     ss->version = SSL_LIBRARY_VERSION_3_0;
 
     ssl_GetSSL3HandshakeLock(ss);
     ssl_GetXmitBufLock(ss);
-    rv = ssl3_SendClientHello(ss, PR_FALSE);
+    rv = ssl3_SendClientHello(ss, client_hello_initial);
     ssl_ReleaseXmitBufLock(ss);
     ssl_ReleaseSSL3HandshakeLock(ss);
 
     return rv;
 
 loser:
     return SECFailure;
 }
--- a/security/nss/lib/ssl/sslerr.h
+++ b/security/nss/lib/ssl/sslerr.h
@@ -232,15 +232,18 @@ typedef enum {
     SSL_ERROR_RX_MALFORMED_ENCRYPTED_EXTENSIONS = (SSL_ERROR_BASE + 146),
     SSL_ERROR_MALFORMED_PRE_SHARED_KEY = (SSL_ERROR_BASE + 147),
     SSL_ERROR_MALFORMED_EARLY_DATA = (SSL_ERROR_BASE + 148),
     SSL_ERROR_END_OF_EARLY_DATA_ALERT = (SSL_ERROR_BASE + 149),
     SSL_ERROR_MISSING_ALPN_EXTENSION = (SSL_ERROR_BASE + 150),
     SSL_ERROR_RX_UNEXPECTED_EXTENSION = (SSL_ERROR_BASE + 151),
     SSL_ERROR_MISSING_SUPPORTED_GROUPS_EXTENSION = (SSL_ERROR_BASE + 152),
     SSL_ERROR_TOO_MANY_RECORDS = (SSL_ERROR_BASE + 153),
+    SSL_ERROR_RX_UNEXPECTED_HELLO_RETRY_REQUEST = (SSL_ERROR_BASE + 154),
+    SSL_ERROR_RX_MALFORMED_HELLO_RETRY_REQUEST = (SSL_ERROR_BASE + 155),
+    SSL_ERROR_BAD_2ND_CLIENT_HELLO = (SSL_ERROR_BASE + 156),
     SSL_ERROR_END_OF_LIST   /* let the c compiler determine the value of this. */
 } SSLErrorCodes;
 #endif /* NO_SECURITY_ERROR_ENUM */
 
 /* clang-format on */
 
 #endif /* __SSL_ERR_H_ */
--- a/security/nss/lib/ssl/sslimpl.h
+++ b/security/nss/lib/ssl/sslimpl.h
@@ -142,16 +142,19 @@ typedef enum { SSLAppOpRead = 0,
 
 /* The default value from RFC 4347 is 1s, which is too slow. */
 #define DTLS_RETRANSMIT_INITIAL_MS 50
 /* The maximum time to wait between retransmissions. */
 #define DTLS_RETRANSMIT_MAX_MS 10000
 /* Time to wait in FINISHED state for retransmissions. */
 #define DTLS_RETRANSMIT_FINISHED_MS 30000
 
+/* default number of entries in namedGroupPreferences */
+#define SSL_NAMED_GROUP_COUNT 30
+
 /* Types and names of elliptic curves used in TLS */
 typedef enum {
     ec_type_explicitPrime = 1,      /* not supported */
     ec_type_explicitChar2Curve = 2, /* not supported */
     ec_type_named = 3
 } ECType;
 
 /* TODO: decide if SSLKEAType might be better here. */
@@ -300,17 +303,17 @@ typedef struct {
 #endif
 } ssl3CipherSuiteCfg;
 
 #define ssl_V3_SUITES_IMPLEMENTED 80
 
 #define MAX_DTLS_SRTP_CIPHER_SUITES 4
 
 /* MAX_SIGNATURE_SCHEMES allows for all the values we support. */
-#define MAX_SIGNATURE_SCHEMES 12
+#define MAX_SIGNATURE_SCHEMES 15
 
 typedef struct sslOptionsStr {
     /* If SSL_SetNextProtoNego has been called, then this contains the
      * list of supported protocols. */
     SECItem nextProtoNego;
 
     unsigned int useSecurity : 1;
     unsigned int useSocks : 1;
@@ -602,17 +605,16 @@ struct sslSessionIDStr {
 
     PRUint32 creationTime;   /* seconds since Jan 1, 1970 */
     PRUint32 expirationTime; /* seconds since Jan 1, 1970 */
 
     SSLAuthType authType;
     PRUint32 authKeyBits;
     SSLKEAType keaType;
     PRUint32 keaKeyBits;
-    PRUint32 namedGroups;
 
     union {
         struct {
             /* values that are copied into the server's on-disk SID cache. */
             PRUint8 sessionIDLength;
             SSL3Opaque sessionID[SSL3_SESSIONID_BYTES];
 
             ssl3CipherSuite cipherSuite;
@@ -745,37 +747,56 @@ struct ssl3MACDefStr {
     SSL3MACAlgorithm mac;
     CK_MECHANISM_TYPE mmech;
     int pad_size;
     int mac_size;
     SECOidTag oid;
 };
 
 typedef enum {
+    ssl_0rtt_none,     /* 0-RTT not present */
+    ssl_0rtt_sent,     /* 0-RTT sent (no decision yet) */
+    ssl_0rtt_accepted, /* 0-RTT sent and accepted */
+    ssl_0rtt_ignored,  /* 0-RTT sent but rejected/ignored */
+    ssl_0rtt_done      /* 0-RTT accepted, but finished */
+} sslZeroRttState;
+
+typedef enum {
+    ssl_0rtt_ignore_none,  /* not ignoring */
+    ssl_0rtt_ignore_trial, /* ignoring with trial decryption */
+    ssl_0rtt_ignore_hrr    /* ignoring until ClientHello (due to HRR) */
+} sslZeroRttIgnore;
+
+typedef enum {
     wait_client_hello,
     wait_client_cert,
     wait_client_key,
     wait_cert_verify,
     wait_change_cipher,
     wait_finished,
     wait_server_hello,
     wait_certificate_status,
     wait_server_cert,
     wait_server_key,
     wait_cert_request,
     wait_hello_done,
     wait_new_session_ticket,
     wait_encrypted_extensions,
     idle_handshake,
     wait_0rtt_finished,
-    wait_0rtt_end_of_early_data, /* Not processed by handshake code. */
-    wait_0rtt_trial_decrypt,     /* Wait for trial decryption to succeed. */
-    wait_invalid                 /* Invalid value. There is no handshake message "invalid". */
+    wait_invalid /* Invalid value. There is no handshake message "invalid". */
 } SSL3WaitState;
 
+typedef enum {
+    client_hello_initial,      /* The first attempt. */
+    client_hello_retry,        /* If we receive HelloRetryRequest. */
+    client_hello_retransmit,   /* In DTLS, if we receive HelloVerifyRequest. */
+    client_hello_renegotiation /* A renegotiation attempt. */
+} sslClientHelloType;
+
 /*
  * TLS extension related constants and data structures.
  */
 typedef struct TLSExtensionDataStr TLSExtensionData;
 typedef struct SessionTicketDataStr SessionTicketData;
 
 struct TLSExtensionDataStr {
     /* registered callbacks that send server hello extensions */
@@ -984,19 +1005,23 @@ typedef struct SSL3HandshakeStateStr {
     PK11SymKey *trafficSecret;      /* The source key to use to generate
                                         * traffic keys */
     /* The certificate request from the server. */
     TLS13CertificateRequest *certificateRequest;
     ssl3CipherSuite origCipherSuite; /* The cipher suite from the original
                                         * connection if we are resuming. */
     PRCList cipherSpecs;             /* The cipher specs in the sequence they
                                         * will be applied. */
-    PRBool doing0Rtt;                /* Are we doing a 0-RTT handshake? */
+    ssl3CipherSpec *nullSpec;        /* In case 0-RTT is rejected. */
+    sslZeroRttState zeroRttState;    /* Are we doing a 0-RTT handshake? */
+    sslZeroRttIgnore zeroRttIgnore;  /* Are we ignoring 0-RTT? */
     PRCList bufferedEarlyData;       /* Buffered TLS 1.3 early data
                                         * on server.*/
+    PRBool helloRetry;               /* True if HelloRetryRequest has been sent
+                                      * or received. */
 } SSL3HandshakeState;
 
 /*
 ** This is the "ssl3" struct, as in "ss->ssl3".
 ** note:
 ** usually,   crSpec == cwSpec and prSpec == pwSpec.
 ** Sometimes, crSpec == pwSpec and prSpec == cwSpec.
 ** But there are never more than 2 actual specs.
@@ -1297,21 +1322,28 @@ struct sslSocketStr {
     sslBuffer saveBuf;    /*xmitBufLock*/
     sslBuffer pendingBuf; /*xmitBufLock*/
 
     /* Configuration state for server sockets */
     /* One server cert and key for each authentication type. */
     PRCList /* <sslServerCert> */ serverCerts;
 
     ssl3CipherSuiteCfg cipherSuites[ssl_V3_SUITES_IMPLEMENTED];
-    /* This bit mask determines what EC and FFDHE groups are enabled.  This
+
+    /* A list of groups that are sorted according to user preferences pointing
+     * to entries of ssl_named_groups. By default this list contains pointers
+     * to all elements in ssl_named_groups in the default order.
+     * This list also determines which groups are enabled. This
      * starts with all being enabled and can be modified either by negotiation
      * (in which case groups not supported by a peer are masked off), or by
-     * calling SSL_DHEGroupPrefSet(), which will alter the mask for FFDHE. */
-    PRUint32 namedGroups;
+     * calling SSL_DHEGroupPrefSet().
+     * Note that renegotiation will ignore groups that were disabled in the
+     * first handshake.
+     */
+    const namedGroupDef *namedGroupPreferences[SSL_NAMED_GROUP_COUNT];
 
     /* SSL3 state info.  Formerly was a pointer */
     ssl3State ssl3;
 
     /*
      * TLS extension related data.
      */
     /* True when the current session is a stateless resume. */
@@ -1336,17 +1368,16 @@ extern PRUint32 ssl3_sid_timeout;
 
 extern const char *const ssl3_cipherName[];
 
 extern sslSessionIDLookupFunc ssl_sid_lookup;
 extern sslSessionIDCacheFunc ssl_sid_cache;
 extern sslSessionIDUncacheFunc ssl_sid_uncache;
 
 extern const namedGroupDef ssl_named_groups[];
-extern const unsigned int ssl_named_group_count;
 
 /************************************************************************/
 
 SEC_BEGIN_PROTOS
 
 /* Internal initialization and installation of the SSL error tables */
 extern SECStatus ssl_Init(void);
 extern SECStatus ssl_InitializePRErrorTable(void);
@@ -1643,17 +1674,17 @@ extern SECStatus ssl3_DecodeError(sslSoc
 extern SECStatus ssl3_AuthCertificateComplete(sslSocket *ss, PRErrorCode error);
 
 /*
  * for dealing with SSL 3.0 clients sending SSL 2.0 format hellos
  */
 extern SECStatus ssl3_HandleV2ClientHello(
     sslSocket *ss, unsigned char *buffer, int length, PRUint8 padding);
 
-SECStatus ssl3_SendClientHello(sslSocket *ss, PRBool resending);
+SECStatus ssl3_SendClientHello(sslSocket *ss, sslClientHelloType type);
 
 /*
  * input into the SSL3 machinery from the actualy network reading code
  */
 SECStatus ssl3_HandleRecord(
     sslSocket *ss, SSL3Ciphertext *cipher, sslBuffer *out);
 SECStatus ssl_RemoveTLSCBCPadding(sslBuffer *plaintext, unsigned int macSize);
 
@@ -1686,19 +1717,18 @@ extern void ssl_FreeEphemeralKeyPair(ssl
 extern sslEphemeralKeyPair *ssl_LookupEphemeralKeyPair(
     sslSocket *ss, const namedGroupDef *groupDef);
 extern void ssl_FreeEphemeralKeyPairs(sslSocket *ss);
 
 extern SECStatus ssl_AppendPaddedDHKeyShare(sslSocket *ss,
                                             SECKEYPublicKey *pubKey,
                                             PRBool appendLength);
 extern const ssl3DHParams *ssl_GetDHEParams(const namedGroupDef *groupDef);
-extern SECStatus ssl_SelectDHEParams(sslSocket *ss,
-                                     const namedGroupDef **groupDef,
-                                     const ssl3DHParams **params);
+extern SECStatus ssl_SelectDHEGroup(sslSocket *ss,
+                                    const namedGroupDef **groupDef);
 extern SECStatus ssl_CreateDHEKeyPair(const namedGroupDef *groupDef,
                                       const ssl3DHParams *params,
                                       sslEphemeralKeyPair **keyPair);
 extern PRBool ssl_IsValidDHEShare(const SECItem *dh_p, const SECItem *dh_Ys);
 extern SECStatus ssl_ValidateDHENamedGroup(sslSocket *ss,
                                            const SECItem *dh_p,
                                            const SECItem *dh_g,
                                            const namedGroupDef **groupDef,
@@ -1720,17 +1750,17 @@ extern PRBool ssl_IsDHEEnabled(sslSocket
 extern const namedGroupDef *ssl_LookupNamedGroup(SSLNamedGroup group);
 extern PRBool ssl_NamedGroupEnabled(const sslSocket *ss, const namedGroupDef *group);
 extern SECStatus ssl_NamedGroup2ECParams(PLArenaPool *arena,
                                          const namedGroupDef *curve,
                                          SECKEYECParams *params);
 extern const namedGroupDef *ssl_ECPubKey2NamedGroup(
     const SECKEYPublicKey *pubKey);
 
-extern const namedGroupDef *ssl_GetECGroupWithStrength(PRUint32 curvemsk,
+extern const namedGroupDef *ssl_GetECGroupWithStrength(sslSocket *ss,
                                                        unsigned int requiredECCbits);
 extern const namedGroupDef *ssl_GetECGroupForServerSocket(sslSocket *ss);
 extern void ssl_DisableNonSuiteBGroups(sslSocket *ss);
 
 extern SECStatus ssl3_CipherPrefSetDefault(ssl3CipherSuite which, PRBool on);
 extern SECStatus ssl3_CipherPrefGetDefault(ssl3CipherSuite which, PRBool *on);
 
 extern SECStatus ssl3_CipherPrefSet(sslSocket *ss, ssl3CipherSuite which, PRBool on);
@@ -1800,21 +1830,20 @@ extern SECStatus ssl_CheckSignatureSchem
     sslSocket *ss, SignatureScheme scheme, CERTCertificate *cert);
 extern SECStatus ssl_ParseSignatureSchemes(sslSocket *ss, PLArenaPool *arena,
                                            SignatureScheme **schemesOut,
                                            unsigned int *numSchemesOut,
                                            unsigned char **b,
                                            unsigned int *len);
 extern SECStatus ssl_ConsumeSignatureScheme(
     sslSocket *ss, SSL3Opaque **b, PRUint32 *length, SignatureScheme *out);
-extern SECStatus ssl3_SignHashes(SSL3Hashes *hash, SECKEYPrivateKey *key,
-                                 SECItem *buf, PRBool isTLS);
-extern SECStatus ssl3_VerifySignedHashes(SSL3Hashes *hash,
-                                         CERTCertificate *cert, SECItem *buf, PRBool isTLS,
-                                         void *pwArg);
+extern SECStatus ssl3_SignHashes(sslSocket *ss, SSL3Hashes *hash,
+                                 SECKEYPrivateKey *key, SECItem *buf);
+extern SECStatus ssl3_VerifySignedHashes(sslSocket *ss, SignatureScheme scheme,
+                                         SSL3Hashes *hash, SECItem *buf);
 extern SECStatus ssl3_CacheWrappedMasterSecret(
     sslSocket *ss, sslSessionID *sid,
     ssl3CipherSpec *spec, SSLAuthType authType);
 extern void ssl3_FreeSniNameArray(TLSExtensionData *xtnData);
 
 /* Functions that handle ClientHello and ServerHello extensions. */
 extern SECStatus ssl3_HandleServerNameXtn(sslSocket *ss,
                                           PRUint16 ex_type, SECItem *data);
@@ -1935,24 +1964,25 @@ extern int dtls_RecordGetRecvd(const DTL
                                sslSequenceNumber seq);
 extern void dtls_RecordSetRecvd(DTLSRecvdRecords *records,
                                 sslSequenceNumber seq);
 extern void dtls_RehandshakeCleanup(sslSocket *ss);
 extern SSL3ProtocolVersion
 dtls_TLSVersionToDTLSVersion(SSL3ProtocolVersion tlsv);
 extern SSL3ProtocolVersion
 dtls_DTLSVersionToTLSVersion(SSL3ProtocolVersion dtlsv);
-extern PRBool dtls_IsRelevant(sslSocket *ss, const ssl3CipherSpec *crSpec,
-                              const SSL3Ciphertext *cText,
-                              sslSequenceNumber *seqNum);
+extern PRBool dtls_IsRelevant(sslSocket *ss, const SSL3Ciphertext *cText,
+                              PRBool *sameEpoch, PRUint64 *seqNum);
 extern SECStatus dtls_MaybeRetransmitHandshake(sslSocket *ss,
-                                               const SSL3Ciphertext *cText);
+                                               const SSL3Ciphertext *cText,
+                                               PRBool sameEpoch);
 
 CK_MECHANISM_TYPE ssl3_Alg2Mech(SSLCipherAlgorithm calg);
-SECStatus ssl3_NegotiateCipherSuite(sslSocket *ss, const SECItem *suites);
+SECStatus ssl3_NegotiateCipherSuite(sslSocket *ss, const SECItem *suites,
+                                    PRBool initHashes);
 SECStatus ssl3_ServerCallSNICallback(sslSocket *ss);
 SECStatus ssl3_SetupPendingCipherSpec(sslSocket *ss);
 SECStatus ssl3_FlushHandshake(sslSocket *ss, PRInt32 flags);
 SECStatus ssl3_SendCertificate(sslSocket *ss);
 SECStatus ssl3_CompleteHandleCertificate(sslSocket *ss,
                                          SSL3Opaque *b, PRUint32 length);
 SECStatus ssl3_SendEmptyCertificate(sslSocket *ss);
 SECStatus ssl3_SendCertificateStatus(sslSocket *ss);
--- a/security/nss/lib/ssl/sslinfo.c
+++ b/security/nss/lib/ssl/sslinfo.c
@@ -75,23 +75,19 @@ SSL_GetChannelInfo(PRFileDesc *fd, SSLCh
             inf.lastAccessTime = sid->lastAccessTime;
             inf.expirationTime = sid->expirationTime;
             inf.extendedMasterSecretUsed =
                 (ss->version >= SSL_LIBRARY_VERSION_TLS_1_3 ||
                  sid->u.ssl3.keys.extendedMasterSecretUsed)
                     ? PR_TRUE
                     : PR_FALSE;
 
-            if (ss->sec.isServer) {
-                inf.earlyDataAccepted = ss->ssl3.hs.doing0Rtt;
-            } else {
-                inf.earlyDataAccepted =
-                    ssl3_ExtensionNegotiated(
-                        ss, ssl_tls13_early_data_xtn);
-            }
+            inf.earlyDataAccepted =
+                (ss->ssl3.hs.zeroRttState == ssl_0rtt_accepted ||
+                 ss->ssl3.hs.zeroRttState == ssl_0rtt_done);
             sidLen = sid->u.ssl3.sessionIDLength;
             sidLen = PR_MIN(sidLen, sizeof inf.sessionID);
             inf.sessionIDLength = sidLen;
             memcpy(inf.sessionID, sid->u.ssl3.sessionID, sidLen);
         }
     }
 
     memcpy(info, &inf, inf.length);
--- a/security/nss/lib/ssl/sslsecur.c
+++ b/security/nss/lib/ssl/sslsecur.c
@@ -925,17 +925,21 @@ ssl_SecureSend(sslSocket *ss, const unsi
      * Case 2: TLS 1.3 0-RTT
      */
     if (!ss->firstHsDone) {
         PRBool falseStart = PR_FALSE;
         ssl_Get1stHandshakeLock(ss);
         if (ss->opt.enableFalseStart ||
             (ss->opt.enable0RttData && !ss->sec.isServer)) {
             ssl_GetSSL3HandshakeLock(ss);
-            falseStart = ss->ssl3.hs.canFalseStart || ss->ssl3.hs.doing0Rtt;
+            /* The client can sometimes send before the handshake is fully
+             * complete. In TLS 1.2: false start; in TLS 1.3: 0-RTT. */
+            falseStart = ss->ssl3.hs.canFalseStart ||
+                         ss->ssl3.hs.zeroRttState == ssl_0rtt_sent ||
+                         ss->ssl3.hs.zeroRttState == ssl_0rtt_accepted;
             ssl_ReleaseSSL3HandshakeLock(ss);
         }
         if (!falseStart && ss->handshake) {
             rv = ssl_Do1stHandshake(ss);
         }
         ssl_Release1stHandshakeLock(ss);
     }
     if (rv < 0) {
@@ -955,18 +959,20 @@ ssl_SecureSend(sslSocket *ss, const unsi
         PORT_SetError(PR_INVALID_ARGUMENT_ERROR);
         rv = PR_FAILURE;
         goto done;
     }
 
     if (!ss->firstHsDone) {
 #ifdef DEBUG
         ssl_GetSSL3HandshakeLock(ss);
-        PORT_Assert(ss->ssl3.hs.canFalseStart ||
-                    (ss->ssl3.hs.doing0Rtt && !ss->sec.isServer));
+        PORT_Assert(!ss->sec.isServer &&
+                    (ss->ssl3.hs.canFalseStart ||
+                     ss->ssl3.hs.zeroRttState == ssl_0rtt_sent ||
+                     ss->ssl3.hs.zeroRttState == ssl_0rtt_accepted));
         ssl_ReleaseSSL3HandshakeLock(ss);
 #endif
         SSL_TRC(3, ("%d: SSL[%d]: SecureSend: sending data due to false start",
                     SSL_GETPID(), ss->fd));
     }
 
     ssl_GetXmitBufLock(ss);
     rv = ssl3_SendApplicationData(ss, buf, len, flags);
--- a/security/nss/lib/ssl/sslsock.c
+++ b/security/nss/lib/ssl/sslsock.c
@@ -139,16 +139,17 @@ static const PRUint16 srtpCiphers[] = {
 /* This list is in rough order of speed.  Note that while some smaller groups
  * appear early in the list, smaller groups are generally ignored when iterating
  * through this list. ffdhe_custom must not appear in this list. */
 #define ECGROUP(name, size, oid) \
     ssl_grp_ec_##name, size, group_type_ec, SEC_OID_SECG_EC_##oid
 #define FFGROUP(size, oid) \
     ssl_grp_ffdhe_##size, size, group_type_ff, SEC_OID_TLS_FFDHE_##oid
 
+/* update SSL_NAMED_GROUP_COUNT when changing the number of entries */
 const namedGroupDef ssl_named_groups[] = {
     { 0, ECGROUP(secp192r1, 192, SECP192R1), PR_FALSE },
     { 1, ECGROUP(secp160r2, 160, SECP160R2), PR_FALSE },
     { 2, ECGROUP(secp160k1, 160, SECP160K1), PR_FALSE },
     { 3, ECGROUP(secp160r1, 160, SECP160R1), PR_FALSE },
     { 4, ECGROUP(sect163k1, 163, SECT163K1), PR_FALSE },
     { 5, ECGROUP(sect163r1, 163, SECT163R1), PR_FALSE },
     { 6, ECGROUP(sect163r2, 163, SECT163R2), PR_FALSE },
@@ -174,17 +175,16 @@ const namedGroupDef ssl_named_groups[] =
     { 26, FFGROUP(3072, 3072), PR_FALSE },
     { 27, FFGROUP(4096, 4096), PR_FALSE },
     { 28, FFGROUP(6144, 6144), PR_FALSE },
     { 29, FFGROUP(8192, 8192), PR_FALSE }
 };
 #undef ECGROUP
 #undef FFGROUP
 
-const unsigned int ssl_named_group_count = PR_ARRAY_SIZE(ssl_named_groups);
 /* Check that the supported groups bits will fit into ss->namedGroups. */
 PR_STATIC_ASSERT(PR_ARRAY_SIZE(ssl_named_groups) < (sizeof(PRUint32) * 8));
 
 /* forward declarations. */
 static sslSocket *ssl_NewSocket(PRBool makeLocks, SSLProtocolVariant variant);
 static SECStatus ssl_MakeLocks(sslSocket *ss);
 static void ssl_SetDefaultsFromEnvironment(void);
 static PRStatus ssl_PushIOLayer(sslSocket *ns, PRFileDesc *stack,
@@ -252,16 +252,17 @@ ssl_FindSocket(PRFileDesc *fd)
     return ss;
 }
 
 static sslSocket *
 ssl_DupSocket(sslSocket *os)
 {
     sslSocket *ss;
     SECStatus rv;
+    unsigned int i;
 
     ss = ssl_NewSocket((PRBool)(!os->opt.noLocks), os->protocolVariant);
     if (!ss) {
         return NULL;
     }
 
     ss->opt = os->opt;
     ss->opt.useSocks = PR_FALSE;
@@ -310,17 +311,16 @@ ssl_DupSocket(sslSocket *os)
              cursor != &os->ephemeralKeyPairs;
              cursor = PR_NEXT_LINK(cursor)) {
             sslEphemeralKeyPair *okp = (sslEphemeralKeyPair *)cursor;
             sslEphemeralKeyPair *skp = ssl_CopyEphemeralKeyPair(okp);
             if (!skp)
                 goto loser;
             PR_APPEND_LINK(&skp->link, &ss->ephemeralKeyPairs);
         }
-        ss->namedGroups = os->namedGroups;
 
         /*
          * XXX the preceding CERT_ and SECKEY_ functions can fail and return NULL.
          * XXX We should detect this, and not just march on with NULL pointers.
          */
         ss->authCertificate = os->authCertificate;
         ss->authCertificateArg = os->authCertificateArg;
         ss->getClientAuthData = os->getClientAuthData;
@@ -331,16 +331,19 @@ ssl_DupSocket(sslSocket *os)
         ss->badCertArg = os->badCertArg;
         ss->handshakeCallback = os->handshakeCallback;
         ss->handshakeCallbackData = os->handshakeCallbackData;
         ss->canFalseStartCallback = os->canFalseStartCallback;
         ss->canFalseStartCallbackData = os->canFalseStartCallbackData;
         ss->pkcs11PinArg = os->pkcs11PinArg;
         ss->nextProtoCallback = os->nextProtoCallback;
         ss->nextProtoArg = os->nextProtoArg;
+        for (i = 0; i < SSL_NAMED_GROUP_COUNT; ++i) {
+            ss->namedGroupPreferences[i] = os->namedGroupPreferences[i];
+        }
 
         /* Create security data */
         rv = ssl_CopySecurityInfo(ss, os);
         if (rv != SECSuccess) {
             goto loser;
         }
     }
     return ss;
@@ -793,17 +796,16 @@ SSL_OptionSet(PRFileDesc *fd, PRInt32 wh
             ss->opt.cbcRandomIV = on;
             break;
 
         case SSL_ENABLE_OCSP_STAPLING:
             ss->opt.enableOCSPStapling = on;
             break;
 
         case SSL_ENABLE_NPN:
-            ss->opt.enableNPN = on;
             break;
 
         case SSL_ENABLE_ALPN:
             ss->opt.enableALPN = on;
             break;
 
         case SSL_REUSE_SERVER_ECDHE_KEY:
             ss->opt.reuseServerECDHEKey = on;
@@ -1243,17 +1245,16 @@ SSL_OptionSetDefault(PRInt32 which, PRBo
             ssl_defaults.cbcRandomIV = on;
             break;
 
         case SSL_ENABLE_OCSP_STAPLING:
             ssl_defaults.enableOCSPStapling = on;
             break;
 
         case SSL_ENABLE_NPN:
-            ssl_defaults.enableNPN = on;
             break;
 
         case SSL_ENABLE_ALPN:
             ssl_defaults.enableALPN = on;
             break;
 
         case SSL_REUSE_SERVER_ECDHE_KEY:
             ssl_defaults.reuseServerECDHEKey = on;
@@ -1493,59 +1494,71 @@ NSS_SetExportPolicy(void)
 
 SECStatus
 NSS_SetFrancePolicy(void)
 {
     return NSS_SetDomesticPolicy();
 }
 
 SECStatus
-SSL_NamedGroupPrefSet(PRFileDesc *fd, SSLNamedGroup group, PRBool enable)
+SSL_NamedGroupConfig(PRFileDesc *fd, const SSLNamedGroup *groups,
+                     unsigned int numGroups)
 {
-    sslSocket *ss;
-    unsigned int i;
-
-    ss = ssl_FindSocket(fd);
+    unsigned int i, j;
+    sslSocket *ss = ssl_FindSocket(fd);
     if (!ss) {
+        PORT_SetError(SEC_ERROR_NOT_INITIALIZED);
+        return SECFailure;
+    }
+
+    if (!groups) {
+        PORT_SetError(SEC_ERROR_INVALID_ARGS);
+        return SECFailure;
+    }
+    if (numGroups > SSL_NAMED_GROUP_COUNT) {
+        PORT_SetError(SEC_ERROR_INVALID_ARGS);
         return SECFailure;
     }
 
-    for (i = 0; i < ssl_named_group_count; ++i) {
-        if (ssl_named_groups[i].name == group) {
-            PRUint32 bit = 1U << ssl_named_groups[i].index;
-            if (enable) {
-                ss->namedGroups |= bit;
-            } else {
-                ss->namedGroups &= ~bit;
+    for (i = 0; i < SSL_NAMED_GROUP_COUNT; ++i) {
+        ss->namedGroupPreferences[i] = NULL;
+    }
+
+    for (i = 0; i < numGroups; ++i) {
+        for (j = 0; j < SSL_NAMED_GROUP_COUNT; ++j) {
+            /* skip duplicate groups */
+            if (ss->namedGroupPreferences[j] &&
+                ss->namedGroupPreferences[j]->name == groups[i]) {
+                break;
             }
-            return SECSuccess;
+            if (ssl_named_groups[j].name == groups[i]) {
+                ss->namedGroupPreferences[i] = &ssl_named_groups[j];
+                break;
+            }
         }
     }
 
-    SSL_DBG(("%d: SSL[%d]: unsupported group %d in SSL_NamedGroupPrefSet",
-             SSL_GETPID(), fd, group));
-    PORT_SetError(SEC_ERROR_INVALID_ARGS);
-    return SECFailure;
+    return SECSuccess;
 }
 
 SECStatus
-SSL_DHEGroupPrefSet(PRFileDesc *fd,
-                    const SSLDHEGroupType *groups,
+SSL_DHEGroupPrefSet(PRFileDesc *fd, const SSLDHEGroupType *groups,
                     PRUint16 num_groups)
 {
     sslSocket *ss;
     const SSLDHEGroupType *list;
     unsigned int count;
-    unsigned int i;
-    PRUint32 supportedGroups;
+    int i, k;
+    const namedGroupDef *enabled[SSL_NAMED_GROUP_COUNT] = { 0 };
     static const SSLDHEGroupType default_dhe_groups[] = {
         ssl_ff_dhe_2048_group
     };
 
-    if ((num_groups && !groups) || (!num_groups && groups)) {
+    if ((num_groups && !groups) || (!num_groups && groups) ||
+        num_groups > SSL_NAMED_GROUP_COUNT) {
         PORT_SetError(SEC_ERROR_INVALID_ARGS);
         return SECFailure;
     }
 
     ss = ssl_FindSocket(fd);
     if (!ss) {
         SSL_DBG(("%d: SSL[%d]: bad socket in SSL_DHEGroupPrefSet", SSL_GETPID(), fd));
         return SECFailure;
@@ -1554,24 +1567,29 @@ SSL_DHEGroupPrefSet(PRFileDesc *fd,
     if (groups) {
         list = groups;
         count = num_groups;
     } else {
         list = default_dhe_groups;
         count = PR_ARRAY_SIZE(default_dhe_groups);
     }
 
-    supportedGroups = ss->namedGroups;
-    for (i = 0; i < ssl_named_group_count; ++i) {
-        if (ssl_named_groups[i].type == group_type_ff) {
-            supportedGroups &= ~(1U << ssl_named_groups[i].index);
+    /* save enabled ec groups and clear ss->namedGroupPreferences */
+    k = 0;
+    for (i = 0; i < SSL_NAMED_GROUP_COUNT; ++i) {
+        if (ss->namedGroupPreferences[i] &&
+            ss->namedGroupPreferences[i]->type != group_type_ff) {
+            enabled[k++] = ss->namedGroupPreferences[i];
         }
+        ss->namedGroupPreferences[i] = NULL;
     }
+
     ss->ssl3.dhePreferredGroup = NULL;
     for (i = 0; i < count; ++i) {
+        PRBool duplicate = PR_FALSE;
         SSLNamedGroup name;
         const namedGroupDef *groupDef;
         switch (list[i]) {
             case ssl_ff_dhe_2048_group:
                 name = ssl_grp_ffdhe_2048;
                 break;
             case ssl_ff_dhe_3072_group:
                 name = ssl_grp_ffdhe_3072;
@@ -1589,19 +1607,32 @@ SSL_DHEGroupPrefSet(PRFileDesc *fd,
                 PORT_SetError(SEC_ERROR_INVALID_ARGS);
                 return SECFailure;
         }
         groupDef = ssl_LookupNamedGroup(name);
         PORT_Assert(groupDef);
         if (!ss->ssl3.dhePreferredGroup) {
             ss->ssl3.dhePreferredGroup = groupDef;
         }
-        supportedGroups |= (1U << groupDef->index);
+        PORT_Assert(k < SSL_NAMED_GROUP_COUNT);
+        for (i = 0; i < k; ++i) {
+            /* skip duplicates */
+            if (enabled[i] == groupDef) {
+                duplicate = PR_TRUE;
+                break;
+            }
+        }
+        if (!duplicate) {
+            enabled[k++] = groupDef;
+        }
     }
-    ss->namedGroups = supportedGroups;
+    for (i = 0; i < k; ++i) {
+        ss->namedGroupPreferences[i] = enabled[i];
+    }
+
     return SECSuccess;
 }
 
 PRCallOnceType gWeakDHParamsRegisterOnce;
 int gWeakDHParamsRegisterError;
 
 PRCallOnceType gWeakDHParamsOnce;
 int gWeakDHParamsError;
@@ -1718,16 +1749,19 @@ ssl_GetDHEParams(const namedGroupDef *gr
         case ssl_grp_ffdhe_3072:
             return &ff_dhe_3072_params;
         case ssl_grp_ffdhe_4096:
             return &ff_dhe_4096_params;
         case ssl_grp_ffdhe_6144:
             return &ff_dhe_6144_params;
         case ssl_grp_ffdhe_8192:
             return &ff_dhe_8192_params;
+        case ssl_grp_ffdhe_custom:
+            PORT_Assert(gWeakDHParams);
+            return gWeakDHParams;
         default:
             PORT_Assert(0);
     }
     return NULL;
 }
 
 /* This validates dh_Ys against the group prime. */
 PRBool
@@ -1784,82 +1818,79 @@ SECStatus
 ssl_ValidateDHENamedGroup(sslSocket *ss,
                           const SECItem *dh_p,
                           const SECItem *dh_g,
                           const namedGroupDef **groupDef,
                           const ssl3DHParams **dhParams)
 {
     unsigned int i;
 
-    for (i = 0; i < ssl_named_group_count; ++i) {
+    for (i = 0; i < SSL_NAMED_GROUP_COUNT; ++i) {
         const ssl3DHParams *params;
-        if (ssl_named_groups[i].type != group_type_ff) {
+        if (!ss->namedGroupPreferences[i]) {
             continue;
         }
-        if (!ssl_NamedGroupEnabled(ss, &ssl_named_groups[i])) {
+        if (ss->namedGroupPreferences[i]->type != group_type_ff) {
             continue;
         }
-
-        params = ssl_GetDHEParams(&ssl_named_groups[i]);
+        if (!ssl_NamedGroupEnabled(ss, ss->namedGroupPreferences[i])) {
+            continue;
+        }
+
+        params = ssl_GetDHEParams(ss->namedGroupPreferences[i]);
         PORT_Assert(params);
         if (SECITEM_ItemsAreEqual(&params->prime, dh_p)) {
             if (!SECITEM_ItemsAreEqual(&params->base, dh_g)) {
                 return SECFailure;
             }
             if (groupDef)
-                *groupDef = &ssl_named_groups[i];
+                *groupDef = ss->namedGroupPreferences[i];
             if (dhParams)
                 *dhParams = params;
             return SECSuccess;
         }
     }
 
     return SECFailure;
 }
 
 /* Ensure DH parameters have been selected.  This just picks the first enabled
  * FFDHE group in ssl_named_groups, or the weak one if it was enabled. */
 SECStatus
-ssl_SelectDHEParams(sslSocket *ss,
-                    const namedGroupDef **groupDef,
-                    const ssl3DHParams **params)
+ssl_SelectDHEGroup(sslSocket *ss, const namedGroupDef **groupDef)
 {
     unsigned int i;
     static const namedGroupDef weak_group_def = {
         0, ssl_grp_ffdhe_custom, WEAK_DHE_SIZE, group_type_ff,
         SEC_OID_TLS_DHE_CUSTOM, PR_FALSE
     };
 
     /* Only select weak groups in TLS 1.2 and earlier, but not if the client has
      * indicated that it supports an FFDHE named group. */
     if (ss->ssl3.dheWeakGroupEnabled &&
         ss->version < SSL_LIBRARY_VERSION_TLS_1_3 &&
         !ss->ssl3.hs.peerSupportsFfdheGroups) {
-        PORT_Assert(gWeakDHParams);
         *groupDef = &weak_group_def;
-        *params = gWeakDHParams;
         return SECSuccess;
     }
     if (ss->ssl3.dhePreferredGroup &&
         ssl_NamedGroupEnabled(ss, ss->ssl3.dhePreferredGroup)) {
         *groupDef = ss->ssl3.dhePreferredGroup;
-        *params = ssl_GetDHEParams(ss->ssl3.dhePreferredGroup);
         return SECSuccess;
     }
-    for (i = 0; i < ssl_named_group_count; ++i) {
-        if (ssl_named_groups[i].type == group_type_ff &&
-            ssl_NamedGroupEnabled(ss, &ssl_named_groups[i])) {
-            *groupDef = &ssl_named_groups[i];
-            *params = ssl_GetDHEParams(&ssl_named_groups[i]);
+    for (i = 0; i < SSL_NAMED_GROUP_COUNT; ++i) {
+        if (ss->namedGroupPreferences[i] &&
+            ss->namedGroupPreferences[i]->type == group_type_ff &&
+            ssl_NamedGroupEnabled(ss, ss->namedGroupPreferences[i])) {
+            *groupDef = ss->namedGroupPreferences[i];
             return SECSuccess;
         }
     }
 
     *groupDef = NULL;
-    *params = NULL;
     PORT_SetError(SSL_ERROR_NO_CYPHER_OVERLAP);
     return SECFailure;
 }
 
 /* LOCKS ??? XXX */
 static PRFileDesc *
 ssl_ImportFD(PRFileDesc *model, PRFileDesc *fd, SSLProtocolVariant variant)
 {
@@ -3565,37 +3596,44 @@ ssl_SetDefaultsFromEnvironment(void)
 #endif /* NSS_HAVE_GETENV */
 }
 
 const namedGroupDef *
 ssl_LookupNamedGroup(SSLNamedGroup group)
 {
     unsigned int i;
 
-    for (i = 0; i < ssl_named_group_count; ++i) {
+    for (i = 0; i < SSL_NAMED_GROUP_COUNT; ++i) {
         if (ssl_named_groups[i].name == group) {
             return &ssl_named_groups[i];
         }
     }
     return NULL;
 }
 
 PRBool
 ssl_NamedGroupEnabled(const sslSocket *ss, const namedGroupDef *groupDef)
 {
     PRUint32 policy;
     SECStatus rv;
+    unsigned int i;
 
     PORT_Assert(groupDef);
 
     rv = NSS_GetAlgorithmPolicy(groupDef->oidTag, &policy);
     if (rv == SECSuccess && !(policy & NSS_USE_ALG_IN_SSL_KX)) {
         return PR_FALSE;
     }
-    return (ss->namedGroups & (1U << groupDef->index)) != 0;
+    for (i = 0; i < SSL_NAMED_GROUP_COUNT; ++i) {
+        if (ss->namedGroupPreferences[i] &&
+            ss->namedGroupPreferences[i] == groupDef) {
+            return PR_TRUE;
+        }
+    }
+    return PR_FALSE;
 }
 
 /* Returns a reference counted object that contains a key pair.
  * Or NULL on failure.  Initial ref count is 1.
  * Uses the keys in the pair as input.  Adopts the keys given.
  */
 sslKeyPair *
 ssl_NewKeyPair(SECKEYPrivateKey *privKey, SECKEYPublicKey *pubKey)
@@ -3716,16 +3754,17 @@ ssl_FreeEphemeralKeyPairs(sslSocket *ss)
 /*
 ** Create a newsocket structure for a file descriptor.
 */
 static sslSocket *
 ssl_NewSocket(PRBool makeLocks, SSLProtocolVariant protocolVariant)
 {
     SECStatus rv;
     sslSocket *ss;
+    int i;
 
     ssl_SetDefaultsFromEnvironment();
 
     if (ssl_force_locks)
         makeLocks = PR_TRUE;
 
     /* Make a new socket and get it ready */
     ss = (sslSocket *)PORT_ZAlloc(sizeof(sslSocket));
@@ -3760,17 +3799,19 @@ ssl_NewSocket(PRBool makeLocks, SSLProto
     ss->sniSocketConfigArg = NULL;
     ss->getClientAuthData = NULL;
     ss->handleBadCert = NULL;
     ss->badCertArg = NULL;
     ss->pkcs11PinArg = NULL;
 
     ssl_ChooseOps(ss);
     ssl3_InitSocketPolicy(ss);
-    ss->namedGroups = PR_UINT32_MAX; /* All groups enabled to start. */
+    for (i = 0; i < SSL_NAMED_GROUP_COUNT; ++i) {
+        ss->namedGroupPreferences[i] = &ssl_named_groups[i];
+    }
     PR_INIT_CLIST(&ss->ssl3.hs.lastMessageFlight);
     PR_INIT_CLIST(&ss->ssl3.hs.remoteKeyShares);
     PR_INIT_CLIST(&ss->ssl3.hs.cipherSpecs);
     PR_INIT_CLIST(&ss->ssl3.hs.bufferedEarlyData);
     if (makeLocks) {
         rv = ssl_MakeLocks(ss);
         if (rv != SECSuccess)
             goto loser;
--- a/security/nss/lib/ssl/tls13con.c
+++ b/security/nss/lib/ssl/tls13con.c
@@ -43,45 +43,48 @@ static SECStatus tls13_AESGCM(
     const unsigned char *additionalData, int additionalDataLen);
 static SECStatus tls13_ChaCha20Poly1305(
     ssl3KeyMaterial *keys,
     PRBool doDecrypt,
     unsigned char *out, int *outlen, int maxout,
     const unsigned char *in, int inlen,
     const unsigned char *additionalData, int additionalDataLen);
 static SECStatus tls13_SendEncryptedExtensions(sslSocket *ss);
-static PRBool tls13_ServerAllow0Rtt(sslSocket *ss, const sslSessionID *sid);
 static SECStatus tls13_HandleEncryptedExtensions(sslSocket *ss, SSL3Opaque *b,
                                                  PRUint32 length);
 static SECStatus tls13_HandleCertificate(
     sslSocket *ss, SSL3Opaque *b, PRUint32 length);
 static SECStatus tls13_HandleCertificateRequest(sslSocket *ss, SSL3Opaque *b,
                                                 PRUint32 length);
 static SECStatus
 tls13_SendCertificateVerify(sslSocket *ss, SECKEYPrivateKey *privKey);
 static SECStatus tls13_HandleCertificateVerify(
     sslSocket *ss, SSL3Opaque *b, PRUint32 length,
     TLS13CombinedHash *hashes);
 static SECStatus
 tls13_DeriveSecret(sslSocket *ss, PK11SymKey *key, const char *label,
                    const TLS13CombinedHash *hashes,
                    PK11SymKey **dest);
+static void tls13_SetNullCipherSpec(sslSocket *ss, ssl3CipherSpec **specp);
+static SECStatus tls13_SendEndOfEarlyData(sslSocket *ss);
 static SECStatus tls13_SendFinished(sslSocket *ss, PK11SymKey *baseKey);
 static SECStatus tls13_VerifyFinished(sslSocket *ss, PK11SymKey *secret,
                                       SSL3Opaque *b, PRUint32 length,
                                       const TLS13CombinedHash *hashes);
 static SECStatus tls13_ClientHandleFinished(sslSocket *ss,
                                             SSL3Opaque *b, PRUint32 length,
                                             const TLS13CombinedHash *hashes);
 static SECStatus tls13_ServerHandleFinished(sslSocket *ss,
                                             SSL3Opaque *b, PRUint32 length,
                                             const TLS13CombinedHash *hashes);
 static SECStatus tls13_SendNewSessionTicket(sslSocket *ss);
 static SECStatus tls13_HandleNewSessionTicket(sslSocket *ss, SSL3Opaque *b,
                                               PRUint32 length);
+static SECStatus tls13_HandleHelloRetryRequest(sslSocket *ss, SSL3Opaque *b,
+                                               PRUint32 length);
 static void
 tls13_CombineHashes(sslSocket *ss, const PRUint8 *hhash, unsigned int hlen,
                     TLS13CombinedHash *hashes);
 static SECStatus tls13_ComputeHandshakeHashes(sslSocket *ss,
                                               TLS13CombinedHash *hashes);
 static SECStatus tls13_ComputeEarlySecrets(sslSocket *ss, PRBool setup0Rtt);
 static SECStatus tls13_ComputeHandshakeSecrets(sslSocket *ss);
 static SECStatus tls13_ComputeApplicationSecrets(sslSocket *ss);
@@ -155,18 +158,16 @@ tls13_HandshakeState(SSL3WaitState st)
         STATE_CASE(wait_client_cert);
         STATE_CASE(wait_cert_verify);
         STATE_CASE(wait_finished);
         STATE_CASE(wait_server_hello);
         STATE_CASE(wait_server_cert);
         STATE_CASE(wait_cert_request);
         STATE_CASE(wait_encrypted_extensions);
         STATE_CASE(wait_0rtt_finished);
-        STATE_CASE(wait_0rtt_end_of_early_data);
-        STATE_CASE(wait_0rtt_trial_decrypt);
         STATE_CASE(idle_handshake);
         default:
             break;
     }
     PORT_Assert(0);
     return "unknown";
 }
 #endif
@@ -304,71 +305,92 @@ tls13_GetHmacMechanism(sslSocket *ss)
         case ssl_hash_sha384:
             return CKM_SHA384_HMAC;
         default:
             PORT_Assert(0);
     }
     return CKM_SHA256_HMAC;
 }
 
+SECStatus
+tls13_CreateKeyShare(sslSocket *ss, const namedGroupDef *groupDef)
+{
+    SECStatus rv;
+    sslEphemeralKeyPair *keyPair = NULL;
+    const ssl3DHParams *params;
+
+    switch (groupDef->type) {
+        case group_type_ec:
+            rv = ssl_CreateECDHEphemeralKeyPair(groupDef, &keyPair);
+            if (rv != SECSuccess) {
+                return SECFailure;
+            }
+            break;
+        case group_type_ff:
+            params = ssl_GetDHEParams(groupDef);
+            PORT_Assert(params->name != ssl_grp_ffdhe_custom);
+            rv = ssl_CreateDHEKeyPair(groupDef, params, &keyPair);
+            if (rv != SECSuccess) {
+                return SECFailure;
+            }
+            break;
+    }
+
+    PR_APPEND_LINK(&keyPair->link, &ss->ephemeralKeyPairs);
+    return rv;
+}
+
 /*
  * Generate shares for ECDHE and FFDHE.  This picks the first enabled group of
  * the requisite type and creates a share for that.
  *
  * Called from ssl3_SendClientHello.
  */
 SECStatus
 tls13_SetupClientHello(sslSocket *ss)
 {
     unsigned int i;
     PRBool ecNeeded = ssl_IsECCEnabled(ss);
-    /* This does FFDHE always only while we don't have HelloRetryRequest
-     * support.  FFDHE is too much of a burden for normal requests.  We really
-     * only want it when EC suites are disabled. */
-    PRBool ffNeeded = ssl_IsDHEEnabled(ss);
+    /* Only generate an FFDHE share when EC suites are disabled. */
+    PRBool ffNeeded = !ecNeeded;
 
     PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
     PORT_Assert(ss->opt.noLocks || ssl_HaveXmitBufLock(ss));
 
     PORT_Assert(PR_CLIST_IS_EMPTY(&ss->ephemeralKeyPairs));
 
-    for (i = 0; i < ssl_named_group_count; ++i) {
+    for (i = 0; i < SSL_NAMED_GROUP_COUNT; ++i) {
         SECStatus rv;
-        sslEphemeralKeyPair *keyPair = NULL;
-        const namedGroupDef *groupDef = &ssl_named_groups[i];
-        const ssl3DHParams *params;
+        const namedGroupDef *groupDef = ss->namedGroupPreferences[i];
+        if (!groupDef) {
+            continue;
+        }
+
         if (!ssl_NamedGroupEnabled(ss, groupDef)) {
             continue;
         }
         switch (groupDef->type) {
             case group_type_ec:
                 if (!ecNeeded) {
                     continue;
                 }
-                rv = ssl_CreateECDHEphemeralKeyPair(groupDef, &keyPair);
-                if (rv != SECSuccess) {
-                    return SECFailure;
-                }
                 ecNeeded = PR_FALSE;
                 break;
             case group_type_ff:
                 if (!ffNeeded) {
                     continue;
                 }
-                params = ssl_GetDHEParams(groupDef);
-                PORT_Assert(params->name != ssl_grp_ffdhe_custom);
-                rv = ssl_CreateDHEKeyPair(groupDef, params, &keyPair);
-                if (rv != SECSuccess) {
-                    return SECFailure;
-                }
                 ffNeeded = PR_FALSE;
                 break;
         }
 
-        PR_APPEND_LINK(&keyPair->link, &ss->ephemeralKeyPairs);
+        rv = tls13_CreateKeyShare(ss, groupDef);
+        if (rv != SECSuccess) {
+            return SECFailure;
+        }
     }
 
     PORT_Assert(!PR_CLIST_IS_EMPTY(&ss->ephemeralKeyPairs));
     /* We don't permit all groups of a given type to be disabled, so this should
      * never reach this point wanting for a share of either type. */
     PORT_Assert(!ecNeeded);
     PORT_Assert(!ffNeeded);
 
@@ -468,26 +490,28 @@ loser:
 }
 
 SECStatus
 tls13_HandlePostHelloHandshakeMessage(sslSocket *ss, SSL3Opaque *b,
                                       PRUint32 length, SSL3Hashes *hashesPtr)
 {
     TLS13CombinedHash hashes;
 
-    if (TLS13_IN_HS_STATE(ss, wait_0rtt_trial_decrypt)) {
+    if (ss->sec.isServer && ss->ssl3.hs.zeroRttIgnore != ssl_0rtt_ignore_none) {
         SSL_TRC(3, ("%d: TLS13[%d]: %s successfully decrypted handshake after"
                     "failed 0-RTT",
                     SSL_GETPID(), ss->fd));
-        TLS13_SET_HS_STATE(ss, ss->opt.requestCertificate ? wait_client_cert
-                                                          : wait_finished);
+        ss->ssl3.hs.zeroRttIgnore = ssl_0rtt_ignore_none;
     }
 
     /* TODO(ekr@rtfm.com): Would it be better to check all the states here? */
     switch (ss->ssl3.hs.msg_type) {
+        case hello_retry_request:
+            return tls13_HandleHelloRetryRequest(ss, b, length);
+
         case certificate:
             return tls13_HandleCertificate(ss, b, length);
 
         case certificate_request:
             return tls13_HandleCertificateRequest(ss, b, length);
 
         case certificate_verify:
             if (!hashesPtr) {
@@ -902,16 +926,20 @@ tls13_AllowPskCipher(const sslSocket *ss
 }
 
 /* Check whether resumption-PSK is allowed. */
 static PRBool
 tls13_CanResume(sslSocket *ss, const sslSessionID *sid)
 {
     const sslServerCert *sc;
 
+    if (!sid) {
+        return PR_FALSE;
+    }
+
     if (sid->version != ss->version) {
         return PR_FALSE;
     }
 
     /* Server sids don't remember the server cert we previously sent, but they
      * do remember the type of certificate we originally used, so we can locate
      * it again, provided that the current ssl socket has had its server certs
      * configured the same as the previous one. */
@@ -939,189 +967,347 @@ tls13_AlpnTagAllowed(sslSocket *ss, cons
             !PORT_Memcmp(data + offset + 1, tag->data, tag->len))
             return PR_TRUE;
         offset += 1 + taglen;
     }
 
     return PR_FALSE;
 }
 
+/* Called from tls13_HandleClientHelloPart2 to update the state of 0-RTT handling.
+ *
+ * 0-RTT is only permitted if:
+ * 1. The early data extension was present.
+ * 2. We are resuming a session.
+ * 3. The 0-RTT option is set.
+ * 4. The ticket allowed 0-RTT.
+ * 5. We negotiated the same ALPN value as in the ticket.
+ */
+static void
+tls13_NegotiateZeroRtt(sslSocket *ss, const sslSessionID *sid)
+{
+    SSL_TRC(3, ("%d: TLS13[%d]: negotiate 0-RTT %p",
+                SSL_GETPID(), ss->fd, sid));
+
+    /* tls13_ServerHandleEarlyDataXtn sets this to ssl_0rtt_sent, so this will
+     * be ssl_0rtt_none unless early_data is present. */
+    if (ss->ssl3.hs.zeroRttState == ssl_0rtt_none) {
+        return;
+    }
+
+    /* If we rejected 0-RTT on the first ClientHello, then we can just say that
+     * there is no 0-RTT for the second.  We shouldn't get any more.  Reset the
+     * ignore state so that we treat decryption failure normally. */
+    if (ss->ssl3.hs.zeroRttIgnore == ssl_0rtt_ignore_hrr) {
+        PORT_Assert(ss->ssl3.hs.helloRetry);
+        ss->ssl3.hs.zeroRttState = ssl_0rtt_none;
+        ss->ssl3.hs.zeroRttIgnore = ssl_0rtt_ignore_none;
+        return;
+    }
+
+    PORT_Assert(ss->ssl3.hs.zeroRttState == ssl_0rtt_sent);
+    if (sid && ss->opt.enable0RttData &&
+        (sid->u.ssl3.locked.sessionTicket.flags & ticket_allow_early_data) != 0 &&
+        SECITEM_CompareItem(&ss->ssl3.nextProto, &sid->u.ssl3.alpnSelection) == 0) {
+        SSL_TRC(3, ("%d: TLS13[%d]: enable 0-RTT",
+                    SSL_GETPID(), ss->fd));
+        PORT_Assert(ss->statelessResume);
+        ss->ssl3.hs.zeroRttState = ssl_0rtt_accepted;
+        ss->ssl3.hs.zeroRttIgnore = ssl_0rtt_ignore_none;
+    } else {
+        SSL_TRC(3, ("%d: TLS13[%d]: ignore 0-RTT",
+                    SSL_GETPID(), ss->fd));
+        ss->ssl3.hs.zeroRttState = ssl_0rtt_ignored;
+        ss->ssl3.hs.zeroRttIgnore = ssl_0rtt_ignore_trial;
+    }
+}
+
 /* Called from ssl3_HandleClientHello after we have parsed the
  * ClientHello and are sure that we are going to do TLS 1.3
  * or fail. */
 SECStatus
 tls13_HandleClientHelloPart2(sslSocket *ss,
                              const SECItem *suites,
                              sslSessionID *sid)
 {
     SECStatus rv;
     SSL3Statistics *ssl3stats = SSL_GetStatistics();
     int j;
-
-    if (sid != NULL && !tls13_CanResume(ss, sid)) {
-        /* Destroy SID if it is present an unusable. */
-        SSL_AtomicIncrementLong(&ssl3stats->hch_sid_cache_not_ok);
-        if (ss->sec.uncache)
-            ss->sec.uncache(sid);
-        ssl_FreeSID(sid);
-        sid = NULL;
+    PRBool shouldRetry = PR_FALSE;
+    ssl3CipherSuite previousCipherSuite;
+
+    if (!tls13_CanResume(ss, sid)) {
         ss->statelessResume = PR_FALSE;
     }
 
 #ifndef PARANOID
     /* Look for a matching cipher suite. */
     j = ssl3_config_match_init(ss);
     if (j <= 0) { /* no ciphers are working/supported by PK11 */
         FATAL_ERROR(ss, PORT_GetError(), internal_error);
-        return SECFailure;
+        goto loser;
     }
 #endif
 
-    rv = ssl3_NegotiateCipherSuite(ss, suites);
+    /* Don't init hashes if this is the second ClientHello */
+    previousCipherSuite = ss->ssl3.hs.cipher_suite;
+    rv = ssl3_NegotiateCipherSuite(ss, suites, !ss->ssl3.hs.helloRetry);
     if (rv != SECSuccess) {
         FATAL_ERROR(ss, SSL_ERROR_NO_CYPHER_OVERLAP, handshake_failure);
-        return SECFailure;
+        goto loser;
     }
-
+    /* If we are going around again, then we should make sure that the cipher
+     * suite selection doesn't change. That's a sign of client shennanigans. */
+    if (ss->ssl3.hs.helloRetry &&
+        ss->ssl3.hs.cipher_suite != previousCipherSuite) {
+        FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_CLIENT_HELLO, handshake_failure);
+        goto loser;
+    }
     if (ss->ssl3.hs.kea_def->authKeyType != ssl_auth_psk) {
-        /* TODO(ekr@rtfm.com): Free resumeSID. */
         ss->statelessResume = PR_FALSE;
     }
 
     if (ss->statelessResume) {
         PORT_Assert(sid);
 
         rv = tls13_RecoverWrappedSharedSecret(ss, sid);
         if (rv != SECSuccess) {
             FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error);
-            return SECFailure;
+            goto loser;
         }
 
-        SSL_AtomicIncrementLong(&ssl3stats->hch_sid_cache_hits);
-        SSL_AtomicIncrementLong(&ssl3stats->hch_sid_stateless_resumes);
-
         tls13_RestoreCipherInfo(ss, sid);
 
         ss->sec.serverCert = ssl_FindServerCert(ss, &sid->certType);
         PORT_Assert(ss->sec.serverCert);
         ss->sec.localCert = CERT_DupCertificate(ss->sec.serverCert->serverCert);
         if (sid->peerCert != NULL) {
             ss->sec.peerCert = CERT_DupCertificate(sid->peerCert);
         }
         ssl3_RegisterServerHelloExtensionSender(
             ss, ssl_tls13_pre_shared_key_xtn, tls13_ServerSendPreSharedKeyXtn);
-        ss->sec.ci.sid = sid;
-        ss->ssl3.hs.doing0Rtt = tls13_ServerAllow0Rtt(ss, sid);
+
+        tls13_NegotiateZeroRtt(ss, sid);
     } else {
-        if (sid) { /* we had a sid, but it's no longer valid, free it */
-            SSL_AtomicIncrementLong(&ssl3stats->hch_sid_cache_not_ok);
-            if (ss->sec.uncache)
-                ss->sec.uncache(sid);
-            ssl_FreeSID(sid);
-            sid = NULL;
-        }
         ss->ssl3.hs.origCipherSuite = ss->ssl3.hs.cipher_suite;
-        SSL_AtomicIncrementLong(&ssl3stats->hch_sid_cache_misses);
-    }
-
-    rv = tls13_ComputeEarlySecrets(ss, ss->ssl3.hs.doing0Rtt);
-    if (rv != SECSuccess) {
-        FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error);
-        return SECFailure;
+
+        tls13_NegotiateZeroRtt(ss, NULL);
     }
 
     rv = ssl3_ServerCallSNICallback(ss);
     if (rv != SECSuccess) {
-        return SECFailure; /* An alert has already been sent. */
+        goto loser; /* An alert has already been sent. */
     }
 
-    if (sid) {
+    if (ss->statelessResume) {
         /* Check that the negotiated SID and the cached SID match. */
         if (SECITEM_CompareItem(&sid->u.ssl3.srvName,
                                 &ss->ssl3.hs.srvVirtName) != SECEqual) {
             FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_CLIENT_HELLO,
                         handshake_failure);
-            return SECFailure;
+            goto loser;
         }
-    }
-
-    if (!ss->statelessResume) {
+    } else {
         if (ss->ssl3.hs.numClientSigScheme == 0) {
             /* TODO test what happens when we strip signature_algorithms...
                     this might not be needed */
             PORT_SetError(SSL_ERROR_RX_MALFORMED_CLIENT_HELLO);
             FATAL_ERROR(ss, PORT_GetError(), missing_extension);
             return SECFailure;
         }
 
         rv = ssl3_SelectServerCert(ss);
         if (rv != SECSuccess) {
             FATAL_ERROR(ss, PORT_GetError(), handshake_failure);
-            return SECFailure;
+            goto loser;
         }
     }
 
     /* If this is TLS 1.3 we are expecting a ClientKeyShare
      * extension. Missing/absent extension cause failure
      * below. */
-    rv = tls13_HandleClientKeyShare(ss);
+    rv = tls13_HandleClientKeyShare(ss, &shouldRetry);
     if (rv != SECSuccess) {
-        return SECFailure; /* An alert was sent already. */
+        goto loser; /* An alert was sent already. */
     }
-
-    if (!sid) {
+    if (shouldRetry) {
+        /* Unfortunately, there's a bit of cleanup needed here to back out
+         * changes from the stateless resumption setup. */
+        if (ss->statelessResume) {
+            PK11_FreeSymKey(ss->ssl3.hs.resumptionPsk);
+            SECITEM_FreeItem(&ss->ssl3.hs.resumptionContext, PR_FALSE);
+            CERT_DestroyCertificate(ss->sec.localCert);
+            if (ss->sec.peerCert) {
+                CERT_DestroyCertificate(ss->sec.peerCert);
+            }
+        }
+
+        if (sid) { /* Free the sid. */
+            if (ss->sec.uncache)
+                ss->sec.uncache(sid);
+            ssl_FreeSID(sid);
+        }
+        PORT_Assert(ss->ssl3.hs.helloRetry);
+        return SECSuccess;
+    }
+
+    /* From this point we are either committed to resumption, or not. */
+    if (ss->statelessResume) {
+        SSL_AtomicIncrementLong(&ssl3stats->hch_sid_cache_hits);
+        SSL_AtomicIncrementLong(&ssl3stats->hch_sid_stateless_resumes);
+    } else {
+        if (sid) {
+            /* We had a sid, but it's no longer valid, free it. */
+            SSL_AtomicIncrementLong(&ssl3stats->hch_sid_cache_not_ok);
+            if (ss->sec.uncache)
+                ss->sec.uncache(sid);
+            ssl_FreeSID(sid);
+        } else {
+            SSL_AtomicIncrementLong(&ssl3stats->hch_sid_cache_misses);
+        }
+
         sid = ssl3_NewSessionID(ss, PR_TRUE);
-        if (sid == NULL) {
+        if (!sid) {
             FATAL_ERROR(ss, PORT_GetError(), internal_error);
             return SECFailure;
         }
-        ss->sec.ci.sid = sid;
     }
-
-    if (ss->ssl3.hs.doing0Rtt) {
+    /* Take ownership of the session. */
+    ss->sec.ci.sid = sid;
+    sid = NULL;
+
+    if (ss->ssl3.hs.zeroRttState == ssl_0rtt_accepted) {
         /* Store the handshake hash. We'll want it later. */
         ss->ssl3.hs.clientHelloHash = PK11_CloneContext(ss->ssl3.hs.sha);
         if (!ss->ssl3.hs.clientHelloHash) {
             FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error);
             return SECFailure;
         }
 
         rv = tls13_SetCipherSpec(ss, TrafficKeyEarlyHandshake,
                                  CipherSpecRead, PR_FALSE);
         if (rv != SECSuccess) {
             FATAL_ERROR(ss, PORT_GetError(), handshake_failure);
             return SECFailure;
         }
         TLS13_SET_HS_STATE(ss, wait_0rtt_finished);
     } else {
+        PORT_Assert(ss->ssl3.hs.zeroRttState == ssl_0rtt_none ||
+                    ss->ssl3.hs.zeroRttState == ssl_0rtt_ign