From 5162ef8799d09c2d50ee49b78d89b763ab026c35 Mon Sep 17 00:00:00 2001 From: Franziskus Kiefer Date: Fri, 16 Sep 2016 10:00:57 -0700 Subject: [PATCH] Bug 1296266 - NSS 3.27 tip d9f7136f2ae3, r=ttaubert --HG-- extra : rebase_source : 44fbdcc69ba19a91ca6dcc9f2a3f1610ffad7e89 --- .eslintignore | 3 + .../decision_task.yml => .taskcluster.yml} | 10 +- security/nss/TAG-INFO | 2 +- .../automation/taskcluster/docker/setup.sh | 4 +- .../taskcluster/graph/arm/_build_base.yml | 40 - .../taskcluster/graph/arm/_test_base.yml | 27 - .../taskcluster/graph/arm/build32-debug.yml | 29 - .../nss/automation/taskcluster/graph/build.js | 147 ---- .../taskcluster/graph/image_builder.js | 148 ---- .../taskcluster/graph/image_builder.yml | 49 -- .../taskcluster/graph/linux/_build_base.yml | 39 - .../taskcluster/graph/linux/_test_base.yml | 26 - .../taskcluster/graph/linux/build32-debug.yml | 116 --- .../taskcluster/graph/linux/build32-opt.yml | 124 --- .../taskcluster/graph/linux/build64-asan.yml | 38 - .../taskcluster/graph/linux/build64-debug.yml | 146 ---- .../taskcluster/graph/linux/build64-opt.yml | 129 ---- .../automation/taskcluster/graph/package.json | 8 + .../taskcluster/graph/src/context_hash.js | 40 + .../taskcluster/graph/src/extend.js | 381 ++++++++++ .../taskcluster/graph/src/image_builder.js | 62 ++ .../automation/taskcluster/graph/src/index.js | 14 + .../automation/taskcluster/graph/src/merge.js | 10 + .../automation/taskcluster/graph/src/queue.js | 242 ++++++ .../taskcluster/graph/{ => src}/try_syntax.js | 87 ++- .../taskcluster/graph/tests/chains.yml | 14 - .../taskcluster/graph/tests/cipher.yml | 13 - .../taskcluster/graph/tests/crmf.yml | 14 - .../automation/taskcluster/graph/tests/db.yml | 13 - .../automation/taskcluster/graph/tests/ec.yml | 13 - .../taskcluster/graph/tests/fips.yml | 14 - .../taskcluster/graph/tests/gtests.yml | 13 - .../taskcluster/graph/tests/lowhash.yml | 13 - .../taskcluster/graph/tests/merge.yml | 13 - .../taskcluster/graph/tests/sdr.yml | 13 - .../taskcluster/graph/tests/smime.yml | 13 - .../taskcluster/graph/tests/ssl.yml | 65 -- .../taskcluster/graph/tests/tools.yml | 13 - .../taskcluster/graph/tools/_build_base.yml | 29 - .../taskcluster/graph/tools/clang-format.yml | 15 - .../taskcluster/graph/tools/scan-build.yml | 26 - .../taskcluster/graph/windows/_build_base.yml | 37 - .../taskcluster/graph/windows/_test_base.yml | 24 - .../graph/windows/build64-debug.yml | 55 -- .../taskcluster/graph/windows/build64-opt.yml | 57 -- .../nss/automation/taskcluster/graph/yaml.js | 51 -- .../automation/taskcluster/scripts/build.sh | 8 +- .../taskcluster/scripts/extend_task_graph.sh | 4 +- .../taskcluster/scripts/gen_certs.sh | 29 + .../taskcluster/scripts/run_clang_format.sh | 28 +- .../taskcluster/scripts/run_tests.sh | 9 +- .../automation/taskcluster/scripts/tools.sh | 12 + .../automation/taskcluster/windows/build.sh | 8 +- .../taskcluster/windows/gen_certs.sh | 19 + .../taskcluster/windows/releng.manifest | 8 +- .../automation/taskcluster/windows/setup.sh | 6 +- security/nss/cmd/lib/basicutil.c | 7 +- security/nss/cmd/modutil/install.c | 2 +- security/nss/coreconf/coreconf.dep | 1 - security/nss/doc/certutil.xml | 19 +- security/nss/doc/html/certutil.html | 17 +- security/nss/doc/nroff/certutil.1 | 28 +- .../nss/external_tests/ssl_gtest/manifest.mn | 1 + .../ssl_gtest/ssl_0rtt_unittest.cc | 98 ++- .../ssl_gtest/ssl_auth_unittest.cc | 23 +- .../ssl_gtest/ssl_ciphersuite_unittest.cc | 2 +- .../ssl_gtest/ssl_damage_unittest.cc | 4 +- .../ssl_gtest/ssl_dhe_unittest.cc | 77 +- .../ssl_gtest/ssl_drop_unittest.cc | 21 +- .../ssl_gtest/ssl_ecdh_unittest.cc | 224 +++++- .../ssl_gtest/ssl_ems_unittest.cc | 2 +- .../ssl_gtest/ssl_extension_unittest.cc | 4 +- .../nss/external_tests/ssl_gtest/ssl_gtest.cc | 2 +- .../ssl_gtest/ssl_hrr_unittest.cc | 197 +++++ .../ssl_gtest/ssl_loopback_unittest.cc | 4 +- .../ssl_gtest/ssl_record_unittest.cc | 3 +- .../ssl_gtest/ssl_resumption_unittest.cc | 14 +- .../ssl_gtest/ssl_staticrsa_unittest.cc | 2 +- .../ssl_gtest/ssl_v2_client_hello_unittest.cc | 2 +- .../ssl_gtest/ssl_version_unittest.cc | 2 +- .../nss/external_tests/ssl_gtest/test_io.cc | 7 + .../nss/external_tests/ssl_gtest/test_io.h | 2 +- .../nss/external_tests/ssl_gtest/tls_agent.cc | 37 +- .../nss/external_tests/ssl_gtest/tls_agent.h | 25 +- .../external_tests/ssl_gtest/tls_connect.cc | 69 +- .../external_tests/ssl_gtest/tls_connect.h | 17 + .../external_tests/ssl_gtest/tls_filter.cc | 8 + .../nss/external_tests/ssl_gtest/tls_filter.h | 19 +- .../nss/external_tests/ssl_gtest/tls_parser.h | 4 +- security/nss/lib/certhigh/ocsp.c | 7 + security/nss/lib/ssl/SSLerrs.h | 9 + security/nss/lib/ssl/derive.c | 4 +- security/nss/lib/ssl/dtlscon.c | 24 +- security/nss/lib/ssl/ssl.def | 2 +- security/nss/lib/ssl/ssl.h | 28 +- security/nss/lib/ssl/ssl3con.c | 396 ++++++---- security/nss/lib/ssl/ssl3ecc.c | 96 ++- security/nss/lib/ssl/ssl3ext.c | 35 +- security/nss/lib/ssl/sslcon.c | 2 +- security/nss/lib/ssl/sslerr.h | 3 + security/nss/lib/ssl/sslimpl.h | 80 +- security/nss/lib/ssl/sslinfo.c | 10 +- security/nss/lib/ssl/sslsecur.c | 12 +- security/nss/lib/ssl/sslsock.c | 145 ++-- security/nss/lib/ssl/tls13con.c | 712 +++++++++++++----- security/nss/lib/ssl/tls13con.h | 2 +- 106 files changed, 2656 insertions(+), 2425 deletions(-) rename security/nss/{automation/taskcluster/decision_task.yml => .taskcluster.yml} (91%) delete mode 100644 security/nss/automation/taskcluster/graph/arm/_build_base.yml delete mode 100644 security/nss/automation/taskcluster/graph/arm/_test_base.yml delete mode 100644 security/nss/automation/taskcluster/graph/arm/build32-debug.yml delete mode 100644 security/nss/automation/taskcluster/graph/build.js delete mode 100644 security/nss/automation/taskcluster/graph/image_builder.js delete mode 100644 security/nss/automation/taskcluster/graph/image_builder.yml delete mode 100644 security/nss/automation/taskcluster/graph/linux/_build_base.yml delete mode 100644 security/nss/automation/taskcluster/graph/linux/_test_base.yml delete mode 100644 security/nss/automation/taskcluster/graph/linux/build32-debug.yml delete mode 100644 security/nss/automation/taskcluster/graph/linux/build32-opt.yml delete mode 100644 security/nss/automation/taskcluster/graph/linux/build64-asan.yml delete mode 100644 security/nss/automation/taskcluster/graph/linux/build64-debug.yml delete mode 100644 security/nss/automation/taskcluster/graph/linux/build64-opt.yml create mode 100644 security/nss/automation/taskcluster/graph/src/context_hash.js create mode 100644 security/nss/automation/taskcluster/graph/src/extend.js create mode 100644 security/nss/automation/taskcluster/graph/src/image_builder.js create mode 100644 security/nss/automation/taskcluster/graph/src/index.js create mode 100644 security/nss/automation/taskcluster/graph/src/merge.js create mode 100644 security/nss/automation/taskcluster/graph/src/queue.js rename security/nss/automation/taskcluster/graph/{ => src}/try_syntax.js (57%) delete mode 100644 security/nss/automation/taskcluster/graph/tests/chains.yml delete mode 100644 security/nss/automation/taskcluster/graph/tests/cipher.yml delete mode 100644 security/nss/automation/taskcluster/graph/tests/crmf.yml delete mode 100644 security/nss/automation/taskcluster/graph/tests/db.yml delete mode 100644 security/nss/automation/taskcluster/graph/tests/ec.yml delete mode 100644 security/nss/automation/taskcluster/graph/tests/fips.yml delete mode 100644 security/nss/automation/taskcluster/graph/tests/gtests.yml delete mode 100644 security/nss/automation/taskcluster/graph/tests/lowhash.yml delete mode 100644 security/nss/automation/taskcluster/graph/tests/merge.yml delete mode 100644 security/nss/automation/taskcluster/graph/tests/sdr.yml delete mode 100644 security/nss/automation/taskcluster/graph/tests/smime.yml delete mode 100644 security/nss/automation/taskcluster/graph/tests/ssl.yml delete mode 100644 security/nss/automation/taskcluster/graph/tests/tools.yml delete mode 100644 security/nss/automation/taskcluster/graph/tools/_build_base.yml delete mode 100644 security/nss/automation/taskcluster/graph/tools/clang-format.yml delete mode 100644 security/nss/automation/taskcluster/graph/tools/scan-build.yml delete mode 100644 security/nss/automation/taskcluster/graph/windows/_build_base.yml delete mode 100644 security/nss/automation/taskcluster/graph/windows/_test_base.yml delete mode 100644 security/nss/automation/taskcluster/graph/windows/build64-debug.yml delete mode 100644 security/nss/automation/taskcluster/graph/windows/build64-opt.yml delete mode 100644 security/nss/automation/taskcluster/graph/yaml.js create mode 100755 security/nss/automation/taskcluster/scripts/gen_certs.sh create mode 100644 security/nss/automation/taskcluster/windows/gen_certs.sh create mode 100644 security/nss/external_tests/ssl_gtest/ssl_hrr_unittest.cc diff --git a/.eslintignore b/.eslintignore index 3e370d2118c8..db3ebf0f518d 100644 --- a/.eslintignore +++ b/.eslintignore @@ -251,3 +251,6 @@ toolkit/webapps/** # Third party toolkit/modules/third_party/** + +#NSS +security/nss/** diff --git a/security/nss/automation/taskcluster/decision_task.yml b/security/nss/.taskcluster.yml similarity index 91% rename from security/nss/automation/taskcluster/decision_task.yml rename to security/nss/.taskcluster.yml index 48f9abef0da7..9d56c9bcd29a 100644 --- a/security/nss/automation/taskcluster/decision_task.yml +++ b/security/nss/.taskcluster.yml @@ -77,14 +77,8 @@ tasks: 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 + features: + taskclusterProxy: true extra: treeherder: diff --git a/security/nss/TAG-INFO b/security/nss/TAG-INFO index fb8302aa468d..c232b9b40b9b 100644 --- a/security/nss/TAG-INFO +++ b/security/nss/TAG-INFO @@ -1 +1 @@ -NSS_3_27_BETA3 +NSS_3_27_BRANCH diff --git a/security/nss/automation/taskcluster/docker/setup.sh b/security/nss/automation/taskcluster/docker/setup.sh index a2416dcd6656..3b0b3b550005 100644 --- a/security/nss/automation/taskcluster/docker/setup.sh +++ b/security/nss/automation/taskcluster/docker/setup.sh @@ -44,8 +44,8 @@ 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 diff --git a/security/nss/automation/taskcluster/graph/arm/_build_base.yml b/security/nss/automation/taskcluster/graph/arm/_build_base.yml deleted file mode 100644 index cef0fd48b9d7..000000000000 --- 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 diff --git a/security/nss/automation/taskcluster/graph/arm/_test_base.yml b/security/nss/automation/taskcluster/graph/arm/_test_base.yml deleted file mode 100644 index 5f7fd68d63cb..000000000000 --- 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 diff --git a/security/nss/automation/taskcluster/graph/arm/build32-debug.yml b/security/nss/automation/taskcluster/graph/arm/build32-debug.yml deleted file mode 100644 index e9c61c0918da..000000000000 --- 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 diff --git a/security/nss/automation/taskcluster/graph/build.js b/security/nss/automation/taskcluster/graph/build.js deleted file mode 100644 index 21d17248f5fd..000000000000 --- 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)); -}); diff --git a/security/nss/automation/taskcluster/graph/image_builder.js b/security/nss/automation/taskcluster/graph/image_builder.js deleted file mode 100644 index 4e32f3706f7a..000000000000 --- 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 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 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; diff --git a/security/nss/automation/taskcluster/graph/image_builder.yml b/security/nss/automation/taskcluster/graph/image_builder.yml deleted file mode 100644 index 417646b8373c..000000000000 --- 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 diff --git a/security/nss/automation/taskcluster/graph/linux/_build_base.yml b/security/nss/automation/taskcluster/graph/linux/_build_base.yml deleted file mode 100644 index 3f23d4be209d..000000000000 --- 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 diff --git a/security/nss/automation/taskcluster/graph/linux/_test_base.yml b/security/nss/automation/taskcluster/graph/linux/_test_base.yml deleted file mode 100644 index 7b4f9337b2f2..000000000000 --- 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 diff --git a/security/nss/automation/taskcluster/graph/linux/build32-debug.yml b/security/nss/automation/taskcluster/graph/linux/build32-debug.yml deleted file mode 100644 index c75138fc4f45..000000000000 --- 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 diff --git a/security/nss/automation/taskcluster/graph/linux/build32-opt.yml b/security/nss/automation/taskcluster/graph/linux/build32-opt.yml deleted file mode 100644 index c71b901b5781..000000000000 --- 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 diff --git a/security/nss/automation/taskcluster/graph/linux/build64-asan.yml b/security/nss/automation/taskcluster/graph/linux/build64-asan.yml deleted file mode 100644 index d7fca39e2045..000000000000 --- 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 diff --git a/security/nss/automation/taskcluster/graph/linux/build64-debug.yml b/security/nss/automation/taskcluster/graph/linux/build64-debug.yml deleted file mode 100644 index 197727bb31ec..000000000000 --- 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 diff --git a/security/nss/automation/taskcluster/graph/linux/build64-opt.yml b/security/nss/automation/taskcluster/graph/linux/build64-opt.yml deleted file mode 100644 index ff57c53129ad..000000000000 --- 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 diff --git a/security/nss/automation/taskcluster/graph/package.json b/security/nss/automation/taskcluster/graph/package.json index e58ab0b0d65b..d866adebc965 100644 --- a/security/nss/automation/taskcluster/graph/package.json +++ b/security/nss/automation/taskcluster/graph/package.json @@ -4,7 +4,15 @@ "private": true, "author": "Tim Taubert ", "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", diff --git a/security/nss/automation/taskcluster/graph/src/context_hash.js b/security/nss/automation/taskcluster/graph/src/context_hash.js new file mode 100644 index 000000000000..67bf257842c9 --- /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(",")); +} diff --git a/security/nss/automation/taskcluster/graph/src/extend.js b/security/nss/automation/taskcluster/graph/src/extend.js new file mode 100644 index 000000000000..ef0fb5e57bec --- /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(); +} diff --git a/security/nss/automation/taskcluster/graph/src/image_builder.js b/security/nss/automation/taskcluster/graph/src/image_builder.js new file mode 100644 index 000000000000..093eacb4423c --- /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" + }; +} diff --git a/security/nss/automation/taskcluster/graph/src/index.js b/security/nss/automation/taskcluster/graph/src/index.js new file mode 100644 index 000000000000..4153e1b18f0f --- /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); diff --git a/security/nss/automation/taskcluster/graph/src/merge.js b/security/nss/automation/taskcluster/graph/src/merge.js new file mode 100644 index 000000000000..17043dd8e4eb --- /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); +} diff --git a/security/nss/automation/taskcluster/graph/src/queue.js b/security/nss/automation/taskcluster/graph/src/queue.js new file mode 100644 index 000000000000..4efbb92adab7 --- /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(); +} diff --git a/security/nss/automation/taskcluster/graph/try_syntax.js b/security/nss/automation/taskcluster/graph/src/try_syntax.js similarity index 57% rename from security/nss/automation/taskcluster/graph/try_syntax.js rename to security/nss/automation/taskcluster/graph/src/try_syntax.js index b2fbfbefae5c..032d4515cba0 100644 --- a/security/nss/automation/taskcluster/graph/try_syntax.js +++ b/security/nss/automation/taskcluster/graph/src/try_syntax.js @@ -2,8 +2,9 @@ * 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"); +import * as queue from "./queue"; +import intersect from "intersect"; +import parse_args from "minimist"; function parseOptions(opts) { opts = parse_args(opts.split(/\s+/), { @@ -13,7 +14,7 @@ function parseOptions(opts) { }); // Parse build types (d=debug, o=opt). - var builds = intersect(opts.build.split(""), ["d", "o"]); + let builds = intersect(opts.build.split(""), ["d", "o"]); // If the given value is nonsense default to debug and opt builds. if (builds.length == 0) { @@ -21,8 +22,8 @@ function parseOptions(opts) { } // Parse platforms. - var allPlatforms = ["linux", "linux64", "linux64-asan", "win64", "arm"]; - var platforms = intersect(opts.platform.split(/\s*,\s*/), allPlatforms); + 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") { @@ -30,9 +31,9 @@ function parseOptions(opts) { } // Parse unit tests. - var allUnitTests = ["crmf", "chains", "cipher", "db", "ec", "fips", "gtest", + let allUnitTests = ["crmf", "chains", "cipher", "db", "ec", "fips", "gtest", "lowhash", "merge", "sdr", "smime", "tools", "ssl"]; - var unittests = intersect(opts.unittests.split(/\s*,\s*/), allUnitTests); + 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. @@ -43,8 +44,8 @@ function parseOptions(opts) { } // Parse tools. - var allTools = ["clang-format", "scan-build"]; - var tools = intersect(opts.tools.split(/\s*,\s*/), allTools); + 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. @@ -63,36 +64,20 @@ function parseOptions(opts) { }; } -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; - +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 (machine == "nss-tools") { - return opts.tools.some(function (tool) { - var symbol = th.symbol.toLowerCase(); - return symbol.startsWith(tool); + if (task.platform == "nss-tools") { + return opts.tools.some(tool => { + return task.symbol.toLowerCase().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 (task.tests) { + let found = opts.unittests.some(test => { + return (task.group || task.symbol).toLowerCase().startsWith(test); }); if (!found) { @@ -101,13 +86,15 @@ function filterTasks(tasks, comment) { } // Filter extra builds. - if (th.groupSymbol == "Builds" && !opts.extra) { + if (task.group == "Builds" && !opts.extra) { return false; } + let coll = name => name == (task.collection || "opt"); + // Filter by platform. - found = opts.platforms.some(function (platform) { - var aliases = { + let found = opts.platforms.some(platform => { + let aliases = { "linux": "linux32", "linux64-asan": "linux64", "win64": "windows2012-64", @@ -115,15 +102,15 @@ function filterTasks(tasks, comment) { }; // Check the platform name. - var keep = machine == (aliases[platform] || platform); + let keep = (task.platform == (aliases[platform] || platform)); // Additional checks. if (platform == "linux64-asan") { - keep &= coll.asan; + keep &= coll("asan"); } else if (platform == "arm") { - keep &= (coll["arm-opt"] || coll["arm-debug"]); + keep &= coll("arm-opt") || coll("arm-debug"); } else { - keep &= (coll.opt || coll.debug); + keep &= coll("opt") || coll("debug"); } return keep; @@ -134,10 +121,20 @@ function filterTasks(tasks, comment) { } // 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); - }); + let isDebug = coll("debug") || coll("asan") || coll("arm-debug"); + return (isDebug && opts.builds.includes("d")) || + (!isDebug && opts.builds.includes("o")); + } } -module.exports.filterTasks = filterTasks; +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]))); + } +} diff --git a/security/nss/automation/taskcluster/graph/tests/chains.yml b/security/nss/automation/taskcluster/graph/tests/chains.yml deleted file mode 100644 index b553d8d3925b..000000000000 --- 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 diff --git a/security/nss/automation/taskcluster/graph/tests/cipher.yml b/security/nss/automation/taskcluster/graph/tests/cipher.yml deleted file mode 100644 index e1d0f9faaccf..000000000000 --- 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 diff --git a/security/nss/automation/taskcluster/graph/tests/crmf.yml b/security/nss/automation/taskcluster/graph/tests/crmf.yml deleted file mode 100644 index a29a9c5e29df..000000000000 --- 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 diff --git a/security/nss/automation/taskcluster/graph/tests/db.yml b/security/nss/automation/taskcluster/graph/tests/db.yml deleted file mode 100644 index b5ee776537c6..000000000000 --- 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 diff --git a/security/nss/automation/taskcluster/graph/tests/ec.yml b/security/nss/automation/taskcluster/graph/tests/ec.yml deleted file mode 100644 index c6e21917e91c..000000000000 --- 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 diff --git a/security/nss/automation/taskcluster/graph/tests/fips.yml b/security/nss/automation/taskcluster/graph/tests/fips.yml deleted file mode 100644 index 58484c7b2959..000000000000 --- 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 diff --git a/security/nss/automation/taskcluster/graph/tests/gtests.yml b/security/nss/automation/taskcluster/graph/tests/gtests.yml deleted file mode 100644 index d4a355a24010..000000000000 --- 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 diff --git a/security/nss/automation/taskcluster/graph/tests/lowhash.yml b/security/nss/automation/taskcluster/graph/tests/lowhash.yml deleted file mode 100644 index 097a92377407..000000000000 --- 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 diff --git a/security/nss/automation/taskcluster/graph/tests/merge.yml b/security/nss/automation/taskcluster/graph/tests/merge.yml deleted file mode 100644 index 7db008a1d699..000000000000 --- 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 diff --git a/security/nss/automation/taskcluster/graph/tests/sdr.yml b/security/nss/automation/taskcluster/graph/tests/sdr.yml deleted file mode 100644 index 12d881edad90..000000000000 --- 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 diff --git a/security/nss/automation/taskcluster/graph/tests/smime.yml b/security/nss/automation/taskcluster/graph/tests/smime.yml deleted file mode 100644 index f5c3ab8fbe09..000000000000 --- 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 diff --git a/security/nss/automation/taskcluster/graph/tests/ssl.yml b/security/nss/automation/taskcluster/graph/tests/ssl.yml deleted file mode 100644 index da66c438474e..000000000000 --- 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 diff --git a/security/nss/automation/taskcluster/graph/tests/tools.yml b/security/nss/automation/taskcluster/graph/tests/tools.yml deleted file mode 100644 index 2edcce01a03a..000000000000 --- 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 diff --git a/security/nss/automation/taskcluster/graph/tools/_build_base.yml b/security/nss/automation/taskcluster/graph/tools/_build_base.yml deleted file mode 100644 index cd7244b9702c..000000000000 --- 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 diff --git a/security/nss/automation/taskcluster/graph/tools/clang-format.yml b/security/nss/automation/taskcluster/graph/tools/clang-format.yml deleted file mode 100644 index 16c18dd2cd0e..000000000000 --- 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 diff --git a/security/nss/automation/taskcluster/graph/tools/scan-build.yml b/security/nss/automation/taskcluster/graph/tools/scan-build.yml deleted file mode 100644 index 8733d845d8e2..000000000000 --- 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 diff --git a/security/nss/automation/taskcluster/graph/windows/_build_base.yml b/security/nss/automation/taskcluster/graph/windows/_build_base.yml deleted file mode 100644 index a75a7a1af2ef..000000000000 --- 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 diff --git a/security/nss/automation/taskcluster/graph/windows/_test_base.yml b/security/nss/automation/taskcluster/graph/windows/_test_base.yml deleted file mode 100644 index 0ffaabc75cc1..000000000000 --- 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 diff --git a/security/nss/automation/taskcluster/graph/windows/build64-debug.yml b/security/nss/automation/taskcluster/graph/windows/build64-debug.yml deleted file mode 100644 index ccc47f2d75d3..000000000000 --- 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 diff --git a/security/nss/automation/taskcluster/graph/windows/build64-opt.yml b/security/nss/automation/taskcluster/graph/windows/build64-opt.yml deleted file mode 100644 index ad4d46e31235..000000000000 --- 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 diff --git a/security/nss/automation/taskcluster/graph/yaml.js b/security/nss/automation/taskcluster/graph/yaml.js deleted file mode 100644 index dd4f6ba47bd5..000000000000 --- 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; diff --git a/security/nss/automation/taskcluster/scripts/build.sh b/security/nss/automation/taskcluster/scripts/build.sh index a6a2021643ff..c44a321d8cf5 100755 --- a/security/nss/automation/taskcluster/scripts/build.sh +++ b/security/nss/automation/taskcluster/scripts/build.sh @@ -18,12 +18,6 @@ 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 diff --git a/security/nss/automation/taskcluster/scripts/extend_task_graph.sh b/security/nss/automation/taskcluster/scripts/extend_task_graph.sh index abbd4ecd7035..5a3fb8d98bda 100755 --- a/security/nss/automation/taskcluster/scripts/extend_task_graph.sh +++ b/security/nss/automation/taskcluster/scripts/extend_task_graph.sh @@ -12,5 +12,5 @@ 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 diff --git a/security/nss/automation/taskcluster/scripts/gen_certs.sh b/security/nss/automation/taskcluster/scripts/gen_certs.sh new file mode 100755 index 000000000000..56b690d67911 --- /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 diff --git a/security/nss/automation/taskcluster/scripts/run_clang_format.sh b/security/nss/automation/taskcluster/scripts/run_clang_format.sh index bb3fca81509e..54177f5d2478 100755 --- a/security/nss/automation/taskcluster/scripts/run_clang_format.sh +++ b/security/nss/automation/taskcluster/scripts/run_clang_format.sh @@ -7,18 +7,12 @@ if [ $(id -u) -eq 0 ]; then 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 @@ -47,15 +41,15 @@ else ) 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 ]] diff --git a/security/nss/automation/taskcluster/scripts/run_tests.sh b/security/nss/automation/taskcluster/scripts/run_tests.sh index b917a9d4874e..d8341ee82d82 100755 --- a/security/nss/automation/taskcluster/scripts/run_tests.sh +++ b/security/nss/automation/taskcluster/scripts/run_tests.sh @@ -2,9 +2,9 @@ set -v -e -x -if [ $(id -u) = 0 ]; then - source $(dirname $0)/tools.sh +source $(dirname $0)/tools.sh +if [ $(id -u) = 0 ]; then # Set compiler. switch_compilers @@ -16,10 +16,7 @@ if [ $(id -u) = 0 ]; then 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 diff --git a/security/nss/automation/taskcluster/scripts/tools.sh b/security/nss/automation/taskcluster/scripts/tools.sh index 57f45d6be6b3..632c909c2a41 100644 --- a/security/nss/automation/taskcluster/scripts/tools.sh +++ b/security/nss/automation/taskcluster/scripts/tools.sh @@ -27,3 +27,15 @@ hg_clone() { 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 +} diff --git a/security/nss/automation/taskcluster/windows/build.sh b/security/nss/automation/taskcluster/windows/build.sh index c275df54d1f2..6c8a474708a4 100644 --- a/security/nss/automation/taskcluster/windows/build.sh +++ b/security/nss/automation/taskcluster/windows/build.sh @@ -11,11 +11,5 @@ 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 diff --git a/security/nss/automation/taskcluster/windows/gen_certs.sh b/security/nss/automation/taskcluster/windows/gen_certs.sh new file mode 100644 index 000000000000..ead16bbc384e --- /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 diff --git a/security/nss/automation/taskcluster/windows/releng.manifest b/security/nss/automation/taskcluster/windows/releng.manifest index 403be2b04627..b3f4498540b8 100644 --- 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 } ] diff --git a/security/nss/automation/taskcluster/windows/setup.sh b/security/nss/automation/taskcluster/windows/setup.sh index 32732774a488..80cee2850e15 100644 --- a/security/nss/automation/taskcluster/windows/setup.sh +++ b/security/nss/automation/taskcluster/windows/setup.sh @@ -18,7 +18,7 @@ hg_clone() { 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" @@ -26,5 +26,5 @@ 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" diff --git a/security/nss/cmd/lib/basicutil.c b/security/nss/cmd/lib/basicutil.c index cacf5be691a7..49789bc29b91 100644 --- a/security/nss/cmd/lib/basicutil.c +++ b/security/nss/cmd/lib/basicutil.c @@ -687,12 +687,7 @@ 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 diff --git a/security/nss/cmd/modutil/install.c b/security/nss/cmd/modutil/install.c index 99657de6de6d..ea4aeb77cdfc 100644 --- a/security/nss/cmd/modutil/install.c +++ b/security/nss/cmd/modutil/install.c @@ -267,7 +267,7 @@ static void error(long va_alist, ...) #else static void -error(Pk11Install_Error errcode, ...) +error(PRErrorCode errcode, ...) #endif { diff --git a/security/nss/coreconf/coreconf.dep b/security/nss/coreconf/coreconf.dep index 590d1bfaeee3..5182f75552c8 100644 --- a/security/nss/coreconf/coreconf.dep +++ b/security/nss/coreconf/coreconf.dep @@ -10,4 +10,3 @@ */ #error "Do not include this header file." - diff --git a/security/nss/doc/certutil.xml b/security/nss/doc/certutil.xml index 08a952feb546..ec5081ecd6be 100644 --- a/security/nss/doc/certutil.xml +++ b/security/nss/doc/certutil.xml @@ -381,18 +381,15 @@ of the attribute codes: T - trusted CA for client authentication (ssl server only) - - - u - user - - The attribute codes for the categories are separated by commas, and the entire set of attributes enclosed by quotation marks. For example: --t "TCu,Cu,Tu" +-t "TC,C,T" - Use the -L option to see a list of the current certificates and trust attributes in a certificate database. + Use the -L option to see a list of the current certificates and trust attributes in a certificate database. + + 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. @@ -860,7 +857,7 @@ The interative prompts for key usage and whether any extensions are critical and From there, new certificates can reference the self-signed certificate: -$ 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 Generating a Certificate from a Certificate Request @@ -1023,11 +1020,11 @@ certutil: Checking token "NSS Certificate DB" in slot "NSS User Private Key and For example: -$ 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 A related command option, , is used specifically to add email certificates to the certificate database. The command has the same arguments as the command. The trust arguments for certificates have the format SSL,S/MIME,Code-signing, so the middle trust settings relate most to email certificates (though the others can be set). For example: -$ 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 Deleting Certificates to the Database @@ -1057,7 +1054,7 @@ certutil: Checking token "NSS Certificate DB" in slot "NSS User Private Key and For example: -$ 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" Printing the Certificate Chain diff --git a/security/nss/doc/html/certutil.html b/security/nss/doc/html/certutil.html index 7c4b5e6c5425..0829ca9c73f9 100644 --- a/security/nss/doc/html/certutil.html +++ b/security/nss/doc/html/certutil.html @@ -1,4 +1,4 @@ -CERTUTIL

