Bug 1488622 - land NSS a706ba3c4fa9 UPGRADE_NSS_RELEASE, r=me
authorJ.C. Jones <jjones@mozilla.com>
Fri, 28 Sep 2018 09:17:37 -0700
changeset 494529 3a5aa4e939536f27d4f3be418dd5e3e7bbe9130a
parent 494528 b114236a192ee88c2f3f50a7df4e425f189f28b7
child 494530 1681bd622c63ad7cf045cb70d487e0b538564d3f
push id9984
push userffxbld-merge
push dateMon, 15 Oct 2018 21:07:35 +0000
treeherdermozilla-beta@183d27ea8570 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersme
bugs1488622
milestone64.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1488622 - land NSS a706ba3c4fa9 UPGRADE_NSS_RELEASE, r=me
security/nss/TAG-INFO
security/nss/automation/clang-format/Dockerfile
security/nss/automation/clang-format/setup.sh
security/nss/automation/taskcluster/docker-aarch64/Dockerfile
security/nss/automation/taskcluster/docker-arm/Dockerfile
security/nss/automation/taskcluster/docker-builds/Dockerfile
security/nss/automation/taskcluster/docker-builds/bin/checkout.sh
security/nss/automation/taskcluster/docker-clang-3.9/Dockerfile
security/nss/automation/taskcluster/docker-clang-3.9/bin/checkout.sh
security/nss/automation/taskcluster/docker-clang-3.9/setup.sh
security/nss/automation/taskcluster/docker-clang-format/Dockerfile
security/nss/automation/taskcluster/docker-clang-format/bin/checkout.sh
security/nss/automation/taskcluster/docker-decision/Dockerfile
security/nss/automation/taskcluster/docker-decision/setup.sh
security/nss/automation/taskcluster/docker-fuzz/Dockerfile
security/nss/automation/taskcluster/docker-fuzz/setup.sh
security/nss/automation/taskcluster/docker-fuzz32/Dockerfile
security/nss/automation/taskcluster/docker-fuzz32/bin/checkout.sh
security/nss/automation/taskcluster/docker-gcc-4.4/Dockerfile
security/nss/automation/taskcluster/docker-gcc-4.4/setup.sh
security/nss/automation/taskcluster/docker-interop/Dockerfile
security/nss/automation/taskcluster/docker-interop/bin/checkout.sh
security/nss/automation/taskcluster/docker/Dockerfile
security/nss/automation/taskcluster/docker/setup.sh
security/nss/automation/taskcluster/graph/src/extend.js
security/nss/automation/taskcluster/graph/src/try_syntax.js
security/nss/automation/taskcluster/scripts/build_image.sh
security/nss/automation/taskcluster/scripts/tools.sh
security/nss/cmd/tstclnt/Makefile
security/nss/cmd/tstclnt/tstclnt.c
security/nss/coreconf/coreconf.dep
security/nss/gtests/pk11_gtest/pk11_cipherop_unittest.cc
security/nss/gtests/ssl_gtest/manifest.mn
security/nss/gtests/ssl_gtest/rsa8193.h
security/nss/gtests/ssl_gtest/ssl_extension_unittest.cc
security/nss/gtests/ssl_gtest/ssl_gtest.gyp
security/nss/gtests/ssl_gtest/ssl_loopback_unittest.cc
security/nss/gtests/ssl_gtest/ssl_resumption_unittest.cc
security/nss/gtests/ssl_gtest/tls_esni_unittest.cc
security/nss/gtests/ssl_gtest/tls_filter.cc
security/nss/gtests/ssl_gtest/tls_filter.h
security/nss/lib/freebl/ctr.c
security/nss/lib/ssl/SSLerrs.h
security/nss/lib/ssl/config.mk
security/nss/lib/ssl/manifest.mn
security/nss/lib/ssl/ssl.gyp
security/nss/lib/ssl/ssl3con.c
security/nss/lib/ssl/ssl3ecc.c
security/nss/lib/ssl/ssl3ext.c
security/nss/lib/ssl/ssl3ext.h
security/nss/lib/ssl/ssl3exthandle.c
security/nss/lib/ssl/ssl3exthandle.h
security/nss/lib/ssl/sslerr.h
security/nss/lib/ssl/sslexp.h
security/nss/lib/ssl/sslimpl.h
security/nss/lib/ssl/sslsecur.c
security/nss/lib/ssl/sslsock.c
security/nss/lib/ssl/sslspec.c
security/nss/lib/ssl/sslt.h
security/nss/lib/ssl/tls13con.c
security/nss/lib/ssl/tls13con.h
security/nss/lib/ssl/tls13esni.c
security/nss/lib/ssl/tls13esni.h
security/nss/lib/ssl/tls13exthandle.c
security/nss/lib/ssl/tls13exthandle.h
security/nss/lib/util/secitem.c
security/nss/lib/util/secitem.h
security/nss/tests/all.sh
security/nss/tests/chains/chains.sh
security/nss/tests/common/certsetup.sh
security/nss/tests/common/init.sh
security/nss/tests/interop/interop.sh
security/nss/tests/ssl_gtests/ssl_gtests.sh
security/nss/tests/tlsfuzzer/config.json.in
security/nss/tests/tlsfuzzer/tlsfuzzer.sh
--- a/security/nss/TAG-INFO
+++ b/security/nss/TAG-INFO
@@ -1,1 +1,1 @@
-2c85f81f9b5e
+a706ba3c4fa9
--- a/security/nss/automation/clang-format/Dockerfile
+++ b/security/nss/automation/clang-format/Dockerfile
@@ -1,26 +1,35 @@
-FROM ubuntu:16.04
-MAINTAINER Franziskus Kiefer <franziskuskiefer@gmail.com>
-
-RUN useradd -d /home/worker -s /bin/bash -m worker
-WORKDIR /home/worker
+# Minimal image with clang-format 3.9.
+FROM ubuntu:18.04
+LABEL maintainer="Martin Thomson <martin.thomson@gmail.com>"
 
-# Install dependencies.
-ADD setup.sh /tmp/setup.sh
-RUN bash /tmp/setup.sh
+RUN apt-get update \
+ && apt-get install -y --no-install-recommends \
+    ca-certificates \
+    clang-format-3.9 \
+    locales \
+    mercurial \
+ && rm -rf /var/lib/apt/lists/* \
+ && apt-get autoremove -y && apt-get clean -y
 
-# Change user.
-USER worker
+RUN update-alternatives --install /usr/bin/clang-format \
+    clang-format $(which clang-format-3.9) 10
 
-# Env variables.
-ENV HOME /home/worker
 ENV SHELL /bin/bash
 ENV USER worker
-ENV LOGNAME worker
+ENV LOGNAME $USER
+ENV HOME /home/$USER
 ENV HOSTNAME taskcluster-worker
 ENV LANG en_US.UTF-8
-ENV LC_ALL en_US.UTF-8
+ENV LC_ALL $LANG
 ENV HOST localhost
 ENV DOMSUF localdomain
 
-# Entrypoint.
+RUN locale-gen $LANG \
+ && DEBIAN_FRONTEND=noninteractive dpkg-reconfigure locales
+
+RUN useradd -d $HOME -s $SHELL -m $USER
+WORKDIR $HOME
+USER $USER
+
+# Entrypoint - which only works if /home/worker/nss is mounted.
 ENTRYPOINT ["/home/worker/nss/automation/clang-format/run_clang_format.sh"]
deleted file mode 100644
--- a/security/nss/automation/clang-format/setup.sh
+++ /dev/null
@@ -1,44 +0,0 @@
-#!/usr/bin/env bash
-
-set -v -e -x
-
-# Update packages.
-export DEBIAN_FRONTEND=noninteractive
-apt-get -y update && apt-get -y upgrade
-
-# Install packages.
-apt_packages=()
-apt_packages+=('ca-certificates')
-apt_packages+=('curl')
-apt_packages+=('xz-utils')
-apt_packages+=('mercurial')
-apt_packages+=('git')
-apt_packages+=('locales')
-apt-get install -y --no-install-recommends ${apt_packages[@]}
-
-# Download clang.
-curl -L https://releases.llvm.org/3.9.1/clang+llvm-3.9.1-x86_64-linux-gnu-ubuntu-16.04.tar.xz -o clang.tar.xz
-curl -L https://releases.llvm.org/3.9.1/clang+llvm-3.9.1-x86_64-linux-gnu-ubuntu-16.04.tar.xz.sig -o clang.tar.xz.sig
-# Verify the signature.
-gpg --keyserver pool.sks-keyservers.net --recv-keys B6C8F98282B944E3B0D5C2530FC3042E345AD05D
-gpg --verify clang.tar.xz.sig
-# Install into /usr/local/.
-tar xJvf *.tar.xz -C /usr/local --strip-components=1
-
-# Cleanup.
-function cleanup() {
-  rm -f clang.tar.xz clang.tar.xz.sig
-}
-trap cleanup ERR EXIT
-
-locale-gen en_US.UTF-8
-dpkg-reconfigure locales
-
-# Cleanup.
-rm -rf ~/.ccache ~/.cache
-apt-get autoremove -y
-apt-get clean
-apt-get autoclean
-
-# We're done. Remove this script.
-rm $0
--- a/security/nss/automation/taskcluster/docker-aarch64/Dockerfile
+++ b/security/nss/automation/taskcluster/docker-aarch64/Dockerfile
@@ -15,16 +15,15 @@ RUN bash /tmp/setup.sh
 # Change user.
 # USER worker # See bug 1347473.
 
 # Env variables.
 ENV HOME /home/worker
 ENV SHELL /bin/bash
 ENV USER worker
 ENV LOGNAME worker
-ENV HOSTNAME taskcluster-worker
 ENV LANG en_US.UTF-8
 ENV LC_ALL en_US.UTF-8
 ENV HOST localhost
 ENV DOMSUF localdomain
 
 # Set a default command for debugging.
 CMD ["/bin/bash", "--login"]
--- a/security/nss/automation/taskcluster/docker-arm/Dockerfile
+++ b/security/nss/automation/taskcluster/docker-arm/Dockerfile
@@ -12,16 +12,15 @@ RUN chmod +x /home/worker/bin/*
 ADD setup.sh /tmp/setup.sh
 RUN bash /tmp/setup.sh
 
 # Env variables.
 ENV HOME /home/worker
 ENV SHELL /bin/bash
 ENV USER worker
 ENV LOGNAME worker
-ENV HOSTNAME taskcluster-worker
 ENV LANG en_US.UTF-8
 ENV LC_ALL en_US.UTF-8
 ENV HOST localhost
 ENV DOMSUF localdomain
 
 # Set a default command for debugging.
 CMD ["/bin/bash", "--login"]
new file mode 100644
--- /dev/null
+++ b/security/nss/automation/taskcluster/docker-builds/Dockerfile
@@ -0,0 +1,75 @@
+# Dockerfile for building extra builds.  This includes more tools than the
+# default image, so it's a fair bit bigger.  Only use this for builds where
+# the smaller docker image is missing something.  These builds will run on
+# the leaner configuration.
+FROM ubuntu:18.04
+LABEL maintainer="Martin Thomson <martin.thomson@gmail.com>"
+
+RUN dpkg --add-architecture i386
+RUN apt-get update \
+ && apt-get install -y --no-install-recommends \
+    build-essential \
+    ca-certificates \
+    clang-4.0 \
+    clang \
+    cmake \
+    curl \
+    g++-4.8-multilib \
+    g++-5-multilib \
+    g++-6-multilib \
+    g++-multilib \
+    git \
+    gyp \
+    libelf-dev \
+    libdw-dev \
+    libssl-dev \
+    libssl-dev:i386 \
+    libxml2-utils \
+    lib32z1-dev \
+    linux-libc-dev:i386 \
+    llvm-dev \
+    locales \
+    mercurial \
+    ninja-build \
+    pkg-config \
+    valgrind \
+    zlib1g-dev \
+ && rm -rf /var/lib/apt/lists/* \
+ && apt-get autoremove -y && apt-get clean -y
+
+# Latest version of abigail-tools
+RUN apt-get update \
+ && apt-get install -y --no-install-recommends automake libtool libxml2-dev \
+ && git clone git://sourceware.org/git/libabigail.git /tmp/libabigail \
+ && cd /tmp/libabigail \
+ && autoreconf -fi \
+ && ./configure --prefix=/usr --disable-static --disable-apidoc --disable-manual \
+ && make && make install \
+ && rm -rf /tmp/libabigail \
+ && apt-get remove -y automake libtool libxml2-dev \
+ && rm -rf /var/lib/apt/lists/* \
+ && apt-get autoremove -y && apt-get clean -y
+
+ENV SHELL /bin/bash
+ENV USER worker
+ENV LOGNAME $USER
+ENV HOME /home/$USER
+ENV LANG en_US.UTF-8
+ENV LC_ALL $LANG
+ENV HOST localhost
+ENV DOMSUF localdomain
+
+RUN locale-gen $LANG \
+ && DEBIAN_FRONTEND=noninteractive dpkg-reconfigure locales
+
+RUN useradd -d $HOME -s $SHELL -m $USER
+WORKDIR $HOME
+
+# Add build and test scripts.
+ADD bin $HOME/bin
+RUN chmod +x $HOME/bin/*
+
+USER $USER
+
+# Set a default command for debugging.
+CMD ["/bin/bash", "--login"]
rename from security/nss/automation/taskcluster/docker-clang-3.9/bin/checkout.sh
rename to security/nss/automation/taskcluster/docker-builds/bin/checkout.sh
deleted file mode 100644
--- a/security/nss/automation/taskcluster/docker-clang-3.9/Dockerfile
+++ /dev/null
@@ -1,30 +0,0 @@
-FROM ubuntu:16.04
-MAINTAINER Tim Taubert <ttaubert@mozilla.com>
-
-RUN useradd -d /home/worker -s /bin/bash -m worker
-WORKDIR /home/worker
-
-# Add build and test scripts.
-ADD bin /home/worker/bin
-RUN chmod +x /home/worker/bin/*
-
-# Install dependencies.
-ADD setup.sh /tmp/setup.sh
-RUN bash /tmp/setup.sh
-
-# Change user.
-USER worker
-
-# Env variables.
-ENV HOME /home/worker
-ENV SHELL /bin/bash
-ENV USER worker
-ENV LOGNAME worker
-ENV HOSTNAME taskcluster-worker
-ENV LANG en_US.UTF-8
-ENV LC_ALL en_US.UTF-8
-ENV HOST localhost
-ENV DOMSUF localdomain
-
-# Set a default command for debugging.
-CMD ["/bin/bash", "--login"]
deleted file mode 100644
--- a/security/nss/automation/taskcluster/docker-clang-3.9/setup.sh
+++ /dev/null
@@ -1,46 +0,0 @@
-#!/usr/bin/env bash
-
-set -v -e -x
-
-# Update packages.
-export DEBIAN_FRONTEND=noninteractive
-apt-get -y update && apt-get -y upgrade
-
-# Need this to add keys for PPAs below.
-apt-get install -y --no-install-recommends apt-utils
-
-apt_packages=()
-apt_packages+=('ca-certificates')
-apt_packages+=('curl')
-apt_packages+=('locales')
-apt_packages+=('xz-utils')
-
-# Latest Mercurial.
-apt_packages+=('mercurial')
-apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 41BD8711B1F0EC2B0D85B91CF59CE3A8323293EE
-echo "deb http://ppa.launchpad.net/mercurial-ppa/releases/ubuntu xenial main" > /etc/apt/sources.list.d/mercurial.list
-
-# Install packages.
-apt-get -y update
-apt-get install -y --no-install-recommends ${apt_packages[@]}
-
-# Download clang.
-curl -LO https://releases.llvm.org/3.9.1/clang+llvm-3.9.1-x86_64-linux-gnu-ubuntu-16.04.tar.xz
-curl -LO https://releases.llvm.org/3.9.1/clang+llvm-3.9.1-x86_64-linux-gnu-ubuntu-16.04.tar.xz.sig
-# Verify the signature.
-gpg --keyserver pool.sks-keyservers.net --recv-keys B6C8F98282B944E3B0D5C2530FC3042E345AD05D
-gpg --verify *.tar.xz.sig
-# Install into /usr/local/.
-tar xJvf *.tar.xz -C /usr/local --strip-components=1
-# Cleanup.
-rm *.tar.xz*
-
-locale-gen en_US.UTF-8
-dpkg-reconfigure locales
-
-# Cleanup.
-rm -rf ~/.ccache ~/.cache
-apt-get autoremove -y
-apt-get clean
-apt-get autoclean
-rm $0
new file mode 100644
--- /dev/null
+++ b/security/nss/automation/taskcluster/docker-clang-format/Dockerfile
@@ -0,0 +1,38 @@
+# Minimal image with clang-format 3.9.
+FROM ubuntu:18.04
+LABEL maintainer="Martin Thomson <martin.thomson@gmail.com>"
+
+RUN apt-get update \
+ && apt-get install -y --no-install-recommends \
+    ca-certificates \
+    clang-format-3.9 \
+    locales \
+    mercurial \
+ && rm -rf /var/lib/apt/lists/* \
+ && apt-get autoremove -y && apt-get clean -y
+
+RUN update-alternatives --install /usr/bin/clang-format \
+    clang-format $(which clang-format-3.9) 10
+
+ENV SHELL /bin/bash
+ENV USER worker
+ENV LOGNAME $USER
+ENV HOME /home/$USER
+ENV LANG en_US.UTF-8
+ENV LC_ALL $LANG
+ENV HOST localhost
+ENV DOMSUF localdomain
+
+RUN locale-gen $LANG \
+ && DEBIAN_FRONTEND=noninteractive dpkg-reconfigure locales
+
+RUN useradd -d $HOME -s $SHELL -m $USER
+WORKDIR $HOME
+
+ADD bin $HOME/bin
+RUN chmod +x $HOME/bin/*
+
+USER $USER
+
+# Set a default command for debugging.
+CMD ["/bin/bash", "--login"]
copy from security/nss/automation/taskcluster/docker-clang-3.9/bin/checkout.sh
copy to security/nss/automation/taskcluster/docker-clang-format/bin/checkout.sh
--- a/security/nss/automation/taskcluster/docker-decision/Dockerfile
+++ b/security/nss/automation/taskcluster/docker-decision/Dockerfile
@@ -1,30 +1,37 @@
-FROM ubuntu:16.04
-MAINTAINER Tim Taubert <ttaubert@mozilla.com>
-
-RUN useradd -d /home/worker -s /bin/bash -m worker
-WORKDIR /home/worker
-
-# Add build and test scripts.
-ADD bin /home/worker/bin
-RUN chmod +x /home/worker/bin/*
+# Minimal image for running the decision task.
+FROM ubuntu:18.04
+LABEL maintainer="Martin Thomson <martin.thomson@gmail.com>"
 
-# Install dependencies.
-ADD setup.sh /tmp/setup.sh
-RUN bash /tmp/setup.sh
+RUN apt-get update \
+ && apt-get install -y --no-install-recommends \
+    ca-certificates \
+    curl \
+    locales \
+    mercurial \
+    nodejs \
+ && rm -rf /var/lib/apt/lists/* \
+ && apt-get autoremove -y && apt-get clean -y
 
-# Change user.
-USER worker
-
-# Env variables.
-ENV HOME /home/worker
 ENV SHELL /bin/bash
 ENV USER worker
-ENV LOGNAME worker
-ENV HOSTNAME taskcluster-worker
+ENV LOGNAME $USER
+ENV HOME /home/$USER
 ENV LANG en_US.UTF-8
-ENV LC_ALL en_US.UTF-8
+ENV LC_ALL $LANG
 ENV HOST localhost
 ENV DOMSUF localdomain
 
+RUN locale-gen $LANG \
+ && DEBIAN_FRONTEND=noninteractive dpkg-reconfigure locales
+
+RUN useradd -d $HOME -s $SHELL -m $USER
+WORKDIR $HOME
+
+# Add build and test scripts.
+ADD bin $HOME/bin
+RUN chmod +x $HOME/bin/*
+
+USER $USER
+
 # Set a default command for debugging.
 CMD ["/bin/bash", "--login"]
deleted file mode 100644
--- a/security/nss/automation/taskcluster/docker-decision/setup.sh
+++ /dev/null
@@ -1,31 +0,0 @@
-#!/usr/bin/env bash
-
-set -v -e -x
-
-# Update packages.
-export DEBIAN_FRONTEND=noninteractive
-apt-get -y update && apt-get -y upgrade
-
-# Need those to install newer packages below.
-apt-get install -y --no-install-recommends apt-utils curl ca-certificates locales
-
-# Latest Mercurial.
-apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 41BD8711B1F0EC2B0D85B91CF59CE3A8323293EE
-echo "deb http://ppa.launchpad.net/mercurial-ppa/releases/ubuntu xenial main" > /etc/apt/sources.list.d/mercurial.list
-
-# Install packages.
-apt-get -y update && apt-get install -y --no-install-recommends mercurial
-
-# Latest Node.JS.
-curl -sL https://deb.nodesource.com/setup_6.x | bash -
-apt-get install -y --no-install-recommends nodejs
-
-locale-gen en_US.UTF-8
-dpkg-reconfigure locales
-
-# Cleanup.
-rm -rf ~/.ccache ~/.cache
-apt-get autoremove -y
-apt-get clean
-apt-get autoclean
-rm $0
--- a/security/nss/automation/taskcluster/docker-fuzz/Dockerfile
+++ b/security/nss/automation/taskcluster/docker-fuzz/Dockerfile
@@ -1,33 +1,59 @@
-FROM ubuntu:16.04
-MAINTAINER Tim Taubert <ttaubert@mozilla.com>
-
-RUN useradd -d /home/worker -s /bin/bash -m worker
-WORKDIR /home/worker
-
-# Add build and test scripts.
-ADD bin /home/worker/bin
-RUN chmod +x /home/worker/bin/*
+# Dockerfile for running fuzzing tests.
+#
+# Note that when running this, you need to add `--cap-add SYS_PTRACE` to the
+# docker invocation or ASAN won't work.
+# On taskcluster use `features: ["allowPtrace"]`.
+# See https://github.com/google/sanitizers/issues/764#issuecomment-276700920
+FROM ubuntu:18.04
+LABEL maintainer="Martin Thomson <martin.thomson@gmail.com>"
 
-# Install dependencies.
-ADD setup.sh /tmp/setup.sh
-RUN bash /tmp/setup.sh
+RUN dpkg --add-architecture i386
+RUN apt-get update \
+ && apt-get install -y --no-install-recommends \
+    build-essential \
+    ca-certificates \
+    clang \
+    clang-tools \
+    curl \
+    g++-multilib \
+    git \
+    gyp \
+    libssl-dev \
+    libssl-dev:i386 \
+    libxml2-utils \
+    lib32z1-dev \
+    linux-libc-dev:i386 \
+    llvm-dev \
+    locales \
+    mercurial \
+    ninja-build \
+    pkg-config \
+    valgrind \
+    zlib1g-dev \
+ && rm -rf /var/lib/apt/lists/* \
+ && apt-get autoremove -y && apt-get clean -y
 
-# Change user.
-USER worker
-
-# Env variables.
-ENV HOME /home/worker
 ENV SHELL /bin/bash
 ENV USER worker
-ENV LOGNAME worker
-ENV HOSTNAME taskcluster-worker
+ENV LOGNAME $USER
+ENV HOME /home/$USER
 ENV LANG en_US.UTF-8
-ENV LC_ALL en_US.UTF-8
+ENV LC_ALL $LANG
 ENV HOST localhost
 ENV DOMSUF localdomain
 
-# LLVM 4.0
-ENV PATH "${PATH}:/home/worker/third_party/llvm-build/Release+Asserts/bin/"
+RUN locale-gen $LANG \
+ && DEBIAN_FRONTEND=noninteractive dpkg-reconfigure locales
+
+RUN useradd -d $HOME -s $SHELL -m $USER
+WORKDIR $HOME
+
+# Add build and test scripts.
+ADD bin $HOME/bin
+RUN chmod +x $HOME/bin/*
+
+# Change user.
+USER $USER
 
 # Set a default command for debugging.
 CMD ["/bin/bash", "--login"]
deleted file mode 100644
--- a/security/nss/automation/taskcluster/docker-fuzz/setup.sh
+++ /dev/null
@@ -1,58 +0,0 @@
-#!/usr/bin/env bash
-
-set -v -e -x
-
-# Update packages.
-export DEBIAN_FRONTEND=noninteractive
-apt-get -y update && apt-get -y upgrade
-
-# Need this to add keys for PPAs below.
-apt-get install -y --no-install-recommends apt-utils
-
-apt_packages=()
-apt_packages+=('build-essential')
-apt_packages+=('ca-certificates')
-apt_packages+=('curl')
-apt_packages+=('git')
-apt_packages+=('gyp')
-apt_packages+=('libssl-dev')
-apt_packages+=('libxml2-utils')
-apt_packages+=('locales')
-apt_packages+=('ninja-build')
-apt_packages+=('pkg-config')
-apt_packages+=('zlib1g-dev')
-
-# 32-bit builds
-apt_packages+=('gcc-multilib')
-apt_packages+=('g++-multilib')
-
-# Latest Mercurial.
-apt_packages+=('mercurial')
-apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 41BD8711B1F0EC2B0D85B91CF59CE3A8323293EE
-echo "deb http://ppa.launchpad.net/mercurial-ppa/releases/ubuntu xenial main" > /etc/apt/sources.list.d/mercurial.list
-
-# Install packages.
-apt-get -y update
-apt-get install -y --no-install-recommends ${apt_packages[@]}
-
-# 32-bit builds
-dpkg --add-architecture i386
-apt-get -y update
-apt-get install -y --no-install-recommends libssl-dev:i386
-
-# Install LLVM/clang-4.0.
-mkdir clang-tmp
-git clone -n --depth 1 https://chromium.googlesource.com/chromium/src/tools/clang clang-tmp/clang
-git -C clang-tmp/clang checkout HEAD scripts/update.py
-clang-tmp/clang/scripts/update.py
-rm -fr clang-tmp
-
-locale-gen en_US.UTF-8
-dpkg-reconfigure locales
-
-# Cleanup.
-rm -rf ~/.ccache ~/.cache
-apt-get autoremove -y
-apt-get clean
-apt-get autoclean
-rm $0
new file mode 100644
--- /dev/null
+++ b/security/nss/automation/taskcluster/docker-fuzz32/Dockerfile
@@ -0,0 +1,73 @@
+# Dockerfile for running fuzzing tests on linux32.
+#
+# This is a temporary workaround for bugs in clang that make it incompatible
+# with Ubuntu 18.04 (see bug 1488148). This image can be removed once a new
+# release of LLVM includes the necessary fixes.
+
+FROM ubuntu:16.04
+LABEL maintainer="Martin Thomson <martin.thomson@gmail.com>"
+
+RUN dpkg --add-architecture i386
+RUN apt-get update \
+ && apt-get install -y --no-install-recommends \
+    build-essential \
+    ca-certificates \
+    curl \
+    g++-multilib \
+    git \
+    gyp \
+    libssl-dev \
+    libssl-dev:i386 \
+    libxml2-utils \
+    lib32z1-dev \
+    linux-libc-dev:i386 \
+    locales \
+    mercurial \
+    ninja-build \
+    pkg-config \
+    software-properties-common \
+    valgrind \
+    zlib1g-dev \
+ && rm -rf /var/lib/apt/lists/* \
+ && apt-get autoremove -y && apt-get clean -y
+
+# Install clang and tools from the LLVM PPA.
+RUN curl -sf https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add - \
+ && apt-add-repository "deb http://apt.llvm.org/xenial/ llvm-toolchain-xenial-6.0 main" \
+ && apt-get update \
+ && apt-get install -y --no-install-recommends \
+    clang-6.0 \
+    clang-tools-6.0 \
+    llvm-6.0-dev \
+ && rm -rf /var/lib/apt/lists/* \
+ && apt-get autoremove -y && apt-get clean -y
+
+# Alias all the clang commands.
+RUN for i in $(dpkg -L clang-6.0 clang-tools-6.0 | grep '^/usr/bin/' | xargs -i basename {} -6.0); do \
+      update-alternatives --install "/usr/bin/$i" "$i" "/usr/bin/${i}-6.0" 10; \
+    done
+
+ENV SHELL /bin/bash
+ENV USER worker
+ENV LOGNAME $USER
+ENV HOME /home/$USER
+ENV LANG en_US.UTF-8
+ENV LC_ALL $LANG
+ENV HOST localhost
+ENV DOMSUF localdomain
+
+RUN locale-gen $LANG \
+ && DEBIAN_FRONTEND=noninteractive dpkg-reconfigure locales
+
+RUN useradd -d $HOME -s $SHELL -m $USER
+WORKDIR $HOME
+
+# Add build and test scripts.
+ADD bin $HOME/bin
+RUN chmod +x $HOME/bin/*
+
+# Change user.
+USER $USER
+
+# Set a default command for debugging.
+CMD ["/bin/bash", "--login"]
copy from security/nss/automation/taskcluster/docker-clang-3.9/bin/checkout.sh
copy to security/nss/automation/taskcluster/docker-fuzz32/bin/checkout.sh
--- a/security/nss/automation/taskcluster/docker-gcc-4.4/Dockerfile
+++ b/security/nss/automation/taskcluster/docker-gcc-4.4/Dockerfile
@@ -1,30 +1,39 @@
 FROM ubuntu:14.04
-MAINTAINER Tim Taubert <ttaubert@mozilla.com>
-
-RUN useradd -d /home/worker -s /bin/bash -m worker
-WORKDIR /home/worker
-
-# Add build and test scripts.
-ADD bin /home/worker/bin
-RUN chmod +x /home/worker/bin/*
+LABEL maintainer="Martin Thomson <martin.thomson@gmail.com>"
 
-# Install dependencies.
-ADD setup.sh /tmp/setup.sh
-RUN bash /tmp/setup.sh
+RUN dpkg --add-architecture i386
+RUN apt-get update \
+ && apt-get install -y --no-install-recommends \
+    ca-certificates \
+    g++-4.4 \
+    gcc-4.4 \
+    locales \
+    make \
+    mercurial \
+    zlib1g-dev \
+ && rm -rf /var/lib/apt/lists/* \
+ && apt-get autoremove -y && apt-get clean -y
 
-# Change user.
-USER worker
-
-# Env variables.
-ENV HOME /home/worker
 ENV SHELL /bin/bash
 ENV USER worker
-ENV LOGNAME worker
-ENV HOSTNAME taskcluster-worker
+ENV LOGNAME $USER
+ENV HOME /home/$USER
 ENV LANG en_US.UTF-8
-ENV LC_ALL en_US.UTF-8
+ENV LC_ALL $LANG
 ENV HOST localhost
 ENV DOMSUF localdomain
 
+RUN locale-gen $LANG \
+ && DEBIAN_FRONTEND=noninteractive dpkg-reconfigure locales
+
+RUN useradd -d $HOME -s $SHELL -m $USER
+WORKDIR $HOME
+
+# Add build and test scripts.
+ADD bin $HOME/bin
+RUN chmod +x $HOME/bin/*
+
+USER $USER
+
 # Set a default command for debugging.
 CMD ["/bin/bash", "--login"]
deleted file mode 100644
--- a/security/nss/automation/taskcluster/docker-gcc-4.4/setup.sh
+++ /dev/null
@@ -1,30 +0,0 @@
-#!/usr/bin/env bash
-
-set -v -e -x
-
-# Update packages.
-export DEBIAN_FRONTEND=noninteractive
-apt-get -y update && apt-get -y upgrade
-
-apt_packages=()
-apt_packages+=('ca-certificates')
-apt_packages+=('g++-4.4')
-apt_packages+=('gcc-4.4')
-apt_packages+=('locales')
-apt_packages+=('make')
-apt_packages+=('mercurial')
-apt_packages+=('zlib1g-dev')
-
-# Install packages.
-apt-get -y update
-apt-get install -y --no-install-recommends ${apt_packages[@]}
-
-locale-gen en_US.UTF-8
-dpkg-reconfigure locales
-
-# Cleanup.
-rm -rf ~/.ccache ~/.cache
-apt-get autoremove -y
-apt-get clean
-apt-get autoclean
-rm $0
new file mode 100644
--- /dev/null
+++ b/security/nss/automation/taskcluster/docker-interop/Dockerfile
@@ -0,0 +1,56 @@
+# Dockerfile for running interop tests.
+# This includes Rust, golang, and nodejs.
+FROM ubuntu:18.04
+LABEL maintainer="Martin Thomson <martin.thomson@gmail.com>"
+
+RUN dpkg --add-architecture i386
+RUN apt-get update \
+ && apt-get install -y --no-install-recommends \
+    build-essential \
+    ca-certificates \
+    clang \
+    cmake \
+    curl \
+    g++-multilib \
+    git \
+    golang \
+    gyp \
+    libxml2-utils \
+    lib32z1-dev \
+    linux-libc-dev:i386 \
+    llvm-dev \
+    locales \
+    mercurial \
+    ninja-build \
+    npm \
+    pkg-config \
+    zlib1g-dev \
+ && rm -rf /var/lib/apt/lists/* \
+ && apt-get autoremove -y && apt-get clean -y
+
+ENV SHELL /bin/bash
+ENV USER worker
+ENV LOGNAME $USER
+ENV HOME /home/$USER
+ENV LANG en_US.UTF-8
+ENV LC_ALL $LANG
+ENV HOST localhost
+ENV DOMSUF localdomain
+
+RUN locale-gen $LANG \
+ && DEBIAN_FRONTEND=noninteractive dpkg-reconfigure locales
+
+RUN useradd -d $HOME -s $SHELL -m $USER
+WORKDIR $HOME
+
+# Add build and test scripts.
+ADD bin $HOME/bin
+RUN chmod +x $HOME/bin/*
+
+USER $USER
+
+# Install Rust stable as $USER.
+RUN curl https://sh.rustup.rs -sSf | sh -s -- -y
+
+# Set a default command for debugging.
+CMD ["/bin/bash", "--login"]
copy from security/nss/automation/taskcluster/docker-clang-3.9/bin/checkout.sh
copy to security/nss/automation/taskcluster/docker-interop/bin/checkout.sh
--- a/security/nss/automation/taskcluster/docker/Dockerfile
+++ b/security/nss/automation/taskcluster/docker/Dockerfile
@@ -1,30 +1,49 @@
-FROM ubuntu:16.04
-MAINTAINER Tim Taubert <ttaubert@mozilla.com>
-
-RUN useradd -d /home/worker -s /bin/bash -m worker
-WORKDIR /home/worker
+# Lean image for running the bulk of the NSS CI tests on taskcluster.
+FROM ubuntu:18.04
+LABEL maintainer="Martin Thomson <martin.thomson@gmail.com>"
 
-# Add build and test scripts.
-ADD bin /home/worker/bin
-RUN chmod +x /home/worker/bin/*
+RUN dpkg --add-architecture i386
+RUN apt-get update \
+ && apt-get install -y --no-install-recommends \
+    build-essential \
+    ca-certificates \
+    clang \
+    curl \
+    g++-multilib \
+    git \
+    gyp \
+    libxml2-utils \
+    lib32z1-dev \
+    linux-libc-dev:i386 \
+    llvm-dev \
+    locales \
+    mercurial \
+    ninja-build \
+    pkg-config \
+    zlib1g-dev \
+ && rm -rf /var/lib/apt/lists/* \
+ && apt-get autoremove -y && apt-get clean -y
 
-# Install dependencies.
-ADD setup.sh /tmp/setup.sh
-RUN bash /tmp/setup.sh
-
-# Env variables.
-ENV HOME /home/worker
 ENV SHELL /bin/bash
 ENV USER worker
-ENV LOGNAME worker
-ENV HOSTNAME taskcluster-worker
+ENV LOGNAME $USER
+ENV HOME /home/$USER
 ENV LANG en_US.UTF-8
-ENV LC_ALL en_US.UTF-8
+ENV LC_ALL $LANG
 ENV HOST localhost
 ENV DOMSUF localdomain
 
-# Rust + Go
-ENV PATH "${PATH}:/home/worker/.cargo/bin/:/usr/lib/go-1.6/bin"
+RUN locale-gen $LANG \
+ && DEBIAN_FRONTEND=noninteractive dpkg-reconfigure locales
+
+RUN useradd -d $HOME -s $SHELL -m $USER
+WORKDIR $HOME
+
+# Add build and test scripts.
+ADD bin $HOME/bin
+RUN chmod +x $HOME/bin/*
+
+USER $USER
 
 # Set a default command for debugging.
 CMD ["/bin/bash", "--login"]
deleted file mode 100644
--- a/security/nss/automation/taskcluster/docker/setup.sh
+++ /dev/null
@@ -1,75 +0,0 @@
-#!/usr/bin/env bash
-
-set -v -e -x
-
-# Update packages.
-export DEBIAN_FRONTEND=noninteractive
-apt-get -y update && apt-get -y upgrade
-
-# Need this to add keys for PPAs below.
-apt-get install -y --no-install-recommends apt-utils
-
-apt_packages=()
-apt_packages+=('build-essential')
-apt_packages+=('ca-certificates')
-apt_packages+=('clang-5.0')
-apt_packages+=('curl')
-apt_packages+=('npm')
-apt_packages+=('git')
-apt_packages+=('golang-1.6')
-apt_packages+=('libxml2-utils')
-apt_packages+=('locales')
-apt_packages+=('ninja-build')
-apt_packages+=('pkg-config')
-apt_packages+=('zlib1g-dev')
-apt_packages+=('cmake')
-
-# 32-bit builds
-apt_packages+=('lib32z1-dev')
-apt_packages+=('gcc-multilib')
-apt_packages+=('g++-multilib')
-
-# ct-verif and sanitizers
-apt_packages+=('valgrind')
-
-# Latest Mercurial.
-apt_packages+=('mercurial')
-apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 41BD8711B1F0EC2B0D85B91CF59CE3A8323293EE
-echo "deb http://ppa.launchpad.net/mercurial-ppa/releases/ubuntu xenial main" > /etc/apt/sources.list.d/mercurial.list
-
-# gcc 4.8 and 6
-apt_packages+=('g++-6')
-apt_packages+=('g++-4.8')
-apt_packages+=('g++-6-multilib')
-apt_packages+=('g++-4.8-multilib')
-apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 60C317803A41BA51845E371A1E9377A2BA9EF27F
-echo "deb http://ppa.launchpad.net/ubuntu-toolchain-r/test/ubuntu xenial main" > /etc/apt/sources.list.d/toolchain.list
-
-# Install packages.
-apt-get -y update
-apt-get install -y --no-install-recommends ${apt_packages[@]}
-
-# Latest version of abigail-tools
-apt-get install -y libxml2-dev autoconf libelf-dev libdw-dev libtool
-git clone git://sourceware.org/git/libabigail.git
-cd ./libabigail
-autoreconf -fi
-./configure --prefix=/usr --disable-static --disable-apidoc --disable-manual
-make
-make install
-cd ..
-apt-get remove -y libxml2-dev autoconf libtool
-rm -rf libabigail
-
-# Install latest Rust (stable).
-su worker -c "curl https://sh.rustup.rs -sSf | sh -s -- -y"
-
-locale-gen en_US.UTF-8
-dpkg-reconfigure locales
-
-# Cleanup.
-rm -rf ~/.ccache ~/.cache
-apt-get autoremove -y
-apt-get clean
-apt-get autoclean
-rm $0
--- a/security/nss/automation/taskcluster/graph/src/extend.js
+++ b/security/nss/automation/taskcluster/graph/src/extend.js
@@ -5,31 +5,47 @@
 import merge from "./merge";
 import * as queue from "./queue";
 
 const LINUX_IMAGE = {
   name: "linux",
   path: "automation/taskcluster/docker"
 };
 
-const LINUX_CLANG39_IMAGE = {
-  name: "linux-clang-3.9",
-  path: "automation/taskcluster/docker-clang-3.9"
+const LINUX_BUILDS_IMAGE = {
+  name: "linux-builds",
+  path: "automation/taskcluster/docker-builds"
+};
+
+const LINUX_INTEROP_IMAGE = {
+  name: "linux-interop",
+  path: "automation/taskcluster/docker-interop"
+};
+
+const CLANG_FORMAT_IMAGE = {
+  name: "clang-format",
+  path: "automation/taskcluster/docker-clang-format"
 };
 
 const LINUX_GCC44_IMAGE = {
   name: "linux-gcc-4.4",
   path: "automation/taskcluster/docker-gcc-4.4"
 };
 
 const FUZZ_IMAGE = {
   name: "fuzz",
   path: "automation/taskcluster/docker-fuzz"
 };
 
+// Bug 1488148 - temporary image for fuzzing 32-bit builds.
+const FUZZ_IMAGE_32 = {
+  name: "fuzz32",
+  path: "automation/taskcluster/docker-fuzz32"
+};
+
 const HACL_GEN_IMAGE = {
   name: "hacl",
   path: "automation/taskcluster/docker-hacl"
 };
 
 const SAW_IMAGE = {
   name: "saw",
   path: "automation/taskcluster/docker-saw"
@@ -54,17 +70,17 @@ queue.filter(task => {
     }
 
     // Make modular builds only on Linux make.
     if (task.symbol == "modular" && task.collection != "make") {
       return false;
     }
   }
 
-  if (task.tests == "bogo" || task.tests == "interop") {
+  if (task.tests == "bogo" || task.tests == "interop" || task.tests == "tlsfuzzer") {
     // No windows
     if (task.platform == "windows2012-64" ||
         task.platform == "windows2012-32") {
       return false;
     }
 
     // No ARM; TODO: enable
     if (task.platform == "aarch64") {
@@ -84,17 +100,19 @@ queue.filter(task => {
 
   // Only old make builds have -Ddisable_libpkix=0 and can run chain tests.
   if (task.tests == "chains" && task.collection != "make") {
     return false;
   }
 
   if (task.group == "Test") {
     // Don't run test builds on old make platforms, and not for fips gyp.
-    if (task.collection == "make" || task.collection == "fips") {
+    // Disable on aarch64, see bug 1488331.
+    if (task.collection == "make" || task.collection == "fips"
+        || task.platform == "aarch64") {
       return false;
     }
   }
 
   // Don't run all additional hardware tests on ARM.
   if (task.group == "Cipher" && task.platform == "aarch64" && task.env &&
       (task.env.NSS_DISABLE_PCLMUL == "1" || task.env.NSS_DISABLE_HW_AES == "1"
        || task.env.NSS_DISABLE_AVX == "1")) {
@@ -188,18 +206,18 @@ export default async function main() {
     ],
   });
 
   await scheduleLinux("Linux 64 (ASan, debug)", {
     env: {
       UBSAN_OPTIONS: "print_stacktrace=1",
       NSS_DISABLE_ARENA_FREE_LIST: "1",
       NSS_DISABLE_UNLOAD: "1",
-      CC: "clang-5.0",
-      CCC: "clang++-5.0",
+      CC: "clang",
+      CCC: "clang++",
     },
     platform: "linux64",
     collection: "asan",
     image: LINUX_IMAGE,
     features: ["allowPtrace"],
   }, "--ubsan --asan");
 
   await scheduleLinux("Linux 64 (FIPS opt)", {
@@ -246,47 +264,47 @@ export default async function main() {
     image: "franziskus/nss-aarch64-ci",
     provisioner: "localprovisioner",
     workerType: "nss-aarch64",
     platform: "aarch64",
     maxRunTime: 7200
   };
 
   await scheduleLinux("Linux AArch64 (debug)",
-    merge({
+    merge(aarch64_base, {
       command: [
         "/bin/bash",
         "-c",
         "bin/checkout.sh && nss/automation/taskcluster/scripts/build_gyp.sh"
       ],
       collection: "debug",
-    }, aarch64_base)
+    })
   );
 
   await scheduleLinux("Linux AArch64 (opt)",
-    merge({
+    merge(aarch64_base, {
       command: [
         "/bin/bash",
         "-c",
         "bin/checkout.sh && nss/automation/taskcluster/scripts/build_gyp.sh --opt"
       ],
       collection: "opt",
-    }, aarch64_base)
+    })
   );
 
   await scheduleLinux("Linux AArch64 (debug, make)",
-    merge({
+    merge(aarch64_base, {
       env: {USE_64: "1"},
       command: [
          "/bin/bash",
          "-c",
          "bin/checkout.sh && nss/automation/taskcluster/scripts/build.sh"
       ],
       collection: "make",
-    }, aarch64_base)
+    })
   );
 
   await scheduleMac("Mac (opt)", {collection: "opt"}, "--opt");
   await scheduleMac("Mac (debug)", {collection: "debug"});
 }
 
 
 async function scheduleMac(name, base, args = "") {
@@ -298,34 +316,34 @@ async function scheduleMac(name, base, a
       HOST: "localhost",
     },
     provisioner: "localprovisioner",
     workerType: "nss-macos-10-12",
     platform: "mac"
   });
 
   // Build base definition.
-  let build_base = merge({
+  let build_base = merge(mac_base, {
     command: [
       MAC_CHECKOUT_CMD,
       ["bash", "-c",
        "nss/automation/taskcluster/scripts/build_gyp.sh", args]
     ],
     provisioner: "localprovisioner",
     workerType: "nss-macos-10-12",
     platform: "mac",
     maxRunTime: 7200,
     artifacts: [{
       expires: 24 * 7,
       type: "directory",
       path: "public"
     }],
     kind: "build",
     symbol: "B"
-  }, mac_base);
+  });
 
   // 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: [
@@ -346,34 +364,38 @@ async function scheduleMac(name, base, a
     ]
   }));
 
   return queue.submit();
 }
 
 /*****************************************************************************/
 