Name

certutil — Manage keys and certificate in both NSS databases and other NSS tokens

Synopsis

certutil [options] [[arguments]]

STATUS

This documentation is still work in progress. Please contribute to the initial review in Mozilla NSS bug 836477 +CERTUTIL

Name

certutil — Manage keys and certificate in both NSS databases and other NSS tokens

Synopsis

certutil [options] [[arguments]]

STATUS

This documentation is still work in progress. Please contribute to the initial review in Mozilla NSS bug 836477

Description

The Certificate Database Tool, certutil, 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.

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 modutil manpage.

Command Options and Arguments

Running certutil 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 -H will list all the command options and their relevant arguments.

Command Options

-A

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.

-B

Run a series of commands from the specified batch file. This requires the -i argument.

-C

Create a new binary certificate file from a binary certificate request file. Use the -i argument to specify the certificate request file. If this argument is not used, certutil prompts for a filename.

-D

Delete a certificate from the certificate database.

--rename

Change the database nickname of a certificate.

-E

Add an email certificate to the certificate database.

-F

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 -d argument. Use the -k argument to specify explicitly whether to delete a DSA, RSA, or ECC key. If you don't use the -k argument, the option looks for an RSA key matching the specified nickname.

@@ -51,12 +51,11 @@ of the attribute codes: C - Trusted CA (implies c)

  • T - trusted CA for client authentication (ssl server only) -

  • - u - user

  • The attribute codes for the categories are separated by commas, and the entire set of attributes enclosed by quotation marks. For example: -

    -t "TCu,Cu,Tu"

    - Use the -L option to see a list of the current certificates and trust attributes in a certificate database.

    -u certusage

    Specify a usage context to apply when validating a certificate with the -V option.

    The contexts are the following:

    • C (as an SSL client)

    • V (as an SSL server)

    • L (as an SSL CA)

    • A (as Any CA)

    • Y (Verify CA)

    • S (as an email signer)

    • R (as an email recipient)

    • O (as an OCSP status responder)

    • J (as an object signer)

    -v valid-months

    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 -w option. If this argument is not used, the default validity period is three months.

    -w offset-months

    Set an offset from the current system time, in months, +

    -t "TC,C,T"

    + Use the -L option to see a list of the current certificates and trust attributes in a certificate database.

    + 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.

    -u certusage

    Specify a usage context to apply when validating a certificate with the -V option.

    The contexts are the following:

    • C (as an SSL client)

    • V (as an SSL server)

    • L (as an SSL CA)

    • A (as Any CA)

    • Y (Verify CA)

    • S (as an email signer)

    • R (as an email recipient)

    • O (as an OCSP status responder)

    • J (as an object signer)

    -v valid-months

    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 -w option. If this argument is not used, the default validity period is three months.

    -w offset-months

    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 @@ -162,7 +161,7 @@ Generating key. This may take a few moments... The interative prompts for key usage and whether any extensions are critical and responses have been ommitted for brevity.

    From there, new certificates can reference the self-signed certificate: -

    $ 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

    Generating a Certificate from a Certificate Request

    +

    $ certutil -S -s "CN=My Server Cert" -n my-server-cert -c "my-ca-cert" -t ",," -1 -5 -6 -8 -m 730

    Generating a Certificate from a Certificate Request

    When a certificate request is created, a certificate can be generated by using the request and then referencing a certificate authority signing certificate (the issuer specified in the -c argument). The issuing certificate must be in the certificate database in the specified directory.

    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]

    For example: @@ -284,9 +283,9 @@ certutil: Checking token "NSS Certificate DB" in slot "NSS User Private Key and Existing certificates or certificate requests can be added manually to the certificate database, even if they were generated elsewhere. This uses the -A command option.

    certutil -A -n certname -t trustargs -d [sql:]directory [-a] [-i input-file]

    For example: -

    $ 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

    A related command option, -E, is used specifically to add email certificates to the certificate database. The -E command has the same arguments as the -A command. The trust arguments for certificates have the format SSL,S/MIME,Code-signing, so the middle trust settings relate most to email certificates (though the others can be set). For example: -

    $ certutil -E -n "CN=John Smith Email Cert" -t ",Pu," -d sql:/home/my/sharednssdb -i /home/example-certs/email.cer

    Deleting Certificates to the Database

    +

    $ certutil -E -n "CN=John Smith Email Cert" -t ",P," -d sql:/home/my/sharednssdb -i /home/example-certs/email.cer

    Deleting Certificates to the Database

    Certificates can be deleted from a database using the -D option. The only required options are to give the security database directory and to identify the certificate nickname.

    certutil -D -d [sql:]directory -n "nickname"

    For example: @@ -298,7 +297,7 @@ certutil: Checking token "NSS Certificate DB" in slot "NSS User Private Key and 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.

    certutil -M -n certificate-name -t trust-args -d [sql:]directory

    For example: -

    $ certutil -M -n "My CA Certificate" -d sql:/home/my/sharednssdb -t "CTu,CTu,CTu"

    Printing the Certificate Chain

    +

    $ certutil -M -n "My CA Certificate" -d sql:/home/my/sharednssdb -t "CT,CT,CT"

    Printing the Certificate Chain

    Certificates can be issued in chains because every certificate authority itself has a certificate; when a CA issues a certificate, it essentially stamps that certificate with its own fingerprint. The -O 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:

    $ 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]
    diff --git a/security/nss/doc/nroff/certutil.1 b/security/nss/doc/nroff/certutil.1
    index 1f75f978ed24..58c41b2b82c9 100644
    --- a/security/nss/doc/nroff/certutil.1
    +++ b/security/nss/doc/nroff/certutil.1
    @@ -2,12 +2,12 @@
     .\"     Title: CERTUTIL
     .\"    Author: [see the "Authors" section]
     .\" Generator: DocBook XSL Stylesheets v1.78.1 
    -.\"      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
     .\" -----------------------------------------------------------------
    @@ -452,23 +452,13 @@ for each trust setting\&. In each category position, use none, any, or all of th
     \- 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
    @@ -1322,7 +1312,7 @@ From there, new certificates can reference the self\-signed certificate:
     .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
    @@ -1598,7 +1588,7 @@ For example:
     .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
    @@ -1616,7 +1606,7 @@ command\&. The trust arguments for certificates have the format
     .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
    @@ -1698,7 +1688,7 @@ For example:
     .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
    diff --git a/security/nss/external_tests/ssl_gtest/manifest.mn b/security/nss/external_tests/ssl_gtest/manifest.mn
    index 4a7feba0e83e..540f518b0227 100644
    --- a/security/nss/external_tests/ssl_gtest/manifest.mn
    +++ b/security/nss/external_tests/ssl_gtest/manifest.mn
    @@ -24,6 +24,7 @@ CPPSRCS = \
           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 \
    diff --git a/security/nss/external_tests/ssl_gtest/ssl_0rtt_unittest.cc b/security/nss/external_tests/ssl_gtest/ssl_0rtt_unittest.cc
    index 291e635ab3e2..12cf42f0a95f 100644
    --- a/security/nss/external_tests/ssl_gtest/ssl_0rtt_unittest.cc
    +++ b/security/nss/external_tests/ssl_gtest/ssl_0rtt_unittest.cc
    @@ -4,8 +4,8 @@
      * 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 "secerr.h"
    +#include "ssl.h"
     #include "sslerr.h"
     #include "sslproto.h"
     
    @@ -22,20 +22,19 @@ extern "C" {
     
     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);
    @@ -45,7 +44,7 @@ TEST_F(TlsConnectTest, ZeroRttServerRejectByOption) {
       SendReceive();
     }
     
    -TEST_F(TlsConnectTest, ZeroRttServerForgetTicket) {
    +TEST_P(TlsConnectTls13, ZeroRttServerForgetTicket) {
       SetupForZeroRtt();
       client_->Set0RttEnabled(true);
       server_->Set0RttEnabled(true);
    @@ -58,11 +57,7 @@ TEST_F(TlsConnectTest, ZeroRttServerForgetTicket) {
       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();
    @@ -84,19 +79,7 @@ TEST_F(TlsConnectTest, ZeroRttServerOnly) {
       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();
    @@ -114,28 +97,9 @@ TEST_F(TlsConnectTest, TestTls13ZeroRttAlpn) {
       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"
    @@ -159,7 +123,7 @@ TEST_F(TlsConnectTest, TestTls13ZeroRttAlpnChangeServer) {
     // 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);
    @@ -181,7 +145,7 @@ TEST_F(TlsConnectTest, TestTls13ZeroRttNoAlpnServer) {
     // 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);
    @@ -197,4 +161,36 @@ TEST_F(TlsConnectTest, TestTls13ZeroRttNoAlpnClient) {
       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
    diff --git a/security/nss/external_tests/ssl_gtest/ssl_auth_unittest.cc b/security/nss/external_tests/ssl_gtest/ssl_auth_unittest.cc
    index dbae86dce92f..184e88e4efa2 100644
    --- a/security/nss/external_tests/ssl_gtest/ssl_auth_unittest.cc
    +++ b/security/nss/external_tests/ssl_gtest/ssl_auth_unittest.cc
    @@ -4,8 +4,8 @@
      * 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 "secerr.h"
    +#include "ssl.h"
     #include "sslerr.h"
     #include "sslproto.h"
     
    @@ -68,12 +68,14 @@ TEST_P(TlsConnectGeneric, ClientAuthBigRsa) {
     }
     
     // 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(scheme));
     
       ScopedCERTCertificate remote_cert(SSL_PeerCertificate(peer->ssl_fd()));
       ScopedSECKEYPublicKey remote_key(CERT_ExtractPublicKey(remote_cert.get()));
    @@ -97,7 +99,8 @@ TEST_P(TlsConnectTls12, ServerAuthCheckSigAlg) {
       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) {
    @@ -110,7 +113,8 @@ TEST_P(TlsConnectTls12, ClientAuthCheckSigAlg) {
       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) {
    @@ -122,7 +126,8 @@ TEST_P(TlsConnectTls12, ClientAuthBigRsaCheckSigAlg) {
       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[] = {
    diff --git a/security/nss/external_tests/ssl_gtest/ssl_ciphersuite_unittest.cc b/security/nss/external_tests/ssl_gtest/ssl_ciphersuite_unittest.cc
    index 7b2120953d11..579035bc0ff7 100644
    --- a/security/nss/external_tests/ssl_gtest/ssl_ciphersuite_unittest.cc
    +++ b/security/nss/external_tests/ssl_gtest/ssl_ciphersuite_unittest.cc
    @@ -4,10 +4,10 @@
      * 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 
     #include 
     #include "secerr.h"
    +#include "ssl.h"
     #include "sslerr.h"
     #include "sslproto.h"
     
    diff --git a/security/nss/external_tests/ssl_gtest/ssl_damage_unittest.cc b/security/nss/external_tests/ssl_gtest/ssl_damage_unittest.cc
    index 25c67554f8db..98ed01a85fe0 100644
    --- a/security/nss/external_tests/ssl_gtest/ssl_damage_unittest.cc
    +++ b/security/nss/external_tests/ssl_gtest/ssl_damage_unittest.cc
    @@ -4,10 +4,10 @@
      * 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 
     #include 
     #include "secerr.h"
    +#include "ssl.h"
     #include "sslerr.h"
     #include "sslproto.h"
     
    @@ -55,7 +55,7 @@ TEST_F(TlsConnectTest, DamageSecretHandleServerFinished) {
           [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
    diff --git a/security/nss/external_tests/ssl_gtest/ssl_dhe_unittest.cc b/security/nss/external_tests/ssl_gtest/ssl_dhe_unittest.cc
    index 0f0e09cdbe20..b4b817e7d3c2 100644
    --- a/security/nss/external_tests/ssl_gtest/ssl_dhe_unittest.cc
    +++ b/security/nss/external_tests/ssl_gtest/ssl_dhe_unittest.cc
    @@ -4,11 +4,11 @@
      * 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 
     #include 
     #include 
     #include "secerr.h"
    +#include "ssl.h"
     #include "sslerr.h"
     #include "sslproto.h"
     
    @@ -26,52 +26,6 @@ TEST_P(TlsConnectGeneric, ConnectDhe) {
       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 groups_;
    -};
    -
    -// Check the group of each of the supported groups
    -static void CheckGroups(const DataBuffer& groups,
    -                        std::function check_group) {
    -  CheckDuplicateGroup group_set;
    -  uint32_t tmp;
    -  EXPECT_TRUE(groups.Read(0, 2, &tmp));
    -  EXPECT_EQ(groups.len() - 2, static_cast(tmp));
    -  for (size_t i = 2; i < groups.len(); i += 2) {
    -    EXPECT_TRUE(groups.Read(i, 2, &tmp));
    -    uint16_t group = static_cast(tmp);
    -    group_set.AddAndCheckGroup(group);
    -    check_group(group);
    -  }
    -}
    -
    -// Check the group of each of the shares
    -static void CheckShares(const DataBuffer& shares,
    -                        std::function check_group) {
    -  CheckDuplicateGroup group_set;
    -  uint32_t tmp;
    -  EXPECT_TRUE(shares.Read(0, 2, &tmp));
    -  EXPECT_EQ(shares.len() - 2, static_cast(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(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();
    @@ -90,7 +44,7 @@ TEST_P(TlsConnectTls13, SharesForBothEcdheAndDhe) {
       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 {
    @@ -118,7 +72,7 @@ TEST_P(TlsConnectTls13, NoDheOnEcdheConnections) {
       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);
     }
    @@ -137,7 +91,7 @@ TEST_P(TlsConnectGeneric, ConnectFfdheClient) {
       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);
    @@ -507,7 +461,8 @@ TEST_P(TlsConnectGenericPre13, WeakDHGroup) {
     
     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();
     }
    @@ -523,6 +478,22 @@ TEST_P(TlsConnectGenericPre13, PreferredFfdhe) {
       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);
    @@ -534,10 +505,10 @@ TEST_P(TlsConnectTls13, ResumeFfdhe) {
       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();
    diff --git a/security/nss/external_tests/ssl_gtest/ssl_drop_unittest.cc b/security/nss/external_tests/ssl_gtest/ssl_drop_unittest.cc
    index f8d7960a0a2a..b3649a6adaf9 100644
    --- a/security/nss/external_tests/ssl_gtest/ssl_drop_unittest.cc
    +++ b/security/nss/external_tests/ssl_gtest/ssl_drop_unittest.cc
    @@ -4,8 +4,8 @@
      * 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 "secerr.h"
    +#include "ssl.h"
     
     extern "C" {
     // This is not something that should make you happy.
    @@ -20,25 +20,6 @@ extern "C" {
     
     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();
    diff --git a/security/nss/external_tests/ssl_gtest/ssl_ecdh_unittest.cc b/security/nss/external_tests/ssl_gtest/ssl_ecdh_unittest.cc
    index ea9e1141b9ad..af0fbe2f0ad1 100644
    --- a/security/nss/external_tests/ssl_gtest/ssl_ecdh_unittest.cc
    +++ b/security/nss/external_tests/ssl_gtest/ssl_ecdh_unittest.cc
    @@ -4,10 +4,10 @@
      * 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 
     #include 
     #include "secerr.h"
    +#include "ssl.h"
     #include "sslerr.h"
     #include "sslproto.h"
     
    @@ -51,33 +51,233 @@ TEST_P(TlsConnectGeneric, ConnectEcdhe) {
     // 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 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 GetGroupDetails(const DataBuffer &ext) {
    +    uint32_t tmp = 0;
    +    EXPECT_TRUE(ext.Read(0, 2, &tmp));
    +    EXPECT_EQ(ext.len() - 2, static_cast(tmp));
    +    EXPECT_TRUE(ext.len() % 2 == 0);
    +    std::vector 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(tmp));
    +    }
    +    return groups;
    +  }
    +
    +  std::vector GetShareDetails(const DataBuffer &ext) {
    +    uint32_t tmp = 0;
    +    EXPECT_TRUE(ext.Read(0, 2, &tmp));
    +    EXPECT_EQ(ext.len() - 2, static_cast(tmp));
    +    std::vector shares;
    +    size_t i = 2;
    +    while (i < ext.len()) {
    +      EXPECT_TRUE(ext.Read(i, 2, &tmp));
    +      shares.push_back(static_cast(tmp));
    +      EXPECT_TRUE(ext.Read(i + 2, 2, &tmp));
    +      i += 4 + tmp;
    +    }
    +    EXPECT_EQ(ext.len(), i);
    +    return shares;
    +  }
    +
    +  void CheckKEXDetails(std::vector expectedGroups,
    +                       std::vector expectedShares) {
    +    std::vector groups =
    +        GetGroupDetails(groups_capture_->extension());
    +    EXPECT_EQ(expectedGroups, groups);
    +
    +    if (version_ >= SSL_LIBRARY_VERSION_TLS_1_3) {
    +      ASSERT_TRUE(expectedShares.size());
    +      std::vector 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 shares = {ssl_grp_ec_secp384r1};
    +  std::vector 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 shares = {ssl_grp_ec_secp384r1};
    +  std::vector 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 shares = {ssl_grp_ec_secp384r1};
    +    std::vector expected_groups(groups,
    +                                               groups + PR_ARRAY_SIZE(groups));
    +    CheckKEXDetails(expected_groups, shares);
    +  } else {
    +    std::vector oldtlsgroups = {
    +        ssl_grp_ec_secp384r1, ssl_grp_ec_secp256r1, ssl_grp_ec_secp521r1};
    +    CheckKEXDetails(oldtlsgroups, std::vector());
    +  }
    +}
    +
    +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:
    @@ -136,4 +336,8 @@ TEST_P(TlsConnectGenericPre13, ConnectECDHEmptyClientPoint) {
       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
    diff --git a/security/nss/external_tests/ssl_gtest/ssl_ems_unittest.cc b/security/nss/external_tests/ssl_gtest/ssl_ems_unittest.cc
    index 697a7ec96397..50325383f8af 100644
    --- a/security/nss/external_tests/ssl_gtest/ssl_ems_unittest.cc
    +++ b/security/nss/external_tests/ssl_gtest/ssl_ems_unittest.cc
    @@ -4,8 +4,8 @@
      * 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 "secerr.h"
    +#include "ssl.h"
     #include "sslerr.h"
     #include "sslproto.h"
     
    diff --git a/security/nss/external_tests/ssl_gtest/ssl_extension_unittest.cc b/security/nss/external_tests/ssl_gtest/ssl_extension_unittest.cc
    index 8d43fd68a2cb..193d3382756a 100644
    --- a/security/nss/external_tests/ssl_gtest/ssl_extension_unittest.cc
    +++ b/security/nss/external_tests/ssl_gtest/ssl_extension_unittest.cc
    @@ -542,8 +542,8 @@ TEST_P(TlsExtensionTest13, ModifyDraftVersionAndFail) {
       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));
    diff --git a/security/nss/external_tests/ssl_gtest/ssl_gtest.cc b/security/nss/external_tests/ssl_gtest/ssl_gtest.cc
    index 7a2c21251c9b..2d08dd865d70 100644
    --- a/security/nss/external_tests/ssl_gtest/ssl_gtest.cc
    +++ b/security/nss/external_tests/ssl_gtest/ssl_gtest.cc
    @@ -1,7 +1,7 @@
    -#include "ssl.h"
     #include "nspr.h"
     #include "nss.h"
     #include "prenv.h"
    +#include "ssl.h"
     
     #include 
     
    diff --git a/security/nss/external_tests/ssl_gtest/ssl_hrr_unittest.cc b/security/nss/external_tests/ssl_gtest/ssl_hrr_unittest.cc
    new file mode 100644
    index 000000000000..751e92188611
    --- /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(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 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(group >> 8),
    +        static_cast(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
    diff --git a/security/nss/external_tests/ssl_gtest/ssl_loopback_unittest.cc b/security/nss/external_tests/ssl_gtest/ssl_loopback_unittest.cc
    index f5739f85116b..b694cfbd95b3 100644
    --- a/security/nss/external_tests/ssl_gtest/ssl_loopback_unittest.cc
    +++ b/security/nss/external_tests/ssl_gtest/ssl_loopback_unittest.cc
    @@ -4,10 +4,10 @@
      * 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 
     #include 
     #include "secerr.h"
    +#include "ssl.h"
     #include "sslerr.h"
     #include "sslproto.h"
     
    @@ -53,6 +53,8 @@ TEST_P(TlsConnectGeneric, ConnectAlpn) {
     
     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");
     }
    diff --git a/security/nss/external_tests/ssl_gtest/ssl_record_unittest.cc b/security/nss/external_tests/ssl_gtest/ssl_record_unittest.cc
    index 965060d2c42e..ef81b222c8ac 100644
    --- a/security/nss/external_tests/ssl_gtest/ssl_record_unittest.cc
    +++ b/security/nss/external_tests/ssl_gtest/ssl_record_unittest.cc
    @@ -3,8 +3,9 @@
     /* 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 "nss.h"
    +#include "ssl.h"
     #include "sslimpl.h"
     
     #include "databuffer.h"
    diff --git a/security/nss/external_tests/ssl_gtest/ssl_resumption_unittest.cc b/security/nss/external_tests/ssl_gtest/ssl_resumption_unittest.cc
    index 347270f61238..e9c1a00073c0 100644
    --- a/security/nss/external_tests/ssl_gtest/ssl_resumption_unittest.cc
    +++ b/security/nss/external_tests/ssl_gtest/ssl_resumption_unittest.cc
    @@ -4,10 +4,10 @@
      * 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 
     #include 
     #include "secerr.h"
    +#include "ssl.h"
     #include "sslerr.h"
     #include "sslproto.h"
     
    @@ -328,7 +328,8 @@ TEST_F(TlsConnectTest, TestTls13ResumptionTwice) {
     
       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);
    @@ -348,7 +349,8 @@ TEST_F(TlsConnectTest, TestTls13ResumptionTwice) {
       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);
    @@ -388,7 +390,7 @@ TEST_F(TlsConnectTest, DisableClientPSKAndFailToResume) {
       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);
    @@ -417,10 +419,10 @@ TEST_F(TlsConnectTest, DisableServerPSKAndFailToResume) {
       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);
    diff --git a/security/nss/external_tests/ssl_gtest/ssl_staticrsa_unittest.cc b/security/nss/external_tests/ssl_gtest/ssl_staticrsa_unittest.cc
    index 57d8a341a7b6..57b593438ad2 100644
    --- a/security/nss/external_tests/ssl_gtest/ssl_staticrsa_unittest.cc
    +++ b/security/nss/external_tests/ssl_gtest/ssl_staticrsa_unittest.cc
    @@ -4,10 +4,10 @@
      * 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 
     #include 
     #include "secerr.h"
    +#include "ssl.h"
     #include "sslerr.h"
     #include "sslproto.h"
     
    diff --git 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
    index b51d11c14487..a434fe7f135a 100644
    --- 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
    @@ -4,8 +4,8 @@
      * 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 "pk11pub.h"
    +#include "ssl.h"
     #include "sslerr.h"
     #include "sslproto.h"
     
    diff --git a/security/nss/external_tests/ssl_gtest/ssl_version_unittest.cc b/security/nss/external_tests/ssl_gtest/ssl_version_unittest.cc
    index 75fe5377ad4c..72c58a090b31 100644
    --- a/security/nss/external_tests/ssl_gtest/ssl_version_unittest.cc
    +++ b/security/nss/external_tests/ssl_gtest/ssl_version_unittest.cc
    @@ -4,8 +4,8 @@
      * 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 "secerr.h"
    +#include "ssl.h"
     #include "sslerr.h"
     #include "sslproto.h"
     
    diff --git a/security/nss/external_tests/ssl_gtest/test_io.cc b/security/nss/external_tests/ssl_gtest/test_io.cc
    index 5fb61f7caff3..646c5a5771ad 100644
    --- a/security/nss/external_tests/ssl_gtest/test_io.cc
    +++ b/security/nss/external_tests/ssl_gtest/test_io.cc
    @@ -260,6 +260,13 @@ static int32_t DummyReserved(PRFileDesc *f) {
     
     DummyPrSocket::~DummyPrSocket() { Reset(); }
     
    +void DummyPrSocket::SetPacketFilter(PacketFilter *filter) {
    +  if (filter_) {
    +    delete filter_;
    +  }
    +  filter_ = filter;
    +}
    +
     void DummyPrSocket::Reset() {
       delete filter_;
       if (peer_) {
    diff --git a/security/nss/external_tests/ssl_gtest/test_io.h b/security/nss/external_tests/ssl_gtest/test_io.h
    index e7b27f6e96da..9ea444c413a0 100644
    --- a/security/nss/external_tests/ssl_gtest/test_io.h
    +++ b/security/nss/external_tests/ssl_gtest/test_io.h
    @@ -58,7 +58,7 @@ class DummyPrSocket {
     
       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();
     
    diff --git a/security/nss/external_tests/ssl_gtest/tls_agent.cc b/security/nss/external_tests/ssl_gtest/tls_agent.cc
    index 1fec3f4d26c5..ee6b88c6941e 100644
    --- a/security/nss/external_tests/ssl_gtest/tls_agent.cc
    +++ b/security/nss/external_tests/ssl_gtest/tls_agent.cc
    @@ -92,6 +92,13 @@ TlsAgent::~TlsAgent() {
       }
     }
     
    +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));
    @@ -121,18 +128,10 @@ bool TlsAgent::ConfigServerCert(const std::string& name, bool updateKeyBits,
     // (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
     }
     
    @@ -290,9 +289,9 @@ void TlsAgent::EnableSingleCipher(uint16_t cipher) {
       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);
     }
     
    @@ -802,6 +801,7 @@ void TlsAgentTestBase::EnsureInit() {
     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();
    @@ -830,19 +830,22 @@ void TlsAgentTestBase::MakeRecord(Mode mode, uint8_t type, uint16_t version,
     
     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.
    diff --git a/security/nss/external_tests/ssl_gtest/tls_agent.h b/security/nss/external_tests/ssl_gtest/tls_agent.h
    index c50e5c1ed669..6e80dd41a8cd 100644
    --- a/security/nss/external_tests/ssl_gtest/tls_agent.h
    +++ b/security/nss/external_tests/ssl_gtest/tls_agent.h
    @@ -141,7 +141,7 @@ class TlsAgent : public PollTarget {
       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_; }
     
    @@ -156,7 +156,7 @@ class TlsAgent : public PollTarget {
     
       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_; }
    @@ -213,13 +213,7 @@ class TlsAgent : public PollTarget {
      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,
    @@ -340,6 +334,11 @@ class TlsAgent : public PollTarget {
       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 kTlsRolesAll;
    @@ -355,17 +354,17 @@ class TlsAgentTestBase : public ::testing::Test {
       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) {
    diff --git a/security/nss/external_tests/ssl_gtest/tls_connect.cc b/security/nss/external_tests/ssl_gtest/tls_connect.cc
    index aba7df084165..be74f78b617e 100644
    --- a/security/nss/external_tests/ssl_gtest/tls_connect.cc
    +++ b/security/nss/external_tests/ssl_gtest/tls_connect.cc
    @@ -11,6 +11,7 @@ extern "C" {
     
     #include 
     
    +#include "databuffer.h"
     #include "gtest_utils.h"
     #include "sslproto.h"
     
    @@ -120,6 +121,39 @@ TlsConnectTestBase::TlsConnectTestBase(Mode mode, uint16_t version)
     
     TlsConnectTestBase::~TlsConnectTestBase() {}
     
    +// Check the group of each of the supported groups
    +void TlsConnectTestBase::CheckGroups(
    +    const DataBuffer& groups, std::function check_group) {
    +  DuplicateGroupChecker group_set;
    +  uint32_t tmp = 0;
    +  EXPECT_TRUE(groups.Read(0, 2, &tmp));
    +  EXPECT_EQ(groups.len() - 2, static_cast(tmp));
    +  for (size_t i = 2; i < groups.len(); i += 2) {
    +    EXPECT_TRUE(groups.Read(i, 2, &tmp));
    +    SSLNamedGroup group = static_cast(tmp);
    +    group_set.AddAndCheckGroup(group);
    +    check_group(group);
    +  }
    +}
    +
    +// Check the group of each of the shares
    +void TlsConnectTestBase::CheckShares(
    +    const DataBuffer& shares, std::function check_group) {
    +  DuplicateGroupChecker group_set;
    +  uint32_t tmp = 0;
    +  EXPECT_TRUE(shares.Read(0, 2, &tmp));
    +  EXPECT_EQ(shares.len() - 2, static_cast(tmp));
    +  size_t i;
    +  for (i = 2; i < shares.len(); i += 4 + tmp) {
    +    ASSERT_TRUE(shares.Read(i, 2, &tmp));
    +    SSLNamedGroup group = static_cast(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();
    @@ -344,23 +378,27 @@ void TlsConnectTestBase::ConfigureSessionCache(SessionResumptionMode client,
     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_ct, stats->hch_sid_stateless_resumes);
    -  EXPECT_EQ(stateless_ct, stats->hsh_sid_stateless_resumes);
    +  EXPECT_EQ(stateless_count, stats->hch_sid_stateless_resumes);
    +  EXPECT_EQ(stateless_count, 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);
    +    }
       }
     }
     
    @@ -385,11 +423,6 @@ void TlsConnectTestBase::EnsureModelSockets() {
       // 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) {
    diff --git a/security/nss/external_tests/ssl_gtest/tls_connect.h b/security/nss/external_tests/ssl_gtest/tls_connect.h
    index 9dba06f283a7..4bbcd0322b57 100644
    --- a/security/nss/external_tests/ssl_gtest/tls_connect.h
    +++ b/security/nss/external_tests/ssl_gtest/tls_connect.h
    @@ -73,6 +73,10 @@ class TlsConnectTestBase : public ::testing::Test {
       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 check_group);
    +  void CheckShares(const DataBuffer& shares,
    +                   std::function check_group);
     
       void SetExpectedVersion(uint16_t version);
       // Expect resumption of a particular type.
    @@ -122,6 +126,19 @@ class TlsConnectTestBase : public ::testing::Test {
     
       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 groups_;
    +  };
     };
     
     // A non-parametrized TLS test base.
    diff --git a/security/nss/external_tests/ssl_gtest/tls_filter.cc b/security/nss/external_tests/ssl_gtest/tls_filter.cc
    index 60bbfde5e518..ebea6f5ec567 100644
    --- a/security/nss/external_tests/ssl_gtest/tls_filter.cc
    +++ b/security/nss/external_tests/ssl_gtest/tls_filter.cc
    @@ -464,4 +464,12 @@ PacketFilter::Action TlsInspectorClientHelloVersionChanger::FilterHandshake(
       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
    diff --git a/security/nss/external_tests/ssl_gtest/tls_filter.h b/security/nss/external_tests/ssl_gtest/tls_filter.h
    index 07e72c141f86..1c9ff6d6c59e 100644
    --- a/security/nss/external_tests/ssl_gtest/tls_filter.h
    +++ b/security/nss/external_tests/ssl_gtest/tls_filter.h
    @@ -223,10 +223,12 @@ 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_;
    @@ -281,6 +283,21 @@ class TlsInspectorClientHelloVersionChanger : public TlsHandshakeFilter {
       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
    diff --git a/security/nss/external_tests/ssl_gtest/tls_parser.h b/security/nss/external_tests/ssl_gtest/tls_parser.h
    index 1f786336ad89..743377d4215d 100644
    --- a/security/nss/external_tests/ssl_gtest/tls_parser.h
    +++ b/security/nss/external_tests/ssl_gtest/tls_parser.h
    @@ -26,6 +26,7 @@ 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;
    @@ -46,7 +47,8 @@ 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
    diff --git a/security/nss/lib/certhigh/ocsp.c b/security/nss/lib/certhigh/ocsp.c
    index 3f89fb6731c2..1672b661a1d6 100644
    --- a/security/nss/lib/certhigh/ocsp.c
    +++ b/security/nss/lib/certhigh/ocsp.c
    @@ -2195,6 +2195,10 @@ SetRequestExts(void *object, CERTCertExtension **exts)
         request->tbsRequest->requestExtensions = exts;
     }
     
    +#if defined(__GNUC__)
    +#pragma GCC diagnostic push
    +#pragma GCC diagnostic ignored "-Wvarargs"
    +#endif
     SECStatus
     CERT_AddOCSPAcceptableResponses(CERTOCSPRequest *request,
                                     SECOidTag responseType0, ...)
    @@ -2261,6 +2265,9 @@ loser:
             (void)CERT_FinishExtensions(extHandle);
         return rv;
     }
    +#if defined(__GNUC__)
    +#pragma GCC diagnostic pop
    +#endif
     
     /*
      * FUNCTION: CERT_DestroyOCSPRequest
    diff --git a/security/nss/lib/ssl/SSLerrs.h b/security/nss/lib/ssl/SSLerrs.h
    index 2bb4d764edff..f1c73a562b1d 100644
    --- a/security/nss/lib/ssl/SSLerrs.h
    +++ b/security/nss/lib/ssl/SSLerrs.h
    @@ -487,3 +487,12 @@ ER3(SSL_ERROR_MISSING_SUPPORTED_GROUPS, (SSL_ERROR_BASE + 152),
     
     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.")
    diff --git a/security/nss/lib/ssl/derive.c b/security/nss/lib/ssl/derive.c
    index d7e19b99c198..c39bc86d20bf 100644
    --- a/security/nss/lib/ssl/derive.c
    +++ b/security/nss/lib/ssl/derive.c
    @@ -790,9 +790,7 @@ SSL_CanBypass(CERTCertificate *cert, SECKEYPrivateKey *srvPrivkey,
                     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;
    diff --git a/security/nss/lib/ssl/dtlscon.c b/security/nss/lib/ssl/dtlscon.c
    index 80e68832c018..8aebda38991f 100644
    --- a/security/nss/lib/ssl/dtlscon.c
    +++ b/security/nss/lib/ssl/dtlscon.c
    @@ -946,7 +946,11 @@ dtls_FinishedTimerCb(sslSocket *ss)
     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;
    @@ -1039,7 +1043,7 @@ dtls_HandleHelloVerifyRequest(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
         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); /*******************************/
     
    @@ -1189,14 +1193,16 @@ DTLS_GetHandshakeTimeout(PRFileDesc *socket, PRIntervalTime *timeout)
      * 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));
    @@ -1229,11 +1235,17 @@ dtls_IsRelevant(sslSocket *ss, const ssl3CipherSpec *crSpec,
      * 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);
    diff --git a/security/nss/lib/ssl/ssl.def b/security/nss/lib/ssl/ssl.def
    index 4f405e62ad73..3713627afc38 100644
    --- a/security/nss/lib/ssl/ssl.def
    +++ b/security/nss/lib/ssl/ssl.def
    @@ -208,7 +208,7 @@ SSL_ConfigServerCert;
     ;+};
     ;+NSS_3.27 {    # NSS 3.27 release
     ;+    global:
    -SSL_NamedGroupPrefSet;
    +SSL_NamedGroupConfig;
     ;+    local:
     ;+*;
     ;+};
    diff --git a/security/nss/lib/ssl/ssl.h b/security/nss/lib/ssl/ssl.h
    index d1160f3f6130..ca3b7fd60abc 100644
    --- a/security/nss/lib/ssl/ssl.h
    +++ b/security/nss/lib/ssl/ssl.h
    @@ -216,7 +216,7 @@ SSL_IMPORT PRFileDesc *DTLS_ImportFD(PRFileDesc *model, PRFileDesc *fd);
      * 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.
      */
    @@ -392,16 +392,16 @@ SSL_IMPORT SECStatus 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
    @@ -432,11 +432,11 @@ SSL_IMPORT SECStatus SSL_DHEGroupPrefSet(PRFileDesc *fd,
     ** 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);
     
    diff --git a/security/nss/lib/ssl/ssl3con.c b/security/nss/lib/ssl/ssl3con.c
    index 213912882bb0..ce144bd49d00 100644
    --- a/security/nss/lib/ssl/ssl3con.c
    +++ b/security/nss/lib/ssl/ssl3con.c
    @@ -81,6 +81,10 @@ static SECStatus ssl3_AESGCMBypass(ssl3KeyMaterial *keys, PRBool doDecrypt,
                                        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
     
    @@ -210,10 +214,9 @@ static const SignatureScheme defaultSignatureSchemes[] = {
         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,
    @@ -344,9 +347,6 @@ static const ssl3BulkCipherDef bulk_cipher_defs[] = {
         {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 */
    @@ -622,6 +622,9 @@ ssl3_DecodeHandshakeType(int msgType)
             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;
    @@ -835,9 +838,10 @@ 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;
             }
         }
    @@ -861,6 +865,11 @@ ssl_KEAEnabled(const sslSocket *ss, SSLKEAType keaType)
                  * 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
    @@ -1150,12 +1159,13 @@ ssl3_GetNewRandom(SSL3Random *random)
     
     /* 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;
    @@ -1195,8 +1205,16 @@ ssl3_SignHashes(SSL3Hashes *hash, SECKEYPrivateKey *key, SECItem *buf,
         }
         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;
    @@ -1207,7 +1225,15 @@ ssl3_SignHashes(SSL3Hashes *hash, SECKEYPrivateKey *key, SECItem *buf,
             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);
    @@ -1238,8 +1264,8 @@ done:
     
     /* 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;
    @@ -1247,11 +1273,13 @@ ssl3_VerifySignedHashes(SSL3Hashes *hash, CERTCertificate *cert,
         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;
    @@ -1276,7 +1304,8 @@ ssl3_VerifySignedHashes(SSL3Hashes *hash, CERTCertificate *cert,
                     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);
    @@ -1313,13 +1342,31 @@ ssl3_VerifySignedHashes(SSL3Hashes *hash, CERTCertificate *cert,
         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);
    @@ -1480,17 +1527,18 @@ ssl3_ComputeDHKeyHash(sslSocket *ss, SSLHashType hashAlg, SSL3Hashes *hashes,
         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 {
    @@ -1504,19 +1552,13 @@ ssl3_ComputeDHKeyHash(sslSocket *ss, SSLHashType hashAlg, SSL3Hashes *hashes,
         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;
    @@ -3475,11 +3517,10 @@ ssl3_FlushHandshakeMessages(sslSocket *ss, PRInt32 flags)
         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);
    @@ -3852,10 +3893,6 @@ ssl3_HandleAlert(sslSocket *ss, sslBuffer *buf)
             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)) {
    @@ -4012,6 +4049,22 @@ ssl3_HandleChangeCipherSpecs(sslSocket *ss, sslBuffer *buf)
         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)
    @@ -5145,6 +5198,9 @@ ssl_IsSupportedSignatureScheme(SignatureScheme scheme)
             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:
    @@ -5155,10 +5211,6 @@ ssl_IsSupportedSignatureScheme(SignatureScheme scheme)
             case ssl_sig_ecdsa_sha1:
                 return PR_TRUE;
     
    -        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:
    @@ -5167,6 +5219,21 @@ ssl_IsSupportedSignatureScheme(SignatureScheme scheme)
         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:
    +            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.
    @@ -5575,13 +5642,40 @@ ssl3_ComputeHandshakeHashes(sslSocket *ss,
      * 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;
    @@ -5595,41 +5689,43 @@ ssl3_SendClientHello(sslSocket *ss, PRBool resending)
         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;
    @@ -5776,7 +5872,8 @@ ssl3_SendClientHello(sslSocket *ss, PRBool resending)
             }
         }
     
    -    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) {
    @@ -5934,7 +6031,8 @@ ssl3_SendClientHello(sslSocket *ss, PRBool resending)
             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) {
    @@ -6108,14 +6206,6 @@ ssl3_SendClientHello(sslSocket *ss, PRBool resending)
                 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) {
    @@ -6123,6 +6213,31 @@ ssl3_SendClientHello(sslSocket *ss, PRBool resending)
             }
         }
     
    +    /* 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;
     }
    @@ -6169,7 +6284,7 @@ ssl3_HandleHelloRequest(sslSocket *ss)
         }
     
         ssl_GetXmitBufLock(ss);
    -    rv = ssl3_SendClientHello(ss, PR_FALSE);
    +    rv = ssl3_SendClientHello(ss, client_hello_renegotiation);
         ssl_ReleaseXmitBufLock(ss);
     
         return rv;
    @@ -7081,7 +7196,6 @@ static SECStatus
     ssl3_SendCertificateVerify(sslSocket *ss, SECKEYPrivateKey *privKey)
     {
         SECStatus rv = SECFailure;
    -    PRBool isTLS;
         PRBool isTLS12;
         SECItem buf = { siBuffer, NULL, 0 };
         SSL3Hashes hashes;
    @@ -7128,11 +7242,10 @@ ssl3_SendCertificateVerify(sslSocket *ss, SECKEYPrivateKey *privKey)
             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.
    @@ -7266,6 +7379,13 @@ ssl3_HandleServerHello(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
             }
         }
     
    +    /* 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
    @@ -7698,6 +7818,7 @@ ssl_HandleRSAServerKeyExchange(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
         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 };
    @@ -7724,8 +7845,6 @@ ssl_HandleRSAServerKeyExchange(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
             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. */
    @@ -7766,8 +7885,7 @@ ssl_HandleRSAServerKeyExchange(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
                 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);
    @@ -7821,6 +7939,7 @@ ssl_HandleDHServerKeyExchange(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
         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 };
    @@ -7876,8 +7995,6 @@ ssl_HandleDHServerKeyExchange(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
         }
     
         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. */
    @@ -7891,6 +8008,7 @@ ssl_HandleDHServerKeyExchange(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
         } 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) {
    @@ -7921,8 +8039,7 @@ ssl_HandleDHServerKeyExchange(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
                 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);
    @@ -8810,7 +8927,8 @@ ssl3_KEASupportsTickets(const ssl3KEADef *kea_def)
     ** (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;
    @@ -8824,7 +8942,7 @@ ssl3_NegotiateCipherSuite(sslSocket *ss, const SECItem *suites)
             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);
                 }
             }
         }
    @@ -9512,7 +9630,7 @@ ssl3_HandleClientHelloPart2(sslSocket *ss,
         }
     #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;
    @@ -10231,7 +10349,6 @@ 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;
    @@ -10249,12 +10366,13 @@ ssl3_SendDHServerKeyExchange(sslSocket *ss)
             return SECFailure;
         }
     
    -    rv = ssl_SelectDHEParams(ss, &groupDef, ¶ms);
    +    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);
    @@ -10283,9 +10401,8 @@ ssl3_SendDHServerKeyExchange(sslSocket *ss)
             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. */
         }
    @@ -10351,7 +10468,6 @@ 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 */
    @@ -10390,9 +10506,9 @@ ssl3_SendServerKeyExchange(sslSocket *ss)
                     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. */
                 }
    @@ -10624,7 +10740,7 @@ ssl3_HandleCertificateVerify(sslSocket *ss, SSL3Opaque *b, PRUint32 length,
         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;
    @@ -10700,8 +10816,7 @@ ssl3_HandleCertificateVerify(sslSocket *ss, SSL3Opaque *b, PRUint32 length,
         }
     
         /* 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;
    @@ -11102,20 +11217,20 @@ ssl3_HandleClientKeyExchange(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
             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);
    @@ -12555,7 +12670,6 @@ ssl3_FillInCachedSID(sslSocket *ss, sslSessionID *sid)
         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);
    @@ -12696,14 +12810,11 @@ ssl3_HandleHandshakeMessage(sslSocket *ss, SSL3Opaque *b, PRUint32 length,
             }
         } 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);
             }
         }
     
    @@ -12734,8 +12845,9 @@ ssl3_HandleHandshakeMessage(sslSocket *ss, SSL3Opaque *b, PRUint32 length,
         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;
    @@ -13553,11 +13665,13 @@ ssl3_HandleRecord(sslSocket *ss, SSL3Ciphertext *cText, sslBuffer *databuf)
         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;
    @@ -13593,6 +13707,19 @@ ssl3_HandleRecord(sslSocket *ss, SSL3Ciphertext *cText, sslBuffer *databuf)
             }
         }
     
    +    /* 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. */
    @@ -13609,7 +13736,9 @@ ssl3_HandleRecord(sslSocket *ss, SSL3Ciphertext *cText, sslBuffer *databuf)
     
             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;
    @@ -13714,7 +13843,7 @@ ssl3_HandleRecord(sslSocket *ss, SSL3Ciphertext *cText, sslBuffer *databuf)
                 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);
    @@ -13835,12 +13964,12 @@ ssl3_InitState(sslSocket *ss)
         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));
     
    @@ -13873,6 +14002,8 @@ ssl3_InitState(sslSocket *ss)
         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;
    @@ -14143,8 +14274,11 @@ ssl3_RedoHandshake(sslSocket *ss, PRBool flushCache)
         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;
    @@ -14243,7 +14377,7 @@ ssl3_DestroySSL3Info(sslSocket *ss)
         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);
     
    diff --git a/security/nss/lib/ssl/ssl3ecc.c b/security/nss/lib/ssl/ssl3ecc.c
    index e556c6dd3738..b659bcdc9bed 100644
    --- a/security/nss/lib/ssl/ssl3ecc.c
    +++ b/security/nss/lib/ssl/ssl3ecc.c
    @@ -129,7 +129,7 @@ ssl_ECPubKey2NamedGroup(const SECKEYPublicKey *pubKey)
             !(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];
             }
    @@ -424,17 +424,23 @@ tls13_ImportECDHKeyShare(sslSocket *ss, SECKEYPublicKey *peerKey,
     }
     
     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);
    @@ -486,7 +492,7 @@ ssl_GetECGroupForServerSocket(sslSocket *ss)
             requiredECCbits = certKeySize;
         }
     
    -    return ssl_GetECGroupWithStrength(ss->namedGroups, requiredECCbits);
    +    return ssl_GetECGroupWithStrength(ss, requiredECCbits);
     }
     
     /* function to clear out the lists */
    @@ -508,7 +514,7 @@ 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();
    @@ -624,6 +630,7 @@ ssl3_HandleECDHServerKeyExchange(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
         SSL3Hashes hashes;
         SECItem signature = { siBuffer, NULL, 0 };
         SSLHashType hashAlg;
    +    SignatureScheme sigScheme;
     
         SECItem ec_params = { siBuffer, NULL, 0 };
         SECItem ec_point = { siBuffer, NULL, 0 };
    @@ -672,7 +679,6 @@ ssl3_HandleECDHServerKeyExchange(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
     
         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. */
    @@ -686,6 +692,7 @@ ssl3_HandleECDHServerKeyExchange(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
         } 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);
    @@ -720,8 +727,7 @@ ssl3_HandleECDHServerKeyExchange(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
                 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);
    @@ -780,7 +786,7 @@ 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;
    @@ -843,11 +849,10 @@ ssl3_SendECDHServerKeyExchange(sslSocket *ss)
             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. */
         }
    @@ -1015,11 +1020,11 @@ ssl_DisableNonSuiteBGroups(sslSocket *ss)
             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;
             }
         }
     }
    @@ -1049,24 +1054,26 @@ ssl_SendSupportedGroupsXtn(sslSocket *ss, PRBool append, PRUint32 maxBytes)
             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 =
    @@ -1168,7 +1175,9 @@ 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);
    @@ -1182,7 +1191,13 @@ ssl_UpdateSupportedGroups(sslSocket *ss, SECItem *data)
             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 =
    @@ -1192,7 +1207,12 @@ ssl_UpdateSupportedGroups(sslSocket *ss, SECItem *data)
             }
             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
    @@ -1203,21 +1223,19 @@ ssl_UpdateSupportedGroups(sslSocket *ss, SECItem *data)
                 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;
     }
     
    diff --git a/security/nss/lib/ssl/ssl3ext.c b/security/nss/lib/ssl/ssl3ext.c
    index 4ebd0310ceb0..cceff92d0d6d 100644
    --- a/security/nss/lib/ssl/ssl3ext.c
    +++ b/security/nss/lib/ssl/ssl3ext.c
    @@ -2131,6 +2131,9 @@ ssl3_ProcessSessionTicketCommon(sslSocket *ss, SECItem *data)
                 }
             }
             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) {
    @@ -3300,15 +3303,16 @@ tls13_HandleKeyShareEntry(sslSocket *ss, SECItem *data)
             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;
    @@ -3724,7 +3728,7 @@ tls13_ClientSendEarlyDataXtn(sslSocket *ss,
                 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;
     
    @@ -3758,11 +3762,18 @@ tls13_ServerHandleEarlyDataXtn(sslSocket *ss, PRUint16 ex_type,
             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;
    @@ -3777,6 +3788,7 @@ tls13_ServerSendEarlyDataXtn(sslSocket *ss,
         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;
    @@ -3818,6 +3830,7 @@ tls13_ClientHandleEarlyDataXtn(sslSocket *ss, PRUint16 ex_type,
     
         /* Keep track of negotiated extensions. */
         ss->xtnData.negotiated[ss->xtnData.numNegotiated++] = ex_type;
    +    ss->ssl3.hs.zeroRttState = ssl_0rtt_accepted;
     
         return SECSuccess;
     }
    diff --git a/security/nss/lib/ssl/sslcon.c b/security/nss/lib/ssl/sslcon.c
    index b38e1b82e0ba..7befcd6bc633 100644
    --- a/security/nss/lib/ssl/sslcon.c
    +++ b/security/nss/lib/ssl/sslcon.c
    @@ -205,7 +205,7 @@ ssl_BeginClientHandshake(sslSocket *ss)
     
         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);
     
    diff --git a/security/nss/lib/ssl/sslerr.h b/security/nss/lib/ssl/sslerr.h
    index 0e312ead4239..cdbe54ed55b3 100644
    --- a/security/nss/lib/ssl/sslerr.h
    +++ b/security/nss/lib/ssl/sslerr.h
    @@ -237,6 +237,9 @@ typedef enum {
         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 */
    diff --git a/security/nss/lib/ssl/sslimpl.h b/security/nss/lib/ssl/sslimpl.h
    index 3227ce64f336..d06e8be6de58 100644
    --- a/security/nss/lib/ssl/sslimpl.h
    +++ b/security/nss/lib/ssl/sslimpl.h
    @@ -147,6 +147,9 @@ typedef enum { SSLAppOpRead = 0,
     /* 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 */
    @@ -305,7 +308,7 @@ typedef struct {
     #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
    @@ -607,7 +610,6 @@ struct sslSessionIDStr {
         PRUint32 authKeyBits;
         SSLKEAType keaType;
         PRUint32 keaKeyBits;
    -    PRUint32 namedGroups;
     
         union {
             struct {
    @@ -749,6 +751,20 @@ struct ssl3MACDefStr {
         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,
    @@ -766,11 +782,16 @@ typedef enum {
         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.
      */
    @@ -989,9 +1010,13 @@ typedef struct SSL3HandshakeStateStr {
                                             * 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;
     
     /*
    @@ -1302,11 +1327,18 @@ struct sslSocketStr {
         PRCList /*  */ 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;
    @@ -1341,7 +1373,6 @@ extern sslSessionIDCacheFunc ssl_sid_cache;
     extern sslSessionIDUncacheFunc ssl_sid_uncache;
     
     extern const namedGroupDef ssl_named_groups[];
    -extern const unsigned int ssl_named_group_count;
     
     /************************************************************************/
     
    @@ -1648,7 +1679,7 @@ extern SECStatus ssl3_AuthCertificateComplete(sslSocket *ss, PRErrorCode error);
     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
    @@ -1691,9 +1722,8 @@ 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);
    @@ -1725,7 +1755,7 @@ extern SECStatus ssl_NamedGroup2ECParams(PLArenaPool *arena,
     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);
    @@ -1805,11 +1835,10 @@ extern SECStatus ssl_ParseSignatureSchemes(sslSocket *ss, PLArenaPool *arena,
                                                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);
    @@ -1940,14 +1969,15 @@ 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);
    diff --git a/security/nss/lib/ssl/sslinfo.c b/security/nss/lib/ssl/sslinfo.c
    index 0eaaa11a4c4d..e196c63812b9 100644
    --- a/security/nss/lib/ssl/sslinfo.c
    +++ b/security/nss/lib/ssl/sslinfo.c
    @@ -80,13 +80,9 @@ SSL_GetChannelInfo(PRFileDesc *fd, SSLChannelInfo *info, PRUintn len)
                         ? 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;
    diff --git a/security/nss/lib/ssl/sslsecur.c b/security/nss/lib/ssl/sslsecur.c
    index 27877842a6d0..f94d0945fdae 100644
    --- a/security/nss/lib/ssl/sslsecur.c
    +++ b/security/nss/lib/ssl/sslsecur.c
    @@ -930,7 +930,11 @@ ssl_SecureSend(sslSocket *ss, const unsigned char *buf, int len, int flags)
             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) {
    @@ -960,8 +964,10 @@ ssl_SecureSend(sslSocket *ss, const unsigned char *buf, int len, int flags)
         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",
    diff --git a/security/nss/lib/ssl/sslsock.c b/security/nss/lib/ssl/sslsock.c
    index f93587075c54..8fbd72b756b2 100644
    --- a/security/nss/lib/ssl/sslsock.c
    +++ b/security/nss/lib/ssl/sslsock.c
    @@ -144,6 +144,7 @@ static const PRUint16 srtpCiphers[] = {
     #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 },
    @@ -179,7 +180,6 @@ const namedGroupDef ssl_named_groups[] = {
     #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));
     
    @@ -257,6 +257,7 @@ ssl_DupSocket(sslSocket *os)
     {
         sslSocket *ss;
         SECStatus rv;
    +    unsigned int i;
     
         ss = ssl_NewSocket((PRBool)(!os->opt.noLocks), os->protocolVariant);
         if (!ss) {
    @@ -315,7 +316,6 @@ ssl_DupSocket(sslSocket *os)
                     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.
    @@ -336,6 +336,9 @@ ssl_DupSocket(sslSocket *os)
             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);
    @@ -798,7 +801,6 @@ SSL_OptionSet(PRFileDesc *fd, PRInt32 which, PRBool on)
                 break;
     
             case SSL_ENABLE_NPN:
    -            ss->opt.enableNPN = on;
                 break;
     
             case SSL_ENABLE_ALPN:
    @@ -1248,7 +1250,6 @@ SSL_OptionSetDefault(PRInt32 which, PRBool on)
                 break;
     
             case SSL_ENABLE_NPN:
    -            ssl_defaults.enableNPN = on;
                 break;
     
             case SSL_ENABLE_ALPN:
    @@ -1498,49 +1499,61 @@ NSS_SetFrancePolicy(void)
     }
     
     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;
         }
     
    -    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;
    +    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) {
    +        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;
    +            }
    +            if (ssl_named_groups[j].name == groups[i]) {
    +                ss->namedGroupPreferences[i] = &ssl_named_groups[j];
    +                break;
                 }
    -            return SECSuccess;
             }
         }
     
    -    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;
         }
    @@ -1559,14 +1572,19 @@ SSL_DHEGroupPrefSet(PRFileDesc *fd,
             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]) {
    @@ -1594,9 +1612,22 @@ SSL_DHEGroupPrefSet(PRFileDesc *fd,
             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;
     }
     
    @@ -1723,6 +1754,9 @@ ssl_GetDHEParams(const namedGroupDef *groupDef)
                 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);
         }
    @@ -1789,23 +1823,26 @@ ssl_ValidateDHENamedGroup(sslSocket *ss,
     {
         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;
    +        }
    +        if (!ssl_NamedGroupEnabled(ss, ss->namedGroupPreferences[i])) {
                 continue;
             }
     
    -        params = ssl_GetDHEParams(&ssl_named_groups[i]);
    +        params = ssl_GetDHEParams(ss->namedGroupPreferences[i]);
             PORT_Assert(params);
             if (SECITEM_ItemsAreEqual(¶ms->prime, dh_p)) {
                 if (!SECITEM_ItemsAreEqual(¶ms->base, dh_g)) {
                     return SECFailure;
                 }
                 if (groupDef)
    -                *groupDef = &ssl_named_groups[i];
    +                *groupDef = ss->namedGroupPreferences[i];
                 if (dhParams)
                     *dhParams = params;
                 return SECSuccess;
    @@ -1818,9 +1855,7 @@ ssl_ValidateDHENamedGroup(sslSocket *ss,
     /* 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 = {
    @@ -1833,28 +1868,24 @@ ssl_SelectDHEParams(sslSocket *ss,
         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;
     }
    @@ -3570,7 +3601,7 @@ 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];
             }
    @@ -3583,6 +3614,7 @@ ssl_NamedGroupEnabled(const sslSocket *ss, const namedGroupDef *groupDef)
     {
         PRUint32 policy;
         SECStatus rv;
    +    unsigned int i;
     
         PORT_Assert(groupDef);
     
    @@ -3590,7 +3622,13 @@ ssl_NamedGroupEnabled(const sslSocket *ss, const namedGroupDef *groupDef)
         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.
    @@ -3721,6 +3759,7 @@ ssl_NewSocket(PRBool makeLocks, SSLProtocolVariant protocolVariant)
     {
         SECStatus rv;
         sslSocket *ss;
    +    int i;
     
         ssl_SetDefaultsFromEnvironment();
     
    @@ -3765,7 +3804,9 @@ ssl_NewSocket(PRBool makeLocks, SSLProtocolVariant protocolVariant)
     
         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);
    diff --git a/security/nss/lib/ssl/tls13con.c b/security/nss/lib/ssl/tls13con.c
    index 47434efed130..1355f8c19998 100644
    --- a/security/nss/lib/ssl/tls13con.c
    +++ b/security/nss/lib/ssl/tls13con.c
    @@ -48,7 +48,6 @@ static SECStatus tls13_ChaCha20Poly1305(
         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(
    @@ -64,6 +63,8 @@ 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,
    @@ -77,6 +78,8 @@ static SECStatus tls13_ServerHandleFinished(sslSocket *ss,
     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);
    @@ -160,8 +163,6 @@ tls13_HandshakeState(SSL3WaitState st)
             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;
    @@ -309,6 +310,34 @@ tls13_GetHmacMechanism(sslSocket *ss)
         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.
    @@ -320,21 +349,21 @@ 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;
             }
    @@ -343,27 +372,20 @@ tls13_SetupClientHello(sslSocket *ss)
                     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));
    @@ -473,16 +495,18 @@ tls13_HandlePostHelloHandshakeMessage(sslSocket *ss, SSL3Opaque *b,
     {
         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);
     
    @@ -907,6 +931,10 @@ tls13_CanResume(sslSocket *ss, const sslSessionID *sid)
     {
         const sslServerCert *sc;
     
    +    if (!sid) {
    +        return PR_FALSE;
    +    }
    +
         if (sid->version != ss->version) {
             return PR_FALSE;
         }
    @@ -944,6 +972,54 @@ tls13_AlpnTagAllowed(sslSocket *ss, const SECItem *tag)
         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. */
    @@ -955,14 +1031,10 @@ tls13_HandleClientHelloPart2(sslSocket *ss,
         SECStatus rv;
         SSL3Statistics *ssl3stats = SSL_GetStatistics();
         int j;
    +    PRBool shouldRetry = PR_FALSE;
    +    ssl3CipherSuite previousCipherSuite;
     
    -    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;
    +    if (!tls13_CanResume(ss, sid)) {
             ss->statelessResume = PR_FALSE;
         }
     
    @@ -971,18 +1043,25 @@ tls13_HandleClientHelloPart2(sslSocket *ss,
         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;
         }
     
    @@ -992,12 +1071,9 @@ tls13_HandleClientHelloPart2(sslSocket *ss,
             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);
    @@ -1008,42 +1084,28 @@ tls13_HandleClientHelloPart2(sslSocket *ss,
             }
             ssl3_RegisterServerHelloExtensionSender(
                 ss, ssl_tls13_pre_shared_key_xtn, tls13_ServerSendPreSharedKeyXtn);
    -        ss->sec.ci.sid = sid;
    -        ss->ssl3.hs.doing0Rtt = tls13_ServerAllow0Rtt(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, sid);
    +    } else {
    +        ss->ssl3.hs.origCipherSuite = ss->ssl3.hs.cipher_suite;
    +
    +        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 */
    @@ -1055,28 +1117,64 @@ tls13_HandleClientHelloPart2(sslSocket *ss,
             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 (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;
         }
     
    -    if (!sid) {
    +    /* 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;
         }
    +    /* Take ownership of the session. */
    +    ss->sec.ci.sid = sid;
    +    sid = NULL;
     
    -    if (ss->ssl3.hs.doing0Rtt) {
    +    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) {
    @@ -1092,7 +1190,10 @@ tls13_HandleClientHelloPart2(sslSocket *ss,
             }
             TLS13_SET_HS_STATE(ss, wait_0rtt_finished);
         } else {
    +        PORT_Assert(ss->ssl3.hs.zeroRttState == ssl_0rtt_none ||
    +                    ss->ssl3.hs.zeroRttState == ssl_0rtt_ignored);
             ssl_GetXmitBufLock(ss);
    +
             rv = tls13_SendServerHelloSequence(ss);
             ssl_ReleaseXmitBufLock(ss);
             if (rv != SECSuccess) {
    @@ -1102,6 +1203,92 @@ tls13_HandleClientHelloPart2(sslSocket *ss,
         }
     
         return SECSuccess;
    +
    +loser:
    +    if (sid) {
    +        if (ss->sec.uncache)
    +            ss->sec.uncache(sid);
    +        ssl_FreeSID(sid);
    +    }
    +    return SECFailure;
    +}
    +
    +static SECStatus
    +tls13_SendHelloRetryRequest(sslSocket *ss, const namedGroupDef *selectedGroup)
    +{
    +    SECStatus rv;
    +
    +    SSL_TRC(3, ("%d: TLS13[%d]: send hello retry request handshake",
    +                SSL_GETPID(), ss->fd));
    +
    +    PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
    +
    +    /* We asked already, but made no progress. */
    +    if (ss->ssl3.hs.helloRetry) {
    +        FATAL_ERROR(ss, SSL_ERROR_BAD_2ND_CLIENT_HELLO, illegal_parameter);
    +        return SECFailure;
    +    }
    +
    +    ssl_GetXmitBufLock(ss);
    +    rv = ssl3_AppendHandshakeHeader(ss, hello_retry_request,
    +                                    2 +     /* version */
    +                                        2 + /* cipher suite */
    +                                        2 + /* group */
    +                                        2 /* (empty) extensions */);
    +    if (rv != SECSuccess) {
    +        FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error);
    +        goto loser;
    +    }
    +
    +    rv = ssl3_AppendHandshakeNumber(ss, ss->version, 2);
    +    if (rv != SECSuccess) {
    +        FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error);
    +        goto loser;
    +    }
    +
    +    rv = ssl3_AppendHandshakeNumber(ss, ss->ssl3.hs.cipher_suite, 2);
    +    if (rv != SECSuccess) {
    +        FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error);
    +        goto loser;
    +    }
    +
    +    rv = ssl3_AppendHandshakeNumber(ss, selectedGroup->name, 2);
    +    if (rv != SECSuccess) {
    +        FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error);
    +        goto loser;
    +    }
    +
    +    /* Send an empty extensions block. */
    +    rv = ssl3_AppendHandshakeNumber(ss, 0, 2);
    +    if (rv != SECSuccess) {
    +        FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error);
    +        goto loser;
    +    }
    +
    +    rv = ssl3_FlushHandshake(ss, 0);
    +    if (rv != SECSuccess) {
    +        goto loser; /* error code set by ssl3_FlushHandshake */
    +    }
    +    ssl_ReleaseXmitBufLock(ss);
    +
    +    ss->ssl3.hs.helloRetry = PR_TRUE;
    +
    +    /* We previously thought that we could accept 0-RTT.  Change of plans. */
    +    if (ss->ssl3.hs.zeroRttState == ssl_0rtt_accepted) {
    +        ss->ssl3.hs.zeroRttState = ssl_0rtt_ignored;
    +        ss->ssl3.hs.zeroRttIgnore = ssl_0rtt_ignore_hrr;
    +    }
    +    /* Clients will have sent Finished for 0-RTT.  We won't be seeing them, so
    +     * we won't count them, but they will. */
    +    if (IS_DTLS(ss) && ss->ssl3.hs.zeroRttState == ssl_0rtt_ignored) {
    +        ss->ssl3.hs.recvMessageSeq++;
    +    }
    +
    +    return SECSuccess;
    +
    +loser:
    +    ssl_ReleaseXmitBufLock(ss);
    +    return SECFailure;
     }
     
     /* Called from tls13_HandleClientHello.
    @@ -1109,14 +1296,13 @@ tls13_HandleClientHelloPart2(sslSocket *ss,
      * Caller must hold Handshake and RecvBuf locks.
      */
     SECStatus
    -tls13_HandleClientKeyShare(sslSocket *ss)
    +tls13_HandleClientKeyShare(sslSocket *ss, PRBool *shouldRetry)
     {
    -    const namedGroupDef *expectedGroup;
    +    const namedGroupDef *selectedGroup;
         SECStatus rv;
         TLS13KeyShareEntry *peerShare = NULL; /* theirs */
         sslEphemeralKeyPair *keyPair;         /* ours */
         PRCList *cur_p;
    -    const ssl3DHParams *dheParams = NULL;
     
         SSL_TRC(3, ("%d: TLS13[%d]: handle client_key_share handshake",
                     SSL_GETPID(), ss->fd));
    @@ -1136,8 +1322,8 @@ tls13_HandleClientKeyShare(sslSocket *ss)
         switch (ss->ssl3.hs.kea_def->exchKeyType) {
             case ssl_kea_ecdh:
             case ssl_kea_ecdh_psk:
    -            expectedGroup = ssl_GetECGroupForServerSocket(ss);
    -            if (!expectedGroup) {
    +            selectedGroup = ssl_GetECGroupForServerSocket(ss);
    +            if (!selectedGroup) {
                     FATAL_ERROR(ss, SSL_ERROR_NO_CYPHER_OVERLAP,
                                 handshake_failure);
                     return SECFailure;
    @@ -1146,14 +1332,13 @@ tls13_HandleClientKeyShare(sslSocket *ss)
     
             case ssl_kea_dh:
             case ssl_kea_dh_psk:
    -            rv = ssl_SelectDHEParams(ss, &expectedGroup, &dheParams);
    +            rv = ssl_SelectDHEGroup(ss, &selectedGroup);
                 if (rv != SECSuccess) {
                     FATAL_ERROR(ss, SSL_ERROR_NO_CYPHER_OVERLAP,
                                 handshake_failure);
                     return SECFailure;
                 }
    -            PORT_Assert(expectedGroup);
    -            PORT_Assert(dheParams);
    +            PORT_Assert(selectedGroup);
                 break;
     
             default:
    @@ -1169,7 +1354,7 @@ tls13_HandleClientKeyShare(sslSocket *ss)
         while (cur_p != &ss->ssl3.hs.remoteKeyShares) {
             TLS13KeyShareEntry *offer = (TLS13KeyShareEntry *)cur_p;
     
    -        if (offer->group == expectedGroup) {
    +        if (offer->group == selectedGroup) {
                 peerShare = offer;
                 break;
             }
    @@ -1177,27 +1362,28 @@ tls13_HandleClientKeyShare(sslSocket *ss)
         }
     
         if (!peerShare) {
    -        /* No acceptable group. In future, we will need to correct the client.
    -         * Currently just generate an error.
    -         * TODO(ekr@rtfm.com): Write code to correct client.
    -         */
    -        FATAL_ERROR(ss, SSL_ERROR_NO_CYPHER_OVERLAP, handshake_failure);
    +        *shouldRetry = PR_TRUE;
    +        return tls13_SendHelloRetryRequest(ss, selectedGroup);
    +    }
    +
    +    PORT_Assert(ss->ssl3.hs.zeroRttState != ssl_0rtt_sent);
    +    rv = tls13_ComputeEarlySecrets(ss, ss->ssl3.hs.zeroRttState == ssl_0rtt_accepted);
    +    if (rv != SECSuccess) {
    +        FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error);
             return SECFailure;
         }
     
         /* Generate our key */
    -    switch (expectedGroup->type) {
    -        case group_type_ec:
    -            rv = ssl_CreateECDHEphemeralKeyPair(expectedGroup, &keyPair);
    -            break;
    -        case group_type_ff:
    -            PORT_Assert(dheParams);
    -            rv = ssl_CreateDHEKeyPair(expectedGroup, dheParams, &keyPair);
    -            break;
    -    }
    +    rv = tls13_CreateKeyShare(ss, selectedGroup);
         if (rv != SECSuccess)
             return rv;
    -    PR_APPEND_LINK(&keyPair->link, &ss->ephemeralKeyPairs);
    +
    +    /* We should have exactly one key share. */
    +    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));
     
         ss->sec.keaType = ss->ssl3.hs.kea_def->exchKeyType;
         ss->sec.keaKeyBits = SECKEY_PublicKeyStrengthInBits(keyPair->keys->pubKey);
    @@ -1290,6 +1476,104 @@ tls13_SendCertificateRequest(sslSocket *ss)
         return SECSuccess;
     }
     
    +static SECStatus
    +tls13_HandleHelloRetryRequest(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
    +{
    +    SECStatus rv;
    +    PRInt32 tmp;
    +    const namedGroupDef *group;
    +
    +    SSL_TRC(3, ("%d: TLS13[%d]: handle hello retry request",
    +                SSL_GETPID(), ss->fd));
    +
    +    PORT_Assert(ss->opt.noLocks || ssl_HaveRecvBufLock(ss));
    +    PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
    +
    +    /* Client */
    +    rv = TLS13_CHECK_HS_STATE(ss, SSL_ERROR_RX_UNEXPECTED_HELLO_RETRY_REQUEST,
    +                              wait_server_hello);
    +    if (rv != SECSuccess) {
    +        return SECFailure;
    +    }
    +
    +    /* Fool me once, shame on you; fool me twice... */
    +    if (ss->ssl3.hs.helloRetry) {
    +        FATAL_ERROR(ss, SSL_ERROR_RX_UNEXPECTED_HELLO_RETRY_REQUEST,
    +                    unexpected_message);
    +        return SECFailure;
    +    }
    +
    +    if (ss->ssl3.hs.zeroRttState == ssl_0rtt_sent) {
    +        /* Oh well, back to the start. */
    +        tls13_SetNullCipherSpec(ss, &ss->ssl3.cwSpec);
    +        ss->ssl3.hs.zeroRttState = ssl_0rtt_ignored;
    +    } else {
    +        PORT_Assert(ss->ssl3.hs.zeroRttState == ssl_0rtt_none);
    +    }
    +
    +    tmp = ssl3_ConsumeHandshakeNumber(ss, 2, &b, &length);
    +    if (tmp < 0) {
    +        return SECFailure; /* error code already set */
    +    }
    +    if (tmp > ss->vrange.max || tmp < SSL_LIBRARY_VERSION_TLS_1_3) {
    +        FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_HELLO_RETRY_REQUEST,
    +                    protocol_version);
    +        return SECFailure;
    +    }
    +
    +    /* Ignore the cipher suite, it's going to be removed soon anyway. */
    +    tmp = ssl3_ConsumeHandshakeNumber(ss, 2, &b, &length);
    +    if (tmp < 0) {
    +        return SECFailure; /* error code already set */
    +    }
    +
    +    tmp = ssl3_ConsumeHandshakeNumber(ss, 2, &b, &length);
    +    if (tmp < 0) {
    +        return SECFailure; /* error code already set */
    +    }
    +    group = ssl_LookupNamedGroup((SSLNamedGroup)tmp);
    +    if (!group || !ssl_NamedGroupEnabled(ss, group)) {
    +        FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_HELLO_RETRY_REQUEST,
    +                    illegal_parameter);
    +        return SECFailure;
    +    }
    +
    +    tmp = ssl3_ConsumeHandshakeNumber(ss, 2, &b, &length);
    +    if (tmp < 0) {
    +        return SECFailure; /* error code already set */
    +    }
    +    /* Ignore the extensions. */
    +    if (tmp != length) {
    +        FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_HELLO_RETRY_REQUEST,
    +                    decode_error);
    +        return SECFailure;
    +    }
    +
    +    /* If there is already a share for the requested group, abort. */
    +    if (ssl_LookupEphemeralKeyPair(ss, group)) {
    +        FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_HELLO_RETRY_REQUEST,
    +                    illegal_parameter);
    +        return SECFailure;
    +    }
    +
    +    rv = tls13_CreateKeyShare(ss, group);
    +    if (rv != SECSuccess) {
    +        FATAL_ERROR(ss, SEC_ERROR_KEYGEN_FAIL, internal_error);
    +        return SECFailure;
    +    }
    +
    +    ss->ssl3.hs.helloRetry = PR_TRUE;
    +
    +    ssl_GetXmitBufLock(ss);
    +    rv = ssl3_SendClientHello(ss, client_hello_retry);
    +    ssl_ReleaseXmitBufLock(ss);
    +    if (rv != SECSuccess) {
    +        return SECFailure;
    +    }
    +
    +    return SECSuccess;
    +}
    +
     static SECStatus
     tls13_HandleCertificateRequest(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
     {
    @@ -1390,7 +1674,7 @@ tls13_SendEncryptedServerSequence(sslSocket *ss)
             return SECFailure;
         }
     
    -    if (ss->ssl3.hs.doing0Rtt) {
    +    if (ss->ssl3.hs.zeroRttState == ssl_0rtt_accepted) {
             rv = ssl3_RegisterServerHelloExtensionSender(ss, ssl_tls13_early_data_xtn,
                                                          tls13_ServerSendEarlyDataXtn);
             if (rv != SECSuccess) {
    @@ -1437,6 +1721,7 @@ SECStatus
     tls13_SendServerHelloSequence(sslSocket *ss)
     {
         SECStatus rv;
    +    PRErrorCode err = 0;
     
         SSL_TRC(3, ("%d: TLS13[%d]: begin send server_hello sequence",
                     SSL_GETPID(), ss->fd));
    @@ -1451,13 +1736,17 @@ tls13_SendServerHelloSequence(sslSocket *ss)
     
         rv = tls13_SendEncryptedServerSequence(ss);
         if (rv != SECSuccess) {
    -        /* Since the ServerHello was successfully serialized, give it a chance
    -         * to reach the network.  This gives the client a chance to perform the
    -         * key exchange and decrypt the alert we're about to send. This is best
    -         * effort only: ignore any error code and restore the existing one. */
    -        PRErrorCode code = PORT_GetError();
    -        (void)ssl3_FlushHandshake(ss, IS_DTLS(ss) ? ssl_SEND_FLAG_NO_RETRANSMIT : 0);
    -        PORT_SetError(code);
    +        err = PORT_GetError();
    +    }
    +    /* Even if we get an error, since the ServerHello was successfully
    +     * serialized, we should give it a chance to reach the network.  This gives
    +     * the client a chance to perform the key exchange and decrypt the alert
    +     * we're about to send. */
    +    rv |= ssl3_FlushHandshake(ss, 0);
    +    if (rv != SECSuccess) {
    +        if (err) {
    +            PORT_SetError(err);
    +        }
             return SECFailure;
         }
     
    @@ -1476,7 +1765,7 @@ tls13_SendServerHelloSequence(sslSocket *ss)
             return SECFailure;
         }
     
    -    if (ss->ssl3.hs.doing0Rtt) {
    +    if (ss->ssl3.hs.zeroRttState == ssl_0rtt_accepted) {
             rv = tls13_SetCipherSpec(ss,
                                      TrafficKeyEarlyApplicationData,
                                      CipherSpecRead, PR_TRUE);
    @@ -1484,9 +1773,16 @@ tls13_SendServerHelloSequence(sslSocket *ss)
                 LOG_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE);
                 return SECFailure;
             }
    -
    -        TLS13_SET_HS_STATE(ss, wait_0rtt_end_of_early_data);
         } else {
    +        PORT_Assert(ss->ssl3.hs.zeroRttState == ssl_0rtt_none ||
    +                    ss->ssl3.hs.zeroRttState == ssl_0rtt_ignored);
    +
    +        /* If we are ignoring 0-RTT, then we will ignore a handshake
    +         * message. But the client will have counted them. */
    +        if (IS_DTLS(ss) && ss->ssl3.hs.zeroRttState == ssl_0rtt_ignored) {
    +            ss->ssl3.hs.recvMessageSeq++;
    +        }
    +
             rv = tls13_SetCipherSpec(ss,
                                      TrafficKeyHandshake,
                                      CipherSpecRead, PR_FALSE);
    @@ -1494,22 +1790,11 @@ tls13_SendServerHelloSequence(sslSocket *ss)
                 LOG_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE);
                 return SECFailure;
             }
    -
    -        if (ssl3_ExtensionNegotiated(ss, ssl_tls13_early_data_xtn)) {
    -            /* If for any reason we rejected 0-RTT, we need to trial decrypt.
    -             * Note: we use this API point because
    -             * ssl3_ClientExtensionAdvertised() is only set on the server.
    -             * This extension handler only fails to set the negotiated
    -             * flag if the extension is malformed.
    -             */
    -            TLS13_SET_HS_STATE(ss, wait_0rtt_trial_decrypt);
    -        } else {
    -            TLS13_SET_HS_STATE(ss,
    -                               ss->opt.requestCertificate ? wait_client_cert
    -                                                          : wait_finished);
    -        }
         }
     
    +    TLS13_SET_HS_STATE(ss,
    +                       ss->opt.requestCertificate ? wait_client_cert
    +                                                  : wait_finished);
         return SECSuccess;
     }
     
    @@ -1521,14 +1806,6 @@ tls13_HandleServerHelloPart2(sslSocket *ss)
         sslSessionID *sid = ss->sec.ci.sid;
         SSL3Statistics *ssl3stats = SSL_GetStatistics();
     
    -    if (ss->ssl3.hs.doing0Rtt) {
    -        rv = SSL3_SendAlert(ss, alert_warning, end_of_early_data);
    -        if (rv != SECSuccess) {
    -            FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error);
    -            return SECFailure;
    -        }
    -    }
    -
         if (isPSK) {
             PRBool cacheOK = PR_FALSE;
             do {
    @@ -1539,7 +1816,7 @@ tls13_HandleServerHelloPart2(sslSocket *ss)
                 }
                 /* If we offered early data, then we already have the shared secret
                  * recovered. */
    -            if (!ssl3_ClientExtensionAdvertised(ss, ssl_tls13_early_data_xtn)) {
    +            if (ss->ssl3.hs.zeroRttState == ssl_0rtt_none) {
                     rv = tls13_RecoverWrappedSharedSecret(ss, sid);
                     if (rv != SECSuccess) {
                         FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error);
    @@ -1572,7 +1849,7 @@ tls13_HandleServerHelloPart2(sslSocket *ss)
                             illegal_parameter);
                 return SECFailure;
             }
    -        if (ssl3_ClientExtensionAdvertised(ss, ssl_tls13_early_data_xtn)) {
    +        if (ss->ssl3.hs.zeroRttState != ssl_0rtt_none) {
                 PORT_Assert(ss->ssl3.hs.currentSecret);
                 /* If we tried 0-RTT and didn't even get PSK, we need to clean
                  * stuff up. */
    @@ -1604,7 +1881,7 @@ tls13_HandleServerHelloPart2(sslSocket *ss)
         }
     
         if (!ss->ssl3.hs.currentSecret) {
    -        PORT_Assert(!isPSK || !ss->ssl3.hs.doing0Rtt);
    +        PORT_Assert(!isPSK || ss->ssl3.hs.zeroRttState == ssl_0rtt_none);
     
             /* If we don't already have the Early Secret we need to make it
              * now. */
    @@ -1640,12 +1917,6 @@ tls13_HandleServerHelloPart2(sslSocket *ss)
             return SECFailure; /* error code is set. */
         }
     
    -    rv = tls13_SetCipherSpec(ss, TrafficKeyHandshake,
    -                             CipherSpecWrite, PR_FALSE);
    -    if (rv != SECSuccess) {
    -        FATAL_ERROR(ss, SSL_ERROR_INIT_CIPHER_SUITE_FAILURE, internal_error);
    -        return SECFailure;
    -    }
         rv = tls13_SetCipherSpec(ss, TrafficKeyHandshake,
                                  CipherSpecRead, PR_FALSE);
         if (rv != SECSuccess) {
    @@ -1760,7 +2031,7 @@ void
     tls13_CipherSpecAddRef(ssl3CipherSpec *spec)
     {
         ++spec->refCt;
    -    SSL_TRC(10, ("%d: TLS 1.3: Increment ref ct for spec %d. new ct = %d",
    +    SSL_TRC(10, ("%d: TLS13[-]: Increment ref ct for spec %d. new ct = %d",
                      SSL_GETPID(), spec, spec->refCt));
     }
     
    @@ -1771,11 +2042,10 @@ tls13_CipherSpecRelease(ssl3CipherSpec *spec)
     {
         PORT_Assert(spec->refCt > 0);
         --spec->refCt;
    -    SSL_TRC(10, ("%d: TLS 1.3: decrement refct for spec %d. phase=%s new ct = %d",
    -                 SSL_GETPID(),
    -                 spec, spec->phase, spec->refCt));
    +    SSL_TRC(10, ("%d: TLS13[-]: decrement refct for spec %d. phase=%s new ct = %d",
    +                 SSL_GETPID(), spec, spec->phase, spec->refCt));
         if (!spec->refCt) {
    -        SSL_TRC(10, ("%d: TLS 1.3: Freeing spec %d. phase=%s",
    +        SSL_TRC(10, ("%d: TLS13[-]: Freeing spec %d. phase=%s",
                          SSL_GETPID(), spec, spec->phase));
             PR_REMOVE_LINK(&spec->link);
             ssl3_DestroyCipherSpec(spec, PR_TRUE);
    @@ -2044,7 +2314,6 @@ tls13_SetCipherSpec(sslSocket *ss, TrafficKeyType type,
         /* We use the epoch for cipher suite identification, so increment
          * it in both TLS and DTLS. */
         if ((*specp)->epoch == PR_UINT16_MAX) {
    -        ssl_ReleaseSpecWriteLock(ss);
             return SECFailure;
         }
         spec->epoch = (*specp)->epoch + 1;
    @@ -2065,10 +2334,10 @@ tls13_SetCipherSpec(sslSocket *ss, TrafficKeyType type,
         *specp = spec;                   /* Overwrite. */
         ssl_ReleaseSpecWriteLock(ss);
     
    -    SSL_TRC(3, ("%d: TLS13[%d]: %s installed key for phase='%s' dir=%s",
    +    SSL_TRC(3, ("%d: TLS13[%d]: %s installed key for phase='%s'.%d dir=%s",
                     SSL_GETPID(), ss->fd,
                     ss->sec.isServer ? "server" : "client",
    -                spec->phase,
    +                spec->phase, spec->epoch,
                     direction == CipherSpecRead ? "read" : "write"));
     
         return SECSuccess;
    @@ -2350,7 +2619,7 @@ tls13_HandleEncryptedExtensions(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
     
         /* If we are doing 0-RTT, then we already have an NPN value. Stash
          * it for comparison. */
    -    if (ss->ssl3.hs.doing0Rtt &&
    +    if (ss->ssl3.hs.zeroRttState == ssl_0rtt_sent &&
             ss->ssl3.nextProtoState == SSL_NEXT_PROTO_EARLY_VALUE) {
             oldNpn = ss->ssl3.nextProto;
             ss->ssl3.nextProto.data = NULL;
    @@ -2361,16 +2630,25 @@ tls13_HandleEncryptedExtensions(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
             return SECFailure; /* Error code set below */
         }
     
    -    if (ss->ssl3.hs.doing0Rtt &&
    -        ssl3_ExtensionNegotiated(ss, ssl_tls13_early_data_xtn)) {
    -        /* check that the server negotiated the same ALPN (if any). */
    +    if (ss->ssl3.hs.zeroRttState == ssl_0rtt_accepted) {
    +        /* Check that the server negotiated the same ALPN (if any). */
             if (SECITEM_CompareItem(&oldNpn, &ss->ssl3.nextProto)) {
                 SECITEM_FreeItem(&oldNpn, PR_FALSE);
                 FATAL_ERROR(ss, SSL_ERROR_NEXT_PROTOCOL_DATA_INVALID,
                             illegal_parameter);
                 return SECFailure;
             }
    +    } else if (ss->ssl3.hs.zeroRttState == ssl_0rtt_sent) {
    +        /* Though we sent 0-RTT, the early_data extension wasn't present so the
    +         * state is unmodified; the server must have rejected 0-RTT. */
    +        ss->ssl3.hs.zeroRttState = ssl_0rtt_ignored;
    +        ss->ssl3.hs.zeroRttIgnore = ssl_0rtt_ignore_trial;
    +    } else {
    +        PORT_Assert(ss->ssl3.hs.zeroRttState == ssl_0rtt_none ||
    +                    (ss->ssl3.hs.helloRetry &&
    +                     ss->ssl3.hs.zeroRttState == ssl_0rtt_ignored));
         }
    +
         SECITEM_FreeItem(&oldNpn, PR_FALSE);
         if (ss->ssl3.hs.kea_def->authKeyType == ssl_auth_psk) {
             TLS13_SET_HS_STATE(ss, wait_finished);
    @@ -2459,7 +2737,7 @@ tls13_SendCertificateVerify(sslSocket *ss, SECKEYPrivateKey *privKey)
             return SECFailure;
         }
     
    -    rv = ssl3_SignHashes(&tbsHash, privKey, &buf, PR_TRUE);
    +    rv = ssl3_SignHashes(ss, &tbsHash, 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.
    @@ -2560,8 +2838,7 @@ tls13_HandleCertificateVerify(sslSocket *ss, SSL3Opaque *b, PRUint32 length,
             return SECFailure;
         }
     
    -    rv = ssl3_VerifySignedHashes(&tbsHash, ss->sec.peerCert, &signed_hash,
    -                                 PR_TRUE, ss->pkcs11PinArg);
    +    rv = ssl3_VerifySignedHashes(ss, sigScheme, &tbsHash, &signed_hash);
         if (rv != SECSuccess) {
             FATAL_ERROR(ss, PORT_GetError(), decrypt_error);
             return SECFailure;
    @@ -2691,12 +2968,6 @@ tls13_SendFinished(sslSocket *ss, PK11SymKey *baseKey)
             return SECFailure; /* Error code already set. */
         }
     
    -    rv = ssl3_FlushHandshake(ss,
    -                             (IS_DTLS(ss) && !ss->sec.isServer) ? ssl_SEND_FLAG_NO_RETRANSMIT : 0);
    -    if (rv != SECSuccess) {
    -        return SECFailure; /* Error code already set. */
    -    }
    -
         /* TODO(ekr@rtfm.com): Record key log */
         return SECSuccess;
     }
    @@ -2888,13 +3159,34 @@ tls13_SendClientSecondRound(sslSocket *ss)
             return SECFailure;
         }
         if (ss->ssl3.hs.authCertificatePending) {
    -        SSL_TRC(3, ("%d: TLS13[%p]: deferring ssl3_SendClientSecondRound because"
    +        SSL_TRC(3, ("%d: TLS13[%d]: deferring ssl3_SendClientSecondRound because"
                         " certificate authentication is still pending.",
                         SSL_GETPID(), ss->fd));
             ss->ssl3.hs.restartTarget = tls13_SendClientSecondRound;
             return SECWouldBlock;
         }
     
    +    if (ss->ssl3.hs.zeroRttState != ssl_0rtt_none) {
    +        if (ss->ssl3.hs.zeroRttState == ssl_0rtt_accepted) {
    +            rv = tls13_SendEndOfEarlyData(ss);
    +            if (rv != SECSuccess) {
    +                return SECFailure; /* Error code already set. */
    +            }
    +        }
    +        if (IS_DTLS(ss) && !ss->ssl3.hs.helloRetry) {
    +            /* Reset the counters so that the next epoch isn't set
    +             * incorrectly. */
    +            tls13_SetNullCipherSpec(ss, &ss->ssl3.cwSpec);
    +        }
    +    }
    +
    +    rv = tls13_SetCipherSpec(ss, TrafficKeyHandshake,
    +                             CipherSpecWrite, PR_FALSE);
    +    if (rv != SECSuccess) {
    +        FATAL_ERROR(ss, SSL_ERROR_INIT_CIPHER_SUITE_FAILURE, internal_error);
    +        return SECFailure;
    +    }
    +
         rv = tls13_ComputeApplicationSecrets(ss);
         if (rv != SECSuccess) {
             FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error);
    @@ -2940,6 +3232,10 @@ tls13_SendClientSecondRound(sslSocket *ss)
         if (rv != SECSuccess) {
             goto loser; /* err code was set. */
         }
    +    rv = ssl3_FlushHandshake(ss, IS_DTLS(ss) ? ssl_SEND_FLAG_NO_RETRANSMIT : 0);
    +    if (rv != SECSuccess) {
    +        goto loser;
    +    }
     
         rv = dtls_StartHolddownTimer(ss);
         if (rv != SECSuccess) {
    @@ -3431,10 +3727,11 @@ tls13_UnprotectRecord(sslSocket *ss, SSL3Ciphertext *cText, sslBuffer *plaintext
     /* 0-RTT is only permitted if:
      *
      * 1. We are doing TLS 1.3
    - * 2. The 0-RTT option is set.
    - * 3. We have a valid ticket.
    - * 4. The server is willing to accept 0-RTT.
    - * 5. We have not changed our ALPN settings to disallow the ALPN tag
    + * 2. This isn't a second ClientHello (in response to HelloRetryRequest)
    + * 3. The 0-RTT option is set.
    + * 4. We have a valid ticket.
    + * 5. The server is willing to accept 0-RTT.
    + * 6. We have not changed our ALPN settings to disallow the ALPN tag
      *    in the ticket.
      *
      * Called from tls13_ClientSendEarlyDataXtn().
    @@ -3444,6 +3741,8 @@ tls13_ClientAllow0Rtt(sslSocket *ss, const sslSessionID *sid)
     {
         if (sid->version < SSL_LIBRARY_VERSION_TLS_1_3)
             return PR_FALSE;
    +    if (ss->ssl3.hs.helloRetry)
    +        return PR_FALSE;
         if (!ss->opt.enable0RttData)
             return PR_FALSE;
         if (!ss->xtnData.ticketTimestampVerified &&
    @@ -3454,36 +3753,17 @@ tls13_ClientAllow0Rtt(sslSocket *ss, const sslSessionID *sid)
         return tls13_AlpnTagAllowed(ss, &sid->u.ssl3.alpnSelection);
     }
     
    -/* 0-RTT is only permitted if:
    - *
    - * 1. The 0-RTT option is set.
    - * 2. The ticket allowed 0-RTT.
    - * 3. We negotiated the same ALPN value as in the ticket.
    - * 4. Early data was negotiated.
    - */
    -static PRBool
    -tls13_ServerAllow0Rtt(sslSocket *ss, const sslSessionID *sid)
    -{
    -    if (!ss->opt.enable0RttData)
    -        return PR_FALSE;
    -    if ((sid->u.ssl3.locked.sessionTicket.flags & ticket_allow_early_data) == 0) {
    -        return PR_FALSE;
    -    }
    -    if (SECITEM_CompareItem(&ss->ssl3.nextProto, &sid->u.ssl3.alpnSelection))
    -        return PR_FALSE;
    -    if (!ssl3_ExtensionNegotiated(ss, ssl_tls13_early_data_xtn))
    -        return PR_FALSE;
    -    return PR_TRUE;
    -}
    -
     SECStatus
     tls13_MaybeDo0RTTHandshake(sslSocket *ss)
     {
         SECStatus rv;
         int bufferLen = ss->ssl3.hs.messages.len;
     
    -    if (!ss->ssl3.hs.doing0Rtt)
    +    /* Don't do anything if this is the second ClientHello or we decided not to
    +     * do 0-RTT (which means that there is no early_data extension). */
    +    if (ss->ssl3.hs.zeroRttState != ssl_0rtt_sent) {
             return SECSuccess;
    +    }
     
         SSL_TRC(3, ("%d: TLS13[%d]: in 0-RTT mode", SSL_GETPID(), ss->fd));
     
    @@ -3516,6 +3796,11 @@ tls13_MaybeDo0RTTHandshake(sslSocket *ss)
             return SECFailure;
         }
     
    +    ssl_GetSpecReadLock(ss);
    +    ss->ssl3.hs.nullSpec = ss->ssl3.cwSpec;
    +    tls13_CipherSpecAddRef(ss->ssl3.hs.nullSpec);
    +    ssl_ReleaseSpecReadLock(ss);
    +
         rv = tls13_SetCipherSpec(ss, TrafficKeyEarlyHandshake,
                                  CipherSpecWrite, PR_FALSE);
         if (rv != SECSuccess) {
    @@ -3564,17 +3849,58 @@ tls13_Read0RttData(sslSocket *ss, void *buf, PRInt32 len)
         return len;
     }
     
    +/* 0-RTT data will be followed by a different cipher spec; this resets the
    + * current spec to the null spec so that the following state can be set as
    + * though 0-RTT didn't happen. TODO: work out if this is the best plan. */
    +static void
    +tls13_SetNullCipherSpec(sslSocket *ss, ssl3CipherSpec **specp)
    +{
    +    PORT_Assert(ss->ssl3.hs.nullSpec);
    +
    +    ssl_GetSpecWriteLock(ss);
    +    tls13_CipherSpecRelease(*specp);
    +    *specp = ss->ssl3.hs.nullSpec;
    +    ssl_ReleaseSpecWriteLock(ss);
    +    ss->ssl3.hs.nullSpec = NULL;
    +}
    +
    +static SECStatus
    +tls13_SendEndOfEarlyData(sslSocket *ss)
    +{
    +    SECStatus rv;
    +
    +    SSL_TRC(3, ("%d: TLS13[%d]: send end_of_early_data extension",
    +                SSL_GETPID(), ss->fd));
    +
    +    rv = SSL3_SendAlert(ss, alert_warning, end_of_early_data);
    +    if (rv != SECSuccess) {
    +        FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error);
    +        return SECFailure;
    +    }
    +
    +    ss->ssl3.hs.zeroRttState = ssl_0rtt_done;
    +    return SECSuccess;
    +}
    +
     SECStatus
     tls13_HandleEndOfEarlyData(sslSocket *ss)
     {
         SECStatus rv;
     
    -    rv = TLS13_CHECK_HS_STATE(ss, SSL_ERROR_END_OF_EARLY_DATA_ALERT,
    -                              wait_0rtt_end_of_early_data);
    -    if (rv != SECSuccess) {
    +    if (ss->version < SSL_LIBRARY_VERSION_TLS_1_3 ||
    +        ss->ssl3.hs.zeroRttState != ssl_0rtt_accepted) {
    +        (void)SSL3_SendAlert(ss, alert_fatal, unexpected_message);
    +        PORT_SetError(SSL_ERROR_END_OF_EARLY_DATA_ALERT);
             return SECFailure;
         }
     
    +    PORT_Assert(TLS13_IN_HS_STATE(ss, ss->opt.requestCertificate ? wait_client_cert : wait_finished));
    +
    +    if (IS_DTLS(ss)) {
    +        /* Reset the cipher spec so that the epoch counter is properly reset. */
    +        tls13_SetNullCipherSpec(ss, &ss->ssl3.crSpec);
    +    }
    +
         rv = tls13_SetCipherSpec(ss, TrafficKeyHandshake,
                                  CipherSpecRead, PR_FALSE);
         if (rv != SECSuccess) {
    @@ -3582,9 +3908,7 @@ tls13_HandleEndOfEarlyData(sslSocket *ss)
             return SECFailure;
         }
     
    -    TLS13_SET_HS_STATE(ss, ss->opt.requestCertificate ? wait_client_cert
    -                                                      : wait_finished);
    -
    +    ss->ssl3.hs.zeroRttState = ssl_0rtt_done;
         return SECSuccess;
     }
     
    @@ -3595,8 +3919,8 @@ tls13_HandleEarlyApplicationData(sslSocket *ss, sslBuffer *origBuf)
         SECItem it = { siBuffer, NULL, 0 };
     
         PORT_Assert(ss->sec.isServer);
    -    PORT_Assert(ss->ssl3.hs.doing0Rtt);
    -    if (!ss->ssl3.hs.doing0Rtt) {
    +    PORT_Assert(ss->ssl3.hs.zeroRttState == ssl_0rtt_accepted);
    +    if (ss->ssl3.hs.zeroRttState != ssl_0rtt_accepted) {
             /* Belt and suspenders. */
             FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error);
             return SECFailure;
    diff --git a/security/nss/lib/ssl/tls13con.h b/security/nss/lib/ssl/tls13con.h
    index e11dab83cb79..58842d15f9ef 100644
    --- a/security/nss/lib/ssl/tls13con.h
    +++ b/security/nss/lib/ssl/tls13con.h
    @@ -50,7 +50,7 @@ SECStatus tls13_HandleServerHelloPart2(sslSocket *ss);
     SECStatus tls13_HandlePostHelloHandshakeMessage(sslSocket *ss, SSL3Opaque *b,
                                                     PRUint32 length,
                                                     SSL3Hashes *hashesPtr);
    -SECStatus tls13_HandleClientKeyShare(sslSocket *ss);
    +SECStatus tls13_HandleClientKeyShare(sslSocket *ss, PRBool *retry);
     SECStatus tls13_SendServerHelloSequence(sslSocket *ss);
     SECStatus tls13_HandleServerKeyShare(sslSocket *ss);
     void tls13_DestroyKeyShareEntry(TLS13KeyShareEntry *entry);