-async function scheduleLinux(name, base, args = "") {
-  // Build base definition.
-  let build_base = merge({
+async function scheduleLinux(name, overrides, args = "") {
+  // Construct a base definition.  This takes |overrides| second because
+  // callers expect to be able to overwrite the |command| key.
+  let base = merge({
     command: [
       "/bin/bash",
       "-c",
       "bin/checkout.sh && nss/automation/taskcluster/scripts/build_gyp.sh " + args
     ],
+  }, overrides);
+  // The base for building.
+  let build_base = merge(base, {
     artifacts: {
       public: {
         expires: 24 * 7,
         type: "directory",
         path: "/home/worker/artifacts"
       }
     },
     kind: "build",
-    symbol: "B"
-  }, base);
+    symbol: "B",
+  });
 
   // The task that builds NSPR+NSS.
   let task_build = queue.scheduleTask(merge(build_base, {name}));
 
   // Make builds run FIPS tests, which need an extra FIPS build.
   if (base.collection == "make") {
     let extra_build = queue.scheduleTask(merge(build_base, {
       env: { NSS_FORCE_FIPS: "1" },
@@ -429,24 +451,27 @@ async function scheduleLinux(name, 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);
+  let extra_base = merge(build_base, {
+    group: "Builds",
+    image: LINUX_BUILDS_IMAGE,
+  });
   queue.scheduleTask(merge(extra_base, {
-    name: `${name} w/ clang-5.0`,
+    name: `${name} w/ clang-4`,
     env: {
-      CC: "clang-5.0",
-      CCC: "clang++-5.0",
+      CC: "clang-4.0",
+      CCC: "clang++-4.0",
     },
-    symbol: "clang-5.0"
+    symbol: "clang-4"
   }));
 
   queue.scheduleTask(merge(extra_base, {
     name: `${name} w/ gcc-4.4`,
     image: LINUX_GCC44_IMAGE,
     env: {
       USE_64: "1",
       CC: "gcc-4.4",
@@ -469,36 +494,46 @@ async function scheduleLinux(name, base,
     env: {
       CC: "gcc-4.8",
       CCC: "g++-4.8"
     },
     symbol: "gcc-4.8"
   }));
 
   queue.scheduleTask(merge(extra_base, {
-    name: `${name} w/ gcc-6.1`,
+    name: `${name} w/ gcc-5`,
+    env: {
+      CC: "gcc-5",
+      CCC: "g++-5"
+    },
+    symbol: "gcc-5"
+  }));
+
+  queue.scheduleTask(merge(extra_base, {
+    name: `${name} w/ gcc-6`,
     env: {
       CC: "gcc-6",
       CCC: "g++-6"
     },
-    symbol: "gcc-6.1"
+    symbol: "gcc-6"
   }));
 
   queue.scheduleTask(merge(extra_base, {
     name: `${name} w/ modular builds`,
+    image: LINUX_IMAGE,
     env: {NSS_BUILD_MODULAR: "1"},
     command: [
       "/bin/bash",
       "-c",
       "bin/checkout.sh && nss/automation/taskcluster/scripts/build.sh",
     ],
     symbol: "modular"
   }));
 
-  await scheduleTestBuilds(merge(base, {group: "Test"}), args);
+  await scheduleTestBuilds(name + " Test", merge(base, {group: "Test"}), args);
 
   return queue.submit();
 }
 
 /*****************************************************************************/
 
 function scheduleFuzzingRun(base, name, target, max_len, symbol = null, corpus = null) {
   const MAX_FUZZ_TIME = 300;
@@ -529,33 +564,33 @@ async function scheduleFuzzing() {
     },
     features: ["allowPtrace"],
     platform: "linux64",
     collection: "fuzz",
     image: FUZZ_IMAGE
   };
 
   // Build base definition.
-  let build_base = merge({
+  let build_base = merge(base, {
     command: [
       "/bin/bash",
       "-c",
       "bin/checkout.sh && " +
       "nss/automation/taskcluster/scripts/build_gyp.sh -g -v --fuzz"
     ],
     artifacts: {
       public: {
         expires: 24 * 7,
         type: "directory",
         path: "/home/worker/artifacts"
       }
     },
     kind: "build",
     symbol: "B"
-  }, base);
+  });
 
   // The task that builds NSPR+NSS.
   let task_build = queue.scheduleTask(merge(build_base, {
     name: "Linux x64 (debug, fuzz)"
   }));
 
   // The task that builds NSPR+NSS (TLS fuzzing mode).
   let task_build_tls = queue.scheduleTask(merge(build_base, {
@@ -630,37 +665,37 @@ async function scheduleFuzzing32() {
       NSS_DISABLE_ARENA_FREE_LIST: "1",
       NSS_DISABLE_UNLOAD: "1",
       CC: "clang",
       CCC: "clang++"
     },
     features: ["allowPtrace"],
     platform: "linux32",
     collection: "fuzz",
-    image: FUZZ_IMAGE
+    image: FUZZ_IMAGE_32
   };
 
   // Build base definition.
-  let build_base = merge({
+  let build_base = merge(base, {
     command: [
       "/bin/bash",
       "-c",
       "bin/checkout.sh && " +
       "nss/automation/taskcluster/scripts/build_gyp.sh -g -v --fuzz -m32"
     ],
     artifacts: {
       public: {
         expires: 24 * 7,
         type: "directory",
         path: "/home/worker/artifacts"
       }
     },
     kind: "build",
     symbol: "B"
-  }, base);
+  });
 
   // The task that builds NSPR+NSS.
   let task_build = queue.scheduleTask(merge(build_base, {
     name: "Linux 32 (debug, fuzz)"
   }));
 
   // The task that builds NSPR+NSS (TLS fuzzing mode).
   let task_build_tls = queue.scheduleTask(merge(build_base, {
@@ -723,44 +758,51 @@ async function scheduleFuzzing32() {
   scheduleFuzzingRun(tls_fm_base, "DTLS Client", "dtls-client", 20000, "dtls-client");
   scheduleFuzzingRun(tls_fm_base, "DTLS Server", "dtls-server", 20000, "dtls-server");
 
   return queue.submit();
 }
 
 /*****************************************************************************/
 
-async function scheduleTestBuilds(base, args = "") {
+async function scheduleTestBuilds(name, base, args = "") {
   // Build base definition.
-  let build = merge({
+  let build = merge(base, {
     command: [
       "/bin/bash",
       "-c",
       "bin/checkout.sh && " +
       "nss/automation/taskcluster/scripts/build_gyp.sh -g -v --test --ct-verif " + args
     ],
     artifacts: {
       public: {
         expires: 24 * 7,
         type: "directory",
         path: "/home/worker/artifacts"
       }
     },
     kind: "build",
     symbol: "B",
-    name: "Linux 64 (debug, test)"
-  }, base);
+    name: `${name} build`,
+  });
+
+  // On linux we have a specialized build image for building.
+  if (build.platform === "linux32" || build.platform === "linux64") {
+    build = merge(build, {
+      image: LINUX_BUILDS_IMAGE,
+    });
+  }
 
   // The task that builds NSPR+NSS.
   let task_build = queue.scheduleTask(build);
 
   // Schedule tests.
   queue.scheduleTask(merge(base, {
     parent: task_build,
-    name: "mpi",
+    name: `${name} mpi tests`,
     command: [
       "/bin/bash",
       "-c",
       "bin/checkout.sh && nss/automation/taskcluster/scripts/run_tests.sh"
     ],
     tests: "mpi",
     cycle: "standard",
     symbol: "mpi",
@@ -768,17 +810,17 @@ async function scheduleTestBuilds(base, 
   }));
   queue.scheduleTask(merge(base, {
     parent: task_build,
     command: [
       "/bin/bash",
       "-c",
       "bin/checkout.sh && nss/automation/taskcluster/scripts/run_tests.sh"
     ],
-    name: "Gtests",
+    name: `${name} gtests`,
     symbol: "Gtest",
     tests: "gtests",
     cycle: "standard",
     kind: "test"
   }));
 
   return queue.submit();
 }
@@ -876,28 +918,39 @@ async function scheduleWindows(name, bas
   }));
 
   return queue.submit();
 }
 
 /*****************************************************************************/
 
 function scheduleTests(task_build, task_cert, test_base) {
-  test_base = merge({kind: "test"}, test_base);
+  test_base = merge(test_base, {kind: "test"});
 
   // 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", cycle: "standard"
   }));
   queue.scheduleTask(merge(no_cert_base, {
-    name: "Bogo tests", symbol: "Bogo", tests: "bogo", cycle: "standard"
+    name: "Bogo tests",
+    symbol: "Bogo",
+    tests: "bogo",
+    cycle: "standard",
+    image: LINUX_INTEROP_IMAGE,
   }));
   queue.scheduleTask(merge(no_cert_base, {
-    name: "Interop tests", symbol: "Interop", tests: "interop", cycle: "standard"
+    name: "Interop tests",
+    symbol: "Interop",
+    tests: "interop",
+    cycle: "standard",
+    image: LINUX_INTEROP_IMAGE,
+  }));
+  queue.scheduleTask(merge(no_cert_base, {
+    name: "tlsfuzzer tests", symbol: "tlsfuzzer", tests: "tlsfuzzer", cycle: "standard"
   }));
   queue.scheduleTask(merge(no_cert_base, {
     name: "Chains tests", symbol: "Chains", tests: "chains"
   }));
   queue.scheduleTask(merge(no_cert_base, {
     name: "Cipher tests", symbol: "Default", tests: "cipher", group: "Cipher"
   }));
   queue.scheduleTask(merge(no_cert_base, {
@@ -969,32 +1022,32 @@ function scheduleTests(task_build, task_
 /*****************************************************************************/
 
 async function scheduleTools() {
   let base = {
     platform: "nss-tools",
     kind: "test"
   };
 
-  //ABI check task
+  // ABI check task
   queue.scheduleTask(merge(base, {
     symbol: "abi",
     name: "abi",
-    image: LINUX_IMAGE,
+    image: LINUX_BUILDS_IMAGE,
     command: [
       "/bin/bash",
       "-c",
       "bin/checkout.sh && nss/automation/taskcluster/scripts/check_abi.sh"
     ],
   }));
 
   queue.scheduleTask(merge(base, {
-    symbol: "clang-format-3.9",
-    name: "clang-format-3.9",
-    image: LINUX_CLANG39_IMAGE,
+    symbol: "clang-format",
+    name: "clang-format",
+    image: CLANG_FORMAT_IMAGE,
     command: [
       "/bin/bash",
       "-c",
       "bin/checkout.sh && nss/automation/clang-format/run_clang_format.sh"
     ]
   }));
 
   queue.scheduleTask(merge(base, {
--- a/security/nss/automation/taskcluster/graph/src/try_syntax.js
+++ b/security/nss/automation/taskcluster/graph/src/try_syntax.js
@@ -32,17 +32,17 @@ function parseOptions(opts) {
   if (platforms.length == 0 && opts.platform != "none") {
     platforms = allPlatforms;
   }
 
   // Parse unit tests.
   let aliases = {"gtests": "gtest"};
   let allUnitTests = ["bogo", "crmf", "chains", "cipher", "db", "ec", "fips",
                       "gtest", "interop", "lowhash", "merge", "sdr", "smime", "tools",
-                      "ssl", "mpi", "scert", "spki", "policy"];
+                      "ssl", "mpi", "scert", "spki", "policy", "tlsfuzzer"];
   let unittests = intersect(opts.unittests.split(/\s*,\s*/).map(t => {
     return aliases[t] || t;
   }), allUnitTests);
 
   // If the given value is "all" run all tests.
   // If it's nonsense then don't run any tests.
   if (opts.unittests == "all") {
     unittests = allUnitTests;
--- a/security/nss/automation/taskcluster/scripts/build_image.sh
+++ b/security/nss/automation/taskcluster/scripts/build_image.sh
@@ -8,17 +8,17 @@ raise_error() {
    echo "[taskcluster-image-build:error] $1"
    exit 1
 }
 
 # Ensure that the PROJECT is specified so the image can be indexed
 test -n "$PROJECT" || raise_error "Project must be provided."
 test -n "$HASH" || raise_error "Context Hash must be provided."
 
-CONTEXT_PATH=/home/worker/nss/$CONTEXT_PATH
+CONTEXT_PATH="/home/worker/nss/$CONTEXT_PATH"
 
-test -d $CONTEXT_PATH || raise_error "Context Path $CONTEXT_PATH does not exist."
+test -d "$CONTEXT_PATH" || raise_error "Context Path $CONTEXT_PATH does not exist."
 test -f "$CONTEXT_PATH/Dockerfile" || raise_error "Dockerfile must be present in $CONTEXT_PATH."
 
-docker build -t $PROJECT:$HASH $CONTEXT_PATH
+docker build -t "$PROJECT:$HASH" "$CONTEXT_PATH"
 
 mkdir /artifacts
-docker save $PROJECT:$HASH > /artifacts/image.tar
+docker save "$PROJECT:$HASH" > /artifacts/image.tar
--- a/security/nss/automation/taskcluster/scripts/tools.sh
+++ b/security/nss/automation/taskcluster/scripts/tools.sh
@@ -1,18 +1,17 @@
 #!/usr/bin/env bash
 
 set -v -e -x
 
+# Assert that we're not running as root.
 if [[ $(id -u) -eq 0 ]]; then
-    # Stupid Docker. It works without sometimes... But not always.
-    echo "127.0.0.1 localhost.localdomain" >> /etc/hosts
-
-    # Drop privileges by re-running this script.
-    # Note: this mangles arguments, better to avoid running scripts as root.
+    # This exec is still needed until aarch64 images are updated (Bug 1488325).
+    # Remove when images are updated.  Until then, assert that things are good.
+    [[ $(uname -m) == aarch64 ]]
     exec su worker -c "$0 $*"
 fi
 
 export PATH="${PATH}:/home/worker/.cargo/bin/:/usr/lib/go-1.6/bin"
 
 # Usage: hg_clone repo dir [revision=@]
 hg_clone() {
     repo=$1
--- a/security/nss/cmd/tstclnt/Makefile
+++ b/security/nss/cmd/tstclnt/Makefile
@@ -1,10 +1,10 @@
 #! gmake
-# 
+#
 # 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/.
 
 #######################################################################
 # (1) Include initial platform-independent assignments (MANDATORY).   #
 #######################################################################
 
--- a/security/nss/cmd/tstclnt/tstclnt.c
+++ b/security/nss/cmd/tstclnt/tstclnt.c
@@ -23,16 +23,17 @@
 #include <errno.h>
 #include <fcntl.h>
 #include <stdarg.h>
 
 #include "nspr.h"
 #include "prio.h"
 #include "prnetdb.h"
 #include "nss.h"
+#include "nssb64.h"
 #include "ocsp.h"
 #include "ssl.h"
 #include "sslproto.h"
 #include "sslexp.h"
 #include "pk11func.h"
 #include "secmod.h"
 #include "plgetopt.h"
 #include "plstr.h"
@@ -219,17 +220,18 @@ PrintUsageHeader()
 {
     fprintf(stderr,
             "Usage:  %s -h host [-a 1st_hs_name ] [-a 2nd_hs_name ] [-p port]\n"
             "  [-D | -d certdir] [-C] [-b | -R root-module] \n"
             "  [-n nickname] [-Bafosvx] [-c ciphers] [-Y] [-Z]\n"
             "  [-V [min-version]:[max-version]] [-K] [-T] [-U]\n"
             "  [-r N] [-w passwd] [-W pwfile] [-q [-t seconds]]\n"
             "  [-I groups] [-J signatureschemes]\n"
-            "  [-A requestfile] [-L totalconnections] [-P {client,server}] [-Q]\n"
+            "  [-A requestfile] [-L totalconnections] [-P {client,server}]\n"
+            "  [-N encryptedSniKeys] [-Q]\n"
             "\n",
             progName);
 }
 
 static void
 PrintParameterUsage()
 {
     fprintf(stderr, "%-20s Send different SNI name. 1st_hs_name - at first\n"
@@ -303,16 +305,17 @@ PrintParameterUsage()
                     "%-20s ecdsa_secp521r1_sha512,\n"
                     "%-20s rsa_pss_rsae_sha256, rsa_pss_rsae_sha384, rsa_pss_rsae_sha512,\n"
                     "%-20s rsa_pss_pss_sha256, rsa_pss_pss_sha384, rsa_pss_pss_sha512,\n"
                     "%-20s dsa_sha1, dsa_sha256, dsa_sha384, dsa_sha512\n",
             "-J", "", "", "", "", "", "", "");
     fprintf(stderr, "%-20s Enable alternative TLS 1.3 handshake\n", "-X alt-server-hello");
     fprintf(stderr, "%-20s Use DTLS\n", "-P {client, server}");
     fprintf(stderr, "%-20s Exit after handshake\n", "-Q");
+    fprintf(stderr, "%-20s Encrypted SNI Keys\n", "-N");
 }
 
 static void
 Usage()
 {
     PrintUsageHeader();
     PrintParameterUsage();
     exit(1);
@@ -980,16 +983,17 @@ PRUint8 *zeroRttData;
 unsigned int zeroRttLen = 0;
 PRBool enableAltServerHello = PR_FALSE;
 PRBool useDTLS = PR_FALSE;
 PRBool actAsServer = PR_FALSE;
 PRBool stopAfterHandshake = PR_FALSE;
 PRBool requestToExit = PR_FALSE;
 char *versionString = NULL;
 PRBool handshakeComplete = PR_FALSE;
+char *encryptedSNIKeys = NULL;
 
 static int
 writeBytesToServer(PRFileDesc *s, const PRUint8 *buf, int nb)
 {
     SECStatus rv;
     const PRUint8 *bufp = buf;
     PRPollDesc pollDesc;
 
@@ -1419,16 +1423,36 @@ run()
         rv = SSL_SignatureSchemePrefSet(s, enabledSigSchemes, enabledSigSchemeCount);
         if (rv < 0) {
             SECU_PrintError(progName, "SSL_SignatureSchemePrefSet failed");
             error = 1;
             goto done;
         }
     }
 
+    if (encryptedSNIKeys) {
+        SECItem esniKeysBin = { siBuffer, NULL, 0 };
+
+        if (!NSSBase64_DecodeBuffer(NULL, &esniKeysBin, encryptedSNIKeys,
+                                    strlen(encryptedSNIKeys))) {
+            SECU_PrintError(progName, "ESNIKeys record is invalid base64");
+            error = 1;
+            goto done;
+        }
+
+        rv = SSL_EnableESNI(s, esniKeysBin.data, esniKeysBin.len,
+                            "dummy.invalid");
+        SECITEM_FreeItem(&esniKeysBin, PR_FALSE);
+        if (rv < 0) {
+            SECU_PrintError(progName, "SSL_EnableESNI failed");
+            error = 1;
+            goto done;
+        }
+    }
+
     serverCertAuth.dbHandle = CERT_GetDefaultCertDB();
 
     SSL_AuthCertificateHook(s, ownAuthCertificate, &serverCertAuth);
     if (override) {
         SSL_BadCertHook(s, ownBadCertHandler, NULL);
     }
     if (actAsServer) {
         rv = installServerCertificate(s, nickname);
@@ -1678,17 +1702,17 @@ main(int argc, char **argv)
         }
     }
 
     /* Note: 'B' was used in the past but removed in 3.28
      *       'z' was removed in 3.39
      * Please leave some time before reusing these.
      */
     optstate = PL_CreateOptState(argc, argv,
-                                 "46A:CDFGHI:J:KL:M:OP:QR:STUV:W:X:YZa:bc:d:fgh:m:n:op:qr:st:uvw:");
+                                 "46A:CDFGHI:J:KL:M:N:OP:QR:STUV:W:X:YZa:bc:d:fgh:m:n:op:qr:st:uvw:");
     while ((optstatus = PL_GetNextOpt(optstate)) == PL_OPT_OK) {
         switch (optstate->option) {
             case '?':
             default:
                 Usage();
                 break;
 
             case '4':
@@ -1755,16 +1779,20 @@ main(int argc, char **argv)
                     case 0:
                     default:
                         serverCertAuth.allowOCSPSideChannelData = PR_TRUE;
                         serverCertAuth.allowCRLSideChannelData = PR_TRUE;
                         break;
                 };
                 break;
 
+            case 'N':
+                encryptedSNIKeys = PORT_Strdup(optstate->value);
+                break;
+
             case 'P':
                 useDTLS = PR_TRUE;
                 if (!strcmp(optstate->value, "server")) {
                     actAsServer = 1;
                 } else {
                     if (strcmp(optstate->value, "client")) {
                         Usage();
                     }
@@ -2103,16 +2131,17 @@ done:
 
     PORT_Free((void *)requestFile);
     PORT_Free(hs1SniHostName);
     PORT_Free(hs2SniHostName);
     PORT_Free(nickname);
     PORT_Free(pwdata.data);
     PORT_Free(host);
     PORT_Free(zeroRttData);
+    PORT_Free(encryptedSNIKeys);
 
     if (enabledGroups) {
         PORT_Free(enabledGroups);
     }
     if (NSS_IsInitialized()) {
         SSL_ClearSessionCache();
         if (initializedServerSessionCache) {
             if (SSL_ShutdownServerSessionIDCache() != SECSuccess) {
--- a/security/nss/coreconf/coreconf.dep
+++ b/security/nss/coreconf/coreconf.dep
@@ -5,9 +5,8 @@
 
 /*
  * A dummy header file that is a dependency for all the object files.
  * Used to force a full recompilation of NSS in Mozilla's Tinderbox
  * depend builds.  See comments in rules.mk.
  */
 
 #error "Do not include this header file."
-
--- a/security/nss/gtests/pk11_gtest/pk11_cipherop_unittest.cc
+++ b/security/nss/gtests/pk11_gtest/pk11_cipherop_unittest.cc
@@ -5,68 +5,69 @@
 #include "gtest/gtest.h"
 
 #include <assert.h>
 #include <limits.h>
 #include <prinit.h>
 #include <nss.h>
 #include <pk11pub.h>
 
-static const size_t kKeyLen = 128/8;
+static const size_t kKeyLen = 128 / 8;
 
 namespace nss_test {
 
 //
 // The ciper tests using the bltest command cover a great deal of testing.
 // However, Bug 1489691 revealed a corner case which is covered here.
 // This test will make multiple calls to PK11_CipherOp using the same
 // cipher context with data that is not cipher block aligned.
 //
 
-static SECStatus GetBytes(PK11Context *ctx, uint8_t *bytes, size_t len)
-{
+static SECStatus GetBytes(PK11Context* ctx, uint8_t* bytes, size_t len) {
   std::vector<uint8_t> in(len, 0);
 
   int outlen;
   SECStatus rv = PK11_CipherOp(ctx, bytes, &outlen, len, &in[0], len);
   if (static_cast<size_t>(outlen) != len) {
     return SECFailure;
   }
   return rv;
 }
 
 TEST(Pkcs11CipherOp, SingleCtxMultipleUnalignedCipherOps) {
   PK11SlotInfo* slot;
   PK11SymKey* key;
   PK11Context* ctx;
 
-  NSSInitContext* globalctx = NSS_InitContext("", "", "", "", NULL,
-                    NSS_INIT_READONLY | NSS_INIT_NOCERTDB | NSS_INIT_NOMODDB |
-                      NSS_INIT_FORCEOPEN | NSS_INIT_NOROOTINIT);
+  NSSInitContext* globalctx =
+      NSS_InitContext("", "", "", "", NULL,
+                      NSS_INIT_READONLY | NSS_INIT_NOCERTDB | NSS_INIT_NOMODDB |
+                          NSS_INIT_FORCEOPEN | NSS_INIT_NOROOTINIT);
 
   const CK_MECHANISM_TYPE cipher = CKM_AES_CTR;
 
   slot = PK11_GetInternalSlot();
   ASSERT_TRUE(slot);
 
   // Use arbitrary bytes for the AES key
   uint8_t key_bytes[kKeyLen];
   for (size_t i = 0; i < kKeyLen; i++) {
     key_bytes[i] = i;
   }
 
-  SECItem keyItem = { siBuffer, key_bytes, kKeyLen };
+  SECItem keyItem = {siBuffer, key_bytes, kKeyLen};
 
   // The IV can be all zeros since we only encrypt once with
   // each AES key.
-  CK_AES_CTR_PARAMS param = { 128, {} };
-  SECItem paramItem = { siBuffer, reinterpret_cast<unsigned char*>(&param), sizeof(CK_AES_CTR_PARAMS) };
+  CK_AES_CTR_PARAMS param = {128, {}};
+  SECItem paramItem = {siBuffer, reinterpret_cast<unsigned char*>(&param),
+                       sizeof(CK_AES_CTR_PARAMS)};
 
-  key = PK11_ImportSymKey(slot, cipher, PK11_OriginUnwrap,
-                                        CKA_ENCRYPT, &keyItem, NULL);
+  key = PK11_ImportSymKey(slot, cipher, PK11_OriginUnwrap, CKA_ENCRYPT,
+                          &keyItem, NULL);
   ctx = PK11_CreateContextBySymKey(cipher, CKA_ENCRYPT, key, &paramItem);
   ASSERT_TRUE(key);
   ASSERT_TRUE(ctx);
 
   uint8_t outbuf[128];
   ASSERT_EQ(GetBytes(ctx, outbuf, 7), SECSuccess);
   ASSERT_EQ(GetBytes(ctx, outbuf, 17), SECSuccess);
 
--- a/security/nss/gtests/ssl_gtest/manifest.mn
+++ b/security/nss/gtests/ssl_gtest/manifest.mn
@@ -47,16 +47,17 @@ CPPSRCS = \
       ssl_versionpolicy_unittest.cc \
       selfencrypt_unittest.cc \
       test_io.cc \
       tls_agent.cc \
       tls_connect.cc \
       tls_hkdf_unittest.cc \
       tls_filter.cc \
       tls_protect.cc \
+      tls_esni_unittest.cc \
       $(NULL)
 
 INCLUDES += -I$(CORE_DEPTH)/gtests/google_test/gtest/include \
             -I$(CORE_DEPTH)/gtests/common \
             -I$(CORE_DEPTH)/cpputil
 
 REQUIRES = nspr nss libdbm gtest cpputil
 
--- a/security/nss/gtests/ssl_gtest/rsa8193.h
+++ b/security/nss/gtests/ssl_gtest/rsa8193.h
@@ -201,9 +201,9 @@ static const uint8_t rsa8193[] = {
     0x3a, 0x3c, 0xb7, 0x5f, 0xab, 0x1e, 0x51, 0x17, 0x4f, 0xec, 0xc1, 0x6d,
     0x82, 0x79, 0x8e, 0xba, 0x7c, 0x47, 0x8e, 0x99, 0x00, 0x17, 0x9e, 0xda,
     0x10, 0x42, 0x70, 0x25, 0x42, 0x84, 0xc8, 0xb1, 0x95, 0x56, 0xb2, 0x08,
     0xa0, 0x4f, 0xdc, 0xcd, 0x9e, 0x31, 0x4b, 0x0c, 0x0b, 0x03, 0x5d, 0x2c,
     0x26, 0xbc, 0xa9, 0x4b, 0x19, 0xdf, 0x90, 0x01, 0x9a, 0xe0, 0x06, 0x05,
     0x13, 0x34, 0x9d, 0x34, 0xb8, 0xef, 0x13, 0x3a, 0x20, 0xf5, 0x74, 0x02,
     0x70, 0x3b, 0x41, 0x60, 0x1f, 0x5e, 0x76, 0x0a, 0xb1, 0x17, 0xd5, 0xcf,
     0x79, 0xef, 0xf7, 0xab, 0xe7, 0xd6, 0x0f, 0xad, 0x85, 0x2c, 0x52, 0x67,
-    0xb5, 0xa0, 0x4a, 0xfd, 0xaf};
\ No newline at end of file
+    0xb5, 0xa0, 0x4a, 0xfd, 0xaf};
--- a/security/nss/gtests/ssl_gtest/ssl_extension_unittest.cc
+++ b/security/nss/gtests/ssl_gtest/ssl_extension_unittest.cc
@@ -39,38 +39,16 @@ class TlsExtensionTruncator : public Tls
     return CHANGE;
   }
 
  private:
   uint16_t extension_;
   size_t length_;
 };
 
-class TlsExtensionDamager : public TlsExtensionFilter {
- public:
-  TlsExtensionDamager(const std::shared_ptr<TlsAgent>& a, uint16_t extension,
-                      size_t index)
-      : TlsExtensionFilter(a), extension_(extension), index_(index) {}
-  virtual PacketFilter::Action FilterExtension(uint16_t extension_type,
-                                               const DataBuffer& input,
-                                               DataBuffer* output) {
-    if (extension_type != extension_) {
-      return KEEP;
-    }
-
-    *output = input;
-    output->data()[index_] += 73;  // Increment selected for maximum damage
-    return CHANGE;
-  }
-
- private:
-  uint16_t extension_;
-  size_t index_;
-};
-
 class TlsExtensionAppender : public TlsHandshakeFilter {
  public:
   TlsExtensionAppender(const std::shared_ptr<TlsAgent>& a,
                        uint8_t handshake_type, uint16_t ext, DataBuffer& data)
       : TlsHandshakeFilter(a, {handshake_type}), extension_(ext), data_(data) {}
 
   virtual PacketFilter::Action FilterHandshake(const HandshakeHeader& header,
                                                const DataBuffer& input,
@@ -606,34 +584,33 @@ TEST_F(TlsExtensionTest13Stream, WrongSe
   MakeTlsFilter<TlsExtensionReplacer>(server_, ssl_tls13_key_share_xtn, buf);
   client_->ExpectSendAlert(kTlsAlertIllegalParameter);
   server_->ExpectSendAlert(kTlsAlertBadRecordMac);
   ConnectExpectFail();
   EXPECT_EQ(SSL_ERROR_RX_MALFORMED_KEY_SHARE, client_->error_code());
   EXPECT_EQ(SSL_ERROR_BAD_MAC_READ, server_->error_code());
 }
 
-// TODO(ekr@rtfm.com): This is the wrong error code. See bug 1307269.
 TEST_F(TlsExtensionTest13Stream, UnknownServerKeyShare) {
   const uint16_t wrong_group = 0xffff;
 
   static const uint8_t key_share[] = {
       wrong_group >> 8,
       wrong_group & 0xff,  // Group we didn't offer.
       0x00,
       0x02,  // length = 2
       0x01,
       0x02};
   DataBuffer buf(key_share, sizeof(key_share));
   EnsureTlsSetup();
   MakeTlsFilter<TlsExtensionReplacer>(server_, ssl_tls13_key_share_xtn, buf);
-  client_->ExpectSendAlert(kTlsAlertMissingExtension);
+  client_->ExpectSendAlert(kTlsAlertIllegalParameter);
   server_->ExpectSendAlert(kTlsAlertBadRecordMac);
   ConnectExpectFail();
-  EXPECT_EQ(SSL_ERROR_MISSING_KEY_SHARE, client_->error_code());
+  EXPECT_EQ(SSL_ERROR_RX_MALFORMED_KEY_SHARE, client_->error_code());
   EXPECT_EQ(SSL_ERROR_BAD_MAC_READ, server_->error_code());
 }
 
 TEST_F(TlsExtensionTest13Stream, AddServerSignatureAlgorithmsOnResumption) {
   SetupForResume();
   DataBuffer empty;
   MakeTlsFilter<TlsExtensionInjector>(server_, ssl_signature_algorithms_xtn,
                                       empty);
--- a/security/nss/gtests/ssl_gtest/ssl_gtest.gyp
+++ b/security/nss/gtests/ssl_gtest/ssl_gtest.gyp
@@ -46,16 +46,17 @@
         'ssl_v2_client_hello_unittest.cc',
         'ssl_version_unittest.cc',
         'ssl_versionpolicy_unittest.cc',
         'test_io.cc',
         'tls_agent.cc',
         'tls_connect.cc',
         'tls_filter.cc',
         'tls_hkdf_unittest.cc',
+        'tls_esni_unittest.cc',
         'tls_protect.cc'
       ],
       'dependencies': [
         '<(DEPTH)/exports.gyp:nss_exports',
         '<(DEPTH)/lib/util/util.gyp:nssutil3',
         '<(DEPTH)/gtests/google_test/google_test.gyp:gtest',
         '<(DEPTH)/lib/smime/smime.gyp:smime',
         '<(DEPTH)/lib/ssl/ssl.gyp:ssl',
--- a/security/nss/gtests/ssl_gtest/ssl_loopback_unittest.cc
+++ b/security/nss/gtests/ssl_gtest/ssl_loopback_unittest.cc
@@ -315,16 +315,63 @@ TEST_F(TlsConnectStreamTls13, DropRecord
   auto filter = MakeTlsFilter<DropTlsRecord>(client_, 2);
   filter->EnableDecryption();
   Connect();
   client_->SendData(26, 26);  // This should be dropped, so it won't be counted.
   client_->ResetSentBytes();
   SendReceive();
 }
 
+// Check that a server can use 0.5 RTT if client authentication isn't enabled.
+TEST_P(TlsConnectTls13, WriteBeforeClientFinished) {
+  EnsureTlsSetup();
+  StartConnect();
+  client_->Handshake();  // ClientHello
+  server_->Handshake();  // ServerHello
+
+  server_->SendData(10);
+  client_->ReadBytes(10);  // Client should emit the Finished as a side-effect.
+  server_->Handshake();    // Server consumes the Finished.
+  CheckConnected();
+}
+
+// We don't allow 0.5 RTT if client authentication is requested.
+TEST_P(TlsConnectTls13, WriteBeforeClientFinishedClientAuth) {
+  client_->SetupClientAuth();
+  server_->RequestClientAuth(false);
+  StartConnect();
+  client_->Handshake();  // ClientHello
+  server_->Handshake();  // ServerHello
+
+  static const uint8_t data[] = {1, 2, 3};
+  EXPECT_GT(0, PR_Write(server_->ssl_fd(), data, sizeof(data)));
+  EXPECT_EQ(PR_WOULD_BLOCK_ERROR, PORT_GetError());
+
+  Handshake();
+  CheckConnected();
+  SendReceive();
+}
+
+// 0.5 RTT should fail with client authentication required.
+TEST_P(TlsConnectTls13, WriteBeforeClientFinishedClientAuthRequired) {
+  client_->SetupClientAuth();
+  server_->RequestClientAuth(true);
+  StartConnect();
+  client_->Handshake();  // ClientHello
+  server_->Handshake();  // ServerHello
+
+  static const uint8_t data[] = {1, 2, 3};
+  EXPECT_GT(0, PR_Write(server_->ssl_fd(), data, sizeof(data)));
+  EXPECT_EQ(PR_WOULD_BLOCK_ERROR, PORT_GetError());
+
+  Handshake();
+  CheckConnected();
+  SendReceive();
+}
+
 // The next two tests takes advantage of the fact that we
 // automatically read the first 1024 bytes, so if
 // we provide 1200 bytes, they overrun the read buffer
 // provided by the calling test.
 
 // DTLS should return an error.
 TEST_P(TlsConnectDatagram, ShortRead) {
   Connect();
--- a/security/nss/gtests/ssl_gtest/ssl_resumption_unittest.cc
+++ b/security/nss/gtests/ssl_gtest/ssl_resumption_unittest.cc
@@ -543,16 +543,47 @@ TEST_P(TlsConnectTls13, TestTls13ResumeN
   EXPECT_EQ(0U, cr_capture->buffer().len()) << "expect nothing captured yet";
 
   // Sanity check whether the client certificate matches the one
   // decrypted from ticket.
   ScopedCERTCertificate cert2(SSL_PeerCertificate(server_->ssl_fd()));
   EXPECT_TRUE(SECITEM_ItemsAreEqual(&cert1->derCert, &cert2->derCert));
 }
 
+// Here we test that 0.5 RTT is available at the server when resuming, even if
+// configured to request a client certificate.  The resumed handshake relies on
+// the authentication from the original handshake, so no certificate is
+// requested this time around.  The server can write before the handshake
+// completes because the PSK binder is sufficient authentication for the client.
+TEST_P(TlsConnectTls13, WriteBeforeHandshakeCompleteOnResumption) {
+  ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
+  client_->SetupClientAuth();
+  server_->RequestClientAuth(true);
+  Connect();
+  SendReceive();  // Absorb the session ticket.
+  ScopedCERTCertificate cert1(SSL_LocalCertificate(client_->ssl_fd()));
+
+  Reset();
+  ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
+  ExpectResumption(RESUME_TICKET);
+  server_->RequestClientAuth(false);
+  StartConnect();
+  client_->Handshake();  // ClientHello
+  server_->Handshake();  // ServerHello
+
+  server_->SendData(10);
+  client_->ReadBytes(10);  // Client should emit the Finished as a side-effect.
+  server_->Handshake();    // Server consumes the Finished.
+  CheckConnected();
+
+  // Check whether the client certificate matches the one from the ticket.
+  ScopedCERTCertificate cert2(SSL_PeerCertificate(server_->ssl_fd()));
+  EXPECT_TRUE(SECITEM_ItemsAreEqual(&cert1->derCert, &cert2->derCert));
+}
+
 // We need to enable different cipher suites at different times in the following
 // tests.  Those cipher suites need to be suited to the version.
 static uint16_t ChooseOneCipher(uint16_t version) {
   if (version >= SSL_LIBRARY_VERSION_TLS_1_3) {
     return TLS_AES_128_GCM_SHA256;
   }
   return TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA;
 }
new file mode 100644
--- /dev/null
+++ b/security/nss/gtests/ssl_gtest/tls_esni_unittest.cc
@@ -0,0 +1,453 @@
+/* -*- 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 <ctime>
+
+#include "secerr.h"
+#include "ssl.h"
+
+#include "gtest_utils.h"
+#include "tls_agent.h"
+#include "tls_connect.h"
+
+namespace nss_test {
+
+static const char* kDummySni("dummy.invalid");
+
+std::vector<uint16_t> kDefaultSuites = {TLS_AES_256_GCM_SHA384,
+                                        TLS_AES_128_GCM_SHA256};
+std::vector<uint16_t> kChaChaSuite = {TLS_CHACHA20_POLY1305_SHA256};
+std::vector<uint16_t> kBogusSuites = {0};
+std::vector<uint16_t> kTls12Suites = {
+    TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256};
+
+static void NamedGroup2ECParams(SSLNamedGroup group, SECItem* params) {
+  auto groupDef = ssl_LookupNamedGroup(group);
+  ASSERT_NE(nullptr, groupDef);
+
+  auto oidData = SECOID_FindOIDByTag(groupDef->oidTag);
+  ASSERT_NE(nullptr, oidData);
+  ASSERT_NE(nullptr,
+            SECITEM_AllocItem(nullptr, params, (2 + oidData->oid.len)));
+
+  /*
+   * params->data needs to contain the ASN encoding of an object ID (OID)
+   * representing the named curve. The actual OID is in
+   * oidData->oid.data so we simply prepend 0x06 and OID length
+   */
+  params->data[0] = SEC_ASN1_OBJECT_ID;
+  params->data[1] = oidData->oid.len;
+  memcpy(params->data + 2, oidData->oid.data, oidData->oid.len);
+}
+
+/* Checksum is a 4-byte array. */
+static void UpdateEsniKeysChecksum(DataBuffer* buf) {
+  SECStatus rv;
+  PRUint8 sha256[32];
+
+  /* Stomp the checksum. */
+  PORT_Memset(buf->data() + 2, 0, 4);
+
+  rv = PK11_HashBuf(ssl3_HashTypeToOID(ssl_hash_sha256), sha256, buf->data(),
+                    buf->len());
+  ASSERT_EQ(SECSuccess, rv);
+  buf->Write(2, sha256, 4);
+}
+
+static void GenerateEsniKey(time_t windowStart, SSLNamedGroup group,
+                            std::vector<uint16_t>& cipher_suites,
+                            DataBuffer* record,
+                            ScopedSECKEYPublicKey* pubKey = nullptr,
+                            ScopedSECKEYPrivateKey* privKey = nullptr) {
+  SECKEYECParams ecParams = {siBuffer, NULL, 0};
+  NamedGroup2ECParams(group, &ecParams);
+
+  SECKEYPublicKey* pub = nullptr;
+  SECKEYPrivateKey* priv = SECKEY_CreateECPrivateKey(&ecParams, &pub, nullptr);
+  ASSERT_NE(nullptr, priv);
+  SECITEM_FreeItem(&ecParams, PR_FALSE);
+  PRUint8 encoded[1024];
+  unsigned int encoded_len;
+
+  SECStatus rv = SSL_EncodeESNIKeys(
+      &cipher_suites[0], cipher_suites.size(), group, pub, 100, windowStart,
+      windowStart + 10, encoded, &encoded_len, sizeof(encoded));
+  ASSERT_EQ(SECSuccess, rv);
+  ASSERT_GT(encoded_len, 0U);
+
+  if (pubKey) {
+    pubKey->reset(pub);
+  } else {
+    SECKEY_DestroyPublicKey(pub);
+  }
+  if (privKey) {
+    privKey->reset(priv);
+  } else {
+    SECKEY_DestroyPrivateKey(priv);
+  }
+  record->Truncate(0);
+  record->Write(0, encoded, encoded_len);
+}
+
+static void SetupEsni(const std::shared_ptr<TlsAgent>& client,
+                      const std::shared_ptr<TlsAgent>& server,
+                      SSLNamedGroup group = ssl_grp_ec_curve25519) {
+  ScopedSECKEYPublicKey pub;
+  ScopedSECKEYPrivateKey priv;
+  DataBuffer record;
+
+  GenerateEsniKey(time(nullptr), ssl_grp_ec_curve25519, kDefaultSuites, &record,
+                  &pub, &priv);
+  SECStatus rv = SSL_SetESNIKeyPair(server->ssl_fd(), priv.get(), record.data(),
+                                    record.len());
+  ASSERT_EQ(SECSuccess, rv);
+
+  rv = SSL_EnableESNI(client->ssl_fd(), record.data(), record.len(), kDummySni);
+  ASSERT_EQ(SECSuccess, rv);
+}
+
+static void CheckSniExtension(const DataBuffer& data) {
+  TlsParser parser(data.data(), data.len());
+  uint32_t tmp;
+  ASSERT_TRUE(parser.Read(&tmp, 2));
+  ASSERT_EQ(parser.remaining(), tmp);
+  ASSERT_TRUE(parser.Read(&tmp, 1));
+  ASSERT_EQ(0U, tmp); /* sni_nametype_hostname */
+  DataBuffer name;
+  ASSERT_TRUE(parser.ReadVariable(&name, 2));
+  ASSERT_EQ(0U, parser.remaining());
+  DataBuffer expected(reinterpret_cast<const uint8_t*>(kDummySni),
+                      strlen(kDummySni));
+  ASSERT_EQ(expected, name);
+}
+
+static void ClientInstallEsni(std::shared_ptr<TlsAgent>& agent,
+                              const DataBuffer& record, PRErrorCode err = 0) {
+  SECStatus rv =
+      SSL_EnableESNI(agent->ssl_fd(), record.data(), record.len(), kDummySni);
+  if (err == 0) {
+    ASSERT_EQ(SECSuccess, rv);
+  } else {
+    ASSERT_EQ(SECFailure, rv);
+    ASSERT_EQ(err, PORT_GetError());
+  }
+}
+
+TEST_P(TlsAgentTestClient13, EsniInstall) {
+  EnsureInit();
+  DataBuffer record;
+  GenerateEsniKey(time(0), ssl_grp_ec_curve25519, kDefaultSuites, &record);
+  ClientInstallEsni(agent_, record);
+}
+
+// The next set of tests fail at setup time.
+TEST_P(TlsAgentTestClient13, EsniInvalidHash) {
+  EnsureInit();
+  DataBuffer record;
+  GenerateEsniKey(time(0), ssl_grp_ec_curve25519, kDefaultSuites, &record);
+  record.data()[2]++;
+  ClientInstallEsni(agent_, record, SSL_ERROR_RX_MALFORMED_ESNI_KEYS);
+}
+
+TEST_P(TlsAgentTestClient13, EsniInvalidVersion) {
+  EnsureInit();
+  DataBuffer record;
+  GenerateEsniKey(time(0), ssl_grp_ec_curve25519, kDefaultSuites, &record);
+  record.Write(0, 0xffff, 2);
+  ClientInstallEsni(agent_, record, SSL_ERROR_UNSUPPORTED_VERSION);
+}
+
+TEST_P(TlsAgentTestClient13, EsniShort) {
+  EnsureInit();
+  DataBuffer record;
+  GenerateEsniKey(time(0), ssl_grp_ec_curve25519, kDefaultSuites, &record);
+  record.Truncate(record.len() - 1);
+  UpdateEsniKeysChecksum(&record);
+  ClientInstallEsni(agent_, record, SSL_ERROR_RX_MALFORMED_ESNI_KEYS);
+}
+
+TEST_P(TlsAgentTestClient13, EsniLong) {
+  EnsureInit();
+  DataBuffer record;
+  GenerateEsniKey(time(0), ssl_grp_ec_curve25519, kDefaultSuites, &record);
+  record.Write(record.len(), 1, 1);
+  UpdateEsniKeysChecksum(&record);
+  ClientInstallEsni(agent_, record, SSL_ERROR_RX_MALFORMED_ESNI_KEYS);
+}
+
+TEST_P(TlsAgentTestClient13, EsniExtensionMismatch) {
+  EnsureInit();
+  DataBuffer record;
+  GenerateEsniKey(time(0), ssl_grp_ec_curve25519, kDefaultSuites, &record);
+  record.Write(record.len() - 1, 1, 1);
+  UpdateEsniKeysChecksum(&record);
+  ClientInstallEsni(agent_, record, SSL_ERROR_RX_MALFORMED_ESNI_KEYS);
+}
+
+// The following tests fail by ignoring the Esni block.
+TEST_P(TlsAgentTestClient13, EsniUnknownGroup) {
+  EnsureInit();
+  DataBuffer record;
+  GenerateEsniKey(time(0), ssl_grp_ec_curve25519, kDefaultSuites, &record);
+  record.Write(8, 0xffff, 2);  // Fake group
+  UpdateEsniKeysChecksum(&record);
+  ClientInstallEsni(agent_, record, 0);
+  auto filter =
+      MakeTlsFilter<TlsExtensionCapture>(agent_, ssl_tls13_encrypted_sni_xtn);
+  agent_->Handshake();
+  ASSERT_EQ(TlsAgent::STATE_CONNECTING, agent_->state());
+  ASSERT_TRUE(!filter->captured());
+}
+
+TEST_P(TlsAgentTestClient13, EsniUnknownCS) {
+  EnsureInit();
+  DataBuffer record;
+  GenerateEsniKey(time(0), ssl_grp_ec_curve25519, kBogusSuites, &record);
+  ClientInstallEsni(agent_, record, 0);
+  auto filter =
+      MakeTlsFilter<TlsExtensionCapture>(agent_, ssl_tls13_encrypted_sni_xtn);
+  agent_->Handshake();
+  ASSERT_EQ(TlsAgent::STATE_CONNECTING, agent_->state());
+  ASSERT_TRUE(!filter->captured());
+}
+
+TEST_P(TlsAgentTestClient13, EsniInvalidCS) {
+  EnsureInit();
+  DataBuffer record;
+  GenerateEsniKey(time(0), ssl_grp_ec_curve25519, kTls12Suites, &record);
+  UpdateEsniKeysChecksum(&record);
+  ClientInstallEsni(agent_, record, 0);
+  auto filter =
+      MakeTlsFilter<TlsExtensionCapture>(agent_, ssl_tls13_encrypted_sni_xtn);
+  agent_->Handshake();
+  ASSERT_EQ(TlsAgent::STATE_CONNECTING, agent_->state());
+  ASSERT_TRUE(!filter->captured());
+}
+
+TEST_P(TlsAgentTestClient13, EsniNotReady) {
+  EnsureInit();
+  DataBuffer record;
+  GenerateEsniKey(time(0) + 1000, ssl_grp_ec_curve25519, kDefaultSuites,
+                  &record);
+  ClientInstallEsni(agent_, record, 0);
+  auto filter =
+      MakeTlsFilter<TlsExtensionCapture>(agent_, ssl_tls13_encrypted_sni_xtn);
+  agent_->Handshake();
+  ASSERT_TRUE(!filter->captured());
+}
+
+TEST_P(TlsAgentTestClient13, EsniExpired) {
+  EnsureInit();
+  DataBuffer record;
+  GenerateEsniKey(time(0) - 1000, ssl_grp_ec_curve25519, kDefaultSuites,
+                  &record);
+  ClientInstallEsni(agent_, record, 0);
+  auto filter =
+      MakeTlsFilter<TlsExtensionCapture>(agent_, ssl_tls13_encrypted_sni_xtn);
+  agent_->Handshake();
+  ASSERT_TRUE(!filter->captured());
+}
+
+TEST_P(TlsAgentTestClient13, NoSniSoNoEsni) {
+  EnsureInit();
+  DataBuffer record;
+  GenerateEsniKey(time(0), ssl_grp_ec_curve25519, kDefaultSuites, &record);
+  SSL_SetURL(agent_->ssl_fd(), "");
+  ClientInstallEsni(agent_, record, 0);
+  auto filter =
+      MakeTlsFilter<TlsExtensionCapture>(agent_, ssl_tls13_encrypted_sni_xtn);
+  agent_->Handshake();
+  ASSERT_TRUE(!filter->captured());
+}
+
+static int32_t SniCallback(TlsAgent* agent, const SECItem* srvNameAddr,
+                           PRUint32 srvNameArrSize) {
+  EXPECT_EQ(1U, srvNameArrSize);
+  SECItem expected = {
+      siBuffer, reinterpret_cast<unsigned char*>(const_cast<char*>("server")),
+      6};
+  EXPECT_TRUE(!SECITEM_CompareItem(&expected, &srvNameAddr[0]));
+  return SECSuccess;
+}
+
+TEST_P(TlsConnectTls13, ConnectEsni) {
+  EnsureTlsSetup();
+  SetupEsni(client_, server_);
+  auto cFilterSni =
+      MakeTlsFilter<TlsExtensionCapture>(client_, ssl_server_name_xtn);
+  auto cFilterEsni =
+      MakeTlsFilter<TlsExtensionCapture>(client_, ssl_tls13_encrypted_sni_xtn);
+  client_->SetFilter(std::make_shared<ChainedPacketFilter>(
+      ChainedPacketFilterInit({cFilterSni, cFilterEsni})));
+  auto sfilter =
+      MakeTlsFilter<TlsExtensionCapture>(server_, ssl_server_name_xtn);
+  sfilter->EnableDecryption();
+  server_->SetSniCallback(SniCallback);
+  Connect();
+  CheckSniExtension(cFilterSni->extension());
+  ASSERT_TRUE(cFilterEsni->captured());
+  // Check that our most preferred suite got chosen.
+  uint32_t suite;
+  ASSERT_TRUE(cFilterEsni->extension().Read(0, 2, &suite));
+  ASSERT_EQ(TLS_AES_128_GCM_SHA256, static_cast<PRUint16>(suite));
+  ASSERT_TRUE(!sfilter->captured());
+}
+
+TEST_P(TlsConnectTls13, ConnectEsniHrr) {
+  EnsureTlsSetup();
+  const std::vector<SSLNamedGroup> groups = {ssl_grp_ec_secp384r1};
+  server_->ConfigNamedGroups(groups);
+  SetupEsni(client_, server_);
+  auto hrr_capture = MakeTlsFilter<TlsHandshakeRecorder>(
+      server_, kTlsHandshakeHelloRetryRequest);
+  auto filter =
+      MakeTlsFilter<TlsExtensionCapture>(client_, ssl_server_name_xtn);
+  auto cfilter =
+      MakeTlsFilter<TlsExtensionCapture>(client_, ssl_server_name_xtn);
+  server_->SetSniCallback(SniCallback);
+  Connect();
+  CheckSniExtension(cfilter->extension());
+  EXPECT_NE(0UL, hrr_capture->buffer().len());
+}
+
+TEST_P(TlsConnectTls13, ConnectEsniNoDummy) {
+  EnsureTlsSetup();
+  ScopedSECKEYPublicKey pub;
+  ScopedSECKEYPrivateKey priv;
+  DataBuffer record;
+
+  GenerateEsniKey(time(nullptr), ssl_grp_ec_curve25519, kDefaultSuites, &record,
+                  &pub, &priv);
+  SECStatus rv = SSL_SetESNIKeyPair(server_->ssl_fd(), priv.get(),
+                                    record.data(), record.len());
+  ASSERT_EQ(SECSuccess, rv);
+  rv = SSL_EnableESNI(client_->ssl_fd(), record.data(), record.len(), "");
+  ASSERT_EQ(SECSuccess, rv);
+
+  auto cfilter =
+      MakeTlsFilter<TlsExtensionCapture>(client_, ssl_server_name_xtn);
+  auto sfilter =
+      MakeTlsFilter<TlsExtensionCapture>(server_, ssl_server_name_xtn);
+  server_->SetSniCallback(SniCallback);
+  Connect();
+  ASSERT_TRUE(!cfilter->captured());
+  ASSERT_TRUE(!sfilter->captured());
+}
+
+TEST_P(TlsConnectTls13, ConnectEsniNullDummy) {
+  EnsureTlsSetup();
+  ScopedSECKEYPublicKey pub;
+  ScopedSECKEYPrivateKey priv;
+  DataBuffer record;
+
+  GenerateEsniKey(time(nullptr), ssl_grp_ec_curve25519, kDefaultSuites, &record,
+                  &pub, &priv);
+  SECStatus rv = SSL_SetESNIKeyPair(server_->ssl_fd(), priv.get(),
+                                    record.data(), record.len());
+  ASSERT_EQ(SECSuccess, rv);
+  rv = SSL_EnableESNI(client_->ssl_fd(), record.data(), record.len(), nullptr);
+  ASSERT_EQ(SECSuccess, rv);
+
+  auto cfilter =
+      MakeTlsFilter<TlsExtensionCapture>(client_, ssl_server_name_xtn);
+  auto sfilter =
+      MakeTlsFilter<TlsExtensionCapture>(server_, ssl_server_name_xtn);
+  server_->SetSniCallback(SniCallback);
+  Connect();
+  ASSERT_TRUE(!cfilter->captured());
+  ASSERT_TRUE(!sfilter->captured());
+}
+
+/* Tell the client that it supports AES but the server that it supports ChaCha
+ */
+TEST_P(TlsConnectTls13, ConnectEsniCSMismatch) {
+  EnsureTlsSetup();
+  ScopedSECKEYPublicKey pub;
+  ScopedSECKEYPrivateKey priv;
+  DataBuffer record;
+
+  GenerateEsniKey(time(nullptr), ssl_grp_ec_curve25519, kDefaultSuites, &record,
+                  &pub, &priv);
+  PRUint8 encoded[1024];
+  unsigned int encoded_len;
+
+  SECStatus rv = SSL_EncodeESNIKeys(
+      &kChaChaSuite[0], kChaChaSuite.size(), ssl_grp_ec_curve25519, pub.get(),
+      100, time(0), time(0) + 10, encoded, &encoded_len, sizeof(encoded));
+  rv = SSL_SetESNIKeyPair(server_->ssl_fd(), priv.get(), encoded, encoded_len);
+  ASSERT_EQ(SECSuccess, rv);
+  rv = SSL_EnableESNI(client_->ssl_fd(), record.data(), record.len(), "");
+  ASSERT_EQ(SECSuccess, rv);
+  ConnectExpectAlert(server_, illegal_parameter);
+  server_->CheckErrorCode(SSL_ERROR_RX_MALFORMED_CLIENT_HELLO);
+}
+
+TEST_P(TlsConnectTls13, ConnectEsniP256) {
+  EnsureTlsSetup();
+  SetupEsni(client_, server_, ssl_grp_ec_secp256r1);
+  auto cfilter =
+      MakeTlsFilter<TlsExtensionCapture>(client_, ssl_server_name_xtn);
+  auto sfilter =
+      MakeTlsFilter<TlsExtensionCapture>(server_, ssl_server_name_xtn);
+  server_->SetSniCallback(SniCallback);
+  Connect();
+  CheckSniExtension(cfilter->extension());
+  ASSERT_TRUE(!sfilter->captured());
+}
+
+TEST_P(TlsConnectTls13, ConnectMismatchedEsniKeys) {
+  EnsureTlsSetup();
+  SetupEsni(client_, server_);
+  // Now install a new set of keys on the client, so we have a mismatch.
+  DataBuffer record;
+  GenerateEsniKey(time(0), ssl_grp_ec_curve25519, kDefaultSuites, &record);
+  ClientInstallEsni(client_, record, 0);
+  ConnectExpectAlert(server_, illegal_parameter);
+  server_->CheckErrorCode(SSL_ERROR_RX_MALFORMED_CLIENT_HELLO);
+}
+
+TEST_P(TlsConnectTls13, ConnectDamagedEsniExtensionCH) {
+  EnsureTlsSetup();
+  SetupEsni(client_, server_);
+  auto filter = MakeTlsFilter<TlsExtensionDamager>(
+      client_, ssl_tls13_encrypted_sni_xtn, 50);  // in the ciphertext
+  ConnectExpectAlert(server_, illegal_parameter);
+  server_->CheckErrorCode(SSL_ERROR_RX_MALFORMED_CLIENT_HELLO);
+}
+
+TEST_P(TlsConnectTls13, ConnectRemoveEsniExtensionEE) {
+  EnsureTlsSetup();
+  SetupEsni(client_, server_);
+  auto filter =
+      MakeTlsFilter<TlsExtensionDropper>(server_, ssl_tls13_encrypted_sni_xtn);
+  filter->EnableDecryption();
+  ConnectExpectAlert(client_, missing_extension);
+  client_->CheckErrorCode(SSL_ERROR_MISSING_ESNI_EXTENSION);
+}
+
+TEST_P(TlsConnectTls13, ConnectShortEsniExtensionEE) {
+  EnsureTlsSetup();
+  SetupEsni(client_, server_);
+  DataBuffer shortNonce;
+  auto filter = MakeTlsFilter<TlsExtensionReplacer>(
+      server_, ssl_tls13_encrypted_sni_xtn, shortNonce);
+  filter->EnableDecryption();
+  ConnectExpectAlert(client_, illegal_parameter);
+  client_->CheckErrorCode(SSL_ERROR_RX_MALFORMED_ESNI_EXTENSION);
+}
+
+TEST_P(TlsConnectTls13, ConnectBogusEsniExtensionEE) {
+  EnsureTlsSetup();
+  SetupEsni(client_, server_);
+  const uint8_t bogusNonceBuf[16] = {0};
+  DataBuffer bogusNonce(bogusNonceBuf, sizeof(bogusNonceBuf));
+  auto filter = MakeTlsFilter<TlsExtensionReplacer>(
+      server_, ssl_tls13_encrypted_sni_xtn, bogusNonce);
+  filter->EnableDecryption();
+  ConnectExpectAlert(client_, illegal_parameter);
+  client_->CheckErrorCode(SSL_ERROR_RX_MALFORMED_ESNI_EXTENSION);
+}
+}
--- a/security/nss/gtests/ssl_gtest/tls_filter.cc
+++ b/security/nss/gtests/ssl_gtest/tls_filter.cc
@@ -873,16 +873,27 @@ PacketFilter::Action TlsExtensionReplace
 PacketFilter::Action TlsExtensionDropper::FilterExtension(
     uint16_t extension_type, const DataBuffer& input, DataBuffer* output) {
   if (extension_type == extension_) {
     return DROP;
   }
   return KEEP;
 }
 
+PacketFilter::Action TlsExtensionDamager::FilterExtension(
+    uint16_t extension_type, const DataBuffer& input, DataBuffer* output) {
+  if (extension_type != extension_) {
+    return KEEP;
+  }
+
+  *output = input;
+  output->data()[index_] += 73;  // Increment selected for maximum damage
+  return CHANGE;
+}
+
 PacketFilter::Action TlsExtensionInjector::FilterHandshake(
     const HandshakeHeader& header, const DataBuffer& input,
     DataBuffer* output) {
   TlsParser parser(input);
   if (!TlsExtensionFilter::FindExtensions(&parser, header)) {
     return KEEP;
   }
   size_t offset = parser.consumed();
--- a/security/nss/gtests/ssl_gtest/tls_filter.h
+++ b/security/nss/gtests/ssl_gtest/tls_filter.h
@@ -460,16 +460,30 @@ class TlsExtensionInjector : public TlsH
                                        const DataBuffer& input,
                                        DataBuffer* output) override;
 
  private:
   const uint16_t extension_;
   const DataBuffer data_;
 };
 
+class TlsExtensionDamager : public TlsExtensionFilter {
+ public:
+  TlsExtensionDamager(const std::shared_ptr<TlsAgent>& a, uint16_t extension,
+                      size_t index)
+      : TlsExtensionFilter(a), extension_(extension), index_(index) {}
+  virtual PacketFilter::Action FilterExtension(uint16_t extension_type,
+                                               const DataBuffer& input,
+                                               DataBuffer* output);
+
+ private:
+  uint16_t extension_;
+  size_t index_;
+};
+
 typedef std::function<void(void)> VoidFunction;
 
 class AfterRecordN : public TlsRecordFilter {
  public:
   AfterRecordN(const std::shared_ptr<TlsAgent>& src,
                const std::shared_ptr<TlsAgent>& dest, unsigned int record,
                VoidFunction func)
       : TlsRecordFilter(src),
--- a/security/nss/lib/freebl/ctr.c
+++ b/security/nss/lib/freebl/ctr.c
@@ -216,17 +216,17 @@ CTR_Update_HW_AES(CTRContext *ctr, unsig
         if (inlen == 0) {
             return SECSuccess;
         }
         PORT_Assert(ctr->bufPtr == blocksize);
     }
 
     if (inlen >= blocksize) {
         rv = intel_aes_ctr_worker(((AESContext *)(ctr->context))->Nr)(
-                 ctr, outbuf, outlen, maxout, inbuf, inlen, blocksize);
+            ctr, outbuf, outlen, maxout, inbuf, inlen, blocksize);
         if (rv != SECSuccess) {
             return SECFailure;
         }
         fullblocks = (inlen / blocksize) * blocksize;
         *outlen += fullblocks;
         outbuf += fullblocks;
         inbuf += fullblocks;
         inlen -= fullblocks;
--- a/security/nss/lib/ssl/SSLerrs.h
+++ b/security/nss/lib/ssl/SSLerrs.h
@@ -547,8 +547,17 @@ ER3(SSL_ERROR_HANDSHAKE_FAILED, (SSL_ERR
 ER3(SSL_ERROR_BAD_RESUMPTION_TOKEN_ERROR, (SSL_ERROR_BASE + 173),
     "SSL received an invalid resumption token.")
 
 ER3(SSL_ERROR_RX_MALFORMED_DTLS_ACK, (SSL_ERROR_BASE + 174),
     "SSL received a malformed DTLS ACK")
 
 ER3(SSL_ERROR_DH_KEY_TOO_LONG, (SSL_ERROR_BASE + 175),
     "SSL received a DH key share that's too long (>8192 bit).")
+
+ER3(SSL_ERROR_RX_MALFORMED_ESNI_KEYS, (SSL_ERROR_BASE + 176),
+    "SSL received a malformed ESNI keys structure")
+
+ER3(SSL_ERROR_RX_MALFORMED_ESNI_EXTENSION, (SSL_ERROR_BASE + 177),
+    "SSL received a malformed ESNI extension")
+
+ER3(SSL_ERROR_MISSING_ESNI_EXTENSION, (SSL_ERROR_BASE + 178),
+    "SSL did not receive an ESNI extension")
--- a/security/nss/lib/ssl/config.mk
+++ b/security/nss/lib/ssl/config.mk
@@ -55,8 +55,12 @@ ifeq ($(OS_ARCH), BeOS)
 EXTRA_SHARED_LIBS += -lbe
 endif
 
 endif
 
 ifdef NSS_DISABLE_TLS_1_3
 DEFINES += -DNSS_DISABLE_TLS_1_3
 endif
+
+ifeq (,$(filter-out DragonFly FreeBSD Linux NetBSD OpenBSD, $(OS_TARGET)))
+CFLAGS += -std=gnu99
+endif
--- a/security/nss/lib/ssl/manifest.mn
+++ b/security/nss/lib/ssl/manifest.mn
@@ -1,9 +1,8 @@
-#
 # 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/.
 CORE_DEPTH = ../..
 
 # DEFINES = -DTRACE
 
 EXPORTS = \
@@ -51,15 +50,16 @@ CSRCS = \
         ssl3ecc.c \
         tls13con.c \
         tls13exthandle.c \
         tls13hashstate.c \
         tls13hkdf.c \
         tls13replay.c \
         sslcert.c \
         sslgrp.c \
+        tls13esni.c \
         $(NULL)
 
 LIBRARY_NAME = ssl
 LIBRARY_VERSION = 3
 
 # This part of the code, including all sub-dirs, can be optimized for size
 export ALLOW_OPT_CODE_SIZE = 1
--- a/security/nss/lib/ssl/ssl.gyp
+++ b/security/nss/lib/ssl/ssl.gyp
@@ -38,16 +38,17 @@
         'sslreveal.c',
         'sslsecur.c',
         'sslsnce.c',
         'sslsock.c',
         'sslspec.c',
         'ssltrace.c',
         'sslver.c',
         'tls13con.c',
+        'tls13esni.c',
         'tls13exthandle.c',
         'tls13hashstate.c',
         'tls13hkdf.c',
         'tls13replay.c',
       ],
       'conditions': [
         [ 'OS=="win"', {
           'sources': [
@@ -62,16 +63,21 @@
             'unix_err.c'
           ],
         }],
         [ 'fuzz_tls==1', {
           'defines': [
             'UNSAFE_FUZZER_MODE',
           ],
         }],
+        [ 'OS=="dragonfly" or OS=="freebsd" or OS=="netbsd" or OS=="openbsd" or OS=="linux"', {
+          'cflags': [
+            '-std=gnu99',
+          ],
+        }],
       ],
       'dependencies': [
         '<(DEPTH)/exports.gyp:nss_exports',
       ],
     },
     {
       'target_name': 'ssl3',
       'type': 'shared_library',
--- a/security/nss/lib/ssl/ssl3con.c
+++ b/security/nss/lib/ssl/ssl3con.c
@@ -651,17 +651,17 @@ ssl_LookupCipherSuiteCfgMutable(ssl3Ciph
         if (suites[i].cipher_suite == suite)
             return &suites[i];
     }
     /* return NULL and let the caller handle it.  */
     PORT_SetError(SSL_ERROR_UNKNOWN_CIPHER_SUITE);
     return NULL;
 }
 
-const static ssl3CipherSuiteCfg *
+const ssl3CipherSuiteCfg *
 ssl_LookupCipherSuiteCfg(ssl3CipherSuite suite, const ssl3CipherSuiteCfg *suites)
 {
     return ssl_LookupCipherSuiteCfgMutable(suite,
                                            CONST_CAST(ssl3CipherSuiteCfg, suites));
 }
 
 static PRBool
 ssl_NamedGroupTypeEnabled(const sslSocket *ss, SSLKEAType keaType)
@@ -849,19 +849,19 @@ ssl3_config_match_init(sslSocket *ss)
         PORT_SetError(SSL_ERROR_NO_CIPHERS_SUPPORTED);
     }
     return numPresent;
 }
 
 /* Return PR_TRUE if suite is usable.  This if the suite is permitted by policy,
  * enabled, has a certificate (as needed), has a viable key agreement method, is
  * usable with the negotiated TLS version, and is otherwise usable. */
-static PRBool
-config_match(const ssl3CipherSuiteCfg *suite, PRUint8 policy,
-             const SSLVersionRange *vrange, const sslSocket *ss)
+PRBool
+ssl3_config_match(const ssl3CipherSuiteCfg *suite, PRUint8 policy,
+                  const SSLVersionRange *vrange, const sslSocket *ss)
 {
     const ssl3CipherSuiteDef *cipher_def;
     const ssl3KEADef *kea_def;
 
     PORT_Assert(policy != SSL_NOT_ALLOWED);
     if (policy == SSL_NOT_ALLOWED)
         return PR_FALSE;
 
@@ -894,17 +894,17 @@ static unsigned int
 count_cipher_suites(sslSocket *ss, PRUint8 policy)
 {
     unsigned int i, count = 0;
 
     if (SSL_ALL_VERSIONS_DISABLED(&ss->vrange)) {
         return 0;
     }
     for (i = 0; i < ssl_V3_SUITES_IMPLEMENTED; i++) {
-        if (config_match(&ss->cipherSuites[i], policy, &ss->vrange, ss))
+        if (ssl3_config_match(&ss->cipherSuites[i], policy, &ss->vrange, ss))
             count++;
     }
     if (count == 0) {
         PORT_SetError(SSL_ERROR_SSL_DISABLED);
     }
     return count;
 }
 
@@ -4793,17 +4793,17 @@ ssl3_SendClientHello(sslSocket *ss, sslC
     if (sid) {
         PRBool sidOK = PR_TRUE;
         const ssl3CipherSuiteCfg *suite;
 
         /* Check that the cipher suite we need is enabled. */
         suite = ssl_LookupCipherSuiteCfg(sid->u.ssl3.cipherSuite,
                                          ss->cipherSuites);
         PORT_Assert(suite);
-        if (!suite || !config_match(suite, ss->ssl3.policy, &ss->vrange, ss)) {
+        if (!suite || !ssl3_config_match(suite, ss->ssl3.policy, &ss->vrange, ss)) {
             sidOK = PR_FALSE;
         }
 
         /* Check that we can recover the master secret. */
         if (sidOK) {
             PK11SlotInfo *slot = NULL;
             if (sid->u.ssl3.masterValid) {
                 slot = SECMOD_LookupSlot(sid->u.ssl3.masterModuleID,
@@ -4941,23 +4941,32 @@ ssl3_SendClientHello(sslSocket *ss, sslC
      * is OK for the ticket to change, so we just need to make sure we hold
      * the lock across the calls to ssl_ConstructExtensions.
      */
     if (sid->u.ssl3.lock) {
         unlockNeeded = PR_TRUE;
         PR_RWLock_Rlock(sid->u.ssl3.lock);
     }
 
+    /* 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) {
+            goto loser; /* err set by GetNewRandom. */
+        }
+    }
+
     if (ss->vrange.max >= SSL_LIBRARY_VERSION_TLS_1_3 &&
         type == client_hello_initial) {
         rv = tls13_SetupClientHello(ss);
         if (rv != SECSuccess) {
             goto loser;
         }
     }
+
     if (isTLS || (ss->firstHsDone && ss->peerRequestedProtection)) {
         rv = ssl_ConstructExtensions(ss, &extensionBuf, ssl_hs_client_hello);
         if (rv != SECSuccess) {
             goto loser;
         }
     }
 
     if (IS_DTLS(ss)) {
@@ -5019,23 +5028,16 @@ ssl3_SendClientHello(sslSocket *ss, sslC
         rv = ssl3_AppendHandshakeNumber(ss, dtlsVersion, 2);
     } else {
         rv = ssl3_AppendHandshakeNumber(ss, ss->clientHelloVersion, 2);
     }
     if (rv != SECSuccess) {
         goto loser; /* err set by ssl3_AppendHandshake* */
     }
 
-    /* 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) {
-            goto loser; /* err set by GetNewRandom. */
-        }
-    }
     rv = ssl3_AppendHandshake(ss, ss->ssl3.hs.client_random,
                               SSL3_RANDOM_LENGTH);
     if (rv != SECSuccess) {
         goto loser; /* err set by ssl3_AppendHandshake* */
     }
 
     if (sid->version < SSL_LIBRARY_VERSION_TLS_1_3) {
         rv = ssl3_AppendHandshakeVariable(
@@ -5080,17 +5082,17 @@ ssl3_SendClientHello(sslSocket *ss, sslC
                                         sizeof(ssl3CipherSuite));
         if (rv != SECSuccess) {
             goto loser; /* err set by ssl3_AppendHandshake* */
         }
         actual_count++;
     }
     for (i = 0; i < ssl_V3_SUITES_IMPLEMENTED; i++) {
         ssl3CipherSuiteCfg *suite = &ss->cipherSuites[i];
-        if (config_match(suite, ss->ssl3.policy, &ss->vrange, ss)) {
+        if (ssl3_config_match(suite, ss->ssl3.policy, &ss->vrange, ss)) {
             actual_count++;
             if (actual_count > num_suites) {
                 /* set error card removal/insertion error */
                 PORT_SetError(SSL_ERROR_TOKEN_INSERTION_REMOVAL);
                 goto loser;
             }
             rv = ssl3_AppendHandshakeNumber(ss, suite->cipher_suite,
                                             sizeof(ssl3CipherSuite));
@@ -6321,17 +6323,17 @@ ssl_ClientSetCipherSuite(sslSocket *ss, 
     if (ssl3_config_match_init(ss) == 0) {
         PORT_Assert(PR_FALSE);
         return SECFailure;
     }
     for (i = 0; i < ssl_V3_SUITES_IMPLEMENTED; i++) {
         ssl3CipherSuiteCfg *suiteCfg = &ss->cipherSuites[i];
         if (suite == suiteCfg->cipher_suite) {
             SSLVersionRange vrange = { version, version };
-            if (!config_match(suiteCfg, ss->ssl3.policy, &vrange, ss)) {
+            if (!ssl3_config_match(suiteCfg, ss->ssl3.policy, &vrange, ss)) {
                 /* config_match already checks whether the cipher suite is
                  * acceptable for the version, but the check is repeated here
                  * in order to give a more precise error code. */
                 if (!ssl3_CipherSuiteAllowedForVersionRange(suite, &vrange)) {
                     PORT_SetError(SSL_ERROR_CIPHER_DISALLOWED_FOR_VERSION);
                 } else {
                     PORT_SetError(SSL_ERROR_NO_CYPHER_OVERLAP);
                 }
@@ -7853,16 +7855,40 @@ ssl3_KEASupportsTickets(const ssl3KEADef
     if (kea_def->signKeyType == dsaKey) {
         /* TODO: Fix session tickets for DSS. The server code rejects the
          * session ticket received from the client. Bug 1174677 */
         return PR_FALSE;
     }
     return PR_TRUE;
 }
 
+SECStatus
+ssl3_NegotiateCipherSuiteInner(sslSocket *ss, const SECItem *suites,
+                               PRUint16 version, PRUint16 *suitep)
+{
+    unsigned int j;
+    unsigned int i;
+
+    for (j = 0; j < ssl_V3_SUITES_IMPLEMENTED; j++) {
+        ssl3CipherSuiteCfg *suite = &ss->cipherSuites[j];
+        SSLVersionRange vrange = { version, version };
+        if (!ssl3_config_match(suite, ss->ssl3.policy, &vrange, ss)) {
+            continue;
+        }
+        for (i = 0; i + 1 < suites->len; i += 2) {
+            PRUint16 suite_i = (suites->data[i] << 8) | suites->data[i + 1];
+            if (suite_i == suite->cipher_suite) {
+                *suitep = suite_i;
+                return SECSuccess;
+            }
+        }
+    }
+    return SECFailure;
+}
+
 /* Select a cipher suite.
 **
 ** NOTE: This suite selection algorithm should be the same as the one in
 ** ssl3_HandleV2ClientHello().
 **
 ** If TLS 1.0 is enabled, we could handle the case where the client
 ** offered TLS 1.1 but offered only export cipher suites by choosing TLS
 ** 1.0 and selecting one of those export cipher suites. However, a secure
@@ -7871,34 +7897,26 @@ ssl3_KEASupportsTickets(const ssl3KEADef
 ** cipher suites. Therefore, we refuse to negotiate export cipher suites
 ** with any client that indicates support for TLS 1.1 or higher when we
 ** (the server) have TLS 1.1 support enabled.
 */
 SECStatus
 ssl3_NegotiateCipherSuite(sslSocket *ss, const SECItem *suites,
                           PRBool initHashes)
 {
-    unsigned int j;
-    unsigned int i;
-
-    for (j = 0; j < ssl_V3_SUITES_IMPLEMENTED; j++) {
-        ssl3CipherSuiteCfg *suite = &ss->cipherSuites[j];
-        SSLVersionRange vrange = { ss->version, ss->version };
-        if (!config_match(suite, ss->ssl3.policy, &vrange, ss)) {
-            continue;
-        }
-        for (i = 0; i + 1 < suites->len; i += 2) {
-            PRUint16 suite_i = (suites->data[i] << 8) | suites->data[i + 1];
-            if (suite_i == suite->cipher_suite) {
-                ss->ssl3.hs.cipher_suite = suite_i;
-                return ssl3_SetupCipherSuite(ss, initHashes);
-            }
-        }
-    }
-    return SECFailure;
+    PRUint16 selected;
+    SECStatus rv;
+
+    rv = ssl3_NegotiateCipherSuiteInner(ss, suites, ss->version, &selected);
+    if (rv != SECSuccess) {
+        return SECFailure;
+    }
+
+    ss->ssl3.hs.cipher_suite = selected;
+    return ssl3_SetupCipherSuite(ss, initHashes);
 }
 
 /*
  * Call the SNI config hook.
  *
  * Called from:
  *   ssl3_HandleClientHello
  *   tls13_HandleClientHelloPart2
@@ -8020,19 +8038,22 @@ ssl3_ServerCallSNICallback(sslSocket *ss
                     /* no ciphers are working/supported */
                     errCode = PORT_GetError();
                     desc = handshake_failure;
                     ret = SSL_SNI_SEND_ALERT;
                     break;
                 }
                 /* Need to tell the client that application has picked
                  * the name from the offered list and reconfigured the socket.
+                 * Don't do this if we negotiated ESNI.
                  */
-                ssl3_RegisterExtensionSender(ss, &ss->xtnData, ssl_server_name_xtn,
-                                             ssl_SendEmptyExtension);
+                if (!ssl3_ExtensionNegotiated(ss, ssl_tls13_encrypted_sni_xtn)) {
+                    ssl3_RegisterExtensionSender(ss, &ss->xtnData, ssl_server_name_xtn,
+                                                 ssl_SendEmptyExtension);
+                }
             } else {
                 /* Callback returned index outside of the boundary. */
                 PORT_Assert((unsigned int)ret < ss->xtnData.sniNameArrSize);
                 errCode = SSL_ERROR_INTERNAL_ERROR_ALERT;
                 desc = internal_error;
                 ret = SSL_SNI_SEND_ALERT;
                 break;
             }
@@ -8626,17 +8647,17 @@ ssl3_HandleClientHelloPart2(sslSocket *s
             if (j == 0)
                 break;
 #ifdef PARANOID
             /* Double check that the cached cipher suite is still enabled,
              * implemented, and allowed by policy.  Might have been disabled.
              * The product policy won't change during the process lifetime.
              * Implemented ("isPresent") shouldn't change for servers.
              */
-            if (!config_match(suite, ss->ssl3.policy, &vrange, ss))
+            if (!ssl3_config_match(suite, ss->ssl3.policy, &vrange, ss))
                 break;
 #else
             if (!suite->enabled)
                 break;
 #endif
             /* Double check that the cached cipher suite is in the client's
              * list.  If it isn't, fall through and start a new session. */
             for (i = 0; i + 1 < suites->len; i += 2) {
@@ -9008,17 +9029,17 @@ ssl3_HandleV2ClientHello(sslSocket *ss, 
     /* Select a cipher suite.
     **
     ** NOTE: This suite selection algorithm should be the same as the one in
     ** ssl3_HandleClientHello().
     */
     for (j = 0; j < ssl_V3_SUITES_IMPLEMENTED; j++) {
         ssl3CipherSuiteCfg *suite = &ss->cipherSuites[j];
         SSLVersionRange vrange = { ss->version, ss->version };
-        if (!config_match(suite, ss->ssl3.policy, &vrange, ss)) {
+        if (!ssl3_config_match(suite, ss->ssl3.policy, &vrange, ss)) {
             continue;
         }
         for (i = 0; i + 2 < suite_length; i += 3) {
             PRUint32 suite_i = (suites[i] << 16) | (suites[i + 1] << 8) | suites[i + 2];
             if (suite_i == suite->cipher_suite) {
                 ss->ssl3.hs.cipher_suite = suite_i;
                 rv = ssl3_SetupCipherSuite(ss, PR_TRUE);
                 if (rv != SECSuccess) {
--- a/security/nss/lib/ssl/ssl3ecc.c
+++ b/security/nss/lib/ssl/ssl3ecc.c
@@ -322,26 +322,23 @@ ssl3_HandleECDHClientKeyExchange(sslSock
     ss->sec.keaGroup = ssl_ECPubKey2NamedGroup(&clntPubKey);
     return SECSuccess;
 }
 
 /*
 ** Take an encoded key share and make a public key out of it.
 */
 SECStatus
-ssl_ImportECDHKeyShare(sslSocket *ss, SECKEYPublicKey *peerKey,
+ssl_ImportECDHKeyShare(SECKEYPublicKey *peerKey,
                        PRUint8 *b, PRUint32 length,
                        const sslNamedGroupDef *ecGroup)
 {
     SECStatus rv;
     SECItem ecPoint = { siBuffer, NULL, 0 };
 
-    PORT_Assert(ss->opt.noLocks || ssl_HaveRecvBufLock(ss));
-    PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
-
     if (!length) {
         PORT_SetError(SSL_ERROR_RX_MALFORMED_ECDHE_KEY_SHARE);
         return SECFailure;
     }
 
     /* Fail if the ec point uses compressed representation */
     if (b[0] != EC_POINT_FORM_UNCOMPRESSED &&
         ecGroup->name != ssl_grp_ec_curve25519) {
@@ -611,17 +608,17 @@ ssl3_HandleECDHServerKeyExchange(sslSock
     peerKey = PORT_ArenaZNew(arena, SECKEYPublicKey);
     if (peerKey == NULL) {
         errCode = SEC_ERROR_NO_MEMORY;
         goto loser;
     }
     peerKey->arena = arena;
 
     /* create public key from point data */
-    rv = ssl_ImportECDHKeyShare(ss, peerKey, ec_point.data, ec_point.len,
+    rv = ssl_ImportECDHKeyShare(peerKey, ec_point.data, ec_point.len,
                                 ecGroup);
     if (rv != SECSuccess) {
         /* error code is set */
         desc = handshake_failure;
         errCode = PORT_GetError();
         goto alert_loser;
     }
     peerKey->pkcs11Slot = NULL;
--- a/security/nss/lib/ssl/ssl3ext.c
+++ b/security/nss/lib/ssl/ssl3ext.c
@@ -45,16 +45,17 @@ static const ssl3ExtensionHandler client
     { ssl_signature_algorithms_xtn, &ssl3_HandleSigAlgsXtn },
     { ssl_extended_master_secret_xtn, &ssl3_HandleExtendedMasterSecretXtn },
     { ssl_signed_cert_timestamp_xtn, &ssl3_ServerHandleSignedCertTimestampXtn },
     { ssl_tls13_key_share_xtn, &tls13_ServerHandleKeyShareXtn },
     { ssl_tls13_pre_shared_key_xtn, &tls13_ServerHandlePreSharedKeyXtn },
     { ssl_tls13_early_data_xtn, &tls13_ServerHandleEarlyDataXtn },
     { ssl_tls13_psk_key_exchange_modes_xtn, &tls13_ServerHandlePskModesXtn },
     { ssl_tls13_cookie_xtn, &tls13_ServerHandleCookieXtn },
+    { ssl_tls13_encrypted_sni_xtn, &tls13_ServerHandleEsniXtn },
     { ssl_record_size_limit_xtn, &ssl_HandleRecordSizeLimitXtn },
     { 0, NULL }
 };
 
 /* These two tables are used by the client, to handle server hello
  * extensions. */
 static const ssl3ExtensionHandler serverHelloHandlersTLS[] = {
     { ssl_server_name_xtn, &ssl3_HandleServerNameXtn },
@@ -131,16 +132,17 @@ static const sslExtensionBuilder clientH
       /* Some servers (e.g. WebSphere Application Server 7.0 and Tomcat) will
        * time out or terminate the connection if the last extension in the
        * client hello is empty. They are not intolerant of TLS 1.2, so list
        * signature_algorithms at the end. See bug 1243641. */
       { ssl_tls13_supported_versions_xtn, &tls13_ClientSendSupportedVersionsXtn },
       { ssl_signature_algorithms_xtn, &ssl3_SendSigAlgsXtn },
       { ssl_tls13_cookie_xtn, &tls13_ClientSendHrrCookieXtn },
       { ssl_tls13_psk_key_exchange_modes_xtn, &tls13_ClientSendPskModesXtn },
+      { ssl_tls13_encrypted_sni_xtn, &tls13_ClientSendEsniXtn },
       { ssl_record_size_limit_xtn, &ssl_SendRecordSizeLimitXtn },
       /* The pre_shared_key extension MUST be last. */
       { ssl_tls13_pre_shared_key_xtn, &tls13_ClientSendPreSharedKeyXtn },
       { 0, NULL }
     };
 
 static const sslExtensionBuilder clientHelloSendersSSL3[] = {
     { ssl_renegotiation_info_xtn, &ssl3_SendRenegotiationInfoXtn },
@@ -333,18 +335,16 @@ ssl3_ParseExtensions(sslSocket *ss, PRUi
         PRCList *cursor;
 
         /* Get the extension's type field */
         rv = ssl3_ConsumeHandshakeNumber(ss, &extension_type, 2, b, length);
         if (rv != SECSuccess) {
             return SECFailure; /* alert already sent */
         }
 
-        SSL_TRC(10, ("%d: SSL3[%d]: parsing extension %d",
-                     SSL_GETPID(), ss->fd, extension_type));
         /* Check whether an extension has been sent multiple times. */
         for (cursor = PR_NEXT_LINK(&ss->ssl3.hs.remoteExtensions);
              cursor != &ss->ssl3.hs.remoteExtensions;
              cursor = PR_NEXT_LINK(cursor)) {
             if (((TLSExtension *)cursor)->type == extension_type) {
                 (void)SSL3_SendAlert(ss, alert_fatal, illegal_parameter);
                 PORT_SetError(SSL_ERROR_RX_UNEXPECTED_EXTENSION);
                 return SECFailure;
@@ -352,16 +352,19 @@ ssl3_ParseExtensions(sslSocket *ss, PRUi
         }
 
         /* Get the data for this extension, so we can pass it or skip it. */
         rv = ssl3_ConsumeHandshakeVariable(ss, &extension_data, 2, b, length);
         if (rv != SECSuccess) {
             return rv; /* alert already sent */
         }
 
+        SSL_TRC(10, ("%d: SSL3[%d]: parsed extension %d len=%u",
+                     SSL_GETPID(), ss->fd, extension_type, extension_data.len));
+
         extension = PORT_ZNew(TLSExtension);
         if (!extension) {
             return SECFailure;
         }
 
         extension->type = (PRUint16)extension_type;
         extension->data = extension_data;
         PR_APPEND_LINK(&extension->link, &ss->ssl3.hs.remoteExtensions);
@@ -404,17 +407,19 @@ ssl_CallExtensionHandler(sslSocket *ss, 
                                       extension->data.data,
                                       extension->data.len,
                                       &alert, customHooks->handlerArg);
         }
     } else {
         /* Find extension_type in table of Hello Extension Handlers. */
         for (; handler->ex_handler != NULL; ++handler) {
             if (handler->ex_type == extension->type) {
-                rv = (*handler->ex_handler)(ss, &ss->xtnData, &extension->data);
+                SECItem tmp = extension->data;
+
+                rv = (*handler->ex_handler)(ss, &ss->xtnData, &tmp);
                 break;
             }
         }
     }
 
     if (rv != SECSuccess) {
         if (!ss->ssl3.fatalAlertSent) {
             /* Send an alert if the handler didn't already. */
@@ -955,16 +960,18 @@ ssl3_DestroyExtensionData(TLSExtensionDa
     tls13_DestroyKeyShares(&xtnData->remoteKeyShares);
     SECITEM_FreeItem(&xtnData->certReqContext, PR_FALSE);
     SECITEM_FreeItem(&xtnData->applicationToken, PR_FALSE);
     if (xtnData->certReqAuthorities.arena) {
         PORT_FreeArena(xtnData->certReqAuthorities.arena, PR_FALSE);
         xtnData->certReqAuthorities.arena = NULL;
     }
     PORT_Free(xtnData->advertised);
+    ssl_FreeEphemeralKeyPair(xtnData->esniPrivateKey);
+    SECITEM_FreeItem(&xtnData->keyShareExtension, PR_FALSE);
 }
 
 /* Free everything that has been allocated and then reset back to
  * the starting state. */
 void
 ssl3_ResetExtensionData(TLSExtensionData *xtnData, const sslSocket *ss)
 {
     ssl3_DestroyExtensionData(xtnData);
--- a/security/nss/lib/ssl/ssl3ext.h
+++ b/security/nss/lib/ssl/ssl3ext.h
@@ -6,16 +6,18 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef __ssl3ext_h_
 #define __ssl3ext_h_
 
 #include "sslencode.h"
 
+#define TLS13_ESNI_NONCE_SIZE 16
+
 typedef enum {
     sni_nametype_hostname
 } SNINameType;
 typedef struct TLSExtensionDataStr TLSExtensionData;
 
 /* Registerable callback function that either appends extension to buffer
  * or returns length of data that it would have appended.
  */
@@ -96,16 +98,24 @@ struct TLSExtensionDataStr {
     SECItem cookie;                        /* HRR Cookie. */
     const sslNamedGroupDef *selectedGroup; /* For HRR. */
     /* The application token contains a value that was passed to the client via
      * a session ticket, or the cookie in a HelloRetryRequest. */
     SECItem applicationToken;
 
     /* The record size limit set by the peer. Our value is kept in ss->opt. */
     PRUint16 recordSizeLimit;
+
+    /* ESNI working state */
+    SECItem keyShareExtension;
+    ssl3CipherSuite esniSuite;
+    sslEphemeralKeyPair *esniPrivateKey;
+    /* Pointer into |ss->esniKeys->keyShares| */
+    TLS13KeyShareEntry *peerEsniShare;
+    PRUint8 esniNonce[TLS13_ESNI_NONCE_SIZE];
 };
 
 typedef struct TLSExtensionStr {
     PRCList link;  /* The linked list link */
     PRUint16 type; /* Extension type */
     SECItem data;  /* Pointers into the handshake data. */
 } TLSExtension;
 
--- a/security/nss/lib/ssl/ssl3exthandle.c
+++ b/security/nss/lib/ssl/ssl3exthandle.c
@@ -10,73 +10,115 @@
 #include "sslproto.h"
 #include "sslimpl.h"
 #include "pk11pub.h"
 #include "blapit.h"
 #include "prinit.h"
 #include "selfencrypt.h"
 #include "ssl3ext.h"
 #include "ssl3exthandle.h"
+#include "tls13esni.h"
 #include "tls13exthandle.h" /* For tls13_ServerSendStatusRequestXtn. */
 
+PRBool
+ssl_ShouldSendSNIExtension(const sslSocket *ss, const char *url)
+{
+    PRNetAddr netAddr;
+
+    /* must have a hostname */
+    if (!url || !url[0]) {
+        return PR_FALSE;
+    }
+    /* must not be an IPv4 or IPv6 address */
+    if (PR_SUCCESS == PR_StringToNetAddr(url, &netAddr)) {
+        /* is an IP address (v4 or v6) */
+        return PR_FALSE;
+    }
+
+    return PR_TRUE;
+}
+
 /* Format an SNI extension, using the name from the socket's URL,
  * unless that name is a dotted decimal string.
  * Used by client and server.
  */
 SECStatus
-ssl3_ClientSendServerNameXtn(const sslSocket *ss, TLSExtensionData *xtnData,
-                             sslBuffer *buf, PRBool *added)
+ssl3_ClientFormatServerNameXtn(const sslSocket *ss, const char *url,
+                               TLSExtensionData *xtnData,
+                               sslBuffer *buf)
 {
     unsigned int len;
-    PRNetAddr netAddr;
     SECStatus rv;
 
-    /* must have a hostname */
-    if (!ss->url || !ss->url[0]) {
-        return SECSuccess;
-    }
-    /* must not be an IPv4 or IPv6 address */
-    if (PR_SUCCESS == PR_StringToNetAddr(ss->url, &netAddr)) {
-        /* is an IP address (v4 or v6) */
-        return SECSuccess;
-    }
-    len = PORT_Strlen(ss->url);
+    len = PORT_Strlen(url);
     /* length of server_name_list */
     rv = sslBuffer_AppendNumber(buf, len + 3, 2);
     if (rv != SECSuccess) {
         return SECFailure;
     }
     /* Name Type (sni_host_name) */
     rv = sslBuffer_AppendNumber(buf, 0, 1);
     if (rv != SECSuccess) {
         return SECFailure;
     }
     /* HostName (length and value) */
-    rv = sslBuffer_AppendVariable(buf, (const PRUint8 *)ss->url, len, 2);
+    rv = sslBuffer_AppendVariable(buf, (const PRUint8 *)url, len, 2);
+    if (rv != SECSuccess) {
+        return SECFailure;
+    }
+
+    return SECSuccess;
+}
+
+SECStatus
+ssl3_ClientSendServerNameXtn(const sslSocket *ss, TLSExtensionData *xtnData,
+                             sslBuffer *buf, PRBool *added)
+{
+    SECStatus rv;
+
+    const char *url = ss->url;
+
+    /* We only make an ESNI private key if we are going to
+     * send ESNI. */
+    if (ss->xtnData.esniPrivateKey != NULL) {
+        url = ss->esniKeys->dummySni;
+    }
+
+    if (!ssl_ShouldSendSNIExtension(ss, url)) {
+        return SECSuccess;
+    }
+
+    rv = ssl3_ClientFormatServerNameXtn(ss, url, xtnData, buf);
     if (rv != SECSuccess) {
         return SECFailure;
     }
 
     *added = PR_TRUE;
     return SECSuccess;
 }
 
-/* Handle an incoming SNI extension. */
 SECStatus
 ssl3_HandleServerNameXtn(const sslSocket *ss, TLSExtensionData *xtnData,
                          SECItem *data)
 {
     SECItem *names = NULL;
     PRUint32 listLenBytes = 0;
     SECStatus rv;
 
     if (!ss->sec.isServer) {
         return SECSuccess; /* ignore extension */
     }
 
+    if (ssl3_ExtensionNegotiated(ss, ssl_tls13_encrypted_sni_xtn)) {
+        /* If we already have ESNI, make sure we don't overwrite
+         * the value. */
+        PORT_Assert(ss->version >= SSL_LIBRARY_VERSION_TLS_1_3);
+        return SECSuccess;
+    }
+
     /* Server side - consume client data and register server sender. */
     /* do not parse the data if don't have user extension handling function. */
     if (!ss->sniSocketConfig) {
         return SECSuccess;
     }
 
     /* length of server_name_list */
     rv = ssl3_ExtConsumeHandshakeNumber(ss, &listLenBytes, 2, &data->data, &data->len);
--- a/security/nss/lib/ssl/ssl3exthandle.h
+++ b/security/nss/lib/ssl/ssl3exthandle.h
@@ -86,16 +86,20 @@ SECStatus ssl3_ServerHandleSignedCertTim
 SECStatus ssl3_SendExtendedMasterSecretXtn(const sslSocket *ss,
                                            TLSExtensionData *xtnData,
                                            sslBuffer *buf, PRBool *added);
 SECStatus ssl3_HandleExtendedMasterSecretXtn(const sslSocket *ss,
                                              TLSExtensionData *xtnData,
                                              SECItem *data);
 SECStatus ssl3_ProcessSessionTicketCommon(sslSocket *ss, const SECItem *ticket,
                                           /* out */ SECItem *appToken);
+PRBool ssl_ShouldSendSNIExtension(const sslSocket *ss, const char *url);
+SECStatus ssl3_ClientFormatServerNameXtn(const sslSocket *ss, const char *url,
+                                         TLSExtensionData *xtnData,
+                                         sslBuffer *buf);
 SECStatus ssl3_ClientSendServerNameXtn(const sslSocket *ss,
                                        TLSExtensionData *xtnData,
                                        sslBuffer *buf, PRBool *added);
 SECStatus ssl3_HandleServerNameXtn(const sslSocket *ss,
                                    TLSExtensionData *xtnData,
                                    SECItem *data);
 SECStatus ssl_HandleSupportedGroupsXtn(const sslSocket *ss,
                                        TLSExtensionData *xtnData,
--- a/security/nss/lib/ssl/sslerr.h
+++ b/security/nss/lib/ssl/sslerr.h
@@ -259,15 +259,18 @@ typedef enum {
 
     SSL_ERROR_RX_UNEXPECTED_KEY_UPDATE = (SSL_ERROR_BASE + 169),
     SSL_ERROR_RX_MALFORMED_KEY_UPDATE = (SSL_ERROR_BASE + 170),
     SSL_ERROR_TOO_MANY_KEY_UPDATES = (SSL_ERROR_BASE + 171),
     SSL_ERROR_HANDSHAKE_FAILED = (SSL_ERROR_BASE + 172),
     SSL_ERROR_BAD_RESUMPTION_TOKEN_ERROR = (SSL_ERROR_BASE + 173),
     SSL_ERROR_RX_MALFORMED_DTLS_ACK = (SSL_ERROR_BASE + 174),
     SSL_ERROR_DH_KEY_TOO_LONG = (SSL_ERROR_BASE + 175),
+    SSL_ERROR_RX_MALFORMED_ESNI_KEYS = (SSL_ERROR_BASE + 176),
+    SSL_ERROR_RX_MALFORMED_ESNI_EXTENSION = (SSL_ERROR_BASE + 177),
+    SSL_ERROR_MISSING_ESNI_EXTENSION = (SSL_ERROR_BASE + 178),
     SSL_ERROR_END_OF_LIST   /* let the c compiler determine the value of this. */
 } SSLErrorCodes;
 #endif /* NO_SECURITY_ERROR_ENUM */
 
 /* clang-format on */
 
 #endif /* __SSL_ERR_H_ */
--- a/security/nss/lib/ssl/sslexp.h
+++ b/security/nss/lib/ssl/sslexp.h
@@ -447,15 +447,72 @@ typedef SECStatus(PR_CALLBACK *SSLResump
 /* TLS 1.3 allows a server to set a limit on the number of bytes of early data
  * that can be received. This allows that limit to be set. This function has no
  * effect on a client. */
 #define SSL_SetMaxEarlyDataSize(fd, size)                    \
     SSL_EXPERIMENTAL_API("SSL_SetMaxEarlyDataSize",          \
                          (PRFileDesc * _fd, PRUint32 _size), \
                          (fd, size))
 
+/* Set the ESNI key pair on a socket (server side)
+ *
+ * fd -- the socket
+ * record/recordLen -- the encoded DNS record (not base64)
+ *
+ * Important: the suites that are advertised in the record must
+ * be configured on, or this call will fail.
+ */
+#define SSL_SetESNIKeyPair(fd,                                              \
+                           privKey, record, recordLen)                      \
+    SSL_EXPERIMENTAL_API("SSL_SetESNIKeyPair",                              \
+                         (PRFileDesc * _fd,                                 \
+                          SECKEYPrivateKey * _privKey,                      \
+                          const PRUint8 *_record, unsigned int _recordLen), \
+                         (fd, privKey,                                      \
+                          record, recordLen))
+
+/* Set the ESNI keys on a client
+ *
+ * fd -- the socket
+ * ensikeys/esniKeysLen -- the ESNI key structure (not base64)
+ * dummyESNI -- the dummy ESNI to use (if any)
+ */
+#define SSL_EnableESNI(fd, esniKeys, esniKeysLen, dummySNI) \
+    SSL_EXPERIMENTAL_API("SSL_EnableESNI",                  \
+                         (PRFileDesc * _fd,                 \
+                          const PRUint8 *_esniKeys,         \
+                          unsigned int _esniKeysLen,        \
+                          const char *_dummySNI),           \
+                         (fd, esniKeys, esniKeysLen, dummySNI))
+
+/*
+ * Generate an encoded ESNIKeys structure (presumably server side).
+ *
+ * cipherSuites -- the cipher suites that can be used
+ * cipherSuitesCount -- the number of suites in cipherSuites
+ * group -- the named group this key corresponds to
+ * pubKey -- the public key for the key pair
+ * pad -- the length to pad to
+ * notBefore/notAfter -- validity range
+ * out/outlen/maxlen -- where to output the data
+ */
+#define SSL_EncodeESNIKeys(cipherSuites, cipherSuiteCount,          \
+                           group, pubKey, pad, notBefore, notAfter, \
+                           out, outlen, maxlen)                     \
+    SSL_EXPERIMENTAL_API("SSL_EncodeESNIKeys",                      \
+                         (PRUint16 * _cipherSuites,                 \
+                          unsigned int _cipherSuiteCount,           \
+                          SSLNamedGroup _group,                     \
+                          SECKEYPublicKey *_pubKey,                 \
+                          PRUint16 _pad,                            \
+                          PRUint64 _notBefore, PRUint64 _notAfter,  \
+                          PRUint8 *_out, unsigned int *_outlen,     \
+                          unsigned int _maxlen),                    \
+                         (cipherSuites, cipherSuiteCount,           \
+                          group, pubKey, pad, notBefore, notAfter,  \
+                          out, outlen, maxlen))
+
 /* Deprecated experimental APIs */
-
 #define SSL_UseAltServerHelloType(fd, enable) SSL_DEPRECATED_EXPERIMENTAL_API
 
 SEC_END_PROTOS
 
 #endif /* __sslexp_h_ */
--- a/security/nss/lib/ssl/sslimpl.h
+++ b/security/nss/lib/ssl/sslimpl.h
@@ -31,16 +31,20 @@
 #include "prthread.h"
 #include "prclist.h"
 #include "private/pprthred.h"
 
 #include "sslt.h" /* for some formerly private types, now public */
 
 typedef struct sslSocketStr sslSocket;
 typedef struct sslNamedGroupDefStr sslNamedGroupDef;
+typedef struct sslEsniKeysStr sslEsniKeys;
+typedef struct sslEphemeralKeyPairStr sslEphemeralKeyPair;
+typedef struct TLS13KeyShareEntryStr TLS13KeyShareEntry;
+
 #include "sslencode.h"
 #include "sslexp.h"
 #include "ssl3ext.h"
 #include "sslspec.h"
 
 #if defined(DEBUG) || defined(TRACE)
 #ifdef __cplusplus
 #define Debug 1
@@ -554,21 +558,21 @@ typedef SECStatus (*sslRestartTarget)(ss
 typedef struct DTLSQueuedMessageStr {
     PRCList link;           /* The linked list link */
     ssl3CipherSpec *cwSpec; /* The cipher spec to use, null for none */
     SSLContentType type;    /* The message type */
     unsigned char *data;    /* The data */
     PRUint16 len;           /* The data length */
 } DTLSQueuedMessage;
 
-typedef struct TLS13KeyShareEntryStr {
+struct TLS13KeyShareEntryStr {
     PRCList link;                  /* The linked list link */
     const sslNamedGroupDef *group; /* The group for the entry */
     SECItem key_exchange;          /* The share itself */
-} TLS13KeyShareEntry;
+};
 
 typedef struct TLS13EarlyDataStr {
     PRCList link; /* The linked list link */
     SECItem data; /* The data */
 } TLS13EarlyData;
 
 typedef enum {
     handshake_hash_unknown = 0,
@@ -800,21 +804,21 @@ typedef struct {
 } SSL3Ciphertext;
 
 struct sslKeyPairStr {
     SECKEYPrivateKey *privKey;
     SECKEYPublicKey *pubKey;
     PRInt32 refCount; /* use PR_Atomic calls for this. */
 };
 
-typedef struct {
+struct sslEphemeralKeyPairStr {
     PRCList link;
     const sslNamedGroupDef *group;
     sslKeyPair *keys;
-} sslEphemeralKeyPair;
+};
 
 struct ssl3DHParamsStr {
     SSLNamedGroup name;
     SECItem prime; /* p */
     SECItem base;  /* g */
 };
 
 typedef struct SSLWrappedSymWrappingKeyStr {
@@ -1061,16 +1065,20 @@ struct sslSocketStr {
      * TLS extension related data.
      */
     /* True when the current session is a stateless resume. */
     PRBool statelessResume;
     TLSExtensionData xtnData;
 
     /* Whether we are doing stream or datagram mode */
     SSLProtocolVariant protocolVariant;
+
+    /* The information from the ESNI keys record
+     * (also the private key for the server). */
+    sslEsniKeys *esniKeys;
 };
 
 struct sslSelfEncryptKeysStr {
     PRCallOnceType setup;
     PRUint8 keyName[SELF_ENCRYPT_KEY_NAME_LEN];
     PK11SymKey *encKey;
     PK11SymKey *macKey;
 };
@@ -1165,16 +1173,17 @@ extern int ssl_SendSavedWriteData(sslSoc
 extern SECStatus ssl_SaveWriteData(sslSocket *ss,
                                    const void *p, unsigned int l);
 extern SECStatus ssl_BeginClientHandshake(sslSocket *ss);
 extern SECStatus ssl_BeginServerHandshake(sslSocket *ss);
 extern int ssl_Do1stHandshake(sslSocket *ss);
 
 extern SECStatus ssl3_InitPendingCipherSpecs(sslSocket *ss, PK11SymKey *secret,
                                              PRBool derive);
+extern void ssl_DestroyKeyMaterial(ssl3KeyMaterial *keyMaterial);
 extern sslSessionID *ssl3_NewSessionID(sslSocket *ss, PRBool is_server);
 extern sslSessionID *ssl_LookupSID(const PRIPv6Addr *addr, PRUint16 port,
                                    const char *peerID, const char *urlSvrName);
 extern void ssl_FreeSID(sslSessionID *sid);
 extern void ssl_DestroySID(sslSessionID *sid, PRBool freeIt);
 
 extern int ssl3_SendApplicationData(sslSocket *ss, const PRUint8 *in,
                                     int len, int flags);
@@ -1494,17 +1503,17 @@ extern SECStatus ssl3_SendECDHClientKeyE
                                                 SECKEYPublicKey *svrPubKey);
 extern SECStatus ssl3_HandleECDHServerKeyExchange(sslSocket *ss,
                                                   PRUint8 *b, PRUint32 length);
 extern SECStatus ssl3_HandleECDHClientKeyExchange(sslSocket *ss,
                                                   PRUint8 *b, PRUint32 length,
                                                   sslKeyPair *serverKeys);
 extern SECStatus ssl3_SendECDHServerKeyExchange(sslSocket *ss);
 extern SECStatus ssl_ImportECDHKeyShare(
-    sslSocket *ss, SECKEYPublicKey *peerKey,
+    SECKEYPublicKey *peerKey,
     PRUint8 *b, PRUint32 length, const sslNamedGroupDef *curve);
 
 extern SECStatus ssl3_ComputeCommonKeyHash(SSLHashType hashAlg,
                                            PRUint8 *hashBuf,
                                            unsigned int bufLen,
                                            SSL3Hashes *hashes);
 extern SECStatus ssl3_AppendSignatureAndHashAlgorithm(
     sslSocket *ss, const SSLSignatureAndHashAlg *sigAndHash);
@@ -1559,16 +1568,22 @@ extern SECStatus ssl3_ValidateAppProtoco
 /* Construct a new NSPR socket for the app to use */
 extern PRFileDesc *ssl_NewPRSocket(sslSocket *ss, PRFileDesc *fd);
 extern void ssl_FreePRSocket(PRFileDesc *fd);
 
 /* Internal config function so SSL3 can initialize the present state of
  * various ciphers */
 extern unsigned int ssl3_config_match_init(sslSocket *);
 
+/* Return PR_TRUE if suite is usable.  This if the suite is permitted by policy,
+ * enabled, has a certificate (as needed), has a viable key agreement method, is
+ * usable with the negotiated TLS version, and is otherwise usable. */
+PRBool ssl3_config_match(const ssl3CipherSuiteCfg *suite, PRUint8 policy,
+                         const SSLVersionRange *vrange, const sslSocket *ss);
+
 /* calls for accessing wrapping keys across processes. */
 extern SECStatus
 ssl_GetWrappingKey(unsigned int symWrapMechIndex, unsigned int wrapKeyIndex,
                    SSLWrappedSymWrappingKey *wswk);
 
 /* The caller passes in the new value it wants
  * to set.  This code tests the wrapped sym key entry in the file on disk.
  * If it is uninitialized, this function writes the caller's value into
@@ -1588,16 +1603,18 @@ extern SECStatus ssl_InitSymWrapKeysLock
 
 extern SECStatus ssl_FreeSymWrapKeysLock(void);
 
 extern SECStatus ssl_InitSessionCacheLocks(PRBool lazyInit);
 
 extern SECStatus ssl_FreeSessionCacheLocks(void);
 
 CK_MECHANISM_TYPE ssl3_Alg2Mech(SSLCipherAlgorithm calg);
+SECStatus ssl3_NegotiateCipherSuiteInner(sslSocket *ss, const SECItem *suites,
+                                         PRUint16 version, PRUint16 *suitep);
 SECStatus ssl3_NegotiateCipherSuite(sslSocket *ss, const SECItem *suites,
                                     PRBool initHashes);
 SECStatus ssl3_InitHandshakeHashes(sslSocket *ss);
 SECStatus ssl3_ServerCallSNICallback(sslSocket *ss);
 SECStatus ssl3_FlushHandshake(sslSocket *ss, PRInt32 flags);
 SECStatus ssl3_CompleteHandleCertificate(sslSocket *ss,
                                          PRUint8 *b, PRUint32 length);
 void ssl3_SendAlertForCertError(sslSocket *ss, PRErrorCode errCode);
@@ -1635,16 +1652,19 @@ SECStatus ssl_CreateStaticECDHEKey(sslSo
 SECStatus ssl3_FlushHandshake(sslSocket *ss, PRInt32 flags);
 PK11SymKey *ssl3_GetWrappingKey(sslSocket *ss,
                                 PK11SlotInfo *masterSecretSlot,
                                 CK_MECHANISM_TYPE masterWrapMech,
                                 void *pwArg);
 SECStatus ssl3_FillInCachedSID(sslSocket *ss, sslSessionID *sid,
                                PK11SymKey *secret);
 const ssl3CipherSuiteDef *ssl_LookupCipherSuiteDef(ssl3CipherSuite suite);
+const ssl3CipherSuiteCfg *ssl_LookupCipherSuiteCfg(ssl3CipherSuite suite,
+                                                   const ssl3CipherSuiteCfg *suites);
+
 SECStatus ssl3_SelectServerCert(sslSocket *ss);
 SECStatus ssl_PickSignatureScheme(sslSocket *ss,
                                   CERTCertificate *cert,
                                   SECKEYPublicKey *pubKey,
                                   SECKEYPrivateKey *privKey,
                                   const SSLSignatureScheme *peerSchemes,
                                   unsigned int peerSchemeCount,
                                   PRBool requireSha1);
--- a/security/nss/lib/ssl/sslsecur.c
+++ b/security/nss/lib/ssl/sslsecur.c
@@ -931,16 +931,35 @@ ssl_SecureSend(sslSocket *ss, const unsi
             (ss->opt.enableFalseStart || ss->opt.enable0RttData)) {
             ssl_GetSSL3HandshakeLock(ss);
             zeroRtt = ss->ssl3.hs.zeroRttState == ssl_0rtt_sent ||
                       ss->ssl3.hs.zeroRttState == ssl_0rtt_accepted;
             allowEarlySend = ss->ssl3.hs.canFalseStart || zeroRtt;
             firstClientWrite = ss->ssl3.hs.ws == idle_handshake;
             ssl_ReleaseSSL3HandshakeLock(ss);
         }
+        /* Allow the server to send 0.5 RTT data in TLS 1.3. Requesting a
+         * certificate implies that the server might condition its sending on
+         * client authentication, so force servers that do that to wait.
+         *
+         * What might not be obvious here is that this allows 0.5 RTT when doing
+         * PSK-based resumption.  As a result, 0.5 RTT is always enabled when
+         * early data is accepted.
+         *
+         * This check might be more conservative than absolutely necessary.
+         * It's possible that allowing 0.5 RTT data when the server requests,
+         * but does not require client authentication is safe because we can
+         * expect the server to check for a client certificate properly. */
+        if (ss->sec.isServer &&
+            ss->version >= SSL_LIBRARY_VERSION_TLS_1_3 &&
+            !tls13_ShouldRequestClientAuth(ss)) {
+            ssl_GetSSL3HandshakeLock(ss);
+            allowEarlySend = TLS13_IN_HS_STATE(ss, wait_finished);
+            ssl_ReleaseSSL3HandshakeLock(ss);
+        }
         if (!allowEarlySend && ss->handshake) {
             rv = ssl_Do1stHandshake(ss);
         }
         if (firstClientWrite) {
             /* Wait until after sending ClientHello and double-check 0-RTT. */
             ssl_GetSSL3HandshakeLock(ss);
             zeroRtt = ss->ssl3.hs.zeroRttState == ssl_0rtt_sent ||
                       ss->ssl3.hs.zeroRttState == ssl_0rtt_accepted;
--- a/security/nss/lib/ssl/sslsock.c
+++ b/security/nss/lib/ssl/sslsock.c
@@ -13,16 +13,17 @@
 #include "ssl.h"
 #include "sslexp.h"
 #include "sslimpl.h"
 #include "sslproto.h"
 #include "nspr.h"
 #include "private/pprio.h"
 #include "nss.h"
 #include "pk11pqg.h"
+#include "tls13esni.h"
 
 static const sslSocketOps ssl_default_ops = { /* No SSL. */
                                               ssl_DefConnect,
                                               NULL,
                                               ssl_DefBind,
                                               ssl_DefListen,
                                               ssl_DefShutdown,
                                               ssl_DefClose,
@@ -356,16 +357,23 @@ ssl_DupSocket(sslSocket *os)
         ss->nextProtoArg = os->nextProtoArg;
         PORT_Memcpy((void *)ss->namedGroupPreferences,
                     os->namedGroupPreferences,
                     sizeof(ss->namedGroupPreferences));
         ss->additionalShares = os->additionalShares;
         ss->resumptionTokenCallback = os->resumptionTokenCallback;
         ss->resumptionTokenContext = os->resumptionTokenContext;
 
+        if (os->esniKeys) {
+            ss->esniKeys = tls13_CopyESNIKeys(os->esniKeys);
+            if (!ss->esniKeys) {
+                goto loser;
+            }
+        }
+
         /* Create security data */
         rv = ssl_CopySecurityInfo(ss, os);
         if (rv != SECSuccess) {
             goto loser;
         }
     }
 
     return ss;
@@ -441,16 +449,18 @@ ssl_DestroySocketContents(sslSocket *ss)
     ssl_ClearPRCList(&ss->extensionHooks, NULL);
 
     ssl_FreeEphemeralKeyPairs(ss);
     SECITEM_FreeItem(&ss->opt.nextProtoNego, PR_FALSE);
     ssl3_FreeSniNameArray(&ss->xtnData);
 
     ssl_ClearPRCList(&ss->ssl3.hs.dtlsSentHandshake, NULL);
     ssl_ClearPRCList(&ss->ssl3.hs.dtlsRcvdHandshake, NULL);
+
+    tls13_DestroyESNIKeys(ss->esniKeys);
 }
 
 /*
  * free an sslSocket struct, and all the stuff that hangs off of it
  */
 void
 ssl_FreeSocket(sslSocket *ss)
 {
@@ -3767,16 +3777,20 @@ ssl_GetKeyPairRef(sslKeyPair *keyPair)
 {
     PR_ATOMIC_INCREMENT(&keyPair->refCount);
     return keyPair;
 }
 
 void
 ssl_FreeKeyPair(sslKeyPair *keyPair)
 {
+    if (!keyPair) {
+        return;
+    }
+
     PRInt32 newCount = PR_ATOMIC_DECREMENT(&keyPair->refCount);
     if (!newCount) {
         SECKEY_DestroyPrivateKey(keyPair->privKey);
         SECKEY_DestroyPublicKey(keyPair->pubKey);
         PORT_Free(keyPair);
     }
 }
 
@@ -3826,16 +3840,20 @@ ssl_CopyEphemeralKeyPair(sslEphemeralKey
     pair->keys = ssl_GetKeyPairRef(keyPair->keys);
 
     return pair;
 }
 
 void
 ssl_FreeEphemeralKeyPair(sslEphemeralKeyPair *keyPair)
 {
+    if (!keyPair) {
+        return;
+    }
+
     ssl_FreeKeyPair(keyPair->keys);
     PR_REMOVE_LINK(&keyPair->link);
     PORT_Free(keyPair);
 }
 
 PRBool
 ssl_HaveEphemeralKeyPair(const sslSocket *ss, const sslNamedGroupDef *groupDef)
 {
@@ -3933,16 +3951,18 @@ ssl_NewSocket(PRBool makeLocks, SSLProto
     PR_INIT_CLIST(&ss->ssl3.hs.lastMessageFlight);
     PR_INIT_CLIST(&ss->ssl3.hs.cipherSpecs);
     PR_INIT_CLIST(&ss->ssl3.hs.bufferedEarlyData);
     ssl3_InitExtensionData(&ss->xtnData, ss);
     PR_INIT_CLIST(&ss->ssl3.hs.dtlsSentHandshake);
     PR_INIT_CLIST(&ss->ssl3.hs.dtlsRcvdHandshake);
     dtls_InitTimers(ss);
 
+    ss->esniKeys = NULL;
+
     if (makeLocks) {
         rv = ssl_MakeLocks(ss);
         if (rv != SECSuccess)
             goto loser;
     }
     rv = ssl_CreateSecurityInfo(ss);
     if (rv != SECSuccess)
         goto loser;
@@ -4009,16 +4029,19 @@ struct {
     EXP(KeyUpdate),
     EXP(SendSessionTicket),
     EXP(SetMaxEarlyDataSize),
     EXP(SetupAntiReplay),
     EXP(SetResumptionTokenCallback),
     EXP(SetResumptionToken),
     EXP(GetResumptionTokenInfo),
     EXP(DestroyResumptionTokenInfo),
+    EXP(SetESNIKeyPair),
+    EXP(EncodeESNIKeys),
+    EXP(EnableESNI),
 #endif
     { "", NULL }
 };
 #undef EXP
 #undef PUB
 
 void *
 SSL_GetExperimentalAPI(const char *name)
--- a/security/nss/lib/ssl/sslspec.c
+++ b/security/nss/lib/ssl/sslspec.c
@@ -198,17 +198,17 @@ ssl_SetupNullCipherSpec(sslSocket *ss, C
 void
 ssl_CipherSpecAddRef(ssl3CipherSpec *spec)
 {
     ++spec->refCt;
     SSL_TRC(10, ("%d: SSL[-]: Increment ref ct for %s spec %d. new ct = %d",
                  SSL_GETPID(), SPEC_DIR(spec), spec, spec->refCt));
 }
 
-static void
+void
 ssl_DestroyKeyMaterial(ssl3KeyMaterial *keyMaterial)
 {
     PK11_FreeSymKey(keyMaterial->key);
     PK11_FreeSymKey(keyMaterial->macKey);
     if (keyMaterial->macContext != NULL) {
         PK11_DestroyContext(keyMaterial->macContext, PR_TRUE);
     }
 }
--- a/security/nss/lib/ssl/sslt.h
+++ b/security/nss/lib/ssl/sslt.h
@@ -449,17 +449,18 @@ typedef enum {
     ssl_tls13_cookie_xtn = 44,
     ssl_tls13_psk_key_exchange_modes_xtn = 45,
     ssl_tls13_ticket_early_data_info_xtn = 46, /* Deprecated. */
     ssl_tls13_certificate_authorities_xtn = 47,
     ssl_signature_algorithms_cert_xtn = 50,
     ssl_tls13_key_share_xtn = 51,
     ssl_next_proto_nego_xtn = 13172, /* Deprecated. */
     ssl_renegotiation_info_xtn = 0xff01,
-    ssl_tls13_short_header_xtn = 0xff03 /* Deprecated. */
+    ssl_tls13_short_header_xtn = 0xff03, /* Deprecated. */
+    ssl_tls13_encrypted_sni_xtn = 0xffce,
 } SSLExtensionType;
 
 /* This is the old name for the supported_groups extensions. */
 #define ssl_elliptic_curves_xtn ssl_supported_groups_xtn
 
 /* SSL_MAX_EXTENSIONS includes the maximum number of extensions that are
  * supported for any single message type.  That is, a ClientHello; ServerHello
  * and TLS 1.3 NewSessionTicket and HelloRetryRequest extensions have fewer. */
--- a/security/nss/lib/ssl/tls13con.c
+++ b/security/nss/lib/ssl/tls13con.c
@@ -16,16 +16,17 @@
 #include "secmod.h"
 #include "sslimpl.h"
 #include "sslproto.h"
 #include "sslerr.h"
 #include "ssl3exthandle.h"
 #include "tls13hkdf.h"
 #include "tls13con.h"
 #include "tls13err.h"
+#include "tls13esni.h"
 #include "tls13exthandle.h"
 #include "tls13hashstate.h"
 
 static SECStatus tls13_SetCipherSpec(sslSocket *ss, PRUint16 epoch,
                                      CipherSpecDirection install,
                                      PRBool deleteSecret);
 static SECStatus tls13_AESGCM(
     ssl3KeyMaterial *keys,
@@ -128,31 +129,16 @@ const char keylogLabelClientTrafficSecre
 const char keylogLabelServerTrafficSecret[] = "SERVER_TRAFFIC_SECRET_0";
 const char keylogLabelEarlyExporterSecret[] = "EARLY_EXPORTER_SECRET";
 const char keylogLabelExporterSecret[] = "EXPORTER_SECRET";
 
 /* Belt and suspenders in case we ever add a TLS 1.4. */
 PR_STATIC_ASSERT(SSL_LIBRARY_VERSION_MAX_SUPPORTED <=
                  SSL_LIBRARY_VERSION_TLS_1_3);
 
-/* Use this instead of FATAL_ERROR when no alert shall be sent. */
-#define LOG_ERROR(ss, prError)                                                     \
-    do {                                                                           \
-        SSL_TRC(3, ("%d: TLS13[%d]: fatal error %d in %s (%s:%d)",                 \
-                    SSL_GETPID(), ss->fd, prError, __func__, __FILE__, __LINE__)); \
-        PORT_SetError(prError);                                                    \
-    } while (0)
-
-/* Log an error and generate an alert because something is irreparably wrong. */
-#define FATAL_ERROR(ss, prError, desc)       \
-    do {                                     \
-        LOG_ERROR(ss, prError);              \
-        tls13_FatalError(ss, prError, desc); \
-    } while (0)
-
 void
 tls13_FatalError(sslSocket *ss, PRErrorCode prError, SSL3AlertDescription desc)
 {
     PORT_Assert(desc != internal_error); /* These should never happen */
     (void)SSL3_SendAlert(ss, alert_fatal, desc);
     PORT_SetError(prError);
 }
 
@@ -350,46 +336,59 @@ tls13_ComputeHash(sslSocket *ss, SSL3Has
         return SECFailure;
     }
     hashes->len = tls13_GetHashSize(ss);
 
     return SECSuccess;
 }
 
 SECStatus
-tls13_CreateKeyShare(sslSocket *ss, const sslNamedGroupDef *groupDef)
+tls13_CreateKeyShare(sslSocket *ss, const sslNamedGroupDef *groupDef,
+                     sslEphemeralKeyPair **keyPair)
 {
     SECStatus rv;
-    sslEphemeralKeyPair *keyPair = NULL;
     const ssl3DHParams *params;
 
     PORT_Assert(groupDef);
     switch (groupDef->keaType) {
         case ssl_kea_ecdh:
-            rv = ssl_CreateECDHEphemeralKeyPair(ss, groupDef, &keyPair);
+            rv = ssl_CreateECDHEphemeralKeyPair(ss, groupDef, keyPair);
             if (rv != SECSuccess) {
                 return SECFailure;
             }
             break;
         case ssl_kea_dh:
             params = ssl_GetDHEParams(groupDef);
             PORT_Assert(params->name != ssl_grp_ffdhe_custom);
-            rv = ssl_CreateDHEKeyPair(groupDef, params, &keyPair);
+            rv = ssl_CreateDHEKeyPair(groupDef, params, keyPair);
             if (rv != SECSuccess) {
                 return SECFailure;
             }
             break;
         default:
             PORT_Assert(0);
             PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
             return SECFailure;
     }
 
+    return rv;
+}
+
+SECStatus
+tls13_AddKeyShare(sslSocket *ss, const sslNamedGroupDef *groupDef)
+{
+    sslEphemeralKeyPair *keyPair = NULL;
+    SECStatus rv;
+
+    rv = tls13_CreateKeyShare(ss, groupDef, &keyPair);
+    if (rv != SECSuccess) {
+        return SECFailure;
+    }
     PR_APPEND_LINK(&keyPair->link, &ss->ephemeralKeyPairs);
-    return rv;
+    return SECSuccess;
 }
 
 SECStatus
 SSL_SendAdditionalKeyShares(PRFileDesc *fd, unsigned int count)
 {
     sslSocket *ss = ssl_FindSocket(fd);
     if (!ss) {
         PORT_SetError(SEC_ERROR_INVALID_ARGS);
@@ -409,30 +408,36 @@ SSL_SendAdditionalKeyShares(PRFileDesc *
 SECStatus
 tls13_SetupClientHello(sslSocket *ss)
 {
     unsigned int i;
     SSL3Statistics *ssl3stats = SSL_GetStatistics();
     NewSessionTicket *session_ticket = NULL;
     sslSessionID *sid = ss->sec.ci.sid;
     unsigned int numShares = 0;
+    SECStatus rv;
 
     PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
     PORT_Assert(ss->opt.noLocks || ssl_HaveXmitBufLock(ss));
     PORT_Assert(PR_CLIST_IS_EMPTY(&ss->ephemeralKeyPairs));
 
+    /* Do encrypted SNI. This may create a key share as a side effect. */
+    rv = tls13_ClientSetupESNI(ss);
+    if (rv != SECSuccess) {
+        return SECFailure;
+    }
+
     /* Select the first enabled group.
      * TODO(ekr@rtfm.com): be smarter about offering the group
      * that the other side negotiated if we are resuming. */
     for (i = 0; i < SSL_NAMED_GROUP_COUNT; ++i) {
-        SECStatus rv;
         if (!ss->namedGroupPreferences[i]) {
             continue;
         }
-        rv = tls13_CreateKeyShare(ss, ss->namedGroupPreferences[i]);
+        rv = tls13_AddKeyShare(ss, ss->namedGroupPreferences[i]);
         if (rv != SECSuccess) {
             return SECFailure;
         }
         if (++numShares > ss->additionalShares) {
             break;
         }
     }
 
@@ -451,18 +456,16 @@ tls13_SetupClientHello(sslSocket *ss)
     session_ticket = &sid->u.ssl3.locked.sessionTicket;
     PORT_Assert(session_ticket && session_ticket->ticket.data);
 
     if (ssl_TicketTimeValid(session_ticket)) {
         ss->statelessResume = PR_TRUE;
     }
 
     if (ss->statelessResume) {
-        SECStatus rv;
-
         PORT_Assert(ss->sec.ci.sid);
         rv = tls13_RecoverWrappedSharedSecret(ss, ss->sec.ci.sid);
         if (rv != SECSuccess) {
             FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error);
             SSL_AtomicIncrementLong(&ssl3stats->sch_sid_cache_not_ok);
             ssl_UncacheSessionID(ss);
             ssl_FreeSID(ss->sec.ci.sid);
             ss->sec.ci.sid = NULL;
@@ -482,17 +485,17 @@ tls13_SetupClientHello(sslSocket *ss)
             return SECFailure;
         }
     }
 
     return SECSuccess;
 }
 
 static SECStatus
-tls13_ImportDHEKeyShare(sslSocket *ss, SECKEYPublicKey *peerKey,
+tls13_ImportDHEKeyShare(SECKEYPublicKey *peerKey,
                         PRUint8 *b, PRUint32 length,
                         SECKEYPublicKey *pubKey)
 {
     SECStatus rv;
     SECItem publicValue = { siBuffer, NULL, 0 };
 
     publicValue.data = b;
     publicValue.len = length;
@@ -513,66 +516,72 @@ tls13_ImportDHEKeyShare(sslSocket *ss, S
     rv = SECITEM_CopyItem(peerKey->arena, &peerKey->u.dh.publicValue,
                           &publicValue);
     if (rv != SECSuccess)
         return SECFailure;
 
     return SECSuccess;
 }
 
-static SECStatus
+SECStatus
 tls13_HandleKeyShare(sslSocket *ss,
                      TLS13KeyShareEntry *entry,
-                     sslKeyPair *keyPair)
+                     sslKeyPair *keyPair,
+                     SSLHashType hash,
+                     PK11SymKey **out)
 {
     PORTCheapArenaPool arena;
     SECKEYPublicKey *peerKey;
     CK_MECHANISM_TYPE mechanism;
     PRErrorCode errorCode;
+    PK11SymKey *key;
     SECStatus rv;
+    int keySize = 0;
 
     PORT_InitCheapArena(&arena, DER_DEFAULT_CHUNKSIZE);
     peerKey = PORT_ArenaZNew(&arena.arena, SECKEYPublicKey);
     if (peerKey == NULL) {
         goto loser;
     }
     peerKey->arena = &arena.arena;
     peerKey->pkcs11Slot = NULL;
     peerKey->pkcs11ID = CK_INVALID_HANDLE;
 
     switch (entry->group->keaType) {
         case ssl_kea_ecdh:
-            rv = ssl_ImportECDHKeyShare(ss, peerKey,
+            rv = ssl_ImportECDHKeyShare(peerKey,
                                         entry->key_exchange.data,
                                         entry->key_exchange.len,
                                         entry->group);
             mechanism = CKM_ECDH1_DERIVE;
             break;
         case ssl_kea_dh:
-            rv = tls13_ImportDHEKeyShare(ss, peerKey,
+            rv = tls13_ImportDHEKeyShare(peerKey,
                                          entry->key_exchange.data,
                                          entry->key_exchange.len,
                                          keyPair->pubKey);
             mechanism = CKM_DH_PKCS_DERIVE;
+            keySize = peerKey->u.dh.publicValue.len;
             break;
         default:
             PORT_Assert(0);
             goto loser;
     }
     if (rv != SECSuccess) {
         goto loser;
     }
 
-    ss->ssl3.hs.dheSecret = PK11_PubDeriveWithKDF(
+    key = PK11_PubDeriveWithKDF(
         keyPair->privKey, peerKey, PR_FALSE, NULL, NULL, mechanism,
-        tls13_GetHkdfMechanism(ss), CKA_DERIVE, 0, CKD_NULL, NULL, NULL);
-    if (!ss->ssl3.hs.dheSecret) {
+        tls13_GetHkdfMechanismForHash(hash), CKA_DERIVE, keySize, CKD_NULL, NULL, NULL);
+    if (!key) {
         ssl_MapLowLevelError(SSL_ERROR_KEY_EXCHANGE_FAILURE);
         goto loser;
     }
+    *out = key;
     PORT_DestroyCheapArena(&arena);
     return SECSuccess;
 
 loser:
     PORT_DestroyCheapArena(&arena);
     errorCode = PORT_GetError(); /* don't overwrite the error code */
     tls13_FatalError(ss, errorCode, illegal_parameter);
     return SECFailure;
@@ -2020,17 +2029,17 @@ tls13_HandleClientKeyShare(sslSocket *ss
 
     PORT_Assert(ss->opt.noLocks || ssl_HaveRecvBufLock(ss));
     PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
     PORT_Assert(peerShare);
 
     tls13_SetKeyExchangeType(ss, peerShare->group);
 
     /* Generate our key */
-    rv = tls13_CreateKeyShare(ss, peerShare->group);
+    rv = tls13_AddKeyShare(ss, peerShare->group);
     if (rv != SECSuccess) {
         return rv;
     }
 
     /* 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));
@@ -2040,17 +2049,19 @@ tls13_HandleClientKeyShare(sslSocket *ss
 
     /* Register the sender */
     rv = ssl3_RegisterExtensionSender(ss, &ss->xtnData, ssl_tls13_key_share_xtn,
                                       tls13_ServerSendKeyShareXtn);
     if (rv != SECSuccess) {
         return SECFailure; /* Error code set already. */
     }
 
-    rv = tls13_HandleKeyShare(ss, peerShare, keyPair->keys);
+    rv = tls13_HandleKeyShare(ss, peerShare, keyPair->keys,
+                              tls13_GetHash(ss),
+                              &ss->ssl3.hs.dheSecret);
     return rv; /* Error code set already. */
 }
 
 /*
  *     [draft-ietf-tls-tls13-11] Section 6.3.3.2
  *
  *     opaque DistinguishedName<1..2^16-1>;
  *
@@ -2307,18 +2318,18 @@ tls13_HandleCertificateRequest(sslSocket
         return SECFailure;
     }
 
     ss->ssl3.hs.clientCertRequested = PR_TRUE;
     TLS13_SET_HS_STATE(ss, wait_server_cert);
     return SECSuccess;
 }
 
-static PRBool
-tls13_CanRequestClientAuth(sslSocket *ss)
+PRBool
+tls13_ShouldRequestClientAuth(sslSocket *ss)
 {
     return ss->opt.requestCertificate &&
            ss->ssl3.hs.kea_def->authKeyType != ssl_auth_psk;
 }
 
 static SECStatus
 tls13_SendEncryptedServerSequence(sslSocket *ss)
 {
@@ -2345,17 +2356,17 @@ tls13_SendEncryptedServerSequence(sslSoc
         }
     }
 
     rv = tls13_SendEncryptedExtensions(ss);
     if (rv != SECSuccess) {
         return SECFailure; /* error code is set. */
     }
 
-    if (tls13_CanRequestClientAuth(ss)) {
+    if (tls13_ShouldRequestClientAuth(ss)) {
         rv = tls13_SendCertificateRequest(ss);
         if (rv != SECSuccess) {
             return SECFailure; /* error code is set. */
         }
     }
     if (ss->ssl3.hs.signatureScheme != ssl_sig_none) {
         SECKEYPrivateKey *svrPrivKey;
 
@@ -2464,17 +2475,17 @@ tls13_SendServerHelloSequence(sslSocket 
 
         rv = tls13_SetCipherSpec(ss,
                                  TrafficKeyHandshake,
                                  CipherSpecRead, PR_FALSE);
         if (rv != SECSuccess) {
             LOG_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE);
             return SECFailure;
         }
-        if (tls13_CanRequestClientAuth(ss)) {
+        if (tls13_ShouldRequestClientAuth(ss)) {
             TLS13_SET_HS_STATE(ss, wait_client_cert);
         } else {
             TLS13_SET_HS_STATE(ss, wait_finished);
         }
     }
 
     ss->ssl3.hs.serverHelloTime = ssl_TimeUsec();
     return SECSuccess;
@@ -2640,17 +2651,19 @@ tls13_HandleServerKeyShare(sslSocket *ss
     keyPair = ssl_LookupEphemeralKeyPair(ss, entry->group);
     if (!keyPair) {
         FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_KEY_SHARE, illegal_parameter);
         return SECFailure;
     }
 
     PORT_Assert(ssl_NamedGroupEnabled(ss, entry->group));
 
-    rv = tls13_HandleKeyShare(ss, entry, keyPair->keys);
+    rv = tls13_HandleKeyShare(ss, entry, keyPair->keys,
+                              tls13_GetHash(ss),
+                              &ss->ssl3.hs.dheSecret);
     if (rv != SECSuccess)
         return SECFailure; /* Error code set by caller. */
 
     tls13_SetKeyExchangeType(ss, entry->group);
     ss->sec.keaKeyBits = SECKEY_PublicKeyStrengthInBits(keyPair->keys->pubKey);
 
     return SECSuccess;
 }
@@ -3196,16 +3209,31 @@ tls13_SetSpecRecordVersion(sslSocket *ss
         spec->recordVersion = SSL_LIBRARY_VERSION_DTLS_1_2_WIRE;
     } else {
         spec->recordVersion = SSL_LIBRARY_VERSION_TLS_1_2;
     }
     SSL_TRC(10, ("%d: TLS13[%d]: set spec=%d record version to 0x%04x",
                  SSL_GETPID(), ss->fd, spec, spec->recordVersion));
 }
 
+SSLAEADCipher
+tls13_GetAead(const ssl3BulkCipherDef *cipherDef)
+{
+    switch (cipherDef->calg) {
+        case ssl_calg_aes_gcm:
+            return tls13_AESGCM;
+        case ssl_calg_chacha20:
+            return tls13_ChaCha20Poly1305;
+        default:
+            PORT_Assert(PR_FALSE);
+            PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+            return NULL;
+    }
+}
+
 static SECStatus
 tls13_SetupPendingCipherSpec(sslSocket *ss, ssl3CipherSpec *spec)
 {
     ssl3CipherSuite suite = ss->ssl3.hs.cipher_suite;
 
     PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
     PORT_Assert(spec->epoch);
 
@@ -3219,26 +3247,19 @@ tls13_SetupPendingCipherSpec(sslSocket *
     if (IS_DTLS(ss) && spec->direction == CipherSpecRead) {
         ssl_CipherSpecAddRef(spec);
     }
 
     SSL_TRC(3, ("%d: TLS13[%d]: Set Pending Cipher Suite to 0x%04x",
                 SSL_GETPID(), ss->fd, suite));
 
     spec->cipherDef = ssl_GetBulkCipherDef(ssl_LookupCipherSuiteDef(suite));
-    switch (spec->cipherDef->calg) {
-        case ssl_calg_aes_gcm:
-            spec->aead = tls13_AESGCM;
-            break;
-        case ssl_calg_chacha20:
-            spec->aead = tls13_ChaCha20Poly1305;
-            break;
-        default:
-            PORT_Assert(0);
-            return SECFailure;
+    spec->aead = tls13_GetAead(spec->cipherDef);
+    if (!spec->aead) {
+        return SECFailure;
     }
 
     if (spec->epoch == TrafficKeyEarlyApplicationData) {
         spec->earlyDataRemaining =
             ss->sec.ci.sid->u.ssl3.locked.sessionTicket.max_early_data_size;
     }
 
     tls13_SetSpecRecordVersion(ss, spec);
@@ -3410,19 +3431,40 @@ tls13_ComputeHandshakeHashes(sslSocket *
 
     return SECSuccess;
 
 loser:
     PK11_DestroyContext(ctx, PR_TRUE);
     return SECFailure;
 }
 
+TLS13KeyShareEntry *
+tls13_CopyKeyShareEntry(TLS13KeyShareEntry *o)
+{
+    TLS13KeyShareEntry *n;
+
+    PORT_Assert(o);
+    n = PORT_ZNew(TLS13KeyShareEntry);
+    if (!n) {
+        return NULL;
+    }
+
+    if (SECSuccess != SECITEM_CopyItem(NULL, &n->key_exchange, &o->key_exchange)) {
+        PORT_Free(n);
+    }
+    n->group = o->group;
+    return n;
+}
+
 void
 tls13_DestroyKeyShareEntry(TLS13KeyShareEntry *offer)
 {
+    if (!offer) {
+        return;
+    }
     SECITEM_ZfreeItem(&offer->key_exchange, PR_FALSE);
     PORT_ZFree(offer, sizeof(*offer));
 }
 
 void
 tls13_DestroyKeyShares(PRCList *list)
 {
     PRCList *cur_p;
@@ -3533,17 +3575,17 @@ tls13_AESGCM(ssl3KeyMaterial *keys,
              const unsigned char *in,
              int inlen,
              const unsigned char *additionalData,
              int additionalDataLen)
 {
     CK_GCM_PARAMS gcmParams;
     unsigned char nonce[12];
 
-    PORT_Assert(additionalDataLen > 8);
+    PORT_Assert(additionalDataLen >= 8);
     memset(&gcmParams, 0, sizeof(gcmParams));
     gcmParams.pIv = nonce;
     gcmParams.ulIvLen = sizeof(nonce);
     gcmParams.pAAD = (PRUint8 *)(additionalData + 8);
     gcmParams.ulAADLen = additionalDataLen - 8;
     gcmParams.ulTagBits = 128; /* GCM measures tag length in bits. */
 
     tls13_WriteNonce(keys, additionalData, 8,
@@ -3610,17 +3652,33 @@ tls13_HandleEncryptedExtensions(sslSocke
     /* If we are doing 0-RTT, then we already have an ALPN value. Stash
      * it for comparison. */
     if (ss->ssl3.hs.zeroRttState == ssl_0rtt_sent &&
         ss->xtnData.nextProtoState == SSL_NEXT_PROTO_EARLY_VALUE) {
         oldAlpn = ss->xtnData.nextProto;
         ss->xtnData.nextProto.data = NULL;
         ss->xtnData.nextProtoState = SSL_NEXT_PROTO_NO_SUPPORT;
     }
-    rv = ssl3_HandleExtensions(ss, &b, &length, ssl_hs_encrypted_extensions);
+
+    rv = ssl3_ParseExtensions(ss, &b, &length);
+    if (rv != SECSuccess) {
+        return SECFailure; /* Error code set below */
+    }
+
+    /* If we sent ESNI, check the nonce. */
+    if (ss->xtnData.esniPrivateKey) {
+        PORT_Assert(ssl3_ExtensionAdvertised(ss, ssl_tls13_encrypted_sni_xtn));
+        rv = tls13_ClientCheckEsniXtn(ss);
+        if (rv != SECSuccess) {
+            return SECFailure;
+        }
+    }
+
+    /* Handle the rest of the extensions. */
+    rv = ssl3_HandleParsedExtensions(ss, ssl_hs_encrypted_extensions);
     if (rv != SECSuccess) {
         return SECFailure; /* Error code set below */
     }
 
     /* We can only get here if we offered 0-RTT. */
     if (ssl3_ExtensionNegotiated(ss, ssl_tls13_early_data_xtn)) {
         PORT_Assert(ss->ssl3.hs.zeroRttState == ssl_0rtt_sent);
         if (!ss->statelessResume) {
@@ -4173,17 +4231,17 @@ tls13_ServerHandleFinished(sslSocket *ss
                 SSL_GETPID(), ss->fd));
 
     rv = tls13_CommonHandleFinished(ss, ss->ssl3.hs.clientHsTrafficSecret,
                                     b, length);
     if (rv != SECSuccess) {
         return SECFailure;
     }
 
-    if (!tls13_CanRequestClientAuth(ss) &&
+    if (!tls13_ShouldRequestClientAuth(ss) &&
         (ss->ssl3.hs.zeroRttState != ssl_0rtt_done)) {
         dtls_ReceivedFirstMessageInFlight(ss);
     }
 
     rv = tls13_SetCipherSpec(ss, TrafficKeyApplicationData,
                              CipherSpecRead, PR_FALSE);
     if (rv != SECSuccess) {
         FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error);
@@ -4742,17 +4800,18 @@ static const struct {
     { ssl_signed_cert_timestamp_xtn, _M3(client_hello, certificate_request,
                                          certificate) },
     { ssl_cert_status_xtn, _M3(client_hello, certificate_request,
                                certificate) },
     { ssl_tls13_cookie_xtn, _M2(client_hello, hello_retry_request) },
     { ssl_tls13_certificate_authorities_xtn, _M1(certificate_request) },
     { ssl_tls13_supported_versions_xtn, _M3(client_hello, server_hello,
                                             hello_retry_request) },
-    { ssl_record_size_limit_xtn, _M2(client_hello, encrypted_extensions) }
+    { ssl_record_size_limit_xtn, _M2(client_hello, encrypted_extensions) },
+    { ssl_tls13_encrypted_sni_xtn, _M2(client_hello, encrypted_extensions) }
 };
 
 tls13ExtensionStatus
 tls13_ExtensionStatus(PRUint16 extension, SSLHandshakeType message)
 {
     unsigned int i;
 
     PORT_Assert((message == ssl_hs_client_hello) ||
@@ -5226,17 +5285,17 @@ tls13_HandleEndOfEarlyData(sslSocket *ss
     rv = tls13_SetCipherSpec(ss, TrafficKeyHandshake,
                              CipherSpecRead, PR_FALSE);
     if (rv != SECSuccess) {
         PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
         return SECFailure;
     }
 
     ss->ssl3.hs.zeroRttState = ssl_0rtt_done;
-    if (tls13_CanRequestClientAuth(ss)) {
+    if (tls13_ShouldRequestClientAuth(ss)) {
         TLS13_SET_HS_STATE(ss, wait_client_cert);
     } else {
         TLS13_SET_HS_STATE(ss, wait_finished);
     }
     return SECSuccess;
 }
 
 SECStatus
--- a/security/nss/lib/ssl/tls13con.h
+++ b/security/nss/lib/ssl/tls13con.h
@@ -80,19 +80,27 @@ SECStatus tls13_HandlePostHelloHandshake
 SECStatus tls13_ConstructHelloRetryRequest(sslSocket *ss,
                                            ssl3CipherSuite cipherSuite,
                                            const sslNamedGroupDef *selectedGroup,
                                            PRUint8 *cookie,
                                            unsigned int cookieLen,
                                            sslBuffer *buffer);
 SECStatus tls13_HandleHelloRetryRequest(sslSocket *ss, const PRUint8 *b,
                                         PRUint32 length);
+SECStatus tls13_HandleKeyShare(sslSocket *ss,
+                               TLS13KeyShareEntry *entry,
+                               sslKeyPair *keyPair,
+                               SSLHashType hash,
+                               PK11SymKey **out);
+TLS13KeyShareEntry *tls13_CopyKeyShareEntry(TLS13KeyShareEntry *o);
 void tls13_DestroyKeyShareEntry(TLS13KeyShareEntry *entry);
 void tls13_DestroyKeyShares(PRCList *list);
-SECStatus tls13_CreateKeyShare(sslSocket *ss, const sslNamedGroupDef *groupDef);
+SECStatus tls13_CreateKeyShare(sslSocket *ss, const sslNamedGroupDef *groupDef,
+                               sslEphemeralKeyPair **keyPair);
+SECStatus tls13_AddKeyShare(sslSocket *ss, const sslNamedGroupDef *groupDef);
 void tls13_DestroyEarlyData(PRCList *list);
 SECStatus tls13_SetAlertCipherSpec(sslSocket *ss);
 tls13ExtensionStatus tls13_ExtensionStatus(PRUint16 extension,
                                            SSLHandshakeType message);
 SECStatus tls13_ProtectRecord(sslSocket *ss,
                               ssl3CipherSpec *cwSpec,
                               SSLContentType type,
                               const PRUint8 *pIn,
@@ -101,25 +109,42 @@ SECStatus tls13_ProtectRecord(sslSocket 
 PRInt32 tls13_Read0RttData(sslSocket *ss, void *buf, PRInt32 len);
 SECStatus tls13_HandleEarlyApplicationData(sslSocket *ss, sslBuffer *origBuf);
 PRBool tls13_ClientAllow0Rtt(const sslSocket *ss, const sslSessionID *sid);
 PRUint16 tls13_EncodeDraftVersion(SSL3ProtocolVersion version,
                                   SSLProtocolVariant variant);
 SECStatus tls13_ClientReadSupportedVersion(sslSocket *ss);
 SECStatus tls13_NegotiateVersion(sslSocket *ss,
                                  const TLSExtension *supported_versions);
+PRBool tls13_ShouldRequestClientAuth(sslSocket *ss);
 
 PRBool tls13_IsReplay(const sslSocket *ss, const sslSessionID *sid);
 void tls13_AntiReplayRollover(PRTime now);
 
 SECStatus SSLExp_SetupAntiReplay(PRTime window, unsigned int k,
                                  unsigned int bits);
 
 SECStatus SSLExp_HelloRetryRequestCallback(PRFileDesc *fd,
                                            SSLHelloRetryRequestCallback cb,
                                            void *arg);
 SECStatus tls13_SendKeyUpdate(sslSocket *ss, tls13KeyUpdateRequest request,
                               PRBool buffer);
 SECStatus SSLExp_KeyUpdate(PRFileDesc *fd, PRBool requestUpdate);
 PRBool tls13_MaybeTls13(sslSocket *ss);
+SSLAEADCipher tls13_GetAead(const ssl3BulkCipherDef *cipherDef);
 void tls13_SetSpecRecordVersion(sslSocket *ss, ssl3CipherSpec *spec);
 
+/* Use this instead of FATAL_ERROR when no alert shall be sent. */
+#define LOG_ERROR(ss, prError)                                                     \
+    do {                                                                           \
+        SSL_TRC(3, ("%d: TLS13[%d]: fatal error %d in %s (%s:%d)",                 \
+                    SSL_GETPID(), ss->fd, prError, __func__, __FILE__, __LINE__)); \
+        PORT_SetError(prError);                                                    \
+    } while (0)
+
+/* Log an error and generate an alert because something is irreparably wrong. */
+#define FATAL_ERROR(ss, prError, desc)       \
+    do {                                     \
+        LOG_ERROR(ss, prError);              \
+        tls13_FatalError(ss, prError, desc); \
+    } while (0)
+
 #endif /* __tls13con_h_ */
new file mode 100644
--- /dev/null
+++ b/security/nss/lib/ssl/tls13esni.c
@@ -0,0 +1,844 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#define TLS13_ESNI_VERSION 0xff01
+
+/*
+ *  struct {
+ *      uint16 version;
+ *      uint8 checksum[4];
+ *      KeyShareEntry keys<4..2^16-1>;
+ *      CipherSuite cipher_suites<2..2^16-2>;
+ *      uint16 padded_length;
+ *      uint64 not_before;
+ *      uint64 not_after;
+ *      Extension extensions<0..2^16-1>;
+ *  } ESNIKeys;
+ */
+#include "nss.h"
+#include "pk11func.h"
+#include "ssl.h"
+#include "sslproto.h"
+#include "sslimpl.h"
+#include "ssl3exthandle.h"
+#include "tls13esni.h"
+#include "tls13exthandle.h"
+#include "tls13hkdf.h"
+
+const char kHkdfPurposeEsniKey[] = "esni key";
+const char kHkdfPurposeEsniIv[] = "esni iv";
+
+void
+tls13_DestroyESNIKeys(sslEsniKeys *keys)
+{
+    if (!keys) {
+        return;
+    }
+    SECITEM_FreeItem(&keys->data, PR_FALSE);
+    PORT_Free((void *)keys->dummySni);
+    tls13_DestroyKeyShares(&keys->keyShares);
+    ssl_FreeEphemeralKeyPair(keys->privKey);
+    SECITEM_FreeItem(&keys->suites, PR_FALSE);
+    PORT_ZFree(keys, sizeof(sslEsniKeys));
+}
+
+sslEsniKeys *
+tls13_CopyESNIKeys(sslEsniKeys *okeys)
+{
+    sslEsniKeys *nkeys;
+    SECStatus rv;
+
+    PORT_Assert(okeys);
+
+    nkeys = PORT_ZNew(sslEsniKeys);
+    if (!nkeys) {
+        return NULL;
+    }
+    PR_INIT_CLIST(&nkeys->keyShares);
+    rv = SECITEM_CopyItem(NULL, &nkeys->data, &okeys->data);
+    if (rv != SECSuccess) {
+        goto loser;
+    }
+    if (okeys->dummySni) {
+        nkeys->dummySni = PORT_Strdup(okeys->dummySni);
+        if (!nkeys->dummySni) {
+            goto loser;
+        }
+    }
+    for (PRCList *cur_p = PR_LIST_HEAD(&okeys->keyShares);
+         cur_p != &okeys->keyShares;
+         cur_p = PR_NEXT_LINK(cur_p)) {
+        TLS13KeyShareEntry *copy = tls13_CopyKeyShareEntry(
+            (TLS13KeyShareEntry *)cur_p);
+        if (!copy) {
+            goto loser;
+        }
+        PR_APPEND_LINK(&copy->link, &nkeys->keyShares);
+    }
+    if (okeys->privKey) {
+        nkeys->privKey = ssl_CopyEphemeralKeyPair(okeys->privKey);
+        if (!nkeys->privKey) {
+            goto loser;
+        }
+    }
+    rv = SECITEM_CopyItem(NULL, &nkeys->suites, &okeys->suites);
+    if (rv != SECSuccess) {
+        goto loser;
+    }
+    nkeys->paddedLength = okeys->paddedLength;
+    nkeys->notBefore = okeys->notBefore;
+    nkeys->notAfter = okeys->notAfter;
+    return nkeys;
+
+loser:
+    tls13_DestroyESNIKeys(nkeys);
+    return NULL;
+}
+
+/* Checksum is a 4-byte array. */
+static SECStatus
+tls13_ComputeESNIKeysChecksum(const PRUint8 *buf, unsigned int len,
+                              PRUint8 *checksum)
+{
+    SECItem copy;
+    SECStatus rv;
+    PRUint8 sha256[32];
+
+    rv = SECITEM_MakeItem(NULL, &copy, buf, len);
+    if (rv != SECSuccess) {
+        return SECFailure;
+    }
+
+    /* Stomp the checksum. */
+    PORT_Memset(copy.data + 2, 0, 4);
+
+    rv = PK11_HashBuf(ssl3_HashTypeToOID(ssl_hash_sha256),
+                      sha256,
+                      copy.data, copy.len);
+    SECITEM_FreeItem(&copy, PR_FALSE);
+    if (rv != SECSuccess) {
+        return SECFailure;
+    }
+    PORT_Memcpy(checksum, sha256, 4);
+    return SECSuccess;
+}
+
+static SECStatus
+tls13_DecodeESNIKeys(SECItem *data, sslEsniKeys **keysp)
+{
+    SECStatus rv;
+    sslReadBuffer tmp;
+    PRUint64 tmpn;
+    sslEsniKeys *keys;
+    PRUint8 checksum[4];
+    sslReader rdr = SSL_READER(data->data, data->len);
+
+    rv = sslRead_ReadNumber(&rdr, 2, &tmpn);
+    if (rv != SECSuccess) {
+        return SECFailure;
+    }
+    if (tmpn != TLS13_ESNI_VERSION) {
+        PORT_SetError(SSL_ERROR_UNSUPPORTED_VERSION);
+        return SECFailure;
+    }
+    keys = PORT_ZNew(sslEsniKeys);
+    if (!keys) {
+        return SECFailure;
+    }
+    PR_INIT_CLIST(&keys->keyShares);
+
+    /* Make a copy. */
+    rv = SECITEM_CopyItem(NULL, &keys->data, data);
+    if (rv != SECSuccess) {
+        goto loser;
+    }
+
+    rv = tls13_ComputeESNIKeysChecksum(data->data, data->len, checksum);
+    if (rv != SECSuccess) {
+        goto loser;
+    }
+
+    /* Read and check checksum. */
+    rv = sslRead_Read(&rdr, 4, &tmp);
+    if (rv != SECSuccess) {
+        goto loser;
+    }
+
+    if (0 != NSS_SecureMemcmp(tmp.buf, checksum, 4)) {
+        goto loser;
+    }
+
+    /* Parse the key shares. */
+    rv = sslRead_ReadVariable(&rdr, 2, &tmp);
+    if (rv != SECSuccess) {
+        goto loser;
+    }
+
+    sslReader rdr2 = SSL_READER(tmp.buf, tmp.len);
+    while (SSL_READER_REMAINING(&rdr2)) {
+        TLS13KeyShareEntry *ks = NULL;
+
+        rv = tls13_DecodeKeyShareEntry(&rdr2, &ks);
+        if (rv != SECSuccess) {
+            goto loser;
+        }
+
+        if (ks) {
+            PR_APPEND_LINK(&ks->link, &keys->keyShares);
+        }
+    }
+
+    /* Parse cipher suites. */
+    rv = sslRead_ReadVariable(&rdr, 2, &tmp);
+    if (rv != SECSuccess) {
+        goto loser;
+    }
+    /* This can't be odd. */
+    if (tmp.len & 1) {
+        goto loser;
+    }
+    rv = SECITEM_MakeItem(NULL, &keys->suites, (PRUint8 *)tmp.buf, tmp.len);
+    if (rv != SECSuccess) {
+        goto loser;
+    }
+
+    /* Padded Length */
+    rv = sslRead_ReadNumber(&rdr, 2, &tmpn);
+    if (rv != SECSuccess) {
+        goto loser;
+    }
+    keys->paddedLength = (PRUint16)tmpn;
+
+    /* Not Before */
+    rv = sslRead_ReadNumber(&rdr, 8, &keys->notBefore);
+    if (rv != SECSuccess) {
+        goto loser;
+    }
+
+    /* Not After */
+    rv = sslRead_ReadNumber(&rdr, 8, &keys->notAfter);
+    if (rv != SECSuccess) {
+        goto loser;
+    }
+
+    /* Extensions, which we ignore. */
+    rv = sslRead_ReadVariable(&rdr, 2, &tmp);
+    if (rv != SECSuccess) {
+        goto loser;
+    }
+
+    /* Check that this is empty. */
+    if (SSL_READER_REMAINING(&rdr) > 0) {
+        goto loser;
+    }
+
+    *keysp = keys;
+    return SECSuccess;
+
+loser:
+    tls13_DestroyESNIKeys(keys);
+    PORT_SetError(SSL_ERROR_RX_MALFORMED_ESNI_KEYS);
+
+    return SECFailure;
+}
+
+/* Encode an ESNI keys structure. We only allow one key
+ * share. */
+SECStatus
+SSLExp_EncodeESNIKeys(PRUint16 *cipherSuites, unsigned int cipherSuiteCount,
+                      SSLNamedGroup group, SECKEYPublicKey *pubKey,
+                      PRUint16 pad, PRUint64 notBefore, PRUint64 notAfter,
+                      PRUint8 *out, unsigned int *outlen, unsigned int maxlen)
+{
+    unsigned int savedOffset;
+    SECStatus rv;
+    sslBuffer b = SSL_BUFFER_EMPTY;
+
+    rv = sslBuffer_AppendNumber(&b, TLS13_ESNI_VERSION, 2);
+    if (rv != SECSuccess) {
+        goto loser;
+    }
+
+    rv = sslBuffer_Skip(&b, 4, &savedOffset);
+    if (rv != SECSuccess) {
+        goto loser;
+    }
+
+    /* Length of vector. */
+    rv = sslBuffer_AppendNumber(
+        &b, tls13_SizeOfKeyShareEntry(pubKey), 2);
+    if (rv != SECSuccess) {
+        goto loser;
+    }
+
+    /* Our one key share. */
+    rv = tls13_EncodeKeyShareEntry(&b, group, pubKey);
+    if (rv != SECSuccess) {
+        goto loser;
+    }
+
+    /* Cipher suites. */
+    rv = sslBuffer_AppendNumber(&b, cipherSuiteCount * 2, 2);
+    if (rv != SECSuccess) {
+        goto loser;
+    }
+    for (unsigned int i = 0; i < cipherSuiteCount; i++) {
+        rv = sslBuffer_AppendNumber(&b, cipherSuites[i], 2);
+        if (rv != SECSuccess) {
+            goto loser;
+        }
+    }
+
+    /* Padding Length. Fixed for now. */
+    rv = sslBuffer_AppendNumber(&b, pad, 2);
+    if (rv != SECSuccess) {
+        goto loser;
+    }
+
+    /* Start time. */
+    rv = sslBuffer_AppendNumber(&b, notBefore, 8);
+    if (rv != SECSuccess) {
+        goto loser;
+    }
+
+    /* End time. */
+    rv = sslBuffer_AppendNumber(&b, notAfter, 8);
+    if (rv != SECSuccess) {
+        goto loser;
+    }
+
+    /* No extensions. */
+    rv = sslBuffer_AppendNumber(&b, 0, 2);
+    if (rv != SECSuccess) {
+        goto loser;
+    }
+
+    rv = tls13_ComputeESNIKeysChecksum(SSL_BUFFER_BASE(&b),
+                                       SSL_BUFFER_LEN(&b),
+                                       SSL_BUFFER_BASE(&b) + 2);
+    if (rv != SECSuccess) {
+        PORT_Assert(PR_FALSE);
+        goto loser;
+    }
+
+    if (SSL_BUFFER_LEN(&b) > maxlen) {
+        PORT_SetError(SEC_ERROR_INVALID_ARGS);
+        goto loser;
+    }
+    PORT_Memcpy(out, SSL_BUFFER_BASE(&b), SSL_BUFFER_LEN(&b));
+    *outlen = SSL_BUFFER_LEN(&b);
+
+    sslBuffer_Clear(&b);
+    return SECSuccess;
+loser:
+    sslBuffer_Clear(&b);
+    return SECFailure;
+}
+
+SECStatus
+SSLExp_SetESNIKeyPair(PRFileDesc *fd,
+                      SECKEYPrivateKey *privKey,
+                      const PRUint8 *record, unsigned int recordLen)
+{
+    sslSocket *ss;
+    SECStatus rv;
+    sslEsniKeys *keys = NULL;
+    SECKEYPublicKey *pubKey = NULL;
+    SECItem data = { siBuffer, CONST_CAST(PRUint8, record), recordLen };
+    PLArenaPool *arena = NULL;
+
+    ss = ssl_FindSocket(fd);
+    if (!ss) {
+        SSL_DBG(("%d: SSL[%d]: bad socket in %s",
+                 SSL_GETPID(), fd, __FUNCTION__));
+        return SECFailure;
+    }
+
+    rv = tls13_DecodeESNIKeys(&data, &keys);
+    if (rv != SECSuccess) {
+        return SECFailure;
+    }
+
+    /* Check the cipher suites. */
+    (void)ssl3_config_match_init(ss);
+    /* Make sure the cipher suite is OK. */
+    SSLVersionRange vrange = { SSL_LIBRARY_VERSION_TLS_1_3,
+                               SSL_LIBRARY_VERSION_TLS_1_3 };
+
+    sslReader csrdr = SSL_READER(keys->suites.data,
+                                 keys->suites.len);
+    while (SSL_READER_REMAINING(&csrdr)) {
+        PRUint64 asuite;
+
+        rv = sslRead_ReadNumber(&csrdr, 2, &asuite);
+        if (rv != SECSuccess) {
+            goto loser;
+        }
+        const ssl3CipherSuiteCfg *suiteCfg =
+            ssl_LookupCipherSuiteCfg(asuite, ss->cipherSuites);
+        if (!ssl3_config_match(suiteCfg, ss->ssl3.policy, &vrange, ss)) {
+            /* Illegal suite. */
+            PORT_SetError(SEC_ERROR_INVALID_ARGS);
+            goto loser;
+        }
+    }
+
+    if (PR_CLIST_IS_EMPTY(&keys->keyShares)) {
+        PORT_SetError(SEC_ERROR_INVALID_ARGS);
+        goto loser;
+    }
+    if (PR_PREV_LINK(&keys->keyShares) != PR_NEXT_LINK(&keys->keyShares)) {
+        PORT_SetError(SEC_ERROR_INVALID_ARGS);
+        goto loser;
+    }
+    TLS13KeyShareEntry *entry = (TLS13KeyShareEntry *)PR_LIST_HEAD(
+        &keys->keyShares);
+    if (entry->group->keaType != ssl_kea_ecdh) {
+        PORT_SetError(SEC_ERROR_INVALID_ARGS);
+        goto loser;
+    }
+    arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+    if (!arena) {
+        goto loser;
+    }
+    pubKey = PORT_ArenaZNew(arena, SECKEYPublicKey);
+    if (!pubKey) {
+        goto loser;
+    }
+    pubKey->arena = arena;
+    arena = NULL; /* From here, this will be destroyed with the pubkey. */
+    /* Dummy PKCS11 values because this key isn't on a slot. */
+    pubKey->pkcs11Slot = NULL;
+    pubKey->pkcs11ID = CK_INVALID_HANDLE;
+    rv = ssl_ImportECDHKeyShare(pubKey,
+                                entry->key_exchange.data,
+                                entry->key_exchange.len,
+                                entry->group);
+    if (rv != SECSuccess) {
+        goto loser;
+    }
+
+    privKey = SECKEY_CopyPrivateKey(privKey);
+    if (!privKey) {
+        goto loser;
+    }
+    keys->privKey = ssl_NewEphemeralKeyPair(entry->group, privKey, pubKey);
+    if (!keys->privKey) {
+        goto loser;
+    }
+    pubKey = NULL;
+    ss->esniKeys = keys;
+    return SECSuccess;
+
+loser:
+    PORT_FreeArena(arena, PR_FALSE);
+    SECKEY_DestroyPublicKey(pubKey);
+    tls13_DestroyESNIKeys(keys);
+    return SECFailure;
+}
+
+SECStatus
+SSLExp_EnableESNI(PRFileDesc *fd,
+                  const PRUint8 *esniKeys,
+                  unsigned int esniKeysLen,
+                  const char *dummySNI)
+{
+    sslSocket *ss;
+    sslEsniKeys *keys = NULL;
+    SECItem data = { siBuffer, CONST_CAST(PRUint8, esniKeys), esniKeysLen };
+    SECStatus rv;
+
+    ss = ssl_FindSocket(fd);
+    if (!ss) {
+        SSL_DBG(("%d: SSL[%d]: bad socket in %s",
+                 SSL_GETPID(), fd, __FUNCTION__));
+        return SECFailure;
+    }
+
+    rv = tls13_DecodeESNIKeys(&data, &keys);
+    if (rv != SECSuccess) {
+        return SECFailure;
+    }
+
+    if (dummySNI) {
+        keys->dummySni = PORT_Strdup(dummySNI);
+        if (!keys->dummySni) {
+            tls13_DestroyESNIKeys(keys);
+            return SECFailure;
+        }
+    }
+
+    /* Delete in case it was set before. */
+    tls13_DestroyESNIKeys(ss->esniKeys);
+    ss->esniKeys = keys;
+
+    return SECSuccess;
+}
+
+/*
+ * struct {
+ *     opaque record_digest<0..2^16-1>;
+ *     KeyShareEntry esni_key_share;
+ *     Random client_hello_random;
+ * } ESNIContents;
+ */
+SECStatus
+tls13_ComputeESNIKeys(const sslSocket *ss,
+                      TLS13KeyShareEntry *entry,
+                      sslKeyPair *keyPair,
+                      const ssl3CipherSuiteDef *suite,
+                      const PRUint8 *esniKeysHash,
+                      const PRUint8 *keyShareBuf,
+                      unsigned int keyShareBufLen,
+                      const PRUint8 *clientRandom,
+                      ssl3KeyMaterial *keyMat)
+{
+    PK11SymKey *Z = NULL;
+    PK11SymKey *Zx = NULL;
+    SECStatus ret = SECFailure;
+    PRUint8 esniContentsBuf[256]; /* Just big enough. */
+    sslBuffer esniContents = SSL_BUFFER(esniContentsBuf);
+    PRUint8 hash[64];
+    const ssl3BulkCipherDef *cipherDef = ssl_GetBulkCipherDef(suite);
+    size_t keySize = cipherDef->key_size;
+    size_t ivSize = cipherDef->iv_size +
+                    cipherDef->explicit_nonce_size; /* This isn't always going to
+                                                     * work, but it does for
+                                                     * AES-GCM */
+    unsigned int hashSize = tls13_GetHashSizeForHash(suite->prf_hash);
+    SECStatus rv;
+
+    rv = tls13_HandleKeyShare(CONST_CAST(sslSocket, ss), entry, keyPair,
+                              suite->prf_hash, &Z);
+    if (rv != SECSuccess) {
+        goto loser;
+    }
+    rv = tls13_HkdfExtract(NULL, Z, suite->prf_hash, &Zx);
+    if (rv != SECSuccess) {
+        goto loser;
+    }
+
+    /* Encode ESNIContents. */
+    rv = sslBuffer_AppendVariable(&esniContents,
+                                  esniKeysHash, hashSize, 2);
+    if (rv != SECSuccess) {
+        goto loser;
+    }
+    rv = sslBuffer_Append(&esniContents, keyShareBuf, keyShareBufLen);
+    if (rv != SECSuccess) {
+        goto loser;
+    }
+    rv = sslBuffer_Append(&esniContents, clientRandom, SSL3_RANDOM_LENGTH);
+    if (rv != SECSuccess) {
+        goto loser;
+    }
+
+    PORT_Assert(hashSize <= sizeof(hash));
+    rv = PK11_HashBuf(ssl3_HashTypeToOID(suite->prf_hash),
+                      hash,
+                      SSL_BUFFER_BASE(&esniContents),
+                      SSL_BUFFER_LEN(&esniContents));
+    ;
+    if (rv != SECSuccess) {
+        goto loser;
+    }
+
+    rv = tls13_HkdfExpandLabel(Zx, suite->prf_hash,
+                               hash, hashSize,
+                               kHkdfPurposeEsniKey, strlen(kHkdfPurposeEsniKey),
+                               ssl3_Alg2Mech(cipherDef->calg),
+                               keySize,
+                               &keyMat->key);
+    if (rv != SECSuccess) {
+        goto loser;
+    }
+    rv = tls13_HkdfExpandLabelRaw(Zx, suite->prf_hash,
+                                  hash, hashSize,
+                                  kHkdfPurposeEsniIv, strlen(kHkdfPurposeEsniIv),
+                                  keyMat->iv, ivSize);
+    if (rv != SECSuccess) {
+        goto loser;
+    }
+
+    ret = SECSuccess;
+
+loser:
+    PK11_FreeSymKey(Z);
+    PK11_FreeSymKey(Zx);
+    return ret;
+}
+
+/* Set up ESNI. This generates a private key as a side effect. */
+SECStatus
+tls13_ClientSetupESNI(sslSocket *ss)
+{
+    ssl3CipherSuite suite;
+    sslEphemeralKeyPair *keyPair;
+    size_t i;
+    PRCList *cur;
+    SECStatus rv;
+    TLS13KeyShareEntry *share;
+    const sslNamedGroupDef *group = NULL;
+    PRTime now = PR_Now() / PR_USEC_PER_SEC;
+
+    if (!ss->esniKeys) {
+        return SECSuccess;
+    }
+
+    if ((ss->esniKeys->notBefore > now) || (ss->esniKeys->notAfter < now)) {
+        return SECSuccess;
+    }
+
+    /* If we're not sending SNI, don't send ESNI. */
+    if (!ssl_ShouldSendSNIExtension(ss, ss->url)) {
+        return SECSuccess;
+    }
+
+    /* Pick the group. */
+    for (i = 0; i < SSL_NAMED_GROUP_COUNT; ++i) {
+        for (cur = PR_NEXT_LINK(&ss->esniKeys->keyShares);
+             cur != &ss->esniKeys->keyShares;
+             cur = PR_NEXT_LINK(cur)) {
+            if (!ss->namedGroupPreferences[i]) {
+                continue;
+            }
+            share = (TLS13KeyShareEntry *)cur;
+            if (share->group->name == ss->namedGroupPreferences[i]->name) {
+                group = ss->namedGroupPreferences[i];
+                break;
+            }
+        }
+    }
+
+    if (!group) {
+        /* No compatible group. */
+        return SECSuccess;
+    }
+
+    rv = ssl3_NegotiateCipherSuiteInner(ss, &ss->esniKeys->suites,
+                                        SSL_LIBRARY_VERSION_TLS_1_3, &suite);
+    if (rv != SECSuccess) {
+        return SECSuccess;
+    }
+
+    rv = tls13_CreateKeyShare(ss, group, &keyPair);
+    if (rv != SECSuccess) {
+        return SECFailure;
+    }
+
+    ss->xtnData.esniPrivateKey = keyPair;
+    ss->xtnData.esniSuite = suite;
+    ss->xtnData.peerEsniShare = share;
+
+    return SECSuccess;
+}
+
+/*
+ * struct {
+ *     CipherSuite suite;
+ *     KeyShareEntry key_share;
+ *     opaque record_digest<0..2^16-1>;
+ *     opaque encrypted_sni<0..2^16-1>;
+ * } ClientEncryptedSNI;
+ *
+ * struct {
+ *     ServerNameList sni;
+ *     opaque zeros[ESNIKeys.padded_length - length(sni)];
+ * } PaddedServerNameList;
+ *
+ * struct {
+ *     uint8 nonce[16];
+ *     PaddedServerNameList realSNI;
+ * } ClientESNIInner;
+ */
+SECStatus
+tls13_FormatEsniAADInput(sslBuffer *aadInput,
+                         PRUint8 *keyShare, unsigned int keyShareLen)
+{
+    SECStatus rv;
+
+    /* 8 bytes of 0 for the sequence number. */
+    rv = sslBuffer_AppendNumber(aadInput, 0, 8);
+    if (rv != SECSuccess) {
+        return SECFailure;
+    }
+
+    /* Key share. */
+    PORT_Assert(keyShareLen > 0);
+    rv = sslBuffer_Append(aadInput, keyShare, keyShareLen);
+    if (rv != SECSuccess) {
+        return SECFailure;
+    }
+
+    return SECSuccess;
+}
+
+static SECStatus
+tls13_ServerGetEsniAEAD(const sslSocket *ss, PRUint64 suite,
+                        const ssl3CipherSuiteDef **suiteDefp,
+                        SSLAEADCipher *aeadp)
+{
+    SECStatus rv;
+    const ssl3CipherSuiteDef *suiteDef;
+    SSLAEADCipher aead;
+
+    /* Check against the suite list for ESNI */
+    PRBool csMatch = PR_FALSE;
+    sslReader csrdr = SSL_READER(ss->esniKeys->suites.data,
+                                 ss->esniKeys->suites.len);
+    while (SSL_READER_REMAINING(&csrdr)) {
+        PRUint64 asuite;
+
+        rv = sslRead_ReadNumber(&csrdr, 2, &asuite);
+        if (rv != SECSuccess) {
+            return SECFailure;
+        }
+        if (asuite == suite) {
+            csMatch = PR_TRUE;
+            break;
+        }
+    }
+    if (!csMatch) {
+        return SECFailure;
+    }
+
+    suiteDef = ssl_LookupCipherSuiteDef(suite);
+    PORT_Assert(suiteDef);
+    if (!suiteDef) {
+        return SECFailure;
+    }
+    aead = tls13_GetAead(ssl_GetBulkCipherDef(suiteDef));
+    if (!aead) {
+        return SECFailure;
+    }
+
+    *suiteDefp = suiteDef;
+    *aeadp = aead;
+    return SECSuccess;
+}
+
+SECStatus
+tls13_ServerDecryptEsniXtn(const sslSocket *ss, PRUint8 *in, unsigned int inLen,
+                           PRUint8 *out, int *outLen, int maxLen)
+{
+    sslReader rdr = SSL_READER(in, inLen);
+    PRUint64 suite;
+    const ssl3CipherSuiteDef *suiteDef;
+    SSLAEADCipher aead = NULL;
+    TLSExtension *keyShareExtension;
+    TLS13KeyShareEntry *entry = NULL;
+    ssl3KeyMaterial keyMat = { NULL };
+
+    sslBuffer aadInput = SSL_BUFFER_EMPTY;
+    const PRUint8 *keyShareBuf;
+    sslReadBuffer buf;
+    unsigned int keyShareBufLen;
+    PRUint8 hash[64];
+    SECStatus rv;
+
+    /* Read the cipher suite. */
+    rv = sslRead_ReadNumber(&rdr, 2, &suite);
+    if (rv != SECSuccess) {
+        goto loser;
+    }
+
+    /* Find the AEAD */
+    rv = tls13_ServerGetEsniAEAD(ss, suite, &suiteDef, &aead);
+    if (rv != SECSuccess) {
+        goto loser;
+    }
+
+    /* Note where the KeyShare starts. */
+    keyShareBuf = SSL_READER_CURRENT(&rdr);
+    rv = tls13_DecodeKeyShareEntry(&rdr, &entry);
+    if (rv != SECSuccess) {
+        goto loser;
+    }
+    keyShareBufLen = SSL_READER_CURRENT(&rdr) - keyShareBuf;
+    if (!entry || entry->group->name != ss->esniKeys->privKey->group->name) {
+        goto loser;
+    }
+
+    /* The hash of the ESNIKeys structure. */
+    rv = sslRead_ReadVariable(&rdr, 2, &buf);
+    if (rv != SECSuccess) {
+        goto loser;
+    }
+
+    /* Check that the hash matches. */
+    unsigned int hashLen = tls13_GetHashSizeForHash(suiteDef->prf_hash);
+    PORT_Assert(hashLen <= sizeof(hash));
+    rv = PK11_HashBuf(ssl3_HashTypeToOID(suiteDef->prf_hash),
+                      hash,
+                      ss->esniKeys->data.data, ss->esniKeys->data.len);
+    if (rv != SECSuccess) {
+        goto loser;
+    }
+
+    if (buf.len != hashLen) {
+        /* This is malformed. */
+        goto loser;
+    }
+    if (0 != NSS_SecureMemcmp(hash, buf.buf, hashLen)) {
+        goto loser;
+    }
+
+    rv = tls13_ComputeESNIKeys(ss, entry,
+                               ss->esniKeys->privKey->keys,
+                               suiteDef,
+                               hash, keyShareBuf, keyShareBufLen,
+                               ((sslSocket *)ss)->ssl3.hs.client_random,
+                               &keyMat);
+    if (rv != SECSuccess) {
+        goto loser;
+    }
+
+    /* Read the ciphertext. */
+    rv = sslRead_ReadVariable(&rdr, 2, &buf);
+    if (rv != SECSuccess) {
+        goto loser;
+    }
+
+    /* Check that this is empty. */
+    if (SSL_READER_REMAINING(&rdr) > 0) {
+        goto loser;
+    }
+
+    /* Find the key share extension. */
+    keyShareExtension = ssl3_FindExtension(CONST_CAST(sslSocket, ss),
+                                           ssl_tls13_key_share_xtn);
+    if (!keyShareExtension) {
+        goto loser;
+    }
+    rv = tls13_FormatEsniAADInput(&aadInput,
+                                  keyShareExtension->data.data,
+                                  keyShareExtension->data.len);
+    if (rv != SECSuccess) {
+        goto loser;
+    }
+
+    rv = aead(&keyMat, PR_TRUE /* Decrypt */,
+              out, outLen, maxLen,
+              buf.buf, buf.len,
+              SSL_BUFFER_BASE(&aadInput),
+              SSL_BUFFER_LEN(&aadInput));
+    sslBuffer_Clear(&aadInput);
+    if (rv != SECSuccess) {
+        goto loser;
+    }
+
+    ssl_DestroyKeyMaterial(&keyMat);
+    tls13_DestroyKeyShareEntry(entry);
+    return SECSuccess;
+
+loser:
+    FATAL_ERROR(CONST_CAST(sslSocket, ss), SSL_ERROR_RX_MALFORMED_ESNI_EXTENSION, illegal_parameter);
+    ssl_DestroyKeyMaterial(&keyMat); /* Safe because zeroed. */
+    if (entry) {
+        tls13_DestroyKeyShareEntry(entry);
+    }
+    return SECFailure;
+}
new file mode 100644
--- /dev/null
+++ b/security/nss/lib/ssl/tls13esni.h
@@ -0,0 +1,51 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is PRIVATE to SSL.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef __tls13esni_h_
+#define __tls13esni_h_
+
+struct sslEsniKeysStr {
+    SECItem data; /* The encoded record. */
+    sslEphemeralKeyPair *privKey;
+    const char *dummySni;
+    PRCList keyShares; /* List of TLS13KeyShareEntry */
+    SECItem suites;
+    PRUint16 paddedLength;
+    PRUint64 notBefore;
+    PRUint64 notAfter;
+};
+
+SECStatus SSLExp_SetESNIKeyPair(PRFileDesc *fd,
+                                SECKEYPrivateKey *privKey,
+                                const PRUint8 *record, unsigned int recordLen);
+
+SECStatus SSLExp_EnableESNI(PRFileDesc *fd, const PRUint8 *esniKeys,
+                            unsigned int esniKeysLen, const char *dummySNI);
+SECStatus SSLExp_EncodeESNIKeys(PRUint16 *cipherSuites, unsigned int cipherSuiteCount,
+                                SSLNamedGroup group, SECKEYPublicKey *pubKey,
+                                PRUint16 pad, PRUint64 notBefore, PRUint64 notAfter,
+                                PRUint8 *out, unsigned int *outlen, unsigned int maxlen);
+sslEsniKeys *tls13_CopyESNIKeys(sslEsniKeys *okeys);
+void tls13_DestroyESNIKeys(sslEsniKeys *keys);
+SECStatus tls13_ClientSetupESNI(sslSocket *ss);
+SECStatus tls13_ComputeESNIKeys(const sslSocket *ss,
+                                TLS13KeyShareEntry *entry,
+                                sslKeyPair *keyPair,
+                                const ssl3CipherSuiteDef *suite,
+                                const PRUint8 *esniKeysHash,
+                                const PRUint8 *keyShareBuf,
+                                unsigned int keyShareBufLen,
+                                const PRUint8 *clientRandom,
+                                ssl3KeyMaterial *keyMat);
+SECStatus tls13_FormatEsniAADInput(sslBuffer *aadInput,
+                                   PRUint8 *keyShare, unsigned int keyShareLen);
+
+SECStatus tls13_ServerDecryptEsniXtn(const sslSocket *ss, PRUint8 *in, unsigned int inLen,
+                                     PRUint8 *out, int *outLen, int maxLen);
+
+#endif
--- a/security/nss/lib/ssl/tls13exthandle.c
+++ b/security/nss/lib/ssl/tls13exthandle.c
@@ -7,16 +7,17 @@
 #include "nssrenam.h"
 #include "nss.h"
 #include "ssl.h"
 #include "sslproto.h"
 #include "sslimpl.h"
 #include "pk11pub.h"
 #include "ssl3ext.h"
 #include "ssl3exthandle.h"
+#include "tls13esni.h"
 #include "tls13exthandle.h"
 
 SECStatus
 tls13_ServerSendStatusRequestXtn(const sslSocket *ss, TLSExtensionData *xtnData,
                                  sslBuffer *buf, PRBool *added)
 {
     const sslServerCert *serverCert = ss->sec.serverCert;
     const SECItem *item;
@@ -66,39 +67,39 @@ tls13_ServerSendStatusRequestXtn(const s
  * DH is Section 6.3.2.3.1.
  *
  *     opaque dh_Y<1..2^16-1>;
  *
  * ECDH is Section 6.3.2.3.2.
  *
  *     opaque point <1..2^8-1>;
  */
-static PRUint32
+PRUint32
 tls13_SizeOfKeyShareEntry(const SECKEYPublicKey *pubKey)
 {
     /* Size = NamedGroup(2) + length(2) + opaque<?> share */
     switch (pubKey->keyType) {
         case ecKey:
             return 2 + 2 + pubKey->u.ec.publicValue.len;
         case dhKey:
             return 2 + 2 + pubKey->u.dh.prime.len;
         default:
             PORT_Assert(0);
     }
     return 0;
 }
 
-static SECStatus
-tls13_EncodeKeyShareEntry(sslBuffer *buf, const sslEphemeralKeyPair *keyPair)
+SECStatus
+tls13_EncodeKeyShareEntry(sslBuffer *buf, SSLNamedGroup group,
+                          SECKEYPublicKey *pubKey)
 {
     SECStatus rv;
-    SECKEYPublicKey *pubKey = keyPair->keys->pubKey;
     unsigned int size = tls13_SizeOfKeyShareEntry(pubKey);
 
-    rv = sslBuffer_AppendNumber(buf, keyPair->group->name, 2);
+    rv = sslBuffer_AppendNumber(buf, group, 2);
     if (rv != SECSuccess)
         return rv;
     rv = sslBuffer_AppendNumber(buf, size - 4, 2);
     if (rv != SECSuccess)
         return rv;
 
     switch (pubKey->keyType) {
         case ecKey:
@@ -118,124 +119,145 @@ tls13_EncodeKeyShareEntry(sslBuffer *buf
 }
 
 SECStatus
 tls13_ClientSendKeyShareXtn(const sslSocket *ss, TLSExtensionData *xtnData,
                             sslBuffer *buf, PRBool *added)
 {
     SECStatus rv;
     PRCList *cursor;
+    unsigned int extStart;
     unsigned int lengthOffset;
 
     if (ss->vrange.max < SSL_LIBRARY_VERSION_TLS_1_3) {
         return SECSuccess;
     }
 
     /* Optimistically try to send an ECDHE key using the
      * preexisting key (in future will be keys) */
     SSL_TRC(3, ("%d: TLS13[%d]: send client key share xtn",
                 SSL_GETPID(), ss->fd));
 
+    extStart = SSL_BUFFER_LEN(buf);
+
     /* Save the offset to the length. */
     rv = sslBuffer_Skip(buf, 2, &lengthOffset);
     if (rv != SECSuccess) {
         return SECFailure;
     }
 
     for (cursor = PR_NEXT_LINK(&ss->ephemeralKeyPairs);
          cursor != &ss->ephemeralKeyPairs;
          cursor = PR_NEXT_LINK(cursor)) {
         sslEphemeralKeyPair *keyPair = (sslEphemeralKeyPair *)cursor;
-        rv = tls13_EncodeKeyShareEntry(buf, keyPair);
+        rv = tls13_EncodeKeyShareEntry(buf,
+                                       keyPair->group->name,
+                                       keyPair->keys->pubKey);
         if (rv != SECSuccess) {
             return SECFailure;
         }
     }
     rv = sslBuffer_InsertLength(buf, lengthOffset, 2);
     if (rv != SECSuccess) {
         return SECFailure;
     }
 
+    rv = SECITEM_MakeItem(NULL, &xtnData->keyShareExtension,
+                          SSL_BUFFER_BASE(buf) + extStart,
+                          SSL_BUFFER_LEN(buf) - extStart);
+    if (rv != SECSuccess) {
+        return SECFailure;
+    }
+
     *added = PR_TRUE;
     return SECSuccess;
 }
 
-static SECStatus
-tls13_HandleKeyShareEntry(const sslSocket *ss, TLSExtensionData *xtnData, SECItem *data)
+SECStatus
+tls13_DecodeKeyShareEntry(sslReader *rdr, TLS13KeyShareEntry **ksp)
 {
     SECStatus rv;
-    PRUint32 group;
+    PRUint64 group;
     const sslNamedGroupDef *groupDef;
     TLS13KeyShareEntry *ks = NULL;
-    SECItem share = { siBuffer, NULL, 0 };
+    sslReadBuffer share;
 
-    rv = ssl3_ExtConsumeHandshakeNumber(ss, &group, 2, &data->data, &data->len);
+    rv = sslRead_ReadNumber(rdr, 2, &group);
     if (rv != SECSuccess) {
-        PORT_SetError(SSL_ERROR_RX_MALFORMED_KEY_SHARE);
         goto loser;
     }
     groupDef = ssl_LookupNamedGroup(group);
-    rv = ssl3_ExtConsumeHandshakeVariable(ss, &share, 2, &data->data,
-                                          &data->len);
+    rv = sslRead_ReadVariable(rdr, 2, &share);
     if (rv != SECSuccess) {
         goto loser;
     }
+
+    /* This has to happen here because we want to consume
+     * the entire entry even if the group is unknown
+     * or disabled. */
     /* If the group is disabled, continue. */
     if (!groupDef) {
         return SECSuccess;
     }
 
     ks = PORT_ZNew(TLS13KeyShareEntry);
-    if (!ks)
+    if (!ks) {
         goto loser;
+    }
     ks->group = groupDef;
 
-    rv = SECITEM_CopyItem(NULL, &ks->key_exchange, &share);
-    if (rv != SECSuccess)
+    rv = SECITEM_MakeItem(NULL, &ks->key_exchange,
+                          share.buf, share.len);
+    if (rv != SECSuccess) {
         goto loser;
+    }
 
-    PR_APPEND_LINK(&ks->link, &xtnData->remoteKeyShares);
+    *ksp = ks;
     return SECSuccess;
 
 loser:
-    if (ks)
-        tls13_DestroyKeyShareEntry(ks);
+    tls13_DestroyKeyShareEntry(ks);
+
     return SECFailure;
 }
 /* Handle an incoming KeyShare extension at the client and copy to
  * |xtnData->remoteKeyShares| for future use. The key
  * share is processed in tls13_HandleServerKeyShare(). */
 SECStatus
 tls13_ClientHandleKeyShareXtn(const sslSocket *ss, TLSExtensionData *xtnData,
                               SECItem *data)
 {
     SECStatus rv;
     PORT_Assert(PR_CLIST_IS_EMPTY(&xtnData->remoteKeyShares));
+    TLS13KeyShareEntry *ks = NULL;
 
     PORT_Assert(!ss->sec.isServer);
 
     /* The server must not send this extension when negotiating < TLS 1.3. */
     if (ss->version < SSL_LIBRARY_VERSION_TLS_1_3) {
         PORT_SetError(SSL_ERROR_EXTENSION_DISALLOWED_FOR_VERSION);
         return SECFailure;
     }
 
     SSL_TRC(3, ("%d: SSL3[%d]: handle key_share extension",
                 SSL_GETPID(), ss->fd));
 
-    rv = tls13_HandleKeyShareEntry(ss, xtnData, data);
-    if (rv != SECSuccess) {
+    sslReader rdr = SSL_READER(data->data, data->len);
+    rv = tls13_DecodeKeyShareEntry(&rdr, &ks);
+    if ((rv != SECSuccess) || !ks) {
+        ssl3_ExtSendAlert(ss, alert_fatal, illegal_parameter);
         PORT_SetError(SSL_ERROR_RX_MALFORMED_KEY_SHARE);
         return SECFailure;
     }
 
-    if (data->len) {
+    if (SSL_READER_REMAINING(&rdr)) {
         PORT_SetError(SSL_ERROR_RX_MALFORMED_KEY_SHARE);
         return SECFailure;
     }
+    PR_APPEND_LINK(&ks->link, &xtnData->remoteKeyShares);
 
     return SECSuccess;
 }
 
 SECStatus
 tls13_ClientHandleKeyShareXtnHrr(const sslSocket *ss, TLSExtensionData *xtnData,
                                  SECItem *data)
 {
@@ -268,17 +290,17 @@ tls13_ClientHandleKeyShareXtnHrr(const s
         PORT_SetError(SSL_ERROR_RX_MALFORMED_HELLO_RETRY_REQUEST);
         return SECFailure;
     }
 
     /* Now delete all the key shares per [draft-ietf-tls-tls13 S 4.1.2] */
     ssl_FreeEphemeralKeyPairs(CONST_CAST(sslSocket, ss));
 
     /* And replace with our new share. */
-    rv = tls13_CreateKeyShare(CONST_CAST(sslSocket, ss), group);
+    rv = tls13_AddKeyShare(CONST_CAST(sslSocket, ss), group);
     if (rv != SECSuccess) {
         ssl3_ExtSendAlert(ss, alert_fatal, internal_error);
         PORT_SetError(SEC_ERROR_KEYGEN_FAIL);
         return SECFailure;
     }
 
     return SECSuccess;
 }
@@ -310,22 +332,34 @@ tls13_ServerHandleKeyShareXtn(const sslS
     if (rv != SECSuccess)
         goto loser;
     if (length != data->len) {
         /* Check for consistency */
         PORT_SetError(SSL_ERROR_RX_MALFORMED_KEY_SHARE);
         goto loser;
     }
 
-    while (data->len) {
-        rv = tls13_HandleKeyShareEntry(ss, xtnData, data);
-        if (rv != SECSuccess)
+    sslReader rdr = SSL_READER(data->data, data->len);
+    while (SSL_READER_REMAINING(&rdr)) {
+        TLS13KeyShareEntry *ks = NULL;
+        rv = tls13_DecodeKeyShareEntry(&rdr, &ks);
+        if (rv != SECSuccess) {
+            PORT_SetError(SSL_ERROR_RX_MALFORMED_KEY_SHARE);
             goto loser;
+        }
+        if (ks) {
+            /* |ks| == NULL if this is an unknown group. */
+            PR_APPEND_LINK(&ks->link, &xtnData->remoteKeyShares);
+        }
     }
 
+    /* Keep track of negotiated extensions. */
+    xtnData->negotiated[xtnData->numNegotiated++] =
+        ssl_tls13_key_share_xtn;
+
     return SECSuccess;
 
 loser:
     tls13_DestroyKeyShares(&xtnData->remoteKeyShares);
     return SECFailure;
 }
 
 SECStatus
@@ -337,17 +371,18 @@ tls13_ServerSendKeyShareXtn(const sslSoc
 
     /* There should be 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);
 
-    rv = tls13_EncodeKeyShareEntry(buf, keyPair);
+    rv = tls13_EncodeKeyShareEntry(buf, keyPair->group->name,
+                                   keyPair->keys->pubKey);
     if (rv != SECSuccess) {
         return SECFailure;
     }
 
     *added = PR_TRUE;
     return SECSuccess;
 }
 
@@ -1055,8 +1090,278 @@ tls13_ServerSendHrrCookieXtn(const sslSo
                                   xtnData->cookie.data, xtnData->cookie.len, 2);
     if (rv != SECSuccess) {
         return SECFailure;
     }
 
     *added = PR_TRUE;
     return SECSuccess;
 }
+
+SECStatus
+tls13_ClientSendEsniXtn(const sslSocket *ss, TLSExtensionData *xtnData,
+                        sslBuffer *buf, PRBool *added)
+{
+    SECStatus rv;
+    PRUint8 sniBuf[1024];
+    PRUint8 hash[64];
+    sslBuffer sni = SSL_BUFFER(sniBuf);
+    const ssl3CipherSuiteDef *suiteDef;
+    ssl3KeyMaterial keyMat;
+    SSLAEADCipher aead;
+    PRUint8 outBuf[1024];
+    int outLen;
+    unsigned int sniStart;
+    unsigned int sniLen;
+    sslBuffer aadInput = SSL_BUFFER_EMPTY;
+    unsigned int keyShareBufStart;
+    unsigned int keyShareBufLen;
+
+    PORT_Memset(&keyMat, 0, sizeof(keyMat));
+
+    if (!ss->xtnData.esniPrivateKey) {
+        return SECSuccess;
+    }
+
+    /* nonce */
+    rv = PK11_GenerateRandom(
+        (unsigned char *)xtnData->esniNonce, sizeof(xtnData->esniNonce));
+    if (rv != SECSuccess) {
+        return SECFailure;
+    }
+    rv = sslBuffer_Append(&sni, xtnData->esniNonce, sizeof(xtnData->esniNonce));
+    if (rv != SECSuccess) {
+        return SECFailure;
+    }
+
+    /* sni */
+    sniStart = SSL_BUFFER_LEN(&sni);
+    rv = ssl3_ClientFormatServerNameXtn(ss, ss->url, xtnData, &sni);
+    if (rv != SECSuccess) {
+        return SECFailure;
+    }
+
+    sniLen = SSL_BUFFER_LEN(&sni) - sniStart;
+    /* Padding. */
+    if (ss->esniKeys->paddedLength > sniLen) {
+        unsigned int paddingRequired = ss->esniKeys->paddedLength - sniLen;
+        while (paddingRequired--) {
+            rv = sslBuffer_AppendNumber(&sni, 0, 1);
+            if (rv != SECSuccess) {
+                return SECFailure;
+            }
+        }
+    }
+
+    suiteDef = ssl_LookupCipherSuiteDef(xtnData->esniSuite);
+    PORT_Assert(suiteDef);
+    if (!suiteDef) {
+        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+        return SECFailure;
+    }
+    aead = tls13_GetAead(ssl_GetBulkCipherDef(suiteDef));
+    if (!aead) {
+        return SECFailure;
+    }
+
+    /* Format the first part of the extension so we have the
+     * encoded KeyShareEntry. */
+    rv = sslBuffer_AppendNumber(buf, xtnData->esniSuite, 2);
+    if (rv != SECSuccess) {
+        return SECFailure;
+    }
+    keyShareBufStart = SSL_BUFFER_LEN(buf);
+    rv = tls13_EncodeKeyShareEntry(buf,
+                                   xtnData->esniPrivateKey->group->name,
+                                   xtnData->esniPrivateKey->keys->pubKey);
+    if (rv != SECSuccess) {
+        return SECFailure;
+    }
+    keyShareBufLen = SSL_BUFFER_LEN(buf) - keyShareBufStart;
+
+    if (tls13_GetHashSizeForHash(suiteDef->prf_hash) > sizeof(hash)) {
+        PORT_Assert(PR_FALSE);
+        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+        return SECFailure;
+    }
+
+    rv = PK11_HashBuf(ssl3_HashTypeToOID(suiteDef->prf_hash),
+                      hash,
+                      ss->esniKeys->data.data,
+                      ss->esniKeys->data.len);
+    if (rv != SECSuccess) {
+        PORT_Assert(PR_FALSE);
+        return SECFailure;
+    }
+
+    rv = sslBuffer_AppendVariable(buf, hash,
+                                  tls13_GetHashSizeForHash(suiteDef->prf_hash), 2);
+    if (rv != SECSuccess) {
+        return SECFailure;
+    }
+
+    /* Compute the ESNI keys. */
+    rv = tls13_ComputeESNIKeys(ss, xtnData->peerEsniShare,
+                               xtnData->esniPrivateKey->keys,
+                               suiteDef,
+                               hash,
+                               SSL_BUFFER_BASE(buf) + keyShareBufStart,
+                               keyShareBufLen,
+                               CONST_CAST(PRUint8, ss->ssl3.hs.client_random),
+                               &keyMat);
+    if (rv != SECSuccess) {
+        return SECFailure;
+    }
+
+    rv = tls13_FormatEsniAADInput(&aadInput,
+                                  xtnData->keyShareExtension.data,
+                                  xtnData->keyShareExtension.len);
+    if (rv != SECSuccess) {
+        ssl_DestroyKeyMaterial(&keyMat);
+        return SECFailure;
+    }
+    /* Now encrypt. */
+    rv = aead(&keyMat, PR_FALSE /* Encrypt */,
+              outBuf, &outLen, sizeof(outBuf),
+              SSL_BUFFER_BASE(&sni),
+              SSL_BUFFER_LEN(&sni),
+              SSL_BUFFER_BASE(&aadInput),
+              SSL_BUFFER_LEN(&aadInput));
+    ssl_DestroyKeyMaterial(&keyMat);
+    sslBuffer_Clear(&aadInput);
+    if (rv != SECSuccess) {
+        return SECFailure;
+    }
+
+    /* Encode the rest. */
+    rv = sslBuffer_AppendVariable(buf, outBuf, outLen, 2);
+    if (rv != SECSuccess) {
+        return SECFailure;
+    }
+
+    *added = PR_TRUE;
+    return SECSuccess;
+}
+
+static SECStatus
+tls13_ServerSendEsniXtn(const sslSocket *ss, TLSExtensionData *xtnData,
+                        sslBuffer *buf, PRBool *added)
+{
+    SECStatus rv;
+
+    rv = sslBuffer_Append(buf, xtnData->esniNonce, sizeof(xtnData->esniNonce));
+    if (rv != SECSuccess) {
+        return SECFailure;
+    }
+
+    *added = PR_TRUE;
+    return SECSuccess;
+}
+
+SECStatus
+tls13_ServerHandleEsniXtn(const sslSocket *ss, TLSExtensionData *xtnData,
+                          SECItem *data)
+{
+    sslReadBuffer buf;
+    PRUint8 *plainText = NULL;
+    int ptLen;
+    SECStatus rv;
+
+    /* If we are doing < TLS 1.3, then ignore this. */
+    if (ss->version < SSL_LIBRARY_VERSION_TLS_1_3) {
+        return SECSuccess;
+    }
+
+    if (!ss->esniKeys) {
+        /* Apparently we used to be configured for ESNI, but
+         * no longer. This violates the spec, or the client is
+         * broken. */
+        return SECFailure;
+    }
+
+    plainText = PORT_ZAlloc(data->len);
+    if (!plainText) {
+        return SECFailure;
+    }
+    rv = tls13_ServerDecryptEsniXtn(ss, data->data, data->len,
+                                    plainText, &ptLen, data->len);
+    if (rv) {
+        goto loser;
+    }
+
+    /* Read out the interior extension. */
+    sslReader sniRdr = SSL_READER(plainText, ptLen);
+
+    rv = sslRead_Read(&sniRdr, sizeof(xtnData->esniNonce), &buf);
+    if (rv != SECSuccess) {
+        goto loser;
+    }
+    PORT_Memcpy(xtnData->esniNonce, buf.buf, sizeof(xtnData->esniNonce));
+
+    /* We need to capture the whole block with the length. */
+    SECItem sniItem = { siBuffer, (unsigned char *)SSL_READER_CURRENT(&sniRdr), 0 };
+    rv = sslRead_ReadVariable(&sniRdr, 2, &buf);
+    if (rv != SECSuccess) {
+        goto loser;
+    }
+    sniItem.len = buf.len + 2;
+
+    /* Check the padding. Note we don't need to do this in constant time
+     * because it's inside the AEAD boundary. */
+    /* TODO(ekr@rtfm.com): check that the padding is the right length. */
+    PRUint64 tmp;
+    while (SSL_READER_REMAINING(&sniRdr)) {
+        rv = sslRead_ReadNumber(&sniRdr, 1, &tmp);
+        if (tmp != 0) {
+            goto loser;
+        }
+    }
+
+    rv = ssl3_HandleServerNameXtn(ss, xtnData, &sniItem);
+    if (rv != SECSuccess) {
+        goto loser;
+    }
+
+    rv = ssl3_RegisterExtensionSender(ss, xtnData,
+                                      ssl_tls13_encrypted_sni_xtn,
+                                      tls13_ServerSendEsniXtn);
+    if (rv != SECSuccess) {
+        goto loser;
+    }
+
+    /* Keep track of negotiated extensions. */
+    xtnData->negotiated[xtnData->numNegotiated++] =
+        ssl_tls13_encrypted_sni_xtn;
+
+    PORT_ZFree(plainText, data->len);
+    return SECSuccess;
+loser:
+    PORT_ZFree(plainText, data->len);
+    return SECFailure;
+}
+
+/* Function to check the extension. We don't install a handler here
+ * because we need to check for the presence of the extension as
+ * well and it's easier to do it in one place. */
+SECStatus
+tls13_ClientCheckEsniXtn(sslSocket *ss)
+{
+    TLSExtension *esniExtension =
+        ssl3_FindExtension(ss, ssl_tls13_encrypted_sni_xtn);
+    if (!esniExtension) {
+        FATAL_ERROR(ss, SSL_ERROR_MISSING_ESNI_EXTENSION, missing_extension);
+        return SECFailure;
+    }
+
+    if (esniExtension->data.len != sizeof(ss->xtnData.esniNonce)) {
+        FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_ESNI_EXTENSION, illegal_parameter);
+        return SECFailure;
+    }
+
+    if (0 != NSS_SecureMemcmp(esniExtension->data.data,
+                              ss->xtnData.esniNonce,
+                              sizeof(ss->xtnData.esniNonce))) {
+        FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_ESNI_EXTENSION, illegal_parameter);
+        return SECFailure;
+    }
+
+    return SECSuccess;
+}
--- a/security/nss/lib/ssl/tls13exthandle.h
+++ b/security/nss/lib/ssl/tls13exthandle.h
@@ -79,10 +79,19 @@ SECStatus tls13_ServerHandleCookieXtn(co
                                       TLSExtensionData *xtnData,
                                       SECItem *data);
 SECStatus tls13_ServerSendHrrKeyShareXtn(const sslSocket *ss,
                                          TLSExtensionData *xtnData,
                                          sslBuffer *buf, PRBool *added);
 SECStatus tls13_ServerSendHrrCookieXtn(const sslSocket *ss,
                                        TLSExtensionData *xtnData,
                                        sslBuffer *buf, PRBool *added);
+SECStatus tls13_DecodeKeyShareEntry(sslReader *rdr, TLS13KeyShareEntry **ksp);
+PRUint32 tls13_SizeOfKeyShareEntry(const SECKEYPublicKey *pubKey);
+SECStatus tls13_EncodeKeyShareEntry(sslBuffer *buf, SSLNamedGroup group,
+                                    SECKEYPublicKey *pubKey);
+SECStatus tls13_ClientSendEsniXtn(const sslSocket *ss, TLSExtensionData *xtnData,
+                                  sslBuffer *buf, PRBool *added);
+SECStatus tls13_ServerHandleEsniXtn(const sslSocket *ss, TLSExtensionData *xtnData,
+                                    SECItem *data);
+SECStatus tls13_ClientCheckEsniXtn(sslSocket *ss);
 
 #endif
--- a/security/nss/lib/util/secitem.c
+++ b/security/nss/lib/util/secitem.c
@@ -71,20 +71,20 @@ loser:
          * If item is not NULL, the above has set item->data and
          * item->len to 0.
          */
     }
     return (NULL);
 }
 
 SECStatus
-SECITEM_MakeItem(PLArenaPool *arena, SECItem *dest, unsigned char *data,
+SECITEM_MakeItem(PLArenaPool *arena, SECItem *dest, const unsigned char *data,
                  unsigned int len)
 {
-    SECItem it = { siBuffer, data, len };
+    SECItem it = { siBuffer, (unsigned char *)data, len };
 
     return SECITEM_CopyItem(arena, dest, &it);
 }
 
 SECStatus
 SECITEM_ReallocItem(PLArenaPool *arena, SECItem *item, unsigned int oldlen,
                     unsigned int newlen)
 {
--- a/security/nss/lib/util/secitem.h
+++ b/security/nss/lib/util/secitem.h
@@ -36,17 +36,17 @@ extern SECItem *SECITEM_AllocItem(PLAren
                                   unsigned int len);
 
 /* Allocate and make an item with the requested contents.
  *
  * We seem to have mostly given up on SECItemType, so the result is
  * always siBuffer.
  */
 extern SECStatus SECITEM_MakeItem(PLArenaPool *arena, SECItem *dest,
-                                  unsigned char *data, unsigned int len);
+                                  const unsigned char *data, unsigned int len);
 
 /*
 ** This is a legacy function containing bugs. It doesn't update item->len,
 ** and it has other issues as described in bug 298649 and bug 298938.
 ** However, the function is  kept unchanged for consumers that might depend
 ** on the broken behaviour. New code should call SECITEM_ReallocItemV2.
 **
 ** Reallocate the data for the specified "item".  If "arena" is not NULL,
--- a/security/nss/tests/all.sh
+++ b/security/nss/tests/all.sh
@@ -37,16 +37,18 @@
 #   memleak.sh   - memory leak testing (optional)
 #   ssl_gtests.sh- Gtest based unit tests for ssl
 #   gtests.sh    - Gtest based unit tests for everything else
 #   policy.sh    - Crypto Policy tests
 #   bogo.sh      - Bogo interop tests (disabled by default)
 #                  https://boringssl.googlesource.com/boringssl/+/master/ssl/test/PORTING.md
 #   interop.sh   - Interoperability tests (disabled by default)
 #                  https://github.com/ekr/tls_interop
+#   tlsfuzzer.sh - tlsfuzzer interop tests (disabled by default)
+#                  https://github.com/tomato42/tlsfuzzer/
 #
 # NSS testing is now devided to 4 cycles:
 # ---------------------------------------
 #   standard     - run test suites with defaults settings
 #   pkix         - run test suites with PKIX enabled
 #   upgradedb    - upgrade existing certificate databases to shareable
 #                  format (creates them if doesn't exist yet) and run
 #                  test suites with those databases
--- a/security/nss/tests/chains/chains.sh
+++ b/security/nss/tests/chains/chains.sh
@@ -46,23 +46,23 @@ is_httpserv_alive()
 }
 
 ########################### wait_for_httpserv ##########################
 # local shell function to wait until httpserver is running and initialized
 ########################################################################
 wait_for_httpserv()
 {
   echo "trying to connect to httpserv at `date`"
-  echo "tstclnt -p ${NSS_AIA_PORT} -h ${HOSTADDR} -q -v"
-  ${BINDIR}/tstclnt -p ${NSS_AIA_PORT} -h ${HOSTADDR} -q -v
+  echo "tstclnt -4 -p ${NSS_AIA_PORT} -h ${HOSTADDR} -q -v"
+  ${BINDIR}/tstclnt -4 -p ${NSS_AIA_PORT} -h ${HOSTADDR} -q -v
   if [ $? -ne 0 ]; then
       sleep 5
       echo "retrying to connect to httpserv at `date`"
-      echo "tstclnt -p ${NSS_AIA_PORT} -h ${HOSTADDR} -q -v"
-      ${BINDIR}/tstclnt -p ${NSS_AIA_PORT} -h ${HOSTADDR} -q -v
+      echo "tstclnt -4 -p ${NSS_AIA_PORT} -h ${HOSTADDR} -q -v"
+      ${BINDIR}/tstclnt -4 -p ${NSS_AIA_PORT} -h ${HOSTADDR} -q -v
       if [ $? -ne 0 ]; then
           html_failed "Waiting for Server"
       fi
   fi
   is_httpserv_alive
 }
 
 ########################### kill_httpserv ##############################
@@ -969,18 +969,18 @@ check_ocsp()
         CERT_FILE=${CERT}
     fi
 
     # sample line:
     #   URI: "http://ocsp.server:2601"
     OCSP_HOST=$(${BINDIR}/pp -w -t certificate -i ${CERT_FILE} | grep URI | sed "s/.*:\/\///" | sed "s/:.*//")
     OCSP_PORT=$(${BINDIR}/pp -w -t certificate -i ${CERT_FILE} | grep URI | sed "s/^.*:.*:\/\/.*:\([0-9]*\).*$/\1/")
 
-    echo "tstclnt -h ${OCSP_HOST} -p ${OCSP_PORT} -q -t 20"
-    tstclnt -h ${OCSP_HOST} -p ${OCSP_PORT} -q -t 20
+    echo "tstclnt -4 -h ${OCSP_HOST} -p ${OCSP_PORT} -q -t 20"
+    tstclnt -4 -h ${OCSP_HOST} -p ${OCSP_PORT} -q -t 20
     return $?
 }
 
 ############################ parse_result ##############################
 # local shell function to process expected result value
 # this function was created for case that expected result depends on
 # some conditions - in our case type of cert DB
 #
new file mode 100644
--- /dev/null
+++ b/security/nss/tests/common/certsetup.sh
@@ -0,0 +1,57 @@
+# Generate input to certutil
+certscript() {
+  ca=n
+  while [ $# -gt 0 ]; do
+    case $1 in
+      sign) echo 0 ;;
+      kex) echo 2 ;;
+      ca) echo 5;echo 6;ca=y ;;
+    esac; shift
+  done;
+  echo 9
+  echo n
+  echo $ca
+  echo
+  echo n
+}
+
+# $1: name
+# $2: type
+# $3+: usages: sign or kex
+make_cert() {
+  name=$1
+  type=$2
+
+  # defaults
+  type_args=()
+  trust=',,'
+  sign=(-x)
+  sighash=(-Z SHA256)
+
+  case $type in
+    dsa) type_args=(-g 1024) ;;
+    rsa) type_args=(-g 1024) ;;
+    rsa2048) type_args=(-g 2048);type=rsa ;;
+    rsa8192) type_args=(-g 8192);type=rsa ;;
+    rsapss) type_args=(-g 1024 --pss);type=rsa ;;
+    rsapss384) type_args=(-g 1024 --pss);type=rsa;sighash=(-Z SHA384) ;;
+    rsapss512) type_args=(-g 2048 --pss);type=rsa;sighash=(-Z SHA512) ;;
+    rsapss_noparam) type_args=(-g 2048 --pss);type=rsa;sighash=() ;;
+    p256) type_args=(-q nistp256);type=ec ;;
+    p384) type_args=(-q secp384r1);type=ec ;;
+    p521) type_args=(-q secp521r1);type=ec ;;
+    rsa_ca) type_args=(-g 1024);trust='CT,CT,CT';type=rsa ;;
+    rsa_chain) type_args=(-g 1024);sign=(-c rsa_ca);type=rsa;;
+    rsapss_ca) type_args=(-g 1024 --pss);trust='CT,CT,CT';type=rsa ;;
+    rsapss_chain) type_args=(-g 1024);sign=(-c rsa_pss_ca);type=rsa;;
+    rsa_ca_rsapss_chain) type_args=(-g 1024 --pss-sign);sign=(-c rsa_ca);type=rsa;;
+    ecdh_rsa) type_args=(-q nistp256);sign=(-c rsa_ca);type=ec ;;
+  esac
+  shift 2
+  counter=$(($counter + 1))
+  certscript $@ | ${BINDIR}/certutil -S \
+    -z ${R_NOISE_FILE} -d "${PROFILEDIR}" \
+    -n $name -s "CN=$name" -t "$trust" "${sign[@]}" -m "$counter" \
+    -w -2 -v 120 -k "$type" "${type_args[@]}" "${sighash[@]}" -1 -2
+  html_msg $? 0 "create certificate: $@"
+}
--- a/security/nss/tests/common/init.sh
+++ b/security/nss/tests/common/init.sh
@@ -351,59 +351,53 @@ if [ -z "${INIT_SOURCED}" -o "${INIT_SOU
 
     if [ ! -d "${TESTDIR}" ]; then
         echo "$SCRIPTNAME init: Creating ${TESTDIR}"
         mkdir -p ${TESTDIR}
     fi
 
 #HOST and DOMSUF are needed for the server cert
 
-    DOMAINNAME=`which domainname`
-    if [ -z "${DOMSUF}" -a $? -eq 0 -a -n "${DOMAINNAME}" ]; then
+    if [ -z "$DOMSUF" ] && hash domainname 2>/dev/null; then
         DOMSUF=`domainname`
     fi
+    # hostname -d and domainname both return (none) if hostname doesn't
+    # include a dot.  Pretend we didn't get an answer.
+    if [ "$DOMSUF" = "(none)" ]; then
+        DOMSUF=
+    fi
 
-    case $HOST in
+    if [ -z "$HOST" ]; then
+        HOST=`uname -n`
+    fi
+    case "$HOST" in
         *\.*)
-            if [ -z "${DOMSUF}" ]; then
-                DOMSUF=`echo $HOST | sed -e "s/^[^.]*\.//"`
+            if [ -z "$DOMSUF" ]; then
+                DOMSUF="${HOST#*.}"
             fi
-            HOST=`echo $HOST | sed -e "s/\..*//"`
+            HOST="${HOST%%.*}"
             ;;
         ?*)
             ;;
         *)
-            HOST=`uname -n`
-            case $HOST in
-                *\.*)
-                    if [ -z "${DOMSUF}" ]; then
-                        DOMSUF=`echo $HOST | sed -e "s/^[^.]*\.//"`
-                    fi
-                    HOST=`echo $HOST | sed -e "s/\..*//"`
-                    ;;
-                ?*)
-                    ;;
-                *)
-                    echo "$SCRIPTNAME: Fatal HOST environment variable is not defined."
-                    exit 1 #does not need to be Exit, very early in script
-                    ;;
-            esac
+            echo "$SCRIPTNAME: Fatal HOST environment variable is not defined."
+            exit 1 #does not need to be Exit, very early in script
             ;;
     esac
 
-    if [ -z "${DOMSUF}" -a "${OS_ARCH}" != "Android" ]; then
+    if [ -z "$DOMSUF" -a "$OS_ARCH" != "Android" ]; then
         echo "$SCRIPTNAME: Fatal DOMSUF env. variable is not defined."
         exit 1 #does not need to be Exit, very early in script
     fi
 
 #HOSTADDR was a workaround for the dist. stress test, and is probably
 #not needed anymore (purpose: be able to use IP address for the server
 #cert instead of PC name which was not in the DNS because of dyn IP address
-    if [ -z "$USE_IP" -o "$USE_IP" != "TRUE" ] ; then
-        if [ -z "${DOMSUF}" ]; then
+    if [ "$USE_IP" != "TRUE" ] ; then
+        if [ -z "$DOMSUF" ]; then
             HOSTADDR=${HOST}
         else
             HOSTADDR=${HOST}.${DOMSUF}
         fi
     else
         HOSTADDR=${IP_ADDRESS}
     fi
 
@@ -590,17 +584,17 @@ if [ -z "${INIT_SOURCED}" -o "${INIT_SOU
         P_R_DAVEDIR="multiaccess:${D_DAVE}"
         P_R_EVEDIR="multiaccess:${D_EVE}"
         P_R_SERVERDIR="multiaccess:${D_SERVER}"
         P_R_CLIENTDIR="multiaccess:${D_CLIENT}"
         P_R_NOLOGINDIR="multiaccess:${D_NOLOGIN}"
         P_R_EXT_SERVERDIR="multiaccess:${D_EXT_SERVER}"
         P_R_EXT_CLIENTDIR="multiaccess:${D_EXT_CLIENT}"
         P_R_IMPLICIT_INIT_DIR="multiaccess:${D_IMPLICIT_INIT}"
-	P_R_RSAPSSDIR="multiaccess:${D_RSAPSS}"
+        P_R_RSAPSSDIR="multiaccess:${D_RSAPSS}"
     fi
 
     R_PWFILE=../tests.pw
     R_EMPTY_FILE=../tests_empty
     R_NOISE_FILE=../tests_noise
 
     R_FIPSPWFILE=../tests.fipspw
     R_FIPSBADPWFILE=../tests.fipsbadpw
--- a/security/nss/tests/interop/interop.sh
+++ b/security/nss/tests/interop/interop.sh
@@ -19,18 +19,18 @@ interop_init()
     cd ../common
     . ./init.sh
   fi
 
   mkdir -p "${HOSTDIR}/interop"
   cd "${HOSTDIR}/interop"
   INTEROP=${INTEROP:=tls_interop}
   if [ ! -d "$INTEROP" ]; then
-    git clone -q https://github.com/jallmann/tls-interop "$INTEROP"
-    git -C "$INTEROP" checkout -q a4878c30db639053fc4f08573d3ae6cbd3cf1d6f
+    git clone -q https://github.com/mozilla/tls-interop "$INTEROP"
+    git -C "$INTEROP" checkout -q 8348561ae922bb21ecc1415dba15ca68bb5d18a2
   fi
   INTEROP=$(cd "$INTEROP";pwd -P)
 
   # We use the BoringSSL keyfiles
   BORING=${BORING:=boringssl}
   if [ ! -d "$BORING" ]; then
     git clone -q https://boringssl.googlesource.com/boringssl "$BORING"
     git -C "$BORING" checkout -q 7f4f41fa81c03e0f8ef1ab5b3d1d566b5968f107
--- a/security/nss/tests/ssl_gtests/ssl_gtests.sh
+++ b/security/nss/tests/ssl_gtests/ssl_gtests.sh
@@ -14,86 +14,32 @@
 #
 # special strings
 # ---------------
 #   FIXME ... known problems, search for this string
 #   NOTE .... unexpected behavior
 #
 ########################################################################
 
-# Generate input to certutil
-certscript() {
-  ca=n
-  while [ $# -gt 0 ]; do
-    case $1 in
-      sign) echo 0 ;;
-      kex) echo 2 ;;
-      ca) echo 5;echo 6;ca=y ;;
-    esac; shift
-  done;
-  echo 9
-  echo n
-  echo $ca
-  echo
-  echo n
-}
-
-# $1: name
-# $2: type
-# $3+: usages: sign or kex
-make_cert() {
-  name=$1
-  type=$2
-
-  # defaults
-  type_args=()
-  trust=',,'
-  sign=(-x)
-  sighash=(-Z SHA256)
-
-  case $type in
-    dsa) type_args=(-g 1024) ;;
-    rsa) type_args=(-g 1024) ;;
-    rsa2048) type_args=(-g 2048);type=rsa ;;
-    rsa8192) type_args=(-g 8192);type=rsa ;;
-    rsapss) type_args=(-g 1024 --pss);type=rsa ;;
-    rsapss384) type_args=(-g 1024 --pss);type=rsa;sighash=(-Z SHA384) ;;
-    rsapss512) type_args=(-g 2048 --pss);type=rsa;sighash=(-Z SHA512) ;;
-    rsapss_noparam) type_args=(-g 2048 --pss);type=rsa;sighash=() ;;
-    p256) type_args=(-q nistp256);type=ec ;;
-    p384) type_args=(-q secp384r1);type=ec ;;
-    p521) type_args=(-q secp521r1);type=ec ;;
-    rsa_ca) type_args=(-g 1024);trust='CT,CT,CT';type=rsa ;;
-    rsa_chain) type_args=(-g 1024);sign=(-c rsa_ca);type=rsa;;
-    rsapss_ca) type_args=(-g 1024 --pss);trust='CT,CT,CT';type=rsa ;;
-    rsapss_chain) type_args=(-g 1024);sign=(-c rsa_pss_ca);type=rsa;;
-    rsa_ca_rsapss_chain) type_args=(-g 1024 --pss-sign);sign=(-c rsa_ca);type=rsa;;
-    ecdh_rsa) type_args=(-q nistp256);sign=(-c rsa_ca);type=ec ;;
-  esac
-  shift 2
-  counter=$(($counter + 1))
-  certscript $@ | ${BINDIR}/certutil -S \
-    -z ${R_NOISE_FILE} -d "${PROFILEDIR}" \
-    -n $name -s "CN=$name" -t "$trust" "${sign[@]}" -m "$counter" \
-    -w -2 -v 120 -k "$type" "${type_args[@]}" "${sighash[@]}" -1 -2
-  html_msg $? 0 "create certificate: $@"
-}
-
 ssl_gtest_certs() {
   mkdir -p "${SSLGTESTDIR}"
   cd "${SSLGTESTDIR}"
 
   PROFILEDIR=`pwd`
   if [ "${OS_ARCH}" = "WINNT" -a "$OS_NAME" = "CYGWIN_NT" ]; then
     PROFILEDIR=`cygpath -m "${PROFILEDIR}"`
   fi
 
   ${BINDIR}/certutil -N -d "${PROFILEDIR}" --empty-password 2>&1
   html_msg $? 0 "create ssl_gtest database"
 
+  pushd "${QADIR}"
+  . common/certsetup.sh
+  popd
+
   counter=0
   make_cert client rsa sign
   make_cert rsa rsa sign kex
   make_cert rsa2048 rsa2048 sign kex
   make_cert rsa8192 rsa8192 sign kex
   make_cert rsa_sign rsa sign
   make_cert rsa_pss rsapss sign
   make_cert rsa_pss384 rsapss384 sign
new file mode 100644
--- /dev/null
+++ b/security/nss/tests/tlsfuzzer/config.json.in
@@ -0,0 +1,166 @@
+[
+    {
+        "server_command": [
+            "@SELFSERV@", "-w", "nss", "-d", "@SERVERDIR@",
+            "-V", "tls1.0:", "-H", "1",
+            "-n", "rsa",
+            "-n", "rsa-pss",
+	    "-J", "rsa_pss_rsae_sha256,rsa_pss_rsae_sha384,rsa_pss_rsae_sha512,rsa_pss_pss_sha256",
+            "-u", "-Z", "-p", "@PORT@"
+        ],
+        "server_hostname": "@HOSTADDR@",
+        "server_port": @PORT@,
+        "tests" : [
+            {
+                "name" : "test-tls13-conversation.py",
+                "arguments": [
+                    "-p", "@PORT@"
+                ]
+            },
+            {
+                "name" : "test-tls13-count-tickets.py",
+                "arguments": [
+                    "-p", "@PORT@", "-t", "1"
+                ]
+            },
+            {
+                "name" : "test-tls13-dhe-shared-secret-padding.py",
+                "comment": "https://bugzilla.mozilla.org/show_bug.cgi?id=1305243",
+                "arguments": [
+                    "-p", "@PORT@",
+                    "-e", "TLS 1.3 with x448"
+                ]
+            },
+            {
+                "name" : "test-tls13-empty-alert.py",
+                "arguments": [
+                    "-p", "@PORT@"
+                ],
+                "comment": "https://bugzilla.mozilla.org/show_bug.cgi?id=1471656",
+                "exp_pass": false
+            },
+            {
+                "name" : "test-tls13-ffdhe-sanity.py",
+                "arguments": [
+                    "-p", "@PORT@"
+                ]
+            },
+            {
+                "name" : "test-tls13-finished.py",
+                "arguments": [
+                    "-p", "@PORT@"
+                ],
+                "comment" : "https://bugzilla.mozilla.org/show_bug.cgi?id=1472747",
+                "exp_pass": false
+            },
+            {
+                "name" : "test-tls13-0rtt-garbage.py",
+                "comment": "the disabled test timeouts because of https://bugzilla.mozilla.org/show_bug.cgi?id=1472747",
+                "arguments": [
+                    "-p", "@PORT@", "--cookie",
+                    "-e", "undecryptable record later in handshake together with early_data"
+                ]
+            },
+            {
+                "name" : "test-tls13-hrr.py",
+                "arguments": [
+                    "-p", "@PORT@", "--cookie"
+                ]
+            },
+            {
+                "name" : "test-tls13-legacy-version.py",
+                "arguments": [
+                    "-p", "@PORT@"
+                ],
+                "comment": "https://bugzilla.mozilla.org/show_bug.cgi?id=1490006",
+                "exp_pass": false
+            },
+            {
+                "name" : "test-tls13-nociphers.py",
+                "arguments": [
+                    "-p", "@PORT@"
+                ]
+            },
+            {
+                "name" : "test-tls13-pkcs-signature.py",
+                "comment": "https://bugzilla.mozilla.org/show_bug.cgi?id=1489997",
+                "arguments": [
+                    "-p", "@PORT@",
+                    "-e", "rsa_pkcs1_sha256 signature",
+                    "-e", "rsa_pkcs1_sha384 signature",
+                    "-e", "rsa_pkcs1_sha512 signature"
+                ]
+            },
+            {
+                "name" : "test-tls13-rsa-signatures.py",
+		"comment": "selfserv can be set up to use multiple certs, but only one for each auth type",
+                "arguments": [
+                    "-p", "@PORT@", "-b",
+		    "-e", "tls13 signature rsa_pss_pss_sha384",
+		    "-e", "tls13 signature rsa_pss_pss_sha512"
+                ]
+            },
+            {
+                "name" : "test-tls13-rsapss-signatures.py",
+		"comment": "selfserv can be set up to use multiple certs, but only one to each auth type",
+                "arguments": [
+                    "-p", "@PORT@", "-b",
+		    "-e", "tls13 signature rsa_pss_pss_sha384",
+		    "-e", "tls13 signature rsa_pss_pss_sha512"
+                ]
+            },
+            {
+                "name" : "test-tls13-record-padding.py",
+                "arguments": [
+                    "-p", "@PORT@"
+                ]
+            },
+            {
+                "name" : "test-tls13-session-resumption.py",
+                "arguments": [
+                    "-p", "@PORT@"
+                ]
+            },
+            {
+                "name" : "test-tls13-signature-algorithms.py",
+                "arguments": [
+                    "-p", "@PORT@"
+                ],
+                "comment": "https://bugzilla.mozilla.org/show_bug.cgi?id=1482386",
+                "exp_pass": false
+            },
+            {
+                "name" : "test-tls13-unrecognised-groups.py",
+                "arguments": [
+                    "-p", "@PORT@", "--cookie"
+                ]
+            },
+            {
+                "name" : "test-tls13-version-negotiation.py",
+                "comment": "the disabled test timeouts because of https://github.com/tomato42/tlsfuzzer/issues/452",
+                "arguments": [
+                    "-p", "@PORT@",
+                    "-e", "SSL 2.0 ClientHello with TLS 1.3 version and TLS 1.3 only ciphersuites"
+                ]
+            },
+            {
+                "name" : "test-tls13-zero-length-data.py",
+                "arguments": [
+                    "-p", "@PORT@"
+                ]
+            },
+            {
+                "name" : "test-dhe-no-shared-secret-padding.py",
+                "comment": "https://bugzilla.mozilla.org/show_bug.cgi?id=1494221 and SSLv3 cannot be enabled in server",
+                "arguments": [
+                    "-p", "@PORT@",
+                    "-e", "Protocol (3, 0) in SSLv2 compatible ClientHello",
+                    "-e", "Protocol (3, 1) in SSLv2 compatible ClientHello",
+                    "-e", "Protocol (3, 2) in SSLv2 compatible ClientHello",
+                    "-e", "Protocol (3, 3) in SSLv2 compatible ClientHello",
+                    "-e", "Protocol (3, 0)"
+                ]
+            }
+        ]
+    }
+]
new file mode 100644
--- /dev/null
+++ b/security/nss/tests/tlsfuzzer/tlsfuzzer.sh
@@ -0,0 +1,110 @@
+#!/bin/bash
+#
+# 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/.
+
+########################################################################
+#
+# tests/tlsfuzzer/tlsfuzzer.sh
+#
+# Script to drive the ssl tlsfuzzer interop unit tests
+#
+########################################################################
+
+tlsfuzzer_certs()
+{
+  PROFILEDIR=`pwd`
+
+  ${BINDIR}/certutil -N -d "${PROFILEDIR}" --empty-password 2>&1
+  html_msg $? 0 "create tlsfuzzer database"
+
+  pushd "${QADIR}"
+  . common/certsetup.sh
+  popd
+
+  counter=0
+  make_cert rsa rsa2048 sign kex
+  make_cert rsa-pss rsapss sign kex
+}
+
+tlsfuzzer_init()
+{
+  SCRIPTNAME="tlsfuzzer.sh"
+  if [ -z "${INIT_SOURCED}" -o "${INIT_SOURCED}" != "TRUE" ] ; then
+    cd ../common
+    . ./init.sh
+  fi
+
+  mkdir -p "${HOSTDIR}/tlsfuzzer"
+  pushd "${HOSTDIR}/tlsfuzzer"
+  tlsfuzzer_certs
+
+  TLSFUZZER=${TLSFUZZER:=tlsfuzzer}
+  if [ ! -d "$TLSFUZZER" ]; then
+    # Can't use git-copy.sh here, as tlsfuzzer doesn't have any tags
+    git clone -q https://github.com/tomato42/tlsfuzzer/ "$TLSFUZZER"
+    git -C "$TLSFUZZER" checkout a40ce4085052a4da9a05f9149b835a76c194a0c6
+
+    # We could use tlslite-ng from pip, but the pip command installed
+    # on TC is too old to support --pre
+    ${QADIR}/../fuzz/config/git-copy.sh https://github.com/tomato42/tlslite-ng/ v0.8.0-alpha18 tlslite-ng
+
+    pushd "$TLSFUZZER"
+    ln -s ../tlslite-ng/tlslite tlslite
+    popd
+
+    # Install tlslite-ng dependencies
+    ${QADIR}/../fuzz/config/git-copy.sh https://github.com/warner/python-ecdsa master python-ecdsa
+    ${QADIR}/../fuzz/config/git-copy.sh https://github.com/benjaminp/six master six
+
+    pushd "$TLSFUZZER"
+    ln -s ../python-ecdsa/src/ecdsa ecdsa
+    ln -s ../six/six.py .
+    popd
+  fi
+
+  # Find usable port
+  PORT=${PORT-8443}
+  while true; do
+    "${BINDIR}/selfserv" -w nss -d "${HOSTDIR}/tlsfuzzer" -n rsa \
+			 -p "${PORT}" -i selfserv.pid &
+    [ -f selfserv.pid ] || sleep 5
+    if [ -f selfserv.pid ]; then
+      kill $(cat selfserv.pid)
+      wait $(cat selfserv.pid)
+      rm -f selfserv.pid
+      break
+    fi
+    PORT=$(($PORT + 1))
+  done
+
+  sed -e "s|@PORT@|${PORT}|g" \
+      -e "s|@SELFSERV@|${BINDIR}/selfserv|g" \
+      -e "s|@SERVERDIR@|${HOSTDIR}/tlsfuzzer|g" \
+      -e "s|@HOSTADDR@|${HOSTADDR}|g" \
+      ${QADIR}/tlsfuzzer/config.json.in > ${TLSFUZZER}/config.json
+  popd
+
+  SCRIPTNAME="tlsfuzzer.sh"
+  html_head "tlsfuzzer test"
+}
+
+tlsfuzzer_cleanup()
+{
+  cd ${QADIR}
+  . common/cleanup.sh
+}
+
+tlsfuzzer_run_tests()
+{
+  pushd "${HOSTDIR}/tlsfuzzer/${TLSFUZZER}"
+  PYTHONPATH=. python tests/scripts_retention.py config.json "${BINDIR}/selfserv"
+  html_msg $? 0 "tlsfuzzer" "Run successfully"
+  popd
+}
+
+cd "$(dirname "$0")"
+tlsfuzzer_init
+tlsfuzzer_run_tests
+tlsfuzzer_cleanup